diff --git a/src/gdt.rs b/src/gdt.rs index 378a889..c379e1e 100644 --- a/src/gdt.rs +++ b/src/gdt.rs @@ -3,15 +3,24 @@ use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector use x86_64::structures::tss::TaskStateSegment; use x86_64::VirtAddr; +// struct Selectors ::: selectors returned by GDT +// code_selector: SegmentSelector :field: indicates where the CS register +// needs to be put in case of a double fault +// tss_selector: SegmentSelector :field: indicates where the TSS ought to start +// in case of a double fault struct Selectors { code_selector: SegmentSelector, tss_selector: SegmentSelector, } +// const DOUBLE_FAULT_IST_INDEX: u16 ::: virtual location of double fault stack +pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; + lazy_static! { + // GDT: (GlobalDescriptorTable, Selectors) ::: antiquated, used to specify TSS + // SAFETY ensure the correct selectors are returned static ref GDT: (GlobalDescriptorTable, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); - // SAFETY ensure these values are safe in init() let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); ( @@ -22,12 +31,8 @@ lazy_static! { }, ) }; -} -// IST index to use for double fault stack -pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; - -lazy_static! { + // TSS: TaskStateSegment ::: holds stack tables static ref TSS: TaskStateSegment = { let mut tss = TaskStateSegment::new(); tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { @@ -41,6 +46,7 @@ lazy_static! { }; } +// fn init ::: initializes and loads GDT and TSS and places CS accordingly pub fn init() { use x86_64::instructions::segmentation::{Segment, CS}; use x86_64::instructions::tables::load_tss; diff --git a/src/interrupts.rs b/src/interrupts.rs index e82a6d8..acdd4e9 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -4,9 +4,11 @@ use pic8259::ChainedPics; use spin; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; +// const {PIC_1_OFFSET, PIC_2_OFFSET}: u8 ::: defines where PICs are located pub const PIC_1_OFFSET: u8 = 32; pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; +// enum InterruptIndex ::: index in IDT of each interrupt #[derive(Debug, Clone, Copy)] #[repr(u8)] pub enum InterruptIndex { @@ -15,27 +17,34 @@ pub enum InterruptIndex { } impl InterruptIndex { + // fn as_u8 ::: + // self :param: + // -> u8 :ret: fn as_u8(self) -> u8 { self as u8 } + // fn as_usize ::: + // self :param: + // -> usize :ret: pointer-sized type for IDT index fn as_usize(self) -> usize { usize::from(self.as_u8()) } } -// set offsets for PICs -// SAFETY +// PICS: spin::Mutex ::: magic words to create mutable PICs at PIC_1_OFFSET +// SAFETY ensure PIC_1_OFFSET and PIC_2_OFFSET are not 0-15 and are otherwise unused memory pub static PICS: spin::Mutex = spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); // only init idt in memory when first referred to // allows static mut lazy_static! { + // IDT: InterruptDescriptorTable ::: declares handlers for each interrupt static ref IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); idt.breakpoint.set_handler_fn(breakpoint_handler); - // SAFETY ensure index is valid and not passed to any other exceptions + // SAFETY ensure index is valid stack index and otherwise unused unsafe { idt.double_fault.set_handler_fn(double_fault_handler).set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); } @@ -46,12 +55,15 @@ lazy_static! { }; } -// handle breakpoint exceptions +// fn breakpoint_handler ::: prints error message +// stack_frame: InterruptStackFrame :param: extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); } -// handle double fault exceptions +// fn double_fault_handler ::: prints error message +// stack_frame: InterruptStackFrame :param: +// _error_code: u64 :param: extern "x86-interrupt" fn double_fault_handler( stack_frame: InterruptStackFrame, _error_code: u64, @@ -59,17 +71,21 @@ extern "x86-interrupt" fn double_fault_handler( panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); } -// init idt +// fn init_idt ::: loads IDT pub fn init_idt() { IDT.load(); } +// fn keyboard_interrupt_handler ::: prints keys when pressed +// _keyboard_stack_frame: InterruptStackFrame :param: extern "x86-interrupt" fn keyboard_interrupt_handler(_keyboard_stack_frame: InterruptStackFrame) { use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; use spin::Mutex; use x86_64::instructions::port::Port; lazy_static! { + // KEYBOARD: Mutex> ::: keyboard to compare + // key_event to static ref KEYBOARD: Mutex> = Mutex::new(Keyboard::new( ScancodeSet1::new(), @@ -81,6 +97,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_keyboard_stack_frame: Inte let mut keyboard = KEYBOARD.lock(); let mut port = Port::new(0x60); + // SAFETY ensure port is set to 0x60, the keyboard port let scancode: u8 = unsafe { port.read() }; if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { if let Some(key) = keyboard.process_keyevent(key_event) { @@ -97,6 +114,9 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_keyboard_stack_frame: Inte } } +// fn page_fault_handler ::: prints error message +// stack_frame: InterruptStackFrame :param: +// error_code: PageFaultErrorCode :param: extern "x86-interrupt" fn page_fault_handler( stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode, @@ -110,12 +130,14 @@ extern "x86-interrupt" fn page_fault_handler( hlt_loop(); } -// forcefully throws breakpoint exception +// fn test_breakpoint_exception ::: checks if breakpoint exception gets thrown correctly #[test_case] fn test_breakpoint_exception() { x86_64::instructions::interrupts::int3(); } +// fn timer_interrupt_handler ::: prints . when timer interrupt +// _stack_frame: InterruptStackFrame :param: extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { print!("."); unsafe { diff --git a/src/lib.rs b/src/lib.rs index 8eeb13c..9f018ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ pub mod vga_buffer; use core::panic::PanicInfo; -// wizardry for Testable Fn() type +// trait Testable ::: defines run() for all test functions pub trait Testable { fn run(&self) -> (); } @@ -28,7 +28,9 @@ where } } -// defines QemuExitCodes +// enum QemuExitCode ::: used when exiting with Qemu's isa-debug-exit device +// Success :field: +// Failed :field: #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum QemuExitCode { @@ -36,6 +38,7 @@ pub enum QemuExitCode { Failed = 0x11, } +// fn _start ::: runs tests #[cfg(test)] #[unsafe(no_mangle)] pub extern "C" fn _start() -> ! { @@ -44,8 +47,8 @@ pub extern "C" fn _start() -> ! { hlt_loop(); } -// exits qemu with an exit code -// qemu has special escape port at 0xf4 +// fn exit_qemu ::: uses Qemu's isa-debug-exit device to quit +// exit_code: QemuExitCode :param: pub fn exit_qemu(exit_code: QemuExitCode) { use x86_64::instructions::port::Port; unsafe { @@ -54,29 +57,32 @@ pub fn exit_qemu(exit_code: QemuExitCode) { } } +// fn hlt_loop ::: idles the cpu pub fn hlt_loop() -> ! { loop { x86_64::instructions::hlt(); } } -// common init fn for kernel + +// fn init ::: initializes everything; ought be the first thing that's called pub fn init() { gdt::init(); interrupts::init_idt(); - // SAFETY ensure the PICs are properly defined in interrupts.rs unsafe { interrupts::PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); } -// prints error code for failed test and exits qemu with a failure +// fn test_panic_handler ::: prints error info, exits qemu +// info: &PanicInfo :param: pub fn test_panic_handler(info: &PanicInfo) -> ! { serial_println!("[failed]\n"); serial_println!("Error: {}\n", info); exit_qemu(QemuExitCode::Failed); - hlt_loop(); + hlt_loop(); // unreachable but Rust doesn't know that } -// runs tests +// fn test_runner ::: cycles through all Testable +// tests: &[&dyn Testable] :param: every Testable function in scope pub fn test_runner(tests: &[&dyn Testable]) { serial_println!("Running {} tests", tests.len()); for test in tests { @@ -85,7 +91,8 @@ pub fn test_runner(tests: &[&dyn Testable]) { exit_qemu(QemuExitCode::Success); } -// called on panic +// fn panic ::: calls test_panic_handler, which is basically a public wrapper +// info: &PanicInfo :param: #[cfg(test)] #[panic_handler] fn panic(info: &PanicInfo) -> ! { diff --git a/src/main.rs b/src/main.rs index e2cc647..f23f266 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,21 +7,12 @@ use core::panic::PanicInfo; use rustos::println; -// SAFETY: make sure no other global fn _start exists -// called on start -#[unsafe(no_mangle)] +// fn _start ::: called on start +#[unsafe(no_mangle)] // SAFETY: make sure no other global fn _start exists pub extern "C" fn _start() -> ! { println!("Hello World{}", "!"); rustos::init(); // initializes kernel - use x86_64::registers::control::Cr3; - - let (level_4_page_table, _) = Cr3::read(); - println!( - "Level 4 page table at: {:?}", - level_4_page_table.start_address() - ); - #[cfg(test)] test_main(); @@ -29,19 +20,15 @@ pub extern "C" fn _start() -> ! { rustos::hlt_loop(); } -// called on test panic -// prints to console error message -#[cfg(test)] +// fn panic ::: prints panic message +// info: &PanicInfo :param: passed by panic_handler, includes panic information #[panic_handler] fn panic(info: &PanicInfo) -> ! { - rustos::test_panic_handler(info) -} - -// called on panic -// prints to vga error message -#[cfg(not(test))] -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - println!("{}", info); - rustos::hlt_loop(); + #[cfg(test)] + rustos::test_panic_handler(info); + #[cfg(not(test))] + { + println!("{}", info); + rustos::hlt_loop(); + }; } diff --git a/src/serial.rs b/src/serial.rs index f5fd2fb..40cff67 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -2,11 +2,13 @@ use lazy_static::lazy_static; use spin::Mutex; use uart_16550::SerialPort; +// macro serial_print ::: prints arg to serial port #[macro_export] macro_rules! serial_print { ($($arg:tt)*) => { $crate::serial::_print(format_args!($($arg)*)); }; } +// macro serial_println ::: prints arg to serial port with \n appended #[macro_export] macro_rules! serial_println { () => ($crate::serial_print!("\n")); @@ -15,17 +17,23 @@ macro_rules! serial_println { } lazy_static! { + // SERIAL1: Mutex ::: serial port to write to pub static ref SERIAL1: Mutex = { + // SAFETY ensure correct SerialPort is initialized to write output to let mut serial_port = unsafe { SerialPort::new(0x3F8) }; serial_port.init(); Mutex::new(serial_port) }; } +// fn _print ::: prints to serial port; internal fn +// args: ::core::fmt::Arguments :param: goofy ah macro magic #[doc(hidden)] pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; + + // locked to avoid race conditions interrupts::without_interrupts(|| { SERIAL1 .lock() diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 594e33e..8c15996 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -3,21 +3,20 @@ use lazy_static::lazy_static; use spin::Mutex; use volatile::Volatile; -// macros exported on root ie $crate::print +// macro print ::: prints to vga_buffer #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); } +// macro println ::: prints to vga_buffer with \n appended #[macro_export] macro_rules! println { () => ($crate::print!("\n")); ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } -// program ignores unused code -// enable semantics for the type -// represent it as u8 +// enum Color ::: defines color bytes for VGA buffer as u8 #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -40,28 +39,38 @@ pub enum Color { White = 15, } +// const BUFFER_HEIGHT ::: +// const BUFFER_WIDTH ::: const BUFFER_HEIGHT: usize = 25; const BUFFER_WIDTH: usize = 80; +// struct Buffer ::: +// chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT] :field: char array of text buffer. +// Volatile ensures it'll never be optimized away because this buffer is a side effect #[repr(transparent)] struct Buffer { chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], } -// full byte color code -// repr(transparent) ensures struct has same memory layout as type, in this case u8 +// struct ColorCode ::: formatted for VGA buffer; first 4 foreground, last 4 background +// (u8) :field: #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] struct ColorCode(u8); -// implementation of colorCode impl ColorCode { + // fn new ::: creates color code to VGA specs + // foreground: Color :param: + // background: Color :param: + // : ColorCode :ret: fn new(foreground: Color, background: Color) -> ColorCode { ColorCode((background as u8) << 4 | (foreground as u8)) } } -// uses C-like struct to gurantee field ordering +// struct ScreenChar ::: specifies character and color of 1 character for VGA buffer +// ascii_character: u8 :field: +// color_code: ColorCode :field: #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] struct ScreenChar { @@ -69,7 +78,10 @@ struct ScreenChar { color_code: ColorCode, } -// 'static tells the runtime how long the reference is valid; 'static is forever +// struct Writer ::: +// column_position: usize :field: +// color_code: ColorCode :field: +// buffer: &'static mut Buffer :field: buffer to write to pub struct Writer { column_position: usize, color_code: ColorCode, @@ -77,40 +89,49 @@ pub struct Writer { } impl Writer { - // writes byte to screen, automatically handling newlines - // expects a printable byte + // fn write_byte ::: writes byte to screen + // &mut self :param: self whose buffer will be written to + // byte: u8 :param: byte to write to screen. Must be writable pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), byte => { + // wraps automatically if self.column_position >= BUFFER_WIDTH { self.new_line(); } + // bottom row of screen will be written to let row = BUFFER_HEIGHT - 1; let col = self.column_position; - // .write with Volatile ensures this will never be optimised away let color_code = self.color_code; self.buffer.chars[row][col].write(ScreenChar { ascii_character: byte, color_code, }); + + // moves on to next byte's position preemptively self.column_position += 1; } } } - // doesn't expect printable bytes + + // fn write_string ::: + // &mut self :param: self whose buffer will be written to + // s: &str :param: string to write pub fn write_string(&mut self, s: &str) { for byte in s.bytes() { match byte { 0x20..=0x7e | b'\n' => self.write_byte(byte), - // filters out non-printable bytes + // placeholder for unknown bytes _ => self.write_byte(0xfe), } } } - // iterates each character 1 row up and replaces cursor + + // fn new_line ::: shifts buffer contents up, replaces cursor on the left + // &mut self :param: self whose buffer will be written to fn new_line(&mut self) { for row in 1..BUFFER_HEIGHT { for col in 0..BUFFER_WIDTH { @@ -121,7 +142,10 @@ impl Writer { self.clear_row(BUFFER_HEIGHT - 1); self.column_position = 0; } - // overwrites a row with spaces + + // fn clear_row ::: replaces row with spaces + // &mut self :param: self whose buffer will be written to + // row: usize :param: row of buffer to clear fn clear_row(&mut self, row: usize) { let blank = ScreenChar { ascii_character: b' ', @@ -133,17 +157,19 @@ impl Writer { } } -// required by fmt: write_str (self, str) -> fmt::Result impl fmt::Write for Writer { + // fn write_str ::: + // &mut self :param: self whose buffer will be written to + // s: &str :param: string to be written + // : fmt::Result :ret: fn write_str(&mut self, s: &str) -> fmt::Result { self.write_string(s); Ok(()) } } -// static WRITER evaluates lazily lazy_static! { - // spin Mutex 'locks' thread without OS features + // WRITER: Mutex ::: pub static ref WRITER: Mutex = Mutex::new(Writer { column_position: 0, color_code: ColorCode::new(Color::Magenta, Color::Black), @@ -151,7 +177,8 @@ lazy_static! { }); } -// hides from generated docs despite publicity +// fn _print ::: used for macro magic +// args: fmt::Arguments :param: #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; @@ -161,6 +188,7 @@ pub fn _print(args: fmt::Arguments) { }); } +// fn test_println_many ::: #[test_case] fn test_println_many() { for _ in 0..1023 { @@ -168,6 +196,7 @@ fn test_println_many() { } } +// fn test_println_output ::: checks that what is getting written is getting printed #[test_case] fn test_println_output() { use core::fmt::Write; @@ -185,6 +214,7 @@ fn test_println_output() { }); } +// fn test_println_simple ::: #[test_case] fn test_println_simple() { println!("test_println_simple output");