mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Refactor CPUID to allow for use in KVM and ring0.
Updates #5039 PiperOrigin-RevId: 421696994
This commit is contained in:
committed by
gVisor bot
parent
8b8a181868
commit
266cabd008
+16
-6
@@ -5,21 +5,29 @@ package(licenses = ["notice"])
|
||||
go_library(
|
||||
name = "cpuid",
|
||||
srcs = [
|
||||
"cpu_amd64.s",
|
||||
"cpuid.go",
|
||||
"cpuid_amd64.go",
|
||||
"cpuid_arm64.go",
|
||||
"cpuid_x86.go",
|
||||
"features_amd64.go",
|
||||
"features_arm64.go",
|
||||
"native_amd64.go",
|
||||
"native_amd64.s",
|
||||
"native_arm64.go",
|
||||
"static_amd64.go",
|
||||
],
|
||||
visibility = ["//:sandbox"],
|
||||
deps = ["//pkg/log"],
|
||||
deps = [
|
||||
"//pkg/context",
|
||||
"//pkg/log",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "cpuid_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"cpuid_arm64_test.go",
|
||||
"cpuid_x86_test.go",
|
||||
"cpuid_amd64_test.go",
|
||||
"cpuid_test.go",
|
||||
],
|
||||
library = ":cpuid",
|
||||
)
|
||||
@@ -28,7 +36,9 @@ go_test(
|
||||
name = "cpuid_parse_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"cpuid_parse_x86_test.go",
|
||||
"cpuid_parse_amd64_test.go",
|
||||
"cpuid_parse_arm64_test.go",
|
||||
"cpuid_parse_test.go",
|
||||
],
|
||||
library = ":cpuid",
|
||||
tags = ["manual"],
|
||||
|
||||
+135
-22
@@ -15,43 +15,156 @@
|
||||
// Package cpuid provides basic functionality for creating and adjusting CPU
|
||||
// feature sets.
|
||||
//
|
||||
// To use FeatureSets, one should start with an existing FeatureSet (either a
|
||||
// known platform, or HostFeatureSet()) and then add, remove, and test for
|
||||
// features as desired.
|
||||
// Each architecture should define its own FeatureSet type, that must be
|
||||
// savable, along with an allFeatures map, appropriate arch hooks and a
|
||||
// HostFeatureSet function. This file contains common functionality to all
|
||||
// architectures, which is essentially string munging and some errors.
|
||||
//
|
||||
// For example: on x86, test for hardware extended state saving, and if
|
||||
// we don't have it, don't expose AVX, which cannot be saved with fxsave.
|
||||
//
|
||||
// if !HostFeatureSet().HasFeature(X86FeatureXSAVE) {
|
||||
// exposedFeatures.Remove(X86FeatureAVX)
|
||||
// }
|
||||
// Individual architectures may export methods on FeatureSet that are relevant,
|
||||
// e.g. FeatureSet.Vendor(). Common to all architectures, FeatureSets include
|
||||
// HasFeature, which provides a trivial mechanism to test for the presence of
|
||||
// specific hardware features. The hardware features are also defined on a
|
||||
// per-architecture basis.
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
)
|
||||
|
||||
// contextID is the package for context.Context.Value keys.
|
||||
type contextID int
|
||||
|
||||
const (
|
||||
// CtxFeatureSet is the FeatureSet for the context.
|
||||
CtxFeatureSet contextID = iota
|
||||
)
|
||||
|
||||
// FromContext returns the FeatureSet from the context, if available.
|
||||
func FromContext(ctx context.Context) FeatureSet {
|
||||
v := ctx.Value(CtxFeatureSet)
|
||||
if v == nil {
|
||||
return FeatureSet{} // Panics if used.
|
||||
}
|
||||
return v.(FeatureSet)
|
||||
}
|
||||
|
||||
// Feature is a unique identifier for a particular cpu feature. We just use an
|
||||
// int as a feature number on x86 and arm64.
|
||||
//
|
||||
// On x86, features are numbered according to "blocks". Each block is 32 bits, and
|
||||
// feature bits from the same source (cpuid leaf/level) are in the same block.
|
||||
//
|
||||
// On arm64, features are numbered according to the ELF HWCAP definition.
|
||||
// arch/arm64/include/uapi/asm/hwcap.h
|
||||
// On arm64, features are numbered according to the ELF HWCAP definition, from:
|
||||
// arch/arm64/include/uapi/asm/hwcap.h
|
||||
type Feature int
|
||||
|
||||
// HostFeatureSet returns a FeatureSet that matches that of the host machine.
|
||||
// Callers must not mutate the returned FeatureSet.
|
||||
func HostFeatureSet() *FeatureSet {
|
||||
return hostFeatureSet
|
||||
// allFeatureInfo is the value for allFeatures.
|
||||
type allFeatureInfo struct {
|
||||
// displayName is the short display name for the feature.
|
||||
displayName string
|
||||
|
||||
// shouldAppear indicates whether the feature normally appears in
|
||||
// cpuinfo. This affects FlagString only.
|
||||
shouldAppear bool
|
||||
}
|
||||
|
||||
var hostFeatureSet *FeatureSet
|
||||
// String implements fmt.Stringer.String.
|
||||
func (f Feature) String() string {
|
||||
info, ok := allFeatures[f]
|
||||
if ok {
|
||||
return info.displayName
|
||||
}
|
||||
return fmt.Sprintf("[0x%x?]", int(f)) // No given name.
|
||||
}
|
||||
|
||||
// ErrIncompatible is returned by FeatureSet.HostCompatible if fs is not a
|
||||
// subset of the host feature set.
|
||||
// reverseMap is a map from displayName to Feature.
|
||||
var reverseMap = func() map[string]Feature {
|
||||
m := make(map[string]Feature)
|
||||
for feature, info := range allFeatures {
|
||||
if info.displayName != "" {
|
||||
// Sanity check that the name is unique.
|
||||
if old, ok := m[info.displayName]; ok {
|
||||
panic(fmt.Sprintf("feature %v has conflicting values (0x%x vs 0x%x)", info.displayName, old, feature))
|
||||
}
|
||||
m[info.displayName] = feature
|
||||
}
|
||||
}
|
||||
return m
|
||||
}()
|
||||
|
||||
// FeatureFromString returns the Feature associated with the given feature
|
||||
// string plus a bool to indicate if it could find the feature.
|
||||
func FeatureFromString(s string) (Feature, bool) {
|
||||
feature, ok := reverseMap[s]
|
||||
return feature, ok
|
||||
}
|
||||
|
||||
// AllFeatures returns the full set of all possible features.
|
||||
func AllFeatures() (features []Feature) {
|
||||
archFlagOrder(func(f Feature) {
|
||||
features = append(features, f)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Subtract returns the features present in fs that are not present in other.
|
||||
// If all features in fs are present in other, Subtract returns nil.
|
||||
//
|
||||
// This does not check for any kinds of incompatibility.
|
||||
func (fs FeatureSet) Subtract(other FeatureSet) (left map[Feature]struct{}) {
|
||||
for feature := range allFeatures {
|
||||
thisHas := fs.HasFeature(feature)
|
||||
otherHas := other.HasFeature(feature)
|
||||
if thisHas && !otherHas {
|
||||
if left == nil {
|
||||
left = make(map[Feature]struct{})
|
||||
}
|
||||
left[feature] = struct{}{}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FlagString prints out supported CPU flags.
|
||||
func (fs FeatureSet) FlagString() string {
|
||||
var s []string
|
||||
archFlagOrder(func(feature Feature) {
|
||||
if !fs.HasFeature(feature) {
|
||||
return
|
||||
}
|
||||
info := allFeatures[feature]
|
||||
if !info.shouldAppear {
|
||||
return
|
||||
}
|
||||
s = append(s, info.displayName)
|
||||
})
|
||||
return strings.Join(s, " ")
|
||||
}
|
||||
|
||||
// ErrIncompatible is returned for incompatible feature sets.
|
||||
type ErrIncompatible struct {
|
||||
message string
|
||||
reason string
|
||||
}
|
||||
|
||||
// Error implements error.
|
||||
func (e ErrIncompatible) Error() string {
|
||||
return e.message
|
||||
// Error implements error.Error.
|
||||
func (e *ErrIncompatible) Error() string {
|
||||
return fmt.Sprintf("incompatible FeatureSet: %v", e.reason)
|
||||
}
|
||||
|
||||
// CheckHostCompatible returns nil if fs is a subset of the host feature set.
|
||||
func (fs FeatureSet) CheckHostCompatible() error {
|
||||
hfs := HostFeatureSet()
|
||||
|
||||
// Check that hfs is a superset of fs.
|
||||
if diff := fs.Subtract(hfs); len(diff) > 0 {
|
||||
return &ErrIncompatible{
|
||||
reason: fmt.Sprintf("missing features: %v", diff),
|
||||
}
|
||||
}
|
||||
|
||||
// Make arch-specific checks.
|
||||
return fs.archCheckHostCompatible(hfs)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// FeatureSet defines features in terms of CPUID leaves and bits.
|
||||
//
|
||||
// Common references:
|
||||
//
|
||||
// Intel:
|
||||
// * Intel SDM Volume 2, Chapter 3.2 "CPUID" (more up-to-date)
|
||||
// * Intel Application Note 485 (more detailed)
|
||||
//
|
||||
// AMD:
|
||||
// * AMD64 APM Volume 3, Appendix 3 "Obtaining Processor Information ..."
|
||||
//
|
||||
// +stateify savable
|
||||
type FeatureSet struct {
|
||||
// Function is the underlying CPUID Function.
|
||||
//
|
||||
// This is exported to allow direct calls of the underlying CPUID
|
||||
// function, where required.
|
||||
Function `state:".(Static)"`
|
||||
}
|
||||
|
||||
// saveFunction saves the function as a static query.
|
||||
func (fs *FeatureSet) saveFunction() Static {
|
||||
if s, ok := fs.Function.(Static); ok {
|
||||
return s
|
||||
}
|
||||
return fs.ToStatic()
|
||||
}
|
||||
|
||||
// loadFunction saves the function as a static query.
|
||||
func (fs *FeatureSet) loadFunction(s Static) {
|
||||
fs.Function = s
|
||||
}
|
||||
|
||||
// Helper to convert 3 regs into 12-byte vendor ID.
|
||||
//
|
||||
//go:nosplit
|
||||
func vendorIDFromRegs(bx, cx, dx uint32) (r [12]byte) {
|
||||
for i := uint(0); i < 4; i++ {
|
||||
b := byte(bx >> (i * 8))
|
||||
r[i] = b
|
||||
}
|
||||
|
||||
for i := uint(0); i < 4; i++ {
|
||||
b := byte(dx >> (i * 8))
|
||||
r[4+i] = b
|
||||
}
|
||||
|
||||
for i := uint(0); i < 4; i++ {
|
||||
b := byte(cx >> (i * 8))
|
||||
r[8+i] = b
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Helper to merge a 12-byte vendor ID back to registers.
|
||||
//
|
||||
// Used by static_amd64.go.
|
||||
func regsFromVendorID(r [12]byte) (bx, cx, dx uint32) {
|
||||
bx |= uint32(r[0])
|
||||
bx |= uint32(r[1]) << 8
|
||||
bx |= uint32(r[2]) << 16
|
||||
bx |= uint32(r[3]) << 24
|
||||
cx |= uint32(r[4])
|
||||
cx |= uint32(r[5]) << 8
|
||||
cx |= uint32(r[6]) << 16
|
||||
cx |= uint32(r[7]) << 24
|
||||
dx |= uint32(r[8])
|
||||
dx |= uint32(r[9]) << 8
|
||||
dx |= uint32(r[10]) << 16
|
||||
dx |= uint32(r[10]) << 24
|
||||
return
|
||||
}
|
||||
|
||||
// VendorID is the 12-char string returned in ebx:edx:ecx for eax=0.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) VendorID() [12]byte {
|
||||
_, bx, cx, dx := fs.query(vendorID)
|
||||
return vendorIDFromRegs(bx, cx, dx)
|
||||
}
|
||||
|
||||
// Helper to deconstruct signature dword.
|
||||
//
|
||||
//go:nosplit
|
||||
func signatureSplit(v uint32) (ef, em, pt, f, m, sid uint8) {
|
||||
sid = uint8(v & 0xf)
|
||||
m = uint8(v>>4) & 0xf
|
||||
f = uint8(v>>8) & 0xf
|
||||
pt = uint8(v>>12) & 0x3
|
||||
em = uint8(v>>16) & 0xf
|
||||
ef = uint8(v >> 20)
|
||||
return
|
||||
}
|
||||
|
||||
// ExtendedFamily is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ExtendedFamily() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
ef, _, _, _, _, _ := signatureSplit(ax)
|
||||
return ef
|
||||
}
|
||||
|
||||
// ExtendedModel is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ExtendedModel() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, em, _, _, _, _ := signatureSplit(ax)
|
||||
return em
|
||||
}
|
||||
|
||||
// ProcessorType is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ProcessorType() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, pt, _, _, _ := signatureSplit(ax)
|
||||
return pt
|
||||
}
|
||||
|
||||
// Family is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) Family() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, _, f, _, _ := signatureSplit(ax)
|
||||
return f
|
||||
}
|
||||
|
||||
// Model is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) Model() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, _, _, m, _ := signatureSplit(ax)
|
||||
return m
|
||||
}
|
||||
|
||||
// SteppingID is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) SteppingID() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, _, _, _, sid := signatureSplit(ax)
|
||||
return sid
|
||||
}
|
||||
|
||||
// VirtualAddressBits returns the number of bits available for virtual
|
||||
// addresses.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) VirtualAddressBits() uint32 {
|
||||
ax, _, _, _ := fs.query(addressSizes)
|
||||
return (ax >> 8) & 0xff
|
||||
}
|
||||
|
||||
// PhysicalAddressBits returns the number of bits available for physical
|
||||
// addresses.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) PhysicalAddressBits() uint32 {
|
||||
ax, _, _, _ := fs.query(addressSizes)
|
||||
return ax & 0xff
|
||||
}
|
||||
|
||||
// CacheType describes the type of a cache, as returned in eax[4:0] for eax=4.
|
||||
type CacheType uint8
|
||||
|
||||
const (
|
||||
// cacheNull indicates that there are no more entries.
|
||||
cacheNull CacheType = iota
|
||||
|
||||
// CacheData is a data cache.
|
||||
CacheData
|
||||
|
||||
// CacheInstruction is an instruction cache.
|
||||
CacheInstruction
|
||||
|
||||
// CacheUnified is a unified instruction and data cache.
|
||||
CacheUnified
|
||||
)
|
||||
|
||||
// Cache describes the parameters of a single cache on the system.
|
||||
//
|
||||
// This is returned by the Caches method on FeatureSet.
|
||||
type Cache struct {
|
||||
// Level is the hierarchical level of this cache (L1, L2, etc).
|
||||
Level uint32
|
||||
|
||||
// Type is the type of cache.
|
||||
Type CacheType
|
||||
|
||||
// FullyAssociative indicates that entries may be placed in any block.
|
||||
FullyAssociative bool
|
||||
|
||||
// Partitions is the number of physical partitions in the cache.
|
||||
Partitions uint32
|
||||
|
||||
// Ways is the number of ways of associativity in the cache.
|
||||
Ways uint32
|
||||
|
||||
// Sets is the number of sets in the cache.
|
||||
Sets uint32
|
||||
|
||||
// InvalidateHierarchical indicates that WBINVD/INVD from threads
|
||||
// sharing this cache acts upon lower level caches for threads sharing
|
||||
// this cache.
|
||||
InvalidateHierarchical bool
|
||||
|
||||
// Inclusive indicates that this cache is inclusive of lower cache
|
||||
// levels.
|
||||
Inclusive bool
|
||||
|
||||
// DirectMapped indicates that this cache is directly mapped from
|
||||
// address, rather than using a hash function.
|
||||
DirectMapped bool
|
||||
}
|
||||
|
||||
// Caches describes the caches on the CPU.
|
||||
//
|
||||
// Only supported on Intel; requires allocation.
|
||||
func (fs FeatureSet) Caches() (caches []Cache) {
|
||||
if !fs.Intel() {
|
||||
return
|
||||
}
|
||||
// Check against the cache line, which should be consistent.
|
||||
cacheLine := fs.CacheLine()
|
||||
for i := uint32(0); ; i++ {
|
||||
out := fs.Query(In{
|
||||
Eax: uint32(intelDeterministicCacheParams),
|
||||
Ecx: i,
|
||||
})
|
||||
t := CacheType(out.Eax & 0xf)
|
||||
if t == cacheNull {
|
||||
break
|
||||
}
|
||||
lineSize := (out.Ebx & 0xfff) + 1
|
||||
if lineSize != cacheLine {
|
||||
panic(fmt.Sprintf("Mismatched cache line size: %d vs %d", lineSize, cacheLine))
|
||||
}
|
||||
caches = append(caches, Cache{
|
||||
Type: t,
|
||||
Level: (out.Eax >> 5) & 0x7,
|
||||
FullyAssociative: ((out.Eax >> 9) & 1) == 1,
|
||||
Partitions: ((out.Ebx >> 12) & 0x3ff) + 1,
|
||||
Ways: ((out.Ebx >> 22) & 0x3ff) + 1,
|
||||
Sets: out.Ecx + 1,
|
||||
InvalidateHierarchical: (out.Edx & 1) == 0,
|
||||
Inclusive: ((out.Edx >> 1) & 1) == 1,
|
||||
DirectMapped: ((out.Edx >> 2) & 1) == 0,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CacheLine is the size of a cache line in bytes.
|
||||
//
|
||||
// All caches use the same line size. This is not enforced in the CPUID
|
||||
// encoding, but is true on all known x86 processors.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) CacheLine() uint32 {
|
||||
_, bx, _, _ := fs.query(featureInfo)
|
||||
return 8 * (bx >> 8) & 0xff
|
||||
}
|
||||
|
||||
// HasFeature tests whether or not a feature is in the given feature set.
|
||||
//
|
||||
// This function is safe to call from a nosplit context, as long as the
|
||||
// FeatureSet does not have any masked features.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) HasFeature(feature Feature) bool {
|
||||
return feature.check(fs)
|
||||
}
|
||||
|
||||
// WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is
|
||||
// a minimal /proc/cpuinfo, it is missing some fields like "microcode" that are
|
||||
// not always printed in Linux. The bogomips field is simply made up.
|
||||
func (fs FeatureSet) WriteCPUInfoTo(cpu uint, w io.Writer) {
|
||||
// Avoid many redunant calls here, since this can occasionally appear
|
||||
// in the hot path. Read all basic information up front, see above.
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
ef, em, _, f, m, _ := signatureSplit(ax)
|
||||
vendor := fs.VendorID()
|
||||
fmt.Fprintf(w, "processor\t: %d\n", cpu)
|
||||
fmt.Fprintf(w, "vendor_id\t: %s\n", string(vendor[:]))
|
||||
fmt.Fprintf(w, "cpu family\t: %d\n", ((ef<<4)&0xff)|f)
|
||||
fmt.Fprintf(w, "model\t\t: %d\n", ((em<<4)&0xff)|m)
|
||||
fmt.Fprintf(w, "model name\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(w, "stepping\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(w, "cpu MHz\t\t: %.3f\n", cpuFreqMHz)
|
||||
fmt.Fprintf(w, "fpu\t\t: yes\n")
|
||||
fmt.Fprintf(w, "fpu_exception\t: yes\n")
|
||||
fmt.Fprintf(w, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID.
|
||||
fmt.Fprintf(w, "wp\t\t: yes\n")
|
||||
fmt.Fprintf(w, "flags\t\t: %s\n", fs.FlagString())
|
||||
fmt.Fprintf(w, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
|
||||
fmt.Fprintf(w, "clflush size\t: %d\n", fs.CacheLine())
|
||||
fmt.Fprintf(w, "cache_alignment\t: %d\n", fs.CacheLine())
|
||||
fmt.Fprintf(w, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48)
|
||||
fmt.Fprintf(w, "power management:\n") // This is always here, but can be blank.
|
||||
fmt.Fprintf(w, "\n") // The /proc/cpuinfo file ends with an extra newline.
|
||||
}
|
||||
|
||||
var (
|
||||
authenticAMD = [12]byte{'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D'}
|
||||
genuineIntel = [12]byte{'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l'}
|
||||
)
|
||||
|
||||
// AMD returns true if fs describes an AMD CPU.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) AMD() bool {
|
||||
return fs.VendorID() == authenticAMD
|
||||
}
|
||||
|
||||
// Intel returns true if fs describes an Intel CPU.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) Intel() bool {
|
||||
return fs.VendorID() == genuineIntel
|
||||
}
|
||||
|
||||
// Leaf 0 of xsaveinfo function returns the size for currently
|
||||
// enabled xsave features in ebx, the maximum size if all valid
|
||||
// features are saved with xsave in ecx, and valid XCR0 bits in
|
||||
// edx:eax.
|
||||
//
|
||||
// If xSaveInfo isn't supported, cpuid will not fault but will
|
||||
// return bogus values.
|
||||
var maxXsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ecx
|
||||
|
||||
// ExtendedStateSize returns the number of bytes needed to save the "extended
|
||||
// state" for this processor and the boundary it must be aligned to. Extended
|
||||
// state includes floating point registers, and other cpu state that's not
|
||||
// associated with the normal task context.
|
||||
//
|
||||
// Note: We can save some space here with an optimization where we use a
|
||||
// smaller chunk of memory depending on features that are actually enabled.
|
||||
// Currently we just use the largest possible size for simplicity (which is
|
||||
// about 2.5K worst case, with avx512).
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ExtendedStateSize() (size, align uint) {
|
||||
if fs.UseXsave() {
|
||||
return uint(maxXsaveSize), 64
|
||||
}
|
||||
|
||||
// If we don't support xsave, we fall back to fxsave, which requires
|
||||
// 512 bytes aligned to 16 bytes.
|
||||
return 512, 16
|
||||
}
|
||||
|
||||
// ValidXCR0Mask returns the valid bits in control register XCR0.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ValidXCR0Mask() uint64 {
|
||||
if !fs.HasFeature(X86FeatureXSAVE) {
|
||||
return 0
|
||||
}
|
||||
ax, _, _, dx := fs.query(xSaveInfo)
|
||||
return uint64(dx)<<32 | uint64(ax)
|
||||
}
|
||||
|
||||
// UseXsave returns the choice of fp state saving instruction.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) UseXsave() bool {
|
||||
return fs.HasFeature(X86FeatureXSAVE) && fs.HasFeature(X86FeatureOSXSAVE)
|
||||
}
|
||||
|
||||
// UseXsaveopt returns true if 'fs' supports the "xsaveopt" instruction.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) UseXsaveopt() bool {
|
||||
return fs.UseXsave() && fs.HasFeature(X86FeatureXSAVEOPT)
|
||||
}
|
||||
|
||||
// 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
|
||||
// utilizing CLFLUSH. Other cache properties are allowed to change, as
|
||||
// they are not important to correctness.
|
||||
fsCache := fs.CacheLine()
|
||||
hostCache := hfs.CacheLine()
|
||||
if fsCache != hostCache {
|
||||
return &ErrIncompatible{
|
||||
reason: fmt.Sprintf("CPU cache line size %d incompatible with host cache line size %d", fsCache, hostCache),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// makeFatureSet creates a new FeatureSet.
|
||||
func makeFeatureSet(features ...Feature) FeatureSet {
|
||||
s := make(Static)
|
||||
for _, f := range features {
|
||||
s.Add(f)
|
||||
}
|
||||
return FeatureSet{
|
||||
Function: s,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
justFPU = makeFeatureSet(X86FeatureFPU)
|
||||
justFPUandPAE = makeFeatureSet(X86FeatureFPU, X86FeaturePAE)
|
||||
)
|
||||
|
||||
func TestSubtract(t *testing.T) {
|
||||
if left := justFPU.Subtract(justFPUandPAE); len(left) > 0 {
|
||||
t.Errorf("Got %q is not subset of %q, want left (%v) to be non-empty", justFPU.FlagString(), justFPUandPAE.FlagString(), left)
|
||||
}
|
||||
if left := justFPUandPAE.Subtract(justFPU); len(left) == 0 {
|
||||
t.Errorf("Got %q is a subset of %q, want left (%v) to be empty", justFPU.FlagString(), justFPUandPAE.FlagString(), left)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/73346484): Run this test on a very old platform, and make sure more
|
||||
// bits are enabled than just FPU and PAE. This test currently may not detect
|
||||
// if HostFeatureSet gives back junk bits.
|
||||
func TestHostFeatureSet(t *testing.T) {
|
||||
hostFeatures := HostFeatureSet()
|
||||
if justFPUandPAE.Subtract(hostFeatures) != nil {
|
||||
t.Errorf("Got invalid feature set %v from HostFeatureSet()", hostFeatures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasFeature(t *testing.T) {
|
||||
if !justFPU.HasFeature(X86FeatureFPU) {
|
||||
t.Errorf("HasFeature failed, %q should contain %v", justFPU.FlagString(), X86FeatureFPU)
|
||||
}
|
||||
if justFPU.HasFeature(X86FeatureAVX) {
|
||||
t.Errorf("HasFeature failed, %q should not contain %v", justFPU.FlagString(), X86FeatureAVX)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
// Test a basic insertion into the FeatureSet.
|
||||
testFeatures := makeFeatureSet(X86FeatureCLFSH)
|
||||
if !testFeatures.HasFeature(X86FeatureCLFSH) {
|
||||
t.Errorf("Add failed, got %v want set with %v", testFeatures, X86FeatureCLFSH)
|
||||
}
|
||||
|
||||
// Test that duplicates are ignored.
|
||||
testFeatures.Function.(Static).Add(X86FeatureCLFSH)
|
||||
if !testFeatures.HasFeature(X86FeatureCLFSH) {
|
||||
t.Errorf("Duplicate add removed entry, got %v want set with %v", testFeatures, X86FeatureCLFSH)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
// Try removing the last feature.
|
||||
testFeatures := makeFeatureSet(X86FeatureFPU, X86FeaturePAE)
|
||||
testFeatures.Function.(Static).Remove(X86FeaturePAE)
|
||||
if !testFeatures.HasFeature(X86FeatureFPU) || testFeatures.HasFeature(X86FeaturePAE) {
|
||||
t.Errorf("Remove failed, got %q want %q", testFeatures.FlagString(), justFPU.FlagString())
|
||||
}
|
||||
|
||||
// Try removing a feature not in the set.
|
||||
testFeatures.Function.(Static).Remove(X86FeatureRDRAND)
|
||||
if !testFeatures.HasFeature(X86FeatureFPU) {
|
||||
t.Errorf("Remove failed, got %q want %q", testFeatures.FlagString(), justFPU.FlagString())
|
||||
}
|
||||
}
|
||||
+42
-429
File diff suppressed because it is too large
Load Diff
@@ -1,56 +0,0 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var justFP = &FeatureSet{
|
||||
Set: map[Feature]bool{
|
||||
ARM64FeatureFP: true,
|
||||
}}
|
||||
|
||||
func TestHostFeatureSet(t *testing.T) {
|
||||
hostFeatures := HostFeatureSet()
|
||||
if len(hostFeatures.Set) == 0 {
|
||||
t.Errorf("Got invalid feature set %v from HostFeatureSet()", hostFeatures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasFeature(t *testing.T) {
|
||||
if !justFP.HasFeature(ARM64FeatureFP) {
|
||||
t.Errorf("HasFeature failed, %v should contain %v", justFP, ARM64FeatureFP)
|
||||
}
|
||||
|
||||
if justFP.HasFeature(ARM64FeatureSM3) {
|
||||
t.Errorf("HasFeature failed, %v should not contain %v", justFP, ARM64FeatureSM3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeatureFromString(t *testing.T) {
|
||||
f, ok := FeatureFromString("asimd")
|
||||
if f != ARM64FeatureASIMD || !ok {
|
||||
t.Errorf("got %v want asimd", f)
|
||||
}
|
||||
|
||||
f, ok = FeatureFromString("bad")
|
||||
if ok {
|
||||
t.Errorf("got %v want nothing", f)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
func archSkipFeature(feature Feature, major, minor int) bool {
|
||||
switch {
|
||||
// Block 0.
|
||||
case feature == X86FeatureSDBG && (major < 4 || major == 4 && minor < 3):
|
||||
// SDBG only exposed in
|
||||
// b1c599b8ff80ea79b9f8277a3f9f36a7b0cfedce (4.3).
|
||||
return true
|
||||
// Block 2.
|
||||
case feature == X86FeatureRDT && (major < 4 || major == 4 && minor < 10):
|
||||
// RDT only exposed in
|
||||
// 4ab1586488cb56ed8728e54c4157cc38646874d9 (4.10).
|
||||
return true
|
||||
// Block 3.
|
||||
case feature == X86FeatureAVX512VBMI && (major < 4 || major == 4 && minor < 10):
|
||||
// AVX512VBMI only exposed in
|
||||
// a8d9df5a509a232a959e4ef2e281f7ecd77810d6 (4.10).
|
||||
return true
|
||||
case feature == X86FeatureUMIP && (major < 4 || major == 4 && minor < 15):
|
||||
// UMIP only exposed in
|
||||
// 3522c2a6a4f341058b8291326a945e2a2d2aaf55 (4.15).
|
||||
return true
|
||||
case feature == X86FeaturePKU && (major < 4 || major == 4 && minor < 9):
|
||||
// PKU only exposed in
|
||||
// dfb4a70f20c5b3880da56ee4c9484bdb4e8f1e65 (4.9).
|
||||
return true
|
||||
// Block 4.
|
||||
case feature == X86FeatureXSAVES && (major < 4 || major == 4 && minor < 8):
|
||||
// XSAVES only exposed in
|
||||
// b8be15d588060a03569ac85dc4a0247460988f5b (4.8).
|
||||
return true
|
||||
// Block 5.
|
||||
case feature == X86FeaturePERFCTR_LLC && (major < 4 || major == 4 && minor < 14):
|
||||
// PERFCTR_LLC renamed in
|
||||
// 910448bbed066ab1082b510eef1ae61bb792d854 (4.14).
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package cpuid
|
||||
|
||||
func archSkipFeature(feature Feature, major, minor int) bool {
|
||||
return false
|
||||
}
|
||||
@@ -12,9 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build 386 || amd64
|
||||
// +build 386 amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
@@ -64,83 +61,43 @@ func kernelVersion() (int, int, error) {
|
||||
// on the host.
|
||||
//
|
||||
// It does *not* verify that all features reported by the host are detected by
|
||||
// HostFeatureSet.
|
||||
//
|
||||
// i.e., test that HostFeatureSet is a subset of the host features.
|
||||
// HostFeatureSet. Linux has synthetic Linux-specific features that have no
|
||||
// analog in the actual CPUID feature set.
|
||||
func TestHostFeatureFlags(t *testing.T) {
|
||||
cpuinfoBytes, _ := ioutil.ReadFile("/proc/cpuinfo")
|
||||
cpuinfo := string(cpuinfoBytes)
|
||||
t.Logf("Host cpu info:\n%s", cpuinfo)
|
||||
|
||||
// Extract the kernel version.
|
||||
major, minor, err := kernelVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to parse kernel version: %v", err)
|
||||
}
|
||||
|
||||
// Extract all cpuinfo flags.
|
||||
cpuinfoBytes, _ := ioutil.ReadFile("/proc/cpuinfo")
|
||||
cpuinfo := string(cpuinfoBytes)
|
||||
re := regexp.MustCompile(`(?m)^flags\s+: (.*)$`)
|
||||
m := re.FindStringSubmatch(cpuinfo)
|
||||
if len(m) != 2 {
|
||||
t.Fatalf("Unable to extract flags from %q", cpuinfo)
|
||||
}
|
||||
|
||||
cpuinfoFlags := make(map[string]struct{})
|
||||
for _, f := range strings.Split(m[1], " ") {
|
||||
cpuinfoFlags[f] = struct{}{}
|
||||
}
|
||||
|
||||
// Check against host flags.
|
||||
fs := HostFeatureSet()
|
||||
|
||||
// All features have a string and appear in host cpuinfo.
|
||||
for f := range fs.Set {
|
||||
name := f.flagString(false)
|
||||
if name == "" {
|
||||
t.Errorf("Non-parsable feature: %v", f)
|
||||
}
|
||||
|
||||
for feature, info := range allFeatures {
|
||||
// Special cases not consistently visible. We don't mind if
|
||||
// they are exposed in earlier versions.
|
||||
switch {
|
||||
// Block 0.
|
||||
case f == X86FeatureSDBG && (major < 4 || major == 4 && minor < 3):
|
||||
// SDBG only exposed in
|
||||
// b1c599b8ff80ea79b9f8277a3f9f36a7b0cfedce (4.3).
|
||||
continue
|
||||
// Block 2.
|
||||
case f == X86FeatureRDT && (major < 4 || major == 4 && minor < 10):
|
||||
// RDT only exposed in
|
||||
// 4ab1586488cb56ed8728e54c4157cc38646874d9 (4.10).
|
||||
continue
|
||||
// Block 3.
|
||||
case f == X86FeatureAVX512VBMI && (major < 4 || major == 4 && minor < 10):
|
||||
// AVX512VBMI only exposed in
|
||||
// a8d9df5a509a232a959e4ef2e281f7ecd77810d6 (4.10).
|
||||
continue
|
||||
case f == X86FeatureUMIP && (major < 4 || major == 4 && minor < 15):
|
||||
// UMIP only exposed in
|
||||
// 3522c2a6a4f341058b8291326a945e2a2d2aaf55 (4.15).
|
||||
continue
|
||||
case f == X86FeaturePKU && (major < 4 || major == 4 && minor < 9):
|
||||
// PKU only exposed in
|
||||
// dfb4a70f20c5b3880da56ee4c9484bdb4e8f1e65 (4.9).
|
||||
continue
|
||||
// Block 4.
|
||||
case f == X86FeatureXSAVES && (major < 4 || major == 4 && minor < 8):
|
||||
// XSAVES only exposed in
|
||||
// b8be15d588060a03569ac85dc4a0247460988f5b (4.8).
|
||||
continue
|
||||
// Block 5.
|
||||
case f == X86FeaturePERFCTR_LLC && (major < 4 || major == 4 && minor < 14):
|
||||
// PERFCTR_LLC renamed in
|
||||
// 910448bbed066ab1082b510eef1ae61bb792d854 (4.14).
|
||||
if archSkipFeature(feature, major, minor) {
|
||||
continue
|
||||
}
|
||||
|
||||
hidden := f.flagString(true) == ""
|
||||
_, ok := cpuinfoFlags[name]
|
||||
if hidden && ok {
|
||||
t.Errorf("Unexpectedly hidden flag: %v", f)
|
||||
} else if !hidden && !ok {
|
||||
t.Errorf("Non-native flag: %v", f)
|
||||
// Check against the flags.
|
||||
_, ok := cpuinfoFlags[feature.String()]
|
||||
if !info.shouldAppear && ok {
|
||||
t.Errorf("Unexpected flag: %v", feature)
|
||||
} else if info.shouldAppear && fs.HasFeature(feature) && !ok {
|
||||
t.Errorf("Missing flag: %v", feature)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cpuid
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFeatureFromString(t *testing.T) {
|
||||
// Check that known features do match.
|
||||
for feature, _ := range allFeatures {
|
||||
f, ok := FeatureFromString(feature.String())
|
||||
if f != feature || !ok {
|
||||
t.Errorf("got %v, %v want %v, true", f, ok, feature)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that "bad" doesn't match.
|
||||
f, ok := FeatureFromString("bad")
|
||||
if ok {
|
||||
t.Errorf("got %v, %v want false", f, ok)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,244 +0,0 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build 386 || amd64
|
||||
// +build 386 amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// These are the default values of various FeatureSet fields.
|
||||
const (
|
||||
defaultVendorID = "GenuineIntel"
|
||||
|
||||
// These processor signature defaults are derived from the values
|
||||
// listed in Intel Application Note 485 for i7/Xeon processors.
|
||||
defaultExtFamily uint8 = 0
|
||||
defaultExtModel uint8 = 1
|
||||
defaultType uint8 = 0
|
||||
defaultFamily uint8 = 0x06
|
||||
defaultModel uint8 = 0x0a
|
||||
defaultSteppingID uint8 = 0
|
||||
)
|
||||
|
||||
// newEmptyFeatureSet creates a new FeatureSet with a sensible default model and no features.
|
||||
func newEmptyFeatureSet() *FeatureSet {
|
||||
return &FeatureSet{
|
||||
Set: make(map[Feature]bool),
|
||||
VendorID: defaultVendorID,
|
||||
ExtendedFamily: defaultExtFamily,
|
||||
ExtendedModel: defaultExtModel,
|
||||
ProcessorType: defaultType,
|
||||
Family: defaultFamily,
|
||||
Model: defaultModel,
|
||||
SteppingID: defaultSteppingID,
|
||||
}
|
||||
}
|
||||
|
||||
var justFPU = &FeatureSet{
|
||||
Set: map[Feature]bool{
|
||||
X86FeatureFPU: true,
|
||||
}}
|
||||
|
||||
var justFPUandPAE = &FeatureSet{
|
||||
Set: map[Feature]bool{
|
||||
X86FeatureFPU: true,
|
||||
X86FeaturePAE: true,
|
||||
}}
|
||||
|
||||
func TestSubtract(t *testing.T) {
|
||||
if diff := justFPU.Subtract(justFPUandPAE); diff != nil {
|
||||
t.Errorf("Got %v is not subset of %v, want diff (%v) to be nil", justFPU, justFPUandPAE, diff)
|
||||
}
|
||||
|
||||
if justFPUandPAE.Subtract(justFPU) == nil {
|
||||
t.Errorf("Got %v is a subset of %v, want diff to be nil", justFPU, justFPUandPAE)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/73346484): Run this test on a very old platform, and make sure more
|
||||
// bits are enabled than just FPU and PAE. This test currently may not detect
|
||||
// if HostFeatureSet gives back junk bits.
|
||||
func TestHostFeatureSet(t *testing.T) {
|
||||
hostFeatures := HostFeatureSet()
|
||||
if justFPUandPAE.Subtract(hostFeatures) != nil {
|
||||
t.Errorf("Got invalid feature set %v from HostFeatureSet()", hostFeatures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasFeature(t *testing.T) {
|
||||
if !justFPU.HasFeature(X86FeatureFPU) {
|
||||
t.Errorf("HasFeature failed, %v should contain %v", justFPU, X86FeatureFPU)
|
||||
}
|
||||
|
||||
if justFPU.HasFeature(X86FeatureAVX) {
|
||||
t.Errorf("HasFeature failed, %v should not contain %v", justFPU, X86FeatureAVX)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: these tests are aware of and abuse internal details of FeatureSets.
|
||||
// Users of FeatureSets should not depend on this.
|
||||
func TestAdd(t *testing.T) {
|
||||
// Test a basic insertion into the FeatureSet.
|
||||
testFeatures := newEmptyFeatureSet()
|
||||
testFeatures.Add(X86FeatureCLFSH)
|
||||
if len(testFeatures.Set) != 1 {
|
||||
t.Errorf("Got length %v want 1", len(testFeatures.Set))
|
||||
}
|
||||
|
||||
if !testFeatures.HasFeature(X86FeatureCLFSH) {
|
||||
t.Errorf("Add failed, got %v want set with %v", testFeatures, X86FeatureCLFSH)
|
||||
}
|
||||
|
||||
// Test that duplicates are ignored.
|
||||
testFeatures.Add(X86FeatureCLFSH)
|
||||
if len(testFeatures.Set) != 1 {
|
||||
t.Errorf("Got length %v, want 1", len(testFeatures.Set))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
// Try removing the last feature.
|
||||
testFeatures := newEmptyFeatureSet()
|
||||
testFeatures.Add(X86FeatureFPU)
|
||||
testFeatures.Add(X86FeaturePAE)
|
||||
testFeatures.Remove(X86FeaturePAE)
|
||||
if !testFeatures.HasFeature(X86FeatureFPU) || len(testFeatures.Set) != 1 || testFeatures.HasFeature(X86FeaturePAE) {
|
||||
t.Errorf("Remove failed, got %v want %v", testFeatures, justFPU)
|
||||
}
|
||||
|
||||
// Try removing a feature not in the set.
|
||||
testFeatures.Remove(X86FeatureRDRAND)
|
||||
if !testFeatures.HasFeature(X86FeatureFPU) || len(testFeatures.Set) != 1 {
|
||||
t.Errorf("Remove failed, got %v want %v", testFeatures, justFPU)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeatureFromString(t *testing.T) {
|
||||
f, ok := FeatureFromString("avx")
|
||||
if f != X86FeatureAVX || !ok {
|
||||
t.Errorf("got %v want avx", f)
|
||||
}
|
||||
|
||||
f, ok = FeatureFromString("bad")
|
||||
if ok {
|
||||
t.Errorf("got %v want nothing", f)
|
||||
}
|
||||
}
|
||||
|
||||
// This tests function 0 (eax=0), which returns the vendor ID and highest cpuid
|
||||
// function reported to be available.
|
||||
func TestEmulateIDVendorAndLength(t *testing.T) {
|
||||
testFeatures := newEmptyFeatureSet()
|
||||
|
||||
ax, bx, cx, dx := testFeatures.EmulateID(0, 0)
|
||||
wantEax := uint32(0xd) // Highest supported cpuid function.
|
||||
|
||||
// These magical constants are the characters of "GenuineIntel".
|
||||
// See Intel AN485 for a reference on why they are laid out like this.
|
||||
wantEbx := uint32(0x756e6547)
|
||||
wantEcx := uint32(0x6c65746e)
|
||||
wantEdx := uint32(0x49656e69)
|
||||
if wantEax != ax {
|
||||
t.Errorf("highest function failed, got %x want %x", ax, wantEax)
|
||||
}
|
||||
|
||||
if wantEbx != bx || wantEcx != cx || wantEdx != dx {
|
||||
t.Errorf("vendor string emulation failed, bx:cx:dx, got %x:%x:%x want %x:%x:%x", bx, cx, dx, wantEbx, wantEcx, wantEdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmulateIDBasicFeatures(t *testing.T) {
|
||||
// Make a minimal test feature set.
|
||||
testFeatures := newEmptyFeatureSet()
|
||||
testFeatures.Add(X86FeatureCLFSH)
|
||||
testFeatures.Add(X86FeatureAVX)
|
||||
testFeatures.CacheLine = 64
|
||||
|
||||
ax, bx, cx, dx := testFeatures.EmulateID(1, 0)
|
||||
ECXAVXBit := uint32(1 << uint(X86FeatureAVX))
|
||||
EDXCLFlushBit := uint32(1 << uint(X86FeatureCLFSH-32)) // We adjust by 32 since it's in block 1.
|
||||
|
||||
if EDXCLFlushBit&dx == 0 || dx&^EDXCLFlushBit != 0 {
|
||||
t.Errorf("EmulateID failed, got feature bits %x want %x", dx, testFeatures.blockMask(1))
|
||||
}
|
||||
|
||||
if ECXAVXBit&cx == 0 || cx&^ECXAVXBit != 0 {
|
||||
t.Errorf("EmulateID failed, got feature bits %x want %x", cx, testFeatures.blockMask(0))
|
||||
}
|
||||
|
||||
// Default signature bits, based on values for i7/Xeon.
|
||||
// See Intel AN485 for information on stepping/model bits.
|
||||
defaultSignature := uint32(0x000106a0)
|
||||
if defaultSignature != ax {
|
||||
t.Errorf("EmulateID stepping emulation failed, got %x want %x", ax, defaultSignature)
|
||||
}
|
||||
|
||||
clflushSizeInfo := uint32(8 << 8)
|
||||
if clflushSizeInfo != bx {
|
||||
t.Errorf("EmulateID bx emulation failed, got %x want %x", bx, clflushSizeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmulateIDExtendedFeatures(t *testing.T) {
|
||||
// Make a minimal test feature set, one bit in each extended feature word.
|
||||
testFeatures := newEmptyFeatureSet()
|
||||
testFeatures.Add(X86FeatureSMEP)
|
||||
testFeatures.Add(X86FeatureAVX512VBMI)
|
||||
|
||||
ax, bx, cx, dx := testFeatures.EmulateID(7, 0)
|
||||
EBXSMEPBit := uint32(1 << uint(X86FeatureSMEP-2*32)) // Adjust by 2*32 since SMEP is a block 2 feature.
|
||||
ECXAVXBit := uint32(1 << uint(X86FeatureAVX512VBMI-3*32)) // We adjust by 3*32 since it's a block 3 feature.
|
||||
|
||||
// Test that the desired bit is set and no other bits are set.
|
||||
if EBXSMEPBit&bx == 0 || bx&^EBXSMEPBit != 0 {
|
||||
t.Errorf("extended feature emulation failed, got feature bits %x want %x", bx, testFeatures.blockMask(2))
|
||||
}
|
||||
|
||||
if ECXAVXBit&cx == 0 || cx&^ECXAVXBit != 0 {
|
||||
t.Errorf("extended feature emulation failed, got feature bits %x want %x", cx, testFeatures.blockMask(3))
|
||||
}
|
||||
|
||||
if ax != 0 || dx != 0 {
|
||||
t.Errorf("extended feature emulation failed, ax:dx, got %x:%x want 0:0", ax, dx)
|
||||
}
|
||||
|
||||
// Check that no subleaves other than 0 do anything.
|
||||
ax, bx, cx, dx = testFeatures.EmulateID(7, 1)
|
||||
if ax != 0 || bx != 0 || cx != 0 || dx != 0 {
|
||||
t.Errorf("extended feature emulation failed, got %x:%x:%x:%x want 0:0", ax, bx, cx, dx)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Checks that the expected extended features are available via cpuid functions
|
||||
// 0x80000000 and up.
|
||||
func TestEmulateIDExtended(t *testing.T) {
|
||||
testFeatures := newEmptyFeatureSet()
|
||||
testFeatures.Add(X86FeatureSYSCALL)
|
||||
EDXSYSCALLBit := uint32(1 << uint(X86FeatureSYSCALL-6*32)) // Adjust by 6*32 since SYSCALL is a block 6 feature.
|
||||
|
||||
ax, bx, cx, dx := testFeatures.EmulateID(0x80000000, 0)
|
||||
if ax != 0x80000001 || bx != 0 || cx != 0 || dx != 0 {
|
||||
t.Errorf("EmulateID extended emulation failed, ax:bx:cx:dx, got %x:%x:%x:%x want 0x80000001:0:0:0", ax, bx, cx, dx)
|
||||
}
|
||||
|
||||
_, _, _, dx = testFeatures.EmulateID(0x80000001, 0)
|
||||
if EDXSYSCALLBit&dx == 0 || dx&^EDXSYSCALLBit != 0 {
|
||||
t.Errorf("extended feature emulation failed, got feature bits %x want %x", dx, testFeatures.blockMask(6))
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package cpuid
|
||||
|
||||
const (
|
||||
// ARM64FeatureFP indicates support for single and double precision
|
||||
// float point types.
|
||||
ARM64FeatureFP Feature = iota
|
||||
|
||||
// ARM64FeatureASIMD indicates support for Advanced SIMD with single
|
||||
// and double precision float point arithmetic.
|
||||
ARM64FeatureASIMD
|
||||
|
||||
// ARM64FeatureEVTSTRM indicates support for the generic timer
|
||||
// configured to generate events at a frequency of approximately
|
||||
// 100KHz.
|
||||
ARM64FeatureEVTSTRM
|
||||
|
||||
// ARM64FeatureAES indicates support for AES instructions
|
||||
// (AESE/AESD/AESMC/AESIMC).
|
||||
ARM64FeatureAES
|
||||
|
||||
// ARM64FeaturePMULL indicates support for AES instructions
|
||||
// (PMULL/PMULL2).
|
||||
ARM64FeaturePMULL
|
||||
|
||||
// ARM64FeatureSHA1 indicates support for SHA1 instructions
|
||||
// (SHA1C/SHA1P/SHA1M etc).
|
||||
ARM64FeatureSHA1
|
||||
|
||||
// ARM64FeatureSHA2 indicates support for SHA2 instructions
|
||||
// (SHA256H/SHA256H2/SHA256SU0 etc).
|
||||
ARM64FeatureSHA2
|
||||
|
||||
// ARM64FeatureCRC32 indicates support for CRC32 instructions
|
||||
// (CRC32B/CRC32H/CRC32W etc).
|
||||
ARM64FeatureCRC32
|
||||
|
||||
// ARM64FeatureATOMICS indicates support for atomic instructions
|
||||
// (LDADD/LDCLR/LDEOR/LDSET etc).
|
||||
ARM64FeatureATOMICS
|
||||
|
||||
// ARM64FeatureFPHP indicates support for half precision float point
|
||||
// arithmetic.
|
||||
ARM64FeatureFPHP
|
||||
|
||||
// ARM64FeatureASIMDHP indicates support for ASIMD with half precision
|
||||
// float point arithmetic.
|
||||
ARM64FeatureASIMDHP
|
||||
|
||||
// ARM64FeatureCPUID indicates support for EL0 access to certain ID
|
||||
// registers is available.
|
||||
ARM64FeatureCPUID
|
||||
|
||||
// ARM64FeatureASIMDRDM indicates support for SQRDMLAH and SQRDMLSH
|
||||
// instructions.
|
||||
ARM64FeatureASIMDRDM
|
||||
|
||||
// ARM64FeatureJSCVT indicates support for the FJCVTZS instruction.
|
||||
ARM64FeatureJSCVT
|
||||
|
||||
// ARM64FeatureFCMA indicates support for the FCMLA and FCADD
|
||||
// instructions.
|
||||
ARM64FeatureFCMA
|
||||
|
||||
// ARM64FeatureLRCPC indicates support for the LDAPRB/LDAPRH/LDAPR
|
||||
// instructions.
|
||||
ARM64FeatureLRCPC
|
||||
|
||||
// ARM64FeatureDCPOP indicates support for DC instruction (DC CVAP).
|
||||
ARM64FeatureDCPOP
|
||||
|
||||
// ARM64FeatureSHA3 indicates support for SHA3 instructions
|
||||
// (EOR3/RAX1/XAR/BCAX).
|
||||
ARM64FeatureSHA3
|
||||
|
||||
// ARM64FeatureSM3 indicates support for SM3 instructions
|
||||
// (SM3SS1/SM3TT1A/SM3TT1B).
|
||||
ARM64FeatureSM3
|
||||
|
||||
// ARM64FeatureSM4 indicates support for SM4 instructions
|
||||
// (SM4E/SM4EKEY).
|
||||
ARM64FeatureSM4
|
||||
|
||||
// ARM64FeatureASIMDDP indicates support for dot product instructions
|
||||
// (UDOT/SDOT).
|
||||
ARM64FeatureASIMDDP
|
||||
|
||||
// ARM64FeatureSHA512 indicates support for SHA2 instructions
|
||||
// (SHA512H/SHA512H2/SHA512SU0).
|
||||
ARM64FeatureSHA512
|
||||
|
||||
// ARM64FeatureSVE indicates support for Scalable Vector Extension.
|
||||
ARM64FeatureSVE
|
||||
|
||||
// ARM64FeatureASIMDFHM indicates support for FMLAL and FMLSL
|
||||
// instructions.
|
||||
ARM64FeatureASIMDFHM
|
||||
)
|
||||
|
||||
var allFeatures = map[Feature]allFeatureInfo{
|
||||
ARM64FeatureFP: {"fp", true},
|
||||
ARM64FeatureASIMD: {"asimd", true},
|
||||
ARM64FeatureEVTSTRM: {"evtstrm", true},
|
||||
ARM64FeatureAES: {"aes", true},
|
||||
ARM64FeaturePMULL: {"pmull", true},
|
||||
ARM64FeatureSHA1: {"sha1", true},
|
||||
ARM64FeatureSHA2: {"sha2", true},
|
||||
ARM64FeatureCRC32: {"crc32", true},
|
||||
ARM64FeatureATOMICS: {"atomics", true},
|
||||
ARM64FeatureFPHP: {"fphp", true},
|
||||
ARM64FeatureASIMDHP: {"asimdhp", true},
|
||||
ARM64FeatureCPUID: {"cpuid", true},
|
||||
ARM64FeatureASIMDRDM: {"asimdrdm", true},
|
||||
ARM64FeatureJSCVT: {"jscvt", true},
|
||||
ARM64FeatureFCMA: {"fcma", true},
|
||||
ARM64FeatureLRCPC: {"lrcpc", true},
|
||||
ARM64FeatureDCPOP: {"dcpop", true},
|
||||
ARM64FeatureSHA3: {"sha3", true},
|
||||
ARM64FeatureSM3: {"sm3", true},
|
||||
ARM64FeatureSM4: {"sm4", true},
|
||||
ARM64FeatureASIMDDP: {"asimddp", true},
|
||||
ARM64FeatureSHA512: {"sha512", true},
|
||||
ARM64FeatureSVE: {"sve", true},
|
||||
ARM64FeatureASIMDFHM: {"asimdfhm", true},
|
||||
}
|
||||
|
||||
func archFlagOrder(fn func(Feature)) {
|
||||
for i := 0; i < len(allFeatures); i++ {
|
||||
fn(Feature(i))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
// cpuididFunction is a useful type wrapper.
|
||||
type cpuidFunction uint32
|
||||
|
||||
// The constants below are the lower or "standard" cpuid functions, ordered as
|
||||
// defined by the hardware. Note that these may not be included in the standard
|
||||
// set of functions that we are allowed to execute, which are filtered in the
|
||||
// Native.Query function defined below.
|
||||
const (
|
||||
vendorID cpuidFunction = 0x0 // Returns vendor ID and largest standard function.
|
||||
featureInfo cpuidFunction = 0x1 // Returns basic feature bits and processor signature.
|
||||
intelCacheDescriptors cpuidFunction = 0x2 // Returns list of cache descriptors. Intel only.
|
||||
intelSerialNumber cpuidFunction = 0x3 // Returns processor serial number (obsolete on new hardware). Intel only.
|
||||
intelDeterministicCacheParams cpuidFunction = 0x4 // Returns deterministic cache information. Intel only.
|
||||
monitorMwaitParams cpuidFunction = 0x5 // Returns information about monitor/mwait instructions.
|
||||
powerParams cpuidFunction = 0x6 // Returns information about power management and thermal sensors.
|
||||
extendedFeatureInfo cpuidFunction = 0x7 // Returns extended feature bits.
|
||||
_ // Function 0x8 is reserved.
|
||||
intelDCAParams cpuidFunction = 0x9 // Returns direct cache access information. Intel only.
|
||||
intelPMCInfo cpuidFunction = 0xa // Returns information about performance monitoring features. Intel only.
|
||||
intelX2APICInfo cpuidFunction = 0xb // Returns core/logical processor topology. Intel only.
|
||||
_ // Function 0xc is reserved.
|
||||
xSaveInfo cpuidFunction = 0xd // Returns information about extended state management.
|
||||
)
|
||||
|
||||
// The "extended" functions.
|
||||
const (
|
||||
extendedStart cpuidFunction = 0x80000000
|
||||
extendedFunctionInfo cpuidFunction = extendedStart + 0 // Returns highest available extended function in eax.
|
||||
extendedFeatures = extendedStart + 1 // Returns some extended feature bits in edx and ecx.
|
||||
addressSizes = extendedStart + 8 // Physical and virtual address sizes.
|
||||
)
|
||||
|
||||
var allowedBasicFunctions = [...]bool{
|
||||
vendorID: true,
|
||||
featureInfo: true,
|
||||
extendedFeatureInfo: true,
|
||||
intelCacheDescriptors: true,
|
||||
intelDeterministicCacheParams: true,
|
||||
xSaveInfo: true,
|
||||
}
|
||||
|
||||
var allowedExtendedFunctions = [...]bool{
|
||||
extendedFunctionInfo - extendedStart: true,
|
||||
extendedFeatures - extendedStart: true,
|
||||
addressSizes - extendedStart: true,
|
||||
}
|
||||
|
||||
// Function executes a CPUID function.
|
||||
//
|
||||
// This is typically the native function or a Static definition.
|
||||
type Function interface {
|
||||
Query(In) Out
|
||||
}
|
||||
|
||||
// Native is a native Function.
|
||||
//
|
||||
// This implements Function.
|
||||
type Native struct{}
|
||||
|
||||
// In is input to the Query function.
|
||||
//
|
||||
// +stateify savable
|
||||
type In struct {
|
||||
Eax uint32
|
||||
Ecx uint32
|
||||
}
|
||||
|
||||
// normalize drops irrelevant Ecx values.
|
||||
func (i *In) normalize() {
|
||||
switch cpuidFunction(i.Eax) {
|
||||
case vendorID, featureInfo, intelCacheDescriptors, extendedFunctionInfo, extendedFeatures:
|
||||
i.Ecx = 0 // Ignore.
|
||||
case intelDeterministicCacheParams, extendedFeatureInfo:
|
||||
// Preserve i.Ecx.
|
||||
}
|
||||
}
|
||||
|
||||
// Out is output from the Query function.
|
||||
//
|
||||
// +stateify savable
|
||||
type Out struct {
|
||||
Eax uint32
|
||||
Ebx uint32
|
||||
Ecx uint32
|
||||
Edx uint32
|
||||
}
|
||||
|
||||
// native is the native Query function.
|
||||
func native(In) Out
|
||||
|
||||
// Query executes CPUID natively.
|
||||
//
|
||||
// This implements Function.
|
||||
//
|
||||
//go:nosplit
|
||||
func (*Native) Query(in In) Out {
|
||||
if int(in.Eax) < len(allowedBasicFunctions) && allowedBasicFunctions[in.Eax] {
|
||||
return native(in)
|
||||
} else if in.Eax >= uint32(extendedStart) {
|
||||
if l := int(in.Eax - uint32(extendedStart)); l < len(allowedExtendedFunctions) && allowedExtendedFunctions[l] {
|
||||
return native(in)
|
||||
}
|
||||
}
|
||||
return Out{} // All zeros.
|
||||
}
|
||||
|
||||
// query is a internal wrapper.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) query(fn cpuidFunction) (uint32, uint32, uint32, uint32) {
|
||||
out := fs.Query(In{Eax: uint32(fn)})
|
||||
return out.Eax, out.Ebx, out.Ecx, out.Edx
|
||||
}
|
||||
|
||||
// HostFeatureSet returns a host CPUID.
|
||||
//
|
||||
//go:nosplit
|
||||
func HostFeatureSet() FeatureSet {
|
||||
return FeatureSet{
|
||||
Function: &Native{},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// cpuFreqMHz is the native CPU frequency.
|
||||
cpuFreqMHz float64
|
||||
)
|
||||
|
||||
// Reads max cpu frequency from host /proc/cpuinfo. Must run before syscall
|
||||
// filter installation. This value is used to create the fake /proc/cpuinfo
|
||||
// from a FeatureSet.
|
||||
func init() {
|
||||
cpuinfob, err := ioutil.ReadFile("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
// Leave it as 0... the VDSO bails out in the same way.
|
||||
log.Warningf("Could not read /proc/cpuinfo: %v", err)
|
||||
return
|
||||
}
|
||||
cpuinfo := string(cpuinfob)
|
||||
|
||||
// We get the value straight from host /proc/cpuinfo. On machines with
|
||||
// frequency scaling enabled, this will only get the current value
|
||||
// which will likely be inaccurate. This is fine on machines with
|
||||
// frequency scaling disabled.
|
||||
for _, line := range strings.Split(cpuinfo, "\n") {
|
||||
if strings.Contains(line, "cpu MHz") {
|
||||
splitMHz := strings.Split(line, ":")
|
||||
if len(splitMHz) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed cpu MHz line")
|
||||
return
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuFreqMHz as 0.
|
||||
var err error
|
||||
cpuFreqMHz, err = strconv.ParseFloat(strings.TrimSpace(splitMHz[1]), 64)
|
||||
if err != nil {
|
||||
log.Warningf("Could not parse cpu MHz value %v: %v", splitMHz[1], err)
|
||||
cpuFreqMHz = 0
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Warningf("Could not parse /proc/cpuinfo, it is empty or does not contain cpu MHz")
|
||||
}
|
||||
@@ -12,8 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// func HostID(rax, rcx uint32) (ret0, ret1, ret2, ret3 uint32)
|
||||
TEXT ·HostID(SB),$0-48
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·native(SB),NOSPLIT,$0-24
|
||||
MOVL ax+0(FP), AX
|
||||
MOVL cx+4(FP), CX
|
||||
CPUID
|
||||
@@ -0,0 +1,195 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
// hostFeatureSet is initialized at startup.
|
||||
//
|
||||
// This is copied for HostFeatureSet, below.
|
||||
var hostFeatureSet FeatureSet
|
||||
|
||||
// HostFeatureSet returns a copy of the host FeatureSet.
|
||||
func HostFeatureSet() FeatureSet {
|
||||
return hostFeatureSet
|
||||
}
|
||||
|
||||
// Fixed returns the same feature set.
|
||||
func (fs FeatureSet) Fixed() FeatureSet {
|
||||
return fs
|
||||
}
|
||||
|
||||
// Reads CPU information from host /proc/cpuinfo.
|
||||
//
|
||||
// Must run before syscall filter installation. This value is used to create
|
||||
// the fake /proc/cpuinfo from a FeatureSet.
|
||||
func initCPUInfo() {
|
||||
cpuinfob, err := ioutil.ReadFile("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
// Leave everything at 0, nothing can be done.
|
||||
log.Warningf("Could not read /proc/cpuinfo: %v", err)
|
||||
return
|
||||
}
|
||||
cpuinfo := string(cpuinfob)
|
||||
|
||||
// We get the value straight from host /proc/cpuinfo.
|
||||
for _, line := range strings.Split(cpuinfo, "\n") {
|
||||
switch {
|
||||
case strings.Contains(line, "BogoMIPS"):
|
||||
splitMHz := strings.Split(line, ":")
|
||||
if len(splitMHz) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed BogoMIPS")
|
||||
break
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuFreqMHz as 0.
|
||||
var err error
|
||||
hostFeatureSet.cpuFreqMHz, err = strconv.ParseFloat(strings.TrimSpace(splitMHz[1]), 64)
|
||||
if err != nil {
|
||||
hostFeatureSet.cpuFreqMHz = 0.0
|
||||
log.Warningf("Could not parse BogoMIPS value %v: %v", splitMHz[1], err)
|
||||
}
|
||||
case strings.Contains(line, "CPU implementer"):
|
||||
splitImpl := strings.Split(line, ":")
|
||||
if len(splitImpl) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed CPU implementer")
|
||||
break
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuImplHex as 0.
|
||||
var err error
|
||||
hostFeatureSet.cpuImplHex, err = strconv.ParseUint(strings.TrimSpace(splitImpl[1]), 0, 64)
|
||||
if err != nil {
|
||||
hostFeatureSet.cpuImplHex = 0
|
||||
log.Warningf("Could not parse CPU implementer value %v: %v", splitImpl[1], err)
|
||||
}
|
||||
case strings.Contains(line, "CPU architecture"):
|
||||
splitArch := strings.Split(line, ":")
|
||||
if len(splitArch) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed CPU architecture")
|
||||
break
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuArchDec as 0.
|
||||
var err error
|
||||
hostFeatureSet.cpuArchDec, err = strconv.ParseUint(strings.TrimSpace(splitArch[1]), 0, 64)
|
||||
if err != nil {
|
||||
hostFeatureSet.cpuArchDec = 0
|
||||
log.Warningf("Could not parse CPU architecture value %v: %v", splitArch[1], err)
|
||||
}
|
||||
case strings.Contains(line, "CPU variant"):
|
||||
splitVar := strings.Split(line, ":")
|
||||
if len(splitVar) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed CPU variant")
|
||||
break
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuVarHex as 0.
|
||||
var err error
|
||||
hostFeatureSet.cpuVarHex, err = strconv.ParseUint(strings.TrimSpace(splitVar[1]), 0, 64)
|
||||
if err != nil {
|
||||
hostFeatureSet.cpuVarHex = 0
|
||||
log.Warningf("Could not parse CPU variant value %v: %v", splitVar[1], err)
|
||||
}
|
||||
case strings.Contains(line, "CPU part"):
|
||||
splitPart := strings.Split(line, ":")
|
||||
if len(splitPart) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed CPU part")
|
||||
break
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuPartHex as 0.
|
||||
var err error
|
||||
hostFeatureSet.cpuPartHex, err = strconv.ParseUint(strings.TrimSpace(splitPart[1]), 0, 64)
|
||||
if err != nil {
|
||||
hostFeatureSet.cpuPartHex = 0
|
||||
log.Warningf("Could not parse CPU part value %v: %v", splitPart[1], err)
|
||||
}
|
||||
case strings.Contains(line, "CPU revision"):
|
||||
splitRev := strings.Split(line, ":")
|
||||
if len(splitRev) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed CPU revision")
|
||||
break
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuRevDec as 0.
|
||||
var err error
|
||||
hostFeatureSet.cpuRevDec, err = strconv.ParseUint(strings.TrimSpace(splitRev[1]), 0, 64)
|
||||
if err != nil {
|
||||
hostFeatureSet.cpuRevDec = 0
|
||||
log.Warningf("Could not parse CPU revision value %v: %v", splitRev[1], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
// Static is a static CPUID function.
|
||||
//
|
||||
// +stateify savable
|
||||
type Static map[In]Out
|
||||
|
||||
// Fixed converts the FeatureSet to a fixed set.
|
||||
func (fs FeatureSet) Fixed() FeatureSet {
|
||||
return fs.ToStatic().ToFeatureSet()
|
||||
}
|
||||
|
||||
// ToStatic converts a FeatureSet to a Static function.
|
||||
//
|
||||
// You can create a new static feature set as:
|
||||
//
|
||||
// fs := otherFeatureSet.ToStatic().ToFeatureSet()
|
||||
func (fs FeatureSet) ToStatic() Static {
|
||||
s := make(Static)
|
||||
|
||||
// Save all allowed top-level functions.
|
||||
for fn, allowed := range allowedBasicFunctions {
|
||||
if allowed {
|
||||
in := In{Eax: uint32(fn)}
|
||||
s[in] = fs.Query(in)
|
||||
}
|
||||
}
|
||||
|
||||
// Save all allowed extended functions.
|
||||
for fn, allowed := range allowedExtendedFunctions {
|
||||
if allowed {
|
||||
in := In{Eax: uint32(fn) + uint32(extendedStart)}
|
||||
s[in] = fs.Query(in)
|
||||
}
|
||||
}
|
||||
|
||||
// Save all features (may be redundant).
|
||||
for feature := range allFeatures {
|
||||
feature.set(s, fs.HasFeature(feature))
|
||||
}
|
||||
|
||||
// Save all cache information.
|
||||
out := fs.Query(In{Eax: uint32(featureInfo)})
|
||||
for i := uint32(0); i < out.Ecx; i++ {
|
||||
in := In{Eax: uint32(intelDeterministicCacheParams), Ecx: i}
|
||||
out := fs.Query(in)
|
||||
s[in] = out
|
||||
if CacheType(out.Eax&0xf) == cacheNull {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// ToFeatureSet converts a static specification to a FeatureSet.
|
||||
//
|
||||
// This overloads some local values, where required.
|
||||
func (s Static) ToFeatureSet() FeatureSet {
|
||||
// Make a copy.
|
||||
ns := make(Static)
|
||||
for k, v := range s {
|
||||
ns[k] = v
|
||||
}
|
||||
ns.normalize()
|
||||
return FeatureSet{ns}
|
||||
}
|
||||
|
||||
// afterLoad calls normalize.
|
||||
func (s Static) afterLoad() {
|
||||
s.normalize()
|
||||
}
|
||||
|
||||
// normalize normalizes FPU sizes.
|
||||
func (s Static) normalize() {
|
||||
// Override local FPU sizes, which must be fixed.
|
||||
fs := FeatureSet{s}
|
||||
if fs.HasFeature(X86FeatureXSAVE) {
|
||||
in := In{Eax: uint32(xSaveInfo)}
|
||||
out := s[in]
|
||||
out.Ecx = maxXsaveSize
|
||||
s[in] = out
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a feature.
|
||||
func (s Static) Add(feature Feature) Static {
|
||||
feature.set(s, true)
|
||||
return s
|
||||
}
|
||||
|
||||
// Remove removes a feature.
|
||||
func (s Static) Remove(feature Feature) Static {
|
||||
feature.set(s, false)
|
||||
return s
|
||||
}
|
||||
|
||||
// Query implements Function.Query.
|
||||
func (s Static) Query(in In) Out {
|
||||
in.normalize()
|
||||
return s[in]
|
||||
}
|
||||
@@ -82,5 +82,6 @@ go_library(
|
||||
"//pkg/safecopy",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/arch/fpu",
|
||||
"//pkg/sync",
|
||||
],
|
||||
)
|
||||
|
||||
+6
-10
@@ -30,6 +30,12 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
// VirtualAddressBits is fixed at 48.
|
||||
VirtualAddressBits = 48
|
||||
|
||||
// PhysicalAddressBits is fixed at 40.
|
||||
PhysicalAddressBits = 40
|
||||
|
||||
// DAIF bits:debug, sError, IRQ, FIQ.
|
||||
_PSR_D_BIT = 0x00000200
|
||||
_PSR_A_BIT = 0x00000100
|
||||
@@ -111,13 +117,3 @@ const (
|
||||
PageFault Vector = El0SyncDa
|
||||
VirtualizationException Vector = El0ErrBounce
|
||||
)
|
||||
|
||||
// VirtualAddressBits returns the number bits available for virtual addresses.
|
||||
func VirtualAddressBits() uint32 {
|
||||
return 48
|
||||
}
|
||||
|
||||
// PhysicalAddressBits returns the number of bits available for physical addresses.
|
||||
func PhysicalAddressBits() uint32 {
|
||||
return 40
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user