kvm: enable CPUID faulting on all VCPUs

This feature is controlled by an MSR; MSRs are per-CPU.

The Intel SDM doesn't document CPUID faulting, at least as of the Dec 2024
revision; despite the deleted comment in ring0/kernel_amd64.go, there is no
Vol. 3 Table 2-43, and every table in Vol. 4 ("Model-Specific Registers") lists
bit 31 in MSR_PLATFORM_INFO as "reserved". The only documentation seems to be
that cited by Linux's e9ea1e7f53b85 ("x86/arch_prctl: Add
ARCH_[GET|SET]_CPUID"): "Intel Virtualization Technology FlexMigration
Application Note" 323850-004, 2012. This document positions CPUID faulting as
an alternative way to support cross-CPU migration for VMs that don't use VMX;
consequently it does not clarify if CPUID faulting is effective in guest ("VMX
non-root") mode, or if the CPUID VM exit takes precedence. If the former is the
case then CPUID faulting is probably faster than setting app CPUID with
KVM_SET_CPUID2, and vice versa. But regardless, this is much simpler.

PiperOrigin-RevId: 733113944
This commit is contained in:
Jamie Liu
2025-03-03 17:15:39 -08:00
committed by gVisor bot
parent f06d4e7ebe
commit 2247aceb99
4 changed files with 44 additions and 49 deletions
-21
View File
@@ -315,27 +315,6 @@ func startGo(c *CPU) {
wrmsr(_MSR_CSTAR, kernelFunc(addrOfSysenter()))
}
// SetCPUIDFaulting sets CPUID faulting per the boolean value.
//
// True is returned if faulting could be set.
//
//go:nosplit
func SetCPUIDFaulting(on bool) bool {
// Per the SDM (Vol 3, Table 2-43), PLATFORM_INFO bit 31 denotes support
// for CPUID faulting, and we enable and disable via the MISC_FEATURES MSR.
if rdmsr(_MSR_PLATFORM_INFO)&_PLATFORM_INFO_CPUID_FAULT != 0 {
features := rdmsr(_MSR_MISC_FEATURES)
if on {
features |= _MISC_FEATURE_CPUID_TRAP
} else {
features &^= _MISC_FEATURE_CPUID_TRAP
}
wrmsr(_MSR_MISC_FEATURES, features)
return true // Setting successful.
}
return false
}
// ReadCR2 reads the current CR2 value.
//
//go:nosplit
+4 -10
View File
@@ -52,16 +52,10 @@ const (
_EFER_LMA = 0x400
_EFER_NX = 0x800
_MSR_STAR = 0xc0000081
_MSR_LSTAR = 0xc0000082
_MSR_CSTAR = 0xc0000083
_MSR_SYSCALL_MASK = 0xc0000084
_MSR_PLATFORM_INFO = 0xce
_MSR_MISC_FEATURES = 0x140
_PLATFORM_INFO_CPUID_FAULT = 1 << 31
_MISC_FEATURE_CPUID_TRAP = 0x1
_MSR_STAR = 0xc0000081
_MSR_LSTAR = 0xc0000082
_MSR_CSTAR = 0xc0000083
_MSR_SYSCALL_MASK = 0xc0000084
)
const (
+20 -18
View File
@@ -22,17 +22,18 @@ import (
"math/big"
"reflect"
"runtime"
"runtime/debug"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/hostsyscall"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
"gvisor.dev/gvisor/pkg/sentry/platform"
ktime "gvisor.dev/gvisor/pkg/sentry/time"
"gvisor.dev/gvisor/pkg/sync"
)
// initArchState initializes architecture-specific state.
@@ -54,22 +55,6 @@ func (m *machine) initArchState() error {
}
m.mu.Unlock()
c := m.Get()
defer m.Put(c)
// Enable CPUID faulting, if possible. Note that this also serves as a
// basic platform sanity tests, since we will enter guest mode for the
// first time here. The recovery is necessary, since if we fail to read
// the platform info register, we will retry to host mode and
// ultimately need to handle a segmentation fault.
old := debug.SetPanicOnFault(true)
defer func() {
recover()
debug.SetPanicOnFault(old)
}()
bluepill(c)
ring0.SetCPUIDFaulting(true)
return nil
}
@@ -97,6 +82,8 @@ const (
poolPCIDs = 8
)
var cpuidFaultingWarnOnce sync.Once
// initArchState initializes architecture-specific state.
func (c *vCPU) initArchState() error {
var (
@@ -158,7 +145,22 @@ func (c *vCPU) initArchState() error {
}
// Set the time offset to the host native time.
return c.setSystemTime()
if err := c.setSystemTime(); err != nil {
return err
}
// Try to enable CPUID faulting. This is required to handle app CPUID
// correctly, since we always pass the CPUID returned by
// KVM_GET_SUPPORTED_CPUID to KVM_SET_CPUID2. Note that while hardware
// support for CPUID faulting is inconsistent, KVM always supports it after
// db2336a80489e ("KVM: x86: virtualize cpuid faulting"), Linux 4.12+.
if err := c.enableCPUIDFaulting(); err != nil {
cpuidFaultingWarnOnce.Do(func() {
log.Warningf("Application CPUID will be incorrect: %v", err)
})
}
return nil
}
// bitsForScaling returns the bits available for storing the fraction component
@@ -140,6 +140,26 @@ func (c *vCPU) setTSC(value uint64) error {
return nil
}
func (c *vCPU) enableCPUIDFaulting() error {
const (
_MSR_MISC_FEATURES_ENABLE = 0x140
_MSR_MISC_FEATURES_ENABLES_CPUID_FAULT = 1 << 0
)
registers := modelControlRegisters{
nmsrs: 1,
}
registers.entries[0].index = _MSR_MISC_FEATURES_ENABLE
registers.entries[0].data = _MSR_MISC_FEATURES_ENABLES_CPUID_FAULT
if errno := hostsyscall.RawSyscallErrno(
unix.SYS_IOCTL,
uintptr(c.fd),
KVM_SET_MSRS,
uintptr(unsafe.Pointer(&registers))); errno != 0 {
return fmt.Errorf("error enabling CPUID faulting: %v", errno)
}
return nil
}
// setUserRegisters sets user registers in the vCPU.
//
//go:nosplit