Skip to content

Commit

Permalink
cpu: support reading arm64 CPU feature registers
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
tklauser committed Dec 4, 2019
1 parent 6d18c01 commit 85b82a3
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 50 deletions.
134 changes: 134 additions & 0 deletions cpu/cpu_arm64.go
Original file line number Diff line number Diff line change
@@ -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)
}
31 changes: 31 additions & 0 deletions cpu/cpu_arm64.s
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions cpu/cpu_gc_arm64.go
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions cpu/cpu_gccgo_arm64.go
Original file line number Diff line number Diff line change
@@ -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 }
File renamed without changes.
File renamed without changes.
48 changes: 2 additions & 46 deletions cpu/cpu_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !amd64,!amd64p32,!386
// +build !386,!amd64,!amd64p32,!arm64

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() {
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
if err := readHWCAP(); err != nil {
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()

Initialized = true
}
8 changes: 6 additions & 2 deletions cpu/cpu_linux_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

package cpu

const cacheLineSize = 64

// HWCAP/HWCAP2 bits. These are exposed by Linux.
const (
hwcap_FP = 1 << 0
Expand Down Expand Up @@ -35,6 +33,12 @@ const (
)

func doinit() {
if err := readHWCAP(); err != nil {
// failed to read /proc/self/auxv, try reading registers directly
readARM64Registers()
return
}

// HWCAP feature bits
ARM64.HasFP = isSet(hwCap, hwcap_FP)
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)
Expand Down
2 changes: 1 addition & 1 deletion cpu/cpu_other_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

package cpu

const cacheLineSize = 64
func doinit() {}
2 changes: 1 addition & 1 deletion cpu/cpu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestAVX2hasAVX(t *testing.T) {
}

func TestARM64minimalFeatures(t *testing.T) {
if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
if runtime.GOARCH != "arm64" || runtime.GOOS == "darwin" {
return
}
if !cpu.ARM64.HasASIMD {
Expand Down
56 changes: 56 additions & 0 deletions cpu/hwcap_linux.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 85b82a3

Please sign in to comment.