From 0d80aa1032ebe3e4e71c92a483bd3ce99873458c Mon Sep 17 00:00:00 2001 From: hanliyang Date: Tue, 24 Sep 2024 15:16:39 +0800 Subject: [PATCH 1/8] anolis: KVM: SVM: CSV: Fix the vm_size even if CSV3 feature is unsupported on Hygon CPUs ANBZ: #13046 Assume the userspace request CSV3's KVM ioctl interface on Hygon CPUs before C86-4G, the vm_size if as value sizeof(struct kvm_svm), and functions for the CSV3's KVM ioctl interface try to check whether the guest is a CSV3 guest by access the structure as below: struct kvm_csv_info { ...... bool csv3_active; ...... }; struct kvm_svm_csv { struct kvm_svm kvm_svm; struct kvm_csv_info csv_info; }; But the csv_info field of struct kvm_svm_csv will not be allocated, the functions for the CSV3's KVM ioctl interface will not get value of csv3_active field of struct kvm_csv_info. Always fix the vm_size in csv_init() to address the above issue. [ hly: always points mem_enc_op to the function csv_mem_enc_op() ] Signed-off-by: hanliyang --- arch/x86/kvm/svm/csv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index 3956c3b08eac..2168f6672d9c 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -1551,14 +1551,15 @@ void __init csv_init(struct kvm_x86_ops *ops) if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) return; + memcpy(&csv_x86_ops, ops, sizeof(struct kvm_x86_ops)); + + ops->mem_enc_op = csv_mem_enc_op; + ops->vm_size = sizeof(struct kvm_svm_csv); + /* Retrieve CSV CPUID information */ cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); if (eax & CSV_BIT) { - memcpy(&csv_x86_ops, ops, sizeof(struct kvm_x86_ops)); - - ops->mem_enc_op = csv_mem_enc_op; ops->vm_destroy = csv_vm_destroy; - ops->vm_size = sizeof(struct kvm_svm_csv); ops->handle_exit = csv_handle_exit; ops->guest_memory_reclaimed = csv_guest_memory_reclaimed; } -- Gitee From bf3d8b7d1f5a109fbb49e4e0baaae1931a5dfa94 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Mon, 7 Oct 2024 18:16:23 +0800 Subject: [PATCH 2/8] anolis: crypto: ccp: Get api version again when update Hygon CSV firmware at runtime ANBZ: #13046 The commit 5c6a31eeb443 ("anolis: crypto: ccp: Implement CSV_DOWNLOAD_FIRMWARE ioctl command") support update Hygon CSV firmware at runtime, but it don't update API version info in the driver after issues the DOWNLOAD_FIRMWARE command. When we want use the new features in the updated firmware, the version check in this driver will fail. To address this problem, we should regain the api version when DOWNLOAD_FIRMWARE command returns. Fixes: 5c6a31eeb443 ("crypto: ccp: Implement CSV_DOWNLOAD_FIRMWARE ioctl command") Signed-off-by: hanliyang --- drivers/crypto/ccp/sev-dev.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index aca53b2fd1fb..686eaae9f643 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1329,10 +1329,19 @@ static int csv_ioctl_do_download_firmware(struct sev_issue_cmd *argp) data->len = input.length; ret = __sev_do_cmd_locked(SEV_CMD_DOWNLOAD_FIRMWARE, data, &argp->error); - if (ret) + if (ret) { pr_err("Failed to update CSV firmware: %#x\n", argp->error); - else + goto err_free_page; + } else { pr_info("CSV firmware update successful\n"); + } + + /* + * Synchronize API version status, and return -EIO if the Hygon PSP fails + * to respond to the PLATFORM_STATUS API. + */ + if (sev_get_api_version()) + ret = -EIO; err_free_page: __free_pages(p, order); -- Gitee From 9931364cb693334585eb8e939583547f49b54ada Mon Sep 17 00:00:00 2001 From: hanliyang Date: Sat, 2 Nov 2024 18:56:25 +0800 Subject: [PATCH 3/8] anolis: KVM: SVM: CSV: Ensure all the GPRs and some non-GPRs are synced before LAUNCH_ENCRYPT_VMCB ANBZ: #13046 Even though most of the GPRs is zero at reset state, we should explicitly set these before LAUNCH_ENCRYPT_VMCB. The DR6 register is not zero at reset state, we should explicitly set DR6 before LAUNCH_ENCRYPT_VMCB. The PKRU currently is unsupported on Hygon CPUs, this register is zero at reset state, nevertheless explicitly set PKRU before LAUNCH_ENCRYPT_VMCB. Signed-off-by: hanliyang --- arch/x86/kvm/svm/csv.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index 2168f6672d9c..b984283c9c75 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -214,11 +214,37 @@ static int csv_sync_vmsa(struct vcpu_svm *svm) /* Sync registgers per spec. */ save->rax = svm->vcpu.arch.regs[VCPU_REGS_RAX]; + save->rbx = svm->vcpu.arch.regs[VCPU_REGS_RBX]; + save->rcx = svm->vcpu.arch.regs[VCPU_REGS_RCX]; save->rdx = svm->vcpu.arch.regs[VCPU_REGS_RDX]; + save->rsp = svm->vcpu.arch.regs[VCPU_REGS_RSP]; + save->rbp = svm->vcpu.arch.regs[VCPU_REGS_RBP]; + save->rsi = svm->vcpu.arch.regs[VCPU_REGS_RSI]; + save->rdi = svm->vcpu.arch.regs[VCPU_REGS_RDI]; +#ifdef CONFIG_X86_64 + save->r8 = svm->vcpu.arch.regs[VCPU_REGS_R8]; + save->r9 = svm->vcpu.arch.regs[VCPU_REGS_R9]; + save->r10 = svm->vcpu.arch.regs[VCPU_REGS_R10]; + save->r11 = svm->vcpu.arch.regs[VCPU_REGS_R11]; + save->r12 = svm->vcpu.arch.regs[VCPU_REGS_R12]; + save->r13 = svm->vcpu.arch.regs[VCPU_REGS_R13]; + save->r14 = svm->vcpu.arch.regs[VCPU_REGS_R14]; + save->r15 = svm->vcpu.arch.regs[VCPU_REGS_R15]; +#endif save->rip = svm->vcpu.arch.regs[VCPU_REGS_RIP]; + + /* Sync some non-GPR registers before encrypting */ save->xcr0 = svm->vcpu.arch.xcr0; + save->pkru = svm->vcpu.arch.pkru; save->xss = svm->vcpu.arch.ia32_xss; + save->dr6 = svm->vcpu.arch.dr6; + /* + * CSV3 will use a VMSA that is pointed to by the VMCB, not + * the traditional VMSA that is part of the VMCB. Copy the + * traditional VMSA as it has been built so far (in prep + * for LAUNCH_ENCRYPT_VMCB) to be the initial CSV3 state. + */ memcpy(svm->vmsa, save, sizeof(*save)); return 0; } -- Gitee From 53901c4136dc1c00b3bf9a93ccc274348a033d9d Mon Sep 17 00:00:00 2001 From: hanliyang Date: Wed, 25 Sep 2024 21:18:41 +0800 Subject: [PATCH 4/8] anolis: crypto: ccp: Provide csv_get_extension_info() to present extensions of newer CSV firmware ANBZ: #13046 As more and more confidential computing features are provided, the hypervisor and userspace VMM should recognize the extended features. Provide csv_get_extension_info() to present the extended confidential computing features of the newer CSV firmware so that the hypervisor can utilize the extended features when launch and running a confidential guest. Signed-off-by: hanliyang --- drivers/crypto/ccp/Makefile | 3 +- drivers/crypto/ccp/hygon/csv-dev.c | 70 ++++++++++++++++++++++++++++++ drivers/crypto/ccp/hygon/csv-dev.h | 26 +++++++++++ drivers/crypto/ccp/hygon/sp-dev.h | 24 ++++++++++ drivers/crypto/ccp/sev-dev.c | 23 ++++------ include/linux/psp-csv.h | 28 +++++++++++- 6 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 drivers/crypto/ccp/hygon/csv-dev.c create mode 100644 drivers/crypto/ccp/hygon/csv-dev.h create mode 100644 drivers/crypto/ccp/hygon/sp-dev.h diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile index 426ef8f2bd47..a4cc3a6d3fd1 100644 --- a/drivers/crypto/ccp/Makefile +++ b/drivers/crypto/ccp/Makefile @@ -13,7 +13,8 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o \ sev-dev.o \ tee-dev.o \ psp-ringbuf.o \ - vpsp.o + vpsp.o \ + hygon/csv-dev.o ccp-$(CONFIG_TDM_DEV_HYGON) += tdm-dev.o diff --git a/drivers/crypto/ccp/hygon/csv-dev.c b/drivers/crypto/ccp/hygon/csv-dev.c new file mode 100644 index 000000000000..fae8fbb5072c --- /dev/null +++ b/drivers/crypto/ccp/hygon/csv-dev.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HYGON CSV interface + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "csv-dev.h" + +/* + * Hygon CSV build info: + * Hygon CSV build info is 32-bit in length other than 8-bit as that + * in AMD SEV. + */ +u32 hygon_csv_build; + +/* + * csv_update_api_version used to update the api version of HYGON CSV + * firmwareat driver side. + * Currently, we only need to update @hygon_csv_build. + */ +void csv_update_api_version(struct sev_user_data_status *status) +{ + if (status) { + hygon_csv_build = (status->flags >> 9) | + ((u32)status->build << 23); + } +} + +int csv_get_extension_info(void *buf, size_t *size) +{ + /* If @hygon_csv_build is 0, this means CSV firmware doesn't exist or + * the psp device doesn't exist. + */ + if (hygon_csv_build == 0) + return -ENODEV; + + /* The caller must provide valid @buf and the @buf must >= 4 bytes in + * size. + */ + if (!buf || !size || *size < sizeof(uint32_t)) { + if (size) + *size = sizeof(uint32_t); + + return -EINVAL; + } + + /* Since firmware with build id 2200, support: + * a. issue LAUNCH_ENCRYPT_DATA command more than once for a + * CSV3 guest. + * b. inject secret to a CSV3 guest. + */ + if (csv_version_greater_or_equal(2200)) { + *(uint32_t *)buf |= CSV_EXT_CSV3_MULT_LUP_DATA; + *(uint32_t *)buf |= CSV_EXT_CSV3_INJ_SECRET; + } + + return 0; +} +EXPORT_SYMBOL_GPL(csv_get_extension_info); diff --git a/drivers/crypto/ccp/hygon/csv-dev.h b/drivers/crypto/ccp/hygon/csv-dev.h new file mode 100644 index 000000000000..dfdb4261fc52 --- /dev/null +++ b/drivers/crypto/ccp/hygon/csv-dev.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * HYGON CSV driver interface + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + */ + +#ifndef __CCP_HYGON_CSV_DEV_H__ +#define __CCP_HYGON_CSV_DEV_H__ + +#include + +#include "sp-dev.h" + +extern u32 hygon_csv_build; + +void csv_update_api_version(struct sev_user_data_status *status); + +static inline bool csv_version_greater_or_equal(u32 build) +{ + return hygon_csv_build >= build; +} + +#endif /* __CCP_HYGON_CSV_DEV_H__ */ diff --git a/drivers/crypto/ccp/hygon/sp-dev.h b/drivers/crypto/ccp/hygon/sp-dev.h new file mode 100644 index 000000000000..dfe6fd50933a --- /dev/null +++ b/drivers/crypto/ccp/hygon/sp-dev.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * HYGON Secure Processor interface + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + */ + +#ifndef __CCP_HYGON_SP_DEV_H__ +#define __CCP_HYGON_SP_DEV_H__ + +#include + +#ifdef CONFIG_X86_64 +static inline bool is_vendor_hygon(void) +{ + return boot_cpu_data.x86_vendor == X86_VENDOR_HYGON; +} +#else +static inline bool is_vendor_hygon(void) { return false; } +#endif + +#endif /* __CCP_HYGON_SP_DEV_H__ */ diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 686eaae9f643..568add3319dd 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -31,6 +31,8 @@ #include "psp-dev.h" #include "sev-dev.h" +#include "hygon/csv-dev.h" + #define DEVICE_NAME "sev" #define SEV_FW_FILE "amd/sev.fw" #define CSV_FW_FILE "hygon/csv.fw" @@ -85,13 +87,6 @@ static int csv_comm_mode = CSV_COMM_MAILBOX_ON; #define SEV_ES_TMR_SIZE (1024 * 1024) static void *sev_es_tmr; -/* - * Hygon CSV build info: - * Hygon CSV build info is 32-bit in length other than 8-bit as that - * in AMD SEV. - */ -static u32 hygon_csv_build; - static inline bool sev_version_greater_or_equal(u8 maj, u8 min) { struct sev_device *sev = psp_master->sev_data; @@ -105,11 +100,6 @@ static inline bool sev_version_greater_or_equal(u8 maj, u8 min) return false; } -static inline bool csv_version_greater_or_equal(u32 build) -{ - return hygon_csv_build >= build; -} - static void sev_irq_handler(int irq, void *data, unsigned int status) { struct sev_device *sev = data; @@ -929,9 +919,12 @@ static int sev_get_api_version(void) sev->build = status.build; sev->state = status.state; - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) - hygon_csv_build = (status.flags >> 9) | - ((u32)status.build << 23); + /* + * The api version fields of HYGON CSV firmware are not consistent + * with AMD SEV firmware. + */ + if (is_vendor_hygon()) + csv_update_api_version(&status); return 0; } diff --git a/include/linux/psp-csv.h b/include/linux/psp-csv.h index 2a899e32ae89..86028fc1b1ec 100644 --- a/include/linux/psp-csv.h +++ b/include/linux/psp-csv.h @@ -10,6 +10,11 @@ #include +#define CSV_EXT_CSV3_MULT_LUP_DATA_BIT 0 +#define CSV_EXT_CSV3_MULT_LUP_DATA (1 << CSV_EXT_CSV3_MULT_LUP_DATA_BIT) +#define CSV_EXT_CSV3_INJ_SECRET_BIT 1 +#define CSV_EXT_CSV3_INJ_SECRET (1 << CSV_EXT_CSV3_INJ_SECRET_BIT) + /** * Guest/platform management commands */ @@ -285,4 +290,25 @@ struct csv_data_receive_encrypt_context { u32 vmcb_block_len; /* In */ } __packed; -#endif +#ifdef CONFIG_CRYPTO_DEV_SP_PSP + +/** + * csv_get_extension_info - collect extension set of the firmware + * + * @buf: The buffer to save extension set + * @size: The size of the @buf + * + * Returns: + * 0 if @buf is filled with extension bitflags + * -%ENODEV if the CSV device is not available + * -%EINVAL if @buf is NULL or @size is too smaller + */ +int csv_get_extension_info(void *buf, size_t *size); + +#else /* !CONFIG_CRYPTO_DEV_SP_PSP */ + +static inline int csv_get_extension_info(void *buf, size_t *size) { return -ENODEV; } + +#endif /* !CONFIG_CRYPTO_DEV_SP_PSP */ + +#endif /* __PSP_CSV_H__ */ -- Gitee From 582e3a8b4f2afdf7a0864611a80226e10a332052 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Thu, 26 Sep 2024 14:59:25 +0800 Subject: [PATCH 5/8] anolis: KVM: SVM: CSV: Provide KVM_CAP_HYGON_COCO_EXT interface ANBZ: #13046 The CSV1/2/3 firmware will provide more confidential features, it's recommended that the user space VMM (e.g. Qemu) inquiry about which features are supported by the system and decide to utilise some of these supported features. Provide KVM_CAP_HYGON_COCO_EXT ioctl interface so that the user space VMM, KVM, and firmware can negotiate how to interoperate with each other. The KVM_CAP_HYGON_COCO_EXT interface will address many compatibility issues when any one of the user space VMM, KVM, or firmware is not up-to-date. Signed-off-by: hanliyang --- arch/x86/include/asm/kvm_host.h | 12 ++- arch/x86/include/asm/processor-hygon.h | 23 ++++++ arch/x86/kvm/svm/csv.c | 109 ++++++++++++++++++++++--- arch/x86/kvm/x86.c | 25 ++++++ include/uapi/linux/kvm.h | 7 ++ 5 files changed, 159 insertions(+), 17 deletions(-) create mode 100644 arch/x86/include/asm/processor-hygon.h diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 68547fc20ac3..2adf01ebd236 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1359,16 +1359,20 @@ struct kvm_x86_ops { void (*migrate_timers)(struct kvm_vcpu *vcpu); void (*msr_filter_changed)(struct kvm_vcpu *vcpu); - int (*vm_attestation)(struct kvm *kvm, unsigned long gpa, unsigned long len); - int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); + int (*arch_hypercall)(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64 a2, u64 a3); + + /* + * Interfaces for HYGON CSV guest + */ + int (*vm_attestation)(struct kvm *kvm, unsigned long gpa, unsigned long len); int (*control_pre_system_reset)(struct kvm *kvm); int (*control_post_system_reset)(struct kvm *kvm); - - int (*arch_hypercall)(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64 a2, u64 a3); + int (*get_hygon_coco_extension)(struct kvm *kvm); + int (*enable_hygon_coco_extension)(struct kvm *kvm, u32 arg); }; struct kvm_x86_nested_ops { diff --git a/arch/x86/include/asm/processor-hygon.h b/arch/x86/include/asm/processor-hygon.h new file mode 100644 index 000000000000..a19bda3ed005 --- /dev/null +++ b/arch/x86/include/asm/processor-hygon.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The helpers to support Hygon CPU specific code path. + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + */ + +#ifndef _ASM_X86_PROCESSOR_HYGON_H +#define _ASM_X86_PROCESSOR_HYGON_H + +#include + +/* + * helper to determine HYGON CPU + */ +static inline bool is_x86_vendor_hygon(void) +{ + return boot_cpu_data.x86_vendor == X86_VENDOR_HYGON; +} + +#endif /* _ASM_X86_PROCESSOR_HYGON_H */ diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index b984283c9c75..0bbff5360096 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -93,6 +93,13 @@ struct kvm_csv_info { struct list_head smr_list; /* List of guest secure memory regions */ unsigned long nodemask; /* Nodemask where CSV guest's memory resides */ + + /* The following 5 fields record the extension status for current VM */ + bool fw_ext_valid; /* if @fw_ext field is valid */ + u32 fw_ext; /* extensions supported by current platform */ + bool kvm_ext_valid; /* if @kvm_ext field is valid */ + u32 kvm_ext; /* extensions supported by KVM */ + u32 inuse_ext; /* extensions inused by current VM */ }; struct kvm_svm_csv { @@ -197,7 +204,7 @@ static int to_csv_pg_level(int level) return ret; } -static bool csv_guest(struct kvm *kvm) +static bool csv3_guest(struct kvm *kvm) { struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; @@ -348,7 +355,7 @@ static int csv_set_guest_private_memory(struct kvm *kvm) int npages; struct page *page; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; nodes_clear(nodemask); @@ -478,7 +485,7 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) unsigned long pfn, pfn_sme_mask; int ret = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -573,7 +580,7 @@ static int csv_launch_encrypt_vmcb(struct kvm *kvm, struct kvm_sev_cmd *argp) int ret = 0; unsigned long i = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; encrypt_vmcb = kzalloc(sizeof(*encrypt_vmcb), GFP_KERNEL); @@ -649,7 +656,7 @@ static int csv_send_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) int ret = 0; int i; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -801,7 +808,7 @@ static int csv_send_encrypt_context(struct kvm *kvm, struct kvm_sev_cmd *argp) u32 offset; int ret = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -896,7 +903,7 @@ static int csv_receive_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) u32 offset; int ret = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (unlikely(list_empty(&csv->smr_list))) { @@ -1016,7 +1023,7 @@ static int csv_receive_encrypt_context(struct kvm *kvm, struct kvm_sev_cmd *argp struct kvm_vcpu *vcpu; unsigned long i; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -1403,7 +1410,7 @@ static void csv_vm_destroy(struct kvm *kvm) struct rb_node *node; unsigned long i = 0; - if (csv_guest(kvm)) { + if (csv3_guest(kvm)) { mutex_lock(&csv->sp_lock); while ((node = rb_first(&csv->sp_mgr.root))) { sp = rb_entry(node, struct shared_page, node); @@ -1426,7 +1433,7 @@ static void csv_vm_destroy(struct kvm *kvm) if (likely(csv_x86_ops.vm_destroy)) csv_x86_ops.vm_destroy(kvm); - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return; /* free secure memory region */ @@ -1470,7 +1477,7 @@ static int csv_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) /* * NPF for csv is dedicated. */ - if (csv_guest(vcpu->kvm) && exit_code == SVM_EXIT_NPF) { + if (csv3_guest(vcpu->kvm) && exit_code == SVM_EXIT_NPF) { gpa_t gpa = __sme_clr(svm->vmcb->control.exit_info_2); u64 error_code = svm->vmcb->control.exit_info_1; @@ -1485,7 +1492,7 @@ static int csv_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) static void csv_guest_memory_reclaimed(struct kvm *kvm) { - if (!csv_guest(kvm)) { + if (!csv3_guest(kvm)) { if (likely(csv_x86_ops.guest_memory_reclaimed)) csv_x86_ops.guest_memory_reclaimed(kvm); } @@ -1496,7 +1503,7 @@ static int csv_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) struct kvm_csv_handle_memory params; int r = -EINVAL; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -1568,6 +1575,80 @@ static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) return r; } +/** + * When userspace recognizes these extensions, it is suggested that the userspace + * enables these extensions through KVM_ENABLE_CAP, so that both the userspace + * and KVM can utilize these extensions. + */ +static int csv_get_hygon_coco_extension(struct kvm *kvm) +{ + struct kvm_csv_info *csv; + size_t len = sizeof(uint32_t); + int ret = 0; + + if (!kvm) + return 0; + + csv = &to_kvm_svm_csv(kvm)->csv_info; + + if (csv->fw_ext_valid == false) { + ret = csv_get_extension_info(&csv->fw_ext, &len); + + if (ret == -ENODEV) { + pr_err("Unable to interact with CSV firmware!\n"); + return 0; + } else if (ret == -EINVAL) { + pr_err("Need %ld bytes to record fw extension!\n", len); + return 0; + } + + csv->fw_ext_valid = true; + } + + /* The kvm_ext field of kvm_csv_info is filled in only if the fw_ext + * field of kvm_csv_info is valid. + */ + if (csv->kvm_ext_valid == false) { + /* Currently, KVM doesn't support any extensions, we don't need + * to fill in kvm_ext field of kvm_csv_info here. + */ + csv->kvm_ext_valid = true; + } + + /* Return extension info only if both fw_ext and kvm_ext fields of + * kvm_csv_info are valid. + */ + pr_debug("%s: fw_ext=%#x kvm_ext=%#x\n", + __func__, csv->fw_ext, csv->kvm_ext); + return (int)csv->kvm_ext; +} + +/** + * Return 0 means KVM accept the negotiation from userspace. Both the + * userspace and KVM should not utilise extensions if failed to negotiate. + */ +static int csv_enable_hygon_coco_extension(struct kvm *kvm, u32 arg) +{ + struct kvm_csv_info *csv; + + if (!kvm) + return -EINVAL; + + csv = &to_kvm_svm_csv(kvm)->csv_info; + + /* Negotiation is accepted only if both the fw_ext and kvm_ext fields + * of kvm_csv_info are valid and the virtual machine is a CSV3 guest. + */ + if (csv->fw_ext_valid && csv->kvm_ext_valid && csv3_guest(kvm)) { + csv->inuse_ext = csv->kvm_ext & arg; + pr_debug("%s: inuse_ext=%#x\n", __func__, csv->inuse_ext); + return csv->inuse_ext; + } + + /* Userspace should not utilise the extensions */ + return -EINVAL; +} + #define CSV_BIT BIT(30) void __init csv_init(struct kvm_x86_ops *ops) @@ -1581,6 +1662,8 @@ void __init csv_init(struct kvm_x86_ops *ops) ops->mem_enc_op = csv_mem_enc_op; ops->vm_size = sizeof(struct kvm_svm_csv); + ops->get_hygon_coco_extension = csv_get_hygon_coco_extension; + ops->enable_hygon_coco_extension = csv_enable_hygon_coco_extension; /* Retrieve CSV CPUID information */ cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 74f3c35a193d..1e93b27839e8 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -80,6 +80,8 @@ #include #include +#include + #define CREATE_TRACE_POINTS #include "trace.h" @@ -4078,6 +4080,18 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = kvm_x86_ops.has_emulated_msr(kvm, MSR_AMD64_SEV_ES_GHCB); break; + case KVM_CAP_HYGON_COCO_EXT: + r = 0; + + /* + * Before running a Hygon confidential guest, the userspace + * should find the advanced extensions of the Hygon CSV + * technology. If the userspace recognize the extensions, it's + * suggested that the userspace to utilise extensions. + */ + if (is_x86_vendor_hygon() && kvm_x86_ops.get_hygon_coco_extension) + r = kvm_x86_ops.get_hygon_coco_extension(kvm); + break; default: break; } @@ -5655,6 +5669,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, } mutex_unlock(&kvm->lock); break; + case KVM_CAP_HYGON_COCO_EXT: + r = -EINVAL; + + /* + * The userspace negotiate with KVM to utilise extensions of + * Hygon CSV technology. + */ + if (is_x86_vendor_hygon() && kvm_x86_ops.enable_hygon_coco_extension) + r = kvm_x86_ops.enable_hygon_coco_extension(kvm, + (u32)cap->args[0]); + break; default: r = -EINVAL; break; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 3eb70c7c5c6c..47511047d4e7 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1099,6 +1099,13 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_X86_NOTIFY_VMEXIT 219 #define KVM_CAP_SEV_ES_GHCB 500 +#define KVM_CAP_HYGON_COCO_EXT 501 +/* support userspace to request firmware to build CSV3 guest's memory space */ +#define KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM (1 << 0) +/* support request to update CSV3 guest's memory region multiple times */ +#define KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA (1 << 1) +/* support request to inject secret to CSV3 guest */ +#define KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET (1 << 2) #define KVM_CAP_ARM_CPU_FEATURE 555 -- Gitee From 7c14aaa3b953c6b1390f30381735e7a7cf3f958d Mon Sep 17 00:00:00 2001 From: hanliyang Date: Thu, 26 Sep 2024 16:49:28 +0800 Subject: [PATCH 6/8] anolis: KVM: SVM: CSV: Provide KVM_CSV3_SET_GUEST_PRIVATE_MEMORY ioctl interface ANBZ: #13046 For newer CSV1/2/3 firmware, multiple LAUNCH_ENCRYPT_DATA commands are allowed to be issued. However, SET_GUEST_PRIVATE_MEMORY command can only be issued once. Provide a separate ioctl interface KVM_CSV3_SET_GUEST_PRIVATE_MEMORY here. The user space VMM can negotiate with KVM on whether to enable the capability KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM. When this capability is enabled, the user space VMM should explicitly request the KVM_CSV3_SET_GUEST_PRIVATE_MEMORY ioctl interface, and the KVM_CSV3_LAUNCH_ENCRYPT_DATA ioctl handler will skip the process of issuing the SET_GUEST_PRIVATE_MEMORY command, in addition, the user space VMM will have the chance to request KVM_CSV3_LAUNCH_ENCRYPT_DATA ioctl interface more than once. When this capability is disabled, the user space will not request the KVM_CSV3_SET_GUEST_PRIVATE_MEMORY ioctl interface, and the KVM_CSV3_LAUNCH_ENCRYPT_DATA ioctl handler will still issue the SET_GUEST_PRIVATE_MEMORY command. Signed-off-by: hanliyang --- arch/x86/kvm/svm/csv.c | 57 ++++++++++++++++++++++-------------- drivers/crypto/ccp/sev-dev.c | 4 +-- include/linux/psp-csv.h | 6 ++-- include/uapi/linux/kvm.h | 28 ++++++++++-------- 4 files changed, 55 insertions(+), 40 deletions(-) diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index 0bbff5360096..4f14353d12fc 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -332,14 +332,14 @@ static bool csv_is_mmio_pfn(kvm_pfn_t pfn) E820_TYPE_RAM); } -static int csv_set_guest_private_memory(struct kvm *kvm) +static int csv3_set_guest_private_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) { struct kvm_memslots *slots = kvm_memslots(kvm); struct kvm_memory_slot *memslot; struct secure_memory_region *smr; struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; - struct csv_data_set_guest_private_memory *set_guest_private_memory; + struct csv3_data_set_guest_private_memory *set_guest_private_memory; struct csv_data_memory_region *regions; nodemask_t nodemask; nodemask_t *nodemask_ptr; @@ -347,7 +347,7 @@ static int csv_set_guest_private_memory(struct kvm *kvm) LIST_HEAD(tmp_list); struct list_head *pos, *q; u32 i = 0, count = 0, remainder; - int ret = 0, error; + int ret = 0; u64 size = 0, nr_smr = 0, nr_pages = 0; u32 smr_entry_shift; @@ -358,6 +358,10 @@ static int csv_set_guest_private_memory(struct kvm *kvm) if (!csv3_guest(kvm)) return -ENOTTY; + /* The smr_list should be initialized only once */ + if (!list_empty(&csv->smr_list)) + return -EFAULT; + nodes_clear(nodemask); for_each_set_bit(i, &csv->nodemask, BITS_PER_LONG) if (i < MAX_NUMNODES) @@ -438,8 +442,8 @@ static int csv_set_guest_private_memory(struct kvm *kvm) set_guest_private_memory->regions_paddr = __sme_pa(regions); /* set secury memory region for launch enrypt data */ - ret = csv_issue_cmd(kvm, CSV_CMD_SET_GUEST_PRIVATE_MEMORY, - set_guest_private_memory, &error); + ret = csv_issue_cmd(kvm, CSV3_CMD_SET_GUEST_PRIVATE_MEMORY, + set_guest_private_memory, &argp->error); if (ret) goto e_free_smr; @@ -499,10 +503,17 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) goto exit; } - /* Allocate all the guest memory from CMA */ - ret = csv_set_guest_private_memory(kvm); - if (ret) - goto exit; + /* + * If userspace request to invoke CSV3_CMD_SET_GUEST_PRIVATE_MEMORY + * explicitly, we should not calls to csv3_set_guest_private_memory() + * here. + */ + if (!(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM)) { + /* Allocate all the guest memory from CMA */ + ret = csv3_set_guest_private_memory(kvm, argp); + if (ret) + goto exit; + } num_entries = params.len / PAGE_SIZE; num_entries_in_block = ARRAY_SIZE(blocks->entry); @@ -908,7 +919,7 @@ static int csv_receive_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) if (unlikely(list_empty(&csv->smr_list))) { /* Allocate all the guest memory from CMA */ - ret = csv_set_guest_private_memory(kvm); + ret = csv3_set_guest_private_memory(kvm, argp); if (ret) goto exit; } @@ -1511,7 +1522,7 @@ static int csv_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) return -EFAULT; switch (params.opcode) { - case KVM_CSV_RELEASE_SHARED_MEMORY: + case KVM_CSV3_RELEASE_SHARED_MEMORY: r = csv_unpin_shared_memory(kvm, params.gpa, params.num_pages); break; default: @@ -1535,30 +1546,33 @@ static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) mutex_lock(&kvm->lock); switch (sev_cmd.id) { - case KVM_CSV_INIT: + case KVM_CSV3_INIT: r = csv_guest_init(kvm, &sev_cmd); break; - case KVM_CSV_LAUNCH_ENCRYPT_DATA: + case KVM_CSV3_LAUNCH_ENCRYPT_DATA: r = csv_launch_encrypt_data(kvm, &sev_cmd); break; - case KVM_CSV_LAUNCH_ENCRYPT_VMCB: + case KVM_CSV3_LAUNCH_ENCRYPT_VMCB: r = csv_launch_encrypt_vmcb(kvm, &sev_cmd); break; - case KVM_CSV_SEND_ENCRYPT_DATA: + case KVM_CSV3_SEND_ENCRYPT_DATA: r = csv_send_encrypt_data(kvm, &sev_cmd); break; - case KVM_CSV_SEND_ENCRYPT_CONTEXT: + case KVM_CSV3_SEND_ENCRYPT_CONTEXT: r = csv_send_encrypt_context(kvm, &sev_cmd); break; - case KVM_CSV_RECEIVE_ENCRYPT_DATA: + case KVM_CSV3_RECEIVE_ENCRYPT_DATA: r = csv_receive_encrypt_data(kvm, &sev_cmd); break; - case KVM_CSV_RECEIVE_ENCRYPT_CONTEXT: + case KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT: r = csv_receive_encrypt_context(kvm, &sev_cmd); break; - case KVM_CSV_HANDLE_MEMORY: + case KVM_CSV3_HANDLE_MEMORY: r = csv_handle_memory(kvm, &sev_cmd); break; + case KVM_CSV3_SET_GUEST_PRIVATE_MEMORY: + r = csv3_set_guest_private_memory(kvm, &sev_cmd); + break; default: mutex_unlock(&kvm->lock); if (likely(csv_x86_ops.mem_enc_op)) @@ -1609,9 +1623,8 @@ static int csv_get_hygon_coco_extension(struct kvm *kvm) * field of kvm_csv_info is valid. */ if (csv->kvm_ext_valid == false) { - /* Currently, KVM doesn't support any extensions, we don't need - * to fill in kvm_ext field of kvm_csv_info here. - */ + if (csv3_guest(kvm)) + csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM; csv->kvm_ext_valid = true; } diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 568add3319dd..943430f97e5b 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -168,8 +168,8 @@ static int sev_cmd_buffer_len(int cmd) return sizeof(struct csv_data_set_smr); case CSV_CMD_SET_SMCR: return sizeof(struct csv_data_set_smcr); - case CSV_CMD_SET_GUEST_PRIVATE_MEMORY: - return sizeof(struct csv_data_set_guest_private_memory); + case CSV3_CMD_SET_GUEST_PRIVATE_MEMORY: + return sizeof(struct csv3_data_set_guest_private_memory); case CSV_CMD_DBG_READ_VMSA: return sizeof(struct csv_data_dbg_read_vmsa); case CSV_CMD_DBG_READ_MEM: diff --git a/include/linux/psp-csv.h b/include/linux/psp-csv.h index 86028fc1b1ec..0eacfb4341fc 100644 --- a/include/linux/psp-csv.h +++ b/include/linux/psp-csv.h @@ -20,7 +20,7 @@ */ enum csv3_cmd { /* Guest launch commands */ - CSV_CMD_SET_GUEST_PRIVATE_MEMORY = 0x200, + CSV3_CMD_SET_GUEST_PRIVATE_MEMORY = 0x200, CSV_CMD_LAUNCH_ENCRYPT_DATA = 0x201, CSV_CMD_LAUNCH_ENCRYPT_VMCB = 0x202, /* Guest NPT(Nested Page Table) management commands */ @@ -124,14 +124,14 @@ struct csv_data_memory_region { } __packed; /** - * struct csv_data_set_guest_private_memory - CSV_CMD_SET_GUEST_PRIVATE_MEMORY + * struct csv3_data_set_guest_private_memory - CSV3_CMD_SET_GUEST_PRIVATE_MEMORY * command parameters * * @handle: handle assigned to the VM * @nregions: number of memory regions * @regions_paddr: address of memory containing multiple memory regions */ -struct csv_data_set_guest_private_memory { +struct csv3_data_set_guest_private_memory { u32 handle; /* In */ u32 nregions; /* In */ u64 regions_paddr; /* In */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 47511047d4e7..c25c601db6cd 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1929,20 +1929,22 @@ struct kvm_hyperv_eventfd { #define KVM_X86_NOTIFY_VMEXIT_ENABLED (1ULL << 0) #define KVM_X86_NOTIFY_VMEXIT_USER (1ULL << 1) -/* CSV command */ -enum csv_cmd_id { - KVM_CSV_NR_MIN = 0xc0, +/* CSV3 command */ +enum csv3_cmd_id { + KVM_CSV3_NR_MIN = 0xc0, - KVM_CSV_INIT = KVM_CSV_NR_MIN, - KVM_CSV_LAUNCH_ENCRYPT_DATA, - KVM_CSV_LAUNCH_ENCRYPT_VMCB, - KVM_CSV_SEND_ENCRYPT_DATA, - KVM_CSV_SEND_ENCRYPT_CONTEXT, - KVM_CSV_RECEIVE_ENCRYPT_DATA, - KVM_CSV_RECEIVE_ENCRYPT_CONTEXT, - KVM_CSV_HANDLE_MEMORY, + KVM_CSV3_INIT = KVM_CSV3_NR_MIN, + KVM_CSV3_LAUNCH_ENCRYPT_DATA, + KVM_CSV3_LAUNCH_ENCRYPT_VMCB, + KVM_CSV3_SEND_ENCRYPT_DATA, + KVM_CSV3_SEND_ENCRYPT_CONTEXT, + KVM_CSV3_RECEIVE_ENCRYPT_DATA, + KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, + KVM_CSV3_HANDLE_MEMORY, - KVM_CSV_NR_MAX, + KVM_CSV3_SET_GUEST_PRIVATE_MEMORY = 0xc8, + + KVM_CSV3_NR_MAX, }; struct kvm_csv_init_data { @@ -1987,7 +1989,7 @@ struct kvm_csv_receive_encrypt_context { __u32 trans_len; }; -#define KVM_CSV_RELEASE_SHARED_MEMORY (0x0001) +#define KVM_CSV3_RELEASE_SHARED_MEMORY (0x0001) struct kvm_csv_handle_memory { __u64 gpa; -- Gitee From 6797d2fdc7e69b98f7eb5992bb29bc98f071a284 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Fri, 27 Sep 2024 15:12:20 +0800 Subject: [PATCH 7/8] anolis: KVM: SVM: CSV: Support issue non-4K aligned CSV3_CMD_LAUNCH_ENCRYPT_DATA and more than once ANBZ: #13046 So far, the KVM_CSV3_LAUNCH_ENCRYPT_DATA handler only process 4K aligned data, this is insufficient because we need encrypt Non-4K aligned data to CSV3 guest's private memory in some cases. To address this, we provide new function csv3_launch_encrypt_data_alt_2 to process Non-4K aligned data. The new function will be called only when the cap KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA is enabled for current CSV3 guest. In addition, to simplify the KVM_CSV3_LAUNCH_ENCRYPT_DATA request from the user space, the function csv3_launch_encrypt_data_alt_2 allows issue CSV3_CMD_LAUNCH_ENCRYPT_DATA more than once if necessary. Signed-off-by: hanliyang --- arch/x86/kvm/svm/csv.c | 241 +++++++++++++++++++++++++++++++++-- drivers/crypto/ccp/sev-dev.c | 4 +- include/linux/psp-csv.h | 6 +- include/uapi/linux/kvm.h | 2 +- 4 files changed, 238 insertions(+), 15 deletions(-) diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index 4f14353d12fc..7c38120c051b 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -475,11 +475,16 @@ static int csv3_set_guest_private_memory(struct kvm *kvm, struct kvm_sev_cmd *ar return ret; } -static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) +/** + * csv3_launch_encrypt_data_alt_1 - The legacy handler to encrypt CSV3 + * guest's memory before VMRUN. + */ +static int csv3_launch_encrypt_data_alt_1(struct kvm *kvm, + struct kvm_sev_cmd *argp) { struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; - struct kvm_csv_launch_encrypt_data params; - struct csv_data_launch_encrypt_data *encrypt_data = NULL; + struct kvm_csv3_launch_encrypt_data params; + struct csv3_data_launch_encrypt_data *encrypt_data = NULL; struct encrypt_data_block *blocks = NULL; u8 *data = NULL; u32 offset; @@ -489,9 +494,6 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) unsigned long pfn, pfn_sme_mask; int ret = 0; - if (!csv3_guest(kvm)) - return -ENOTTY; - if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) { ret = -EFAULT; @@ -571,7 +573,7 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) } clflush_cache_range(data, params.len); - ret = csv_issue_cmd(kvm, CSV_CMD_LAUNCH_ENCRYPT_DATA, + ret = csv_issue_cmd(kvm, CSV3_CMD_LAUNCH_ENCRYPT_DATA, encrypt_data, &argp->error); kfree(encrypt_data); @@ -583,6 +585,224 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) return ret; } +#define MAX_ENTRIES_PER_BLOCK \ + (sizeof(((struct encrypt_data_block *)0)->entry) / \ + sizeof(((struct encrypt_data_block *)0)->entry[0])) +#define MAX_BLOCKS_PER_CSV3_LUP_DATA \ + (sizeof(((struct csv3_data_launch_encrypt_data *)0)->data_blocks) / \ + sizeof(((struct csv3_data_launch_encrypt_data *)0)->data_blocks[0])) +#define MAX_ENTRIES_PER_CSV3_LUP_DATA \ + (MAX_BLOCKS_PER_CSV3_LUP_DATA * MAX_ENTRIES_PER_BLOCK) + +/** + * __csv3_launch_encrypt_data - The helper for handler + * csv3_launch_encrypt_data_alt_2. + */ +static int __csv3_launch_encrypt_data(struct kvm *kvm, + struct kvm_sev_cmd *argp, + struct kvm_csv3_launch_encrypt_data *params, + void *src_buf, + unsigned int start_pgoff, + unsigned int end_pgoff) +{ + struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; + struct csv3_data_launch_encrypt_data *data = NULL; + struct encrypt_data_block *block = NULL; + struct page **pages = NULL; + unsigned long len, remain_len; + unsigned long pfn, pfn_sme_mask, last_pfn; + unsigned int pgoff = start_pgoff; + int i, j; + int ret = -ENOMEM; + + /* Alloc command buffer for CSV3_CMD_LAUNCH_ENCRYPT_DATA command */ + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); + if (!data) + return -ENOMEM; + + /* Alloc pages for data_blocks[] in the command buffer */ + len = ARRAY_SIZE(data->data_blocks) * sizeof(struct page *); + pages = kzalloc(len, GFP_KERNEL_ACCOUNT); + if (!pages) + goto e_free_data; + + for (i = 0; i < ARRAY_SIZE(data->data_blocks); i++) { + pages[i] = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!pages[i]) + goto e_free_pages; + } + + i = 0; + while (i < ARRAY_SIZE(data->data_blocks) && pgoff < end_pgoff) { + block = (struct encrypt_data_block *)page_to_virt(pages[i]); + + j = 0; + last_pfn = 0; + while (j < ARRAY_SIZE(block->entry) && pgoff < end_pgoff) { + pfn = vmalloc_to_pfn(src_buf + (pgoff << PAGE_SHIFT)); + pfn_sme_mask = __sme_set(pfn << PAGE_SHIFT) >> PAGE_SHIFT; + + /* + * One entry can record a number of contiguous physical + * pages. If the current page is not adjacent to the + * previous physical page, we should record the page to + * the next entry. If entries of current block is used + * up, we should try the next block. + */ + if (last_pfn && (last_pfn + 1 == pfn)) { + block->entry[j].npages++; + } else if (j < (ARRAY_SIZE(block->entry) - 1)) { + /* @last_pfn == 0 means fill in entry[0] */ + if (likely(last_pfn != 0)) + j++; + block->entry[j].pfn = pfn_sme_mask; + block->entry[j].npages = 1; + } else { + break; + } + + /* + * Succeed to record one page, increase the page offset. + * We also record the pfn of current page so that we can + * record the contiguous physical pages into one entry. + */ + last_pfn = pfn; + pgoff++; + } + + i++; + } + + if (pgoff < end_pgoff) { + pr_err("CSV3: Fail to fill in LAUNCH_ENCRYPT_DATA command!\n"); + goto e_free_pages; + } + + len = (end_pgoff - start_pgoff) << PAGE_SHIFT; + clflush_cache_range(src_buf + (start_pgoff << PAGE_SHIFT), len); + + /* Fill in command buffer */ + data->handle = csv->sev->handle; + + if (start_pgoff == 0) { + data->gpa = params->gpa; + len -= params->gpa & ~PAGE_MASK; + } else { + data->gpa = (params->gpa & PAGE_MASK) + (start_pgoff << PAGE_SHIFT); + } + remain_len = params->len - (data->gpa - params->gpa); + + data->length = (len <= remain_len) ? len : remain_len; + + for (j = 0; j < i; j++) + data->data_blocks[j] = __sme_set(page_to_phys(pages[j])); + + /* Issue command */ + ret = csv_issue_cmd(kvm, CSV3_CMD_LAUNCH_ENCRYPT_DATA, data, &argp->error); + +e_free_pages: + for (i = 0; i < ARRAY_SIZE(data->data_blocks); i++) { + if (pages[i]) + __free_page(pages[i]); + } + kfree(pages); +e_free_data: + kfree(data); + + return ret; +} + +/** + * csv3_launch_encrypt_data_alt_2 - The handler to support encrypt CSV3 + * guest's memory before VMRUN. This handler support issue API command + * multiple times, both the GPA and length of the memory region are not + * required to be 4K-aligned. + */ +static int csv3_launch_encrypt_data_alt_2(struct kvm *kvm, + struct kvm_sev_cmd *argp) +{ + struct kvm_csv3_launch_encrypt_data params; + void *buffer = NULL; + unsigned long len; + unsigned int total_pages, start_pgoff, next_pgoff; + int ret = 0; + + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, + sizeof(params))) { + return -EFAULT; + } + + /* Both the GPA and length must be 16 Bytes aligned at least */ + if (!params.len || + !params.uaddr || + !IS_ALIGNED(params.len, 16) || + !IS_ALIGNED(params.gpa, 16)) { + return -EINVAL; + } + + /* + * Alloc buffer to save source data. When we copy source data from + * userspace to the buffer, the data in the first page of the buffer + * should keep the offset as params.gpa. + */ + len = PAGE_ALIGN((params.gpa & ~PAGE_MASK) + params.len); + total_pages = len >> PAGE_SHIFT; + next_pgoff = 0; + + buffer = vzalloc(len); + if (!buffer) + return -ENOMEM; + + if (copy_from_user(buffer + (params.gpa & ~PAGE_MASK), + (void __user *)params.uaddr, params.len)) { + ret = -EFAULT; + goto e_free_buffer; + } + + /* + * If the source data is too large, we should issue command more than + * once. The LAUNCH_ENCRYPT_DATA API updates not only the measurement + * of the data, but also the measurement of the metadata correspond to + * the data. The guest owner is obligated to verify the launch + * measurement, so guest owner must be aware of the launch measurement + * of each LAUNCH_ENCRYPT_DATA API command. If we processing pages more + * than MAX_ENTRIES_PER_CSV3_LUP_DATA in each API command, the guest + * owner could not able to calculate the correct measurement and fail + * to verify the launch measurement. For this reason, we limit the + * maximum number of pages processed by each API command to + * MAX_ENTRIES_PER_CSV3_LUP_DATA. + */ + while (next_pgoff < total_pages) { + start_pgoff = next_pgoff; + next_pgoff += MAX_ENTRIES_PER_CSV3_LUP_DATA; + + if (next_pgoff > total_pages) + next_pgoff = total_pages; + + ret = __csv3_launch_encrypt_data(kvm, argp, ¶ms, + buffer, start_pgoff, next_pgoff); + if (ret) + goto e_free_buffer; + } + +e_free_buffer: + vfree(buffer); + return ret; +} + +static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) +{ + struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; + + if (!csv3_guest(kvm)) + return -ENOTTY; + + if (!(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA)) + return csv3_launch_encrypt_data_alt_1(kvm, argp); + + return csv3_launch_encrypt_data_alt_2(kvm, argp); +} + static int csv_launch_encrypt_vmcb(struct kvm *kvm, struct kvm_sev_cmd *argp) { struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; @@ -1550,7 +1770,7 @@ static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) r = csv_guest_init(kvm, &sev_cmd); break; case KVM_CSV3_LAUNCH_ENCRYPT_DATA: - r = csv_launch_encrypt_data(kvm, &sev_cmd); + r = csv3_launch_encrypt_data(kvm, &sev_cmd); break; case KVM_CSV3_LAUNCH_ENCRYPT_VMCB: r = csv_launch_encrypt_vmcb(kvm, &sev_cmd); @@ -1623,8 +1843,11 @@ static int csv_get_hygon_coco_extension(struct kvm *kvm) * field of kvm_csv_info is valid. */ if (csv->kvm_ext_valid == false) { - if (csv3_guest(kvm)) + if (csv3_guest(kvm)) { csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM; + if (csv->fw_ext & CSV_EXT_CSV3_MULT_LUP_DATA) + csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA; + } csv->kvm_ext_valid = true; } diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 943430f97e5b..ccc5950dd6b5 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -158,8 +158,8 @@ static int sev_cmd_buffer_len(int cmd) return sizeof(struct csv_data_hgsc_cert_import); case CSV_CMD_RING_BUFFER: return sizeof(struct csv_data_ring_buffer); - case CSV_CMD_LAUNCH_ENCRYPT_DATA: - return sizeof(struct csv_data_launch_encrypt_data); + case CSV3_CMD_LAUNCH_ENCRYPT_DATA: + return sizeof(struct csv3_data_launch_encrypt_data); case CSV_CMD_LAUNCH_ENCRYPT_VMCB: return sizeof(struct csv_data_launch_encrypt_vmcb); case CSV_CMD_UPDATE_NPT: diff --git a/include/linux/psp-csv.h b/include/linux/psp-csv.h index 0eacfb4341fc..d8fbb06319a8 100644 --- a/include/linux/psp-csv.h +++ b/include/linux/psp-csv.h @@ -21,7 +21,7 @@ enum csv3_cmd { /* Guest launch commands */ CSV3_CMD_SET_GUEST_PRIVATE_MEMORY = 0x200, - CSV_CMD_LAUNCH_ENCRYPT_DATA = 0x201, + CSV3_CMD_LAUNCH_ENCRYPT_DATA = 0x201, CSV_CMD_LAUNCH_ENCRYPT_VMCB = 0x202, /* Guest NPT(Nested Page Table) management commands */ CSV_CMD_UPDATE_NPT = 0x203, @@ -44,14 +44,14 @@ enum csv3_cmd { }; /** - * struct csv_data_launch_encrypt_data - CSV_CMD_LAUNCH_ENCRYPT_DATA command + * struct csv3_data_launch_encrypt_data - CSV3_CMD_LAUNCH_ENCRYPT_DATA command * * @handle: handle of the VM to update * @gpa: guest address where data is copied * @length: len of memory to be encrypted * @data_blocks: memory regions to hold data page address */ -struct csv_data_launch_encrypt_data { +struct csv3_data_launch_encrypt_data { u32 handle; /* In */ u32 reserved; /* In */ u64 gpa; /* In */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index c25c601db6cd..2d899f9e6cc2 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1951,7 +1951,7 @@ struct kvm_csv_init_data { __u64 nodemask; }; -struct kvm_csv_launch_encrypt_data { +struct kvm_csv3_launch_encrypt_data { __u64 gpa; __u64 uaddr; __u32 len; -- Gitee From 6d7354a518c19e54dbd75c51292bca70acdb75e7 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Fri, 27 Sep 2024 21:07:06 +0800 Subject: [PATCH 8/8] anolis: KVM: SVM: CSV: Support inject secret to Hygon CSV3 guest ANBZ: #13046 We should provide GPA in LAUNCH_SECRET API command buffer for CSV3 guest. We introduce a appropriate function csv_launch_secret to process user space KVM_SEV_LAUNCH_SECRET ioctl request, irrespective of whether it is a CSV, CSV2 or CSV3 guest. For CSV3 guest, the member guest_uaddr of the structure kvm_sev_launch_secret should be the value of GPA. [ hly: Implement hygon_kvm_hooks for referencing external functions and variables in arch/x86/kvm/csv.c. ] Signed-off-by: hanliyang --- arch/x86/kvm/svm/csv.c | 114 ++++++++++++++++++++++++++++++++++++++++- arch/x86/kvm/svm/csv.h | 20 ++++++++ arch/x86/kvm/svm/sev.c | 30 +++++++++++ arch/x86/kvm/svm/svm.c | 8 ++- 4 files changed, 169 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index 7c38120c051b..2751ef92c60c 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,9 @@ #undef pr_fmt #define pr_fmt(fmt) "CSV: " fmt +/* Function and variable pointers for hooks */ +struct hygon_kvm_hooks_table hygon_kvm_hooks; + struct encrypt_data_block { struct { u64 npages: 12; @@ -1752,11 +1756,108 @@ static int csv_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) return r; }; +static int csv_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) +{ + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; + struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; + struct sev_data_launch_secret *data; + struct kvm_sev_launch_secret params; + struct page **pages; + void *blob, *hdr; + unsigned long n, i; + int ret, offset; + + if (!sev_guest(kvm)) + return -ENOTTY; + + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) + return -EFAULT; + + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); + if (!data) + return -ENOMEM; + + if (!csv3_guest(kvm) || + !(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET)) { + pages = hygon_kvm_hooks.sev_pin_memory(kvm, params.guest_uaddr, + params.guest_len, &n, 1); + if (IS_ERR(pages)) { + ret = PTR_ERR(pages); + goto e_free_data; + } + + /* + * Flush (on non-coherent CPUs) before LAUNCH_SECRET encrypts pages in + * place; the cache may contain the data that was written unencrypted. + */ + hygon_kvm_hooks.sev_clflush_pages(pages, n); + + /* + * The secret must be copied into contiguous memory region, lets verify + * that userspace memory pages are contiguous before we issue command. + */ + if (hygon_kvm_hooks.get_num_contig_pages(0, pages, n) != n) { + ret = -EINVAL; + goto e_unpin_memory; + } + + offset = params.guest_uaddr & (PAGE_SIZE - 1); + data->guest_address = __sme_page_pa(pages[0]) + offset; + } else { + /* It's gpa for CSV3 guest */ + data->guest_address = params.guest_uaddr; + } + data->guest_len = params.guest_len; + + blob = psp_copy_user_blob(params.trans_uaddr, params.trans_len); + if (IS_ERR(blob)) { + ret = PTR_ERR(blob); + goto e_unpin_memory; + } + + data->trans_address = __psp_pa(blob); + data->trans_len = params.trans_len; + + hdr = psp_copy_user_blob(params.hdr_uaddr, params.hdr_len); + if (IS_ERR(hdr)) { + ret = PTR_ERR(hdr); + goto e_free_blob; + } + data->hdr_address = __psp_pa(hdr); + data->hdr_len = params.hdr_len; + + data->handle = sev->handle; + ret = hygon_kvm_hooks.sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_SECRET, + data, &argp->error); + + kfree(hdr); + +e_free_blob: + kfree(blob); +e_unpin_memory: + if (!csv3_guest(kvm) || + !(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET)) { + /* content of memory is updated, mark pages dirty */ + for (i = 0; i < n; i++) { + set_page_dirty_lock(pages[i]); + mark_page_accessed(pages[i]); + } + hygon_kvm_hooks.sev_unpin_memory(kvm, pages, n); + } +e_free_data: + kfree(data); + return ret; +} + static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) { struct kvm_sev_cmd sev_cmd; int r = -EINVAL; + if (!hygon_kvm_hooks.sev_hooks_installed || + !(*hygon_kvm_hooks.sev_enabled)) + return -ENOTTY; + if (!argp) return 0; @@ -1766,6 +1867,9 @@ static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) mutex_lock(&kvm->lock); switch (sev_cmd.id) { + case KVM_SEV_LAUNCH_SECRET: + r = csv_launch_secret(kvm, &sev_cmd); + break; case KVM_CSV3_INIT: r = csv_guest_init(kvm, &sev_cmd); break; @@ -1847,6 +1951,8 @@ static int csv_get_hygon_coco_extension(struct kvm *kvm) csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM; if (csv->fw_ext & CSV_EXT_CSV3_MULT_LUP_DATA) csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA; + if (csv->fw_ext & CSV_EXT_CSV3_INJ_SECRET) + csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET; } csv->kvm_ext_valid = true; } @@ -1891,7 +1997,11 @@ void __init csv_init(struct kvm_x86_ops *ops) { unsigned int eax, ebx, ecx, edx; - if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) + /* + * Hygon CSV is indicated by X86_FEATURE_SEV, return directly if CSV + * is unsupported. + */ + if (!boot_cpu_has(X86_FEATURE_SEV)) return; memcpy(&csv_x86_ops, ops, sizeof(struct kvm_x86_ops)); @@ -1903,7 +2013,7 @@ void __init csv_init(struct kvm_x86_ops *ops) /* Retrieve CSV CPUID information */ cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); - if (eax & CSV_BIT) { + if (boot_cpu_has(X86_FEATURE_SEV_ES) && (eax & CSV_BIT)) { ops->vm_destroy = csv_vm_destroy; ops->handle_exit = csv_handle_exit; ops->guest_memory_reclaimed = csv_guest_memory_reclaimed; diff --git a/arch/x86/kvm/svm/csv.h b/arch/x86/kvm/svm/csv.h index c70fe083b6bf..baade835b9f2 100644 --- a/arch/x86/kvm/svm/csv.h +++ b/arch/x86/kvm/svm/csv.h @@ -12,6 +12,26 @@ #ifdef CONFIG_HYGON_CSV +/* + * Hooks table: a table of function and variable pointers filled in + * when module init. + */ +extern struct hygon_kvm_hooks_table { + bool sev_hooks_installed; + int *sev_enabled; + unsigned long *sev_me_mask; + int (*sev_issue_cmd)(struct kvm *kvm, int id, void *data, int *error); + unsigned long (*get_num_contig_pages)(unsigned long idx, + struct page **inpages, + unsigned long npages); + struct page **(*sev_pin_memory)(struct kvm *kvm, unsigned long uaddr, + unsigned long ulen, unsigned long *n, + int write); + void (*sev_unpin_memory)(struct kvm *kvm, struct page **pages, + unsigned long npages); + void (*sev_clflush_pages)(struct page *pages[], unsigned long npages); +} hygon_kvm_hooks; + void __init csv_init(struct kvm_x86_ops *ops); #else /* !CONFIG_HYGON_CSV */ diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 97df8a375bd5..ccb652975f50 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -28,6 +28,9 @@ #include "cpuid.h" #include "trace.h" +#include +#include "csv.h" + #define __ex(x) __kvm_handle_fault_on_reboot(x) static u8 sev_enc_bit; @@ -2737,6 +2740,22 @@ void __init sev_set_cpu_caps(void) kvm_cpu_cap_clear(X86_FEATURE_SEV_ES); } +#ifdef CONFIG_HYGON_CSV +/* Code to set all of the function and vaiable pointers */ +void sev_install_hooks(void) +{ + hygon_kvm_hooks.sev_enabled = &sev; + hygon_kvm_hooks.sev_me_mask = &sev_me_mask; + hygon_kvm_hooks.sev_issue_cmd = sev_issue_cmd; + hygon_kvm_hooks.get_num_contig_pages = get_num_contig_pages; + hygon_kvm_hooks.sev_pin_memory = sev_pin_memory; + hygon_kvm_hooks.sev_unpin_memory = sev_unpin_memory; + hygon_kvm_hooks.sev_clflush_pages = sev_clflush_pages; + + hygon_kvm_hooks.sev_hooks_installed = true; +} +#endif + void __init sev_hardware_setup(void) { unsigned int eax, ebx, ecx, edx; @@ -2839,6 +2858,17 @@ void __init sev_hardware_setup(void) out: sev = sev_supported; sev_es = sev_es_supported; + +#ifdef CONFIG_HYGON_CSV + /* Setup resources which are necessary for HYGON CSV */ + if (is_x86_vendor_hygon()) { + /* + * Install sev related function and variable pointers hooks + * no matter @sev is false. + */ + sev_install_hooks(); + } +#endif } void sev_hardware_teardown(void) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index def2fd83633b..dcb7c1d68efc 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -44,6 +44,8 @@ #include "trace.h" #include "svm.h" + +#include #include "csv.h" #define __ex(x) __kvm_handle_fault_on_reboot(x) @@ -4784,7 +4786,11 @@ static struct kvm_x86_init_ops svm_init_ops __initdata = { static int __init svm_init(void) { __unused_size_checks(); - csv_init(&svm_x86_ops); + + /* Register CSV specific interface for Hygon CPUs */ + if (is_x86_vendor_hygon()) + csv_init(&svm_x86_ops); + return kvm_init(&svm_init_ops, sizeof(struct vcpu_svm), __alignof__(struct vcpu_svm), THIS_MODULE); } -- Gitee