From 3a0395453b0ed9fb5b9c9ca0e05964766880338f Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 13:29:28 +0200 Subject: [PATCH] refactor: extract shared hw-virtio crate, eliminate VirtIO protocol duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- Cargo.lock | 7 + Cargo.toml | 4 + crates/adapter-riscv/Cargo.toml | 1 + .../src/devices/storage_setup.rs | 19 +- crates/adapter-riscv/src/hw/virtio_blk.rs | 229 +++--------------- crates/adapter-riscv/src/hw/virtio_gpu.rs | 167 ++----------- crates/drivers/block/virtio/Cargo.toml | 1 + crates/drivers/block/virtio/src/lib.rs | 149 ++++-------- crates/drivers/framebuffer/virtio/Cargo.toml | 1 + crates/drivers/framebuffer/virtio/src/lib.rs | 120 +++------ crates/hw-virtio/Cargo.toml | 6 + crates/hw-virtio/src/block.rs | 61 +++++ crates/hw-virtio/src/gpu.rs | 87 +++++++ crates/hw-virtio/src/lib.rs | 9 + crates/hw-virtio/src/mmio.rs | 96 ++++++++ crates/hw-virtio/src/queue.rs | 80 ++++++ .../servers/input-server/src/adapters/mock.rs | 2 +- .../input-server/src/adapters/qemu_uart.rs | 2 +- .../servers/input-server/src/input_device.rs | 1 - crates/servers/input-server/src/lib.rs | 1 - crates/servers/input-server/src/server.rs | 2 +- 21 files changed, 483 insertions(+), 562 deletions(-) create mode 100644 crates/hw-virtio/Cargo.toml create mode 100644 crates/hw-virtio/src/block.rs create mode 100644 crates/hw-virtio/src/gpu.rs create mode 100644 crates/hw-virtio/src/lib.rs create mode 100644 crates/hw-virtio/src/mmio.rs create mode 100644 crates/hw-virtio/src/queue.rs delete mode 100644 crates/servers/input-server/src/input_device.rs diff --git a/Cargo.lock b/Cargo.lock index bdf01a7..13033dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,7 @@ version = "0.1.0" dependencies = [ "domain", "elf", + "hw-virtio", "kernel", "platform", "types", @@ -81,6 +82,7 @@ dependencies = [ name = "driver-block-virtio" version = "0.1.0" dependencies = [ + "hw-virtio", "port-block-device", ] @@ -95,6 +97,7 @@ dependencies = [ name = "driver-framebuffer-virtio" version = "0.1.0" dependencies = [ + "hw-virtio", "port-framebuffer", ] @@ -125,6 +128,10 @@ dependencies = [ "userspace-rt", ] +[[package]] +name = "hw-virtio" +version = "0.1.0" + [[package]] name = "init" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 27cd478..d589192 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ members = [ "crates/drivers/block/virtio", "crates/bins/display-qemu", "crates/bins/storage-qemu", + "crates/hw-virtio", ] resolver = "2" @@ -92,6 +93,9 @@ telephony-server = { path = "crates/servers/telephony-server" } usb-server = { path = "crates/servers/usb-server" } user-manager = { path = "crates/servers/user-manager" } +# hw +hw-virtio = { path = "crates/hw-virtio" } + # ports port-framebuffer = { path = "crates/ports/framebuffer" } port-block-device = { path = "crates/ports/block-device" } diff --git a/crates/adapter-riscv/Cargo.toml b/crates/adapter-riscv/Cargo.toml index ce9e7ed..2146002 100644 --- a/crates/adapter-riscv/Cargo.toml +++ b/crates/adapter-riscv/Cargo.toml @@ -14,6 +14,7 @@ domain = { workspace = true } elf = { workspace = true } platform = { workspace = true } kernel = { workspace = true } +hw-virtio = { workspace = true } [profile.release] panic = "abort" diff --git a/crates/adapter-riscv/src/devices/storage_setup.rs b/crates/adapter-riscv/src/devices/storage_setup.rs index e90731b..e5e4cf5 100644 --- a/crates/adapter-riscv/src/devices/storage_setup.rs +++ b/crates/adapter-riscv/src/devices/storage_setup.rs @@ -1,5 +1,4 @@ use crate::hw::uart::Uart; -use crate::hw::virtio_blk::{mmio_read, mmio_write}; use crate::{kernel_ref, mmu}; use types::id::ProcessId; @@ -86,22 +85,8 @@ fn write_phys_info(pt: &mut mmu::PageTable, reqhdr_phys: usize) { } fn reinit_device(base: usize, vqueue_phys: usize) { - mmio_write(base, 0x070, 0); - while mmio_read(base, 0x070) != 0 {} - - mmio_write(base, 0x070, 1); - mmio_write(base, 0x070, mmio_read(base, 0x070) | 2); - mmio_write(base, 0x028, 4096); - mmio_write(base, 0x014, 0); - mmio_write(base, 0x024, 0); - mmio_write(base, 0x020, 0); - - mmio_write(base, 0x030, 0); - let queue_max = mmio_read(base, 0x034); - mmio_write(base, 0x038, 8u32.min(queue_max)); - mmio_write(base, 0x040, (vqueue_phys / 4096) as u32); - - mmio_write(base, 0x070, mmio_read(base, 0x070) | 4); + let mmio = hw_virtio::mmio::DirectMmio::new(base); + hw_virtio::mmio::init_legacy_device(&mmio, vqueue_phys, hw_virtio::queue::QUEUE_SIZE as u32); } fn map_shared_buffer(init_pt: &mut mmu::PageTable, storage_pt: &mut mmu::PageTable) { diff --git a/crates/adapter-riscv/src/hw/virtio_blk.rs b/crates/adapter-riscv/src/hw/virtio_blk.rs index c118d61..17aa1ec 100644 --- a/crates/adapter-riscv/src/hw/virtio_blk.rs +++ b/crates/adapter-riscv/src/hw/virtio_blk.rs @@ -1,220 +1,53 @@ -const VIRTIO_MAGIC: u32 = 0x74726976; -const VIRTIO_BLK_DEVICE_ID: u32 = 2; -const VIRTIO_MMIO_FIRST: usize = 0x10001000; -const VIRTIO_MMIO_STRIDE: usize = 0x1000; -const VIRTIO_MMIO_SLOTS: usize = 8; +use hw_virtio::mmio::{self, DirectMmio}; +use hw_virtio::queue::{QueueAddrs, VirtioQueue, QUEUE_SIZE}; +use hw_virtio::block::{self, DmaAddrs}; -const VIRTIO_STATUS_ACK: u32 = 1; -const VIRTIO_STATUS_DRIVER: u32 = 2; -const VIRTIO_STATUS_FEATURES_OK: u32 = 8; -const VIRTIO_STATUS_DRIVER_OK: u32 = 4; - -const QUEUE_SIZE: usize = 8; const SECTOR_SIZE: usize = 512; -const PAGE_SIZE: usize = 4096; -// Legacy VirtIO layout: used ring must start at page boundary after avail ring. -// descs: 8 * 16 = 128 bytes, avail: 22 bytes → total 150 → pad to 4096. #[repr(C, align(4096))] struct VirtqMem { - descs: [VirtqDesc; QUEUE_SIZE], - avail: VirtqAvail, - _pad: [u8; 3946], - used: VirtqUsed, + _data: [u8; 8192], } -#[repr(C)] -#[derive(Clone, Copy)] -struct VirtqDesc { - addr: u64, - len: u32, - flags: u16, - next: u16, -} - -#[repr(C)] -struct VirtqAvail { - flags: u16, - idx: u16, - ring: [u16; QUEUE_SIZE], - used_event: u16, -} - -#[repr(C)] -struct VirtqUsed { - flags: u16, - idx: u16, - ring: [VirtqUsedElem; QUEUE_SIZE], -} - -#[repr(C)] -#[derive(Clone, Copy)] -struct VirtqUsedElem { - id: u32, - len: u32, -} - -#[repr(C)] -struct VirtioBlkReqHeader { - req_type: u32, - reserved: u32, - sector: u64, -} - -static mut VQUEUE: VirtqMem = VirtqMem { - descs: [const { VirtqDesc { addr: 0, len: 0, flags: 0, next: 0 } }; QUEUE_SIZE], - avail: VirtqAvail { flags: 0, idx: 0, ring: [0; QUEUE_SIZE], used_event: 0 }, - _pad: [0; 3946], - used: VirtqUsed { - flags: 0, idx: 0, - ring: [const { VirtqUsedElem { id: 0, len: 0 } }; QUEUE_SIZE], - }, -}; - -static mut REQ_HEADER: VirtioBlkReqHeader = VirtioBlkReqHeader { - req_type: 0, reserved: 0, sector: 0, -}; - +static mut VQUEUE: VirtqMem = VirtqMem { _data: [0; 8192] }; +static mut REQ_HEADER: block::BlockReqHeader = block::BlockReqHeader { req_type: 0, reserved: 0, sector: 0 }; static mut REQ_STATUS: u8 = 0xFF; - -pub fn mmio_read(base: usize, offset: usize) -> u32 { - unsafe { core::ptr::read_volatile((base + offset) as *const u32) } -} - -pub fn mmio_write(base: usize, offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((base + offset) as *mut u32, val) } -} +static mut SECTOR_BUF: [u8; SECTOR_SIZE] = [0; SECTOR_SIZE]; pub struct VirtioBlk { - base: usize, - legacy: bool, + mmio: DirectMmio, + queue: VirtioQueue, + dma: DmaAddrs, } impl VirtioBlk { - pub fn base(&self) -> usize { - self.base - } - pub fn probe() -> Option { - for slot in 0..VIRTIO_MMIO_SLOTS { - let base = VIRTIO_MMIO_FIRST + slot * VIRTIO_MMIO_STRIDE; - let magic = mmio_read(base, 0x000); - let device_id = mmio_read(base, 0x008); - if magic == VIRTIO_MAGIC && device_id == VIRTIO_BLK_DEVICE_ID { - return Self::init_at(base); + let mmio = mmio::scan_for_device(mmio::DEVICE_ID_BLOCK)?; + + let vq_phys = unsafe { core::ptr::addr_of!(VQUEUE) as usize }; + mmio::init_legacy_device(&mmio, vq_phys, QUEUE_SIZE as u32); + + let queue = VirtioQueue::new(QueueAddrs::from_base(vq_phys)); + + let dma = unsafe { + DmaAddrs { + header_pa: core::ptr::addr_of!(REQ_HEADER) as u64, + header_va: core::ptr::addr_of_mut!(REQ_HEADER) as usize, + status_pa: core::ptr::addr_of!(REQ_STATUS) as u64, + status_va: core::ptr::addr_of_mut!(REQ_STATUS) as usize, + data_pa: core::ptr::addr_of!(SECTOR_BUF) as u64, + data_va: core::ptr::addr_of_mut!(SECTOR_BUF) as usize, } - } - None + }; + + Some(Self { mmio, queue, dma }) } - fn init_at(base: usize) -> Option { - let version = mmio_read(base, 0x004); - let legacy = version == 1; - - // Reset - mmio_write(base, 0x070, 0); - while mmio_read(base, 0x070) != 0 {} - - // Acknowledge + Driver - mmio_write(base, 0x070, VIRTIO_STATUS_ACK); - mmio_write(base, 0x070, mmio_read(base, 0x070) | VIRTIO_STATUS_DRIVER); - - if legacy { - // Legacy: set guest page size - mmio_write(base, 0x028, PAGE_SIZE as u32); - } - - // Feature negotiation - mmio_write(base, 0x014, 0); - let _features = mmio_read(base, 0x010); - mmio_write(base, 0x024, 0); - mmio_write(base, 0x020, 0); - - if !legacy { - mmio_write(base, 0x070, mmio_read(base, 0x070) | VIRTIO_STATUS_FEATURES_OK); - if mmio_read(base, 0x070) & VIRTIO_STATUS_FEATURES_OK == 0 { - return None; - } - } - - // Queue setup - mmio_write(base, 0x030, 0); - let queue_max = mmio_read(base, 0x034); - if queue_max == 0 { return None; } - let queue_num = (QUEUE_SIZE as u32).min(queue_max); - mmio_write(base, 0x038, queue_num); - - unsafe { - let vq_addr = core::ptr::addr_of!(VQUEUE) as usize; - - if legacy { - mmio_write(base, 0x040, (vq_addr / PAGE_SIZE) as u32); - } else { - let desc_addr = vq_addr as u64; - let avail_addr = (vq_addr + core::mem::offset_of!(VirtqMem, avail)) as u64; - let used_addr = (vq_addr + core::mem::offset_of!(VirtqMem, used)) as u64; - - mmio_write(base, 0x080, desc_addr as u32); - mmio_write(base, 0x084, (desc_addr >> 32) as u32); - mmio_write(base, 0x090, avail_addr as u32); - mmio_write(base, 0x094, (avail_addr >> 32) as u32); - mmio_write(base, 0x0A0, used_addr as u32); - mmio_write(base, 0x0A4, (used_addr >> 32) as u32); - mmio_write(base, 0x044, 1); - } - } - - mmio_write(base, 0x070, mmio_read(base, 0x070) | VIRTIO_STATUS_DRIVER_OK); - - Some(Self { base, legacy }) + pub fn base(&self) -> usize { + self.mmio.base() } pub fn read_sector(&mut self, sector: u32, buf: &mut [u8; SECTOR_SIZE]) -> bool { - unsafe { - REQ_HEADER.req_type = 0; - REQ_HEADER.reserved = 0; - REQ_HEADER.sector = sector as u64; - REQ_STATUS = 0xFF; - - VQUEUE.descs[0] = VirtqDesc { - addr: core::ptr::addr_of!(REQ_HEADER) as u64, - len: 16, - flags: 1, // NEXT - next: 1, - }; - VQUEUE.descs[1] = VirtqDesc { - addr: buf.as_ptr() as u64, - len: SECTOR_SIZE as u32, - flags: 2 | 1, // WRITE | NEXT - next: 2, - }; - VQUEUE.descs[2] = VirtqDesc { - addr: core::ptr::addr_of!(REQ_STATUS) as u64, - len: 1, - flags: 2, // WRITE - next: 0, - }; - - let avail_idx = core::ptr::read_volatile(core::ptr::addr_of!(VQUEUE.avail.idx)); - VQUEUE.avail.ring[(avail_idx as usize) % QUEUE_SIZE] = 0; - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - core::ptr::write_volatile( - core::ptr::addr_of_mut!(VQUEUE.avail.idx), - avail_idx.wrapping_add(1), - ); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - - mmio_write(self.base, 0x050, 0); - - let expected = avail_idx.wrapping_add(1); - let mut timeout = 10_000_000u32; - while core::ptr::read_volatile(core::ptr::addr_of!(VQUEUE.used.idx)) != expected { - core::hint::spin_loop(); - timeout -= 1; - if timeout == 0 { return false; } - } - - REQ_STATUS == 0 - } + block::read_sector(&self.mmio, &self.queue, &self.dma, sector, buf) } } diff --git a/crates/adapter-riscv/src/hw/virtio_gpu.rs b/crates/adapter-riscv/src/hw/virtio_gpu.rs index 9e3ec82..af06ce1 100644 --- a/crates/adapter-riscv/src/hw/virtio_gpu.rs +++ b/crates/adapter-riscv/src/hw/virtio_gpu.rs @@ -1,165 +1,34 @@ -const VIRTIO_MAGIC: u32 = 0x74726976; -const VIRTIO_GPU_DEVICE_ID: u32 = 16; -const VIRTIO_MMIO_FIRST: usize = 0x10001000; -const VIRTIO_MMIO_STRIDE: usize = 0x1000; -const VIRTIO_MMIO_SLOTS: usize = 8; -const QUEUE_SIZE: usize = 8; - -use super::virtio_blk::{mmio_read, mmio_write}; +use hw_virtio::mmio::{self, DirectMmio}; +use hw_virtio::queue::{QueueAddrs, VirtioQueue, QUEUE_SIZE}; +use hw_virtio::gpu::{self, GpuDmaAddrs}; pub struct VirtioGpu { - base: usize, -} - -#[repr(C)] -struct GpuCtrlHdr { - hdr_type: u32, - flags: u32, - fence_id: u64, - ctx_id: u32, - ring_idx: u8, - _pad: [u8; 3], + mmio: DirectMmio, } impl VirtioGpu { pub fn probe() -> Option { - for slot in 0..VIRTIO_MMIO_SLOTS { - let base = VIRTIO_MMIO_FIRST + slot * VIRTIO_MMIO_STRIDE; - let magic = mmio_read(base, 0x000); - let device_id = mmio_read(base, 0x008); - if magic == VIRTIO_MAGIC && device_id == VIRTIO_GPU_DEVICE_ID { - return Some(Self { base }); - } - } - None + let mmio = mmio::scan_for_device(mmio::DEVICE_ID_GPU)?; + Some(Self { mmio }) } pub fn base(&self) -> usize { - self.base + self.mmio.base() } pub fn init_device(&self, vqueue_phys: usize) { - let base = self.base; - - mmio_write(base, 0x070, 0); - while mmio_read(base, 0x070) != 0 {} - - mmio_write(base, 0x070, 1); // ACK - mmio_write(base, 0x070, mmio_read(base, 0x070) | 2); // DRIVER - mmio_write(base, 0x028, 4096); // legacy guest page size - mmio_write(base, 0x014, 0); // features sel - mmio_write(base, 0x024, 0); // driver features sel - mmio_write(base, 0x020, 0); // driver features - - // controlq (index 0) - mmio_write(base, 0x030, 0); - let qmax = mmio_read(base, 0x034); - let qnum = (QUEUE_SIZE as u32).min(qmax); - mmio_write(base, 0x038, qnum); - mmio_write(base, 0x040, (vqueue_phys / 4096) as u32); - - mmio_write(base, 0x070, mmio_read(base, 0x070) | 4); // DRIVER_OK + mmio::init_legacy_device(&self.mmio, vqueue_phys, QUEUE_SIZE as u32); } - pub fn setup_framebuffer( - &self, - cmd_buf_phys: usize, - vqueue_phys: usize, - fb_phys: usize, - width: u32, - height: u32, - ) { - self.send_create_2d(cmd_buf_phys, vqueue_phys, 1, width, height); - self.send_attach_backing(cmd_buf_phys, vqueue_phys, 1, fb_phys, width * height * 4); - self.send_set_scanout(cmd_buf_phys, vqueue_phys, 0, 1, width, height); - } - - fn send_cmd(&self, cmd_phys: usize, cmd_len: u32, resp_phys: usize, resp_len: u32, vqueue_phys: usize) { - unsafe { - let descs = vqueue_phys as *mut [u64; 2]; // each desc = addr(u64) + len(u32) + flags(u16) + next(u16) - let desc0 = vqueue_phys as *mut u8; - // desc 0: request (readable) - core::ptr::write_volatile(desc0 as *mut u64, cmd_phys as u64); - core::ptr::write_volatile(desc0.add(8) as *mut u32, cmd_len); - core::ptr::write_volatile(desc0.add(12) as *mut u16, 1); // NEXT - core::ptr::write_volatile(desc0.add(14) as *mut u16, 1); // next=1 - - // desc 1: response (writable) - let desc1 = desc0.add(16); - core::ptr::write_volatile(desc1 as *mut u64, resp_phys as u64); - core::ptr::write_volatile(desc1.add(8) as *mut u32, resp_len); - core::ptr::write_volatile(desc1.add(12) as *mut u16, 2); // WRITE - core::ptr::write_volatile(desc1.add(14) as *mut u16, 0); - - // avail ring: after descs (8 descs * 16 bytes = 128 bytes) - let avail = vqueue_phys + QUEUE_SIZE * 16; - 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(self.base, 0x050, 0); // notify queue 0 - - // poll used ring (at page boundary = vqueue_phys + 4096) - let used = vqueue_phys + 4096; - let expected = avail_idx.wrapping_add(1); - let mut timeout = 10_000_000u32; - while core::ptr::read_volatile((used + 2) as *const u16) != expected { - core::hint::spin_loop(); - timeout -= 1; - if timeout == 0 { return; } - } - } - } - - fn send_create_2d(&self, cmd_phys: usize, vq_phys: usize, resource_id: u32, width: u32, height: u32) { - // cmd header (24 bytes) + create_2d fields (16 bytes) = 40 bytes - unsafe { - let p = cmd_phys as *mut u8; - core::ptr::write_bytes(p, 0, 4096); - core::ptr::write_volatile(p as *mut u32, 0x0101); // RESOURCE_CREATE_2D - core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); - core::ptr::write_volatile(p.add(28) as *mut u32, 1); // B8G8R8A8_UNORM - core::ptr::write_volatile(p.add(32) as *mut u32, width); - core::ptr::write_volatile(p.add(36) as *mut u32, height); - } - let resp_phys = cmd_phys + 512; - self.send_cmd(cmd_phys, 40, resp_phys, 24, vq_phys); - } - - fn send_attach_backing(&self, cmd_phys: usize, vq_phys: usize, resource_id: u32, fb_phys: usize, fb_size: u32) { - // header (24) + attach_backing (8) + mem_entry (16) = 48 bytes - unsafe { - let p = cmd_phys as *mut u8; - core::ptr::write_bytes(p, 0, 512); - core::ptr::write_volatile(p as *mut u32, 0x0104); // RESOURCE_ATTACH_BACKING - core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); - core::ptr::write_volatile(p.add(28) as *mut u32, 1); // nr_entries - // mem_entry - core::ptr::write_volatile(p.add(32) as *mut u64, fb_phys as u64); - core::ptr::write_volatile(p.add(40) as *mut u32, fb_size); - } - let resp_phys = cmd_phys + 512; - self.send_cmd(cmd_phys, 48, resp_phys, 24, vq_phys); - } - - fn send_set_scanout(&self, cmd_phys: usize, vq_phys: usize, scanout_id: u32, resource_id: u32, width: u32, height: u32) { - // header (24) + rect (16) + scanout_id (4) + resource_id (4) = 48 bytes - unsafe { - let p = cmd_phys as *mut u8; - core::ptr::write_bytes(p, 0, 512); - core::ptr::write_volatile(p as *mut u32, 0x0103); // SET_SCANOUT - // rect: x=0, y=0, width, height at offset 24 - core::ptr::write_volatile(p.add(32) as *mut u32, width); - core::ptr::write_volatile(p.add(36) as *mut u32, height); - core::ptr::write_volatile(p.add(40) as *mut u32, scanout_id); - core::ptr::write_volatile(p.add(44) as *mut u32, resource_id); - } - let resp_phys = cmd_phys + 512; - self.send_cmd(cmd_phys, 48, resp_phys, 24, vq_phys); + pub fn setup_framebuffer(&self, cmd_phys: usize, vq_phys: usize, fb_phys: usize, width: u32, height: u32) { + let queue = VirtioQueue::new(QueueAddrs::from_base(vq_phys)); + let dma = GpuDmaAddrs { + cmd_pa: cmd_phys as u64, + cmd_va: cmd_phys, + resp_pa: (cmd_phys + 512) as u64, + }; + gpu::create_2d(&self.mmio, &queue, &dma, 1, width, height); + gpu::attach_backing(&self.mmio, &queue, &dma, 1, fb_phys as u64, width * height * 4); + gpu::set_scanout(&self.mmio, &queue, &dma, 0, 1, width, height); } } diff --git a/crates/drivers/block/virtio/Cargo.toml b/crates/drivers/block/virtio/Cargo.toml index 2b17753..bd76394 100644 --- a/crates/drivers/block/virtio/Cargo.toml +++ b/crates/drivers/block/virtio/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] port-block-device = { workspace = true } +hw-virtio = { workspace = true } diff --git a/crates/drivers/block/virtio/src/lib.rs b/crates/drivers/block/virtio/src/lib.rs index 09ad723..b502525 100644 --- a/crates/drivers/block/virtio/src/lib.rs +++ b/crates/drivers/block/virtio/src/lib.rs @@ -1,87 +1,56 @@ #![no_std] +use hw_virtio::mmio::MmioAccess; +use hw_virtio::queue::{QueueAddrs, VirtioQueue}; +use hw_virtio::block::{self, DmaAddrs}; use port_block_device::{BlockDevice, SectorId}; -const QUEUE_SIZE: usize = 8; -const SECTOR_SIZE: usize = 512; - const MMIO_BASE: usize = 0x20000; const VQUEUE_VA: usize = 0x21000; const REQ_HDR_VA: usize = 0x23000; -const PHYS_INFO_VA: usize = 0x24000; +const PHYSINFO_VA: usize = 0x24000; +const SECTOR_SIZE: usize = 512; -#[repr(C)] -#[derive(Clone, Copy)] -struct VirtqDesc { - addr: u64, - len: u32, - flags: u16, - next: u16, -} +struct UserMmio; -#[repr(C)] -struct VirtqAvail { - flags: u16, - idx: u16, - ring: [u16; QUEUE_SIZE], - used_event: u16, -} - -#[repr(C)] -#[derive(Clone, Copy)] -struct VirtqUsedElem { - id: u32, - len: u32, -} - -#[repr(C)] -struct VirtqUsed { - flags: u16, - idx: u16, - ring: [VirtqUsedElem; QUEUE_SIZE], -} - -#[repr(C)] -struct VirtioBlkReqHeader { - req_type: u32, - reserved: u32, - sector: u64, -} - -fn mmio_write(offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((MMIO_BASE + offset) as *mut u32, val) } -} - -fn descs() -> *mut VirtqDesc { VQUEUE_VA as *mut VirtqDesc } -fn avail() -> *mut VirtqAvail { (VQUEUE_VA + QUEUE_SIZE * 16) as *mut VirtqAvail } -fn used() -> *mut VirtqUsed { (VQUEUE_VA + 0x1000) as *mut VirtqUsed } -fn req_header() -> *mut VirtioBlkReqHeader { REQ_HDR_VA as *mut VirtioBlkReqHeader } -fn req_status() -> *mut u8 { (REQ_HDR_VA + 16) as *mut u8 } - -struct PhysAddrs { - req_hdr: u64, - status: u64, - sector_buf: u64, -} - -fn read_phys_addrs() -> PhysAddrs { - unsafe { - let base = PHYS_INFO_VA as *const u64; - PhysAddrs { - req_hdr: core::ptr::read_volatile(base), - status: core::ptr::read_volatile(base.add(1)), - sector_buf: core::ptr::read_volatile(base.add(2)), - } +impl MmioAccess for UserMmio { + fn read(&self, offset: usize) -> u32 { + unsafe { core::ptr::read_volatile((MMIO_BASE + offset) as *const u32) } + } + fn write(&self, offset: usize, val: u32) { + unsafe { core::ptr::write_volatile((MMIO_BASE + offset) as *mut u32, val) } } } pub struct VirtioBlockDevice { - phys: PhysAddrs, + mmio: UserMmio, + queue: VirtioQueue, + dma: DmaAddrs, } impl VirtioBlockDevice { pub fn new() -> Self { - Self { phys: read_phys_addrs() } + let queue = VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)); + + let (hdr_pa, status_pa, data_pa) = unsafe { + let p = PHYSINFO_VA as *const u64; + ( + core::ptr::read_volatile(p), + core::ptr::read_volatile(p.add(1)), + core::ptr::read_volatile(p.add(2)), + ) + }; + + let dma = DmaAddrs { + header_pa: hdr_pa, + header_va: REQ_HDR_VA, + status_pa, + status_va: REQ_HDR_VA + 16, + data_pa, + data_va: REQ_HDR_VA + 32, + }; + + Self { mmio: UserMmio, queue, dma } } } @@ -91,49 +60,13 @@ impl BlockDevice for VirtioBlockDevice { fn read_sector(&mut self, sector: SectorId, buf: &mut [u8]) -> bool { if buf.len() < SECTOR_SIZE { return false; } - - unsafe { - let hdr = req_header(); - (*hdr).req_type = 0; - (*hdr).reserved = 0; - (*hdr).sector = sector.raw() as u64; - - core::ptr::write_volatile(req_status(), 0xFF); - - let sector_buf_va = (REQ_HDR_VA + 32) as *mut u8; - - let d = descs(); - (*d.add(0)) = VirtqDesc { addr: self.phys.req_hdr, len: 16, flags: 1, next: 1 }; - (*d.add(1)) = VirtqDesc { addr: self.phys.sector_buf, len: SECTOR_SIZE as u32, flags: 2 | 1, next: 2 }; - (*d.add(2)) = VirtqDesc { addr: self.phys.status, len: 1, flags: 2, next: 0 }; - - let a = avail(); - let avail_idx = core::ptr::read_volatile(&(*a).idx); - (*a).ring[(avail_idx as usize) % QUEUE_SIZE] = 0; - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - core::ptr::write_volatile(&mut (*a).idx, avail_idx.wrapping_add(1)); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - - mmio_write(0x050, 0); - - let expected = avail_idx.wrapping_add(1); - let u = used(); - let mut timeout = 10_000_000u32; - while core::ptr::read_volatile(&(*u).idx) != expected { - core::hint::spin_loop(); - timeout -= 1; - if timeout == 0 { return false; } - } - - if core::ptr::read_volatile(req_status()) != 0 { return false; } - - core::ptr::copy_nonoverlapping(sector_buf_va, buf.as_mut_ptr(), SECTOR_SIZE); + let mut sector_buf = [0u8; SECTOR_SIZE]; + if !block::read_sector(&self.mmio, &self.queue, &self.dma, sector.raw(), &mut sector_buf) { + return false; } - + buf[..SECTOR_SIZE].copy_from_slice(§or_buf); true } - fn write_sector(&mut self, _sector: SectorId, _buf: &[u8]) -> bool { - false - } + fn write_sector(&mut self, _sector: SectorId, _buf: &[u8]) -> bool { false } } diff --git a/crates/drivers/framebuffer/virtio/Cargo.toml b/crates/drivers/framebuffer/virtio/Cargo.toml index ab885e3..bb8d5fe 100644 --- a/crates/drivers/framebuffer/virtio/Cargo.toml +++ b/crates/drivers/framebuffer/virtio/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] port-framebuffer = { workspace = true } +hw-virtio = { workspace = true } diff --git a/crates/drivers/framebuffer/virtio/src/lib.rs b/crates/drivers/framebuffer/virtio/src/lib.rs index 6ca6f30..ada8977 100644 --- a/crates/drivers/framebuffer/virtio/src/lib.rs +++ b/crates/drivers/framebuffer/virtio/src/lib.rs @@ -1,5 +1,8 @@ #![no_std] +use hw_virtio::mmio::MmioAccess; +use hw_virtio::queue::{QueueAddrs, VirtioQueue}; +use hw_virtio::gpu::{self, GpuDmaAddrs}; use port_framebuffer::Framebuffer; const GPU_MMIO_VA: usize = 0x20000; @@ -8,13 +11,23 @@ const CMDBUF_VA: usize = 0x23000; const PHYSINFO_VA: usize = 0x24000; const FB_VA: usize = 0x100000; const FB_BPP: usize = 4; -const QUEUE_SIZE: usize = 8; + +struct GpuMmio; + +impl MmioAccess for GpuMmio { + fn read(&self, offset: usize) -> u32 { + unsafe { core::ptr::read_volatile((GPU_MMIO_VA + offset) as *const u32) } + } + fn write(&self, offset: usize, val: u32) { + unsafe { core::ptr::write_volatile((GPU_MMIO_VA + offset) as *mut u32, val) } + } +} struct PhysInfo { cmd_pa: u64, resp_pa: u64, - width: u64, - height: u64, + width: u16, + height: u16, } fn read_phys_info() -> PhysInfo { @@ -23,95 +36,34 @@ fn read_phys_info() -> PhysInfo { PhysInfo { cmd_pa: core::ptr::read_volatile(p), resp_pa: core::ptr::read_volatile(p.add(1)), - width: core::ptr::read_volatile(p.add(3)), - height: core::ptr::read_volatile(p.add(4)), + width: core::ptr::read_volatile(p.add(3)) as u16, + height: core::ptr::read_volatile(p.add(4)) as u16, } } } -fn mmio_write(offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((GPU_MMIO_VA + offset) as *mut u32, val) } -} - pub struct VirtioFramebuffer { + mmio: GpuMmio, + queue: VirtioQueue, + dma: GpuDmaAddrs, width: u16, height: u16, stride: usize, - phys: PhysInfo, } impl VirtioFramebuffer { pub fn new() -> Self { let phys = read_phys_info(); - let width = phys.width as u16; - let height = phys.height as u16; - let stride = width as usize * FB_BPP; - Self { width, height, stride, phys } - } - - fn send_gpu_cmd(&self, cmd_len: u32, resp_len: u32) { - unsafe { - let desc0 = VQUEUE_VA as *mut u8; - core::ptr::write_volatile(desc0 as *mut u64, self.phys.cmd_pa); - core::ptr::write_volatile(desc0.add(8) as *mut u32, cmd_len); - core::ptr::write_volatile(desc0.add(12) as *mut u16, 1); - core::ptr::write_volatile(desc0.add(14) as *mut u16, 1); - - let desc1 = desc0.add(16); - core::ptr::write_volatile(desc1 as *mut u64, self.phys.resp_pa); - core::ptr::write_volatile(desc1.add(8) as *mut u32, resp_len); - core::ptr::write_volatile(desc1.add(12) as *mut u16, 2); - core::ptr::write_volatile(desc1.add(14) as *mut u16, 0); - - let avail = VQUEUE_VA + QUEUE_SIZE * 16; - 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(0x050, 0); - - let used = VQUEUE_VA + 4096; - let expected = avail_idx.wrapping_add(1); - let mut timeout = 10_000_000u32; - while core::ptr::read_volatile((used + 2) as *const u16) != expected { - core::hint::spin_loop(); - timeout -= 1; - if timeout == 0 { return; } - } + let stride = phys.width as usize * FB_BPP; + Self { + mmio: GpuMmio, + queue: VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)), + dma: GpuDmaAddrs { cmd_pa: phys.cmd_pa, cmd_va: CMDBUF_VA, resp_pa: phys.resp_pa }, + width: phys.width, + height: phys.height, + stride, } } - - fn transfer_and_flush(&self, x: u32, y: u32, w: u32, h: u32) { - unsafe { - let p = CMDBUF_VA as *mut u8; - core::ptr::write_bytes(p, 0, 512); - core::ptr::write_volatile(p as *mut u32, 0x0105); - core::ptr::write_volatile(p.add(24) as *mut u32, x); - core::ptr::write_volatile(p.add(28) as *mut u32, y); - core::ptr::write_volatile(p.add(32) as *mut u32, w); - core::ptr::write_volatile(p.add(36) as *mut u32, h); - core::ptr::write_volatile(p.add(40) as *mut u64, 0); - core::ptr::write_volatile(p.add(48) as *mut u32, 1); - } - self.send_gpu_cmd(56, 24); - - unsafe { - let p = CMDBUF_VA as *mut u8; - core::ptr::write_bytes(p, 0, 512); - core::ptr::write_volatile(p as *mut u32, 0x0106); - core::ptr::write_volatile(p.add(24) as *mut u32, x); - core::ptr::write_volatile(p.add(28) as *mut u32, y); - core::ptr::write_volatile(p.add(32) as *mut u32, w); - core::ptr::write_volatile(p.add(36) as *mut u32, h); - core::ptr::write_volatile(p.add(40) as *mut u32, 1); - } - self.send_gpu_cmd(48, 24); - } } impl Framebuffer for VirtioFramebuffer { @@ -125,22 +77,20 @@ impl Framebuffer for VirtioFramebuffer { } fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: u32) { - for dy in 0..h { - for dx in 0..w { - self.write_pixel(x + dx, y + dy, color); - } - } + for dy in 0..h { for dx in 0..w { self.write_pixel(x + dx, y + dy, color); } } } fn clear(&mut self, color: u32) { let total = self.width as usize * self.height as usize; - let buf = FB_VA as *mut u32; for i in 0..total { - unsafe { core::ptr::write_volatile(buf.add(i), color); } + unsafe { core::ptr::write_volatile((FB_VA as *mut u32).add(i), color); } } } fn flush(&mut self) { - self.transfer_and_flush(0, 0, self.width as u32, self.height as u32); + let w = self.width as u32; + let h = self.height as u32; + gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, 1, 0, 0, w, h); + gpu::resource_flush(&self.mmio, &self.queue, &self.dma, 1, 0, 0, w, h); } } diff --git a/crates/hw-virtio/Cargo.toml b/crates/hw-virtio/Cargo.toml new file mode 100644 index 0000000..824735f --- /dev/null +++ b/crates/hw-virtio/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "hw-virtio" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/crates/hw-virtio/src/block.rs b/crates/hw-virtio/src/block.rs new file mode 100644 index 0000000..5de4bda --- /dev/null +++ b/crates/hw-virtio/src/block.rs @@ -0,0 +1,61 @@ +use crate::mmio::MmioAccess; +use crate::queue::{Descriptor, VirtioQueue}; + +const SECTOR_SIZE: usize = 512; + +#[repr(C)] +pub struct BlockReqHeader { + pub req_type: u32, + pub reserved: u32, + pub sector: u64, +} + +pub struct DmaAddrs { + pub header_pa: u64, + pub header_va: usize, + pub status_pa: u64, + pub status_va: usize, + pub data_pa: u64, + pub data_va: usize, +} + +pub fn read_sector( + mmio: &impl MmioAccess, + queue: &VirtioQueue, + dma: &DmaAddrs, + sector: u32, + buf: &mut [u8], +) -> bool { + if buf.len() < SECTOR_SIZE { return false; } + + unsafe { + let hdr = dma.header_va as *mut BlockReqHeader; + core::ptr::write_volatile(&raw mut (*hdr).req_type, 0); + core::ptr::write_volatile(&raw mut (*hdr).reserved, 0); + core::ptr::write_volatile(&raw mut (*hdr).sector, sector as u64); + core::ptr::write_volatile(dma.status_va as *mut u8, 0xFF); + } + + let descs = [ + Descriptor { addr: dma.header_pa, len: 16, writable: false }, + Descriptor { addr: dma.data_pa, len: SECTOR_SIZE as u32, writable: true }, + Descriptor { addr: dma.status_pa, len: 1, writable: true }, + ]; + + if !queue.submit(mmio, &descs) { return false; } + + let status = unsafe { core::ptr::read_volatile(dma.status_va as *const u8) }; + if status != 0 { return false; } + + if dma.data_va != buf.as_ptr() as usize { + unsafe { + core::ptr::copy_nonoverlapping( + dma.data_va as *const u8, + buf.as_mut_ptr(), + SECTOR_SIZE, + ); + } + } + + true +} diff --git a/crates/hw-virtio/src/gpu.rs b/crates/hw-virtio/src/gpu.rs new file mode 100644 index 0000000..d96515d --- /dev/null +++ b/crates/hw-virtio/src/gpu.rs @@ -0,0 +1,87 @@ +use crate::mmio::MmioAccess; +use crate::queue::{Descriptor, VirtioQueue}; + +pub struct GpuDmaAddrs { + pub cmd_pa: u64, + pub cmd_va: usize, + pub resp_pa: u64, +} + +pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: u32, height: u32) { + write_cmd_header(dma.cmd_va, 0x0101); + unsafe { + let p = dma.cmd_va as *mut u8; + core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); + core::ptr::write_volatile(p.add(28) as *mut u32, 1); // B8G8R8A8_UNORM + core::ptr::write_volatile(p.add(32) as *mut u32, width); + core::ptr::write_volatile(p.add(36) as *mut u32, height); + } + submit_cmd(mmio, queue, dma, 40); +} + +pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: u64, fb_size: u32) { + write_cmd_header(dma.cmd_va, 0x0104); + unsafe { + let p = dma.cmd_va as *mut u8; + core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); + core::ptr::write_volatile(p.add(28) as *mut u32, 1); + core::ptr::write_volatile(p.add(32) as *mut u64, fb_pa); + core::ptr::write_volatile(p.add(40) as *mut u32, fb_size); + } + submit_cmd(mmio, queue, dma, 48); +} + +pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: u32, height: u32) { + write_cmd_header(dma.cmd_va, 0x0103); + unsafe { + let p = dma.cmd_va as *mut u8; + core::ptr::write_volatile(p.add(32) as *mut u32, width); + core::ptr::write_volatile(p.add(36) as *mut u32, height); + core::ptr::write_volatile(p.add(40) as *mut u32, scanout_id); + core::ptr::write_volatile(p.add(44) as *mut u32, resource_id); + } + submit_cmd(mmio, queue, dma, 48); +} + +pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) { + write_cmd_header(dma.cmd_va, 0x0105); + unsafe { + let p = dma.cmd_va as *mut u8; + core::ptr::write_volatile(p.add(24) as *mut u32, x); + core::ptr::write_volatile(p.add(28) as *mut u32, y); + core::ptr::write_volatile(p.add(32) as *mut u32, w); + core::ptr::write_volatile(p.add(36) as *mut u32, h); + core::ptr::write_volatile(p.add(40) as *mut u64, 0); + core::ptr::write_volatile(p.add(48) as *mut u32, resource_id); + } + submit_cmd(mmio, queue, dma, 56); +} + +pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) { + write_cmd_header(dma.cmd_va, 0x0106); + unsafe { + let p = dma.cmd_va as *mut u8; + core::ptr::write_volatile(p.add(24) as *mut u32, x); + core::ptr::write_volatile(p.add(28) as *mut u32, y); + core::ptr::write_volatile(p.add(32) as *mut u32, w); + core::ptr::write_volatile(p.add(36) as *mut u32, h); + core::ptr::write_volatile(p.add(40) as *mut u32, resource_id); + } + submit_cmd(mmio, queue, dma, 48); +} + +fn write_cmd_header(cmd_va: usize, cmd_type: u32) { + unsafe { + let p = cmd_va as *mut u8; + core::ptr::write_bytes(p, 0, 512); + core::ptr::write_volatile(p as *mut u32, cmd_type); + } +} + +fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) { + let descs = [ + Descriptor { addr: dma.cmd_pa, len: cmd_len, writable: false }, + Descriptor { addr: dma.resp_pa, len: 24, writable: true }, + ]; + queue.submit(mmio, &descs); +} diff --git a/crates/hw-virtio/src/lib.rs b/crates/hw-virtio/src/lib.rs new file mode 100644 index 0000000..fc4bdb4 --- /dev/null +++ b/crates/hw-virtio/src/lib.rs @@ -0,0 +1,9 @@ +#![no_std] + +pub mod mmio; +pub mod queue; +pub mod block; +pub mod gpu; + +pub use mmio::MmioAccess; +pub use queue::{VirtioQueue, QueueAddrs}; diff --git a/crates/hw-virtio/src/mmio.rs b/crates/hw-virtio/src/mmio.rs new file mode 100644 index 0000000..f106378 --- /dev/null +++ b/crates/hw-virtio/src/mmio.rs @@ -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 { + 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 { + for slot in 0..MMIO_SLOTS { + if let Some(mmio) = probe_slot(MMIO_FIRST + slot * MMIO_STRIDE, device_id) { + return Some(mmio); + } + } + None +} diff --git a/crates/hw-virtio/src/queue.rs b/crates/hw-virtio/src/queue.rs new file mode 100644 index 0000000..a8cb83b --- /dev/null +++ b/crates/hw-virtio/src/queue.rs @@ -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 + } +} diff --git a/crates/servers/input-server/src/adapters/mock.rs b/crates/servers/input-server/src/adapters/mock.rs index 6b79b41..57dc946 100644 --- a/crates/servers/input-server/src/adapters/mock.rs +++ b/crates/servers/input-server/src/adapters/mock.rs @@ -1,4 +1,4 @@ -use crate::input_device::{InputDevice, InputEvent}; +use port_input_device::{InputDevice, InputEvent}; pub struct MockInputDevice { events: [Option; 16], diff --git a/crates/servers/input-server/src/adapters/qemu_uart.rs b/crates/servers/input-server/src/adapters/qemu_uart.rs index 7a402a5..c3a7267 100644 --- a/crates/servers/input-server/src/adapters/qemu_uart.rs +++ b/crates/servers/input-server/src/adapters/qemu_uart.rs @@ -1,4 +1,4 @@ -use crate::input_device::{InputDevice, InputEvent, Key}; +use port_input_device::{InputDevice, InputEvent, Key}; const UART_BASE: usize = 0x10000000; const UART_LSR: usize = 5; diff --git a/crates/servers/input-server/src/input_device.rs b/crates/servers/input-server/src/input_device.rs deleted file mode 100644 index d4bd2c1..0000000 --- a/crates/servers/input-server/src/input_device.rs +++ /dev/null @@ -1 +0,0 @@ -pub use port_input_device::{InputDevice, InputEvent, Key, GamepadButton, Axis}; diff --git a/crates/servers/input-server/src/lib.rs b/crates/servers/input-server/src/lib.rs index 74cec3e..4347493 100644 --- a/crates/servers/input-server/src/lib.rs +++ b/crates/servers/input-server/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] pub mod adapters; -pub mod input_device; pub mod protocol; pub mod server; diff --git a/crates/servers/input-server/src/server.rs b/crates/servers/input-server/src/server.rs index 0826620..9ad9447 100644 --- a/crates/servers/input-server/src/server.rs +++ b/crates/servers/input-server/src/server.rs @@ -1,6 +1,6 @@ use server_framework::Server; -use crate::input_device::InputDevice; +use port_input_device::InputDevice; use crate::protocol::{InputRequest, InputResponse}; pub struct InputServer {