mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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(®isters))); errno != 0 {
|
||||
return fmt.Errorf("error enabling CPUID faulting: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setUserRegisters sets user registers in the vCPU.
|
||||
//
|
||||
//go:nosplit
|
||||
|
||||
Reference in New Issue
Block a user