From 35731e66cf406144c83c7d9b6db576539b0e13fb Mon Sep 17 00:00:00 2001 From: laytan Date: Mon, 2 Sep 2024 13:44:22 +0000 Subject: sys/info: more CPU feature detection for RISC-V --- core/sys/info/cpu_linux_riscv64.odin | 121 +++++++++++++++++++++++++++-------- core/sys/info/cpu_riscv64.odin | 98 ++++++++++++++++++++++++++-- core/sys/linux/bits.odin | 84 ++++++++++++++++++++++++ core/sys/linux/sys.odin | 15 +++++ core/sys/linux/syscall_riscv64.odin | 1 + core/sys/linux/types.odin | 17 +++++ 6 files changed, 302 insertions(+), 34 deletions(-) diff --git a/core/sys/info/cpu_linux_riscv64.odin b/core/sys/info/cpu_linux_riscv64.odin index 0f109e7ba..0b64c3725 100644 --- a/core/sys/info/cpu_linux_riscv64.odin +++ b/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 } + } + } } } diff --git a/core/sys/info/cpu_riscv64.odin b/core/sys/info/cpu_riscv64.odin index 754110911..c3319c48c 100644 --- a/core/sys/info/cpu_riscv64.odin +++ b/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] diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin index f78891bc8..8a4a6dd7a 100644 --- a/core/sys/linux/bits.odin +++ b/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, +} + diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index 6e4194be7..5ee07a93d 100644 --- a/core/sys/linux/sys.odin +++ b/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) + } +} diff --git a/core/sys/linux/syscall_riscv64.odin b/core/sys/linux/syscall_riscv64.odin index ce374312e..d2fd0c2ff 100644 --- a/core/sys/linux/syscall_riscv64.odin +++ b/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) diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 3f873f96c..0e5b8218b 100644 --- a/core/sys/linux/types.odin +++ b/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, + }, +} -- cgit v1.2.3