From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749822161; cv=none; d=zohomail.com; s=zohoarc; b=nkMh+AGwhypHtAwx6kjSjQlE9h2thbL16DoR3RzAnnw0oY4/Qa6Ozo6av0Rw/9k9yQKGRRIJ/gWrXv6w5UgWB+7VoVuUrwRfbE0mflSNTer5XBRpLNAYVr8ANEZfHMzzO5V8kCr+KjLzFocPk/KLmRXyVEpMMGvC1oTVefSkwyI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749822161; h=Content-Type: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=vgd/YRpkkNuHZnvBAYwXe918JhXbWCMlbXb89sQYDSg=; b=LYeNH4U5qNaV8q9FS4MIRn8dVgto6WO1p8hRb4JowuKMV+i6v7AuWrcj5XumqMaEgEq9lB5plhB5kQTsMN0tVLxIHoQK4w2yCJ1swZUfZo9p2OrqkeBTa5OX5+WPHi3lNIMH135PpAJiqanWk+7CZvROhG5IFAGY/ZtxEgsCH8g= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749822161441249.76417186840263; Fri, 13 Jun 2025 06:42:41 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ4fi-0001hK-LE; Fri, 13 Jun 2025 09:42:20 -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 1uQ4fL-0001bW-RB for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:42:03 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ4fJ-0006CX-Q2 for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:41:55 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.81]) by dkim.livemail.co.uk (Postfix) with ESMTPS id A5CB5401DA; Fri, 13 Jun 2025 14:41:49 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 1E325400DC; Fri, 13 Jun 2025 14:41:44 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749822109; bh=ab+3xdR1UyLAFwZmY/oF9kz1Xi2vy1PNKbZA5izqDbo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZtL55F4o2c5l+u83HJ4LaUcKyRzWTMMJj6tAd4TWV2DuvrFtQoyveMRHUfFRMDbZS NgpbrCDixQf1aw5j7dt/7lfjamTlJoVj2LVcXDFA6v8aJR/LhUC4cQP/wpUlEnLYd+ v9AVx9T3dzX0R8lqTwKG8U19whEx0TZ1QUNStmh0= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 01/16] meson: Add optional dependency on IGVM library Date: Fri, 13 Jun 2025 14:41:17 +0100 Message-ID: <18ca3c22494971f9bbd892592e97d2be8b31dd03.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749822163438116600 The IGVM library allows Independent Guest Virtual Machine files to be parsed and processed. IGVM files are used to configure guest memory layout, initial processor state and other configuration pertaining to secure virtual machines. This adds the --enable-igvm configure option, enabled by default, which attempts to locate and link against the IGVM library via pkgconfig and sets CONFIG_IGVM if found. The library is added to the system_ss target in backends/meson.build where the IGVM parsing will be performed by the ConfidentialGuestSupport object. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Daniel P. Berrang=C3=A9 Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- backends/meson.build | 3 +++ meson.build | 8 ++++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 4 files changed, 16 insertions(+) diff --git a/backends/meson.build b/backends/meson.build index 9b88d22685..ac0fac7845 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -34,6 +34,9 @@ if have_vhost_user_crypto endif system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) +if igvm.found() + system_ss.add(igvm) +endif =20 system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) =20 diff --git a/meson.build b/meson.build index 34729c2a3d..c4b620be42 100644 --- a/meson.build +++ b/meson.build @@ -1427,6 +1427,12 @@ if host_os =3D=3D 'linux' and (have_system or have_t= ools) method: 'pkg-config', required: get_option('libudev')) endif +igvm =3D not_found +if not get_option('igvm').auto() or have_system + igvm =3D dependency('igvm', version: '>=3D 0.3.0', + method: 'pkg-config', + required: get_option('igvm')) +endif =20 mpathlibs =3D [libudev] mpathpersist =3D not_found @@ -2604,6 +2610,7 @@ config_host_data.set('CONFIG_CFI', get_option('cfi')) config_host_data.set('CONFIG_SELINUX', selinux.found()) config_host_data.set('CONFIG_XEN_BACKEND', xen.found()) config_host_data.set('CONFIG_LIBDW', libdw.found()) +config_host_data.set('CONFIG_IGVM', igvm.found()) if xen.found() # protect from xen.version() having less than three components xen_version =3D xen.version().split('.') + ['0', '0'] @@ -5004,6 +5011,7 @@ summary_info +=3D {'seccomp support': seccomp} summary_info +=3D {'GlusterFS support': glusterfs} summary_info +=3D {'hv-balloon support': hv_balloon} summary_info +=3D {'TPM support': have_tpm} +summary_info +=3D {'IGVM support': igvm} summary_info +=3D {'libssh support': libssh} summary_info +=3D {'lzo support': lzo} summary_info +=3D {'snappy support': snappy} diff --git a/meson_options.txt b/meson_options.txt index a442be2995..1e429311a2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -117,6 +117,8 @@ option('tpm', type : 'feature', value : 'auto', description: 'TPM support') option('valgrind', type : 'feature', value: 'auto', description: 'valgrind debug support for coroutine stacks') +option('igvm', type: 'feature', value: 'auto', + description: 'Independent Guest Virtual Machine (IGVM) file support= ') =20 # Do not enable it by default even for Mingw32, because it doesn't # work on Wine. diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 73e0770f42..7851540445 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -130,6 +130,7 @@ meson_options_help() { printf "%s\n" ' hv-balloon hv-balloon driver (requires Glib 2.68+ = GTree API)' printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' + printf "%s\n" ' igvm IGVM file support' printf "%s\n" ' jack JACK sound support' printf "%s\n" ' keyring Linux keyring support' printf "%s\n" ' kvm KVM acceleration support' @@ -346,6 +347,8 @@ _meson_option_parse() { --iasl=3D*) quote_sh "-Diasl=3D$2" ;; --enable-iconv) printf "%s" -Diconv=3Denabled ;; --disable-iconv) printf "%s" -Diconv=3Ddisabled ;; + --enable-igvm) printf "%s" -Digvm=3Denabled ;; + --disable-igvm) printf "%s" -Digvm=3Ddisabled ;; --includedir=3D*) quote_sh "-Dincludedir=3D$2" ;; --enable-install-blobs) printf "%s" -Dinstall_blobs=3Dtrue ;; --disable-install-blobs) printf "%s" -Dinstall_blobs=3Dfalse ;; --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749822163; cv=none; d=zohomail.com; s=zohoarc; b=CVTD5aXt+9s7Q6IrUKEPdZ08DtDc7b+pfjPtoQSYd35dBa4Bul/ZDgXTO8LFzv9e8KCqPmDsqPf8U7Jtwb32Qa/2KvOISwA4ZDMlDf218paJbEhtFWv0VTXR1d5BL4uLSvEpHIGTxjO0/KY7KD3jMvrohNh7dRlj6dQ+suLEyrM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749822163; 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=UPVt4KcnMxNENXlZr10gdKmRCLW3FwoO/tvSgfYmQP4=; b=ais2u2m51KBXY8E1UgzUyRjtGGcQjFO3Lnd1E52NgQ/jGOX8udGQhHA2ZUsZlu3mAEuETHhLbRV/nigQO6SQ63qf0389KrWZ02XIKipY38PJsml6o9X2ptmr0oX+HHXHh7Fcsc3VTtyVIMMVjITKZ6KDpbfdC/tJQzSD24BbLok= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749822163738618.2432843616689; Fri, 13 Jun 2025 06:42:43 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ4fu-0001if-9h; Fri, 13 Jun 2025 09:42:32 -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 1uQ4ff-0001g5-2J for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:42:15 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ4fc-0006FX-Tu for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:42:14 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.82]) by dkim.livemail.co.uk (Postfix) with ESMTPS id E0E991800EC; Fri, 13 Jun 2025 14:42:10 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 63BC8C03ED; Fri, 13 Jun 2025 14:42:06 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749822130; bh=8WDayUXOUYSKXNknQzubyvbhxavKEHBHznDRF5Thtfk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FCkU9O7bHYaPGLap3LPSEayv136lWz6AxpRpdHxXQBtuB4ZlF9TnWDTTKGeixmJJk PaIDuGu3vSeMioJcdsIUCQ6wQp9x2JW6jpqdVnnMy4zxKsnyZhaQR2dDRoGKweZBW9 kacFiZTHS+XdbRFKFdnHh696cs/0aNW9TOB2HLPQ= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 02/16] backends/confidential-guest-support: Add functions to support IGVM Date: Fri, 13 Jun 2025 14:41:18 +0100 Message-ID: <70b35ab980311273eb8cc9c19386363072851ea1.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749822165681116600 Content-Type: text/plain; charset="utf-8" In preparation for supporting the processing of IGVM files to configure guests, this adds a set of functions to ConfidentialGuestSupport allowing configuration of secure virtual machines that can be implemented for each supported isolation platform type such as Intel TDX or AMD SEV-SNP. These functions will be called by IGVM processing code in subsequent patches. This commit provides a default implementation of the functions that either perform no action or generate an error when they are called. Targets that support ConfidentalGuestSupport should override these implementations. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- backends/confidential-guest-support.c | 31 ++++++++++ include/system/confidential-guest-support.h | 67 +++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/backends/confidential-guest-support.c b/backends/confidential-= guest-support.c index 8ff7bfa857..c5bef1fbfa 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -14,15 +14,46 @@ #include "qemu/osdep.h" =20 #include "system/confidential-guest-support.h" +#include "qapi/error.h" =20 OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, confidential_guest_support, CONFIDENTIAL_GUEST_SUPPORT, OBJECT) =20 +static bool check_support(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary) +{ + /* Default: no support. */ + return false; +} + +static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp) +{ + error_setg(errp, + "Setting confidential guest state is not supported for this= platform"); + return -1; +} + +static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *e= ntry, + Error **errp) +{ + error_setg( + errp, + "Obtaining the confidential guest memory map is not supported for = this platform"); + return -1; +} + static void confidential_guest_support_class_init(ObjectClass *oc, const void *data) { + ConfidentialGuestSupportClass *cgsc =3D CONFIDENTIAL_GUEST_SUPPORT_CLA= SS(oc); + cgsc->check_support =3D check_support; + cgsc->set_guest_state =3D set_guest_state; + cgsc->get_mem_map_entry =3D get_mem_map_entry; } =20 static void confidential_guest_support_init(Object *obj) diff --git a/include/system/confidential-guest-support.h b/include/system/c= onfidential-guest-support.h index ea46b50c56..79ecd21f42 100644 --- a/include/system/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -19,6 +19,7 @@ #define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H =20 #include "qom/object.h" +#include "exec/hwaddr.h" =20 #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, @@ -26,6 +27,36 @@ OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) =20 =20 +typedef enum ConfidentialGuestPlatformType { + CGS_PLATFORM_SEV, + CGS_PLATFORM_SEV_ES, + CGS_PLATFORM_SEV_SNP, +} ConfidentialGuestPlatformType; + +typedef enum ConfidentialGuestMemoryType { + CGS_MEM_RAM, + CGS_MEM_RESERVED, + CGS_MEM_ACPI, + CGS_MEM_NVS, + CGS_MEM_UNUSABLE, +} ConfidentialGuestMemoryType; + +typedef struct ConfidentialGuestMemoryMapEntry { + uint64_t gpa; + uint64_t size; + ConfidentialGuestMemoryType type; +} ConfidentialGuestMemoryMapEntry; + +typedef enum ConfidentialGuestPageType { + CGS_PAGE_TYPE_NORMAL, + CGS_PAGE_TYPE_VMSA, + CGS_PAGE_TYPE_ZERO, + CGS_PAGE_TYPE_UNMEASURED, + CGS_PAGE_TYPE_SECRETS, + CGS_PAGE_TYPE_CPUID, + CGS_PAGE_TYPE_REQUIRED_MEMORY, +} ConfidentialGuestPageType; + struct ConfidentialGuestSupport { Object parent; =20 @@ -64,6 +95,42 @@ typedef struct ConfidentialGuestSupportClass { =20 int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); int (*kvm_reset)(ConfidentialGuestSupport *cgs, Error **errp); + + /* + * Check to see if this confidential guest supports a particular + * platform or configuration. + * + * Return true if supported or false if not supported. + */ + bool (*check_support)(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary); + + /* + * Configure part of the state of a guest for a particular set of data= , page + * type and gpa. This can be used for example to pre-populate and meas= ure + * guest memory contents, define private ranges or set the initial CPU= state + * for one or more CPUs. + * + * If memory_type is CGS_PAGE_TYPE_VMSA then ptr points to the initial= CPU + * context for a virtual CPU. The format of the data depends on the ty= pe of + * confidential virtual machine. For example, for SEV-ES ptr will poin= t to a + * vmcb_save_area structure that should be copied into guest memory at= the + * address specified in gpa. The cpu_index parameter contains the inde= x of + * the CPU the VMSA applies to. + */ + int (*set_guest_state)(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp); + + /* + * Iterate the system memory map, getting the entry with the given ind= ex + * that can be populated into guest memory. + * + * Returns 0 for ok, 1 if the index is out of range and -1 on error. + */ + int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *e= ntry, + Error **errp); } ConfidentialGuestSupportClass; =20 static inline int confidential_guest_kvm_init(ConfidentialGuestSupport *cg= s, --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749822905; cv=none; d=zohomail.com; s=zohoarc; b=b3zZWrLCbLPRleMkrfmcDAEgkKmG3SIMDKEeBK8W8ottVkj9ciTDN1TTowmmBTPBjTXxz2XW83OT6jG3QJPk3domtGT7T4y5wuaPFlLWZVs8iMngEH2ssmISscXWWFIvaAo+iP5twsuBW0x8nbqd29y44Qyb344ubyM9ApnLVxc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749822905; 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=xijmFEuGoT62BJTbEu/sBsiQFOPA8L7p8XwM/yKBcEQ=; b=HGjoP7mcurv6wLxhBftNOjkTwoiyXsuM7Uo/Dq975G38aWWuhj3wuGh2ayWxW1lkB/iU4L/glfDe2uezlusbQPw0z1wvKrpl7QjeclrOsehY11bIe3e/oD6Zq8DtrjdQtRXO908iUoCr7OEhM7CCBoOqgIuI2WqA3RxD1ohOLhI= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 174982290569029.955484810331313; Fri, 13 Jun 2025 06:55:05 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ4r4-0007x5-FE; Fri, 13 Jun 2025 09:54:02 -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 1uQ4qy-0007wk-Ce for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:53:56 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ4qs-0007VU-9i for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:53:55 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.84]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 7D1A840025; Fri, 13 Jun 2025 14:53:47 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id E8C15A022B; Fri, 13 Jun 2025 14:53:42 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749822827; bh=T1F4PkCZc9hM269QFlUAsS4pAdaE4fkVc8PPMV67P6o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fm+C/QeZi6SteqUN9yX6gtRNwvxvmM2Pe7Zp1w2ocAueZ+hyvP6PbYTFzdHYxB5hA ZFcOD81ZKWYQfbHJD1rIZMefAOE4xcYpyvZBWuJJu3GIcA+xnuSPxgR8CqnSaOlvPV uP6o5Ymg/UM/DV5onAQvDTv1jqlcZzMIW8ZljSi4= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 03/16] backends/igvm: Add IGVM loader and configuration Date: Fri, 13 Jun 2025 14:53:18 +0100 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749822906658116600 Content-Type: text/plain; charset="utf-8" Adds an IGVM loader to QEMU which processes a given IGVM file and applies the directives within the file to the current guest configuration. The IGVM loader can be used to configure both confidential and non-confidential guests. For confidential guests, the ConfidentialGuestSupport object for the system is used to encrypt memory, apply the initial CPU state and perform other confidential guest operations. The loader is configured via a new IgvmCfg QOM object which allows the user to provide a path to the IGVM file to process. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- backends/igvm-cfg.c | 51 +++ backends/igvm.c | 807 ++++++++++++++++++++++++++++++++++++++ backends/igvm.h | 22 ++ backends/meson.build | 2 + include/system/igvm-cfg.h | 46 +++ qapi/qom.json | 17 + 6 files changed, 945 insertions(+) create mode 100644 backends/igvm-cfg.c create mode 100644 backends/igvm.c create mode 100644 backends/igvm.h create mode 100644 include/system/igvm-cfg.h diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c new file mode 100644 index 0000000000..45df63e06c --- /dev/null +++ b/backends/igvm-cfg.c @@ -0,0 +1,51 @@ +/* + * QEMU IGVM interface + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "system/igvm-cfg.h" +#include "igvm.h" +#include "qom/object_interfaces.h" + +static char *get_igvm(Object *obj, Error **errp) +{ + IgvmCfg *igvm =3D IGVM_CFG(obj); + return g_strdup(igvm->filename); +} + +static void set_igvm(Object *obj, const char *value, Error **errp) +{ + IgvmCfg *igvm =3D IGVM_CFG(obj); + g_free(igvm->filename); + igvm->filename =3D g_strdup(value); +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(IgvmCfg, igvm_cfg, IGVM_CFG, OBJECT, + { TYPE_USER_CREATABLE }, { NULL }) + +static void igvm_cfg_class_init(ObjectClass *oc, const void *data) +{ + IgvmCfgClass *igvmc =3D IGVM_CFG_CLASS(oc); + + object_class_property_add_str(oc, "file", get_igvm, set_igvm); + object_class_property_set_description(oc, "file", + "Set the IGVM filename to use"); + + igvmc->process =3D qigvm_process_file; +} + +static void igvm_cfg_init(Object *obj) +{ +} + +static void igvm_cfg_finalize(Object *obj) +{ +} diff --git a/backends/igvm.c b/backends/igvm.c new file mode 100644 index 0000000000..2a31021d44 --- /dev/null +++ b/backends/igvm.c @@ -0,0 +1,807 @@ +/* + * QEMU IGVM configuration backend for guests + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "igvm.h" +#include "qapi/error.h" +#include "system/memory.h" +#include "system/address-spaces.h" +#include "hw/core/cpu.h" + +#include +#include + +typedef struct QIgvmParameterData { + QTAILQ_ENTRY(QIgvmParameterData) next; + uint8_t *data; + uint32_t size; + uint32_t index; +} QIgvmParameterData; + +/* + * QIgvm contains the information required during processing + * of a single IGVM file. + */ +typedef struct QIgvm { + IgvmHandle file; + ConfidentialGuestSupport *cgs; + ConfidentialGuestSupportClass *cgsc; + uint32_t compatibility_mask; + unsigned current_header_index; + QTAILQ_HEAD(, QIgvmParameterData) parameter_data; + + /* These variables keep track of contiguous page regions */ + IGVM_VHS_PAGE_DATA region_prev_page_data; + uint64_t region_start; + unsigned region_start_index; + unsigned region_last_index; + unsigned region_page_count; +} QIgvm; + +static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_dat= a, + Error **errp); +static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_da= ta, + Error **errp); +static int qigvm_directive_parameter_area(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_parameter_insert(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_da= ta, + Error **errp); +static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_environment_info(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_required_memory(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); + +struct QIGVMHandler { + uint32_t type; + uint32_t section; + int (*handler)(QIgvm *ctx, const uint8_t *header_data, Error **errp); +}; + +static struct QIGVMHandler handlers[] =3D { + { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_page_data }, + { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_vp_context }, + { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_parameter_area }, + { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_parameter_insert }, + { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_memory_map }, + { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_vp_count }, + { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_environment_info }, + { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_required_memory }, +}; + +static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp) +{ + size_t handler; + IgvmHandle header_handle; + const uint8_t *header_data; + int result; + + for (handler =3D 0; handler < G_N_ELEMENTS(handlers); handler++) { + if (handlers[handler].type !=3D type) { + continue; + } + header_handle =3D igvm_get_header(ctx->file, handlers[handler].sec= tion, + ctx->current_header_index); + if (header_handle < 0) { + error_setg( + errp, + "IGVM file is invalid: Failed to read directive header (co= de: %d)", + (int)header_handle); + return -1; + } + header_data =3D igvm_get_buffer(ctx->file, header_handle) + + sizeof(IGVM_VHS_VARIABLE_HEADER); + result =3D handlers[handler].handler(ctx, header_data, errp); + igvm_free_buffer(ctx->file, header_handle); + return result; + } + error_setg(errp, + "IGVM: Unknown header type encountered when processing file= : " + "(type 0x%X)", + type); + return -1; +} + +static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, + int region_identifier, Error **errp) +{ + ERRP_GUARD(); + MemoryRegion *igvm_pages =3D NULL; + Int128 gpa_region_size; + MemoryRegionSection mrs =3D + memory_region_find(get_system_memory(), addr, size); + if (mrs.mr) { + if (!memory_region_is_ram(mrs.mr)) { + memory_region_unref(mrs.mr); + error_setg( + errp, + "Processing of IGVM file failed: Could not prepare memory " + "at address 0x%lX due to existing non-RAM region", + addr); + return NULL; + } + + gpa_region_size =3D int128_make64(size); + if (int128_lt(mrs.size, gpa_region_size)) { + memory_region_unref(mrs.mr); + error_setg( + errp, + "Processing of IGVM file failed: Could not prepare memory " + "at address 0x%lX: region size exceeded", + addr); + return NULL; + } + return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_regio= n); + } else { + /* + * The region_identifier is the is the index of the IGVM directive= that + * contains the page with the lowest GPA in the region. This will + * generate a unique region name. + */ + g_autofree char *region_name =3D + g_strdup_printf("igvm.%X", region_identifier); + igvm_pages =3D g_new0(MemoryRegion, 1); + if (ctx->cgs && ctx->cgs->require_guest_memfd) { + if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL, + region_name, size, err= p)) { + return NULL; + } + } else { + if (!memory_region_init_ram(igvm_pages, NULL, region_name, siz= e, + errp)) { + return NULL; + } + } + memory_region_add_subregion(get_system_memory(), addr, igvm_pages); + return memory_region_get_ram_ptr(igvm_pages); + } +} + +static int qigvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmea= sured, + bool zero) +{ + switch (memory_type) { + case IGVM_PAGE_DATA_TYPE_NORMAL: { + if (unmeasured) { + return CGS_PAGE_TYPE_UNMEASURED; + } else { + return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL; + } + } + case IGVM_PAGE_DATA_TYPE_SECRETS: + return CGS_PAGE_TYPE_SECRETS; + case IGVM_PAGE_DATA_TYPE_CPUID_DATA: + return CGS_PAGE_TYPE_CPUID; + case IGVM_PAGE_DATA_TYPE_CPUID_XF: + return CGS_PAGE_TYPE_CPUID; + default: + return -1; + } +} + +static bool qigvm_page_attrs_equal(IgvmHandle igvm, unsigned header_index, + const IGVM_VHS_PAGE_DATA *page_1, + const IGVM_VHS_PAGE_DATA *page_2) +{ + IgvmHandle data_handle1, data_handle2; + + /* + * If one page has data and the other doesn't then this results in dif= ferent + * page types: NORMAL vs ZERO. + */ + data_handle1 =3D igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECT= IVE, + header_index - 1); + data_handle2 =3D + igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_i= ndex); + if ((data_handle1 =3D=3D IGVMAPI_NO_DATA || + data_handle2 =3D=3D IGVMAPI_NO_DATA) && + data_handle1 !=3D data_handle2) { + return false; + } + return ((*(const uint32_t *)&page_1->flags =3D=3D + *(const uint32_t *)&page_2->flags) && + (page_1->data_type =3D=3D page_2->data_type) && + (page_1->compatibility_mask =3D=3D page_2->compatibility_mask)= ); +} + +static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, + uint64_t gpa_start, unsigned page_coun= t, + const IgvmPageDataFlags *flags, + const IgvmPageDataType page_type, + Error **errp) +{ + uint8_t *region; + IgvmHandle data_handle; + const void *data; + uint32_t data_size; + unsigned page_index; + bool zero =3D true; + const uint64_t page_size =3D flags->is_2mb_page ? 0x200000 : 0x1000; + int result; + int cgs_page_type; + + region =3D qigvm_prepare_memory(ctx, gpa_start, page_count * page_size, + start_index, errp); + if (!region) { + return -1; + } + + for (page_index =3D 0; page_index < page_count; page_index++) { + data_handle =3D igvm_get_header_data( + ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_i= ndex); + if (data_handle =3D=3D IGVMAPI_NO_DATA) { + /* No data indicates a zero page */ + memset(®ion[page_index * page_size], 0, page_size); + } else if (data_handle < 0) { + error_setg( + errp, + "IGVM file contains invalid page data for directive with " + "index %d", + page_index + start_index); + return -1; + } else { + zero =3D false; + data_size =3D igvm_get_buffer_size(ctx->file, data_handle); + if (data_size < page_size) { + memset(®ion[page_index * page_size], 0, page_size); + } else if (data_size > page_size) { + error_setg(errp, + "IGVM file contains page data with invalid size= for " + "directive with index %d", + page_index + start_index); + return -1; + } + data =3D igvm_get_buffer(ctx->file, data_handle); + memcpy(®ion[page_index * page_size], data, data_size); + igvm_free_buffer(ctx->file, data_handle); + } + } + + /* + * If a confidential guest support object is provided then use it to s= et the + * guest state. + */ + if (ctx->cgs) { + cgs_page_type =3D + qigvm_type_to_cgs_type(page_type, flags->unmeasured, zero); + if (cgs_page_type < 0) { + error_setg(errp, + "Invalid page type in IGVM file. Directives: %d to = %d, " + "page type: %d", + start_index, start_index + page_count, page_type); + return -1; + } + + result =3D ctx->cgsc->set_guest_state( + gpa_start, region, page_size * page_count, cgs_page_type, 0, e= rrp); + if (result < 0) { + return result; + } + } + return 0; +} + +static int qigvm_process_mem_page(QIgvm *ctx, + const IGVM_VHS_PAGE_DATA *page_data, + Error **errp) +{ + if (page_data) { + if (ctx->region_page_count =3D=3D 0) { + ctx->region_start =3D page_data->gpa; + ctx->region_start_index =3D ctx->current_header_index; + } else { + if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_ind= ex, + page_data, + &ctx->region_prev_page_data) || + ((ctx->region_prev_page_data.gpa + + (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000= : + 0x1000))= !=3D + page_data->gpa) || + (ctx->region_last_index !=3D (ctx->current_header_index - = 1))) { + /* End of current region */ + if (qigvm_process_mem_region( + ctx, ctx->region_start_index, ctx->region_start, + ctx->region_page_count, + &ctx->region_prev_page_data.flags, + ctx->region_prev_page_data.data_type, errp) < 0) { + return -1; + } + ctx->region_page_count =3D 0; + ctx->region_start =3D page_data->gpa; + ctx->region_start_index =3D ctx->current_header_index; + } + } + memcpy(&ctx->region_prev_page_data, page_data, + sizeof(ctx->region_prev_page_data)); + ctx->region_last_index =3D ctx->current_header_index; + ctx->region_page_count++; + } else { + if (ctx->region_page_count > 0) { + if (qigvm_process_mem_region( + ctx, ctx->region_start_index, ctx->region_start, + ctx->region_page_count, &ctx->region_prev_page_data.fl= ags, + ctx->region_prev_page_data.data_type, errp) < 0) { + return -1; + } + ctx->region_page_count =3D 0; + } + } + return 0; +} + +static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_dat= a, + Error **errp) +{ + const IGVM_VHS_PAGE_DATA *page_data =3D + (const IGVM_VHS_PAGE_DATA *)header_data; + if (page_data->compatibility_mask & ctx->compatibility_mask) { + return qigvm_process_mem_page(ctx, page_data, errp); + } + return 0; +} + +static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_da= ta, + Error **errp) +{ + const IGVM_VHS_VP_CONTEXT *vp_context =3D + (const IGVM_VHS_VP_CONTEXT *)header_data; + IgvmHandle data_handle; + uint8_t *data; + int result; + + if (!(vp_context->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + /* + * A confidential guest support object must be provided for setting + * a VP context. + */ + if (!ctx->cgs) { + error_setg( + errp, + "A VP context is present in the IGVM file but is not supported= " + "by the current system."); + return -1; + } + + data_handle =3D igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DI= RECTIVE, + ctx->current_header_index); + if (data_handle < 0) { + error_setg(errp, "Invalid VP context in IGVM file. Error code: %X", + data_handle); + return -1; + } + + data =3D (uint8_t *)igvm_get_buffer(ctx->file, data_handle); + result =3D ctx->cgsc->set_guest_state( + vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle= ), + CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); + igvm_free_buffer(ctx->file, data_handle); + if (result < 0) { + return result; + } + return 0; +} + +static int qigvm_directive_parameter_area(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER_AREA *param_area =3D + (const IGVM_VHS_PARAMETER_AREA *)header_data; + QIgvmParameterData *param_entry; + + param_entry =3D g_new0(QIgvmParameterData, 1); + param_entry->size =3D param_area->number_of_bytes; + param_entry->index =3D param_area->parameter_area_index; + param_entry->data =3D g_malloc0(param_entry->size); + + QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next); + return 0; +} + +static int qigvm_directive_parameter_insert(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER_INSERT *param =3D + (const IGVM_VHS_PARAMETER_INSERT *)header_data; + QIgvmParameterData *param_entry; + int result; + void *region; + + if (!(param->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index =3D=3D param->parameter_area_index) { + region =3D qigvm_prepare_memory(ctx, param->gpa, param_entry->= size, + ctx->current_header_index, errp); + if (!region) { + return -1; + } + memcpy(region, param_entry->data, param_entry->size); + g_free(param_entry->data); + param_entry->data =3D NULL; + + /* + * If a confidential guest support object is provided then use= it to + * set the guest state. + */ + if (ctx->cgs) { + result =3D ctx->cgsc->set_guest_state(param->gpa, region, + param_entry->size, + CGS_PAGE_TYPE_UNMEASUR= ED, 0, + errp); + if (result < 0) { + return -1; + } + } + } + } + return 0; +} + +static int qigvm_cmp_mm_entry(const void *a, const void *b) +{ + const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a =3D + (const IGVM_VHS_MEMORY_MAP_ENTRY *)a; + const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b =3D + (const IGVM_VHS_MEMORY_MAP_ENTRY *)b; + if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_num= ber) { + return -1; + } else if (entry_a->starting_gpa_page_number > + entry_b->starting_gpa_page_number) { + return 1; + } else { + return 0; + } +} + +static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_da= ta, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param =3D (const IGVM_VHS_PARAMETER *)header= _data; + QIgvmParameterData *param_entry; + int max_entry_count; + int entry =3D 0; + IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry; + ConfidentialGuestMemoryMapEntry cgmm_entry; + int retval =3D 0; + + if (!ctx->cgs) { + error_setg(errp, + "IGVM file contains a memory map but this is not suppor= ted " + "by the current system."); + return -1; + } + + /* Find the parameter area that should hold the memory map */ + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index =3D=3D param->parameter_area_index) { + max_entry_count =3D + param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY); + mm_entry =3D (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data; + + retval =3D ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, er= rp); + while (retval =3D=3D 0) { + if (entry > max_entry_count) { + error_setg( + errp, + "IGVM: guest memory map size exceeds parameter are= a defined in IGVM file"); + return -1; + } + mm_entry[entry].starting_gpa_page_number =3D cgmm_entry.gp= a >> 12; + mm_entry[entry].number_of_pages =3D cgmm_entry.size >> 12; + + switch (cgmm_entry.type) { + case CGS_MEM_RAM: + mm_entry[entry].entry_type =3D + IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY; + break; + case CGS_MEM_RESERVED: + mm_entry[entry].entry_type =3D + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + case CGS_MEM_ACPI: + mm_entry[entry].entry_type =3D + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + case CGS_MEM_NVS: + mm_entry[entry].entry_type =3D + IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT; + break; + case CGS_MEM_UNUSABLE: + mm_entry[entry].entry_type =3D + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + } + retval =3D + ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, err= p); + } + if (retval < 0) { + return retval; + } + /* The entries need to be sorted */ + qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY), + qigvm_cmp_mm_entry); + + break; + } + } + return 0; +} + +static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param =3D (const IGVM_VHS_PARAMETER *)header= _data; + QIgvmParameterData *param_entry; + uint32_t *vp_count; + CPUState *cpu; + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index =3D=3D param->parameter_area_index) { + vp_count =3D (uint32_t *)(param_entry->data + param->byte_offs= et); + *vp_count =3D 0; + CPU_FOREACH(cpu) + { + (*vp_count)++; + } + break; + } + } + return 0; +} + +static int qigvm_directive_environment_info(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param =3D (const IGVM_VHS_PARAMETER *)header= _data; + QIgvmParameterData *param_entry; + IgvmEnvironmentInfo *environmental_state; + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index =3D=3D param->parameter_area_index) { + environmental_state =3D + (IgvmEnvironmentInfo *)(param_entry->data + param->byte_of= fset); + environmental_state->memory_is_shared =3D 1; + break; + } + } + return 0; +} + +static int qigvm_directive_required_memory(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_REQUIRED_MEMORY *mem =3D + (const IGVM_VHS_REQUIRED_MEMORY *)header_data; + uint8_t *region; + int result; + + if (!(mem->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + region =3D qigvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes, + ctx->current_header_index, errp); + if (!region) { + return -1; + } + if (ctx->cgs) { + result =3D + ctx->cgsc->set_guest_state(mem->gpa, region, mem->number_of_by= tes, + CGS_PAGE_TYPE_REQUIRED_MEMORY, 0, e= rrp); + if (result < 0) { + return result; + } + } + return 0; +} + +static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) +{ + int32_t header_count; + unsigned header_index; + IgvmHandle header_handle; + IGVM_VHS_SUPPORTED_PLATFORM *platform; + uint32_t compatibility_mask_sev =3D 0; + uint32_t compatibility_mask_sev_es =3D 0; + uint32_t compatibility_mask_sev_snp =3D 0; + uint32_t compatibility_mask =3D 0; + + header_count =3D igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLAT= FORM); + if (header_count < 0) { + error_setg(errp, + "Invalid platform header count in IGVM file. Error code= : %X", + header_count); + return -1; + } + + for (header_index =3D 0; header_index < (unsigned)header_count; + header_index++) { + IgvmVariableHeaderType typ =3D igvm_get_header_type( + ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + if (typ =3D=3D IGVM_VHT_SUPPORTED_PLATFORM) { + header_handle =3D igvm_get_header( + ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + if (header_handle < 0) { + error_setg(errp, + "Invalid platform header in IGVM file. " + "Index: %d, Error code: %X", + header_index, header_handle); + return -1; + } + platform =3D + (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file, + header_han= dle) + + sizeof( + IGVM_VHS_VARIABLE_HEAD= ER)); + if ((platform->platform_type =3D=3D IGVM_PLATFORM_TYPE_SEV_ES)= && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV_ES, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_bounda= ry)) { + compatibility_mask_sev_es =3D platform->compatibility_= mask; + } + } else if ((platform->platform_type =3D=3D IGVM_PLATFORM_TYPE_= SEV) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_bounda= ry)) { + compatibility_mask_sev =3D platform->compatibility_mas= k; + } + } else if ((platform->platform_type =3D=3D + IGVM_PLATFORM_TYPE_SEV_SNP) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV_SNP, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_bounda= ry)) { + compatibility_mask_sev_snp =3D platform->compatibility= _mask; + } + } else if (platform->platform_type =3D=3D IGVM_PLATFORM_TYPE_N= ATIVE) { + compatibility_mask =3D platform->compatibility_mask; + } + igvm_free_buffer(ctx->file, header_handle); + } + } + /* Choose the strongest supported isolation technology */ + if (compatibility_mask_sev_snp !=3D 0) { + ctx->compatibility_mask =3D compatibility_mask_sev_snp; + } else if (compatibility_mask_sev_es !=3D 0) { + ctx->compatibility_mask =3D compatibility_mask_sev_es; + } else if (compatibility_mask_sev !=3D 0) { + ctx->compatibility_mask =3D compatibility_mask_sev; + } else if (compatibility_mask !=3D 0) { + ctx->compatibility_mask =3D compatibility_mask; + } else { + error_setg( + errp, + "IGVM file does not describe a compatible supported platform"); + return -1; + } + return 0; +} + +static IgvmHandle qigvm_file_init(char *filename, Error **errp) +{ + IgvmHandle igvm; + g_autofree uint8_t *buf =3D NULL; + unsigned long len; + g_autoptr(GError) gerr =3D NULL; + + if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) { + error_setg(errp, "Unable to load %s: %s", filename, gerr->message); + return -1; + } + + igvm =3D igvm_new_from_binary(buf, len); + if (igvm < 0) { + error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igv= m); + return -1; + } + return igvm; +} + +int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, + Error **errp) +{ + int32_t header_count; + QIgvmParameterData *parameter; + int retval =3D -1; + QIgvm ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.file =3D qigvm_file_init(cfg->filename, errp); + if (ctx.file < 0) { + return -1; + } + + /* + * The ConfidentialGuestSupport object is optional and allows a confid= ential + * guest platform to perform extra processing, such as page measuremen= t, on + * IGVM directives. + */ + ctx.cgs =3D cgs; + ctx.cgsc =3D cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL; + + /* + * Check that the IGVM file provides configuration for the current + * platform + */ + if (qigvm_supported_platform_compat_mask(&ctx, errp) < 0) { + goto cleanup; + } + + header_count =3D igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIREC= TIVE); + if (header_count <=3D 0) { + error_setg( + errp, "Invalid directive header count in IGVM file. Error code= : %X", + header_count); + goto cleanup; + } + + QTAILQ_INIT(&ctx.parameter_data); + + for (ctx.current_header_index =3D 0; + ctx.current_header_index < (unsigned)header_count; + ctx.current_header_index++) { + IgvmVariableHeaderType type =3D igvm_get_header_type( + ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_in= dex); + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } + } + + /* + * Contiguous pages of data with compatible flags are grouped together= in + * order to reduce the number of memory regions we create. Make sure t= he + * last group is processed with this call. + */ + retval =3D qigvm_process_mem_page(&ctx, NULL, errp); + +cleanup_parameters: + QTAILQ_FOREACH(parameter, &ctx.parameter_data, next) + { + g_free(parameter->data); + parameter->data =3D NULL; + } + +cleanup: + igvm_free(ctx.file); + + return retval; +} diff --git a/backends/igvm.h b/backends/igvm.h new file mode 100644 index 0000000000..db02ea9165 --- /dev/null +++ b/backends/igvm.h @@ -0,0 +1,22 @@ +/* + * QEMU IGVM configuration backend for Confidential Guests + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BACKENDS_IGVM_H +#define BACKENDS_IGVM_H + +#include "system/confidential-guest-support.h" +#include "system/igvm-cfg.h" +#include "qapi/error.h" + +int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs, + Error **errp); + +#endif diff --git a/backends/meson.build b/backends/meson.build index ac0fac7845..60021f45d1 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -36,6 +36,8 @@ system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) if igvm.found() system_ss.add(igvm) + system_ss.add(files('igvm-cfg.c'), igvm) + system_ss.add(files('igvm.c'), igvm) endif =20 system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) diff --git a/include/system/igvm-cfg.h b/include/system/igvm-cfg.h new file mode 100644 index 0000000000..321b3196f0 --- /dev/null +++ b/include/system/igvm-cfg.h @@ -0,0 +1,46 @@ +/* + * QEMU IGVM interface + * + * Copyright (C) 2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_IGVM_CFG_H +#define QEMU_IGVM_CFG_H + +#include "qom/object.h" + +typedef struct IgvmCfg { + ObjectClass parent_class; + + /* + * filename: Filename that specifies a file that contains the configur= ation + * of the guest in Independent Guest Virtual Machine (IGVM) + * format. + */ + char *filename; +} IgvmCfg; + +typedef struct IgvmCfgClass { + ObjectClass parent_class; + + /* + * If an IGVM filename has been specified then process the IGVM file. + * Performs a no-op if no filename has been specified. + * + * Returns 0 for ok and -1 on error. + */ + int (*process)(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, + Error **errp); + +} IgvmCfgClass; + +#define TYPE_IGVM_CFG "igvm-cfg" + +OBJECT_DECLARE_TYPE(IgvmCfg, IgvmCfgClass, IGVM_CFG) + +#endif diff --git a/qapi/qom.json b/qapi/qom.json index 3e8debf78c..9f1b9564e0 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -932,6 +932,19 @@ 'data': { '*filename': 'str' }, 'if': 'CONFIG_POSIX' } =20 +## +# @IgvmCfgProperties: +# +# Properties common to objects that handle IGVM files. +# +# @file: IGVM file to use to configure guest +# +# Since: 10.1 +## +{ 'struct': 'IgvmCfgProperties', + 'if': 'CONFIG_IGVM', + 'data': { 'file': 'str' } } + ## # @SevCommonProperties: # @@ -1136,6 +1149,8 @@ 'filter-redirector', 'filter-replay', 'filter-rewriter', + { 'name': 'igvm-cfg', + 'if': 'CONFIG_IGVM' }, 'input-barrier', { 'name': 'input-linux', 'if': 'CONFIG_LINUX' }, @@ -1212,6 +1227,8 @@ 'filter-redirector': 'FilterRedirectorProperties', 'filter-replay': 'NetfilterProperties', 'filter-rewriter': 'FilterRewriterProperties', + 'igvm-cfg': { 'type': 'IgvmCfgProperties', + 'if': 'CONFIG_IGVM' }, 'input-barrier': 'InputBarrierProperties', 'input-linux': { 'type': 'InputLinuxProperties', 'if': 'CONFIG_LINUX' }, --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749822905; cv=none; d=zohomail.com; s=zohoarc; b=btlW5JDaWF2aroyU3aHG8GyJCsusSWg16EdAPz5JcmouzXEoyHBRvoWPv6Jt6QlKpWq1adFbk/AslGHfDg6x4euEpMNHs+zrg9qGSL6cPRZG0xoVH1/K3PGco2L+dDyRZX0atd3iBvy+oJpQhFzI4YtsBFT88q8MTPzNqAkyEkg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749822905; 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=o6ETkRliv5ope85Nq16lQSeMn0sGfFLTlkM71tjbqEI=; b=KlQBpssmEwf39Kajn1KS/MS43aJyCxAI6yhwH214qUVg2x1uPtbsxJJ6uQerzQq4PkGzohEAYSpokPeTzBv77yEd44sqSHf5CgCH8KusnWi1lSpURf8xiJpI/DmlcWtmAxkmM38G/MDFmvZgyiA7sDApaIfklxDN9oZQ7Rhcqb8= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749822905692708.5563507057433; Fri, 13 Jun 2025 06:55:05 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ4rF-0007yO-GV; Fri, 13 Jun 2025 09:54:13 -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 1uQ4r8-0007xc-7s for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:54:07 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ4r6-0007XF-D4 for qemu-devel@nongnu.org; Fri, 13 Jun 2025 09:54:05 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.84]) by dkim.livemail.co.uk (Postfix) with ESMTPS id F126D4025C; Fri, 13 Jun 2025 14:54:02 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 7D364A0150; Fri, 13 Jun 2025 14:53:58 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749822843; bh=LZfTdki+OPVrFMVVHMQu7T3vmvH39joXfTMuUuF27/E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UlFnDKi+Z6dFA3bvzBfl5Iq+XNfRGefIbHXZlHhv4Y2MjO33QswG0eoNGLFN9/KtV rO4MkR2sig3QIU4TZ2a4y8OS3Pd55uKyx0ImLr7/thANbZmLnJT7aNAGhyzZ++peTi vBIcnMt5Hu4SKzUT2IDHCnNbD2W58Ec+6j1Rz01U= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 04/16] hw/i386: Add igvm-cfg object and processing for IGVM files Date: Fri, 13 Jun 2025 14:53:19 +0100 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749822908467116600 Content-Type: text/plain; charset="utf-8" An IGVM file contains configuration of guest state that should be applied during configuration of the guest, before the guest is started. This patch allows the user to add an igvm-cfg object to an X86 machine configuration that allows an IGVM file to be configured that will be applied to the guest before it is started. If an IGVM configuration is provided then the IGVM file is processed at the end of the board initialization, before the state transition to PHASE_MACHINE_INITIALIZED. Signed-off-by: Roy Hopkins Acked-by: Gerd Hoffman Reviewed-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- hw/i386/pc.c | 12 ++++++++++++ hw/i386/pc_piix.c | 10 ++++++++++ hw/i386/pc_q35.c | 10 ++++++++++ include/hw/i386/x86.h | 3 +++ qemu-options.hx | 28 ++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b211633575..432ab288a8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1833,6 +1833,18 @@ static void pc_machine_class_init(ObjectClass *oc, c= onst void *data) object_class_property_add_bool(oc, "fd-bootchk", pc_machine_get_fd_bootchk, pc_machine_set_fd_bootchk); + +#if defined(CONFIG_IGVM) + object_class_property_add_link(oc, "igvm-cfg", + TYPE_IGVM_CFG, + offsetof(X86MachineState, igvm), + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_class_property_set_description(oc, "igvm-cfg", + "Set IGVM configuration"); +#endif + + } =20 static const TypeInfo pc_machine_info =3D { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index ea7572e783..3184ea1b37 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -366,6 +366,16 @@ static void pc_init1(MachineState *machine, const char= *pci_type) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } =20 typedef enum PCSouthBridgeOption { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 33211b1876..6990e1c669 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -325,6 +325,16 @@ static void pc_q35_init(MachineState *machine) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } =20 #define DEFINE_Q35_MACHINE(major, minor) \ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index fc460b82f8..8755cad50a 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -25,6 +25,7 @@ #include "hw/intc/ioapic.h" #include "hw/isa/isa.h" #include "qom/object.h" +#include "system/igvm-cfg.h" =20 struct X86MachineClass { MachineClass parent; @@ -92,6 +93,8 @@ struct X86MachineState { * which means no limitation on the guest's bus locks. */ uint64_t bus_lock_ratelimit; + + IgvmCfg *igvm; }; =20 #define X86_MACHINE_SMM "smm" diff --git a/qemu-options.hx b/qemu-options.hx index 7eb8e02b4b..2e782f95d4 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5992,6 +5992,34 @@ SRST -machine ...,memory-encryption=3Dsev0 \\ ..... =20 + ``-object igvm-cfg,file=3Dfile`` + Create an IGVM configuration object that defines the initial state + of the guest using a file in that conforms to the Independent Guest + Virtual Machine (IGVM) file format. + + This is currently only supported by ``-machine q35`` and + ``-machine pc``. + + The ``file`` parameter is used to specify the IGVM file to load. + When provided, the IGVM file is used to populate the initial + memory of the virtual machine and, depending on the platform, can + define the initial processor state, memory map and parameters. + + The IGVM file is expected to contain the firmware for the virtual + machine, therefore an ``igvm-cfg`` object cannot be provided along + with other ways of specifying firmware, such as the ``-bios`` + parameter on x86 machines. + + e.g to launch a machine providing the firmware in an IGVM file + + .. parsed-literal:: + + # |qemu_system_x86| \\ + ...... \\ + -object igvm-cfg,id=3Digvm0,file=3Dbios.igvm \\ + -machine ...,igvm-cfg=3Digvm0 \\ + ..... + ``-object authz-simple,id=3Did,identity=3Dstring`` Create an authorization object that will control access to network services. --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749823878; cv=none; d=zohomail.com; s=zohoarc; b=PcJEC61iRX58Epwq3TViBZHf2zZvDyuws9tfu4IENw8O4vo4jRDjCeghBCDZKs800mJMC5JpiVFBSD3i4U24I0/eg9mG1Tk0CreA7fgq9MzWmObGQSnuvSuHK/6qPmXbU1s3op8H0KpOBdrRALlUV58KTkBy/9Y/f+UqMc+kIv8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749823878; h=Content-Type: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=tZMKlaN7fczHOhzhVmHGnLtz8u3QhfWidB0hAdbpUMY=; b=bwtKl6LW3yc8hjLsEXCbH5WQFEsLeaPyI/VKCgHMS1NtCvkGXjb6r7Rwsq80YSmEF8tCkQHjD+bv3KZ4uEkkvrkMQMCr4HGdxFt7f71FYNlW2U/rDECEdqhaS7s44henL+hVgJkC3eY57sjJbvpQhwfBCIeMrlZ7Z9xi9CyjsPI= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17498238785921006.2637564420739; Fri, 13 Jun 2025 07:11:18 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ57g-0000Lj-TA; Fri, 13 Jun 2025 10:11:13 -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 1uQ57b-0008Eh-Ae for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:11:07 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ57Y-0002AK-0H for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:11:07 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.81]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 4C065201AB; Fri, 13 Jun 2025 15:11:01 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id B8EAE4032A; Fri, 13 Jun 2025 15:10:56 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749823861; bh=ihgXP9VYIPe7IIMnaZhSQ3ipe9TIN+zvA+5AW5ZG/9Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S5Rz9owM0PzOTOl7X0Digu73Sf0CDKW0PsMty1UjYD/ZtOuBhQQaslfVT0A65TLZZ YEmTOBIk6uLZVMYqhLgHFxypyHCIV3sNL5QkH6LrRYs/obil1nyvoGnmkeXcJ55Q8j ZYlGFtA66zeprIvm4Uk0jUblwxEnFRyyZWUcWdAo= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 05/16] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM Date: Fri, 13 Jun 2025 14:53:20 +0100 Message-ID: <7d5eee4ba03ad11b1ee6d32fea96b160b4d3931c.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749823880514116600 When using an IGVM file the configuration of the system firmware is defined by IGVM directives contained in the file. In this case the user should not configure any pflash devices. This commit skips initialization of the ROM mode when pflash0 is not set then checks to ensure no pflash devices have been configured when using IGVM, exiting with an error message if this is not the case. Signed-off-by: Roy Hopkins Acked-by: Gerd Hoffman Reviewed-by: Daniel P. Berrang=C3=A9 Reviewed-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Reviewed-by: Pankaj Gupta Reviewed-by: Ani Sinha --- hw/i386/pc_sysfw.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 821396c16e..1a12b635ad 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -220,7 +220,13 @@ void pc_system_firmware_init(PCMachineState *pcms, BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; =20 if (!pcmc->pci_enabled) { - x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); + /* + * If an IGVM file is specified then the firmware must be provided + * in the IGVM file. + */ + if (!X86_MACHINE(pcms)->igvm) { + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, t= rue); + } return; } =20 @@ -240,8 +246,13 @@ void pc_system_firmware_init(PCMachineState *pcms, } =20 if (!pflash_blk[0]) { - /* Machine property pflash0 not set, use ROM mode */ - x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false= ); + /* + * Machine property pflash0 not set, use ROM mode unless using IGV= M, + * in which case the firmware must be provided by the IGVM file. + */ + if (!X86_MACHINE(pcms)->igvm) { + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, f= alse); + } } else { if (kvm_enabled() && !kvm_readonly_mem_enabled()) { /* @@ -257,6 +268,20 @@ void pc_system_firmware_init(PCMachineState *pcms, } =20 pc_system_flash_cleanup_unused(pcms); + + /* + * The user should not have specified any pflash devices when using IG= VM + * to configure the guest. + */ + if (X86_MACHINE(pcms)->igvm) { + for (i =3D 0; i < ARRAY_SIZE(pcms->flash); i++) { + if (pcms->flash[i]) { + error_report("pflash devices cannot be configured when " + "using IGVM"); + exit(1); + } + } + } } =20 void x86_firmware_configure(hwaddr gpa, void *ptr, int size) --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749823892; cv=none; d=zohomail.com; s=zohoarc; b=OBICNxzvTnxJQAJz5HEkvA8h5baZ1TF0ZEmZJX2kPO70q5SUw8EgPt20GfB6OYXqGmriBxhyr8kbgFr1FtEmjCKzG/98mpI0aSGi9r/5OLeUmaKcYZerY1GqXcDUX4Fio6E6Nvsxsht4eynrxvUYxKwB4jKsQPMr/8BCKO2UHxU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749823892; 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=LivkRhWTse65rOfgM7l5NWAnSW4oxJWij6EkqDkElE4=; b=Xymd5pFcVMbGVR2W4eYQC1WFqN2FMgK772CF/ypwjJ0DELHqwypWHXCRmtTgaCf52/nKzCVHjqzSDkfKKRSfSL/V0mNRNeJ4VwenjPkOaxK9+gBw9J6zsi0B3H6f/mvBmmPqke+dRWK6dGnhkQDZAh9qxLLxTyAdE2r6di2a4Yg= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749823892895500.58184708284114; Fri, 13 Jun 2025 07:11:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ57v-0001cM-HX; Fri, 13 Jun 2025 10:11:27 -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 1uQ57s-0001Ob-3W for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:11:24 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ57p-0002CM-MA for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:11:23 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.82]) by dkim.livemail.co.uk (Postfix) with ESMTPS id D97611802D4; Fri, 13 Jun 2025 15:11:19 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 56462C0B3E; Fri, 13 Jun 2025 15:11:15 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749823879; bh=2Ql4x0vibybJS4USky7CMG78BCCbeUBBq6FeJfBQwEs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k+n238ocloDzLuWDki7losdBtSVaP+aLw5ypF1VYMyrovyvy78zzy4t/yx11yuBXd LCL8D+6NDSYlSheIZuvrwQaHBelB3KBd9dLsyKdBLvDwXqSI/aQuVEAO5xeEGj6uau B1fkFKYhHJzh5zeLI84JS/miej6JjmU1Q18tePQs= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 06/16] sev: Update launch_update_data functions to use Error handling Date: Fri, 13 Jun 2025 14:53:21 +0100 Message-ID: <8e41059626469c94459b6a6b6cc1ce745968dae9.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749823894394116600 Content-Type: text/plain; charset="utf-8" The class function and implementations for updating launch data return a code in case of error. In some cases an error message is generated and in other cases, just the error return value is used. This small refactor adds an 'Error **errp' parameter to all functions which consistently set an error condition if a non-zero value is returned. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Pankaj Gupta Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- target/i386/sev.c | 68 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 1a12f0671c..a84f5f5d28 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -122,7 +122,8 @@ struct SevCommonStateClass { Error **errp); int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); - int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint= 8_t *ptr, size_t len); + int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, size_t len, Error **errp); int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); }; =20 @@ -970,9 +971,8 @@ sev_snp_adjust_cpuid_features(X86ConfidentialGuest *cg,= uint32_t feature, uint32 return value; } =20 -static int -sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *addr, size_t len) +static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *addr, size_t len, Error **errp) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -987,8 +987,8 @@ sev_launch_update_data(SevCommonState *sev_common, hwad= dr gpa, ret =3D sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { - error_report("%s: LAUNCH_UPDATE ret=3D%d fw_error=3D%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_setg(errp, "%s: LAUNCH_UPDATE ret=3D%d fw_error=3D%d '%s'", = __func__, + ret, fw_error, fw_error_to_str(fw_error)); } =20 return ret; @@ -1116,8 +1116,8 @@ sev_launch_finish(SevCommonState *sev_common) migrate_add_blocker(&sev_mig_blocker, &error_fatal); } =20 -static int -snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) +static int snp_launch_update_data(uint64_t gpa, void *hva, size_t len, + int type, Error **errp) { SevLaunchUpdateData *data; =20 @@ -1132,23 +1132,21 @@ snp_launch_update_data(uint64_t gpa, void *hva, siz= e_t len, int type) return 0; } =20 -static int -sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *ptr, size_t len) +static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr g= pa, + uint8_t *ptr, size_t len, Error **er= rp) { - int ret =3D snp_launch_update_data(gpa, ptr, len, - KVM_SEV_SNP_PAGE_TYPE_NORMAL); - return ret; + return snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_NORMAL, errp); } =20 static int sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, - const KvmCpuidInfo *kvm_cpuid_info) + const KvmCpuidInfo *kvm_cpuid_info, Error **errp) { size_t i; =20 if (kvm_cpuid_info->cpuid.nent > SNP_CPUID_FUNCTION_MAXCOUNT) { - error_report("SEV-SNP: CPUID entry count (%d) exceeds max (%d)", + error_setg(errp, "SEV-SNP: CPUID entry count (%d) exceeds max (%d)= ", kvm_cpuid_info->cpuid.nent, SNP_CPUID_FUNCTION_MAXCOU= NT); return -1; } @@ -1190,8 +1188,8 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, return 0; } =20 -static int -snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) +static int snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, + size_t cpuid_len, Error **errp) { KvmCpuidInfo kvm_cpuid_info =3D {0}; SnpCpuidInfo snp_cpuid_info; @@ -1208,26 +1206,25 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void *= hva, size_t cpuid_len) } while (ret =3D=3D -E2BIG); =20 if (ret) { - error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'", - strerror(-ret)); - return 1; + error_setg(errp, "SEV-SNP: unable to query CPUID values for CPU: '= %s'", + strerror(-ret)); + return -1; } =20 - ret =3D sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info); - if (ret) { - error_report("SEV-SNP: failed to generate CPUID table information"= ); - return 1; + ret =3D sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info, errp= ); + if (ret < 0) { + return -1; } =20 memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info)); =20 return snp_launch_update_data(cpuid_addr, hva, cpuid_len, - KVM_SEV_SNP_PAGE_TYPE_CPUID); + KVM_SEV_SNP_PAGE_TYPE_CPUID, errp); } =20 -static int -snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, - void *hva, uint32_t len) +static int snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, + uint32_t addr, void *hva, + uint32_t len, Error **errp) { int type =3D KVM_SEV_SNP_PAGE_TYPE_ZERO; if (sev_snp->parent_obj.kernel_hashes) { @@ -1239,7 +1236,7 @@ snp_launch_update_kernel_hashes(SevSnpGuestState *sev= _snp, uint32_t addr, sizeof(*sev_snp->kernel_hashes_data)); type =3D KVM_SEV_SNP_PAGE_TYPE_NORMAL; } - return snp_launch_update_data(addr, hva, len, type); + return snp_launch_update_data(addr, hva, len, type, errp); } =20 static int @@ -1277,12 +1274,14 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_s= np, } =20 if (type =3D=3D KVM_SEV_SNP_PAGE_TYPE_CPUID) { - ret =3D snp_launch_update_cpuid(desc->base, hva, desc->len); + ret =3D snp_launch_update_cpuid(desc->base, hva, desc->len, + &error_fatal); } else if (desc->type =3D=3D SEV_DESC_TYPE_SNP_KERNEL_HASHES) { ret =3D snp_launch_update_kernel_hashes(sev_snp, desc->base, h= va, - desc->len); + desc->len, &error_fatal); } else { - ret =3D snp_launch_update_data(desc->base, hva, desc->len, typ= e); + ret =3D snp_launch_update_data(desc->base, hva, desc->len, typ= e, + &error_fatal); } =20 if (ret) { @@ -1615,9 +1614,8 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t = len, Error **errp) if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { int ret; =20 - ret =3D klass->launch_update_data(sev_common, gpa, ptr, len); + ret =3D klass->launch_update_data(sev_common, gpa, ptr, len, errp); if (ret < 0) { - error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; } } --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749825379; cv=none; d=zohomail.com; s=zohoarc; b=E20xw1KF5+ykTKG4APSRL4kLQQxGbuCT4pqKJYVk6nv5wLZ0OF1jin4GvObexr/tUkWbEk2watSIrApBSQvUZOefVoRTEO8emTJBpBvOqP6o0lZHu2VkHBNfY3zDwAokBkXbHlRM4F8V0DforJB/73lp4ND9QtAKby6ZhGJCSHo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749825379; 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=d2UjKPTJ6QkXuN9bMGOpDBI0kuPGuKCsAuE0nCGskcU=; b=S8JR28N9LcwtQVr1+ly32pRyK+dNR5DmXdMGFJWNKMkI4RHXWkYMhyMUEE+PG28FeSvCO+Gei1vmffm/k6XSuWeYRq20H69FSsnjAERTciiBs1/0vGcP8KycfvSxnG7z1nmU8HKgAq9werd9ySwFTrbeFKiZx2eBgVx0l5N6vJA= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749825379638412.3287594947193; Fri, 13 Jun 2025 07:36:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ5V8-0002mi-Ad; Fri, 13 Jun 2025 10:35:26 -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 1uQ5V5-0002m2-Ma for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:35:23 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ5V3-0005YL-IS for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:35:23 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.84]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 6B314401DA; Fri, 13 Jun 2025 15:35:18 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id E95F6A0158; Fri, 13 Jun 2025 15:35:13 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749825318; bh=4wzFmIqJLDF3bsupyhMTUOQH5GKFecnztjCeomUpFbs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Hs4lDjfb5H2UeuH2oXUQlhDr4yXr6B5BiE7ut6tCdd1tYFGvGVaJTjKw0XKkQflzz ZDBdqG1sIGGVEA12NkR7YGzDRcxoEX7mlvz5MNmGmhzjsrBAP9eIVa/cGOvoKWPDBB guGMpHq9LNWC9UeZNpGgxSyZMZTnB77wAR6RMJaM= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 07/16] target/i386: Allow setting of R_LDTR and R_TR with cpu_x86_load_seg_cache() Date: Fri, 13 Jun 2025 15:11:47 +0100 Message-ID: <78e48ebe9b96e9efcd0bb8dc43cf0757c993a98c.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749825381553116600 Content-Type: text/plain; charset="utf-8" The x86 segment registers are identified by the X86Seg enumeration which includes LDTR and TR as well as the normal segment registers. The function 'cpu_x86_load_seg_cache()' uses the enum to determine which segment to set. However, specifying R_LDTR or R_TR results in an out-of-bounds access of the segment array. Possibly by coincidence, the function does correctly set LDTR or TR in this case as the structures for these registers immediately follow the array which is accessed out of bounds. This patch adds correct handling for R_LDTR and R_TR in the function. Signed-off-by: Roy Hopkins Acked-by: Gerd Hoffman Reviewed-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- target/i386/cpu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 5910dcf74d..cc56f0e74f 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2417,7 +2417,14 @@ static inline void cpu_x86_load_seg_cache(CPUX86Stat= e *env, SegmentCache *sc; unsigned int new_hflags; =20 - sc =3D &env->segs[seg_reg]; + if (seg_reg =3D=3D R_LDTR) { + sc =3D &env->ldt; + } else if (seg_reg =3D=3D R_TR) { + sc =3D &env->tr; + } else { + sc =3D &env->segs[seg_reg]; + } + sc->selector =3D selector; sc->base =3D base; sc->limit =3D limit; --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749825380; cv=none; d=zohomail.com; s=zohoarc; b=Gt0+ijMeNrMHodh2kvdWAJTa/ksTAPQlCliLAD93bonAqdvD31t3jUqR4CmM1F2IHA6yuNN0z9TMOX/appGNaBotVosa5sCUm5mf+Gr+AcyBSKqZ9kgG0+1dvBkFW6J+3mSAelBaqLaAsKPN2iGHGO3p0IZLP/vCz+bkqrrNZwM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749825380; 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=/XQWDPLErze6rhFTFfkFiZ8NI+q46MK/JAmGu+FnCu4=; b=NzTkq0SGFs11Kf5zcthQGejwmA2RT8gYEvv1Tjsb5j4lPn4A+IgG1x+NgaBAR8Wg6goI9Xo4B2Y+vWK35HC2pswdAApE+w8jUJQdUcJkY+M2WaOoVAexrL/uzzjFtJ4pSI6i81j2rz0Avbui6juy1BkSo7k4Fs++D0OXVtyjA04= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749825379999540.7072364816194; Fri, 13 Jun 2025 07:36:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ5VK-0002s4-OC; Fri, 13 Jun 2025 10:35:38 -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 1uQ5VB-0002oW-Fv for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:35:30 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ5V8-0005Yb-E6 for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:35:29 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.83]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 0838A1800EC; Fri, 13 Jun 2025 15:35:25 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 6DF5940304; Fri, 13 Jun 2025 15:35:20 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749825325; bh=hrRgmT5aoq0eHDrLQspv6VRWreqZgMogSVeUFGJqizI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Zwc5fkmsOAFXrL9U13VeKboUwk0+rUIJq6nv6s0zD/uZEsoqBw6zwuoWUBCqh+jt3 LOS1whMHDCEg0T50gQFfQO2VeGWMzlFnlLkepHsRwME5mwhMytQ4e6zmthoEmd8MG2 MJmfGvE71q2nQ63/DRRFUQ4ThsozTyhcoFq+coIw= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 08/16] i386/sev: Refactor setting of reset vector and initial CPU state Date: Fri, 13 Jun 2025 15:11:48 +0100 Message-ID: <6a923e1cb5cdcc9d8bd3115c60b90f87b49aafa4.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749825381544116600 Content-Type: text/plain; charset="utf-8" When an SEV guest is started, the reset vector and state are extracted from metadata that is contained in the firmware volume. In preparation for using IGVM to setup the initial CPU state, the code has been refactored to populate vmcb_save_area for each CPU which is then applied during guest startup and CPU reset. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Pankaj Gupta Reviewed-by: Ani Sinha --- target/i386/sev.c | 323 +++++++++++++++++++++++++++++++++++++++++----- target/i386/sev.h | 110 ++++++++++++++++ 2 files changed, 400 insertions(+), 33 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index a84f5f5d28..2e4cf9f6c1 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -50,6 +50,12 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClas= s, SEV_SNP_GUEST) /* hard code sha256 digest size */ #define HASH_SIZE 32 =20 +/* Convert between SEV-ES VMSA and SegmentCache flags/attributes */ +#define FLAGS_VMSA_TO_SEGCACHE(flags) \ + ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8)) +#define FLAGS_SEGCACHE_TO_VMSA(flags) \ + ((((flags) & 0xff00) >> 8) | (((flags) & 0xf00000) >> 12)) + typedef struct QEMU_PACKED SevHashTableEntry { QemuUUID guid; uint16_t len; @@ -89,6 +95,14 @@ typedef struct QEMU_PACKED SevHashTableDescriptor { uint32_t size; } SevHashTableDescriptor; =20 +typedef struct SevLaunchVmsa { + QTAILQ_ENTRY(SevLaunchVmsa) next; + + uint16_t cpu_index; + uint64_t gpa; + struct sev_es_save_area vmsa; +} SevLaunchVmsa; + struct SevCommonState { X86ConfidentialGuest parent_obj; =20 @@ -107,9 +121,7 @@ struct SevCommonState { int sev_fd; SevState state; =20 - uint32_t reset_cs; - uint32_t reset_ip; - bool reset_data_valid; + QTAILQ_HEAD(, SevLaunchVmsa) launch_vmsa; }; =20 struct SevCommonStateClass { @@ -364,6 +376,172 @@ static struct RAMBlockNotifier sev_ram_notifier =3D { .ram_block_removed =3D sev_ram_block_removed, }; =20 +static void sev_apply_cpu_context(CPUState *cpu) +{ + SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); + X86CPU *x86; + CPUX86State *env; + struct SevLaunchVmsa *launch_vmsa; + + /* See if an initial VMSA has been provided for this CPU */ + QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next) + { + if (cpu->cpu_index =3D=3D launch_vmsa->cpu_index) { + x86 =3D X86_CPU(cpu); + env =3D &x86->env; + + /* + * Ideally we would provide the VMSA directly to kvm which wou= ld + * ensure that the resulting initial VMSA measurement which is + * calculated during KVM_SEV_LAUNCH_UPDATE_VMSA is calculated = from + * exactly what we provide here. Currently this is not possibl= e so + * we need to copy the parts of the VMSA structure that we cur= rently + * support into the CPU state. + */ + cpu_load_efer(env, launch_vmsa->vmsa.efer); + cpu_x86_update_cr4(env, launch_vmsa->vmsa.cr4); + cpu_x86_update_cr0(env, launch_vmsa->vmsa.cr0); + cpu_x86_update_cr3(env, launch_vmsa->vmsa.cr3); + env->xcr0 =3D launch_vmsa->vmsa.xcr0; + env->pat =3D launch_vmsa->vmsa.g_pat; + + cpu_x86_load_seg_cache( + env, R_CS, launch_vmsa->vmsa.cs.selector, + launch_vmsa->vmsa.cs.base, launch_vmsa->vmsa.cs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.cs.attrib)); + cpu_x86_load_seg_cache( + env, R_DS, launch_vmsa->vmsa.ds.selector, + launch_vmsa->vmsa.ds.base, launch_vmsa->vmsa.ds.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ds.attrib)); + cpu_x86_load_seg_cache( + env, R_ES, launch_vmsa->vmsa.es.selector, + launch_vmsa->vmsa.es.base, launch_vmsa->vmsa.es.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.es.attrib)); + cpu_x86_load_seg_cache( + env, R_FS, launch_vmsa->vmsa.fs.selector, + launch_vmsa->vmsa.fs.base, launch_vmsa->vmsa.fs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.fs.attrib)); + cpu_x86_load_seg_cache( + env, R_GS, launch_vmsa->vmsa.gs.selector, + launch_vmsa->vmsa.gs.base, launch_vmsa->vmsa.gs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gs.attrib)); + cpu_x86_load_seg_cache( + env, R_SS, launch_vmsa->vmsa.ss.selector, + launch_vmsa->vmsa.ss.base, launch_vmsa->vmsa.ss.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ss.attrib)); + + env->gdt.base =3D launch_vmsa->vmsa.gdtr.base; + env->gdt.limit =3D launch_vmsa->vmsa.gdtr.limit; + env->gdt.flags =3D + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gdtr.attrib); + env->idt.base =3D launch_vmsa->vmsa.idtr.base; + env->idt.limit =3D launch_vmsa->vmsa.idtr.limit; + env->idt.flags =3D + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.idtr.attrib); + + cpu_x86_load_seg_cache( + env, R_LDTR, launch_vmsa->vmsa.ldtr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.ldtr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ldtr.attrib)); + cpu_x86_load_seg_cache( + env, R_TR, launch_vmsa->vmsa.tr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.tr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.tr.attrib)); + + env->dr[6] =3D launch_vmsa->vmsa.dr6; + env->dr[7] =3D launch_vmsa->vmsa.dr7; + + env->regs[R_EAX] =3D launch_vmsa->vmsa.rax; + env->regs[R_ECX] =3D launch_vmsa->vmsa.rcx; + env->regs[R_EDX] =3D launch_vmsa->vmsa.rdx; + env->regs[R_EBX] =3D launch_vmsa->vmsa.rbx; + env->regs[R_ESP] =3D launch_vmsa->vmsa.rsp; + env->regs[R_EBP] =3D launch_vmsa->vmsa.rbp; + env->regs[R_ESI] =3D launch_vmsa->vmsa.rsi; + env->regs[R_EDI] =3D launch_vmsa->vmsa.rdi; +#ifdef TARGET_X86_64 + env->regs[R_R8] =3D launch_vmsa->vmsa.r8; + env->regs[R_R9] =3D launch_vmsa->vmsa.r9; + env->regs[R_R10] =3D launch_vmsa->vmsa.r10; + env->regs[R_R11] =3D launch_vmsa->vmsa.r11; + env->regs[R_R12] =3D launch_vmsa->vmsa.r12; + env->regs[R_R13] =3D launch_vmsa->vmsa.r13; + env->regs[R_R14] =3D launch_vmsa->vmsa.r14; + env->regs[R_R15] =3D launch_vmsa->vmsa.r15; +#endif + env->eip =3D launch_vmsa->vmsa.rip; + env->eflags =3D launch_vmsa->vmsa.rflags; + + cpu_set_fpuc(env, launch_vmsa->vmsa.x87_fcw); + env->mxcsr =3D launch_vmsa->vmsa.mxcsr; + + break; + } + } +} + +static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx, + uint32_t ctx_len, hwaddr gpa, Error **errp) +{ + SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); + SevLaunchVmsa *launch_vmsa; + CPUState *cpu; + bool exists =3D false; + + /* + * Setting the CPU context is only supported for SEV-ES and SEV-SNP. T= he + * context buffer will contain a sev_es_save_area from the Linux kernel + * which is defined by "Table B-4. VMSA Layout, State Save Area for SE= V-ES" + * in the AMD64 APM, Volume 2. + */ + + if (!sev_es_enabled()) { + error_setg(errp, "SEV: unable to set CPU context: Not supported"); + return -1; + } + + if (ctx_len < sizeof(struct sev_es_save_area)) { + error_setg(errp, "SEV: unable to set CPU context: " + "Invalid context provided"); + return -1; + } + + cpu =3D qemu_get_cpu(cpu_index); + if (!cpu) { + error_setg(errp, "SEV: unable to set CPU context for out of bounds= " + "CPU index %d", cpu_index); + return -1; + } + + /* + * If the context of this VP has already been set then replace it with= the + * new context. + */ + QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next) + { + if (cpu_index =3D=3D launch_vmsa->cpu_index) { + launch_vmsa->gpa =3D gpa; + memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa)); + exists =3D true; + break; + } + } + + if (!exists) { + /* New VP context */ + launch_vmsa =3D g_new0(SevLaunchVmsa, 1); + memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa)); + launch_vmsa->cpu_index =3D cpu_index; + launch_vmsa->gpa =3D gpa; + QTAILQ_INSERT_TAIL(&sev_common->launch_vmsa, launch_vmsa, next); + } + + /* Synchronise the VMSA with the current CPU state */ + sev_apply_cpu_context(cpu); + + return 0; +} + bool sev_enabled(void) { @@ -998,6 +1176,16 @@ static int sev_launch_update_vmsa(SevGuestState *sev_guest) { int ret, fw_error; + CPUState *cpu; + + /* + * The initial CPU state is measured as part of KVM_SEV_LAUNCH_UPDATE_= VMSA. + * Synchronise the CPU state to any provided launch VMSA structures. + */ + CPU_FOREACH(cpu) { + sev_apply_cpu_context(cpu); + } + =20 ret =3D sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE= _VMSA, NULL, &fw_error); @@ -1780,40 +1968,110 @@ sev_es_find_reset_vector(void *flash_ptr, uint64_t= flash_size, return sev_es_parse_reset_block(info, addr); } =20 -void sev_es_set_reset_vector(CPUState *cpu) + +static void seg_to_vmsa(const SegmentCache *cpu_seg, struct vmcb_seg *vmsa= _seg) { - X86CPU *x86; - CPUX86State *env; - ConfidentialGuestSupport *cgs =3D MACHINE(qdev_get_machine())->cgs; - SevCommonState *sev_common =3D SEV_COMMON( - object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON)); + vmsa_seg->selector =3D cpu_seg->selector; + vmsa_seg->base =3D cpu_seg->base; + vmsa_seg->limit =3D cpu_seg->limit; + vmsa_seg->attrib =3D FLAGS_SEGCACHE_TO_VMSA(cpu_seg->flags); +} =20 - /* Only update if we have valid reset information */ - if (!sev_common || !sev_common->reset_data_valid) { - return; - } +static void initialize_vmsa(const CPUState *cpu, struct sev_es_save_area *= vmsa) +{ + const X86CPU *x86 =3D X86_CPU(cpu); + const CPUX86State *env =3D &x86->env; =20 - /* Do not update the BSP reset state */ - if (cpu->cpu_index =3D=3D 0) { - return; + /* + * Initialize the SEV-ES save area from the current state of + * the CPU. The entire state does not need to be copied, only the state + * that is copied back to the CPUState in sev_apply_cpu_context. + */ + memset(vmsa, 0, sizeof(struct sev_es_save_area)); + vmsa->efer =3D env->efer; + vmsa->cr0 =3D env->cr[0]; + vmsa->cr3 =3D env->cr[3]; + vmsa->cr4 =3D env->cr[4]; + vmsa->xcr0 =3D env->xcr0; + vmsa->g_pat =3D env->pat; + + seg_to_vmsa(&env->segs[R_CS], &vmsa->cs); + seg_to_vmsa(&env->segs[R_DS], &vmsa->ds); + seg_to_vmsa(&env->segs[R_ES], &vmsa->es); + seg_to_vmsa(&env->segs[R_FS], &vmsa->fs); + seg_to_vmsa(&env->segs[R_GS], &vmsa->gs); + seg_to_vmsa(&env->segs[R_SS], &vmsa->ss); + + seg_to_vmsa(&env->gdt, &vmsa->gdtr); + seg_to_vmsa(&env->idt, &vmsa->idtr); + seg_to_vmsa(&env->ldt, &vmsa->ldtr); + seg_to_vmsa(&env->tr, &vmsa->tr); + + vmsa->dr6 =3D env->dr[6]; + vmsa->dr7 =3D env->dr[7]; + + vmsa->rax =3D env->regs[R_EAX]; + vmsa->rcx =3D env->regs[R_ECX]; + vmsa->rdx =3D env->regs[R_EDX]; + vmsa->rbx =3D env->regs[R_EBX]; + vmsa->rsp =3D env->regs[R_ESP]; + vmsa->rbp =3D env->regs[R_EBP]; + vmsa->rsi =3D env->regs[R_ESI]; + vmsa->rdi =3D env->regs[R_EDI]; + +#ifdef TARGET_X86_64 + vmsa->r8 =3D env->regs[R_R8]; + vmsa->r9 =3D env->regs[R_R9]; + vmsa->r10 =3D env->regs[R_R10]; + vmsa->r11 =3D env->regs[R_R11]; + vmsa->r12 =3D env->regs[R_R12]; + vmsa->r13 =3D env->regs[R_R13]; + vmsa->r14 =3D env->regs[R_R14]; + vmsa->r15 =3D env->regs[R_R15]; +#endif + + vmsa->rip =3D env->eip; + vmsa->rflags =3D env->eflags; +} + +static void sev_es_set_ap_context(uint32_t reset_addr) +{ + CPUState *cpu; + struct sev_es_save_area vmsa; + SegmentCache cs; + + cs.selector =3D 0xf000; + cs.base =3D reset_addr & 0xffff0000; + cs.limit =3D 0xffff; + cs.flags =3D DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | DESC_R_MASK | + DESC_A_MASK; + + CPU_FOREACH(cpu) { + if (cpu->cpu_index =3D=3D 0) { + /* Do not update the BSP reset state */ + continue; + } + initialize_vmsa(cpu, &vmsa); + seg_to_vmsa(&cs, &vmsa.cs); + vmsa.rip =3D reset_addr & 0x0000ffff; + sev_set_cpu_context(cpu->cpu_index, &vmsa, + sizeof(struct sev_es_save_area), + 0, &error_fatal); + sev_apply_cpu_context(cpu); } +} =20 - x86 =3D X86_CPU(cpu); - env =3D &x86->env; - - cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_common->reset_cs, 0xffff, - DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | - DESC_R_MASK | DESC_A_MASK); - - env->eip =3D sev_common->reset_ip; +void sev_es_set_reset_vector(CPUState *cpu) +{ + if (sev_enabled()) { + sev_apply_cpu_context(cpu); + } } =20 int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) { - CPUState *cpu; uint32_t addr; int ret; - SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); =20 if (!sev_es_enabled()) { return 0; @@ -1826,14 +2084,12 @@ int sev_es_save_reset_vector(void *flash_ptr, uint6= 4_t flash_size) return ret; } =20 + /* + * The reset vector is saved into a CPU context for each AP but not for + * the BSP. This is applied during guest startup or when the CPU is re= set. + */ if (addr) { - sev_common->reset_cs =3D addr & 0xffff0000; - sev_common->reset_ip =3D addr & 0x0000ffff; - sev_common->reset_data_valid =3D true; - - CPU_FOREACH(cpu) { - sev_es_set_reset_vector(cpu); - } + sev_es_set_ap_context(addr); } =20 return 0; @@ -2068,6 +2324,7 @@ sev_common_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "reduced-phys-bits", &sev_common->reduced_phys_bits, OBJ_PROP_FLAG_READWRITE); + QTAILQ_INIT(&sev_common->launch_vmsa); } =20 /* sev guest info common to sev/sev-es/sev-snp */ diff --git a/target/i386/sev.h b/target/i386/sev.h index 373669eaac..38caa849f5 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -55,6 +55,116 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; =20 +/* Save area definition for SEV-ES and SEV-SNP guests */ +struct QEMU_PACKED sev_es_save_area { + struct vmcb_seg es; + struct vmcb_seg cs; + struct vmcb_seg ss; + struct vmcb_seg ds; + struct vmcb_seg fs; + struct vmcb_seg gs; + struct vmcb_seg gdtr; + struct vmcb_seg ldtr; + struct vmcb_seg idtr; + struct vmcb_seg tr; + uint64_t vmpl0_ssp; + uint64_t vmpl1_ssp; + uint64_t vmpl2_ssp; + uint64_t vmpl3_ssp; + uint64_t u_cet; + uint8_t reserved_0xc8[2]; + uint8_t vmpl; + uint8_t cpl; + uint8_t reserved_0xcc[4]; + uint64_t efer; + uint8_t reserved_0xd8[104]; + uint64_t xss; + uint64_t cr4; + uint64_t cr3; + uint64_t cr0; + uint64_t dr7; + uint64_t dr6; + uint64_t rflags; + uint64_t rip; + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr0_addr_mask; + uint64_t dr1_addr_mask; + uint64_t dr2_addr_mask; + uint64_t dr3_addr_mask; + uint8_t reserved_0x1c0[24]; + uint64_t rsp; + uint64_t s_cet; + uint64_t ssp; + uint64_t isst_addr; + uint64_t rax; + uint64_t star; + uint64_t lstar; + uint64_t cstar; + uint64_t sfmask; + uint64_t kernel_gs_base; + uint64_t sysenter_cs; + uint64_t sysenter_esp; + uint64_t sysenter_eip; + uint64_t cr2; + uint8_t reserved_0x248[32]; + uint64_t g_pat; + uint64_t dbgctl; + uint64_t br_from; + uint64_t br_to; + uint64_t last_excp_from; + uint64_t last_excp_to; + uint8_t reserved_0x298[80]; + uint32_t pkru; + uint32_t tsc_aux; + uint8_t reserved_0x2f0[24]; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t reserved_0x320; /* rsp already available at 0x01d8 */ + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint8_t reserved_0x380[16]; + uint64_t guest_exit_info_1; + uint64_t guest_exit_info_2; + uint64_t guest_exit_int_info; + uint64_t guest_nrip; + uint64_t sev_features; + uint64_t vintr_ctrl; + uint64_t guest_exit_code; + uint64_t virtual_tom; + uint64_t tlb_id; + uint64_t pcpu_id; + uint64_t event_inj; + uint64_t xcr0; + uint8_t reserved_0x3f0[16]; + + /* Floating point area */ + uint64_t x87_dp; + uint32_t mxcsr; + uint16_t x87_ftw; + uint16_t x87_fsw; + uint16_t x87_fcw; + uint16_t x87_fop; + uint16_t x87_ds; + uint16_t x87_cs; + uint64_t x87_rip; + uint8_t fpreg_x87[80]; + uint8_t fpreg_xmm[256]; + uint8_t fpreg_ymm[256]; +}; + bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **err= p); =20 int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp= ); --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749826125; cv=none; d=zohomail.com; s=zohoarc; b=C735veX0cSgags6+S8jZo2ltEsJc0EOX7BmyTXD2058hPDdLxyxsfEu3MYd0mOTr03/wLIBwPdBq5BOjQebiUnl9oRwkZizzhjy7wwUEEBe1NbN+J9IqsVKkyTSh5WQ8VxOFNWGnINR9/OUx21EOIR9AKSrmoxybaV+jw2+O7aE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749826125; 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=I5Tl9nblzg3WLDjONe0dHu9PNjW5+06QKI/+CiwmX8k=; b=cPmwyesE2By403K616EA37dbQVXy698FJU+UR1wWD2/ZyU8ODuTJN0sYqIUeT5MavuZ5sz8VspyTtTLJPpJA725CqRprcrGIksCJQfMnr5jCkEvSvmWiB7LgYqtYxtqhk4NCVb5/LRn+xhsfYuJxwRiS4LtCIpjKTOg+o2aKGz8= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749826125127122.08969658492526; Fri, 13 Jun 2025 07:48:45 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ5gp-00006X-Kx; Fri, 13 Jun 2025 10:47:31 -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 1uQ5go-00006I-Cm for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:47:30 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ5gk-0006nr-RL for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:47:30 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.81]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 2417B40025; Fri, 13 Jun 2025 15:47:24 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 9A8E4400DC; Fri, 13 Jun 2025 15:47:19 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749826044; bh=CqQ9dhwnXiFmEwMC6nU/dnMz3Vpf6Y8jeR1nuNJpLwE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uhUDennTrqIKbs/U3POBxUWwrLaECYVNvaky7+kGtOrt0eQdN0+aTTHzNQuwbuVGW JTTzHDeKXAp1dcbZB3LIoAnZXu7K4EsiZWqEzmrAcT3xRpMDYmKW6V1RGNabtuWyOa XvlxhQrX+wtuwHD/D82k4PWs6P5TM/NRiZc5nTG4= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 09/16] i386/sev: Implement ConfidentialGuestSupport functions for SEV Date: Fri, 13 Jun 2025 15:11:49 +0100 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749826127296116600 Content-Type: text/plain; charset="utf-8" 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 --- target/i386/sev.c | 254 ++++++++++++++++++++++++++++++++++++++++++++-- target/i386/sev.h | 2 + 2 files changed, 246 insertions(+), 10 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 2e4cf9f6c1..fc57890d92 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); @@ -2291,6 +2401,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) { @@ -2314,6 +2542,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 @@ -2324,6 +2554,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 diff --git a/target/i386/sev.h b/target/i386/sev.h index 38caa849f5..d2eb06db32 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; --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749826108; cv=none; d=zohomail.com; s=zohoarc; b=kpoNQ+khbVVbDMwjBKoFqCCOZbtZCfMaAtwUodhAwV19DHmixnnNC+xrrJOJp6vhJu7CiHPiYoFVEnzZAosflFiO5zeU2bnmRtk2O9HbqoETXpLSM6OwUdJiWeDtCl1OXHqmWHRNHLunOdaW9OWxivoad8CxQafMlUhYRvADWQw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749826108; h=Content-Type: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=0CTFuUQ8eH+Mkcj+OE6Sd/uvRxqW+RqtH6i2bdFazZ0=; b=bSfLffHKGe86HNOUNHWVfNW6Chgh2ABPeEbM256d+xbUPlggKiCnjUANFpE+xtnDrj28Vvg/yZ246fi/DiBlLAfJ6rgiib3gEPbTInQjZGaQSDNAo1CQJBZeiOSUGAnCgMFjoopg1Q9zXLlR2lATr0iTi+xafGoX4aDxjjDPqOI= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749826107893545.1534953930516; Fri, 13 Jun 2025 07:48:27 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ5hR-0000Xh-SQ; Fri, 13 Jun 2025 10:48:13 -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 1uQ5gt-00009a-Pr for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:47:39 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ5gr-0006on-3N for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:47:35 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.84]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 003E320051; Fri, 13 Jun 2025 15:47:31 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 58544A0238; Fri, 13 Jun 2025 15:47:27 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749826052; bh=fFgtrBbqNCSfmX77EtWC9+PyNqIsW+H4zE3yndzYblA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hAduEQCtJX4+EnvpFCTcDERvdsszTLCMRAy8P23IOgUWgryyl9gPszONp4mDeGAmS J/bikbvf/YPjs8JdA2HMX3HkBB/LMFSa/dWhooHAvJiu5ppQ0aaY6n1FnyAqtHzW17 bioOs9qLaMvpJlOGqYq/VOc7uNfOMYAwNa1Aky1M= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 10/16] docs/system: Add documentation on support for IGVM Date: Fri, 13 Jun 2025 15:11:50 +0100 Message-ID: <78606676411e2f4b5e639b6dab65ce66a7b83b38.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749826109394116600 IGVM support has been implemented for Confidential Guests that support AMD SEV and AMD SEV-ES. Add some documentation that gives some background on the IGVM format and how to use it to configure a confidential guest. Signed-off-by: Roy Hopkins Reviewed-by: Daniel P. Berrang=C3=A9 Reviewed-by: Stefano Garzarella Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha --- docs/system/i386/amd-memory-encryption.rst | 2 + docs/system/igvm.rst | 173 +++++++++++++++++++++ docs/system/index.rst | 1 + 3 files changed, 176 insertions(+) create mode 100644 docs/system/igvm.rst diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/= amd-memory-encryption.rst index 748f5094ba..6c23f3535f 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -1,3 +1,5 @@ +.. _amd-sev: + AMD Secure Encrypted Virtualization (SEV) =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 diff --git a/docs/system/igvm.rst b/docs/system/igvm.rst new file mode 100644 index 0000000000..79508d9588 --- /dev/null +++ b/docs/system/igvm.rst @@ -0,0 +1,173 @@ +Independent Guest Virtual Machine (IGVM) support +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +IGVM files are designed to encapsulate all the information required to lau= nch a +virtual machine on any given virtualization stack in a deterministic way. = This +allows the cryptographic measurement of initial guest state for Confidenti= al +Guests to be calculated when the IGVM file is built, allowing a relying pa= rty to +verify the initial state of a guest via a remote attestation. + +Although IGVM files are designed with Confidential Computing in mind, they= can +also be used to configure non-confidential guests. Multiple platforms can = be +defined by a single IGVM file, allowing a single IGVM file to configure a +virtual machine that can run on, for example, TDX, SEV and non-confidential +hosts. + +QEMU supports IGVM files through the user-creatable ``igvm-cfg`` object. T= his +object is used to define the filename of the IGVM file to process. A refer= ence +to the object is added to the ``-machine`` to configure the virtual machine +to use the IGVM file for configuration. + +Confidential platform support is provided through the use of +the ``ConfidentialGuestSupport`` object. If the virtual machine provides an +instance of this object then this is used by the IGVM loader to configure = the +isolation properties of the directives within the file. + +Further Information on IGVM +--------------------------- + +Information about the IGVM format, including links to the format specifica= tion +and documentation for the Rust and C libraries can be found at the project +repository: + +https://github.com/microsoft/igvm + + +Supported Platforms +------------------- + +Currently, IGVM files can be provided for Confidential Guests on host syst= ems +that support AMD SEV, SEV-ES and SEV-SNP with KVM. IGVM files can also be +provided for non-confidential guests. + + +Limitations when using IGVM with AMD SEV, SEV-ES and SEV-SNP +------------------------------------------------------------ + +IGVM files configure the initial state of the guest using a set of directi= ves. +Not every directive is supported by every Confidential Guest type. For exa= mple, +AMD SEV does not support encrypted save state regions, therefore setting t= he +initial CPU state using IGVM for SEV is not possible. When an IGVM file co= ntains +directives that are not supported for the active platform, an error is gen= erated +and the guest launch is aborted. + +The table below describes the list of directives that are supported for SE= V, +SEV-ES, SEV-SNP and non-confidential platforms. + +.. list-table:: SEV, SEV-ES, SEV-SNP & non-confidential Supported Directiv= es + :widths: 35 65 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_PAGE_DATA + - ``NORMAL`` zero, measured and unmeasured page types are supported. = Other + page types result in an error. + * - IGVM_VHT_PARAMETER_AREA + - + * - IGVM_VHT_PARAMETER_INSERT + - + * - IGVM_VHT_VP_COUNT_PARAMETER + - The guest parameter page is populated with the CPU count. + * - IGVM_VHT_ENVIRONMENT_INFO_PARAMETER + - The ``memory_is_shared`` parameter is set to 1 in the guest paramet= er + page. + +.. list-table:: Additional SEV, SEV-ES & SEV_SNP Supported Directives + :widths: 25 75 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_MEMORY_MAP + - The memory map page is populated using entries from the E820 table. + * - IGVM_VHT_REQUIRED_MEMORY + - Ensures memory is available in the guest at the specified range. + +.. list-table:: Additional SEV-ES & SEV-SNP Supported Directives + :widths: 25 75 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_VP_CONTEXT + - Setting of the initial CPU state for the boot CPU and additional CP= Us is + supported with limitations on the fields that can be provided in the + VMSA. See below for details on which fields are supported. + +Initial CPU state with VMSA +--------------------------- + +The initial state of guest CPUs can be defined in the IGVM file for AMD SE= V-ES +and SEV-SNP. The state data is provided as a VMSA structure as defined in = Table +B-4 in the AMD64 Architecture Programmer's Manual, Volume 2 [1]. + +The IGVM VMSA is translated to CPU state in QEMU which is then synchronized +by KVM to the guest VMSA during the launch process where it contributes to= the +launch measurement. See :ref:`amd-sev` for details on the launch process a= nd +guest launch measurement. + +It is important that no information is lost or changed when translating the +VMSA provided by the IGVM file into the VSMA that is used to launch the gu= est. +Therefore, QEMU restricts the VMSA fields that can be provided in the IGVM +VMSA structure to the following registers: + +RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8-R15, RSP, RIP, CS, DS, ES, FS, GS, S= S, +CR0, CR3, CR4, XCR0, EFER, PAT, GDT, IDT, LDTR, TR, DR6, DR7, RFLAGS, X87_= FCW, +MXCSR. + +When processing the IGVM file, QEMU will check if any fields other than the +above are non-zero and generate an error if this is the case. + +KVM uses a hardcoded GPA of 0xFFFFFFFFF000 for the VMSA. When an IGVM file +defines initial CPU state, the GPA for each VMSA must match this hardcoded +value. + +Firmware Images with IGVM +------------------------- + +When an IGVM filename is specified for a Confidential Guest Support object= it +overrides the default handling of system firmware: the firmware image, suc= h as +an OVMF binary should be contained as a payload of the IGVM file and not +provided as a flash drive or via the ``-bios`` parameter. The default QEMU +firmware is not automatically populated into the guest memory space. + +If an IGVM file is provided along with either the ``-bios`` parameter or p= flash +devices then an error is displayed and the guest startup is aborted. + +Running a guest configured using IGVM +------------------------------------- + +To run a guest configured with IGVM you firstly need to generate an IGVM f= ile +that contains a guest configuration compatible with the platform you are +targeting. + +The ``buildigvm`` tool [2] is an example of a tool that can be used to gen= erate +IGVM files for non-confidential X86 platforms as well as for SEV, SEV-ES a= nd +SEV-SNP confidential platforms. + +Example using this tool to generate an IGVM file for AMD SEV-SNP:: + + buildigvm --firmware /path/to/OVMF.fd --output sev-snp.igvm \ + --cpucount 4 sev-snp + +To run a guest configured with the generated IGVM you need to add an +``igvm-cfg`` object and refer to it from the ``-machine`` parameter: + +Example (for AMD SEV):: + + qemu-system-x86_64 \ + \ + -machine ...,confidential-guest-support=3Dsev0,igvm-cfg=3Digvm0 \ + -object sev-guest,id=3Dsev0,cbitpos=3D47,reduced-phys-bits=3D1 \ + -object igvm-cfg,id=3Digvm0,file=3D/path/to/sev-snp.igvm + +References +---------- + +[1] AMD64 Architecture Programmer's Manual, Volume 2: System Programming + Rev 3.41 + https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/pro= grammer-references/24593.pdf + +[2] ``buildigvm`` - A tool to build example IGVM files containing OVMF fir= mware + https://github.com/roy-hopkins/buildigvm \ No newline at end of file diff --git a/docs/system/index.rst b/docs/system/index.rst index 718e9d3c56..427b020483 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -38,5 +38,6 @@ or Hypervisor.Framework. security multi-process confidential-guest-support + igvm vm-templating sriov --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749826750; cv=none; d=zohomail.com; s=zohoarc; b=V+kXbGl4r+z1iRWBwgLNFOq6+DiSHoB7KOmP0c3VxrUuMxpd8L0GxsL4rV8nyQIDGERHhjGpvqQEUJxeIsrLY6dF8hl+NY8tAQXDkjF4JQNsduozas9srzX9CJjS7z9RElkhmxzEhqCwa0nwZZqgdkq2AaPV7PAd3FeZqJ+M6WM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749826750; 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=hXPzzKfvHQRNC/Bfni7dzwJ/wO5DNhuPa36CNw7ZhYw=; b=iEH8+X+ncp0D1b6T9WM4R54c7qNr1XFOKAKri6+lw6x9nz1cLLSpizmFzv2gYlT1dATn9vjhwj0lYnFo76/OZi3Y9LidrF12kRaeMYcdL0ykYUBN8hvDAANhuXnk6PT3HPRFDzSwgXLJL1t3pcxCrc0yl1FUKzlmAWc3zkOAtsc= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749826750965645.6881120692884; Fri, 13 Jun 2025 07:59:10 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ5rR-0008BU-Fr; Fri, 13 Jun 2025 10:58:30 -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 1uQ5rE-0008Aw-2D for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:58:17 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ5rC-0008IQ-86 for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:58:15 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.82]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 1529C40238; Fri, 13 Jun 2025 15:58:12 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 8EF41C02CE; Fri, 13 Jun 2025 15:58:07 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749826692; bh=m/KpfL2N4w7qhT2MGX2IKCF5EKCVZd7L9Ka+XVTB8oc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lrDtATW4neP6RNhZEwHY51ke9HbpSMDq3Vh6TZ4qmaxp2Jzj5lvvSHx13hzRSu2EF fgmRhWxUA4WfJC1i1ii2hb4qZf4Gdzk0napthBzCqCmaN8c7JxO9+MwkbbvPUh7BtG /dvRrnN+qV2ErqQ4hn/qewGVf1WFAu8PzxpNyLjg= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 11/16] docs/interop/firmware.json: Add igvm to FirmwareDevice Date: Fri, 13 Jun 2025 15:11:51 +0100 Message-ID: <0527eab371d62ca011fb645d20f317d0ba662e21.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749826752684116600 Content-Type: text/plain; charset="utf-8" Create an enum entry within FirmwareDevice for 'igvm' to describe that an IGVM file can be used to map firmware into memory as an alternative to pre-existing firmware devices. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- docs/interop/firmware.json | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 745d21d822..0711b6f323 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -57,10 +57,17 @@ # # @memory: The firmware is to be mapped into memory. # +# @igvm: The firmware is defined by a file conforming to the IGVM +# specification and mapped into memory according to directives +# defined in the file. This is similar to @memory but may +# include additional processing defined by the IGVM file +# including initial CPU state or population of metadata into +# the guest address space. Since: 10.1 +# # Since: 3.0 ## { 'enum' : 'FirmwareDevice', - 'data' : [ 'flash', 'kernel', 'memory' ] } + 'data' : [ 'flash', 'kernel', 'memory', 'igvm' ] } =20 ## # @FirmwareArchitecture: @@ -377,6 +384,24 @@ { 'struct' : 'FirmwareMappingMemory', 'data' : { 'filename' : 'str' } } =20 +## +# @FirmwareMappingIgvm: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @igvm. +# +# @filename: Identifies the IGVM file containing the firmware executable +# along with other information used to configure the initial +# state of the guest. The IGVM file may be shared by multiple +# virtual machine definitions. This corresponds to creating +# an object on the command line with "-object igvm-cfg, +# file=3D@filename". +# +# Since: 10.1 +## +{ 'struct' : 'FirmwareMappingIgvm', + 'data' : { 'filename' : 'str' } } + ## # @FirmwareMapping: # @@ -393,7 +418,8 @@ 'discriminator' : 'device', 'data' : { 'flash' : 'FirmwareMappingFlash', 'kernel' : 'FirmwareMappingKernel', - 'memory' : 'FirmwareMappingMemory' } } + 'memory' : 'FirmwareMappingMemory', + 'igvm' : 'FirmwareMappingIgvm' } } =20 ## # @Firmware: --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749826757; cv=none; d=zohomail.com; s=zohoarc; b=W8L3LbWNlC5Nm0K1XWUeVzxRoS6AGYhEtsELNj6ut9bnxgUn1UfgcKkf1SpKB7f3afG/SMi/qEb7gqAEaJ/efO8nSYljKg1VSFWeID4wY8gMSiEWFmc1xD3T97uRP0POE1/Yt/wrmZ5YdcBcM3hs73u1hhkdKqYoTR13HaxUSLc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749826757; h=Content-Type: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=Bj0S0aQ2NFdim9APBVmOVAQDoAZiFU9V3Bmq+5VeJOg=; b=JAA2tONBb7Ruyh48Yg3RmPHuas678DjmDr4lOth0yzG+MmljOBAtQ2gVwh5qW0D/ctGlRwajIVI0Jmuqn4jOczzQat1vZBJU42bJEiVKamxiL8yFQ0P8C4XoQ5bpNBBs0wkuO9nALGYWE2ojvwidYbOlSRHFORABhxAl0AEpkMI= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749826757743355.7678364005901; Fri, 13 Jun 2025 07:59:17 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ5rV-0008CO-6W; Fri, 13 Jun 2025 10:58:33 -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 1uQ5rO-0008Ba-Mg for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:58:28 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ5rL-0008Im-Et for qemu-devel@nongnu.org; Fri, 13 Jun 2025 10:58:25 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.82]) by dkim.livemail.co.uk (Postfix) with ESMTPS id F0082A03C3; Fri, 13 Jun 2025 15:58:21 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 657B8C03ED; Fri, 13 Jun 2025 15:58:17 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749826702; bh=BmgYGfqxadfNjre5CbBsoPtvW6352tZwhB9WZhQQwgc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lGxnxoiGLa0XLnVMlZHgwcxpC3TzRXMdlux4ZCDAifUSYFNx+o9qDf+aACDnWLZAE SXq5Zu6GmEwBX6z5Xi7HqI4YEy+RSrZ9bOl/p/sTJdjg8gEf/I4bIOto31YS6vW2cD aLENzvtVR5ER+Im1qNtsk19joPc0exk3JISrCTQA= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 12/16] backends/confidential-guest-support: Add set_guest_policy() function Date: Fri, 13 Jun 2025 15:11:52 +0100 Message-ID: <65089ffb0e3e935e8f046098db3cd2c2f7b0d950.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749826758660116600 For confidential guests a policy can be provided that defines the security level, debug status, expected launch measurement and other parameters that define the configuration of the confidential platform. This commit adds a new function named set_guest_policy() that can be implemented by each confidential platform, such as AMD SEV to set the policy. This will allow configuration of the policy from a multi-platform resource such as an IGVM file without the IGVM processor requiring specific implementation details for each platform. Signed-off-by: Roy Hopkins Reviewed-by: Daniel P. Berrang=C3=A9 Reviewed-by: Stefano Garzarella Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha --- backends/confidential-guest-support.c | 12 ++++++++++++ include/system/confidential-guest-support.h | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/backends/confidential-guest-support.c b/backends/confidential-= guest-support.c index c5bef1fbfa..156dd15e66 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -38,6 +38,17 @@ static int set_guest_state(hwaddr gpa, uint8_t *ptr, uin= t64_t len, return -1; } =20 +static int set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp) +{ + error_setg(errp, + "Setting confidential guest policy is not supported for thi= s platform"); + return -1; +} + static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *e= ntry, Error **errp) { @@ -53,6 +64,7 @@ static void confidential_guest_support_class_init(ObjectC= lass *oc, ConfidentialGuestSupportClass *cgsc =3D CONFIDENTIAL_GUEST_SUPPORT_CLA= SS(oc); cgsc->check_support =3D check_support; cgsc->set_guest_state =3D set_guest_state; + cgsc->set_guest_policy =3D set_guest_policy; cgsc->get_mem_map_entry =3D get_mem_map_entry; } =20 diff --git a/include/system/confidential-guest-support.h b/include/system/c= onfidential-guest-support.h index 79ecd21f42..0cc8b26e64 100644 --- a/include/system/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -57,6 +57,10 @@ typedef enum ConfidentialGuestPageType { CGS_PAGE_TYPE_REQUIRED_MEMORY, } ConfidentialGuestPageType; =20 +typedef enum ConfidentialGuestPolicyType { + GUEST_POLICY_SEV, +} ConfidentialGuestPolicyType; + struct ConfidentialGuestSupport { Object parent; =20 @@ -123,6 +127,23 @@ typedef struct ConfidentialGuestSupportClass { ConfidentialGuestPageType memory_type, uint16_t cpu_index, Error **errp); =20 + /* + * Set the guest policy. The policy can be used to configure the + * confidential platform, such as if debug is enabled or not and can c= ontain + * information about expected launch measurements, signed verification= of + * guest configuration and other platform data. + * + * The format of the policy data is specific to each platform. For exa= mple, + * SEV-SNP uses a policy bitfield in the 'policy' argument and provide= s an + * ID block and ID authentication in the 'policy_data' parameters. The= type + * of policy data is identified by the 'policy_type' argument. + */ + int (*set_guest_policy)(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp); + /* * Iterate the system memory map, getting the entry with the given ind= ex * that can be populated into guest memory. --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749828152; cv=none; d=zohomail.com; s=zohoarc; b=DOB3l3krSwBdCe1kbbiBm6QwNY8iDJcfMkax8ZlJtbM4T6oQU1fT+EsjMX0PUV84ecwk3DunB+G/RV3GeDvcnkuwNCAzTq6COwVjLGReqgsCTIlUjkbNb9a0VNlmGV7RLdV6cdVYOnNE8ujf1vSDFlbuvYZzL9dVhrruykEAbis= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749828152; 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=qG3yjxC0sGLSRdTVIMO6a4GdSMrxYXOzVEly6cR/QM4=; b=UjCvn8t7zgmOPW/vPZCmlgKO+VGBeUu/1hZIFUeEu/J53nxxQSeCF2wuKFodQrCYDxOvZqE50DaQjnIjmEf36dVoXpE0Vm7/0RL6ncIKf+l9PRukjjlRk0ozEPaPEGsLnkaB2ULYaYwFyuArQwfPougUphI0QD4w5Ty/acTUzJI= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749828152116964.405086105916; Fri, 13 Jun 2025 08:22:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ6EW-0007Kk-IH; Fri, 13 Jun 2025 11:22:20 -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 1uQ6EU-0007KT-LE for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:22:18 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ6EO-0003ij-5A for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:22:18 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.81]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 9FBF3402D6; Fri, 13 Jun 2025 16:22:09 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 1D296403E0; Fri, 13 Jun 2025 16:22:05 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749828129; bh=ecTIuxyp7oiXl3s93ruq/tFQSppGfPgY6uUdoS1LyCg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hnLXU1awgUEXUi2n+LNazat07R/YmaIoQWM+GTOAWX3obY3eq6RY49mvxmc13uhFT 1MScm+kXDG4CsmEfzc4F8Ne2cA1ke5Zi6MEhwpKeF7Qv8k+co9gzl/c/15ro6YO73o SO8jRl7Hx27mkOzatCAyCRk9ORV72FaaKTZdFfA4= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 13/16] backends/igvm: Process initialization sections in IGVM file Date: Fri, 13 Jun 2025 16:20:29 +0100 Message-ID: <12d0e71fd1b37ae132f78498d497cb72b90da5d1.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749828154344116600 Content-Type: text/plain; charset="utf-8" The initialization sections in IGVM files contain configuration that should be applied to the guest platform before it is started. This includes guest policy and other information that can affect the security level and the startup measurement of a guest. This commit introduces handling of the initialization sections during processing of the IGVM file. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha --- backends/igvm.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/backends/igvm.c b/backends/igvm.c index 2a31021d44..ebdb4594d1 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -786,6 +786,27 @@ int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuest= Support *cgs, } } =20 + header_count =3D + igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); + if (header_count < 0) { + error_setg( + errp, + "Invalid initialization header count in IGVM file. Error code:= %X", + header_count); + goto cleanup_parameters; + } + + for (ctx.current_header_index =3D 0; + ctx.current_header_index < (unsigned)header_count; + ctx.current_header_index++) { + IgvmVariableHeaderType type =3D + igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZAT= ION, + ctx.current_header_index); + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } + } + /* * Contiguous pages of data with compatible flags are grouped together= in * order to reduce the number of memory regions we create. Make sure t= he --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749828188; cv=none; d=zohomail.com; s=zohoarc; b=RljF1IL6Z5+gQy6Skg9NxvcypZiH4b9MfUKph9VnahYrhGiCWJa8xA2bAmGF6AaTQlD8uu+EZoWHForVKyGEc0U3WcgHID5SlZefSH4dd52W6IRdhgSn29EtAHJjdYz9X2B0Gdl81Mmpeyy7cuQmpIMD4jEkEg4LkOz/fJbXOe8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749828188; 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=4e9WDlvF4h8Yap3Y7MCyKFeB3RPVpJIgOJaghqrPsZI=; b=cfPdH7K7MYU7Oncsptteb7n8DilFf5MRBfhtlVOENgcATct+9mKek/f5BxqPHLeZDWsrl62ixSC/pAPkSrF5knbqVjPGYOVRkNZF0kojfUnT4vEWVX8bFesDA0DMNK6fa1svwb3yG2+xotyczWihnHXoncd6EI0h4DBkBG5QZIc= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749828188791706.0248900068317; Fri, 13 Jun 2025 08:23:08 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ6F0-0007fi-7D; Fri, 13 Jun 2025 11:22:50 -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 1uQ6Ey-0007ae-LP for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:22:48 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ6Ew-0003nn-49 for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:22:47 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.81]) by dkim.livemail.co.uk (Postfix) with ESMTPS id 2C59840238; Fri, 13 Jun 2025 16:22:44 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 901C6404E2; Fri, 13 Jun 2025 16:22:39 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749828164; bh=50J6NrW69gahyMUF3p55BNJCfcp2MKO2gvoPTtCePKk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VHSUrk1Bg6uJsYn1fk1NADFsXXsXrShnrybrr8KyTZXTevVxiyk5XsfSUm7RSi640 lp2ZbNTYS9d225XiCJleDCTIUfTUkb5aCaiWgXRBE7eJ0Wc8LCFJBx0AetJVcTV9AA LmRwnokCQanRUThl8XpQqPYMmpb4H9EJOesLWTyk= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 14/16] backends/igvm: Handle policy for SEV guests Date: Fri, 13 Jun 2025 16:22:24 +0100 Message-ID: <0d01d74fdf37f13512b91f2b9cc349458b4cbab9.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749828190772116600 Content-Type: text/plain; charset="utf-8" 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 Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha --- 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; =20 +/* + * Some directives are specific to particular confidential computing platf= orms. + * 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. @@ -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; =20 /* 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); =20 struct QIGVMHandler { uint32_t type; @@ -91,6 +134,10 @@ static struct QIGVMHandler handlers[] =3D { 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 }, }; =20 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; } =20 +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_= data, + Error **errp) +{ + const IGVM_VHS_SNP_ID_BLOCK *igvm_id =3D + (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 =3D g_new0(struct sev_id_block, 1); + ctx->id_auth =3D 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 =3D igvm_id->guest_svn; + ctx->id_block->version =3D 1; + memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld)); + + ctx->id_auth->id_key_alg =3D 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 =3D 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) =3D 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) =3D + 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 =3D + (const IGVM_VHS_GUEST_POLICY *)header_data; + + if (guest->compatibility_mask & ctx->compatibility_mask) { + ctx->sev_policy =3D 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 !=3D 0) { ctx->compatibility_mask =3D compatibility_mask_sev_snp; + ctx->platform_type =3D IGVM_PLATFORM_TYPE_SEV_SNP; } else if (compatibility_mask_sev_es !=3D 0) { ctx->compatibility_mask =3D compatibility_mask_sev_es; + ctx->platform_type =3D IGVM_PLATFORM_TYPE_SEV_ES; } else if (compatibility_mask_sev !=3D 0) { ctx->compatibility_mask =3D compatibility_mask_sev; + ctx->platform_type =3D IGVM_PLATFORM_TYPE_SEV; } else if (compatibility_mask !=3D 0) { ctx->compatibility_mask =3D compatibility_mask; + ctx->platform_type =3D 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; } =20 +static int qigvm_handle_policy(QIgvm *ctx, Error **errp) +{ + if (ctx->platform_type =3D=3D IGVM_PLATFORM_TYPE_SEV_SNP) { + int id_block_len =3D 0; + int id_auth_len =3D 0; + if (ctx->id_block) { + ctx->id_block->policy =3D ctx->sev_policy; + id_block_len =3D sizeof(struct sev_id_block); + id_auth_len =3D sizeof(struct sev_id_authentication); + } + return ctx->cgsc->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_poli= cy, + 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, ConfidentialGues= tSupport *cgs, */ retval =3D qigvm_process_mem_page(&ctx, NULL, errp); =20 + if (retval =3D=3D 0) { + retval =3D qigvm_handle_policy(&ctx, errp); + } + cleanup_parameters: QTAILQ_FOREACH(parameter, &ctx.parameter_data, next) { g_free(parameter->data); parameter->data =3D NULL; } + g_free(ctx.id_block); + g_free(ctx.id_auth); =20 cleanup: igvm_free(ctx.file); --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749828799; cv=none; d=zohomail.com; s=zohoarc; b=KCUCGrhsnypa+xdDYvQT7odlXFJIVorQHY8wMwz5CiC30KYMmrK8aGoFRX3QrMFNkGo9cVoUMc8R2ltAvI0O8JCQjvuw9M6vCDJUdqDv2LMrpHRqIS/3N6NIkRq41SWFzatgch2X7rUyVF7VNGLeFoH5f1VVV+KrYugUHVp+Lx0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749828799; 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=7cV2NeQI8y1Q4oVGDgxysdDpTtTLsTZVt7u33DrOMg0=; b=D8T5OcgSV+cf/zNyRPUY1tsIrjQqukDfaXAJNs634dy3sM/sZKKahpoDWj5N/kKIK6sk/bsr2RIPKSJfw9QN8/X9vqZb0nxTzqfwXluB7xp25jAewoG5MXTgYsJJR38DGTxsKMpfLnRPBavU0bQIUpe8iklkkRJd60kvJIO5J64= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749828799804978.6421313210087; Fri, 13 Jun 2025 08:33:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ6Ok-0002z5-DA; Fri, 13 Jun 2025 11:32:54 -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 1uQ6Od-0002wn-08 for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:32:49 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ6Oa-0005VE-RX for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:32:46 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.82]) by dkim.livemail.co.uk (Postfix) with ESMTPS id A91201800EC; Fri, 13 Jun 2025 16:32:42 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 30B0DC0513; Fri, 13 Jun 2025 16:32:38 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749828762; bh=KQMKRLs/eJYqV0gt1qwUclduu+Ajpw2h9y3uwcgk6KE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VGU8FAAbqQEKCCqkh/+4d41KVH9bwc4cbffOXzsxTifhykFGczomrUg1FSp1OkPpM TuMrXPAkIP2XD/ebs7OrnXTMNrqpBevft0PzfdAZPnxTD1vKXRQhK4lLBZcsdoT5YU gfWFvXDLlQlWsrlucwb48OQNYxF1FLBBw7zMs5fw= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 15/16] i386/sev: Add implementation of CGS set_guest_policy() Date: Fri, 13 Jun 2025 16:32:25 +0100 Message-ID: <5620b6c752f5c19a773d688a80ae1321a63a8386.1749820158.git.roy.hopkins@randomman.co.uk> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749828801693116600 Content-Type: text/plain; charset="utf-8" The new cgs_set_guest_policy() function is provided to receive the guest policy flags, SNP ID block and SNP ID authentication from guest configuration such as an IGVM file and apply it to the platform prior to launching the guest. The policy is used to populate values for the existing 'policy', 'id_block' and 'id_auth' parameters. When provided, the guest policy is applied and the ID block configuration is used to verify the launch measurement and signatures. The guest is only successfully started if the expected launch measurements match the actual measurements and the signatures are valid. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha --- target/i386/sev.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ target/i386/sev.h | 12 +++++++ 2 files changed, 95 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index fc57890d92..2798fe1c38 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2519,6 +2519,88 @@ static int cgs_get_mem_map_entry(int index, return 0; } =20 +static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, void *policy_data1, + uint32_t policy_data1_size, void *policy_d= ata2, + uint32_t policy_data2_size, Error **errp) +{ + if (policy_type !=3D GUEST_POLICY_SEV) { + error_setg(errp, "%s: Invalid guest policy type provided for SEV: = %d", + __func__, policy_type); + return -1; + } + /* + * SEV-SNP handles policy differently. The policy flags are defined in + * kvm_start_conf.policy and an ID block and ID auth can be provided. + */ + if (sev_snp_enabled()) { + SevSnpGuestState *sev_snp_guest =3D + SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs); + struct kvm_sev_snp_launch_finish *finish =3D + &sev_snp_guest->kvm_finish_conf; + + /* + * The policy consists of flags in 'policy' and optionally an ID b= lock + * and ID auth in policy_data1 and policy_data2 respectively. The = ID + * block and auth are optional so clear any previous ID block and = auth + * and set them if provided, but always set the policy flags. + */ + g_free(sev_snp_guest->id_block); + g_free((guchar *)finish->id_block_uaddr); + g_free(sev_snp_guest->id_auth); + g_free((guchar *)finish->id_auth_uaddr); + sev_snp_guest->id_block =3D NULL; + finish->id_block_uaddr =3D 0; + sev_snp_guest->id_auth =3D NULL; + finish->id_auth_uaddr =3D 0; + + if (policy_data1_size > 0) { + struct sev_snp_id_authentication *id_auth =3D + (struct sev_snp_id_authentication *)policy_data2; + + if (policy_data1_size !=3D KVM_SEV_SNP_ID_BLOCK_SIZE) { + error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect = size", + __func__); + return -1; + } + if (policy_data2_size !=3D KVM_SEV_SNP_ID_AUTH_SIZE) { + error_setg(errp, + "%s: Invalid SEV-SNP ID auth block: incorrect s= ize", + __func__); + return -1; + } + assert(policy_data1 !=3D NULL); + assert(policy_data2 !=3D NULL); + + finish->id_block_uaddr =3D + (__u64)g_memdup2(policy_data1, KVM_SEV_SNP_ID_BLOCK_SIZE); + finish->id_auth_uaddr =3D + (__u64)g_memdup2(policy_data2, KVM_SEV_SNP_ID_AUTH_SIZE); + + /* + * Check if an author key has been provided and use that to fl= ag + * whether the author key is enabled. The first of the author = key + * must be non-zero to indicate the key type, which will curre= ntly + * always be 2. + */ + sev_snp_guest->kvm_finish_conf.auth_key_en =3D + id_auth->author_key[0] ? 1 : 0; + finish->id_block_en =3D 1; + } + sev_snp_guest->kvm_start_conf.policy =3D policy; + } else { + SevGuestState *sev_guest =3D SEV_GUEST(MACHINE(qdev_get_machine())= ->cgs); + /* Only the policy flags are supported for SEV and SEV-ES */ + if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_gue= st) { + error_setg(errp, "%s: An ID block/ID auth block has been provi= ded " + "but SEV-SNP is not enabled", __func__); + return -1; + } + sev_guest->policy =3D policy; + } + return 0; +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2557,6 +2639,7 @@ sev_common_instance_init(Object *obj) 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; + cgs->set_guest_policy =3D cgs_set_guest_policy; =20 QTAILQ_INIT(&sev_common->launch_vmsa); } diff --git a/target/i386/sev.h b/target/i386/sev.h index d2eb06db32..9db1a802f6 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -167,6 +167,18 @@ struct QEMU_PACKED sev_es_save_area { uint8_t fpreg_ymm[256]; }; =20 +struct QEMU_PACKED sev_snp_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]; +}; + bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **err= p); =20 int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp= ); --=20 2.43.0 From nobody Sun Oct 12 14:46:26 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=none dis=none) header.from=randomman.co.uk ARC-Seal: i=1; a=rsa-sha256; t=1749828799; cv=none; d=zohomail.com; s=zohoarc; b=U6eMyZN7mYQM2r1sHTbFpfo6LgtcVNF4ZFAGbeMrJezLV8O/3TAkGvjParYSOuRY5Itlx0wsbzDjnZFPTyLNXTCVxdcO58yds0VUgHmxZCRGKQN70Tz7nawTX9whwyeOIOn8CFH+rnghdWKaSQyzKAiSDezuHYAQgp4/c0XKiHk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749828799; 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=r4CjSD1iXLbvUkWWVMkRfYc/xRNXuzjLMs/YY76+C2Q=; b=CueTVO4NEnhiHfRIEGC0UioP8w0fY4qGlLKQsBprRj0L+IyL+wnOXUXpt6q2vgWZHHIAsYCExK/zWkilYS1zIDIpObFtbH6JEJh+wytQA2alkKTV0DT7HyT9aF67LsGheGydPg1kQY6sG4od88aPNTsuKfB++Ol+ch7G4vPK6Yg= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749828799918761.0492826085684; Fri, 13 Jun 2025 08:33:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uQ6Om-00030n-U5; Fri, 13 Jun 2025 11:32:56 -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 1uQ6Om-00030W-6j for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:32:56 -0400 Received: from smtp-out-60.livemail.co.uk ([213.171.216.60] helo=dkim.livemail.co.uk) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uQ6Oi-0005WZ-SO for qemu-devel@nongnu.org; Fri, 13 Jun 2025 11:32:55 -0400 Received: from smtp.livemail.co.uk (unknown [10.44.132.84]) by dkim.livemail.co.uk (Postfix) with ESMTPS id A7A86401DA; Fri, 13 Jun 2025 16:32:50 +0100 (BST) Received: from localhost.localdomain (unknown [145.40.191.116]) (Authenticated sender: roy.hopkins@randomman.co.uk) by smtp.livemail.co.uk (Postfix) with ESMTPSA id 1256DA0232; Fri, 13 Jun 2025 16:32:46 +0100 (BST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=randomman.co.uk; s=livemail2; t=1749828770; bh=HsR51g/RYvSxr0PjCbXbzIbeC0wc9IOog+RU+6oMGhY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TRwjekyiidGuq8/k5AXU3Vdcj3ybQmAYKrwmoDStRNDRjeQkqa3iLPqnymSIdhQbl /DxUJ4D/cStckJIP64r7LdospqzchjTvnt4fWYywu0vP2LLOyLJHWmm24Lfv+mw3Gx Xuj29l5fjdj8niqjOhAfQsCNnHfclLooITfNCw5g= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , "Daniel P . Berrange" , Stefano Garzarella , Marcelo Tosatti , "Michael S . Tsirkin" , Cornelia Huck , Marcel Apfelbaum , Sergio Lopez , Eduardo Habkost , Alistair Francis , Peter Xu , David Hildenbrand , Igor Mammedov , Tom Lendacky , Michael Roth , Ani Sinha , Gerd Hoffman , Pankaj Gupta , Joerg Roedel Subject: [PATCH v8 16/16] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2 Date: Fri, 13 Jun 2025 16:32:26 +0100 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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=213.171.216.60; envelope-from=roy.hopkins@randomman.co.uk; helo=dkim.livemail.co.uk 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, 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_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_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 @randomman.co.uk) X-ZM-MESSAGEID: 1749828801909116600 Content-Type: text/plain; charset="utf-8" IGVM files can contain an initial VMSA that should be applied to each vcpu as part of the initial guest state. The sev_features flags are provided as part of the VMSA structure. However, KVM only allows sev_features to be set during initialization and not as the guest is being prepared for launch. This patch queries KVM for the supported set of sev_features flags and processes the VP context entries in the IGVM file during kvm_init to determine any sev_features flags set in the IGVM file. These are then provided in the call to KVM_SEV_INIT2 to ensure the guest state matches that specified in the IGVM file. The igvm process() function is modified to allow a partial processing of the file during initialization, with on the IGVM_VHT_VP_CONTEXT fields being processed. This means the function is called twice, firstly to extract the sev_features then secondly to actually configure the guest. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Tested-by: Stefano Garzarella Reviewed-by: Ani Sinha Reviewed-by: Liam Merwick --- backends/igvm.c | 17 +++- backends/igvm.h | 2 +- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- include/system/igvm-cfg.h | 5 +- target/i386/sev.c | 161 +++++++++++++++++++++++++++++++++----- 6 files changed, 163 insertions(+), 26 deletions(-) diff --git a/backends/igvm.c b/backends/igvm.c index 034f504716..9853ba941e 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -869,7 +869,7 @@ static IgvmHandle qigvm_file_init(char *filename, Error= **errp) } =20 int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, - Error **errp) + bool onlyVpContext, Error **errp) { int32_t header_count; QIgvmParameterData *parameter; @@ -913,11 +913,22 @@ int qigvm_process_file(IgvmCfg *cfg, ConfidentialGues= tSupport *cgs, ctx.current_header_index++) { IgvmVariableHeaderType type =3D igvm_get_header_type( ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_in= dex); - if (qigvm_handler(&ctx, type, errp) < 0) { - goto cleanup_parameters; + if (!onlyVpContext || (type =3D=3D IGVM_VHT_VP_CONTEXT)) { + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } } } =20 + /* + * If only processing the VP context then we don't need to process + * any more of the file. + */ + if (onlyVpContext) { + retval =3D 0; + goto cleanup_parameters; + } + header_count =3D igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); if (header_count < 0) { diff --git a/backends/igvm.h b/backends/igvm.h index db02ea9165..a4abab043a 100644 --- a/backends/igvm.h +++ b/backends/igvm.h @@ -17,6 +17,6 @@ #include "qapi/error.h" =20 int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs, - Error **errp); + bool onlyVpContext, Error **errp); =20 #endif diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3184ea1b37..a3285fbc64 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -371,7 +371,7 @@ static void pc_init1(MachineState *machine, const char = *pci_type) /* Apply guest state from IGVM if supplied */ if (x86ms->igvm) { if (IGVM_CFG_GET_CLASS(x86ms->igvm) - ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) = < 0) { g_assert_not_reached(); } } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6990e1c669..cf871cfdad 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -330,7 +330,7 @@ static void pc_q35_init(MachineState *machine) /* Apply guest state from IGVM if supplied */ if (x86ms->igvm) { if (IGVM_CFG_GET_CLASS(x86ms->igvm) - ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) = < 0) { g_assert_not_reached(); } } diff --git a/include/system/igvm-cfg.h b/include/system/igvm-cfg.h index 321b3196f0..944f23a814 100644 --- a/include/system/igvm-cfg.h +++ b/include/system/igvm-cfg.h @@ -31,11 +31,14 @@ typedef struct IgvmCfgClass { /* * If an IGVM filename has been specified then process the IGVM file. * Performs a no-op if no filename has been specified. + * If onlyVpContext is true then only the IGVM_VHT_VP_CONTEXT entries + * in the IGVM file will be processed, allowing information about the + * CPU state to be determined before processing the entire file. * * Returns 0 for ok and -1 on error. */ int (*process)(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, - Error **errp); + bool onlyVpContext, Error **errp); =20 } IgvmCfgClass; =20 diff --git a/target/i386/sev.c b/target/i386/sev.c index 2798fe1c38..df55a25a67 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -118,6 +118,8 @@ struct SevCommonState { uint32_t cbitpos; uint32_t reduced_phys_bits; bool kernel_hashes; + uint64_t sev_features; + uint64_t supported_sev_features; =20 /* runtime state */ uint8_t api_major; @@ -485,7 +487,40 @@ static void sev_apply_cpu_context(CPUState *cpu) } } =20 -static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area = *vmsa, +static int check_sev_features(SevCommonState *sev_common, uint64_t sev_fea= tures, + Error **errp) +{ + /* + * Ensure SEV_FEATURES is configured for correct SEV hardware and that + * the requested features are supported. If SEV-SNP is enabled then + * that feature must be enabled, otherwise it must be cleared. + */ + if (sev_snp_enabled() && !(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg( + errp, + "%s: SEV_SNP is enabled but is not enabled in VMSA sev_feature= s", + __func__); + return -1; + } else if (!sev_snp_enabled() && + (sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg( + errp, + "%s: SEV_SNP is not enabled but is enabled in VMSA sev_feature= s", + __func__); + return -1; + } + if (sev_features & ~sev_common->supported_sev_features) { + error_setg(errp, + "%s: VMSA contains unsupported sev_features: %lX, " + "supported features: %lX", + __func__, sev_features, sev_common->supported_sev_featu= res); + return -1; + } + return 0; +} + +static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, + const struct sev_es_save_area *vmsa, Error **errp) { struct sev_es_save_area vmsa_check; @@ -551,24 +586,10 @@ static int check_vmsa_supported(hwaddr gpa, const str= uct sev_es_save_area *vmsa, vmsa_check.x87_fcw =3D 0; vmsa_check.mxcsr =3D 0; =20 - 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 (check_sev_features(sev_common, vmsa_check.sev_features, errp) < 0)= { + return -1; } + vmsa_check.sev_features =3D 0; =20 if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { error_setg(errp, @@ -1722,6 +1743,39 @@ static int sev_snp_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_SNP_VM; } =20 +static int sev_init_supported_features(ConfidentialGuestSupport *cgs, + SevCommonState *sev_common, Error *= *errp) +{ + X86ConfidentialGuestClass *x86_klass =3D + X86_CONFIDENTIAL_GUEST_GET_CLASS(cgs); + /* + * Older kernels do not support query or setting of sev_features. In t= his + * case the set of supported features must be zero to match the settin= gs + * in the kernel. + */ + if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) =3D=3D + KVM_X86_DEFAULT_VM) { + sev_common->supported_sev_features =3D 0; + return 0; + } + + /* Query KVM for the supported set of sev_features */ + struct kvm_device_attr attr =3D { + .group =3D KVM_X86_GRP_SEV, + .attr =3D KVM_X86_SEV_VMSA_FEATURES, + .addr =3D (unsigned long)&sev_common->supported_sev_features, + }; + if (kvm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr) < 0) { + error_setg(errp, "%s: failed to query supported sev_features", + __func__); + return -1; + } + if (sev_snp_enabled()) { + sev_common->supported_sev_features |=3D SVM_SEV_FEAT_SNP_ACTIVE; + } + return 0; +} + static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { char *devname; @@ -1802,6 +1856,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupp= ort *cgs, Error **errp) } } =20 + if (sev_init_supported_features(cgs, sev_common, errp) < 0) { + return -1; + } + trace_kvm_sev_init(); switch (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common))) { case KVM_X86_DEFAULT_VM: @@ -1813,6 +1871,40 @@ static int sev_common_kvm_init(ConfidentialGuestSupp= ort *cgs, Error **errp) case KVM_X86_SEV_ES_VM: case KVM_X86_SNP_VM: { struct kvm_sev_init args =3D { 0 }; + MachineState *machine =3D MACHINE(qdev_get_machine()); + X86MachineState *x86machine =3D X86_MACHINE(qdev_get_machine()); + + /* + * If configuration is provided via an IGVM file then the IGVM file + * might contain configuration of the initial vcpu context. For SEV + * the vcpu context includes the sev_features which should be appl= ied + * to the vcpu. + * + * KVM does not synchronize sev_features from CPU state. Instead it + * requires sev_features to be provided as part of this initializa= tion + * call which is subsequently automatically applied to the VMSA of + * each vcpu. + * + * The IGVM file is normally processed after initialization. There= fore + * we need to pre-process it here to extract sev_features in order= to + * provide it to KVM_SEV_INIT2. Each cgs_* function that is called= by + * the IGVM processor detects this pre-process by observing the st= ate + * as SEV_STATE_UNINIT. + */ + if (x86machine->igvm) { + if (IGVM_CFG_GET_CLASS(x86machine->igvm) + ->process(x86machine->igvm, machine->cgs, true, errp) = =3D=3D + -1) { + return -1; + } + /* + * KVM maintains a bitmask of allowed sev_features. This does = not + * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by= KVM + * itself. Therefore we need to clear this flag. + */ + args.vmsa_features =3D sev_common->sev_features & + ~SVM_SEV_FEAT_SNP_ACTIVE; + } =20 ret =3D sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_er= ror); break; @@ -2417,6 +2509,24 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *= ptr, uint64_t len, SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); SevCommonStateClass *klass =3D SEV_COMMON_GET_CLASS(sev_common); =20 + if (sev_common->state =3D=3D SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + if ((cpu_index =3D=3D 0) && (memory_type =3D=3D CGS_PAGE_TYPE_VMSA= )) { + const struct sev_es_save_area *sa =3D + (const struct sev_es_save_area *)ptr; + if (len < sizeof(*sa)) { + error_setg(errp, "%s: invalid VMSA length encountered", + __func__); + return -1; + } + if (check_sev_features(sev_common, sa->sev_features, errp) < 0= ) { + return -1; + } + sev_common->sev_features =3D sa->sev_features; + } + return 0; + } + if (!sev_enabled()) { error_setg(errp, "%s: attempt to configure guest memory, but SEV " "is not enabled", __func__); @@ -2436,7 +2546,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *p= tr, uint64_t len, __func__); return -1; } - if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr, + if (check_vmsa_supported(sev_common, gpa, + (const struct sev_es_save_area *)ptr, errp) < 0) { return -1; } @@ -2493,6 +2604,12 @@ static int cgs_get_mem_map_entry(int index, struct e820_entry *table; int num_entries; =20 + SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); + if (sev_common->state =3D=3D SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + return 1; + } + num_entries =3D e820_get_table(&table); if ((index < 0) || (index >=3D num_entries)) { return 1; @@ -2524,6 +2641,12 @@ static int cgs_set_guest_policy(ConfidentialGuestPol= icyType policy_type, uint32_t policy_data1_size, void *policy_d= ata2, uint32_t policy_data2_size, Error **errp) { + SevCommonState *sev_common =3D SEV_COMMON(MACHINE(qdev_get_machine())-= >cgs); + if (sev_common->state =3D=3D SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + return 0; + } + if (policy_type !=3D GUEST_POLICY_SEV) { error_setg(errp, "%s: Invalid guest policy type provided for SEV: = %d", __func__, policy_type); --=20 2.43.0