|
| 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_KEY_CPUPERF_0 = 0x5 |
| 54 | + riscv_HWPROBE_MISALIGNED_FAST = 0x3 |
| 55 | + riscv_HWPROBE_MISALIGNED_MASK = 0x7 |
| 56 | +) |
| 57 | + |
| 58 | +// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. |
| 59 | +type riscvHWProbePairs struct { |
| 60 | + key int64 |
| 61 | + value uint64 |
| 62 | +} |
| 63 | + |
| 64 | +//go:linkname riscvHWProbe |
| 65 | +func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool |
| 66 | + |
| 67 | +func osInit() { |
| 68 | + // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key |
| 69 | + // field should be initialised with one of the key constants defined above, e.g., |
| 70 | + // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. |
| 71 | + // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. |
| 72 | + |
| 73 | + pairs := []riscvHWProbePairs{ |
| 74 | + {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, |
| 75 | + {riscv_HWPROBE_KEY_CPUPERF_0, 0}, |
| 76 | + } |
| 77 | + |
| 78 | + // This call only indicates that extensions are supported if they are implemented on all cores. |
| 79 | + if !riscvHWProbe(pairs, 0) { |
| 80 | + return |
| 81 | + } |
| 82 | + |
| 83 | + if pairs[0].key != -1 { |
| 84 | + v := uint(pairs[0].value) |
| 85 | + RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) |
| 86 | + } |
| 87 | + if pairs[1].key != -1 { |
| 88 | + v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK |
| 89 | + RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST |
| 90 | + } |
| 91 | +} |
0 commit comments