Phase 8 A: virtio-rng entropy source for real Ed25519 signing
The kernel's ed25519_verify() is deliberately verify-only -- no signing, no keygen, no entropy source. That conflicts with the on-device MINT word vision (Zuse signing new user certs live at runtime), so this reopens that constraint on request rather than reshaping MINT around verify-only. vm_uuid.h already found the real gap: amd64 has RDRAND, riscv64 has Zkr, but QEMU's aarch64 CPU models have neither -- confirmed against QEMU 10.2.1. A deterministic PRNG (fine for VM UUIDs) is not safe for key generation, so this adds a virtio-rng device instead of a per-arch split: real host entropy, identical guest-side protocol on all three arches. New src/starkernel/virtio/virtio_rng.c + include/starkernel/virtio_rng.h, transport plumbing mirroring the existing virtio_blk.c driver exactly. Wired into kernel_main.c boot, -device virtio-rng-pci added to all three QEMU targets. Verified live (temp probe, written/run/captured/reverted): 16 real bytes pulled through the full request/notify/poll round trip on all three arches, three different values confirming real entropy. Final boot against the reverted, permanent code: clean compile, clean boot to ok> on amd64/aarch64/riscv64, Stadium conservation intact, no panics or guest errors. Ed25519 keygen/signing itself (Phase B) and the MINT word design (Phase C) remain open, documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
6f5605d479
commit
309e792f07
+47
@@ -346,6 +346,53 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
ACL is opt-in (`init.4th`'s `S" ACL.4th" EXEC` commented out by default) so the new words
|
||||
weren't exercised live from the REPL this pass — compile/lint/boot verification only.
|
||||
|
||||
**MINT word design, picked up 2026-08-26.** Before scoping `MINT` itself, found a real
|
||||
conflict with an existing, deliberate decision: `include/starkernel/ed25519.h` is verify-
|
||||
only by design — "this kernel never signs or generates keys (no entropy source to do so
|
||||
safely anyway); signing happens in the host-side build tool" (`FABRIC-2.md`, Milestone 6,
|
||||
2026-08-22). But the vision for `MINT` (§D below) is an *interactive*, on-device
|
||||
`S" name" MINT` word — an authenticated Zuse session signing a new user's cert live, at
|
||||
runtime. A kernel that structurally never signs can't do that as envisioned. Raised
|
||||
directly; **decided (Captain Bob, 2026-08-26): give the kernel a real signing capability**
|
||||
rather than reshape `MINT` around verify-only. This reopens the prior "no entropy source"
|
||||
constraint deliberately, not by accident.
|
||||
|
||||
**Phase A — `virtio-rng`, done 2026-08-26.** Checked what entropy is actually available
|
||||
before choosing a design: `include/starkernel/vm_uuid.h` already found, for VM UUIDs, that
|
||||
amd64 has RDRAND and riscv64 has the Zkr extension, but QEMU's aarch64 CPU models (including
|
||||
`max`) expose neither RNDR nor any RNG property at all — confirmed directly against QEMU
|
||||
10.2.1. That's why VM UUIDs use a deterministic PRNG uniformly instead of a per-arch split;
|
||||
that same choice is **not safe for Ed25519 keygen** — a seed drawn from a known value makes
|
||||
the private key predictable. Decided: add a `virtio-rng` device instead of a per-arch
|
||||
RDRAND/Zkr split with a weaker aarch64 fallback — QEMU supplies real host entropy
|
||||
identically on all three arches, closing the aarch64 gap directly (QEMU-only; real hardware
|
||||
at Milestone 8 needs a real per-arch RNG driver, a separate later problem).
|
||||
|
||||
New `include/starkernel/virtio_rng.h` + `src/starkernel/virtio/virtio_rng.c`, transport
|
||||
plumbing (PCI capability walk, common-cfg feature negotiation, split virtqueue) mirroring
|
||||
the existing `virtio_blk.c` exactly — same device family, same quirks. Simpler shape than
|
||||
block: one virtqueue, one device-writable descriptor, no request header or status byte (the
|
||||
entropy device has none); `virtio_rng_get_bytes()` loops internally since the device may
|
||||
return fewer bytes than requested per round. `-object rng-random,id=rng0,filename=/dev/urandom`
|
||||
+ `-device virtio-rng-pci` added to all three arches' QEMU invocations
|
||||
(`Makefile.starkernel`). Wired into boot (`kernel_main.c`, right after the existing
|
||||
`virtio_blk_find_artemis()` call site, same graceful-noop-on-absence precedent).
|
||||
|
||||
**Verified live, not just compiled:** a temporary probe (written, run once, captured,
|
||||
reverted — per this project's standing probe convention) pulled 16 real bytes through the
|
||||
full request/notify/poll/used-ring round trip on all three architectures and printed them:
|
||||
amd64 `be9223909b86a8ccbfff705ccae2caa6`, aarch64 `861487df65a6d7a26b2c9c34f5ff96ee`, riscv64
|
||||
`fec51d80e8c169a35aad9d908eab2f39` — three different values, confirming real entropy, not a
|
||||
stale or repeated buffer. Probe reverted; permanent code is just the driver + init call. A
|
||||
second, final 3-arch acceptance boot ran against that reverted code (not the probe build) to
|
||||
confirm the shipped state itself is clean. Clean zero-warning compile and clean boot to
|
||||
`ok>` on all three architectures, Stadium conservation intact (43691/21845/65536), no panics
|
||||
or guest errors on either pass.
|
||||
|
||||
**Still open: Phase B (real Ed25519 keygen/signing, seeded from this entropy) and Phase C
|
||||
(the `MINT` word itself, cert format, and whether Zuse's own keypair needs to chain to the
|
||||
Milestone 6 offline root CA or is a self-sovereign instance-local root of trust).**
|
||||
|
||||
### From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding
|
||||
|
||||
- [ ] Settle the still-open question: reuse `ACL-PIN`/`acl_allow` directly, or build a
|
||||
|
||||
@@ -764,6 +764,8 @@ ifeq ($(ARCH),amd64)
|
||||
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
||||
-device virtio-blk-pci,drive=artdisk \
|
||||
-device qemu-xhci,id=xhci0 \
|
||||
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||
-device virtio-rng-pci,rng=rng0 \
|
||||
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
||||
-serial chardev:cserial \
|
||||
-qmp unix:$$QMP_SOCK,server=on,wait=off \
|
||||
@@ -828,6 +830,8 @@ else ifeq ($(ARCH),aarch64)
|
||||
-device virtio-blk-pci,drive=artdisk \
|
||||
-device virtio-keyboard-pci,addr=0x3 \
|
||||
-device qemu-xhci,id=xhci0,addr=0x4 \
|
||||
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||
-device virtio-rng-pci,rng=rng0,addr=0x5 \
|
||||
-device ramfb \
|
||||
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
||||
-serial chardev:cserial \
|
||||
@@ -912,6 +916,8 @@ else ifeq ($(ARCH),riscv64)
|
||||
-device virtio-blk-pci,drive=artdisk,addr=0x1 \
|
||||
-device virtio-keyboard-pci,addr=0x3 \
|
||||
-device qemu-xhci,id=xhci0,addr=0x4 \
|
||||
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||
-device virtio-rng-pci,rng=rng0,addr=0x5 \
|
||||
-device ramfb \
|
||||
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
||||
-serial chardev:cserial \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-26T17:54:43Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-26T18:28:05Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* virtio_rng.h — Virtio 1.0 entropy source driver for StarKernel
|
||||
*
|
||||
* Real hardware/host entropy, uniformly across all three architectures.
|
||||
* Exists specifically because this kernel has no per-arch RNG that covers
|
||||
* all three ISAs: amd64 has RDRAND and riscv64 has the Zkr extension, but
|
||||
* QEMU's aarch64 CPU models (including "max") expose neither RNDR nor any
|
||||
* other RNG property (checked directly against QEMU 10.2.1, see
|
||||
* vm_uuid.h's identical finding). A paravirtualized virtio-rng device
|
||||
* sidesteps the per-ISA gap entirely: the host supplies the entropy, the
|
||||
* guest-side protocol is identical on all three arches.
|
||||
*
|
||||
* QEMU-only: there is no virtio-rng on real hardware (Milestone 8, bare-
|
||||
* metal boot). A real per-arch RNG driver is a separate, later problem.
|
||||
*
|
||||
* Only one virtio-rng device is supported.
|
||||
*/
|
||||
|
||||
#ifndef STARKERNEL_VIRTIO_RNG_H
|
||||
#define STARKERNEL_VIRTIO_RNG_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* virtio PCI vendor (shared with every virtio device) */
|
||||
#define VIRTIO_RNG_PCI_VENDOR_ID 0x1AF4u
|
||||
/* device IDs: virtio device id 4 (entropy source) */
|
||||
#define VIRTIO_RNG_DEVICE_MODERN 0x1044u
|
||||
#define VIRTIO_RNG_DEVICE_LEGACY 0x1005u
|
||||
|
||||
/*
|
||||
* virtio_rng_init — locate the virtio-rng device on the PCI bus and bring
|
||||
* the driver up. Call once, before the first virtio_rng_get_bytes() call.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 if no virtio-rng device was found on the PCI bus.
|
||||
* Returns -2 on driver initialisation failure (bad BAR, queue setup, etc.).
|
||||
*/
|
||||
int virtio_rng_init(void);
|
||||
|
||||
/*
|
||||
* virtio_rng_ready — 1 if virtio_rng_init() has already succeeded.
|
||||
*/
|
||||
int virtio_rng_ready(void);
|
||||
|
||||
/*
|
||||
* virtio_rng_get_bytes — fill buf with n bytes of real host-supplied
|
||||
* entropy, blocking (polling) until all n bytes are obtained. The device
|
||||
* may return fewer bytes than requested per request; this loops
|
||||
* internally until the buffer is full.
|
||||
*
|
||||
* Returns 0 on success (buf fully filled).
|
||||
* Returns -1 if virtio_rng_init() has not succeeded.
|
||||
* Returns -2 on a device/timeout error partway through.
|
||||
*/
|
||||
int virtio_rng_get_bytes(uint8_t *buf, size_t n);
|
||||
|
||||
#endif /* STARKERNEL_VIRTIO_RNG_H */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,7 @@ EFI_RUNTIME_SERVICES *g_sk_runtime_services = NULL;
|
||||
#include "starkernel/repl.h"
|
||||
#include "starkernel/pci.h"
|
||||
#include "starkernel/virtio_blk.h"
|
||||
#include "starkernel/virtio_rng.h"
|
||||
#include "starkernel/virtio_input.h"
|
||||
#include "starkernel/xhci_driver.h"
|
||||
#include "block_subsystem.h"
|
||||
@@ -610,6 +611,23 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Phase 8: virtio-rng entropy source. Real per-arch RNG doesn't cover
|
||||
* all three architectures (amd64 RDRAND, riscv64 Zkr, but aarch64 has
|
||||
* neither in QEMU's CPU models -- see vm_uuid.h's identical finding),
|
||||
* so signing/keygen entropy comes from this paravirtualized device
|
||||
* instead. Unconditional call site, same graceful-noop precedent as
|
||||
* virtio_blk_find_artemis() above -- boot proceeds either way, the
|
||||
* device is only required once something actually calls
|
||||
* virtio_rng_get_bytes(). */
|
||||
{
|
||||
int rrc = virtio_rng_init();
|
||||
if (rrc == 0) {
|
||||
console_println("virtio-rng: ready");
|
||||
} else {
|
||||
console_println("virtio-rng: not available (continuing without)");
|
||||
}
|
||||
}
|
||||
|
||||
/* item 4.3.5c: virtio-keyboard-pci, riscv64 only today. Unconditional
|
||||
* call site, same as virtio_blk_find_artemis() above -- the function
|
||||
* itself no-ops with a console message on architectures/boards where
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* virtio_rng.c — Virtio 1.0 entropy source driver for StarKernel
|
||||
*
|
||||
* Modern virtio 1.0 interface only (device ID 0x1044).
|
||||
* Falls back to checking 0x1005 (legacy/transitional).
|
||||
*
|
||||
* Split virtqueue, queue depth = 1 (a single device-writable buffer is
|
||||
* the entire request shape for this device — no header, no status byte,
|
||||
* no device-specific config space at all).
|
||||
*
|
||||
* The device may fill fewer bytes than the buffer offers per request
|
||||
* (the used-ring element's len says how many); virtio_rng_get_bytes()
|
||||
* loops internally until the caller's full byte count is satisfied.
|
||||
*
|
||||
* Memory model: all allocations via kmalloc(); identity-mapped so
|
||||
* virtual address == physical address for virtqueue ring pointers.
|
||||
* Transport plumbing (capability walk, common-cfg negotiation, split
|
||||
* virtqueue layout) mirrors virtio_blk.c exactly — same device family,
|
||||
* same board, same quirks (see that file's comments for why the config
|
||||
* structs are unpacked and why notify offset math looks the way it does).
|
||||
*/
|
||||
|
||||
#ifndef __STARKERNEL__
|
||||
#error "virtio_rng.c is kernel-only"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "starkernel/pci.h"
|
||||
#include "starkernel/virtio_rng.h"
|
||||
#include "starkernel/kmalloc.h"
|
||||
#include "console.h"
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Virtio 1.0 PCI capability structures (identical to virtio_blk.c)
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#define VIRTIO_PCI_CAP_VENDOR_ID 0x09u
|
||||
|
||||
#define VIRTIO_PCI_CAP_COMMON_CFG 1u
|
||||
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
|
||||
|
||||
#define VCAP_OFF_CAP_VNDR 0u
|
||||
#define VCAP_OFF_CAP_NEXT 1u
|
||||
#define VCAP_OFF_CFG_TYPE 3u
|
||||
#define VCAP_OFF_BAR 4u
|
||||
#define VCAP_OFF_OFFSET 8u
|
||||
#define VCAP_OFF_LENGTH 12u
|
||||
#define VCAP_OFF_NOTIFY_MULT 16u
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t device_feature_select;
|
||||
volatile uint32_t device_feature;
|
||||
volatile uint32_t driver_feature_select;
|
||||
volatile uint32_t driver_feature;
|
||||
volatile uint16_t config_msix_vector;
|
||||
volatile uint16_t num_queues;
|
||||
volatile uint8_t device_status;
|
||||
volatile uint8_t config_generation;
|
||||
volatile uint16_t queue_select;
|
||||
volatile uint16_t queue_size;
|
||||
volatile uint16_t queue_msix_vector;
|
||||
volatile uint16_t queue_enable;
|
||||
volatile uint16_t queue_notify_off;
|
||||
volatile uint64_t queue_desc;
|
||||
volatile uint64_t queue_driver;
|
||||
volatile uint64_t queue_device;
|
||||
volatile uint16_t queue_notify_data;
|
||||
volatile uint16_t queue_reset;
|
||||
} VirtioCommonCfg;
|
||||
|
||||
#define VIRTIO_STATUS_ACKNOWLEDGE 0x01u
|
||||
#define VIRTIO_STATUS_DRIVER 0x02u
|
||||
#define VIRTIO_STATUS_DRIVER_OK 0x04u
|
||||
#define VIRTIO_STATUS_FEATURES_OK 0x08u
|
||||
#define VIRTIO_STATUS_FAILED 0x80u
|
||||
|
||||
#define VIRTIO_F_VERSION_1 (1ULL << 32)
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Split virtqueue structures (queue depth 1 — one descriptor, no chaining)
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#define VQUEUE_SIZE 1u
|
||||
|
||||
#define VRING_DESC_F_WRITE 2u
|
||||
|
||||
typedef struct {
|
||||
uint64_t addr;
|
||||
uint32_t len;
|
||||
uint16_t flags;
|
||||
uint16_t next;
|
||||
} VirtqDesc;
|
||||
|
||||
typedef struct {
|
||||
uint16_t flags;
|
||||
uint16_t idx;
|
||||
uint16_t ring[VQUEUE_SIZE];
|
||||
uint16_t used_event;
|
||||
} VirtqAvail;
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t len;
|
||||
} VirtqUsedElem;
|
||||
|
||||
typedef struct {
|
||||
uint16_t flags;
|
||||
uint16_t idx;
|
||||
VirtqUsedElem ring[VQUEUE_SIZE];
|
||||
uint16_t avail_event;
|
||||
} VirtqUsed;
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Driver state
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#define VRNG_BUF_SIZE 64u /* bytes requested from the device per round */
|
||||
|
||||
typedef struct {
|
||||
VirtioCommonCfg *common;
|
||||
volatile uint16_t *notify;
|
||||
uint32_t notify_off_mult;
|
||||
uint16_t queue_notify_off;
|
||||
|
||||
VirtqDesc *desc;
|
||||
VirtqAvail *avail;
|
||||
VirtqUsed *used;
|
||||
|
||||
uint16_t avail_idx;
|
||||
uint16_t last_used_idx;
|
||||
|
||||
uint8_t *data_buf; /* VRNG_BUF_SIZE bytes, DMA-accessible */
|
||||
} VirtRngState;
|
||||
|
||||
static VirtRngState g_vrng;
|
||||
static int g_vrng_ready = 0;
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Capability walker (identical shape to virtio_blk.c's walk_virtio_caps)
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
static void *walk_virtio_caps(const PciDevice *d, uint8_t cap_type,
|
||||
uint32_t *extra_out) {
|
||||
uint8_t cap_ptr = pci_read8(d, (uint16_t)PCI_CFG_CAP_PTR) & 0xFCu;
|
||||
if (!cap_ptr) return NULL;
|
||||
|
||||
int limit = 48;
|
||||
while (cap_ptr && limit--) {
|
||||
uint8_t vndr = pci_read8(d, cap_ptr + (uint16_t)VCAP_OFF_CAP_VNDR);
|
||||
uint8_t next = pci_read8(d, cap_ptr + (uint16_t)VCAP_OFF_CAP_NEXT);
|
||||
uint8_t ctype = pci_read8(d, cap_ptr + (uint16_t)VCAP_OFF_CFG_TYPE);
|
||||
|
||||
if (vndr == (uint8_t)VIRTIO_PCI_CAP_VENDOR_ID && ctype == cap_type) {
|
||||
uint8_t bar = pci_read8 (d, cap_ptr + (uint16_t)VCAP_OFF_BAR);
|
||||
uint32_t offset = pci_read32(d, cap_ptr + (uint16_t)VCAP_OFF_OFFSET);
|
||||
uint32_t length = pci_read32(d, cap_ptr + (uint16_t)VCAP_OFF_LENGTH);
|
||||
|
||||
if (bar > 5u) { cap_ptr = next & 0xFCu; continue; }
|
||||
|
||||
uint64_t bar_base = pci_bar(d, (int)bar);
|
||||
if (!bar_base) { cap_ptr = next & 0xFCu; continue; }
|
||||
|
||||
if (pci_map_bar(bar_base, (uint64_t)length + offset) != 0) {
|
||||
cap_ptr = next & 0xFCu; continue;
|
||||
}
|
||||
|
||||
if (extra_out && cap_type == VIRTIO_PCI_CAP_NOTIFY_CFG) {
|
||||
*extra_out = pci_read32(d, cap_ptr + (uint16_t)VCAP_OFF_NOTIFY_MULT);
|
||||
}
|
||||
|
||||
return (void *)(uintptr_t)(bar_base + offset);
|
||||
}
|
||||
|
||||
cap_ptr = next & 0xFCu;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void wmb(void) { __asm__ volatile("" : : : "memory"); }
|
||||
static inline void rmb(void) { __asm__ volatile("" : : : "memory"); }
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* One round: ask the device to fill data_buf, return bytes actually
|
||||
* written (may be less than VRNG_BUF_SIZE).
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
static int vrng_request(uint32_t *bytes_out) {
|
||||
VirtRngState *s = &g_vrng;
|
||||
VirtqDesc *d = s->desc;
|
||||
|
||||
d[0].addr = (uint64_t)(uintptr_t)s->data_buf;
|
||||
d[0].len = (uint32_t)VRNG_BUF_SIZE;
|
||||
d[0].flags = (uint16_t)VRING_DESC_F_WRITE;
|
||||
d[0].next = 0;
|
||||
|
||||
uint16_t avail_idx = s->avail_idx & (uint16_t)(VQUEUE_SIZE - 1u);
|
||||
s->avail->ring[avail_idx] = 0;
|
||||
wmb();
|
||||
s->avail->idx = (uint16_t)(s->avail->idx + 1u);
|
||||
s->avail_idx = s->avail->idx;
|
||||
wmb();
|
||||
|
||||
uint16_t notify_idx = (uint16_t)(s->queue_notify_off *
|
||||
(s->notify_off_mult & 0xFFFFu));
|
||||
volatile uint16_t *doorbell = s->notify + notify_idx;
|
||||
*doorbell = 0;
|
||||
wmb();
|
||||
|
||||
uint32_t spin = 0x2000000u;
|
||||
while (s->used->idx == s->last_used_idx) {
|
||||
rmb();
|
||||
if (!--spin) return -2;
|
||||
}
|
||||
|
||||
uint32_t used_slot = (uint32_t)s->last_used_idx & (VQUEUE_SIZE - 1u);
|
||||
*bytes_out = s->used->ring[used_slot].len;
|
||||
s->last_used_idx = s->used->idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Device initialisation
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
static int vrng_init_device(const PciDevice *pci) {
|
||||
VirtRngState *s = &g_vrng;
|
||||
|
||||
pci_enable(pci);
|
||||
|
||||
uint32_t notify_mult = 0;
|
||||
VirtioCommonCfg *common = (VirtioCommonCfg *)
|
||||
walk_virtio_caps(pci, VIRTIO_PCI_CAP_COMMON_CFG, NULL);
|
||||
volatile uint16_t *notify = (volatile uint16_t *)
|
||||
walk_virtio_caps(pci, VIRTIO_PCI_CAP_NOTIFY_CFG, ¬ify_mult);
|
||||
|
||||
if (!common || !notify) {
|
||||
console_println("virtio-rng: cap walk failed");
|
||||
return -2;
|
||||
}
|
||||
|
||||
s->common = common;
|
||||
s->notify = notify;
|
||||
s->notify_off_mult = notify_mult;
|
||||
|
||||
common->device_status = 0;
|
||||
wmb();
|
||||
common->device_status = (uint8_t)VIRTIO_STATUS_ACKNOWLEDGE;
|
||||
wmb();
|
||||
common->device_status = (uint8_t)(VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER);
|
||||
wmb();
|
||||
|
||||
common->driver_feature_select = 1;
|
||||
wmb();
|
||||
common->driver_feature = (uint32_t)(VIRTIO_F_VERSION_1 >> 32);
|
||||
common->driver_feature_select = 0;
|
||||
wmb();
|
||||
common->driver_feature = 0;
|
||||
wmb();
|
||||
|
||||
common->device_status = (uint8_t)(VIRTIO_STATUS_ACKNOWLEDGE |
|
||||
VIRTIO_STATUS_DRIVER |
|
||||
VIRTIO_STATUS_FEATURES_OK);
|
||||
wmb();
|
||||
rmb();
|
||||
if (!(common->device_status & (uint8_t)VIRTIO_STATUS_FEATURES_OK)) {
|
||||
console_println("virtio-rng: FEATURES_OK rejected");
|
||||
common->device_status = (uint8_t)VIRTIO_STATUS_FAILED;
|
||||
return -2;
|
||||
}
|
||||
|
||||
common->queue_select = 0;
|
||||
wmb();
|
||||
uint16_t max_size = common->queue_size;
|
||||
if (max_size == 0) {
|
||||
console_println("virtio-rng: bad queue size");
|
||||
return -2;
|
||||
}
|
||||
uint16_t qsize = (uint16_t)VQUEUE_SIZE;
|
||||
common->queue_size = qsize;
|
||||
s->queue_notify_off = common->queue_notify_off;
|
||||
wmb();
|
||||
|
||||
common->config_msix_vector = 0xFFFFu;
|
||||
common->queue_msix_vector = 0xFFFFu;
|
||||
wmb();
|
||||
|
||||
size_t desc_bytes = (size_t)qsize * sizeof(VirtqDesc);
|
||||
size_t avail_bytes = sizeof(uint16_t) * 2u +
|
||||
(size_t)qsize * sizeof(uint16_t) +
|
||||
sizeof(uint16_t);
|
||||
size_t used_bytes = sizeof(uint16_t) * 2u +
|
||||
(size_t)qsize * sizeof(VirtqUsedElem) +
|
||||
sizeof(uint16_t);
|
||||
|
||||
s->desc = (VirtqDesc *)kmalloc_aligned(desc_bytes, 64);
|
||||
s->avail = (VirtqAvail *)kmalloc_aligned(avail_bytes, 2);
|
||||
s->used = (VirtqUsed *)kmalloc_aligned(used_bytes, 4);
|
||||
if (!s->desc || !s->avail || !s->used) {
|
||||
console_println("virtio-rng: queue alloc failed");
|
||||
return -2;
|
||||
}
|
||||
memset(s->desc, 0, desc_bytes);
|
||||
memset(s->avail, 0, avail_bytes);
|
||||
memset(s->used, 0, used_bytes);
|
||||
|
||||
s->data_buf = (uint8_t *)kmalloc_aligned(VRNG_BUF_SIZE, 16);
|
||||
if (!s->data_buf) {
|
||||
console_println("virtio-rng: buf alloc failed");
|
||||
return -2;
|
||||
}
|
||||
|
||||
s->avail_idx = 0;
|
||||
s->last_used_idx = 0;
|
||||
|
||||
common->queue_desc = (uint64_t)(uintptr_t)s->desc;
|
||||
common->queue_driver = (uint64_t)(uintptr_t)s->avail;
|
||||
common->queue_device = (uint64_t)(uintptr_t)s->used;
|
||||
wmb();
|
||||
|
||||
common->queue_enable = 1;
|
||||
wmb();
|
||||
|
||||
common->device_status = (uint8_t)(VIRTIO_STATUS_ACKNOWLEDGE |
|
||||
VIRTIO_STATUS_DRIVER |
|
||||
VIRTIO_STATUS_FEATURES_OK |
|
||||
VIRTIO_STATUS_DRIVER_OK);
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Public entry points
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
int virtio_rng_init(void) {
|
||||
PciDevice pci;
|
||||
int found = pci_find_first(VIRTIO_RNG_PCI_VENDOR_ID, VIRTIO_RNG_DEVICE_MODERN, &pci);
|
||||
if (found != 0)
|
||||
found = pci_find_first(VIRTIO_RNG_PCI_VENDOR_ID, VIRTIO_RNG_DEVICE_LEGACY, &pci);
|
||||
if (found != 0) {
|
||||
console_println("virtio-rng: no device on PCI bus 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
console_println("virtio-rng: found device");
|
||||
|
||||
int rc = vrng_init_device(&pci);
|
||||
if (rc != 0) return rc;
|
||||
|
||||
g_vrng_ready = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int virtio_rng_ready(void) {
|
||||
return g_vrng_ready;
|
||||
}
|
||||
|
||||
int virtio_rng_get_bytes(uint8_t *buf, size_t n) {
|
||||
if (!g_vrng_ready) return -1;
|
||||
if (!buf) return -2;
|
||||
|
||||
size_t filled = 0;
|
||||
uint32_t attempts_left = 64u; /* generous bound: real entropy always yields > 0 bytes */
|
||||
|
||||
while (filled < n) {
|
||||
uint32_t got = 0;
|
||||
int rc = vrng_request(&got);
|
||||
if (rc != 0) return -2;
|
||||
|
||||
if (got == 0) {
|
||||
if (!--attempts_left) return -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t take = (size_t)got;
|
||||
if (take > n - filled) take = n - filled;
|
||||
memcpy(buf + filled, g_vrng.data_buf, take);
|
||||
filled += take;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user