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 }