mirror of
https://github.com/rwinkhart/sys.git
synced 2026-08-28 04:46:44 -04:00
cpu: support reading arm64 CPU feature registers
This allows to detect ARM64 CPU features on non-Linux systems. On Linux, this is used in case /proc/self/auxv cannot be read. Change-Id: I67d55e989f2beda0c05a97ca5e55781840a8e01c Reviewed-on: https://go-review.googlesource.com/c/sys/+/209478 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
committed by
Tobias Klauser
parent
6d18c012ae
commit
85b82a3add
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
const cacheLineSize = 64
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
// iOS does not seem to allow reading these registers
|
||||||
|
case "android", "linux":
|
||||||
|
doinit()
|
||||||
|
default:
|
||||||
|
readARM64Registers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readARM64Registers() {
|
||||||
|
Initialized = true
|
||||||
|
|
||||||
|
// ID_AA64ISAR0_EL1
|
||||||
|
isar0 := getisar0()
|
||||||
|
|
||||||
|
switch extractBits(isar0, 4, 7) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasAES = true
|
||||||
|
case 2:
|
||||||
|
ARM64.HasAES = true
|
||||||
|
ARM64.HasPMULL = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 8, 11) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSHA1 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 12, 15) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSHA2 = true
|
||||||
|
case 2:
|
||||||
|
ARM64.HasSHA2 = true
|
||||||
|
ARM64.HasSHA512 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 16, 19) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasCRC32 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 20, 23) {
|
||||||
|
case 2:
|
||||||
|
ARM64.HasATOMICS = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 28, 31) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasASIMDRDM = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 32, 35) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSHA3 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 36, 39) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSM3 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 40, 43) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSM4 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar0, 44, 47) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasASIMDDP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID_AA64ISAR1_EL1
|
||||||
|
isar1 := getisar1()
|
||||||
|
|
||||||
|
switch extractBits(isar1, 0, 3) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasDCPOP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 12, 15) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasJSCVT = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 16, 19) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasFCMA = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(isar1, 20, 23) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasLRCPC = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID_AA64PFR0_EL1
|
||||||
|
pfr0 := getpfr0()
|
||||||
|
|
||||||
|
switch extractBits(pfr0, 16, 19) {
|
||||||
|
case 0:
|
||||||
|
ARM64.HasFP = true
|
||||||
|
case 1:
|
||||||
|
ARM64.HasFP = true
|
||||||
|
ARM64.HasFPHP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(pfr0, 20, 23) {
|
||||||
|
case 0:
|
||||||
|
ARM64.HasASIMD = true
|
||||||
|
case 1:
|
||||||
|
ARM64.HasASIMD = true
|
||||||
|
ARM64.HasASIMDHP = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch extractBits(pfr0, 32, 35) {
|
||||||
|
case 1:
|
||||||
|
ARM64.HasSVE = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractBits(data uint64, start, end uint) uint {
|
||||||
|
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func getisar0() uint64
|
||||||
|
TEXT ·getisar0(SB),NOSPLIT,$0-8
|
||||||
|
// get Instruction Set Attributes 0 into x0
|
||||||
|
// mrs x0, ID_AA64ISAR0_EL1 = d5380600
|
||||||
|
WORD $0xd5380600
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func getisar1() uint64
|
||||||
|
TEXT ·getisar1(SB),NOSPLIT,$0-8
|
||||||
|
// get Instruction Set Attributes 1 into x0
|
||||||
|
// mrs x0, ID_AA64ISAR1_EL1 = d5380620
|
||||||
|
WORD $0xd5380620
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func getpfr0() uint64
|
||||||
|
TEXT ·getpfr0(SB),NOSPLIT,$0-8
|
||||||
|
// get Processor Feature Register 0 into x0
|
||||||
|
// mrs x0, ID_AA64PFR0_EL1 = d5380400
|
||||||
|
WORD $0xd5380400
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func getisar0() uint64
|
||||||
|
func getisar1() uint64
|
||||||
|
func getpfr0() uint64
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build gccgo
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func getisar0() uint64 { return 0 }
|
||||||
|
func getisar1() uint64 { return 0 }
|
||||||
|
func getpfr0() uint64 { return 0 }
|
||||||
+2
-46
@@ -2,58 +2,14 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !amd64,!amd64p32,!386
|
// +build !386,!amd64,!amd64p32,!arm64
|
||||||
|
|
||||||
package cpu
|
package cpu
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
_AT_HWCAP = 16
|
|
||||||
_AT_HWCAP2 = 26
|
|
||||||
|
|
||||||
procAuxv = "/proc/self/auxv"
|
|
||||||
|
|
||||||
uintSize = int(32 << (^uint(0) >> 63))
|
|
||||||
)
|
|
||||||
|
|
||||||
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2
|
|
||||||
// These are initialized in cpu_$GOARCH.go
|
|
||||||
// and should not be changed after they are initialized.
|
|
||||||
var hwCap uint
|
|
||||||
var hwCap2 uint
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
buf, err := ioutil.ReadFile(procAuxv)
|
if err := readHWCAP(); err != nil {
|
||||||
if err != nil {
|
|
||||||
// e.g. on android /proc/self/auxv is not accessible, so silently
|
|
||||||
// ignore the error and leave Initialized = false
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bo := hostByteOrder()
|
|
||||||
for len(buf) >= 2*(uintSize/8) {
|
|
||||||
var tag, val uint
|
|
||||||
switch uintSize {
|
|
||||||
case 32:
|
|
||||||
tag = uint(bo.Uint32(buf[0:]))
|
|
||||||
val = uint(bo.Uint32(buf[4:]))
|
|
||||||
buf = buf[8:]
|
|
||||||
case 64:
|
|
||||||
tag = uint(bo.Uint64(buf[0:]))
|
|
||||||
val = uint(bo.Uint64(buf[8:]))
|
|
||||||
buf = buf[16:]
|
|
||||||
}
|
|
||||||
switch tag {
|
|
||||||
case _AT_HWCAP:
|
|
||||||
hwCap = val
|
|
||||||
case _AT_HWCAP2:
|
|
||||||
hwCap2 = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doinit()
|
doinit()
|
||||||
|
|
||||||
Initialized = true
|
Initialized = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
package cpu
|
package cpu
|
||||||
|
|
||||||
const cacheLineSize = 64
|
|
||||||
|
|
||||||
// HWCAP/HWCAP2 bits. These are exposed by Linux.
|
// HWCAP/HWCAP2 bits. These are exposed by Linux.
|
||||||
const (
|
const (
|
||||||
hwcap_FP = 1 << 0
|
hwcap_FP = 1 << 0
|
||||||
@@ -35,6 +33,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func doinit() {
|
func doinit() {
|
||||||
|
if err := readHWCAP(); err != nil {
|
||||||
|
// failed to read /proc/self/auxv, try reading registers directly
|
||||||
|
readARM64Registers()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// HWCAP feature bits
|
// HWCAP feature bits
|
||||||
ARM64.HasFP = isSet(hwCap, hwcap_FP)
|
ARM64.HasFP = isSet(hwCap, hwcap_FP)
|
||||||
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)
|
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)
|
||||||
|
|||||||
@@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
package cpu
|
package cpu
|
||||||
|
|
||||||
const cacheLineSize = 64
|
func doinit() {}
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ func TestAVX2hasAVX(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestARM64minimalFeatures(t *testing.T) {
|
func TestARM64minimalFeatures(t *testing.T) {
|
||||||
if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
|
if runtime.GOARCH != "arm64" || runtime.GOOS == "darwin" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !cpu.ARM64.HasASIMD {
|
if !cpu.ARM64.HasASIMD {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_AT_HWCAP = 16
|
||||||
|
_AT_HWCAP2 = 26
|
||||||
|
|
||||||
|
procAuxv = "/proc/self/auxv"
|
||||||
|
|
||||||
|
uintSize = int(32 << (^uint(0) >> 63))
|
||||||
|
)
|
||||||
|
|
||||||
|
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2
|
||||||
|
// These are initialized in cpu_$GOARCH.go
|
||||||
|
// and should not be changed after they are initialized.
|
||||||
|
var hwCap uint
|
||||||
|
var hwCap2 uint
|
||||||
|
|
||||||
|
func readHWCAP() error {
|
||||||
|
buf, err := ioutil.ReadFile(procAuxv)
|
||||||
|
if err != nil {
|
||||||
|
// e.g. on android /proc/self/auxv is not accessible, so silently
|
||||||
|
// ignore the error and leave Initialized = false. On some
|
||||||
|
// architectures (e.g. arm64) doinit() implements a fallback
|
||||||
|
// readout and will set Initialized = true again.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bo := hostByteOrder()
|
||||||
|
for len(buf) >= 2*(uintSize/8) {
|
||||||
|
var tag, val uint
|
||||||
|
switch uintSize {
|
||||||
|
case 32:
|
||||||
|
tag = uint(bo.Uint32(buf[0:]))
|
||||||
|
val = uint(bo.Uint32(buf[4:]))
|
||||||
|
buf = buf[8:]
|
||||||
|
case 64:
|
||||||
|
tag = uint(bo.Uint64(buf[0:]))
|
||||||
|
val = uint(bo.Uint64(buf[8:]))
|
||||||
|
buf = buf[16:]
|
||||||
|
}
|
||||||
|
switch tag {
|
||||||
|
case _AT_HWCAP:
|
||||||
|
hwCap = val
|
||||||
|
case _AT_HWCAP2:
|
||||||
|
hwCap2 = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user