Browse Source

sys/info: more CPU feature detection for RISC-V

laytan 11 months ago
parent
commit
35731e66cf

+ 94 - 27
core/sys/info/cpu_linux_riscv64.odin

@@ -8,35 +8,102 @@ import "core:sys/linux"
 
 @(init, private)
 init_cpu_features :: proc() {
-	fd, err := linux.open("/proc/self/auxv", {})
-	if err != .NONE { return }
-	defer linux.close(fd)
-
-	// This is probably enough right?
-	buf: [4096]byte
-	n, rerr := linux.read(fd, buf[:])
-	if rerr != .NONE || n == 0 { return }
-
-	ulong     :: u64
-	AT_HWCAP  :: 16
-
-	// TODO: using these we could get more information than just the basics.
-	// AT_HWCAP2 :: 26
-	// AT_HWCAP3 :: 29
-	// AT_HWCAP4 :: 30
-
-	auxv := buf[:n]
-	for len(auxv) >= size_of(ulong)*2 {
-		key := intrinsics.unaligned_load((^ulong)(&auxv[0]))
-		val := intrinsics.unaligned_load((^ulong)(&auxv[size_of(ulong)]))
-		auxv = auxv[2*size_of(ulong):]
-
-		if key != AT_HWCAP {
-			continue
+	_features: CPU_Features
+	defer cpu_features = _features
+
+	HWCAP_Bits :: enum u64 {
+		I = 'I' - 'A',
+		M = 'M' - 'A',
+		A = 'A' - 'A',
+		F = 'F' - 'A',
+		D = 'D' - 'A',
+		C = 'C' - 'A',
+		V = 'V' - 'A',
+	}
+	HWCAP :: bit_set[HWCAP_Bits; u64]
+
+	// Read HWCAP for base extensions, we can get this info through hwprobe too but that is Linux 6.4+ only.
+	{
+		fd, err := linux.open("/proc/self/auxv", {})
+		if err != .NONE { return }
+		defer linux.close(fd)
+
+		// This is probably enough right?
+		buf: [4096]byte
+		n, rerr := linux.read(fd, buf[:])
+		if rerr != .NONE || n == 0 { return }
+
+		ulong     :: u64
+		AT_HWCAP  :: 16
+
+		auxv := buf[:n]
+		for len(auxv) >= size_of(ulong)*2 {
+			key := intrinsics.unaligned_load((^ulong)(&auxv[0]))
+			val := intrinsics.unaligned_load((^ulong)(&auxv[size_of(ulong)]))
+			auxv = auxv[2*size_of(ulong):]
+
+			if key != AT_HWCAP {
+				continue
+			}
+
+			cap := transmute(HWCAP)(val)
+			if .I in cap {
+				_features += { .I }
+			}
+			if .M in cap {
+				_features += { .M }
+			}
+			if .A in cap {
+				_features += { .A }
+			}
+			if .F in cap {
+				_features += { .F }
+			}
+			if .D in cap {
+				_features += { .D }
+			}
+			if .C in cap {
+				_features += { .C }
+			}
+			if .V in cap {
+				_features += { .V }
+			}
+			break
 		}
+	}
 
-		cpu_features = transmute(CPU_Features)(val)
-		break
+	// hwprobe for other features.
+	{
+		pairs := []linux.RISCV_HWProbe{
+			{ key = .IMA_EXT_0 },
+			{ key = .CPUPERF_0 },
+			{ key = .MISALIGNED_SCALAR_PERF },
+		}
+		err := linux.riscv_hwprobe(raw_data(pairs), len(pairs), 0, nil, {})
+		if err != nil {
+			assert(err == .ENOSYS, "unexpected error from riscv_hwprobe()")
+			return
+		}
+
+		assert(pairs[0].key == .IMA_EXT_0)
+		exts := pairs[0].value.ima_ext_0
+		exts -= { .FD, .C, .V }
+		_features += transmute(CPU_Features)exts
+
+		if pairs[2].key == .MISALIGNED_SCALAR_PERF {
+			if pairs[2].value.misaligned_scalar_perf == .FAST {
+				_features += { .Misaligned_Supported, .Misaligned_Fast }
+			} else if pairs[2].value.misaligned_scalar_perf != .UNSUPPORTED {
+				_features += { .Misaligned_Supported }
+			}
+		} else {
+			assert(pairs[1].key == .CPUPERF_0)
+			if .FAST in pairs[1].value.cpu_perf_0 {
+				_features += { .Misaligned_Supported, .Misaligned_Fast }
+			} else if .UNSUPPORTED not_in pairs[1].value.cpu_perf_0 {
+				_features += { .Misaligned_Supported }
+			}
+		}
 	}
 }
 

+ 91 - 7
core/sys/info/cpu_riscv64.odin

@@ -1,13 +1,97 @@
 package sysinfo
 
 CPU_Feature :: enum u64 {
-	I = 'I' - 'A', // Base features, don't think this is ever not here.
-	M = 'M' - 'A', // Integer multiplication and division, currently required by Odin.
-	A = 'A' - 'A', // Atomics.
-	F = 'F' - 'A', // Single precision floating point, currently required by Odin.
-	D = 'D' - 'A', // Double precision floating point, currently required by Odin.
-	C = 'C' - 'A', // Compressed instructions.
-	V = 'V' - 'A', // Vector operations.
+	// Bit-Manipulation ISA Extensions v1.
+	Zba = 3,
+	Zbb,
+	Zbs,
+
+	// CMOs (ratified).
+	Zicboz,
+
+	// Bit-Manipulation ISA Extensions v1.
+	Zbc,
+
+	// Scalar Crypto ISA extensions v1.
+	Zbkb,
+	Zbkc,
+	Zbkx,
+	Zknd,
+	Zkne,
+	Zknh,
+	Zksed,
+	Zksh,
+	Zkt,
+
+	// Cryptography Extensions Volume II v1.
+	Zvbb,
+	Zvbc,
+	Zvkb,
+	Zvkg,
+	Zvkned,
+	Zvknha,
+	Zvknhb,
+	Zvksed,
+	Zvksh,
+	Zvkt,
+
+	// ISA Manual v1.
+	Zfh,
+	Zfhmin,
+	Zihintntl,
+
+	// ISA manual (ratified).
+	Zvfh,
+	Zvfhmin,
+	Zfa,
+	Ztso,
+
+	// Atomic Compare-and-Swap Instructions Manual (ratified).
+	Zacas,
+	Zicond,
+
+	// ISA manual (ratified).
+	Zihintpause,
+
+	// Vector Extensions Manual v1.
+	Zve32x,
+	Zve32f,
+	Zve64x,
+	Zve64f,
+	Zve64d,
+
+	// ISA manual (ratified).
+	Zimop,
+
+	// Code Size Reduction (ratified).
+	Zca,
+	Zcb,
+	Zcd,
+	Zcf,
+
+	// ISA manual (ratified).
+	Zcmop,
+	Zawrs,
+
+ 	// Base features, don't think this is ever not here.
+	I,
+ 	// Integer multiplication and division, currently required by Odin.
+	M,
+ 	// Atomics.
+	A,
+ 	// Single precision floating point, currently required by Odin.
+	F,
+ 	// Double precision floating point, currently required by Odin.
+	D,
+ 	// Compressed instructions.
+	C,
+ 	// Vector operations.
+	V,
+
+	// Indicates Misaligned Scalar Loads will not trap the program.
+	Misaligned_Supported,
+	// Indicates Hardware Support for Misaligned Scalar Loads.
+	Misaligned_Fast,
 }
 
 CPU_Features :: distinct bit_set[CPU_Feature; u64]

+ 84 - 0
core/sys/linux/bits.odin

@@ -1839,3 +1839,87 @@ Execveat_Flags_Bits :: enum {
 	AT_SYMLINK_NOFOLLOW = 8,
 	AT_EMPTY_PATH       = 12,
 }
+
+RISCV_HWProbe_Key :: enum i64 {
+	UNSUPPORTED            = -1,
+	MVENDORID              = 0,
+	MARCHID                = 1,
+	MIMPID                 = 2,
+	BASE_BEHAVIOR          = 3,
+	IMA_EXT_0              = 4,
+ 	// Deprecated, try `.MISALIGNED_SCALAR_PERF` first, if that is `.UNSUPPORTED`, use this.
+	CPUPERF_0              = 5,
+	ZICBOZ_BLOCK_SIZE      = 6,
+	HIGHEST_VIRT_ADDRESS   = 7,
+	TIME_CSR_FREQ          = 8,
+	MISALIGNED_SCALAR_PERF = 9,
+}
+
+RISCV_HWProbe_Flags_Bits :: enum {
+	WHICH_CPUS,
+}
+
+RISCV_HWProbe_Base_Behavior_Bits :: enum {
+	IMA,
+}
+
+RISCV_HWProbe_IMA_Ext_0_Bits :: enum {
+	FD,
+	C,
+	V,
+	EXT_ZBA,
+	EXT_ZBB,
+	EXT_ZBS,
+	EXT_ZICBOZ,
+	EXT_ZBC,
+	EXT_ZBKB,
+	EXT_ZBKC,
+	EXT_ZBKX,
+	EXT_ZKND,
+	EXT_ZKNE,
+	EXT_ZKNH,
+	EXT_ZKSED,
+	EXT_ZKSH,
+	EXT_ZKT,
+	EXT_ZVBB,
+	EXT_ZVBC,
+	EXT_ZVKB,
+	EXT_ZVKG,
+	EXT_ZVKNED,
+	EXT_ZVKNHA,
+	EXT_ZVKNHB,
+	EXT_ZVKSED,
+	EXT_ZVKSH,
+	EXT_ZVKT,
+	EXT_ZFH,
+	EXT_ZFHMIN,
+	EXT_ZIHINTNTL,
+	EXT_ZVFH,
+	EXT_ZVFHMIN,
+	EXT_ZFA,
+	EXT_ZTSO,
+	EXT_ZACAS,
+	EXT_ZICOND,
+	EXT_ZIHINTPAUSE,
+	EXT_ZVE32X,
+	EXT_ZVE32F,
+	EXT_ZVE64X,
+	EXT_ZVE64F,
+	EXT_ZVE64D,
+	EXT_ZIMOP,
+	EXT_ZCA,
+	EXT_ZCB,
+	EXT_ZCD,
+	EXT_ZCF,
+	EXT_ZCMOP,
+	EXT_ZAWRS,
+}
+
+RISCV_HWProbe_Misaligned_Scalar_Perf :: enum {
+	UNKNOWN,
+	EMULATED,
+	SLOW,
+	FAST,
+	UNSUPPORTED,
+}
+

+ 15 - 0
core/sys/linux/sys.odin

@@ -2993,3 +2993,18 @@ epoll_pwait2 :: proc(epfd: Fd, events: [^]EPoll_Event, count: i32, timeout: ^Tim
 // TODO(flysand): fchmodat2
 
 // TODO(flysand): map_shadow_stack
+
+when ODIN_ARCH == .riscv64 {
+	/*
+		Probe for RISC-V Hardware Support.
+		Available since Linux 6.4.
+
+		TODO: cpu_set_t
+
+		See: https://docs.kernel.org/arch/riscv/hwprobe.html
+	*/
+	riscv_hwprobe :: proc "contextless" (pairs: [^]RISCV_HWProbe, pair_count: uint, cpu_count: uint, cpus: rawptr /* cpu_set_t */, flags: RISCV_HWProbe_Flags) -> Errno {
+		ret := syscall(SYS_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, transmute(u32)flags)
+		return Errno(-ret)
+	}
+}

+ 1 - 0
core/sys/linux/syscall_riscv64.odin

@@ -248,6 +248,7 @@ SYS_rt_tgsigqueueinfo            :: uintptr(240)
 SYS_perf_event_open              :: uintptr(241)
 SYS_accept4                      :: uintptr(242)
 SYS_recvmmsg                     :: uintptr(243)
+SYS_riscv_hwprobe                :: uintptr(258)
 SYS_wait4                        :: uintptr(260)
 SYS_prlimit64                    :: uintptr(261)
 SYS_fanotify_init                :: uintptr(262)

+ 17 - 0
core/sys/linux/types.odin

@@ -1335,3 +1335,20 @@ EPoll_Event :: struct #packed {
 	Flags for execveat(2) syscall.
 */
 Execveat_Flags :: bit_set[Execveat_Flags_Bits; i32]
+
+RISCV_HWProbe_Flags         :: bit_set[RISCV_HWProbe_Flags_Bits; u32]
+RISCV_HWProbe_CPU_Perf_0    :: bit_set[RISCV_HWProbe_Misaligned_Scalar_Perf; u64]
+RISCV_HWProbe_Base_Behavior :: bit_set[RISCV_HWProbe_Base_Behavior_Bits; u64]
+RISCV_HWProbe_IMA_Ext_0     :: bit_set[RISCV_HWProbe_IMA_Ext_0_Bits; u64]
+
+RISCV_HWProbe :: struct {
+ 	// set to `.UNSUPPORTED` by the kernel if that is the case.
+	key:   RISCV_HWProbe_Key,
+	value: struct #raw_union {
+		base_behavior:          RISCV_HWProbe_Base_Behavior,
+		ima_ext_0:              RISCV_HWProbe_IMA_Ext_0,
+		cpu_perf_0:             RISCV_HWProbe_CPU_Perf_0,
+		misaligned_scalar_perf: RISCV_HWProbe_Misaligned_Scalar_Perf,
+		raw:                    u64,
+	},
+}