Compare commits

..

4 Commits

Author SHA1 Message Date
Philipp Oppermann
ca8cd46863 Update 2020-03-22 11:58:16 +01:00
Philipp Oppermann
816f8746fb Implement a ScancodeStream type 2020-03-20 14:14:45 +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
18 changed files with 128 additions and 319 deletions

77
Cargo.lock generated
View File

@@ -33,14 +33,12 @@ dependencies = [
"spin",
"uart_16550",
"volatile",
"x86_64 0.11.1",
"x86_64",
]
[[package]]
name = "bootloader"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44ac0bdf4930c3c4d7f0d04eb6f15d7dcb9d5972b1ff9cd2bee0128112260fc7"
version = "0.8.8"
[[package]]
name = "cfg-if"
@@ -65,9 +63,9 @@ checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a"
[[package]]
name = "cpuio"
version = "0.3.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d531514efb06912141fa65967447de805691b685a7565c87d1765afe34a98aa7"
checksum = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa"
[[package]]
name = "crossbeam-queue"
@@ -147,9 +145,9 @@ checksum = "c48392db76c4e9a69e0b3be356c5f97ebb7b14413c5e4fd0af4755dbf86e2fce"
[[package]]
name = "pic8259_simple"
version = "0.2.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af2a5497fb8e59bf8015f67b7dff238d75ef445e03f23edac24ac3a8f09be952"
checksum = "dc64b2fd10828da8521b6cdabe0679385d7d2a3a6d4c336b819d1fa31ba35c72"
dependencies = [
"cpuio",
]
@@ -160,24 +158,6 @@ 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.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -199,33 +179,16 @@ dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "uart_16550"
version = "0.2.7"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e58fc40dc1712664fc9b0a7bd8ca2f21ab49960924fb245a80a05e1e92f3dfe9"
checksum = "d44b0f30cb82b0fbc15b78ade1064226529ad52028bc8cb8accb98ff6f3d7131"
dependencies = [
"bitflags",
"x86_64 0.11.0",
"x86_64",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "volatile"
version = "0.2.6"
@@ -234,28 +197,10 @@ checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29"
[[package]]
name = "x86_64"
version = "0.11.0"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f"
checksum = "4206b60c9f99766329b66962aa8ddc01df6c7edd02edc046b7a69d5df9fcdbcf"
dependencies = [
"bit_field",
"bitflags",
]
[[package]]
name = "x86_64"
version = "0.11.1"
dependencies = [
"bit_field",
"bitflags",
"x86_64-idt-general-handler",
]
[[package]]
name = "x86_64-idt-general-handler"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -13,12 +13,12 @@ name = "stack_overflow"
harness = false
[dependencies]
bootloader = { version = "0.9.3", features = ["map_physical_memory"]}
bootloader = { version = "0.8.0", features = ["map_physical_memory"]}
volatile = "0.2.6"
spin = "0.5.2"
x86_64 = { path = "../../x86_64" }
x86_64 = "0.9.6"
uart_16550 = "0.2.0"
pic8259_simple = "0.2.0"
pic8259_simple = "0.1.1"
pc-keyboard = "0.5.0"
linked_list_allocator = "0.8.0"
@@ -38,7 +38,7 @@ default-features = false
[dependencies.futures-util]
version = "0.3.4"
default-features = false
features = ["alloc"]
features = ["alloc", "async-await"]
[package.metadata.bootimage]
test-args = [

View File

@@ -1,10 +1,10 @@
# Blog OS (Async/Await)
# Blog OS (Heap Allocation)
[![Build Status](https://github.com/phil-opp/blog_os/workflows/Build%20Code/badge.svg?branch=post-12)](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Build+Code%22+branch%3Apost-12)
[![Build Status](https://github.com/phil-opp/blog_os/workflows/Build%20Code/badge.svg?branch=post-10)](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Build+Code%22+branch%3Apost-10)
This repository contains the source code for the [Async/Await][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.
This repository contains the source code for the [Heap Allocation][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.
[post]: https://os.phil-opp.com/async-await/
[post]: https://os.phil-opp.com/heap-allocation/
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**

View File

@@ -35,7 +35,7 @@ pub fn init_heap(
.allocate_frame()
.ok_or(MapToError::FrameAllocationFailed)?;
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() };
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
}
unsafe {

View File

@@ -45,7 +45,7 @@ impl LinkedListAllocator {
/// Adds the given memory region to the front of the list.
unsafe fn add_free_region(&mut self, addr: usize, size: usize) {
// ensure that the freed region is capable of holding ListNode
assert_eq!(align_up(addr, mem::align_of::<ListNode>()), addr);
assert!(align_up(addr, mem::align_of::<ListNode>()) == addr);
assert!(size >= mem::size_of::<ListNode>());
// create a new list node and append it at the start of the list

View File

@@ -2,9 +2,7 @@ use crate::{gdt, hlt_loop, print, println};
use lazy_static::lazy_static;
use pic8259_simple::ChainedPics;
use spin;
use x86_64::structures::idt::{
self, InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode,
};
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
@@ -32,8 +30,6 @@ pub static PICS: spin::Mutex<ChainedPics> =
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt::set_general_handler!(&mut idt, default_handler);
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.page_fault.set_handler_fn(page_fault_handler);
unsafe {
@@ -41,6 +37,8 @@ lazy_static! {
.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
idt
};
}
@@ -49,18 +47,6 @@ pub fn init_idt() {
IDT.load();
}
fn default_handler(stack_frame: &mut InterruptStackFrame, index: u8, error_code: Option<u64>) {
if index == 32 {
print!("{} ", index);
} else {
println!("INTERRUPT {}: \n{:#?}", index, stack_frame);
}
unsafe {
PICS.lock().notify_end_of_interrupt(index);
}
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
@@ -94,11 +80,28 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptSt
}
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;
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 scancode: u8 = unsafe { port.read() };
crate::task::keyboard::add_scancode(scancode);
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),
}
}
}
unsafe {
PICS.lock()
@@ -106,8 +109,13 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut Interrup
}
}
#[cfg(test)]
use crate::{serial_print, serial_println};
#[test_case]
fn test_breakpoint_exception() {
serial_print!("test_breakpoint_exception...");
// invoke a breakpoint exception
x86_64::instructions::interrupts::int3();
serial_println!("[ok]");
}

View File

@@ -4,8 +4,10 @@
#![feature(abi_x86_interrupt)]
#![feature(alloc_error_handler)]
#![feature(const_fn)]
#![feature(alloc_layout_extra)]
#![feature(const_in_array_repeat_expressions)]
#![feature(wake_trait)]
#![feature(async_closure)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
@@ -27,25 +29,11 @@ pub fn init() {
unsafe { interrupts::PICS.lock().initialize() };
x86_64::instructions::interrupts::enable();
}
pub trait Testable {
fn run(&self) -> ();
}
impl<T> Testable for T
where
T: Fn(),
{
fn run(&self) {
serial_print!("{}...\t", core::any::type_name::<T>());
self();
serial_println!("[ok]");
}
}
pub fn test_runner(tests: &[&dyn Testable]) {
pub fn test_runner(tests: &[&dyn Fn()]) {
serial_println!("Running {} tests", tests.len());
for test in tests {
test.run();
test();
}
exit_qemu(QemuExitCode::Success);
}

View File

@@ -7,7 +7,6 @@
extern crate alloc;
use blog_os::println;
use blog_os::task::{executor::Executor, keyboard, Task};
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::{simple_executor::SimpleExecutor, Task, keyboard};
use x86_64::VirtAddr;
println!("Hello World{}", "!");
@@ -27,13 +27,25 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
#[cfg(test)]
test_main();
let mut executor = Executor::new();
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.
@@ -49,17 +61,3 @@ fn panic(info: &PanicInfo) -> ! {
fn panic(info: &PanicInfo) -> ! {
blog_os::test_panic_handler(info)
}
async fn async_number() -> u32 {
42
}
async fn example_task() {
let number = async_number().await;
println!("async number: {}", number);
}
#[test_case]
fn trivial_assertion() {
assert_eq!(1, 1);
}

View File

@@ -2,6 +2,7 @@ use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
use x86_64::{
structures::paging::{
FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
UnusedPhysFrame,
},
PhysAddr, VirtAddr,
};
@@ -44,12 +45,11 @@ pub fn create_example_mapping(
use x86_64::structures::paging::PageTableFlags as Flags;
let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
// FIXME: ONLY FOR TEMPORARY TESTING
let unused_frame = unsafe { UnusedPhysFrame::new(frame) };
let flags = Flags::PRESENT | Flags::WRITABLE;
let map_to_result = unsafe {
// FIXME: this is not safe, we do it only for testing
mapper.map_to(page, frame, flags, frame_allocator)
};
let map_to_result = mapper.map_to(page, unused_frame, flags, frame_allocator);
map_to_result.expect("map_to failed").flush();
}
@@ -57,7 +57,7 @@ pub fn create_example_mapping(
pub struct EmptyFrameAllocator;
unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame> {
fn allocate_frame(&mut self) -> Option<UnusedPhysFrame> {
None
}
}
@@ -82,7 +82,7 @@ impl BootInfoFrameAllocator {
}
/// Returns an iterator over the usable frames specified in the memory map.
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
fn usable_frames(&self) -> impl Iterator<Item = UnusedPhysFrame> {
// get usable regions from memory map
let regions = self.memory_map.iter();
let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable);
@@ -91,12 +91,14 @@ impl BootInfoFrameAllocator {
// transform to an iterator of frame start addresses
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
// create `PhysFrame` types from the start addresses
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
let frames = frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)));
// we know that the frames are really unused
frames.map(|f| unsafe { UnusedPhysFrame::new(f) })
}
}
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame> {
fn allocate_frame(&mut self) -> Option<UnusedPhysFrame> {
let frame = self.usable_frames().nth(self.next);
self.next += 1;
frame

View File

@@ -1,102 +0,0 @@
use super::{Task, TaskId};
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
use core::task::{Context, Poll, Waker};
use crossbeam_queue::ArrayQueue;
pub struct Executor {
tasks: BTreeMap<TaskId, Task>,
task_queue: Arc<ArrayQueue<TaskId>>,
waker_cache: BTreeMap<TaskId, Waker>,
}
impl Executor {
pub fn new() -> Self {
Executor {
tasks: BTreeMap::new(),
task_queue: Arc::new(ArrayQueue::new(100)),
waker_cache: BTreeMap::new(),
}
}
pub fn spawn(&mut self, task: Task) {
let task_id = task.id;
if self.tasks.insert(task.id, task).is_some() {
panic!("task with same ID already in tasks");
}
self.task_queue.push(task_id).expect("queue full");
}
pub fn run(&mut self) -> ! {
loop {
self.run_ready_tasks();
self.sleep_if_idle();
}
}
fn run_ready_tasks(&mut self) {
// destructure `self` to avoid borrow checker errors
let Self {
tasks,
task_queue,
waker_cache,
} = self;
while let Ok(task_id) = task_queue.pop() {
let task = match tasks.get_mut(&task_id) {
Some(task) => task,
None => continue, // task no longer exists
};
let waker = waker_cache
.entry(task_id)
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
let mut context = Context::from_waker(waker);
match task.poll(&mut context) {
Poll::Ready(()) => {
// task done -> remove it and its cached waker
tasks.remove(&task_id);
waker_cache.remove(&task_id);
}
Poll::Pending => {}
}
}
}
fn sleep_if_idle(&self) {
use x86_64::instructions::interrupts::{self, enable_interrupts_and_hlt};
interrupts::disable();
if self.task_queue.is_empty() {
enable_interrupts_and_hlt();
} else {
interrupts::enable();
}
}
}
struct TaskWaker {
task_id: TaskId,
task_queue: Arc<ArrayQueue<TaskId>>,
}
impl TaskWaker {
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
Waker::from(Arc::new(TaskWaker {
task_id,
task_queue,
}))
}
fn wake_task(&self) {
self.task_queue.push(self.task_id).expect("task_queue full");
}
}
impl Wake for TaskWaker {
fn wake(self: Arc<Self>) {
self.wake_task();
}
fn wake_by_ref(self: &Arc<Self>) {
self.wake_task();
}
}

View File

@@ -1,33 +1,11 @@
use crate::{print, println};
use conquer_once::spin::OnceCell;
use core::{
pin::Pin,
task::{Context, Poll},
};
use crossbeam_queue::ArrayQueue;
use futures_util::{
stream::{Stream, StreamExt},
task::AtomicWaker,
};
use futures_util::stream::{Stream, StreamExt};
use core::{pin::Pin, task::{Context, Poll}};
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use crate::print;
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: (),
@@ -35,32 +13,23 @@ pub struct ScancodeStream {
impl ScancodeStream {
pub fn new() -> Self {
SCANCODE_QUEUE
.try_init_once(|| ArrayQueue::new(100))
SCANCODE_QUEUE.try_init_once(|| ArrayQueue::new(100))
.expect("ScancodeStream::new should only be called once");
ScancodeStream { _private: () }
ScancodeStream {
_private: (),
}
}
}
impl Stream for ScancodeStream {
type Item = u8;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
fn poll_next(self: Pin<&mut Self>, context: &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) => {
WAKER.take();
Poll::Ready(Some(scancode))
}
Ok(scancode) => Poll::Ready(Some(scancode)),
Err(crossbeam_queue::PopError) => Poll::Pending,
}
}

View File

@@ -1,24 +1,17 @@
use alloc::boxed::Box;
use core::{
future::Future,
pin::Pin,
sync::atomic::{AtomicU64, Ordering},
task::{Context, Poll},
};
use core::task::{Context, Poll};
use core::{future::Future, pin::Pin};
pub mod executor;
pub mod keyboard;
pub mod simple_executor;
pub struct Task {
id: TaskId,
future: Pin<Box<dyn Future<Output = ()>>>,
}
impl Task {
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
Task {
id: TaskId::new(),
future: Box::pin(future),
}
}
@@ -27,13 +20,3 @@ impl Task {
self.future.as_mut().poll(context)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct TaskId(u64);
impl TaskId {
fn new() -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
}
}

View File

@@ -1,6 +1,6 @@
use super::Task;
use alloc::collections::VecDeque;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use alloc::{collections::VecDeque, sync::Arc, task::Wake};
use core::task::{Context, Poll, Waker};
pub struct SimpleExecutor {
task_queue: VecDeque<Task>,
@@ -19,7 +19,7 @@ impl SimpleExecutor {
pub fn run(&mut self) {
while let Some(mut task) = self.task_queue.pop_front() {
let waker = dummy_waker();
let waker = DummyWaker.to_waker();
let mut context = Context::from_waker(&waker);
match task.poll(&mut context) {
Poll::Ready(()) => {} // task done
@@ -29,16 +29,16 @@ impl SimpleExecutor {
}
}
fn dummy_raw_waker() -> RawWaker {
fn no_op(_: *const ()) {}
fn clone(_: *const ()) -> RawWaker {
dummy_raw_waker()
struct DummyWaker;
impl Wake for DummyWaker {
fn wake(self: Arc<Self>) {
// do nothing
}
let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op);
RawWaker::new(0 as *const (), vtable)
}
fn dummy_waker() -> Waker {
unsafe { Waker::from_raw(dummy_raw_waker()) }
impl DummyWaker {
fn to_waker(self) -> Waker {
Waker::from(Arc::new(self))
}
}

View File

@@ -3,6 +3,9 @@ use lazy_static::lazy_static;
use spin::Mutex;
use volatile::Volatile;
#[cfg(test)]
use crate::{serial_print, serial_println};
lazy_static! {
/// A global `Writer` instance that can be used for printing to the VGA text buffer.
///
@@ -177,14 +180,18 @@ pub fn _print(args: fmt::Arguments) {
#[test_case]
fn test_println_simple() {
serial_print!("test_println... ");
println!("test_println_simple output");
serial_println!("[ok]");
}
#[test_case]
fn test_println_many() {
serial_print!("test_println_many... ");
for _ in 0..200 {
println!("test_println_many output");
}
serial_println!("[ok]");
}
#[test_case]
@@ -192,6 +199,8 @@ fn test_println_output() {
use core::fmt::Write;
use x86_64::instructions::interrupts;
serial_print!("test_println_output... ");
let s = "Some test string that fits on a single line";
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
@@ -201,4 +210,6 @@ fn test_println_output() {
assert_eq!(char::from(screen_char.ascii_character), c);
}
});
serial_println!("[ok]");
}

View File

@@ -4,7 +4,7 @@
#![test_runner(blog_os::test_runner)]
#![reexport_test_harness_main = "test_main"]
use blog_os::println;
use blog_os::{println, serial_print, serial_println};
use core::panic::PanicInfo;
#[no_mangle] // don't mangle the name of this function
@@ -21,5 +21,7 @@ fn panic(info: &PanicInfo) -> ! {
#[test_case]
fn test_println() {
serial_print!("test_println... ");
println!("test_println output");
serial_println!("[ok]");
}

View File

@@ -7,7 +7,7 @@
extern crate alloc;
use alloc::{boxed::Box, vec::Vec};
use blog_os::allocator::HEAP_SIZE;
use blog_os::{allocator::HEAP_SIZE, serial_print, serial_println};
use bootloader::{entry_point, BootInfo};
use core::panic::PanicInfo;
@@ -30,38 +30,44 @@ fn main(boot_info: &'static BootInfo) -> ! {
#[test_case]
fn simple_allocation() {
let heap_value_1 = Box::new(41);
let heap_value_2 = Box::new(13);
assert_eq!(*heap_value_1, 41);
assert_eq!(*heap_value_2, 13);
serial_print!("simple_allocation... ");
let heap_value = Box::new(41);
assert_eq!(*heap_value, 41);
serial_println!("[ok]");
}
#[test_case]
fn large_vec() {
serial_print!("large_vec... ");
let n = 1000;
let mut vec = Vec::new();
for i in 0..n {
vec.push(i);
}
assert_eq!(vec.iter().sum::<u64>(), (n - 1) * n / 2);
serial_println!("[ok]");
}
#[test_case]
fn many_boxes() {
serial_print!("many_boxes... ");
for i in 0..HEAP_SIZE {
let x = Box::new(i);
assert_eq!(*x, i);
}
serial_println!("[ok]");
}
#[test_case]
fn many_boxes_long_lived() {
serial_print!("many_boxes_long_lived... ");
let long_lived = Box::new(1); // new
for i in 0..HEAP_SIZE {
let x = Box::new(i);
assert_eq!(*x, i);
}
assert_eq!(*long_lived, 1); // new
serial_println!("[ok]");
}
#[panic_handler]

View File

@@ -13,7 +13,7 @@ pub extern "C" fn _start() -> ! {
}
fn should_fail() {
serial_print!("should_panic::should_fail...\t");
serial_print!("should_fail... ");
assert_eq!(0, 1);
}

View File

@@ -9,7 +9,7 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
#[no_mangle]
pub extern "C" fn _start() -> ! {
serial_print!("stack_overflow::stack_overflow...\t");
serial_print!("stack_overflow... ");
blog_os::gdt::init();
init_test_idt();
@@ -23,7 +23,6 @@ pub extern "C" fn _start() -> ! {
#[allow(unconditional_recursion)]
fn stack_overflow() {
stack_overflow(); // for each recursion, the return address is pushed
volatile::Volatile::new(0).read(); // prevent tail recursion optimizations
}
lazy_static! {