@@ -28,6 +28,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 */
+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];
+};
+
/*
* QIgvm contains the information required during processing
* of a single IGVM file.
@@ -39,6 +66,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;
@@ -68,6 +106,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;
@@ -92,6 +135,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)
@@ -633,6 +680,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.");
+ 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;
+ 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 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;
@@ -702,12 +813,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,
@@ -717,6 +832,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;
@@ -815,12 +947,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:
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;
}