Skip to content

Commit 85b82a3

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 6d18c01 commit 85b82a3

11 files changed

+253
-50
lines changed

cpu/cpu_arm64.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package cpu
6+
7+
import "runtime"
8+
9+
const cacheLineSize = 64
10+
11+
func init() {
12+
switch runtime.GOOS {
13+
case "darwin":
14+
// iOS does not seem to allow reading these registers
15+
case "android", "linux":
16+
doinit()
17+
default:
18+
readARM64Registers()
19+
}
20+
}
21+
22+
func readARM64Registers() {
23+
Initialized = true
24+
25+
// ID_AA64ISAR0_EL1
26+
isar0 := getisar0()
27+
28+
switch extractBits(isar0, 4, 7) {
29+
case 1:
30+
ARM64.HasAES = true
31+
case 2:
32+
ARM64.HasAES = true
33+
ARM64.HasPMULL = true
34+
}
35+
36+
switch extractBits(isar0, 8, 11) {
37+
case 1:
38+
ARM64.HasSHA1 = true
39+
}
40+
41+
switch extractBits(isar0, 12, 15) {
42+
case 1:
43+
ARM64.HasSHA2 = true
44+
case 2:
45+
ARM64.HasSHA2 = true
46+
ARM64.HasSHA512 = true
47+
}
48+
49+
switch extractBits(isar0, 16, 19) {
50+
case 1:
51+
ARM64.HasCRC32 = true
52+
}
53+
54+
switch extractBits(isar0, 20, 23) {
55+
case 2:
56+
ARM64.HasATOMICS = true
57+
}
58+
59+
switch extractBits(isar0, 28, 31) {
60+
case 1:
61+
ARM64.HasASIMDRDM = true
62+
}
63+
64+
switch extractBits(isar0, 32, 35) {
65+
case 1:
66+
ARM64.HasSHA3 = true
67+
}
68+
69+
switch extractBits(isar0, 36, 39) {
70+
case 1:
71+
ARM64.HasSM3 = true
72+
}
73+
74+
switch extractBits(isar0, 40, 43) {
75+
case 1:
76+
ARM64.HasSM4 = true
77+
}
78+
79+
switch extractBits(isar0, 44, 47) {
80+
case 1:
81+
ARM64.HasASIMDDP = true
82+
}
83+
84+
// ID_AA64ISAR1_EL1
85+
isar1 := getisar1()
86+
87+
switch extractBits(isar1, 0, 3) {
88+
case 1:
89+
ARM64.HasDCPOP = true
90+
}
91+
92+
switch extractBits(isar1, 12, 15) {
93+
case 1:
94+
ARM64.HasJSCVT = true
95+
}
96+
97+
switch extractBits(isar1, 16, 19) {
98+
case 1:
99+
ARM64.HasFCMA = true
100+
}
101+
102+
switch extractBits(isar1, 20, 23) {
103+
case 1:
104+
ARM64.HasLRCPC = true
105+
}
106+
107+
// ID_AA64PFR0_EL1
108+
pfr0 := getpfr0()
109+
110+
switch extractBits(pfr0, 16, 19) {
111+
case 0:
112+
ARM64.HasFP = true
113+
case 1:
114+
ARM64.HasFP = true
115+
ARM64.HasFPHP = true
116+
}
117+
118+
switch extractBits(pfr0, 20, 23) {
119+
case 0:
120+
ARM64.HasASIMD = true
121+
case 1:
122+
ARM64.HasASIMD = true
123+
ARM64.HasASIMDHP = true
124+
}
125+
126+
switch extractBits(pfr0, 32, 35) {
127+
case 1:
128+
ARM64.HasSVE = true
129+
}
130+
}
131+
132+
func extractBits(data uint64, start, end uint) uint {
133+
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
134+
}

cpu/cpu_arm64.s

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build !gccgo
6+
7+
#include "textflag.h"
8+
9+
// func getisar0() uint64
10+
TEXT ·getisar0(SB),NOSPLIT,$0-8
11+
// get Instruction Set Attributes 0 into x0
12+
// mrs x0, ID_AA64ISAR0_EL1 = d5380600
13+
WORD $0xd5380600
14+
MOVD R0, ret+0(FP)
15+
RET
16+
17+
// func getisar1() uint64
18+
TEXT ·getisar1(SB),NOSPLIT,$0-8
19+
// get Instruction Set Attributes 1 into x0
20+
// mrs x0, ID_AA64ISAR1_EL1 = d5380620
21+
WORD $0xd5380620
22+
MOVD R0, ret+0(FP)
23+
RET
24+
25+
// func getpfr0() uint64
26+
TEXT ·getpfr0(SB),NOSPLIT,$0-8
27+
// get Processor Feature Register 0 into x0
28+
// mrs x0, ID_AA64PFR0_EL1 = d5380400
29+
WORD $0xd5380400
30+
MOVD R0, ret+0(FP)
31+
RET

cpu/cpu_gc_arm64.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build !gccgo
6+
7+
package cpu
8+
9+
func getisar0() uint64
10+
func getisar1() uint64
11+
func getpfr0() uint64

cpu/cpu_gccgo_arm64.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build gccgo
6+
7+
package cpu
8+
9+
func getisar0() uint64 { return 0 }
10+
func getisar1() uint64 { return 0 }
11+
func getpfr0() uint64 { return 0 }
File renamed without changes.
File renamed without changes.

cpu/cpu_linux.go

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,14 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build !amd64,!amd64p32,!386
5+
// +build !386,!amd64,!amd64p32,!arm64
66

77
package cpu
88

9-
import (
10-
"io/ioutil"
11-
)
12-
13-
const (
14-
_AT_HWCAP = 16
15-
_AT_HWCAP2 = 26
16-
17-
procAuxv = "/proc/self/auxv"
18-
19-
uintSize = int(32 << (^uint(0) >> 63))
20-
)
21-
22-
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2
23-
// These are initialized in cpu_$GOARCH.go
24-
// and should not be changed after they are initialized.
25-
var hwCap uint
26-
var hwCap2 uint
27-
289
func init() {
29-
buf, err := ioutil.ReadFile(procAuxv)
30-
if err != nil {
31-
// e.g. on android /proc/self/auxv is not accessible, so silently
32-
// ignore the error and leave Initialized = false
10+
if err := readHWCAP(); err != nil {
3311
return
3412
}
35-
36-
bo := hostByteOrder()
37-
for len(buf) >= 2*(uintSize/8) {
38-
var tag, val uint
39-
switch uintSize {
40-
case 32:
41-
tag = uint(bo.Uint32(buf[0:]))
42-
val = uint(bo.Uint32(buf[4:]))
43-
buf = buf[8:]
44-
case 64:
45-
tag = uint(bo.Uint64(buf[0:]))
46-
val = uint(bo.Uint64(buf[8:]))
47-
buf = buf[16:]
48-
}
49-
switch tag {
50-
case _AT_HWCAP:
51-
hwCap = val
52-
case _AT_HWCAP2:
53-
hwCap2 = val
54-
}
55-
}
5613
doinit()
57-
5814
Initialized = true
5915
}

cpu/cpu_linux_arm64.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
package cpu
66

7-
const cacheLineSize = 64
8-
97
// HWCAP/HWCAP2 bits. These are exposed by Linux.
108
const (
119
hwcap_FP = 1 << 0
@@ -35,6 +33,12 @@ const (
3533
)
3634

3735
func doinit() {
36+
if err := readHWCAP(); err != nil {
37+
// failed to read /proc/self/auxv, try reading registers directly
38+
readARM64Registers()
39+
return
40+
}
41+
3842
// HWCAP feature bits
3943
ARM64.HasFP = isSet(hwCap, hwcap_FP)
4044
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)

cpu/cpu_other_arm64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66

77
package cpu
88

9-
const cacheLineSize = 64
9+
func doinit() {}

cpu/cpu_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func TestAVX2hasAVX(t *testing.T) {
3131
}
3232

3333
func TestARM64minimalFeatures(t *testing.T) {
34-
if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
34+
if runtime.GOARCH != "arm64" || runtime.GOOS == "darwin" {
3535
return
3636
}
3737
if !cpu.ARM64.HasASIMD {

cpu/hwcap_linux.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package cpu
6+
7+
import (
8+
"io/ioutil"
9+
)
10+
11+
const (
12+
_AT_HWCAP = 16
13+
_AT_HWCAP2 = 26
14+
15+
procAuxv = "/proc/self/auxv"
16+
17+
uintSize = int(32 << (^uint(0) >> 63))
18+
)
19+
20+
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2
21+
// These are initialized in cpu_$GOARCH.go
22+
// and should not be changed after they are initialized.
23+
var hwCap uint
24+
var hwCap2 uint
25+
26+
func readHWCAP() error {
27+
buf, err := ioutil.ReadFile(procAuxv)
28+
if err != nil {
29+
// e.g. on android /proc/self/auxv is not accessible, so silently
30+
// ignore the error and leave Initialized = false. On some
31+
// architectures (e.g. arm64) doinit() implements a fallback
32+
// readout and will set Initialized = true again.
33+
return err
34+
}
35+
bo := hostByteOrder()
36+
for len(buf) >= 2*(uintSize/8) {
37+
var tag, val uint
38+
switch uintSize {
39+
case 32:
40+
tag = uint(bo.Uint32(buf[0:]))
41+
val = uint(bo.Uint32(buf[4:]))
42+
buf = buf[8:]
43+
case 64:
44+
tag = uint(bo.Uint64(buf[0:]))
45+
val = uint(bo.Uint64(buf[8:]))
46+
buf = buf[16:]
47+
}
48+
switch tag {
49+
case _AT_HWCAP:
50+
hwCap = val
51+
case _AT_HWCAP2:
52+
hwCap2 = val
53+
}
54+
}
55+
return nil
56+
}

0 commit comments

Comments
 (0)