From nobody Sat Nov 15 12:46:53 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1752492499; cv=none; d=zohomail.com; s=zohoarc; b=VQyP9e5cs+/0RZxzophinQVJ2YrxJu3XTCNGI8bwfFyOUPOR4rNRuerPokXr6Rfpt2hTPzm+5apfxJbBf6IUesiprHJ5pMks4E6msjnwoqCix6j7Hq0eEU+Wf5fyRRt1TPsnGV6j0EJcurIewoX6Y6viRbogSKOcFfwgkezvp3U= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752492499; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=EraEnuG15QUJZaSBHNd845dFPUl1vsqabBZ9Zpw8ues=; b=Zgs7VxrcbHWu6bQKS4tAa9Qua8n7D41wVZQRWf3lEwcO5cQI8bx9M8hrSkGdfkPCnX7bo09wZ+Tz2k63CpxSSfY6MlAfU0NBg3f0nj0mpBj4EUp/+R77kIwxXLqWrKb9Ec9L2e7QqJ7JYHwVLk9UReUudH9UEfYSie2udw4z7Rk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1752492499863458.0234701495665; Mon, 14 Jul 2025 04:28:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubHK0-00042x-9x; Mon, 14 Jul 2025 07:26:12 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ubGzs-00018M-AJ for qemu-devel@nongnu.org; Mon, 14 Jul 2025 07:05:29 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ubGzp-0001sn-Cn for qemu-devel@nongnu.org; Mon, 14 Jul 2025 07:05:23 -0400 Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-3-SlM8aBGaNxmpua3oogU2HQ-1; Mon, 14 Jul 2025 07:05:19 -0400 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-3a579058758so1669246f8f.1 for ; Mon, 14 Jul 2025 04:05:18 -0700 (PDT) Received: from [192.168.10.48] ([151.49.73.155]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3b5e8e14d07sm12278723f8f.66.2025.07.14.04.05.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 14 Jul 2025 04:05:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752491120; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EraEnuG15QUJZaSBHNd845dFPUl1vsqabBZ9Zpw8ues=; b=Fe91064qmNJvCwxkR0q8YRA99KXzxz8NDOM56E/CWwWl85UMlU+0naCB9WULc0+C++7FxS mS96ssiYEiAev+423OIgwz7aWbTLoxE96Fn1dYlLw/80xUIJj3qgyj9fh6y84pPnkmBj7e j9Gm9C9UXeAECNhUjlNVjWAvvbPoACQ= X-MC-Unique: SlM8aBGaNxmpua3oogU2HQ-1 X-Mimecast-MFC-AGG-ID: SlM8aBGaNxmpua3oogU2HQ_1752491118 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752491116; x=1753095916; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=EraEnuG15QUJZaSBHNd845dFPUl1vsqabBZ9Zpw8ues=; b=DzG8SKNd43ccbyeBQJe1KL2H415VSKQ3/xJavsEs6c7T4iFCVjt6xiHmzYi3N5fQ+u 48CIh9jJq2g/t8+9aLO56Bu3Lbet50Y0KI6yHoyogAFIjACHeHFhqb1WpMVb98+J8DNM DzNaV4rS4YJW/dCNkOwRKwU7/hqnwnhvZL1L64qHaKr/y4bjFDcBc/0VmepMauThhsaB xsd4uGDpT3EqYqMZA3Bc8Pa/tjpatSgr41vTqyZ6SyeN8/81oDdSDLlfwjHGuCBwDrZm 5bxzPW/eiT3AWpXk8Kk+YDvlPKM4nkSG70c0lCWWrErwJlgSZM77XDr4ONFfss5wJAzH UfCw== X-Gm-Message-State: AOJu0YyN7YaaERz9iZMv//R4Qyibhd/XJHxpclT26KKzuc4bcg0XWnyr mpoGWIG0xH38BL8ueSR+xjoElH1dQi8kM5tUovQrgIMEtZK1emNipxWHgrmbDAVn4y3XR8Vpnxp Q5owWRAGpX97bfBq5Qs/7cGtlM5TP5q4w4k97fqfnpQ4oi7wR0TXZa5xKTc4tTXUgeUPFqu/T+K kSqypIhGrCieLMsyiacGCRzheXC4hNbLNlb3M9S/kE X-Gm-Gg: ASbGncuLNwlt64qgSDzFO15WTRcCF0B9eNvi5Wr8bFY/7zMzHbGoVcxP/PeVfqM3nn2 qhXQdJ4/oj0cjFW37Eq6eQ0WwePTx795D1aUG7QxPGzFbutZBwsU02Zlctivb4WJzjRiJQf5YWT 7UO4tjLCirIlgCdy55YI2p7KNMNIiC3wJej5yJCpHXxA1pKEU4MwawXL6uCOAoA2O4iF6MoSr6+ +C+3av1F96mOWglWBP0KuABIFU2jc4jBN82EWTUJi6iCjPVhY0ojR8Za7+Nphhud6FrYpBSSXvc 2wgzJ2ajJjGf8TueDonCC9SgzLeEFbuRBuu67v3P2PI= X-Received: by 2002:a05:6000:25fc:b0:3a5:8a09:70b7 with SMTP id ffacd0b85a97d-3b5f18dc449mr11467187f8f.38.1752491116191; Mon, 14 Jul 2025 04:05:16 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHWTPSlS+/tu2naGURDAVma1g4jFnsUJ01SV0iQPtZyWrN6+CI0BvMjBIBx9Tt5Wk3HDgjUrw== X-Received: by 2002:a05:6000:25fc:b0:3a5:8a09:70b7 with SMTP id ffacd0b85a97d-3b5f18dc449mr11467151f8f.38.1752491115612; Mon, 14 Jul 2025 04:05:15 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Roy Hopkins , "Michael S. Tsirkin" , Stefano Garzarella , Gerd Hoffman , Ani Sinha Subject: [PULL 25/77] i386/sev: Implement ConfidentialGuestSupport functions for SEV Date: Mon, 14 Jul 2025 13:03:14 +0200 Message-ID: <20250714110406.117772-26-pbonzini@redhat.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250714110406.117772-1-pbonzini@redhat.com> References: <20250714110406.117772-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1752492501742116600 Content-Type: text/plain; charset="utf-8" From: Roy Hopkins The ConfidentialGuestSupport object defines a number of virtual functions that are called during processing of IGVM directives to query or configure initial guest state. In order to support processing of IGVM files, these functions need to be implemented by relevant isolation hardware support code such as SEV. This commit implements the required functions for SEV-ES and adds support for processing IGVM files for configuring the guest. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/7145835f729e6195f2fbda308aa90e089a96ae6e.17= 51554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/sev.h | 2 + target/i386/sev.c | 254 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 246 insertions(+), 10 deletions(-) diff --git a/target/i386/sev.h b/target/i386/sev.h index 38caa849f5e..d2eb06db321 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -44,6 +44,8 @@ bool sev_snp_enabled(void); #define SEV_SNP_POLICY_SMT 0x10000 #define SEV_SNP_POLICY_DBG 0x80000 =20 +#define SVM_SEV_FEAT_SNP_ACTIVE 1 + typedef struct SevKernelLoaderContext { char *setup_data; size_t setup_size; diff --git a/target/i386/sev.c b/target/i386/sev.c index a13f91e615d..1296f4feb62 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -41,7 +41,9 @@ #include "confidential-guest.h" #include "hw/i386/pc.h" #include "system/address-spaces.h" +#include "hw/i386/e820_memory_layout.h" #include "qemu/queue.h" +#include "qemu/cutils.h" =20 OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) @@ -50,6 +52,9 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass= , SEV_SNP_GUEST) /* hard code sha256 digest size */ #define HASH_SIZE 32 =20 +/* Hard coded GPA that KVM uses for the VMSA */ +#define KVM_VMSA_GPA 0xFFFFFFFFF000 + /* Convert between SEV-ES VMSA and SegmentCache flags/attributes */ #define FLAGS_VMSA_TO_SEGCACHE(flags) \ ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8)) @@ -480,6 +485,103 @@ static void sev_apply_cpu_context(CPUState *cpu) } } =20 +static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area = *vmsa, + Error **errp) +{ + struct sev_es_save_area vmsa_check; + + /* + * KVM always populates the VMSA at a fixed GPA which cannot be modifi= ed + * from userspace. Specifying a different GPA will not prevent the gue= st + * from starting but will cause the launch measurement to be different + * from expected. Therefore check that the provided GPA matches the KVM + * hardcoded value. + */ + if (gpa !=3D KVM_VMSA_GPA) { + error_setg(errp, + "%s: The VMSA GPA must be %lX but is specified as %lX", + __func__, KVM_VMSA_GPA, gpa); + return -1; + } + + /* + * Clear all supported fields so we can then check the entire structure + * is zero. + */ + memcpy(&vmsa_check, vmsa, sizeof(struct sev_es_save_area)); + memset(&vmsa_check.es, 0, sizeof(vmsa_check.es)); + memset(&vmsa_check.cs, 0, sizeof(vmsa_check.cs)); + memset(&vmsa_check.ss, 0, sizeof(vmsa_check.ss)); + 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)); + 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 =3D 0; + vmsa_check.cr0 =3D 0; + vmsa_check.cr3 =3D 0; + vmsa_check.cr4 =3D 0; + vmsa_check.xcr0 =3D 0; + vmsa_check.dr6 =3D 0; + vmsa_check.dr7 =3D 0; + vmsa_check.rax =3D 0; + vmsa_check.rcx =3D 0; + vmsa_check.rdx =3D 0; + vmsa_check.rbx =3D 0; + vmsa_check.rsp =3D 0; + vmsa_check.rbp =3D 0; + vmsa_check.rsi =3D 0; + vmsa_check.rdi =3D 0; + vmsa_check.r8 =3D 0; + vmsa_check.r9 =3D 0; + vmsa_check.r10 =3D 0; + vmsa_check.r11 =3D 0; + vmsa_check.r12 =3D 0; + vmsa_check.r13 =3D 0; + vmsa_check.r14 =3D 0; + vmsa_check.r15 =3D 0; + vmsa_check.rip =3D 0; + vmsa_check.rflags =3D 0; + + vmsa_check.g_pat =3D 0; + vmsa_check.xcr0 =3D 0; + + vmsa_check.x87_fcw =3D 0; + vmsa_check.mxcsr =3D 0; + + if (sev_snp_enabled()) { + if (vmsa_check.sev_features !=3D SVM_SEV_FEAT_SNP_ACTIVE) { + error_setg(errp, + "%s: sev_features in the VMSA contains an unsupport= ed " + "value. For SEV-SNP, sev_features must be set to %x= .", + __func__, SVM_SEV_FEAT_SNP_ACTIVE); + return -1; + } + vmsa_check.sev_features =3D 0; + } else { + if (vmsa_check.sev_features !=3D 0) { + error_setg(errp, + "%s: sev_features in the VMSA contains an unsupport= ed " + "value. For SEV-ES and SEV, sev_features must be " + "set to 0.", __func__); + return -1; + } + } + + if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { + error_setg(errp, + "%s: The VMSA contains fields that are not " + "synchronized with KVM. Continuing would result in " + "either unpredictable guest behavior, or a " + "mismatched launch measurement.", + __func__); + return -1; + } + return 0; +} + static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx, uint32_t ctx_len, hwaddr gpa, Error **errp) { @@ -1491,18 +1593,26 @@ sev_snp_launch_finish(SevCommonState *sev_common) struct kvm_sev_snp_launch_finish *finish =3D &sev_snp->kvm_finish_conf; =20 /* - * To boot the SNP guest, the hypervisor is required to populate the C= PUID - * and Secrets page before finalizing the launch flow. The location of - * the secrets and CPUID page is available through the OVMF metadata G= UID. + * Populate all the metadata pages if not using an IGVM file. In the c= ase + * where an IGVM file is provided it will be used to configure the met= adata + * pages directly. */ - metadata =3D pc_system_get_ovmf_sev_metadata_ptr(); - if (metadata =3D=3D NULL) { - error_report("%s: Failed to locate SEV metadata header", __func__); - exit(1); - } + if (!X86_MACHINE(qdev_get_machine())->igvm) { + /* + * To boot the SNP guest, the hypervisor is required to populate t= he + * CPUID and Secrets page before finalizing the launch flow. The + * location of the secrets and CPUID page is available through the + * OVMF metadata GUID. + */ + metadata =3D pc_system_get_ovmf_sev_metadata_ptr(); + if (metadata =3D=3D NULL) { + error_report("%s: Failed to locate SEV metadata header", __fun= c__); + exit(1); + } =20 - /* Populate all the metadata pages */ - snp_populate_metadata_pages(sev_snp, metadata); + /* Populate all the metadata pages */ + snp_populate_metadata_pages(sev_snp, metadata); + } =20 QTAILQ_FOREACH(data, &launch_update, next) { ret =3D sev_snp_launch_update(sev_snp, data); @@ -2290,6 +2400,124 @@ static void sev_common_set_kernel_hashes(Object *ob= j, bool value, Error **errp) SEV_COMMON(obj)->kernel_hashes =3D value; } =20 +static bool cgs_check_support(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vt= l, + uint64_t shared_gpa_boundary) +{ + return (((platform =3D=3D CGS_PLATFORM_SEV_SNP) && sev_snp_enabled()) = || + ((platform =3D=3D CGS_PLATFORM_SEV_ES) && sev_es_enabled()) || + ((platform =3D=3D CGS_PLATFORM_SEV) && sev_enabled())); +} + +static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp) +{ + SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); + SevCommonStateClass *klass =3D SEV_COMMON_GET_CLASS(sev_common); + + if (!sev_enabled()) { + error_setg(errp, "%s: attempt to configure guest memory, but SEV " + "is not enabled", __func__); + return -1; + } + + switch (memory_type) { + case CGS_PAGE_TYPE_NORMAL: + case CGS_PAGE_TYPE_ZERO: + return klass->launch_update_data(sev_common, gpa, ptr, len, errp); + + case CGS_PAGE_TYPE_VMSA: + if (!sev_es_enabled()) { + error_setg(errp, + "%s: attempt to configure initial VMSA, but SEV-ES " + "is not supported", + __func__); + return -1; + } + if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr, + errp) < 0) { + return -1; + } + return sev_set_cpu_context(cpu_index, ptr, len, gpa, errp); + + case CGS_PAGE_TYPE_UNMEASURED: + if (sev_snp_enabled()) { + return snp_launch_update_data( + gpa, ptr, len, KVM_SEV_SNP_PAGE_TYPE_UNMEASURED, errp); + } + /* No action required if not SEV-SNP */ + return 0; + + case CGS_PAGE_TYPE_SECRETS: + if (!sev_snp_enabled()) { + error_setg(errp, + "%s: attempt to configure secrets page, but SEV-SNP= " + "is not supported", + __func__); + return -1; + } + return snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_SECRETS, errp); + + case CGS_PAGE_TYPE_REQUIRED_MEMORY: + if (kvm_convert_memory(gpa, len, true) < 0) { + error_setg( + errp, + "%s: failed to configure required memory. gpa: %lX, type: = %d", + __func__, gpa, memory_type); + return -1; + } + return 0; + + case CGS_PAGE_TYPE_CPUID: + if (!sev_snp_enabled()) { + error_setg(errp, + "%s: attempt to configure CPUID page, but SEV-SNP " + "is not supported", + __func__); + return -1; + } + return snp_launch_update_cpuid(gpa, ptr, len, errp); + } + error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d", __f= unc__, + gpa, memory_type); + return -1; +} + +static int cgs_get_mem_map_entry(int index, + ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + struct e820_entry *table; + int num_entries; + + num_entries =3D e820_get_table(&table); + if ((index < 0) || (index >=3D num_entries)) { + return 1; + } + entry->gpa =3D table[index].address; + entry->size =3D table[index].length; + switch (table[index].type) { + case E820_RAM: + entry->type =3D CGS_MEM_RAM; + break; + case E820_RESERVED: + entry->type =3D CGS_MEM_RESERVED; + break; + case E820_ACPI: + entry->type =3D CGS_MEM_ACPI; + break; + case E820_NVS: + entry->type =3D CGS_MEM_NVS; + break; + case E820_UNUSABLE: + entry->type =3D CGS_MEM_UNUSABLE; + break; + } + return 0; +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2313,6 +2541,8 @@ static void sev_common_instance_init(Object *obj) { SevCommonState *sev_common =3D SEV_COMMON(obj); + ConfidentialGuestSupportClass *cgs =3D + CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(obj); =20 sev_common->kvm_type =3D -1; =20 @@ -2323,6 +2553,10 @@ sev_common_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "reduced-phys-bits", &sev_common->reduced_phys_bits, OBJ_PROP_FLAG_READWRITE); + cgs->check_support =3D cgs_check_support; + cgs->set_guest_state =3D cgs_set_guest_state; + cgs->get_mem_map_entry =3D cgs_get_mem_map_entry; + QTAILQ_INIT(&sev_common->launch_vmsa); } =20 --=20 2.50.0