mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
x86: Add function to see if FSGSBASE is enabled.
PiperOrigin-RevId: 510512647
This commit is contained in:
committed by
gVisor bot
parent
b460bf9475
commit
45b3776646
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<<feature) != 0
|
||||
return fs.hwCap.hwCap1&(1<<feature) != 0
|
||||
}
|
||||
|
||||
// WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is
|
||||
|
||||
+60
-1
@@ -14,7 +14,12 @@
|
||||
|
||||
package cpuid
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFeatureFromString(t *testing.T) {
|
||||
// Check that known features do match.
|
||||
@@ -31,3 +36,57 @@ func TestFeatureFromString(t *testing.T) {
|
||||
t.Errorf("got %v, %v want false", f, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadHwCap(t *testing.T) {
|
||||
// Make an auxv with fake entries
|
||||
const (
|
||||
auxvEntries = 16
|
||||
uint64Size = 8
|
||||
)
|
||||
auxv := [auxvEntries * uint64Size * 2]byte{}
|
||||
|
||||
hwCap1Idx := rand.Intn(auxvEntries)
|
||||
hwCap2Idx := rand.Intn(auxvEntries)
|
||||
if hwCap1Idx == hwCap2Idx {
|
||||
hwCap2Idx = (hwCap2Idx + 1 + rand.Intn(auxvEntries-2)) % auxvEntries
|
||||
}
|
||||
// Set the entries we are interested in to not 0.
|
||||
hwCap1Val := 1 + uint64(rand.Int63())
|
||||
hwCap2Val := 1 + uint64(rand.Int63())
|
||||
|
||||
binary.LittleEndian.PutUint64(auxv[hwCap1Idx*uint64Size*2:], _AT_HWCAP)
|
||||
binary.LittleEndian.PutUint64(auxv[hwCap1Idx*uint64Size*2+8:], hwCap1Val)
|
||||
binary.LittleEndian.PutUint64(auxv[hwCap2Idx*uint64Size*2:], _AT_HWCAP2)
|
||||
binary.LittleEndian.PutUint64(auxv[hwCap2Idx*uint64Size*2+8:], hwCap2Val)
|
||||
|
||||
file, err := os.CreateTemp(t.TempDir(), "fake-self-auxv")
|
||||
if err != nil {
|
||||
t.Errorf("failed to create file: %v", err)
|
||||
}
|
||||
_, err = file.Write(auxv[:])
|
||||
if err != nil {
|
||||
t.Errorf("failed to write to file: %v", err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
t.Errorf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
c, err := readHWCap(file.Name())
|
||||
if err != nil {
|
||||
t.Errorf("readHwCap got err %v, want nil", err)
|
||||
}
|
||||
if c.hwCap1 != hwCap1Val {
|
||||
t.Errorf("c.hwCap1 got %d, want %d", c.hwCap1, hwCap1Val)
|
||||
}
|
||||
if c.hwCap2 != hwCap2Val {
|
||||
t.Errorf("c.hwCap2 got %d, want %d", c.hwCap1, hwCap1Val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadingSelfProcAuxv(t *testing.T) {
|
||||
_, err := readHWCap("/proc/self/auxv")
|
||||
if err != nil {
|
||||
t.Errorf("got %v, expected nil", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,4 +221,5 @@ func init() {
|
||||
}.Fixed()
|
||||
|
||||
readMaxCPUFreq()
|
||||
initHWCap()
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -151,57 +150,7 @@ func initCPUInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 initHwCap() {
|
||||
if runtime.GOOS != "linux" {
|
||||
// Don't try to read Linux-specific /proc files or
|
||||
// warn about them not existing.
|
||||
return
|
||||
}
|
||||
auxv, err := ioutil.ReadFile("/proc/self/auxv")
|
||||
if err != nil {
|
||||
log.Warningf("Could not read /proc/self/auxv: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
const _AT_HWCAP = 16 // hardware capability bit vector.
|
||||
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 {
|
||||
hostFeatureSet.hwCap = uint(val)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
initCPUInfo()
|
||||
initHwCap()
|
||||
initHWCap()
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func (s Static) ToFeatureSet() FeatureSet {
|
||||
ns[k] = v
|
||||
}
|
||||
ns.normalize()
|
||||
return FeatureSet{ns}
|
||||
return FeatureSet{ns, hwCap{}}
|
||||
}
|
||||
|
||||
// afterLoad calls normalize.
|
||||
@@ -97,7 +97,7 @@ func (s Static) afterLoad() {
|
||||
// normalize normalizes FPU sizes.
|
||||
func (s Static) normalize() {
|
||||
// Override local FPU sizes, which must be fixed.
|
||||
fs := FeatureSet{s}
|
||||
fs := FeatureSet{s, hwCap{}}
|
||||
if fs.HasFeature(X86FeatureXSAVE) {
|
||||
in := In{Eax: uint32(xSaveInfo)}
|
||||
out := s[in]
|
||||
|
||||
Reference in New Issue
Block a user