-
Notifications
You must be signed in to change notification settings - Fork 580
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cpu: add support for detecting RISC-V extensions
Add a RISCV64 variable to cpu that indicates both the presence of RISC-V extensions and performance information about the underlying RISC-V cores. The variable is only populated with non false values on Linux. The detection code first attempts to use the riscv_hwprobe syscall introduced in Linux 6.4, falling back to HWCAP if riscv_hwprobe is not supported. The patch can detect the C, V, Zba, Zbb and Zbs extensions. V, Zba, Zbb and Zbs can only be detected on a 6.5 kernel or later (without backports). Updates golang/go#61416 Change-Id: I40f92724ee3d337c06bdc559ff0b18a8f6bfda9f Reviewed-on: https://go-review.googlesource.com/c/sys/+/605815 Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Joel Sing <[email protected]> Reviewed-by: Meng Zhuo <[email protected]>
- Loading branch information
Showing
5 changed files
with
175 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// Copyright 2024 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package cpu | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
// RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe | ||
// syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available. | ||
// | ||
// A note on detection of the Vector extension using HWCAP. | ||
// | ||
// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. | ||
// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe | ||
// syscall is not available then neither is the Vector extension (which needs kernel support). | ||
// The riscv_hwprobe syscall should then be all we need to detect the Vector extension. | ||
// However, some RISC-V board manufacturers ship boards with an older kernel on top of which | ||
// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe | ||
// patches. These kernels advertise support for the Vector extension using HWCAP. Falling | ||
// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not | ||
// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. | ||
// | ||
// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by | ||
// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board | ||
// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified | ||
// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use | ||
// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector | ||
// extension are binary incompatible. HWCAP can then not be used in isolation to populate the | ||
// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. | ||
// | ||
// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector | ||
// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype | ||
// register. This check would allow us to safely detect version 1.0 of the Vector extension | ||
// with HWCAP, if riscv_hwprobe were not available. However, the check cannot | ||
// be added until the assembler supports the Vector instructions. | ||
// | ||
// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the | ||
// extensions it advertises support for are explicitly versioned. It's also worth noting that | ||
// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba. | ||
// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority | ||
// of RISC-V extensions. | ||
// | ||
// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information. | ||
|
||
// golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must | ||
// reproduce the constants, types and functions needed to make the riscv_hwprobe syscall | ||
// here. | ||
|
||
const ( | ||
// Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. | ||
riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 | ||
riscv_HWPROBE_IMA_C = 0x2 | ||
riscv_HWPROBE_IMA_V = 0x4 | ||
riscv_HWPROBE_EXT_ZBA = 0x8 | ||
riscv_HWPROBE_EXT_ZBB = 0x10 | ||
riscv_HWPROBE_EXT_ZBS = 0x20 | ||
riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 | ||
riscv_HWPROBE_MISALIGNED_FAST = 0x3 | ||
riscv_HWPROBE_MISALIGNED_MASK = 0x7 | ||
) | ||
|
||
const ( | ||
// sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go. | ||
sys_RISCV_HWPROBE = 258 | ||
) | ||
|
||
// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. | ||
type riscvHWProbePairs struct { | ||
key int64 | ||
value uint64 | ||
} | ||
|
||
const ( | ||
// CPU features | ||
hwcap_RISCV_ISA_C = 1 << ('C' - 'A') | ||
) | ||
|
||
func doinit() { | ||
// A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key | ||
// field should be initialised with one of the key constants defined above, e.g., | ||
// RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. | ||
// If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. | ||
|
||
pairs := []riscvHWProbePairs{ | ||
{riscv_HWPROBE_KEY_IMA_EXT_0, 0}, | ||
{riscv_HWPROBE_KEY_CPUPERF_0, 0}, | ||
} | ||
|
||
// This call only indicates that extensions are supported if they are implemented on all cores. | ||
if riscvHWProbe(pairs, 0) { | ||
if pairs[0].key != -1 { | ||
v := uint(pairs[0].value) | ||
RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C) | ||
RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) | ||
RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA) | ||
RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) | ||
RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS) | ||
} | ||
if pairs[1].key != -1 { | ||
v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK | ||
RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST | ||
} | ||
} | ||
|
||
// Let's double check with HWCAP if the C extension does not appear to be supported. | ||
// This may happen if we're running on a kernel older than 6.4. | ||
|
||
if !RISCV64.HasC { | ||
RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C) | ||
} | ||
} | ||
|
||
func isSet(hwc uint, value uint) bool { | ||
return hwc&value != 0 | ||
} | ||
|
||
// riscvHWProbe is a simplified version of the generated wrapper function found in | ||
// golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the | ||
// cpuCount and cpus parameters which we do not need. We always want to pass 0 for | ||
// these parameters here so the kernel only reports the extensions that are present | ||
// on all cores. | ||
func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool { | ||
var _zero uintptr | ||
var p0 unsafe.Pointer | ||
if len(pairs) > 0 { | ||
p0 = unsafe.Pointer(&pairs[0]) | ||
} else { | ||
p0 = unsafe.Pointer(&_zero) | ||
} | ||
|
||
_, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0) | ||
return e1 == 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters