Index | Thread | Search

From:
Mike Larkin <mlarkin@nested.page>
Subject:
Re: SEV-ES: vmm(4): vmentry/vmexit for SEV-ES guests
To:
tech@openbsd.org
Date:
Tue, 20 May 2025 02:34:07 -0700

Download raw body.

Thread
On Tue, May 20, 2025 at 11:28:16AM +0200, Hans-Jörg Höxer wrote:
> Hi,
>
> this diff prepares the entry/exit path for SEV-ES enabled guests:
>
>     With SEV-ES the full vCPU state is automatically loaded from or
>     saved to the encrypted VMSA.  However, host state is not fully saved
>     and restored.  Therefore, we need a seperate entry/exit path for
>     SEV-ES enabled guests.
>
>     svm_seves_enter_guest() accomplishes this.  Maybe we can streamline
>     svm_enter_guest() and svm_seves_enter_guest() to share code.
>
> Take care,
> HJ.

was this the same diff I ok'ed yesterday? It looks fine regardless.

ok mlarkin

> ---------------------------------------------------------------------------
> commit 1d876e9aa856bac3d580470133c6936c094164d6
> Author: Hans-Joerg Hoexer <hshoexer@genua.de>
> Date:   Mon Jul 29 15:25:31 2024 +0200
>
>     vmm(4): vmentry/vmexit for SEV-ES guests
>
>     With SEV-ES the full vCPU state is automatically loaded from or
>     saved to the encrypted VMSA.  However, host state is not fully saved
>     and restored.  Therefore, we need a seperate entry/exit path for
>     SEV-ES enabled guests.
>
>     svm_seves_enter_guest() accomplishes this.  Maybe we can streamline
>     svm_enter_guest() and svm_seves_enter_guest() to share code.
>
> diff --git a/sys/arch/amd64/amd64/vmm_machdep.c b/sys/arch/amd64/amd64/vmm_machdep.c
> index 7351221b3a5..9470222c351 100644
> --- a/sys/arch/amd64/amd64/vmm_machdep.c
> +++ b/sys/arch/amd64/amd64/vmm_machdep.c
> @@ -6399,8 +6399,13 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *vrp)
>  		KASSERT(vmcb->v_intercept1 & SVM_INTERCEPT_INTR);
>  		wrmsr(MSR_AMD_VM_HSAVE_PA, vcpu->vc_svm_hsa_pa);
>
> -		ret = svm_enter_guest(vcpu->vc_control_pa,
> -		    &vcpu->vc_gueststate, &gdt);
> +		if (vcpu->vc_seves) {
> +			ret = svm_seves_enter_guest(vcpu->vc_control_pa,
> +			    vcpu->vc_svm_hsa_va + SVM_HSA_OFFSET, &gdt);
> +		} else {
> +			ret = svm_enter_guest(vcpu->vc_control_pa,
> +			    &vcpu->vc_gueststate, &gdt);
> +		}
>
>  		/* Restore host PKRU state. */
>  		if (vmm_softc->sc_md.pkru_enabled) {
> diff --git a/sys/arch/amd64/amd64/vmm_support.S b/sys/arch/amd64/amd64/vmm_support.S
> index 30c1b75834f..f9f663cff62 100644
> --- a/sys/arch/amd64/amd64/vmm_support.S
> +++ b/sys/arch/amd64/amd64/vmm_support.S
> @@ -42,6 +42,7 @@
>  	.global vmx_enter_guest
>  	.global vmm_dispatch_intr
>  	.global svm_enter_guest
> +	.global svm_seves_enter_guest
>
>  	.text
>  	.code64
> @@ -662,3 +663,163 @@ restore_host_svm:
>  	ret
>  	lfence
>  END(svm_enter_guest)
> +
> +/*
> + * When using SEV-ES we have to save some of the host registers to
> + * the host state save area (HSA).  According to the AMD Programmer's
> + * Manual Volume 2 Appendix B the HSA has the same layout as the guest
> + * save area (VMSA) except that it starts at offset 0x400 in the HSA
> + * page.
> + */
> +ENTRY(svm_seves_enter_guest)
> +	RETGUARD_SETUP(svm_seves_enter_guest, r11)
> +	clgi
> +	movq	%rdi, %r8
> +	pushfq
> +
> +	pushq	%rdx	/* gdt pointer */
> +
> +	/*
> +	 * Save (possibly) lazy-switched selectors
> +	 */
> +	strw	%ax
> +	pushw	%ax
> +	movw	%es, %ax
> +	pushw	%ax
> +	movw	%ds, %ax
> +	pushw	%ax
> +	movw	%ss, %ax
> +	pushw	%ax
> +
> +	movq	$MSR_FSBASE, %rcx
> +	rdmsr
> +	pushq	%rax
> +	pushq	%rdx
> +	pushw	%fs
> +	movq	$MSR_GSBASE, %rcx
> +	rdmsr
> +	pushq	%rax
> +	pushq	%rdx
> +	pushw	%gs
> +	movq	$MSR_KERNELGSBASE, %rcx
> +	rdmsr
> +	pushq	%rax
> +	pushq	%rdx
> +
> +	/*
> +	 * Save various MSRs
> +	 */
> +	movq	$MSR_STAR, %rcx
> +	rdmsr
> +	pushq	%rax
> +	pushq	%rdx
> +
> +	movq	$MSR_LSTAR, %rcx
> +	rdmsr
> +	pushq	%rax
> +	pushq	%rdx
> +
> +	movq	$MSR_SFMASK, %rcx
> +	rdmsr
> +	pushq	%rax
> +	pushq	%rdx
> +
> +	RETGUARD_PUSH(r11)
> +
> +	/*
> +	 * Preserve callee-preserved registers as per AMD64 ABI in
> +	 * HSA. Although all registers will be restored from HSA
> +	 * on vmexit, these will not be saved on vmrun.
> +	 */
> +	movq	%r15, 0x378(%rsi)
> +	movq	%r14, 0x370(%rsi)
> +	movq	%r13, 0x368(%rsi)
> +	movq	%r12, 0x360(%rsi)
> +	movq	%rbp, 0x328(%rsi)
> +	movq	%rbx, 0x318(%rsi)
> +
> +	movq	%r8, %rax	/* rax = vmcb pa */
> +
> +	vmrun	%rax
> +
> +	/* %rdi = 0 means we took an exit */
> +	xorq	%rdi, %rdi
> +
> +	RETGUARD_POP(r11)
> +
> +	/*
> +	 * Restore saved MSRs
> +	 */
> +	popq	%rdx
> +	popq	%rax
> +	movq	$MSR_SFMASK, %rcx
> +	wrmsr
> +
> +	/* make sure guest doesn't bleed into host */
> +	xorl	%edx, %edx
> +	xorl	%eax, %eax
> +	movq	$MSR_CSTAR, %rcx
> +	wrmsr
> +
> +	popq	%rdx
> +	popq	%rax
> +	movq	$MSR_LSTAR, %rcx
> +	wrmsr
> +
> +	popq	%rdx
> +	popq	%rax
> +	movq	$MSR_STAR, %rcx
> +	wrmsr
> +
> +	/*
> +	 * popw %gs will reset gsbase to 0, so preserve it
> +	 * first. This is to accommodate possibly lazy-switched
> +	 * selectors from above
> +	 */
> +	cli
> +	popq	%rdx
> +	popq	%rax
> +	movq	$MSR_KERNELGSBASE, %rcx
> +	wrmsr
> +
> +	popw	%gs
> +	popq	%rdx
> +	popq	%rax
> +	movq	$MSR_GSBASE, %rcx
> +	wrmsr
> +
> +	popw	%fs
> +	popq	%rdx
> +	popq	%rax
> +	movq	$MSR_FSBASE, %rcx
> +	wrmsr
> +
> +	popw	%ax
> +	movw	%ax, %ss
> +	popw	%ax
> +	movw	%ax, %ds
> +	popw	%ax
> +	movw	%ax, %es
> +
> +	xorq	%rax, %rax
> +	lldtw	%ax		/* Host LDT is always 0 */
> +
> +	popw	%ax		/* ax = saved TR */
> +
> +	popq	%rdx
> +	addq	$0x2, %rdx
> +	movq	(%rdx), %rdx
> +
> +	/* rdx = GDTR base addr */
> +	andb	$0xF9, 5(%rdx, %rax)
> +
> +	ltrw	%ax
> +
> +	popfq
> +
> +	movq	%rdi, %rax
> +
> +	RETGUARD_CHECK(svm_seves_enter_guest, r11)
> +	ret
> +	lfence
> +END(svm_seves_enter_guest)
> diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h
> index 8714fd1aace..03d86639476 100644
> --- a/sys/arch/amd64/include/vmmvar.h
> +++ b/sys/arch/amd64/include/vmmvar.h
> @@ -852,6 +852,13 @@ struct vmsa {
>  		uint64_t		v_ic_ibs_xtd_ct;/* 7C0h */
>  };
>
> +/*
> + * With SEV-ES the host save are (HSA) has the same layout as the
> + * VMSA.  However, it has the offset 0x400 into the HSA page.
> + * See AMD APM Vol 2, Appendix B.
> + */
> +#define SVM_HSA_OFFSET			0x400
> +
>  struct vmcs {
>  	uint32_t	vmcs_revision;
>  };
> @@ -1028,6 +1035,7 @@ int	invept(uint64_t, struct vmx_invept_descriptor *);
>  int	vmx_enter_guest(paddr_t *, struct vcpu_gueststate *, int, uint8_t);
>  int	svm_enter_guest(uint64_t, struct vcpu_gueststate *,
>      struct region_descriptor *);
> +int	svm_seves_enter_guest(uint64_t, vaddr_t, struct region_descriptor *);
>  void	start_vmm_on_cpu(struct cpu_info *);
>  void	stop_vmm_on_cpu(struct cpu_info *);
>  void	vmclear_on_cpu(struct cpu_info *);