Source file src/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go
1 // Copyright 2024 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 package cpu 6 7 import ( 8 "syscall" 9 "unsafe" 10 ) 11 12 // RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe 13 // syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available. 14 // 15 // A note on detection of the Vector extension using HWCAP. 16 // 17 // Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. 18 // Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe 19 // syscall is not available then neither is the Vector extension (which needs kernel support). 20 // The riscv_hwprobe syscall should then be all we need to detect the Vector extension. 21 // However, some RISC-V board manufacturers ship boards with an older kernel on top of which 22 // they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe 23 // patches. These kernels advertise support for the Vector extension using HWCAP. Falling 24 // back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not 25 // bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. 26 // 27 // Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by 28 // RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board 29 // and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified 30 // 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use 31 // it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector 32 // extension are binary incompatible. HWCAP can then not be used in isolation to populate the 33 // HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. 34 // 35 // There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector 36 // specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype 37 // register. This check would allow us to safely detect version 1.0 of the Vector extension 38 // with HWCAP, if riscv_hwprobe were not available. However, the check cannot 39 // be added until the assembler supports the Vector instructions. 40 // 41 // Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the 42 // extensions it advertises support for are explicitly versioned. It's also worth noting that 43 // the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba. 44 // These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority 45 // of RISC-V extensions. 46 // 47 // Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information. 48 49 // golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must 50 // reproduce the constants, types and functions needed to make the riscv_hwprobe syscall 51 // here. 52 53 const ( 54 // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. 55 riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 56 riscv_HWPROBE_IMA_C = 0x2 57 riscv_HWPROBE_IMA_V = 0x4 58 riscv_HWPROBE_EXT_ZBA = 0x8 59 riscv_HWPROBE_EXT_ZBB = 0x10 60 riscv_HWPROBE_EXT_ZBS = 0x20 61 riscv_HWPROBE_EXT_ZVBB = 0x20000 62 riscv_HWPROBE_EXT_ZVBC = 0x40000 63 riscv_HWPROBE_EXT_ZVKB = 0x80000 64 riscv_HWPROBE_EXT_ZVKG = 0x100000 65 riscv_HWPROBE_EXT_ZVKNED = 0x200000 66 riscv_HWPROBE_EXT_ZVKNHB = 0x800000 67 riscv_HWPROBE_EXT_ZVKSED = 0x1000000 68 riscv_HWPROBE_EXT_ZVKSH = 0x2000000 69 riscv_HWPROBE_EXT_ZVKT = 0x4000000 70 riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 71 riscv_HWPROBE_MISALIGNED_FAST = 0x3 72 riscv_HWPROBE_MISALIGNED_MASK = 0x7 73 ) 74 75 const ( 76 // sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go. 77 sys_RISCV_HWPROBE = 258 78 ) 79 80 // riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. 81 type riscvHWProbePairs struct { 82 key int64 83 value uint64 84 } 85 86 const ( 87 // CPU features 88 hwcap_RISCV_ISA_C = 1 << ('C' - 'A') 89 ) 90 91 func doinit() { 92 // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key 93 // field should be initialised with one of the key constants defined above, e.g., 94 // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. 95 // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. 96 97 pairs := []riscvHWProbePairs{ 98 {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, 99 {riscv_HWPROBE_KEY_CPUPERF_0, 0}, 100 } 101 102 // This call only indicates that extensions are supported if they are implemented on all cores. 103 if riscvHWProbe(pairs, 0) { 104 if pairs[0].key != -1 { 105 v := uint(pairs[0].value) 106 RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C) 107 RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) 108 RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA) 109 RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) 110 RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS) 111 RISCV64.HasZvbb = isSet(v, riscv_HWPROBE_EXT_ZVBB) 112 RISCV64.HasZvbc = isSet(v, riscv_HWPROBE_EXT_ZVBC) 113 RISCV64.HasZvkb = isSet(v, riscv_HWPROBE_EXT_ZVKB) 114 RISCV64.HasZvkg = isSet(v, riscv_HWPROBE_EXT_ZVKG) 115 RISCV64.HasZvkt = isSet(v, riscv_HWPROBE_EXT_ZVKT) 116 // Cryptography shorthand extensions 117 RISCV64.HasZvkn = isSet(v, riscv_HWPROBE_EXT_ZVKNED) && 118 isSet(v, riscv_HWPROBE_EXT_ZVKNHB) && RISCV64.HasZvkb && RISCV64.HasZvkt 119 RISCV64.HasZvknc = RISCV64.HasZvkn && RISCV64.HasZvbc 120 RISCV64.HasZvkng = RISCV64.HasZvkn && RISCV64.HasZvkg 121 RISCV64.HasZvks = isSet(v, riscv_HWPROBE_EXT_ZVKSED) && 122 isSet(v, riscv_HWPROBE_EXT_ZVKSH) && RISCV64.HasZvkb && RISCV64.HasZvkt 123 RISCV64.HasZvksc = RISCV64.HasZvks && RISCV64.HasZvbc 124 RISCV64.HasZvksg = RISCV64.HasZvks && RISCV64.HasZvkg 125 } 126 if pairs[1].key != -1 { 127 v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK 128 RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST 129 } 130 } 131 132 // Let's double check with HWCAP if the C extension does not appear to be supported. 133 // This may happen if we're running on a kernel older than 6.4. 134 135 if !RISCV64.HasC { 136 RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C) 137 } 138 } 139 140 func isSet(hwc uint, value uint) bool { 141 return hwc&value != 0 142 } 143 144 // riscvHWProbe is a simplified version of the generated wrapper function found in 145 // golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the 146 // cpuCount and cpus parameters which we do not need. We always want to pass 0 for 147 // these parameters here so the kernel only reports the extensions that are present 148 // on all cores. 149 func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool { 150 var _zero uintptr 151 var p0 unsafe.Pointer 152 if len(pairs) > 0 { 153 p0 = unsafe.Pointer(&pairs[0]) 154 } else { 155 p0 = unsafe.Pointer(&_zero) 156 } 157 158 _, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0) 159 return e1 == 0 160 } 161