> On 13/06/2025 16:22, Roy Hopkins wrote:
> > 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 <roy.hopk...@randomman.co.uk>
> > Acked-by: Michael S. Tsirkin <m...@redhat.com>
> > Acked-by: Stefano Garzarella <sgarz...@redhat.com>
> > Acked-by: Gerd Hoffman <kra...@redhat.com>
> > ---
> > backends/igvm.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 138 insertions(+)
> > 
> > diff --git a/backends/igvm.c b/backends/igvm.c
> > index ebdb4594d1..034f504716 100644
> > --- a/backends/igvm.c
> > +++ b/backends/igvm.c
> > @@ -27,6 +27,33 @@ typedef struct QIgvmParameterData {
> > uint32_t index;
> > } QIgvmParameterData;
> > 
> > +/*
> > + * Some directives are specific to particular confidential computing 
> > platforms.
> > + * Define required types for each of those platforms here.
> > + */
> > +
> > +/* SEV/SEV-ES/SEV-SNP */
> 
> 
> Could mention that the following are in Chapter 8 of
> "SEV Secure Nested Paging Firmware ABI Specification"
> 
Good suggestion. I'll add it.
> 
> > +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];
> > +};
> > +
> 
> In the next patch (#15), sev_snp_id_authentication is in target/i386/sev.h
> Should they be all together? (and sev_snp_id_authentication seems
> identical to sev_id_authentication - do we we need both structs?)
> 
That does make sense and I hoped to do that, but the two files should not really
depend on each other in their current locations. Perhaps we can look at moving
the SEV definitions into a common location in a future patch? 

> > /*
> > * QIgvm contains the information required during processing
> > * of a single IGVM file.
> > @@ -38,6 +65,17 @@ typedef struct QIgvm {
> > uint32_t compatibility_mask;
> > unsigned current_header_index;
> > QTAILQ_HEAD(, QIgvmParameterData) parameter_data;
> > + IgvmPlatformType platform_type;
> > +
> > + /*
> > + * SEV-SNP platforms can contain an 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;
> > 
> > /* These variables keep track of contiguous page regions */
> > IGVM_VHS_PAGE_DATA region_prev_page_data;
> > @@ -67,6 +105,11 @@ static int qigvm_directive_environment_info(QIgvm *ctx,
> > static int qigvm_directive_required_memory(QIgvm *ctx,
> > const uint8_t *header_data,
> > Error **errp);
> > +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t 
> > *header_data,
> > + Error **errp);
> > +static int qigvm_initialization_guest_policy(QIgvm *ctx,
> > + const uint8_t *header_data,
> > + Error **errp);
> > 
> > struct QIGVMHandler {
> > uint32_t type;
> > @@ -91,6 +134,10 @@ static struct QIGVMHandler handlers[] = {
> > qigvm_directive_environment_info },
> > { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
> > qigvm_directive_required_memory },
> > + { IGVM_VHT_SNP_ID_BLOCK, IGVM_HEADER_SECTION_DIRECTIVE,
> > + qigvm_directive_snp_id_block },
> > + { IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION,
> > + qigvm_initialization_guest_policy },
> > };
> > 
> > static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp)
> > @@ -632,6 +679,70 @@ static int qigvm_directive_required_memory(QIgvm *ctx,
> > return 0;
> > }
> > 
> > +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t 
> > *header_data,
> > + Error **errp)
> > +{
> > + const IGVM_VHS_SNP_ID_BLOCK *igvm_id =
> > + (const IGVM_VHS_SNP_ID_BLOCK *)header_data;
> > +
> > + if (!(igvm_id->compatibility_mask & ctx->compatibility_mask)) {
> > + return 0;
> > + }
> > +
> > + if (ctx->id_block) {
> > + error_setg(errp, "IGVM: Multiple ID blocks encountered "
> > + "in IGVM file.");
> 
> Error string should be on one line to make it easier to search/grep for.

This, and the other formatting comments are this way due to the formatting
requirements in the project. checkpatch.pl fails with an error if I try
to stretch these lines out.

> 
> > + return -1;
> > + }
> > + ctx->id_block = g_new0(struct sev_id_block, 1);
> > + ctx->id_auth = g_new0(struct sev_id_authentication, 1);
> > +
> > + 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;
> 
> Worth a #define for version?
> 
Yes. Done.

> > + 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));
> > +
> 
> Should the sizeof be ctx->id_auth->id_block_sig (the dest) ?
> 
> > + 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));
> > +
> 
> Should the sizeof be ctx->id_auth->id_key_sig ?
> 
Unfortunately the two structures are different sizes. I've added an assert
for this and the other one below to catch any future errors.

> > + /*
> > + * 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);
> > +
> 
> For both memcpy calls, they could fit on one line and be more readable.
> 
> > + return 0;
> > +}
> > +
> > +static int qigvm_initialization_guest_policy(QIgvm *ctx,
> > + 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 qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
> > {
> > int32_t header_count;
> > @@ -701,12 +812,16 @@ static int qigvm_supported_platform_compat_mask(QIgvm 
> > *ctx, Error **errp)
> > /* Choose the strongest supported isolation technology */
> > if (compatibility_mask_sev_snp != 0) {
> > ctx->compatibility_mask = compatibility_mask_sev_snp;
> > + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_SNP;
> > } else if (compatibility_mask_sev_es != 0) {
> > ctx->compatibility_mask = compatibility_mask_sev_es;
> > + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_ES;
> > } else if (compatibility_mask_sev != 0) {
> > ctx->compatibility_mask = compatibility_mask_sev;
> > + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV;
> > } else if (compatibility_mask != 0) {
> > ctx->compatibility_mask = compatibility_mask;
> > + ctx->platform_type = IGVM_PLATFORM_TYPE_NATIVE;
> > } else {
> > error_setg(
> > errp,
> > @@ -716,6 +831,23 @@ static int qigvm_supported_platform_compat_mask(QIgvm 
> > *ctx, Error **errp)
> > return 0;
> > }
> > 
> > +static int qigvm_handle_policy(QIgvm *ctx, Error **errp)
> > +{
> > + if (ctx->platform_type == IGVM_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->cgsc->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_policy,
> > + ctx->id_block, id_block_len,
> > + ctx->id_auth, id_auth_len, errp);
> > + }
> > + return 0;
> > +}
> > +
> > static IgvmHandle qigvm_file_init(char *filename, Error **errp)
> > {
> > IgvmHandle igvm;
> > @@ -814,12 +946,18 @@ int qigvm_process_file(IgvmCfg *cfg, 
> > ConfidentialGuestSupport *cgs,
> > */
> > retval = qigvm_process_mem_page(&ctx, NULL, errp);
> > 
> > + if (retval == 0) {
> > + retval = qigvm_handle_policy(&ctx, errp);
> > + }
> > +
> > cleanup_parameters:
> > QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
> > {
> > g_free(parameter->data);
> > parameter->data = NULL;
> > }
> > + g_free(ctx.id_block);
> > + g_free(ctx.id_auth);
> > 
> > cleanup:
> > igvm_free(ctx.file);

Reply via email to