diff --git a/Cargo.lock b/Cargo.lock index 7c8e16e..fd3c691 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,15 @@ dependencies = [ "port-input-device", ] +[[package]] +name = "driver-input-virtio" +version = "0.1.0" +dependencies = [ + "hw-virtio", + "port-input-device", + "types", +] + [[package]] name = "echo" version = "0.1.0" @@ -164,6 +173,7 @@ name = "input-qemu" version = "0.1.0" dependencies = [ "driver-input-uart", + "driver-input-virtio", "input-server", "server-framework", "types", diff --git a/Cargo.toml b/Cargo.toml index 5d7db8c..b32345e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ members = [ "crates/bins/input-qemu", "crates/bins/storage-qemu", "crates/drivers/input/uart", + "crates/drivers/input/virtio", "crates/hw-virtio", ] resolver = "2" @@ -108,6 +109,7 @@ 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" } +driver-input-virtio = { path = "crates/drivers/input/virtio" } # apps snake = { path = "crates/apps/snake" } diff --git a/Makefile b/Makefile index 0d02a7b..e679cfc 100644 --- a/Makefile +++ b/Makefile @@ -52,5 +52,6 @@ run-gui: image -kernel $(KERNEL_BIN) \ -drive file=boot.img,if=none,format=raw,id=hd0 \ -device virtio-blk-device,drive=hd0 \ - -device virtio-gpu-device \ - -serial stdio + -device virtio-gpu-device \ + -device virtio-keyboard-device \ + -serial stdio diff --git a/crates/adapter-riscv/src/devices/input_setup.rs b/crates/adapter-riscv/src/devices/input_setup.rs index cc07297..ef875a2 100644 --- a/crates/adapter-riscv/src/devices/input_setup.rs +++ b/crates/adapter-riscv/src/devices/input_setup.rs @@ -3,18 +3,66 @@ use crate::{board, mmu}; use types::id::ProcessId; const UART_VA: usize = 0x20000; +const INPUT_MMIO_VA: usize = 0x25000; +const INPUT_VQUEUE_VA: usize = 0x26000; +const INPUT_EVBUF_VA: usize = 0x28000; +const INPUT_PHYSINFO_VA: usize = 0x29000; + +fn kern() -> &'static mut kernel::Kernel { + unsafe { &mut *crate::kernel_ptr() } +} pub fn configure(uart: &Uart, input_pid: ProcessId) { - let kern = unsafe { &mut *crate::kernel_ptr() }; - let pt_addr = kern.platform_mut().page_table_for(input_pid); + let pt_addr = kern().platform_mut().page_table_for(input_pid); let pt = unsafe { &mut *(pt_addr as *mut mmu::PageTable) }; + // Always map UART for fallback + map_page(pt, UART_VA, board::UART_BASE); + + // Try to find VirtIO input device + if let Some(mmio) = hw_virtio::mmio::scan_for_device(hw_virtio::mmio::DEVICE_ID_INPUT) { + let base = mmio.base(); + map_page(pt, INPUT_MMIO_VA, base); + + let vq_phys = alloc_contiguous(2); + map_range(pt, INPUT_VQUEUE_VA, vq_phys, 2); + + let evbuf_phys = mmu::allocator::alloc_zeroed_page(); + map_page(pt, INPUT_EVBUF_VA, evbuf_phys); + + let info_phys = mmu::allocator::alloc_zeroed_page(); + map_page(pt, INPUT_PHYSINFO_VA, info_phys); + unsafe { + core::ptr::write_volatile(info_phys as *mut u64, evbuf_phys as u64); + core::ptr::write_volatile((info_phys as *mut u64).add(1), vq_phys as u64); + } + + hw_virtio::mmio::init_legacy_device(&mmio, vq_phys, 32); + uart.write_str("k-os: VirtIO keyboard configured\n"); + } else { + uart.write_str("k-os: UART mapped for input\n"); + } +} + +fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) { mmu::map(pt, mmu::Mapping { - va: types::address::VirtualAddress::from_raw(UART_VA), - pa: types::address::PhysicalAddress::from_raw(board::UART_BASE), + va: types::address::VirtualAddress::from_raw(va), + pa: types::address::PhysicalAddress::from_raw(pa), pages: 1, flags: mmu::Sv39Flags::user_rw(), }); - - uart.write_str("k-os: UART mapped for input\n"); +} + +fn map_range(pt: &mut mmu::PageTable, va_base: usize, pa_base: usize, count: usize) { + for i in 0..count { + map_page(pt, va_base + i * 4096, pa_base + i * 4096); + } +} + +fn alloc_contiguous(pages: usize) -> usize { + let first = mmu::allocator::alloc_zeroed_page(); + for _ in 1..pages { + mmu::allocator::alloc_zeroed_page(); + } + first } diff --git a/crates/bins/input-qemu/Cargo.toml b/crates/bins/input-qemu/Cargo.toml index 3775cfc..9af6057 100644 --- a/crates/bins/input-qemu/Cargo.toml +++ b/crates/bins/input-qemu/Cargo.toml @@ -15,3 +15,4 @@ userspace = { workspace = true } server-framework = { workspace = true } input-server = { workspace = true } driver-input-uart = { workspace = true } +driver-input-virtio = { workspace = true } diff --git a/crates/bins/input-qemu/src/main.rs b/crates/bins/input-qemu/src/main.rs index 7c0cd29..42faa40 100644 --- a/crates/bins/input-qemu/src/main.rs +++ b/crates/bins/input-qemu/src/main.rs @@ -3,14 +3,30 @@ 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); + + if let Some(dev) = driver_input_virtio::VirtioInputDevice::try_new() { + run_input(dev, ep); + } else { + run_uart(ep); + } + loop { userspace::syscall::yield_now(); } } + +#[inline(never)] +fn run_input(device: driver_input_virtio::VirtioInputDevice, ep: CapSlot) { + let mut srv = input_server::server::InputServer::new(device); + server_framework::serve(&mut srv, ep); +} + +#[inline(never)] +fn run_uart(ep: CapSlot) { + let device = driver_input_uart::UartInputDevice::new(); + let mut srv = input_server::server::InputServer::new(device); + server_framework::serve(&mut srv, ep); +} diff --git a/crates/drivers/input/virtio/Cargo.toml b/crates/drivers/input/virtio/Cargo.toml new file mode 100644 index 0000000..cfe2bf1 --- /dev/null +++ b/crates/drivers/input/virtio/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "driver-input-virtio" +version = "0.1.0" +edition = "2024" + +[dependencies] +port-input-device = { workspace = true } +hw-virtio = { workspace = true } +types = { workspace = true } diff --git a/crates/drivers/input/virtio/src/lib.rs b/crates/drivers/input/virtio/src/lib.rs new file mode 100644 index 0000000..ed4240c --- /dev/null +++ b/crates/drivers/input/virtio/src/lib.rs @@ -0,0 +1,159 @@ +#![no_std] + +use hw_virtio::mmio::MmioAccess; +use port_input_device::{InputDevice, InputEvent, Key}; + +const INPUT_MMIO_VA: usize = 0x25000; +const VQUEUE_VA: usize = 0x26000; +const EVBUF_VA: usize = 0x28000; +const PHYSINFO_VA: usize = 0x29000; + +const QUEUE_SIZE: usize = 32; +const EVENT_SIZE: usize = 8; + +const EV_KEY: u16 = 1; +const KEY_PRESS: u32 = 1; + +struct MmioInput; + +impl hw_virtio::mmio::MmioAccess for MmioInput { + fn read(&self, offset: usize) -> u32 { + unsafe { core::ptr::read_volatile((INPUT_MMIO_VA + offset) as *const u32) } + } + fn write(&self, offset: usize, val: u32) { + unsafe { core::ptr::write_volatile((INPUT_MMIO_VA + offset) as *mut u32, val) } + } +} + +pub struct VirtioInputDevice { + last_used: u16, + next_avail: u16, +} + +impl VirtioInputDevice { + pub fn try_new() -> Option { + let phys = unsafe { core::ptr::read_volatile(PHYSINFO_VA as *const u64) }; + if phys == 0 { return None; } + + let mut dev = Self { last_used: 0, next_avail: 0 }; + dev.fill_queue(); + Some(dev) + } + + fn fill_queue(&mut self) { + let evbuf_phys = unsafe { core::ptr::read_volatile(PHYSINFO_VA as *const u64) } as usize; + let desc_base = VQUEUE_VA; + let avail_base = VQUEUE_VA + QUEUE_SIZE * 16; + + for i in 0..QUEUE_SIZE { + let desc = desc_base + i * 16; + let buf_pa = evbuf_phys + i * EVENT_SIZE; + unsafe { + core::ptr::write_volatile(desc as *mut u64, buf_pa as u64); + core::ptr::write_volatile((desc + 8) as *mut u32, EVENT_SIZE as u32); + core::ptr::write_volatile((desc + 12) as *mut u16, 2); // WRITE flag + core::ptr::write_volatile((desc + 14) as *mut u16, 0); // no next + } + + unsafe { + let ring_entry = avail_base + 4 + (i % QUEUE_SIZE) * 2; + core::ptr::write_volatile(ring_entry as *mut u16, i as u16); + } + } + + unsafe { + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + core::ptr::write_volatile((avail_base + 2) as *mut u16, QUEUE_SIZE as u16); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + } + + let mmio = MmioInput; + mmio.write(hw_virtio::mmio::REG_QUEUE_NOTIFY, 0); + self.next_avail = QUEUE_SIZE as u16; + } + + fn resubmit(&mut self, desc_idx: u16) { + let avail_base = VQUEUE_VA + QUEUE_SIZE * 16; + let slot = self.next_avail as usize % QUEUE_SIZE; + unsafe { + core::ptr::write_volatile((avail_base + 4 + slot * 2) as *mut u16, desc_idx); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + self.next_avail = self.next_avail.wrapping_add(1); + core::ptr::write_volatile((avail_base + 2) as *mut u16, self.next_avail); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + } + let mmio = MmioInput; + mmio.write(hw_virtio::mmio::REG_QUEUE_NOTIFY, 0); + } + + fn read_event(&self, desc_idx: u16) -> (u16, u16, u32) { + let buf_va = EVBUF_VA + (desc_idx as usize) * EVENT_SIZE; + unsafe { + let ev_type = core::ptr::read_volatile(buf_va as *const u16); + let ev_code = core::ptr::read_volatile((buf_va + 2) as *const u16); + let ev_value = core::ptr::read_volatile((buf_va + 4) as *const u32); + (ev_type, ev_code, ev_value) + } + } + + fn used_idx(&self) -> u16 { + let used_base = VQUEUE_VA + 4096; + unsafe { core::ptr::read_volatile((used_base + 2) as *const u16) } + } + + fn used_entry_id(&self, idx: u16) -> u16 { + let used_base = VQUEUE_VA + 4096; + let entry = used_base + 4 + (idx as usize % QUEUE_SIZE) * 8; + unsafe { core::ptr::read_volatile(entry as *const u32) as u16 } + } +} + +fn linux_keycode_to_key(code: u16) -> Option { + match code { + 17 => Some(Key::Up), // KEY_W + 31 => Some(Key::Down), // KEY_S + 30 => Some(Key::Left), // KEY_A + 32 => Some(Key::Right), // KEY_D + 103 => Some(Key::Up), // KEY_UP + 108 => Some(Key::Down), // KEY_DOWN + 105 => Some(Key::Left), // KEY_LEFT + 106 => Some(Key::Right),// KEY_RIGHT + 28 => Some(Key::Enter), // KEY_ENTER + 1 => Some(Key::Escape), // KEY_ESC + 57 => Some(Key::Space), // KEY_SPACE + 14 => Some(Key::Backspace), // KEY_BACKSPACE + 15 => Some(Key::Tab), // KEY_TAB + _ => None, + } +} + +impl InputDevice for VirtioInputDevice { + fn poll(&mut self) -> Option { + let current_used = self.used_idx(); + if current_used == self.last_used { + return None; + } + + let desc_idx = self.used_entry_id(self.last_used); + self.last_used = self.last_used.wrapping_add(1); + + let (ev_type, ev_code, ev_value) = self.read_event(desc_idx); + self.resubmit(desc_idx); + + if ev_type == EV_KEY { + if let Some(key) = linux_keycode_to_key(ev_code) { + if ev_value == KEY_PRESS { + return Some(InputEvent::KeyDown(key)); + } else if ev_value == 0 { + return Some(InputEvent::KeyUp(key)); + } + } + } + + None + } + + fn has_event(&self) -> bool { + self.used_idx() != self.last_used + } +} diff --git a/crates/hw-virtio/src/mmio.rs b/crates/hw-virtio/src/mmio.rs index f106378..9143689 100644 --- a/crates/hw-virtio/src/mmio.rs +++ b/crates/hw-virtio/src/mmio.rs @@ -26,6 +26,7 @@ pub const STATUS_DRIVER_OK: u32 = 4; pub const DEVICE_ID_BLOCK: u32 = 2; pub const DEVICE_ID_GPU: u32 = 16; +pub const DEVICE_ID_INPUT: u32 = 18; pub const PAGE_SIZE: usize = 4096;