From 4f1bb1eec41e67cf38c55d754d4948f384f81b5a 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 0853944ba8..a54bb86012 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -868,6 +868,11 @@ # # @user-id: the user id of the guest owner, only support on Hygon CPUs # +# @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', @@ -879,7 +884,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 f64cf2b556..bffb9f31a8 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5645,7 +5645,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 80867734df11225959012d7e84c23efa98683c27 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 c75e4cde48..2ff308b82b 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1202,6 +1202,13 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230 #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_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE) diff --git a/target/i386/csv.c b/target/i386/csv.c index a869cc2a7e..2377cac71f 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 a32588ab9a..78f8adcfa8 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 fdceecc846..fd049789ac 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 a53f5ef3f2a09a8bcba2da7544b723538ff5a4e7 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 | 21 +++++++++++++++++++++ target/i386/csv.h | 2 ++ target/i386/trace-events | 3 ++- 7 files changed, 44 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 7d08aae9fa..1f7d36f4d3 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 2ff308b82b..f0abf968b2 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -2121,6 +2121,8 @@ enum csv3_cmd_id { KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, KVM_CSV3_HANDLE_MEMORY, + 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 f3224a0154..ce4850f5e4 100644 --- a/target/i386/csv-sysemu-stub.c +++ b/target/i386/csv-sysemu-stub.c @@ -44,3 +44,8 @@ void csv3_shared_region_relese(uint64_t gpa, uint32_t num_pages) { } + +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 2377cac71f..27cd84d912 100644 --- a/target/i386/csv.c +++ b/target/i386/csv.c @@ -734,3 +734,24 @@ 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", __func__); + } + + return ret; +} diff --git a/target/i386/csv.h b/target/i386/csv.h index 78f8adcfa8..d6af8b9c80 100644 --- a/target/i386/csv.h +++ b/target/i386/csv.h @@ -131,4 +131,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 515441c4f3..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%" PRIu64 +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 384abba493208775b289fb267a2450126ca586c2 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 7443f5b22c..40f74967ad 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 f2b258de4327eef141e3f8164e580df64d61f178 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 40f74967ad..23122068f0 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