cpuid_amd64.go raw

   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