cpu: add linux/arm64

Port from Go internal/cpu

Updates golang/go#25185

Change-Id: I8390980e38b61f6c428fafa0665a03952e7b00bb
Reviewed-on: https://go-review.googlesource.com/c/150718
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
diff --git a/cpu/cpu.go b/cpu/cpu.go
index 3d88f86..11e8fb9 100644
--- a/cpu/cpu.go
+++ b/cpu/cpu.go
@@ -36,3 +36,35 @@
 	HasSSE42     bool // Streaming SIMD extension 4 and 4.2
 	_            CacheLinePad
 }
+
+// ARM64 contains the supported CPU features of the
+// current ARMv8(aarch64) platform. If the current platform
+// is not arm64 then all feature flags are false.
+var ARM64 struct {
+	_           CacheLinePad
+	HasFP       bool // Floating-point instruction set (always available)
+	HasASIMD    bool // Advanced SIMD (always available)
+	HasEVTSTRM  bool // Event stream support
+	HasAES      bool // AES hardware implementation
+	HasPMULL    bool // Polynomial multiplication instruction set
+	HasSHA1     bool // SHA1 hardware implementation
+	HasSHA2     bool // SHA2 hardware implementation
+	HasCRC32    bool // CRC32 hardware implementation
+	HasATOMICS  bool // Atomic memory operation instruction set
+	HasFPHP     bool // Half precision floating-point instruction set
+	HasASIMDHP  bool // Advanced SIMD half precision instruction set
+	HasCPUID    bool // CPUID identification scheme registers
+	HasASIMDRDM bool // Rounding double multiply add/subtract instruction set
+	HasJSCVT    bool // Javascript conversion from floating-point to integer
+	HasFCMA     bool // Floating-point multiplication and addition of complex numbers
+	HasLRCPC    bool // Release Consistent processor consistent support
+	HasDCPOP    bool // Persistent memory support
+	HasSHA3     bool // SHA3 hardware implementation
+	HasSM3      bool // SM3 hardware implementation
+	HasSM4      bool // SM4 hardware implementation
+	HasASIMDDP  bool // Advanced SIMD double precision instruction set
+	HasSHA512   bool // SHA512 hardware implementation
+	HasSVE      bool // Scalable Vector Extensions
+	HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32
+	_           CacheLinePad
+}
diff --git a/cpu/cpu_arm.go b/cpu/cpu_arm.go
index d93036f..7f2348b 100644
--- a/cpu/cpu_arm.go
+++ b/cpu/cpu_arm.go
@@ -5,3 +5,5 @@
 package cpu
 
 const cacheLineSize = 32
+
+func doinit() {}
diff --git a/cpu/cpu_arm64.go b/cpu/cpu_arm64.go
index 1d2ab29..02ed58b 100644
--- a/cpu/cpu_arm64.go
+++ b/cpu/cpu_arm64.go
@@ -5,3 +5,63 @@
 package cpu
 
 const cacheLineSize = 64
+
+// HWCAP/HWCAP2 bits. These are exposed by Linux.
+const (
+	hwcap_FP       = 1 << 0
+	hwcap_ASIMD    = 1 << 1
+	hwcap_EVTSTRM  = 1 << 2
+	hwcap_AES      = 1 << 3
+	hwcap_PMULL    = 1 << 4
+	hwcap_SHA1     = 1 << 5
+	hwcap_SHA2     = 1 << 6
+	hwcap_CRC32    = 1 << 7
+	hwcap_ATOMICS  = 1 << 8
+	hwcap_FPHP     = 1 << 9
+	hwcap_ASIMDHP  = 1 << 10
+	hwcap_CPUID    = 1 << 11
+	hwcap_ASIMDRDM = 1 << 12
+	hwcap_JSCVT    = 1 << 13
+	hwcap_FCMA     = 1 << 14
+	hwcap_LRCPC    = 1 << 15
+	hwcap_DCPOP    = 1 << 16
+	hwcap_SHA3     = 1 << 17
+	hwcap_SM3      = 1 << 18
+	hwcap_SM4      = 1 << 19
+	hwcap_ASIMDDP  = 1 << 20
+	hwcap_SHA512   = 1 << 21
+	hwcap_SVE      = 1 << 22
+	hwcap_ASIMDFHM = 1 << 23
+)
+
+func doinit() {
+	// HWCAP feature bits
+	ARM64.HasFP = isSet(HWCap, hwcap_FP)
+	ARM64.HasASIMD = isSet(HWCap, hwcap_ASIMD)
+	ARM64.HasEVTSTRM = isSet(HWCap, hwcap_EVTSTRM)
+	ARM64.HasAES = isSet(HWCap, hwcap_AES)
+	ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL)
+	ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1)
+	ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2)
+	ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32)
+	ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS)
+	ARM64.HasFPHP = isSet(HWCap, hwcap_FPHP)
+	ARM64.HasASIMDHP = isSet(HWCap, hwcap_ASIMDHP)
+	ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID)
+	ARM64.HasASIMDRDM = isSet(HWCap, hwcap_ASIMDRDM)
+	ARM64.HasJSCVT = isSet(HWCap, hwcap_JSCVT)
+	ARM64.HasFCMA = isSet(HWCap, hwcap_FCMA)
+	ARM64.HasLRCPC = isSet(HWCap, hwcap_LRCPC)
+	ARM64.HasDCPOP = isSet(HWCap, hwcap_DCPOP)
+	ARM64.HasSHA3 = isSet(HWCap, hwcap_SHA3)
+	ARM64.HasSM3 = isSet(HWCap, hwcap_SM3)
+	ARM64.HasSM4 = isSet(HWCap, hwcap_SM4)
+	ARM64.HasASIMDDP = isSet(HWCap, hwcap_ASIMDDP)
+	ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512)
+	ARM64.HasSVE = isSet(HWCap, hwcap_SVE)
+	ARM64.HasASIMDFHM = isSet(HWCap, hwcap_ASIMDFHM)
+}
+
+func isSet(hwc uint, value uint) bool {
+	return hwc&value != 0
+}
diff --git a/cpu/cpu_linux.go b/cpu/cpu_linux.go
new file mode 100644
index 0000000..126bb68
--- /dev/null
+++ b/cpu/cpu_linux.go
@@ -0,0 +1,55 @@
+// Copyright 2018 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 !amd64,!amd64p32,!386
+
+package cpu
+
+import (
+	"encoding/binary"
+	"io/ioutil"
+)
+
+const (
+	_AT_HWCAP  = 16
+	_AT_HWCAP2 = 26
+
+	procAuxv = "/proc/self/auxv"
+
+	uintSize uint = 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 {
+		panic("read proc auxv failed: " + err.Error())
+	}
+
+	pb := int(uintSize / 8)
+
+	for i := 0; i < len(buf)-pb*2; i += pb * 2 {
+		var tag, val uint
+		switch uintSize {
+		case 32:
+			tag = uint(binary.LittleEndian.Uint32(buf[i:]))
+			val = uint(binary.LittleEndian.Uint32(buf[i+pb:]))
+		case 64:
+			tag = uint(binary.LittleEndian.Uint64(buf[i:]))
+			val = uint(binary.LittleEndian.Uint64(buf[i+pb:]))
+		}
+		switch tag {
+		case _AT_HWCAP:
+			HWCap = val
+		case _AT_HWCAP2:
+			HWCap2 = val
+		}
+	}
+	doinit()
+}
diff --git a/cpu/cpu_mips64x.go b/cpu/cpu_mips64x.go
index 6165f12..f55e0c8 100644
--- a/cpu/cpu_mips64x.go
+++ b/cpu/cpu_mips64x.go
@@ -7,3 +7,5 @@
 package cpu
 
 const cacheLineSize = 32
+
+func doinit() {}
diff --git a/cpu/cpu_mipsx.go b/cpu/cpu_mipsx.go
index 1269eee..cda87b1 100644
--- a/cpu/cpu_mipsx.go
+++ b/cpu/cpu_mipsx.go
@@ -7,3 +7,5 @@
 package cpu
 
 const cacheLineSize = 32
+
+func doinit() {}
diff --git a/cpu/cpu_ppc64x.go b/cpu/cpu_ppc64x.go
index d10759a..70c959b 100644
--- a/cpu/cpu_ppc64x.go
+++ b/cpu/cpu_ppc64x.go
@@ -7,3 +7,5 @@
 package cpu
 
 const cacheLineSize = 128
+
+func doinit() {}
diff --git a/cpu/cpu_s390x.go b/cpu/cpu_s390x.go
index 684c4f0..ce8a228 100644
--- a/cpu/cpu_s390x.go
+++ b/cpu/cpu_s390x.go
@@ -5,3 +5,5 @@
 package cpu
 
 const cacheLineSize = 256
+
+func doinit() {}
diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go
index baf4b87..0c8cc83 100644
--- a/cpu/cpu_test.go
+++ b/cpu/cpu_test.go
@@ -26,3 +26,15 @@
 		}
 	}
 }
+
+func TestARM64minimalFeatures(t *testing.T) {
+	if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
+		return
+	}
+	if !cpu.ARM64.HasASIMD {
+		t.Fatal("HasASIMD expected true, got false")
+	}
+	if !cpu.ARM64.HasFP {
+		t.Fatal("HasFP expected true, got false")
+	}
+}