Compare commits

...

10 Commits

Author SHA1 Message Date
Philipp Oppermann
d21a78dc0b Switch back to official nightly 2020-03-22 17:30:00 +01:00
Philipp Oppermann
0186f65ece Use cooked-waker crate for creating wakers 2020-03-22 17:24:58 +01:00
Philipp Oppermann
2772abc8eb Implement proper wakeups using RawWaker 2020-03-22 16:54:47 +01:00
Philipp Oppermann
51f90236a5 Base dummy waker on RawWaker to avoid allocations
The Wake trait is based on Arc, which leads to deallocation on wakes. Since interrupts should not (de)allocate, this can lead to a deadlock.
2020-03-22 16:11:25 +01:00
Philipp Oppermann
90abd5c8c5 Add a keyboard task that prints keypresses 2020-03-22 12:45:24 +01:00
Philipp Oppermann
a6273614e4 Add waker support to ScancodeStream 2020-03-22 12:16:17 +01:00
Philipp Oppermann
3a2a468a0b Add a ScancodeStream based on the SCANCODE_QUEUE 2020-03-22 12:12:11 +01:00
Philipp Oppermann
05f6abd261 Fill the scancode queue on keyboard interrupts 2020-03-22 12:11:09 +01:00
Philipp Oppermann
255982a8b7 Implement scancode queue 2020-03-20 13:03:41 +01:00
Philipp Oppermann
f885f17b70 Implement a simple poll-loop executor 2020-03-19 16:46:37 +01:00
8 changed files with 366 additions and 45 deletions

146
Cargo.lock generated
View File

@@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]] [[package]]
name = "bit_field" name = "bit_field"
version = "0.9.0" version = "0.9.0"
@@ -17,6 +23,10 @@ name = "blog_os"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bootloader", "bootloader",
"conquer-once",
"cooked-waker",
"crossbeam-queue",
"futures-util",
"lazy_static", "lazy_static",
"linked_list_allocator", "linked_list_allocator",
"pc-keyboard", "pc-keyboard",
@@ -29,9 +39,51 @@ dependencies = [
[[package]] [[package]]
name = "bootloader" name = "bootloader"
version = "0.8.8" version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ed4f735c4e455ba86a3d2939b1c0729414153642106c9d035693355630a42c" checksum = "152a28c753e229e037e910b4cd4cd16a90c53dd9a67fd751fa304b4b4a03970c"
[[package]]
name = "cfg-if"
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 = "cooked-waker"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0032e8be0680b63daf14b0fd7fd57f85fd6897951e110b041d32e839a831914"
dependencies = [
"cooked-waker-derive",
"stowaway",
]
[[package]]
name = "cooked-waker-derive"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ded02b04a8ff53ea7328cd71297cde598bee70d165ad92512bdb7e20be9d74"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "cpuio" name = "cpuio"
@@ -39,6 +91,49 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa" checksum = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa"
[[package]]
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"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]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -81,6 +176,30 @@ dependencies = [
"cpuio", "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 = "proc-macro2"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@@ -102,6 +221,23 @@ dependencies = [
"lock_api", "lock_api",
] ]
[[package]]
name = "stowaway"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b13d61e782863c0eaeeaf7e8e580dc956a625851fd2fb73e5e92db9601c891"
[[package]]
name = "syn"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]] [[package]]
name = "uart_16550" name = "uart_16550"
version = "0.2.4" version = "0.2.4"
@@ -112,6 +248,12 @@ dependencies = [
"x86_64", "x86_64",
] ]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]] [[package]]
name = "volatile" name = "volatile"
version = "0.2.6" version = "0.2.6"

View File

@@ -13,7 +13,7 @@ name = "stack_overflow"
harness = false harness = false
[dependencies] [dependencies]
bootloader = { version = "0.8.0", features = ["map_physical_memory"]} bootloader = { version = "0.8.8", features = ["map_physical_memory"]}
volatile = "0.2.6" volatile = "0.2.6"
spin = "0.5.2" spin = "0.5.2"
x86_64 = "0.9.6" x86_64 = "0.9.6"
@@ -21,11 +21,25 @@ uart_16550 = "0.2.0"
pic8259_simple = "0.1.1" pic8259_simple = "0.1.1"
pc-keyboard = "0.5.0" pc-keyboard = "0.5.0"
linked_list_allocator = "0.8.0" linked_list_allocator = "0.8.0"
cooked-waker = "1.0.2"
[dependencies.lazy_static] [dependencies.lazy_static]
version = "1.0" version = "1.0"
features = ["spin_no_std"] features = ["spin_no_std"]
[dependencies.crossbeam-queue]
version = "0.2.1"
default-features = false
features = ["alloc"]
[dependencies.conquer-once]
version = "0.2.0"
default-features = false
[dependencies.futures-util]
version = "0.3.4"
default-features = false
features = ["alloc"]
[package.metadata.bootimage] [package.metadata.bootimage]
test-args = [ test-args = [

View File

@@ -80,28 +80,11 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptSt
} }
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port; use x86_64::instructions::port::Port;
lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)
);
}
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60); let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() }; let scancode: u8 = unsafe { port.read() };
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { crate::task::keyboard::add_scancode(scancode);
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => print!("{}", character),
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
unsafe { unsafe {
PICS.lock() PICS.lock()

View File

@@ -18,6 +18,7 @@ pub mod gdt;
pub mod interrupts; pub mod interrupts;
pub mod memory; pub mod memory;
pub mod serial; pub mod serial;
pub mod task;
pub mod vga_buffer; pub mod vga_buffer;
pub fn init() { pub fn init() {

View File

@@ -6,7 +6,6 @@
extern crate alloc; extern crate alloc;
use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};
use blog_os::println; use blog_os::println;
use bootloader::{entry_point, BootInfo}; use bootloader::{entry_point, BootInfo};
use core::panic::PanicInfo; use core::panic::PanicInfo;
@@ -16,6 +15,7 @@ entry_point!(kernel_main);
fn kernel_main(boot_info: &'static BootInfo) -> ! { fn kernel_main(boot_info: &'static BootInfo) -> ! {
use blog_os::allocator; use blog_os::allocator;
use blog_os::memory::{self, BootInfoFrameAllocator}; use blog_os::memory::{self, BootInfoFrameAllocator};
use blog_os::task::{keyboard, simple_executor::SimpleExecutor, Task};
use x86_64::VirtAddr; use x86_64::VirtAddr;
println!("Hello World{}", "!"); println!("Hello World{}", "!");
@@ -27,29 +27,10 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
// allocate a number on the heap let mut executor = SimpleExecutor::new();
let heap_value = Box::new(41); executor.spawn(Task::new(example_task()));
println!("heap_value at {:p}", heap_value); executor.spawn(Task::new(keyboard::print_keypresses()));
executor.run();
// create a dynamically sized vector
let mut vec = Vec::new();
for i in 0..500 {
vec.push(i);
}
println!("vec at {:p}", vec.as_slice());
// create a reference counted vector -> will be freed when count reaches 0
let reference_counted = Rc::new(vec![1, 2, 3]);
let cloned_reference = reference_counted.clone();
println!(
"current reference count is {}",
Rc::strong_count(&cloned_reference)
);
core::mem::drop(reference_counted);
println!(
"reference count is {} now",
Rc::strong_count(&cloned_reference)
);
#[cfg(test)] #[cfg(test)]
test_main(); test_main();
@@ -58,6 +39,15 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
blog_os::hlt_loop(); blog_os::hlt_loop();
} }
async fn async_number() -> u32 {
42
}
async fn example_task() {
let number = async_number().await;
println!("async number: {}", number);
}
/// This function is called on panic. /// This function is called on panic.
#[cfg(not(test))] #[cfg(not(test))]
#[panic_handler] #[panic_handler]

79
src/task/keyboard.rs Normal file
View File

@@ -0,0 +1,79 @@
use crate::print;
use crate::println;
use conquer_once::spin::OnceCell;
use core::{
pin::Pin,
task::{Context, Poll},
};
use crossbeam_queue::ArrayQueue;
use futures_util::stream::StreamExt;
use futures_util::{stream::Stream, task::AtomicWaker};
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
static WAKER: AtomicWaker = AtomicWaker::new();
/// Called by the keyboard interrupt handler
///
/// Must not block or allocate.
pub(crate) fn add_scancode(scancode: u8) {
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
if let Err(_) = queue.push(scancode) {
println!("WARNING: scancode queue full; dropping keyboard input");
} else {
WAKER.wake();
}
} else {
println!("WARNING: scancode queue uninitialized");
}
}
pub struct ScancodeStream {
_private: (),
}
impl ScancodeStream {
pub fn new() -> Self {
SCANCODE_QUEUE
.try_init_once(|| ArrayQueue::new(100))
.expect("ScancodeStream::new should only be called once");
ScancodeStream { _private: () }
}
}
impl Stream for ScancodeStream {
type Item = u8;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
let queue = SCANCODE_QUEUE
.try_get()
.expect("scancode queue not initialized");
// fast path
if let Ok(scancode) = queue.pop() {
return Poll::Ready(Some(scancode));
}
WAKER.register(&cx.waker());
match queue.pop() {
Ok(scancode) => Poll::Ready(Some(scancode)),
Err(crossbeam_queue::PopError) => Poll::Pending,
}
}
}
pub async fn print_keypresses() {
let mut scancodes = ScancodeStream::new();
let mut keyboard = Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore);
while let Some(scancode) = scancodes.next().await {
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),
}
}
}
}
}

26
src/task/mod.rs Normal file
View File

@@ -0,0 +1,26 @@
use alloc::boxed::Box;
use core::task::{Context, Poll};
use core::{future::Future, pin::Pin};
pub mod keyboard;
pub mod simple_executor;
pub struct Task {
future: Pin<Box<dyn Future<Output = ()>>>,
}
impl Task {
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
Task {
future: Box::pin(future),
}
}
fn poll(&mut self, context: &mut Context) -> Poll<()> {
self.future.as_mut().poll(context)
}
fn id(&self) -> usize {
&*self.future as *const _ as *const () as usize
}
}

View File

@@ -0,0 +1,86 @@
use super::Task;
use alloc::{
collections::{BTreeMap, VecDeque},
sync::Arc,
};
use cooked_waker::IntoWaker;
use core::task::{Context, Poll};
use crossbeam_queue::ArrayQueue;
pub struct SimpleExecutor {
task_queue: VecDeque<Task>,
waiting_tasks: BTreeMap<usize, Task>,
wake_queue: Arc<ArrayQueue<usize>>,
}
impl SimpleExecutor {
pub fn new() -> SimpleExecutor {
SimpleExecutor {
task_queue: VecDeque::new(),
waiting_tasks: BTreeMap::new(),
wake_queue: Arc::new(ArrayQueue::new(100)),
}
}
pub fn spawn(&mut self, task: Task) {
self.task_queue.push_back(task)
}
pub fn run(&mut self) {
loop {
self.handle_wakeups();
self.run_ready_tasks();
}
}
fn handle_wakeups(&mut self) {
while let Ok(task_id) = self.wake_queue.pop() {
if let Some(task) = self.waiting_tasks.remove(&task_id) {
self.task_queue.push_back(task);
}
}
}
fn run_ready_tasks(&mut self) {
while let Some(mut task) = self.task_queue.pop_front() {
let waker = TaskWaker {
task_id: task.id(),
wake_queue: self.wake_queue.clone(),
}
.into_waker();
let mut context = Context::from_waker(&waker);
match task.poll(&mut context) {
Poll::Ready(()) => {} // task done
Poll::Pending => {
if self.waiting_tasks.insert(task.id(), task).is_some() {
panic!("Same task inserted into waiting_tasks twice");
}
}
}
}
}
}
#[derive(Debug, Clone, IntoWaker)]
struct TaskWaker {
task_id: usize,
wake_queue: Arc<ArrayQueue<usize>>,
}
impl TaskWaker {
fn wake_task(&self) {
self.wake_queue.push(self.task_id).expect("wake queue full");
}
}
impl cooked_waker::WakeRef for TaskWaker {
fn wake_by_ref(&self) {
self.wake_task();
}
}
impl cooked_waker::Wake for TaskWaker {
fn wake(self) {
self.wake_task();
}
}