From 3356d685dbdaf8f442c652fc216ecbc20bc7f6e3 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Tue, 24 Mar 2026 18:19:32 +0000 Subject: [PATCH] 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 Reviewed-by: Andrew Cooper --- xen/arch/x86/hvm/svm/nestedsvm.c | 67 +------------------- xen/arch/x86/hvm/svm/svm.c | 81 +----------------------- xen/arch/x86/hvm/svm/vmcb.c | 4 +- xen/arch/x86/include/asm/hvm/svm-types.h | 6 -- 4 files changed, 4 insertions(+), 154 deletions(-) diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c index a63ec61346..ef6fa5d23b 100644 --- a/xen/arch/x86/hvm/svm/nestedsvm.c +++ b/xen/arch/x86/hvm/svm/nestedsvm.c @@ -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) ) diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 243c41fb13..2546705d24 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -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; diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c index e583ef8548..5ed7123d9a 100644 --- a/xen/arch/x86/hvm/svm/vmcb.c +++ b/xen/arch/x86/hvm/svm/vmcb.c @@ -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) ) { diff --git a/xen/arch/x86/include/asm/hvm/svm-types.h b/xen/arch/x86/include/asm/hvm/svm-types.h index 051b235d8f..3ede62cade 100644 --- a/xen/arch/x86/include/asm/hvm/svm-types.h +++ b/xen/arch/x86/include/asm/hvm/svm-types.h @@ -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.