Skip to content

Instantly share code, notes, and snippets.

@teknoraver
Last active February 20, 2026 02:02
Show Gist options
  • Select an option

  • Save teknoraver/6a8313e8c4f2ed804c9f91b9c3891b43 to your computer and use it in GitHub Desktop.

Select an option

Save teknoraver/6a8313e8c4f2ed804c9f91b9c3891b43 to your computer and use it in GitHub Desktop.
Prints supported RISC-V extensions using the riscv_hwprobe() syscall
/*
* 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