diff --git a/Cargo.toml b/Cargo.toml index ff994fa..694ce9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Ryan Kennedy "] edition = "2018" description = "Support for vga specific functions, data structures, and registers." +documentation = "https://docs.rs/vga" keywords = [ "vga", "no_std", diff --git a/src/lib.rs b/src/lib.rs index 7834726..b248818 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,5 +14,5 @@ mod registers; pub mod vga; mod writers; -pub use self::vga::VGA; +pub use self::vga::{VideoMode, VGA}; pub use self::writers::{Graphics640x480x16, Text40x25, Text40x50, Text80x25}; diff --git a/testing/cargo.toml b/testing/cargo.toml index b76bfc2..b620181 100644 --- a/testing/cargo.toml +++ b/testing/cargo.toml @@ -5,9 +5,11 @@ authors = ["Ryan Kennedy "] edition = "2018" [dependencies] -bootloader = "0.8.9" +bootloader = { version = "0.8.9", features = ["map_physical_memory"] } conquer-once = { version = "0.2.0", default-features = false } spinning_top = { version = "0.1.0", features = ["nightly"] } +pic8259_simple = "0.1.1" +vga = { path = "../" } uart_16550 = "0.2.4" x86_64 = "0.9.6" diff --git a/testing/src/interrupts.rs b/testing/src/interrupts.rs new file mode 100644 index 0000000..d68cdcf --- /dev/null +++ b/testing/src/interrupts.rs @@ -0,0 +1,68 @@ +use crate::gdt; +use crate::{hlt_loop, serial_print, serial_println}; +use conquer_once::spin::Lazy; +use core::convert::Into; +use pic8259_simple::ChainedPics; +use spinning_top::Spinlock; +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; + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, +} + +impl Into for InterruptIndex { + fn into(self) -> u8 { + self as u8 + } +} + +impl Into for InterruptIndex { + fn into(self) -> usize { + self as usize + } +} + +pub static PICS: Spinlock = + Spinlock::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); + +static IDT: Lazy = Lazy::new(|| { + let mut idt = InterruptDescriptorTable::new(); + idt.page_fault.set_handler_fn(page_fault_handler); + unsafe { + idt.double_fault + .set_handler_fn(double_fault_handler) + .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); + } + + idt +}); + +pub fn init_idt() { + IDT.load(); +} + +extern "x86-interrupt" fn double_fault_handler( + stack_frame: &mut InterruptStackFrame, + _error_code: u64, +) -> ! { + panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); +} + +extern "x86-interrupt" fn page_fault_handler( + stack_frame: &mut InterruptStackFrame, + error_code: PageFaultErrorCode, +) { + use x86_64::registers::control::Cr2; + + serial_print!("EXCEPTION: PAGE FAULT"); + serial_println!("Accessed Address: {:?}", Cr2::read()); + serial_println!("Error Code: {:?}", error_code); + serial_println!("{:#?}", stack_frame); + hlt_loop(); +} diff --git a/testing/src/lib.rs b/testing/src/lib.rs index b4d9928..b8bd579 100644 --- a/testing/src/lib.rs +++ b/testing/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(test, no_main)] +#![feature(abi_x86_interrupt)] #![feature(custom_test_frameworks)] #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] @@ -6,9 +7,9 @@ use core::panic::PanicInfo; -#[macro_use] -pub mod serial; pub mod gdt; +pub mod interrupts; +pub mod serial; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] @@ -26,6 +27,12 @@ pub fn exit_qemu(exit_code: QemuExitCode) { } } +pub fn hlt_loop() -> ! { + loop { + x86_64::instructions::hlt(); + } +} + pub fn test_runner(tests: &[&dyn Fn()]) { serial_println!("Running {} tests", tests.len()); for test in tests { diff --git a/testing/tests/basic_boot.rs b/testing/tests/vga.rs similarity index 51% rename from testing/tests/basic_boot.rs rename to testing/tests/vga.rs index 1f61114..50e61f8 100644 --- a/testing/tests/basic_boot.rs +++ b/testing/tests/vga.rs @@ -5,23 +5,35 @@ #![test_runner(testing::test_runner)] use core::panic::PanicInfo; -use testing::{serial_print, serial_println}; +use testing::{gdt, interrupts, serial_print, serial_println}; +use vga::{VideoMode, VGA}; #[no_mangle] // don't mangle the name of this function pub extern "C" fn _start() -> ! { + init(); test_main(); loop {} } +fn init() { + gdt::init(); + interrupts::init_idt(); + unsafe { interrupts::PICS.lock().initialize() }; + x86_64::instructions::interrupts::enable(); +} + #[panic_handler] fn panic(info: &PanicInfo) -> ! { testing::test_panic_handler(info) } #[test_case] -fn basic_boot() { - serial_print!("basic_boot... "); - assert_eq!(0, 0); +fn set_mode_80x25() { + serial_print!("mode 80x25... "); + + let mut vga = VGA.lock(); + vga.set_video_mode(VideoMode::Mode80x25); + serial_println!("[ok]"); }