|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH] x86/nestedsvm: Record NextRIP with the deferred #VMEXIT
nsvm_vmcb_prepare4vmexit() copies NextRIP from the hardware #VMEXIT,
but L0 also delivers #VMEXITs it creates itself, for which that value
belongs to a different exit. Such a #VMEXIT saves no nRIP (AMD64 APM
vol 2, publication 24593 rev 3.44, section 15.7.1). For example:
1. L1 intercepts #GP but not MSR accesses.
2. L2 executes RDMSR of an MSR which Xen doesn't implement.
3. Hardware exits to L0 with VMEXIT_MSR and NextRIP after the RDMSR.
4. L0 handles the RDMSR itself and injects #GP into L2.
5. L1 intercepts #GP, so L0 delivers VMEXIT_EXCP_GP to L1.
6. L1 sees NextRIP after the RDMSR, instead of 0.
Record NextRIP in ns_vmexit alongside the exitcode and exitinfo, and
have each source of a #VMEXIT supply it: the hardware value for a
reflected #VMEXIT, the next RIP for an INT3/INTO which L0 emulated, and
zero for everything else, including a failed VMRUN.
Fixes: 9a779e4fc161 ("Implement SVM specific part for Nested Virtualization")
Signed-off-by: Stephen Cheng <stephen.cheng@xxxxxxxxxx>
Assisted-by: Claude:claude-opus-5-5
---
Found with a nested-SVM XTF test, not yet upstream.
This is the #VMEXIT counterpart of commit 9d6b6e4f7394 ("x86/svm: Sync
nextrip during virtual VMRUN").
BOUND's #BR is raised by the emulator as a plain hardware exception
without an instruction length, so it still reports 0 rather than the
next RIP.
Chunjie's "x86/nestedsvm: rework nested VMRUN/VMEXIT handling" series
also changes nestedsvm_vmexit_defer() and nsvm_vcpu_vmexit_inject();
under it NextRIP would become one more value passed alongside
exitinfo1/2. I'm happy to rebase on top of it, or the other way round,
whichever merge order is preferred.
xen/arch/x86/hvm/svm/nestedhvm.h | 3 ++-
xen/arch/x86/hvm/svm/nestedsvm.c | 32 ++++++++++++++++++++----
xen/arch/x86/hvm/svm/svm.c | 8 +++---
xen/arch/x86/include/asm/hvm/svm-types.h | 1 +
4 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/xen/arch/x86/hvm/svm/nestedhvm.h b/xen/arch/x86/hvm/svm/nestedhvm.h
index 9bb04a0434..963390e8a9 100644
--- a/xen/arch/x86/hvm/svm/nestedhvm.h
+++ b/xen/arch/x86/hvm/svm/nestedhvm.h
@@ -28,7 +28,8 @@
int nestedsvm_vmcb_map(struct vcpu *v, uint64_t vmcbaddr);
void nestedsvm_vmexit_defer(struct vcpu *v,
- uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2);
+ uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2,
+ uint64_t nextrip);
enum nestedhvm_vmexits
nestedsvm_vmexit_n2n1(struct vcpu *v, struct cpu_user_regs *regs);
enum nestedhvm_vmexits
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index 2b998e26bc..241912e1ad 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -154,6 +154,7 @@ int cf_check nsvm_vcpu_reset(struct vcpu *v)
svm->ns_hostflags.bytes = 0;
svm->ns_vmexit.exitinfo1 = 0;
svm->ns_vmexit.exitinfo2 = 0;
+ svm->ns_vmexit.nextrip = 0;
svm->ns_iomap = NULL;
@@ -696,6 +697,7 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs *regs)
if ( inst_len == 0 )
{
svm->ns_vmexit.exitcode = VMEXIT_SHUTDOWN;
+ svm->ns_vmexit.nextrip = 0;
return -1;
}
@@ -717,6 +719,7 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs *regs)
case NSVM_ERROR_VVMCB:
gdprintk(XENLOG_ERR, "inject VMEXIT(INVALID)\n");
svm->ns_vmexit.exitcode = VMEXIT_INVALID;
+ svm->ns_vmexit.nextrip = 0;
return -1;
case NSVM_ERROR_VMENTRY:
default:
@@ -782,6 +785,7 @@ nsvm_vcpu_vmexit_inject(struct vcpu *v, struct
cpu_user_regs *regs,
}
ns_vmcb->exitcode = exitcode;
+ ns_vmcb->nextrip = svm->ns_vmexit.nextrip;
ns_vmcb->event_inj.raw = 0;
return 0;
}
@@ -789,10 +793,26 @@ nsvm_vcpu_vmexit_inject(struct vcpu *v, struct
cpu_user_regs *regs,
int cf_check nsvm_vcpu_vmexit_event(
struct vcpu *v, const struct x86_event *event)
{
+ const struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ uint64_t nextrip = 0;
+
ASSERT(vcpu_nestedhvm(v).nv_vvmcx != NULL);
+ /*
+ * An exception intercept saves NextRIP only for INT3, INTO and BOUND
+ * (AMD APM vol 2 rev 3.44, 15.7.1). The emulator raises BOUND's #BR as
+ * a plain hardware exception, so only INT3/INTO can be recognised here.
+ * As in svm_inject_event(), truncate it outside of 64bit mode.
+ */
+ if ( event->type == X86_ET_SW_EXC )
+ {
+ nextrip = guest_cpu_user_regs()->rip + event->insn_len;
+ if ( !((vmcb_get_efer(vmcb) & EFER_LMA) && vmcb->cs.l) )
+ nextrip = (uint32_t)nextrip;
+ }
+
nestedsvm_vmexit_defer(v, VMEXIT_EXCEPTION_DE + event->vector,
- event->error_code, event->data);
+ event->error_code, event->data, nextrip);
return NESTEDHVM_VMEXIT_DONE;
}
@@ -1063,8 +1083,7 @@ nsvm_vmcb_prepare4vmexit(struct vcpu *v, struct
cpu_user_regs *regs)
/* LBR virtualization - keep lbr control as is */
- /* NextRIP */
- ns_vmcb->nextrip = n2vmcb->nextrip;
+ /* NextRIP is set by nsvm_vcpu_vmexit_inject(). */
/* Decode Assist */
ns_vmcb->guest_ins_len = n2vmcb->guest_ins_len;
@@ -1219,7 +1238,8 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
/* VMEXIT emulation */
void
nestedsvm_vmexit_defer(struct vcpu *v,
- uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2)
+ uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2,
+ uint64_t nextrip)
{
struct nestedsvm *svm = &vcpu_nestedsvm(v);
struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
@@ -1232,6 +1252,7 @@ nestedsvm_vmexit_defer(struct vcpu *v,
svm->ns_vmexit.exitcode = exitcode;
svm->ns_vmexit.exitinfo1 = exitinfo1;
svm->ns_vmexit.exitinfo2 = exitinfo2;
+ svm->ns_vmexit.nextrip = nextrip;
vcpu_nestedhvm(v).nv_vmexit_pending = 1;
}
@@ -1359,6 +1380,7 @@ nestedsvm_vcpu_vmexit(struct vcpu *v, struct
cpu_user_regs *regs,
case NESTEDHVM_VMEXIT_ERROR:
exitcode = VMEXIT_INVALID;
+ vcpu_nestedsvm(v).ns_vmexit.nextrip = 0;
break;
default:
ASSERT(!nestedhvm_vcpu_in_guestmode(v));
@@ -1478,7 +1500,7 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct
hvm_intack intack)
guest_cpu_user_regs(), exitcode);
if ( ret )
{
- nestedsvm_vmexit_defer(v, exitcode, intack.source, exitinfo2);
+ nestedsvm_vmexit_defer(v, exitcode, intack.source, exitinfo2, 0);
return NSVM_INTR_FORCEVMEXIT;
}
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 71e48351f2..cf9e3ef1ab 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -1618,7 +1618,7 @@ static void svm_do_nested_pgfault(struct vcpu *v,
case -1:
ASSERT(nestedhvm_enabled(v->domain) && nestedhvm_vcpu_in_guestmode(v));
/* inject #VMEXIT(NPF) into guest. */
- nestedsvm_vmexit_defer(v, VMEXIT_NPF, pfec, gpa);
+ nestedsvm_vmexit_defer(v, VMEXIT_NPF, pfec, gpa, 0);
return;
}
@@ -2556,7 +2556,7 @@ void asmlinkage svm_vmexit_handler(void)
enum nestedhvm_vmexits nsret;
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
struct vmcb_struct *ns_vmcb = nv->nv_vvmcx;
- uint64_t exitinfo1, exitinfo2;
+ uint64_t exitinfo1, exitinfo2, nextrip;
paging_update_nestedmode(v);
@@ -2581,6 +2581,7 @@ void asmlinkage svm_vmexit_handler(void)
*/
exitinfo1 = vmcb->exitinfo1;
exitinfo2 = vmcb->exitinfo2;
+ nextrip = vmcb->nextrip;
nv->nv_vmswitch_in_progress = 1;
nsret = nestedsvm_vmexit_n2n1(v, regs);
nv->nv_vmswitch_in_progress = 0;
@@ -2588,7 +2589,8 @@ void asmlinkage svm_vmexit_handler(void)
{
case NESTEDHVM_VMEXIT_DONE:
/* defer VMEXIT injection */
- nestedsvm_vmexit_defer(v, exit_reason, exitinfo1, exitinfo2);
+ nestedsvm_vmexit_defer(v, exit_reason, exitinfo1, exitinfo2,
+ nextrip);
goto out;
case NESTEDHVM_VMEXIT_FATALERROR:
gdprintk(XENLOG_ERR, "unexpected nestedsvm_vmexit() error\n");
diff --git a/xen/arch/x86/include/asm/hvm/svm-types.h
b/xen/arch/x86/include/asm/hvm/svm-types.h
index beab9a3af2..fafbd646cc 100644
--- a/xen/arch/x86/include/asm/hvm/svm-types.h
+++ b/xen/arch/x86/include/asm/hvm/svm-types.h
@@ -74,6 +74,7 @@ struct nestedsvm {
uint64_t exitcode; /* native exitcode to inject into l1 guest */
uint64_t exitinfo1; /* additional information to the exitcode */
uint64_t exitinfo2; /* additional information to the exitcode */
+ uint64_t nextrip; /* NextRIP to report to the l1 guest */
} ns_vmexit;
union {
--
2.49.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |