internal/cpu: detect cpu features in internal/cpu package

change hash/crc32 package to use cpu package instead of using
runtime internal variables to check crc32 instruction

Change-Id: I8f88d2351bde8ed4e256f9adf822a08b9a00f532
diff --git a/src/hash/crc32/crc32_arm64.go b/src/hash/crc32/crc32_arm64.go
index 1704486..1f8779d 100644
--- a/src/hash/crc32/crc32_arm64.go
+++ b/src/hash/crc32/crc32_arm64.go
@@ -8,11 +8,12 @@
 
 package crc32
 
-func supportsCRC32() bool
+import "internal/cpu"
+
 func castagnoliUpdate(crc uint32, p []byte) uint32
 func ieeeUpdate(crc uint32, p []byte) uint32
 
-var hasCRC32 = supportsCRC32()
+var hasCRC32 = cpu.ARM64.HasCRC32
 
 func archAvailableCastagnoli() bool {
 	return hasCRC32
diff --git a/src/hash/crc32/crc32_arm64.s b/src/hash/crc32/crc32_arm64.s
index 26a86e4..53274c5 100644
--- a/src/hash/crc32/crc32_arm64.s
+++ b/src/hash/crc32/crc32_arm64.s
@@ -89,9 +89,3 @@
 done:
 	MOVWU	R9, ret+32(FP)
 	RET
-
-// func supportsCRC32() bool
-TEXT ·supportsCRC32(SB),NOSPLIT,$0-1
-	MOVB	runtime·supportCRC32(SB), R0
-	MOVB	R0, ret+0(FP)
-	RET
diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go
index 38fedc4..e81ae7e 100644
--- a/src/internal/cpu/cpu.go
+++ b/src/internal/cpu/cpu.go
@@ -57,3 +57,21 @@
 	IsPOWER9   bool // ISA v3.00 (POWER9)
 	_          [CacheLineSize]byte
 }
+
+var ARM64 arm64
+
+// The booleans in arm64 contain the correspondingly named cpu feature bit.
+// The struct is padded to avoid false sharing.
+type arm64 struct {
+	_            [CacheLineSize]byte
+	HasFP        bool
+	HasASIMD     bool
+	HasEVTSTRM   bool
+	HasAES       bool
+	HasPMULL     bool
+	HasSHA1      bool
+	HasSHA2      bool
+	HasCRC32     bool
+	HasATOMICS   bool
+	_            [CacheLineSize]byte
+}
diff --git a/src/internal/cpu/cpu_arm64.go b/src/internal/cpu/cpu_arm64.go
index d3c6cc8..2e2f733 100644
--- a/src/internal/cpu/cpu_arm64.go
+++ b/src/internal/cpu/cpu_arm64.go
@@ -2,6 +2,44 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build arm64
+
 package cpu
 
 const CacheLineSize = 64
+
+// arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
+// These are linknamed in runtime/os_linux_arm64.go and are initialized by
+// archauxv().
+var arm64_hwcap uint
+var arm64_hwcap2 uint
+
+// HWCAP/HWCAP2 bits. These are exposed by the kernel.
+const (
+        _ARM64_FEATURE_HAS_FP       = (1 << 0)
+        _ARM64_FEATURE_HAS_ASIMD    = (1 << 1)
+        _ARM64_FEATURE_HAS_EVTSTRM  = (1 << 2)
+        _ARM64_FEATURE_HAS_AES      = (1 << 3)
+        _ARM64_FEATURE_HAS_PMULL    = (1 << 4)
+        _ARM64_FEATURE_HAS_SHA1     = (1 << 5)
+        _ARM64_FEATURE_HAS_SHA2     = (1 << 6)
+        _ARM64_FEATURE_HAS_CRC32    = (1 << 7)
+        _ARM64_FEATURE_HAS_ATOMICS  = (1 << 8)
+)
+
+func init() {
+	// HWCAP feature bits
+	ARM64.HasFP       = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_FP)
+	ARM64.HasASIMD    = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ASIMD)
+	ARM64.HasEVTSTRM  = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_EVTSTRM)
+	ARM64.HasAES      = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_AES)
+	ARM64.HasPMULL    = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_PMULL)
+	ARM64.HasSHA1     = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA1)
+	ARM64.HasSHA2     = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA2)
+	ARM64.HasCRC32    = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_CRC32)
+	ARM64.HasATOMICS  = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ATOMICS)
+}
+
+func isSet(hwc uint, value uint) bool {
+	return hwc&value != 0
+}
diff --git a/src/runtime/os_darwin_arm64.go b/src/runtime/os_darwin_arm64.go
index 01285af..8de132d 100644
--- a/src/runtime/os_darwin_arm64.go
+++ b/src/runtime/os_darwin_arm64.go
@@ -4,8 +4,6 @@
 
 package runtime
 
-var supportCRC32 = false
-
 //go:nosplit
 func cputicks() int64 {
 	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go
index 986a341..96827e7 100644
--- a/src/runtime/os_linux_arm64.go
+++ b/src/runtime/os_linux_arm64.go
@@ -2,14 +2,22 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build arm64
+
 package runtime
 
-const (
-	_ARM64_FEATURE_HAS_CRC32 = 0x80
-)
+// For go:linkname
+import _ "unsafe"
 
 var randomNumber uint32
-var supportCRC32 bool
+
+// arm64 doesn't have a 'cpuid' instruction equivalent and relies on
+// HWCAP/HWCAP2 bits for hardware capabilities.
+
+//go:linkname cpu_hwcap internal/cpu.arm64_hwcap
+//go:linkname cpu_hwcap2 internal/cpu.arm64_hwcap2
+var cpu_hwcap uint
+var cpu_hwcap2 uint
 
 func archauxv(tag, val uintptr) {
 	switch tag {
@@ -20,7 +28,9 @@
 		randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
 			uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
 	case _AT_HWCAP:
-		supportCRC32 = val&_ARM64_FEATURE_HAS_CRC32 != 0
+		cpu_hwcap = uint(val)
+	case _AT_HWCAP2:
+		cpu_hwcap2 = uint(val)
 	}
 }