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. Several fields are simply made up.
312 func (fs FeatureSet) WriteCPUInfoTo(cpu, numCPU 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 // Pretend the CPU has 8192 KB of cache. Note that real /proc/cpuinfo exposes total L3 cache
326 // size on Intel and per-core L2 cache size on AMD (as of Linux 6.1.0), so the value of this
327 // field is not really important in practice. Any value that is chosen here will be wrong
328 // by an order of magnitude on a significant chunk of x86 machines.
329 // 8192 KB is selected because it is a reasonable size that will be effectively usable on
330 // lightly loaded machines - most machines have 1-4MB of L3 cache per core.
331 fmt.Fprintf(w, "cache size\t: 8192 KB\n")
332 fmt.Fprintf(w, "physical id\t: 0\n") // Pretend all CPUs are in the same socket.
333 fmt.Fprintf(w, "siblings\t: %d\n", numCPU)
334 fmt.Fprintf(w, "core id\t\t: %d\n", cpu)
335 fmt.Fprintf(w, "cpu cores\t: %d\n", numCPU) // Pretend each CPU is a distinct core (rather than a hyperthread).
336 fmt.Fprintf(w, "apicid\t\t: %d\n", cpu)
337 fmt.Fprintf(w, "initial apicid\t: %d\n", cpu)
338 fmt.Fprintf(w, "fpu\t\t: yes\n")
339 fmt.Fprintf(w, "fpu_exception\t: yes\n")
340 fmt.Fprintf(w, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID.
341 fmt.Fprintf(w, "wp\t\t: yes\n")
342 fmt.Fprintf(w, "flags\t\t: %s\n", fs.FlagString())
343 fmt.Fprintf(w, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
344 fmt.Fprintf(w, "clflush size\t: %d\n", fs.CacheLine())
345 fmt.Fprintf(w, "cache_alignment\t: %d\n", fs.CacheLine())
346 fmt.Fprintf(w, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48)
347 fmt.Fprintf(w, "power management:\n") // This is always here, but can be blank.
348 fmt.Fprintf(w, "\n") // The /proc/cpuinfo file ends with an extra newline.
349 }
350 351 var (
352 authenticAMD = [12]byte{'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D'}
353 genuineIntel = [12]byte{'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l'}
354 )
355 356 // AMD returns true if fs describes an AMD CPU.
357 //
358 //go:nosplit
359 func (fs FeatureSet) AMD() bool {
360 return fs.VendorID() == authenticAMD
361 }
362 363 // Intel returns true if fs describes an Intel CPU.
364 //
365 //go:nosplit
366 func (fs FeatureSet) Intel() bool {
367 return fs.VendorID() == genuineIntel
368 }
369 370 // Leaf 0 of xsaveinfo function returns the size for currently
371 // enabled xsave features in ebx, the maximum size if all valid
372 // features are saved with xsave in ecx, and valid XCR0 bits in
373 // edx:eax.
374 //
375 // If xSaveInfo isn't supported, cpuid will not fault but will
376 // return bogus values.
377 var (
378 xsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ebx
379 maxXsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ecx
380 amxTileCfgSize = native(In{Eax: uint32(xSaveInfo), Ecx: 17}).Eax
381 amxTileDataSize = native(In{Eax: uint32(xSaveInfo), Ecx: 18}).Eax
382 )
383 384 const (
385 // XCR0AMXMask are the bits that enable xsave to operate on AMX TILECFG
386 // and TILEDATA.
387 //
388 // Note: TILECFG and TILEDATA are always either both enabled or both
389 // disabled.
390 //
391 // See Intel® 64 and IA-32 Architectures Software Developer’s Manual Vol.1
392 // section 13.3 for details.
393 XCR0AMXMask = uint64((1 << 17) | (1 << 18))
394 )
395 396 // ExtendedStateSize returns the number of bytes needed to save the "extended
397 // state" for the enabled features and the boundary it must be aligned to.
398 // Extended state includes floating point registers, and other cpu state that's
399 // not associated with the normal task context.
400 //
401 // Note: the return value matches the size of signal FP state frames.
402 // Look at check_xstate_in_sigframe() in the kernel sources for more details.
403 //
404 //go:nosplit
405 func (fs FeatureSet) ExtendedStateSize() (size, align uint) {
406 if fs.UseXsave() {
407 return uint(xsaveSize), 64
408 }
409 410 // If we don't support xsave, we fall back to fxsave, which requires
411 // 512 bytes aligned to 16 bytes.
412 return 512, 16
413 }
414 415 // AMXExtendedStateSize returns the number of bytes within the "extended state"
416 // area that is used for AMX.
417 func (fs FeatureSet) AMXExtendedStateSize() uint {
418 if fs.UseXsave() {
419 xcr0 := xgetbv(0)
420 if (xcr0 & XCR0AMXMask) != 0 {
421 return uint(amxTileCfgSize + amxTileDataSize)
422 }
423 }
424 return 0
425 }
426 427 // ValidXCR0Mask returns the valid bits in control register XCR0.
428 //
429 // Always exclude AMX bits, because we do not support it.
430 // TODO(gvisor.dev/issues/9896): Implement AMX Support.
431 //
432 //go:nosplit
433 func (fs FeatureSet) ValidXCR0Mask() uint64 {
434 if !fs.HasFeature(X86FeatureXSAVE) {
435 return 0
436 }
437 ax, _, _, dx := fs.query(xSaveInfo)
438 return (uint64(dx)<<32 | uint64(ax)) &^ XCR0AMXMask
439 }
440 441 // UseXsave returns the choice of fp state saving instruction.
442 //
443 //go:nosplit
444 func (fs FeatureSet) UseXsave() bool {
445 return fs.HasFeature(X86FeatureXSAVE) && fs.HasFeature(X86FeatureOSXSAVE)
446 }
447 448 // UseXsaveopt returns true if 'fs' supports the "xsaveopt" instruction.
449 //
450 //go:nosplit
451 func (fs FeatureSet) UseXsaveopt() bool {
452 return fs.UseXsave() && fs.HasFeature(X86FeatureXSAVEOPT)
453 }
454 455 // UseXsavec returns true if 'fs' supports the "xsavec" instruction.
456 //
457 //go:nosplit
458 func (fs FeatureSet) UseXsavec() bool {
459 return fs.UseXsaveopt() && fs.HasFeature(X86FeatureXSAVEC)
460 }
461 462 // UseFSGSBASE returns true if 'fs' supports the (RD|WR)(FS|GS)BASE instructions.
463 func (fs FeatureSet) UseFSGSBASE() bool {
464 HWCAP2_FSGSBASE := uint64(1) << 1
465 return fs.HasFeature(X86FeatureFSGSBase) && ((fs.hwCap.hwCap2 & HWCAP2_FSGSBASE) != 0)
466 }
467 468 // archCheckHostCompatible checks for compatibility.
469 func (fs FeatureSet) archCheckHostCompatible(hfs FeatureSet) error {
470 // The size of a cache line must match, as it is critical to correctly
471 // utilizing CLFLUSH. Other cache properties are allowed to change, as
472 // they are not important to correctness.
473 fsCache := fs.CacheLine()
474 hostCache := hfs.CacheLine()
475 if fsCache != hostCache {
476 return &ErrIncompatible{
477 reason: fmt.Sprintf("CPU cache line size %d incompatible with host cache line size %d", fsCache, hostCache),
478 }
479 }
480 481 return nil
482 }
483 484 // AllowedHWCap1 returns the HWCAP1 bits that the guest is allowed to depend
485 // on.
486 func (fs FeatureSet) AllowedHWCap1() uint64 {
487 // HWCAPS are not supported on amd64.
488 return 0
489 }
490 491 // AllowedHWCap2 returns the HWCAP2 bits that the guest is allowed to depend
492 // on.
493 func (fs FeatureSet) AllowedHWCap2() uint64 {
494 // HWCAPS are not supported on amd64.
495 return 0
496 }
497