Last active
February 20, 2026 02:02
-
-
Save teknoraver/6a8313e8c4f2ed804c9f91b9c3891b43 to your computer and use it in GitHub Desktop.
Prints supported RISC-V extensions using the riscv_hwprobe() syscall
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (C) 2026 Matteo Croce <matteo@teknoraver.net> | |
| * | |
| * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation, either version 3 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| */ | |
| /* | |
| * Prints supported RISC-V extensions using the riscv_hwprobe() syscall. | |
| * Requires Linux >= 6.4 and a RISC-V target. | |
| * | |
| * Build: make riscv_hwprobe | |
| * Run: ./riscv_hwprobe | |
| */ | |
| /* Requires linux-libc-dev >= 6.8 (or equivalent kernel-headers). | |
| * Extensions guarded with #ifdef were added in later headers. */ | |
| #include <stdio.h> | |
| #include <stdint.h> | |
| #include <unistd.h> | |
| #include <sys/hwprobe.h> | |
| #define ANSI_GREEN "\033[32m" | |
| #define ANSI_RED "\033[31m" | |
| #define ANSI_RESET "\033[0m" | |
| #define _hwprobe(p, n) __riscv_hwprobe(p, n, 0, NULL, 0) | |
| #define print_sep() puts("-------------------------------------------------------------") | |
| struct ext_info { | |
| const char *name; | |
| const char *desc; | |
| }; | |
| /* IMA_EXT_0 extensions (key 4) — array index == bit position */ | |
| static const struct ext_info exts0[] = { | |
| /* 0 */ | |
| { "FD", "Single/double-precision float: FLW, FLD, FADD.S/D, ..." }, | |
| { "C", "Compressed 16-bit instructions: C.ADD, C.LW, ..." }, | |
| { "V", "Vector: VSETVLI, VLE/VSE, VADD, VMUL, ..." }, | |
| { "Zba", "Address generation: SH1ADD, SH2ADD, SH3ADD" }, | |
| { "Zbb", "Basic bit-manip: CLZ, CTZ, CPOP, MIN, MAX, ROL, ROR, ..." }, | |
| { "Zbs", "Single-bit: BSET, BCLR, BINV, BEXT" }, | |
| { "Zicboz", "Cache-block zero: CBO.ZERO" }, | |
| { "Zbc", "Carry-less multiply: CLMUL, CLMULH, CLMULR" }, | |
| { "Zbkb", "Bit-manip for crypto: PACK, BREV8, ..." }, | |
| { "Zbkc", "Carry-less multiply for crypto" }, | |
| /* 10 */ | |
| { "Zbkx", "Crossbar permute: XPERM4, XPERM8" }, | |
| { "Zknd", "AES decrypt: AES32DSI, AES64DS, AES64DSM, ..." }, | |
| { "Zkne", "AES encrypt: AES32ESI, AES64ES, AES64ESM, ..." }, | |
| { "Zknh", "SHA hash: SHA256SIG0/1, SHA512SIG0/1, ..." }, | |
| { "Zksed", "SM4 block cipher: SM4ED, SM4KS" }, | |
| { "Zksh", "SM3 hash: SM3P0, SM3P1" }, | |
| { "Zkt", "Data-independent execution latency guarantee" }, | |
| { "Zvbb", "Vector bit-manip: VBREV8, VREV8, VROL, VROR, ..." }, | |
| { "Zvbc", "Vector carry-less multiply: VCLMUL, VCLMULH" }, | |
| { "Zvkb", "Vector crypto bit-manip (subset of Zvbb)" }, | |
| /* 20 */ | |
| { "Zvkg", "Vector GCM/GMAC: VGHSH, VGMUL" }, | |
| { "Zvkned", "Vector AES block cipher: VAESDF, VAESDM, ..." }, | |
| { "Zvknha", "Vector SHA-256 hash: VSHA2CH, VSHA2CL, VSHA2MS" }, | |
| { "Zvknhb", "Vector SHA-256/512 hash" }, | |
| { "Zvksed", "Vector SM4 block cipher: VSM4K, VSM4R" }, | |
| { "Zvksh", "Vector SM3 hash: VSM3C, VSM3ME" }, | |
| { "Zvkt", "Vector data-independent execution latency" }, | |
| { "Zfh", "Half-precision float: FLH, FSH, FADD.H, FMUL.H, ..." }, | |
| { "Zfhmin", "Half-precision load/store/convert only" }, | |
| { "Zihintntl", "Non-temporal locality hints: NTL.P1, NTL.ALL, ..." }, | |
| /* 30 */ | |
| { "Zvfh", "Vector half-precision float" }, | |
| { "Zvfhmin", "Vector half-precision float (min subset)" }, | |
| { "Zfa", "Additional float: FLI, FMINM, FMAXM, FROUND, ..." }, | |
| { "Ztso", "Total store ordering memory model" }, | |
| { "Zacas", "Atomic compare-and-swap: AMOCAS.W/D/Q" }, | |
| { "Zicond", "Integer conditional: CZERO.EQZ, CZERO.NEZ" }, | |
| { "Zihintpause","Pause hint: PAUSE" }, | |
| { "Zve32x", "Vector embedded 32-bit integer" }, | |
| { "Zve32f", "Vector embedded 32-bit float" }, | |
| { "Zve64x", "Vector embedded 64-bit integer" }, | |
| /* 40 */ | |
| { "Zve64f", "Vector embedded 64-bit float" }, | |
| { "Zve64d", "Vector embedded 64-bit double" }, | |
| { "Zimop", "May-be-ops (reserved NOP space)" }, | |
| { "Zca", "Compressed integer (subset of C)" }, | |
| { "Zcb", "Compressed basic ops: C.ZEXT.B, C.MUL, ..." }, | |
| { "Zcd", "Compressed double-float loads/stores" }, | |
| { "Zcf", "Compressed single-float loads/stores (RV32 only)" }, | |
| { "Zcmop", "Compressed may-be-ops" }, | |
| { "Zawrs", "Wait-on-reservation-set: WRS.NTO, WRS.STO" }, | |
| { "Supm", "Supervisor user-mode pointer masking" }, | |
| /* 50 */ | |
| { "Zicntr", "Base counters: RDCYCLE, RDTIME, RDINSTRET" }, | |
| { "Zihpm", "Hardware performance counters" }, | |
| { "Zfbfmin", "Scalar BFloat16 convert: FCVT.BF16.S, FCVT.S.BF16" }, | |
| { "Zvfbfmin", "Vector BFloat16 convert" }, | |
| { "Zvfbfwma", "Vector BFloat16 widening mul-add: VFWMACCBF16" }, | |
| { "Zicbom", "Cache-block management: CBO.CLEAN, CBO.FLUSH, CBO.INVAL" }, | |
| { "Zaamo", "Atomic AMO ops: AMOSWAP, AMOADD, ..." }, | |
| { "Zalrsc", "Atomic LR/SC: LR.W/D, SC.W/D" }, | |
| { "Zabha", "Byte/halfword atomic AMOs" }, | |
| { "Zalasr", "Atomic load-acquire / store-release" }, | |
| /* 60 */ | |
| { "Zicbop", "Cache-block prefetch: PREFETCH.I, PREFETCH.R, PREFETCH.W" }, | |
| { "Zilsd", "Load/store pair for double (RV32)" }, | |
| { "Zclsd", "Compressed load/store pair for double (RV32)" }, | |
| { "Zicfilp", "Landing pad forward-CFI: LPAD" }, | |
| }; | |
| /* IMA_EXT_1 extensions (key 16) — array index == bit position */ | |
| static const struct ext_info exts1[] = { | |
| /* 0 */ | |
| { "Zicfiss", "Shadow stack backward-CFI: SSPUSH, SSPOP, SSCHKRA" }, | |
| }; | |
| static int hwprobe(int64_t key, uint64_t *value) | |
| { | |
| struct riscv_hwprobe p = { key, 0 }; | |
| _hwprobe(&p, 1); | |
| *value = p.value; | |
| return p.key != -1; | |
| } | |
| static void print_exts(const char *block, const struct ext_info *exts, | |
| size_t n, uint64_t v, int tty) | |
| { | |
| printf("\n %s:\n", block); | |
| printf(" %-3s %-5s %-12s %s\n", "Bit", "Set", "Ext", "Key instructions"); | |
| print_sep(); | |
| for (size_t i = 0; i < n; i++) { | |
| if (!exts[i].name) | |
| continue; | |
| int set = !!(v & (1ULL << i)); | |
| printf(" %3zu [%s%s%s] %-12s %s\n", | |
| i, | |
| tty ? (set ? ANSI_GREEN : ANSI_RED) : "", | |
| set ? "YES" : " NO", | |
| tty ? ANSI_RESET : "", | |
| exts[i].name, | |
| exts[i].desc); | |
| } | |
| puts(""); | |
| } | |
| static const char *misaligned_str(uint64_t val) | |
| { | |
| switch (val & RISCV_HWPROBE_MISALIGNED_MASK) { | |
| case RISCV_HWPROBE_MISALIGNED_EMULATED: | |
| return "emulated (trap to kernel)"; | |
| case RISCV_HWPROBE_MISALIGNED_SLOW: | |
| return "slow (handled in hardware)"; | |
| case RISCV_HWPROBE_MISALIGNED_FAST: | |
| return "fast (hardware, no penalty)"; | |
| case RISCV_HWPROBE_MISALIGNED_UNSUPPORTED: | |
| return "unsupported (will fault)"; | |
| default: | |
| return "unknown"; | |
| } | |
| } | |
| int main(void) | |
| { | |
| uint64_t val; | |
| /* chip identity */ | |
| struct riscv_hwprobe id[] = { | |
| { RISCV_HWPROBE_KEY_MVENDORID, 0 }, | |
| { RISCV_HWPROBE_KEY_MARCHID, 0 }, | |
| { RISCV_HWPROBE_KEY_MIMPID, 0 }, | |
| }; | |
| if (_hwprobe(id, 3) < 0) { | |
| perror("riscv_hwprobe"); | |
| fprintf(stderr, "Requires Linux >= 6.4 on a RISC-V system.\n"); | |
| return 1; | |
| } | |
| print_sep(); | |
| printf(" RISC-V Hardware Capabilities\n"); | |
| print_sep(); | |
| printf(" mvendorid: 0x%llx\n", id[0].value); | |
| printf(" marchid : 0x%llx\n", id[1].value); | |
| printf(" mimpid : 0x%llx\n", id[2].value); | |
| print_sep(); | |
| /* base ISA */ | |
| if (hwprobe(RISCV_HWPROBE_KEY_BASE_BEHAVIOR, &val)) | |
| printf(" Base IMA behavior: %s\n", | |
| val & RISCV_HWPROBE_BASE_BEHAVIOR_IMA ? "yes" : "no"); | |
| int tty = isatty(STDOUT_FILENO); | |
| if (hwprobe(RISCV_HWPROBE_KEY_IMA_EXT_0, &val)) | |
| print_exts("IMA_EXT_0", exts0, sizeof exts0 / sizeof *exts0, val, tty); | |
| #ifdef RISCV_HWPROBE_KEY_IMA_EXT_1 | |
| if (hwprobe(RISCV_HWPROBE_KEY_IMA_EXT_1, &val)) | |
| print_exts("IMA_EXT_1", exts1, sizeof exts1 / sizeof *exts1, val, tty); | |
| #endif | |
| #ifdef RISCV_HWPROBE_EXT_ZICBOZ | |
| if (hwprobe(RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE, &val) && val) | |
| printf(" Zicboz CBO.ZERO block size : %llu bytes\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_EXT_ZICBOM | |
| if (hwprobe(RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE, &val) && val) | |
| printf(" Zicbom CBO.CLEAN/FLUSH block size : %llu bytes\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_EXT_ZICBOP | |
| if (hwprobe(RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE, &val) && val) | |
| printf(" Zicbop PREFETCH block size : %llu bytes\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS | |
| if (hwprobe(RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS, &val)) | |
| printf(" Highest virtual address : 0x%llx\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_TIME_CSR_FREQ | |
| if (hwprobe(RISCV_HWPROBE_KEY_TIME_CSR_FREQ, &val) && val) | |
| printf(" Time CSR frequency : %llu Hz\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF | |
| if (hwprobe(RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF, &val)) | |
| printf(" Misaligned scalar access : %s\n", misaligned_str(val)); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF | |
| if (hwprobe(RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF, &val)) | |
| printf(" Misaligned vector access : %s\n", misaligned_str(val)); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0 | |
| /* vendor extensions — print raw bitmask (bits defined in asm/vendor/) */ | |
| if (hwprobe(RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0, &val) && val) | |
| printf(" Vendor ext T-Head : 0x%llx\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0 | |
| if (hwprobe(RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0, &val) && val) | |
| printf(" Vendor ext SiFive : 0x%llx\n", val); | |
| #endif | |
| #ifdef RISCV_HWPROBE_KEY_VENDOR_EXT_MIPS_0 | |
| if (hwprobe(RISCV_HWPROBE_KEY_VENDOR_EXT_MIPS_0, &val) && val) | |
| printf(" Vendor ext MIPS : 0x%llx\n", val); | |
| #endif | |
| print_sep(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment