// 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) case 7: out := s.Query(In{Eax: uint32(extendedFeatureInfo)}) if on { out.Edx |= f.bit() } else { out.Edx &^= f.bit() } s.Set(In{Eax: uint32(extendedFeatureInfo)}, 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 case 7: _, _, _, dx := fs.query(extendedFeatureInfo) return (dx & f.bit()) != 0 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 ) // Block 7 constants are the extended features bits in // CPUID.(EAX=07H,ECX=0):EDX. const ( _ Feature = 7*32 + iota // edx bit 0 is reserved. _ // edx bit 1 is reserved. X86FeatureAVX512_4VNNIW X86FeatureAVX512_4FMAPS X86FeatureFSRM _ // edx bit 5 is not used in Linux. _ // edx bit 6 is reserved. _ // edx bit 7 is reserved. X86FeatureAVX512_VP2INTERSECT X86FeatureSRBDS_CTRL X86FeatureMD_CLEAR X86FeatureRTM_ALWAYS_ABORT _ // edx bit 12 is reserved. X86FeatureTSX_FORCE_ABORT X86FeatureSERIALIZE X86FeatureHYBRID_CPU X86FeatureTSXLDTRK _ // edx bit 17 is reserved. X86FeaturePCONFIG X86FeatureARCH_LBR X86FeatureIBT _ // edx bit 21 is reserved. X86FeatureAMX_BF16 X86FeatureAVX512_FP16 X86FeatureAMX_TILE X86FeatureAMX_INT8 X86FeatureSPEC_CTRL X86FeatureINTEL_STIBP X86FeatureFLUSH_L1D X86FeatureARCH_CAPABILITIES X86FeatureCORE_CAPABILITIES X86FeatureSPEC_CTRL_SSBD ) // 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}, // Block 7. X86FeatureAVX512_4VNNIW: {"avx512_4vnniw", true}, X86FeatureAVX512_4FMAPS: {"avx512_4fmaps", true}, X86FeatureFSRM: {"fsrm", true}, X86FeatureAVX512_VP2INTERSECT: {"avx512_vp2intersect", true}, X86FeatureSRBDS_CTRL: {"srbds_ctrl", false}, X86FeatureMD_CLEAR: {"md_clear", true}, X86FeatureRTM_ALWAYS_ABORT: {"rtm_always_abort", false}, X86FeatureTSX_FORCE_ABORT: {"tsx_force_abort", false}, X86FeatureSERIALIZE: {"serialize", true}, X86FeatureHYBRID_CPU: {"hybrid_cpu", false}, X86FeatureTSXLDTRK: {"tsxldtrk", true}, X86FeaturePCONFIG: {"pconfig", true}, X86FeatureARCH_LBR: {"arch_lbr", true}, X86FeatureIBT: {"ibt", true}, X86FeatureAMX_BF16: {"amx_bf16", true}, X86FeatureAVX512_FP16: {"avx512_fp16", true}, X86FeatureAMX_TILE: {"amx_tile", true}, X86FeatureAMX_INT8: {"amx_int8", true}, X86FeatureSPEC_CTRL: {"spec_ctrl", false}, X86FeatureINTEL_STIBP: {"intel_stibp", false}, X86FeatureFLUSH_L1D: {"flush_l1d", true}, X86FeatureARCH_CAPABILITIES: {"arch_capabilities", true}, X86FeatureCORE_CAPABILITIES: {"core_capabilities", false}, X86FeatureSPEC_CTRL_SSBD: {"spec_ctrl_ssbd", false}, } // 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, 7} 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) } } } }