diff --git a/pkg/cpuid/cpuid.go b/pkg/cpuid/cpuid.go index 585a9a7d2..fae7d277f 100644 --- a/pkg/cpuid/cpuid.go +++ b/pkg/cpuid/cpuid.go @@ -28,8 +28,13 @@ package cpuid import ( + "encoding/binary" "fmt" + "os" + "runtime" "strings" + + "gvisor.dev/gvisor/pkg/log" ) // contextID is the package for context.Context.Value keys. @@ -38,6 +43,11 @@ type contextID int const ( // CtxFeatureSet is the FeatureSet for the context. CtxFeatureSet contextID = iota + + // hardware capability bit vector. + _AT_HWCAP = 16 + // hardware capability bit vector 2. + _AT_HWCAP2 = 26 ) // context represents context.Context. @@ -171,3 +181,75 @@ func (fs FeatureSet) CheckHostCompatible() error { // Make arch-specific checks. return fs.archCheckHostCompatible(hfs) } + +// +stateify savable +type hwCap struct { + // hwCap1 stores HWCAP bits exposed through the elf auxiliary vector. + hwCap1 uint64 + // hwCap2 stores HWCAP2 bits exposed through the elf auxiliary vector. + hwCap2 uint64 +} + +// The auxiliary vector of a process on the Linux system can be read +// from /proc/self/auxv, and tags and values are stored as 8-bytes +// decimal key-value pairs on the 64-bit system. +// +// $ od -t d8 /proc/self/auxv +// +// 0000000 33 140734615224320 +// 0000020 16 3219913727 +// 0000040 6 4096 +// 0000060 17 100 +// 0000100 3 94665627353152 +// 0000120 4 56 +// 0000140 5 9 +// 0000160 7 140425502162944 +// 0000200 8 0 +// 0000220 9 94665627365760 +// 0000240 11 1000 +// 0000260 12 1000 +// 0000300 13 1000 +// 0000320 14 1000 +// 0000340 23 0 +// 0000360 25 140734614619513 +// 0000400 26 0 +// 0000420 31 140734614626284 +// 0000440 15 140734614619529 +// 0000460 0 0 +func readHWCap(auxvFilepath string) (hwCap, error) { + c := hwCap{} + if runtime.GOOS != "linux" { + // Don't try to read Linux-specific /proc files. + return c, fmt.Errorf("readHwCap only supported on linux, not %s", runtime.GOOS) + } + + auxv, err := os.ReadFile(auxvFilepath) + if err != nil { + return c, fmt.Errorf("failed to read file %s: %w", auxvFilepath, err) + } + + l := len(auxv) / 16 + for i := 0; i < l; i++ { + tag := binary.LittleEndian.Uint64(auxv[i*16:]) + val := binary.LittleEndian.Uint64(auxv[i*16+8:]) + if tag == _AT_HWCAP { + c.hwCap1 = val + } else if tag == _AT_HWCAP2 { + c.hwCap2 = val + } + + if (c.hwCap1 != 0) && (c.hwCap2 != 0) { + break + } + } + return c, nil +} + +func initHWCap() { + c, err := readHWCap("/proc/self/auxv") + if err != nil { + log.Warningf("cpuid HWCap not initialized: %w", err) + } else { + hostFeatureSet.hwCap = c + } +} diff --git a/pkg/cpuid/cpuid_amd64.go b/pkg/cpuid/cpuid_amd64.go index d62f9082d..187e96668 100644 --- a/pkg/cpuid/cpuid_amd64.go +++ b/pkg/cpuid/cpuid_amd64.go @@ -23,6 +23,9 @@ import ( ) // FeatureSet defines features in terms of CPUID leaves and bits. +// The kernel also exposes the presence of features to userspace through +// a set of flags(HWCAP/HWCAP2) bits, exposed in the auxiliary vector, which +// are necessary to read for some features (e.g. FSGSBASE). // // Common references: // @@ -40,6 +43,8 @@ type FeatureSet struct { // This is exported to allow direct calls of the underlying CPUID // function, where required. Function `state:".(Static)"` + // hwCap stores HWCAP1/2 exposed from the elf auxiliary vector. + hwCap hwCap } // saveFunction saves the function as a static query. @@ -410,6 +415,12 @@ func (fs FeatureSet) UseXsavec() bool { return fs.UseXsaveopt() && fs.HasFeature(X86FeatureXSAVEC) } +// UseFSGSBASE returns true if 'fs' supports the (RD|WR)(FS|GS)BASE instructions. +func (fs FeatureSet) UseFSGSBASE() bool { + HWCAP2_FSGSBASE := uint64(1) << 1 + return fs.HasFeature(X86FeatureFSGSBase) && ((fs.hwCap.hwCap2 & HWCAP2_FSGSBASE) != 0) +} + // archCheckHostCompatible checks for compatibility. func (fs FeatureSet) archCheckHostCompatible(hfs FeatureSet) error { // The size of a cache line must match, as it is critical to correctly diff --git a/pkg/cpuid/cpuid_arm64.go b/pkg/cpuid/cpuid_arm64.go index a7c24c765..7a22e98b3 100644 --- a/pkg/cpuid/cpuid_arm64.go +++ b/pkg/cpuid/cpuid_arm64.go @@ -34,7 +34,7 @@ import ( // // +stateify savable type FeatureSet struct { - hwCap uint + hwCap hwCap cpuFreqMHz float64 cpuImplHex uint64 cpuArchDec uint64 @@ -79,15 +79,15 @@ func (fs FeatureSet) ExtendedStateSize() (size, align uint) { // struct user_fpsimd_state { // __uint128_t vregs[32]; // __u32 fpsr; - // __u32 fpcr; - // __u32 __reserved[2]; + // __u32 fpcr; + // __u32 __reserved[2]; // }; return 528, 16 } // HasFeature checks for the presence of a feature. func (fs FeatureSet) HasFeature(feature Feature) bool { - return fs.hwCap&(1<