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