Merge branch 'post-08-new' into post-09-new

This commit is contained in:
Philipp Oppermann
2019-04-26 15:28:56 +02:00
18 changed files with 297 additions and 291 deletions

View File

@@ -1,33 +0,0 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)] // disable all Rust-level entry points
#![cfg_attr(test, allow(unused_imports))]
use blog_os::{exit_qemu, serial_println};
use core::panic::PanicInfo;
/// This function is the entry point, since the linker looks for a function
/// named `_start` by default.
#[cfg(not(test))]
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start() -> ! {
serial_println!("ok");
unsafe {
exit_qemu();
}
loop {}
}
/// This function is called on panic.
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
serial_println!("failed");
serial_println!("{}", info);
unsafe {
exit_qemu();
}
loop {}
}

View File

@@ -1,34 +0,0 @@
#![no_std]
#![cfg_attr(not(test), no_main)]
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
use blog_os::{exit_qemu, serial_println};
use core::panic::PanicInfo;
#[cfg(not(test))]
#[no_mangle]
pub extern "C" fn _start() -> ! {
blog_os::interrupts::init_idt();
x86_64::instructions::interrupts::int3();
serial_println!("ok");
unsafe {
exit_qemu();
}
loop {}
}
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
serial_println!("failed");
serial_println!("{}", info);
unsafe {
exit_qemu();
}
loop {}
}

View File

@@ -1,77 +0,0 @@
#![feature(abi_x86_interrupt)]
#![no_std]
#![cfg_attr(not(test), no_main)]
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
use blog_os::{exit_qemu, serial_println};
use core::panic::PanicInfo;
use lazy_static::lazy_static;
#[cfg(not(test))]
#[no_mangle]
#[allow(unconditional_recursion)]
pub extern "C" fn _start() -> ! {
blog_os::gdt::init();
init_test_idt();
fn stack_overflow() {
stack_overflow(); // for each recursion, the return address is pushed
}
// trigger a stack overflow
stack_overflow();
serial_println!("failed");
serial_println!("No exception occured");
unsafe {
exit_qemu();
}
loop {}
}
/// This function is called on panic.
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
serial_println!("failed");
serial_println!("{}", info);
unsafe {
exit_qemu();
}
loop {}
}
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
lazy_static! {
static ref TEST_IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
unsafe {
idt.double_fault
.set_handler_fn(double_fault_handler)
.set_stack_index(blog_os::gdt::DOUBLE_FAULT_IST_INDEX);
}
idt
};
}
pub fn init_test_idt() {
TEST_IDT.load();
}
extern "x86-interrupt" fn double_fault_handler(
_stack_frame: &mut InterruptStackFrame,
_error_code: u64,
) {
serial_println!("ok");
unsafe {
exit_qemu();
}
loop {}
}

View File

@@ -1,23 +0,0 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]
#![cfg_attr(test, allow(unused_imports))]
use blog_os::{exit_qemu, serial_println};
use core::panic::PanicInfo;
#[cfg(not(test))]
#[no_mangle]
pub extern "C" fn _start() -> ! {
panic!();
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
serial_println!("ok");
unsafe {
exit_qemu();
}
loop {}
}

View File

@@ -61,7 +61,6 @@ extern "x86-interrupt" fn page_fault_handler(
stack_frame: &mut InterruptStackFrame,
_error_code: PageFaultErrorCode,
) {
use crate::hlt_loop;
use x86_64::registers::control::Cr2;
println!("EXCEPTION: PAGE FAULT");
@@ -114,3 +113,14 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut Interrup
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
}
}
#[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

@@ -1,5 +1,11 @@
#![cfg_attr(not(test), no_std)]
#![no_std]
#![cfg_attr(test, no_main)]
#![feature(custom_test_frameworks)]
#![feature(abi_x86_interrupt)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
use core::panic::PanicInfo;
pub mod gdt;
pub mod interrupts;
@@ -7,11 +13,42 @@ pub mod memory;
pub mod serial;
pub mod vga_buffer;
pub unsafe fn exit_qemu() {
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()]) {
serial_println!("Running {} tests", tests.len());
for test in tests {
test();
}
exit_qemu(QemuExitCode::Success);
}
pub fn test_panic_handler(info: &PanicInfo) -> ! {
serial_println!("[failed]\n");
serial_println!("Error: {}\n", info);
exit_qemu(QemuExitCode::Failed);
hlt_loop();
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
}
pub fn exit_qemu(exit_code: QemuExitCode) {
use x86_64::instructions::port::Port;
let mut port = Port::<u32>::new(0xf4);
port.write(0);
unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
}
pub fn hlt_loop() -> ! {
@@ -19,3 +56,18 @@ pub fn hlt_loop() -> ! {
x86_64::instructions::hlt();
}
}
/// Entry point for `cargo xtest`
#[cfg(test)]
#[no_mangle]
pub extern "C" fn _start() -> ! {
init();
test_main();
hlt_loop();
}
#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
test_panic_handler(info)
}

View File

@@ -1,6 +1,8 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(blog_os::test_runner)]
#![reexport_test_harness_main = "test_main"]
use blog_os::println;
use bootloader::{entry_point, BootInfo};
@@ -8,18 +10,12 @@ use core::panic::PanicInfo;
entry_point!(kernel_main);
#[cfg(not(test))]
fn kernel_main(boot_info: &'static BootInfo) -> ! {
use blog_os::interrupts::PICS;
use blog_os::memory;
use x86_64::{structures::paging::Page, VirtAddr};
println!("Hello World{}", "!");
blog_os::gdt::init();
blog_os::interrupts::init_idt();
unsafe { PICS.lock().initialize() };
x86_64::instructions::interrupts::enable();
blog_os::init();
let mut mapper = unsafe { memory::init(boot_info.physical_memory_offset) };
let mut frame_allocator = memory::init_frame_allocator(&boot_info.memory_map);
@@ -32,6 +28,9 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
let page_ptr: *mut u64 = page.start_address().as_mut_ptr();
unsafe { page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e) };
#[cfg(test)]
test_main();
println!("It did not crash!");
blog_os::hlt_loop();
}
@@ -43,3 +42,9 @@ fn panic(info: &PanicInfo) -> ! {
println!("{}", info);
blog_os::hlt_loop();
}
#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
blog_os::test_panic_handler(info)
}

View File

@@ -4,7 +4,7 @@ use uart_16550::SerialPort;
lazy_static! {
pub static ref SERIAL1: Mutex<SerialPort> = {
let mut serial_port = SerialPort::new(0x3F8);
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
serial_port.init();
Mutex::new(serial_port)
};
@@ -36,5 +36,6 @@ macro_rules! serial_print {
macro_rules! serial_println {
() => ($crate::serial_print!("\n"));
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(concat!($fmt, "\n"), $($arg)*));
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
concat!($fmt, "\n"), $($arg)*));
}

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.
///
@@ -175,85 +178,38 @@ pub fn _print(args: fmt::Arguments) {
});
}
#[cfg(test)]
mod test {
use super::*;
fn construct_writer() -> Writer {
use std::boxed::Box;
let buffer = construct_buffer();
Writer {
column_position: 0,
color_code: ColorCode::new(Color::Blue, Color::Magenta),
buffer: Box::leak(Box::new(buffer)),
}
}
fn construct_buffer() -> Buffer {
use array_init::array_init;
Buffer {
chars: array_init(|_| array_init(|_| Volatile::new(empty_char()))),
}
}
fn empty_char() -> ScreenChar {
ScreenChar {
ascii_character: b' ',
color_code: ColorCode::new(Color::Green, Color::Brown),
}
}
#[test]
fn write_byte() {
let mut writer = construct_writer();
writer.write_byte(b'X');
writer.write_byte(b'Y');
for (i, row) in writer.buffer.chars.iter().enumerate() {
for (j, screen_char) in row.iter().enumerate() {
let screen_char = screen_char.read();
if i == BUFFER_HEIGHT - 1 && j == 0 {
assert_eq!(screen_char.ascii_character, b'X');
assert_eq!(screen_char.color_code, writer.color_code);
} else if i == BUFFER_HEIGHT - 1 && j == 1 {
assert_eq!(screen_char.ascii_character, b'Y');
assert_eq!(screen_char.color_code, writer.color_code);
} else {
assert_eq!(screen_char, empty_char());
}
}
}
}
#[test]
fn write_formatted() {
use core::fmt::Write;
let mut writer = construct_writer();
writeln!(&mut writer, "a").unwrap();
writeln!(&mut writer, "b{}", "c").unwrap();
for (i, row) in writer.buffer.chars.iter().enumerate() {
for (j, screen_char) in row.iter().enumerate() {
let screen_char = screen_char.read();
if i == BUFFER_HEIGHT - 3 && j == 0 {
assert_eq!(screen_char.ascii_character, b'a');
assert_eq!(screen_char.color_code, writer.color_code);
} else if i == BUFFER_HEIGHT - 2 && j == 0 {
assert_eq!(screen_char.ascii_character, b'b');
assert_eq!(screen_char.color_code, writer.color_code);
} else if i == BUFFER_HEIGHT - 2 && j == 1 {
assert_eq!(screen_char.ascii_character, b'c');
assert_eq!(screen_char.color_code, writer.color_code);
} else if i >= BUFFER_HEIGHT - 2 {
assert_eq!(screen_char.ascii_character, b' ');
assert_eq!(screen_char.color_code, writer.color_code);
} else {
assert_eq!(screen_char, empty_char());
}
}
}
}
#[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]
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();
writeln!(writer, "\n{}", s).expect("writeln failed");
for (i, c) in s.chars().enumerate() {
let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
});
serial_println!("[ok]");
}