From 5785124124b2ce04840da01bc95bcbf13da67e4a Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Tue, 2 Apr 2024 09:43:21 +0100 Subject: [PATCH 1/6] backends/confidential-guest-support: Add set_guest_policy() function For confidential guests a policy can be provided that defines the security level, debug status, expected launch measurement and other parameters that define the configuration of the confidential platform. This commit adds a new function named set_guest_policy() that can be implemented by each confidential platform, such as AMD SEV to set the policy. This will allow configuration of the policy from a multi-platform resource such as an IGVM file without the IGVM processor requiring specific implementation details for each platform. Signed-off-by: Roy Hopkins --- backends/confidential-guest-support.c | 12 ++++++++++++ include/exec/confidential-guest-support.h | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c index 79c0f3fc56dc..fe5d7a46cc44 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -68,6 +68,17 @@ static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, return -1; } +static int set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp) +{ + error_setg(errp, + "Setting confidential guest policy is not supported for this platform"); + return -1; +} + static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry, Error **errp) { @@ -82,6 +93,7 @@ static void confidential_guest_support_init(Object *obj) ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj); cgs->check_support = check_support; cgs->set_guest_state = set_guest_state; + cgs->set_guest_policy = set_guest_policy; cgs->get_mem_map_entry = get_mem_map_entry; } diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h index 82db678a198d..4ad0ed9ea461 100644 --- a/include/exec/confidential-guest-support.h +++ b/include/exec/confidential-guest-support.h @@ -64,6 +64,10 @@ typedef enum ConfidentialGuestPageType { CGS_PAGE_TYPE_REQUIRED_MEMORY, } ConfidentialGuestPageType; +typedef enum ConfidentialGuestPolicyType { + GUEST_POLICY_SEV, +} ConfidentialGuestPolicyType; + struct ConfidentialGuestSupport { Object parent; @@ -132,6 +136,23 @@ struct ConfidentialGuestSupport { ConfidentialGuestPageType memory_type, uint16_t cpu_index, Error **errp); + /* + * Set the guest policy. The policy can be used to configure the + * confidential platform, such as if debug is enabled or not and can contain + * information about expected launch measurements, signed verification of + * guest configuration and other platform data. + * + * The format of the policy data is specific to each platform. For example, + * SEV-SNP uses a policy bitfield in the 'policy' argument and provides an + * ID block and ID authentication in the 'policy_data' parameters. The type + * of policy data is identified by the 'policy_type' argument. + */ + int (*set_guest_policy)(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp); + /* * Iterate the system memory map, getting the entry with the given index * that can be populated into guest memory. From b6fb5430928b65de5a696b521ec4fc57ff5c5771 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Tue, 2 Apr 2024 09:54:17 +0100 Subject: [PATCH 2/6] backends/igvm: Process initialization sections in IGVM file The initialization secions in IGVM files contains configuration that should be applied to the guest platform before it is started. This includes guest policy and other information that can affect the security level and measurement of a confidential guest. This commit refactors the existing section processing code to prepare it for handling initialization sections as well as directives and iterates the initialization sections in the IGVM file. Signed-off-by: Roy Hopkins --- backends/igvm.c | 357 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 248 insertions(+), 109 deletions(-) diff --git a/backends/igvm.c b/backends/igvm.c index e6624856cfbd..bbdf954b4d4c 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -35,82 +35,133 @@ typedef struct IgvmParameterData { uint32_t index; } IgvmParameterData; -static QTAILQ_HEAD(, IgvmParameterData) parameter_data; +/* + * Some directives are specific individual confidential computing platforms. + * Define required types for each of those platforms here. + */ + +/* SEV/SEV-ES/SEV-SNP */ +struct QEMU_PACKED sev_id_block { + uint8_t ld[48]; + uint8_t family_id[16]; + uint8_t image_id[16]; + uint32_t version; + uint32_t guest_svn; + uint64_t policy; +}; + +struct QEMU_PACKED sev_id_authentication { + uint32_t id_key_alg; + uint32_t auth_key_algo; + uint8_t reserved[56]; + uint8_t id_block_sig[512]; + uint8_t id_key[1028]; + uint8_t reserved2[60]; + uint8_t id_key_sig[512]; + uint8_t author_key[1028]; + uint8_t reserved3[892]; +}; + +struct igvm_context { + /* + * Compatibility mask that is used to check if IGVM directives apply + * to the current platform. + */ + uint32_t compatibility_mask; -static int directive_page_data(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, + /* + * IGVM definition of the current platform type. + */ + IgvmPlatformType platform_type; + + /* + * The ConfidentialGuestSupport object that is used to process directives + * in the IGVM file. + */ + ConfidentialGuestSupport *cgs; + + /* + * For SEV platforms, optionally contains the ID block and authentication + * that should be verified by the guest. + */ + struct sev_id_block *id_block; + struct sev_id_authentication *id_auth; + + /* Define the guest policy for SEV guests */ + uint64_t sev_policy; + + /* List of all parameters to populate in the guest */ + QTAILQ_HEAD(, IgvmParameterData) parameter_data; +}; + +static int directive_page_data(struct igvm_context *ctx, int i, + const uint8_t *header_data, Error **errp); +static int directive_vp_context(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_vp_context(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_parameter_area(struct igvm_context *ctx, int i, + const uint8_t *header_data, Error **errp); +static int directive_parameter_insert(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_parameter_area(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_memory_map(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_parameter_insert(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_vp_count(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_memory_map(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_environment_info(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_vp_count(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_required_memory(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_environment_info(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_snp_id_block(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); -static int directive_required_memory(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, - const uint8_t *header_data, Error **errp); -struct IGVMDirectiveHandler { +struct IGVMHandler { uint32_t type; - int (*handler)(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, const uint8_t *header_data, - Error **errp); + uint32_t section; + int (*handler)(struct igvm_context *ctx, int i, + const uint8_t *header_data, Error **errp); }; -static struct IGVMDirectiveHandler directive_handlers[] = { - { IGVM_VHT_PAGE_DATA, directive_page_data }, - { IGVM_VHT_VP_CONTEXT, directive_vp_context }, - { IGVM_VHT_PARAMETER_AREA, directive_parameter_area }, - { IGVM_VHT_PARAMETER_INSERT, directive_parameter_insert }, - { IGVM_VHT_MEMORY_MAP, directive_memory_map }, - { IGVM_VHT_VP_COUNT_PARAMETER, directive_vp_count }, - { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, directive_environment_info }, - { IGVM_VHT_REQUIRED_MEMORY, directive_required_memory }, +static struct IGVMHandler handlers[] = { + { IGVM_VHT_PAGE_DATA, HEADER_SECTION_DIRECTIVE, directive_page_data }, + { IGVM_VHT_VP_CONTEXT, HEADER_SECTION_DIRECTIVE, directive_vp_context }, + { IGVM_VHT_PARAMETER_AREA, HEADER_SECTION_DIRECTIVE, directive_parameter_area }, + { IGVM_VHT_PARAMETER_INSERT, HEADER_SECTION_DIRECTIVE, directive_parameter_insert }, + { IGVM_VHT_MEMORY_MAP, HEADER_SECTION_DIRECTIVE, directive_memory_map }, + { IGVM_VHT_VP_COUNT_PARAMETER, HEADER_SECTION_DIRECTIVE, directive_vp_count }, + { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, HEADER_SECTION_DIRECTIVE, directive_environment_info }, + { IGVM_VHT_REQUIRED_MEMORY, HEADER_SECTION_DIRECTIVE, directive_required_memory }, + { IGVM_VHT_SNP_ID_BLOCK, HEADER_SECTION_DIRECTIVE, directive_snp_id_block }, + }; -static int directive(uint32_t type, ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, Error **errp) +static int handle(uint32_t type, struct igvm_context *ctx, int i, Error **errp) { size_t handler; IgvmHandle header_handle; const uint8_t *header_data; int result; - for (handler = 0; handler < (sizeof(directive_handlers) / - sizeof(struct IGVMDirectiveHandler)); + for (handler = 0; handler < (sizeof(handlers) / + sizeof(struct IGVMHandler)); ++handler) { - if (directive_handlers[handler].type == type) { + if (handlers[handler].type == type) { header_handle = - igvm_get_header(cgs->igvm, HEADER_SECTION_DIRECTIVE, i); + igvm_get_header(ctx->cgs->igvm, handlers[handler].section, i); if (header_handle < 0) { error_setg( errp, - "IGVM file is invalid: Failed to read directive header (code: %d)", + "IGVM file is invalid: Failed to read header (code: %d)", (int)header_handle); return -1; } - header_data = igvm_get_buffer(cgs->igvm, header_handle) + + header_data = igvm_get_buffer(ctx->cgs->igvm, header_handle) + sizeof(IGVM_VHS_VARIABLE_HEADER); - result = directive_handlers[handler].handler(cgs, i, compatibility_mask, - header_data, errp); - igvm_free_buffer(cgs->igvm, header_handle); + result = handlers[handler].handler(ctx, i, header_data, errp); + igvm_free_buffer(ctx->cgs->igvm, header_handle); return result; } } error_setg(errp, - "IGVM: Unknown directive type encountered when processing file: " + "IGVM: Unknown header type encountered when processing file: " "(type 0x%X)", type); return -1; @@ -212,8 +263,8 @@ static bool page_attrs_equal(IgvmHandle igvm, int i, (page_1->compatibility_mask == page_2->compatibility_mask)); } -static int igvm_process_mem_region(ConfidentialGuestSupport *cgs, - IgvmHandle igvm, int start_index, +static int igvm_process_mem_region(struct igvm_context *ctx, + int start_index, uint64_t gpa_start, int page_count, const IgvmPageDataFlags *flags, const IgvmPageDataType page_type, @@ -237,7 +288,7 @@ static int igvm_process_mem_region(ConfidentialGuestSupport *cgs, } for (i = 0; i < page_count; ++i) { - data_handle = igvm_get_header_data(igvm, HEADER_SECTION_DIRECTIVE, + data_handle = igvm_get_header_data(ctx->cgs->igvm, HEADER_SECTION_DIRECTIVE, i + start_index); if (data_handle == IGVMAPI_NO_DATA) { /* No data indicates a zero page */ @@ -251,7 +302,7 @@ static int igvm_process_mem_region(ConfidentialGuestSupport *cgs, return -1; } else { zero = false; - data_size = igvm_get_buffer_size(igvm, data_handle); + data_size = igvm_get_buffer_size(ctx->cgs->igvm, data_handle); if (data_size < page_size) { memset(®ion[i * page_size], 0, page_size); } else if (data_size > page_size) { @@ -261,9 +312,9 @@ static int igvm_process_mem_region(ConfidentialGuestSupport *cgs, i + start_index); return -1; } - data = igvm_get_buffer(igvm, data_handle); + data = igvm_get_buffer(ctx->cgs->igvm, data_handle); memcpy(®ion[i * page_size], data, data_size); - igvm_free_buffer(igvm, data_handle); + igvm_free_buffer(ctx->cgs->igvm, data_handle); } } @@ -277,7 +328,7 @@ static int igvm_process_mem_region(ConfidentialGuestSupport *cgs, return -1; } - result = cgs->set_guest_state(gpa_start, region, page_size * page_count, + result = ctx->cgs->set_guest_state(gpa_start, region, page_size * page_count, cgs_page_type, 0, errp); if ((result < 0) && !*errp) { error_setg(errp, "IGVM set guest state failed with code %d", result); @@ -286,7 +337,7 @@ static int igvm_process_mem_region(ConfidentialGuestSupport *cgs, return 0; } -static int process_mem_page(ConfidentialGuestSupport *cgs, int i, +static int process_mem_page(struct igvm_context *ctx, int i, const IGVM_VHS_PAGE_DATA *page_data, Error **errp) { ERRP_GUARD(); @@ -301,13 +352,13 @@ static int process_mem_page(ConfidentialGuestSupport *cgs, int i, region_start = page_data->gpa; region_start_i = i; } else { - if (!page_attrs_equal(cgs->igvm, i, page_data, &prev_page_data) || + if (!page_attrs_equal(ctx->cgs->igvm, i, page_data, &prev_page_data) || ((prev_page_data.gpa + (prev_page_data.flags.is_2mb_page ? 0x200000 : 0x1000)) != page_data->gpa) || (last_i != (i - 1))) { /* End of current region */ - if (igvm_process_mem_region(cgs, cgs->igvm, region_start_i, + if (igvm_process_mem_region(ctx, region_start_i, region_start, page_count, &prev_page_data.flags, prev_page_data.data_type, errp) < 0) { @@ -323,7 +374,7 @@ static int process_mem_page(ConfidentialGuestSupport *cgs, int i, ++page_count; } else { if (page_count > 0) { - if (igvm_process_mem_region(cgs, cgs->igvm, region_start_i, + if (igvm_process_mem_region(ctx, region_start_i, region_start, page_count, &prev_page_data.flags, prev_page_data.data_type, errp) < 0) { @@ -335,20 +386,18 @@ static int process_mem_page(ConfidentialGuestSupport *cgs, int i, return 0; } -static int directive_page_data(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_page_data(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { const IGVM_VHS_PAGE_DATA *page_data = (const IGVM_VHS_PAGE_DATA *)header_data; - if (page_data->compatibility_mask & compatibility_mask) { - return process_mem_page(cgs, i, page_data, errp); + if (page_data->compatibility_mask & ctx->compatibility_mask) { + return process_mem_page(ctx, i, page_data, errp); } return 0; } -static int directive_vp_context(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_vp_context(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { ERRP_GUARD(); @@ -358,20 +407,21 @@ static int directive_vp_context(ConfidentialGuestSupport *cgs, int i, uint8_t *data; int result; - if (vp_context->compatibility_mask & compatibility_mask) { + if (vp_context->compatibility_mask & ctx->compatibility_mask) { data_handle = - igvm_get_header_data(cgs->igvm, HEADER_SECTION_DIRECTIVE, i); + igvm_get_header_data(ctx->cgs->igvm, HEADER_SECTION_DIRECTIVE, i); if (data_handle < 0) { error_setg(errp, "Invalid VP context in IGVM file. Error code: %X", data_handle); return -1; } - data = (uint8_t *)igvm_get_buffer(cgs->igvm, data_handle); - result = cgs->set_guest_state( - vp_context->gpa, data, igvm_get_buffer_size(cgs->igvm, data_handle), + data = (uint8_t *)igvm_get_buffer(ctx->cgs->igvm, data_handle); + result = ctx->cgs->set_guest_state( + vp_context->gpa, data, + igvm_get_buffer_size(ctx->cgs->igvm, data_handle), CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); - igvm_free_buffer(cgs->igvm, data_handle); + igvm_free_buffer(ctx->cgs->igvm, data_handle); if (result != 0) { if (!*errp) { error_setg(errp, @@ -384,8 +434,7 @@ static int directive_vp_context(ConfidentialGuestSupport *cgs, int i, return 0; } -static int directive_parameter_area(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_parameter_area(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { const IGVM_VHS_PARAMETER_AREA *param_area = @@ -397,12 +446,11 @@ static int directive_parameter_area(ConfidentialGuestSupport *cgs, int i, param_entry->index = param_area->parameter_area_index; param_entry->data = g_malloc0(param_entry->size); - QTAILQ_INSERT_TAIL(¶meter_data, param_entry, next); + QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next); return 0; } -static int directive_parameter_insert(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_parameter_insert(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { ERRP_GUARD(); @@ -412,7 +460,7 @@ static int directive_parameter_insert(ConfidentialGuestSupport *cgs, int i, int result; void *region; - QTAILQ_FOREACH(param_entry, ¶meter_data, next) + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) { if (param_entry->index == param->parameter_area_index) { region = @@ -424,7 +472,7 @@ static int directive_parameter_insert(ConfidentialGuestSupport *cgs, int i, g_free(param_entry->data); param_entry->data = NULL; - result = cgs->set_guest_state(param->gpa, region, param_entry->size, + result = ctx->cgs->set_guest_state(param->gpa, region, param_entry->size, CGS_PAGE_TYPE_UNMEASURED, 0, errp); if (result != 0) { if (!*errp) { @@ -455,8 +503,7 @@ static int cmp_mm_entry(const void *a, const void *b) } } -static int directive_memory_map(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_memory_map(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; @@ -468,14 +515,14 @@ static int directive_memory_map(ConfidentialGuestSupport *cgs, int i, int retval = 0; /* Find the parameter area that should hold the memory map */ - QTAILQ_FOREACH(param_entry, ¶meter_data, next) + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) { if (param_entry->index == param->parameter_area_index) { max_entry_count = param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY); mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data; - retval = cgs->get_mem_map_entry(entry, &cgmm_entry, errp); + retval = ctx->cgs->get_mem_map_entry(entry, &cgmm_entry, errp); while (retval == 0) { if (entry > max_entry_count) { error_setg( @@ -503,7 +550,7 @@ static int directive_memory_map(ConfidentialGuestSupport *cgs, int i, mm_entry[entry].entry_type = PLATFORM_RESERVED; break; } - retval = cgs->get_mem_map_entry(++entry, &cgmm_entry, errp); + retval = ctx->cgs->get_mem_map_entry(++entry, &cgmm_entry, errp); } if (retval < 0) { return retval; @@ -518,8 +565,7 @@ static int directive_memory_map(ConfidentialGuestSupport *cgs, int i, return 0; } -static int directive_vp_count(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_vp_count(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; @@ -527,7 +573,7 @@ static int directive_vp_count(ConfidentialGuestSupport *cgs, int i, uint32_t *vp_count; CPUState *cpu; - QTAILQ_FOREACH(param_entry, ¶meter_data, next) + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) { if (param_entry->index == param->parameter_area_index) { vp_count = (uint32_t *)(param_entry->data + param->byte_offset); @@ -542,15 +588,14 @@ static int directive_vp_count(ConfidentialGuestSupport *cgs, int i, return 0; } -static int directive_environment_info(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_environment_info(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; IgvmParameterData *param_entry; IgvmEnvironmentInfo *environmental_state; - QTAILQ_FOREACH(param_entry, ¶meter_data, next) + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) { if (param_entry->index == param->parameter_area_index) { environmental_state = @@ -562,8 +607,7 @@ static int directive_environment_info(ConfidentialGuestSupport *cgs, int i, return 0; } -static int directive_required_memory(ConfidentialGuestSupport *cgs, int i, - uint32_t compatibility_mask, +static int directive_required_memory(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp) { ERRP_GUARD(); @@ -572,12 +616,12 @@ static int directive_required_memory(ConfidentialGuestSupport *cgs, int i, uint8_t *region; int result; - if (mem->compatibility_mask & compatibility_mask) { + if (mem->compatibility_mask & ctx->compatibility_mask) { region = igvm_prepare_memory(mem->gpa, mem->number_of_bytes, i, errp); if (!region) { return -1; } - result = cgs->set_guest_state(mem->gpa, region, mem->number_of_bytes, + result = ctx->cgs->set_guest_state(mem->gpa, region, mem->number_of_bytes, CGS_PAGE_TYPE_REQUIRED_MEMORY, 0, errp); if (result < 0) { if (!*errp) { @@ -591,16 +635,67 @@ static int directive_required_memory(ConfidentialGuestSupport *cgs, int i, return 0; } -static uint32_t supported_platform_compat_mask(ConfidentialGuestSupport *cgs, +static int directive_snp_id_block(struct igvm_context *ctx, int i, + const uint8_t *header_data, Error **errp) +{ + const IGVM_VHS_SNP_ID_BLOCK *igvm_id = + (const IGVM_VHS_SNP_ID_BLOCK *)header_data; + + if (ctx->compatibility_mask & igvm_id->compatibility_mask) { + if (ctx->id_block) { + error_setg(errp, "IGVM: Multiple ID blocks encountered " + "in IGVM file."); + return -1; + } + ctx->id_block = g_malloc0(sizeof(struct sev_id_block)); + ctx->id_auth = g_malloc0(sizeof(struct sev_id_authentication)); + + memcpy(ctx->id_block->family_id, igvm_id->family_id, + sizeof(ctx->id_block->family_id)); + memcpy(ctx->id_block->image_id, igvm_id->image_id, + sizeof(ctx->id_block->image_id)); + ctx->id_block->guest_svn = igvm_id->guest_svn; + ctx->id_block->version = 1; + memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld)); + + ctx->id_auth->id_key_alg = igvm_id->id_key_algorithm; + memcpy(ctx->id_auth->id_block_sig, &igvm_id->id_key_signature, + sizeof(igvm_id->id_key_signature)); + + ctx->id_auth->auth_key_algo = igvm_id->author_key_algorithm; + memcpy(ctx->id_auth->id_key_sig, &igvm_id->author_key_signature, + sizeof(igvm_id->author_key_signature)); + + /* + * SEV and IGVM public key structure population are slightly different. + * See SEV Secure Nested Paging Firmware ABI Specification, Chapter 10. + */ + *((uint32_t *)ctx->id_auth->id_key) = igvm_id->id_public_key.curve; + memcpy(&ctx->id_auth->id_key[4], &igvm_id->id_public_key.qx, 72); + memcpy(&ctx->id_auth->id_key[76], &igvm_id->id_public_key.qy, 72); + + *((uint32_t *)ctx->id_auth->author_key) = + igvm_id->author_public_key.curve; + memcpy(&ctx->id_auth->author_key[4], &igvm_id->author_public_key.qx, + 72); + memcpy(&ctx->id_auth->author_key[76], &igvm_id->author_public_key.qy, + 72); + } + + return 0; +} + +static int supported_platform_compat_mask(struct igvm_context *ctx, Error **errp) { int32_t result; int i; IgvmHandle header_handle; IGVM_VHS_SUPPORTED_PLATFORM *platform; - uint32_t compatibility_mask = 0; - result = igvm_header_count(cgs->igvm, HEADER_SECTION_PLATFORM); + ctx->compatibility_mask = 0; + + result = igvm_header_count(ctx->cgs->igvm, HEADER_SECTION_PLATFORM); if (result < 0) { error_setg(errp, "Invalid platform header count in IGVM file. Error code: %X", @@ -610,10 +705,10 @@ static uint32_t supported_platform_compat_mask(ConfidentialGuestSupport *cgs, for (i = 0; i < (int)result; ++i) { IgvmVariableHeaderType typ = - igvm_get_header_type(cgs->igvm, HEADER_SECTION_PLATFORM, i); + igvm_get_header_type(ctx->cgs->igvm, HEADER_SECTION_PLATFORM, i); if (typ == IGVM_VHT_SUPPORTED_PLATFORM) { header_handle = - igvm_get_header(cgs->igvm, HEADER_SECTION_PLATFORM, i); + igvm_get_header(ctx->cgs->igvm, HEADER_SECTION_PLATFORM, i); if (header_handle < 0) { error_setg(errp, "Invalid platform header in IGVM file. " @@ -622,7 +717,7 @@ static uint32_t supported_platform_compat_mask(ConfidentialGuestSupport *cgs, return 0; } platform = - (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(cgs->igvm, + (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->cgs->igvm, header_handle) + sizeof( IGVM_VHS_VARIABLE_HEADER)); @@ -635,28 +730,47 @@ static uint32_t supported_platform_compat_mask(ConfidentialGuestSupport *cgs, * check whether each IGVM directive results in an operation * that is supported by the particular derivative of SEV. */ - if (cgs->check_support( + if (ctx->cgs->check_support( CGS_PLATFORM_SEV_SNP, platform->platform_version, platform->highest_vtl, platform->shared_gpa_boundary) || - cgs->check_support( + ctx->cgs->check_support( CGS_PLATFORM_SEV_ES, platform->platform_version, platform->highest_vtl, platform->shared_gpa_boundary) || - cgs->check_support( + ctx->cgs->check_support( CGS_PLATFORM_SEV, platform->platform_version, platform->highest_vtl, platform->shared_gpa_boundary)) { - compatibility_mask = platform->compatibility_mask; + ctx->compatibility_mask = platform->compatibility_mask; + ctx->platform_type = platform->platform_type; break; } } - igvm_free_buffer(cgs->igvm, header_handle); + igvm_free_buffer(ctx->cgs->igvm, header_handle); } } - if (compatibility_mask == 0) { + if (ctx->compatibility_mask == 0) { error_setg( errp, "IGVM file does not describe a compatible supported platform"); + return -1; } - return compatibility_mask; + return 0; +} + +static int handle_policy(struct igvm_context *ctx, Error **errp) +{ + if (ctx->platform_type == SEV_SNP) { + int id_block_len = 0; + int id_auth_len = 0; + if (ctx->id_block) { + ctx->id_block->policy = ctx->sev_policy; + id_block_len = sizeof(struct sev_id_block); + id_auth_len = sizeof(struct sev_id_authentication); + } + return ctx->cgs->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_policy, + ctx->id_block, id_block_len, + ctx->id_auth, id_auth_len, errp); + } + return 0; } int igvm_file_init(ConfidentialGuestSupport *cgs, Error **errp) @@ -688,9 +802,9 @@ int igvm_process(ConfidentialGuestSupport *cgs, Error **errp) { int32_t result; int i; - uint32_t compatibility_mask; IgvmParameterData *parameter; int retval = 0; + struct igvm_context ctx; /* * If this is not a Confidential guest or no IGVM has been provided then @@ -700,12 +814,15 @@ int igvm_process(ConfidentialGuestSupport *cgs, Error **errp) return 0; } + memset(&ctx, 0, sizeof(struct igvm_context)); + QTAILQ_INIT(&ctx.parameter_data); + ctx.cgs = cgs; + /* * Check that the IGVM file provides configuration for the current * platform */ - compatibility_mask = supported_platform_compat_mask(cgs, errp); - if (compatibility_mask == 0) { + if (supported_platform_compat_mask(&ctx, errp) != 0) { return -1; } @@ -717,12 +834,27 @@ int igvm_process(ConfidentialGuestSupport *cgs, Error **errp) return -1; } - QTAILQ_INIT(¶meter_data); - for (i = 0; i < (int)result; ++i) { IgvmVariableHeaderType type = igvm_get_header_type(cgs->igvm, HEADER_SECTION_DIRECTIVE, i); - if (directive(type, cgs, i, compatibility_mask, errp) < 0) { + if (handle(type, &ctx, i, errp) < 0) { + retval = -1; + break; + } + } + + result = igvm_header_count(cgs->igvm, HEADER_SECTION_INITIALIZATION); + if (result < 0) { + error_setg( + errp, "Invalid initialization header count in IGVM file. Error code: %X", + result); + return -1; + } + + for (i = 0; i < (int)result; ++i) { + IgvmVariableHeaderType type = + igvm_get_header_type(cgs->igvm, HEADER_SECTION_INITIALIZATION, i); + if (handle(type, &ctx, i, errp) < 0) { retval = -1; break; } @@ -734,14 +866,21 @@ int igvm_process(ConfidentialGuestSupport *cgs, Error **errp) * last group is processed with this call. */ if (retval == 0) { - retval = process_mem_page(cgs, i, NULL, errp); + retval = process_mem_page(&ctx, i, NULL, errp); + } + + if (retval == 0) { + retval = handle_policy(&ctx, errp); } - QTAILQ_FOREACH(parameter, ¶meter_data, next) + /* Clean up the context */ + QTAILQ_FOREACH(parameter, &ctx.parameter_data, next) { g_free(parameter->data); parameter->data = NULL; } + g_free(ctx.id_block); + g_free(ctx.id_auth); return retval; } From ff68d547f8cad2f1b72d7acefc5d07363cedca1f Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Tue, 2 Apr 2024 09:59:52 +0100 Subject: [PATCH 3/6] backends/igvm: Handle policy for SEV guests Adds a handler for the guest policy initialization IGVM section and builds an SEV policy based on this information and the ID block directive if present. The policy is applied using by calling 'set_guest_policy()' on the ConfidentialGuestSupport object. Signed-off-by: Roy Hopkins --- backends/igvm.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/backends/igvm.c b/backends/igvm.c index bbdf954b4d4c..aba5586bc01d 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -101,17 +101,20 @@ static int directive_vp_context(struct igvm_context *ctx, int i, static int directive_parameter_area(struct igvm_context *ctx, int i, const uint8_t *header_data, Error **errp); static int directive_parameter_insert(struct igvm_context *ctx, int i, - const uint8_t *header_data, Error **errp); + const uint8_t *header_data, Error **errp); static int directive_memory_map(struct igvm_context *ctx, int i, - const uint8_t *header_data, Error **errp); + const uint8_t *header_data, Error **errp); static int directive_vp_count(struct igvm_context *ctx, int i, - const uint8_t *header_data, Error **errp); + const uint8_t *header_data, Error **errp); static int directive_environment_info(struct igvm_context *ctx, int i, - const uint8_t *header_data, Error **errp); + const uint8_t *header_data, Error **errp); static int directive_required_memory(struct igvm_context *ctx, int i, - const uint8_t *header_data, Error **errp); + const uint8_t *header_data, Error **errp); static int directive_snp_id_block(struct igvm_context *ctx, int i, - const uint8_t *header_data, Error **errp); + const uint8_t *header_data, Error **errp); + +static int initialization_guest_policy(struct igvm_context *ctx, int i, + const uint8_t *header_data, Error **errp); struct IGVMHandler { uint32_t type; @@ -130,7 +133,7 @@ static struct IGVMHandler handlers[] = { { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, HEADER_SECTION_DIRECTIVE, directive_environment_info }, { IGVM_VHT_REQUIRED_MEMORY, HEADER_SECTION_DIRECTIVE, directive_required_memory }, { IGVM_VHT_SNP_ID_BLOCK, HEADER_SECTION_DIRECTIVE, directive_snp_id_block }, - + { IGVM_VHT_GUEST_POLICY, HEADER_SECTION_INITIALIZATION, initialization_guest_policy }, }; static int handle(uint32_t type, struct igvm_context *ctx, int i, Error **errp) @@ -685,6 +688,18 @@ static int directive_snp_id_block(struct igvm_context *ctx, int i, return 0; } +static int initialization_guest_policy(struct igvm_context *ctx, int i, + const uint8_t *header_data, Error **errp) +{ + const IGVM_VHS_GUEST_POLICY *guest = + (const IGVM_VHS_GUEST_POLICY *)header_data; + + if (guest->compatibility_mask & ctx->compatibility_mask) { + ctx->sev_policy = guest->policy; + } + return 0; +} + static int supported_platform_compat_mask(struct igvm_context *ctx, Error **errp) { From 0fd5b1fc7db53994002c612901b12312c2d6b106 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Tue, 2 Apr 2024 10:03:01 +0100 Subject: [PATCH 4/6] i386/sev: Add implementation of CGS set_guest_policy() The new cgs_set_guest_policy() function is provided to receive the guest policy flags, SNP ID block and SNP ID authentication from guest configuration such as an IGVM file and apply it to the platform prior to launching the guest. The policy is used to populate values for the existing 'policy', 'id_block' and 'id_auth' parameters. When provided, the guest policy is applied and the ID block configuration is used to verify the launch measurement and signatures. The guest is only successfully started if the expected launch measurements match the actual measurements and the signatures are valid. Signed-off-by: Roy Hopkins --- target/i386/sev.c | 82 +++++++++++++++++++++++++++++++++++++++++++++-- target/i386/sev.h | 12 +++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 23a4b2dba009..bfcf2e1b7738 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -433,7 +433,7 @@ static void sev_apply_cpu_context(CPUState *cpu) launch_vmsa->vmsa.ss.base, launch_vmsa->vmsa.ss.limit, FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ss.attrib)); - env->dr[6] = launch_vmsa->vmsa.dr6; + env->dr[6] = launch_vmsa->vmsa.dr6; env->dr[7] = launch_vmsa->vmsa.dr7; env->regs[R_EAX] = launch_vmsa->vmsa.rax; @@ -482,7 +482,7 @@ static int check_vmsa_supported(const struct sev_es_save_area *vmsa) memset(&vmsa_check.ds, 0, sizeof(vmsa_check.ds)); memset(&vmsa_check.fs, 0, sizeof(vmsa_check.fs)); memset(&vmsa_check.gs, 0, sizeof(vmsa_check.gs)); - vmsa_check.efer = 0; + vmsa_check.efer = 0; vmsa_check.cr0 = 0; vmsa_check.cr3 = 0; vmsa_check.cr4 = 0; @@ -667,6 +667,83 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, return ret; } +static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp) +{ + if (policy_type != GUEST_POLICY_SEV) { + error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d", + __func__, policy_type); + return -1; + } + /* + * SEV-SNP handles policy differently. The policy flags are defined in + * kvm_start_conf.policy and an ID block and ID auth can be provided. + */ + if (sev_snp_enabled()) { + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + + /* + * The policy consists of flags in 'policy' and optionally an ID block and + * ID auth in policy_data1 and policy_data2 respectively. + * The ID block and auth are optional so clear any previous ID block and + * auth and set them if provided, but always set the policy flags. + */ + g_free(sev_snp_guest->id_block); + g_free((guchar *)finish->id_block_uaddr); + g_free(sev_snp_guest->id_auth); + g_free((guchar *)finish->id_auth_uaddr); + sev_snp_guest->id_block = NULL; + finish->id_block_uaddr = 0; + sev_snp_guest->id_auth = NULL; + finish->id_auth_uaddr = 0; + + if (policy_data1_size > 0) { + struct sev_snp_id_authentication *id_auth = (struct sev_snp_id_authentication *)policy_data2; + + if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) { + error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size", + __func__); + return -1; + } + if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) { + error_setg(errp, "%s: Invalid SEV-SNP ID auth block: incorrect size", + __func__); + return -1; + } + finish->id_block_uaddr = (__u64)g_malloc0(KVM_SEV_SNP_ID_BLOCK_SIZE); + finish->id_auth_uaddr = (__u64)g_malloc0(KVM_SEV_SNP_ID_AUTH_SIZE); + memcpy((void *)finish->id_block_uaddr, policy_data1, KVM_SEV_SNP_ID_BLOCK_SIZE); + memcpy((void *)finish->id_auth_uaddr, policy_data2, KVM_SEV_SNP_ID_AUTH_SIZE); + + /* + * Check if an author key has been provided and use that to flag + * whether the author key is enabled. The first of the author key + * must be non-zero to indicate the key type, which will currently + * always be 2. + */ + sev_snp_guest->kvm_finish_conf.auth_key_en = + id_auth->author_key[0] ? 1 : 0; + finish->id_block_en = 1; + } + sev_snp_guest->kvm_start_conf.policy = policy; + } + else { + SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); + /* Only the policy flags are supported for SEV and SEV-ES */ + if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) { + error_setg(errp, "%s: An ID block/ID auth block has been provided " + "but SEV-SNP is not supported", __func__); + return -1; + } + sev_guest->policy = policy; + } + return 0; +} + static int cgs_get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry, Error **errp) @@ -749,6 +826,7 @@ sev_common_instance_init(Object *obj) cgs->check_support = cgs_check_support; cgs->set_guest_state = cgs_set_guest_state; + cgs->set_guest_policy = cgs_set_guest_policy; cgs->get_mem_map_entry = cgs_get_mem_map_entry; QTAILQ_INIT(&sev_common->launch_vmsa); diff --git a/target/i386/sev.h b/target/i386/sev.h index 6c915dd708b0..cbb967bd48cb 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -155,6 +155,18 @@ struct QEMU_PACKED sev_es_save_area { uint8_t fpreg_ymm[256]; }; +struct QEMU_PACKED sev_snp_id_authentication { + uint32_t id_key_alg; + uint32_t auth_key_algo; + uint8_t reserved[56]; + uint8_t id_block_sig[512]; + uint8_t id_key[1028]; + uint8_t reserved2[60]; + uint8_t id_key_sig[512]; + uint8_t author_key[1028]; + uint8_t reserved3[892]; +}; + #ifdef CONFIG_SEV bool sev_enabled(void); bool sev_es_enabled(void); From e0eece1f67396aed198a5d2078d936c5037ac931 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Tue, 2 Apr 2024 10:08:09 +0100 Subject: [PATCH 5/6] i386/sev: Fix error handling in sev_snp_launch_finish() This fixes the handling of return codes from the KVM_SEV_SNP_LAUNCH_FINISH ioctl. The ioctl returns a success code if the command was successfully sent to the SEV PSP regardless of whether the command itself was successful or not. Any error from the PSP is returned instead via the '&error' parameter. This commit checks 'error' to see if the processing resulted in an error and reports it as required. Signed-off-by: Roy Hopkins --- target/i386/sev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index bfcf2e1b7738..96cfdb7565fa 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2066,7 +2066,7 @@ sev_snp_launch_finish(SevSnpGuestState *sev_snp) sev_snp->host_data); ret = sev_ioctl(SEV_COMMON(sev_snp)->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error); - if (ret) { + if (ret || error) { error_report("%s: SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(error)); exit(1); From 7910d8b81719b4e79457cdb0b201ab633e325786 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Mon, 8 Apr 2024 12:00:54 +0100 Subject: [PATCH 6/6] i386/sev: Sync additional VMSA segment registers The synchronization between VMSA and CPU state did not include the GDT, IDT, LDTR or TR registers. These registers had default, non-zero values, therefore if the VMSA did not match these values then a difference in launch measurement is observed. This change adds synchronization of these registers and adds them to the VMSA field check. Signed-off-by: Roy Hopkins --- target/i386/sev.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 96cfdb7565fa..9e5a9f753c89 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -433,7 +433,23 @@ static void sev_apply_cpu_context(CPUState *cpu) launch_vmsa->vmsa.ss.base, launch_vmsa->vmsa.ss.limit, FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ss.attrib)); - env->dr[6] = launch_vmsa->vmsa.dr6; + env->gdt.base = launch_vmsa->vmsa.gdtr.base; + env->gdt.limit = launch_vmsa->vmsa.gdtr.limit; + env->gdt.flags = FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gdtr.attrib); + env->idt.base = launch_vmsa->vmsa.idtr.base; + env->idt.limit = launch_vmsa->vmsa.idtr.limit; + env->idt.flags = FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.idtr.attrib); + + cpu_x86_load_seg_cache( + env, R_LDTR, launch_vmsa->vmsa.ldtr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.ldtr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ldtr.attrib)); + cpu_x86_load_seg_cache( + env, R_TR, launch_vmsa->vmsa.tr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.tr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.tr.attrib)); + + env->dr[6] = launch_vmsa->vmsa.dr6; env->dr[7] = launch_vmsa->vmsa.dr7; env->regs[R_EAX] = launch_vmsa->vmsa.rax; @@ -482,7 +498,11 @@ static int check_vmsa_supported(const struct sev_es_save_area *vmsa) memset(&vmsa_check.ds, 0, sizeof(vmsa_check.ds)); memset(&vmsa_check.fs, 0, sizeof(vmsa_check.fs)); memset(&vmsa_check.gs, 0, sizeof(vmsa_check.gs)); - vmsa_check.efer = 0; + memset(&vmsa_check.gdtr, 0, sizeof(vmsa_check.gdtr)); + memset(&vmsa_check.idtr, 0, sizeof(vmsa_check.idtr)); + memset(&vmsa_check.ldtr, 0, sizeof(vmsa_check.ldtr)); + memset(&vmsa_check.tr, 0, sizeof(vmsa_check.tr)); + vmsa_check.efer = 0; vmsa_check.cr0 = 0; vmsa_check.cr3 = 0; vmsa_check.cr4 = 0;