From 10f5fa07068f54b23b01bf875259dc1a259d66b4 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Fri, 2 Aug 2024 01:35:25 +0800 Subject: [PATCH 1/5] qapi/qom,target/i386: csv-guest: Introduce secret-header-file=str and secret-file=str options This feature only applied to Hygon CSV. User can utilize the hag to generate secret header file and secret file, and inject these data to guest encrypted secret area automatically. Signed-off-by: hanliyang --- qapi/qom.json | 9 ++++- qemu-options.hx | 8 +++- target/i386/sev.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/qapi/qom.json b/qapi/qom.json index 51d9daf55a..a74c7a91f9 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -869,6 +869,11 @@ # @user-id: the user id of the guest owner, only support on Hygon CPUs # (since 8.2) # +# @secret-header-file: the header file of guest owner's secret, only +# support on Hygon CPUs (since 8.2) +# @secret-file: the file guest owner's secret, only support on Hygon +# CPUs (since 8.2) +# # Since: 2.12 ## { 'struct': 'SevGuestProperties', @@ -880,7 +885,9 @@ '*cbitpos': 'uint32', 'reduced-phys-bits': 'uint32', '*kernel-hashes': 'bool', - '*user-id': 'str' } } + '*user-id': 'str', + '*secret-header-file': 'str', + '*secret-file': 'str' } } ## # @ThreadContextProperties: diff --git a/qemu-options.hx b/qemu-options.hx index 51ba9378b9..8516b73206 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5637,7 +5637,7 @@ SRST -object secret,id=sec0,keyid=secmaster0,format=base64,\\ data=$SECRET,iv=$(user_id = g_strdup(value); } +static char * +sev_guest_get_secret_header_file(Object *obj, Error **errp) +{ + SevGuestState *s = SEV_GUEST(obj); + + return g_strdup(s->secret_header_file); +} + +static void +sev_guest_set_secret_header_file(Object *obj, const char *value, Error **errp) +{ + SevGuestState *s = SEV_GUEST(obj); + + s->secret_header_file = g_strdup(value); +} + +static char * +sev_guest_get_secret_file(Object *obj, Error **errp) +{ + SevGuestState *s = SEV_GUEST(obj); + + return g_strdup(s->secret_file); +} + +static void +sev_guest_set_secret_file(Object *obj, const char *value, Error **errp) +{ + SevGuestState *s = SEV_GUEST(obj); + + s->secret_file = g_strdup(value); +} + static char * sev_guest_get_sev_device(Object *obj, Error **errp) { @@ -448,6 +482,16 @@ sev_guest_class_init(ObjectClass *oc, void *data) sev_guest_set_user_id); object_class_property_set_description(oc, "user-id", "user id of the guest owner"); + object_class_property_add_str(oc, "secret-header-file", + sev_guest_get_secret_header_file, + sev_guest_set_secret_header_file); + object_class_property_set_description(oc, "secret-header-file", + "header file of the guest owner's secret"); + object_class_property_add_str(oc, "secret-file", + sev_guest_get_secret_file, + sev_guest_set_secret_file); + object_class_property_set_description(oc, "secret-file", + "file of the guest owner's secret"); } static void @@ -867,6 +911,9 @@ sev_launch_update_vmsa(SevGuestState *sev) return ret; } +static int +csv_load_launch_secret(const char *secret_header_file, const char *secret_file); + static void sev_launch_get_measure(Notifier *notifier, void *unused) { @@ -917,6 +964,15 @@ sev_launch_get_measure(Notifier *notifier, void *unused) /* encode the measurement value and emit the event */ sev->measurement = g_base64_encode(data, measurement.len); trace_kvm_sev_launch_measurement(sev->measurement); + + /* Hygon CSV will auto load guest owner's secret */ + if (is_hygon_cpu()) { + if (sev->secret_header_file && + strlen(sev->secret_header_file) && + sev->secret_file && + strlen(sev->secret_file)) + csv_load_launch_secret(sev->secret_header_file, sev->secret_file); + } } static char *sev_get_launch_measurement(void) @@ -2526,6 +2582,50 @@ int csv_load_incoming_cpu_state(QEMUFile *f) return ret; } +static int +csv_load_launch_secret(const char *secret_header_file, const char *secret_file) +{ + gsize secret_header_size, secret_size; + gchar *secret_header = NULL, *secret = NULL; + uint8_t *data; + struct sev_secret_area *area; + uint64_t gpa; + GError *error = NULL; + Error *local_err = NULL; + int ret = 0; + + if (!g_file_get_contents(secret_header_file, + &secret_header, + &secret_header_size, &error)) { + error_report("CSV: Failed to read '%s' (%s)", + secret_header_file, error->message); + g_error_free(error); + return -1; + } + + if (!g_file_get_contents(secret_file, &secret, &secret_size, &error)) { + error_report("CSV: Failed to read '%s' (%s)", secret_file, error->message); + g_error_free(error); + return -1; + } + + if (!pc_system_ovmf_table_find(SEV_SECRET_GUID, &data, NULL)) { + error_report("CSV: no secret area found in OVMF, gpa must be" + " specified."); + return -1; + } + area = (struct sev_secret_area *)data; + gpa = area->base; + + ret = sev_inject_launch_secret((char *)secret_header, + (char *)secret, gpa, &local_err); + + if (local_err) { + error_report_err(local_err); + } + return ret; +} + static const QemuUUID sev_hash_table_header_guid = { .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, 0xd4, 0x11, 0xfd, 0x21) -- Gitee From 9eb75830e70638d12efa0ec15a2f8b55e7c905da Mon Sep 17 00:00:00 2001 From: hanliyang Date: Sat, 28 Sep 2024 14:46:28 +0800 Subject: [PATCH 2/5] target/i386: kvm: Support to get and enable extensions for Hygon CoCo guest To enable advanced Hygon CoCo features, we should detect these features during the initialization of VMs in the KVM accelerator. It is suggested to enable these features if they are detected, allowing the guest VM to run with additional functionalities. Signed-off-by: hanliyang --- linux-headers/linux/kvm.h | 7 +++++++ target/i386/csv.c | 2 ++ target/i386/csv.h | 2 ++ target/i386/kvm/csv-stub.c | 2 ++ target/i386/kvm/kvm.c | 17 +++++++++++++++++ 5 files changed, 30 insertions(+) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 05e499b45b..ab28e9af5e 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1204,6 +1204,13 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ARM_TMM 300 #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_VIRT_MSI_BYPASS 799 diff --git a/target/i386/csv.c b/target/i386/csv.c index 571beeb61f..4aed225763 100644 --- a/target/i386/csv.c +++ b/target/i386/csv.c @@ -34,6 +34,8 @@ #include "csv.h" bool csv_kvm_cpu_reset_inhibit; +uint32_t kvm_hygon_coco_ext; +uint32_t kvm_hygon_coco_ext_inuse; struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops = { .save_setup = sev_save_setup, diff --git a/target/i386/csv.h b/target/i386/csv.h index 8621f0b6fd..c1d4cec3e0 100644 --- a/target/i386/csv.h +++ b/target/i386/csv.h @@ -58,6 +58,8 @@ bool csv3_enabled(void); #define CSV_OUTGOING_PAGE_WINDOW_SIZE (4094 * TARGET_PAGE_SIZE) extern bool csv_kvm_cpu_reset_inhibit; +extern uint32_t kvm_hygon_coco_ext; +extern uint32_t kvm_hygon_coco_ext_inuse; typedef struct CsvBatchCmdList CsvBatchCmdList; typedef void (*CsvDestroyCmdNodeFn) (void *data); diff --git a/target/i386/kvm/csv-stub.c b/target/i386/kvm/csv-stub.c index 4d1376f268..8662d33206 100644 --- a/target/i386/kvm/csv-stub.c +++ b/target/i386/kvm/csv-stub.c @@ -15,3 +15,5 @@ #include "csv.h" bool csv_kvm_cpu_reset_inhibit; +uint32_t kvm_hygon_coco_ext; +uint32_t kvm_hygon_coco_ext_inuse; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 925f4f8040..12e920bbb4 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2639,6 +2639,23 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + if (is_hygon_cpu()) { + /* check and enable Hygon coco extensions */ + kvm_hygon_coco_ext = (uint32_t)kvm_vm_check_extension(s, + KVM_CAP_HYGON_COCO_EXT); + if (kvm_hygon_coco_ext) { + ret = kvm_vm_enable_cap(s, KVM_CAP_HYGON_COCO_EXT, 0, + (uint64_t)kvm_hygon_coco_ext); + if (ret == -EINVAL) { + error_report("kvm: Failed to enable KVM_CAP_HYGON_COCO_EXT cap: %s", + strerror(-ret)); + kvm_hygon_coco_ext_inuse = 0; + } else { + kvm_hygon_coco_ext_inuse = (uint32_t)ret; + } + } + } + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; -- Gitee From ded4216fbfe740196a3ace80f5cb162b73f676b2 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Sat, 28 Sep 2024 17:37:17 +0800 Subject: [PATCH 3/5] target/i386: csv: Request to set private memory of CSV3 guest if the extension is enabled If Qemu negotiates with Linux KVM to enable the KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM capability, then Qemu should explicitly request the issuance of the CSV3_CMD_SET_GUEST_PRIVATE_MEMORY command. Signed-off-by: hanliyang --- hw/i386/pc_sysfw.c | 3 +++ include/sysemu/kvm.h | 9 +++++++++ linux-headers/linux/kvm.h | 2 ++ target/i386/csv-sysemu-stub.c | 5 +++++ target/i386/csv.c | 23 +++++++++++++++++++++++ target/i386/csv.h | 2 ++ target/i386/trace-events | 3 ++- 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 2bbcbb8d35..7c6a910250 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -268,6 +268,9 @@ void x86_firmware_configure(void *ptr, int size) ram_addr_t offset = 0; MemoryRegion *mr; + if (kvm_csv3_should_set_priv_mem()) + csv3_set_guest_private_memory(&error_fatal); + mr = memory_region_from_host(ptr, &offset); if (!mr) { error_report("failed to get memory region of flash"); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 438b4e9183..176aa53cbe 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -154,6 +154,14 @@ extern bool kvm_csv3_allowed; */ #define kvm_csv3_enabled() (kvm_csv3_allowed) +/** + * kvm_csv3_should_set_priv_mem: + * Returns: true if we should explicitly request + * KVM_CSV3_SET_GUEST_PRIVATE_MEMORY. + */ +#define kvm_csv3_should_set_priv_mem() \ + (kvm_hygon_coco_ext_inuse & KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM) + #else #define kvm_enabled() (0) @@ -171,6 +179,7 @@ extern bool kvm_csv3_allowed; #define kvm_readonly_mem_enabled() (false) #define kvm_msi_devid_required() (false) #define kvm_csv3_enabled() (false) +#define kvm_csv3_should_set_priv_mem() (false) #endif /* CONFIG_KVM_IS_POSSIBLE */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index ab28e9af5e..84cec64b88 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -2133,6 +2133,8 @@ enum csv3_cmd_id { KVM_CSV3_RECEIVE_ENCRYPT_DATA, KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, + KVM_CSV3_SET_GUEST_PRIVATE_MEMORY = 0xc8, + KVM_CSV3_NR_MAX, }; diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c index db22c299a6..e49755da5c 100644 --- a/target/i386/csv-sysemu-stub.c +++ b/target/i386/csv-sysemu-stub.c @@ -39,3 +39,8 @@ void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end) { } + +int csv3_set_guest_private_memory(Error **errp) +{ + g_assert_not_reached(); +} diff --git a/target/i386/csv.c b/target/i386/csv.c index 4aed225763..d9b50040a3 100644 --- a/target/i386/csv.c +++ b/target/i386/csv.c @@ -698,3 +698,26 @@ int csv3_load_incoming_context(QEMUFile *f) /* receive csv3 context. */ return csv3_receive_encrypt_context(s, f); } + +int csv3_set_guest_private_memory(Error **errp) +{ + int fw_error; + int ret = 0; + + if (!csv3_enabled()) { + error_setg(errp, "%s: CSV3 is not enabled", __func__); + return -1; + } + + /* if CSV3 is in update state then load the data to secure memory */ + if (csv3_check_state(SEV_STATE_LAUNCH_UPDATE)) { + trace_kvm_csv3_set_guest_private_memory(); + ret = csv3_ioctl(KVM_CSV3_SET_GUEST_PRIVATE_MEMORY, NULL, &fw_error); + if (ret) + error_setg(errp, "%s: CSV3 fail set private memory, ret=%d" + " fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + } + + return ret; +} diff --git a/target/i386/csv.h b/target/i386/csv.h index c1d4cec3e0..fb669279a8 100644 --- a/target/i386/csv.h +++ b/target/i386/csv.h @@ -130,4 +130,6 @@ int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); int csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); int csv3_save_outgoing_context(QEMUFile *f, uint64_t *bytes_sent); +int csv3_set_guest_private_memory(Error **errp); + #endif diff --git a/target/i386/trace-events b/target/i386/trace-events index ad3cfb9612..5d4a709a39 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -21,8 +21,9 @@ kvm_sev_send_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *dst, int len kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int len, void *hdr, int hdr_len) "cpu_id %d cpu_index %d trans %p len %d hdr %p hdr_len %d" # csv.c -kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIx64 +kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 " addr %p len 0x%" PRIx64 kvm_csv3_send_encrypt_data(void *dst, int len) "trans %p len %d" kvm_csv3_send_encrypt_context(void *dst, int len) "trans %p len %d" kvm_csv3_receive_encrypt_data(void *dst, int len, void *hdr, int hdr_len) "trans %p len %d hdr %p hdr_len %d" kvm_csv3_receive_encrypt_context(void *dst, int len, void *hdr, int hdr_len) "trans %p len %d hdr %p hdr_len %d" +kvm_csv3_set_guest_private_memory(void) "" -- Gitee From ca6d5f032ab4c93d78c90a83beefcfb05bf1ad79 Mon Sep 17 00:00:00 2001 From: hanliyang Date: Sat, 28 Sep 2024 17:55:13 +0800 Subject: [PATCH 4/5] target/i386: csv: Support load kernel hashes for CSV3 guest only if the extension is enabled The CSV3 guest can only update kernel hashes when the KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA capability is enabled. Signed-off-by: hanliyang --- target/i386/sev.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 721eca2150..3a9c9ceec7 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2748,7 +2748,17 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) /* zero the excess data so the measurement can be reliably calculated */ memset(padded_ht->padding, 0, sizeof(padded_ht->padding)); - if (sev_encrypt_flash((uint8_t *)padded_ht, sizeof(*padded_ht), errp) < 0) { + if (csv3_enabled()) { + if (kvm_hygon_coco_ext_inuse & KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA) { + if (csv3_load_data(area->base, (uint8_t *)padded_ht, + sizeof(*padded_ht), errp) < 0) { + ret = false; + } + } else { + error_report("%s: CSV3 load kernel hashes unsupported!", __func__); + ret = false; + } + } else if (sev_encrypt_flash((uint8_t *)padded_ht, sizeof(*padded_ht), errp) < 0) { ret = false; } -- Gitee From b74c6b8971610ffc9c901a9b22c92b40084a74bf Mon Sep 17 00:00:00 2001 From: hanliyang Date: Sun, 29 Sep 2024 15:03:47 +0800 Subject: [PATCH 5/5] target/i386: csv: Support inject secret for CSV3 guest only if the extension is enabled The CSV3 guest can only inject secrets when the KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET capability is enabled. Additionally, if the guest is a CSV3 guest, the guest_uaddr field of the KVM ioctl's input should be set to the value of the GPA. Signed-off-by: hanliyang --- target/i386/sev.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 3a9c9ceec7..b4b42fd716 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1416,7 +1416,17 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, input.trans_uaddr = (uint64_t)(unsigned long)data; input.trans_len = data_sz; - input.guest_uaddr = (uint64_t)(unsigned long)hva; + /* For Hygon CSV3 guest, the guest_uaddr should be the gpa */ + if (csv3_enabled()) { + if (kvm_hygon_coco_ext_inuse & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET) { + input.guest_uaddr = gpa; + } else { + error_setg(errp, "CSV3 inject secret unsupported!"); + return 1; + } + } else { + input.guest_uaddr = (uint64_t)(unsigned long)hva; + } input.guest_len = data_sz; trace_kvm_sev_launch_secret(gpa, input.guest_uaddr, -- Gitee