|
| 1 | +// Copyright 2021 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 | +// +build arm64 |
| 6 | +// +build darwin |
| 7 | +// +build !ios |
| 8 | + |
| 9 | +package cpu |
| 10 | + |
| 11 | +import ( |
| 12 | + "fmt" |
| 13 | + "strings" |
| 14 | + "syscall" |
| 15 | + "unsafe" |
| 16 | +) |
| 17 | + |
| 18 | +func osInit() { |
| 19 | + ARM64.HasFP = sysctlEnabled("hw.optional.floatingpoint") |
| 20 | + ARM64.HasASIMD = sysctlEnabled("hw.optional.neon") |
| 21 | + ARM64.HasCRC32 = sysctlEnabled("hw.optional.armv8_crc32") |
| 22 | + ARM64.HasATOMICS = sysctlEnabled("hw.optional.armv8_1_atomics") |
| 23 | + ARM64.HasFPHP = sysctlEnabled("hw.optional.neon_hpfp") |
| 24 | + ARM64.HasASIMDHP = sysctlEnabled("hw.optional.floatingpoint") |
| 25 | + ARM64.HasSHA3 = sysctlEnabled("hw.optional.armv8_2_sha3") |
| 26 | + ARM64.HasSHA512 = sysctlEnabled("hw.optional.armv8_2_sha512") |
| 27 | + ARM64.HasASIMDFHM = sysctlEnabled("hw.optional.armv8_2_fhm") |
| 28 | + |
| 29 | + // There are no hw.optional sysctl values for the below features on Mac OS 11.0 |
| 30 | + // to detect their supported state dynamically. Assume the CPU features that |
| 31 | + // Apple Silicon M1 supports to be available as a minimal set of features |
| 32 | + // to all Go programs running on darwin/arm64. |
| 33 | + ARM64.HasEVTSTRM = true |
| 34 | + ARM64.HasAES = true |
| 35 | + ARM64.HasPMULL = true |
| 36 | + ARM64.HasSHA1 = true |
| 37 | + ARM64.HasSHA2 = true |
| 38 | + ARM64.HasCPUID = true |
| 39 | + ARM64.HasASIMDRDM = true |
| 40 | + ARM64.HasJSCVT = true |
| 41 | + ARM64.HasFCMA = true |
| 42 | + ARM64.HasLRCPC = true |
| 43 | + ARM64.HasDCPOP = true |
| 44 | + ARM64.HasSM3 = true |
| 45 | + ARM64.HasSM4 = true |
| 46 | + ARM64.HasASIMDDP = true |
| 47 | + ARM64.HasSVE = true |
| 48 | +} |
| 49 | + |
| 50 | +// The following is minimal copy of functionality from x/sys/unix so the cpu package can call |
| 51 | +// sysctl without depending on x/sys/unix. |
| 52 | + |
| 53 | +func sysctlEnabled(name string, args ...int) bool { |
| 54 | + mib, err := nametomib(name) |
| 55 | + if err != nil { |
| 56 | + return false |
| 57 | + } |
| 58 | + |
| 59 | + for _, a := range args { |
| 60 | + mib = append(mib, _C_int(a)) |
| 61 | + } |
| 62 | + |
| 63 | + // Find size. |
| 64 | + n := uintptr(0) |
| 65 | + if err := sysctl(mib, nil, &n, nil, 0); err != nil { |
| 66 | + return false |
| 67 | + } |
| 68 | + |
| 69 | + return true |
| 70 | +} |
| 71 | + |
| 72 | +type _C_int int32 |
| 73 | + |
| 74 | +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { |
| 75 | + var _zero uintptr |
| 76 | + var _p0 unsafe.Pointer |
| 77 | + if len(mib) > 0 { |
| 78 | + _p0 = unsafe.Pointer(&mib[0]) |
| 79 | + } else { |
| 80 | + _p0 = unsafe.Pointer(&_zero) |
| 81 | + } |
| 82 | + _, _, errno := syscall.Syscall6( |
| 83 | + syscall.SYS___SYSCTL, |
| 84 | + uintptr(_p0), |
| 85 | + uintptr(len(mib)), |
| 86 | + uintptr(unsafe.Pointer(old)), |
| 87 | + uintptr(unsafe.Pointer(oldlen)), |
| 88 | + uintptr(unsafe.Pointer(new)), |
| 89 | + uintptr(newlen)) |
| 90 | + if errno != 0 { |
| 91 | + return errno |
| 92 | + } |
| 93 | + return nil |
| 94 | +} |
| 95 | + |
| 96 | +// nametomib is a copy from "unix.nametomib()" in "unix/syscall_darwin.go". |
| 97 | +func nametomib(name string) (mib []_C_int, err error) { |
| 98 | + const CTL_MAXNAME = 0xc |
| 99 | + const siz = unsafe.Sizeof(mib[0]) |
| 100 | + |
| 101 | + // NOTE(rsc): It seems strange to set the buffer to have |
| 102 | + // size CTL_MAXNAME+2 but use only CTL_MAXNAME |
| 103 | + // as the size. I don't know why the +2 is here, but the |
| 104 | + // kernel uses +2 for its own implementation of this function. |
| 105 | + // I am scared that if we don't include the +2 here, the kernel |
| 106 | + // will silently write 2 words farther than we specify |
| 107 | + // and we'll get memory corruption. |
| 108 | + var buf [CTL_MAXNAME + 2]_C_int |
| 109 | + n := uintptr(CTL_MAXNAME) * siz |
| 110 | + |
| 111 | + p := (*byte)(unsafe.Pointer(&buf[0])) |
| 112 | + bytes, err := byteSliceFromString(name) |
| 113 | + if err != nil { |
| 114 | + return nil, err |
| 115 | + } |
| 116 | + |
| 117 | + // Magic sysctl: "setting" 0.3 to a string name |
| 118 | + // lets you read back the array of integers form. |
| 119 | + if err = sysctl([]_C_int{0, 3}, p, &n, &bytes[0], uintptr(len(name))); err != nil { |
| 120 | + return nil, err |
| 121 | + } |
| 122 | + return buf[0 : n/siz], nil |
| 123 | +} |
| 124 | + |
| 125 | +// byteSliceFromString is a simple copy of "unix.ByteSliceFromString()" |
| 126 | +func byteSliceFromString(s string) ([]byte, error) { |
| 127 | + if strings.IndexByte(s, 0) != -1 { |
| 128 | + return nil, fmt.Errorf("invalid argument in cpu.byteSliceFromString()") |
| 129 | + } |
| 130 | + a := make([]byte, len(s)+1) |
| 131 | + copy(a, s) |
| 132 | + return a, nil |
| 133 | +} |
0 commit comments