blob: f14b0baf229951de28ef0975c6cc04c9ddea4a43 [file] [log] [blame]
// 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
// block is a collection of 32 Feature bits.
type block int
// blockSize is the number of bits in a single block.
const blockSize = 32
// featureID returns the feature identified by the given block and bit.
//
// Feature bits 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.
func featureID(b block, bit int) Feature {
return Feature(blockSize*int(b) + bit)
}
// block returns the block associated with the feature.
func (f Feature) block() block {
return block(f / blockSize)
}
// Bit returns the bit associated with the feature.
func (f Feature) bit() uint32 {
return uint32(1 << (f % blockSize))
}
// ChangeableSet is a feature set that can allows changes.
type ChangeableSet interface {
Query(in In) Out
Set(in In, out Out)
}
// Set sets the given feature.
func (f Feature) Set(s ChangeableSet) {
f.set(s, true)
}
// Unset unsets the given feature.
func (f Feature) Unset(s ChangeableSet) {
f.set(s, false)
}
// set sets the given feature.
func (f Feature) set(s ChangeableSet, on bool) {
switch f.block() {
case 0:
out := s.Query(In{Eax: uint32(featureInfo)})
if on {
out.Ecx |= f.bit()
} else {
out.Ecx &^= f.bit()
}
s.Set(In{Eax: uint32(featureInfo)}, out)
case 1:
out := s.Query(In{Eax: uint32(featureInfo)})
if on {
out.Edx |= f.bit()
} else {
out.Edx &^= f.bit()
}
s.Set(In{Eax: uint32(featureInfo)}, out)
case 2:
out := s.Query(In{Eax: uint32(extendedFeatureInfo)})
if on {
out.Ebx |= f.bit()
} else {
out.Ebx &^= f.bit()
}
s.Set(In{Eax: uint32(extendedFeatureInfo)}, out)
case 3:
out := s.Query(In{Eax: uint32(extendedFeatureInfo)})
if on {
out.Ecx |= f.bit()
} else {
out.Ecx &^= f.bit()
}
s.Set(In{Eax: uint32(extendedFeatureInfo)}, out)
case 4:
// Need to turn on the bit in block 0.
out := s.Query(In{Eax: uint32(featureInfo)})
out.Ecx |= (1 << 26)
s.Set(In{Eax: uint32(featureInfo)}, out)
out = s.Query(In{Eax: xSaveInfoSub.eax(), Ecx: xSaveInfoSub.ecx()})
if on {
out.Eax |= f.bit()
} else {
out.Eax &^= f.bit()
}
s.Set(In{Eax: xSaveInfoSub.eax(), Ecx: xSaveInfoSub.ecx()}, out)
case 5, 6:
// Need to enable extended features.
out := s.Query(In{Eax: uint32(extendedFunctionInfo)})
if out.Eax < uint32(extendedFeatures) {
out.Eax = uint32(extendedFeatures)
}
s.Set(In{Eax: uint32(extendedFunctionInfo)}, out)
out = s.Query(In{Eax: uint32(extendedFeatures)})
if f.block() == 5 {
if on {
out.Ecx |= f.bit()
} else {
out.Ecx &^= f.bit()
}
} else {
if on {
out.Edx |= f.bit()
} else {
out.Edx &^= f.bit()
}
}
s.Set(In{Eax: uint32(extendedFeatures)}, out)
}
}
// check checks for the given feature.
//
//go:nosplit
func (f Feature) check(fs FeatureSet) bool {
switch f.block() {
case 0:
_, _, cx, _ := fs.query(featureInfo)
return (cx & f.bit()) != 0
case 1:
_, _, _, dx := fs.query(featureInfo)
return (dx & f.bit()) != 0
case 2:
_, bx, _, _ := fs.query(extendedFeatureInfo)
return (bx & f.bit()) != 0
case 3:
_, _, cx, _ := fs.query(extendedFeatureInfo)
return (cx & f.bit()) != 0
case 4:
// Need to check appropriate bit in block 0.
_, _, cx, _ := fs.query(featureInfo)
if (cx & (1 << 26)) == 0 {
return false
}
ax, _, _, _ := fs.query(xSaveInfoSub)
return (ax & f.bit()) != 0
case 5, 6:
// eax=0x80000000 gets supported extended levels. We use this
// to determine if there are any non-zero block 4 or block 6
// bits to find.
ax, _, _, _ := fs.query(extendedFunctionInfo)
if ax >= uint32(extendedFeatures) {
_, _, cx, dx := fs.query(extendedFeatures)
if f.block() == 5 {
return (cx & f.bit()) != 0
}
// Ignore features duplicated from block 1 on AMD.
// These bits are reserved on Intel.
return ((dx &^ block6DuplicateMask) & f.bit()) != 0
}
return false
default:
return false
}
}
// Block 0 constants are all of the "basic" feature bits returned by a cpuid in
// ecx with eax=1.
const (
X86FeatureSSE3 Feature = iota
X86FeaturePCLMULDQ
X86FeatureDTES64
X86FeatureMONITOR
X86FeatureDSCPL
X86FeatureVMX
X86FeatureSMX
X86FeatureEST
X86FeatureTM2
X86FeatureSSSE3 // Not a typo, "supplemental" SSE3.
X86FeatureCNXTID
X86FeatureSDBG
X86FeatureFMA
X86FeatureCX16
X86FeatureXTPR
X86FeaturePDCM
_ // ecx bit 16 is reserved.
X86FeaturePCID
X86FeatureDCA
X86FeatureSSE4_1
X86FeatureSSE4_2
X86FeatureX2APIC
X86FeatureMOVBE
X86FeaturePOPCNT
X86FeatureTSCD
X86FeatureAES
X86FeatureXSAVE
X86FeatureOSXSAVE
X86FeatureAVX
X86FeatureF16C
X86FeatureRDRAND
X86FeatureHypervisor
)
// Block 1 constants are all of the "basic" feature bits returned by a cpuid in
// edx with eax=1.
const (
X86FeatureFPU Feature = 32 + iota
X86FeatureVME
X86FeatureDE
X86FeaturePSE
X86FeatureTSC
X86FeatureMSR
X86FeaturePAE
X86FeatureMCE
X86FeatureCX8
X86FeatureAPIC
_ // edx bit 10 is reserved.
X86FeatureSEP
X86FeatureMTRR
X86FeaturePGE
X86FeatureMCA
X86FeatureCMOV
X86FeaturePAT
X86FeaturePSE36
X86FeaturePSN
X86FeatureCLFSH
_ // edx bit 20 is reserved.
X86FeatureDS
X86FeatureACPI
X86FeatureMMX
X86FeatureFXSR
X86FeatureSSE
X86FeatureSSE2
X86FeatureSS
X86FeatureHTT
X86FeatureTM
X86FeatureIA64
X86FeaturePBE
)
// Block 2 bits are the "structured extended" features returned in ebx for
// eax=7, ecx=0.
const (
X86FeatureFSGSBase Feature = 2*32 + iota
X86FeatureTSC_ADJUST
_ // ebx bit 2 is reserved.
X86FeatureBMI1
X86FeatureHLE
X86FeatureAVX2
X86FeatureFDP_EXCPTN_ONLY
X86FeatureSMEP
X86FeatureBMI2
X86FeatureERMS
X86FeatureINVPCID
X86FeatureRTM
X86FeatureCQM
X86FeatureFPCSDS
X86FeatureMPX
X86FeatureRDT
X86FeatureAVX512F
X86FeatureAVX512DQ
X86FeatureRDSEED
X86FeatureADX
X86FeatureSMAP
X86FeatureAVX512IFMA
X86FeaturePCOMMIT
X86FeatureCLFLUSHOPT
X86FeatureCLWB
X86FeatureIPT // Intel processor trace.
X86FeatureAVX512PF
X86FeatureAVX512ER
X86FeatureAVX512CD
X86FeatureSHA
X86FeatureAVX512BW
X86FeatureAVX512VL
)
// Block 3 bits are the "extended" features returned in ecx for eax=7, ecx=0.
const (
X86FeaturePREFETCHWT1 Feature = 3*32 + iota
X86FeatureAVX512VBMI
X86FeatureUMIP
X86FeaturePKU
X86FeatureOSPKE
X86FeatureWAITPKG
X86FeatureAVX512_VBMI2
X86FeatureCET_SS
X86FeatureGFNI
X86FeatureVAES
X86FeatureVPCLMULQDQ
X86FeatureAVX512_VNNI
X86FeatureAVX512_BITALG
X86FeatureTME
X86FeatureAVX512_VPOPCNTDQ
_ // ecx bit 15 is reserved
X86FeatureLA57
// ecx bits 17-21 are reserved
_
_
_
_
_
X86FeatureRDPID
// ecx bits 23-24 are reserved
_
_
X86FeatureCLDEMOTE
_ // ecx bit 26 is reserved
X86FeatureMOVDIRI
X86FeatureMOVDIR64B
)
// Block 4 constants are for xsave capabilities in CPUID.(EAX=0DH,ECX=01H):EAX.
// The CPUID leaf is available only if 'X86FeatureXSAVE' is present.
const (
X86FeatureXSAVEOPT Feature = 4*32 + iota
X86FeatureXSAVEC
X86FeatureXGETBV1
X86FeatureXSAVES
// EAX[31:4] are reserved.
)
// Block 5 constants are the extended feature bits in
// CPUID.(EAX=0x80000001):ECX.
const (
X86FeatureLAHF64 Feature = 5*32 + iota
X86FeatureCMP_LEGACY
X86FeatureSVM
X86FeatureEXTAPIC
X86FeatureCR8_LEGACY
X86FeatureLZCNT
X86FeatureSSE4A
X86FeatureMISALIGNSSE
X86FeaturePREFETCHW
X86FeatureOSVW
X86FeatureIBS
X86FeatureXOP
X86FeatureSKINIT
X86FeatureWDT
_ // ecx bit 14 is reserved.
X86FeatureLWP
X86FeatureFMA4
X86FeatureTCE
_ // ecx bit 18 is reserved.
_ // ecx bit 19 is reserved.
_ // ecx bit 20 is reserved.
X86FeatureTBM
X86FeatureTOPOLOGY
X86FeaturePERFCTR_CORE
X86FeaturePERFCTR_NB
_ // ecx bit 25 is reserved.
X86FeatureBPEXT
X86FeaturePERFCTR_TSC
X86FeaturePERFCTR_LLC
X86FeatureMWAITX
X86FeatureADMSKEXTN
_ // ecx bit 31 is reserved.
)
// Block 6 constants are the extended feature bits in
// CPUID.(EAX=0x80000001):EDX.
//
// These are sparse, and so the bit positions are assigned manually.
const (
// On AMD, EDX[24:23] | EDX[17:12] | EDX[9:0] are duplicate features
// also defined in block 1 (in identical bit positions). Those features
// are not listed here.
block6DuplicateMask = 0x183f3ff
X86FeatureSYSCALL Feature = 6*32 + 11
X86FeatureNX Feature = 6*32 + 20
X86FeatureMMXEXT Feature = 6*32 + 22
X86FeatureFXSR_OPT Feature = 6*32 + 25
X86FeatureGBPAGES Feature = 6*32 + 26
X86FeatureRDTSCP Feature = 6*32 + 27
X86FeatureLM Feature = 6*32 + 29
X86Feature3DNOWEXT Feature = 6*32 + 30
X86Feature3DNOW Feature = 6*32 + 31
)
// These are the extended floating point state features. They are used to
// enumerate floating point features in XCR0, XSTATE_BV, etc.
const (
XSAVEFeatureX87 = 1 << 0
XSAVEFeatureSSE = 1 << 1
XSAVEFeatureAVX = 1 << 2
XSAVEFeatureBNDREGS = 1 << 3
XSAVEFeatureBNDCSR = 1 << 4
XSAVEFeatureAVX512op = 1 << 5
XSAVEFeatureAVX512zmm0 = 1 << 6
XSAVEFeatureAVX512zmm16 = 1 << 7
XSAVEFeaturePKRU = 1 << 9
)
// allFeatures is the set of allFeatures.
//
// These match names used in arch/x86/kernel/cpu/capflags.c.
var allFeatures = map[Feature]allFeatureInfo{
// Block 0.
X86FeatureSSE3: {"pni", true},
X86FeaturePCLMULDQ: {"pclmulqdq", true},
X86FeatureDTES64: {"dtes64", true},
X86FeatureMONITOR: {"monitor", true},
X86FeatureDSCPL: {"ds_cpl", true},
X86FeatureVMX: {"vmx", true},
X86FeatureSMX: {"smx", true},
X86FeatureEST: {"est", true},
X86FeatureTM2: {"tm2", true},
X86FeatureSSSE3: {"ssse3", true},
X86FeatureCNXTID: {"cid", true},
X86FeatureSDBG: {"sdbg", true},
X86FeatureFMA: {"fma", true},
X86FeatureCX16: {"cx16", true},
X86FeatureXTPR: {"xtpr", true},
X86FeaturePDCM: {"pdcm", true},
X86FeaturePCID: {"pcid", true},
X86FeatureDCA: {"dca", true},
X86FeatureSSE4_1: {"sse4_1", true},
X86FeatureSSE4_2: {"sse4_2", true},
X86FeatureX2APIC: {"x2apic", true},
X86FeatureMOVBE: {"movbe", true},
X86FeaturePOPCNT: {"popcnt", true},
X86FeatureTSCD: {"tsc_deadline_timer", true},
X86FeatureAES: {"aes", true},
X86FeatureXSAVE: {"xsave", true},
X86FeatureAVX: {"avx", true},
X86FeatureF16C: {"f16c", true},
X86FeatureRDRAND: {"rdrand", true},
X86FeatureHypervisor: {"hypervisor", true},
X86FeatureOSXSAVE: {"osxsave", false},
// Block 1.
X86FeatureFPU: {"fpu", true},
X86FeatureVME: {"vme", true},
X86FeatureDE: {"de", true},
X86FeaturePSE: {"pse", true},
X86FeatureTSC: {"tsc", true},
X86FeatureMSR: {"msr", true},
X86FeaturePAE: {"pae", true},
X86FeatureMCE: {"mce", true},
X86FeatureCX8: {"cx8", true},
X86FeatureAPIC: {"apic", true},
X86FeatureSEP: {"sep", true},
X86FeatureMTRR: {"mtrr", true},
X86FeaturePGE: {"pge", true},
X86FeatureMCA: {"mca", true},
X86FeatureCMOV: {"cmov", true},
X86FeaturePAT: {"pat", true},
X86FeaturePSE36: {"pse36", true},
X86FeaturePSN: {"pn", true},
X86FeatureCLFSH: {"clflush", true},
X86FeatureDS: {"dts", true},
X86FeatureACPI: {"acpi", true},
X86FeatureMMX: {"mmx", true},
X86FeatureFXSR: {"fxsr", true},
X86FeatureSSE: {"sse", true},
X86FeatureSSE2: {"sse2", true},
X86FeatureSS: {"ss", true},
X86FeatureHTT: {"ht", true},
X86FeatureTM: {"tm", true},
X86FeatureIA64: {"ia64", true},
X86FeaturePBE: {"pbe", true},
// Block 2.
X86FeatureFSGSBase: {"fsgsbase", true},
X86FeatureTSC_ADJUST: {"tsc_adjust", true},
X86FeatureBMI1: {"bmi1", true},
X86FeatureHLE: {"hle", true},
X86FeatureAVX2: {"avx2", true},
X86FeatureSMEP: {"smep", true},
X86FeatureBMI2: {"bmi2", true},
X86FeatureERMS: {"erms", true},
X86FeatureINVPCID: {"invpcid", true},
X86FeatureRTM: {"rtm", true},
X86FeatureCQM: {"cqm", true},
X86FeatureMPX: {"mpx", true},
X86FeatureRDT: {"rdt_a", true},
X86FeatureAVX512F: {"avx512f", true},
X86FeatureAVX512DQ: {"avx512dq", true},
X86FeatureRDSEED: {"rdseed", true},
X86FeatureADX: {"adx", true},
X86FeatureSMAP: {"smap", true},
X86FeatureCLWB: {"clwb", true},
X86FeatureAVX512PF: {"avx512pf", true},
X86FeatureAVX512ER: {"avx512er", true},
X86FeatureAVX512CD: {"avx512cd", true},
X86FeatureSHA: {"sha_ni", true},
X86FeatureAVX512BW: {"avx512bw", true},
X86FeatureAVX512VL: {"avx512vl", true},
X86FeatureFDP_EXCPTN_ONLY: {"fdp_excptn_only", false},
X86FeatureFPCSDS: {"fpcsds", false},
X86FeatureIPT: {"ipt", false},
X86FeatureCLFLUSHOPT: {"clfushopt", false},
// Block 3.
X86FeatureAVX512VBMI: {"avx512vbmi", true},
X86FeatureUMIP: {"umip", true},
X86FeaturePKU: {"pku", true},
X86FeatureOSPKE: {"ospke", true},
X86FeatureWAITPKG: {"waitpkg", true},
X86FeatureAVX512_VBMI2: {"avx512_vbmi2", true},
X86FeatureGFNI: {"gfni", true},
X86FeatureCET_SS: {"cet_ss", false},
X86FeatureVAES: {"vaes", true},
X86FeatureVPCLMULQDQ: {"vpclmulqdq", true},
X86FeatureAVX512_VNNI: {"avx512_vnni", true},
X86FeatureAVX512_BITALG: {"avx512_bitalg", true},
X86FeatureTME: {"tme", true},
X86FeatureAVX512_VPOPCNTDQ: {"avx512_vpopcntdq", true},
X86FeatureLA57: {"la57", true},
X86FeatureRDPID: {"rdpid", true},
X86FeatureCLDEMOTE: {"cldemote", true},
X86FeatureMOVDIRI: {"movdiri", true},
X86FeatureMOVDIR64B: {"movdir64b", true},
X86FeaturePREFETCHWT1: {"prefetchwt1", false},
// Block 4.
X86FeatureXSAVEOPT: {"xsaveopt", true},
X86FeatureXSAVEC: {"xsavec", true},
X86FeatureXGETBV1: {"xgetbv1", true},
X86FeatureXSAVES: {"xsaves", true},
// Block 5.
X86FeatureLAHF64: {"lahf_lm", true}, // LAHF/SAHF in long mode.
X86FeatureCMP_LEGACY: {"cmp_legacy", true},
X86FeatureSVM: {"svm", true},
X86FeatureEXTAPIC: {"extapic", true},
X86FeatureCR8_LEGACY: {"cr8_legacy", true},
X86FeatureLZCNT: {"abm", true}, // Advanced bit manipulation.
X86FeatureSSE4A: {"sse4a", true},
X86FeatureMISALIGNSSE: {"misalignsse", true},
X86FeaturePREFETCHW: {"3dnowprefetch", true},
X86FeatureOSVW: {"osvw", true},
X86FeatureIBS: {"ibs", true},
X86FeatureXOP: {"xop", true},
X86FeatureSKINIT: {"skinit", true},
X86FeatureWDT: {"wdt", true},
X86FeatureLWP: {"lwp", true},
X86FeatureFMA4: {"fma4", true},
X86FeatureTCE: {"tce", true},
X86FeatureTBM: {"tbm", true},
X86FeatureTOPOLOGY: {"topoext", true},
X86FeaturePERFCTR_CORE: {"perfctr_core", true},
X86FeaturePERFCTR_NB: {"perfctr_nb", true},
X86FeatureBPEXT: {"bpext", true},
X86FeaturePERFCTR_TSC: {"ptsc", true},
X86FeaturePERFCTR_LLC: {"perfctr_llc", true},
X86FeatureMWAITX: {"mwaitx", true},
X86FeatureADMSKEXTN: {"ad_mask_extn", false},
// Block 6.
X86FeatureSYSCALL: {"syscall", true},
X86FeatureNX: {"nx", true},
X86FeatureMMXEXT: {"mmxext", true},
X86FeatureFXSR_OPT: {"fxsr_opt", true},
X86FeatureGBPAGES: {"pdpe1gb", true},
X86FeatureRDTSCP: {"rdtscp", true},
X86FeatureLM: {"lm", true},
X86Feature3DNOWEXT: {"3dnowext", true},
X86Feature3DNOW: {"3dnow", true},
}
// linuxBlockOrder defines the order in which linux organizes the feature
// blocks. Linux also tracks feature bits in 32-bit blocks, but in an order
// which doesn't match well here, so for the /proc/cpuinfo generation we simply
// re-map the blocks to Linux's ordering and then go through the bits in each
// block.
var linuxBlockOrder = []block{1, 6, 0, 5, 2, 4, 3}
func archFlagOrder(fn func(Feature)) {
for _, b := range linuxBlockOrder {
for i := 0; i < blockSize; i++ {
f := featureID(b, i)
if _, ok := allFeatures[f]; ok {
fn(f)
}
}
}
}