From 1205d8ce7ae26e796522a18ebd2427c07f9fb2bc Mon Sep 17 00:00:00 2001 From: csd4ni3l <96988024+csd4ni3l@users.noreply.github.com> Date: Sun, 17 May 2026 20:32:42 +0200 Subject: [PATCH] Add virtio driver for aarch64 with only input for now, move keyboard, mouse and other io logic to an io folder, make ps2 convert to linux event codes like virtio, force modern mmio and gic version 2 on aarch64 --- GNUmakefile | 6 +- kernel/build.rs | 3 +- kernel/src/arch/aarch64/init.rs | 5 +- kernel/src/arch/aarch64/interrupts.rs | 31 ++- kernel/src/arch/aarch64/paging.rs | 4 +- kernel/src/arch/syscall.rs | 14 +- kernel/src/arch/x86_64/init.rs | 2 +- kernel/src/arch/x86_64/interrupts.rs | 3 +- kernel/src/config.rs | 3 +- kernel/src/driver/{ => io}/fs/mod.rs | 0 kernel/src/driver/{ => io}/fs/vfs.rs | 6 +- kernel/src/driver/io/keyboard.rs | 192 +++++++++++++ kernel/src/driver/io/mod.rs | 6 + kernel/src/driver/io/mouse.rs | 45 ++++ kernel/src/driver/io/ps2.rs | 325 +++++++++++++++++++++++ kernel/src/driver/io/virtio/input.rs | 275 +++++++++++++++++++ kernel/src/driver/io/virtio/mod.rs | 141 ++++++++++ kernel/src/driver/io/virtio/queue.rs | 124 +++++++++ kernel/src/driver/io/virtio/transport.rs | 49 ++++ kernel/src/driver/keyboard.rs | 99 ------- kernel/src/driver/kmi.rs | 190 ------------- kernel/src/driver/mod.rs | 4 +- kernel/src/main.rs | 65 +++-- kernel/src/task/process.rs | 3 +- user/apps/doomgeneric | 2 +- user/init | 2 +- user/libxunil | 2 +- 27 files changed, 1256 insertions(+), 345 deletions(-) rename kernel/src/driver/{ => io}/fs/mod.rs (100%) rename kernel/src/driver/{ => io}/fs/vfs.rs (94%) create mode 100644 kernel/src/driver/io/keyboard.rs create mode 100644 kernel/src/driver/io/mod.rs create mode 100644 kernel/src/driver/io/mouse.rs create mode 100644 kernel/src/driver/io/ps2.rs create mode 100644 kernel/src/driver/io/virtio/input.rs create mode 100644 kernel/src/driver/io/virtio/mod.rs create mode 100644 kernel/src/driver/io/virtio/queue.rs create mode 100644 kernel/src/driver/io/virtio/transport.rs delete mode 100644 kernel/src/driver/keyboard.rs delete mode 100644 kernel/src/driver/kmi.rs diff --git a/GNUmakefile b/GNUmakefile index 569a83e..9fc495c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -4,7 +4,7 @@ include config.mk MAKEFLAGS += -rR .SUFFIXES: -override QEMUFLAGS := -m $(MEMORY) +override QEMUFLAGS := -m $(MEMORY) -global virtio-mmio.force-legacy=false override IMAGE_NAME := XunilOS-$(KARCH) .PHONY: all @@ -39,12 +39,14 @@ run-hdd-x86_64: edk2-ovmf $(IMAGE_NAME).hdd .PHONY: run-aarch64 run-aarch64: edk2-ovmf $(IMAGE_NAME).iso qemu-system-$(KARCH) \ - -M virt \ + -M virt,gic-version=2,secure=off \ -cpu cortex-a72 \ -device ramfb \ -device qemu-xhci \ -device usb-kbd \ -device usb-mouse \ + -device virtio-keyboard-device \ + -device virtio-mouse-device \ -serial stdio \ -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-$(KARCH).fd,readonly=on \ -cdrom $(IMAGE_NAME).iso \ diff --git a/kernel/build.rs b/kernel/build.rs index f7c93d1..87dca85 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -6,11 +6,12 @@ fn main() { let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let timer_frequency_hz = env::var("TIMER_FREQUENCY_HZ").unwrap_or_else(|_| "1000".to_string()); + let karch = env::var("KARCH").unwrap_or_else(|_| "x86_64".to_string()); let out_dir = PathBuf::from("src"); fs::write( out_dir.join("config.rs"), - format!(r#"pub const TIMER_FREQUENCY_HZ: usize = {timer_frequency_hz};"#), + format!("pub const TIMER_FREQUENCY_HZ: usize = {timer_frequency_hz};\npub const KARCH: &str = \"{karch}\";"), ) .unwrap(); diff --git a/kernel/src/arch/aarch64/init.rs b/kernel/src/arch/aarch64/init.rs index 74e7445..a6fc9ef 100644 --- a/kernel/src/arch/aarch64/init.rs +++ b/kernel/src/arch/aarch64/init.rs @@ -2,10 +2,10 @@ use crate::{ arch::aarch64::{ heap::init_heap, interrupts::init_interrupts, - kmi::setup_kmi, paging::{AArchPageTable, initialize_paging_aarch64}, }, config::TIMER_FREQUENCY_HZ, + driver::io::virtio::scan_devices, }; use limine::response::{ExecutableAddressResponse, HhdmResponse, MemoryMapResponse}; @@ -17,7 +17,7 @@ pub static KERNEL_STACK: Stack = Stack([0; 64 * 1024]); pub fn set_timer_freq(freq: usize) { unsafe { - core::arch::asm!("mrs {}, cntp_tval_el0", in(reg) freq); + core::arch::asm!("mrs {}, cntp_tval_el0", "isb", in(reg) freq); } } @@ -39,6 +39,7 @@ pub unsafe fn init_aarch64_trampoline(mapper: &mut AArchPageTable) { pub extern "C" fn init_aarch64(mapper: &mut AArchPageTable) { init_heap(mapper); set_timer_freq(TIMER_FREQUENCY_HZ); + scan_devices(); init_interrupts(); } diff --git a/kernel/src/arch/aarch64/interrupts.rs b/kernel/src/arch/aarch64/interrupts.rs index 828ec48..88c6473 100644 --- a/kernel/src/arch/aarch64/interrupts.rs +++ b/kernel/src/arch/aarch64/interrupts.rs @@ -2,10 +2,10 @@ use core::{arch::global_asm, sync::atomic::Ordering}; use crate::{ arch::syscall::syscall_dispatch, + config::TIMER_FREQUENCY_HZ, driver::{ graphics::framebuffer::with_framebuffer, - keyboard::push_scancode, - kmi::{keyboard_interrupt, mouse_interrupt}, + io::virtio::{KEYBOARD_SLOT, MOUSE_SLOT, input::input_interrupt}, serial::with_serial_console, timer::TIMER, }, @@ -161,8 +161,13 @@ pub fn init_interrupts() { pub fn enable_interrupts() { unsafe { gic_enable_interrupt(30); // timer IRQ - gic_enable_interrupt(17); // keyboard IRQ - gic_enable_interrupt(18); // mouse IRQ + + let keyboard_irq = 32 + 0x10 + KEYBOARD_SLOT.load(Ordering::Relaxed); + let mouse_irq = 32 + 0x10 + MOUSE_SLOT.load(Ordering::Relaxed); + + gic_enable_interrupt(keyboard_irq as u32); + gic_enable_interrupt(mouse_irq as u32); + core::arch::asm!("msr cntp_ctl_el0, {}", in(reg) 1u64); // enable timer } DAIF.write(DAIF::D::Masked + DAIF::A::Masked + DAIF::I::Unmasked + DAIF::F::Masked); @@ -183,6 +188,10 @@ unsafe extern "C" fn no_operation(ctx: *mut UserContext) { #[unsafe(no_mangle)] unsafe extern "C" fn irq_handler(ctx: *mut UserContext) { let interrupt_id = unsafe { gic_acknowledge() }; + + let keyboard_irq = 32 + 0x10 + KEYBOARD_SLOT.load(Ordering::Relaxed); + let mouse_irq = 32 + 0x10 + MOUSE_SLOT.load(Ordering::Relaxed); + match interrupt_id { 30 => { TIMER.interrupt(); @@ -198,14 +207,14 @@ unsafe extern "C" fn irq_handler(ctx: *mut UserContext) { fb.present(); }); } + + unsafe { core::arch::asm!("msr cntp_tval_el0, {}", in(reg) TIMER_FREQUENCY_HZ) }; } - 17 => { - if let Some(scancode) = keyboard_interrupt() { - push_scancode(scancode); - } + interrupt_id if keyboard_irq == interrupt_id as u64 => { + input_interrupt("kbd"); } - 18 => { - mouse_interrupt(); + interrupt_id if mouse_irq == interrupt_id as u64 => { + input_interrupt("mouse"); } _ => {} @@ -288,6 +297,6 @@ exception_handler!(current_el_spx_serror, no_operation); // usermode exception_handler!(lower_el_aarch64_sync, sync_handler_user); -exception_handler!(lower_el_aarch64_irq, no_operation); +exception_handler!(lower_el_aarch64_irq, irq_handler); exception_handler!(lower_el_aarch64_fiq, no_operation); exception_handler!(lower_el_aarch64_serror, no_operation); diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index f0dfa18..c4f9c87 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -184,9 +184,7 @@ pub fn initialize_paging_aarch64<'a>( page_table.map_range(0xFFFF_0000_0900_0000, 0x0900_0000, 0x1000, device_flags()); // the UART page_table.map_range(0xFFFF_0000_0800_0000, 0x0800_0000, 0x10000, device_flags()); // the GICD page_table.map_range(0xFFFF_0000_0801_0000, 0x0801_0000, 0x10000, device_flags()); // the GICC - page_table.map_page(0xFFFF_0000_0905_0000, 0x0905_0000, device_flags()); // KMI0 (keyboard) - page_table.map_page(0xFFFF_0000_0906_0000, 0x0906_0000, device_flags()); // KMI1 (mouse) - setup_mair(); + page_table.map_range(0xFFFF_0000_0a00_0000, 0x0a00_0000, 0x8000, device_flags()); // Virtio setup_mair(); setup_tcr(); let stack_phys = KERNEL_STACK.0.as_ptr() as u64 - k_virt_base + k_phys_base; diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index 1195ae1..7ac6e91 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -13,13 +13,19 @@ use x86_64::{ #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::paging::AArchPageTable; +#[cfg(target_arch = "x86_64")] +use crate::driver::io::ps2::process_scancodes; +#[cfg(target_arch = "aarch64")] +use crate::driver::io::virtio::input::process_keycodes; use crate::{ arch::arch::{FRAME_ALLOCATOR, sleep}, driver::{ elf::loader::run_elf, - fs::vfs::{vfs_close, vfs_lseek, vfs_open, vfs_read}, graphics::framebuffer::{FRAMEBUFFER, USER_FB_BASE, with_framebuffer}, - keyboard::{KeyboardEvent, process_scancodes}, + io::{ + fs::vfs::{vfs_close, vfs_lseek, vfs_open, vfs_read}, + keyboard::KeyboardEvent, + }, timer::TIMER, }, print, @@ -192,7 +198,11 @@ fn close(fd: isize) -> isize { } fn kbd_read(user_ptr: *mut KeyboardEvent, max_events: isize) -> isize { + #[cfg(target_arch = "x86_64")] process_scancodes(); + #[cfg(target_arch = "aarch64")] + process_keycodes(); + if max_events <= 0 || user_ptr.is_null() { return -1; } diff --git a/kernel/src/arch/x86_64/init.rs b/kernel/src/arch/x86_64/init.rs index 8be72b2..d0f5584 100644 --- a/kernel/src/arch/x86_64/init.rs +++ b/kernel/src/arch/x86_64/init.rs @@ -8,7 +8,7 @@ use crate::{ syscall::init_syscalls, }, config::TIMER_FREQUENCY_HZ, - driver::kmi::MOUSE, + driver::io::mouse::MOUSE, }; use x86_64::{ diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 1ece733..fcc44ae 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -4,8 +4,7 @@ use crate::{ arch::x86_64::gdt, driver::{ graphics::framebuffer::with_framebuffer, - keyboard::push_scancode, - kmi::{keyboard_interrupt, mouse_interrupt}, + io::ps2::{keyboard_interrupt, mouse_interrupt, push_scancode}, serial::with_serial_console, timer::TIMER, }, diff --git a/kernel/src/config.rs b/kernel/src/config.rs index 3511ca7..82f0fdc 100644 --- a/kernel/src/config.rs +++ b/kernel/src/config.rs @@ -1 +1,2 @@ -pub const TIMER_FREQUENCY_HZ: usize = 1000; \ No newline at end of file +pub const TIMER_FREQUENCY_HZ: usize = 1000; +pub const KARCH: &str = "aarch64"; \ No newline at end of file diff --git a/kernel/src/driver/fs/mod.rs b/kernel/src/driver/io/fs/mod.rs similarity index 100% rename from kernel/src/driver/fs/mod.rs rename to kernel/src/driver/io/fs/mod.rs diff --git a/kernel/src/driver/fs/vfs.rs b/kernel/src/driver/io/fs/vfs.rs similarity index 94% rename from kernel/src/driver/fs/vfs.rs rename to kernel/src/driver/io/fs/vfs.rs index 1efe18f..ec06378 100644 --- a/kernel/src/driver/fs/vfs.rs +++ b/kernel/src/driver/io/fs/vfs.rs @@ -38,9 +38,9 @@ fn fd_ok(fd: Fd) -> bool { fd >= 0 && (fd as usize) < MAX_FD } -static DOOM_WAD: &[u8] = include_bytes!("../../../../assets/doom1.wad"); -static DOOM_ELF: &[u8] = include_bytes!("../../../../assets/doomgeneric"); -static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../assets/helloworld.elf"); +static DOOM_WAD: &[u8] = include_bytes!("../../../../../assets/doom1.wad"); +static DOOM_ELF: &[u8] = include_bytes!("../../../../../assets/doomgeneric"); +static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../../assets/helloworld.elf"); static FILES: &[FakeFileEntry] = &[ FakeFileEntry { diff --git a/kernel/src/driver/io/keyboard.rs b/kernel/src/driver/io/keyboard.rs new file mode 100644 index 0000000..6442ff8 --- /dev/null +++ b/kernel/src/driver/io/keyboard.rs @@ -0,0 +1,192 @@ +pub const KEY_RESERVED: u8 = 0; +pub const KEY_ESC: u8 = 1; +pub const KEY_1: u8 = 2; +pub const KEY_2: u8 = 3; +pub const KEY_3: u8 = 4; +pub const KEY_4: u8 = 5; +pub const KEY_5: u8 = 6; +pub const KEY_6: u8 = 7; +pub const KEY_7: u8 = 8; +pub const KEY_8: u8 = 9; +pub const KEY_9: u8 = 10; +pub const KEY_0: u8 = 11; +pub const KEY_MINUS: u8 = 12; +pub const KEY_EQUAL: u8 = 13; +pub const KEY_BACKSPACE: u8 = 14; +pub const KEY_TAB: u8 = 15; +pub const KEY_Q: u8 = 16; +pub const KEY_W: u8 = 17; +pub const KEY_E: u8 = 18; +pub const KEY_R: u8 = 19; +pub const KEY_T: u8 = 20; +pub const KEY_Y: u8 = 21; +pub const KEY_U: u8 = 22; +pub const KEY_I: u8 = 23; +pub const KEY_O: u8 = 24; +pub const KEY_P: u8 = 25; +pub const KEY_LEFTBRACE: u8 = 26; +pub const KEY_RIGHTBRACE: u8 = 27; +pub const KEY_ENTER: u8 = 28; +pub const KEY_LEFTCTRL: u8 = 29; +pub const KEY_A: u8 = 30; +pub const KEY_S: u8 = 31; +pub const KEY_D: u8 = 32; +pub const KEY_F: u8 = 33; +pub const KEY_G: u8 = 34; +pub const KEY_H: u8 = 35; +pub const KEY_J: u8 = 36; +pub const KEY_K: u8 = 37; +pub const KEY_L: u8 = 38; +pub const KEY_SEMICOLON: u8 = 39; +pub const KEY_APOSTROPHE: u8 = 40; +pub const KEY_GRAVE: u8 = 41; +pub const KEY_LEFTSHIFT: u8 = 42; +pub const KEY_BACKSLASH: u8 = 43; +pub const KEY_Z: u8 = 44; +pub const KEY_X: u8 = 45; +pub const KEY_C: u8 = 46; +pub const KEY_V: u8 = 47; +pub const KEY_B: u8 = 48; +pub const KEY_N: u8 = 49; +pub const KEY_M: u8 = 50; +pub const KEY_COMMA: u8 = 51; +pub const KEY_DOT: u8 = 52; +pub const KEY_SLASH: u8 = 53; +pub const KEY_RIGHTSHIFT: u8 = 54; +pub const KEY_KPASTERISK: u8 = 55; +pub const KEY_LEFTALT: u8 = 56; +pub const KEY_SPACE: u8 = 57; +pub const KEY_CAPSLOCK: u8 = 58; +pub const KEY_F1: u8 = 59; +pub const KEY_F2: u8 = 60; +pub const KEY_F3: u8 = 61; +pub const KEY_F4: u8 = 62; +pub const KEY_F5: u8 = 63; +pub const KEY_F6: u8 = 64; +pub const KEY_F7: u8 = 65; +pub const KEY_F8: u8 = 66; +pub const KEY_F9: u8 = 67; +pub const KEY_F10: u8 = 68; +pub const KEY_NUMLOCK: u8 = 69; +pub const KEY_SCROLLLOCK: u8 = 70; +pub const KEY_KP7: u8 = 71; +pub const KEY_KP8: u8 = 72; +pub const KEY_KP9: u8 = 73; +pub const KEY_KPMINUS: u8 = 74; +pub const KEY_KP4: u8 = 75; +pub const KEY_KP5: u8 = 76; +pub const KEY_KP6: u8 = 77; +pub const KEY_KPPLUS: u8 = 78; +pub const KEY_KP1: u8 = 79; +pub const KEY_KP2: u8 = 80; +pub const KEY_KP3: u8 = 81; +pub const KEY_KP0: u8 = 82; +pub const KEY_KPDOT: u8 = 83; +pub const KEY_RIGHTCTRL: u8 = 97; +pub const KEY_UP: u8 = 103; +pub const KEY_LEFT: u8 = 105; +pub const KEY_RIGHT: u8 = 106; +pub const KEY_DOWN: u8 = 108; + +pub const KEY_TO_CHAR: [Option<(char, char)>; 84] = [ + None, // 0 KEY_RESERVED + Some(('\x1b', '\x1b')), // 1 KEY_ESC + Some(('1', '!')), // 2 KEY_1 + Some(('2', '@')), // 3 KEY_2 + Some(('3', '#')), // 4 KEY_3 + Some(('4', '$')), // 5 KEY_4 + Some(('5', '%')), // 6 KEY_5 + Some(('6', '^')), // 7 KEY_6 + Some(('7', '&')), // 8 KEY_7 + Some(('8', '*')), // 9 KEY_8 + Some(('9', '(')), // 10 KEY_9 + Some(('0', ')')), // 11 KEY_0 + Some(('-', '_')), // 12 KEY_MINUS + Some(('=', '+')), // 13 KEY_EQUAL + Some(('\x08', '\x08')), // 14 KEY_BACKSPACE + Some(('\t', '\t')), // 15 KEY_TAB + Some(('q', 'Q')), // 16 KEY_Q + Some(('w', 'W')), // 17 KEY_W + Some(('e', 'E')), // 18 KEY_E + Some(('r', 'R')), // 19 KEY_R + Some(('t', 'T')), // 20 KEY_T + Some(('y', 'Y')), // 21 KEY_Y + Some(('u', 'U')), // 22 KEY_U + Some(('i', 'I')), // 23 KEY_I + Some(('o', 'O')), // 24 KEY_O + Some(('p', 'P')), // 25 KEY_P + Some(('[', '{')), // 26 KEY_LEFTBRACE + Some((']', '}')), // 27 KEY_RIGHTBRACE + Some(('\n', '\n')), // 28 KEY_ENTER + None, // 29 KEY_LEFTCTRL + Some(('a', 'A')), // 30 KEY_A + Some(('s', 'S')), // 31 KEY_S + Some(('d', 'D')), // 32 KEY_D + Some(('f', 'F')), // 33 KEY_F + Some(('g', 'G')), // 34 KEY_G + Some(('h', 'H')), // 35 KEY_H + Some(('j', 'J')), // 36 KEY_J + Some(('k', 'K')), // 37 KEY_K + Some(('l', 'L')), // 38 KEY_L + Some((';', ':')), // 39 KEY_SEMICOLON + Some(('\'', '"')), // 40 KEY_APOSTROPHE + Some(('`', '~')), // 41 KEY_GRAVE + None, // 42 KEY_LEFTSHIFT + Some(('\\', '|')), // 43 KEY_BACKSLASH + Some(('z', 'Z')), // 44 KEY_Z + Some(('x', 'X')), // 45 KEY_X + Some(('c', 'C')), // 46 KEY_C + Some(('v', 'V')), // 47 KEY_V + Some(('b', 'B')), // 48 KEY_B + Some(('n', 'N')), // 49 KEY_N + Some(('m', 'M')), // 50 KEY_M + Some((',', '<')), // 51 KEY_COMMA + Some(('.', '>')), // 52 KEY_DOT + Some(('/', '?')), // 53 KEY_SLASH + None, // 54 KEY_RIGHTSHIFT + Some(('*', '*')), // 55 KEY_KPASTERISK + None, // 56 KEY_LEFTALT + Some((' ', ' ')), // 57 KEY_SPACE + None, // 58 KEY_CAPSLOCK + None, // 59 KEY_F1 + None, // 60 KEY_F2 + None, // 61 KEY_F3 + None, // 62 KEY_F4 + None, // 63 KEY_F5 + None, // 64 KEY_F6 + None, // 65 KEY_F7 + None, // 66 KEY_F8 + None, // 67 KEY_F9 + None, // 68 KEY_F10 + None, // 69 KEY_NUMLOCK + None, // 70 KEY_SCROLLLOCK + Some(('7', '7')), // 71 KEY_KP7 + Some(('8', '8')), // 72 KEY_KP8 + Some(('9', '9')), // 73 KEY_KP9 + Some(('-', '-')), // 74 KEY_KPMINUS + Some(('4', '4')), // 75 KEY_KP4 + Some(('5', '5')), // 76 KEY_KP5 + Some(('6', '6')), // 77 KEY_KP6 + Some((('+'), '+')), // 78 KEY_KPPLUS + Some(('1', '1')), // 79 KEY_KP1 + Some(('2', '2')), // 80 KEY_KP2 + Some(('3', '3')), // 81 KEY_KP3 + Some(('0', '0')), // 82 KEY_KP0 + Some(('.', '.')), // 83 KEY_KPDOT +]; + +pub fn keycode_to_char(keycode: u8, shift: bool) -> Option { + let entry = KEY_TO_CHAR.get(keycode as usize)?.as_ref()?; + Some(if shift { entry.1 } else { entry.0 }) +} + +#[repr(C)] +#[derive(Clone, Debug, Copy, Default)] +pub struct KeyboardEvent { + pub state: u8, + pub _pad1: u8, + pub key: u16, + pub mods: u16, + pub _pad2: u16, + pub unicode: u32, +} diff --git a/kernel/src/driver/io/mod.rs b/kernel/src/driver/io/mod.rs new file mode 100644 index 0000000..aab5f37 --- /dev/null +++ b/kernel/src/driver/io/mod.rs @@ -0,0 +1,6 @@ +pub mod fs; +pub mod keyboard; +pub mod mouse; +#[cfg(target_arch = "x86_64")] +pub mod ps2; +pub mod virtio; diff --git a/kernel/src/driver/io/mouse.rs b/kernel/src/driver/io/mouse.rs new file mode 100644 index 0000000..610e46d --- /dev/null +++ b/kernel/src/driver/io/mouse.rs @@ -0,0 +1,45 @@ +use core::sync::atomic::{AtomicBool, AtomicI16, AtomicU8, Ordering}; + +pub struct Mouse { + pub left_button_pressed: AtomicBool, + pub right_button_pressed: AtomicBool, + pub middle_button_pressed: AtomicBool, + pub x_delta: AtomicI16, + pub y_delta: AtomicI16, + pub status: AtomicU8, +} + +impl Mouse { + const fn new() -> Mouse { + Mouse { + left_button_pressed: AtomicBool::new(false), + right_button_pressed: AtomicBool::new(false), + middle_button_pressed: AtomicBool::new(false), + x_delta: AtomicI16::new(0), + y_delta: AtomicI16::new(0), + status: AtomicU8::new(0), + } + } + + pub fn button_state(&self) -> (bool, bool, bool) { + ( + self.left_button_pressed.load(Ordering::Relaxed), + self.right_button_pressed.load(Ordering::Relaxed), + self.middle_button_pressed.load(Ordering::Relaxed), + ) + } + pub fn take_motion(&self) -> (i16, i16) { + ( + self.x_delta.swap(0, Ordering::Relaxed), + self.y_delta.swap(0, Ordering::Relaxed), + ) + } + pub fn set_status(&self, status: u8) { + self.status.store(status, Ordering::Relaxed); + } + pub fn get_status(&self) -> u8 { + self.status.load(Ordering::Relaxed) + } +} + +pub static MOUSE: Mouse = Mouse::new(); diff --git a/kernel/src/driver/io/ps2.rs b/kernel/src/driver/io/ps2.rs new file mode 100644 index 0000000..18746f9 --- /dev/null +++ b/kernel/src/driver/io/ps2.rs @@ -0,0 +1,325 @@ +use core::sync::atomic::{AtomicU8, AtomicU64, Ordering}; +use heapless::spsc::{Consumer, Producer, Queue}; +use pc_keyboard::{DecodedKey, HandleControl, KeyCode, KeyState, Keyboard, ScancodeSet2, layouts}; + +use static_cell::StaticCell; + +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::kmi::{ + read_keyboard_control, read_keyboard_data, read_mouse_control, read_mouse_data, +}; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::kmi::{ + read_keyboard_control, read_keyboard_data, read_mouse_control, read_mouse_data, +}; +use crate::{ + driver::io::{keyboard::*, mouse::MOUSE}, + task::scheduler::SCHEDULER, + util::get_bit, +}; + +pub fn keycode_to_linux(kc: KeyCode) -> Option { + let code = match kc { + KeyCode::Escape => KEY_ESC, + KeyCode::Key1 => KEY_1, + KeyCode::Key2 => KEY_2, + KeyCode::Key3 => KEY_3, + KeyCode::Key4 => KEY_4, + KeyCode::Key5 => KEY_5, + KeyCode::Key6 => KEY_6, + KeyCode::Key7 => KEY_7, + KeyCode::Key8 => KEY_8, + KeyCode::Key9 => KEY_9, + KeyCode::Key0 => KEY_0, + KeyCode::OemMinus => KEY_MINUS, + KeyCode::OemPlus => KEY_EQUAL, + KeyCode::Backspace => KEY_BACKSPACE, + KeyCode::Tab => KEY_TAB, + KeyCode::Q => KEY_Q, + KeyCode::W => KEY_W, + KeyCode::E => KEY_E, + KeyCode::R => KEY_R, + KeyCode::T => KEY_T, + KeyCode::Y => KEY_Y, + KeyCode::U => KEY_U, + KeyCode::I => KEY_I, + KeyCode::O => KEY_O, + KeyCode::P => KEY_P, + KeyCode::Oem4 => KEY_LEFTBRACE, + KeyCode::Oem6 => KEY_RIGHTBRACE, + KeyCode::Return => KEY_ENTER, + KeyCode::LControl => KEY_LEFTCTRL, + KeyCode::A => KEY_A, + KeyCode::S => KEY_S, + KeyCode::D => KEY_D, + KeyCode::F => KEY_F, + KeyCode::G => KEY_G, + KeyCode::H => KEY_H, + KeyCode::J => KEY_J, + KeyCode::K => KEY_K, + KeyCode::L => KEY_L, + KeyCode::Oem1 => KEY_SEMICOLON, + KeyCode::Oem7 => KEY_APOSTROPHE, + KeyCode::LShift => KEY_LEFTSHIFT, + KeyCode::Oem5 => KEY_BACKSLASH, + KeyCode::Z => KEY_Z, + KeyCode::X => KEY_X, + KeyCode::C => KEY_C, + KeyCode::V => KEY_V, + KeyCode::B => KEY_B, + KeyCode::N => KEY_N, + KeyCode::M => KEY_M, + KeyCode::OemComma => KEY_COMMA, + KeyCode::OemPeriod => KEY_DOT, + KeyCode::Oem2 => KEY_SLASH, + KeyCode::RShift => KEY_RIGHTSHIFT, + KeyCode::NumpadMultiply => KEY_KPASTERISK, + KeyCode::LAlt => KEY_LEFTALT, + KeyCode::Spacebar => KEY_SPACE, + KeyCode::CapsLock => KEY_CAPSLOCK, + KeyCode::F1 => KEY_F1, + KeyCode::F2 => KEY_F2, + KeyCode::F3 => KEY_F3, + KeyCode::F4 => KEY_F4, + KeyCode::F5 => KEY_F5, + KeyCode::F6 => KEY_F6, + KeyCode::F7 => KEY_F7, + KeyCode::F8 => KEY_F8, + KeyCode::F9 => KEY_F9, + KeyCode::F10 => KEY_F10, + KeyCode::NumpadLock => KEY_NUMLOCK, + KeyCode::ScrollLock => KEY_SCROLLLOCK, + KeyCode::Numpad7 => KEY_KP7, + KeyCode::Numpad8 => KEY_KP8, + KeyCode::Numpad9 => KEY_KP9, + KeyCode::NumpadSubtract => KEY_KPMINUS, + KeyCode::Numpad4 => KEY_KP4, + KeyCode::Numpad5 => KEY_KP5, + KeyCode::Numpad6 => KEY_KP6, + KeyCode::NumpadAdd => KEY_KPPLUS, + KeyCode::Numpad1 => KEY_KP1, + KeyCode::Numpad2 => KEY_KP2, + KeyCode::Numpad3 => KEY_KP3, + KeyCode::Numpad0 => KEY_KP0, + KeyCode::NumpadPeriod => KEY_KPDOT, + KeyCode::RControl => KEY_RIGHTCTRL, + KeyCode::ArrowUp => KEY_UP, + KeyCode::ArrowLeft => KEY_LEFT, + KeyCode::ArrowRight => KEY_RIGHT, + KeyCode::ArrowDown => KEY_DOWN, + _ => return None, + }; + Some(code) +} + +static SCANCODE_QUEUE: StaticCell> = StaticCell::new(); +static mut SCANCODE_PROD: Option> = None; +static mut SCANCODE_CONS: Option> = None; +static mut KEYBOARD: Option> = None; + +static DROPPED_SCANCODES: AtomicU64 = AtomicU64::new(0); + +pub fn init_keyboard() { + let q = SCANCODE_QUEUE.init(Queue::new()); + let (p, c) = q.split(); + + unsafe { + SCANCODE_PROD = Some(p); + SCANCODE_CONS = Some(c); + KEYBOARD = Some(Keyboard::new( + ScancodeSet2::new(), + layouts::Us104Key, + HandleControl::Ignore, + )) + } +} + +pub fn push_scancode(scancode: u8) { + let pushed = unsafe { + #[allow(static_mut_refs)] + match SCANCODE_PROD.as_mut() { + Some(prod) => prod.enqueue(scancode).is_ok(), + _ => false, + } + }; + + if !pushed { + DROPPED_SCANCODES.fetch_add(1, Ordering::Relaxed); + } +} + +pub fn process_scancodes() { + loop { + let scancode = unsafe { + #[allow(static_mut_refs)] + match SCANCODE_CONS.as_mut() { + Some(cons) => match cons.dequeue() { + Some(b) => b, + None => break, + }, + None => break, + } + }; + + if let Some(kbd_event) = process_scancode(scancode) { + let mut scheduler = SCHEDULER.lock(); + for process in scheduler.processes.values_mut() { + process.kbd_buffer.push(kbd_event); + } + drop(scheduler); + } + } +} + +pub fn process_scancode(scancode: u8) -> Option { + #[allow(static_mut_refs)] + let kbd = unsafe { KEYBOARD.as_mut().expect("keyboard not initialized") }; + if let Ok(Some(key_event)) = kbd.add_byte(scancode) { + let keycode = key_event.code; + let keystate = key_event.state; + let (unicode, state) = match (kbd.process_keyevent(key_event), keystate) { + (Some(DecodedKey::Unicode(ch)), st) => (ch as u32, st), + _ => (0, keystate), + }; + + if let Some(linux_keycode) = keycode_to_linux(keycode) { + return Some(KeyboardEvent { + state: if state == KeyState::Down { 1 } else { 0 }, + _pad1: 0, + key: linux_keycode as u16, + mods: 0, + _pad2: 0, + unicode, + }); + } else { + return None; + } + } else { + return None; + } +} + +static CURRENTLY_RECEIVING_STATE: AtomicU8 = AtomicU8::new(0); +static FLAGS_BYTE: AtomicU8 = AtomicU8::new(0); +static X_DELTA_BYTE: AtomicU8 = AtomicU8::new(0); +static Y_DELTA_BYTE: AtomicU8 = AtomicU8::new(0); + +fn reset_state() { + CURRENTLY_RECEIVING_STATE.store(0, Ordering::Relaxed); + FLAGS_BYTE.store(0, Ordering::Relaxed); + X_DELTA_BYTE.store(0, Ordering::Relaxed); + Y_DELTA_BYTE.store(0, Ordering::Relaxed); +} + +pub fn process_mouse_interrupt() -> Option<(bool, bool, bool, i16, i16)> { + #[cfg(target_arch = "x86_64")] + if (unsafe { read_mouse_control() } & 0x20) == 0 { + return None; + } + #[cfg(target_arch = "aarch64")] + if (unsafe { read_mouse_control() } & 0x10) == 0 { + return None; + } + + let byte = unsafe { read_mouse_data() }; + + let state_idx = CURRENTLY_RECEIVING_STATE.fetch_add(1, Ordering::Relaxed); + + if state_idx == 0 { + if (byte & 0x08) == 0 { + // if sync bit unset, return + reset_state(); + return None; + } + + if (byte & 0b0100_0000) != 0 { + // if x overflow set, return + reset_state(); + return None; + } + + if (byte & 0b1000_0000) != 0 { + // if y overflow set, return + reset_state(); + return None; + } + + FLAGS_BYTE.store(byte, Ordering::Relaxed); + None + } else if state_idx == 1 { + X_DELTA_BYTE.store(byte, Ordering::Relaxed); + None + } else if state_idx == 2 { + Y_DELTA_BYTE.store(byte, Ordering::Relaxed); + let flags = FLAGS_BYTE.load(Ordering::Relaxed); + let left_button_pressed = get_bit(flags, 0); + let right_button_pressed = get_bit(flags, 1); + let middle_button_pressed = get_bit(flags, 2); + let x_delta_sign = get_bit(flags, 4); + let y_delta_sign = get_bit(flags, 5); + + let x_delta: i16 = { + let x_delta = X_DELTA_BYTE.load(Ordering::Relaxed); + if x_delta_sign == 1 { + (x_delta as i16) - 256 + } else { + x_delta as i16 + } + }; + + let y_delta: i16 = -{ + let y_delta = Y_DELTA_BYTE.load(Ordering::Relaxed); + if y_delta_sign == 1 { + (y_delta as i16) - 256 + } else { + y_delta as i16 + } + }; + + reset_state(); + + Some(( + left_button_pressed == 1, + right_button_pressed == 1, + middle_button_pressed == 1, + x_delta, + y_delta, + )) + } else { + None + } +} + +pub fn mouse_interrupt() { + if let Some(interrupt_result) = process_mouse_interrupt() { + MOUSE + .left_button_pressed + .store(interrupt_result.0, Ordering::Relaxed); + MOUSE + .right_button_pressed + .store(interrupt_result.1, Ordering::Relaxed); + MOUSE + .middle_button_pressed + .store(interrupt_result.2, Ordering::Relaxed); + MOUSE + .x_delta + .fetch_add(interrupt_result.3, Ordering::Relaxed); + MOUSE + .y_delta + .fetch_add(interrupt_result.4, Ordering::Relaxed); + } +} + +pub fn keyboard_interrupt() -> Option { + #[cfg(target_arch = "x86_64")] + if (unsafe { read_keyboard_control() } & 0x01) == 0 { + return None; // OBF clear, no data + } + #[cfg(target_arch = "aarch64")] + if (unsafe { read_keyboard_control() } & 0x10) == 0 { + return None; // RXFULL clear, no data + } + + let scancode = unsafe { read_keyboard_data() }; + Some(scancode) +} diff --git a/kernel/src/driver/io/virtio/input.rs b/kernel/src/driver/io/virtio/input.rs new file mode 100644 index 0000000..3e70cec --- /dev/null +++ b/kernel/src/driver/io/virtio/input.rs @@ -0,0 +1,275 @@ +use heapless::spsc::{Consumer, Producer, Queue}; +use static_cell::StaticCell; + +use crate::{ + arch::arch::serial_print, + driver::io::{ + keyboard::{ + KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, KeyboardEvent, + keycode_to_char, + }, + mouse::MOUSE, + virtio::{ + KEYBOARD_SLOT, MOUSE_SLOT, VIRTIO_MMIO_BASE, VIRTIO_MMIO_STRIDE, + queue::{QUEUE_SIZE, VirtqueueMem}, + transport::{VirtioMmio, VirtioMmioReg}, + }, + }, + task::scheduler::SCHEDULER, +}; + +const BTN_LEFT: u16 = 0x110; +const BTN_RIGHT: u16 = 0x111; +const BTN_MIDDLE: u16 = 0x112; + +const REL_X: u16 = 0x00; +const REL_Y: u16 = 0x01; +const REL_WHEEL: u16 = 0x08; + +const EVENT_KEY: u16 = 0x01; +const EVENT_REL: u16 = 0x02; + +#[repr(u8)] +pub enum VirtioInputCfgSelect { + Unset = 0x00, + IdName = 0x01, + IdSerial = 0x02, + IdDevids = 0x03, + PropBits = 0x10, + EvBits = 0x11, + AbsInfo = 0x12, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct VirtioInputEvent { + event_type: u16, + code: u16, + value: u32, +} + +pub struct ModState { + shift: bool, + caps_lock: bool, + ctrl: bool, + alt: bool, +} + +impl ModState { + pub const fn new() -> Self { + Self { + shift: false, + caps_lock: false, + ctrl: false, + alt: false, + } + } + pub fn update(&mut self, code: u8, value: u32) { + match code { + KEY_LEFTSHIFT | KEY_RIGHTSHIFT => self.shift = value != 0, + KEY_LEFTCTRL => self.ctrl = value != 0, + KEY_CAPSLOCK => self.caps_lock = value != 0, + KEY_LEFTALT => self.alt = value != 0, + _ => {} + } + } + + pub fn effective_shift(&self) -> bool { + self.shift ^ self.caps_lock + } +} + +pub static mut VIRTIO_KEYBOARD_QUEUE: VirtqueueMem = unsafe { core::mem::zeroed() }; +pub static mut VIRTIO_MOUSE_QUEUE: VirtqueueMem = unsafe { core::mem::zeroed() }; +pub static mut MODIFIERS: ModState = ModState::new(); + +static KEYCODE_QUEUE: StaticCell> = StaticCell::new(); +static mut KEYCODE_PROD: Option> = None; +static mut KEYCODE_CONS: Option> = None; + +pub fn init_keyboard() { + let q = KEYCODE_QUEUE.init(Queue::new()); + let (p, c) = q.split(); + + unsafe { + KEYCODE_PROD = Some(p); + KEYCODE_CONS = Some(c); + } +} + +pub fn process_keycodes() { + let mut scheduler = SCHEDULER.lock(); + + loop { + let keycode_data = unsafe { + #[allow(static_mut_refs)] + match KEYCODE_CONS.as_mut() { + Some(cons) => match cons.dequeue() { + Some(b) => b, + None => break, + }, + None => break, + } + }; + + for process in scheduler.processes.values_mut() { + process.kbd_buffer.push(KeyboardEvent { + state: keycode_data.1, + _pad1: 0, + key: keycode_data.0 as u16, + mods: 0, + _pad2: 0, + unicode: keycode_to_char(keycode_data.0, keycode_data.2).unwrap_or('\0') as u32, + }); + } + } + + drop(scheduler); +} + +pub fn read_device_name(device: &VirtioMmio) -> &str { + return unsafe { + let select_ptr = (device.base + 0x100) as *mut u8; + let subsel_ptr = (device.base + 0x101) as *mut u8; + let size_ptr = (device.base + 0x102) as *mut u8; + let name_ptr = (device.base + 0x108) as *mut u8; + + select_ptr.write_volatile(VirtioInputCfgSelect::IdName as u8); + subsel_ptr.write_volatile(0x00); + + let size = size_ptr.read_volatile() as usize; + + if size == 0 || size > 128 { + return "Error"; + } + + let slice = core::slice::from_raw_parts(name_ptr, size); + core::str::from_utf8(slice).unwrap_or("Error") + }; +} + +pub fn input_interrupt(device_type: &str) { + #[allow(static_mut_refs)] + let queue: Option<&mut VirtqueueMem> = { + if device_type == "kbd" { + Some(unsafe { &mut VIRTIO_KEYBOARD_QUEUE }) + } else if device_type == "mouse" { + Some(unsafe { &mut VIRTIO_MOUSE_QUEUE }) + } else { + None + } + }; + + let device = VirtioMmio::new( + VIRTIO_MMIO_BASE + { + if device_type == "kbd" { + KEYBOARD_SLOT.load(core::sync::atomic::Ordering::Relaxed) + } else if device_type == "mouse" { + MOUSE_SLOT.load(core::sync::atomic::Ordering::Relaxed) + } else { + 0 + } + } * VIRTIO_MMIO_STRIDE, + ); + + if queue.is_none() { + return; + } + + let queue = queue.unwrap(); + + let status = device.read(VirtioMmioReg::InterruptStatus); + device.write(VirtioMmioReg::InterruptAck, status); + + loop { + let used_idx = unsafe { core::ptr::read_volatile(&queue.used.idx) }; + let last_idx = queue.last_used_idx; + + if last_idx == used_idx { + return; + } + + let used_element = &queue.used.ring[last_idx as usize % QUEUE_SIZE]; + let desc_idx = used_element.id; + + queue.last_used_idx = last_idx.wrapping_add(1); + + let event = unsafe { core::ptr::read_volatile(&queue.buffers[desc_idx as usize]) }; + + let avail_idx = unsafe { core::ptr::read_volatile(&queue.avail.idx) }; + queue.avail.ring[(avail_idx as usize) % QUEUE_SIZE] = desc_idx as u16; + + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + + unsafe { + core::ptr::write_volatile(&mut queue.avail.idx, avail_idx.wrapping_add(1)); + } + + device.write(VirtioMmioReg::QueueNotify, 0); + + handle_event(&event); + } +} + +#[allow(static_mut_refs)] +pub fn handle_event(event: &VirtioInputEvent) { + if event.event_type == 0 || event.code == 0 { + return; + } + + match event.event_type { + EVENT_KEY => { + unsafe { MODIFIERS.update(event.code as u8, event.value) }; + match event.code { + BTN_LEFT => { + MOUSE + .left_button_pressed + .store(event.value == 1, core::sync::atomic::Ordering::Relaxed); + } + BTN_RIGHT => { + MOUSE + .right_button_pressed + .store(event.value == 1, core::sync::atomic::Ordering::Relaxed); + } + BTN_MIDDLE => { + MOUSE + .middle_button_pressed + .store(event.value == 1, core::sync::atomic::Ordering::Relaxed); + } + _ => { + let state = match event.value { + 1 => 1, + 0 => 0, + _ => return, + }; + + let _ = match unsafe { KEYCODE_PROD.as_mut() } { + Some(prod) => prod + .enqueue(unsafe { + (event.code as u8, state, MODIFIERS.effective_shift()) + }) + .is_ok(), + _ => false, + }; + } + } + } + EVENT_REL => match event.code { + REL_X => { + MOUSE.x_delta.fetch_add( + event.value as i32 as i16, + core::sync::atomic::Ordering::Relaxed, + ); + } + REL_Y => { + MOUSE.y_delta.fetch_add( + event.value as i32 as i16, + core::sync::atomic::Ordering::Relaxed, + ); + } + REL_WHEEL => {} + _ => {} + }, + _ => serial_print("Could not recognize virtio input event from interrupt\n"), + }; +} diff --git a/kernel/src/driver/io/virtio/mod.rs b/kernel/src/driver/io/virtio/mod.rs new file mode 100644 index 0000000..5c907ae --- /dev/null +++ b/kernel/src/driver/io/virtio/mod.rs @@ -0,0 +1,141 @@ +use core::sync::atomic::AtomicU64; + +use crate::{ + arch::arch::serial_print, + driver::io::virtio::{ + input::read_device_name, + queue::setup_queue, + transport::{VirtioMmio, VirtioMmioReg}, + }, + util::U64Buf, +}; + +pub mod input; +pub mod queue; +pub mod transport; + +pub const VIRTIO_MMIO_BASE: u64 = 0xFFFF_0000_0a00_0000; +pub const VIRTIO_MMIO_STRIDE: u64 = 0x200; +pub const VIRTIO_MMIO_COUNT: u64 = 32; + +const VIRTIO_MAGIC: u32 = 0x74726976; + +const VIRTIO_DEVICE_INPUT: u32 = 0x12; +const VIRTIO_DEVICE_BLOCK: u32 = 0x02; + +const DEVICE_ACKNOWLEDGE: u32 = 0x01; +const DEVICE_DRIVER: u32 = 0x02; +const DEVICE_FEATURES_OK: u32 = 0x08; +const DEVICE_DRIVER_OK: u32 = 0x04; +const DEVICE_FAILED: u32 = 0x80; + +pub static KEYBOARD_SLOT: AtomicU64 = AtomicU64::new(u64::MAX); +pub static MOUSE_SLOT: AtomicU64 = AtomicU64::new(u64::MAX); + +pub fn get_device(slot: u64) -> Option { + let base = VIRTIO_MMIO_BASE + slot * VIRTIO_MMIO_STRIDE; + let device = VirtioMmio::new(base); + + if device.read(VirtioMmioReg::MagicValue) != VIRTIO_MAGIC { + return None; + } + + if device.read(VirtioMmioReg::Version) != 0x02 { + return None; + } + + let device_id = device.read(VirtioMmioReg::DeviceID); + + if device_id == 0 { + return None; + } + + Some(device) +} + +pub fn scan_devices() { + for i in 0..VIRTIO_MMIO_COUNT { + let base = VIRTIO_MMIO_BASE + i * VIRTIO_MMIO_STRIDE; + let device = VirtioMmio::new(base); + + if device.read(VirtioMmioReg::MagicValue) != VIRTIO_MAGIC { + continue; + } + + if device.read(VirtioMmioReg::Version) != 0x02 { + continue; + } + + let device_id = device.read(VirtioMmioReg::DeviceID); + + if device_id == 0 { + continue; + } + + serial_print("Slot #"); + serial_print(U64Buf::new(i).as_str()); + serial_print(" device_id="); + serial_print(U64Buf::new(device.read(VirtioMmioReg::DeviceID) as u64).as_str()); + serial_print("\n"); + + match device_id { + VIRTIO_DEVICE_INPUT => { + serial_print("Input device found!"); + serial_print("\n"); + if init_device(&device) { + serial_print("Successfully initialized device!\n"); + } else { + serial_print("Device initialization failed.\n"); + } + + let device_name = read_device_name(&device); + + serial_print("Found name of device: "); + serial_print(device_name); + serial_print("\n"); + + serial_print("Setting up queue for device...\n"); + + if read_device_name(&device).contains("QEMU Virtio Keyboard") { + KEYBOARD_SLOT.store(i, core::sync::atomic::Ordering::Relaxed); + setup_queue(&device, "kbd"); + } else if read_device_name(&device).contains("QEMU Virtio Mouse") { + MOUSE_SLOT.store(i, core::sync::atomic::Ordering::Relaxed); + setup_queue(&device, "mouse"); + } else { + serial_print("Could not recognize device."); + continue; + } + + device.write( + VirtioMmioReg::Status, + DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_FEATURES_OK | DEVICE_DRIVER_OK, + ); + + serial_print("Successfully set up queue for device and notified it.\n"); + } + VIRTIO_DEVICE_BLOCK => { + // TODO: add block device support + } + _ => {} + } + } +} + +pub fn init_device(device: &VirtioMmio) -> bool { + device.write(VirtioMmioReg::Status, 0); // reset + device.write(VirtioMmioReg::Status, DEVICE_ACKNOWLEDGE); + device.write(VirtioMmioReg::Status, DEVICE_ACKNOWLEDGE | DEVICE_DRIVER); + device.write(VirtioMmioReg::DriverFeatures, 0); // no features for now + device.write( + VirtioMmioReg::Status, + DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_FEATURES_OK, + ); + + if device.read(VirtioMmioReg::Status) & DEVICE_FEATURES_OK == 0 { + device.write(VirtioMmioReg::Status, DEVICE_FAILED); + return false; + } + + true +} diff --git a/kernel/src/driver/io/virtio/queue.rs b/kernel/src/driver/io/virtio/queue.rs new file mode 100644 index 0000000..6d7f854 --- /dev/null +++ b/kernel/src/driver/io/virtio/queue.rs @@ -0,0 +1,124 @@ +use core::{mem::offset_of, sync::atomic::Ordering}; + +use crate::{ + KERNEL_PHYS_BASE, KERNEL_VIRT_BASE, + driver::io::virtio::{ + input::{VIRTIO_KEYBOARD_QUEUE, VIRTIO_MOUSE_QUEUE, VirtioInputEvent}, + transport::{VirtioMmio, VirtioMmioReg}, + }, +}; + +const VIRTQ_DESC_F_WRITE: u16 = 0x02; +pub const QUEUE_SIZE: usize = 64; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct VirtQDesc { + pub addr: u64, + pub len: u32, + pub flags: u16, + pub next: u16, +} + +impl VirtQDesc { + pub const fn writable() -> Self { + Self { + addr: 0, + len: 0, + flags: VIRTQ_DESC_F_WRITE, + next: 0, + } + } +} + +#[repr(C)] +pub struct VirtQAvail { + pub flags: u16, + pub idx: u16, + pub ring: [u16; QUEUE_SIZE], +} + +#[repr(C)] +pub struct VirtQUsedElem { + pub id: u32, + pub len: u32, +} + +#[repr(C)] +pub struct VirtQUsed { + pub flags: u16, + pub idx: u16, + pub ring: [VirtQUsedElem; QUEUE_SIZE], +} + +#[repr(C, align(16))] +pub struct VirtqueueMem { + pub desc: [VirtQDesc; QUEUE_SIZE], + pub avail: VirtQAvail, + pub used: VirtQUsed, + pub buffers: [VirtioInputEvent; QUEUE_SIZE], + pub last_used_idx: u16, +} + +pub fn fill_descriptors(queue: &mut VirtqueueMem) { + let kernel_virt_base = KERNEL_VIRT_BASE.load(core::sync::atomic::Ordering::Relaxed); + let kernel_phys_base = KERNEL_PHYS_BASE.load(core::sync::atomic::Ordering::Relaxed); + for i in 0..QUEUE_SIZE { + let virt_addr = &queue.buffers[i] as *const VirtioInputEvent as u64; + let phys_addr = virt_addr - kernel_virt_base + kernel_phys_base; + queue.desc[i].addr = phys_addr; + queue.desc[i].len = size_of::() as u32; + queue.desc[i].flags = VIRTQ_DESC_F_WRITE; + queue.avail.ring[i] = i as u16; + } + queue.avail.idx = QUEUE_SIZE as u16; +} +pub fn setup_queue(device: &VirtioMmio, device_type: &str) { + #[allow(static_mut_refs)] + let queue: Option<&mut VirtqueueMem> = { + if device_type == "kbd" { + Some(unsafe { &mut VIRTIO_KEYBOARD_QUEUE }) + } else if device_type == "mouse" { + Some(unsafe { &mut VIRTIO_MOUSE_QUEUE }) + } else { + None + } + }; + + if queue.is_none() { + return; + } + + let queue = queue.unwrap(); + + let phys_base: u64 = queue as *const VirtqueueMem as u64 + - KERNEL_VIRT_BASE.load(Ordering::Relaxed) + + KERNEL_PHYS_BASE.load(Ordering::Relaxed); + + device.write(VirtioMmioReg::QueueSel, 0); + device.write(VirtioMmioReg::QueueNum, QUEUE_SIZE as u32); + + let desc_phys = phys_base + offset_of!(VirtqueueMem, desc) as u64; + device.write(VirtioMmioReg::QueueDescLow, (desc_phys & 0xFFFFFFFF) as u32); + device.write(VirtioMmioReg::QueueDescHigh, (desc_phys >> 32) as u32); + + let avail_phys = phys_base + offset_of!(VirtqueueMem, avail) as u64; + device.write( + VirtioMmioReg::QueueDriverLow, + (avail_phys & 0xFFFFFFFF) as u32, + ); + device.write(VirtioMmioReg::QueueDriverHigh, (avail_phys >> 32) as u32); + + let used_phys = phys_base + offset_of!(VirtqueueMem, used) as u64; + device.write( + VirtioMmioReg::QueueDeviceLow, + (used_phys & 0xFFFFFFFF) as u32, + ); + device.write(VirtioMmioReg::QueueDeviceHigh, (used_phys >> 32) as u32); + + fill_descriptors(queue); + + core::sync::atomic::fence(Ordering::SeqCst); + device.write(VirtioMmioReg::QueueReady, 1); + device.write(VirtioMmioReg::QueueNotify, 0); +} diff --git a/kernel/src/driver/io/virtio/transport.rs b/kernel/src/driver/io/virtio/transport.rs new file mode 100644 index 0000000..e627dc8 --- /dev/null +++ b/kernel/src/driver/io/virtio/transport.rs @@ -0,0 +1,49 @@ +#[repr(u64)] +pub enum VirtioMmioReg { + MagicValue = 0x000, + Version = 0x004, + DeviceID = 0x008, + VendorID = 0x00c, + DeviceFeatures = 0x010, + DriverFeatures = 0x020, + QueueSel = 0x030, + QueueNumMax = 0x034, + QueueNum = 0x038, + QueueReady = 0x044, + QueueNotify = 0x050, + InterruptStatus = 0x060, + InterruptAck = 0x064, + Status = 0x070, + QueueDescLow = 0x080, + QueueDescHigh = 0x084, + QueueDriverLow = 0x090, + QueueDriverHigh = 0x094, + QueueDeviceLow = 0x0a0, + QueueDeviceHigh = 0x0a4, + ConfigGeneration = 0x0fc, + Config = 0x100, +} + +pub struct VirtioMmio { + pub base: u64, +} + +impl VirtioMmio { + pub fn new(base: u64) -> Self { + Self { base } + } + + pub fn read(&self, reg: VirtioMmioReg) -> u32 { + unsafe { + let ptr = (self.base + reg as u64) as *const u32; + ptr.read_volatile() + } + } + + pub fn write(&self, reg: VirtioMmioReg, value: u32) { + unsafe { + let ptr = (self.base + reg as u64) as *mut u32; + ptr.write_volatile(value); + } + } +} diff --git a/kernel/src/driver/keyboard.rs b/kernel/src/driver/keyboard.rs deleted file mode 100644 index 4dba619..0000000 --- a/kernel/src/driver/keyboard.rs +++ /dev/null @@ -1,99 +0,0 @@ -use core::sync::atomic::{AtomicU64, Ordering}; -use heapless::spsc::{Consumer, Producer, Queue}; -use pc_keyboard::{DecodedKey, HandleControl, KeyState, Keyboard, ScancodeSet2, layouts}; -use static_cell::StaticCell; - -use crate::task::scheduler::SCHEDULER; -#[repr(C)] -#[derive(Clone, Debug, Copy, Default)] -pub struct KeyboardEvent { - pub state: u8, - pub _pad1: u8, - pub key: u16, - pub mods: u16, - pub _pad2: u16, - pub unicode: u32, -} - -static SCANCODE_QUEUE: StaticCell> = StaticCell::new(); -static mut SCANCODE_PROD: Option> = None; -static mut SCANCODE_CONS: Option> = None; -static mut KEYBOARD: Option> = None; - -static DROPPED_SCANCODES: AtomicU64 = AtomicU64::new(0); - -pub fn init_keyboard() { - let q = SCANCODE_QUEUE.init(Queue::new()); - let (p, c) = q.split(); - - unsafe { - SCANCODE_PROD = Some(p); - SCANCODE_CONS = Some(c); - KEYBOARD = Some(Keyboard::new( - ScancodeSet2::new(), - layouts::Us104Key, - HandleControl::Ignore, - )) - } -} - -pub fn push_scancode(scancode: u8) { - let pushed = unsafe { - #[allow(static_mut_refs)] - match SCANCODE_PROD.as_mut() { - Some(prod) => prod.enqueue(scancode).is_ok(), - _ => false, - } - }; - - if !pushed { - DROPPED_SCANCODES.fetch_add(1, Ordering::Relaxed); - } -} - -pub fn process_scancodes() { - loop { - let scancode = unsafe { - #[allow(static_mut_refs)] - match SCANCODE_CONS.as_mut() { - Some(cons) => match cons.dequeue() { - Some(b) => b, - None => break, - }, - None => break, - } - }; - - if let Some(kbd_event) = process_scancode(scancode) { - let mut scheduler = SCHEDULER.lock(); - for process in scheduler.processes.values_mut() { - process.kbd_buffer.push(kbd_event); - } - drop(scheduler); - } - } -} - -pub fn process_scancode(scancode: u8) -> Option { - #[allow(static_mut_refs)] - let kbd = unsafe { KEYBOARD.as_mut().expect("keyboard not initialized") }; - if let Ok(Some(key_event)) = kbd.add_byte(scancode) { - let keycode = key_event.code; - let keystate = key_event.state; - let (unicode, state) = match (kbd.process_keyevent(key_event), keystate) { - (Some(DecodedKey::Unicode(ch)), st) => (ch as u32, st), - _ => (0, keystate), - }; - - return Some(KeyboardEvent { - state: if state == KeyState::Down { 1 } else { 0 }, - _pad1: 0, - key: keycode as u16, - mods: 0, - _pad2: 0, - unicode, - }); - } else { - return None; - } -} diff --git a/kernel/src/driver/kmi.rs b/kernel/src/driver/kmi.rs deleted file mode 100644 index c50c9c4..0000000 --- a/kernel/src/driver/kmi.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::util::get_bit; -use core::sync::atomic::{AtomicI16, AtomicU8, Ordering}; - -#[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::kmi::{ - read_keyboard_control, read_keyboard_data, read_mouse_control, read_mouse_data, -}; -#[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::kmi::{ - read_keyboard_control, read_keyboard_data, read_mouse_control, read_mouse_data, -}; - -static CURRENTLY_RECEIVING_STATE: AtomicU8 = AtomicU8::new(0); -static FLAGS_BYTE: AtomicU8 = AtomicU8::new(0); -static X_DELTA_BYTE: AtomicU8 = AtomicU8::new(0); -static Y_DELTA_BYTE: AtomicU8 = AtomicU8::new(0); - -pub struct Mouse { - left_button_pressed: AtomicU8, - right_button_pressed: AtomicU8, - middle_button_pressed: AtomicU8, - x_delta: AtomicI16, - y_delta: AtomicI16, - status: AtomicU8, -} - -impl Mouse { - const fn new() -> Mouse { - Mouse { - left_button_pressed: AtomicU8::new(0), - right_button_pressed: AtomicU8::new(0), - middle_button_pressed: AtomicU8::new(0), - x_delta: AtomicI16::new(0), - y_delta: AtomicI16::new(0), - status: AtomicU8::new(0), - } - } - - pub fn interrupt( - &self, - left_button_pressed: u8, - right_button_pressed: u8, - middle_button_pressed: u8, - x_delta: i16, - y_delta: i16, - ) { - self.left_button_pressed - .store(left_button_pressed, Ordering::Relaxed); - self.right_button_pressed - .store(right_button_pressed, Ordering::Relaxed); - self.middle_button_pressed - .store(middle_button_pressed, Ordering::Relaxed); - self.x_delta.fetch_add(x_delta, Ordering::Relaxed); - self.y_delta.fetch_add(y_delta, Ordering::Relaxed); - } - - pub fn button_state(&self) -> (u8, u8, u8) { - ( - self.left_button_pressed.load(Ordering::Relaxed), - self.right_button_pressed.load(Ordering::Relaxed), - self.middle_button_pressed.load(Ordering::Relaxed), - ) - } - pub fn take_motion(&self) -> (i16, i16) { - ( - self.x_delta.swap(0, Ordering::Relaxed), - self.y_delta.swap(0, Ordering::Relaxed), - ) - } - pub fn set_status(&self, status: u8) { - self.status.store(status, Ordering::Relaxed); - } - pub fn get_status(&self) -> u8 { - self.status.load(Ordering::Relaxed) - } -} - -fn reset_state() { - CURRENTLY_RECEIVING_STATE.store(0, Ordering::Relaxed); - FLAGS_BYTE.store(0, Ordering::Relaxed); - X_DELTA_BYTE.store(0, Ordering::Relaxed); - Y_DELTA_BYTE.store(0, Ordering::Relaxed); -} - -pub fn process_mouse_interrupt() -> Option<(u8, u8, u8, i16, i16)> { - #[cfg(target_arch = "x86_64")] - if (unsafe { read_mouse_control() } & 0x20) == 0 { - return None; - } - #[cfg(target_arch = "aarch64")] - if (unsafe { read_mouse_control() } & 0x10) == 0 { - return None; - } - - let byte = unsafe { read_mouse_data() }; - - let state_idx = CURRENTLY_RECEIVING_STATE.fetch_add(1, Ordering::Relaxed); - - if state_idx == 0 { - if (byte & 0x08) == 0 { - // if sync bit unset, return - reset_state(); - return None; - } - - if (byte & 0b0100_0000) != 0 { - // if x overflow set, return - reset_state(); - return None; - } - - if (byte & 0b1000_0000) != 0 { - // if y overflow set, return - reset_state(); - return None; - } - - FLAGS_BYTE.store(byte, Ordering::Relaxed); - None - } else if state_idx == 1 { - X_DELTA_BYTE.store(byte, Ordering::Relaxed); - None - } else if state_idx == 2 { - Y_DELTA_BYTE.store(byte, Ordering::Relaxed); - let flags = FLAGS_BYTE.load(Ordering::Relaxed); - let left_button_pressed = get_bit(flags, 0); - let right_button_pressed = get_bit(flags, 1); - let middle_button_pressed = get_bit(flags, 2); - let x_delta_sign = get_bit(flags, 4); - let y_delta_sign = get_bit(flags, 5); - - let x_delta: i16 = { - let x_delta = X_DELTA_BYTE.load(Ordering::Relaxed); - if x_delta_sign == 1 { - (x_delta as i16) - 256 - } else { - x_delta as i16 - } - }; - - let y_delta: i16 = -{ - let y_delta = Y_DELTA_BYTE.load(Ordering::Relaxed); - if y_delta_sign == 1 { - (y_delta as i16) - 256 - } else { - y_delta as i16 - } - }; - - reset_state(); - - Some(( - left_button_pressed, - right_button_pressed, - middle_button_pressed, - x_delta, - y_delta, - )) - } else { - None - } -} - -pub fn mouse_interrupt() { - if let Some(interrupt_result) = process_mouse_interrupt() { - MOUSE.interrupt( - interrupt_result.0, - interrupt_result.1, - interrupt_result.2, - interrupt_result.3, - interrupt_result.4, - ); - } -} - -pub fn keyboard_interrupt() -> Option { - #[cfg(target_arch = "x86_64")] - if (unsafe { read_keyboard_control() } & 0x01) == 0 { - return None; // OBF clear, no data - } - #[cfg(target_arch = "aarch64")] - if (unsafe { read_keyboard_control() } & 0x10) == 0 { - return None; // RXFULL clear, no data - } - - let scancode = unsafe { read_keyboard_data() }; - Some(scancode) -} - -pub static MOUSE: Mouse = Mouse::new(); diff --git a/kernel/src/driver/mod.rs b/kernel/src/driver/mod.rs index a3ebc3a..0893fa6 100644 --- a/kernel/src/driver/mod.rs +++ b/kernel/src/driver/mod.rs @@ -1,7 +1,5 @@ pub mod elf; -pub mod fs; pub mod graphics; -pub mod keyboard; -pub mod kmi; +pub mod io; pub mod serial; pub mod timer; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 37216fc..03ea95d 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -4,9 +4,33 @@ #![feature(naked_functions_rustic_abi)] extern crate alloc; use core::fmt::Write; +use core::sync::atomic::{AtomicU64, Ordering}; +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::interrupts::enable_interrupts; +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::paging::AArchPageTable; +#[cfg(target_arch = "aarch64")] +use crate::driver::graphics::primitives::rectangle_filled; #[cfg(target_arch = "aarch64")] use aarch64_cpu::registers::{DAIF, Writeable}; + +#[cfg(target_arch = "aarch64")] +use crate::driver::io::virtio::input::init_keyboard; + +#[cfg(target_arch = "x86_64")] +use crate::driver::elf::loader::run_elf; +#[cfg(target_arch = "x86_64")] +use crate::driver::io::ps2::init_keyboard; + +use crate::arch::arch::{HHDM_OFFSET, infinite_idle, init, kernel_crash, serial_print}; + +use crate::driver::graphics::base::rgb; +use crate::driver::graphics::framebuffer::{init_framebuffer, with_framebuffer}; + +use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console}; +use crate::driver::timer::TIMER; + use limine::BaseRevision; use limine::request::{ DateAtBootRequest, ExecutableAddressRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest, @@ -19,21 +43,6 @@ pub mod mm; pub mod task; pub mod util; -#[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::interrupts::enable_interrupts; -#[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::paging::AArchPageTable; -use crate::arch::arch::{HHDM_OFFSET, infinite_idle, init, kernel_crash, serial_print}; -#[cfg(target_arch = "x86_64")] -use crate::driver::elf::loader::run_elf; -use crate::driver::graphics::base::rgb; -use crate::driver::graphics::framebuffer::{init_framebuffer, with_framebuffer}; -#[cfg(target_arch = "aarch64")] -use crate::driver::graphics::primitives::rectangle_filled; -use crate::driver::keyboard::init_keyboard; -use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console}; -use crate::driver::timer::TIMER; - #[repr(C, align(16))] #[allow(dead_code)] struct AlignedElf([u8; include_bytes!("../../assets/init").len()]); @@ -78,6 +87,9 @@ static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new(); #[unsafe(link_section = ".requests_end_marker")] static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); +pub static KERNEL_PHYS_BASE: AtomicU64 = AtomicU64::new(0); +pub static KERNEL_VIRT_BASE: AtomicU64 = AtomicU64::new(0); + #[macro_export] macro_rules! print { ($($arg:tt)*) => { @@ -124,24 +136,35 @@ unsafe extern "C" fn kmain() -> ! { ); #[allow(unused_variables)] if let Some(memory_map_response) = MEMORY_MAP_REQUEST.get_response() { - #[cfg(target_arch = "aarch64")] if let Some(executable_address_response) = EXECUTABLE_ADDRESS_REQUEST.get_response() { + KERNEL_PHYS_BASE.store( + executable_address_response.physical_base(), + Ordering::Relaxed, + ); + + KERNEL_VIRT_BASE.store( + executable_address_response.virtual_base(), + Ordering::Relaxed, + ); + + #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::init::preinit_aarch64; + #[cfg(target_arch = "aarch64")] preinit_aarch64( hhdm_response, memory_map_response, executable_address_response, ); + #[cfg(target_arch = "x86_64")] + unsafe { + kernel_main_x86_64() + } + loop {} } else { kernel_crash() } - - #[cfg(target_arch = "x86_64")] - unsafe { - kernel_main_x86_64() - } } else { kernel_crash(); // Could not get required info from Limine's memory map. } diff --git a/kernel/src/task/process.rs b/kernel/src/task/process.rs index 8375cd6..c6f0e16 100644 --- a/kernel/src/task/process.rs +++ b/kernel/src/task/process.rs @@ -1,7 +1,8 @@ use alloc::vec::Vec; use crate::{ - driver::keyboard::KeyboardEvent, mm::address_space::AddressSpace, task::context::UserContext, + driver::io::keyboard::KeyboardEvent, mm::address_space::AddressSpace, + task::context::UserContext, }; pub enum ProcessState { diff --git a/user/apps/doomgeneric b/user/apps/doomgeneric index 703b2e6..2c34740 160000 --- a/user/apps/doomgeneric +++ b/user/apps/doomgeneric @@ -1 +1 @@ -Subproject commit 703b2e6e219044c815bef1d2900f7b4f2924d94f +Subproject commit 2c34740743d46ed4847406be50ab65ad40f55363 diff --git a/user/init b/user/init index 4690716..601d2ca 160000 --- a/user/init +++ b/user/init @@ -1 +1 @@ -Subproject commit 469071622757e58287d1c3fc9fe12d505951f89d +Subproject commit 601d2cab7f2556d4a4aad24e35c9e33542289dda diff --git a/user/libxunil b/user/libxunil index e134040..7ddebd6 160000 --- a/user/libxunil +++ b/user/libxunil @@ -1 +1 @@ -Subproject commit e134040a9f7a644b7d3405e7a8b2742f2749ff6f +Subproject commit 7ddebd6df5b9090a6ba02e951e1317cdba5c4583