Skip to content

zkr: always allow S-mode seed access regardless of mseccfg.SSEED#5

Merged
SolAstrius merged 1 commit into
stagingfrom
fix/zkr-seed-always-allow-smode
Apr 21, 2026
Merged

zkr: always allow S-mode seed access regardless of mseccfg.SSEED#5
SolAstrius merged 1 commit into
stagingfrom
fix/zkr-seed-always-allow-smode

Conversation

@SolAstrius
Copy link
Copy Markdown
Collaborator

Summary

Remove the `mseccfg.SSEED` gate on S-mode access to the `seed` CSR. U-mode still respects `mseccfg.USEED` (that check actually matters for kernels forbidding unprivileged polling). M and S mode get unconditional access.

Why

Follow-up to #4. After that landed, Linux 6.18.22 still oopsed at `arch_get_random_seed_longs+0x14` with "Illegal instruction" during `start_kernel` / `random_init_early`. Trace:

```
badaddr: 00000000015056f3 cause: 0000000000000002
// 015056f3 = csrrwi x13, seed, 0
epc : arch_get_random_seed_longs+0x14
ra : random_init_early+0x2e
Kernel panic - not syncing: Fatal exception in interrupt
```

Root cause: we set `mseccfg.USEED | mseccfg.SSEED` in `rvvm_create_machine`'s hart init (rvvm.c:1223), but OpenSBI 1.6 clobbers `mseccfg` during hart bring-up — its PMP-enhancement path writes 0 to the CSR while configuring MML/MMWP. By the time Linux runs `csrrwi seed, x0` in S-mode, SSEED is back to 0, `riscv_csr_seed_enabled` returns false, and the emulator traps the access.

A virtual entropy source (Zkr 1.0.1 §4.4.3) doesn't need M-mode firmware cooperation to authorize S-mode access — it is the firmware. This patch lets M and S mode through unconditionally, keeping only the U-mode `mseccfg.USEED` check (which works fine because OpenSBI doesn't touch USEED on its PMP path the same way).

Verified

  • Before: guest kernel oops + panic at random_init_early
  • After: guest boots through random_init_early and on into userspace

Test plan

  • `make lib USE_JNI=1` compiles
  • Boot Linux 6.18+ guest, reach userspace login prompt

Virtual entropy sources per Section 4.4.3 don't need M-mode firmware
cooperation to grant S-mode the right to poll sentropy — they ARE the
firmware. Gating S-mode access on mseccfg.SSEED in an emulator is
theatre at best and a live-fire footgun at worst:

  * We set mseccfg.USEED | mseccfg.SSEED in rvvm_create_machine's hart
    init (circa rvvm.c:1223).

  * OpenSBI 1.6 clobbers mseccfg during hart bring-up — its PMP
    enhancement path writes 0 to the CSR while configuring MML/MMWP,
    zeroing our USEED/SSEED bits as collateral.

  * By the time Linux 6.18+ runs csrrwi x13, seed, 0 at
    start_kernel+0x68a (random_init_early), SSEED is 0, our
    riscv_csr_seed_enabled returns false for PRIV_SUPERVISOR, and the
    emulator traps the access as illegal instruction. Kernel panics
    before the console is even initialized.

Fix: let M and S mode through unconditionally. U-mode still gates on
mseccfg.USEED — that one actually matters (kernels use it to forbid
unprivileged entropy polling), and OpenSBI doesn't interfere with it
post-boot because it's not on the PMP-setup path in the same way.

This keeps the ABI spec-compliant from the kernel's point of view (a
kernel that writes its own SSEED=1 still sees the same behavior) while
removing the OpenSBI ordering hazard.

Empirically verified: before this patch, a Linux 6.18.22 guest oopses
at arch_get_random_seed_longs+0x14 with "Illegal instruction"
(badaddr=015056f3 = csrrwi x13, seed, 0) during start_kernel. After,
it boots through random_init_early and on into userspace.
@SolAstrius SolAstrius merged commit 7aa8e89 into staging Apr 21, 2026
@SolAstrius SolAstrius deleted the fix/zkr-seed-always-allow-smode branch April 26, 2026 21:25
SolAstrius added a commit that referenced this pull request Apr 27, 2026
Four small, related fixes that retire every "real bug in handled code"
left from the original audit.

#5  CORB consumer — sound_hda_corb_wp_write previously fetched only
    the entry at the new WP and never advanced corb_rp. Linux happens
    to bump WP after every entry so it worked, but a guest that
    batches multiple commands before bumping WP (spec §4.4.1.4 says
    you can) silently dropped all but the last. Now walks RP+1..WP
    inclusive, dispatching each verb, advancing RP to match — the
    spec-conformant CORB consumer model.

#6  SET_PIN_WIDGET_CTRL on NID 3 — the verb's macro existed but no
    case dispatched it, and GET hardcoded a constant OUT_ENABLE. So
    Linux's pin power-up sequence ("write OUT_ENABLE, read back to
    confirm") happened to round-trip correctly only by accident. Now
    stores a real per-pin control byte (hda->pin_ctrl, single pin
    today; multi-pin codecs would key by NID), and GET returns it.
    Initialised to OUT_ENABLE so probe-time GET behaviour is unchanged
    for guests that don't write the register first.

#12 RUN/SRST ordering — spec §3.3.35 requires "the RUN bit must be
    cleared before SRST is asserted." We previously accepted both bits
    set in the same write without enforcement — the worker would keep
    running with zeroed BDL pointers, spinning against a freshly-reset
    stream until the guest re-asserted RUN. Now on the SRST 0→1 edge,
    force-stop the worker (running=0) and override the same write's
    RUN bit, matching real HW behaviour where SRST clears the engine
    as part of its "stream registers and FIFO are reset" effect.

#14 spinlock-across-pci_send_irq — was flagged as a smell ("could
    self-deadlock if IRQ delivery is synchronous and re-enters MMIO").
    Verified RVVM's pci_send_irq → rvvm_send_irq just queues into the
    interrupt controller; the vCPU is a separate thread that picks it
    up later and never re-enters our MMIO synchronously from the
    calling thread. Documented the safety reasoning at the call site
    so future readers don't have to re-investigate. Not a bug — just
    under-documented.
SolAstrius added a commit that referenced this pull request May 1, 2026
Virtual entropy sources per Section 4.4.3 don't need M-mode firmware
cooperation to grant S-mode the right to poll sentropy — they ARE the
firmware. Gating S-mode access on mseccfg.SSEED in an emulator is
theatre at best and a live-fire footgun at worst:

  * We set mseccfg.USEED | mseccfg.SSEED in rvvm_create_machine's hart
    init (circa rvvm.c:1223).

  * OpenSBI 1.6 clobbers mseccfg during hart bring-up — its PMP
    enhancement path writes 0 to the CSR while configuring MML/MMWP,
    zeroing our USEED/SSEED bits as collateral.

  * By the time Linux 6.18+ runs csrrwi x13, seed, 0 at
    start_kernel+0x68a (random_init_early), SSEED is 0, our
    riscv_csr_seed_enabled returns false for PRIV_SUPERVISOR, and the
    emulator traps the access as illegal instruction. Kernel panics
    before the console is even initialized.

Fix: let M and S mode through unconditionally. U-mode still gates on
mseccfg.USEED — that one actually matters (kernels use it to forbid
unprivileged entropy polling), and OpenSBI doesn't interfere with it
post-boot because it's not on the PMP-setup path in the same way.

This keeps the ABI spec-compliant from the kernel's point of view (a
kernel that writes its own SSEED=1 still sees the same behavior) while
removing the OpenSBI ordering hazard.

Empirically verified: before this patch, a Linux 6.18.22 guest oopses
at arch_get_random_seed_longs+0x14 with "Illegal instruction"
(badaddr=015056f3 = csrrwi x13, seed, 0) during start_kernel. After,
it boots through random_init_early and on into userspace.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant