cpu_riscv64_linux.mx raw

   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