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"
+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?)
/*
* 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.
+ 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?
+ 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 ?
+ /*
+ * 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);