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  

View as plain text