1// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
2
3// Copyright 2018 The Go Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file located
6// here https://github.com/golang/sys/blob/master/LICENSE
7
8package cpuid
9
10import (
11	"encoding/binary"
12	"io/ioutil"
13	"runtime"
14)
15
16// HWCAP bits.
17const (
18	hwcap_FP       = 1 << 0
19	hwcap_ASIMD    = 1 << 1
20	hwcap_EVTSTRM  = 1 << 2
21	hwcap_AES      = 1 << 3
22	hwcap_PMULL    = 1 << 4
23	hwcap_SHA1     = 1 << 5
24	hwcap_SHA2     = 1 << 6
25	hwcap_CRC32    = 1 << 7
26	hwcap_ATOMICS  = 1 << 8
27	hwcap_FPHP     = 1 << 9
28	hwcap_ASIMDHP  = 1 << 10
29	hwcap_CPUID    = 1 << 11
30	hwcap_ASIMDRDM = 1 << 12
31	hwcap_JSCVT    = 1 << 13
32	hwcap_FCMA     = 1 << 14
33	hwcap_LRCPC    = 1 << 15
34	hwcap_DCPOP    = 1 << 16
35	hwcap_SHA3     = 1 << 17
36	hwcap_SM3      = 1 << 18
37	hwcap_SM4      = 1 << 19
38	hwcap_ASIMDDP  = 1 << 20
39	hwcap_SHA512   = 1 << 21
40	hwcap_SVE      = 1 << 22
41	hwcap_ASIMDFHM = 1 << 23
42)
43
44func detectOS(c *CPUInfo) bool {
45	// For now assuming no hyperthreading is reasonable.
46	c.LogicalCores = runtime.NumCPU()
47	c.PhysicalCores = c.LogicalCores
48	c.ThreadsPerCore = 1
49	if hwcap == 0 {
50		// We did not get values from the runtime.
51		// Try reading /proc/self/auxv
52
53		// From https://github.com/golang/sys
54		const (
55			_AT_HWCAP  = 16
56			_AT_HWCAP2 = 26
57
58			uintSize = int(32 << (^uint(0) >> 63))
59		)
60
61		buf, err := ioutil.ReadFile("/proc/self/auxv")
62		if err != nil {
63			// e.g. on android /proc/self/auxv is not accessible, so silently
64			// ignore the error and leave Initialized = false. On some
65			// architectures (e.g. arm64) doinit() implements a fallback
66			// readout and will set Initialized = true again.
67			return false
68		}
69		bo := binary.LittleEndian
70		for len(buf) >= 2*(uintSize/8) {
71			var tag, val uint
72			switch uintSize {
73			case 32:
74				tag = uint(bo.Uint32(buf[0:]))
75				val = uint(bo.Uint32(buf[4:]))
76				buf = buf[8:]
77			case 64:
78				tag = uint(bo.Uint64(buf[0:]))
79				val = uint(bo.Uint64(buf[8:]))
80				buf = buf[16:]
81			}
82			switch tag {
83			case _AT_HWCAP:
84				hwcap = val
85			case _AT_HWCAP2:
86				// Not used
87			}
88		}
89		if hwcap == 0 {
90			return false
91		}
92	}
93
94	// HWCap was populated by the runtime from the auxiliary vector.
95	// Use HWCap information since reading aarch64 system registers
96	// is not supported in user space on older linux kernels.
97	c.featureSet.setIf(isSet(hwcap, hwcap_AES), AESARM)
98	c.featureSet.setIf(isSet(hwcap, hwcap_ASIMD), ASIMD)
99	c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDDP), ASIMDDP)
100	c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDHP), ASIMDHP)
101	c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDRDM), ASIMDRDM)
102	c.featureSet.setIf(isSet(hwcap, hwcap_CPUID), ARMCPUID)
103	c.featureSet.setIf(isSet(hwcap, hwcap_CRC32), CRC32)
104	c.featureSet.setIf(isSet(hwcap, hwcap_DCPOP), DCPOP)
105	c.featureSet.setIf(isSet(hwcap, hwcap_EVTSTRM), EVTSTRM)
106	c.featureSet.setIf(isSet(hwcap, hwcap_FCMA), FCMA)
107	c.featureSet.setIf(isSet(hwcap, hwcap_FP), FP)
108	c.featureSet.setIf(isSet(hwcap, hwcap_FPHP), FPHP)
109	c.featureSet.setIf(isSet(hwcap, hwcap_JSCVT), JSCVT)
110	c.featureSet.setIf(isSet(hwcap, hwcap_LRCPC), LRCPC)
111	c.featureSet.setIf(isSet(hwcap, hwcap_PMULL), PMULL)
112	c.featureSet.setIf(isSet(hwcap, hwcap_SHA1), SHA1)
113	c.featureSet.setIf(isSet(hwcap, hwcap_SHA2), SHA2)
114	c.featureSet.setIf(isSet(hwcap, hwcap_SHA3), SHA3)
115	c.featureSet.setIf(isSet(hwcap, hwcap_SHA512), SHA512)
116	c.featureSet.setIf(isSet(hwcap, hwcap_SM3), SM3)
117	c.featureSet.setIf(isSet(hwcap, hwcap_SM4), SM4)
118	c.featureSet.setIf(isSet(hwcap, hwcap_SVE), SVE)
119
120	// The Samsung S9+ kernel reports support for atomics, but not all cores
121	// actually support them, resulting in SIGILL. See issue #28431.
122	// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
123	c.featureSet.setIf(isSet(hwcap, hwcap_ATOMICS) && runtime.GOOS != "android", ATOMICS)
124
125	return true
126}
127
128func isSet(hwc uint, value uint) bool {
129	return hwc&value != 0
130}
131