github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/cpuid/cpuid_amd64.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build amd64 16 // +build amd64 17 18 package cpuid 19 20 import ( 21 "fmt" 22 "io" 23 ) 24 25 // FeatureSet defines features in terms of CPUID leaves and bits. 26 // The kernel also exposes the presence of features to userspace through 27 // a set of flags(HWCAP/HWCAP2) bits, exposed in the auxiliary vector, which 28 // are necessary to read for some features (e.g. FSGSBASE). 29 // 30 // Common references: 31 // 32 // Intel: 33 // - Intel SDM Volume 2, Chapter 3.2 "CPUID" (more up-to-date) 34 // - Intel Application Note 485 (more detailed) 35 // 36 // AMD: 37 // - AMD64 APM Volume 3, Appendix 3 "Obtaining Processor Information ..." 38 // 39 // +stateify savable 40 type FeatureSet struct { 41 // Function is the underlying CPUID Function. 42 // 43 // This is exported to allow direct calls of the underlying CPUID 44 // function, where required. 45 Function `state:".(Static)"` 46 // hwCap stores HWCAP1/2 exposed from the elf auxiliary vector. 47 hwCap hwCap 48 } 49 50 // saveFunction saves the function as a static query. 51 func (fs *FeatureSet) saveFunction() Static { 52 if s, ok := fs.Function.(Static); ok { 53 return s 54 } 55 return fs.ToStatic() 56 } 57 58 // loadFunction saves the function as a static query. 59 func (fs *FeatureSet) loadFunction(s Static) { 60 fs.Function = s 61 } 62 63 // Helper to convert 3 regs into 12-byte vendor ID. 64 // 65 //go:nosplit 66 func vendorIDFromRegs(bx, cx, dx uint32) (r [12]byte) { 67 for i := uint(0); i < 4; i++ { 68 b := byte(bx >> (i * 8)) 69 r[i] = b 70 } 71 72 for i := uint(0); i < 4; i++ { 73 b := byte(dx >> (i * 8)) 74 r[4+i] = b 75 } 76 77 for i := uint(0); i < 4; i++ { 78 b := byte(cx >> (i * 8)) 79 r[8+i] = b 80 } 81 82 return r 83 } 84 85 // Helper to merge a 12-byte vendor ID back to registers. 86 // 87 // Used by static_amd64.go. 88 func regsFromVendorID(r [12]byte) (bx, cx, dx uint32) { 89 bx |= uint32(r[0]) 90 bx |= uint32(r[1]) << 8 91 bx |= uint32(r[2]) << 16 92 bx |= uint32(r[3]) << 24 93 cx |= uint32(r[4]) 94 cx |= uint32(r[5]) << 8 95 cx |= uint32(r[6]) << 16 96 cx |= uint32(r[7]) << 24 97 dx |= uint32(r[8]) 98 dx |= uint32(r[9]) << 8 99 dx |= uint32(r[10]) << 16 100 dx |= uint32(r[10]) << 24 101 return 102 } 103 104 // VendorID is the 12-char string returned in ebx:edx:ecx for eax=0. 105 // 106 //go:nosplit 107 func (fs FeatureSet) VendorID() [12]byte { 108 _, bx, cx, dx := fs.query(vendorID) 109 return vendorIDFromRegs(bx, cx, dx) 110 } 111 112 // Helper to deconstruct signature dword. 113 // 114 //go:nosplit 115 func signatureSplit(v uint32) (ef, em, pt, f, m, sid uint8) { 116 sid = uint8(v & 0xf) 117 m = uint8(v>>4) & 0xf 118 f = uint8(v>>8) & 0xf 119 pt = uint8(v>>12) & 0x3 120 em = uint8(v>>16) & 0xf 121 ef = uint8(v >> 20) 122 return 123 } 124 125 // ExtendedFamily is part of the processor signature. 126 // 127 //go:nosplit 128 func (fs FeatureSet) ExtendedFamily() uint8 { 129 ax, _, _, _ := fs.query(featureInfo) 130 ef, _, _, _, _, _ := signatureSplit(ax) 131 return ef 132 } 133 134 // ExtendedModel is part of the processor signature. 135 // 136 //go:nosplit 137 func (fs FeatureSet) ExtendedModel() uint8 { 138 ax, _, _, _ := fs.query(featureInfo) 139 _, em, _, _, _, _ := signatureSplit(ax) 140 return em 141 } 142 143 // ProcessorType is part of the processor signature. 144 // 145 //go:nosplit 146 func (fs FeatureSet) ProcessorType() uint8 { 147 ax, _, _, _ := fs.query(featureInfo) 148 _, _, pt, _, _, _ := signatureSplit(ax) 149 return pt 150 } 151 152 // Family is part of the processor signature. 153 // 154 //go:nosplit 155 func (fs FeatureSet) Family() uint8 { 156 ax, _, _, _ := fs.query(featureInfo) 157 _, _, _, f, _, _ := signatureSplit(ax) 158 return f 159 } 160 161 // Model is part of the processor signature. 162 // 163 //go:nosplit 164 func (fs FeatureSet) Model() uint8 { 165 ax, _, _, _ := fs.query(featureInfo) 166 _, _, _, _, m, _ := signatureSplit(ax) 167 return m 168 } 169 170 // SteppingID is part of the processor signature. 171 // 172 //go:nosplit 173 func (fs FeatureSet) SteppingID() uint8 { 174 ax, _, _, _ := fs.query(featureInfo) 175 _, _, _, _, _, sid := signatureSplit(ax) 176 return sid 177 } 178 179 // VirtualAddressBits returns the number of bits available for virtual 180 // addresses. 181 // 182 //go:nosplit 183 func (fs FeatureSet) VirtualAddressBits() uint32 { 184 ax, _, _, _ := fs.query(addressSizes) 185 return (ax >> 8) & 0xff 186 } 187 188 // PhysicalAddressBits returns the number of bits available for physical 189 // addresses. 190 // 191 //go:nosplit 192 func (fs FeatureSet) PhysicalAddressBits() uint32 { 193 ax, _, _, _ := fs.query(addressSizes) 194 return ax & 0xff 195 } 196 197 // CacheType describes the type of a cache, as returned in eax[4:0] for eax=4. 198 type CacheType uint8 199 200 const ( 201 // cacheNull indicates that there are no more entries. 202 cacheNull CacheType = iota 203 204 // CacheData is a data cache. 205 CacheData 206 207 // CacheInstruction is an instruction cache. 208 CacheInstruction 209 210 // CacheUnified is a unified instruction and data cache. 211 CacheUnified 212 ) 213 214 // Cache describes the parameters of a single cache on the system. 215 // 216 // This is returned by the Caches method on FeatureSet. 217 type Cache struct { 218 // Level is the hierarchical level of this cache (L1, L2, etc). 219 Level uint32 220 221 // Type is the type of cache. 222 Type CacheType 223 224 // FullyAssociative indicates that entries may be placed in any block. 225 FullyAssociative bool 226 227 // Partitions is the number of physical partitions in the cache. 228 Partitions uint32 229 230 // Ways is the number of ways of associativity in the cache. 231 Ways uint32 232 233 // Sets is the number of sets in the cache. 234 Sets uint32 235 236 // InvalidateHierarchical indicates that WBINVD/INVD from threads 237 // sharing this cache acts upon lower level caches for threads sharing 238 // this cache. 239 InvalidateHierarchical bool 240 241 // Inclusive indicates that this cache is inclusive of lower cache 242 // levels. 243 Inclusive bool 244 245 // DirectMapped indicates that this cache is directly mapped from 246 // address, rather than using a hash function. 247 DirectMapped bool 248 } 249 250 // Caches describes the caches on the CPU. 251 // 252 // Only supported on Intel; requires allocation. 253 func (fs FeatureSet) Caches() (caches []Cache) { 254 if !fs.Intel() { 255 return 256 } 257 // Check against the cache line, which should be consistent. 258 cacheLine := fs.CacheLine() 259 for i := uint32(0); ; i++ { 260 out := fs.Query(In{ 261 Eax: uint32(intelDeterministicCacheParams), 262 Ecx: i, 263 }) 264 t := CacheType(out.Eax & 0xf) 265 if t == cacheNull { 266 break 267 } 268 lineSize := (out.Ebx & 0xfff) + 1 269 if lineSize != cacheLine { 270 panic(fmt.Sprintf("Mismatched cache line size: %d vs %d", lineSize, cacheLine)) 271 } 272 caches = append(caches, Cache{ 273 Type: t, 274 Level: (out.Eax >> 5) & 0x7, 275 FullyAssociative: ((out.Eax >> 9) & 1) == 1, 276 Partitions: ((out.Ebx >> 12) & 0x3ff) + 1, 277 Ways: ((out.Ebx >> 22) & 0x3ff) + 1, 278 Sets: out.Ecx + 1, 279 InvalidateHierarchical: (out.Edx & 1) == 0, 280 Inclusive: ((out.Edx >> 1) & 1) == 1, 281 DirectMapped: ((out.Edx >> 2) & 1) == 0, 282 }) 283 } 284 return 285 } 286 287 // CacheLine is the size of a cache line in bytes. 288 // 289 // All caches use the same line size. This is not enforced in the CPUID 290 // encoding, but is true on all known x86 processors. 291 // 292 //go:nosplit 293 func (fs FeatureSet) CacheLine() uint32 { 294 _, bx, _, _ := fs.query(featureInfo) 295 return 8 * (bx >> 8) & 0xff 296 } 297 298 // HasFeature tests whether or not a feature is in the given feature set. 299 // 300 // This function is safe to call from a nosplit context, as long as the 301 // FeatureSet does not have any masked features. 302 // 303 //go:nosplit 304 func (fs FeatureSet) HasFeature(feature Feature) bool { 305 return feature.check(fs) 306 } 307 308 // WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is 309 // a minimal /proc/cpuinfo, it is missing some fields like "microcode" that are 310 // not always printed in Linux. The bogomips field is simply made up. 311 func (fs FeatureSet) WriteCPUInfoTo(cpu uint, w io.Writer) { 312 // Avoid many redunant calls here, since this can occasionally appear 313 // in the hot path. Read all basic information up front, see above. 314 ax, _, _, _ := fs.query(featureInfo) 315 ef, em, _, f, m, _ := signatureSplit(ax) 316 vendor := fs.VendorID() 317 fmt.Fprintf(w, "processor\t: %d\n", cpu) 318 fmt.Fprintf(w, "vendor_id\t: %s\n", string(vendor[:])) 319 fmt.Fprintf(w, "cpu family\t: %d\n", ((ef<<4)&0xff)|f) 320 fmt.Fprintf(w, "model\t\t: %d\n", ((em<<4)&0xff)|m) 321 fmt.Fprintf(w, "model name\t: %s\n", "unknown") // Unknown for now. 322 fmt.Fprintf(w, "stepping\t: %s\n", "unknown") // Unknown for now. 323 fmt.Fprintf(w, "cpu MHz\t\t: %.3f\n", cpuFreqMHz) 324 fmt.Fprintf(w, "fpu\t\t: yes\n") 325 fmt.Fprintf(w, "fpu_exception\t: yes\n") 326 fmt.Fprintf(w, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID. 327 fmt.Fprintf(w, "wp\t\t: yes\n") 328 fmt.Fprintf(w, "flags\t\t: %s\n", fs.FlagString()) 329 fmt.Fprintf(w, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway. 330 fmt.Fprintf(w, "clflush size\t: %d\n", fs.CacheLine()) 331 fmt.Fprintf(w, "cache_alignment\t: %d\n", fs.CacheLine()) 332 fmt.Fprintf(w, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48) 333 fmt.Fprintf(w, "power management:\n") // This is always here, but can be blank. 334 fmt.Fprintf(w, "\n") // The /proc/cpuinfo file ends with an extra newline. 335 } 336 337 var ( 338 authenticAMD = [12]byte{'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D'} 339 genuineIntel = [12]byte{'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l'} 340 ) 341 342 // AMD returns true if fs describes an AMD CPU. 343 // 344 //go:nosplit 345 func (fs FeatureSet) AMD() bool { 346 return fs.VendorID() == authenticAMD 347 } 348 349 // Intel returns true if fs describes an Intel CPU. 350 // 351 //go:nosplit 352 func (fs FeatureSet) Intel() bool { 353 return fs.VendorID() == genuineIntel 354 } 355 356 // Leaf 0 of xsaveinfo function returns the size for currently 357 // enabled xsave features in ebx, the maximum size if all valid 358 // features are saved with xsave in ecx, and valid XCR0 bits in 359 // edx:eax. 360 // 361 // If xSaveInfo isn't supported, cpuid will not fault but will 362 // return bogus values. 363 var ( 364 xsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ebx 365 maxXsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ecx 366 ) 367 368 // ExtendedStateSize returns the number of bytes needed to save the "extended 369 // state" for the enabled features and the boundary it must be aligned to. 370 // Extended state includes floating point registers, and other cpu state that's 371 // not associated with the normal task context. 372 // 373 // Note: the return value matches the size of signal FP state frames. 374 // Look at check_xstate_in_sigframe() in the kernel sources for more details. 375 // 376 //go:nosplit 377 func (fs FeatureSet) ExtendedStateSize() (size, align uint) { 378 if fs.UseXsave() { 379 return uint(xsaveSize), 64 380 } 381 382 // If we don't support xsave, we fall back to fxsave, which requires 383 // 512 bytes aligned to 16 bytes. 384 return 512, 16 385 } 386 387 // ValidXCR0Mask returns the valid bits in control register XCR0. 388 // 389 //go:nosplit 390 func (fs FeatureSet) ValidXCR0Mask() uint64 { 391 if !fs.HasFeature(X86FeatureXSAVE) { 392 return 0 393 } 394 ax, _, _, dx := fs.query(xSaveInfo) 395 return uint64(dx)<<32 | uint64(ax) 396 } 397 398 // UseXsave returns the choice of fp state saving instruction. 399 // 400 //go:nosplit 401 func (fs FeatureSet) UseXsave() bool { 402 return fs.HasFeature(X86FeatureXSAVE) && fs.HasFeature(X86FeatureOSXSAVE) 403 } 404 405 // UseXsaveopt returns true if 'fs' supports the "xsaveopt" instruction. 406 // 407 //go:nosplit 408 func (fs FeatureSet) UseXsaveopt() bool { 409 return fs.UseXsave() && fs.HasFeature(X86FeatureXSAVEOPT) 410 } 411 412 // UseXsavec returns true if 'fs' supports the "xsavec" instruction. 413 // 414 //go:nosplit 415 func (fs FeatureSet) UseXsavec() bool { 416 return fs.UseXsaveopt() && fs.HasFeature(X86FeatureXSAVEC) 417 } 418 419 // UseFSGSBASE returns true if 'fs' supports the (RD|WR)(FS|GS)BASE instructions. 420 func (fs FeatureSet) UseFSGSBASE() bool { 421 HWCAP2_FSGSBASE := uint64(1) << 1 422 return fs.HasFeature(X86FeatureFSGSBase) && ((fs.hwCap.hwCap2 & HWCAP2_FSGSBASE) != 0) 423 } 424 425 // archCheckHostCompatible checks for compatibility. 426 func (fs FeatureSet) archCheckHostCompatible(hfs FeatureSet) error { 427 // The size of a cache line must match, as it is critical to correctly 428 // utilizing CLFLUSH. Other cache properties are allowed to change, as 429 // they are not important to correctness. 430 fsCache := fs.CacheLine() 431 hostCache := hfs.CacheLine() 432 if fsCache != hostCache { 433 return &ErrIncompatible{ 434 reason: fmt.Sprintf("CPU cache line size %d incompatible with host cache line size %d", fsCache, hostCache), 435 } 436 } 437 438 return nil 439 }