|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v4 25/30] x86/svm: introduce svm_vmcb_map() and svm_vmcb_unmap()
Some accessors of a vCPU's VMCB can run with that vCPU paused, from
another vCPU's context: context save/load, the segment and CPL getters,
PAT/TSC/intercept updates reachable from toolstack operations, vm_event,
the 'v' debug key, and construct_vmcb() itself, which runs before the
vCPU ever entered guest context. svm_nested_features_on_efer_update()
is among them: it is reachable remotely via hvm_load_cpu_ctxt() ->
svm_update_guest_efer().
Route them through new svm_vmcb_map/unmap helpers; construct_vmcb()
takes the VMCB from its caller. svm_set_tsc_offset() needs both VMCBs
rather than just the active one, so it maps them individually via
svm_vmcb_map_one(). A vCPU stays in nested guest mode across a
deschedule or a pause, so this path can run remotely with the L2 VMCB
active; guest mode does not imply v == current. Hot vmexit-path
accesses all have v == current and keep dereferencing svm->vmcb
directly.
No functional change: the helpers return the VMCB's existing address.
They prepare for moving VMCBs off always-mapped memory, where a VMCB's
mapping is valid only in its own vCPU's context.
Assisted-by: Claude Code:claude-fable-5, Claude Code:claude-opus-5-5
Signed-off-by: George Dunlap <gwd@xxxxxxxxxxxxxx>
---
Changes in v4:
- New in this version.
---
xen/arch/x86/hvm/svm/nestedsvm.c | 4 +-
xen/arch/x86/hvm/svm/svm.c | 133 +++++++++++++++++++++++--------
xen/arch/x86/hvm/svm/vmcb.c | 29 ++++++-
xen/arch/x86/hvm/svm/vmcb.h | 9 +++
4 files changed, 136 insertions(+), 39 deletions(-)
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index 2b998e26bc..dc853e5f1a 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1554,7 +1554,7 @@ void svm_vmexit_do_clgi(struct cpu_user_regs *regs,
struct vcpu *v)
*/
void svm_nested_features_on_efer_update(struct vcpu *v)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
struct nestedsvm *svm = &vcpu_nestedsvm(v);
u32 general2_intercepts;
vintr_t vintr;
@@ -1612,6 +1612,8 @@ void svm_nested_features_on_efer_update(struct vcpu *v)
vmcb_set_general2_intercepts(vmcb, general2_intercepts);
}
}
+
+ svm_vmcb_unmap(v, vmcb);
}
void __init start_nested_svm(struct hvm_function_table *hvm_function_table)
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 71e48351f2..241843d8c0 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -107,7 +107,7 @@ static void cf_check svm_cpu_down(void)
static void cf_check svm_update_guest_cr(
struct vcpu *v, unsigned int cr, unsigned int flags)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
uint64_t value;
switch ( cr )
@@ -173,11 +173,13 @@ static void cf_check svm_update_guest_cr(
default:
BUG();
}
+
+ svm_vmcb_unmap(v, vmcb);
}
static void cf_check svm_update_guest_efer(struct vcpu *v)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
unsigned long guest_efer = v->arch.hvm.guest_efer,
xen_efer = read_efer();
@@ -204,6 +206,7 @@ static void cf_check svm_update_guest_efer(struct vcpu *v)
guest_efer |= EFER_SVME;
vmcb_set_efer(vmcb, guest_efer);
+ svm_vmcb_unmap(v, vmcb);
ASSERT(nestedhvm_enabled(v->domain) ||
!nsvm_efer_svm_enabled(v));
@@ -352,7 +355,7 @@ static void svm_restore_dr(struct vcpu *v)
static int svm_vmcb_save(struct vcpu *v, struct hvm_hw_cpu *c)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
if ( vmcb->event_inj.v &&
hvm_event_needs_reinjection(vmcb->event_inj.type,
@@ -362,13 +365,15 @@ static int svm_vmcb_save(struct vcpu *v, struct
hvm_hw_cpu *c)
c->error_code = vmcb->event_inj.ec;
}
+ svm_vmcb_unmap(v, vmcb);
+
return 1;
}
static int svm_vmcb_restore(struct vcpu *v, struct hvm_hw_cpu *c)
{
struct page_info *page = NULL;
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb;
struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
if ( c->pending_valid )
@@ -417,6 +422,8 @@ static int svm_vmcb_restore(struct vcpu *v, struct
hvm_hw_cpu *c)
svm_update_guest_cr(v, 0, 0);
svm_update_guest_cr(v, 4, 0);
+ vmcb = svm_vmcb_map(v);
+
if ( !paging_mode_shadow(v->domain) )
{
vmcb_set_np(vmcb, true);
@@ -436,6 +443,8 @@ static int svm_vmcb_restore(struct vcpu *v, struct
hvm_hw_cpu *c)
vmcb->event_inj.raw = 0;
vmcb->cleanbits.raw = 0;
+ svm_vmcb_unmap(v, vmcb);
+
paging_update_paging_modes(v);
return 0;
@@ -443,7 +452,7 @@ static int svm_vmcb_restore(struct vcpu *v, struct
hvm_hw_cpu *c)
static void svm_save_cpu_state(struct vcpu *v, struct hvm_hw_cpu *data)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ const struct vmcb_struct *vmcb = svm_vmcb_map(v);
data->sysenter_cs = vmcb->sysenter_cs;
data->sysenter_esp = vmcb->sysenter_esp;
@@ -453,11 +462,13 @@ static void svm_save_cpu_state(struct vcpu *v, struct
hvm_hw_cpu *data)
data->msr_star = vmcb->star;
data->msr_cstar = vmcb->cstar;
data->msr_syscall_mask = vmcb->sfmask;
+
+ svm_vmcb_unmap(v, vmcb);
}
static void svm_load_cpu_state(struct vcpu *v, struct hvm_hw_cpu *data)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
vmcb->lstar = data->msr_lstar;
vmcb->star = data->msr_star;
@@ -467,6 +478,9 @@ static void svm_load_cpu_state(struct vcpu *v, struct
hvm_hw_cpu *data)
vmcb->sysenter_cs = data->sysenter_cs;
vmcb->sysenter_esp = data->sysenter_esp;
vmcb->sysenter_eip = data->sysenter_eip;
+
+ svm_vmcb_unmap(v, vmcb);
+
v->arch.hvm.guest_efer = data->msr_efer;
svm_update_guest_efer(v);
}
@@ -492,7 +506,7 @@ static int cf_check svm_load_vmcb_ctxt(struct vcpu *v,
struct hvm_hw_cpu *ctxt)
static unsigned cf_check int svm_get_interrupt_shadow(struct vcpu *v)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
unsigned int intr_shadow = 0;
if ( vmcb->int_stat.intr_shadow )
@@ -503,13 +517,15 @@ static unsigned cf_check int
svm_get_interrupt_shadow(struct vcpu *v)
: (vmcb_get_general1_intercepts(vmcb) & GENERAL1_INTERCEPT_IRET) )
intr_shadow |= HVM_INTR_SHADOW_NMI;
+ svm_vmcb_unmap(v, vmcb);
+
return intr_shadow;
}
static void cf_check svm_set_interrupt_shadow(
struct vcpu *v, unsigned int intr_shadow)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
bool block_nmi = intr_shadow & HVM_INTR_SHADOW_NMI;
vmcb->int_stat.intr_shadow =
@@ -527,10 +543,13 @@ static void cf_check svm_set_interrupt_shadow(
vmcb_set_general1_intercepts(vmcb, gen1);
}
+
+ svm_vmcb_unmap(v, vmcb);
}
static int cf_check svm_guest_x86_mode(struct vcpu *v)
{
+ /* Current only (see guest_cpu_user_regs() below): no mapping needed. */
struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
if ( unlikely(!(v->arch.hvm.guest_cr[0] & X86_CR0_PE)) )
@@ -544,8 +563,7 @@ static int cf_check svm_guest_x86_mode(struct vcpu *v)
static void cf_check svm_cpuid_policy_changed(struct vcpu *v)
{
- struct svm_vcpu *svm = &v->arch.hvm.svm;
- struct vmcb_struct *vmcb = svm->vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
const struct cpu_policy *cp = v->domain->arch.cpu_policy;
u32 bitmap = vmcb_get_exception_intercepts(vmcb);
@@ -555,6 +573,7 @@ static void cf_check svm_cpuid_policy_changed(struct vcpu
*v)
bitmap &= ~(1U << X86_EXC_UD);
vmcb_set_exception_intercepts(vmcb, bitmap);
+ svm_vmcb_unmap(v, vmcb);
/* Give access to MSR_SPEC_CTRL if the guest has been told about it. */
svm_intercept_msr(v, MSR_SPEC_CTRL,
@@ -598,13 +617,18 @@ void svm_sync_vmcb(struct vcpu *v, enum vmcb_sync_state
new_state)
static unsigned int cf_check svm_get_cpl(struct vcpu *v)
{
- return vmcb_get_cpl(v->arch.hvm.svm.vmcb);
+ const struct vmcb_struct *vmcb = svm_vmcb_map(v);
+ unsigned int cpl = vmcb_get_cpl(vmcb);
+
+ svm_vmcb_unmap(v, vmcb);
+
+ return cpl;
}
static void cf_check svm_get_segment_register(
struct vcpu *v, enum x86_segment seg, struct segment_register *reg)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
ASSERT((v == current) || !vcpu_runnable(v));
@@ -645,12 +669,14 @@ static void cf_check svm_get_segment_register(
*reg = (struct segment_register){};
break;
}
+
+ svm_vmcb_unmap(v, vmcb);
}
static void cf_check svm_set_segment_register(
struct vcpu *v, enum x86_segment seg, struct segment_register *reg)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
ASSERT((v == current) || !vcpu_runnable(v));
@@ -679,6 +705,7 @@ static void cf_check svm_set_segment_register(
default:
ASSERT_UNREACHABLE();
domain_crash(v->domain);
+ svm_vmcb_unmap(v, vmcb);
return;
}
@@ -716,33 +743,41 @@ static void cf_check svm_set_segment_register(
ASSERT_UNREACHABLE();
break;
}
+
+ svm_vmcb_unmap(v, vmcb);
}
static int cf_check svm_set_guest_pat(struct vcpu *v, u64 gpat)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb;
if ( paging_mode_shadow(v->domain) )
return 0;
+ vmcb = svm_vmcb_map(v);
vmcb_set_g_pat(vmcb, gpat);
+ svm_vmcb_unmap(v, vmcb);
+
return 1;
}
static int cf_check svm_get_guest_pat(struct vcpu *v, u64 *gpat)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ const struct vmcb_struct *vmcb;
if ( paging_mode_shadow(v->domain) )
return 0;
+ vmcb = svm_vmcb_map(v);
*gpat = vmcb_get_g_pat(vmcb);
+ svm_vmcb_unmap(v, vmcb);
+
return 1;
}
static void cf_check svm_set_tsc_offset(struct vcpu *v, uint64_t offset)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
struct vmcb_struct *n1vmcb, *n2vmcb;
uint64_t n2_tsc_offset = 0;
struct domain *d = v->domain;
@@ -750,25 +785,36 @@ static void cf_check svm_set_tsc_offset(struct vcpu *v,
uint64_t offset)
if ( !nestedhvm_enabled(d) )
{
vmcb_set_tsc_offset(vmcb, offset);
+ svm_vmcb_unmap(v, vmcb);
return;
}
- n1vmcb = vcpu_nestedhvm(v).nv_n1vmcx;
- n2vmcb = vcpu_nestedhvm(v).nv_n2vmcx;
-
+ /*
+ * A vCPU stays in nested guest mode across a deschedule or a pause, so
+ * this can run remotely with the L2 VMCB active: map both explicitly.
+ */
if ( nestedhvm_vcpu_in_guestmode(v) )
{
+ const struct nestedvcpu *nv = &vcpu_nestedhvm(v);
+
+ n1vmcb = svm_vmcb_map_one(v, nv->nv_n1vmcx, nv->nv_n1vmcx_pa);
+ n2vmcb = svm_vmcb_map_one(v, nv->nv_n2vmcx, nv->nv_n2vmcx_pa);
+
n2_tsc_offset = vmcb_get_tsc_offset(n2vmcb) -
vmcb_get_tsc_offset(n1vmcb);
vmcb_set_tsc_offset(n1vmcb, offset);
+
+ svm_vmcb_unmap(v, n2vmcb);
+ svm_vmcb_unmap(v, n1vmcb);
}
vmcb_set_tsc_offset(vmcb, offset + n2_tsc_offset);
+ svm_vmcb_unmap(v, vmcb);
}
static void cf_check svm_set_rdtsc_exiting(struct vcpu *v, bool enable)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
u32 general1_intercepts = vmcb_get_general1_intercepts(vmcb);
u32 general2_intercepts = vmcb_get_general2_intercepts(vmcb);
@@ -783,13 +829,14 @@ static void cf_check svm_set_rdtsc_exiting(struct vcpu
*v, bool enable)
vmcb_set_general1_intercepts(vmcb, general1_intercepts);
vmcb_set_general2_intercepts(vmcb, general2_intercepts);
+ svm_vmcb_unmap(v, vmcb);
}
#ifdef CONFIG_VM_EVENT
static void cf_check svm_set_descriptor_access_exiting(
struct vcpu *v, bool enable)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
u32 general1_intercepts = vmcb_get_general1_intercepts(vmcb);
u32 mask = GENERAL1_INTERCEPT_IDTR_READ | GENERAL1_INTERCEPT_GDTR_READ
| GENERAL1_INTERCEPT_LDTR_READ | GENERAL1_INTERCEPT_TR_READ
@@ -802,6 +849,7 @@ static void cf_check svm_set_descriptor_access_exiting(
general1_intercepts &= ~mask;
vmcb_set_general1_intercepts(vmcb, general1_intercepts);
+ svm_vmcb_unmap(v, vmcb);
}
#endif /* CONFIG_VM_EVENT */
@@ -1360,7 +1408,12 @@ static void cf_check svm_inject_event(const struct
x86_event *event)
static bool cf_check svm_event_pending(const struct vcpu *v)
{
- return v->arch.hvm.svm.vmcb->event_inj.v;
+ const struct vmcb_struct *vmcb = svm_vmcb_map(v);
+ bool pending = vmcb->event_inj.v;
+
+ svm_vmcb_unmap(v, vmcb);
+
+ return pending;
}
static void cf_check svm_cpu_dead(unsigned int cpu)
@@ -2318,45 +2371,55 @@ static void cf_check svm_invlpg(struct vcpu *v,
unsigned long linear)
static bool cf_check svm_get_pending_event(
struct vcpu *v, struct x86_event *info)
{
- const struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ const struct vmcb_struct *vmcb = svm_vmcb_map(v);
+ bool pending = vmcb->event_inj.v;
- if ( !vmcb->event_inj.v )
- return false;
+ if ( pending )
+ {
+ info->vector = vmcb->event_inj.vector;
+ info->type = vmcb->event_inj.type;
+ info->error_code = vmcb->event_inj.ec;
+ }
- info->vector = vmcb->event_inj.vector;
- info->type = vmcb->event_inj.type;
- info->error_code = vmcb->event_inj.ec;
+ svm_vmcb_unmap(v, vmcb);
- return true;
+ return pending;
}
static uint64_t cf_check svm_get_reg(struct vcpu *v, unsigned int reg)
{
struct vcpu *curr = current;
- const struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ const struct vmcb_struct *vmcb = svm_vmcb_map(v);
struct domain *d = v->domain;
+ uint64_t val = 0;
switch ( reg )
{
case MSR_SPEC_CTRL:
- return vmcb->spec_ctrl;
+ val = vmcb->spec_ctrl;
+ break;
case MSR_SHADOW_GS_BASE:
if ( v == curr )
svm_sync_vmcb(v, vmcb_in_sync);
- return vmcb->kerngsbase;
+ val = vmcb->kerngsbase;
+ break;
default:
printk(XENLOG_G_ERR "%s(%pv, 0x%08x) Bad register\n",
__func__, v, reg);
domain_crash(d);
- return 0;
+ break;
}
+
+ svm_vmcb_unmap(v, vmcb);
+
+ return val;
}
static void cf_check svm_set_reg(struct vcpu *v, unsigned int reg, uint64_t
val)
{
- struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+ struct vmcb_struct *vmcb = svm_vmcb_map(v);
struct domain *d = v->domain;
switch ( reg )
@@ -2371,6 +2434,8 @@ static void cf_check svm_set_reg(struct vcpu *v, unsigned
int reg, uint64_t val)
domain_crash(d);
break;
}
+
+ svm_vmcb_unmap(v, vmcb);
}
static struct hvm_function_table __initdata_cf_clobber svm_function_table = {
diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
index a6f09672a7..4c48bbc76d 100644
--- a/xen/arch/x86/hvm/svm/vmcb.c
+++ b/xen/arch/x86/hvm/svm/vmcb.c
@@ -43,11 +43,25 @@ void free_vmcb(struct vmcb_struct *vmcb)
free_xenheap_page(vmcb);
}
+struct vmcb_struct *svm_vmcb_map_one(const struct vcpu *v,
+ struct vmcb_struct *va, paddr_t pa)
+{
+ return va;
+}
+
+struct vmcb_struct *svm_vmcb_map(const struct vcpu *v)
+{
+ return svm_vmcb_map_one(v, v->arch.hvm.svm.vmcb, v->arch.hvm.svm.vmcb_pa);
+}
+
+void svm_vmcb_unmap(const struct vcpu *v, const struct vmcb_struct *vmcb)
+{
+}
+
/* This function can directly access fields which are covered by clean bits. */
-static int construct_vmcb(struct vcpu *v)
+static int construct_vmcb(struct vcpu *v, struct vmcb_struct *vmcb)
{
struct svm_vcpu *svm = &v->arch.hvm.svm;
- struct vmcb_struct *vmcb = svm->vmcb;
vmcb->_general1_intercepts =
GENERAL1_INTERCEPT_INTR | GENERAL1_INTERCEPT_NMI |
@@ -201,6 +215,7 @@ int svm_create_vmcb(struct vcpu *v)
{
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
struct svm_vcpu *svm = &v->arch.hvm.svm;
+ struct vmcb_struct *vmcb;
int rc;
if ( (nv->nv_n1vmcx == NULL) &&
@@ -211,7 +226,10 @@ int svm_create_vmcb(struct vcpu *v)
}
svm->vmcb = nv->nv_n1vmcx;
- rc = construct_vmcb(v);
+
+ vmcb = svm_vmcb_map(v);
+ rc = construct_vmcb(v, vmcb);
+ svm_vmcb_unmap(v, vmcb);
if ( rc != 0 )
{
free_vmcb(nv->nv_n1vmcx);
@@ -407,6 +425,7 @@ static void cf_check vmcb_dump(unsigned char ch)
{
struct domain *d;
struct vcpu *v;
+ struct vmcb_struct *vmcb;
printk("*********** VMCB Areas **************\n");
@@ -425,7 +444,9 @@ static void cf_check vmcb_dump(unsigned char ch)
continue;
}
printk("\tVCPU %d\n", v->vcpu_id);
- svm_vmcb_dump("key_handler", v->arch.hvm.svm.vmcb);
+ vmcb = svm_vmcb_map(v);
+ svm_vmcb_dump("key_handler", vmcb);
+ svm_vmcb_unmap(v, vmcb);
process_pending_softirqs();
}
diff --git a/xen/arch/x86/hvm/svm/vmcb.h b/xen/arch/x86/hvm/svm/vmcb.h
index 28dae325ce..9d61767ec7 100644
--- a/xen/arch/x86/hvm/svm/vmcb.h
+++ b/xen/arch/x86/hvm/svm/vmcb.h
@@ -559,6 +559,15 @@ struct vmcb_struct {
struct vmcb_struct *alloc_vmcb(void);
void free_vmcb(struct vmcb_struct *vmcb);
+/*
+ * Access a VMCB of a vCPU that may not be current. svm_vmcb_map() takes
+ * the active VMCB, svm_vmcb_map_one() a specific one.
+ */
+struct vmcb_struct *svm_vmcb_map_one(const struct vcpu *v,
+ struct vmcb_struct *va, paddr_t pa);
+struct vmcb_struct *svm_vmcb_map(const struct vcpu *v);
+void svm_vmcb_unmap(const struct vcpu *v, const struct vmcb_struct *vmcb);
+
int svm_create_vmcb(struct vcpu *v);
void svm_destroy_vmcb(struct vcpu *v);
--
2.55.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |