From 786a7a6922cbf84d36e91faea3efbe92de7004d7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 28 Feb 2020 17:46:29 +0100 Subject: [PATCH] Use crossbeam-queue and AtomicWaker for async keypress handling --- Cargo.lock | 46 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++++++++ src/driver/keyboard.rs | 48 +++++++++++++++++++++++++++++++++++++++++ src/driver/mod.rs | 1 + src/interrupts.rs | 49 +++++++++++++++++++++++++++++------------- src/lib.rs | 4 +++- src/main.rs | 4 ++++ src/task/executor.rs | 24 +++++++++++++++------ 8 files changed, 163 insertions(+), 22 deletions(-) create mode 100644 src/driver/keyboard.rs create mode 100644 src/driver/mod.rs diff --git a/Cargo.lock b/Cargo.lock index bf4bb669..5898ca55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,7 +32,9 @@ name = "blog_os" version = "0.1.0" dependencies = [ "bootloader", + "conquer-once", "crossbeam-queue", + "futures-util", "lazy_static", "linked_list_allocator", "pc-keyboard", @@ -64,6 +66,21 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "conquer-once" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7644600a548ecad74e4a918392af1798f7dd045be610be3203b9e129b4f98f" +dependencies = [ + "conquer-util", +] + +[[package]] +name = "conquer-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" + [[package]] name = "cpuio" version = "0.2.0" @@ -90,6 +107,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" + +[[package]] +name = "futures-task" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +dependencies = [ + "futures-core", + "futures-task", + "pin-utils", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -129,6 +169,12 @@ dependencies = [ "cpuio", ] +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" + [[package]] name = "rustc_version" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index c5864d92..950db65a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,15 @@ version = "0.2.1" default-features = false features = ["alloc"] +[dependencies.futures-util] +version = "0.3.4" +default-features = false +features = ["alloc", "async-await"] + +[dependencies.conquer-once] +version = "0.2.0" +default-features = false + [package.metadata.bootimage] test-args = [ "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio", diff --git a/src/driver/keyboard.rs b/src/driver/keyboard.rs new file mode 100644 index 00000000..316eb238 --- /dev/null +++ b/src/driver/keyboard.rs @@ -0,0 +1,48 @@ +use crate::{interrupts, print}; +use core::future::Future; +use core::{ + pin::Pin, + task::{Context, Poll}, +}; +use pc_keyboard::{layouts, DecodedKey, Keyboard, ScancodeSet1}; + +fn next_scancode() -> impl Future { + NextScancode +} + +struct NextScancode; + +impl Future for NextScancode { + type Output = u8; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let scancodes = interrupts::SCANCODE_QUEUE + .try_get() + .expect("scancode queue not initialized"); + // fast path + if let Ok(scancode) = scancodes.pop() { + return Poll::Ready(scancode); + } + + interrupts::KEYBOARD_INTERRUPT_WAKER.register(&cx.waker()); + match scancodes.pop() { + Ok(scancode) => Poll::Ready(scancode), + Err(crossbeam_queue::PopError) => Poll::Pending, + } + } +} + +pub async fn print_keypresses() { + let mut keyboard = Keyboard::new(layouts::Us104Key, ScancodeSet1); + + loop { + if let Ok(Some(key_event)) = keyboard.add_byte(next_scancode().await) { + if let Some(key) = keyboard.process_keyevent(key_event) { + match key { + DecodedKey::Unicode(character) => print!("{}", character), + DecodedKey::RawKey(key) => print!("{:?}", key), + } + } + } + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs new file mode 100644 index 00000000..45f5b56d --- /dev/null +++ b/src/driver/mod.rs @@ -0,0 +1 @@ +pub mod keyboard; diff --git a/src/interrupts.rs b/src/interrupts.rs index c833af7f..6fe17c4a 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,4 +1,8 @@ use crate::{gdt, hlt_loop, print, println}; +use conquer_once::spin::OnceCell; +use core::task::Waker; +use crossbeam_queue::ArrayQueue; +use futures_util::task::AtomicWaker; use lazy_static::lazy_static; use pic8259_simple::ChainedPics; use spin; @@ -47,6 +51,23 @@ pub fn init_idt() { IDT.load(); } +pub fn init_queues() { + INTERRUPT_WAKEUPS + .try_init_once(|| ArrayQueue::new(10)) + .expect("failed to init interrupt wakeup queue"); + SCANCODE_QUEUE + .try_init_once(|| ArrayQueue::new(10)) + .expect("failed to init scancode queue"); +} + +static INTERRUPT_WAKEUPS: OnceCell> = OnceCell::uninit(); + +pub(crate) fn interrupt_wakeups() -> &'static ArrayQueue { + INTERRUPT_WAKEUPS + .try_get() + .expect("interrupt wakeup queue not initialized") +} + extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) { println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); } @@ -79,26 +100,24 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptSt } } +pub(crate) static SCANCODE_QUEUE: OnceCell> = OnceCell::uninit(); +pub(crate) static KEYBOARD_INTERRUPT_WAKER: AtomicWaker = AtomicWaker::new(); + extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { - use pc_keyboard::{layouts, DecodedKey, Keyboard, ScancodeSet1}; - use spin::Mutex; use x86_64::instructions::port::Port; - lazy_static! { - static ref KEYBOARD: Mutex> = - Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1)); - } - - let mut keyboard = KEYBOARD.lock(); let mut port = Port::new(0x60); - let scancode: u8 = unsafe { port.read() }; - if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { - if let Some(key) = keyboard.process_keyevent(key_event) { - match key { - DecodedKey::Unicode(character) => print!("{}", character), - DecodedKey::RawKey(key) => print!("{:?}", key), - } + + let scancode_queue = SCANCODE_QUEUE + .try_get() + .expect("scancode queue not initialized"); + if let Err(_) = scancode_queue.push(scancode) { + println!("WARNING: dropping keyboard input"); + } + if let Some(waker) = KEYBOARD_INTERRUPT_WAKER.take() { + if let Err(_) = interrupt_wakeups().push(waker) { + println!("WARNING: dropping interrupt wakeup"); } } diff --git a/src/lib.rs b/src/lib.rs index 5550f263..b6fc2750 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ #![feature(alloc_layout_extra)] #![feature(wake_trait)] #![feature(const_in_array_repeat_expressions)] +#![feature(type_alias_impl_trait)] +#![feature(asm)] #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] @@ -15,6 +17,7 @@ extern crate alloc; use core::panic::PanicInfo; pub mod allocator; +pub mod driver; pub mod gdt; pub mod interrupts; pub mod memory; @@ -26,7 +29,6 @@ pub fn init() { gdt::init(); interrupts::init_idt(); unsafe { interrupts::PICS.lock().initialize() }; - x86_64::instructions::interrupts::enable(); } pub fn test_runner(tests: &[&dyn Fn()]) { diff --git a/src/main.rs b/src/main.rs index 16a46706..5640cb70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,8 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); + blog_os::interrupts::init_queues(); + x86_64::instructions::interrupts::enable(); // allocate a number on the heap let heap_value = Box::new(41); @@ -66,6 +68,8 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { println!("It did not crash!"); }); + spawner.spawn(blog_os::driver::keyboard::print_keypresses()); + executor.run(); } diff --git a/src/task/executor.rs b/src/task/executor.rs index 2aeed4e5..d35f370b 100644 --- a/src/task/executor.rs +++ b/src/task/executor.rs @@ -1,3 +1,4 @@ +use crate::{interrupts, println}; use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, task::Wake}; use core::{ future::Future, @@ -34,13 +35,19 @@ impl Executor { pub fn run(&mut self) -> ! { loop { + // perform wakeups caused by interrupts + // the interrupt handlers can't do it themselves since wakers might execute + // arbitrary code, e.g. allocate + while let Ok(waker) = interrupts::interrupt_wakeups().pop() { + waker.wake(); + } // wakeup waiting tasks while let Ok(task_id) = self.wake_queue.pop() { - let task = self - .pending_tasks - .remove(&task_id) - .expect("woken task not found in pending_tasks"); - self.task_queue.push(task); + if let Some(task) = self.pending_tasks.remove(&task_id) { + self.task_queue.push(task); + } else { + println!("WARNING: woken task not found in pending_tasks"); + } } // run ready tasks while let Ok(mut task) = self.task_queue.pop() { @@ -57,7 +64,12 @@ impl Executor { } // wait for next interrupt if there is nothing left to do if self.wake_queue.is_empty() { - crate::hlt_loop(); + unsafe { asm!("cli") }; + if self.wake_queue.is_empty() { + unsafe { asm!("sti; hlt") }; + } else { + unsafe { asm!("sti") }; + } } } }