From nobody Mon Nov 25 03:41:11 2024 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=suse.com ARC-Seal: i=1; a=rsa-sha256; t=1718980348; cv=none; d=zohomail.com; s=zohoarc; b=erOOa3Z/jX2v0DqALA4cmvgGpn2DusPKzvbTSOg2dTW4XMt3tbuLwn8mh4dmCBM016c9/ugKdmmWpCmM0X/5qw6O1ulqPwE0OvUAjvZPYbAFUDXr65usvCRkakIQH/K8Ey77pQQej9v8VH9dQDMjHghaMe+Y7J5N1MTAnemX85w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1718980348; 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=BB9r7BEwZqVRQIzgc4wl4TBA/AA2Dt9nHlbmTtl7eOw=; b=DeUiC0Oc85W0sIF1sCLIrL3LPGHz9cDVleU+P9MFwMMnhaFtiLtGZLIuZr9A2uHzLVo3HnmVVQ5cJ4MvtZpiRiB5RqpvFFp88Nd9FUM1hL2HPKGrmOSd3p0LT7Ze2gxk8QeiOZTTlI8cviQ2aTRiSQumFM3L2FPNkCtvNw6xv3k= 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 1718980348748699.6796408223448; Fri, 21 Jun 2024 07:32:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sKfH4-0001CS-0E; Fri, 21 Jun 2024 10:29:58 -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 1sKfH2-0001Bu-OI for qemu-devel@nongnu.org; Fri, 21 Jun 2024 10:29:56 -0400 Received: from smtp-out2.suse.de ([2a07:de40:b251:101:10:150:64:2]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sKfGz-0005gO-NR for qemu-devel@nongnu.org; Fri, 21 Jun 2024 10:29:56 -0400 Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 6CCA21FB84; Fri, 21 Jun 2024 14:29:52 +0000 (UTC) Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id B051113ABD; Fri, 21 Jun 2024 14:29:51 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id yFqqKF+OdWbwKQAAD6G6ig (envelope-from ); Fri, 21 Jun 2024 14:29:51 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1718980192; h=from:from:reply-to: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=BB9r7BEwZqVRQIzgc4wl4TBA/AA2Dt9nHlbmTtl7eOw=; b=JP9XBQSMGbVJ0PBfl3NnAYgnMNt1qFyHpKdXnD7tcQSJfntjAP5JD2lmdSr/kFeuO1JHx6 5nfKnoSdUCMbKZ2wPCexP5WYWiVlxouyqYt1iM63iedlfJgP9gYdrjtP25IrtW15R1h13T apoZnX8hjMopboSawdbvFZtl1U7+8rI= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1718980192; h=from:from:reply-to: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=BB9r7BEwZqVRQIzgc4wl4TBA/AA2Dt9nHlbmTtl7eOw=; b=JP9XBQSMGbVJ0PBfl3NnAYgnMNt1qFyHpKdXnD7tcQSJfntjAP5JD2lmdSr/kFeuO1JHx6 5nfKnoSdUCMbKZ2wPCexP5WYWiVlxouyqYt1iM63iedlfJgP9gYdrjtP25IrtW15R1h13T apoZnX8hjMopboSawdbvFZtl1U7+8rI= From: Roy Hopkins To: qemu-devel@nongnu.org Cc: Roy Hopkins , Paolo Bonzini , =?UTF-8?q?Daniel=20P=20=2E=20Berrang=C3=A9?= , 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 , =?UTF-8?q?J=C3=B6rg=20Roedel?= Subject: [PATCH v3 07/15] i386/sev: Refactor setting of reset vector and initial CPU state Date: Fri, 21 Jun 2024 15:29:10 +0100 Message-ID: <614e1d50a0fde2512deb096fee20fa926c24fc82.1718979106.git.roy.hopkins@suse.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -1.30 X-Spamd-Result: default: False [-1.30 / 50.00]; BAYES_HAM(-3.00)[100.00%]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; ARC_NA(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; TAGGED_RCPT(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[imap1.dmz-prg2.suse.org:helo,suse.com:email]; RCPT_COUNT_TWELVE(0.00)[19]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; FREEMAIL_CC(0.00)[suse.com,redhat.com,gmail.com,habkost.net,alistair23.me,amd.com]; FROM_HAS_DN(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_TLS_ALL(0.00)[]; FUZZY_BLOCKED(0.00)[rspamd.com]; R_RATELIMIT(0.00)[to_ip_from(RLm8d31jk6dhzwhww9bgqrb1jt)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; FREEMAIL_ENVRCPT(0.00)[gmail.com] 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=2a07:de40:b251:101:10:150:64:2; envelope-from=roy.hopkins@suse.com; helo=smtp-out2.suse.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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_MED=-2.3, 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 @suse.com) X-ZM-MESSAGEID: 1718980349372100001 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 --- target/i386/sev.h | 110 ++++++++++++++++ target/i386/sev.c | 323 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 400 insertions(+), 33 deletions(-) diff --git a/target/i386/sev.h b/target/i386/sev.h index 858005a119..167dd154d6 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -45,6 +45,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]; +}; + #ifdef CONFIG_SEV bool sev_enabled(void); bool sev_es_enabled(void); diff --git a/target/i386/sev.c b/target/i386/sev.c index 1900c3d9b4..6d5f102894 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -49,6 +49,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; @@ -88,6 +94,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 @@ -106,9 +120,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 { @@ -369,6 +381,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) { @@ -973,6 +1151,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); @@ -1711,40 +1899,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; @@ -1757,14 +2015,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; @@ -1999,6 +2255,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.43.0