1// Copyright 2020 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//go:build arm64 && linux
6
7package cpu
8
9import _ "unsafe" // for linkname
10
11// HWCap may be initialized by archauxv and
12// should not be changed after it was initialized.
13//
14// Other widely used packages
15// access HWCap using linkname as well, most notably:
16//   - github.com/klauspost/cpuid/v2
17//
18// Do not remove or change the type signature.
19// See go.dev/issue/67401.
20//
21//go:linkname HWCap
22var HWCap uint
23
24// HWCAP bits. These are exposed by Linux.
25const (
26	hwcap_AES     = 1 << 3
27	hwcap_PMULL   = 1 << 4
28	hwcap_SHA1    = 1 << 5
29	hwcap_SHA2    = 1 << 6
30	hwcap_CRC32   = 1 << 7
31	hwcap_ATOMICS = 1 << 8
32	hwcap_CPUID   = 1 << 11
33	hwcap_SHA512  = 1 << 21
34)
35
36func hwcapInit(os string) {
37	// HWCap was populated by the runtime from the auxiliary vector.
38	// Use HWCap information since reading aarch64 system registers
39	// is not supported in user space on older linux kernels.
40	ARM64.HasAES = isSet(HWCap, hwcap_AES)
41	ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL)
42	ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1)
43	ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2)
44	ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32)
45	ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID)
46	ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512)
47
48	// The Samsung S9+ kernel reports support for atomics, but not all cores
49	// actually support them, resulting in SIGILL. See issue #28431.
50	// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
51	ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) && os != "android"
52
53	// Check to see if executing on a Neoverse core and in order to do that,
54	// check the AUXV for the CPUID bit. The getMIDR function executes an
55	// instruction which would normally be an illegal instruction, but it's
56	// trapped by the kernel, the value sanitized and then returned.
57	// Without the CPUID bit the kernel will not trap the instruction and the
58	// process will be terminated with SIGILL.
59	if ARM64.HasCPUID {
60		midr := getMIDR()
61		part_num := uint16((midr >> 4) & 0xfff)
62		implementer := byte((midr >> 24) & 0xff)
63
64		// d0c - NeoverseN1
65		// d40 - NeoverseV1
66		// d49 - NeoverseN2
67		// d4f - NeoverseV2
68		if implementer == 'A' && (part_num == 0xd0c || part_num == 0xd40 ||
69			part_num == 0xd49 || part_num == 0xd4f) {
70			ARM64.IsNeoverse = true
71		}
72	}
73}
74
75func isSet(hwc uint, value uint) bool {
76	return hwc&value != 0
77}
78