x86/svm: Remove lazy FPU support

Remove lazy FPU support from the SVM code since fully_eager_fpu is now
always true.

No functional change intended.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
This commit is contained in:
Ross Lagerwall authored and Andrew Cooper committed 2026-03-30 15:48:13 +01:00
1 parent 511ab162ef
commit 3356d685db
4 files changed
+4 -154

No files matched your search

+1 -66
View File
@@ -165,58 +165,6 @@ int cf_check nsvm_vcpu_reset(struct vcpu *v)
return 0;
}
static uint64_t nestedsvm_fpu_vmentry(uint64_t n1cr0,
struct vmcb_struct *vvmcb,
struct vmcb_struct *n1vmcb, struct vmcb_struct *n2vmcb)
{
uint64_t vcr0;
vcr0 = vvmcb->_cr0;
if ( !(n1cr0 & X86_CR0_TS) && (n1vmcb->_cr0 & X86_CR0_TS) )
{
/*
* svm_fpu_leave() run while l1 guest was running.
* Sync FPU state with l2 guest.
*/
vcr0 |= X86_CR0_TS;
n2vmcb->_exception_intercepts |= (1U << X86_EXC_NM);
}
else if ( !(vcr0 & X86_CR0_TS) && (n2vmcb->_cr0 & X86_CR0_TS) )
{
/*
* svm_fpu_enter() run while l1 guest was running.
* Sync FPU state with l2 guest.
*/
vcr0 &= ~X86_CR0_TS;
n2vmcb->_exception_intercepts &= ~(1U << X86_EXC_NM);
}
return vcr0;
}
static void nestedsvm_fpu_vmexit(struct vmcb_struct *n1vmcb,
struct vmcb_struct *n2vmcb, uint64_t n1cr0, uint64_t guest_cr0)
{
if ( !(guest_cr0 & X86_CR0_TS) && (n2vmcb->_cr0 & X86_CR0_TS) )
{
/*
* svm_fpu_leave() run while l2 guest was running.
* Sync FPU state with l1 guest.
*/
n1vmcb->_cr0 |= X86_CR0_TS;
n1vmcb->_exception_intercepts |= (1U << X86_EXC_NM);
}
else if ( !(n1cr0 & X86_CR0_TS) && (n1vmcb->_cr0 & X86_CR0_TS) )
{
/*
* svm_fpu_enter() run while l2 guest was running.
* Sync FPU state with l1 guest.
*/
n1vmcb->_cr0 &= ~X86_CR0_TS;
n1vmcb->_exception_intercepts &= ~(1U << X86_EXC_NM);
}
}
static int nsvm_vcpu_hostsave(struct vcpu *v, unsigned int inst_len)
{
struct nestedsvm *svm = &vcpu_nestedsvm(v);
@@ -246,7 +194,6 @@ static int nsvm_vcpu_hostsave(struct vcpu *v, unsigned int inst_len)
static int nsvm_vcpu_hostrestore(struct vcpu *v, struct cpu_user_regs *regs)
{
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
struct nestedsvm *svm = &vcpu_nestedsvm(v);
struct vmcb_struct *n1vmcb, *n2vmcb;
int rc;
@@ -281,8 +228,6 @@ static int nsvm_vcpu_hostrestore(struct vcpu *v, struct cpu_user_regs *regs)
gdprintk(XENLOG_ERR, "hvm_set_cr4 failed, rc: %u\n", rc);
/* CR0 */
nestedsvm_fpu_vmexit(n1vmcb, n2vmcb,
svm->ns_cr0, v->arch.hvm.guest_cr[0]);
v->arch.hvm.guest_cr[0] = n1vmcb->_cr0 | X86_CR0_PE;
n1vmcb->rflags &= ~X86_EFLAGS_VM;
rc = hvm_set_cr0(n1vmcb->_cr0 | X86_CR0_PE, true);
@@ -290,7 +235,6 @@ static int nsvm_vcpu_hostrestore(struct vcpu *v, struct cpu_user_regs *regs)
hvm_inject_hw_exception(X86_EXC_GP, 0);
if ( rc != X86EMUL_OKAY )
gdprintk(XENLOG_ERR, "hvm_set_cr0 failed, rc: %u\n", rc);
svm->ns_cr0 = v->arch.hvm.guest_cr[0];
/* CR2 */
v->arch.hvm.guest_cr[2] = n1vmcb->_cr2;
@@ -418,7 +362,6 @@ static int nsvm_vmcb_prepare4vmrun(struct vcpu *v, struct cpu_user_regs *regs)
struct vmcb_struct *ns_vmcb, *n1vmcb, *n2vmcb;
vmcbcleanbits_t clean = {};
int rc;
uint64_t cr0;
ns_vmcb = nv->nv_vvmcx;
n1vmcb = nv->nv_n1vmcx;
@@ -452,7 +395,6 @@ static int nsvm_vmcb_prepare4vmrun(struct vcpu *v, struct cpu_user_regs *regs)
* safed here.
* The overhead comes from (ordered from highest to lowest):
* - svm_ctxt_switch_to (CPU context switching)
* - svm_fpu_enter, svm_fpu_leave (lazy FPU switching)
* - emulated CLGI (clears VINTR intercept)
* - host clears VINTR intercept
* Test results show that the overhead is high enough that the
@@ -551,10 +493,8 @@ static int nsvm_vmcb_prepare4vmrun(struct vcpu *v, struct cpu_user_regs *regs)
gdprintk(XENLOG_ERR, "hvm_set_cr4 failed, rc: %u\n", rc);
/* CR0 */
svm->ns_cr0 = v->arch.hvm.guest_cr[0];
cr0 = nestedsvm_fpu_vmentry(svm->ns_cr0, ns_vmcb, n1vmcb, n2vmcb);
v->arch.hvm.guest_cr[0] = ns_vmcb->_cr0;
rc = hvm_set_cr0(cr0, true);
rc = hvm_set_cr0(ns_vmcb->_cr0, true);
if ( rc == X86EMUL_EXCEPTION )
hvm_inject_hw_exception(X86_EXC_GP, 0);
if ( rc != X86EMUL_OKAY )
@@ -1305,11 +1245,6 @@ nestedsvm_check_intercepts(struct vcpu *v, struct cpu_user_regs *regs,
case VMEXIT_INTR:
case VMEXIT_NMI:
return NESTEDHVM_VMEXIT_HOST;
case VMEXIT_EXCEPTION_NM:
/* Host must handle lazy fpu context switching first.
* Then inject the VMEXIT if L1 guest intercepts this.
*/
return NESTEDHVM_VMEXIT_HOST;
case VMEXIT_NPF:
if ( nestedhvm_paging_mode_hap(v) )
+2 -79
View File
@@ -104,38 +104,6 @@ static void cf_check svm_cpu_down(void)
write_efer(read_efer() & ~EFER_SVME);
}
static void svm_fpu_enter(struct vcpu *v)
{
struct vmcb_struct *n1vmcb = vcpu_nestedhvm(v).nv_n1vmcx;
vcpu_restore_fpu_lazy(v);
vmcb_set_exception_intercepts(
n1vmcb,
vmcb_get_exception_intercepts(n1vmcb) & ~(1U << X86_EXC_NM));
}
static void cf_check svm_fpu_leave(struct vcpu *v)
{
struct vmcb_struct *n1vmcb = vcpu_nestedhvm(v).nv_n1vmcx;
ASSERT(!v->fpu_dirtied);
ASSERT(read_cr0() & X86_CR0_TS);
/*
* If the guest does not have TS enabled then we must cause and handle an
* exception on first use of the FPU. If the guest *does* have TS enabled
* then this is not necessary: no FPU activity can occur until the guest
* clears CR0.TS, and we will initialise the FPU when that happens.
*/
if ( !(v->arch.hvm.guest_cr[0] & X86_CR0_TS) )
{
vmcb_set_exception_intercepts(
n1vmcb,
vmcb_get_exception_intercepts(n1vmcb) | (1U << X86_EXC_NM));
vmcb_set_cr0(n1vmcb, vmcb_get_cr0(n1vmcb) | X86_CR0_TS);
}
}
static void cf_check svm_update_guest_cr(
struct vcpu *v, unsigned int cr, unsigned int flags)
{
@@ -145,20 +113,6 @@ static void cf_check svm_update_guest_cr(
switch ( cr )
{
case 0:
{
unsigned long hw_cr0_mask = 0;
if ( !(v->arch.hvm.guest_cr[0] & X86_CR0_TS) )
{
if ( v != current )
{
if ( !v->arch.fully_eager_fpu )
hw_cr0_mask |= X86_CR0_TS;
}
else if ( vmcb_get_cr0(vmcb) & X86_CR0_TS )
svm_fpu_enter(v);
}
if ( paging_mode_hap(v->domain) )
{
uint32_t intercepts = vmcb_get_cr_intercepts(vmcb);
@@ -169,12 +123,12 @@ static void cf_check svm_update_guest_cr(
vmcb_set_cr_intercepts(vmcb, intercepts | CR_INTERCEPT_CR3_WRITE);
}
value = v->arch.hvm.guest_cr[0] | hw_cr0_mask;
value = v->arch.hvm.guest_cr[0];
if ( paging_mode_shadow(v->domain) )
value |= X86_CR0_PG | X86_CR0_WP;
vmcb_set_cr0(vmcb, value);
break;
}
case 2:
vmcb_set_cr2(vmcb, v->arch.hvm.guest_cr[2]);
break;
@@ -909,9 +863,6 @@ static void cf_check svm_ctxt_switch_from(struct vcpu *v)
if ( unlikely((read_efer() & EFER_SVME) == 0) )
return;
if ( !v->arch.fully_eager_fpu )
svm_fpu_leave(v);
svm_save_dr(v);
svm_tsc_ratio_save(v);
@@ -1678,28 +1629,6 @@ static void svm_do_nested_pgfault(struct vcpu *v,
domain_crash(v->domain);
}
static void cf_check svm_fpu_dirty_intercept(void)
{
struct vcpu *v = current;
struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
struct vmcb_struct *n1vmcb = vcpu_nestedhvm(v).nv_n1vmcx;
svm_fpu_enter(v);
if ( vmcb != n1vmcb )
{
/* Check if l1 guest must make FPU ready for the l2 guest */
if ( v->arch.hvm.guest_cr[0] & X86_CR0_TS )
hvm_inject_hw_exception(X86_EXC_NM, X86_EVENT_NO_EC);
else
vmcb_set_cr0(n1vmcb, vmcb_get_cr0(n1vmcb) & ~X86_CR0_TS);
return;
}
if ( !(v->arch.hvm.guest_cr[0] & X86_CR0_TS) )
vmcb_set_cr0(vmcb, vmcb_get_cr0(vmcb) & ~X86_CR0_TS);
}
static void svm_vmexit_do_cr_access(
struct vmcb_struct *vmcb, struct cpu_user_regs *regs)
{
@@ -2459,7 +2388,6 @@ static struct hvm_function_table __initdata_cf_clobber svm_function_table = {
.update_guest_cr = svm_update_guest_cr,
.update_guest_efer = svm_update_guest_efer,
.cpuid_policy_changed = svm_cpuid_policy_changed,
.fpu_leave = svm_fpu_leave,
.set_guest_pat = svm_set_guest_pat,
.get_guest_pat = svm_get_guest_pat,
.set_tsc_offset = svm_set_tsc_offset,
@@ -2469,7 +2397,6 @@ static struct hvm_function_table __initdata_cf_clobber svm_function_table = {
.get_pending_event = svm_get_pending_event,
.invlpg = svm_invlpg,
.wbinvd_intercept = svm_wbinvd_intercept,
.fpu_dirty_intercept = svm_fpu_dirty_intercept,
.msr_read_intercept = svm_msr_read_intercept,
.msr_write_intercept = svm_msr_write_intercept,
#ifdef CONFIG_VM_EVENT
@@ -2783,10 +2710,6 @@ void asmlinkage svm_vmexit_handler(void)
}
break;
case VMEXIT_EXCEPTION_NM:
svm_fpu_dirty_intercept();
break;
case VMEXIT_EXCEPTION_PF:
{
unsigned long va = vmcb->ei.exc.cr2;
+1 -3
View File
@@ -138,9 +138,7 @@ static int construct_vmcb(struct vcpu *v)
paging_update_paging_modes(v);
vmcb->_exception_intercepts =
HVM_TRAP_MASK |
(v->arch.fully_eager_fpu ? 0 : (1U << X86_EXC_NM));
vmcb->_exception_intercepts = HVM_TRAP_MASK;
if ( paging_mode_hap(v->domain) )
{
-6
View File
@@ -65,12 +65,6 @@ struct nestedsvm {
/* Shadow io permission map */
unsigned long *ns_iomap;
/*
* Cached guest_cr[0] of l1 guest while l2 guest runs. Needed to handle
* FPU context switching.
*/
uint64_t ns_cr0;
/*
* Cache guest cr3/host cr3 the guest sets up for the l2 guest.
* Used by Shadow-on-Shadow and Nested-on-Nested.
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.