refactor: extract shared hw-virtio crate, eliminate VirtIO protocol duplication

Two VirtIO block and two VirtIO GPU implementations (adapter + driver)
shared 90% of their protocol logic. Extracted the common code into
hw-virtio, parameterized by MmioAccess trait.

New crate: hw-virtio (no_std, zero deps)
  - mmio.rs: MmioAccess trait, DirectMmio, init_legacy_device(), scan
  - queue.rs: VirtioQueue with descriptor chain submit + poll
  - block.rs: block read protocol (3-descriptor chain)
  - gpu.rs: GPU commands (create_2d, attach_backing, set_scanout,
            transfer_to_host, resource_flush)

Adapter thin wrappers (identity-mapped, statics for DMA):
  - virtio_blk.rs: 220 → 53 lines
  - virtio_gpu.rs: 165 → 37 lines

Driver thin wrappers (mapped VAs, phys-info for DMA):
  - drivers/block/virtio: 139 → 70 lines
  - drivers/framebuffer/virtio: 146 → 95 lines

Also deleted input-server/input_device.rs (1-line re-export,
superseded by port-input-device).
This commit is contained in:
2026-10-18 13:29:28 +02:00
parent 8d53fa9ac5
commit 3a0395453b
21 changed files with 483 additions and 562 deletions

View File

@@ -0,0 +1,96 @@
pub trait MmioAccess {
fn read(&self, offset: usize) -> u32;
fn write(&self, offset: usize, val: u32);
}
pub const MAGIC: u32 = 0x74726976;
pub const REG_MAGIC: usize = 0x000;
pub const REG_VERSION: usize = 0x004;
pub const REG_DEVICE_ID: usize = 0x008;
pub const REG_STATUS: usize = 0x070;
pub const REG_GUEST_PAGE_SIZE: usize = 0x028;
pub const REG_FEATURES_SEL: usize = 0x014;
pub const REG_FEATURES: usize = 0x010;
pub const REG_DRIVER_FEATURES_SEL: usize = 0x024;
pub const REG_DRIVER_FEATURES: usize = 0x020;
pub const REG_QUEUE_SEL: usize = 0x030;
pub const REG_QUEUE_NUM_MAX: usize = 0x034;
pub const REG_QUEUE_NUM: usize = 0x038;
pub const REG_QUEUE_PFN: usize = 0x040;
pub const REG_QUEUE_NOTIFY: usize = 0x050;
pub const STATUS_ACK: u32 = 1;
pub const STATUS_DRIVER: u32 = 2;
pub const STATUS_DRIVER_OK: u32 = 4;
pub const DEVICE_ID_BLOCK: u32 = 2;
pub const DEVICE_ID_GPU: u32 = 16;
pub const PAGE_SIZE: usize = 4096;
pub struct DirectMmio {
base: usize,
}
impl DirectMmio {
pub fn new(base: usize) -> Self {
Self { base }
}
pub fn base(&self) -> usize {
self.base
}
}
impl MmioAccess for DirectMmio {
fn read(&self, offset: usize) -> u32 {
unsafe { core::ptr::read_volatile((self.base + offset) as *const u32) }
}
fn write(&self, offset: usize, val: u32) {
unsafe { core::ptr::write_volatile((self.base + offset) as *mut u32, val) }
}
}
pub fn init_legacy_device(mmio: &impl MmioAccess, queue_phys: usize, queue_size: u32) {
mmio.write(REG_STATUS, 0);
while mmio.read(REG_STATUS) != 0 {}
mmio.write(REG_STATUS, STATUS_ACK);
mmio.write(REG_STATUS, mmio.read(REG_STATUS) | STATUS_DRIVER);
mmio.write(REG_GUEST_PAGE_SIZE, PAGE_SIZE as u32);
mmio.write(REG_FEATURES_SEL, 0);
mmio.write(REG_DRIVER_FEATURES_SEL, 0);
mmio.write(REG_DRIVER_FEATURES, 0);
mmio.write(REG_QUEUE_SEL, 0);
let queue_max = mmio.read(REG_QUEUE_NUM_MAX);
mmio.write(REG_QUEUE_NUM, queue_size.min(queue_max));
mmio.write(REG_QUEUE_PFN, (queue_phys / PAGE_SIZE) as u32);
mmio.write(REG_STATUS, mmio.read(REG_STATUS) | STATUS_DRIVER_OK);
}
pub fn probe_slot(base: usize, device_id: u32) -> Option<DirectMmio> {
let mmio = DirectMmio::new(base);
if mmio.read(REG_MAGIC) == MAGIC && mmio.read(REG_DEVICE_ID) == device_id {
Some(mmio)
} else {
None
}
}
pub const MMIO_FIRST: usize = 0x10001000;
pub const MMIO_STRIDE: usize = 0x1000;
pub const MMIO_SLOTS: usize = 8;
pub fn scan_for_device(device_id: u32) -> Option<DirectMmio> {
for slot in 0..MMIO_SLOTS {
if let Some(mmio) = probe_slot(MMIO_FIRST + slot * MMIO_STRIDE, device_id) {
return Some(mmio);
}
}
None
}