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 implemention 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
Signed-off-by: Mostafa Saleh <smostafa@google.com>
diff --git a/drivers/misc/pkvm-smc/pkvm/pkvm-smc.c b/drivers/misc/pkvm-smc/pkvm/pkvm-smc.c
index 8575eb0..c686da1 100644
--- a/drivers/misc/pkvm-smc/pkvm/pkvm-smc.c
+++ b/drivers/misc/pkvm-smc/pkvm/pkvm-smc.c
@@ -6,8 +6,109 @@
*/
#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 kvm_cpu_context *host_ctxt); /* Forward unconditionally if NULL. */
+};
+
+static bool deny_smc(struct kvm_cpu_context *host_ctxt)
+{
+ host_ctxt->regs.regs[0] = SMCCC_RET_NOT_SUPPORTED;
+ /* Module rejected it and not firmware. */
+ host_ctxt->regs.regs[1] = -EPERM;
+ 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 kvm_cpu_context *host_ctxt)
+{
+ size_t num = ARRAY_SIZE(allow_list);
+ u64 smc_id = host_ctxt->regs.regs[0];
+ struct pkvm_smc_filter pval = {smc_id, NULL};
+ struct pkvm_smc_filter *entry;
+
+ /*
+ * Ignore bits that doesn't change the functionality:
+ * Bit[30]: 32/64 bit convention
+ * Bit[16]: SVE hint
+ */
+ smc_id &= ~(ARM_SMCCC_1_3_SVE_HINT | BIT(ARM_SMCCC_CALL_CONV_SHIFT));
+
+ /* alternatively, we can do 2 level binary search or switch case by service. */
+ entry = (struct pkvm_smc_filter *)__inline_bsearch((void *)&pval, allow_list, num,
+ sizeof(allow_list[0]),
+ match_smc);
+ if (!entry)
+ return deny_smc(host_ctxt);
+
+ return entry->cb ? entry->cb(host_ctxt) : false;
+}
int pkvm_smc_filter_hyp_init(const struct pkvm_module_ops *ops)
{
- return 0;
+ return ops->register_host_smc_handler(filter_smc);
}