i-RISCV - The inferred RISC-V SoC

A production-ready RVA23U64 RISC-V SoC. Every mechanism is real, verified hardware: it boots an unmodified Linux kernel out of simulation, and its cryptographic implementations pass NIST ACVP validation.

i-RISCV provides a CPU implementing the RISC-V instruction set architecture (ISA) along with a large set of RISC-V extensions.

Why i-RISCV

  • Full RVA23U64 profile. RV64IMAFDCV with hypervisor (H), vector crypto (Zvk*), AIA interrupts, and IOMMU – every mechanism is real hardware verified end to end, not a teaching core.
  • Configurable at synthesis time. Every optional mechanism – the four Zvk* crypto units, the entropy source, the branch predictor, PMP, IOPMP, the FUSE controller, and the UART – is gated by its own build-time *_ENABLE parameter: set it to 0 and the hardware is removed from the design entirely, not merely tied off. Single-core or multi-core (NUM_HARTS) is likewise a synthesis-time choice.
  • Boots real Linux. Unmodified upstream OpenSBI plus stock, unmodified Linux v6.6 and v7.2: full extension detection, Sstc timer from S-mode, real UART console, KVM initialization.
  • Fully tested, fully documented. Unit tests, directed assembly tests through the real pipeline, a build-permutation matrix, RISCOF conformance, differential fuzzing harness, NIST ACVP crypto validation – with complete design documentation for every mechanism.

Author

Stephan Mueller smueller@chronox.de

Oct 2, 2026

Subsections of i-RISCV - The inferred RISC-V SoC

Supported RISC-V ISA Extensions

The following RISC-V ISA extensions are supported. The *_ENABLE column names the synthesis-time parameter that removes a mechanism from the design entirely (both simulation and real synthesis) where one exists – every such combination is verified end-to-end by verif/enable_matrix.

Extension Description *_ENABLE
I Base RV64I integer ISA (all 64-bit ops incl. *W 32-bit forms) –
M Multiply/divide (RV64M): combinational multiply, multi-cycle restoring divide –
A Atomics (RV64A): AMO* + LR/SC –
F Single-precision floating point (RV64F) –
D Double-precision floating point (RV64D), incl. multi-cycle FDIV/FSQRT –
Zfa Additional FP ops: FLI.S/FLI.D, FMINM/FMAXM, FROUND/FROUNDNX, FCVTMOD.W.D, FLEQ/FLTQ –
C Compressed (RVC): 16-bit instructions expanded to RV64 in fetch –
V Vector with VLEN=512: integer/FP arithmetic, reductions, mask/permutation ops –
H Hypervisor: nested virtualization, VS-level state, G-stage second-stage translation –
Zicsr CSR access instructions; baseline for all CSR interfaces (incl. the entropy-source CSRs) –
Zifencei Instruction-fetch fence (FENCE.I) for I-cache coherence –
Zba Bitmanip: address-calculation instructions (implemented in the ALU) –
Zbb Bitmanip: basic bit-manipulation instructions (implemented in the ALU) –
Zbs Bitmanip: single-bit instructions (implemented in the ALU) –
Zicond Branchless conditional zeroing (CZERO.EQZ/CZERO.NEZ, implemented in the ALU) –
Zicbom Cache-block management: CBO.INVAL/CLEAN/FLUSH (architectural no-ops – no cache exists) –
Zicboz CBO.ZERO: real multi-cycle 64-byte block write –
Zihintpause PAUSE hint (decodes as a NOP on this in-order core) –
Zicbop PREFETCH.R/W/I hints (decode as NOPs – no cache exists) –
Zawrs Wait-for-reservation-set WRS.* (decode as immediate-return NOPs) –
Zkt Data-independent execution timing for integer ops (holds by construction) –
Sstc Supervisor-mode Timer Compare extension –
Smaia Machine-mode AIA CSRs for the advanced interrupt architecture (APLIC/IMSIC) –
Ssaia Supervisor-mode AIA CSRs –
Sv39 39-bit virtual memory (3-level page tables) –
Sv48 48-bit virtual memory (4-level page tables) –
Sv57 57-bit virtual memory (5-level page tables) –
Svade A/D bits set by software: with ADUE=0, a leaf PTE that would otherwise succeed but has A=0 (or D=0 on write/AMO) raises a genuine page fault for the OS to fix up –
Svadu A/D bits set by hardware: with ADUE=1 (the reset default), the walker atomically updates a leaf PTE’s A/D bits in place – both extensions coexist, selected at runtime per translation stage –
Svinval SINVAL.VMA selective invalidation with SFENCE.VMA’s exact selectivity –
Svpbmt Page-based memory types via the PTE PBMT field (folded into the walker’s fault path) –
Svnapot Ratified 64 KiB NAPOT page-table encoding (folded into the walker) –
Zicfilp Forward-edge CFI: LPAD landing pads; a non-exempt JALR arms an expected-landing-pad state enforced on the next fetch (software-check exception, mcause=18) –
Zicfiss Backward-edge CFI: dedicated shadow stack (ssp, SSPUSH/SSPOPCHK) with its own PTE encoding; a mismatched pop raises the software-check exception –
Ssdbltrp/Smdbltrp Double-trap detection: per-privilege sticky trap-in-flight bits redirect an unexpected trap one level up (mcause=16, suppressed cause in mtval2); M-level halts the hart in a permanent critical-error state –
Zvkb Vector crypto bit-manipulation: VANDN/VROL/VROR/VBREV8/VREV8/VWSLL –
Zvkt Data-independent vector execution latency (holds by construction on the serial engine) –
Zvkned Vector AES-256 block cipher ZVKNED_ENABLE
Zvkg Vector AES-256-GCM/GMAC authenticated encryption ZVKG_ENABLE
Zvknhb Vector SHA2-256/384/512 hash ZVKNHB_ENABLE
Zvknhk Vector SHA3/Keccak (draft proposal, not yet ratified) ZVKNHK_ENABLE
PMP Physical Memory Protection mechanism PMP_ENABLE
Smepmp M-mode PMP enhancement: mseccfg RLB/MML/MMWP lock and write-restriction rules PMP_ENABLE
IOPMP I/O Physical Memory Protection for DMA devices IOPMP_ENABLE
Zkr The scalar entropy-source seed CSR (0x015, wipe-on-read, OPST status bits) on top of the hardware entropy source; access gated by mseccfg.sseed/.useed –
Sep 19, 2026

i-RISCV Cryptographic Support

The i-RISCV CPU provides a number of cryptographic support.

The cryptographic implementations are tested with the NIST ACVP framework using simulation.

The testing was conducted using the ACVP Parser.

The algorithm implementations are standalone, reusable RTL modules which are connected to the respective RISC-V ISA extensions. Thus apart from the RISC-V ISA, the algorithms can be instantiated in separate use cases or even extracted from the RTL code to a separate project; each core also ships with a documented C API for driving it from Verilator or an external project.

The overarching goal with the hardware crypographic implementations are:

  • 256 bits of security in general (ignoring the collision security strength of message digests)

  • availability of hardware-stored keys usable by software where software yet cannot access the key

FUSE Keys

The RTL provides a one-time-programmable (OTP) e-fuse controller holding a synthesis-time defined number of banks – three by default – each storing two 256 bit keys. Every key carries one additional guard/“blown” fuse bit: a blown key is permanently unreadable, and an atomic zeroize operation blows the selected key (or all keys) in a single step.

The fuses are structurally one-way – every write path can only ever set bits, never clear them – and the fuse storage is the one module in this design that does not reset on rst_n, so provisioned keys survive power cycles and resets exactly like real e-fuse silicon. In simulation, the array’s power-on content is preloaded from a hex file (INIT_FILE parameter or +FUSEHEX= plusarg) instead of a programming pulse.

The keys are selectable by the caller including software for the cryptographic operations as indicated below. In this case, the caller does not have access to the key data itself, but can only instruct the cryptographic operation to use the key. Inside the hardware, the cryptographic operation fetches the key prior to performing the requested operation.

The core concept is that the caller is not able to access the key.

Creation of FUSE Keys

Two independent paths provision key material:

  • Manufacturing-time provisioning. A dedicated, prov_enable-gated out-of-band port writes a key’s value exactly once after manufacturing and before deployment; in real silicon prov_enable is tied to a physically restricted signal (a factory-test strap or a manufacturing-only debug credential) that is permanently de-asserted afterwards, so no code path can ever provision new key material in the field.

  • Hardware-generated keys. A trigger in M-mode instructs the hardware Random Number Generator to generate a bank of 256 bit keys and write them directly into the FUSE controller – targeting bank 1, which is this mechanism’s exclusive consumer. The generated bits never pass through any register or instruction result software could read; neither software / firmware, nor other parts of the hardware can interfere with this operation, nor can they access the generated bits in any form. This link is one-shot: a non-volatile “provisioned” status bit latches permanently after the single run (even across a full chip reset), so generation can never be re-triggered and OR fresh material into an already-programmed key.

For each generated key, the RNG performs the following steps individually:

  1. Reseed itself with 256 bits from its Entropy Source

  2. Generate a 256 bit random bit string (each key is its own independent generate call, not two halves of one longer output)

  3. Write the 256 bit random bit string directly into the respective FUSE key location

SHA-2

Compliant to FIPS 180-4, the following algorithms are available:

  • SHA2-256

  • SHA2-384

  • SHA2-512

Exposed to software through the Zvknhb vector instructions (vsha2ms.vv/vsha2ch.vv/vsha2cl.vv; SEW=32 selects SHA-256, SEW=64 selects SHA-384/512).

SHA-3 Family

Compliant to FIPS 202, the following algorithms are available:

  • SHA3-256

  • SHA3-384

  • SHA3-512

  • SHAKE128

  • SHAKE256

The implementation is particularly small as a central Keccak mechanism is implemented where the SHA-3 family members only define the rate/capacity and the padding bit. The leancrypto’s SHA-3 C implementation is conceptual identical to the RTL implementation.

KMAC

Following SP800-185, KMAC256 and KMAC256XOF are implemented on a real, separate Keccak core instance (cSHAKE256 as the sixth algorithm of that shared core). The caller is allowed to either provide a key or to point to one of the FUSE keys; an explicit key must be 128-256 bits, enforcing SP 800-185’s own minimum-security-strength floor.

This module is not instantiated anywhere in the SoC – it exists for unit testing and external use through its documented C API; a self-test status bit is reserved for it on the self-test interface.

AES

The RTL implements the raw AES block operation for AES-256. It is equally connected to the FUSE keys: bank 0 of the FUSE controller is the AES hardware’s exclusive consumer, selected through a machine-mode CSR that names one of the two bank-0 keys and declares whether the upcoming instruction sequence encrypts or decrypts. While a FUSE key is active, every round instruction’s software-supplied round key is transparently replaced by the hardware-expanded schedule – the round keys themselves are never exposed on any port, register, or CSR.

In addition, GCM support is provided along with GMAC: one 128-bit data block (plus one optional AAD block) per call under a fixed 96-bit IV, built on a constant-time GF(2^128) GHASH multiply shared with the Zvkg extension.

Random Number Generator

The random number generator is an XDRBG-256 which uses SHAKE-256 as a core. It instantiates the Keccak Core.

The reason for choosing the XDRBG-256 is:

  • availability of Keccak

  • very lean

  • about to be standardized in NIST SP800-90A

The XDRBG-256 is seeded by the hardware entropy sources; in this design, its only instantiations are the entropy source’s own private DRBG pipelines.

The XDRBG-256 implements the following aspects:

  • A multi-staged seeding process is supported where a caller can insert multiple individual seed blocks before the XDRBG re-generates its own internal state. This support is mathematically equivalent to inserting all seed blocks concatenated as one seed block. Yet, one seed block is allowed to be at most 512 bits.

  • All call sizes are hardware-enforced: a finalizing instantiate/reseed accepts at most 2176 bits of combined seed + alpha + control byte (alpha itself at most 84 bytes), and the generate function will at most return 512 bits.

Vector Crypto Extensions

The algorithm cores above are exposed to software through the RISC-V vector-crypto instruction set, all decoded under a dedicated vector-crypto major opcode:

Extension Instructions Execution unit
Zvkb VANDN / VROL / VROR / VBREV8 / VREV8 / VWSLL (crypto bit-manipulation) rides the existing vector engine – no new datapath
Zvkned vaesdf / vaesdm / vaesef / vaesem / vaeskf1 / vaeskf2 / vaesz – AES-256 one round at a time, state in the vector register file dedicated unit; every instruction commits in exactly one cycle, independent of data or key
Zvkg vghsh.vv / vgmul.vv – GF(2^128) GHASH multiply for GCM/GMAC dedicated unit on the shared constant-time GHASH primitive
Zvknhb vsha2ms.vv / vsha2ch.vv / vsha2cl.vv; SEW=32 selects SHA-256, SEW=64 selects SHA-384/512 dedicated unit; strict superset of Zvknha, so no separate Zvknha exists
Zvknhk vkeccak.vi – in-place Keccak-p[1600] permutation across four consecutive vector registers draft proposal, not a ratified extension – deliberately not advertised in the device tree

In addition, Zvkt is a timing guarantee rather than an instruction set: the vector engine’s add/subtract, logical, shift, compare, copy, extend, merge, multiply/multiply-add, permute, and slide instructions (plus all of Zvkb) execute with data-independent latency – satisfied by construction on this core’s serial one-element-per-cycle engine and proved by a cycle-count-equality unit test.

Self-Test Infrastructure

Every cryptographic execution unit carries a built-in known-answer self-test, delegated to its own standalone algorithm core rather than re-implemented:

  • Auto-triggered on first use. The very first instruction a unit ever executes after reset transparently runs the self-test first; software only sees that one instruction take longer.

  • Software-re-runnable. Writing the mechanism’s bit of IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED (0x7C0) triggers another run – the write is a trigger line, never a store: it changes nothing the CSR reads back.

  • Sticky lock with recoverable failure. Once passed, the self-test never re-runs (a later trigger write is a permanent no-op). While failed, the block stays disabled for ordinary requests – every subsequent instruction of that unit traps as an illegal instruction, a hardware-enforced error return to software, not merely a status flag – until a software re-trigger through NOT_EXECUTED runs it again and passes, which clears FAILED and sets PASSED on the same cycle.

Results are reported through four custom CSRs, all readable from every privilege mode: IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED (0x7C0, resets all-1s), IRISCV_CRYPTO_SELFTEST_PASSED (0xFC0), IRISCV_CRYPTO_SELFTEST_FAILED (0xFC1), and IRISCV_CRYPTO_FIPS_APPROVED (0xFC2). Only NOT_EXECUTED accepts a software write at all – and only from M or HS mode; writing any of the other three traps as an illegal instruction even in M-mode. Eight real mechanisms are wired to this interface – AES/Zvkned, AES-GCM/Zvkg, SHA-256/SHA-512 (Zvknhb’s two independent per-width tests), Keccak/Zvknhk, and the entropy source’s own DRBG/Keccak self-test bits (Entropy Source) – each owning its own bit position; one further bit is reserved for KMAC256.

Sep 19, 2026

i-RISCV Hardware Entropy Sources

The i-RISCV provides a complete entropy source along with all tools necessary to analyze it to verify the compliance with the stochastic model.

Configuration FUSES

The RTL implements a FUSE controller which, however is not backed by OTP-FUSES, but with memory that is can be written to by software in RISC-V M privilege mode where the software can set a lock which makes the configuration values read-only until next reset.

The SBI code contains placeholders offering example code showing how set the FUSES and the final lock.

The FUSE allows the following individual setting for each noise source:

  • health test cutoff values individually for each health test

  • number of bits to be collected from the noise source for one complete seeding operation

Therefore, each noise source can be configured individually and independent of all other noise sources.

Noise Sources

The i-RISCV implements two different types of noise sources:

  • 4 ring oscillators based on the Elementary RO TRNG (ERO) with the Baudet et al. phase-noise model, Fischer-Lubicz embedded jitter monitor, and OpenTRNG reference RTL

  • 2 meta-stable flip-flops

Health Tests

Each instance of the noise source, i.e. each individual ring oscillator and meta-stable flip-flop, has its independent set of health tests which independently monitor the quality of the data.

If any health test flags an alarm, the noise source is deactivated independently of the others. The health status register allows software to reset the noise source to become active again. The noise source will be automatically reactivated upon reset.

Stochastic Model

A separate stochastic model for the ring oscillators and the meta-stable flip-flops is used as a basis to determine the entropy rate.

Data Size

Each noise source has its own configured number of bits to be mandatory collected for one seeding operation.

The goal with this individual configuration is to allow a post-tapeout characterization of the noise sources along with its entropy rate. Now, the entropy rate can be set to deliver the amount of entropy used as a baseline specified in the seeding strategy.

Entropy Source

The integration of noise sources is intended to be compliant with SP800-90C as well as AIS 20/31 v3.0. This section explains the integration of the noise sources to form the entropy source used to seed the XDRBG-256.

General Seeding Strategy

Before discussing the Synchronous or asynchronous seeding strategy, the general approach to seeding must be discussed as it applies in general. The XDRBG-256 is seeded with the following general considerations which apply always:

  • The XDRBG-256 after reset receives a trigger to automatically perform a seeding operation. That seeding operation must be completed before the XDRBG generates any data. This means that this seeding operation is synchronous in nature.

  • The XDRBG has two trigger points whose thresholds can be configured with the configuration FUSES.

    • Reseed trigger: When a threshold of generated bits is reached without reseeding (default is 2^16 bits), the XDRBG triggers the reseed with one of the possible seeding strategy. Yet, the XDRBG is allowed to continue to generate random bits.

    • Drop dead trigger: When a threshold of generated bits is reached without reseeding (default is 2^17), the XDRBG refuses to generate any more random bits. It synchronously waits for the seeding operation that is triggered by the reseed trigger to complete. If that seeding operation fails (e.g. a noise source health error)

    It is permissible that both trigger points are set to the same value which implies that any reseed trigger is always synchronous and required to be successful (i.e. at least one noise source must deliver data without a health alarm pending).

RBG2(P) And DRG.4/PTG.2

The XDRBG offers the ES_DRNG_RAND interface which generates data compliant to the SP800-90C RBG2(P) and AIS 20/31 v3.0 DRG.4 + PTG.2 construction.

The general construction of the seeding process can be configured at synthesis time and implements one of the following strategies further discussed in the subsequent sections

  • Synchronous seeding - albeit inferior with its performance compared to the asynchronous operation, it only uses a separate 512 bit transfer buffer and thus has less gate counts compared to the asynchronous approach.

  • Asynchronous seeding - this approach is significantly faster as entropy collection proceeds in parallel with random bit generation by the XDRBG, it requires a separate SHA3-512 instance that collects the bits from the noise sources.

flowchart LR
    RO["4 ring oscillators (health-tested)"] --> SBUF
    FF["2 metastable flip-flops (health-tested)"] --> SBUF
    RO --> AC
    FF --> AC
    subgraph SYN ["Synchronous seeding (caller stalls until seeded)"]
        direction LR
        SBUF["512-bit seed buffer"] -->|multi-staged insert| XDRBG
    end
    subgraph ASY ["Asynchronous seeding (parallel to generation)"]
        direction LR
        AC["SHA3-512 conditioning function"] -->|atomic 512-bit digest insert| XDRBG
    end
    XDRBG["XDRBG-256 (SHAKE-256 core)"] --> OUT["Random bits, max 512 per request"]

Synchronous Seeding Stratey

For the synchronous seeding strategy, the XDRBG will stall the caller until the following seeding process has successfully completed.

For the seeding operation, a 512 bit wide seed buffer is used to hold the data of the following operations.

  1. Startup all noise sources whose health test has not failed

  2. Obtain individual bits from each active noise source and concatenate them in the buffer. Due to the different numbers of bits generated by the noise sources, it is possible that at the beginning, output data from all noise sources are concatenated, later from one less down to obtaining data from the noise source with the longest data bit generation threshold.

  3. When the 512 bit seed buffer is entirely saturated, insert the buffer into the XDRBG seeding interface. That seeding interface allows a multi-staged seed process which implies that, for example, inserting 2 512 bit buffers into the XDRBG is the identical operation as if the 2 buffers are first concatenated and then inserted into the XDRBG. With this consideration, irrespective of how many instances of the seed buffer, the amount of data is mathematically treated as one long seed buffer. Once the seed buffer is inserted go back to step 2 to collect more data.

  4. The steps 2 and 3 are performed until all noise sources delivered their configured data. At that time the potentially partially filled seed buffer is inserted again into the XDRBG seed interface, but marking the seeding operation as complete.

  5. The XDRBG generates the requested number of random bits and returns them to the caller.

A seeding failure, i.e. when no noise source delivered the required amount of data returns an error to the caller.

Asynchronous Seeding Strategy

For the asynchronous seeding strategy, the XDRBG process the seeding operation independently of the XDRBG generate operation. That implies that stall the caller is provided with random bits from the XDRBG while the collection of entropy is ongoing.

For the seeding operation, a 512 bit wide seed buffer is used to hold the data collected by the noise sources.

To ensure that the entire set of data from the noise source is inserted into the XDRBG in an atomic operation and having a runtime-configurable amount of data to be collected from the noise source, a SHA3-512 conditioning function is employed to collect an arbitrary amount of data from the noise sources, compress them with the message digest and insert the 512 bit message digest into the XDRBG in an atomic operation. Thus, the following steps are implemented:

  1. The XDRBG receives the (re)seeding trigger, it triggers the following steps while servicing subsequent caller requests. Starting with the next step, only the seeding process is outlined that now runs in parallel to the XDRBG generate operation.

  2. Startup all noise sources whose health test has not failed

  3. Obtain individual bits from each active noise source and concatenate them in the buffer. Due to the different numbers of bits generated by the noise sources, it is possible that at the beginning, output data from all noise sources are concatenated, later from one less down to obtaining data from the noise source with the longest data bit generation threshold.

  4. When the 512 bit seed buffer is entirely saturated, insert the buffer into the conditioning function with a conceptual hash update operation. I.e. all collected seed buffers are mathematically concatenated for generating the message digest.

  5. The steps 3 and 4 are performed until all noise sources delivered their configured data. At that time the potentially partially filled seed buffer is inserted again into the conditioning function followed by generating the message digest.

  6. The message digest is now inserted into the XDRBG seeding interface in an atomic operation.

Note, the asynchronous seeding process also ensures that there is hardly any user-visible contention for the noise sources when the XD

RBG3(RS) And PTG.3

In addition, the XDRBG also offers the ES_DRNG_SEED interface. When requesting data from that interface, the XDRBG changes its behavior to implement an SP800-90C RBG3(RS) and AIS 20/31 v3.0 PTG.3 construction.

flowchart LR
    RO["4 ring oscillators (health-tested)"] --> BUF
    FF["2 metastable flip-flops (health-tested)"] --> BUF
    BUF["512-bit seed buffer"] -->|multi-staged insert when full| XDRBG["XDRBG-256 (SHAKE-256 core)"]
    XDRBG --> OUT["Random bits, max 256 per request"]
    OUT -.->|reseed trigger| BUF

After generating at most 256 bits of random data, a seeding operation is performed. For the seeding operation, a 512 bit wide seed buffer is used to hold the data of the following operations.

  1. Startup all noise sources whose health test has not failed

  2. Obtain individual bits from each active noise source and concatenate them in the buffer. Due to the different numbers of bits generated by the noise sources, it is possible that at the beginning, output data from all noise sources are concatenated, later from one less down to obtaining data from the noise source with the longest data bit generation threshold.

  3. When the 512 bit seed buffer is entirely saturated, insert the buffer into the XDRBG seeding interface. That seeding interface allows a multi-staged seed process which implies that, for example, inserting 2 512 bit buffers into the XDRBG is the identical operation as if the 2 buffers are first concatenated and then inserted into the XDRBG. With this consideration, irrespective of how many instances of the seed buffer, the amount of data is mathematically treated as one long seed buffer. Once the seed buffer is inserted go back to step 2 to collect more data.

  4. The steps 2 and 3 are performed until all noise sources delivered their configured data. At that time the potentially partially filled seed buffer is inserted again into the XDRBG seed interface, but marking the seeding operation as complete.

  5. The XDRBG generates the requested number of random bits and returns them to the caller.

Note: Access to the noise sources is serialized such that a complete seeding operation is completed by a separate seeding operation can take place.

Sep 19, 2026

i-RISCV Input/Output Support

The i-RISCV SoC provides the following input/output mechanisms.

IOMMU

The SoC implements the RISC-V IOMMU (v1.0) for DMA-capable devices. Supported mechanisms:

  • Device Directory Table (DDT) walk in Off, Bare, 1LVL, 2LVL, and 3LVL modes
  • Second-stage-only DMA address translation (Sv39x4/Sv48x4/Sv57x4) with hardware A/D update on page-table entries
  • MSI address translation, redirecting a device’s writes to a guest’s virtual IMSIC interrupt file onto the real physical guest file (AIA/IMSIC tie-in)
  • IOATC: a device-context cache and an I/O TLB, invalidated via IODIR.INVAL_DDT and IOTINVAL.VMA/.GVMA commands
  • Command queue (IOTINVAL, IOFENCE.C, IODIR) and fault queue for translation errors
  • WSI (wire-signal) interrupt delivery to the PLIC/APLIC on faults or fence completion
  • IOPMP enforcement of the final physical address after every successful translation

AIA and IMSIC

The core implements the RISC-V Advanced Interrupt Architecture (AIA, v1.0). Supported mechanisms:

  • Smaia/Ssaia: M-level and S-level AIA CSRs (miselect/mireg, siselect/sireg, mtopi/stopi, mvien/mvip)
  • IMSIC (Incoming MSI Controller) with per-hart M-file and S-file, memory-mapped MSI delivery (seteipnum_le), and claim-and-clear via mtopei/stopei
  • Two VS-level guest interrupt files per hart for the H extension, selected by hstatus.VGEIN, each with its own physical MSI region to support migrating a guest between files
  • VS-level hypervisor AIA CSRs (hvien, hvictl, vsiselect/vsireg, vstopei/vstopi)
  • A minimal, MSI-mode-only, machine-level APLIC domain (aplic0, 8 sources, msi-parent = imsic_s) that forwards this platform’s wired sources into the IMSIC S-file as MSIs – under Linux, the IOMMU’s and IOPMP’s interrupts are advertised against it (sources 2/3) instead of through plic0’s own S-mode context, which keeps a position-preserving placeholder entry; the PLIC retains its M-mode context for firmware and bare-metal use

IOPMP

The SoC implements RISC-V I/O Physical Memory Protection (IOPMP), which regulates physical-address accesses issued by non-hart bus requesters (the IOMMU’s translation path). Supported mechanisms:

  • 16 requester IDs, 8 memory domains, and 16 protection entries
  • TOR/NA4/NAPOT address matching with lowest-index-wins priority and per-entry r/w/x permissions
  • Memory-domain / source association gating (MDCFG/SRCMD_EN)
  • Locking of MD associations, MDCFG, and entries (W1SS/incremental-only)
  • First-violation error capture (requester ID, address, access type, denial reason) with interrupt reporting

Console UART

A genuine ns16550a-register-compatible serial console (rtl/uncore/uart.sv), build-time optional (UART_ENABLE; at 0 it is removed from the design entirely – registers read 0, interrupt tied low, TX held idle-high). Supported mechanisms:

  • Standard byte-addressed 8-register map with DLAB-banked offsets 0/1 (RBR/THR/DLL, IER/DLM, IIR/FCR, LCR, MCR, LSR, MSR, SCR) – the exact layout a stock Linux 8250 driver expects, no reg-shift/reg-io-width overrides
  • Real baud-rate divisor: one bit period = 16 x DLL/DLM core cycles; reset to 115200 at a 50 MHz clock so bare-metal firmware gets a known rate without programming it; reprogrammed via the standard DLAB sequence
  • Full 16550A hardware FIFOs: 16-byte TX and RX, enabled by sticky FCR.FE (any toggle clears both), four RX trigger levels (1/4/8/14 bytes) plus a real 4-character-time RX timeout interrupt so low-throughput links never stall below the trigger level; without FCR.FE, one physical shift register feeds single holding/latch registers instead (THR writes while busy are silently ignored, THRE and TEMT collapse to one condition)
  • Receiver error detection – framing error, BREAK, overrun – with per-byte error tags in the RX FIFO feeding LSR’s errors-in-FIFO bit; RBR reads clear DR/pop the FIFO while LSR reads clear OE/PE/FE/BI (two distinct read-to-clear conditions, matching real 16550 semantics)
  • Real interrupt priority chain: line status > data available / character timeout > THR-empty (one-shot – fires exactly once per emptying, acknowledged by an IIR read or a new THR write) > modem status, with standard IIR identification codes
  • MCR.LOOP internal loopback feeding the transmitter back into the receiver while the external TX pin is held idle-high; in loopback mode MSR’s CTS/DSR/RI/DCD mirror MCR’s DTR/RTS/OUT1/OUT2 one-to-one with delta bits latched and cleared on read
  • Modem status defaults to “always ready” (CTS=DSR=DCD=1, RI=0) since no real modem lines exist

The UART’s irq is wired to both controllers’ source 1 (source 2 = the IOMMU WSI, source 3 = the IOPMP); sources 2 and 3 are advertised in the device tree against aplic0, while the UART itself carries no interrupts property – a single honest compatible = "ns16550a" – so under Linux the stock driver falls back to polling (see Linux).

Disclosed simplifications. Not silent scope cuts:

  • Fixed 8N1 wire framing – LCR’s word-length/parity/stop-bit fields are stored and read back faithfully but not enforced on the wire
  • No 16750/16950 extensions (no 64-byte FIFO, no auto-flow-control beyond the plain MCR/MSR pins)
Sep 20, 2026

i-RISCV CPU Internals

The i-RISCV core is a single-issue, in-order processor. The following mechanisms are supported.

Pipeline

A six-stage pipeline (IF1/IF2/ID/EX/MEM/WB) with synchronous instruction and data memory access.

flowchart LR
    IF1["IF1<br/>PC gen"] --> IF2["IF2<br/>imem data phase"]
    IF2 --> ID["ID<br/>decode + reg read"]
    ID --> EX["EX<br/>ALU / branch resolve"]
    EX --> MEM["MEM<br/>dmem addr phase,<br/>CSR r/w, traps"]
    MEM --> WB["WB<br/>dmem data phase,<br/>regfile commit"]

Supported mechanisms:

  • Compressed (C) handling entirely within fetch: 16-bit RVC expansion to full-width instructions, self-correcting length prediction, and straddling-instruction splicing
  • Same-cycle result forwarding between stages for dependent instructions
  • Load-use / CSR-use / AMO-use hazard detection with a single-cycle consumer stall; the FP register file carries its own structurally identical check
  • Memory-system back-pressure handling: bus-arbitration waits, multi-cycle atomics, in-flight page-table walks (data-side and instruction-side)
  • Multi-cycle execution units: RV64M divide, RV64F/D divide/square-root, and the serial (one-element-per-cycle) vector engine
  • Vector load/store with per-element address translation, mid-instruction fault latching with vstart resume, and misaligned access splitting across two bus words
  • Vector load-use: any instruction in ID is stalled for one cycle while a vector load/store sits in EX, so neither a vector consumer of the load’s result nor a scalar consumer racing the store’s write landing can observe stale data
  • Branch/jump misprediction resolution in EX with a fixed squash of all younger instructions
  • Trap/MRET/SRET/FENCE.I/SFENCE.VMA redirects resolved at the commit point (MEM)
  • Synchronous exceptions take priority over pending interrupts sampled at the commit point

Branch Prediction

A trained fetch-address predictor, build-time optional (BPU_ENABLE):

  • 64-entry direct-mapped branch target buffer (BTB) remembering last-resolved targets
  • 256-entry pattern history table (PHT) with 2-bit saturating counters for conditional-branch direction
  • Return address stack (RAS, default 8 entries) giving call-site-specific predictions for shared ret instructions; push/pop only on resolved outcomes, never speculatively
  • Mispredictions corrected by the ordinary EX-stage redirect path, in both prediction directions

Spectre/Meltdown resistance. This core is not susceptible to Spectre-v1/v2 or Meltdown-class attacks:

  • Every mis-speculated instruction is squashed before it reaches MEM, the only stage that can touch memory/TLB – so a speculative load can never leave an observable cache/TLB footprint (the precondition both Spectre variants rely on)
  • All trained predictor state (BTB, PHT, and RAS) is unconditionally erased on every privilege-domain crossing (trap, MRET, SRET, FENCE.I), so no prediction history or call stack can survive across a privilege transition (closes the Spectre-v2 branch-target-injection setup at the source)
  • Meltdown requires a permission check deferred past speculative use of a faulting load; this core’s MMU translation and PMP permission checks are combinational and gate the real bus access in the same cycle, so no such path exists

MMU with PMP

The MMU implements Sv39/Sv48/Sv57 virtual memory plus H-extension two-stage (VS + G) translation. Supported mechanisms:

  • Variable-depth page-table walker (3/4/5 levels) for instruction and data accesses, including superpage support
  • H-extension two-stage translation with separate VS-stage and G-stage walkers/TLBs – see the Tagged TLB section for the shared TLB’s tagging and selective invalidation
  • Hardware A/D-bit management implementing both Svade and Svadu: a leaf PTE that would otherwise succeed but has A=0, or D=0 on a write/AMO, is either auto-updated in place by the walker (a real atomic read-modify-write under the cross-hart bus grant; a cached TLB entry whose A/D no longer match the current access is evicted and re-walked) when ADUE=1, or reported as a genuine page fault for software fixup when ADUE=0 – menvcfg.ADUE governs S-mode and G-stage translations, henvcfg.ADUE the VS stage (spec-forced to 0 while menvcfg.ADUE=0); both reset to 1

Physical Memory Protection (PMP), build-time optional (PMP_ENABLE; at 0 the pmpcfg/pmpaddr CSRs raise illegal-instruction and every check passes unconditionally):

  • 64 entries restricting S/U-mode physical read/write/exec access, orthogonal to page-table permissions
  • TOR/NA4/NAPOT address matching with lowest-index-wins priority; addresses WARL-masked to the 54-bit address >> 2 width of this design’s 56-bit physical address space
  • Per-entry r/w/x permissions; M-mode bypasses unlocked entries but not locked ones
  • No-match default: M-mode is always granted, S/U-mode denied – so merely having PMP present (all entries still A=OFF) denies every S/U-mode access until firmware configures at least one permissive entry before entering S-mode
  • Entry locking (config and address read-only until reset), including the locked-TOR lower-bound rule
  • Enforcement on every physical bus access: instruction fetch, load, store, and page-table-walker PTE reads; a denial reports the ordinary access-fault cause of the original access

Smepmp (Enhanced PMP) is layered directly on top via three mseccfg bits:

  • MML (Machine Mode Lockdown): replaces the per-entry permission rule with a fixed 16-row table over {L,R,W,X} – M-mode-only, S/U-mode-only (which removes the base rule’s own M-mode bypass), and shared-region patterns; sticky-set until reset
  • MMWP (Machine-mode Whitelist Policy): flips the M-mode no-match default from granted to denied; sticky-set until reset
  • RLB (Rule Locking Bypass): while set, locked entries’ config/address become writable again; cannot be set once any entry is already locked (stuck at 0 until reset)

Tagged TLB

The MMU’s translation lookaside buffer is tagged so that cached translations remain valid across address-space switches and can be invalidated selectively instead of wholesale. Supported mechanisms:

  • 16-entry TLB whose entries are tagged with the active ASID (full 16-bit satp.ASID width) and the PTE’s global bit
  • Global mappings hit under every address space; non-global entries only match their own ASID, so a satp write needs no flush – a new address space can never match stale entries installed under another ASID
  • Selective invalidation via the 4-case SFENCE.VMA rs1, rs2 predicate: whole-TLB, per-ASID, per-address (a superpage entry matches any address within its range), or per-address-and-ASID; global mappings are exempt only when an ASID is named
  • Svinval: SINVAL.VMA provides identical selectivity to SFENCE.VMA; the SFENCE.W.INVAL/SFENCE.INVAL.IR ordering fences are architecturally no-ops on this in-order core
  • H extension: separate VS-stage and G-stage TLBs; HFENCE.VVMA invalidates the ASID-tagged VS-stage entries with the same 4-case predicate, HFENCE.GVMA the G-stage entries – which are tagged by hgatp.VMID (14 bits) instead of an ASID, so a VMID-scoped flush touches only that guest’s cached translations and two guests under different VMIDs can switch without any intervening flush

Privilege Levels and Traps

M/S/U privilege levels with reset into M-mode; MRET restores the level from mstatus.MPP (resetting it to U), SRET from sstatus.SPP. The H extension layers an orthogonal virtualization bit on top of these – see the Hypervisor page.

Supported mechanisms:

  • Full machine CSR set (read-only identification CSRs mvendorid/marchid/mimpid/mhartid/mconfigptr, plus mstatus, self-describing misa, medeleg/mideleg, mie/mip, mtvec Direct + Vectored, mcounteren, mscratch, mepc/mcause/mtval, mcycle/minstret) and supervisor CSR set (sstatus as a masked view of mstatus, sie/sip, stvec, scounteren, sscratch, sepc/scause/stval, satp WARL-limited to Bare/Sv39/Sv48/Sv57)
  • CSRRS/CSRRC implement the spec’s own strict rs1==x0 no-write rule – a real (non-x0) register holding 0 is still a genuine write attempt
  • mstatus.MXR/SUM are real bits consumed by the MMU’s permission check (SUM never applies to instruction fetch); FS and VS are likewise real, reset to Off (any F/D or vector instruction traps as illegal-instruction until software enables them), and auto-transition to Dirty in hardware on the first state-modifying commit – the vector floating-point forms specifically are gated by both FS and VS at once
  • mstatus.MPRV/MPP/MPV are real and consulted by the data-side translation (the full six-row truth table), never affecting instruction fetch or HLV/HSV
  • Zicfilp forward-edge CFI: a non-exempt JALR arms an expected-landing-pad state; the next fetched instruction must be a label-matching LPAD or a software-check exception (mcause=18, mtval=2) fires – enabled per privilege via menvcfg.LPE/henvcfg.LPE/senvcfg.LPE/mseccfg.MLPE, with the state trap-preserved in xPELP
  • Zicfiss backward-edge CFI: a dedicated shadow stack (ssp CSR) with SSPUSH/SSPOPCHK; a mismatched pop raises the same software-check exception (mtval=3), and the shadow-stack’s own pages use a new PTE encoding that only the SS instructions may write – any other write is a fatal access fault
  • Ssdbltrp/Smdbltrp double-trap detection: per-level sticky trap-in-flight bits (mstatus.MDT, sstatus.SDT, vsstatus.SDT); an unexpected trap into a level already in flight redirects one level up as mcause=16 with the suppressed cause in mtval2, and an unexpected trap targeting M-mode halts the hart in a permanent critical-error state
  • Unprivileged counters (cycle/time/instret) and F/D’s fflags/frm/fcsr; RV32’s high-half shadow CSRs are illegal instructions on this RV64 core
  • Synchronous exceptions: instruction/load/store misaligned access, illegal instruction, breakpoint, ECALL from U/S/M, and instruction/load/store page faults once paging is active; the H extension adds VS-mode ECALL (cause 10), virtual-instruction (22), and guest-page-fault causes (20/21/23)
  • Interrupts: machine/supervisor x {software, timer, external}; MSIP/MTIP/MEIP are hardware-driven from the CLINT/CLINT/PLIC; SEIP becomes a read-only view of the IMSIC S-file’s delivery signal when AIA’s mvien.SEIE is set (the PLIC’s own dedicated S-mode context is ORed in unconditionally); STIP becomes the real mtime >= stimecmp comparator once Sstc’s menvcfg.STCE is enabled
  • Interrupt priority: external > software > timer, machine level before supervisor level; the H extension adds a VS tier below both and AIA adds SGEIP (bit 12) to the same scheme
  • menvcfg/henvcfg/senvcfg: real STCE (Sstc), PBMTE (Svpbmt), ADUE (Svade/Svadu), FIOM, CBZE/CBCFE/CBIE (Zicboz/Zicbom), and LPE/SSE/DTE (Zicfilp/Zicfiss/Ssdbltrp) fields – henvcfg mirrors them for VS-stage (ADUE/SSE/DTE spec-forced to 0 whenever their menvcfg counterpart clears), and senvcfg mirrors the U-mode subset
  • WFI genuinely stalls fetch/issue until an individually-enabled-and-pending interrupt is observed – independent of the global enable bits – with mstatus.TW/hstatus.VTW timeout-wait and the U-mode/VU-mode rules enforced via a bounded hardware timer

Documented simplifications. Not silent scope cuts:

  • XS/UBE read a constant 0 – unimplemented; nothing in this design needs them yet
  • Every remaining menvcfg/henvcfg/senvcfg field is WPRI and reads 0

Synchronized Timer

A single shared high-resolution timer, synchronized across all harts by construction:

  • Exactly one mtime register in the whole design (the CLINT), incrementing every core clock cycle – e.g. 10 ns resolution at a 100 MHz core clock; every hart’s time CSR (rdtime) observes the literal same value in the same cycle, with no per-core replica to resynchronize and no drift to bound
  • Per-hart state is only mtimecmp[hart] and msip[hart]; the register layout matches the SiFive/QEMU “virt” CLINT, so an unmodified OpenSBI/Linux driver works against it
  • Sstc: a real per-hart stimecmp CSR compared against the same shared mtime, gated by M-mode’s menvcfg.STCE – once enabled, S-mode programs its own timer interrupt directly with no CLINT MMIO access and no M-mode relay; while disabled, S-mode access to stimecmp traps illegal-instruction (the mechanism firmware uses to detect whether the hand-off is safe)
Sep 20, 2026

i-RISCV Hypervisor Support

The i-RISCV core implements the RISC-V H (Hypervisor) extension. The following mechanisms are supported.

Virtualization Modes and CSRs

  • A virtualization-mode bit V layering VS-mode and VU-mode on top of HS-mode/U-mode; SRET is the only way a hart enters a guest (hstatus.SPV/sstatus.SPP)
  • The full hypervisor CSR set (hstatus, hedeleg/hideleg, hie/hvip/hip, hgeip/hgeie, henvcfg, hcounteren, htimedelta, htval/htinst, hgatp) and the VS-mode shadow CSRs (vsstatus, vsie/vsip, vstvec, vsscratch, vsepc, vscause, vstval, vsatp, vstimecmp)
  • Transparent CSR substitution while V=1: supervisor CSRs redirect to their VS counterparts, including satp/vsatp and stimecmp/vstimecmp
  • henvcfg mirrors menvcfg’s own enable fields (STCE, PBMTE, ADUE, FIOM, CBZE/CBCFE/CBIE, LPE/SSE/DTE) for VS-stage and genuinely gates VS-mode independently of menvcfg – Svpbmt/Zicbom/Zicboz access in VS-mode is gated by henvcfg’s own bit alone; only STCE and ADUE stack both levels (the guest’s effective enable is the AND), with SSE/DTE spec-forced to 0 whenever their menvcfg counterpart clears

Trap Delegation and Virtual Instructions

  • Three-way M/HS/VS trap routing via medeleg/mideleg plus the guest-level hedeleg/hideleg; VS interrupts (VSSIP/VSTIP/VSEIP) and SGEIP are always delegated at least to HS-level, and SGEIP can never be delegated further down
  • Interrupt-cause renumbering when delegated into VS-mode, so a guest sees ordinary supervisor numbering
  • Virtual-instruction exceptions for HS-qualified instructions blocked by V=1, covering both spec groups (VTVM/VTSR-gated and unconditional, incl. WFI under VTW)

Two-Stage Address Translation

  • Separate VS-stage and G-stage walkers/TLBs in Sv39x4/Sv48x4/Sv57x4, mirroring the single-stage modes
  • “Walk of a walk”: every VS-stage page-table read – root pointer and each PTE – is itself G-stage translated before reaching memory
  • Guest-page-fault causes (20/21/23) distinct from ordinary page faults, always attributed to the original access kind – including when a VS-stage PTE fetch (checked as an implicit read) fails in G-stage
  • HS-level mstatus.MXR overrides both stages while guest vsstatus.MXR overrides only the VS stage; HS-level SUM is ignored for two-stage accesses
  • G-stage’s own permission quirks per spec: the U bit is always checked as though in U-mode, and a cached final result is re-checked whenever the access kind or the active VMID changes

Nested Virtualization

  • A single hardware VS/G-stage context supports nested (“L2+”) virtualization through software trap-and-emulate: a guest hypervisor runs at ordinary VS-mode, and its privileged operations (e.g. hgatp writes, HFENCE.GVMA) virtual-instruction-trap to the outer hypervisor for emulation
  • Shadow G-stage page-table merging enforces the inner hypervisor’s intended permissions on top of the outer hosting mapping; arbitrarily deep nesting by recursively re-applying the same technique, with no additional hardware

Hypervisor Instructions and Guest Interrupts

  • HLV/HSV/HLVX explicit guest-memory access that always acts as though V=1 at hstatus.SPVP, overriding MPRV – including execute-for-read substitution in both stages
  • HFENCE.VVMA / HFENCE.GVMA TLB invalidation for the VS-stage and G-stage respectively, reusing SFENCE.VMA’s 4-case selective predicate – rs2 selects an ASID for VVMA, a single VMID for GVMA (see Tagged TLB)
  • Guest interrupt injection via hvip, real per-guest-file MSI delivery through two IMSIC guest files (GEILEN=2) selected by hstatus.VGEIN – each with its own physical MSI region, so migrating a guest between files never moves the device’s write target – and SGEIP/SGEIE pending/enable plumbing
  • VS-mode virtual timer: a real vstimecmp comparator (Sstc) OR’d with hvip.VSTIP, gated by the guest’s own henvcfg.STCE; accessing vstimecmp itself requires that gate stacked on top of HS-level’s menvcfg.STCE; htimedelta offsets the guest’s view of time

Disclosed Simplifications

Spec-legal, not silent:

  • htval/mtval2 always read 0 – software recovers the faulting address via stval (the guest virtual address) alone
  • G-stage checks R rather than X permission for a guest instruction fetch’s final address (the shared walker instance also serves the always-a-read PTE-fetch case); VS-stage’s own X check is unaffected and correctly enforced
  • A hypervisor must still set hvip.VSEIP=1 once per guest-file assignment: the guest file’s real delivery signal is not independently OR’d into hip.VSEIP (the pre-AIA software-injection path remains required)
Sep 20, 2026

i-RISCV SBI Firmware

The i-RISCV SoC provides two M-mode boot-firmware paths that reach the exact same kernel image from the same device tree. The default path uses upstream OpenSBI, genuinely unmodified; the alternate path is this project’s own standalone SBI implementation. Which firmware runs is selected purely by the linux_boot Makefile target: check/image (and their 72 variants) use OpenSBI, check-sbi/image-sbi (and their 72 variants) the standalone SBI.

OpenSBI (default)

  • A pinned, unmodified upstream OpenSBI release built with PLATFORM=generic – recognized purely through OpenSBI’s own FDT drivers matching the SoC’s device tree nodes (serial@10000000, clint@2000000, plic0), with zero source customization
  • A tiny SoC-specific M-mode prestage programs the entropy-source FUSE bank and jumps directly into OpenSBI’s fw_jump entry point – still in M-mode, no mret
  • All standard boot setup (medeleg/mideleg, menvcfg, mcounteren, PMP) is performed by OpenSBI itself, computed from the hart’s detected extensions – including trap-safe PMP probing on builds where PMP is disabled

Standalone SBI (alternate)

A from-scratch M-mode firmware implementing the modern SBI v2.0 extension set:

  • Base (spec v2.0): spec/implementation ID and version, PROBE_EXTENSION, and vendor/arch/imp IDs read directly from the hart’s own CSRs
  • TIME, IPI, RFENCE, HSM, SRST, DBCN – each a real handler, not a stub
  • The full legacy (v0.1) extension set (SET_TIMER, console get/putchar, IPIs, remote fences, SHUTDOWN) for pre-v2.0 guests
  • Unimplemented extensions are correctly reported absent by PROBE_EXTENSION and return SBI_ERR_NOT_SUPPORTED when called, so a probing guest kernel falls back cleanly

Real Cross-Hart Behavior

IPI/RFENCE/HSM genuinely reach harts other than the one servicing the ecall – the same code path serves 2-hart and 16-hart (SMP16) builds:

  • IPI: decodes the real hart_mask/hart_mask_base operand pair and raises a real CLINT MSIP against every targeted hart that HSM has genuinely released
  • RFENCE: every FID (REMOTE_FENCE_I, REMOTE_SFENCE_VMA[_ASID], REMOTE_HFENCE_VVMA[_ASID], REMOTE_HFENCE_GVMA[_VMID]) is a synchronous cross-hart TLB shootdown – the calling hart fences itself, every other targeted hart is woken via a CLINT-MSIP request/ack handshake that blocks until its fence completes; address/ASID/VMID operands are passed straight through to real selective invalidation (see Tagged TLB)
  • HSM: MAX_HARTS-sized per-hart state tables backing a secondary-hart park loop, so HART_START/HART_GET_STATUS operate on real harts

Boot Setup Performed by the Firmware

  • Trap delegation: medeleg/mideleg set for S-mode, including the five H-extension causes (ECALL-from-VS, guest-page-faults 20/21/23, virtual-instruction) so a VS-mode guest under KVM is serviced by its own hypervisor – both paths delegate all five
  • menvcfg and mcounteren enabled so Sstc/Svpbmt/Zicbom/Zicboz and rdtime work from S-mode without trapping – the kernel’s early entropy initialization reads rdtime before any driver is up, so a missing mcounteren.TM breaks every boot
  • PMP: at least one permissive entry configured before dropping to S-mode (mandatory – a present-but-unconfigured PMP denies all S/U access); the standalone SBI writes one unlocked NAPOT entry covering the whole 56-bit physical address space, runtime-skipped on PMP_ENABLE=0 builds; OpenSBI programs its own RX/RW split from the detected PMP count with trap-safe probing
  • mseccfg’s Zkr sseed/useed bits enabled once at boot
  • Entropy-source FUSE bank programmed once at boot via shared code linked into both firmware paths (alongside the mseccfg setup)
  • The branch predictor needs no firmware action: transparent always-on hardware that clears its own BTB/PHT/RAS state on every privilege-domain crossing

How Linux Actually Uses It

A guest OS discovers what the firmware offers purely through PROBE_EXTENSION; for the kernels this project boots:

  • Timer: the device tree advertises "sstc", so S-mode writes stimecmp directly and the TIME extension’s SET_TIMER is never issued (implemented for spec completeness)
  • Console: bootargs select earlycon=uart8250,mmio,.../console=ttyS0, so Linux’s in-tree 8250 driver binds to the real UART node – no SBI involvement in the active console path; DBCN and legacy console stay implemented for guests configured with earlycon=sbi/hvc0
  • HSM/IPI/RFENCE: exercised by the kernel’s own SMP boot and TLB-shootdown code paths (see Linux)
  • SRST: guest reboot/poweroff issue SRST exactly as on real hardware

Standalone Firmware Validation

Before ever combining it with a kernel, the standalone firmware is validated in isolation by S-mode smoke tests linked to load where the kernel normally would (linux_boot/tools_scripts/run_sbi_smoke.sh {up,smp,dueling}): every implemented extension exercised via real ecalls on the single-hart build; a second real hart started and IPI/fenced on the 2-hart build; and a two-hart dueling-RFENCE regression test that reproduces (and proves fixed) a cross-hart fence deadlock in the non-reentrant M-mode trap context.

Disclosed Simplifications

Spec-legal, not silent:

  • HART_STOP parks the calling hart permanently rather than supporting a later re-HART_START of the same hart – Linux’s normal SMP boot never calls it (only CPU hotplug-remove does)
  • SRST reboot re-enters the firmware entry point with RAM untouched; no real board reset exists, so CSRs that only a hardware reset clears keep their values
  • Legacy v0.1 remote calls take hart_mask as a pointer treated as already physical – correct in practice because every Linux version booted here prefers the modern by-value IPI/RFENCE extensions and never reaches this path
Sep 20, 2026

i-RISCV Linux Support

The i-RISCV SoC boots an unmodified, stock Linux kernel. Both v6.6 and v7.2 are maintained as sibling boot harnesses sharing the same RTL, both firmware paths, and one device tree – a release 2.5 years newer booting with zero RTL or firmware changes is itself a stability test, and v7.2’s extension-detection table additionally recognizes six extensions v6.6’s doesn’t (Zicond, Zfa, Zawrs, Zicbop, Zihintpause, Zkt).

What Boots

  • Unmodified upstream Linux (v6.6 and v7.2), configured for every extension this core implements
  • Two interchangeable M-mode boot-firmware paths: unmodified upstream OpenSBI (default) or this project’s own standalone SBI – both hand off to the exact same kernel image at the same address from the same device tree (see SBI)
  • A single generated device tree (firmware/dts/gen_soc_dts.py) advertising all ratified ISA strings plus the real UART, PLIC, APLIC, IOMMU, and IMSIC nodes – v6.6 leaves the IOMMU/IMSIC/APLIC nodes unprobed (it has no in-tree drivers for them), while v7.2 genuinely resolves all three

What Works During Boot

  • Full extension detection by the kernel itself (base ISA plus every Z/S extension its version recognizes)
  • Sstc: the kernel writes stimecmp directly from S-mode for every tick – no SBI TIME calls at all
  • Real serial console: the stock 8250 driver binds to the ns16550a UART (earlycon bootconsole, then ttyS0)
  • Full boot-to-poweroff confirmed end to end: memory management, RCU, the ASID allocator, devtmpfs, IOMMU/IMSIC/APLIC driver init (v7.2), a real clocksource switch, and KVM all initialize; the non-interactive /init then dumps /proc/cpuinfo and powers off cleanly at roughly 143M-234M simulated cycles with no panic or hang
  • Real 2-hart SMP boot on both kernel versions (CONFIG_SMP=y against the multicore top): the second hart is parked at reset and released by SBI HSM, with IPIs via CLINT MSIP and synchronous cross-hart RFENCE TLB shootdowns (see SBI)
  • KVM: on v6.6 riscv_kvm_init() completes cleanly; on v7.2 the full KVM AIA SGEI path resolves against the real IMSIC S-file node and returns success within seconds of kernel time
  • This project’s own IOPMP platform driver binds on both kernels
  • Non-interactive userspace: PID 1 is a minimal POSIX-C program built against the kernel’s own vendored nolibc – it mounts devtmpfs/proc/sysfs, dumps /proc/cpuinfo to the console between fixed delimiters, and powers off; no interactive shell (an honest scope cut: one would need a full userspace cross-toolchain)
  • The simulator polls a testbench-only DBG_EXIT register that both firmware paths write on SRST shutdown, so a completed run stops itself immediately instead of idling to its cycle ceiling – the register is dead-code-eliminated from any real synthesis

Linux Boot Log + /proc/cpuinfo Output

The following listing is taken from a Verilator simulation run showing the unmodified OpenSBI booting an unmodified Linux 7.2.4 kernel.

At the end of the log the init process mounts /proc and /sys and dumps the content of /proc/cpuinfo.

The logs provide a glimpse on the support. The common regression testing of the i-RISCV SoC include the following testing:

SBI type Number of Harts Linux Kernel
OpenSBI 1 Linux kernel 7.2.4 without SMP support
OpenSBI 2 Linux kernel 7.2.4 with SMP support
OpenSBI 1 Linux kernel 6.6 without SMP support
OpenSBI 2 Linux kernel 6.6 with SMP support
standalone SBI 1 Linux kernel 7.2.4 without SMP support
standalone SBI 2 Linux kernel 7.2.4 with SMP support
standalone SBI 1 Linux kernel 6.6 without SMP support
standalone SBI 2 Linux kernel 6.6 with SMP support

All Linux kernels and OpenSBI code are used unmodified.

Linux Boot with OpenSBI and Single Core

OpenSBI v1.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name               : Chronox i-RISCV RVA23U64 SoC 2.0.0 1-hart
Platform Features           : medeleg
Platform HART Count         : 1
Platform HART Protection    : epmp
Platform IPI Device         : aclint-mswi
Platform Timer Device       : aclint-mtimer @ 50000000Hz
Platform Console Device     : uart8250
Platform HSM Device         : ---
Platform PMU Device         : ---
Platform Reboot Device      : ---
Platform Shutdown Device    : syscon-poweroff
Platform Suspend Device     : ---
Platform CPPC Device        : ---
Firmware Base               : 0x80100000
Firmware Size               : 321 KB
Firmware RW Offset          : 0x40000
Firmware RW Size            : 65 KB
Firmware Heap Offset        : 0x47000
Firmware Heap Size          : 37 KB (total), 0 KB (reserved), 12 KB (used), 24 KB (free)
Firmware Scratch Size       : 4096 B (total), 1472 B (used), 2624 B (free)
Runtime SBI Version         : 3.0
Standard SBI Extensions     : rfnc,ipi,base,hsm,srst,pmu,dbcn,fwft,legacy,sse,time
Experimental SBI Extensions : none

Domain0 Name                : root
Domain0 Boot HART           : 0
Domain0 HARTs               : 0x0*
Domain0 Region00            : 0x0000000080140000-0x000000008015ffff M: (F,R,W) S/U: ()
Domain0 Region01            : 0x0000000080100000-0x000000008013ffff M: (F,R,X) S/U: ()
Domain0 Region02            : 0x000000003f000000-0x000000003f000007 M: (I,R,W) S/U: (R,W)
Domain0 Region03            : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region04            : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region05            : 0x000000000c000000-0x000000000fffffff M: (I,R,W) S/U: (R,W)
Domain0 Region06            : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address        : 0x0000000080200000
Domain0 Next Arg1           : 0x0000000082300000
Domain0 Next Mode           : S-mode
Domain0 SysReset            : yes
Domain0 SysSuspend          : yes

Boot HART ID                : 0
Boot HART Domain            : root
Boot HART Priv Version      : v1.12
Boot HART Base ISA          : rv64imafdcvh
Boot HART ISA Extensions    : smaia,smepmp,sstc,zicntr,zicboz,zicbom,svpbmt,svade,svadu,zicfilp,zicfiss,ssdbltrp,v,f,d
Boot HART PMP Count         : 64
Boot HART PMP Granularity   : 2 bits
Boot HART PMP Address Bits  : 54
Boot HART MHPM Info         : 0 (0x00000000)
Boot HART Debug Triggers    : 0 triggers
Boot HART MIDELEG           : 0x0000000000001666
Boot HART MEDELEG           : 0x0000000000f4b509
[    0.000000] Linux version 7.2.4 (sm@GravitonMB.lan) (riscv64-elf-gcc (GCC) 16.2.0, GNU ld (GNU Binutils) 2.42) #1 PREEMPT Thu Oct  1 13:05:56 CEST 2026
[    0.000000] Machine model: Chronox i-RISCV RVA23U64 SoC 2.0.0 1-hart
[    0.000000] SBI specification v3.0 detected
[    0.000000] SBI implementation ID=0x1 Version=0x10009
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI SRST extension detected
[    0.000000] SBI DBCN extension detected
[    0.000000] SBI FWFT extension detected
[    0.000000] earlycon: uart8250 at MMIO 0x0000000010000000 (options '115200n8,50000000')
[    0.000000] printk: legacy bootconsole [uart8250] enabled
[    0.000000] OF: reserved mem: 0x0000000080100000..0x000000008013ffff (256 KiB) nomap non-reusable mmode_resv1@80100000
[    0.000000] OF: reserved mem: 0x0000000080140000..0x000000008015ffff (128 KiB) nomap non-reusable mmode_resv0@80140000
[    0.000000] OF: reserved mem: 0x0000000080000000..0x00000000801fffff (2048 KiB) nomap non-reusable firmware@80000000
[    0.000000] OF: reserved mem: OVERLAP DETECTED!
[    0.000000] firmware@80000000 (0x0000000080000000--0x0000000080200000) overlaps with mmode_resv1@80100000 (0x0000000080100000--0x0000000080140000)
[    0.000000] riscv: base ISA extensions acdfhimv
[    0.000000] riscv: ELF capabilities acdfimv
[    0.000000] Ticket spinlock: enabled
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080000000-0x000000008fffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080000000-0x00000000801fffff]
[    0.000000]   node   0: [mem 0x0000000080200000-0x000000008fffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x000000008fffffff]
[    0.000000] Kernel command line: earlycon=uart8250,mmio,0x10000000,115200n8,50000000 console=ttyS0,115200 panic=-1
[    0.000000] printk: log buffer data + meta data: 131072 + 458752 = 589824 bytes
[    0.000000] Dentry cache hash table entries: 32768 (order: 6, 262144 bytes, linear)
[    0.000000] Inode-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
[    0.000000] software IO TLB: SWIOTLB bounce buffer size adjusted to 0MB
[    0.000000] software IO TLB: area num 1.
[    0.000000] software IO TLB: mapped [mem 0x000000008ef4e000-0x000000008ef8e000] (0MB)
[    0.000000] Virtual kernel memory layout:
[    0.000000]       fixmap : 0xffffffc4fea00000 - 0xffffffc4ff000000   (6144 kB)
[    0.000000]       pci io : 0xffffffc4ff000000 - 0xffffffc500000000   (  16 MB)
[    0.000000]      vmemmap : 0xffffffc500000000 - 0xffffffc600000000   (4096 MB)
[    0.000000]      vmalloc : 0xffffffc600000000 - 0xffffffd600000000   (  64 GB)
[    0.000000]      modules : 0xffffffff00d07000 - 0xffffffff80000000   (2034 MB)
[    0.000000]       lowmem : 0xffffffd600000000 - 0xffffffd610000000   ( 256 MB)
[    0.000000]       kernel : 0xffffffff80000000 - 0xffffffffffffffff   (2047 MB)
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 65536
[    0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] RCU Tasks: Setting shift to 0 and lim to 1 rcu_task_cb_adjust=1 rcu_task_cpu_ids=1.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped using AIA
[    0.000000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0xb8812736b, max_idle_ns: 440795202655 ns
[    0.000015] sched_clock: 64 bits at 50MHz, resolution 20ns, wraps every 4398046511100ns
[    0.008646] riscv-timer: Timer interrupt in S-mode is available via sstc extension
[    0.019614] Calibrating delay loop (skipped), value calculated using timer frequency.. 100.00 BogoMIPS (lpj=200000)
[    0.030923] pid_max: default: 32768 minimum: 301
[    0.041707] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.049645] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.065359] VFS: Finished mounting rootfs on nullfs
[    0.101805] riscv: ELF compat mode unsupported
[    0.102098] ASID allocator using 16 bits (65536 entries)
[    0.118696] Memory: 223376K/262144K available (3970K kernel code, 4759K rwdata, 2048K rodata, 2188K init, 244K bss, 37584K reserved, 0K cma-reserved)
[    0.140539] devtmpfs: initialized
[    0.183883] posixtimers hash table entries: 512 (order: 2, 16384 bytes, linear)
[    0.193230] futex hash table entries: 256 (12288 bytes on 1 NUMA nodes, total 12 KiB, linear).
[    0.214265] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[    0.225457] cpuidle: using governor menu
[    0.306757] iommu: Default domain type: Translated
[    0.312073] iommu: DMA domain TLB invalidation policy: strict mode
[    0.324128] clocksource: Switched to clocksource riscv_clocksource
[    0.384867] cpu0: scalar unaligned word access speed is 3.30x byte access speed (fast)
[    0.413240] cpu0: vector unaligned word access speed is 2.94x byte access speed (fast)
[    0.428757] kvm [1]: hypervisor extension available
[    0.440926] kvm [1]: highest G-stage page table mode is Sv57x4
[    0.451725] kvm [1]: VMID 14 bits available
[    0.460261] kvm [1]: AIA available with 0 guest external interrupts
[    0.481461] workingset: timestamp_bits=46 (anon: 41) max_order=16 bucket_order=0 (anon: 0)
[    0.498698] io scheduler mq-deadline registered
[    0.503789] io scheduler kyber registered
[    0.508966] io scheduler bfq registered
[    0.516814] riscv-imsic: interrupt-controller@28000000:  hart-index-bits: 0,  guest-index-bits: 0
[    0.526446] riscv-imsic: interrupt-controller@28000000: group-index-bits: 0, group-index-shift: 24
[    0.536325] riscv-imsic: interrupt-controller@28000000: per-CPU IDs 63 at base address 0x0000000028000000
[    0.546695] riscv-imsic: interrupt-controller@28000000: total 62 interrupts available
[    0.560313] riscv-plic: interrupt-controller@c000000: mapped 9 interrupts with 0 handlers for 2 contexts.
[    0.619239] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    0.673387] of_serial 10000000.serial: error -ENXIO: IRQ index 0 not found
[    0.687580] printk: legacy console [ttyS0] disabled
[    0.705365] 10000000.serial: ttyS0 at MMIO 0x10000000 (irq = 0, base_baud = 3125000) is a 16550A
[    0.716003] printk: legacy console [ttyS0] enabled
[    0.716003] printk: legacy console [ttyS0] enabled
[    0.726950] printk: legacy bootconsole [uart8250] disabled
[    0.726950] printk: legacy bootconsole [uart8250] disabled
[    1.560799] debug_vm_pgtable: [debug_vm_pgtable         ]: Validating architecture page table helpers
[    1.574771] riscv-aplic 2c000000.interrupt-controller: 8 interrupts forwarded to MSI base 0x0000000028000000
[    1.594902] riscv,iommu 30000000.iommu: using wire-signaled interrupts
[    1.618206] riscv-iopmp 34000000.iopmp: IOPMP found: spec v8.0, impl 0x00000001, 16 entries, 16 RRIDs, not yet armed (HWCFG0.enable=0, every access currently granted)
[    1.641227] clk: Disabling unused clocks
[    1.645586] PM: genpd: Disabling unused power domains
[    1.651319] Warning: unable to open an initial console.
[    1.697504] Freeing unused kernel image (initmem) memory: 2188K
[    1.704634] Run /init as init process
[    1.718407] [init] setup_console: mount=0 open1=0

[init] POSIX-C init starting (nolibc, riscv64)
=== BEGIN STATUS/CSR DUMP ===
IRISCV_STATUS_NAME: 0x4952562d54535431 ("IRV-TST1")
IRISCV_STATUS_VERSION: 0x0000000000000002
IRISCV_STATUS_MECHANISMS: 0x0000000000003fff
CSR_IRISCV_ES_FUSE_DATA: 0x00000000000061a8
CSR_IRISCV_ES_FUSE_KEYS: 0x0000000000000000
CSR_IRISCV_ES_FUSE_LOCK: 0x0000000000000001
CSR_IRISCV_ES_FUSE_SEL: 0x0000000000000050
CSR_IRISCV_ES_HEALTH: 0x0000000000000000
IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED: 0xfffffffffffffec8
IRISCV_CRYPTO_SELFTEST_PASSED: 0x0000000000000137
IRISCV_CRYPTO_SELFTEST_FAILED: 0x0000000000000000
IRISCV_CRYPTO_FIPS_APPROVED: 0x0000000000000135
=== END STATUS/CSR DUMP ===
[init] mount /proc (proc): OK
[init] mount /sys (sysfs): OK
=== BEGIN /proc/cpuinfo ===
processor	: 0
hart		: 0
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

=== END /proc/cpuinfo ===
[init] userspace checks complete -- powering off
[    2.469642] kvm: exiting hardware virtualization
[    2.474703] reboot: Power down

Linux Boot with OpenSBI and Two Core SMP

OpenSBI v1.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name               : Chronox i-RISCV RVA23U64 SoC 2.0.0 2-hart
Platform Features           : medeleg
Platform HART Count         : 2
Platform HART Protection    : epmp
Platform IPI Device         : aclint-mswi
Platform Timer Device       : aclint-mtimer @ 50000000Hz
Platform Console Device     : uart8250
Platform HSM Device         : ---
Platform PMU Device         : ---
Platform Reboot Device      : ---
Platform Shutdown Device    : syscon-poweroff
Platform Suspend Device     : ---
Platform CPPC Device        : ---
Firmware Base               : 0x80100000
Firmware Size               : 333 KB
Firmware RW Offset          : 0x40000
Firmware RW Size            : 77 KB
Firmware Heap Offset        : 0x49000
Firmware Heap Size          : 41 KB (total), 0 KB (reserved), 12 KB (used), 28 KB (free)
Firmware Scratch Size       : 4096 B (total), 1472 B (used), 2624 B (free)
Runtime SBI Version         : 3.0
Standard SBI Extensions     : rfnc,ipi,base,hsm,srst,pmu,dbcn,fwft,legacy,sse,time
Experimental SBI Extensions : none

Domain0 Name                : root
Domain0 Boot HART           : 0
Domain0 HARTs               : 0x0*,0x1*
Domain0 Region00            : 0x0000000080140000-0x000000008015ffff M: (F,R,W) S/U: ()
Domain0 Region01            : 0x0000000080100000-0x000000008013ffff M: (F,R,X) S/U: ()
Domain0 Region02            : 0x000000003f000000-0x000000003f000007 M: (I,R,W) S/U: (R,W)
Domain0 Region03            : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region04            : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region05            : 0x000000000c000000-0x000000000fffffff M: (I,R,W) S/U: (R,W)
Domain0 Region06            : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address        : 0x0000000080200000
Domain0 Next Arg1           : 0x0000000082300000
Domain0 Next Mode           : S-mode
Domain0 SysReset            : yes
Domain0 SysSuspend          : yes

Boot HART ID                : 0
Boot HART Domain            : root
Boot HART Priv Version      : v1.12
Boot HART Base ISA          : rv64imafdcvh
Boot HART ISA Extensions    : smaia,smepmp,sstc,zicntr,zicboz,zicbom,svpbmt,svade,svadu,zicfilp,zicfiss,ssdbltrp,v,f,d
Boot HART PMP Count         : 64
Boot HART PMP Granularity   : 2 bits
Boot HART PMP Address Bits  : 54
Boot HART MHPM Info         : 0 (0x00000000)
Boot HART Debug Triggers    : 0 triggers
Boot HART MIDELEG           : 0x0000000000001666
Boot HART MEDELEG           : 0x0000000000f4b509
[    0.000000] Booting Linux on hartid 0
[    0.000000] Linux version 7.2.4 (sm@GravitonMB.lan) (riscv64-elf-gcc (GCC) 16.2.0, GNU ld (GNU Binutils) 2.42) #1 SMP PREEMPT Thu Oct  1 13:10:12 CEST 2026
[    0.000000] Machine model: Chronox i-RISCV RVA23U64 SoC 2.0.0 2-hart
[    0.000000] SBI specification v3.0 detected
[    0.000000] SBI implementation ID=0x1 Version=0x10009
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI SRST extension detected
[    0.000000] SBI DBCN extension detected
[    0.000000] SBI FWFT extension detected
[    0.000000] earlycon: uart8250 at MMIO 0x0000000010000000 (options '115200n8,50000000')
[    0.000000] printk: legacy bootconsole [uart8250] enabled
[    0.000000] OF: reserved mem: 0x0000000080100000..0x000000008013ffff (256 KiB) nomap non-reusable mmode_resv1@80100000
[    0.000000] OF: reserved mem: 0x0000000080140000..0x000000008015ffff (128 KiB) nomap non-reusable mmode_resv0@80140000
[    0.000000] OF: reserved mem: 0x0000000080000000..0x00000000801fffff (2048 KiB) nomap non-reusable firmware@80000000
[    0.000000] OF: reserved mem: OVERLAP DETECTED!
[    0.000000] firmware@80000000 (0x0000000080000000--0x0000000080200000) overlaps with mmode_resv1@80100000 (0x0000000080100000--0x0000000080140000)
[    0.000000] SBI HSM extension detected
[    0.000000] riscv: base ISA extensions acdfhimv
[    0.000000] riscv: ELF capabilities acdfimv
[    0.000000] Ticket spinlock: enabled
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080000000-0x000000008fffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080000000-0x00000000801fffff]
[    0.000000]   node   0: [mem 0x0000000080200000-0x000000008fffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x000000008fffffff]
[    0.000000] percpu: Embedded 19 pages/cpu s47664 r0 d30160 u77824
[    0.000000] Kernel command line: earlycon=uart8250,mmio,0x10000000,115200n8,50000000 console=ttyS0,115200 panic=-1
[    0.000000] printk: log buffer data + meta data: 131072 + 458752 = 589824 bytes
[    0.000000] Dentry cache hash table entries: 32768 (order: 6, 262144 bytes, linear)
[    0.000000] Inode-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
[    0.000000] software IO TLB: SWIOTLB bounce buffer size adjusted to 0MB
[    0.000000] software IO TLB: area num 2.
[    0.000000] software IO TLB: SWIOTLB bounce buffer size roundup to 0MB
[    0.000000] software IO TLB: mapped [mem 0x000000008eeee000-0x000000008ef6e000] (0MB)
[    0.000000] Virtual kernel memory layout:
[    0.000000]       fixmap : 0xffffffc4fea00000 - 0xffffffc4ff000000   (6144 kB)
[    0.000000]       pci io : 0xffffffc4ff000000 - 0xffffffc500000000   (  16 MB)
[    0.000000]      vmemmap : 0xffffffc500000000 - 0xffffffc600000000   (4096 MB)
[    0.000000]      vmalloc : 0xffffffc600000000 - 0xffffffd600000000   (  64 GB)
[    0.000000]      modules : 0xffffffff00f02000 - 0xffffffff80000000   (2032 MB)
[    0.000000]       lowmem : 0xffffffd600000000 - 0xffffffd610000000   ( 256 MB)
[    0.000000]       kernel : 0xffffffff80000000 - 0xffffffffffffffff   (2047 MB)
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 65536
[    0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=2, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: 	RCU debug extended QS entry/exit.
[    0.000000] 	Trampoline variant of Tasks RCU enabled.
[    0.000000] 	Tracing variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] RCU Tasks: Setting shift to 1 and lim to 1 rcu_task_cb_adjust=1 rcu_task_cpu_ids=2.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped using AIA
[    0.000000] riscv-imsic: interrupt-controller@28000000: providing IPIs using interrupt 1
[    0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    0.000000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0xb8812736b, max_idle_ns: 440795202655 ns
[    0.000017] sched_clock: 64 bits at 50MHz, resolution 20ns, wraps every 4398046511100ns
[    0.008748] riscv-timer: Timer interrupt in S-mode is available via sstc extension
[    0.020581] Calibrating delay loop (skipped), value calculated using timer frequency.. 100.00 BogoMIPS (lpj=200000)
[    0.032358] pid_max: default: 32768 minimum: 301
[    0.043565] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.051632] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.068995] VFS: Finished mounting rootfs on nullfs
[    0.118617] riscv: ELF compat mode unsupported
[    0.118975] ASID allocator using 16 bits (65536 entries)
[    0.134306] rcu: Hierarchical SRCU implementation.
[    0.139694] rcu: 	Max phase no-delay instances is 1000.
[    0.152925] Timer migration: 1 hierarchy levels; 8 children per group; 1 crossnode level
[    0.170465] smp: Bringing up secondary CPUs ...
[    0.193426] smp: Brought up 1 node, 2 CPUs
[    0.201486] Memory: 220080K/262144K available (4205K kernel code, 4680K rwdata, 2048K rodata, 2245K init, 245K bss, 40020K reserved, 0K cma-reserved)
[    0.227532] devtmpfs: initialized
[    0.285782] posixtimers hash table entries: 1024 (order: 3, 32768 bytes, linear)
[    0.296173] futex hash table entries: 512 (32768 bytes on 1 NUMA nodes, total 32 KiB, linear).
[    0.324912] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[    0.336753] cpuidle: using governor menu
[    0.426277] iommu: Default domain type: Translated
[    0.431732] iommu: DMA domain TLB invalidation policy: strict mode
[    0.447281] clocksource: Switched to clocksource riscv_clocksource
[    0.509928] cpu1: scalar unaligned word access speed is 3.36x byte access speed (fast)
[    0.509970] cpu0: scalar unaligned word access speed is 3.36x byte access speed (fast)
[    0.549709] cpu1: vector unaligned word access speed is 3.25x byte access speed (fast)
[    0.549720] cpu0: vector unaligned word access speed is 3.26x byte access speed (fast)
[    0.570916] kvm [1]: hypervisor extension available
[    0.576610] kvm [1]: highest G-stage page table mode is Sv57x4
[    0.583328] kvm [1]: VMID 14 bits available
[    0.592705] kvm [1]: AIA available with 0 guest external interrupts
[    0.618437] workingset: timestamp_bits=46 (anon: 41) max_order=16 bucket_order=0 (anon: 0)
[    0.635808] io scheduler mq-deadline registered
[    0.641036] io scheduler kyber registered
[    0.646446] io scheduler bfq registered
[    0.656227] riscv-imsic: interrupt-controller@28000000:  hart-index-bits: 1,  guest-index-bits: 0
[    0.666004] riscv-imsic: interrupt-controller@28000000: group-index-bits: 0, group-index-shift: 24
[    0.675979] riscv-imsic: interrupt-controller@28000000: per-CPU IDs 63 at base address 0x0000000028000000
[    0.686503] riscv-imsic: interrupt-controller@28000000: total 124 interrupts available
[    0.701272] riscv-plic: interrupt-controller@c000000: mapped 9 interrupts with 0 handlers for 4 contexts.
[    0.761841] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    0.815693] of_serial 10000000.serial: error -ENXIO: IRQ index 0 not found
[    0.828424] printk: legacy console [ttyS0] disabled
[    0.844996] 10000000.serial: ttyS0 at MMIO 0x10000000 (irq = 0, base_baud = 3125000) is a 16550A
[    0.855892] printk: legacy console [ttyS0] enabled
[    0.855892] printk: legacy console [ttyS0] enabled
[    0.867502] printk: legacy bootconsole [uart8250] disabled
[    0.867502] printk: legacy bootconsole [uart8250] disabled
[    1.773779] debug_vm_pgtable: [debug_vm_pgtable         ]: Validating architecture page table helpers
[    1.788057] riscv-aplic 2c000000.interrupt-controller: 8 interrupts forwarded to MSI base 0x0000000028000000
[    1.805465] riscv,iommu 30000000.iommu: using wire-signaled interrupts
[    1.828844] riscv-iopmp 34000000.iopmp: IOPMP found: spec v8.0, impl 0x00000001, 16 entries, 16 RRIDs, not yet armed (HWCFG0.enable=0, every access currently granted)
[    1.853016] clk: Disabling unused clocks
[    1.857554] PM: genpd: Disabling unused power domains
[    1.863515] Warning: unable to open an initial console.
[    1.899101] Freeing unused kernel image (initmem) memory: 2244K
[    1.906463] Run /init as init process
[    1.920426] [init] setup_console: mount=0 open1=0

[init] POSIX-C init starting (nolibc, riscv64)
=== BEGIN STATUS/CSR DUMP ===
IRISCV_STATUS_NAME: 0x4952562d54535431 ("IRV-TST1")
IRISCV_STATUS_VERSION: 0x0000000000000002
IRISCV_STATUS_MECHANISMS: 0x0000000000003fff
CSR_IRISCV_ES_FUSE_DATA: 0x00000000000061a8
CSR_IRISCV_ES_FUSE_KEYS: 0x0000000000000000
CSR_IRISCV_ES_FUSE_LOCK: 0x0000000000000001
CSR_IRISCV_ES_FUSE_SEL: 0x0000000000000000
CSR_IRISCV_ES_HEALTH: 0x0000000000000000
IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED: 0xfffffffffffffec8
IRISCV_CRYPTO_SELFTEST_PASSED: 0x0000000000000137
IRISCV_CRYPTO_SELFTEST_FAILED: 0x0000000000000000
IRISCV_CRYPTO_FIPS_APPROVED: 0x0000000000000135
=== END STATUS/CSR DUMP ===
[init] mount /proc (proc): OK
[init] mount /sys (sysfs): OK
=== BEGIN /proc/cpuinfo ===
processor	: 0
hart		: 0
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 1
hart		: 1
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

=== END /proc/cpuinfo ===
[init] userspace checks complete -- powering off
[    2.977669] kvm: exiting hardware virtualization
[    2.983092] reboot: Power down

Linux Boot with OpenSBI and Eight Core SMP

In addition, an 8-Hart test is implemented executing Linux 7.2.4 together with OpenSBI upon request. The goal of this test is to validate the heavy contention handling when 8 CPUs are instantiated and operated by Linux.

OpenSBI v1.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name               : Chronox i-RISCV RVA23U64 SoC 2.0.0 8-hart
Platform Features           : medeleg
Platform HART Count         : 8
Platform HART Protection    : epmp
Platform IPI Device         : aclint-mswi
Platform Timer Device       : aclint-mtimer @ 50000000Hz
Platform Console Device     : uart8250
Platform HSM Device         : ---
Platform PMU Device         : ---
Platform Reboot Device      : ---
Platform Shutdown Device    : syscon-poweroff
Platform Suspend Device     : ---
Platform CPPC Device        : ---
Firmware Base               : 0x80100000
Firmware Size               : 407 KB
Firmware RW Offset          : 0x40000
Firmware RW Size            : 151 KB
Firmware Heap Offset        : 0x55000
Firmware Heap Size          : 67 KB (total), 1 KB (reserved), 12 KB (used), 53 KB (free)
Firmware Scratch Size       : 4096 B (total), 1472 B (used), 2624 B (free)
Runtime SBI Version         : 3.0
Standard SBI Extensions     : rfnc,ipi,base,hsm,srst,pmu,dbcn,fwft,legacy,sse,time
Experimental SBI Extensions : none

Domain0 Name                : root
Domain0 Boot HART           : 0
Domain0 HARTs               : 0x0*,0x1*,0x2*,0x3*,0x4*,0x5*,0x6*,0x7*
Domain0 Region00            : 0x0000000080100000-0x000000008013ffff M: (F,R,X) S/U: ()
Domain0 Region01            : 0x0000000080140000-0x000000008017ffff M: (F,R,W) S/U: ()
Domain0 Region02            : 0x000000003f000000-0x000000003f000007 M: (I,R,W) S/U: (R,W)
Domain0 Region03            : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region04            : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region05            : 0x000000000c000000-0x000000000fffffff M: (I,R,W) S/U: (R,W)
Domain0 Region06            : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address        : 0x0000000080200000
Domain0 Next Arg1           : 0x0000000082300000
Domain0 Next Mode           : S-mode
Domain0 SysReset            : yes
Domain0 SysSuspend          : yes

Boot HART ID                : 0
Boot HART Domain            : root
Boot HART Priv Version      : v1.12
Boot HART Base ISA          : rv64imafdcvh
Boot HART ISA Extensions    : smaia,smepmp,sstc,zicntr,zicboz,zicbom,svpbmt,svade,svadu,zicfilp,zicfiss,ssdbltrp,v,f,d
Boot HART PMP Count         : 64
Boot HART PMP Granularity   : 2 bits
Boot HART PMP Address Bits  : 54
Boot HART MHPM Info         : 0 (0x00000000)
Boot HART Debug Triggers    : 0 triggers
Boot HART MIDELEG           : 0x0000000000001666
Boot HART MEDELEG           : 0x0000000000f4b509
[    0.000000] Booting Linux on hartid 0
[    0.000000] Linux version 7.2.4 (sm@GravitonMB.lan) (riscv64-elf-gcc (GCC) 16.2.0, GNU ld (GNU Binutils) 2.42) #2 SMP PREEMPT Fri Oct  2 06:41:05 CEST 2026
[    0.000000] Machine model: Chronox i-RISCV RVA23U64 SoC 2.0.0 8-hart
[    0.000000] SBI specification v3.0 detected
[    0.000000] SBI implementation ID=0x1 Version=0x10009
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI SRST extension detected
[    0.000000] SBI DBCN extension detected
[    0.000000] SBI FWFT extension detected
[    0.000000] earlycon: uart8250 at MMIO 0x0000000010000000 (options '115200n8,50000000')
[    0.000000] printk: legacy bootconsole [uart8250] enabled
[    0.000000] OF: reserved mem: 0x0000000080140000..0x000000008017ffff (256 KiB) nomap non-reusable mmode_resv1@80140000
[    0.000000] OF: reserved mem: 0x0000000080100000..0x000000008013ffff (256 KiB) nomap non-reusable mmode_resv0@80100000
[    0.000000] OF: reserved mem: 0x0000000080000000..0x00000000801fffff (2048 KiB) nomap non-reusable firmware@80000000
[    0.000000] OF: reserved mem: OVERLAP DETECTED!
[    0.000000] firmware@80000000 (0x0000000080000000--0x0000000080200000) overlaps with mmode_resv0@80100000 (0x0000000080100000--0x0000000080140000)
[    0.000000] SBI HSM extension detected
[    0.000000] riscv: base ISA extensions acdfhimv
[    0.000000] riscv: ELF capabilities acdfimv
[    0.000000] Ticket spinlock: enabled
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080000000-0x000000008fffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080000000-0x00000000801fffff]
[    0.000000]   node   0: [mem 0x0000000080200000-0x000000008fffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x000000008fffffff]
[    0.000000] percpu: Embedded 19 pages/cpu s47664 r0 d30160 u77824
[    0.000000] Kernel command line: earlycon=uart8250,mmio,0x10000000,115200n8,50000000 console=ttyS0,115200 panic=-1
[    0.000000] printk: log buffer data + meta data: 131072 + 458752 = 589824 bytes
[    0.000000] Dentry cache hash table entries: 32768 (order: 6, 262144 bytes, linear)
[    0.000000] Inode-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
[    0.000000] software IO TLB: SWIOTLB bounce buffer size adjusted to 0MB
[    0.000000] software IO TLB: area num 8.
[    0.000000] software IO TLB: SWIOTLB bounce buffer size roundup to 2MB
[    0.000000] software IO TLB: mapped [mem 0x000000008e800000-0x000000008ea00000] (2MB)
[    0.000000] Virtual kernel memory layout:
[    0.000000]       fixmap : 0xffffffc4fea00000 - 0xffffffc4ff000000   (6144 kB)
[    0.000000]       pci io : 0xffffffc4ff000000 - 0xffffffc500000000   (  16 MB)
[    0.000000]      vmemmap : 0xffffffc500000000 - 0xffffffc600000000   (4096 MB)
[    0.000000]      vmalloc : 0xffffffc600000000 - 0xffffffd600000000   (  64 GB)
[    0.000000]      modules : 0xffffffff00f02000 - 0xffffffff80000000   (2032 MB)
[    0.000000]       lowmem : 0xffffffd600000000 - 0xffffffd610000000   ( 256 MB)
[    0.000000]       kernel : 0xffffffff80000000 - 0xffffffffffffffff   (2047 MB)
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 65536
[    0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: 	RCU restricting CPUs from NR_CPUS=16 to nr_cpu_ids=8.
[    0.000000] rcu: 	RCU debug extended QS entry/exit.
[    0.000000] 	Trampoline variant of Tasks RCU enabled.
[    0.000000] 	Tracing variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=8
[    0.000000] RCU Tasks: Setting shift to 3 and lim to 1 rcu_task_cb_adjust=1 rcu_task_cpu_ids=8.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped using AIA
[    0.000000] riscv-imsic: interrupt-controller@28000000: providing IPIs using interrupt 1
[    0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    0.000000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0xb8812736b, max_idle_ns: 440795202655 ns
[    0.000017] sched_clock: 64 bits at 50MHz, resolution 20ns, wraps every 4398046511100ns
[    0.008757] riscv-timer: Timer interrupt in S-mode is available via sstc extension
[    0.022598] Calibrating delay loop (skipped), value calculated using timer frequency.. 100.00 BogoMIPS (lpj=200000)
[    0.034225] pid_max: default: 32768 minimum: 301
[    0.047256] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.055472] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.073171] VFS: Finished mounting rootfs on nullfs
[    0.124896] riscv: ELF compat mode unsupported
[    0.125782] ASID allocator using 16 bits (65536 entries)
[    0.140296] rcu: Hierarchical SRCU implementation.
[    0.145937] rcu: 	Max phase no-delay instances is 1000.
[    0.159285] Timer migration: 1 hierarchy levels; 8 children per group; 1 crossnode level
[    0.186156] smp: Bringing up secondary CPUs ...
[    0.333939] smp: Brought up 1 node, 8 CPUs
[    0.349803] Memory: 216864K/262144K available (4230K kernel code, 4680K rwdata, 2048K rodata, 2245K init, 246K bss, 42048K reserved, 0K cma-reserved)
[    0.377836] devtmpfs: initialized
[    0.470643] posixtimers hash table entries: 4096 (order: 5, 131072 bytes, linear)
[    0.486361] futex hash table entries: 2048 (131072 bytes on 1 NUMA nodes, total 128 KiB, linear).
[    0.516132] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[    0.527952] cpuidle: using governor menu
[    0.640130] iommu: Default domain type: Translated
[    0.645747] iommu: DMA domain TLB invalidation policy: strict mode
[    0.670816] clocksource: Switched to clocksource riscv_clocksource
[    0.737117] cpu0: scalar unaligned word access speed is 4.00x byte access speed (fast)
[    0.737152] cpu1: scalar unaligned word access speed is 4.00x byte access speed (fast)
[    0.737161] cpu2: scalar unaligned word access speed is 4.00x byte access speed (fast)
[    0.737163] cpu3: scalar unaligned word access speed is 4.00x byte access speed (fast)
[    0.737167] cpu4: scalar unaligned word access speed is 4.00x byte access speed (fast)
[    0.737171] cpu5: scalar unaligned word access speed is 4.00x byte access speed (fast)
[    0.737175] cpu6: scalar unaligned word access speed is 3.99x byte access speed (fast)
[    0.737177] cpu7: scalar unaligned word access speed is 3.99x byte access speed (fast)
[    0.839987] cpu2: vector unaligned word access speed is 2.67x byte access speed (fast)
[    0.840144] cpu4: vector unaligned word access speed is 2.68x byte access speed (fast)
[    0.840147] cpu1: vector unaligned word access speed is 2.72x byte access speed (fast)
[    0.840212] cpu5: vector unaligned word access speed is 2.68x byte access speed (fast)
[    0.840335] cpu6: vector unaligned word access speed is 2.68x byte access speed (fast)
[    0.840358] cpu3: vector unaligned word access speed is 2.68x byte access speed (fast)
[    0.840364] cpu7: vector unaligned word access speed is 2.69x byte access speed (fast)
[    0.840549] cpu0: vector unaligned word access speed is 2.70x byte access speed (fast)
[    0.912542] kvm [1]: hypervisor extension available
[    0.918038] kvm [1]: highest G-stage page table mode is Sv57x4
[    0.924607] kvm [1]: VMID 14 bits available
[    0.937768] kvm [1]: AIA available with 0 guest external interrupts
[    0.975566] workingset: timestamp_bits=46 (anon: 41) max_order=16 bucket_order=0 (anon: 0)
[    0.993703] io scheduler mq-deadline registered
[    0.998904] io scheduler kyber registered
[    1.004643] io scheduler bfq registered
[    1.017274] riscv-imsic: interrupt-controller@28000000:  hart-index-bits: 3,  guest-index-bits: 0
[    1.027106] riscv-imsic: interrupt-controller@28000000: group-index-bits: 0, group-index-shift: 24
[    1.036983] riscv-imsic: interrupt-controller@28000000: per-CPU IDs 63 at base address 0x0000000028000000
[    1.047525] riscv-imsic: interrupt-controller@28000000: total 496 interrupts available
[    1.072552] riscv-plic: interrupt-controller@c000000: mapped 9 interrupts with 0 handlers for 16 contexts.
[    1.133013] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    1.187356] of_serial 10000000.serial: error -ENXIO: IRQ index 0 not found
[    1.200142] printk: legacy console [ttyS0] disabled
[    1.216869] 10000000.serial: ttyS0 at MMIO 0x10000000 (irq = 0, base_baud = 3125000) is a 16550A
[    1.227351] printk: legacy console [ttyS0] enabled
[    1.227351] printk: legacy console [ttyS0] enabled
[    1.238437] printk: legacy bootconsole [uart8250] disabled
[    1.238437] printk: legacy bootconsole [uart8250] disabled
[    2.242066] debug_vm_pgtable: [debug_vm_pgtable         ]: Validating architecture page table helpers
[    2.256690] riscv-aplic 2c000000.interrupt-controller: 8 interrupts forwarded to MSI base 0x0000000028000000
[    2.273511] riscv,iommu 30000000.iommu: using wire-signaled interrupts
[    2.296395] riscv-iopmp 34000000.iopmp: IOPMP found: spec v8.0, impl 0x00000001, 16 entries, 16 RRIDs, not yet armed (HWCFG0.enable=0, every access currently granted)
[    2.323155] clk: Disabling unused clocks
[    2.327564] PM: genpd: Disabling unused power domains
[    2.333485] Warning: unable to open an initial console.
[    2.369178] Freeing unused kernel image (initmem) memory: 2244K
[    2.376496] Run /init as init process
[    2.390396] [init] setup_console: mount=0 open1=0

[init] POSIX-C init starting (nolibc, riscv64)
=== BEGIN STATUS/CSR DUMP ===
IRISCV_STATUS_NAME: 0x4952562d54535431 ("IRV-TST1")
IRISCV_STATUS_VERSION: 0x0000000000000002
IRISCV_STATUS_MECHANISMS: 0x0000000000003fff
CSR_IRISCV_ES_FUSE_DATA: 0x00000000000061a8
CSR_IRISCV_ES_FUSE_KEYS: 0x0000000000000000
CSR_IRISCV_ES_FUSE_LOCK: 0x0000000000000001
CSR_IRISCV_ES_FUSE_SEL: 0x0000000000000050
CSR_IRISCV_ES_HEALTH: 0x0000000000000000
IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED: 0xfffffffffffffec8
IRISCV_CRYPTO_SELFTEST_PASSED: 0x0000000000000137
IRISCV_CRYPTO_SELFTEST_FAILED: 0x0000000000000000
IRISCV_CRYPTO_FIPS_APPROVED: 0x0000000000000135
=== END STATUS/CSR DUMP ===
[init] mount /proc (proc): OK
[init] mount /sys (sysfs): OK
=== BEGIN /proc/cpuinfo ===
processor	: 0
hart		: 0
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 1
hart		: 1
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 2
hart		: 2
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 3
hart		: 3
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 4
hart		: 4
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 5
hart		: 5
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 6
hart		: 6
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

processor	: 7
hart		: 7
isa		: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt
mmu		: sv39
mvendorid	: 0x0
marchid		: 0x0
mimpid		: 0x0
hart isa	: rv64imafdcvh_zicbom_zicbop_zicboz_zicfilp_zicfiss_zicntr_zicond_zicsr_zifencei_zihintpause_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfhmin_zvkb_zvkg_zvkned_zvknhb_zvkt_smaia_ssaia_sstc_svade_svinval_svnapot_svpbmt

=== END /proc/cpuinfo ===
[init] userspace checks complete -- powering off
[    5.321017] kvm: exiting hardware virtualization
[    5.326324] reboot: Power down
Oct 2, 2026

i-RISCV Testing

The i-RISCV implementation is verified with multiple, complementary test approaches – from module-level unit tests up to booting an unmodified Linux kernel, plus a fully open-source flow that takes the same RTL to a real ECP5 bitstream. The following approaches are used.

Simulation Test Layers

  • Module-level unit tests (tb/unit/): every RTL module gets a standalone Verilator C++ harness that instantiates only that module, drives its ports directly, and self-checks against hand-computed expected values or independent reference models (e.g. an independently re-implemented RVC decoder model swept over all 49152 encodings, the host CPU’s own IEEE-754 FPU as floating-point oracle). Dedicated builds with deliberately wrong known-answer vectors prove each crypto core’s self-test failure/lockout and recovery paths.
  • Directed full-core assembly tests (tb/tests/asm/): self-contained, hand-written RV64I+Zicsr+Zifencei programs, each exercising one pipeline mechanism end-to-end through the real SoC – single-core (check) and multi-core (check-mc). Every test reports its verdict as a P/F byte over the real UART peripheral, so the console path is exercised by every test’s own plumbing. Where a mechanism’s observable effect is timing rather than architectural state – e.g. WFI’s genuine fetch/issue stall versus a NOP-like retirement – the test measures real elapsed cycles (rdtime) around the instruction and asserts it falls inside the expected bound window, across ordinary wait-and-wake, same-cycle-NOP, and every TW/VTW/U-mode/VS/VU trap scenario.
  • Build-parameter enable/disable matrix (verif/enable_matrix/): independently elaborated builds for every combination of the optional-mechanism *_ENABLE parameters (a 32-way Zvk*/entropy-source permutation plus separate UART and async-reseed legs – 356 runs in all), each running a general regression battery plus state-specific tests – real known-answer vectors when enabled, illegal-instruction proofs when disabled. Deliberate negative-control runs confirm the harness would actually fail if the RTL gating were broken.
  • External NIST ACVP test-vector harness (ACVP Parser): externally supplied NIST ACVP JSON vectors reach the real RTL unmodified through a dedicated C backend – no software reimplementation of any algorithm is involved (SHA-2, SHA-3/SHAKE, AES-256, GCM/GMAC, KMAC256). The backend is compiled and linked against the real RTL today; live external-vector reconciliation is tracked follow-on work.

Conformance and System-Level Testing

  • RISCOF conformance suites (verif/riscof/): RISC-V International’s own official riscv-arch-test suites (I, M, C, D, Zifencei, privilege) run through a RISCOF-based harness with a bind-based monitor – test sources and expected signatures are both produced by RVI, independent of this project; the reference is riscv-arch-test’s own shipped .reference_output files rather than a live golden model. 275 of 283 tests pass; the 8 open privilege misalign-* cases stem from that shipped-reference limitation (this core splits misaligned accesses in hardware, while RVI’s static signatures assume trap-based behavior) and are independently verified by directed tests. Where no official suite exists (V, H, AIA, IOMMU, vector crypto), the project’s own directed tests stand in; all results and open issues are disclosed.
  • Linux boot as a system-level test: an entirely unmodified, stock Linux kernel (v6.6 and v7.2) boots against the whole SoC through both firmware paths, on one and two harts – real, independently developed software with its own timing assumptions, catching classes of bug no directed test reaches (peripheral register-ordering hazards, firmware/CSR ordering issues, asynchronous-interrupt races).
  • Firmware smoke tests: standalone S-mode programs (linux_boot/tools_scripts/run_sbi_smoke.sh {up,smp,dueling}) validate the boot firmware in isolation without paying for a full kernel boot – every implemented SBI extension via real ecalls on one hart; a second real hart started and IPI/fenced on two harts; and a dueling-RFENCE regression for a cross-hart fence deadlock.
  • Differential fuzzing harness: generates randomized-but-legal RV64I+Zicsr/Zifencei instruction streams and runs the same stream through both a golden reference model of Spike and the RTL executed with the Verilator simulator, comparing full architectural state after every retired instruction: all GPRs, PC, and the CSR list. Divergences are delta-minimized into self-checking directed reproducers kept in-tree.

Hazard-Family Regressions and Assertion Safety Nets

  • Interrupt-vs-commit-race regressions: real CLINT timer interrupts phase-swept across the commit windows of every multi-cycle unit (AMO, CBO.ZERO, misaligned split stores, vector stores, cold page-table walks), with poison-value consumers and negative-control reverts proving each test genuinely discriminates
  • Cross-hart synchronization regressions: two harts free-running unsynchronized AMO and LR/SC increment races against a shared counter (the final count must be exactly 2N), plus PLIC in-service-latch coverage – every other shared, multi-hart-reachable resource audited safe by construction
  • SVA assertion safety net: assert property blocks checked under Verilator’s --assert in every simulation build point guard the whole hazard family; one property cross-checks an independently maintained enumeration of privilege/interrupt/translation-relevant CSR writes against the redirect-handling list, so a future edit that drops a category fires deterministically instead of waiting for a probabilistic directed test to hit its exact race cycle

Side-Channel and Non-Functional Audits

  • Constant-time (cycle-count) audits: each cryptographic core and vector-crypto unit is audited that execution latency never depends on the value of keys or data – only on public control signals – measured across different contents at fixed operation shapes; the hardware entropy source receives the same treatment.
  • Speculative-execution resistance: a citation-backed argument (the Spectre/Meltdown disclosure papers are archived in docs/specifications/) that no mis-speculated instruction can leave an observable footprint, plus predictor-state erasure on every privilege-domain crossing – verified at unit and directed-test level.

Entropy-Source Statistical Validation

  • Stochastic-model regression (scripts/entropy/): pure-Python statistical tests of the noise-source entropy models (closed-form checks, capture-format round-trips, chi-square machinery, restart-homogeneity, worst-case min-entropy accounting).
  • Captured-data analysis: real noise-source data – from simulation and from real silicon via the FPGA harness – is captured over UART and analyzed against the stochastic model to determine entropy rate.
  • Compliance analysis: SP 800-90C (RBG2(P)/RBG3(RS)) and AIS 20/31 v3.0 (DRG.4+PTG.2 / PTG.3) compliance is documented requirement-by-requirement, with all disclosed gaps listed.

Real-Hardware (FPGA) Testing

A fully open-source flow (yosys read_slang + synth_ecp5 -> nextpnr-ecp5 -> ecppack -> openFPGALoader) targets the ULX3S (Lattice ECP5):

  • The directed assembly tests are reused unchanged – baked into boot RAM and reporting P/F over the real serial line
  • Board-only bring-up checks (heartbeat LED, reset/serial mirrors) that cannot be expressed in simulation at all
  • Entropy-source capture from genuine silicon jitter/metastability – data whose value is real physical noise rather than a pass/fail verdict

Current status: full-hierarchy elaboration through read_slang succeeds with zero errors (one synthesis-compatibility guard added to the RAM init path); a complete synth_ecp5 + place-and-route run is still being finalized – yosys reports logic-loop warnings localized to the FPU’s variable-shift idioms, working hypothesis a false positive pending nextpnr’s independent timing graph. No physical board has been attached yet: on-hardware execution remains a reproducible procedure for anyone with a ULX3S, not an observed result.

Waveform Inspection and CI Integration

  • Any test can produce FST waveforms viewable in Surfer (scripts/gen_waves.sh, per-module make wave-* targets) for signal-level debugging.
  • Per-feature CI runs the unit tests, single-core and multi-core directed sweeps, the enable/disable smoke matrix, the SBI firmware smoke tests, and a small Linux boot run; at the end of a development cycle the full permutation matrix, RISCOF suites, the differential fuzz campaign, and a full Linux boot are added.
Sep 20, 2026

About This Site

Code Signing Certificate

All source code distributed on this web site is signed. In order to verify the signature and thus the integrity and authenticity of the obtained code, use the following command:

​
1
gpg --verify leancrypto-*.tar.xz.asc leancrypto-*.tar.xz

This command only performs the verification if the associated public key was previously imported into the key ring. In case the public key needs to be imported, use the following command which imports the key from 2024:

​
1
curl https://leancrypto.org/about/smuellerDD-2024.asc | gpg --import

The following public keys are available: