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