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/tsi.h | 18 ++ arch/arm64/include/asm/tsi_cmds.h | 79 ++++++++ arch/arm64/include/asm/tsi_smc.h | 81 +++++++++ arch/arm64/include/uapi/asm/tsi_tmm.h | 73 ++++++++ arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/setup.c | 11 ++ arch/arm64/kernel/tsi.c | 45 +++++ arch/arm64/kernel/tsi_tmm.c | 253 ++++++++++++++++++++++++++ 8 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/include/asm/tsi.h create mode 100644 arch/arm64/include/asm/tsi_cmds.h create mode 100644 arch/arm64/include/asm/tsi_smc.h create mode 100644 arch/arm64/include/uapi/asm/tsi_tmm.h create mode 100644 arch/arm64/kernel/tsi.c create mode 100644 arch/arm64/kernel/tsi_tmm.c
diff --git a/arch/arm64/include/asm/tsi.h b/arch/arm64/include/asm/tsi.h new file mode 100644 index 0000000000..13dee2ddd4 --- /dev/null +++ b/arch/arm64/include/asm/tsi.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_TSI_H_ +#define __ASM_TSI_H_ + +#include <linux/jump_label.h> + +extern struct static_key_false tsi_present; + +void arm64_setup_memory(void); + +void __init arm64_tsi_init(void); + +static inline bool is_cvm_world(void) +{ + return static_branch_unlikely(&tsi_present); +} + +#endif /* __ASM_TSI_H_ */ diff --git a/arch/arm64/include/asm/tsi_cmds.h b/arch/arm64/include/asm/tsi_cmds.h new file mode 100644 index 0000000000..e46ecdb37c --- /dev/null +++ b/arch/arm64/include/asm/tsi_cmds.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_TSI_CMDS_H_ +#define __ASM_TSI_CMDS_H_ + +#include <linux/arm-smccc.h> +#include <asm/tsi_smc.h> +#include <asm/tsi_tmm.h> + +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_measurement_extend(struct cvm_measurement_extend *cvm_meas_ext) +{ + + struct arm_smccc_res res; + unsigned char value[MAX_MEASUREMENT_SIZE]; + + memcpy(value, &cvm_meas_ext->value, sizeof(cvm_meas_ext->value)); + + arm_smccc_1_1_smc(SMC_TSI_MEASUREMENT_EXTEND, cvm_meas_ext->index, + cvm_meas_ext->size, virt_to_phys(value), &res); + + return res.a0; +} + +static inline unsigned long tsi_measurement_read(struct cvm_measurement *cvm_meas) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(SMC_TSI_MEASUREMENT_READ, cvm_meas->index, &res); + + memcpy(cvm_meas->value, &res.a1, sizeof(cvm_meas->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_SIZE]; + + memcpy(challenge, attest_cmd->challenge, sizeof(attest_cmd->challenge)); + + arm_smccc_1_1_smc(SMC_TSI_ATTESTATION_TOKEN_INIT, virt_to_phys(challenge), &res); + + 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 /* __ASM_TSI_CMDS_H_ */ diff --git a/arch/arm64/include/asm/tsi_smc.h b/arch/arm64/include/asm/tsi_smc.h new file mode 100644 index 0000000000..9353e3befe --- /dev/null +++ b/arch/arm64/include/asm/tsi_smc.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_TSI_SMC_H_ +#define __ASM_TSI_SMC_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 + * ret0 == Status / error + * ret1 == Measurement value, bytes: 0 - 7 + * ret2 == Measurement value, bytes: 8 - 15 + * ret3 == Measurement value, bytes: 16 - 23 + * ret4 == Measurement value, bytes: 24 - 31 + * ret5 == Measurement value, bytes: 32 - 39 + * ret6 == Measurement value, bytes: 40 - 47 + * ret7 == Measurement value, bytes: 48 - 55 + * ret8 == Measurement value, bytes: 56 - 63 + */ +#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, bytes: 0 - 7 + * arg4 == Measurement value, bytes: 8 - 15 + * arg5 == Measurement value, bytes: 16 - 23 + * arg6 == Measurement value, bytes: 24 - 31 + * arg7 == Measurement value, bytes: 32 - 39 + * arg8 == Measurement value, bytes: 40 - 47 + * arg9 == Measurement value, bytes: 48 - 55 + * arg10 == Measurement value, bytes: 56 - 63 + * ret0 == Status / error + */ +#define SMC_TSI_MEASUREMENT_EXTEND SMC_TSI_FID(0x193) + +/* + * arg1: Challenge value, bytes: 0 - 7 + * arg2: Challenge value, bytes: 8 - 15 + * arg3: Challenge value, bytes: 16 - 23 + * arg4: Challenge value, bytes: 24 - 31 + * arg5: Challenge value, bytes: 32 - 39 + * arg6: Challenge value, bytes: 40 - 47 + * arg7: Challenge value, bytes: 48 - 55 + * arg8: Challenge value, bytes: 56 - 63 + * 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 realm_config addr + * ret0 == Status / error + */ +#define SMC_TSI_DEVICE_CERT SMC_TSI_FID(0x196) + + +#endif /* __ASM_TSI_SMC_H_ */ diff --git a/arch/arm64/include/uapi/asm/tsi_tmm.h b/arch/arm64/include/uapi/asm/tsi_tmm.h new file mode 100644 index 0000000000..04264f77e3 --- /dev/null +++ b/arch/arm64/include/uapi/asm/tsi_tmm.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_TSI_TMM_H_ +#define __ASM_TSI_TMM_H_ + +#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_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 /* __ASM_TSI_TMM_H_ */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 4c6eb5e78e..781c2216a2 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 tsi.o tsi_tmm.o
obj-y += vdso/ probes/ obj-$(CONFIG_COMPAT_VDSO) += vdso32/ diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index c687866612..22c0284cbd 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -43,6 +43,11 @@ #include <asm/cpu_ops.h> #include <asm/kasan.h> #include <asm/numa.h> + +#ifdef CONFIG_CVM_GUEST +#include <asm/tsi.h> +#endif + #include <asm/sections.h> #include <asm/setup.h> #include <asm/smp_plat.h> @@ -384,6 +389,12 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p) * cpufeature code and early parameters. */ jump_label_init(); + +#ifdef CONFIG_CVM_GUEST + /* Init TSI after jump_labels are active */ + arm64_tsi_init(); +#endif + parse_early_param();
/* diff --git a/arch/arm64/kernel/tsi.c b/arch/arm64/kernel/tsi.c new file mode 100644 index 0000000000..40da8a8aec --- /dev/null +++ b/arch/arm64/kernel/tsi.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/jump_label.h> +#include <linux/memblock.h> +#include <linux/swiotlb.h> +#include <linux/cc_platform.h> +#include <asm/tsi_tmm.h> +#include <asm/tsi_cmds.h> +#include <asm/tsi.h> + +unsigned long prot_ns_shared; +EXPORT_SYMBOL(prot_ns_shared); + +unsigned int phys_mask_shift = CONFIG_ARM64_PA_BITS; + +DEFINE_STATIC_KEY_FALSE_RO(tsi_present); + +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 arm64_setup_memory(void) +{ + if (!static_branch_unlikely(&tsi_present)) + return; +} + +void __init arm64_tsi_init(void) +{ + if (!tsi_version_matches()) + return; + + static_branch_enable(&tsi_present); +} diff --git a/arch/arm64/kernel/tsi_tmm.c b/arch/arm64/kernel/tsi_tmm.c new file mode 100644 index 0000000000..68fe474c99 --- /dev/null +++ b/arch/arm64/kernel/tsi_tmm.c @@ -0,0 +1,253 @@ +// 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/tsi_cmds.h> +#include <asm/tsi_tmm.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_open(struct inode *inode, struct file *file); +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 *arg); +static int tmm_get_attestation_token(struct cvm_attestation_cmd *arg, + struct attestation_token *attest_token); +static int tmm_get_device_cert(struct cca_device_cert *arg); + +static const struct file_operations tmm_tsi_fops = { + .owner = THIS_MODULE, + .open = tmm_tsi_open, + .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 int tmm_tsi_open(struct inode *inode, struct file *file) +{ + token.buf = NULL; + return 0; +} + +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) + kfree(token.buf); + return 0; +} + +static int tmm_get_tsi_version(struct cvm_tsi_version *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 *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 *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.");