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
13 changed files with 284 additions and 370 deletions

67
Cargo.lock generated
View File

@@ -24,6 +24,7 @@ version = "0.1.0"
dependencies = [
"bootloader",
"conquer-once",
"cooked-waker",
"crossbeam-queue",
"futures-util",
"lazy_static",
@@ -38,9 +39,9 @@ dependencies = [
[[package]]
name = "bootloader"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ed4f735c4e455ba86a3d2939b1c0729414153642106c9d035693355630a42c"
checksum = "152a28c753e229e037e910b4cd4cd16a90c53dd9a67fd751fa304b4b4a03970c"
[[package]]
name = "cfg-if"
@@ -63,6 +64,27 @@ 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]]
name = "cpuio"
version = "0.2.0"
@@ -160,6 +182,24 @@ 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]]
name = "scopeguard"
version = "1.1.0"
@@ -181,6 +221,23 @@ dependencies = [
"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]]
name = "uart_16550"
version = "0.2.4"
@@ -191,6 +248,12 @@ dependencies = [
"x86_64",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "volatile"
version = "0.2.6"

View File

@@ -13,7 +13,7 @@ name = "stack_overflow"
harness = false
[dependencies]
bootloader = { version = "0.8.0", features = ["map_physical_memory"]}
bootloader = { version = "0.8.8", features = ["map_physical_memory"]}
volatile = "0.2.6"
spin = "0.5.2"
x86_64 = "0.9.6"
@@ -21,6 +21,7 @@ uart_16550 = "0.2.0"
pic8259_simple = "0.1.1"
pc-keyboard = "0.5.0"
linked_list_allocator = "0.8.0"
cooked-waker = "1.0.2"
[dependencies.lazy_static]
version = "1.0"
@@ -31,15 +32,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
[dependencies.futures-util]
version = "0.3.4"
default-features = false
features = ["alloc"]
[package.metadata.bootimage]
test-args = [
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio",

View File

@@ -1,75 +0,0 @@
use crate::{print, println, task::interrupt_wake};
use conquer_once::spin::OnceCell;
use core::future::Future;
use core::{
pin::Pin,
task::{Context, Poll},
};
use crossbeam_queue::ArrayQueue;
use futures_util::task::AtomicWaker;
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
static WAKER: AtomicWaker = AtomicWaker::new();
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
pub fn init() {
SCANCODE_QUEUE
.try_init_once(|| ArrayQueue::new(10))
.expect("failed to init scancode queue");
}
/// Called by the keyboard interrupt handler
///
/// Must not block (including spinlocks).
pub(crate) fn keyboard_scancode(scancode: u8) {
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) = WAKER.take() {
interrupt_wake(waker);
}
}
fn next_scancode() -> impl Future<Output = u8> {
NextScancode
}
struct NextScancode;
impl Future for NextScancode {
type Output = u8;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<u8> {
let scancodes = SCANCODE_QUEUE
.try_get()
.expect("scancode queue not initialized");
// fast path
if let Ok(scancode) = scancodes.pop() {
return Poll::Ready(scancode);
}
WAKER.register(&cx.waker());
match scancodes.pop() {
Ok(scancode) => Poll::Ready(scancode),
Err(crossbeam_queue::PopError) => Poll::Pending,
}
}
}
pub async fn keyboard_task() {
let mut keyboard = Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore);
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),
}
}
}
}
}

View File

@@ -1,6 +0,0 @@
pub mod keyboard;
pub mod timer;
pub fn init() {
keyboard::init();
}

View File

@@ -1,55 +0,0 @@
use crate::{print, task::interrupt_wake};
use core::future::Future;
use core::{
pin::Pin,
sync::atomic::{AtomicU64, Ordering},
task::{Context, Poll},
};
use futures_util::task::AtomicWaker;
static TICKS: AtomicU64 = AtomicU64::new(0);
static WAKER: AtomicWaker = AtomicWaker::new();
/// Called by the timer interrupt handler
///
/// Must not block (including spinlocks).
pub(crate) fn tick() {
TICKS.fetch_add(1, Ordering::Release);
if let Some(waker) = WAKER.take() {
interrupt_wake(waker);
}
}
fn next_tick() -> impl Future<Output = u64> {
static NEXT_TICK: AtomicU64 = AtomicU64::new(1);
NextTick {
ticks: NEXT_TICK.fetch_add(1, Ordering::Release),
}
}
struct NextTick {
ticks: u64,
}
impl Future for NextTick {
type Output = u64;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<u64> {
WAKER.register(&cx.waker());
let current_ticks = TICKS.load(Ordering::Acquire);
if self.ticks < current_ticks {
self.ticks += 1;
Poll::Ready(self.ticks)
} else {
Poll::Pending
}
}
}
pub async fn timer_task() {
loop {
next_tick().await;
print!(".");
}
}

View File

@@ -1,4 +1,4 @@
use crate::{gdt, hlt_loop, println};
use crate::{gdt, hlt_loop, print, println};
use lazy_static::lazy_static;
use pic8259_simple::ChainedPics;
use spin;
@@ -72,8 +72,7 @@ extern "x86-interrupt" fn double_fault_handler(
}
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
crate::driver::timer::tick();
print!(".");
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
@@ -82,10 +81,10 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptSt
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
use x86_64::instructions::port::Port;
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
crate::driver::keyboard::keyboard_scancode(scancode);
crate::task::keyboard::add_scancode(scancode);
unsafe {
PICS.lock()

View File

@@ -5,10 +5,7 @@
#![feature(alloc_error_handler)]
#![feature(const_fn)]
#![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"]
@@ -17,7 +14,6 @@ extern crate alloc;
use core::panic::PanicInfo;
pub mod allocator;
pub mod driver;
pub mod gdt;
pub mod interrupts;
pub mod memory;
@@ -29,11 +25,7 @@ pub fn init() {
gdt::init();
interrupts::init_idt();
unsafe { interrupts::PICS.lock().initialize() };
}
pub fn init_heap_structures() {
task::init();
driver::init();
x86_64::instructions::interrupts::enable();
}
pub fn test_runner(tests: &[&dyn Fn()]) {

View File

@@ -6,7 +6,6 @@
extern crate alloc;
use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};
use blog_os::println;
use bootloader::{entry_point, BootInfo};
use core::panic::PanicInfo;
@@ -16,6 +15,7 @@ entry_point!(kernel_main);
fn kernel_main(boot_info: &'static BootInfo) -> ! {
use blog_os::allocator;
use blog_os::memory::{self, BootInfoFrameAllocator};
use blog_os::task::{keyboard, simple_executor::SimpleExecutor, Task};
use x86_64::VirtAddr;
println!("Hello World{}", "!");
@@ -26,50 +26,26 @@ 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::init_heap_structures();
// allocate a number on the heap
let heap_value = Box::new(41);
println!("heap_value at {:p}", heap_value);
// 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)
);
use blog_os::task::executor::Executor;
let mut executor = Executor::new();
executor.spawn(bar());
executor.spawn(async {
#[cfg(test)]
test_main();
});
executor.spawn(async {
println!("It did not crash!");
});
executor.spawn(blog_os::driver::timer::timer_task());
executor.spawn(blog_os::driver::keyboard::keyboard_task());
let mut executor = SimpleExecutor::new();
executor.spawn(Task::new(example_task()));
executor.spawn(Task::new(keyboard::print_keypresses()));
executor.run();
#[cfg(test)]
test_main();
println!("It did not crash!");
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.
@@ -85,15 +61,3 @@ fn panic(info: &PanicInfo) -> ! {
fn panic(info: &PanicInfo) -> ! {
blog_os::test_panic_handler(info)
}
async fn test() -> u32 {
42
}
async fn foo() -> u32 {
test().await * 2
}
async fn bar() {
println!("foo result: {}", foo().await);
}

View File

@@ -1,127 +0,0 @@
use super::interrupt_wakeups::interrupt_wakeups;
use crate::println;
use alloc::{
boxed::Box,
collections::{BTreeMap, VecDeque},
sync::Arc,
task::Wake,
};
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use crossbeam_queue::SegQueue;
pub type Task = Pin<Box<dyn Future<Output = ()>>>;
type TaskId = usize;
pub struct Executor {
task_queue: VecDeque<Task>,
wake_queue: Arc<SegQueue<TaskId>>,
pending_tasks: BTreeMap<TaskId, Task>,
}
impl Executor {
pub fn new() -> Self {
Executor {
task_queue: VecDeque::new(),
wake_queue: Arc::new(SegQueue::new()),
pending_tasks: BTreeMap::new(),
}
}
pub fn spawn(&mut self, task: impl Future<Output = ()> + 'static) {
self.task_queue.push_back(Box::pin(task))
}
pub fn run(&mut self) -> ! {
loop {
self.run_ready_tasks();
self.apply_interrupt_wakeups();
self.wake_waiting_tasks();
self.hlt_if_idle();
}
}
fn run_ready_tasks(&mut self) {
while let Some(mut task) = self.task_queue.pop_front() {
let waker = self.create_waker(&task).into();
let mut context = Context::from_waker(&waker);
match task.as_mut().poll(&mut context) {
Poll::Ready(()) => {} // task done
Poll::Pending => {
// add task to pending_tasks and wait for wakeup
let task_id = Self::task_id(&task);
if self.pending_tasks.insert(task_id, task).is_some() {
panic!("Task with same ID already in pending_tasks");
}
}
}
}
}
/// Invoke wakers for tasks woken by interrupts
///
/// The interrupt handlers can't invoke the waker directly since wakers
/// might execute arbitrary code, e.g. allocate, which should not be done
/// in interrupt handlers to avoid deadlocks.
fn apply_interrupt_wakeups(&mut self) {
while let Ok(waker) = interrupt_wakeups().pop() {
waker.wake();
}
}
fn wake_waiting_tasks(&mut self) {
while let Ok(task_id) = self.wake_queue.pop() {
if let Some(task) = self.pending_tasks.remove(&task_id) {
self.task_queue.push_back(task);
} else {
println!("WARNING: woken task not found in pending_tasks");
}
}
}
/// Executes the `hlt` instruction if there are no ready tasks
fn hlt_if_idle(&self) {
if self.task_queue.is_empty() {
// disable interrupts to avoid races
x86_64::instructions::interrupts::disable();
// check if relevant interrupts occured since the last check
if interrupt_wakeups().is_empty() {
// no interrupts occured -> hlt to wait for next interrupt
x86_64::instructions::interrupts::enable_interrupts_and_hlt();
} else {
// there were some new wakeups -> continue execution
x86_64::instructions::interrupts::enable();
}
}
}
fn task_id(task: &Task) -> TaskId {
let future_ref: &dyn Future<Output = ()> = &**task;
future_ref as *const _ as *const () as usize
}
fn create_waker(&self, task: &Task) -> Arc<Waker> {
Arc::new(Waker {
wake_queue: self.wake_queue.clone(),
task_id: Self::task_id(task),
})
}
}
pub struct Waker {
wake_queue: Arc<SegQueue<TaskId>>,
task_id: TaskId,
}
impl Wake for Waker {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.wake_queue.push(self.task_id);
}
}

View File

@@ -1,25 +0,0 @@
use crate::println;
use conquer_once::spin::OnceCell;
use core::task::Waker;
use crossbeam_queue::ArrayQueue;
static INTERRUPT_WAKEUPS: OnceCell<ArrayQueue<Waker>> = OnceCell::uninit();
pub fn init() {
INTERRUPT_WAKEUPS
.try_init_once(|| ArrayQueue::new(10))
.expect("failed to init interrupt wakeup queue");
}
/// Queues a waker for waking in an interrupt-safe way
pub(crate) fn interrupt_wake(waker: Waker) {
if let Err(_) = interrupt_wakeups().push(waker) {
println!("WARNING: dropping interrupt wakeup");
}
}
pub(super) fn interrupt_wakeups() -> &'static ArrayQueue<Waker> {
INTERRUPT_WAKEUPS
.try_get()
.expect("interrupt wakeup queue not initialized")
}

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),
}
}
}
}
}

View File

@@ -1,8 +1,26 @@
pub(crate) use interrupt_wakeups::interrupt_wake;
use alloc::boxed::Box;
use core::task::{Context, Poll};
use core::{future::Future, pin::Pin};
pub mod executor;
mod interrupt_wakeups;
pub mod keyboard;
pub mod simple_executor;
pub fn init() {
interrupt_wakeups::init();
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();
}
}