[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and final
From: |
Michael Roth |
Subject: |
[RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch |
Date: |
Thu, 26 Aug 2021 17:26:22 -0500 |
From: Brijesh Singh <brijesh.singh@amd.com>
During the SNP guest launch sequence, a special secrets and cpuid page
needs to be populated by the SEV-SNP firmware. The secrets page contains
the VM Platform Communication Key (VMPCKs) used by the guest to send and
receive secure messages to the PSP. And CPUID page will contain the CPUID
value filtered through the PSP.
The guest BIOS (OVMF) reserves these pages in MEMFD and location of it
is available through the SNP boot block GUID. While finalizing the guest
boot flow, lookup for the boot block and call the SNP_LAUNCH_UPDATE
command to populate secrets and cpuid pages.
In order to support early boot code, the OVMF may ask hypervisor to
request the pre-validation of certain memory range. If such range is
present the call SNP_LAUNCH_UPDATE command to validate those address
range without affecting the measurement. See the SEV-SNP specification
for further details.
Finally, call the SNP_LAUNCH_FINISH to finalize the guest boot.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
target/i386/sev.c | 189 ++++++++++++++++++++++++++++++++++++++-
target/i386/trace-events | 2 +
2 files changed, 189 insertions(+), 2 deletions(-)
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 867c0cb457..0009c93d28 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -33,6 +33,7 @@
#include "monitor/monitor.h"
#include "exec/confidential-guest-support.h"
#include "hw/i386/pc.h"
+#include "qemu/range.h"
#define TYPE_SEV_COMMON "sev-common"
OBJECT_DECLARE_SIMPLE_TYPE(SevCommonState, SEV_COMMON)
@@ -107,6 +108,19 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
uint32_t reset_addr;
} SevInfoBlock;
+#define SEV_SNP_BOOT_BLOCK_GUID "bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9"
+typedef struct __attribute__((__packed__)) SevSnpBootInfoBlock {
+ /* Prevalidate range address */
+ uint32_t pre_validated_start;
+ uint32_t pre_validated_end;
+ /* Secrets page address */
+ uint32_t secrets_addr;
+ uint32_t secrets_len;
+ /* CPUID page address */
+ uint32_t cpuid_addr;
+ uint32_t cpuid_len;
+} SevSnpBootInfoBlock;
+
static Error *sev_mig_blocker;
static const char *const sev_fw_errlist[] = {
@@ -1086,6 +1100,162 @@ static Notifier sev_machine_done_notify = {
.notify = sev_launch_get_measure,
};
+static int
+sev_snp_launch_update_gpa(uint32_t hwaddr, uint32_t size, uint8_t type)
+{
+ void *hva;
+ MemoryRegion *mr = NULL;
+ SevSnpGuestState *sev_snp_guest =
+ SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs);
+
+ hva = gpa2hva(&mr, hwaddr, size, NULL);
+ if (!hva) {
+ error_report("SEV-SNP failed to get HVA for GPA 0x%x", hwaddr);
+ return 1;
+ }
+
+ return sev_snp_launch_update(sev_snp_guest, hwaddr, hva, size, type);
+}
+
+static bool
+detect_first_overlap(uint64_t start, uint64_t end, Range *range_list,
+ size_t range_count, Range *overlap_range)
+{
+ int i;
+ bool overlap = false;
+ Range new;
+
+ assert(overlap_range);
+ range_make_empty(overlap_range);
+ range_init_nofail(&new, start, end - start + 1);
+
+ for (i = 0; i < range_count; i++) {
+ if (range_overlaps_range(&new, &range_list[i]) &&
+ (range_is_empty(overlap_range) ||
+ range_lob(&range_list[i]) < range_lob(overlap_range))) {
+ *overlap_range = range_list[i];
+ overlap = true;
+ }
+ }
+
+ return overlap;
+}
+
+static void snp_ovmf_boot_block_setup(void)
+{
+ SevSnpBootInfoBlock *info;
+ uint32_t start, end, sz;
+ int ret;
+ Range validated_ranges[2];
+
+ /*
+ * Extract the SNP boot block for the SEV-SNP guests by locating the
+ * SNP_BOOT GUID. The boot block contains the information such as location
+ * of secrets and CPUID page, additionaly it may contain the range of
+ * memory that need to be pre-validated for the boot.
+ */
+ if (!pc_system_ovmf_table_find(SEV_SNP_BOOT_BLOCK_GUID,
+ (uint8_t **)&info, NULL)) {
+ error_report("SEV-SNP: failed to find the SNP boot block");
+ exit(1);
+ }
+
+ trace_kvm_sev_snp_ovmf_boot_block_info(info->secrets_addr,
+ info->secrets_len, info->cpuid_addr,
+ info->cpuid_len,
+ info->pre_validated_start,
+ info->pre_validated_end);
+
+ /* Populate the secrets page */
+ ret = sev_snp_launch_update_gpa(info->secrets_addr, info->secrets_len,
+ KVM_SEV_SNP_PAGE_TYPE_SECRETS);
+ if (ret) {
+ error_report("SEV-SNP: failed to insert secret page GPA 0x%x",
+ info->secrets_addr);
+ exit(1);
+ }
+
+ /* Populate the cpuid page */
+ ret = sev_snp_launch_update_gpa(info->cpuid_addr, info->cpuid_len,
+ KVM_SEV_SNP_PAGE_TYPE_CPUID);
+ if (ret) {
+ error_report("SEV-SNP: failed to insert cpuid page GPA 0x%x",
+ info->cpuid_addr);
+ exit(1);
+ }
+
+ /*
+ * Pre-validate the range using the LAUNCH_UPDATE_DATA, if the
+ * pre-validation range contains the CPUID and Secret page GPA then skip
+ * it. This is because SEV-SNP firmware pre-validates those pages as part
+ * of adding secrets and cpuid LAUNCH_UPDATE type.
+ */
+ range_init_nofail(&validated_ranges[0], info->secrets_addr,
info->secrets_len);
+ range_init_nofail(&validated_ranges[1], info->cpuid_addr, info->cpuid_len);
+ start = info->pre_validated_start;
+ end = info->pre_validated_end;
+
+ while (start < end) {
+ Range overlap_range;
+
+ /* Check if the requested range overlaps with Secrets and CPUID page */
+ if (detect_first_overlap(start, end, validated_ranges, 2,
+ &overlap_range)) {
+ if (start < range_lob(&overlap_range)) {
+ sz = range_lob(&overlap_range) - start;
+ if (sev_snp_launch_update_gpa(start, sz,
+ KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {
+ error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
+ start, sz);
+ exit(1);
+ }
+ }
+
+ start = range_upb(&overlap_range) + 1;
+ continue;
+ }
+
+ /* Validate the remaining range */
+ if (sev_snp_launch_update_gpa(start, end - start,
+ KVM_SEV_SNP_PAGE_TYPE_UNMEASURED)) {
+ error_report("SEV-SNP: failed to validate gpa 0x%x sz %d",
+ start, end - start);
+ exit(1);
+ }
+
+ start = end;
+ }
+}
+
+static void
+sev_snp_launch_finish(SevSnpGuestState *sev_snp)
+{
+ int ret, error;
+ Error *local_err = NULL;
+ struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf;
+
+ trace_kvm_sev_snp_launch_finish();
+ ret = sev_ioctl(SEV_COMMON(sev_snp)->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH,
finish, &error);
+ if (ret) {
+ error_report("%s: SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'",
+ __func__, ret, error, fw_error_to_str(error));
+ exit(1);
+ }
+
+ sev_set_guest_state(SEV_COMMON(sev_snp), SEV_STATE_RUNNING);
+
+ /* add migration blocker */
+ error_setg(&sev_mig_blocker,
+ "SEV: Migration is not implemented");
+ ret = migrate_add_blocker(sev_mig_blocker, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ error_free(sev_mig_blocker);
+ exit(1);
+ }
+}
+
+
static void
sev_launch_finish(SevGuestState *sev_guest)
{
@@ -1121,7 +1291,12 @@ sev_vm_state_change(void *opaque, bool running, RunState
state)
if (running) {
if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) {
- sev_launch_finish(SEV_GUEST(sev_common));
+ if (sev_snp_enabled()) {
+ snp_ovmf_boot_block_setup();
+ sev_snp_launch_finish(SEV_SNP_GUEST(sev_common));
+ } else {
+ sev_launch_finish(SEV_GUEST(sev_common));
+ }
}
}
}
@@ -1236,7 +1411,17 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error
**errp)
}
ram_block_notifier_add(&sev_ram_notifier);
- qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
+
+ /*
+ * The machine done notify event is used by the SEV guest to get the
+ * measurement of the encrypted images. When SEV-SNP is enabled then
+ * measurement is part of the attestation report and the measurement
+ * command does not exist. So skip registering the notifier.
+ */
+ if (!sev_snp_enabled()) {
+ qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
+ }
+
qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
cgs->ready = true;
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 0c2d250206..db91287439 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -13,3 +13,5 @@ kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t
secret, int len) "hpa
kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s
data %s"
kvm_sev_snp_launch_start(uint64_t policy) "policy 0x%" PRIx64
kvm_sev_snp_launch_update(void *addr, uint64_t len, int type) "addr %p len
0x%" PRIx64 " type %d"
+kvm_sev_snp_launch_finish(void) ""
+kvm_sev_snp_ovmf_boot_block_info(uint32_t secrets_gpa, uint32_t slen, uint32_t
cpuid_gpa, uint32_t clen, uint32_t s, uint32_t e) "secrets 0x%x+0x%x cpuid
0x%x+0x%x pre-validate 0x%x+0x%x"
--
2.25.1
- [RFC PATCH v2 00/12] Add AMD Secure Nested Paging (SEV-SNP) support, Michael Roth, 2021/08/26
- [RFC PATCH v2 11/12] i386/sev: sev-snp: add support for CPUID validation, Michael Roth, 2021/08/26
- [RFC PATCH v2 12/12] i386/sev: update query-sev QAPI format to handle SEV-SNP, Michael Roth, 2021/08/26
- [RFC PATCH v2 01/12] i386/sev: introduce "sev-common" type to encapsulate common SEV state, Michael Roth, 2021/08/26
- [RFC PATCH v2 02/12] linux-header: add the SNP specific command, Michael Roth, 2021/08/26
- [RFC PATCH v2 04/12] i386/sev: initialize SNP context, Michael Roth, 2021/08/26
- [RFC PATCH v2 05/12] i386/sev: add the SNP launch start context, Michael Roth, 2021/08/26
- [RFC PATCH v2 06/12] i386/sev: add support to encrypt BIOS when SEV-SNP is enabled, Michael Roth, 2021/08/26
- [RFC PATCH v2 03/12] i386/sev: introduce 'sev-snp-guest' object, Michael Roth, 2021/08/26
- [RFC PATCH v2 07/12] i386/sev: populate secrets and cpuid page and finalize the SNP launch,
Michael Roth <=
- [RFC PATCH v2 08/12] target/i386: set SEV-SNP CPUID bit when SNP enabled, Michael Roth, 2021/08/26
- [RFC PATCH v2 10/12] target/i386: add new EPYC CPU versions with updated cache_info, Michael Roth, 2021/08/26
- [RFC PATCH v2 09/12] target/i386: allow versioned CPUs to specify new cache_info, Michael Roth, 2021/08/26