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  }