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