1// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
2
3package cpuid
4
5import (
6 "runtime"
7 "strings"
8
9 "golang.org/x/sys/unix"
10)
11
12func detectOS(c *CPUInfo) bool {
13 if runtime.GOOS != "ios" {
14 tryToFillCPUInfoFomSysctl(c)
15 }
16 // There are no hw.optional sysctl values for the below features on Mac OS 11.0
17 // to detect their supported state dynamically. Assume the CPU features that
18 // Apple Silicon M1 supports to be available as a minimal set of features
19 // to all Go programs running on darwin/arm64.
20 // TODO: Add more if we know them.
21 c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
22
23 return true
24}
25
26func sysctlGetBool(name string) bool {
27 value, err := unix.SysctlUint32(name)
28 if err != nil {
29 return false
30 }
31 return value != 0
32}
33
34func sysctlGetString(name string) string {
35 value, err := unix.Sysctl(name)
36 if err != nil {
37 return ""
38 }
39 return value
40}
41
42func sysctlGetInt(unknown int, names ...string) int {
43 for _, name := range names {
44 value, err := unix.SysctlUint32(name)
45 if err != nil {
46 continue
47 }
48 if value != 0 {
49 return int(value)
50 }
51 }
52 return unknown
53}
54
55func sysctlGetInt64(unknown int, names ...string) int {
56 for _, name := range names {
57 value64, err := unix.SysctlUint64(name)
58 if err != nil {
59 continue
60 }
61 if int(value64) != unknown {
62 return int(value64)
63 }
64 }
65 return unknown
66}
67
68func setFeature(c *CPUInfo, feature FeatureID, aliases ...string) {
69 for _, alias := range aliases {
70 set := sysctlGetBool(alias)
71 c.featureSet.setIf(set, feature)
72 if set {
73 break
74 }
75 }
76}
77
78func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
79 c.BrandName = sysctlGetString("machdep.cpu.brand_string")
80
81 if len(c.BrandName) != 0 {
82 c.VendorString = strings.Fields(c.BrandName)[0]
83 }
84
85 c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
86 c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
87 sysctlGetInt(1, "hw.physicalcpu")
88 c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
89 c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
90 c.Model = sysctlGetInt(0, "machdep.cpu.model")
91 c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
92 c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
93 c.Cache.L1D = sysctlGetInt64(-1, "hw.l1dcachesize")
94 c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
95 c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
96
97 // ARM features:
98 //
99 // Note: On some Apple Silicon system, some feats have aliases. See:
100 // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
101 // When so, we look at all aliases and consider a feature available when at least one identifier matches.
102 setFeature(c, AESARM, "hw.optional.arm.FEAT_AES") // AES instructions
103 setFeature(c, ASIMD, "hw.optional.arm.AdvSIMD", "hw.optional.neon") // Advanced SIMD
104 setFeature(c, ASIMDDP, "hw.optional.arm.FEAT_DotProd") // SIMD Dot Product
105 setFeature(c, ASIMDHP, "hw.optional.arm.AdvSIMD_HPFPCvt", "hw.optional.neon_hpfp") // Advanced SIMD half-precision floating point
106 setFeature(c, ASIMDRDM, "hw.optional.arm.FEAT_RDM") // Rounding Double Multiply Accumulate/Subtract
107 setFeature(c, ATOMICS, "hw.optional.arm.FEAT_LSE", "hw.optional.armv8_1_atomics") // Large System Extensions (LSE)
108 setFeature(c, CRC32, "hw.optional.arm.FEAT_CRC32", "hw.optional.armv8_crc32") // CRC32/CRC32C instructions
109 setFeature(c, DCPOP, "hw.optional.arm.FEAT_DPB") // Data cache clean to Point of Persistence (DC CVAP)
110 setFeature(c, EVTSTRM, "hw.optional.arm.FEAT_ECV") // Generic timer
111 setFeature(c, FCMA, "hw.optional.arm.FEAT_FCMA", "hw.optional.armv8_3_compnum") // Floating point complex number addition and multiplication
112 setFeature(c, FHM, "hw.optional.armv8_2_fhm", "hw.optional.arm.FEAT_FHM") // FMLAL and FMLSL instructions
113 setFeature(c, FP, "hw.optional.floatingpoint") // Single-precision and double-precision floating point
114 setFeature(c, FPHP, "hw.optional.arm.FEAT_FP16", "hw.optional.neon_fp16") // Half-precision floating point
115 setFeature(c, GPA, "hw.optional.arm.FEAT_PAuth") // Generic Pointer Authentication
116 setFeature(c, JSCVT, "hw.optional.arm.FEAT_JSCVT") // Javascript-style double->int convert (FJCVTZS)
117 setFeature(c, LRCPC, "hw.optional.arm.FEAT_LRCPC") // Weaker release consistency (LDAPR, etc)
118 setFeature(c, PMULL, "hw.optional.arm.FEAT_PMULL") // Polynomial Multiply instructions (PMULL/PMULL2)
119 setFeature(c, RNDR, "hw.optional.arm.FEAT_RNG") // Random Number instructions
120 setFeature(c, TLB, "hw.optional.arm.FEAT_TLBIOS", "hw.optional.arm.FEAT_TLBIRANGE") // Outer Shareable and TLB range maintenance instructions
121 setFeature(c, TS, "hw.optional.arm.FEAT_FlagM", "hw.optional.arm.FEAT_FlagM2") // Flag manipulation instructions
122 setFeature(c, SHA1, "hw.optional.arm.FEAT_SHA1") // SHA-1 instructions (SHA1C, etc)
123 setFeature(c, SHA2, "hw.optional.arm.FEAT_SHA256") // SHA-2 instructions (SHA256H, etc)
124 setFeature(c, SHA3, "hw.optional.arm.FEAT_SHA3") // SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
125 setFeature(c, SHA512, "hw.optional.arm.FEAT_SHA512") // SHA512 instructions
126 setFeature(c, SM3, "hw.optional.arm.FEAT_SM3") // SM3 instructions
127 setFeature(c, SM4, "hw.optional.arm.FEAT_SM4") // SM4 instructions
128 setFeature(c, SVE, "hw.optional.arm.FEAT_SVE") // Scalable Vector Extension
129}