Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions arch/x86/include/asm/fpu/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,22 @@ static inline void kernel_fpu_begin(void)
*/
static inline void fpregs_lock(void)
{
#ifndef __PKVM_HYP__
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_bh_disable();
else
preempt_disable();
#endif
}

static inline void fpregs_unlock(void)
{
#ifndef __PKVM_HYP__
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_bh_enable();
else
preempt_enable();
#endif
}

/*
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/asm/kvm_pkvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ enum pkvm_fn {
__pkvm__cache_reg,
__pkvm__update_cpuid_runtime,
__pkvm__update_exception_bitmap,
__pkvm__vcpu_add_fpstate,
};

#define HOST_HANDLE_EXIT 0
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/kernel/fpu/xstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ static inline void os_xsave(struct fpstate *fpstate)
u32 hmask = mask >> 32;
int err;

#ifndef __PKVM_HYP__
WARN_ON_FPU(!alternatives_patched);
#endif
xfd_validate_state(fpstate, mask, false);

XSTATE_XSAVE(&fpstate->regs.xsave, lmask, hmask, err);
Expand Down
10 changes: 0 additions & 10 deletions arch/x86/kvm/pkvm/cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,7 @@ static int kvm_check_cpuid(struct kvm_vcpu *vcpu,
if (!xfeatures)
return 0;

#ifdef __PKVM_HYP__
/*
* TODO: The guest fpu xfd feature is enabled by the host when the host
* KVM run its kvm_check_cpuid function before calling the
* vcpu_after_set_cpuid PV interface. Revisit when implements the fpu
* isolation.
*/
return 0;
#else
return fpu_enable_guest_xfd_features(&vcpu->arch.guest_fpu, xfeatures);
#endif
}

/* Check whether the supplied CPUID data is equal to what is already set for the vCPU. */
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/kvm/pkvm/def.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
/* FIXME: Disable SGX to simplify POC */
#undef CONFIG_X86_SGX_KVM
#undef CONFIG_PREEMPT_COUNT
#undef CONFIG_USE_X86_SEG_SUPPORT
#undef CONFIG_X86_DEBUG_FPU
#define __NO_FORTIFY

#include <linux/types.h>
Expand Down
307 changes: 307 additions & 0 deletions arch/x86/kvm/pkvm/fpu/core.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <asm/fpu/api.h>
#include <asm/fpu/sched.h>
#include <asm/fpu/signal.h>
#include <asm/fpu/types.h>
#include <asm/cpufeatures.h>
#include <asm/cpufeature.h>
#include <asm/msr.h>
#include <asm/smap.h>

#include "internal.h"
#include "legacy.h"
#include "xstate.h"
#include "context.h"

#ifdef CONFIG_X86_64
DEFINE_STATIC_KEY_FALSE(__fpu_state_size_dynamic);
DEFINE_PER_CPU(u64, xfd_state);
#endif

/* The FPU state configuration data for kernel and user space */
struct fpu_state_config fpu_kernel_cfg __ro_after_init;
struct fpu_state_config fpu_user_cfg __ro_after_init;

/*
* Represents the initial FPU state. It's mostly (but not completely) zeroes,
* depending on the FPU hardware format:
*/
struct fpstate init_fpstate __ro_after_init;

/*
* Track which context is using the FPU on the CPU:
*/
DEFINE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);

/*
* Save the FPU register state in fpu->fpstate->regs. The register state is
* preserved.
*
* Must be called with fpregs_lock() held.
*
* The legacy FNSAVE instruction clears all FPU state unconditionally, so
* register state has to be reloaded. That might be a pointless exercise
* when the FPU is going to be used by another task right after that. But
* this only affects 20+ years old 32bit systems and avoids conditionals all
* over the place.
*
* FXSAVE and all XSAVE variants preserve the FPU register state.
*/
void save_fpregs_to_fpstate(struct fpu *fpu)
{
if (likely(use_xsave())) {
os_xsave(fpu->fpstate);
#ifndef __PKVM_HYP__
update_avx_timestamp(fpu);
#endif
return;
}

if (likely(use_fxsr())) {
fxsave(&fpu->fpstate->regs.fxsave);
return;
}

/*
* Legacy FPU register saving, FNSAVE always clears FPU registers,
* so we have to reload them from the memory state.
*/
asm volatile("fnsave %[fp]; fwait" : [fp] "=m" (fpu->fpstate->regs.fsave));
frstor(&fpu->fpstate->regs.fsave);
}

void restore_fpregs_from_fpstate(struct fpstate *fpstate, u64 mask)
{
/*
* AMD K7/K8 and later CPUs up to Zen don't save/restore
* FDP/FIP/FOP unless an exception is pending. Clear the x87 state
* here by setting it to fixed values. "m" is a random variable
* that should be in L1.
*/
if (unlikely(static_cpu_has_bug(X86_BUG_FXSAVE_LEAK))) {
asm volatile(
"fnclex\n\t"
"emms\n\t"
"fildl %[addr]" /* set F?P to defined value */
: : [addr] "m" (*fpstate));
}

if (use_xsave()) {
/*
* Dynamically enabled features are enabled in XCR0, but
* usage requires also that the corresponding bits in XFD
* are cleared. If the bits are set then using a related
* instruction will raise #NM. This allows to do the
* allocation of the larger FPU buffer lazy from #NM or if
* the task has no permission to kill it which would happen
* via #UD if the feature is disabled in XCR0.
*
* XFD state is following the same life time rules as
* XSTATE and to restore state correctly XFD has to be
* updated before XRSTORS otherwise the component would
* stay in or go into init state even if the bits are set
* in fpstate::regs::xsave::xfeatures.
*/
xfd_update_state(fpstate);

/*
* Restoring state always needs to modify all features
* which are in @mask even if the current task cannot use
* extended features.
*
* So fpstate->xfeatures cannot be used here, because then
* a feature for which the task has no permission but was
* used by the previous task would not go into init state.
*/
mask = fpu_kernel_cfg.max_features & mask;

os_xrstor(fpstate, mask);
} else {
if (use_fxsr())
fxrstor(&fpstate->regs.fxsave);
else
frstor(&fpstate->regs.fsave);
}
}

static inline void fpstate_init_fxstate(struct fpstate *fpstate)
{
fpstate->regs.fxsave.cwd = 0x37f;
fpstate->regs.fxsave.mxcsr = MXCSR_DEFAULT;
}

/*
* Legacy x87 fpstate state init:
*/
static inline void fpstate_init_fstate(struct fpstate *fpstate)
{
fpstate->regs.fsave.cwd = 0xffff037fu;
fpstate->regs.fsave.swd = 0xffff0000u;
fpstate->regs.fsave.twd = 0xffffffffu;
fpstate->regs.fsave.fos = 0xffff0000u;
}

/*
* Used in two places:
* 1) Early boot to setup init_fpstate for non XSAVE systems
* 2) fpu_init_fpstate_user() which is invoked from KVM
*/
void fpstate_init_user(struct fpstate *fpstate)
{
if (!cpu_feature_enabled(X86_FEATURE_FPU)) {
#ifndef __PKVM_HYP__
fpstate_init_soft(&fpstate->regs.soft);
#endif
return;
}

xstate_init_xcomp_bv(&fpstate->regs.xsave, fpstate->xfeatures);

if (cpu_feature_enabled(X86_FEATURE_FXSR))
fpstate_init_fxstate(fpstate);
else
fpstate_init_fstate(fpstate);
}

/*
* fpu_enable_guest_xfd_features - Check xfeatures against guest perm and enable
* @guest_fpu: Pointer to the guest FPU container
* @xfeatures: Features requested by guest CPUID
*
* Enable all dynamic xfeatures according to guest perm and requested CPUID.
*
* Return: 0 on success, error code otherwise
*/
int fpu_enable_guest_xfd_features(struct fpu_guest *guest_fpu, u64 xfeatures)
{
#ifndef __PKVM_HYP__
lockdep_assert_preemption_enabled();
#endif

/* Nothing to do if all requested features are already enabled. */
xfeatures &= ~guest_fpu->xfeatures;
if (!xfeatures)
return 0;

return __xfd_enable_feature(xfeatures, guest_fpu);
}
EXPORT_SYMBOL_GPL(fpu_enable_guest_xfd_features);

#ifdef CONFIG_X86_64
void fpu_update_guest_xfd(struct fpu_guest *guest_fpu, u64 xfd)
{
fpregs_lock();
guest_fpu->fpstate->xfd = xfd;
if (guest_fpu->fpstate->in_use)
xfd_update_state(guest_fpu->fpstate);
fpregs_unlock();
}
EXPORT_SYMBOL_GPL(fpu_update_guest_xfd);

/**
* fpu_sync_guest_vmexit_xfd_state - Synchronize XFD MSR and software state
*
* Must be invoked from KVM after a VMEXIT before enabling interrupts when
* XFD write emulation is disabled. This is required because the guest can
* freely modify XFD and the state at VMEXIT is not guaranteed to be the
* same as the state on VMENTER. So software state has to be updated before
* any operation which depends on it can take place.
*
* Note: It can be invoked unconditionally even when write emulation is
* enabled for the price of a then pointless MSR read.
*/
void fpu_sync_guest_vmexit_xfd_state(void)
{
struct fpstate *fps = current->thread.fpu.fpstate;

#ifndef __PKVM_HYP__
lockdep_assert_irqs_disabled();
#endif
if (fpu_state_size_dynamic()) {
rdmsrl(MSR_IA32_XFD, fps->xfd);
__this_cpu_write(xfd_state, fps->xfd);
}
}
EXPORT_SYMBOL_GPL(fpu_sync_guest_vmexit_xfd_state);
#endif

int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
{
struct fpstate *guest_fps = guest_fpu->fpstate;
struct fpu *fpu = &current->thread.fpu;
struct fpstate *cur_fps = fpu->fpstate;

fpregs_lock();
#ifdef __PKVM_HYP__
#ifdef CONFIG_X86_64
if (fpu_state_size_dynamic() && enter_guest) {
/*
* Refresh the xfd_state before guest vmenter so that the xfd can be
* restored after guest vmexit.
*/
rdmsrl(MSR_IA32_XFD, cur_fps->xfd);
__this_cpu_write(xfd_state, cur_fps->xfd);
}
#endif
/*
* Only save the FPU registers when exit guest for the pVM. When enter
* the guest, the FPU registers hold the value of the host, which is
* saved by the host itself.
*/
if (guest_fps->is_confidential && !enter_guest)
#else
if (!cur_fps->is_confidential && !test_thread_flag(TIF_NEED_FPU_LOAD))
#endif
save_fpregs_to_fpstate(fpu);

/* Swap fpstate */
if (enter_guest) {
fpu->__task_fpstate = cur_fps;
fpu->fpstate = guest_fps;
guest_fps->in_use = true;
} else {
guest_fps->in_use = false;
fpu->fpstate = fpu->__task_fpstate;
fpu->__task_fpstate = NULL;
}

cur_fps = fpu->fpstate;

#ifdef __PKVM_HYP__
/*
* For the pVM, when enter guest, restore the FPU with the data from the
* pVM's xsave area. When exit guest, restore the FPU with the initial
* data to wipe the pVM's FPU registers.
*
* For the npVM, no need to restore.
*/
if (guest_fps->is_confidential) {
#else
if (!cur_fps->is_confidential) {
#endif
/* Includes XFD update */
restore_fpregs_from_fpstate(cur_fps, XFEATURE_MASK_FPSTATE);
} else {
/*
* XSTATE is restored by firmware from encrypted
* memory. Make sure XFD state is correct while
* running with guest fpstate
*/
xfd_update_state(cur_fps);
}

fpregs_mark_activate();
fpregs_unlock();

return 0;
}
EXPORT_SYMBOL_GPL(fpu_swap_kvm_fpstate);

void fpregs_mark_activate(void)
{
struct fpu *fpu = &current->thread.fpu;

fpregs_activate(fpu);
fpu->last_cpu = smp_processor_id();
clear_thread_flag(TIF_NEED_FPU_LOAD);
}
Loading