From d53661b9a02228779f3915506b7dc1630ee3a3ce Mon Sep 17 00:00:00 2001 From: Jacob Hinchliffe Date: Tue, 4 Mar 2025 23:06:47 +0000 Subject: [PATCH 1/4] Ran cargo fmt, clippy fixes, suppressed some warns I will start working on stack traces tonight and tomorrow. We need to be able to 'unwind' by finding calling functions. --- kernel/src/arch/x86_64/drivers/ascii/mod.rs | 29 +++++-- .../arch/x86_64/drivers/framebuffer/colour.rs | 13 ++- .../x86_64/drivers/framebuffer/display.rs | 27 ++++--- kernel/src/arch/x86_64/drivers/keyboard.rs | 27 ++++--- kernel/src/arch/x86_64/interrupts.rs | 37 ++++++--- .../x86_64/memory/allocation/heap_alloc.rs | 37 ++++++--- .../src/arch/x86_64/memory/allocation/mod.rs | 2 +- .../x86_64/memory/allocation/page_alloc.rs | 70 ++++++++++------ .../x86_64/memory/allocation/stack_alloc.rs | 79 ++++++++++--------- kernel/src/arch/x86_64/memory/mapping.rs | 3 +- kernel/src/arch/x86_64/memory/mod.rs | 21 ++--- kernel/src/arch/x86_64/memory/units.rs | 4 +- .../arch/x86_64/processing/async_io/task.rs | 12 +-- kernel/src/arch/x86_64/processing/mod.rs | 3 +- .../arch/x86_64/processing/taskrunner/mod.rs | 6 +- .../arch/x86_64/processing/threading/mod.rs | 4 +- kernel/src/lib.rs | 21 ++--- kernel/src/std/application.rs | 5 +- kernel/src/std/application/frame.rs | 19 ++++- kernel/src/std/application/window.rs | 3 +- kernel/src/std/ascii.rs | 6 +- kernel/src/std/io.rs | 19 ++--- kernel/src/util/editor.rs | 18 +++-- libm/src/lib.rs | 7 +- rustfmt.toml | 4 + 25 files changed, 300 insertions(+), 176 deletions(-) create mode 100644 rustfmt.toml diff --git a/kernel/src/arch/x86_64/drivers/ascii/mod.rs b/kernel/src/arch/x86_64/drivers/ascii/mod.rs index 6e3cb37..d380b76 100644 --- a/kernel/src/arch/x86_64/drivers/ascii/mod.rs +++ b/kernel/src/arch/x86_64/drivers/ascii/mod.rs @@ -2,14 +2,17 @@ use core::fmt; use spin::{Lazy, Mutex}; use x86_64::instructions::interrupts; -use crate::arch::x86_64::drivers::framebuffer::{colour::Colour, display::FRAMEBUFFER_WRITER}; +use crate::arch::x86_64::drivers::framebuffer::{ + colour::Colour, display::FRAMEBUFFER_WRITER, +}; use crate::resources::font::{FONT_SPLEEN_8X16, Font}; static FONT_WIDTH: u32 = 8; static FONT_HEIGHT: u32 = 16; -pub static WRITER: Lazy> = Lazy::new(|| Mutex::new(Writer::new())); +pub static WRITER: Lazy> = + Lazy::new(|| Mutex::new(Writer::new())); pub fn screensize_chars() -> (u32, u32) { let writer = WRITER.lock(); @@ -71,21 +74,31 @@ impl Writer { return; } - // Get the character data from the font array. -- each byte is a row of pixels + // Get the character data from the font array. -- each byte is a row of + // pixels let data: &[u8] = self.font.glyph_for(c as u16); if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { for (row, line) in data.iter().enumerate().take(16) { for col in 0..8 { let pixel_x: u32 = self.text_col * FONT_WIDTH + col; - let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32; + let pixel_y: u32 = + self.text_line * FONT_HEIGHT + row as u32; if line & (0x80 >> col) != 0 { // Write the foreground color - writer.write_pixel(pixel_x as usize, pixel_y as usize, self.fg_color); + writer.write_pixel( + pixel_x as usize, + pixel_y as usize, + self.fg_color, + ); } else { // Write the background color - writer.write_pixel(pixel_x as usize, pixel_y as usize, self.bg_color); + writer.write_pixel( + pixel_x as usize, + pixel_y as usize, + self.bg_color, + ); } } } @@ -117,8 +130,8 @@ impl Writer { } } - /// Handles the backspace character. TODO: Implement VT-100 style terminal control - /// codes alongside a shell. Not simple. + /// Handles the backspace character. TODO: Implement VT-100 style terminal + /// control codes alongside a shell. Not simple. pub fn backspace(&mut self) { if self.text_col > 0 { self.text_col -= 1; diff --git a/kernel/src/arch/x86_64/drivers/framebuffer/colour.rs b/kernel/src/arch/x86_64/drivers/framebuffer/colour.rs index 2d6c0b6..f6ccb7d 100644 --- a/kernel/src/arch/x86_64/drivers/framebuffer/colour.rs +++ b/kernel/src/arch/x86_64/drivers/framebuffer/colour.rs @@ -19,9 +19,14 @@ impl From for u32 { fn from(val: Colour) -> Self { match val { Colour::ARGB(a, r, g, b) => { - (a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32) + (a as u32) << 24 + | (r as u32) << 16 + | (g as u32) << 8 + | (b as u32) + } + Colour::RGB(r, g, b) => { + ((r as u32) << 16) | (g as u32) << 8 | (b as u32) } - Colour::RGB(r, g, b) => ((r as u32) << 16) | (g as u32) << 8 | (b as u32), Colour::HexARGB(hex) => hex, Colour::Black => 0xFF000000, Colour::Blue => 0xFF0000FF, @@ -38,7 +43,9 @@ impl From for u32 { impl core::fmt::Display for Colour { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a), + Self::ARGB(r, g, b, a) => { + write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a) + } Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b), Self::HexARGB(hex) => write!(f, "Hex(#{:x})", hex), Self::Black => write!(f, "Black"), diff --git a/kernel/src/arch/x86_64/drivers/framebuffer/display.rs b/kernel/src/arch/x86_64/drivers/framebuffer/display.rs index 576b77b..49d2726 100644 --- a/kernel/src/arch/x86_64/drivers/framebuffer/display.rs +++ b/kernel/src/arch/x86_64/drivers/framebuffer/display.rs @@ -10,17 +10,19 @@ use core::panic; use limine::framebuffer::Framebuffer; use spin::{Lazy, Mutex}; -pub static FRAMEBUFFER_WRITER: Lazy>> = Lazy::new(|| { - Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else( - || { - panic!("Framebuffer request failed"); - }, - |framebuffer_response| { - let framebuffer = framebuffer_response.framebuffers().next().unwrap(); - Some(FramebufferWriter::new(framebuffer)) - }, - )) -}); +pub static FRAMEBUFFER_WRITER: Lazy>> = + Lazy::new(|| { + Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else( + || { + panic!("Framebuffer request failed"); + }, + |framebuffer_response| { + let framebuffer = + framebuffer_response.framebuffers().next().unwrap(); + Some(FramebufferWriter::new(framebuffer)) + }, + )) + }); /// The updated writer stores necessary fields from the [Framebuffer]. /// This ensures that the contained types are Send, as Framebuffer was @@ -28,7 +30,8 @@ pub static FRAMEBUFFER_WRITER: Lazy>> = Lazy::ne /// /// It also avoids the requirement for lifetimes. /// -/// Note this does not implement Writer as these functions only handle drawing pixels. +/// Note this does not implement Writer as these functions only handle drawing +/// pixels. pub struct FramebufferWriter { pitch: u64, bpp: u16, diff --git a/kernel/src/arch/x86_64/drivers/keyboard.rs b/kernel/src/arch/x86_64/drivers/keyboard.rs index 928d17a..90929ba 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard.rs +++ b/kernel/src/arch/x86_64/drivers/keyboard.rs @@ -6,20 +6,24 @@ use core::{ use crate::println; use crossbeam::queue::ArrayQueue; use futures_util::{Stream, StreamExt, task::AtomicWaker}; -use pc_keyboard::{DecodedKey, HandleControl, KeyCode, Keyboard, ScancodeSet1, layouts::Uk105Key}; +use pc_keyboard::{ + DecodedKey, HandleControl, KeyCode, Keyboard, ScancodeSet1, + layouts::Uk105Key, +}; use spin::{Lazy, Mutex, Once}; static KBD_QUEUE: Once> = Once::new(); static WAKER: AtomicWaker = AtomicWaker::new(); -pub static KEYBOARD: Lazy>> = Lazy::new(|| { - Mutex::new(Keyboard::new( - ScancodeSet1::new(), - // TODO: Expose an API to change the default KB layout. - Uk105Key, - HandleControl::Ignore, - )) -}); +pub static KEYBOARD: Lazy>> = + Lazy::new(|| { + Mutex::new(Keyboard::new( + ScancodeSet1::new(), + // TODO: Expose an API to change the default KB layout. + Uk105Key, + HandleControl::Ignore, + )) + }); pub static SCANCODE_STREAM: Lazy> = Lazy::new(|| Mutex::new(ScancodeStream::new())); @@ -59,7 +63,10 @@ impl Default for ScancodeStream { impl Stream for ScancodeStream { type Item = u8; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let queue = KBD_QUEUE.get().unwrap(); if let Some(scancode) = queue.pop() { diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 0e385f3..b869aa6 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,9 +1,13 @@ use crate::serial_print; use pic8259::ChainedPics; use x86_64::registers::control::Cr2; -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; +use x86_64::structures::idt::{ + InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode, +}; use x86_64::structures::paging::mapper::MapperFlushAll; -use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}; +use x86_64::structures::paging::{ + FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, +}; use super::gdt; use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; @@ -25,7 +29,8 @@ static IDT: Lazy = Lazy::new(|| { idt.page_fault.set_handler_fn(page_fault_handler); idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); - idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); + idt[InterruptIndex::Keyboard.as_u8()] + .set_handler_fn(keyboard_interrupt_handler); idt }); @@ -90,19 +95,22 @@ extern "x86-interrupt" fn double_fault_handler( panic!("Exception: Double Fault\n{:#?}", stack_frame); } -extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { +extern "x86-interrupt" fn keyboard_interrupt_handler( + _stack_frame: InterruptStackFrame, +) { use pc_keyboard::{HandleControl, Keyboard, ScancodeSet1, layouts}; // use pc_keyboard::DecodedKey; use spin::Mutex; use x86_64::instructions::port::Port; - static KEYBOARD: Lazy>> = Lazy::new(|| { - Mutex::new(Keyboard::new( - ScancodeSet1::new(), - layouts::Uk105Key, - HandleControl::Ignore, - )) - }); + static KEYBOARD: Lazy>> = + Lazy::new(|| { + Mutex::new(Keyboard::new( + ScancodeSet1::new(), + layouts::Uk105Key, + HandleControl::Ignore, + )) + }); let _keyboard = KEYBOARD.lock(); let mut port = Port::new(0x60); @@ -116,7 +124,9 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac } } -extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { +extern "x86-interrupt" fn timer_interrupt_handler( + _stack_frame: InterruptStackFrame, +) { unsafe { PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); @@ -137,7 +147,8 @@ extern "x86-interrupt" fn page_fault_handler( let frame = f.allocate_frame().unwrap(); let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - let page: Page = Page::containing_address(Cr2::read().unwrap()); + let page: Page = + Page::containing_address(Cr2::read().unwrap()); unsafe { let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); diff --git a/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs index 96c5274..41baa41 100644 --- a/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs +++ b/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs @@ -1,19 +1,27 @@ +use crate::arch::x86_64::memory::{HEAP_SIZE, HEAP_VIRTUAL_SPACE}; use core::alloc::{GlobalAlloc, Layout}; use core::ptr; use spin::{Mutex, MutexGuard}; use x86_64::structures::paging::{Size4KiB, mapper::MapToError}; -use crate::arch::x86_64::memory::{HEAP_SIZE, HEAP_VIRTUAL_SPACE}; -/// We are currently using a linked list heap allocator which uses our underlying page allocator. +/// We are currently using a linked list heap allocator which uses our +/// underlying page allocator. #[global_allocator] -/// This is now Rust's global allocator, so we can use stuff requiring heap allocations. -static ALLOCATOR: Locked = Locked::new(FoundryAllocator::new()); - +/// This is now Rust's global allocator, so we can use stuff requiring heap +/// allocations. +static ALLOCATOR: Locked = + Locked::new(FoundryAllocator::new()); /// Sets up the heap using the backing page frame allocator. +/// +/// # Safety +/// +/// We do not check whether the given heap start address and size are actually +/// valid. pub unsafe fn init_heap() -> Result<(), MapToError> { unsafe { - // code to allocate frames is now done in the page fault interrupt handler! + // The code to allocate frames is now done in the page fault interrupt + // handler! ALLOCATOR.lock().init(HEAP_VIRTUAL_SPACE, HEAP_SIZE); Ok(()) } @@ -60,11 +68,13 @@ impl FoundryAllocator { fallback: Locked::new(FoundryFallbackAllocator::new()), } } - /// Initializes the fallback allocator with the given heap start address and size. + /// Initializes the fallback allocator with the given heap start address and + /// size. /// /// # Safety /// - /// This function is unsafe because it does not check whether the given heap start address and size are valid. + /// This function is unsafe because it does not check whether the given heap + /// start address and size are valid. pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { unsafe { self.fallback.lock().init(heap_start, heap_size); @@ -97,7 +107,9 @@ unsafe impl GlobalAlloc for Locked { let block_size = BLOCK_SIZES[index]; // only works if all block sizes are a power of 2 let block_align = block_size; - let layout = Layout::from_size_align(block_size, block_align).unwrap(); + let layout = + Layout::from_size_align(block_size, block_align) + .unwrap(); unsafe { allocator.fallback_alloc(layout) } } } @@ -183,7 +195,9 @@ impl FoundryFallbackAllocator { let mut current = &mut self.head; // look for a large enough memory region in linked list while let Some(ref mut region) = current.next { - if let Ok(alloc_start) = Self::alloc_from_region(region, size, align) { + if let Ok(alloc_start) = + Self::alloc_from_region(region, size, align) + { // region suitable for allocation -> remove node from list let next = region.next.take(); let ret = Some((current.next.take().unwrap(), alloc_start)); @@ -236,7 +250,8 @@ unsafe impl GlobalAlloc for Locked { // perform layout adjustments let (size, align) = FoundryFallbackAllocator::size_align(layout); - if let Some((region, alloc_start)) = allocator.find_region(size, align) { + if let Some((region, alloc_start)) = allocator.find_region(size, align) + { let alloc_end = alloc_start.checked_add(size).expect("overflow"); let excess_size = region.end_addr() - alloc_end; if excess_size > 0 { diff --git a/kernel/src/arch/x86_64/memory/allocation/mod.rs b/kernel/src/arch/x86_64/memory/allocation/mod.rs index 6877073..979df58 100644 --- a/kernel/src/arch/x86_64/memory/allocation/mod.rs +++ b/kernel/src/arch/x86_64/memory/allocation/mod.rs @@ -1,3 +1,3 @@ pub mod heap_alloc; -pub mod stack_alloc; pub(crate) mod page_alloc; +pub mod stack_alloc; diff --git a/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs index 3fa9aae..e0c87f8 100644 --- a/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs +++ b/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs @@ -1,10 +1,12 @@ -use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB}; +use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; +use limine::memory_map::EntryType; use limine::response::MemoryMapResponse; use spin::Mutex; -use limine::memory_map::EntryType; -use x86_64::{PhysAddr, VirtAddr}; use x86_64::structures::paging::mapper::MapToError; -use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; +use x86_64::structures::paging::{ + FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, +}; +use x86_64::{PhysAddr, VirtAddr}; pub struct FoundryOSFrameAllocator { memory_map: &'static MemoryMapResponse, @@ -14,14 +16,17 @@ pub struct FoundryOSFrameAllocator { impl FoundryOSFrameAllocator { /// Creates a new `FoundryOSFrameAllocator` from a memory map. /// - /// This function takes a reference to a `MemoryMapResponse` and initializes a - /// `FoundryOSFrameAllocator` with it. The `next` field is set to 0, indicating that - /// the first frame to be allocated is the first frame in the memory map. + /// This function takes a reference to a `MemoryMapResponse` and initializes + /// a `FoundryOSFrameAllocator` with it. The `next` field is set to 0, + /// indicating that the first frame to be allocated is the first frame + /// in the memory map. pub fn init(memory_map: &'static MemoryMapResponse) { - FRAME_ALLOCATOR.call_once(|| Mutex::new(Self { - memory_map, - next: 0, - })); + FRAME_ALLOCATOR.call_once(|| { + Mutex::new(Self { + memory_map, + next: 0, + }) + }); } pub fn count_usable_frames(&self) -> u32 { @@ -29,7 +34,7 @@ impl FoundryOSFrameAllocator { } pub fn available_memory(&self) -> u64 { - ( self.memory_map.entries().len() * 4096 ) as u64 // multiply the number of physical frames by 4096 to get the memory size + (self.memory_map.entries().len() * 4096) as u64 // multiply the number of physical frames by 4096 to get the memory size } /// An iterator over all usable frames in the memory map. @@ -39,20 +44,35 @@ impl FoundryOSFrameAllocator { /// This function is used to allocate frames for the pagemap. fn usable_frames(&self) -> impl Iterator + use<> { let regions = self.memory_map.entries().iter(); - let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE); - let addr_ranges = usable_regions.map(|region| region.base..region.base + region.length); + let usable_regions = + regions.filter(|region| region.entry_type == EntryType::USABLE); + let addr_ranges = usable_regions + .map(|region| region.base..region.base + region.length); let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); - frame_addresses.map(|addr| PhysFrame::from_start_address(PhysAddr::new(addr)).unwrap()) + frame_addresses.map(|addr| { + PhysFrame::from_start_address(PhysAddr::new(addr)).unwrap() + }) } - - - fn allocate_page(&mut self, start_addr: VirtAddr, flags: PageTableFlags) -> Result<(), MapToError> { - let page = Page::from_start_address(start_addr).map_err(|_| MapToError::FrameAllocationFailed)?; - let frame = self.allocate_frame().ok_or(MapToError::FrameAllocationFailed)?; + #[expect(unused)] + fn allocate_page( + &mut self, + start_addr: VirtAddr, + flags: PageTableFlags, + ) -> Result<(), MapToError> { + let page = Page::from_start_address(start_addr) + .map_err(|_| MapToError::FrameAllocationFailed)?; + let frame = self + .allocate_frame() + .ok_or(MapToError::FrameAllocationFailed)?; let _ = unsafe { let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); - mapper.map_to(page, frame, flags, &mut *FRAME_ALLOCATOR.get().unwrap().lock())? + mapper.map_to( + page, + frame, + flags, + &mut *FRAME_ALLOCATOR.get().unwrap().lock(), + )? }; Ok(()) } @@ -61,9 +81,9 @@ impl FoundryOSFrameAllocator { unsafe impl FrameAllocator for FoundryOSFrameAllocator { /// Allocates a frame from the list of usable frames. /// - /// This function returns the next available `PhysFrame` from the memory map, - /// if one exists. Once a frame is allocated, the internal counter is incremented - /// to point to the next frame for future allocations. + /// This function returns the next available `PhysFrame` from the memory + /// map, if one exists. Once a frame is allocated, the internal counter + /// is incremented to point to the next frame for future allocations. /// /// # Returns /// @@ -74,4 +94,4 @@ unsafe impl FrameAllocator for FoundryOSFrameAllocator { self.next += 1; frame } -} \ No newline at end of file +} diff --git a/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs index 513b9fe..1665f22 100644 --- a/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs +++ b/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs @@ -1,33 +1,39 @@ -use x86_64::structures::paging::{mapper, FrameAllocator, Mapper, Page, Size4KiB}; -use x86_64::VirtAddr; use crate::arch::x86_64::memory::STACK_VIRTUAL_SPACE; +use x86_64::VirtAddr; +use x86_64::structures::paging::{ + FrameAllocator, Mapper, Page, Size4KiB, mapper, +}; fn reserve_stack_memory(size_in_pages: u64) -> Page { use core::sync::atomic::{AtomicU64, Ordering}; - static STACK_ALLOC_NEXT: AtomicU64 = AtomicU64::new(STACK_VIRTUAL_SPACE as u64); - let start_addr = VirtAddr::new(STACK_ALLOC_NEXT.fetch_add( - size_in_pages * Page::::SIZE, - Ordering::Relaxed, - )); + static STACK_ALLOC_NEXT: AtomicU64 = + AtomicU64::new(STACK_VIRTUAL_SPACE as u64); + let start_addr = + VirtAddr::new(STACK_ALLOC_NEXT.fetch_add( + size_in_pages * Page::::SIZE, + Ordering::Relaxed, + )); Page::from_start_address(start_addr) .expect("`STACK_ALLOC_NEXT` not page aligned") } /// Allocates a stack in the virtual address space, mapped to physical pages. /// -/// This function allocates a stack in the virtual address space, mapped to physical pages. -/// The stack is allocated as a sequence of pages, with the first page allocated as a guard page. -/// The stack is then mapped to the allocated physical frame. +/// This function allocates a stack in the virtual address space, mapped to +/// physical pages. The stack is allocated as a sequence of pages, with the +/// first page allocated as a guard page. The stack is then mapped to the +/// allocated physical frame. /// -/// The function takes the size of the stack in pages, a mutable reference to a mapper, and a -/// mutable reference to a frame allocator. It returns a `Result` containing a `StackBounds` -/// struct, which contains the start and end virtual addresses of the allocated stack. +/// The function takes the size of the stack in pages, a mutable reference to a +/// mapper, and a mutable reference to a frame allocator. It returns a `Result` +/// containing a `StackBounds` struct, which contains the start and end virtual +/// addresses of the allocated stack. /// /// # Safety /// -/// This function is unsafe because it maps physical frames to virtual addresses without any -/// protection. This can lead to bugs if the physical frames are not correctly allocated, or if the -/// virtual addresses are not correctly aligned. +/// This function is unsafe because it maps physical frames to virtual addresses +/// without any protection. This can lead to bugs if the physical frames are not +/// correctly allocated, or if the virtual addresses are not correctly aligned. /// /// # Panics /// @@ -36,27 +42,28 @@ pub unsafe fn alloc_stack( size_in_pages: u64, mapper: &mut impl Mapper, frame_allocator: &mut impl FrameAllocator, -) -> Result> { unsafe { - use x86_64::structures::paging::PageTableFlags as Flags; - - let guard_page = reserve_stack_memory(size_in_pages + 1); - let stack_start = guard_page + 1; - let stack_end = stack_start + size_in_pages; +) -> Result> { + unsafe { + use x86_64::structures::paging::PageTableFlags as Flags; - for page in Page::range(stack_start, stack_end) { - let frame = frame_allocator - .allocate_frame() - .ok_or(mapper::MapToError::FrameAllocationFailed)?; - let flags = Flags::PRESENT | Flags::WRITABLE; - mapper.map_to(page, frame, flags, frame_allocator)?.flush(); + let guard_page = reserve_stack_memory(size_in_pages + 1); + let stack_start = guard_page + 1; + let stack_end = stack_start + size_in_pages; + + for page in Page::range(stack_start, stack_end) { + let frame = frame_allocator + .allocate_frame() + .ok_or(mapper::MapToError::FrameAllocationFailed)?; + let flags = Flags::PRESENT | Flags::WRITABLE; + mapper.map_to(page, frame, flags, frame_allocator)?.flush(); + } + + Ok(StackBounds { + start: stack_start.start_address(), + end: stack_end.start_address(), + }) } - - Ok(StackBounds { - start: stack_start.start_address(), - end: stack_end.start_address(), - }) -}} - +} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct StackBounds { @@ -76,4 +83,4 @@ impl StackBounds { pub const fn end(&self) -> VirtAddr { self.end } -} \ No newline at end of file +} diff --git a/kernel/src/arch/x86_64/memory/mapping.rs b/kernel/src/arch/x86_64/memory/mapping.rs index 1df8437..305d800 100644 --- a/kernel/src/arch/x86_64/memory/mapping.rs +++ b/kernel/src/arch/x86_64/memory/mapping.rs @@ -16,7 +16,8 @@ static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new(); #[used] #[unsafe(link_section = ".requests")] -static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new(); +static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = + KernelAddressRequest::new(); /// ```rs /// let virt_addr = phys_addr + offset; diff --git a/kernel/src/arch/x86_64/memory/mod.rs b/kernel/src/arch/x86_64/memory/mod.rs index fe6d18b..0ee8807 100644 --- a/kernel/src/arch/x86_64/memory/mod.rs +++ b/kernel/src/arch/x86_64/memory/mod.rs @@ -2,20 +2,19 @@ pub mod allocation; pub mod mapping; pub mod units; -use spin::{Mutex, Once}; -use x86_64::{ - registers::control::Cr3, structures::paging::{FrameAllocator, OffsetPageTable, PageTable} - , - VirtAddr, -}; use allocation::page_alloc::FoundryOSFrameAllocator; +use spin::{Mutex, Once}; use units::MemoryUnits::*; +use x86_64::{ + VirtAddr, + registers::control::Cr3, + structures::paging::{OffsetPageTable, PageTable}, +}; pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000; // start address of the memory space where we store allocated stacks pub const HEAP_VIRTUAL_SPACE: usize = 0x4444_4444_0000; // start address of heap allocated memory pub const HEAP_SIZE: usize = MiB(1).to_bytes(); - pub static FRAME_ALLOCATOR: Once> = Once::new(); pub static OFFSET_PAGE_TABLE: Once> = Once::new(); /// Returns a mutable reference to the current level 4 page table. @@ -25,7 +24,9 @@ pub static OFFSET_PAGE_TABLE: Once> = Once::new(); /// The caller must ensure that the level 4 page table is not modified /// simultaneously. The caller must also ensure that the physical memory offset /// is correct, to ensure that the correct virtual address is constructed. -unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { +unsafe fn active_l4_table( + physical_memory_offset: VirtAddr, +) -> &'static mut PageTable { let (level_4_frame, _) = Cr3::read(); let phys_addr = level_4_frame.start_address(); @@ -54,8 +55,8 @@ unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut Page pub fn init_page_table(physical_memory_offset: VirtAddr) { unsafe { let l4_table = active_l4_table(physical_memory_offset); - let offset_table = OffsetPageTable::new(l4_table, physical_memory_offset); + let offset_table = + OffsetPageTable::new(l4_table, physical_memory_offset); OFFSET_PAGE_TABLE.call_once(|| Mutex::new(offset_table)); } } - diff --git a/kernel/src/arch/x86_64/memory/units.rs b/kernel/src/arch/x86_64/memory/units.rs index 4d24d50..7129ab8 100644 --- a/kernel/src/arch/x86_64/memory/units.rs +++ b/kernel/src/arch/x86_64/memory/units.rs @@ -30,7 +30,7 @@ impl MemoryUnits { pub const fn convert(&mut self) { match self { Self::B(b) if *b > 1024 => *self = Self::KiB(*b / 1024), - Self::KiB(kib) if *kib > 1024 => *self = Self::MiB(*kib / 1024), + Self::KiB(kib) if *kib > 1024 => *self = Self::MiB(*kib / 1024), Self::MiB(mib) if *mib > 1024 => *self = Self::GiB(*mib / 1024), _ => (), } @@ -46,4 +46,4 @@ impl core::fmt::Display for MemoryUnits { Self::GiB(gib) => write!(f, "{} GiB", gib), } } -} \ No newline at end of file +} diff --git a/kernel/src/arch/x86_64/processing/async_io/task.rs b/kernel/src/arch/x86_64/processing/async_io/task.rs index e522af0..c2ccf20 100644 --- a/kernel/src/arch/x86_64/processing/async_io/task.rs +++ b/kernel/src/arch/x86_64/processing/async_io/task.rs @@ -2,7 +2,6 @@ //! //! Written by @zxq5 for the most part with code from //! [here](https://github.com/phil-opp/blog_os/). -//! use alloc::boxed::Box; use alloc::collections::BTreeMap; @@ -81,9 +80,9 @@ impl Executor { Some(task) => task, None => continue, // task no longer exists }; - let waker = waker_cache - .entry(task_id) - .or_insert_with(|| TaskWaker::new_waker(task_id, task_queue.clone())); + let waker = waker_cache.entry(task_id).or_insert_with(|| { + TaskWaker::new_waker(task_id, task_queue.clone()) + }); let mut context = Context::from_waker(waker); match task.poll(&mut context) { Poll::Ready(()) => { @@ -129,7 +128,10 @@ impl TaskWaker { self.task_queue.push(self.task_id).expect("task_queue full"); } - fn new_waker(task_id: TaskId, task_queue: Arc>) -> Waker { + fn new_waker( + task_id: TaskId, + task_queue: Arc>, + ) -> Waker { Waker::from(Arc::new(Self { task_id, task_queue, diff --git a/kernel/src/arch/x86_64/processing/mod.rs b/kernel/src/arch/x86_64/processing/mod.rs index 01f827c..b30ed57 100644 --- a/kernel/src/arch/x86_64/processing/mod.rs +++ b/kernel/src/arch/x86_64/processing/mod.rs @@ -1,3 +1,4 @@ +#![expect(unused)] pub mod async_io; -pub mod threading; mod taskrunner; +pub mod threading; diff --git a/kernel/src/arch/x86_64/processing/taskrunner/mod.rs b/kernel/src/arch/x86_64/processing/taskrunner/mod.rs index 0198236..7b041a1 100644 --- a/kernel/src/arch/x86_64/processing/taskrunner/mod.rs +++ b/kernel/src/arch/x86_64/processing/taskrunner/mod.rs @@ -6,7 +6,7 @@ pub struct Task { } impl Task { - pub fn new() {} + // pub const fn new() {} - pub fn run() {} -} \ No newline at end of file + pub const fn run() {} +} diff --git a/kernel/src/arch/x86_64/processing/threading/mod.rs b/kernel/src/arch/x86_64/processing/threading/mod.rs index 2aa540f..db5bf89 100644 --- a/kernel/src/arch/x86_64/processing/threading/mod.rs +++ b/kernel/src/arch/x86_64/processing/threading/mod.rs @@ -1,5 +1,5 @@ -use x86_64::VirtAddr; use crate::arch::x86_64::memory::allocation::stack_alloc::StackBounds; +use x86_64::VirtAddr; mod switch; @@ -10,8 +10,6 @@ pub struct Thread { stack_bounds: Option, } - - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ThreadId(u64); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 1a692bc..a74060b 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -12,17 +12,14 @@ )] extern crate alloc; -use crate::{ - arch::x86_64::memory::init_page_table, - prelude::*, -}; +use crate::arch::x86_64::cpu::apic::enable_apic; +use crate::{arch::x86_64::memory::init_page_table, prelude::*}; use arch::x86_64::memory::allocation::heap_alloc::init_heap; +use arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator; use arch::x86_64::memory::mapping; use core::arch::asm; use limine::BaseRevision; use x86_64::VirtAddr; -use arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator; -use crate::arch::x86_64::cpu::apic::enable_apic; pub mod arch; pub mod resources; @@ -33,15 +30,18 @@ pub mod util; pub mod prelude { pub use crate::std::io::{_print, _print_log, _serial_write}; pub use crate::{ - print, print_log, printerr, println, println_log, printlnerr, serial_print, serial_println, + print, print_log, printerr, println, println_log, printlnerr, + serial_print, serial_println, }; } /// Sets the base revision to the latest revision supported by the crate. /// See specification for further info. -/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler. +/// Be sure to mark all limine requests with #[used], otherwise they may be +/// removed by the compiler. #[used] -// The .requests section allows limine to find the requests faster and more safely. +// The .requests section allows limine to find the requests faster and more +// safely. #[unsafe(link_section = ".requests")] static BASE_REVISION: BaseRevision = BaseRevision::new(); @@ -86,7 +86,8 @@ pub fn boot() -> Result<(), &'static str> { println_log!("[Success]"); print_log!(" Initialising Memory Subsystem... "); - let physical_memory_offset = VirtAddr::new(*mapping::PHYSICAL_MEMORY_OFFSET); + let physical_memory_offset = + VirtAddr::new(*mapping::PHYSICAL_MEMORY_OFFSET); init_page_table(physical_memory_offset); println_log!("[Success]"); diff --git a/kernel/src/std/application.rs b/kernel/src/std/application.rs index 7131253..7976172 100644 --- a/kernel/src/std/application.rs +++ b/kernel/src/std/application.rs @@ -8,7 +8,10 @@ pub mod window; pub trait Application { type Output; - fn run(&mut self, args: Vec) -> impl Future> + Send; + fn run( + &mut self, + args: Vec, + ) -> impl Future> + Send; } #[derive(Debug)] diff --git a/kernel/src/std/application/frame.rs b/kernel/src/std/application/frame.rs index a1cdfb6..b8a6346 100644 --- a/kernel/src/std/application/frame.rs +++ b/kernel/src/std/application/frame.rs @@ -11,20 +11,31 @@ pub struct Frame<'f> { impl<'a> Frame<'a> { pub fn new(window: &'a Window) -> Self { Self { - data: vec![vec![Colour::Black; window.dimensions().x()]; window.dimensions().y()], + data: vec![ + vec![Colour::Black; window.dimensions().x()]; + window.dimensions().y() + ], window, } } pub fn render(&self) -> Result<(), RenderError> { - let data: Vec<&[Colour]> = self.data.iter().map(|v| v.as_slice()).collect::>(); + let data: Vec<&[Colour]> = + self.data.iter().map(|v| v.as_slice()).collect::>(); self.window .render(data.as_slice()) .map_err(|_| RenderError::Generic) } - pub fn write_pixel(&mut self, x: usize, y: usize, color: Colour) -> Result<(), RenderError> { - if x >= self.window.dimensions().x() || y >= self.window.dimensions().y() { + pub fn write_pixel( + &mut self, + x: usize, + y: usize, + color: Colour, + ) -> Result<(), RenderError> { + if x >= self.window.dimensions().x() + || y >= self.window.dimensions().y() + { return Err(RenderError::Generic); } self.data[y][x] = color; diff --git a/kernel/src/std/application/window.rs b/kernel/src/std/application/window.rs index 758cbf6..bcc4156 100644 --- a/kernel/src/std/application/window.rs +++ b/kernel/src/std/application/window.rs @@ -24,7 +24,8 @@ impl Window { } pub fn render(&self, _data: &[&[Colour]]) -> Result<(), RenderError> { - // TODO: error handling!! the kernel should return an error in some cases + // TODO: error handling!! the kernel should return an error in some + // cases if let Some(fb) = FRAMEBUFFER_WRITER.lock().as_mut() { fb.render_frame(_data); } diff --git a/kernel/src/std/ascii.rs b/kernel/src/std/ascii.rs index 1a37feb..97eb294 100644 --- a/kernel/src/std/ascii.rs +++ b/kernel/src/std/ascii.rs @@ -46,7 +46,11 @@ impl<'a> Writer<'a> { if line & (0x80 >> col) != 0 { for i in 0..scale { for j in 0..scale { - frame.write_pixel(pixel_x + i, pixel_y + j, Colour::White)?; + frame.write_pixel( + pixel_x + i, + pixel_y + j, + Colour::White, + )?; } } } diff --git a/kernel/src/std/io.rs b/kernel/src/std/io.rs index 4719ec5..c974bd7 100644 --- a/kernel/src/std/io.rs +++ b/kernel/src/std/io.rs @@ -6,16 +6,16 @@ pub use crate::arch::x86_64::drivers::{ pub mod stdin { use crate::arch::x86_64::drivers::{ ascii::WRITER, - keyboard::{ - get_keystroke_async, get_keystroke_optional, KeyStroke, - } + keyboard::{KeyStroke, get_keystroke_async, get_keystroke_optional}, }; use alloc::string::String; - /// Reads a line of input from standard input asynchronously, returning a `String` containing - /// the input line. Does not include the newline character at the end of the line. + /// Reads a line of input from standard input asynchronously, returning a + /// `String` containing the input line. Does not include the newline + /// character at the end of the line. /// - /// If the user presses the abort key (usually Ctrl+C), the returned string will be empty. + /// If the user presses the abort key (usually Ctrl+C), the returned string + /// will be empty. /// /// This function is currently unimplemented. pub async fn read_line() -> String { @@ -60,8 +60,8 @@ pub mod stdin { } } - /// Reads a character from standard input and blocks the current task until a character is - /// available. + /// Reads a character from standard input and blocks the current task until + /// a character is available. /// /// # Note /// @@ -70,7 +70,8 @@ pub mod stdin { get_keystroke_async().await } - /// Attempt to read a character from standard input without blocking the current task. + /// Attempt to read a character from standard input without blocking the + /// current task. /// /// If no character is available, returns `None`. /// diff --git a/kernel/src/util/editor.rs b/kernel/src/util/editor.rs index 594d2a8..baa454c 100644 --- a/kernel/src/util/editor.rs +++ b/kernel/src/util/editor.rs @@ -1,6 +1,7 @@ -use crate::serial_print; use crate::arch::x86_64::drivers::keyboard::{KeyStroke, get_keystroke_async}; use crate::resources::font::Font; +use crate::serial_print; +use crate::serial_println; use crate::std::application::frame::Frame; use crate::std::application::render::RenderError; use crate::std::application::window::Window; @@ -9,7 +10,6 @@ use crate::std::ascii::Writer; use crate::std::maths::geometry::Vec2; use alloc::string::{String, ToString}; use alloc::vec::Vec; -use crate::serial_println; pub struct Editor { cursor_line: usize, @@ -65,7 +65,10 @@ impl<'a> Editor { writer.render_glyph( &mut frame, - Vec2::new(col * width + Self::PADDING, line * height + Self::PADDING), + Vec2::new( + col * width + Self::PADDING, + line * height + Self::PADDING, + ), ch as u8, scale, )?; @@ -157,7 +160,10 @@ impl<'a> Editor { impl Application for Editor { type Output = (); - async fn run(&mut self, _args: Vec) -> Result { + async fn run( + &mut self, + _args: Vec, + ) -> Result { self.window.set_dimensions(Vec2::new(1280, 800)); self.window.set_position(Vec2::new(0, 0)); self.window.open(); @@ -167,7 +173,9 @@ impl Application for Editor { loop { if let Err(_err) = self.render().and_then(|frame| frame.render()) { // TODO: Handle error - return Err(Error::ApplicationFailed("Rendering failed".to_string())); + return Err(Error::ApplicationFailed( + "Rendering failed".to_string(), + )); } let keystroke = get_keystroke_async().await; diff --git a/libm/src/lib.rs b/libm/src/lib.rs index 7480206..7a30e03 100644 --- a/libm/src/lib.rs +++ b/libm/src/lib.rs @@ -80,7 +80,12 @@ impl FontBuilder { fn revision(data: &[u8]) -> u8 { if (data[0] as u16) << 8 | data[1] as u16 == Self::PSF1_MAGIC { 1 - } else if (data[0] as u32) << 24 | (data[1] as u32) << 16 | (data[2] as u32) << 8 | data[3] as u32 == Self::PSF2_MAGIC { + } else if (data[0] as u32) << 24 + | (data[1] as u32) << 16 + | (data[2] as u32) << 8 + | data[3] as u32 + == Self::PSF2_MAGIC + { 2 } else { 0 diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..f661675 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +wrap_comments = true +max_width = 80 +comment_width = 80 +format_code_in_doc_comments = true From 014ec5310cef6b5f530c97ffd42b3de092a252d6 Mon Sep 17 00:00:00 2001 From: Jacob Hinchliffe Date: Wed, 5 Mar 2025 00:57:06 +0000 Subject: [PATCH 2/4] Some more clippy fixes, warnings are noisy --- .vscode/settings.json | 3 ++- kernel/src/arch/x86_64/cpu/apic.rs | 2 ++ kernel/src/arch/x86_64/cpu/msr.rs | 1 + kernel/src/arch/x86_64/cpu/pic.rs | 2 ++ .../src/arch/x86_64/memory/allocation/heap_alloc.rs | 13 ++++++++++--- kernel/src/resources/font/mod.rs | 10 ++++++---- kernel/src/util/editor.rs | 13 +++++++------ libm/src/lib.rs | 9 ++++----- 8 files changed, 34 insertions(+), 19 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 17d2304..5b7e9e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,6 @@ "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true }, - "rust-analyzer.check.command": "clippy" + "rust-analyzer.check.command": "clippy", + "rust-analyzer.cargo.buildScripts.enable": true } \ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpu/apic.rs b/kernel/src/arch/x86_64/cpu/apic.rs index 3672f38..bcb94cd 100644 --- a/kernel/src/arch/x86_64/cpu/apic.rs +++ b/kernel/src/arch/x86_64/cpu/apic.rs @@ -1,3 +1,5 @@ +#![expect(unused)] + use core::arch::x86_64::__cpuid; use crate::arch::x86_64::memory::mapping::PHYSICAL_MEMORY_OFFSET; diff --git a/kernel/src/arch/x86_64/cpu/msr.rs b/kernel/src/arch/x86_64/cpu/msr.rs index 5dae062..0463b70 100644 --- a/kernel/src/arch/x86_64/cpu/msr.rs +++ b/kernel/src/arch/x86_64/cpu/msr.rs @@ -1,3 +1,4 @@ +#![expect(unused)] use core::arch::x86_64::__cpuid; use spin::Lazy; use x86_64::registers::model_specific::Msr; diff --git a/kernel/src/arch/x86_64/cpu/pic.rs b/kernel/src/arch/x86_64/cpu/pic.rs index b43313d..4f95008 100644 --- a/kernel/src/arch/x86_64/cpu/pic.rs +++ b/kernel/src/arch/x86_64/cpu/pic.rs @@ -1,3 +1,5 @@ +#![allow(clippy::missing_safety_doc)] + use x86_64::instructions::port::Port; const CMD_INIT: u8 = 0x11; diff --git a/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs index 41baa41..4173efd 100644 --- a/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs +++ b/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs @@ -173,15 +173,22 @@ impl FoundryFallbackAllocator { } } + /// Initialises the Foundry OS fallback allocator. This is currently a + /// linked list allocator pulled from the linked_list_allocator crate. + /// + /// # Safety + /// + /// This function assumes you passed a valid `heap_start` and `heap_size`, + /// as these are unchecked. pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { unsafe { self.add_region(heap_start, heap_size) }; } unsafe fn add_region(&mut self, addr: usize, size: usize) { + let mut node = FallbackListNode::new(size); + node.next = self.head.next.take(); + let node_ptr = addr as *mut FallbackListNode; unsafe { - let mut node = FallbackListNode::new(size); - node.next = self.head.next.take(); - let node_ptr = addr as *mut FallbackListNode; node_ptr.write(node); self.head.next = Some(&mut *node_ptr); } diff --git a/kernel/src/resources/font/mod.rs b/kernel/src/resources/font/mod.rs index ebe52ed..cc6b52a 100644 --- a/kernel/src/resources/font/mod.rs +++ b/kernel/src/resources/font/mod.rs @@ -1,8 +1,8 @@ use libm::include_font; -pub static FONT_SPLEEN_8X16: Font = +pub const FONT_SPLEEN_8X16: Font = Font::new(include_font!("../../../resources/font/spleen-8x16.psf")); -pub static FONT_CP850_8X16: Font = +pub const FONT_CP850_8X16: Font = Font::new(include_font!("../../../resources/font/cp850-8x16.psf")); // pub struct Font(pub [[u8; 16]; 512]); @@ -39,8 +39,10 @@ impl Font { pub const fn height(&self) -> usize { self.height } +} - pub fn default() -> &'static Self { - &FONT_CP850_8X16 +impl Default for Font { + fn default() -> Self { + FONT_CP850_8X16 } } diff --git a/kernel/src/util/editor.rs b/kernel/src/util/editor.rs index baa454c..abb21c1 100644 --- a/kernel/src/util/editor.rs +++ b/kernel/src/util/editor.rs @@ -2,10 +2,9 @@ use crate::arch::x86_64::drivers::keyboard::{KeyStroke, get_keystroke_async}; use crate::resources::font::Font; use crate::serial_print; use crate::serial_println; -use crate::std::application::frame::Frame; -use crate::std::application::render::RenderError; -use crate::std::application::window::Window; -use crate::std::application::{Application, Error}; +use crate::std::application::{ + Application, Error, frame::Frame, render::RenderError, window::Window, +}; use crate::std::ascii::Writer; use crate::std::maths::geometry::Vec2; use alloc::string::{String, ToString}; @@ -42,7 +41,8 @@ impl<'a> Editor { fn render(&'a self) -> Result, RenderError> { let mut frame = Frame::new(&self.window); - let writer = Writer::new(Font::default()); + let font = Font::default(); + let writer = Writer::new(&font); let (width, height) = writer.font_size().into(); @@ -127,7 +127,8 @@ impl<'a> Editor { fn get_char_idx(&self) -> usize { let frame = Frame::new(&self.window); - let writer = Writer::new(Font::default()); + let font = Font::default(); + let writer = Writer::new(&font); let (width, _height) = writer.font_size().into(); let mut col = 0; diff --git a/libm/src/lib.rs b/libm/src/lib.rs index 7a30e03..bf776c0 100644 --- a/libm/src/lib.rs +++ b/libm/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] #![feature(proc_macro_span)] #![warn( clippy::correctness, @@ -10,9 +11,6 @@ rustdoc::missing_panics_doc )] -use std::fs::File; -use std::io::{Read, Seek, SeekFrom}; - use proc_macro::{Span, TokenStream}; use quote::quote; use std::path::PathBuf; @@ -36,7 +34,8 @@ pub fn include_font(item: TokenStream) -> TokenStream { let source_filepath: PathBuf = source_file.path(); let file_path = format!( "{}/{}", - source_filepath.parent().unwrap_or_else(|| panic!("Expected to find the calling source file in a folder like src! Got: {}", source_filepath.display())).display(), + source_filepath.parent() + .unwrap_or_else(|| panic!("Expected to find the calling source file in a folder like src! Got: {}", source_filepath.display())).display(), filename.value() ); @@ -124,7 +123,7 @@ impl FontBuilder { }) } - fn parse_psf2(data: &[u8]) -> Result { + const fn parse_psf2(_data: &[u8]) -> Result { Err("PSF2 support is not implemented yet!") } } From af995d1d76da48da4ec0c4df747b86d1c2b40bf0 Mon Sep 17 00:00:00 2001 From: Jacob Hinchliffe Date: Wed, 5 Mar 2025 03:24:26 +0000 Subject: [PATCH 3/4] Tried and failed to use unwinding crate: shall do it myself tomorrow. --- kernel/Cargo.toml | 7 ++ kernel/linker.ld | 9 ++- kernel/src/arch/x86_64/drivers/ascii/mod.rs | 6 +- kernel/src/lib.rs | 7 +- kernel/src/panic.rs | 75 ++++++++++++++++++ kernel/src/std/io.rs | 84 --------------------- kernel/src/std/io/mod.rs | 6 ++ kernel/src/std/io/stdin.rs | 75 ++++++++++++++++++ 8 files changed, 177 insertions(+), 92 deletions(-) create mode 100644 kernel/src/panic.rs delete mode 100644 kernel/src/std/io.rs create mode 100644 kernel/src/std/io/mod.rs create mode 100644 kernel/src/std/io/stdin.rs diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8a42939..b82aae5 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -19,6 +19,13 @@ futures-util = { version = "0.3.31", default-features = false, features = [ "alloc", ] } linked_list_allocator = { version = "0.10.5", features = ["use_spin"] } +# unwinding = { version = "0.2.5", default-features = false, features = [ +# "unwinder", +# "fde-static", +# "personality", +# "panic", +# "hide-trace", +# ] } [build-dependencies] cc = "1.2.14" diff --git a/kernel/linker.ld b/kernel/linker.ld index b327207..a193ba6 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -46,6 +46,12 @@ SECTIONS *(.rodata .rodata.*) } :rodata + /* Adds support for stack unwinding using the unwinding crate. */ + . = ALIGN(8); + PROVIDE(__eh_frame = .); + .eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } + + /* Move to the next memory page for .data */ . = ALIGN(CONSTANT(MAXPAGESIZE)); @@ -62,9 +68,8 @@ SECTIONS *(COMMON) } :data - /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ + /* Discard .note.* and ~~.eh_frame*~~ since they may cause issues on some hosts. */ /DISCARD/ : { - *(.eh_frame*) *(.note .note.*) } } diff --git a/kernel/src/arch/x86_64/drivers/ascii/mod.rs b/kernel/src/arch/x86_64/drivers/ascii/mod.rs index d380b76..e2a75fa 100644 --- a/kernel/src/arch/x86_64/drivers/ascii/mod.rs +++ b/kernel/src/arch/x86_64/drivers/ascii/mod.rs @@ -237,12 +237,12 @@ macro_rules! print { } #[macro_export] -macro_rules! printlnerr { +macro_rules! eprintln { () => ($crate::printerr!("\n")); - ($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*))); + ($($arg:tt)*) => ($crate::eprint!("{}\n", format_args!($($arg)*))); } #[macro_export] -macro_rules! printerr { +macro_rules! eprint { ($($arg:tt)*) => ($crate::prelude::_print_err(format_args!($($arg)*))); } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index a74060b..59d616f 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -22,16 +22,17 @@ use limine::BaseRevision; use x86_64::VirtAddr; pub mod arch; +mod panic; pub mod resources; #[allow(unused)] // We aren't using much of this right now. pub mod std; pub mod util; pub mod prelude { - pub use crate::std::io::{_print, _print_log, _serial_write}; pub use crate::{ - print, print_log, printerr, println, println_log, printlnerr, - serial_print, serial_println, + eprint, eprintln, print, print_log, println, println_log, serial_print, + serial_println, + std::io::{_print, _print_err, _print_log, _serial_write}, }; } diff --git a/kernel/src/panic.rs b/kernel/src/panic.rs new file mode 100644 index 0000000..a10718d --- /dev/null +++ b/kernel/src/panic.rs @@ -0,0 +1,75 @@ +// //! Stack-unwinding code for the Kernel. This is a modified version of https://github.com/nbdd0121/unwinding/blob/trunk/src/panic_handler.rs +// //! which does not support environment variables for obvious reasons, however we +// //! may implement a cmdline similar to Linux in the near future. + +// #![expect( +// clippy::empty_loop, +// reason = "We don't yet have working power management. This is OK for now." +// )] +// use crate::prelude::*; +// use alloc::boxed::Box; +// use core::any::Any; +// use core::cell::Cell; +// use core::ffi::c_void; +// use core::panic::{Location, PanicInfo}; +// use unwinding::abi::*; +// use unwinding::panic::begin_panic; + +// #[thread_local] +// static PANIC_COUNT: Cell = Cell::new(0); + +// #[link(name = "c")] +// unsafe extern "C" {} + +// fn stack_trace() { +// struct CallbackData { +// counter: usize, +// } + +// extern "C" fn callback( +// unwind_ctx: &UnwindContext<'_>, +// arg: *mut c_void, +// ) -> UnwindReasonCode { +// let data = unsafe { &mut *(arg as *mut CallbackData) }; +// data.counter += 1; +// eprintln!( +// "{:4}:{:#19x} - ", +// data.counter, +// _Unwind_GetIP(unwind_ctx) +// ); +// UnwindReasonCode::NO_REASON +// } +// let mut data = CallbackData { counter: 0 }; +// _Unwind_Backtrace(callback, &mut data as *mut _ as _); +// } + +// fn do_panic(msg: Box) -> ! { +// if PANIC_COUNT.get() >= 1 { +// stack_trace(); +// eprintln!("Thread panicked while processing panic. aborting."); +// loop {} +// } + +// PANIC_COUNT.set(1); +// stack_trace(); + +// let code = begin_panic(Box::new(msg)); +// eprintln!("Failed to initiate panic: error {}", code.0); + +// loop {} +// } + +// #[panic_handler] +// fn panic(info: &PanicInfo<'_>) -> ! { +// eprintln!("{}", info); + +// struct NoPayload; +// do_panic(Box::new(NoPayload)) +// } + +// #[track_caller] +// #[expect(unused, reason = "May still be used in the future.")] +// pub fn panic_any(msg: M) -> ! { +// eprintln!("Panicked at {}", Location::caller()); +// do_panic(Box::new(msg)) +// } diff --git a/kernel/src/std/io.rs b/kernel/src/std/io.rs deleted file mode 100644 index c974bd7..0000000 --- a/kernel/src/std/io.rs +++ /dev/null @@ -1,84 +0,0 @@ -pub use crate::arch::x86_64::drivers::{ - ascii::{_print, _print_err, _print_log}, - serial::_serial_write, -}; - -pub mod stdin { - use crate::arch::x86_64::drivers::{ - ascii::WRITER, - keyboard::{KeyStroke, get_keystroke_async, get_keystroke_optional}, - }; - use alloc::string::String; - - /// Reads a line of input from standard input asynchronously, returning a - /// `String` containing the input line. Does not include the newline - /// character at the end of the line. - /// - /// If the user presses the abort key (usually Ctrl+C), the returned string - /// will be empty. - /// - /// This function is currently unimplemented. - pub async fn read_line() -> String { - let mut writer = WRITER.lock(); - - let mut buff = String::new(); - loop { - match get_keystroke_async().await { - KeyStroke::Char(c) => match c { - '\n' => { - writer.write_glyph(c as u8); - return buff; - } - '\r' => { - writer.write_glyph(c as u8); - return buff; - } - '\x08' => { - if !buff.is_empty() { - buff.pop(); - writer.backspace(); - } - } - - c => { - writer.write_glyph(c as u8); - buff.push(c) - } - }, - KeyStroke::Enter => { - writer.write_glyph(b'\n'); - return buff; - } - KeyStroke::Backspace => { - if !buff.is_empty() { - buff.pop(); - writer.backspace(); - } - } - _ => continue, - } - } - } - - /// Reads a character from standard input and blocks the current task until - /// a character is available. - /// - /// # Note - /// - /// This function is not yet implemented. - pub async fn async_keystroke() -> KeyStroke { - get_keystroke_async().await - } - - /// Attempt to read a character from standard input without blocking the - /// current task. - /// - /// If no character is available, returns `None`. - /// - /// # Note - /// - /// This function is not yet implemented. - pub fn keystroke() -> Option { - get_keystroke_optional() - } -} diff --git a/kernel/src/std/io/mod.rs b/kernel/src/std/io/mod.rs new file mode 100644 index 0000000..b43503a --- /dev/null +++ b/kernel/src/std/io/mod.rs @@ -0,0 +1,6 @@ +pub use crate::arch::x86_64::drivers::{ + ascii::{_print, _print_err, _print_log}, + serial::_serial_write, +}; + +pub mod stdin; diff --git a/kernel/src/std/io/stdin.rs b/kernel/src/std/io/stdin.rs new file mode 100644 index 0000000..d9934a8 --- /dev/null +++ b/kernel/src/std/io/stdin.rs @@ -0,0 +1,75 @@ +use crate::arch::x86_64::drivers::{ + ascii::WRITER, + keyboard::{KeyStroke, get_keystroke_async, get_keystroke_optional}, +}; +use alloc::string::String; + +/// Reads a line of input from standard input asynchronously, returning a +/// `String` containing the input line. Does not include the newline +/// character at the end of the line. +/// +/// If the user presses the abort key (usually Ctrl+C), the returned string +/// will be empty. +pub async fn read_line() -> String { + let mut writer = WRITER.lock(); + + let mut buff = String::new(); + loop { + match get_keystroke_async().await { + KeyStroke::Char(c) => match c { + '\n' => { + writer.write_glyph(c as u8); + return buff; + } + '\r' => { + writer.write_glyph(c as u8); + return buff; + } + '\x08' => { + if !buff.is_empty() { + buff.pop(); + writer.backspace(); + } + } + + c => { + writer.write_glyph(c as u8); + buff.push(c) + } + }, + KeyStroke::Enter => { + writer.write_glyph(b'\n'); + return buff; + } + KeyStroke::Backspace => { + if !buff.is_empty() { + buff.pop(); + writer.backspace(); + } + } + _ => continue, + } + } +} + +/// Reads a character from standard input and blocks the current task until +/// a character is available. +/// +/// # Note +/// +/// This function is not yet implemented. +pub async fn async_keystroke() -> KeyStroke { + get_keystroke_async().await +} + +/// Attempt to read a character from standard input without blocking the +/// current task. +/// +/// If no character is available, returns `None`. +/// +/// # Note +/// +/// This function is not yet implemented. +pub fn keystroke() -> Option { + get_keystroke_optional() +} From 2fb1741100fcdb42a8c7198f961dcc083a3eab97 Mon Sep 17 00:00:00 2001 From: Jacob Hinchliffe Date: Wed, 5 Mar 2025 20:44:09 +0000 Subject: [PATCH 4/4] Begin setting up stack unwinding/tracing. This actually worked so I am chuffed, but it doesn't read the necessary DWARF structures just yet. Still a good step forwards. --- Cargo.lock | 14 +++ kernel/Cargo.toml | 9 +- kernel/linker.ld | 6 - kernel/src/arch/x86_64/memory/mapping.rs | 2 +- kernel/src/lib.rs | 15 ++- kernel/src/std/elf/mod.rs | 114 +++++++++++++++++++ kernel/src/std/mod.rs | 2 + kernel/src/std/unwind/eh_info.rs | 135 +++++++++++++++++++++++ kernel/src/std/unwind/mod.rs | 1 + 9 files changed, 283 insertions(+), 15 deletions(-) create mode 100644 kernel/src/std/elf/mod.rs create mode 100644 kernel/src/std/unwind/eh_info.rs create mode 100644 kernel/src/std/unwind/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 16db54d..caceaa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,6 +99,12 @@ dependencies = [ "syn", ] +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "fnv" version = "1.0.7" @@ -111,7 +117,9 @@ version = "0.1.0" dependencies = [ "cc", "crossbeam", + "elf", "futures-util", + "gimli", "libm", "limine", "linked_list_allocator", @@ -145,6 +153,12 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "ident_case" version = "1.0.1" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index b82aae5..1f8948d 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -19,13 +19,8 @@ futures-util = { version = "0.3.31", default-features = false, features = [ "alloc", ] } linked_list_allocator = { version = "0.10.5", features = ["use_spin"] } -# unwinding = { version = "0.2.5", default-features = false, features = [ -# "unwinder", -# "fde-static", -# "personality", -# "panic", -# "hide-trace", -# ] } +gimli = { version = "0.31.1", default-features = false, features = ["read"] } +elf = { version = "0.7.4", default-features = false, features = ["nightly"] } [build-dependencies] cc = "1.2.14" diff --git a/kernel/linker.ld b/kernel/linker.ld index a193ba6..bd24c18 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -46,12 +46,6 @@ SECTIONS *(.rodata .rodata.*) } :rodata - /* Adds support for stack unwinding using the unwinding crate. */ - . = ALIGN(8); - PROVIDE(__eh_frame = .); - .eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } - - /* Move to the next memory page for .data */ . = ALIGN(CONSTANT(MAXPAGESIZE)); diff --git a/kernel/src/arch/x86_64/memory/mapping.rs b/kernel/src/arch/x86_64/memory/mapping.rs index 305d800..a64ceaf 100644 --- a/kernel/src/arch/x86_64/memory/mapping.rs +++ b/kernel/src/arch/x86_64/memory/mapping.rs @@ -16,7 +16,7 @@ static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new(); #[used] #[unsafe(link_section = ".requests")] -static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = +pub static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new(); /// ```rs diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 59d616f..7d0748b 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -19,6 +19,8 @@ use arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator; use arch::x86_64::memory::mapping; use core::arch::asm; use limine::BaseRevision; +use std::unwind; +use std::unwind::eh_info::ELF; use x86_64::VirtAddr; pub mod arch; @@ -64,7 +66,7 @@ pub fn hcf() -> ! { pub fn boot() -> Result<(), &'static str> { if !BASE_REVISION.is_supported() { - return Err("base revision not supported"); + return Err("Base revision not supported"); } use arch::x86_64::{gdt, interrupts}; @@ -118,5 +120,16 @@ pub fn boot() -> Result<(), &'static str> { x86_64::instructions::interrupts::enable(); println_log!("[Success]"); + print_log!(" Setting up stack unwinder, panic handler... "); + // Setup stack traces and proper panic handler. TODO: Handle panics + // differently if not initialised. + let eh_frame_ptr = ELF + .get_section_addr(".eh_frame_hdr") + .expect("Could not get `.eh_frame_hdr` address."); + + let _eh_info = + unsafe { unwind::eh_info::EhInfo::from_hdr_ptr(eh_frame_ptr) }; + println_log!("[Success]"); + Ok(()) } diff --git a/kernel/src/std/elf/mod.rs b/kernel/src/std/elf/mod.rs new file mode 100644 index 0000000..021f0c7 --- /dev/null +++ b/kernel/src/std/elf/mod.rs @@ -0,0 +1,114 @@ +//! Basic ELF parsing functionality using the `elf` crate. +//! +//! This may be extended in the future to support loading programs, however we +//! currently use this for getting the sizes of sections in our kernel ELF at +//! runtime. +//! +//! This is used for implementing stacktraces in std::unwind. +//! +//! # TODO +//! +//! * Add support for loading binary programs (this should probably be written +//! in a different module) + +use alloc::format; +use elf::{ + ElfBytes, ParseError, + endian::LittleEndian, + parse::{ParseAt, ParsingTable}, + section::{SectionHeader, SectionHeaderTable}, + string_table::StringTable, +}; +use limine::request::KernelFileRequest; + +use crate::prelude::*; + +#[cfg(target_arch = "x86_64")] +/// The length of the ELF header in bytes. +pub const ELF_HEADER_LEN: usize = 64; + +/// Information about our own ELF file to make ELF parsing easier, such as the +/// length of the file and a pointer to the contents. +#[used] +pub static KERNEL_FILE_REQUEST: KernelFileRequest = KernelFileRequest::new(); + +/// A list of errors that may occur when parsing ELF files. +#[derive(Debug)] +pub enum ElfError { + /// Returned if a section did not exist in [ElfReader::get_section_size]. + SectionNotExists, + /// Parse errors returned by the `elf` crate. + OtherParseError(elf::ParseError), +} + +impl From for ElfError { + fn from(err: elf::ParseError) -> Self { + Self::OtherParseError(err) + } +} + +pub struct ElfReader { + /// Structure returned by the `elf` crate having parsed the ELF header. + elf: ElfBytes<'static, LittleEndian>, +} + +impl ElfReader { + /// Parses the ELF file for the kernel, this uses data from Limine's Kernel + /// File Request to get a slice over the whole executable file. + /// + /// # Safety + /// + /// Assumes a properly formed ELF file, and that Limine returns a correct + /// pointer to the start of the file as well as a valid length in bytes. + /// + /// Both of these should be satisfied, but this function is marked unsafe + /// just in case, because we are derefererencing arbitrary pointers. + pub unsafe fn new() -> Result { + let response = KERNEL_FILE_REQUEST + .get_response() + .expect("Didn't get the kernel file from Limine. That's odd."); + + // We fetch these from Limine and use them to parse our own ELF file. + let file = response.file(); + let file_start_ptr = file.addr(); + let file_size = file.size() as usize; + + // Safety: This slice should contain the whole bytes of the ELF file. + let elf_hdr_slice = + unsafe { core::slice::from_raw_parts(file_start_ptr, file_size) }; + + let elf: ElfBytes<'static, LittleEndian> = + elf::ElfBytes::minimal_parse(elf_hdr_slice)?; + + Ok(Self { elf }) + } + + /// Gets the section size of `section_name` in bytes. + pub fn get_section_size( + &self, + section_name: &'static str, + ) -> Result { + Ok(self.get_section_header(section_name)?.sh_size) + } + + /// Gets the start address of the section `section_name` in memory. + pub fn get_section_addr( + &self, + section_name: &'static str, + ) -> Result<*const u8, ElfError> { + Ok(self.get_section_header(section_name)?.sh_addr as *const u8) + } + + /// Gets the section header of `section_name`. + pub fn get_section_header( + &self, + section_name: &'static str, + ) -> Result { + let section_hdr = self + .elf + .section_header_by_name(section_name) + .map_err(|_e| ElfError::SectionNotExists)?; + + section_hdr.ok_or(ElfError::SectionNotExists) + } +} diff --git a/kernel/src/std/mod.rs b/kernel/src/std/mod.rs index f174d93..8466fda 100644 --- a/kernel/src/std/mod.rs +++ b/kernel/src/std/mod.rs @@ -1,4 +1,6 @@ pub mod application; pub mod ascii; +pub mod elf; pub mod io; pub mod maths; +pub mod unwind; diff --git a/kernel/src/std/unwind/eh_info.rs b/kernel/src/std/unwind/eh_info.rs new file mode 100644 index 0000000..8048d12 --- /dev/null +++ b/kernel/src/std/unwind/eh_info.rs @@ -0,0 +1,135 @@ +//! Contains a [EhInfo] struct that contains the parsed DWARF exception header +//! data from the ELF .eh_frame and .eh_frame_hdr sections. + +use alloc::{boxed::Box, slice}; +use gimli::{ + BaseAddresses, EhFrame, EhFrameHdr, EhHdrTable, EndianSlice, LittleEndian, + ParsedEhFrameHdr, +}; +use spin::Lazy; + +use crate::{println_log, std::elf::ElfReader}; + +/// Contains useful data parsed from the ELF file in question. In the kernel +/// this will be our own process. +/// +/// We use this to implement stack traces and potential unwinding. +/// +/// # Sources +/// +/// This code is reproduced from [lesenchal.fr](https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way#h5.1-parsing-eh_frame-and-eh_frame_hdr-with-gimli) +/// and will later be extended as required. +pub struct EhInfo { + /// A set of base addresses used for relative addressing. + base_addrs: BaseAddresses, + /// The parsed `.eh_frame_hdr` section. + hdr: &'static ParsedEhFrameHdr>, + /// The lookup table in the parsed `.eh_frame_hdr` section. + /// This is a binary search table, it is optional but should be present as + /// we are linking with LLD(?) \[needs citation]. + hdr_table: EhHdrTable<'static, EndianSlice<'static, LittleEndian>>, + /// The parsed `.eh_frame` containing the CFIs (call frame information). + eh_frame: EhFrame>, +} + +/// Stores the [ElfReader] struct for this ELF file. +pub static ELF: Lazy = + Lazy::new(|| unsafe { ElfReader::new().unwrap() }); + +impl EhInfo { + /// Gets the `.eh_frame_hdr` size in bytes. + /// + /// # Panics + /// + /// If we can't get the size of `.eh_frame_hdr`. + pub fn eh_frame_hdr_size() -> usize { + ELF.get_section_size(".eh_frame_hdr") + .expect("Cannot get size of `.eh_frame_hdr`.") as usize + } + + /// Gets the `.eh_frame` size in bytes. + /// + /// # Panics + /// + /// If we can't get the size of `.eh_frame`. + pub fn eh_frame_size() -> usize { + ELF.get_section_size(".eh_frame") + .expect("Cannot get size of `.eh_frame`.") as usize + } + + /// Constructs a [EhInfo] from the base address. This is defined for a + /// symbol in the linker script so we can initialise stack traces and -- in + /// the future -- unwinding. + /// + /// # Safety + /// + /// Assumes the `.eh_frame_hdr` pointer to be valid, as well as the sizes of + /// the containing sections. These sizes are computed using [ElfReader]. + /// + /// # Panics + /// + /// This function panics if Gimli throws parsing errors -- for example due + /// to a malformed or corrupted binary, or because this is called in + /// release-mode, or on a stripped binary. + /// + /// # TODOs + /// + /// * Support external System.map files which list symbols and contain + /// debugging information. + pub unsafe fn from_hdr_ptr(eh_frame_hdr: *const u8) -> Self { + let mut base_addrs = BaseAddresses::default(); + // We add the `.eh_frame_hdr` pointer to the set of base addresses which + // are used by Gimli for later parsing. This may be used to compute a + // pointer to `.eh_frame`. + base_addrs = base_addrs.set_eh_frame_hdr(eh_frame_hdr as u64); + + // Leaking the Box gives us a reference with `'static` lifetime to use + // in Self. + let hdr = Box::leak(Box::new( + // We need to construct a slice as input for `EhFrameHdr::new`. + // This is sound if data pointer and length are known to be + // correct. + EhFrameHdr::new( + unsafe { + core::slice::from_raw_parts( + eh_frame_hdr, + Self::eh_frame_hdr_size(), + ) + }, + LittleEndian, + ) // Parse the header using the base address we provided (virtual + // memory). Address size is how many bytes make an + // address (64 bits). + .parse(&base_addrs, 8) + .expect( + "Could not parse `.eh_frame_hdr`. The ELF must be malformed.", + ), + )); + + // Create a pointer to the `.eh_frame` ready to parse it. + let eh_frame = match hdr.eh_frame_ptr() { + gimli::Pointer::Direct(addr) => addr as *mut u8, + _ => unimplemented!(), + }; + + // Add the `.eh_frame` address for addresses relative to this section. + base_addrs = base_addrs.set_eh_frame(eh_frame as u64); + + // Finally parse the `.eh_frame` section of our ELF. + let eh_frame = EhFrame::new( + unsafe { + core::slice::from_raw_parts(eh_frame, Self::eh_frame_size()) + }, + LittleEndian, + ); + + Self { + base_addrs, + hdr, + hdr_table: hdr.table().expect( + "The CFI binary search table was not present in this binary, oh dear.", + ), + eh_frame, + } + } +} diff --git a/kernel/src/std/unwind/mod.rs b/kernel/src/std/unwind/mod.rs new file mode 100644 index 0000000..99eb251 --- /dev/null +++ b/kernel/src/std/unwind/mod.rs @@ -0,0 +1 @@ +pub mod eh_info;