mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-17 06:47:49 +00:00
Merge branch 'post-08-new' into post-09-new
This commit is contained in:
@@ -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 {}
|
||||
}
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -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]");
|
||||
}
|
||||
|
||||
60
src/lib.rs
60
src/lib.rs
@@ -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)
|
||||
}
|
||||
|
||||
25
src/main.rs
25
src/main.rs
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)*));
|
||||
}
|
||||
|
||||
@@ -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]");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user