From e0ed423e5293b000a370820150aea4ba456b98e2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 14:37:46 +0100 Subject: [PATCH 1/4] Explain the LLVM bug on Windows in more detail --- src/interrupts.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/interrupts.rs b/src/interrupts.rs index 8eefc8e0..ffe400fd 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,6 +1,7 @@ -// LLVM throws an error if a function with the -// x86-interrupt calling convention is compiled -// for a Windows system. +// The x86-interrupt calling convention leads to the following LLVM error +// when compiled for a Windows target: "offset is not a multiple of 16". This +// happens for example when running `cargo test` on Windows. To avoid this +// problem we skip compilation of this module on Windows. #![cfg(not(windows))] use crate::println; From b5c05193e1de3a278f13814ecaa55c1bda658d5e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 14:39:47 +0100 Subject: [PATCH 2/4] Document vga buffer methods --- src/vga_buffer.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 265557d7..66364272 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -4,6 +4,9 @@ use spin::Mutex; use volatile::Volatile; lazy_static! { + /// A global `Writer` instance that can be used for printing to the VGA text buffer. + /// + /// Used by the `print!` and `println!` macros. pub static ref WRITER: Mutex = Mutex::new(Writer { column_position: 0, color_code: ColorCode::new(Color::Yellow, Color::Black), @@ -11,6 +14,7 @@ lazy_static! { }); } +/// The standard color palette in VGA text mode. #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -33,15 +37,18 @@ pub enum Color { White = 15, } +/// A combination of a foreground and a background color. #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct ColorCode(u8); impl ColorCode { + /// Create a new `ColorCode` with the given foreground and background colors. fn new(foreground: Color, background: Color) -> ColorCode { ColorCode((background as u8) << 4 | (foreground as u8)) } } +/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] struct ScreenChar { @@ -49,13 +56,20 @@ struct ScreenChar { color_code: ColorCode, } +/// The height of the text buffer (normally 25 lines). const BUFFER_HEIGHT: usize = 25; +/// The width of the text buffer (normally 80 columns). const BUFFER_WIDTH: usize = 80; +/// A structure representing the VGA text buffer. struct Buffer { chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], } +/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`. +/// +/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the +/// `core::fmt::Write` trait. pub struct Writer { column_position: usize, color_code: ColorCode, @@ -63,6 +77,9 @@ pub struct Writer { } impl Writer { + /// Writes an ASCII byte to the buffer. + /// + /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), @@ -84,7 +101,12 @@ impl Writer { } } - pub fn write_string(&mut self, s: &str) { + /// Writes the given ASCII string to the buffer. + /// + /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not** + /// support strings with non-ASCII characters, since they can't be printed in the VGA text + /// mode. + fn write_string(&mut self, s: &str) { for byte in s.bytes() { match byte { // printable ASCII byte or newline @@ -95,6 +117,7 @@ impl Writer { } } + /// Shifts all lines one line up and clears the last row. fn new_line(&mut self) { for row in 1..BUFFER_HEIGHT { for col in 0..BUFFER_WIDTH { @@ -106,6 +129,7 @@ impl Writer { self.column_position = 0; } + /// Clears a row by overwriting it with blank characters. fn clear_row(&mut self, row: usize) { let blank = ScreenChar { ascii_character: b' ', @@ -124,17 +148,20 @@ impl fmt::Write for Writer { } } +/// Like the `print!` macro in the standard library, but prints to the VGA text buffer. #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); } +/// Like the `println!` macro in the standard library, but prints to the VGA text buffer. #[macro_export] macro_rules! println { () => ($crate::print!("\n")); ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } +/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; From 8bb0187f35fa069e1a4e2154df4c12dffef2f261 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 14:44:42 +0100 Subject: [PATCH 3/4] Run rustfmt --- src/interrupts.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interrupts.rs b/src/interrupts.rs index 08a2a516..13c54728 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -13,7 +13,8 @@ lazy_static! { idt.breakpoint.set_handler_fn(breakpoint_handler); idt.double_fault.set_handler_fn(double_fault_handler); unsafe { - idt.double_fault.set_handler_fn(double_fault_handler) + idt.double_fault + .set_handler_fn(double_fault_handler) .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); } idt From afe8dc3dcf6968dd24965c5c6c15f9f1eee46869 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 14:45:02 +0100 Subject: [PATCH 4/4] Add stack overflow integration test --- ...t-exception-double-fault-stack-overflow.rs | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/bin/test-exception-double-fault-stack-overflow.rs diff --git a/src/bin/test-exception-double-fault-stack-overflow.rs b/src/bin/test-exception-double-fault-stack-overflow.rs new file mode 100644 index 00000000..3d934a4c --- /dev/null +++ b/src/bin/test-exception-double-fault-stack-overflow.rs @@ -0,0 +1,77 @@ +#![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::{ExceptionStackFrame, InterruptDescriptorTable}; + +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 ExceptionStackFrame, + _error_code: u64, +) { + serial_println!("ok"); + + unsafe { + exit_qemu(); + } + loop {} +}