x86: Add function to see if FSGSBASE is enabled.

PiperOrigin-RevId: 510512647
This commit is contained in:
Konstantin Bogomolov
2023-02-17 13:48:11 -08:00
committed by gVisor bot
parent b460bf9475
commit 45b3776646
7 changed files with 161 additions and 59 deletions
+82
View File
@@ -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
}
}
+11
View File
@@ -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
+4 -4
View File
@@ -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
View File
@@ -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)
}
}
+1
View File
@@ -221,4 +221,5 @@ func init() {
}.Fixed()
readMaxCPUFreq()
initHWCap()
}
+1 -52
View File
@@ -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()
}
+2 -2
View File
@@ -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]