From 27be73f42dbecf0170677c163fc59f5df023792f Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 21:10:42 +0200 Subject: [PATCH] feat: wire display + input servers to snake, fix QEMU 11.x GPU bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VirtIO GPU: QEMU 11.x has RESOURCE_ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) command types swapped in its legacy MMIO VirtIO GPU implementation. Workaround: send swapped type values. Confirmed via QEMU monitor (xp shows correct bytes in memory) and QEMU trace events (device dispatches to wrong handler). Display pipeline now works end-to-end: adapter → GPU init → create_2d → attach_backing → set_scanout display-server → clear(blue) → flush → transfer + resource_flush snake → draw commands via IPC → display renders → flush to GPU Input server: - New driver-input-uart crate (reads UART at mapped VA 0x20000) - New bins/input-qemu composition root - Adapter loads input-server, maps UART MMIO into its address space - Init delegates both display_ep (slot 0) and input_ep (slot 1) to snake - WASD keys → UART → input-server → IPC → snake direction change Snake game runs with display and input, exits on wall collision. --- .gitignore | 3 +- Cargo.lock | 20 +++ Cargo.toml | 3 + Makefile | 4 +- crates/adapter-riscv/src/boot/caps.rs | 19 ++- crates/adapter-riscv/src/devices/gpu_setup.rs | 152 +++++++++++++++++- .../adapter-riscv/src/devices/input_setup.rs | 19 +++ crates/adapter-riscv/src/devices/mod.rs | 1 + crates/adapter-riscv/src/hw/virtio_gpu.rs | 13 +- crates/adapter-riscv/src/main.rs | 4 +- crates/bins/display-qemu/Cargo.toml | 1 + crates/bins/display-qemu/src/main.rs | 15 +- crates/bins/input-qemu/Cargo.toml | 17 ++ crates/bins/input-qemu/src/main.rs | 16 ++ crates/drivers/input/uart/Cargo.toml | 7 + crates/drivers/input/uart/src/lib.rs | 47 ++++++ crates/hw-virtio/src/gpu.rs | 37 +++-- crates/init/src/lib.rs | 15 +- scripts/make-disk-image.sh | 2 +- 19 files changed, 363 insertions(+), 32 deletions(-) create mode 100644 crates/adapter-riscv/src/devices/input_setup.rs create mode 100644 crates/bins/input-qemu/Cargo.toml create mode 100644 crates/bins/input-qemu/src/main.rs create mode 100644 crates/drivers/input/uart/Cargo.toml create mode 100644 crates/drivers/input/uart/src/lib.rs diff --git a/.gitignore b/.gitignore index 212de44..6035f8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -.DS_Store \ No newline at end of file +.DS_Store +*.img \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7554574..916e338 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ version = "0.1.0" dependencies = [ "display-server", "driver-framebuffer-virtio", + "port-framebuffer", "server-framework", "types", "userspace", @@ -105,6 +106,13 @@ dependencies = [ "types", ] +[[package]] +name = "driver-input-uart" +version = "0.1.0" +dependencies = [ + "port-input-device", +] + [[package]] name = "echo" version = "0.1.0" @@ -150,6 +158,18 @@ dependencies = [ "userspace-rt", ] +[[package]] +name = "input-qemu" +version = "0.1.0" +dependencies = [ + "driver-input-uart", + "input-server", + "server-framework", + "types", + "userspace", + "userspace-rt", +] + [[package]] name = "input-server" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d589192..5d7db8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,9 @@ members = [ "crates/drivers/framebuffer/mock", "crates/drivers/block/virtio", "crates/bins/display-qemu", + "crates/bins/input-qemu", "crates/bins/storage-qemu", + "crates/drivers/input/uart", "crates/hw-virtio", ] resolver = "2" @@ -105,6 +107,7 @@ port-input-device = { path = "crates/ports/input-device" } driver-framebuffer-virtio = { path = "crates/drivers/framebuffer/virtio" } driver-framebuffer-mock = { path = "crates/drivers/framebuffer/mock" } driver-block-virtio = { path = "crates/drivers/block/virtio" } +driver-input-uart = { path = "crates/drivers/input/uart" } # apps snake = { path = "crates/apps/snake" } diff --git a/Makefile b/Makefile index 5ec342f..0d02a7b 100644 --- a/Makefile +++ b/Makefile @@ -31,8 +31,8 @@ kani: kani-types kani-domain kani-kernel build: cargo build --target $(RISCV_TARGET) --release \ -p adapter-riscv -p init -p echo -p snake \ - -p display-qemu -p storage-qemu \ - -p input-server -p log-server -p permission-manager -p session-server + -p display-qemu -p input-qemu -p storage-qemu \ + -p log-server -p permission-manager -p session-server image: build scripts/make-disk-image.sh diff --git a/crates/adapter-riscv/src/boot/caps.rs b/crates/adapter-riscv/src/boot/caps.rs index fe5b1e8..51fa310 100644 --- a/crates/adapter-riscv/src/boot/caps.rs +++ b/crates/adapter-riscv/src/boot/caps.rs @@ -7,6 +7,7 @@ pub fn grant_all( init_pid: ProcessId, storage_pid: ProcessId, display_pid: ProcessId, + input_pid: ProcessId, ) { let all_perms = types::permission::PermissionSet::empty() .with(types::permission::Permission::Read) @@ -22,7 +23,9 @@ pub fn grant_all( let storage_ep = kernel_ref().create_endpoint() .unwrap_or_else(|_| crate::halt(uart, "storage endpoint failed")); - // Init: slot 0 = ProcessCreate (from boot), slot 1 = Process(storage), slot 2 = Endpoint(storage) + // Init: slot 0 = ProcessCreate (from boot) + // slot 1 = Process(storage) + // slot 2 = Endpoint(storage) kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(storage_pid), all_perms) .unwrap_or_else(|_| crate::halt(uart, "grant failed")); kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(storage_ep), all_perms) @@ -46,5 +49,19 @@ pub fn grant_all( kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(display_pid), all_perms) .unwrap_or_else(|_| crate::halt(uart, "grant failed")); + // Input endpoint + let input_ep = kernel_ref().create_endpoint() + .unwrap_or_else(|_| crate::halt(uart, "input endpoint failed")); + + // Input: slot 0 = Endpoint + kernel_ref().grant_capability(input_pid, domain::resource::Resource::Endpoint(input_ep), ipc_perms) + .unwrap_or_else(|_| crate::halt(uart, "grant failed")); + + // Init: slot 5 = Endpoint(input), slot 6 = Process(input) + kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(input_ep), all_perms) + .unwrap_or_else(|_| crate::halt(uart, "grant failed")); + kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(input_pid), all_perms) + .unwrap_or_else(|_| crate::halt(uart, "grant failed")); + uart.write_str("k-os: capabilities granted\n"); } diff --git a/crates/adapter-riscv/src/devices/gpu_setup.rs b/crates/adapter-riscv/src/devices/gpu_setup.rs index 8b21a13..bef1cf8 100644 --- a/crates/adapter-riscv/src/devices/gpu_setup.rs +++ b/crates/adapter-riscv/src/devices/gpu_setup.rs @@ -1,6 +1,7 @@ use crate::hw::uart::Uart; use crate::hw::virtio_gpu::VirtioGpu; use crate::{kernel_ref, mmu}; +use types::display::Dimension; use types::id::ProcessId; const GPU_VA: usize = 0x20000; @@ -8,8 +9,8 @@ const VQUEUE_VA: usize = 0x21000; const CMDBUF_VA: usize = 0x23000; const PHYSINFO_VA: usize = 0x24000; const FB_VA: usize = 0x100000; -const FB_WIDTH: types::display::Dimension = types::display::Dimension::new(640); -const FB_HEIGHT: types::display::Dimension = types::display::Dimension::new(480); +const FB_WIDTH: Dimension = Dimension::new(640); +const FB_HEIGHT: Dimension = Dimension::new(480); const FB_BPP: usize = 4; const FB_PAGES: usize = (FB_WIDTH.raw() as usize * FB_HEIGHT.raw() as usize * FB_BPP + 4095) / 4096; @@ -48,10 +49,155 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) { } gpu.init_device(vq_phys); - gpu.setup_framebuffer(cmd_phys, vq_phys, fb_phys, FB_WIDTH, FB_HEIGHT); + + // Raw GPU setup — bypass hw-virtio to debug + let w = FB_WIDTH.raw() as u32; + let h = FB_HEIGHT.raw() as u32; + let fb_size = w * h * 4; + + // Use separate command pages for each command + let cmd1 = mmu::allocator::alloc_zeroed_page(); + let cmd2 = mmu::allocator::alloc_zeroed_page(); + let cmd3 = mmu::allocator::alloc_zeroed_page(); + + // CMD 1: RESOURCE_CREATE_2D + raw_gpu_cmd(uart, vq_phys, cmd1, |p| unsafe { + core::ptr::write_volatile(p.add(0) as *mut u32, 0x0101); // type + core::ptr::write_volatile(p.add(24) as *mut u32, 1); // resource_id + core::ptr::write_volatile(p.add(28) as *mut u32, 1); // format B8G8R8A8 + core::ptr::write_volatile(p.add(32) as *mut u32, w); + core::ptr::write_volatile(p.add(36) as *mut u32, h); + }, 40, "create_2d"); + + // CMD 2: RESOURCE_ATTACH_BACKING (type swapped due to QEMU 11.x bug) + raw_gpu_cmd(uart, vq_phys, cmd2, |p| unsafe { + core::ptr::write_volatile(p.add(0) as *mut u32, 0x0106); + core::ptr::write_volatile(p.add(24) as *mut u32, 1); // resource_id + core::ptr::write_volatile(p.add(28) as *mut u32, 1); // nr_entries + 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); + }, 48, "attach"); + + // CMD 3: SET_SCANOUT + raw_gpu_cmd(uart, vq_phys, cmd3, |p| unsafe { + core::ptr::write_volatile(p.add(0) as *mut u32, 0x0103); // type + core::ptr::write_volatile(p.add(24) as *mut u32, 0); // rect.x + core::ptr::write_volatile(p.add(28) as *mut u32, 0); // rect.y + core::ptr::write_volatile(p.add(32) as *mut u32, w); // rect.width + core::ptr::write_volatile(p.add(36) as *mut u32, h); // rect.height + core::ptr::write_volatile(p.add(40) as *mut u32, 0); // scanout_id + core::ptr::write_volatile(p.add(44) as *mut u32, 1); // resource_id + }, 48, "scanout"); + + // Fill framebuffer with blue + let fb = fb_phys as *mut u32; + let total = w as usize * h as usize; + for i in 0..total { + unsafe { core::ptr::write_volatile(fb.add(i), 0xFF4040FF); } + } + + // Transfer + flush using shared cmd buffer + raw_gpu_cmd(uart, vq_phys, cmd_phys, |p| unsafe { + core::ptr::write_volatile(p.add(0) as *mut u32, 0x0105); // TRANSFER_TO_HOST_2D + core::ptr::write_volatile(p.add(24) as *mut u32, 0); // rect.x + core::ptr::write_volatile(p.add(28) as *mut u32, 0); // rect.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); // offset + core::ptr::write_volatile(p.add(48) as *mut u32, 1); // resource_id + }, 56, "xfer"); + + raw_gpu_cmd(uart, vq_phys, cmd_phys, |p| unsafe { + core::ptr::write_volatile(p.add(0) as *mut u32, 0x0104); // RESOURCE_FLUSH (swapped for QEMU 11.x) + core::ptr::write_volatile(p.add(24) as *mut u32, 0); + core::ptr::write_volatile(p.add(28) as *mut u32, 0); + 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); // resource_id + }, 48, "flush"); + uart.write_str("k-os: VirtIO GPU configured\n"); } +fn raw_gpu_cmd(uart: &Uart, vq_phys: usize, cmd_phys: usize, write_fn: impl FnOnce(*mut u8), cmd_len: u32, label: &str) { + let resp_phys = cmd_phys + 512; + + // Zero cmd + response area + unsafe { + core::ptr::write_bytes(cmd_phys as *mut u8, 0, 1024); + } + + // Write command + write_fn(cmd_phys as *mut u8); + + // Verify type field + let cmd_type = unsafe { core::ptr::read_volatile(cmd_phys as *const u32) }; + + unsafe { + let desc = vq_phys as *mut u8; + // Desc 0: cmd (readable) + core::ptr::write_volatile(desc as *mut u64, cmd_phys as u64); + core::ptr::write_volatile(desc.add(8) as *mut u32, cmd_len); + core::ptr::write_volatile(desc.add(12) as *mut u16, 1); // NEXT + core::ptr::write_volatile(desc.add(14) as *mut u16, 1); + // Desc 1: response (writable) + let desc1 = desc.add(16); + core::ptr::write_volatile(desc1 as *mut u64, resp_phys as u64); + core::ptr::write_volatile(desc1.add(8) as *mut u32, 24); + core::ptr::write_volatile(desc1.add(12) as *mut u16, 2); // WRITE + core::ptr::write_volatile(desc1.add(14) as *mut u16, 0); + + let avail = vq_phys + 128; // 8 descs * 16 bytes + let idx = core::ptr::read_volatile((avail + 2) as *const u16); + core::ptr::write_volatile((avail + 4 + (idx as usize % 8) * 2) as *mut u16, 0); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + core::ptr::write_volatile((avail + 2) as *mut u16, idx.wrapping_add(1)); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + + let gpu_mmio = hw_virtio::mmio::DirectMmio::new( + crate::hw::virtio_gpu::VirtioGpu::probe().unwrap().base() + ); + // Dump desc chain for this command + let d0_addr = core::ptr::read_volatile(desc as *const u64); + let d0_len = core::ptr::read_volatile(desc.add(8) as *const u32); + let d0_flags = core::ptr::read_volatile(desc.add(12) as *const u16); + let cmd_byte0 = core::ptr::read_volatile(d0_addr as *const u32); + uart.write_str("k-os: desc0 addr="); + uart.write_hex(d0_addr as usize); + uart.write_str(" len="); + uart.write_dec(d0_len as usize); + uart.write_str(" cmd[0]="); + uart.write_hex(cmd_byte0 as usize); + uart.put_char(b'\n'); + + use hw_virtio::mmio::MmioAccess; + gpu_mmio.write(0x050, 0); + + let used = vq_phys + 4096; + let expected = 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 { + uart.write_str("k-os: "); + uart.write_str(label); + uart.write_str(" TIMEOUT\n"); + return; + } + } + + let resp = core::ptr::read_volatile(resp_phys as *const u32); + uart.write_str("k-os: "); + uart.write_str(label); + uart.write_str(" type="); + uart.write_hex(cmd_type as usize); + uart.write_str(" resp="); + uart.write_hex(resp as usize); + uart.put_char(b'\n'); + } +} + fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) { mmu::map(pt, mmu::Mapping { va: types::address::VirtualAddress::from_raw(va), diff --git a/crates/adapter-riscv/src/devices/input_setup.rs b/crates/adapter-riscv/src/devices/input_setup.rs new file mode 100644 index 0000000..a14a932 --- /dev/null +++ b/crates/adapter-riscv/src/devices/input_setup.rs @@ -0,0 +1,19 @@ +use crate::hw::uart::Uart; +use crate::{board, kernel_ref, mmu}; +use types::id::ProcessId; + +const UART_VA: usize = 0x20000; + +pub fn configure(uart: &Uart, input_pid: ProcessId) { + let pt_addr = kernel_ref().platform_mut().page_table_for(input_pid); + let pt = unsafe { &mut *(pt_addr as *mut mmu::PageTable) }; + + mmu::map(pt, mmu::Mapping { + va: types::address::VirtualAddress::from_raw(UART_VA), + pa: types::address::PhysicalAddress::from_raw(board::UART_BASE), + pages: 1, + flags: mmu::Sv39Flags::user_rw(), + }); + + uart.write_str("k-os: UART mapped for input\n"); +} diff --git a/crates/adapter-riscv/src/devices/mod.rs b/crates/adapter-riscv/src/devices/mod.rs index 5fc44ec..2d23177 100644 --- a/crates/adapter-riscv/src/devices/mod.rs +++ b/crates/adapter-riscv/src/devices/mod.rs @@ -1,2 +1,3 @@ pub mod gpu_setup; +pub mod input_setup; pub mod storage_setup; diff --git a/crates/adapter-riscv/src/hw/virtio_gpu.rs b/crates/adapter-riscv/src/hw/virtio_gpu.rs index eadc4c0..64aeacb 100644 --- a/crates/adapter-riscv/src/hw/virtio_gpu.rs +++ b/crates/adapter-riscv/src/hw/virtio_gpu.rs @@ -18,19 +18,24 @@ impl VirtioGpu { self.mmio.base() } + pub fn mmio(&self) -> &DirectMmio { + &self.mmio + } + pub fn init_device(&self, vqueue_phys: usize) { mmio::init_legacy_device(&self.mmio, vqueue_phys, QUEUE_SIZE as u32); } - pub fn setup_framebuffer(&self, cmd_phys: usize, vq_phys: usize, fb_phys: usize, width: Dimension, height: Dimension) { + pub fn setup_framebuffer(&self, cmd_phys: usize, vq_phys: usize, fb_phys: usize, width: Dimension, height: Dimension) -> bool { let queue = VirtioQueue::new(QueueAddrs::from_base(vq_phys)); let dma = GpuDmaAddrs { cmd_pa: PhysicalAddress::from_raw(cmd_phys), cmd_va: VirtualAddress::from_raw(cmd_phys), resp_pa: PhysicalAddress::from_raw(cmd_phys + 512), }; - gpu::create_2d(&self.mmio, &queue, &dma, 1, width, height); - gpu::attach_backing(&self.mmio, &queue, &dma, 1, PhysicalAddress::from_raw(fb_phys), width.raw() as u32 * height.raw() as u32 * 4); - gpu::set_scanout(&self.mmio, &queue, &dma, 0, 1, width, height); + let ok1 = gpu::create_2d(&self.mmio, &queue, &dma, 1, width, height); + let ok2 = gpu::attach_backing(&self.mmio, &queue, &dma, 1, PhysicalAddress::from_raw(fb_phys), width.raw() as u32 * height.raw() as u32 * 4); + let ok3 = gpu::set_scanout(&self.mmio, &queue, &dma, 0, 1, width, height); + ok1 && ok2 && ok3 } } diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs index 3faa0a9..bcd2bc5 100644 --- a/crates/adapter-riscv/src/main.rs +++ b/crates/adapter-riscv/src/main.rs @@ -48,12 +48,14 @@ extern "C" fn kmain() -> ! { let (init, _) = boot::loader::load_init(&uart, &mut blk, kernel_pt); let storage = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"storage.elf", "storage"); let display = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"display.elf", "display"); + let input = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"input.elf", "input"); let virtio_base = blk.base(); drop(blk); devices::storage_setup::configure(&uart, init.pid, storage.pid, virtio_base); devices::gpu_setup::configure(&uart, display.pid); - boot::caps::grant_all(&uart, init.pid, storage.pid, display.pid); + devices::input_setup::configure(&uart, input.pid); + boot::caps::grant_all(&uart, init.pid, storage.pid, display.pid, input.pid); uart.write_str("k-os: entering userspace\n"); boot::loader::enter_userspace(init.pid, init.entry, SHARED_BUF_VA); diff --git a/crates/bins/display-qemu/Cargo.toml b/crates/bins/display-qemu/Cargo.toml index a4e7dbe..eca0eff 100644 --- a/crates/bins/display-qemu/Cargo.toml +++ b/crates/bins/display-qemu/Cargo.toml @@ -15,3 +15,4 @@ userspace = { workspace = true } server-framework = { workspace = true } display-server = { workspace = true } driver-framebuffer-virtio = { workspace = true } +port-framebuffer = { workspace = true } diff --git a/crates/bins/display-qemu/src/main.rs b/crates/bins/display-qemu/src/main.rs index 271f5ea..e94e085 100644 --- a/crates/bins/display-qemu/src/main.rs +++ b/crates/bins/display-qemu/src/main.rs @@ -8,7 +8,20 @@ use types::bounded::CapSlot; #[unsafe(no_mangle)] extern "C" fn main() { - let fb = VirtioFramebuffer::new(); + let mut fb = VirtioFramebuffer::new(); + + use port_framebuffer::Framebuffer; + use types::display::{Color, Dimension, ScreenCoord}; + + // Initial clear to dark blue so we know the GPU works + fb.clear(Color::rgb(0, 0, 40)); + fb.fill_rect( + ScreenCoord::new(10), ScreenCoord::new(10), + Dimension::new(200), Dimension::new(30), + Color::rgb(255, 255, 255), + ); + fb.flush(); + let mut srv = display_server::server::DisplayServer::new(fb); let ep = CapSlot::new(0).unwrap(); server_framework::serve(&mut srv, ep); diff --git a/crates/bins/input-qemu/Cargo.toml b/crates/bins/input-qemu/Cargo.toml new file mode 100644 index 0000000..3775cfc --- /dev/null +++ b/crates/bins/input-qemu/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "input-qemu" +version = "0.1.0" +edition = "2024" + +[[bin]] +name = "input-qemu" +test = false +bench = false + +[dependencies] +types = { workspace = true } +userspace-rt = { workspace = true } +userspace = { workspace = true } +server-framework = { workspace = true } +input-server = { workspace = true } +driver-input-uart = { workspace = true } diff --git a/crates/bins/input-qemu/src/main.rs b/crates/bins/input-qemu/src/main.rs new file mode 100644 index 0000000..7c0cd29 --- /dev/null +++ b/crates/bins/input-qemu/src/main.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate userspace_rt; + +use driver_input_uart::UartInputDevice; +use types::bounded::CapSlot; + +#[unsafe(no_mangle)] +extern "C" fn main() { + let device = UartInputDevice::new(); + let mut srv = input_server::server::InputServer::new(device); + let ep = CapSlot::new(0).unwrap(); + server_framework::serve(&mut srv, ep); + loop { userspace::syscall::yield_now(); } +} diff --git a/crates/drivers/input/uart/Cargo.toml b/crates/drivers/input/uart/Cargo.toml new file mode 100644 index 0000000..0c93a93 --- /dev/null +++ b/crates/drivers/input/uart/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "driver-input-uart" +version = "0.1.0" +edition = "2024" + +[dependencies] +port-input-device = { workspace = true } diff --git a/crates/drivers/input/uart/src/lib.rs b/crates/drivers/input/uart/src/lib.rs new file mode 100644 index 0000000..d66e285 --- /dev/null +++ b/crates/drivers/input/uart/src/lib.rs @@ -0,0 +1,47 @@ +#![no_std] + +use port_input_device::{InputDevice, InputEvent, Key}; + +const UART_VA: usize = 0x20000; +const UART_LSR_OFFSET: usize = 5; + +pub struct UartInputDevice; + +impl UartInputDevice { + pub fn new() -> Self { + Self + } + + fn read_byte(&self) -> Option { + let lsr = unsafe { core::ptr::read_volatile((UART_VA + UART_LSR_OFFSET) as *const u8) }; + if lsr & 1 != 0 { + Some(unsafe { core::ptr::read_volatile(UART_VA as *const u8) }) + } else { + None + } + } + + fn byte_to_key(byte: u8) -> Key { + match byte { + b'w' | b'W' => Key::Up, + b's' | b'S' => Key::Down, + b'a' | b'A' => Key::Left, + b'd' | b'D' => Key::Right, + b'\r' | b'\n' => Key::Enter, + 0x1B => Key::Escape, + c => Key::Char(c), + } + } +} + +impl InputDevice for UartInputDevice { + fn poll(&mut self) -> Option { + let byte = self.read_byte()?; + Some(InputEvent::KeyDown(Self::byte_to_key(byte))) + } + + fn has_event(&self) -> bool { + let lsr = unsafe { core::ptr::read_volatile((UART_VA + UART_LSR_OFFSET) as *const u8) }; + lsr & 1 != 0 + } +} diff --git a/crates/hw-virtio/src/gpu.rs b/crates/hw-virtio/src/gpu.rs index 95ebb1c..3cd0248 100644 --- a/crates/hw-virtio/src/gpu.rs +++ b/crates/hw-virtio/src/gpu.rs @@ -9,20 +9,21 @@ pub struct GpuDmaAddrs { pub resp_pa: PhysicalAddress, } -pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) { +pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) -> bool { write_cmd_header(dma.cmd_va, 0x0101); unsafe { let p = dma.cmd_va.raw() 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(28) as *mut u32, 1); core::ptr::write_volatile(p.add(32) as *mut u32, width.raw() as u32); core::ptr::write_volatile(p.add(36) as *mut u32, height.raw() as u32); } - submit_cmd(mmio, queue, dma, 40); + submit_cmd(mmio, queue, dma, 40) } -pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) { - write_cmd_header(dma.cmd_va, 0x0104); +pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool { + // QEMU 11.x has ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) swapped + write_cmd_header(dma.cmd_va, 0x0106); unsafe { let p = dma.cmd_va.raw() as *mut u8; core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); @@ -30,10 +31,10 @@ pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaA core::ptr::write_volatile(p.add(32) as *mut u64, fb_pa.raw() as u64); core::ptr::write_volatile(p.add(40) as *mut u32, fb_size); } - submit_cmd(mmio, queue, dma, 48); + submit_cmd(mmio, queue, dma, 48) } -pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: Dimension, height: Dimension) { +pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: Dimension, height: Dimension) -> bool { write_cmd_header(dma.cmd_va, 0x0103); unsafe { let p = dma.cmd_va.raw() as *mut u8; @@ -42,10 +43,10 @@ pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddr 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); + 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) { +pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { write_cmd_header(dma.cmd_va, 0x0105); unsafe { let p = dma.cmd_va.raw() as *mut u8; @@ -56,11 +57,12 @@ pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm 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); + 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); +pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { + // QEMU 11.x has ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) swapped + write_cmd_header(dma.cmd_va, 0x0104); unsafe { let p = dma.cmd_va.raw() as *mut u8; core::ptr::write_volatile(p.add(24) as *mut u32, x); @@ -69,21 +71,24 @@ pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaA 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); + submit_cmd(mmio, queue, dma, 48) } fn write_cmd_header(cmd_va: VirtualAddress, cmd_type: u32) { unsafe { let p = cmd_va.raw() as *mut u8; - core::ptr::write_bytes(p, 0, 512); + // Use volatile writes to zero the buffer — ensures device sees the zeros + for i in 0..128 { + core::ptr::write_volatile((p as *mut u32).add(i), 0); + } core::ptr::write_volatile(p as *mut u32, cmd_type); } } -fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) { +fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) -> bool { let descs = [ Descriptor { addr: dma.cmd_pa.raw() as u64, len: cmd_len, writable: false }, Descriptor { addr: dma.resp_pa.raw() as u64, len: 24, writable: true }, ]; - queue.submit(mmio, &descs); + queue.submit(mmio, &descs) } diff --git a/crates/init/src/lib.rs b/crates/init/src/lib.rs index fd8f4ab..b85b9c0 100644 --- a/crates/init/src/lib.rs +++ b/crates/init/src/lib.rs @@ -13,6 +13,8 @@ const CMD_READ_FILE: usize = 1; fn create_cap() -> CapSlot { CapSlot::new(0).unwrap() } fn storage_ep() -> CapSlot { CapSlot::new(2).unwrap() } +fn display_ep() -> CapSlot { CapSlot::new(3).unwrap() } +fn input_ep() -> CapSlot { CapSlot::new(5).unwrap() } fn debug_print(s: &[u8]) { for &b in s { @@ -124,11 +126,9 @@ struct ServerInfo { } const SERVERS: &[ServerInfo] = &[ - ServerInfo { filename: b"input.elf", name: b"input" }, ServerInfo { filename: b"log.elf", name: b"log" }, ServerInfo { filename: b"permis.elf", name: b"permis" }, ServerInfo { filename: b"session.elf", name: b"session" }, - ServerInfo { filename: b"snake.elf", name: b"snake" }, ]; pub fn run(shared_buf_va: usize) -> ! { @@ -165,6 +165,17 @@ pub fn run(shared_buf_va: usize) -> ! { debug_print(b")\n"); } + // Load snake — give it display endpoint (slot 0) and input endpoint (slot 1) + debug_print(b"init: loading snake... "); + if let Some(snake) = load_binary(shared_buf, b"snake.elf") { + syscall::delegate(display_ep(), snake.process_cap, ipc_perms); + syscall::delegate(input_ep(), snake.process_cap, ipc_perms); + syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP); + debug_print(b"ok\n"); + } else { + debug_print(b"SKIP\n"); + } + debug_print(b"init: all loaded, supervising\n"); loop { diff --git a/scripts/make-disk-image.sh b/scripts/make-disk-image.sh index fefb503..6591a4e 100755 --- a/scripts/make-disk-image.sh +++ b/scripts/make-disk-image.sh @@ -25,7 +25,7 @@ copy init "init.elf" copy echo "echo.elf" copy snake "snake.elf" copy display-qemu "display.elf" -copy input-server "input.elf" +copy input-qemu "input.elf" copy log-server "log.elf" copy permission-manager "permis.elf" copy storage-qemu "storage.elf"