From b15a7267e1965148731e9d379ec33154f1bb54e6 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 26 Oct 2018 16:54:46 -0700 Subject: Add AMD-specific features to cpuid package Extend the cpuid package to parse and emulate cpuid features that exist only on AMD and not Intel. The least straightforward part of this is that AMD duplicates several block 1 features in block 6. Thus we ignore those features when parsing block 6 and add them when emulating. PiperOrigin-RevId: 218935032 Change-Id: Id41bf1c24720b0d9b968e2c19ab5bc00a6d62bd4 --- pkg/cpuid/cpuid.go | 221 ++++++++++++++++++++++++++++-------------------- pkg/cpuid/cpuid_test.go | 28 ++++++ 2 files changed, 155 insertions(+), 94 deletions(-) (limited to 'pkg') diff --git a/pkg/cpuid/cpuid.go b/pkg/cpuid/cpuid.go index efe9ebe87..f4b1db896 100644 --- a/pkg/cpuid/cpuid.go +++ b/pkg/cpuid/cpuid.go @@ -39,9 +39,20 @@ import ( "gvisor.googlesource.com/gvisor/pkg/log" ) +// Common references for CPUID leaves and bits: +// +// 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 ..." + // Feature is a unique identifier for a particular cpu feature. We just use an -// int as a feature number on x86. It corresponds to the bit position in the -// basic feature mask returned by a cpuid with eax=1. +// int as a feature number 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. type Feature int // block is a collection of 32 Feature bits. @@ -185,36 +196,60 @@ const ( ) // Block 5 constants are the extended feature bits in -// CPUID.(EAX=0x80000001):ECX. These are very sparse, and so the bit positions -// are assigned manually. +// CPUID.(EAX=0x80000001):ECX. const ( - X86FeatureLAHF64 Feature = 5*32 + 0 - X86FeatureLZCNT Feature = 5*32 + 5 - X86FeaturePREFETCHW Feature = 5*32 + 8 + 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_L2 + X86FeatureMWAITX + // ECX[31:30] are reserved. ) // Block 6 constants are the extended feature bits in -// CPUID.(EAX=0x80000001):EDX. These are very sparse, and so the bit positions -// are assigned manually. +// CPUID.(EAX=0x80000001):EDX. +// +// These are sparse, and so the bit positions are assigned manually. const ( - X86FeatureSYSCALL Feature = 6*32 + 11 - X86FeatureNX Feature = 6*32 + 20 - X86FeatureGBPAGES Feature = 6*32 + 26 - X86FeatureRDTSCP Feature = 6*32 + 27 - X86FeatureLM Feature = 6*32 + 29 - // These are not in the most recent intel manual. Not surprising... It - // shouldn't matter but we should find where these bits come from and - // support them. The linux strings are below for completeness. - //X86FeatureMMXEXT - //X86FeatureMP - //X86FeatureFXSR_OPT - //X86Feature3DNOWEXT - //X86Feature3DNOW - //X86FeatureMMXEXT: "mmxext", - //X86FeatureMP: "mp", - //X86FeatureFXSR_OPT: "fxsr_opt", - //X86Feature3DNOWEXT: "3dnowext", - //X86Feature3DNOW: "3dnow", + // 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 ) // linuxBlockOrder defines the order in which linux organizes the feature @@ -224,9 +259,8 @@ const ( // block. var linuxBlockOrder = []block{1, 6, 0, 5, 2, 4, 3} -// To make emulation of /proc/cpuinfo easy down the line, these names match the -// names of the basic features in Linux defined in -// arch/x86/kernel/cpu/capflags.c. +// To make emulation of /proc/cpuinfo easy, these names match the names of the +// basic features in Linux defined in arch/x86/kernel/cpu/capflags.c. var x86FeatureStrings = map[Feature]string{ // Block 0. X86FeatureSSE3: "pni", @@ -329,16 +363,42 @@ var x86FeatureStrings = map[Feature]string{ X86FeatureXGETBV1: "xgetbv1", // Block 5. - X86FeatureLAHF64: "lahf_lm", // LAHF/SAHF in long mode - X86FeatureLZCNT: "abm", // Advanced bit manipulation - X86FeaturePREFETCHW: "3dnowprefetch", + X86FeatureLAHF64: "lahf_lm", // LAHF/SAHF in long mode + X86FeatureCMP_LEGACY: "cmp_legacy", + X86FeatureSVM: "svm", + X86FeatureEXTAPIC: "extapic", + X86FeatureCR8_LEGACY: "cr8_legacy", + X86FeatureLZCNT: "abm", // Advanced bit manipulation + X86FeatureSSE4A: "sse4a", + X86FeatureMISALIGNSSE: "misalignsse", + X86FeaturePREFETCHW: "3dnowprefetch", + X86FeatureOSVW: "osvw", + X86FeatureIBS: "ibs", + X86FeatureXOP: "xop", + X86FeatureSKINIT: "skinit", + X86FeatureWDT: "wdt", + X86FeatureLWP: "lwp", + X86FeatureFMA4: "fma4", + X86FeatureTCE: "tce", + X86FeatureTBM: "tbm", + X86FeatureTOPOLOGY: "topoext", + X86FeaturePERFCTR_CORE: "perfctr_core", + X86FeaturePERFCTR_NB: "perfctr_nb", + X86FeatureBPEXT: "bpext", + X86FeaturePERFCTR_TSC: "ptsc", + X86FeaturePERFCTR_L2: "perfctr_l2", + X86FeatureMWAITX: "mwaitx", // Block 6. - X86FeatureSYSCALL: "syscall", - X86FeatureNX: "nx", - X86FeatureGBPAGES: "pdpe1gb", - X86FeatureRDTSCP: "rdtscp", - X86FeatureLM: "lm", + X86FeatureSYSCALL: "syscall", + X86FeatureNX: "nx", + X86FeatureMMXEXT: "mmxext", + X86FeatureFXSR_OPT: "fxsr_opt", + X86FeatureGBPAGES: "pdpe1gb", + X86FeatureRDTSCP: "rdtscp", + X86FeatureLM: "lm", + X86Feature3DNOWEXT: "3dnowext", + X86Feature3DNOW: "3dnow", } // These flags are parse only---they can be used for setting / unsetting the @@ -358,44 +418,29 @@ var x86FeatureParseOnlyStrings = map[Feature]string{ X86FeatureXSAVES: "xsaves", } -// These are the default values of various FeatureSet fields. -const ( - defaultVendorID = "GenuineIntel" - - // These processor signature defaults are derived from the values - // listed in Intel AN485 for i7/Xeon processors. - defaultExtFamily uint8 = 0 - defaultExtModel uint8 = 1 - defaultType uint8 = 0 - defaultFamily uint8 = 0x06 - defaultModel uint8 = 0x0a - defaultSteppingID uint8 = 0 -) - // Just a way to wrap cpuid function numbers. type cpuidFunction uint32 -// The constants below are the lower or "standard" cpuid functions. See Intel -// AN485 for detailed information about each one. +// The constants below are the lower or "standard" cpuid functions, ordered as +// defined by the hardware. const ( - vendorID cpuidFunction = iota // Returns vendor ID and largest standard function. - featureInfo // Returns basic feature bits and processor signature. - cacheDescriptors // Returns list of cache descriptors. - serialNumber // Returns processor serial number (obsolete on new hardware). - deterministicCacheParams // Returns deterministic cache information. See AN485. - monitorMwaitParams // Returns information about monitor/mwait instructions. - powerParams // Returns information about power management and thermal sensors. - extendedFeatureInfo // Returns extended feature bits. - _ // Function 8 is reserved. - DCAParams // Returns direct cache access information. - pmcInfo // Returns information about performance monitoring features. - x2APICInfo // Returns core/logical processor topology. See AN485 for details. - _ // Function 0xc is reserved. - xSaveInfo // Returns information about extended state management. + vendorID cpuidFunction = iota // Returns vendor ID and largest standard function. + featureInfo // Returns basic feature bits and processor signature. + intelCacheDescriptors // Returns list of cache descriptors. Intel only. + intelSerialNumber // Returns processor serial number (obsolete on new hardware). Intel only. + intelDeterministicCacheParams // Returns deterministic cache information. Intel only. + monitorMwaitParams // Returns information about monitor/mwait instructions. + powerParams // Returns information about power management and thermal sensors. + extendedFeatureInfo // Returns extended feature bits. + _ // Function 0x8 is reserved. + intelDCAParams // Returns direct cache access information. Intel only. + intelPMCInfo // Returns information about performance monitoring features. Intel only. + intelX2APICInfo // Returns core/logical processor topology. Intel only. + _ // Function 0xc is reserved. + xSaveInfo // Returns information about extended state management. ) -// The "extended" functions start at 0x80000000. Intel AP-485 has information -// on these as well. +// The "extended" functions start at 0x80000000. const ( extendedFunctionInfo cpuidFunction = 0x80000000 + iota // Returns highest available extended function in eax. extendedFeatures // Returns some extended feature bits in edx and ecx. @@ -419,7 +464,10 @@ func (f Feature) String() string { if s := f.flagString(false); s != "" { return s } - return fmt.Sprintf("", f) + + block := int(f) / 32 + bit := int(f) % 32 + return fmt.Sprintf("", f, block, bit) } func (f Feature) flagString(cpuinfoOnly bool) string { @@ -597,27 +645,6 @@ func signatureSplit(v uint32) (ef, em, pt, f, m, sid uint8) { return } -// This factory function is only needed inside the package, package users -// should not be creating and using empty feature sets. -func newEmptyFeatureSet() *FeatureSet { - return newFeatureSet(make(map[Feature]bool)) -} - -// newFeatureSet creates a new FeatureSet with sensible default values and the -// provided set of features. -func newFeatureSet(s map[Feature]bool) *FeatureSet { - return &FeatureSet{ - Set: s, - VendorID: defaultVendorID, - ExtendedFamily: defaultExtFamily, - ExtendedModel: defaultExtModel, - ProcessorType: defaultType, - Family: defaultFamily, - Model: defaultModel, - SteppingID: defaultSteppingID, - } -} - // Helper to convert blockwise feature bit masks into a set of features. Masks // must be provided in order for each block, without skipping them. If a block // does not matter for this feature set, 0 is specified. @@ -726,6 +753,10 @@ func (fs *FeatureSet) EmulateID(origAx, origCx uint32) (ax, bx, cx, dx uint32) { case extendedFeatures: cx = fs.blockMask(block(5)) dx = fs.blockMask(block(6)) + if fs.VendorID == "AuthenticAMD" { + // AMD duplicates some block 1 features in block 6. + dx |= fs.blockMask(block(1)) & block6DuplicateMask + } } return @@ -779,7 +810,9 @@ func HostFeatureSet() *FeatureSet { // eax=0x80000001 gets AMD added feature bits. _, _, cx, dx = HostID(uint32(extendedFeatures), 0) featureBlock5 = cx - featureBlock6 = dx + // Ignore features duplicated from block 1 on AMD. These bits + // are reserved on Intel. + featureBlock6 = dx &^ block6DuplicateMask } set := setFromBlockMasks(featureBlock0, featureBlock1, featureBlock2, featureBlock3, featureBlock4, featureBlock5, featureBlock6) @@ -810,7 +843,7 @@ func initCPUFreq() { // 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 innacurate. This is fine on machines with + // 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") { diff --git a/pkg/cpuid/cpuid_test.go b/pkg/cpuid/cpuid_test.go index 0decd8f08..35e7b8e50 100644 --- a/pkg/cpuid/cpuid_test.go +++ b/pkg/cpuid/cpuid_test.go @@ -18,6 +18,34 @@ 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, -- cgit v1.2.3