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
This commit is contained in:
csd4ni3l
2026-05-17 20:32:42 +02:00
parent 5efbef1712
commit 1205d8ce7a
27 changed files with 1256 additions and 345 deletions
+4 -2
View File
@@ -4,7 +4,7 @@ include config.mk
MAKEFLAGS += -rR MAKEFLAGS += -rR
.SUFFIXES: .SUFFIXES:
override QEMUFLAGS := -m $(MEMORY) override QEMUFLAGS := -m $(MEMORY) -global virtio-mmio.force-legacy=false
override IMAGE_NAME := XunilOS-$(KARCH) override IMAGE_NAME := XunilOS-$(KARCH)
.PHONY: all .PHONY: all
@@ -39,12 +39,14 @@ run-hdd-x86_64: edk2-ovmf $(IMAGE_NAME).hdd
.PHONY: run-aarch64 .PHONY: run-aarch64
run-aarch64: edk2-ovmf $(IMAGE_NAME).iso run-aarch64: edk2-ovmf $(IMAGE_NAME).iso
qemu-system-$(KARCH) \ qemu-system-$(KARCH) \
-M virt \ -M virt,gic-version=2,secure=off \
-cpu cortex-a72 \ -cpu cortex-a72 \
-device ramfb \ -device ramfb \
-device qemu-xhci \ -device qemu-xhci \
-device usb-kbd \ -device usb-kbd \
-device usb-mouse \ -device usb-mouse \
-device virtio-keyboard-device \
-device virtio-mouse-device \
-serial stdio \ -serial stdio \
-drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-$(KARCH).fd,readonly=on \ -drive if=pflash,unit=0,format=raw,file=edk2-ovmf/ovmf-code-$(KARCH).fd,readonly=on \
-cdrom $(IMAGE_NAME).iso \ -cdrom $(IMAGE_NAME).iso \
+2 -1
View File
@@ -6,11 +6,12 @@ fn main() {
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 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 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"); let out_dir = PathBuf::from("src");
fs::write( fs::write(
out_dir.join("config.rs"), 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(); .unwrap();
+3 -2
View File
@@ -2,10 +2,10 @@ use crate::{
arch::aarch64::{ arch::aarch64::{
heap::init_heap, heap::init_heap,
interrupts::init_interrupts, interrupts::init_interrupts,
kmi::setup_kmi,
paging::{AArchPageTable, initialize_paging_aarch64}, paging::{AArchPageTable, initialize_paging_aarch64},
}, },
config::TIMER_FREQUENCY_HZ, config::TIMER_FREQUENCY_HZ,
driver::io::virtio::scan_devices,
}; };
use limine::response::{ExecutableAddressResponse, HhdmResponse, MemoryMapResponse}; 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) { pub fn set_timer_freq(freq: usize) {
unsafe { 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) { pub extern "C" fn init_aarch64(mapper: &mut AArchPageTable) {
init_heap(mapper); init_heap(mapper);
set_timer_freq(TIMER_FREQUENCY_HZ); set_timer_freq(TIMER_FREQUENCY_HZ);
scan_devices();
init_interrupts(); init_interrupts();
} }
+20 -11
View File
@@ -2,10 +2,10 @@ use core::{arch::global_asm, sync::atomic::Ordering};
use crate::{ use crate::{
arch::syscall::syscall_dispatch, arch::syscall::syscall_dispatch,
config::TIMER_FREQUENCY_HZ,
driver::{ driver::{
graphics::framebuffer::with_framebuffer, graphics::framebuffer::with_framebuffer,
keyboard::push_scancode, io::virtio::{KEYBOARD_SLOT, MOUSE_SLOT, input::input_interrupt},
kmi::{keyboard_interrupt, mouse_interrupt},
serial::with_serial_console, serial::with_serial_console,
timer::TIMER, timer::TIMER,
}, },
@@ -161,8 +161,13 @@ pub fn init_interrupts() {
pub fn enable_interrupts() { pub fn enable_interrupts() {
unsafe { unsafe {
gic_enable_interrupt(30); // timer IRQ 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 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); 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(no_mangle)]
unsafe extern "C" fn irq_handler(ctx: *mut UserContext) { unsafe extern "C" fn irq_handler(ctx: *mut UserContext) {
let interrupt_id = unsafe { gic_acknowledge() }; 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 { match interrupt_id {
30 => { 30 => {
TIMER.interrupt(); TIMER.interrupt();
@@ -198,14 +207,14 @@ unsafe extern "C" fn irq_handler(ctx: *mut UserContext) {
fb.present(); fb.present();
}); });
} }
unsafe { core::arch::asm!("msr cntp_tval_el0, {}", in(reg) TIMER_FREQUENCY_HZ) };
} }
17 => { interrupt_id if keyboard_irq == interrupt_id as u64 => {
if let Some(scancode) = keyboard_interrupt() { input_interrupt("kbd");
push_scancode(scancode);
} }
} interrupt_id if mouse_irq == interrupt_id as u64 => {
18 => { input_interrupt("mouse");
mouse_interrupt();
} }
_ => {} _ => {}
@@ -288,6 +297,6 @@ exception_handler!(current_el_spx_serror, no_operation);
// usermode // usermode
exception_handler!(lower_el_aarch64_sync, sync_handler_user); 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_fiq, no_operation);
exception_handler!(lower_el_aarch64_serror, no_operation); exception_handler!(lower_el_aarch64_serror, no_operation);
+1 -3
View File
@@ -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_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_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_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_range(0xFFFF_0000_0a00_0000, 0x0a00_0000, 0x8000, device_flags()); // Virtio setup_mair();
page_table.map_page(0xFFFF_0000_0906_0000, 0x0906_0000, device_flags()); // KMI1 (mouse)
setup_mair();
setup_tcr(); setup_tcr();
let stack_phys = KERNEL_STACK.0.as_ptr() as u64 - k_virt_base + k_phys_base; let stack_phys = KERNEL_STACK.0.as_ptr() as u64 - k_virt_base + k_phys_base;
+12 -2
View File
@@ -13,13 +13,19 @@ use x86_64::{
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::paging::AArchPageTable; 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::{ use crate::{
arch::arch::{FRAME_ALLOCATOR, sleep}, arch::arch::{FRAME_ALLOCATOR, sleep},
driver::{ driver::{
elf::loader::run_elf, elf::loader::run_elf,
fs::vfs::{vfs_close, vfs_lseek, vfs_open, vfs_read},
graphics::framebuffer::{FRAMEBUFFER, USER_FB_BASE, with_framebuffer}, 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, timer::TIMER,
}, },
print, print,
@@ -192,7 +198,11 @@ fn close(fd: isize) -> isize {
} }
fn kbd_read(user_ptr: *mut KeyboardEvent, max_events: isize) -> isize { fn kbd_read(user_ptr: *mut KeyboardEvent, max_events: isize) -> isize {
#[cfg(target_arch = "x86_64")]
process_scancodes(); process_scancodes();
#[cfg(target_arch = "aarch64")]
process_keycodes();
if max_events <= 0 || user_ptr.is_null() { if max_events <= 0 || user_ptr.is_null() {
return -1; return -1;
} }
+1 -1
View File
@@ -8,7 +8,7 @@ use crate::{
syscall::init_syscalls, syscall::init_syscalls,
}, },
config::TIMER_FREQUENCY_HZ, config::TIMER_FREQUENCY_HZ,
driver::kmi::MOUSE, driver::io::mouse::MOUSE,
}; };
use x86_64::{ use x86_64::{
+1 -2
View File
@@ -4,8 +4,7 @@ use crate::{
arch::x86_64::gdt, arch::x86_64::gdt,
driver::{ driver::{
graphics::framebuffer::with_framebuffer, graphics::framebuffer::with_framebuffer,
keyboard::push_scancode, io::ps2::{keyboard_interrupt, mouse_interrupt, push_scancode},
kmi::{keyboard_interrupt, mouse_interrupt},
serial::with_serial_console, serial::with_serial_console,
timer::TIMER, timer::TIMER,
}, },
+1
View File
@@ -1 +1,2 @@
pub const TIMER_FREQUENCY_HZ: usize = 1000; pub const TIMER_FREQUENCY_HZ: usize = 1000;
pub const KARCH: &str = "aarch64";
@@ -38,9 +38,9 @@ fn fd_ok(fd: Fd) -> bool {
fd >= 0 && (fd as usize) < MAX_FD fd >= 0 && (fd as usize) < MAX_FD
} }
static DOOM_WAD: &[u8] = include_bytes!("../../../../assets/doom1.wad"); static DOOM_WAD: &[u8] = include_bytes!("../../../../../assets/doom1.wad");
static DOOM_ELF: &[u8] = include_bytes!("../../../../assets/doomgeneric"); static DOOM_ELF: &[u8] = include_bytes!("../../../../../assets/doomgeneric");
static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../assets/helloworld.elf"); static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../../assets/helloworld.elf");
static FILES: &[FakeFileEntry] = &[ static FILES: &[FakeFileEntry] = &[
FakeFileEntry { FakeFileEntry {
+192
View File
@@ -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<char> {
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,
}
+6
View File
@@ -0,0 +1,6 @@
pub mod fs;
pub mod keyboard;
pub mod mouse;
#[cfg(target_arch = "x86_64")]
pub mod ps2;
pub mod virtio;
+45
View File
@@ -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();
+325
View File
@@ -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<u8> {
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<Queue<u8, 256>> = StaticCell::new();
static mut SCANCODE_PROD: Option<Producer<'static, u8>> = None;
static mut SCANCODE_CONS: Option<Consumer<'static, u8>> = None;
static mut KEYBOARD: Option<Keyboard<layouts::Us104Key, ScancodeSet2>> = 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<KeyboardEvent> {
#[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<u8> {
#[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)
}
+275
View File
@@ -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<Queue<(u8, u8, bool), 256>> = StaticCell::new();
static mut KEYCODE_PROD: Option<Producer<'static, (u8, u8, bool)>> = None;
static mut KEYCODE_CONS: Option<Consumer<'static, (u8, u8, bool)>> = 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"),
};
}
+141
View File
@@ -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<VirtioMmio> {
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
}
+124
View File
@@ -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::<VirtioInputEvent>() 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);
}
+49
View File
@@ -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);
}
}
}
-99
View File
@@ -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<Queue<u8, 256>> = StaticCell::new();
static mut SCANCODE_PROD: Option<Producer<'static, u8>> = None;
static mut SCANCODE_CONS: Option<Consumer<'static, u8>> = None;
static mut KEYBOARD: Option<Keyboard<layouts::Us104Key, ScancodeSet2>> = 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<KeyboardEvent> {
#[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;
}
}
-190
View File
@@ -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<u8> {
#[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();
+1 -3
View File
@@ -1,7 +1,5 @@
pub mod elf; pub mod elf;
pub mod fs;
pub mod graphics; pub mod graphics;
pub mod keyboard; pub mod io;
pub mod kmi;
pub mod serial; pub mod serial;
pub mod timer; pub mod timer;
+44 -21
View File
@@ -4,9 +4,33 @@
#![feature(naked_functions_rustic_abi)] #![feature(naked_functions_rustic_abi)]
extern crate alloc; extern crate alloc;
use core::fmt::Write; 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")] #[cfg(target_arch = "aarch64")]
use aarch64_cpu::registers::{DAIF, Writeable}; 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::BaseRevision;
use limine::request::{ use limine::request::{
DateAtBootRequest, ExecutableAddressRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest, DateAtBootRequest, ExecutableAddressRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest,
@@ -19,21 +43,6 @@ pub mod mm;
pub mod task; pub mod task;
pub mod util; 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))] #[repr(C, align(16))]
#[allow(dead_code)] #[allow(dead_code)]
struct AlignedElf([u8; include_bytes!("../../assets/init").len()]); struct AlignedElf([u8; include_bytes!("../../assets/init").len()]);
@@ -78,6 +87,9 @@ static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
#[unsafe(link_section = ".requests_end_marker")] #[unsafe(link_section = ".requests_end_marker")]
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); 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_export]
macro_rules! print { macro_rules! print {
($($arg:tt)*) => { ($($arg:tt)*) => {
@@ -124,24 +136,35 @@ unsafe extern "C" fn kmain() -> ! {
); );
#[allow(unused_variables)] #[allow(unused_variables)]
if let Some(memory_map_response) = MEMORY_MAP_REQUEST.get_response() { 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() { 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; use crate::arch::aarch64::init::preinit_aarch64;
#[cfg(target_arch = "aarch64")]
preinit_aarch64( preinit_aarch64(
hhdm_response, hhdm_response,
memory_map_response, memory_map_response,
executable_address_response, executable_address_response,
); );
loop {}
} else {
kernel_crash()
}
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
unsafe { unsafe {
kernel_main_x86_64() kernel_main_x86_64()
} }
loop {}
} else {
kernel_crash()
}
} else { } else {
kernel_crash(); // Could not get required info from Limine's memory map. kernel_crash(); // Could not get required info from Limine's memory map.
} }
+2 -1
View File
@@ -1,7 +1,8 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use crate::{ 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 { pub enum ProcessState {