cpu_linux_riscv64.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. package cpu
  5. import (
  6. "syscall"
  7. "unsafe"
  8. )
  9. // RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe
  10. // syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available.
  11. //
  12. // A note on detection of the Vector extension using HWCAP.
  13. //
  14. // Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5.
  15. // Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe
  16. // syscall is not available then neither is the Vector extension (which needs kernel support).
  17. // The riscv_hwprobe syscall should then be all we need to detect the Vector extension.
  18. // However, some RISC-V board manufacturers ship boards with an older kernel on top of which
  19. // they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe
  20. // patches. These kernels advertise support for the Vector extension using HWCAP. Falling
  21. // back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not
  22. // bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option.
  23. //
  24. // Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by
  25. // RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board
  26. // and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified
  27. // 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use
  28. // it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector
  29. // extension are binary incompatible. HWCAP can then not be used in isolation to populate the
  30. // HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0.
  31. //
  32. // There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector
  33. // specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype
  34. // register. This check would allow us to safely detect version 1.0 of the Vector extension
  35. // with HWCAP, if riscv_hwprobe were not available. However, the check cannot
  36. // be added until the assembler supports the Vector instructions.
  37. //
  38. // Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the
  39. // extensions it advertises support for are explicitly versioned. It's also worth noting that
  40. // the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba.
  41. // These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority
  42. // of RISC-V extensions.
  43. //
  44. // Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information.
  45. // golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must
  46. // reproduce the constants, types and functions needed to make the riscv_hwprobe syscall
  47. // here.
  48. const (
  49. // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go.
  50. riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4
  51. riscv_HWPROBE_IMA_C = 0x2
  52. riscv_HWPROBE_IMA_V = 0x4
  53. riscv_HWPROBE_EXT_ZBA = 0x8
  54. riscv_HWPROBE_EXT_ZBB = 0x10
  55. riscv_HWPROBE_EXT_ZBS = 0x20
  56. riscv_HWPROBE_EXT_ZVBB = 0x20000
  57. riscv_HWPROBE_EXT_ZVBC = 0x40000
  58. riscv_HWPROBE_EXT_ZVKB = 0x80000
  59. riscv_HWPROBE_EXT_ZVKG = 0x100000
  60. riscv_HWPROBE_EXT_ZVKNED = 0x200000
  61. riscv_HWPROBE_EXT_ZVKNHB = 0x800000
  62. riscv_HWPROBE_EXT_ZVKSED = 0x1000000
  63. riscv_HWPROBE_EXT_ZVKSH = 0x2000000
  64. riscv_HWPROBE_EXT_ZVKT = 0x4000000
  65. riscv_HWPROBE_KEY_CPUPERF_0 = 0x5
  66. riscv_HWPROBE_MISALIGNED_FAST = 0x3
  67. riscv_HWPROBE_MISALIGNED_MASK = 0x7
  68. )
  69. const (
  70. // sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go.
  71. sys_RISCV_HWPROBE = 258
  72. )
  73. // riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go.
  74. type riscvHWProbePairs struct {
  75. key int64
  76. value uint64
  77. }
  78. const (
  79. // CPU features
  80. hwcap_RISCV_ISA_C = 1 << ('C' - 'A')
  81. )
  82. func doinit() {
  83. // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key
  84. // field should be initialised with one of the key constants defined above, e.g.,
  85. // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value.
  86. // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0.
  87. pairs := []riscvHWProbePairs{
  88. {riscv_HWPROBE_KEY_IMA_EXT_0, 0},
  89. {riscv_HWPROBE_KEY_CPUPERF_0, 0},
  90. }
  91. // This call only indicates that extensions are supported if they are implemented on all cores.
  92. if riscvHWProbe(pairs, 0) {
  93. if pairs[0].key != -1 {
  94. v := uint(pairs[0].value)
  95. RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C)
  96. RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V)
  97. RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA)
  98. RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB)
  99. RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS)
  100. RISCV64.HasZvbb = isSet(v, riscv_HWPROBE_EXT_ZVBB)
  101. RISCV64.HasZvbc = isSet(v, riscv_HWPROBE_EXT_ZVBC)
  102. RISCV64.HasZvkb = isSet(v, riscv_HWPROBE_EXT_ZVKB)
  103. RISCV64.HasZvkg = isSet(v, riscv_HWPROBE_EXT_ZVKG)
  104. RISCV64.HasZvkt = isSet(v, riscv_HWPROBE_EXT_ZVKT)
  105. // Cryptography shorthand extensions
  106. RISCV64.HasZvkn = isSet(v, riscv_HWPROBE_EXT_ZVKNED) &&
  107. isSet(v, riscv_HWPROBE_EXT_ZVKNHB) && RISCV64.HasZvkb && RISCV64.HasZvkt
  108. RISCV64.HasZvknc = RISCV64.HasZvkn && RISCV64.HasZvbc
  109. RISCV64.HasZvkng = RISCV64.HasZvkn && RISCV64.HasZvkg
  110. RISCV64.HasZvks = isSet(v, riscv_HWPROBE_EXT_ZVKSED) &&
  111. isSet(v, riscv_HWPROBE_EXT_ZVKSH) && RISCV64.HasZvkb && RISCV64.HasZvkt
  112. RISCV64.HasZvksc = RISCV64.HasZvks && RISCV64.HasZvbc
  113. RISCV64.HasZvksg = RISCV64.HasZvks && RISCV64.HasZvkg
  114. }
  115. if pairs[1].key != -1 {
  116. v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK
  117. RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST
  118. }
  119. }
  120. // Let's double check with HWCAP if the C extension does not appear to be supported.
  121. // This may happen if we're running on a kernel older than 6.4.
  122. if !RISCV64.HasC {
  123. RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C)
  124. }
  125. }
  126. func isSet(hwc uint, value uint) bool {
  127. return hwc&value != 0
  128. }
  129. // riscvHWProbe is a simplified version of the generated wrapper function found in
  130. // golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the
  131. // cpuCount and cpus parameters which we do not need. We always want to pass 0 for
  132. // these parameters here so the kernel only reports the extensions that are present
  133. // on all cores.
  134. func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool {
  135. var _zero uintptr
  136. var p0 unsafe.Pointer
  137. if len(pairs) > 0 {
  138. p0 = unsafe.Pointer(&pairs[0])
  139. } else {
  140. p0 = unsafe.Pointer(&_zero)
  141. }
  142. _, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0)
  143. return e1 == 0
  144. }