github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/cpuid/cpuid_arm64.go (about)

     1  // Copyright 2020 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  // +build arm64
    16  
    17  package cpuid
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/SagerNet/gvisor/pkg/log"
    28  )
    29  
    30  // ARM64 doesn't have a 'cpuid' equivalent, which means it have no architected
    31  // discovery mechanism for hardware features available to userspace code at EL0.
    32  // The kernel exposes the presence of these features to userspace through a set
    33  // of flags(HWCAP/HWCAP2) bits, exposed in the auxilliary vector.
    34  // Ref Documentation/arm64/elf_hwcaps.rst for more info.
    35  //
    36  // Currently, only the HWCAP bits are supported.
    37  
    38  const (
    39  	// ARM64FeatureFP indicates support for single and double precision
    40  	// float point types.
    41  	ARM64FeatureFP Feature = iota
    42  
    43  	// ARM64FeatureASIMD indicates support for Advanced SIMD with single
    44  	// and double precision float point arithmetic.
    45  	ARM64FeatureASIMD
    46  
    47  	// ARM64FeatureEVTSTRM indicates support for the generic timer
    48  	// configured to generate events at a frequency of approximately
    49  	// 100KHz.
    50  	ARM64FeatureEVTSTRM
    51  
    52  	// ARM64FeatureAES indicates support for AES instructions
    53  	// (AESE/AESD/AESMC/AESIMC).
    54  	ARM64FeatureAES
    55  
    56  	// ARM64FeaturePMULL indicates support for AES instructions
    57  	// (PMULL/PMULL2).
    58  	ARM64FeaturePMULL
    59  
    60  	// ARM64FeatureSHA1 indicates support for SHA1 instructions
    61  	// (SHA1C/SHA1P/SHA1M etc).
    62  	ARM64FeatureSHA1
    63  
    64  	// ARM64FeatureSHA2 indicates support for SHA2 instructions
    65  	// (SHA256H/SHA256H2/SHA256SU0 etc).
    66  	ARM64FeatureSHA2
    67  
    68  	// ARM64FeatureCRC32 indicates support for CRC32 instructions
    69  	// (CRC32B/CRC32H/CRC32W etc).
    70  	ARM64FeatureCRC32
    71  
    72  	// ARM64FeatureATOMICS indicates support for atomic instructions
    73  	// (LDADD/LDCLR/LDEOR/LDSET etc).
    74  	ARM64FeatureATOMICS
    75  
    76  	// ARM64FeatureFPHP indicates support for half precision float point
    77  	// arithmetic.
    78  	ARM64FeatureFPHP
    79  
    80  	// ARM64FeatureASIMDHP indicates support for ASIMD with half precision
    81  	// float point arithmetic.
    82  	ARM64FeatureASIMDHP
    83  
    84  	// ARM64FeatureCPUID indicates support for EL0 access to certain ID
    85  	// registers is available.
    86  	ARM64FeatureCPUID
    87  
    88  	// ARM64FeatureASIMDRDM indicates support for SQRDMLAH and SQRDMLSH
    89  	// instructions.
    90  	ARM64FeatureASIMDRDM
    91  
    92  	// ARM64FeatureJSCVT indicates support for the FJCVTZS instruction.
    93  	ARM64FeatureJSCVT
    94  
    95  	// ARM64FeatureFCMA indicates support for the FCMLA and FCADD
    96  	// instructions.
    97  	ARM64FeatureFCMA
    98  
    99  	// ARM64FeatureLRCPC indicates support for the LDAPRB/LDAPRH/LDAPR
   100  	// instructions.
   101  	ARM64FeatureLRCPC
   102  
   103  	// ARM64FeatureDCPOP indicates support for DC instruction (DC CVAP).
   104  	ARM64FeatureDCPOP
   105  
   106  	// ARM64FeatureSHA3 indicates support for SHA3 instructions
   107  	// (EOR3/RAX1/XAR/BCAX).
   108  	ARM64FeatureSHA3
   109  
   110  	// ARM64FeatureSM3 indicates support for SM3 instructions
   111  	// (SM3SS1/SM3TT1A/SM3TT1B).
   112  	ARM64FeatureSM3
   113  
   114  	// ARM64FeatureSM4 indicates support for SM4 instructions
   115  	// (SM4E/SM4EKEY).
   116  	ARM64FeatureSM4
   117  
   118  	// ARM64FeatureASIMDDP indicates support for dot product instructions
   119  	// (UDOT/SDOT).
   120  	ARM64FeatureASIMDDP
   121  
   122  	// ARM64FeatureSHA512 indicates support for SHA2 instructions
   123  	// (SHA512H/SHA512H2/SHA512SU0).
   124  	ARM64FeatureSHA512
   125  
   126  	// ARM64FeatureSVE indicates support for Scalable Vector Extension.
   127  	ARM64FeatureSVE
   128  
   129  	// ARM64FeatureASIMDFHM indicates support for FMLAL and FMLSL
   130  	// instructions.
   131  	ARM64FeatureASIMDFHM
   132  )
   133  
   134  // ELF auxiliary vector tags
   135  const (
   136  	_AT_NULL   = 0  // End of vector
   137  	_AT_HWCAP  = 16 // hardware capability bit vector
   138  	_AT_HWCAP2 = 26 // hardware capability bit vector 2
   139  )
   140  
   141  // These should not be changed after they are initialized.
   142  var hwCap uint
   143  
   144  // To make emulation of /proc/cpuinfo easy, these names match the names of the
   145  // basic features in Linux defined in arch/arm64/kernel/cpuinfo.c.
   146  var arm64FeatureStrings = map[Feature]string{
   147  	ARM64FeatureFP:       "fp",
   148  	ARM64FeatureASIMD:    "asimd",
   149  	ARM64FeatureEVTSTRM:  "evtstrm",
   150  	ARM64FeatureAES:      "aes",
   151  	ARM64FeaturePMULL:    "pmull",
   152  	ARM64FeatureSHA1:     "sha1",
   153  	ARM64FeatureSHA2:     "sha2",
   154  	ARM64FeatureCRC32:    "crc32",
   155  	ARM64FeatureATOMICS:  "atomics",
   156  	ARM64FeatureFPHP:     "fphp",
   157  	ARM64FeatureASIMDHP:  "asimdhp",
   158  	ARM64FeatureCPUID:    "cpuid",
   159  	ARM64FeatureASIMDRDM: "asimdrdm",
   160  	ARM64FeatureJSCVT:    "jscvt",
   161  	ARM64FeatureFCMA:     "fcma",
   162  	ARM64FeatureLRCPC:    "lrcpc",
   163  	ARM64FeatureDCPOP:    "dcpop",
   164  	ARM64FeatureSHA3:     "sha3",
   165  	ARM64FeatureSM3:      "sm3",
   166  	ARM64FeatureSM4:      "sm4",
   167  	ARM64FeatureASIMDDP:  "asimddp",
   168  	ARM64FeatureSHA512:   "sha512",
   169  	ARM64FeatureSVE:      "sve",
   170  	ARM64FeatureASIMDFHM: "asimdfhm",
   171  }
   172  
   173  var (
   174  	cpuFreqMHz float64
   175  	cpuImplHex uint64
   176  	cpuArchDec uint64
   177  	cpuVarHex  uint64
   178  	cpuPartHex uint64
   179  	cpuRevDec  uint64
   180  )
   181  
   182  // arm64FeaturesFromString includes features from arm64FeatureStrings.
   183  var arm64FeaturesFromString = make(map[string]Feature)
   184  
   185  // FeatureFromString returns the Feature associated with the given feature
   186  // string plus a bool to indicate if it could find the feature.
   187  func FeatureFromString(s string) (Feature, bool) {
   188  	f, b := arm64FeaturesFromString[s]
   189  	return f, b
   190  }
   191  
   192  // String implements fmt.Stringer.
   193  func (f Feature) String() string {
   194  	if s := f.flagString(); s != "" {
   195  		return s
   196  	}
   197  
   198  	return fmt.Sprintf("<cpuflag %d>", f)
   199  }
   200  
   201  func (f Feature) flagString() string {
   202  	if s, ok := arm64FeatureStrings[f]; ok {
   203  		return s
   204  	}
   205  
   206  	return ""
   207  }
   208  
   209  // FeatureSet is a set of Features for a CPU.
   210  //
   211  // +stateify savable
   212  type FeatureSet struct {
   213  	// Set is the set of features that are enabled in this FeatureSet.
   214  	Set map[Feature]bool
   215  
   216  	// CPUImplementer is part of the processor signature.
   217  	CPUImplementer uint8
   218  
   219  	// CPUArchitecture is part of the processor signature.
   220  	CPUArchitecture uint8
   221  
   222  	// CPUVariant is part of the processor signature.
   223  	CPUVariant uint8
   224  
   225  	// CPUPartnum is part of the processor signature.
   226  	CPUPartnum uint16
   227  
   228  	// CPURevision is part of the processor signature.
   229  	CPURevision uint8
   230  }
   231  
   232  // CheckHostCompatible returns nil if fs is a subset of the host feature set.
   233  // Noop on arm64.
   234  func (fs *FeatureSet) CheckHostCompatible() error {
   235  	return nil
   236  }
   237  
   238  // ExtendedStateSize returns the number of bytes needed to save the "extended
   239  // state" for this processor and the boundary it must be aligned to. Extended
   240  // state includes floating point(NEON) registers, and other cpu state that's not
   241  // associated with the normal task context.
   242  func (fs *FeatureSet) ExtendedStateSize() (size, align uint) {
   243  	// ARMv8 provide 32x128bits NEON registers.
   244  	//
   245  	// Ref arch/arm64/include/uapi/asm/ptrace.h
   246  	// struct user_fpsimd_state {
   247  	//        __uint128_t     vregs[32];
   248  	//        __u32           fpsr;
   249  	//	  __u32           fpcr;
   250  	//	  __u32           __reserved[2];
   251  	// };
   252  	return 528, 16
   253  }
   254  
   255  // HasFeature tests whether or not a feature is in the given feature set.
   256  func (fs *FeatureSet) HasFeature(feature Feature) bool {
   257  	return fs.Set[feature]
   258  }
   259  
   260  // UseXsave returns true if 'fs' supports the "xsave" instruction.
   261  //
   262  // Irrelevant on arm64.
   263  func (fs *FeatureSet) UseXsave() bool {
   264  	return false
   265  }
   266  
   267  // FlagsString prints out supported CPU "flags" field in /proc/cpuinfo.
   268  func (fs *FeatureSet) FlagsString() string {
   269  	var s []string
   270  	for f := range arm64FeatureStrings {
   271  		if fs.Set[f] {
   272  			if fstr := f.flagString(); fstr != "" {
   273  				s = append(s, fstr)
   274  			}
   275  		}
   276  	}
   277  	return strings.Join(s, " ")
   278  }
   279  
   280  // WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is
   281  // a minimal /proc/cpuinfo, and the bogomips field is simply made up.
   282  func (fs FeatureSet) WriteCPUInfoTo(cpu uint, b *bytes.Buffer) {
   283  	fmt.Fprintf(b, "processor\t: %d\n", cpu)
   284  	fmt.Fprintf(b, "BogoMIPS\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
   285  	fmt.Fprintf(b, "Features\t\t: %s\n", fs.FlagsString())
   286  	fmt.Fprintf(b, "CPU implementer\t: 0x%x\n", cpuImplHex)
   287  	fmt.Fprintf(b, "CPU architecture\t: %d\n", cpuArchDec)
   288  	fmt.Fprintf(b, "CPU variant\t: 0x%x\n", cpuVarHex)
   289  	fmt.Fprintf(b, "CPU part\t: 0x%x\n", cpuPartHex)
   290  	fmt.Fprintf(b, "CPU revision\t: %d\n", cpuRevDec)
   291  	fmt.Fprintln(b, "") // The /proc/cpuinfo file ends with an extra newline.
   292  }
   293  
   294  // HostFeatureSet uses hwCap to get host values and construct a feature set
   295  // that matches that of the host machine.
   296  func HostFeatureSet() *FeatureSet {
   297  	s := make(map[Feature]bool)
   298  
   299  	for f := range arm64FeatureStrings {
   300  		if hwCap&(1<<f) != 0 {
   301  			s[f] = true
   302  		}
   303  	}
   304  
   305  	return &FeatureSet{
   306  		Set:             s,
   307  		CPUImplementer:  uint8(cpuImplHex),
   308  		CPUArchitecture: uint8(cpuArchDec),
   309  		CPUVariant:      uint8(cpuVarHex),
   310  		CPUPartnum:      uint16(cpuPartHex),
   311  		CPURevision:     uint8(cpuRevDec),
   312  	}
   313  }
   314  
   315  // Reads bogomips from host /proc/cpuinfo. Must run before syscall filter
   316  // installation. This value is used to create the fake /proc/cpuinfo from a
   317  // FeatureSet.
   318  func initCPUInfo() {
   319  	cpuinfob, err := ioutil.ReadFile("/proc/cpuinfo")
   320  	if err != nil {
   321  		// Leave it as 0. The standalone VDSO bails out in the same way.
   322  		log.Warningf("Could not read /proc/cpuinfo: %v", err)
   323  		return
   324  	}
   325  	cpuinfo := string(cpuinfob)
   326  
   327  	// We get the value straight from host /proc/cpuinfo.
   328  	for _, line := range strings.Split(cpuinfo, "\n") {
   329  		switch {
   330  		case strings.Contains(line, "BogoMIPS"):
   331  			{
   332  				splitMHz := strings.Split(line, ":")
   333  				if len(splitMHz) < 2 {
   334  					log.Warningf("Could not read /proc/cpuinfo: malformed BogoMIPS")
   335  					break
   336  				}
   337  
   338  				// If there was a problem, leave cpuFreqMHz as 0.
   339  				var err error
   340  				cpuFreqMHz, err = strconv.ParseFloat(strings.TrimSpace(splitMHz[1]), 64)
   341  				if err != nil {
   342  					log.Warningf("Could not parse BogoMIPS value %v: %v", splitMHz[1], err)
   343  					cpuFreqMHz = 0
   344  				}
   345  			}
   346  		case strings.Contains(line, "CPU implementer"):
   347  			{
   348  				splitImpl := strings.Split(line, ":")
   349  				if len(splitImpl) < 2 {
   350  					log.Warningf("Could not read /proc/cpuinfo: malformed CPU implementer")
   351  					break
   352  				}
   353  
   354  				// If there was a problem, leave cpuImplHex as 0.
   355  				var err error
   356  				cpuImplHex, err = strconv.ParseUint(strings.TrimSpace(splitImpl[1]), 0, 64)
   357  				if err != nil {
   358  					log.Warningf("Could not parse CPU implementer value %v: %v", splitImpl[1], err)
   359  					cpuImplHex = 0
   360  				}
   361  			}
   362  		case strings.Contains(line, "CPU architecture"):
   363  			{
   364  				splitArch := strings.Split(line, ":")
   365  				if len(splitArch) < 2 {
   366  					log.Warningf("Could not read /proc/cpuinfo: malformed CPU architecture")
   367  					break
   368  				}
   369  
   370  				// If there was a problem, leave cpuArchDec as 0.
   371  				var err error
   372  				cpuArchDec, err = strconv.ParseUint(strings.TrimSpace(splitArch[1]), 0, 64)
   373  				if err != nil {
   374  					log.Warningf("Could not parse CPU architecture value %v: %v", splitArch[1], err)
   375  					cpuArchDec = 0
   376  				}
   377  			}
   378  		case strings.Contains(line, "CPU variant"):
   379  			{
   380  				splitVar := strings.Split(line, ":")
   381  				if len(splitVar) < 2 {
   382  					log.Warningf("Could not read /proc/cpuinfo: malformed CPU variant")
   383  					break
   384  				}
   385  
   386  				// If there was a problem, leave cpuVarHex as 0.
   387  				var err error
   388  				cpuVarHex, err = strconv.ParseUint(strings.TrimSpace(splitVar[1]), 0, 64)
   389  				if err != nil {
   390  					log.Warningf("Could not parse CPU variant value %v: %v", splitVar[1], err)
   391  					cpuVarHex = 0
   392  				}
   393  			}
   394  		case strings.Contains(line, "CPU part"):
   395  			{
   396  				splitPart := strings.Split(line, ":")
   397  				if len(splitPart) < 2 {
   398  					log.Warningf("Could not read /proc/cpuinfo: malformed CPU part")
   399  					break
   400  				}
   401  
   402  				// If there was a problem, leave cpuPartHex as 0.
   403  				var err error
   404  				cpuPartHex, err = strconv.ParseUint(strings.TrimSpace(splitPart[1]), 0, 64)
   405  				if err != nil {
   406  					log.Warningf("Could not parse CPU part value %v: %v", splitPart[1], err)
   407  					cpuPartHex = 0
   408  				}
   409  			}
   410  		case strings.Contains(line, "CPU revision"):
   411  			{
   412  				splitRev := strings.Split(line, ":")
   413  				if len(splitRev) < 2 {
   414  					log.Warningf("Could not read /proc/cpuinfo: malformed CPU revision")
   415  					break
   416  				}
   417  
   418  				// If there was a problem, leave cpuRevDec as 0.
   419  				var err error
   420  				cpuRevDec, err = strconv.ParseUint(strings.TrimSpace(splitRev[1]), 0, 64)
   421  				if err != nil {
   422  					log.Warningf("Could not parse CPU revision value %v: %v", splitRev[1], err)
   423  					cpuRevDec = 0
   424  				}
   425  			}
   426  		}
   427  	}
   428  }
   429  
   430  // The auxiliary vector of a process on the Linux system can be read
   431  // from /proc/self/auxv, and tags and values are stored as 8-bytes
   432  // decimal key-value pairs on the 64-bit system.
   433  //
   434  // $ od -t d8 /proc/self/auxv
   435  //  0000000                   33      140734615224320
   436  //  0000020                   16           3219913727
   437  //  0000040                    6                 4096
   438  //  0000060                   17                  100
   439  //  0000100                    3       94665627353152
   440  //  0000120                    4                   56
   441  //  0000140                    5                    9
   442  //  0000160                    7      140425502162944
   443  //  0000200                    8                    0
   444  //  0000220                    9       94665627365760
   445  //  0000240                   11                 1000
   446  //  0000260                   12                 1000
   447  //  0000300                   13                 1000
   448  //  0000320                   14                 1000
   449  //  0000340                   23                    0
   450  //  0000360                   25      140734614619513
   451  //  0000400                   26                    0
   452  //  0000420                   31      140734614626284
   453  //  0000440                   15      140734614619529
   454  //  0000460                    0                    0
   455  func initHwCap() {
   456  	auxv, err := ioutil.ReadFile("/proc/self/auxv")
   457  	if err != nil {
   458  		log.Warningf("Could not read /proc/self/auxv: %v", err)
   459  		return
   460  	}
   461  
   462  	l := len(auxv) / 16
   463  	for i := 0; i < l; i++ {
   464  		tag := binary.LittleEndian.Uint64(auxv[i*16:])
   465  		val := binary.LittleEndian.Uint64(auxv[(i*16 + 8):])
   466  		if tag == _AT_HWCAP {
   467  			hwCap = uint(val)
   468  			break
   469  		}
   470  	}
   471  }
   472  
   473  func initFeaturesFromString() {
   474  	for f, s := range arm64FeatureStrings {
   475  		arm64FeaturesFromString[s] = f
   476  	}
   477  }
   478  
   479  func init() {
   480  	initCPUInfo()
   481  	initHwCap()
   482  	initFeaturesFromString()
   483  }