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