13 Commits

Author SHA1 Message Date
zxq5 b80b400c19 fixed interrupts (temporary, we need to do a real fix for this) 2025-03-10 15:40:38 +00:00
nullndvoid 31f14e8aec Get rid of warning 2025-03-10 13:35:27 +00:00
nullndvoid 91e5602e82 Don't enable APIC yet 2025-03-10 13:29:07 +00:00
nullndvoid a83108a08e Clean up dead code 2025-03-08 18:43:43 +00:00
nullndvoid da6690fd8b Save failed stack trace code for future reference. 2025-03-08 18:40:05 +00:00
nullndvoid 4b8388c66d Update boot prints to be a little less noisy.
TODO:

* Debugging, tracing support.
* Logging infra so we can may pass a loglevel like on Linux.
2025-03-06 20:32:46 +00:00
nullndvoid 1d192adde0 Removed messy debug logging, added print_oneshot!() 2025-03-06 20:11:54 +00:00
zxq5 4bf31e653b Merge remote-tracking branch 'refs/remotes/origin/unified' into unified 2025-03-06 01:22:53 +00:00
nullndvoid 5aad3e6096 Fixed stack unwind bugs (works!) 2025-03-06 01:22:17 +00:00
zxq5 c06425949a Merge remote-tracking branch 'refs/remotes/origin/unified' into unified 2025-03-06 01:21:02 +00:00
nullndvoid f197149d80 Forced unwind tables - still bugged 2025-03-06 01:01:04 +00:00
nullndvoid bc51f3ec43 Force rustc to generate unwind-tables - infinite loop :( 2025-03-05 22:37:19 +00:00
nullndvoid b26dc6de01 Wrote stack unwinder. NEEDTO: fix NoUnwindInfo
Probably incorrect PC was set.
2025-03-05 22:21:38 +00:00
22 changed files with 451 additions and 404 deletions
-3
View File
@@ -17,8 +17,5 @@ runner = "scripts/run_debug.sh"
[target.'cfg(all(target_arch = "x86_64", target_os = "none", not(debug_assertions)))']
runner = "scripts/run_release.sh"
# [registry]
# default = "gitea"
[registries.gitea]
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
Generated
-14
View File
@@ -99,12 +99,6 @@ 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"
@@ -117,9 +111,7 @@ version = "0.1.0"
dependencies = [
"cc",
"crossbeam",
"elf",
"futures-util",
"gimli",
"libm",
"limine",
"linked_list_allocator",
@@ -153,12 +145,6 @@ 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"
+5 -2
View File
@@ -19,8 +19,11 @@ futures-util = { version = "0.3.31", default-features = false, features = [
"alloc",
] }
linked_list_allocator = { version = "0.10.5", features = ["use_spin"] }
gimli = { version = "0.31.1", default-features = false, features = ["read"] }
elf = { version = "0.7.4", default-features = false, features = ["nightly"] }
# gimli = { version = "0.31.1", default-features = false, features = ["read"] }
# elf = { version = "0.7.4", default-features = false, features = ["nightly"] }
# fallible-iterator = "0.3.0"
# framehop = { version = "0.13.2", default-features = false }
# object = { version = "0.36.7", default-features = false, features = ["read"] }
[build-dependencies]
cc = "1.2.14"
+12 -14
View File
@@ -2,15 +2,18 @@
use core::arch::x86_64::__cpuid;
use crate::arch::x86_64::memory::mapping::PHYSICAL_MEMORY_OFFSET;
use crate::{debugln, serial_print, serial_println};
use crate::arch::x86_64::{
cpu::msr::*,
memory::{
FRAME_ALLOCATOR, OFFSET_PAGE_TABLE, mapping::PHYSICAL_MEMORY_OFFSET,
},
};
use x86_64::structures::paging::Translate;
use x86_64::{
PhysAddr, VirtAddr,
structures::paging::{Mapper, Page, PageTableFlags, PhysFrame, Size4KiB},
};
use x86_64::structures::paging::Translate;
use crate::arch::x86_64::cpu::msr::*;
use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE};
const IA32_APIC_BASE_MSR: u32 = 0x1b;
const IA32_APIC_BASE_MSR_BSP: u64 = 0x100;
@@ -28,7 +31,6 @@ const CPUID_FEAT_EDX_APIC: u64 = 1 << 9; // the cpuid instruction will return th
fn set_apic_base_enable(apic: PhysAddr) {
let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE;
debugln!("apic {:?}", PhysAddr::new(rax));
cpu_set_msr(IA32_APIC_BASE_MSR, rax);
}
@@ -42,8 +44,6 @@ fn get_apic_base() -> PhysAddr {
let mut value: u64 = 0;
cpu_get_msr(IA32_APIC_BASE_MSR, &mut value);
debugln!("apic base {:#x}", value);
PhysAddr::new(value & 0xfffff0000)
}
@@ -53,8 +53,8 @@ fn write_apic_register(apic_base: &PhysAddr, reg: u64, value: u32) {
let virt_addr = unsafe { phys_to_virt(PhysAddr::new(reg_addr)) };
let phys_check = OFFSET_PAGE_TABLE.get().unwrap().lock().translate(virt_addr);
debugln!("{:?}", phys_check);
let phys_check =
OFFSET_PAGE_TABLE.get().unwrap().lock().translate(virt_addr);
unsafe { *(virt_addr.as_u64() as *mut u32) = value };
}
@@ -75,22 +75,20 @@ pub fn check_apic() -> bool {
unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr {
let phys = phys.as_u64();
phys.checked_add(*PHYSICAL_MEMORY_OFFSET)
.map_or_else(|| panic!(" overflow"), VirtAddr::new)
.map_or_else(|| panic!("Overflow"), VirtAddr::new)
}
pub fn enable_apic() {
let apic_base_physical_addr = get_apic_base();
set_apic_base_enable(apic_base_physical_addr);
debugln!("{:?}", apic_base_physical_addr);
enable_timer();
write_apic_register(
&apic_base_physical_addr,
0xF0,
read_apic_register(&apic_base_physical_addr, 0xF0) | 0x1FF
read_apic_register(&apic_base_physical_addr, 0xF0) | 0x1FF,
);
}
pub fn enable_timer() {
+48 -4
View File
@@ -1,4 +1,4 @@
use core::fmt;
use core::fmt::{self, Write};
use spin::{Lazy, Mutex};
use x86_64::instructions::interrupts;
@@ -7,6 +7,7 @@ use crate::arch::x86_64::drivers::framebuffer::{
};
use crate::resources::font::{FONT_SPLEEN_8X16, Font};
use crate::std::maths::geometry::Vec2;
static FONT_WIDTH: u32 = 8;
static FONT_HEIGHT: u32 = 16;
@@ -25,11 +26,8 @@ pub struct Writer {
screen_width: u32,
/// Measured in chars not pixels.
screen_height: u32,
/// 16 pixels tall.
text_line: u32,
/// 8 pixels wide.
text_col: u32,
fg_color: Colour,
bg_color: Colour,
}
@@ -62,6 +60,15 @@ impl Writer {
self.font = font;
}
pub const fn set_pos(&mut self, coords: Vec2<u32>) {
self.text_col = coords.x();
self.text_line = coords.y();
}
pub const fn pos(&self) -> Vec2<u32> {
Vec2::new(self.text_col, self.text_line)
}
/// This is sent when the user types a backspace.
const BACKSPACE: u8 = 8;
@@ -206,6 +213,34 @@ pub fn clear_screen() {
});
}
/// Prints a string at a given `coords` before returning to original position.
///
/// These are 0 based indices and refer to character position, not pixel
/// positions.
pub fn _print_oneshot(
fg: Colour,
bg: Colour,
coords: Vec2<u32>,
args: fmt::Arguments,
) {
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
// Save the old positions of `text_col` and `text_line`.
let old_pos = writer.pos();
// Also save the colors.
let old_fg_color = writer.fg_color;
let old_bg_color = writer.bg_color;
writer.set_pos(coords);
writer.set_colour(fg, bg);
writer.write_fmt(args).unwrap();
writer.set_colour(old_fg_color, old_bg_color);
writer.set_pos(old_pos);
});
}
pub fn reset_cursor() {
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
@@ -214,6 +249,15 @@ pub fn reset_cursor() {
});
}
#[macro_export]
/// Prints a coloured string at a position before restoring previous position
/// and colour.
macro_rules! print_oneshot {
($position:expr, $fg:expr, $bg:expr, $($arg:tt)*) => {
$crate::arch::x86_64::drivers::ascii::_print_oneshot($fg, $bg, $position, format_args!($($arg)*));
};
}
#[macro_export]
macro_rules! println_log {
() => ($crate::print_log!("\n"));
+27 -17
View File
@@ -1,9 +1,13 @@
use crate::{debug, 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<InterruptDescriptorTable> = 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<Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>>> = Lazy::new(|| {
Mutex::new(Keyboard::new(
ScancodeSet1::new(),
layouts::Uk105Key,
HandleControl::Ignore,
))
});
static KEYBOARD: Lazy<Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>>> =
Lazy::new(|| {
Mutex::new(Keyboard::new(
ScancodeSet1::new(),
layouts::Uk105Key,
HandleControl::Ignore,
))
});
let _keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
@@ -116,8 +124,10 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac
}
}
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
debug!("Timer Interrupt");
extern "x86-interrupt" fn timer_interrupt_handler(
_stack_frame: InterruptStackFrame,
) {
// debug!("Timer Interrupt");
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
@@ -128,10 +138,10 @@ extern "x86-interrupt" fn page_fault_handler(
_stack_frame: InterruptStackFrame,
_error_code: PageFaultErrorCode,
) {
serial_println!("Exception: Page Fault");
serial_println!("Accessed Address: {:?}", Cr2::read());
serial_println!("Error Code: {:?}", _error_code);
serial_println!("{:#?}", _stack_frame);
// serial_println!("Exception: Page Fault");
// serial_println!("Accessed Address: {:?}", Cr2::read());
// serial_println!("Error Code: {:?}", _error_code);
// serial_println!("{:#?}", _stack_frame);
if let Some(frame_allocator) = FRAME_ALLOCATOR.get() {
let mut f = frame_allocator.lock();
@@ -1,19 +1,24 @@
use crate::{debugln, serial_print};
use crate::arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator;
use crate::arch::x86_64::memory::units::MemoryUnits;
use crate::arch::x86_64::memory::{
FRAME_ALLOCATOR, HEAP_SIZE, HEAP_VIRTUAL_SPACE,
};
use crate::debugln;
use core::alloc::{GlobalAlloc, Layout};
use core::ptr;
use spin::{Mutex, MutexGuard};
use x86_64::structures::paging::{Size4KiB, mapper::MapToError, PageTableFlags};
use x86_64::VirtAddr;
use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, HEAP_SIZE, HEAP_VIRTUAL_SPACE};
use crate::arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator;
use crate::arch::x86_64::memory::units::MemoryUnits;
use crate::serial_println;
use x86_64::structures::paging::{
PageTableFlags, Size4KiB, mapper::MapToError,
};
/// 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<FoundryAllocator> = Locked::new(FoundryAllocator::new());
/// This is now Rust's global allocator, so we can use stuff requiring heap
/// allocations.
static ALLOCATOR: Locked<FoundryAllocator> =
Locked::new(FoundryAllocator::new());
/// Initializes the heap.
///
@@ -38,7 +43,8 @@ static ALLOCATOR: Locked<FoundryAllocator> = Locked::new(FoundryAllocator::new()
/// initialized successfully.
pub unsafe fn init_heap() -> Result<(), MapToError<Size4KiB>> {
unsafe {
// code to allocate frames is now done in the page fault interrupt handler!
// code to allocate frames is now done in the page fault interrupt
// handler!
ALLOCATOR.lock().init(HEAP_VIRTUAL_SPACE, HEAP_SIZE);
Ok(())
}
@@ -85,11 +91,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) {
debugln!(" => Start: {:?}", VirtAddr::new(heap_start as u64));
debugln!(" => Size: {}", MemoryUnits::from_bytes(heap_size));
@@ -127,7 +135,9 @@ unsafe impl GlobalAlloc for Locked<FoundryAllocator> {
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) }
}
}
@@ -195,7 +205,8 @@ impl FoundryFallbackAllocator {
///
/// # 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.add_region(heap_start, heap_size) };
}
@@ -222,7 +233,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));
@@ -275,7 +288,8 @@ unsafe impl GlobalAlloc for Locked<FoundryFallbackAllocator> {
// 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 {
@@ -299,8 +313,13 @@ unsafe impl GlobalAlloc for Locked<FoundryFallbackAllocator> {
}
fn ensure_mapped(virt_addr: VirtAddr) {
if ! FoundryOSFrameAllocator::is_mapped(virt_addr) {
if !FoundryOSFrameAllocator::is_mapped(virt_addr) {
let mut foundry_alloc = FRAME_ALLOCATOR.get().unwrap().lock();
foundry_alloc.allocate_page(virt_addr, PageTableFlags::PRESENT | PageTableFlags::WRITABLE).unwrap();
foundry_alloc
.allocate_page(
virt_addr,
PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
)
.unwrap();
}
}
@@ -1,10 +1,13 @@
use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, Translate};
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, TranslateResult};
use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE};
use x86_64::structures::paging::{
FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB,
Translate,
};
use x86_64::{PhysAddr, VirtAddr};
pub struct FoundryOSFrameAllocator {
memory_map: &'static MemoryMapResponse,
@@ -36,7 +39,9 @@ impl FoundryOSFrameAllocator {
.entries()
.iter()
.map(|region| region.base..region.base + region.length)
.flat_map(|r| r.step_by(4096)).count() as u64 * 4096
.flat_map(|r| r.step_by(4096))
.count() as u64
* 4096
}
/// An iterator over all usable frames in the memory map.
@@ -46,10 +51,14 @@ impl FoundryOSFrameAllocator {
/// This function is used to allocate frames for the page map.
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> + 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()
})
}
pub(crate) fn is_mapped(virt_addr: VirtAddr) -> bool {
@@ -57,14 +66,18 @@ impl FoundryOSFrameAllocator {
matches!(mapper.translate(virt_addr), TranslateResult::Mapped { .. })
}
pub(crate) fn allocate_page(&mut self, start_addr: VirtAddr, flags: PageTableFlags) -> Result<(), MapToError<Size4KiB>> {
pub(crate) fn allocate_page(
&mut self,
start_addr: VirtAddr,
flags: PageTableFlags,
) -> Result<(), MapToError<Size4KiB>> {
let page = Page::containing_address(start_addr);
let frame = self.allocate_frame().ok_or(MapToError::<Size4KiB>::FrameAllocationFailed)?;
let frame = self
.allocate_frame()
.ok_or(MapToError::<Size4KiB>::FrameAllocationFailed)?;
let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock();
unsafe {
mapper.map_to(page, frame, flags, self)?
}.flush();
unsafe { mapper.map_to(page, frame, flags, self)? }.flush();
Ok(())
}
+5 -2
View File
@@ -11,12 +11,15 @@ use x86_64::{
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
/// Start address of the memory space where we store allocated stacks.
pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000;
/// Start address of heap allocated memory.
pub const HEAP_VIRTUAL_SPACE: usize = 0x4444_4444_0000;
pub const HEAP_SIZE: usize = MiB(1).to_bytes();
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
/// Returns a mutable reference to the current level 4 page table.
///
/// # Safety
+83 -71
View File
@@ -1,5 +1,5 @@
#![no_std]
#![feature(abi_x86_interrupt)]
#![feature(abi_x86_interrupt, breakpoint)]
#![warn(
clippy::correctness,
clippy::nursery,
@@ -12,38 +12,42 @@
)]
extern crate alloc;
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::mapping;
use crate::{
arch::x86_64::{
// cpu::apic::enable_apic,
drivers::{
ascii::screensize_chars, framebuffer::display::screensize_px,
},
gdt,
interrupts,
memory::{
FRAME_ALLOCATOR,
allocation::{
heap_alloc::init_heap, page_alloc::FoundryOSFrameAllocator,
},
init_page_table, mapping,
units::MemoryUnits,
},
},
prelude::*,
};
use alloc::{boxed::Box, format};
use core::arch::asm;
use limine::BaseRevision;
use std::unwind;
use std::unwind::eh_info::ELF;
use x86_64::VirtAddr;
use arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator;
use crate::arch::x86_64::cpu::apic::enable_apic;
use crate::arch::x86_64::drivers::ascii::screensize_chars;
use crate::arch::x86_64::drivers::framebuffer::display::screensize_px;
use crate::arch::x86_64::memory::FRAME_ALLOCATOR;
use crate::arch::x86_64::memory::units::MemoryUnits;
pub mod arch;
mod panic;
/// Commonly used re-exports.
pub mod prelude;
pub mod resources;
#[allow(unused)] // We aren't using much of this right now.
// We aren't using much of this right now.
#[allow(unused)]
pub mod std;
mod step;
pub mod util;
pub mod prelude {
pub use crate::{
eprint, eprintln, print, print_log, println, println_log, serial_print,
serial_println,
debug, debugln,
std::io::{_print, _print_log, _serial_write, _print_err},
std::debug::_debug,
};
}
/// 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
@@ -54,13 +58,6 @@ pub mod prelude {
#[unsafe(link_section = ".requests")]
static BASE_REVISION: BaseRevision = BaseRevision::new();
#[panic_handler]
fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
println!("Kernel panic: {}", _info);
serial_println!("Kernel panic: {}", _info);
hcf();
}
pub fn hcf() -> ! {
loop {
unsafe {
@@ -70,12 +67,27 @@ pub fn hcf() -> ! {
}
}
pub fn boot() -> Result<(), &'static str> {
if !BASE_REVISION.is_supported() {
return Err("Base revision not supported");
}
#[derive(Debug)]
pub struct NoError {}
use arch::x86_64::{gdt, interrupts};
impl core::fmt::Display for NoError {
fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Ok(())
}
}
pub struct NoTags;
impl core::error::Error for NoError {}
/// Panicking before this is initialised is unwise. We should probably extract
/// very early init into it's own function because Stack Traces may require
/// allocations etc.
#[inline(never)]
pub fn boot() -> Result<(), Box<dyn core::error::Error>> {
if !BASE_REVISION.is_supported() {
return Err("Base revision not supported.".into());
}
let memory_map = mapping::get_memory_map();
@@ -88,12 +100,11 @@ pub fn boot() -> Result<(), &'static str> {
debugln!("[Success]");
}
debugln!(" Display...");
let dimensions = screensize_chars();
let dimensions2 = screensize_px();
debugln!(" => (px) : {}x{} ", dimensions2.0, dimensions2.1);
debugln!(" => (chars) : {}x{} ", dimensions.0, dimensions.1);
debugln!(" [Success]");
debugln!(
"Display is {:?} chars, {:?} px.",
screensize_chars(),
screensize_px()
);
debug!(" Setting Up Global Descriptor Table... ");
gdt::init();
@@ -104,46 +115,47 @@ pub fn boot() -> Result<(), &'static str> {
debugln!("[Success]");
debugln!(" 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);
FoundryOSFrameAllocator::init(memory_map);
let available_bytes = FRAME_ALLOCATOR.get().unwrap().lock().available_memory();
debugln!(" => Available Memory: {}", MemoryUnits::from_bytes(available_bytes as usize));
let available_bytes =
FRAME_ALLOCATOR.get().unwrap().lock().available_memory();
debugln!(
" => Available Memory: {}",
MemoryUnits::from_bytes(available_bytes as usize)
);
// Allocations should be all fine past this point.
debugln!(" Initialising Heap... ");
match unsafe { init_heap() } {
Ok(_) => debugln!(" [Success]"),
Err(why) => return Err(format!("{:?}", why).into()),
}
debug!(" Enabling PICs... ");
interrupts::enable_pic();
debugln!("[Success]");
debugln!(" Initialising Heap... ");
if unsafe { init_heap() }.is_err() {
return Err("Failed to initialise heap: error");
}
debugln!(" [Success]");
// debug!(" Enabling PICs... ");
// interrupts::enable_pic();
// debug!(" Disabling PICs... ");
// interrupts::disable_pic();
// debugln!("[Success]");
debug!(" Disabling PICs... ");
interrupts::disable_pic();
debugln!("[Success]");
debug!(" Initialising APIC... ");
enable_apic();
debugln!("[Success]");
// debug!(" Initialising APIC... ");
// enable_apic();
// debugln!("[Success]");
debug!(" Enabling Interrupts... ");
x86_64::instructions::interrupts::enable();
debugln!("[Success]");
debug!(" 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) };
debugln!("[Success]");
Ok(())
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo<'_>) -> ! {
debugln!("{:?}", _info);
hcf()
}
+8 -6
View File
@@ -2,11 +2,12 @@
#![no_main]
extern crate alloc;
use foundry_os::arch::x86_64::drivers::ascii::screensize_chars;
use foundry_os::arch::x86_64::drivers::framebuffer::display::screensize_px;
use foundry_os::arch::x86_64::processing::async_io::task::{Executor, Task};
use foundry_os::util::shell::shell;
use foundry_os::{println, println_log};
use foundry_os::{
arch::x86_64::processing::async_io::task::{Executor, Task},
prelude::*,
util::shell::shell,
};
#[unsafe(no_mangle)]
extern "C" fn kmain() -> ! {
@@ -14,7 +15,6 @@ extern "C" fn kmain() -> ! {
if let Err(err) = foundry_os::boot() {
panic!("{}", err);
}
println_log!(" [ Kernel Initialised Successfully ] ");
// println!("TESTING :: Allocation");
@@ -23,6 +23,8 @@ extern "C" fn kmain() -> ! {
// println!("{}", somevec.len());
// println!("PASSED!");
// test1();
let mut executor = Executor::new();
executor.spawn(Task::new(shell()));
executor.run()
-75
View File
@@ -1,75 +0,0 @@
// //! 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<usize> = 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} - <unknown>",
// 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<dyn Any + Send>) -> ! {
// 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<M: 'static + Any + Send>(msg: M) -> ! {
// eprintln!("Panicked at {}", Location::caller());
// do_panic(Box::new(msg))
// }
+7
View File
@@ -0,0 +1,7 @@
pub use crate::{
arch::x86_64::drivers::framebuffer::colour::Colour,
debug, debugln, eprint, eprintln, hcf, print, print_log, print_oneshot,
println, println_log, serial_print, serial_println,
std::debug::_debug,
std::io::{_print, _print_err, _print_log, _serial_write},
};
+1 -2
View File
@@ -1,6 +1,6 @@
use crate::prelude::{_print_log, _serial_write};
use core::fmt;
use x86_64::instructions::interrupts;
use crate::prelude::{_print_log, _serial_write};
#[macro_export]
macro_rules! debugln {
@@ -19,4 +19,3 @@ pub fn _debug(args: fmt::Arguments) {
_serial_write(args);
});
}
+71 -17
View File
@@ -11,13 +11,14 @@
//! * Add support for loading binary programs (this should probably be written
//! in a different module)
use alloc::format;
use alloc::{format, vec::Vec};
use elf::{
ElfBytes, ParseError,
endian::LittleEndian,
parse::{ParseAt, ParsingTable},
section::{SectionHeader, SectionHeaderTable},
string_table::StringTable,
symbol::{Symbol, SymbolTable},
};
use limine::request::KernelFileRequest;
@@ -37,6 +38,8 @@ pub static KERNEL_FILE_REQUEST: KernelFileRequest = KernelFileRequest::new();
pub enum ElfError {
/// Returned if a section did not exist in [ElfReader::get_section_size].
SectionNotExists,
/// Returned if we failed to fetch the symbol table.
Symtab,
/// Parse errors returned by the `elf` crate.
OtherParseError(elf::ParseError),
}
@@ -48,8 +51,14 @@ impl From<elf::ParseError> for ElfError {
}
pub struct ElfReader {
/// The underlying bytes for the ELF file.
pub bytes: &'static [u8],
/// Structure returned by the `elf` crate having parsed the ELF header.
elf: ElfBytes<'static, LittleEndian>,
pub elf: ElfBytes<'static, LittleEndian>,
/// A sorted list of symbols to binary search.
pub sorted_syms: Vec<Symbol>,
/// The string table for looking up symbol names.
pub symbol_strtab: StringTable<'static>,
}
impl ElfReader {
@@ -64,23 +73,35 @@ impl ElfReader {
/// 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<Self, ElfError> {
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) };
// Store this for use with other libraries.
let bytes = get_elf_slice();
let elf: ElfBytes<'static, LittleEndian> =
elf::ElfBytes::minimal_parse(elf_hdr_slice)?;
elf::ElfBytes::minimal_parse(bytes)?;
Ok(Self { elf })
let Some((symtab, strtab)) = elf.symbol_table()? else {
return Err(ElfError::Symtab);
};
// Sort the symtab for later use and store the strtab.
let mut symbols = symtab.into_iter().collect::<Vec<Symbol>>();
symbols.sort_by_key(|sym| sym.st_value);
Ok(Self {
elf,
sorted_syms: symbols,
symbol_strtab: strtab,
bytes,
})
}
pub fn get_symbol_table(
&self,
) -> Result<
Option<(SymbolTable<'static, LittleEndian>, StringTable<'static>)>,
ElfError,
> {
Ok(self.elf.symbol_table()?)
}
/// Gets the section size of `section_name` in bytes.
@@ -102,7 +123,7 @@ impl ElfReader {
/// Gets the section header of `section_name`.
pub fn get_section_header(
&self,
section_name: &'static str,
section_name: &str,
) -> Result<SectionHeader, ElfError> {
let section_hdr = self
.elf
@@ -111,4 +132,37 @@ impl ElfReader {
section_hdr.ok_or(ElfError::SectionNotExists)
}
pub fn search_symbol(&self, address: u64) -> Option<elf::symbol::Symbol> {
let entries = self.sorted_syms.iter().collect::<Vec<_>>();
let idx = entries
.as_slice()
.binary_search_by_key(&address, |sym| sym.st_value)
.ok()?;
Some(entries[idx].clone())
}
pub fn get_symbol_name(
&self,
sym: elf::symbol::Symbol,
) -> Option<&'static str> {
self.symbol_strtab.get(sym.st_name as usize).ok()
}
}
/// Gets a slice of the bytes of the kernel ELF file.
pub fn get_elf_slice() -> &'static [u8] {
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.
(unsafe { core::slice::from_raw_parts(file_start_ptr, file_size) }) as _
}
+8
View File
@@ -42,6 +42,14 @@ impl<T: Coordinate> Vec2<T> {
pub const fn y(&self) -> T {
self.y
}
pub const fn set_x(&mut self, x: T) {
self.x = x;
}
pub const fn set_y(&mut self, y: T) {
self.y = y;
}
}
impl<T: Coordinate> AddAssign for Vec2<T> {
+1 -3
View File
@@ -1,7 +1,5 @@
pub mod application;
pub mod ascii;
pub mod elf;
pub mod debug;
pub mod io;
pub mod maths;
pub mod unwind;
pub mod debug;
-135
View File
@@ -1,135 +0,0 @@
//! 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<EndianSlice<'static, LittleEndian>>,
/// 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<EndianSlice<'static, LittleEndian>>,
}
/// Stores the [ElfReader] struct for this ELF file.
pub static ELF: Lazy<ElfReader> =
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,
}
}
}
-1
View File
@@ -1 +0,0 @@
pub mod eh_info;
+104
View File
@@ -0,0 +1,104 @@
/* //! Defines the [Step] struct which may be used when initialising the system.
//!
//! # Warning
//!
//! Use of alloc is currently required, so early initialisation will need a
//! different method. We should possibly abstract some functionality so that
//! consumers don't need to worry or care about the difference.
use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
use crate::{arch::x86_64::drivers::ascii::WRITER, prelude::*};
/// Represents a [Step] when initialising the system.
///
/// Handles printing various information to the framebuffer using steps.
///
/// # Example
///
/// ```rs
/// // Setup a Step for Foo.
/// let foo_step: Step<Foo, FooError> = Step::new("foo", Foo::init()).register_tag(|foo| { foo.bar().to_string() });
/// // This returns a Result<T, E>
/// let foo = foo_step.run()?;
/// ```
///
/// # TODOs
///
/// * If T = Option<T> and None is returned, we print `"[Not found]"`, otherwise
/// `"[Success]`".
pub struct Step<T: Clone + ToOwned> {
/// The initialisation function for the Step, usually this is a closure.
init_fn: fn() -> T,
/// A list of tag generator functions to display under the Step on success.
/// They are displayed like:
/// ```
/// Initialising module... [Success]
/// => Some information here.
/// ```
///
/// # TODOs
///
/// * Support tags without alloc being initialised.
tags: Vec<Box<dyn Fn(T) -> String>>,
/// The name of the module being initialised.
task: &'static str,
}
// This impl block might be so horrible it needs its own file.
impl<T: ToOwned + Clone> Step<T> {
pub fn new(
task: &'static str,
init_fn: fn() -> T,
tags: Vec<Box<dyn Fn(T) -> String>>,
) -> Self {
Self {
init_fn,
tags,
task,
}
}
/// Runs the initialisation function and logs where required.
///
/// This function is a little messy but it just handles pretty printing the
/// [Step] to the terminal for us.
pub fn run(self) -> T {
print_log!(" {}", self.task);
let mut success_position = WRITER.lock().pos();
success_position.set_x(success_position.x() + 1);
let ret = (self.init_fn)();
println!();
for tag in &self.tags[..] {
// Calls the closure on the return value.
println_log!(" => {}", (tag)(ret.clone()));
}
// Print whether or not the step succeeded.
print_oneshot!(
success_position,
Colour::Green,
Colour::Black,
"[Success]"
);
ret
}
/// Registers a 'tag' or note to display under the line which says
/// `Initialising module... [Success]`.
///
/// # Notes
///
/// The tag is a closure taking in a reference to T and returning a
/// presumably formatted String.
#[allow(unused)]
pub fn register_tag(&mut self, tag: Box<dyn Fn(T) -> String>) -> &Self {
self.tags.push(tag);
self
}
}
*/
+1
View File
@@ -2,3 +2,4 @@ wrap_comments = true
max_width = 80
comment_width = 80
format_code_in_doc_comments = true
doc_comment_code_block_width = 80
+1 -1
View File
@@ -32,7 +32,7 @@ else
fi
# Set up test-specific flags
if [ $is_test -eq 1 ]; then
if [$is_test]; then
test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"
serial_flags="-serial stdio"
else