Source file src/internal/cpu/cpu_riscv64_linux.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 //go:build riscv64 && linux 6 7 package cpu 8 9 import _ "unsafe" 10 11 // RISC-V extension discovery code for Linux. 12 // 13 // A note on detection of the Vector extension using HWCAP. 14 // 15 // Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. 16 // Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe 17 // syscall is not available then neither is the Vector extension (which needs kernel support). 18 // The riscv_hwprobe syscall should then be all we need to detect the Vector extension. 19 // However, some RISC-V board manufacturers ship boards with an older kernel on top of which 20 // they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe 21 // patches. These kernels advertise support for the Vector extension using HWCAP. Falling 22 // back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not 23 // bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. 24 // 25 // Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by 26 // RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board 27 // and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified 28 // 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use 29 // it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector 30 // extension are binary incompatible. HWCAP can then not be used in isolation to populate the 31 // HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. 32 // Go will only support the ratified versions >= 1.0 and so any vector code it might generate 33 // would crash on a Scaleway RV1 instance or a Lichee Pi 4a, if allowed to run. 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., Zvbb. 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 const ( 50 // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. 51 riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 52 riscv_HWPROBE_IMA_V = 0x4 53 riscv_HWPROBE_EXT_ZBB = 0x10 54 riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 55 riscv_HWPROBE_MISALIGNED_FAST = 0x3 56 riscv_HWPROBE_MISALIGNED_MASK = 0x7 57 ) 58 59 // riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. 60 type riscvHWProbePairs struct { 61 key int64 62 value uint64 63 } 64 65 //go:linkname riscvHWProbe 66 func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool 67 68 func osInit() { 69 // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key 70 // field should be initialised with one of the key constants defined above, e.g., 71 // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. 72 // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. 73 74 pairs := []riscvHWProbePairs{ 75 {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, 76 {riscv_HWPROBE_KEY_CPUPERF_0, 0}, 77 } 78 79 // This call only indicates that extensions are supported if they are implemented on all cores. 80 if !riscvHWProbe(pairs, 0) { 81 return 82 } 83 84 if pairs[0].key != -1 { 85 v := uint(pairs[0].value) 86 RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) 87 RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) 88 } 89 if pairs[1].key != -1 { 90 v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK 91 RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST 92 } 93 } 94