ANDROID: misc: pkvm_smc: Add an allow list for SMCs
Filter SMCs based on allow list, where they can be further filtered based on arguments (some arguments might hold addresses that aren't allowed for example) The driver has list of different trusted OS services as defined by trusty[1]. The list is hardcoded in the driver, we can improve this to generate them dynamically. The current implementation relies on binary search of all IDs, alternatively we can use switch cases per service then binary search function IDs, or even tweak the search for some services to further split the function ID space. [1] https://android.googlesource.com/trusty/lk/trusty/+/refs/heads/master/lib/sm/include/lib/sm/smcall.h Bug: 278009271 Bug: 357781595 Change-Id: Iaf4802fdeee9b40647648402f9516da883a04ec2 Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
committed by
Treehugger Robot
parent
55d5ad26bf
commit
20d9754be1
@@ -6,8 +6,106 @@
|
||||
*/
|
||||
|
||||
#include <asm/kvm_pkvm_module.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bsearch.h>
|
||||
|
||||
struct pkvm_smc_filter {
|
||||
u64 smc_id;
|
||||
bool (*cb)(struct user_pt_regs *regs); /* Forward unconditionally if NULL. */
|
||||
};
|
||||
|
||||
static bool deny_smc(struct user_pt_regs *regs)
|
||||
{
|
||||
regs->regs[0] = SMCCC_RET_NOT_SUPPORTED;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be sorted.
|
||||
* Allow SMCCCs that are known to be safe.
|
||||
* PSCI and FFA are already handled by the hypervisor.
|
||||
*/
|
||||
const struct pkvm_smc_filter allow_list[] = {
|
||||
/* Trusted OS Calls: Trusty Trusted OS (Yielding) */
|
||||
{0x32000014, NULL,}, /* SMC_SC_VIRTIO_GET_DESCR. */
|
||||
{0x32000015, NULL,}, /* SMC_SC_VIRTIO_START. */
|
||||
{0x32000016, NULL,}, /* SMC_SC_VIRTIO_STOP. */
|
||||
{0x32000017, NULL,}, /* SMC_SC_VDEV_RESET. */
|
||||
{0x32000018, NULL,}, /* SMC_SC_VDEV_KICK_VQ. */
|
||||
{0x32000019, NULL,}, /* SMC_NC_VDEV_KICK_VQ. */
|
||||
{0x3200001E, NULL,}, /* SMC_SC_CREATE_QL_TIPC_DEV. */
|
||||
{0x3200001F, NULL,}, /* SMC_SC_SHUTDOWN_QL_TIPC_DEV. */
|
||||
{0x32000020, NULL,}, /* SMC_SC_HANDLE_QL_TIPC_DEV_CMD. */
|
||||
{0x32000021, NULL,}, /* SMC_FC_HANDLE_QL_TIPC_DEV_CMD. */
|
||||
|
||||
/* Trusted OS Calls: Trusty Secure Monitor (Yielding) */
|
||||
{0x3C000000, NULL,}, /* SMC_SC_RESTART_LAST. */
|
||||
{0x3C000001, NULL,}, /* SMC_SC_LOCKED_NOP. */
|
||||
{0x3C000002, NULL,}, /* SMC_SC_RESTART_FIQ. */
|
||||
{0x3C000003, NULL,}, /* SMC_SC_NOP. */
|
||||
{0x3C000004, NULL,}, /* SMC_SC_SCHED_SHARE_REGISTER. */
|
||||
{0x3C000005, NULL,}, /* SMC_SC_SCHED_SHARE_UNREGISTER. */
|
||||
|
||||
/* Arm Architecture Calls. */
|
||||
{0x80000000, NULL}, /* SMCCC_VERSION. */
|
||||
{0x80000001, NULL}, /* SMCCC_ARCH_FEATURES. */
|
||||
{0x80000002, NULL}, /* SMCCC_ARCH_SOC_ID. */
|
||||
|
||||
/* Standard Secure services: TRNG */
|
||||
{0x84000050, NULL,}, /* TRNG_VERSION. */
|
||||
{0x84000051, NULL,}, /* TRNG_FEATURES. */
|
||||
{0x84000052, NULL,}, /* TRNG_GET_UUID. */
|
||||
{0x84000053, NULL,}, /* TRNG_RND. */
|
||||
|
||||
/* Trusted OS Calls: Trusty Secure Monitor (Fast) */
|
||||
{0xBC000001, NULL,}, /* SMC_FC_FIQ_EXIT. */
|
||||
{0xBC000002, NULL,}, /* SMC_FC_REQUEST_FIQ. */
|
||||
{0xBC000003, NULL,}, /* SMC_FC_GET_NEXT_IRQ. */
|
||||
{0xBC000007, NULL,}, /* SMC_FC_CPU_SUSPEND. */
|
||||
{0xBC000008, NULL,}, /* SMC_FC_CPU_RESUME. */
|
||||
{0xBC000009, NULL,}, /* SMC_FC_AARCH_SWITCH. */
|
||||
{0xBC00000A, NULL,}, /* SMC_FC_GET_VERSION_STR. */
|
||||
{0xBC00000B, NULL,}, /* SMC_FC_API_VERSION. */
|
||||
{0xBC00000C, NULL,}, /* SMC_FC_FIQ_RESUME. */
|
||||
{0xBC00000D, NULL,}, /* SMC_FC_GET_SMP_MAX_CPUS. */
|
||||
};
|
||||
|
||||
static inline int match_smc(const void *key, const void *elt)
|
||||
{
|
||||
u64 smc_id = ((struct pkvm_smc_filter *)key)->smc_id;
|
||||
u64 cur_id = ((struct pkvm_smc_filter *)elt)->smc_id;
|
||||
|
||||
return smc_id - cur_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Block all by default.
|
||||
* return false will allow the SMC to be forwarded.
|
||||
*/
|
||||
bool filter_smc(struct user_pt_regs *regs)
|
||||
{
|
||||
u64 smc_id = regs->regs[0];
|
||||
/*
|
||||
* Ignore bits that doesn't change the functionality:
|
||||
* Bit[30]: 32/64 bit convention
|
||||
* Bit[16]: SVE hint
|
||||
*/
|
||||
u64 mask = ~(ARM_SMCCC_1_3_SVE_HINT | BIT(ARM_SMCCC_CALL_CONV_SHIFT));
|
||||
struct pkvm_smc_filter pval = {smc_id & mask, NULL};
|
||||
struct pkvm_smc_filter *entry;
|
||||
|
||||
/* alternatively, we can do 2 level binary search or switch case by service. */
|
||||
entry = (struct pkvm_smc_filter *)__inline_bsearch((void *)&pval, allow_list,
|
||||
ARRAY_SIZE(allow_list),
|
||||
sizeof(allow_list[0]),
|
||||
match_smc);
|
||||
if (!entry)
|
||||
return deny_smc(regs);
|
||||
|
||||
return entry->cb ? entry->cb(regs) : false;
|
||||
}
|
||||
|
||||
int pkvm_smc_filter_hyp_init(const struct pkvm_module_ops *ops)
|
||||
{
|
||||
return 0;
|
||||
return ops->register_host_smc_handler(filter_smc);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user