From ca8ae131dfc02e28958aaefabcb1beb3cccddae8 Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Thu, 28 Dec 2017 17:22:55 -0600 Subject: [PATCH 1/5] Disable interrupts then restore to original state --- src/device/pic.rs | 9 ++++-- src/interrupts/mod.rs | 13 +++++--- src/lib.rs | 36 +++++++++++---------- src/macros.rs | 6 ++-- src/utils.rs | 74 ++++++++++++++++++++++++++++++++++++++----- 5 files changed, 103 insertions(+), 35 deletions(-) diff --git a/src/device/pic.rs b/src/device/pic.rs index 3253e1a..60afab0 100644 --- a/src/device/pic.rs +++ b/src/device/pic.rs @@ -18,7 +18,7 @@ const MODE_8086: u8 = 0x01; pub struct Pic { offset: u8, command: Port, - data: Port, + pub data: Port, } impl Pic { @@ -37,7 +37,7 @@ impl Pic { ///A master and slave PIC. pub struct ChainedPics { - pics: [Pic; 2], + pub pics: [Pic; 2], } impl ChainedPics { @@ -93,7 +93,10 @@ impl ChainedPics { wait(); self.pics[1].data.write(MODE_8086); - println!("[ OK ] 8259 PIC.") + // Uncommenting this line results in deadlock + // Printing disables interrupt and tries to lock PICS + // Since we got here by calling `PICS.lock().init()`, we will always deadlock + //println!("[ OK ] 8259 PIC.") } ///Cycle through the PICS until we find one that can handle this interrupt. diff --git a/src/interrupts/mod.rs b/src/interrupts/mod.rs index b5ffaa1..a9317f9 100644 --- a/src/interrupts/mod.rs +++ b/src/interrupts/mod.rs @@ -87,6 +87,8 @@ pub extern "x86-interrupt" fn timer_handler(_stack_frame: &mut ExceptionStackFra unsafe { PICS.lock().notify_end_of_interrupt(0x20) }; if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 { + PIT_TICKS.store(0, Ordering::SeqCst); + unsafe { disable_interrupts_and_then(|| { SCHEDULER.resched(); @@ -96,11 +98,12 @@ pub extern "x86-interrupt" fn timer_handler(_stack_frame: &mut ExceptionStackFra } pub extern "x86-interrupt" fn keyboard_handler(_stack_frame: &mut ExceptionStackFrame) { - if let Some(c) = read_char() { - print!("{}", c); - } - - unsafe { PICS.lock().notify_end_of_interrupt(0x21) }; + disable_interrupts_and_then(|| { + if let Some(c) = read_char() { + print!("{}", c); + } + unsafe { PICS.lock().notify_end_of_interrupt(0x21) }; + }); } diff --git a/src/lib.rs b/src/lib.rs index c064745..e3b07b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,21 +42,22 @@ pub use runtime_glue::*; #[no_mangle] pub extern "C" fn kmain(multiboot_information_address: usize) { - device::vga::buffer::clear_screen(); - println!("[ INFO ] lambdaOS: Begin init."); - - //Load a multiboot BootInfo structure using the address passed in ebx. - let boot_info = unsafe { multiboot2::load(multiboot_information_address) }; - - //Safety stuff. - enable_nxe_bit(); - enable_write_protect_bit(); + // Ensure all interrupt masks are set to prevent issues during setup + disable_interrupts(); + { + device::vga::buffer::clear_screen(); + println!("[ INFO ] lambdaOS: Begin init."); + + //Load a multiboot BootInfo structure using the address passed in ebx. + let boot_info = unsafe { multiboot2::load(multiboot_information_address) }; - // set up guard page and map the heap pages - let mut memory_controller = memory::init(boot_info); + //Safety stuff. + enable_nxe_bit(); + enable_write_protect_bit(); + + // set up guard page and map the heap pages + let mut memory_controller = memory::init(boot_info); - // Interrupts. - disable_interrupts_and_then(|| { unsafe { // Load IDT. interrupts::init(&mut memory_controller); @@ -65,9 +66,10 @@ pub extern "C" fn kmain(multiboot_information_address: usize) { // Initalise all other hardware devices. device::init(); } - }); - - /*let proc_closure = || { + } + enable_interrupts(); + + let proc_closure = || { let max_procs = 50; for i in 0..max_procs { @@ -75,7 +77,7 @@ pub extern "C" fn kmain(multiboot_information_address: usize) { } }; - disable_interrupts_and_then(proc_closure);*/ + proc_closure(); use alloc::String; diff --git a/src/macros.rs b/src/macros.rs index 2fc70c3..b69e032 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,6 +1,8 @@ macro_rules! print { - ($($arg:tt)*) => ({ - $crate::device::vga::buffer::print(format_args!($($arg)*)); + ($($arg:tt)*) => ({ + ::utils::disable_interrupts_and_then(|| { + $crate::device::vga::buffer::print(format_args!($($arg)*)); + }); }); } diff --git a/src/utils.rs b/src/utils.rs index 662145f..2ead65f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,27 +14,85 @@ pub fn enable_write_protect_bit() { unsafe { cr0_write(cr0() | Cr0::WRITE_PROTECT) }; } -pub unsafe fn disable_interrupts() { +unsafe fn disable() { asm!("cli"); } -pub unsafe fn enable_interrupts() { +unsafe fn enable() { asm!("sti"); } +/// Disable all interrupts and save the PIC masks +pub fn disable_interrupts() -> (u8, u8) { + use device::pic::PICS; + + unsafe { + disable(); + } + + let saved_masks: (u8, u8) = { + let mask_pic0 = PICS.lock().pics[0].data.read(); + let mask_pic1 = PICS.lock().pics[1].data.read(); + + (mask_pic0, mask_pic1) + }; + + PICS.lock().pics[0].data.write(0xff); + PICS.lock().pics[1].data.write(0xff); + + saved_masks +} + +/// Enable all interrupts +pub fn enable_interrupts() { + use device::pic::PICS; + + // Ensure that PIC manipulation is not interrupted + unsafe { + disable(); + } + + { + // Clear all interrupt masks + PICS.lock().pics[0].data.write(0); + PICS.lock().pics[1].data.write(0); + } + + unsafe { + enable(); + } +} + +/// Restore interrupts to previous state +pub fn restore_interrupts(saved_masks: (u8, u8)) { + use device::pic::PICS; + + // Ensure PIC manipulation is not interrupted + unsafe { + disable(); + } + + let (mask_pic0, mask_pic1) = saved_masks; + + PICS.lock().pics[0].data.write(mask_pic0); + PICS.lock().pics[1].data.write(mask_pic1); + + unsafe { + enable(); + } +} + // Stolen from Robert Gries. // This function disables interrupts, allows a function to run without them enabled, and then // reenables interrupts. pub fn disable_interrupts_and_then(f: F) -> T where F: FnOnce() -> T { - unsafe { - disable_interrupts(); + let saved_masks = disable_interrupts(); - let result: T = f(); + let result: T = f(); - enable_interrupts(); + restore_interrupts(saved_masks); - result - } + result } From 5eaaa8c642102be52b1d035873e0d128bab9de2d Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Thu, 28 Dec 2017 17:25:59 -0600 Subject: [PATCH 2/5] Prevent exceptions from being interrupted --- src/interrupts/mod.rs | 52 ++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/interrupts/mod.rs b/src/interrupts/mod.rs index a9317f9..31caff3 100644 --- a/src/interrupts/mod.rs +++ b/src/interrupts/mod.rs @@ -108,8 +108,10 @@ pub extern "x86-interrupt" fn keyboard_handler(_stack_frame: &mut ExceptionStack pub extern "x86-interrupt" fn divide_by_zero_handler(stack_frame: &mut ExceptionStackFrame) { - println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame); - loop {} + disable_interrupts_and_then(|| { + println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame); + loop {} + }); } pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut ExceptionStackFrame) { @@ -121,35 +123,41 @@ pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut ExceptionStac } pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: &mut ExceptionStackFrame) { - println!( - "\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}", - stack_frame.instruction_pointer, - stack_frame - ); - loop {} + disable_interrupts_and_then(|| { + println!( + "\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}", + stack_frame.instruction_pointer, + stack_frame + ); + loop {} + }); } pub extern "x86-interrupt" fn page_fault_handler( stack_frame: &mut ExceptionStackFrame, error_code: PageFaultErrorCode, ) { - use x86_64::registers::control_regs; - println!( - "\nEXCEPTION: PAGE FAULT while accessing {:#x}\nerror code: \ - {:?}\n{:#?}", - control_regs::cr2(), - error_code, - stack_frame - ); - loop {} + disable_interrupts_and_then(|| { + use x86_64::registers::control_regs; + println!( + "\nEXCEPTION: PAGE FAULT while accessing {:#x}\nerror code: \ + {:?}\n{:#?}", + control_regs::cr2(), + error_code, + stack_frame + ); + loop {} + }); } pub extern "x86-interrupt" fn double_fault_handler( stack_frame: &mut ExceptionStackFrame, _error_code: u64, ) { - println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); - loop {} + disable_interrupts_and_then(|| { + println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); + loop {} + }); } pub extern "x86-interrupt" fn gpf_handler( @@ -157,6 +165,8 @@ pub extern "x86-interrupt" fn gpf_handler( _error_code: u64, ) { - println!("\nEXCEPTION: GPF\n{:#?}", stack_frame); - loop {} + disable_interrupts_and_then(|| { + println!("\nEXCEPTION: GPF\n{:#?}", stack_frame); + loop {} + }); } From 8daceab9f87644d64f7c71dec77e047a16241571 Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Thu, 28 Dec 2017 17:55:23 -0600 Subject: [PATCH 3/5] Wrap allocator to disable interrupts --- Cargo.toml | 3 -- libs/hole_list_allocator/Cargo.toml | 8 ----- libs/hole_list_allocator/src/lib.rs | 49 ----------------------------- src/lib.rs | 9 +++++- src/memory/heap_allocator.rs | 48 ++++++++++++++++++++++++++++ src/memory/mod.rs | 8 ++--- 6 files changed, 59 insertions(+), 66 deletions(-) delete mode 100644 libs/hole_list_allocator/Cargo.toml delete mode 100644 libs/hole_list_allocator/src/lib.rs create mode 100644 src/memory/heap_allocator.rs diff --git a/Cargo.toml b/Cargo.toml index 953b3a1..7d2e983 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,6 @@ linked_list_allocator = { git = "https://github.com/phil-opp/linked-list-allocat bit_field = "0.7.0" x86 = "*" -[dependencies.hole_list_allocator] -path = "libs/hole_list_allocator" - [dependencies.lazy_static] version = "0.2.4" features = ["spin_no_std"] diff --git a/libs/hole_list_allocator/Cargo.toml b/libs/hole_list_allocator/Cargo.toml deleted file mode 100644 index 1e2a189..0000000 --- a/libs/hole_list_allocator/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "hole_list_allocator" -version = "0.1.0" -authors = ["too-r "] - -[dependencies] -linked_list_allocator = { git = "https://github.com/phil-opp/linked-list-allocator.git"} -spin = "*" diff --git a/libs/hole_list_allocator/src/lib.rs b/libs/hole_list_allocator/src/lib.rs deleted file mode 100644 index fb9a79d..0000000 --- a/libs/hole_list_allocator/src/lib.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![deny(warnings)] -#![feature(alloc)] -#![feature(allocator_api)] -#![feature(custom_attribute)] -#![feature(const_fn)] -#![no_std] -#![feature(global_allocator)] - -extern crate alloc; -extern crate spin; -extern crate linked_list_allocator; - -use alloc::heap::{Alloc, AllocErr, Layout}; -use spin::Mutex; -use linked_list_allocator::Heap; - -pub const HEAP_START: usize = 0o_000_001_000_000_0000; -pub const HEAP_SIZE: usize = 100 * 1024; - -pub static HEAP: Mutex> = Mutex::new(None); - -//Create a heap. -pub unsafe fn init(offset: usize, size: usize) { - *HEAP.lock() = Some(Heap::new(offset, size)); -} - -pub struct Allocator; - -unsafe impl<'a> Alloc for &'a Allocator { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - if let Some(ref mut heap) = *HEAP.lock() { - heap.allocate_first_fit(layout) //Allocate memory at the first available space big enough. - } else { - panic!("heap not initialized"); - } - } - - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - if let Some(ref mut heap) = *HEAP.lock() { - heap.deallocate(ptr, layout) //Deallocate the memory using a pointer to the allocated memory. - } else { - panic!("heap not initialized"); - } - } -} - -//Attribute tells Rust to use this as the default heap allocator. -#[global_allocator] -static GLOBAL_ALLOC: Allocator = Allocator; diff --git a/src/lib.rs b/src/lib.rs index e3b07b4..3935bda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ #![feature(const_unique_new)] #![feature(const_max_value)] #![feature(core_intrinsics)] +#![feature(global_allocator)] #![no_std] extern crate rlibc; @@ -22,9 +23,9 @@ extern crate once; extern crate bit_field; #[macro_use] extern crate lazy_static; -extern crate hole_list_allocator as allocator; #[macro_use] extern crate alloc; +extern crate linked_list_allocator; #[macro_use] mod macros; @@ -94,3 +95,9 @@ pub extern "C" fn real_main() { pub extern "C" fn process_test() { println!("Inside test process."); } + +use memory::heap_allocator::HeapAllocator; + +//Attribute tells Rust to use this as the default heap allocator. +#[global_allocator] +static GLOBAL_ALLOC: HeapAllocator = HeapAllocator::new(); diff --git a/src/memory/heap_allocator.rs b/src/memory/heap_allocator.rs new file mode 100644 index 0000000..b017a46 --- /dev/null +++ b/src/memory/heap_allocator.rs @@ -0,0 +1,48 @@ +use alloc::allocator::{Alloc, AllocErr, Layout}; +use linked_list_allocator::LockedHeap; +use utils::disable_interrupts_and_then; + +pub const HEAP_START: usize = 0o_000_001_000_000_0000; +pub const HEAP_SIZE: usize = 500 * 1024; + +pub struct HeapAllocator { + inner: LockedHeap, +} + +impl HeapAllocator { + /// Creates an empty heap. All allocate calls will return `None`. + pub const fn new() -> Self { + HeapAllocator { + inner: LockedHeap::empty(), + } + } + + /// Initializes an empty heap + /// + /// # Unsafety + /// + /// This function must be called at most once and must only be used on an + /// empty heap. Also, it is assumed that interrupts are disabled. + pub unsafe fn init(&self, heap_bottom: usize, heap_size: usize) { + self.inner.lock().init(heap_bottom, heap_size); + } +} + +/// Wrappers for inner Alloc implementation +unsafe impl<'a> Alloc for &'a HeapAllocator { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + disable_interrupts_and_then(|| -> Result<*mut u8, AllocErr> { + self.inner.lock().alloc(layout) + }) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + disable_interrupts_and_then(|| { + self.inner.lock().dealloc(ptr, layout); + }); + } + + fn oom(&mut self, _: AllocErr) -> ! { + panic!("Out of memory"); + } +} diff --git a/src/memory/mod.rs b/src/memory/mod.rs index ed5ec86..2b28b31 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -3,15 +3,12 @@ pub use self::paging::remap_the_kernel; pub use self::stack_allocator::Stack; use self::paging::PhysicalAddress; use multiboot2::BootInformation; -use allocator; mod area_frame_allocator; +pub mod heap_allocator; pub mod paging; mod stack_allocator; -pub const HEAP_START: usize = 0o_000_001_000_000_0000; -pub const HEAP_SIZE: usize = 500 * 1024; - pub const PAGE_SIZE: usize = 4096; pub fn init(boot_info: &BootInformation) -> MemoryController { @@ -57,6 +54,7 @@ pub fn init(boot_info: &BootInformation) -> MemoryController { let mut active_table = paging::remap_the_kernel(&mut frame_allocator, boot_info); use self::paging::Page; + use self::heap_allocator::{HEAP_SIZE, HEAP_START}; let heap_start_page = Page::containing_address(HEAP_START); let heap_end_page = Page::containing_address(HEAP_START + HEAP_SIZE - 1); @@ -67,7 +65,7 @@ pub fn init(boot_info: &BootInformation) -> MemoryController { //Init the heap unsafe { - allocator::init(HEAP_START, HEAP_SIZE); + ::GLOBAL_ALLOC.init(HEAP_START, HEAP_SIZE); } let stack_allocator = { From 00ceeaea9672a8e82d0566493dda9e8dedc125b4 Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Thu, 28 Dec 2017 18:11:35 -0600 Subject: [PATCH 4/5] Remove unnecessary wrapper for printing --- src/macros.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index b69e032..a11e2b2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,9 +1,7 @@ macro_rules! print { ($($arg:tt)*) => ({ - ::utils::disable_interrupts_and_then(|| { - $crate::device::vga::buffer::print(format_args!($($arg)*)); - }); - }); + $crate::device::vga::buffer::print(format_args!($($arg)*)); + }); } macro_rules! println { From 4144ec37df4133a54cdc456410e89bbb9005eba6 Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Thu, 28 Dec 2017 18:25:55 -0600 Subject: [PATCH 5/5] Remove unnecessary comment --- src/device/pic.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/device/pic.rs b/src/device/pic.rs index 60afab0..7895c18 100644 --- a/src/device/pic.rs +++ b/src/device/pic.rs @@ -93,10 +93,7 @@ impl ChainedPics { wait(); self.pics[1].data.write(MODE_8086); - // Uncommenting this line results in deadlock - // Printing disables interrupt and tries to lock PICS - // Since we got here by calling `PICS.lock().init()`, we will always deadlock - //println!("[ OK ] 8259 PIC.") + println!("[ OK ] 8259 PIC.") } ///Cycle through the PICS until we find one that can handle this interrupt.