virtcca inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9Q85J
--------------------------------
Add TSI interface for Confidential VMs: 1. Add smc call for Confidential cVMs 2. Add TSI interface driver
Signed-off-by: Shengjie Li lishengjie12@huawei.com --- arch/arm64/include/asm/cvm_guest.h | 4 + arch/arm64/include/asm/cvm_smc.h | 168 ++++++++++++++++++ arch/arm64/include/uapi/asm/cvm_tsi.h | 81 +++++++++ arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/cvm_guest.c | 30 +++- arch/arm64/kernel/cvm_tsi.c | 246 ++++++++++++++++++++++++++ arch/arm64/kernel/setup.c | 4 + 7 files changed, 533 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/cvm_smc.h create mode 100644 arch/arm64/include/uapi/asm/cvm_tsi.h create mode 100644 arch/arm64/kernel/cvm_tsi.c
diff --git a/arch/arm64/include/asm/cvm_guest.h b/arch/arm64/include/asm/cvm_guest.h index cd487b27ae..46fcfc3c92 100644 --- a/arch/arm64/include/asm/cvm_guest.h +++ b/arch/arm64/include/asm/cvm_guest.h @@ -23,6 +23,8 @@ static inline bool is_swiotlb_for_alloc(struct device *dev)
extern void __init swiotlb_cvm_update_mem_attributes(void);
+extern void cvm_tsi_init(void); + #else
static inline int set_cvm_memory_encrypted(unsigned long addr, int numpages) @@ -42,5 +44,7 @@ static inline bool is_cvm_world(void)
static inline void __init swiotlb_cvm_update_mem_attributes(void) {}
+static inline void cvm_tsi_init(void) {} + #endif /* CONFIG_CVM_GUEST */ #endif /* __CVM_GUEST_H */ diff --git a/arch/arm64/include/asm/cvm_smc.h b/arch/arm64/include/asm/cvm_smc.h new file mode 100644 index 0000000000..c4d56333e3 --- /dev/null +++ b/arch/arm64/include/asm/cvm_smc.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_CVM_SMC_H_ +#define __ASM_CVM_SMC_H_ + +#ifdef CONFIG_CVM_GUEST + +#include <linux/arm-smccc.h> +#include <asm/cvm_tsi.h> +#include <linux/slab.h> + +#define SMC_TSI_CALL_BASE 0xC4000000 +#define TSI_ABI_VERSION_MAJOR 1 +#define TSI_ABI_VERSION_MINOR 0 +#define TSI_ABI_VERSION ((TSI_ABI_VERSION_MAJOR << 16) | TSI_ABI_VERSION_MINOR) + +#define TSI_ABI_VERSION_GET_MAJOR(_version) ((_version) >> 16) +#define TSI_ABI_VERSION_GET_MINOR(_version) ((_version) & 0xFFFF) + +#define TSI_SUCCESS 0 +#define TSI_ERROR_INPUT 1 +#define TSI_ERROR_STATE 2 +#define TSI_INCOMPLETE 3 + +#define SMC_TSI_FID(_x) (SMC_TSI_CALL_BASE + (_x)) +#define SMC_TSI_ABI_VERSION SMC_TSI_FID(0x190) + +/* + * arg1: Index, which measurements slot to read + * arg2: Measurement value + * ret0: Status / error + */ +#define SMC_TSI_MEASUREMENT_READ SMC_TSI_FID(0x192) + +/* + * arg1: Index, which measurements slot to extend + * arg2: Size of realm measurement in bytes, max 64 bytes + * arg3: Measurement value + * ret0: Status / error + */ +#define SMC_TSI_MEASUREMENT_EXTEND SMC_TSI_FID(0x193) + +/* + * arg1: Challenge value + * ret0: Status / error + * ret1: Upper bound on attestation token size in bytes + */ +#define SMC_TSI_ATTESTATION_TOKEN_INIT SMC_TSI_FID(0x194) + +/* + * arg1: IPA of the Granule to which the token will be written + * arg2: Offset within Granule to start of buffer in bytes + * arg3: Size of buffer in bytes + * ret0: Status / error + * ret1: Number of bytes written to buffer + */ +#define SMC_TSI_ATTESTATION_TOKEN_CONTINUE SMC_TSI_FID(0x195) + +/* + * arg1: struct cVM config addr + * ret0: Status / error + */ +#define SMC_TSI_CVM_CONFIG SMC_TSI_FID(0x196) + +/* + * arg1: Device cert buffer + * arg2: Size of buffer in bytes + * ret0: Status / error + */ +#define SMC_TSI_DEVICE_CERT SMC_TSI_FID(0x19A) + +static inline unsigned long tsi_get_version(void) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(SMC_TSI_ABI_VERSION, &res); + + return res.a0; +} + +static inline unsigned long tsi_get_cvm_config(struct cvm_config *cfg) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(SMC_TSI_CVM_CONFIG, &res); + + cfg->ipa_bits = res.a1; + cfg->algorithm = res.a2; + + return res.a0; +} + +static inline unsigned long tsi_measurement_extend(struct cvm_measurement_extend *cvm_meas_ext) +{ + + struct arm_smccc_res res; + unsigned char *value; + + value = kmalloc(MAX_MEASUREMENT_SIZE, GFP_KERNEL); + if (!value) + return -ENOMEM; + memcpy(value, cvm_meas_ext->value, MAX_MEASUREMENT_SIZE); + + arm_smccc_1_1_smc(SMC_TSI_MEASUREMENT_EXTEND, cvm_meas_ext->index, + cvm_meas_ext->size, virt_to_phys(value), &res); + kfree(value); + + return res.a0; +} + +static inline unsigned long tsi_measurement_read(struct cvm_measurement *cvm_meas) +{ + struct arm_smccc_res res; + unsigned char *value; + + value = kmalloc(MAX_MEASUREMENT_SIZE, GFP_KERNEL); + if (!value) + return -ENOMEM; + arm_smccc_1_1_smc(SMC_TSI_MEASUREMENT_READ, cvm_meas->index, + virt_to_phys(value), &res); + + memcpy(cvm_meas->value, value, MAX_MEASUREMENT_SIZE); + kfree(value); + + return res.a0; +} + +static inline unsigned long tsi_attestation_token_init(struct cvm_attestation_cmd *attest_cmd) +{ + struct arm_smccc_res res; + unsigned char *challenge; + + challenge = kmalloc(CHALLENGE_SIZE, GFP_KERNEL); + if (!challenge) + return -ENOMEM; + memcpy(challenge, attest_cmd->challenge, CHALLENGE_SIZE); + + arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_INIT, virt_to_phys(challenge), &res); + kfree(challenge); + + return res.a0; +} + +static inline unsigned long tsi_attestation_token_continue(struct cvm_attestation_cmd *attest_cmd) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_CONTINUE, virt_to_phys(attest_cmd->granule_ipa), + attest_cmd->offset, attest_cmd->size, &res); + + attest_cmd->num_wr_bytes = res.a1; + + return res.a0; +} + +static inline unsigned long tsi_get_device_cert(unsigned char *device_cert, + unsigned long *device_cert_size) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(SMC_TSI_DEVICE_CERT, virt_to_phys(device_cert), *device_cert_size, &res); + + *device_cert_size = res.a1; + + return res.a0; +} + +#endif /* CONFIG_CVM_GUEST */ +#endif /* __ASM_CVM_SMC_H_ */ diff --git a/arch/arm64/include/uapi/asm/cvm_tsi.h b/arch/arm64/include/uapi/asm/cvm_tsi.h new file mode 100644 index 0000000000..6dd419aed2 --- /dev/null +++ b/arch/arm64/include/uapi/asm/cvm_tsi.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_CVM_TSI_H_ +#define __ASM_CVM_TSI_H_ + +#ifdef CONFIG_CVM_GUEST + +#include <linux/ioctl.h> + +#define TSI_MAGIC 'T' + +/* Measurement slot reserved for RIM */ +#define RIM_MEASUREMENT_SLOT (0U) + +/* Maximum number of measurements */ +#define MEASUREMENT_SLOT_NR (5U) + +/* Size in bytes of the SHA256 measurement */ +#define SHA256_SIZE (32U) + +/* Size in bytes of the SHA512 measurement */ +#define SHA512_SIZE (64U) + +/* + * Size in bytes of the largest measurement type that can be supported. + * This macro needs to be updated accordingly if new algorithms are supported. + */ +#define MAX_MEASUREMENT_SIZE SHA512_SIZE +#define MAX_DEV_CERT_SIZE 4096 + +#define MAX_TOKEN_GRANULE_PAGE (10U) +#define CHALLENGE_SIZE (64U) + +struct cvm_attester { + int dev_fd; +}; + +struct cvm_measurement { + int index; + unsigned char value[MAX_MEASUREMENT_SIZE]; +}; + +struct cvm_tsi_version { + int major; + int minor; +}; + +struct cvm_config { + unsigned long ipa_bits; /* Width of IPA in bits */ + unsigned long algorithm; /* Hash algorithm */ +}; + +struct cvm_measurement_extend { + unsigned long index; + unsigned long size; + unsigned char value[MAX_MEASUREMENT_SIZE]; +}; + +struct cvm_attestation_cmd { + unsigned char challenge[CHALLENGE_SIZE]; /* input: challenge value */ + unsigned long token_size; /* return: challenge value */ + void *granule_head; + void *granule_ipa; /* IPA of the Granule to which the token will be written */ + unsigned long granule_count; + unsigned long offset; /* Offset within Granule to start of buffer in bytes */ + unsigned long size; /* Size of buffer in bytes */ + unsigned long num_wr_bytes; /* Number of bytes written to buffer */ +}; + +struct cca_device_cert { + unsigned long size; + unsigned char value[MAX_DEV_CERT_SIZE]; +}; + +#define TMM_GET_TSI_VERSION _IOR(TSI_MAGIC, 0, struct cvm_tsi_version) + +#define TMM_GET_ATTESTATION_TOKEN _IOWR(TSI_MAGIC, 1, struct cvm_attestation_cmd) + +#define TMM_GET_DEVICE_CERT _IOR(TSI_MAGIC, 2, struct cca_device_cert) + +#endif /* CONFIG_CVM_GUEST */ +#endif /* __ASM_CVM_TSI_H_ */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 4c6eb5e78e..c839a123aa 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -73,7 +73,7 @@ obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o obj-$(CONFIG_ARM64_MTE) += mte.o obj-$(CONFIG_MPAM) += mpam/ -obj-$(CONFIG_CVM_GUEST) += cvm_guest.o +obj-$(CONFIG_CVM_GUEST) += cvm_guest.o cvm_tsi.o
obj-y += vdso/ probes/ obj-$(CONFIG_COMPAT_VDSO) += vdso32/ diff --git a/arch/arm64/kernel/cvm_guest.c b/arch/arm64/kernel/cvm_guest.c index c1f27992ad..de60abcf09 100644 --- a/arch/arm64/kernel/cvm_guest.c +++ b/arch/arm64/kernel/cvm_guest.c @@ -11,11 +11,13 @@ #include <asm/cacheflush.h> #include <asm/set_memory.h> #include <asm/tlbflush.h> +#include <asm/cvm_smc.h>
#define CVM_PTE_NS_BIT 5 #define CVM_PTE_NS_MASK (1 << CVM_PTE_NS_BIT)
static bool cvm_guest_enable __read_mostly; +DEFINE_STATIC_KEY_FALSE_RO(cvm_tsi_present);
/* please use 'cvm_guest=1' to enable cvm guest feature */ static int __init setup_cvm_guest(char *str) @@ -37,9 +39,35 @@ static int __init setup_cvm_guest(char *str) } early_param("cvm_guest", setup_cvm_guest);
+static bool tsi_version_matches(void) +{ + unsigned long ver = tsi_get_version(); + + if (ver == SMCCC_RET_NOT_SUPPORTED) + return false; + + pr_info("RME: TSI version %lu.%lu advertised\n", + TSI_ABI_VERSION_GET_MAJOR(ver), + TSI_ABI_VERSION_GET_MINOR(ver)); + + return (ver >= TSI_ABI_VERSION && + TSI_ABI_VERSION_GET_MAJOR(ver) == TSI_ABI_VERSION_MAJOR); +} + +void __init cvm_tsi_init(void) +{ + if (!cvm_guest_enable) + return; + + if (!tsi_version_matches()) + return; + + static_branch_enable(&cvm_tsi_present); +} + bool is_cvm_world(void) { - return cvm_guest_enable; + return cvm_guest_enable && static_branch_likely(&cvm_tsi_present); }
static int change_page_range_cvm(pte_t *ptep, unsigned long addr, void *data) diff --git a/arch/arm64/kernel/cvm_tsi.c b/arch/arm64/kernel/cvm_tsi.c new file mode 100644 index 0000000000..b48e5b17f4 --- /dev/null +++ b/arch/arm64/kernel/cvm_tsi.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/module.h> +#include <linux/memblock.h> +#include <linux/slab.h> +#include <linux/miscdevice.h> +#include <linux/preempt.h> +#include <asm/cvm_smc.h> +#include <asm/cvm_tsi.h> + +#define GRANULE_SIZE PAGE_SIZE + +struct attestation_token { + void *buf; + unsigned long size; +}; + +static struct attestation_token token; + +static long tmm_tsi_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static int tmm_tsi_release(struct inode *inode, struct file *file); +static ssize_t tmm_token_read(struct file *file, char __user *user_buffer, + size_t size, loff_t *offset); + +static int tmm_get_tsi_version(struct cvm_tsi_version __user *arg); +static int tmm_get_attestation_token(struct cvm_attestation_cmd __user *arg, + struct attestation_token *attest_token); +static int tmm_get_device_cert(struct cca_device_cert __user *arg); + +static const struct file_operations tmm_tsi_fops = { + .owner = THIS_MODULE, + .read = tmm_token_read, + .release = tmm_tsi_release, + .unlocked_ioctl = tmm_tsi_ioctl +}; + +static struct miscdevice ioctl_dev = { + MISC_DYNAMIC_MINOR, + "tsi", + &tmm_tsi_fops, +}; + +static int __init tmm_tsi_init(void) +{ + unsigned long ver; + int ret; + + ver = tsi_get_version(); + if (ver == SMCCC_RET_NOT_SUPPORTED) { + pr_err("tmm_tsi: SMC return not supported!\n"); + return -EIO; + } + + ret = misc_register(&ioctl_dev); + if (ret) { + pr_err("tmm_tsi: misc device register failed (%d)!\n", ret); + return ret; + } + + pr_warn("tmm_tsi: module loaded (version %lu.%lu).\n", + TSI_ABI_VERSION_GET_MAJOR(ver), + TSI_ABI_VERSION_GET_MINOR(ver)); + + return 0; +} + +static void __exit tmm_tsi_exit(void) +{ + if (token.buf != NULL) + kfree(token.buf); + misc_deregister(&ioctl_dev); + pr_warn("tmm_tsi: module unloaded.\n"); +} + +static long tmm_tsi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + switch (cmd) { + case TMM_GET_TSI_VERSION: + ret = tmm_get_tsi_version((struct cvm_tsi_version *)arg); + break; + case TMM_GET_ATTESTATION_TOKEN: + ret = tmm_get_attestation_token((struct cvm_attestation_cmd *)arg, &token); + break; + case TMM_GET_DEVICE_CERT: + ret = tmm_get_device_cert((struct cca_device_cert *)arg); + break; + default: + pr_err("tmm_tsi: unknown ioctl command (0x%x)!\n", cmd); + return -ENOTTY; + } + + return ret; +} + +static ssize_t tmm_token_read(struct file *file, char __user *user_buffer, + size_t size, loff_t *offset) +{ + int ret; + int to_copy; + + if (*offset >= token.size) + return 0; + + to_copy = min((int)size, (int)(token.size - *offset)); + ret = copy_to_user(user_buffer, token.buf + *offset, to_copy); + if (ret) { + pr_err("tmm_tsi: copy token to user failed (%d)!\n", ret); + return -1; + } + + *offset += to_copy; + return to_copy; +} + +static int tmm_tsi_release(struct inode *inode, struct file *file) +{ + if (token.buf != NULL) { + memset(token.buf, 0, GRANULE_SIZE * MAX_TOKEN_GRANULE_PAGE); + kfree(token.buf); + } + return 0; +} + +static int tmm_get_tsi_version(struct cvm_tsi_version __user *arg) +{ + struct cvm_tsi_version ver_measured = {0}; + unsigned long ver; + unsigned long ret; + + ver = tsi_get_version(); + ver_measured.major = TSI_ABI_VERSION_GET_MAJOR(ver); + ver_measured.minor = TSI_ABI_VERSION_GET_MINOR(ver); + + ret = copy_to_user(arg, &ver_measured, sizeof(struct cvm_tsi_version)); + if (ret) { + pr_err("tmm_tsi: copy data to user failed (%lu)!\n", ret); + return -EFAULT; + } + + return 0; +} + +static int tmm_get_attestation_token(struct cvm_attestation_cmd __user *arg, + struct attestation_token *attest_token) +{ + unsigned long ret; + struct cvm_attestation_cmd cmd = {0}; + + ret = copy_from_user(&(cmd.challenge), &(arg->challenge), sizeof(cmd.challenge)); + if (ret) { + pr_err("tmm_tsi: copy data from user failed (%lu)!\n", ret); + return -EFAULT; + } + + /* Allocate a large memory */ + attest_token->buf = kmalloc(GRANULE_SIZE * MAX_TOKEN_GRANULE_PAGE, GFP_KERNEL); + if (!attest_token->buf) + return -ENOMEM; + cmd.granule_head = attest_token->buf; + cmd.granule_ipa = cmd.granule_head; + + /* preempt_disable(); */ + + ret = tsi_attestation_token_init(&cmd); + if (ret) { + pr_err("tmm_tsi: tsi call tsi_attestation_token_init failed (%lu)!\n", ret); + return -EIO; + } + + do { /* Retrieve one Granule of data per loop iteration */ + cmd.granule_ipa = cmd.granule_head + + (unsigned long)(cmd.granule_count * GRANULE_SIZE); + cmd.offset = 0; + + do { /* Retrieve sub-Granule chunk of data per loop iteration */ + cmd.size = GRANULE_SIZE - cmd.offset; + ret = tsi_attestation_token_continue(&cmd); + cmd.offset += cmd.num_wr_bytes; + } while (ret == TSI_INCOMPLETE && cmd.offset < GRANULE_SIZE); + + cmd.granule_count += 1; + if (cmd.granule_count >= MAX_TOKEN_GRANULE_PAGE && ret == TSI_INCOMPLETE) { + pr_err("tmm_tsi: macro MAX_TOKEN_GRANULE_PAGE (%d) is too small!\n", + MAX_TOKEN_GRANULE_PAGE); + return -ENOMEM; + } + + } while (ret == TSI_INCOMPLETE); + + /* preempt_enable(); */ + + /* Send to user space the total size of the token */ + cmd.granule_count = cmd.granule_count - 1; + cmd.token_size = (unsigned long)(GRANULE_SIZE * cmd.granule_count) + cmd.offset; + attest_token->size = cmd.token_size; + + ret = copy_to_user(&(arg->token_size), &(cmd.token_size), sizeof(cmd.token_size)); + if (ret) { + pr_err("tmm_tsi: copy data to user failed (%lu)!\n", ret); + return -EFAULT; + } + + return 0; +} + +static int tmm_get_device_cert(struct cca_device_cert __user *arg) +{ + unsigned long ret; + unsigned char *device_cert; + unsigned long device_cert_size; + + device_cert_size = MAX_DEV_CERT_SIZE; + device_cert = kmalloc(device_cert_size, GFP_KERNEL); + if (!device_cert) + return -ENOMEM; + ret = tsi_get_device_cert(device_cert, &device_cert_size); + if (ret != TSI_SUCCESS) { + pr_err("tmm_tsi: tsi call tsi_get_device_cert failed (%lu)!\n", ret); + kfree(device_cert); + return -EIO; + } + + ret = copy_to_user(arg->value, device_cert, device_cert_size); + if (ret) { + pr_err("tmm_tsi: copy data to user failed (%lu)!\n", ret); + kfree(device_cert); + return -EFAULT; + } + kfree(device_cert); + + ret = copy_to_user(&(arg->size), &device_cert_size, sizeof(device_cert_size)); + if (ret) { + pr_err("tmm_tsi: copy data to user failed (%lu)!\n", ret); + return -EFAULT; + } + + return 0; +} + +module_init(tmm_tsi_init); +module_exit(tmm_tsi_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("HUAWEI TECHNOLOGIES CO., LTD."); +MODULE_DESCRIPTION("Interacting with TMM through TSI interface from user space."); diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index c687866612..2148f5cbb5 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -43,6 +43,7 @@ #include <asm/cpu_ops.h> #include <asm/kasan.h> #include <asm/numa.h> +#include <asm/cvm_guest.h> #include <asm/sections.h> #include <asm/setup.h> #include <asm/smp_plat.h> @@ -386,6 +387,9 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p) jump_label_init(); parse_early_param();
+ /* Init TSI after jump_labels are active */ + cvm_tsi_init(); + /* * Unmask asynchronous aborts and fiq after bringing up possible * earlycon. (Report possible System Errors once we can report this