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,80 @@
use crate::mmio::{MmioAccess, REG_QUEUE_NOTIFY};
pub const QUEUE_SIZE: usize = 8;
const DESC_SIZE: usize = 16;
const TIMEOUT: u32 = 10_000_000;
pub struct QueueAddrs {
pub desc: usize,
pub avail: usize,
pub used: usize,
}
impl QueueAddrs {
pub fn from_base(base: usize) -> Self {
Self {
desc: base,
avail: base + QUEUE_SIZE * DESC_SIZE,
used: base + 4096,
}
}
}
pub struct Descriptor {
pub addr: u64,
pub len: u32,
pub writable: bool,
}
pub struct VirtioQueue {
addrs: QueueAddrs,
}
impl VirtioQueue {
pub fn new(addrs: QueueAddrs) -> Self {
Self { addrs }
}
pub fn submit(&self, mmio: &impl MmioAccess, descs: &[Descriptor]) -> bool {
if descs.is_empty() || descs.len() > QUEUE_SIZE {
return false;
}
unsafe {
let desc_base = self.addrs.desc as *mut u8;
for (i, d) in descs.iter().enumerate() {
let p = desc_base.add(i * DESC_SIZE);
core::ptr::write_volatile(p as *mut u64, d.addr);
core::ptr::write_volatile(p.add(8) as *mut u32, d.len);
let last = i == descs.len() - 1;
let flags: u16 = if d.writable { 2 } else { 0 }
| if !last { 1 } else { 0 };
core::ptr::write_volatile(p.add(12) as *mut u16, flags);
core::ptr::write_volatile(p.add(14) as *mut u16, if last { 0 } else { (i + 1) as u16 });
}
let avail = self.addrs.avail;
let avail_idx = core::ptr::read_volatile((avail + 2) as *const u16);
core::ptr::write_volatile(
(avail + 4 + (avail_idx as usize % QUEUE_SIZE) * 2) as *mut u16,
0,
);
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
core::ptr::write_volatile((avail + 2) as *mut u16, avail_idx.wrapping_add(1));
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
mmio.write(REG_QUEUE_NOTIFY, 0);
let expected = avail_idx.wrapping_add(1);
let used = self.addrs.used;
let mut timeout = TIMEOUT;
while core::ptr::read_volatile((used + 2) as *const u16) != expected {
core::hint::spin_loop();
timeout -= 1;
if timeout == 0 { return false; }
}
}
true
}
}