From nobody Sat Nov 15 12:47:42 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1752492447; cv=none; d=zohomail.com; s=zohoarc; b=SkDz9xbZH+sSBp4a/QGYEAfDIXo28bNAGL4WhPUMGCzNOx4Cqxw7Z8lB3gjK7iMusdb4sUZ6ciRUUIEgRhC1NTzt5JIwiF3OCP18oosQ7a4mupCwJLvVlNSRGZIuAhoViixGcOFeatQVmSJxT39RONBTtet3SOzk2q4IpJzvAWw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1752492447; 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=wxMnMbD1XcPxMyEUTBLdpuoI42kvRRrLRupjHn50Puk=; b=kELeOLbZZ2iCCbjq1g64ASh3fxnsMLVV62z2c0Z71/7sguAmTPwo8khxGvh8gNV4Ing60sQxRdZLWu4TWidR+qiFcfPu1GOw2SvNASIHfVDkwisiurjRKQ0DRRfdjapbVzRHP0GEfpSYLLE3E/ncRSd+vj0GNT+Opoat75kc9xw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1752492447118442.95555236581765; Mon, 14 Jul 2025 04:27:27 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ubHJ0-0003L1-9u; Mon, 14 Jul 2025 07:25:11 -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 1ubGzr-00016V-01 for qemu-devel@nongnu.org; Mon, 14 Jul 2025 07:05:23 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ubGzn-0001sd-MH for qemu-devel@nongnu.org; Mon, 14 Jul 2025 07:05:22 -0400 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-172-TKX0yMhDN4-MUPm4CA-aDQ-1; Mon, 14 Jul 2025 07:05:17 -0400 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-451d3f03b74so24015985e9.3 for ; Mon, 14 Jul 2025 04:05:17 -0700 (PDT) Received: from [192.168.10.48] ([151.49.73.155]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-45613c2d84dsm45517715e9.5.2025.07.14.04.05.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 14 Jul 2025 04:05:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752491119; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wxMnMbD1XcPxMyEUTBLdpuoI42kvRRrLRupjHn50Puk=; b=hOEYfKea9xnhxcbryvlgF0jA39OR395lTJ91j+yQkBjETTLZfbpdusutHiX5FWZpqwuKO6 YfQZUP7YaVMscmVHA1UsSps48rxFoeddMt1vfvkaQ+UcZ9qK0CyUj/F7y9zNVEQQcgmLhs Tyx9U1Kh8M51eIn9w6IHOp3fT4+UQ0Y= X-MC-Unique: TKX0yMhDN4-MUPm4CA-aDQ-1 X-Mimecast-MFC-AGG-ID: TKX0yMhDN4-MUPm4CA-aDQ_1752491116 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752491115; x=1753095915; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wxMnMbD1XcPxMyEUTBLdpuoI42kvRRrLRupjHn50Puk=; b=EKys5WK7l5aGmdmFmvE3uBD617BEQQ77ZjimHF9aYZPNkERl3Fx/fwVf73+WsH1fBA VXAqyzj/m6nRYf8vFYjihotsY7hKbyKlarrpEw6w2ZYt2o6YCBYyUY6O4mjtfX5pyoaI M2oEjw8g3CUL6wDoXYYc5XoRARYeEV26gGzqHjQdTsmsgl2J7fp8wAeXKIyLm/y3kSGo KLboL2u6E1oQOSmosBuoIVno/3nmNcLHWx/8vVebNhzrzqiW778dTSDPMsLxLsZS86Yu 3X+w4Ke040b4YaFbKBg48n0PJr1tVQrKz6IyVzTVZcVAyxJCR9ewlc8XgO0HGTCn5gHE 5J+Q== X-Gm-Message-State: AOJu0YykxKJB2Lt84Rk/v1Hd3UAGfuxV5oKQlnGpl+6/d7fzloRyZ04y Bvqu/3OJDcgD0GNmCC6+c5myh4ntKWZNV1+wLQKWfMeHDWrExiKnBFkxPJxOGKz9+8xwTTAER/V 4N4C4UF0vh5zjuq2KbuBBkam4DIx9bwC+UOwikijc/Ow3VF0j7VaXprWP60DyXlVwqSRPmBoThM vM12a2wIg0hBnjAW7jsnPZ5xcHWxioV0EvetN7Hboa X-Gm-Gg: ASbGnctGVr6dPUcCo5M4ZvP1ENz78Eosq/McYR7DshxDQCaoTo9HBEPsdSroiZRSqg+ auekX0w09wOUPxP22YCJsmr2wwwkCvyMBwfq6MT4nu6CDKDvfW459DRhh38k0jbfAzDe9ZLCBzj LbsDPs9Ok8XhlmkaYqxXMyuKdCjIQ5s0FpkLRwS/ddDkbi/u4I7SM2jCC42z/HUiAXadPk00M9K 1WUrcJhzYMmMW9MX3wBz6KF9S8l/i+vnG5hUyzzfqYnQZeEm6z9l49xj1i34IemBNyfs2jP0PaW EVl+/LR7pE6Xv58gChVIaJcLihiJNwHKrhFKjU+vwfk= X-Received: by 2002:a05:600c:1c9a:b0:456:1608:c807 with SMTP id 5b1f17b1804b1-4561608ca40mr39533915e9.26.1752491114685; Mon, 14 Jul 2025 04:05:14 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGChAowa4p8SSul2OyJsN5lzkvikbK3vodneNxkz2OcsR7C0Edf+SLbgaeN8K880LzBIhyAmg== X-Received: by 2002:a05:600c:1c9a:b0:456:1608:c807 with SMTP id 5b1f17b1804b1-4561608ca40mr39533425e9.26.1752491114073; Mon, 14 Jul 2025 04:05:14 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Roy Hopkins , "Michael S. Tsirkin" , Stefano Garzarella , Gerd Hoffman , Pankaj Gupta Subject: [PULL 24/77] i386/sev: Refactor setting of reset vector and initial CPU state Date: Mon, 14 Jul 2025 13:03:13 +0200 Message-ID: <20250714110406.117772-25-pbonzini@redhat.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20250714110406.117772-1-pbonzini@redhat.com> References: <20250714110406.117772-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1752492448852116600 Content-Type: text/plain; charset="utf-8" From: Roy Hopkins 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 Link: https://lore.kernel.org/r/d3c2debca496c4366a278b135f951908f3b9c341.17= 51554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/sev.h | 110 ++++++++++++++++ target/i386/sev.c | 326 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 401 insertions(+), 35 deletions(-) diff --git a/target/i386/sev.h b/target/i386/sev.h index 373669eaace..38caa849f5e 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= ); diff --git a/target/i386/sev.c b/target/i386/sev.c index a84f5f5d28a..a13f91e615d 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,109 @@ sev_es_find_reset_vector(void *flash_ptr, uint64_t= flash_size, return sev_es_parse_reset_block(info, addr); } =20 + +static void seg_to_vmsa(const SegmentCache *cpu_seg, struct vmcb_seg *vmsa= _seg) +{ + 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); +} + +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; + + /* + * 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); + } +} + void sev_es_set_reset_vector(CPUState *cpu) { - 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)); - - /* Only update if we have valid reset information */ - if (!sev_common || !sev_common->reset_data_valid) { - return; + if (sev_enabled()) { + sev_apply_cpu_context(cpu); } - - /* Do not update the BSP reset state */ - if (cpu->cpu_index =3D=3D 0) { - return; - } - - 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; } =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 +2083,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 +2323,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 */ --=20 2.50.0