Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b80b400c19 | |||
| 31f14e8aec | |||
| 91e5602e82 | |||
| a83108a08e | |||
| da6690fd8b | |||
| 4b8388c66d | |||
| 1d192adde0 |
@@ -17,8 +17,5 @@ runner = "scripts/run_debug.sh"
|
|||||||
[target.'cfg(all(target_arch = "x86_64", target_os = "none", not(debug_assertions)))']
|
[target.'cfg(all(target_arch = "x86_64", target_os = "none", not(debug_assertions)))']
|
||||||
runner = "scripts/run_release.sh"
|
runner = "scripts/run_release.sh"
|
||||||
|
|
||||||
[target.x86_64-kernel]
|
|
||||||
rustflags = ["-C", "force-unwind-tables"]
|
|
||||||
|
|
||||||
[registries.gitea]
|
[registries.gitea]
|
||||||
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
|
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
|
||||||
|
|||||||
Generated
-21
@@ -99,18 +99,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "elf"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fallible-iterator"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -123,10 +111,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"elf",
|
|
||||||
"fallible-iterator",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gimli",
|
|
||||||
"libm",
|
"libm",
|
||||||
"limine",
|
"limine",
|
||||||
"linked_list_allocator",
|
"linked_list_allocator",
|
||||||
@@ -160,12 +145,6 @@ dependencies = [
|
|||||||
"pin-utils",
|
"pin-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.31.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|||||||
+5
-3
@@ -19,9 +19,11 @@ futures-util = { version = "0.3.31", default-features = false, features = [
|
|||||||
"alloc",
|
"alloc",
|
||||||
] }
|
] }
|
||||||
linked_list_allocator = { version = "0.10.5", features = ["use_spin"] }
|
linked_list_allocator = { version = "0.10.5", features = ["use_spin"] }
|
||||||
gimli = { version = "0.31.1", default-features = false, features = ["read"] }
|
# gimli = { version = "0.31.1", default-features = false, features = ["read"] }
|
||||||
elf = { version = "0.7.4", default-features = false, features = ["nightly"] }
|
# elf = { version = "0.7.4", default-features = false, features = ["nightly"] }
|
||||||
fallible-iterator = "0.3.0"
|
# 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]
|
[build-dependencies]
|
||||||
cc = "1.2.14"
|
cc = "1.2.14"
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
use core::arch::x86_64::__cpuid;
|
use core::arch::x86_64::__cpuid;
|
||||||
|
|
||||||
use crate::arch::x86_64::cpu::msr::*;
|
use crate::arch::x86_64::{
|
||||||
use crate::arch::x86_64::memory::mapping::PHYSICAL_MEMORY_OFFSET;
|
cpu::msr::*,
|
||||||
use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE};
|
memory::{
|
||||||
use crate::{debugln, serial_print, serial_println};
|
FRAME_ALLOCATOR, OFFSET_PAGE_TABLE, mapping::PHYSICAL_MEMORY_OFFSET,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use x86_64::structures::paging::Translate;
|
use x86_64::structures::paging::Translate;
|
||||||
use x86_64::{
|
use x86_64::{
|
||||||
PhysAddr, VirtAddr,
|
PhysAddr, VirtAddr,
|
||||||
@@ -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) {
|
fn set_apic_base_enable(apic: PhysAddr) {
|
||||||
let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE;
|
let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE;
|
||||||
debugln!("apic {:?}", PhysAddr::new(rax));
|
|
||||||
cpu_set_msr(IA32_APIC_BASE_MSR, rax);
|
cpu_set_msr(IA32_APIC_BASE_MSR, rax);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +44,6 @@ fn get_apic_base() -> PhysAddr {
|
|||||||
let mut value: u64 = 0;
|
let mut value: u64 = 0;
|
||||||
cpu_get_msr(IA32_APIC_BASE_MSR, &mut value);
|
cpu_get_msr(IA32_APIC_BASE_MSR, &mut value);
|
||||||
|
|
||||||
debugln!("apic base {:#x}", value);
|
|
||||||
|
|
||||||
PhysAddr::new(value & 0xfffff0000)
|
PhysAddr::new(value & 0xfffff0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ fn write_apic_register(apic_base: &PhysAddr, reg: u64, value: u32) {
|
|||||||
|
|
||||||
let phys_check =
|
let phys_check =
|
||||||
OFFSET_PAGE_TABLE.get().unwrap().lock().translate(virt_addr);
|
OFFSET_PAGE_TABLE.get().unwrap().lock().translate(virt_addr);
|
||||||
debugln!("{:?}", phys_check);
|
|
||||||
|
|
||||||
unsafe { *(virt_addr.as_u64() as *mut u32) = value };
|
unsafe { *(virt_addr.as_u64() as *mut u32) = value };
|
||||||
}
|
}
|
||||||
@@ -76,14 +75,13 @@ pub fn check_apic() -> bool {
|
|||||||
unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr {
|
unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr {
|
||||||
let phys = phys.as_u64();
|
let phys = phys.as_u64();
|
||||||
phys.checked_add(*PHYSICAL_MEMORY_OFFSET)
|
phys.checked_add(*PHYSICAL_MEMORY_OFFSET)
|
||||||
.map_or_else(|| panic!(" overflow"), VirtAddr::new)
|
.map_or_else(|| panic!("Overflow"), VirtAddr::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_apic() {
|
pub fn enable_apic() {
|
||||||
let apic_base_physical_addr = get_apic_base();
|
let apic_base_physical_addr = get_apic_base();
|
||||||
set_apic_base_enable(apic_base_physical_addr);
|
set_apic_base_enable(apic_base_physical_addr);
|
||||||
|
|
||||||
debugln!("{:?}", apic_base_physical_addr);
|
|
||||||
enable_timer();
|
enable_timer();
|
||||||
|
|
||||||
write_apic_register(
|
write_apic_register(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use core::fmt;
|
use core::fmt::{self, Write};
|
||||||
use spin::{Lazy, Mutex};
|
use spin::{Lazy, Mutex};
|
||||||
use x86_64::instructions::interrupts;
|
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::resources::font::{FONT_SPLEEN_8X16, Font};
|
||||||
|
use crate::std::maths::geometry::Vec2;
|
||||||
|
|
||||||
static FONT_WIDTH: u32 = 8;
|
static FONT_WIDTH: u32 = 8;
|
||||||
static FONT_HEIGHT: u32 = 16;
|
static FONT_HEIGHT: u32 = 16;
|
||||||
@@ -25,11 +26,8 @@ pub struct Writer {
|
|||||||
screen_width: u32,
|
screen_width: u32,
|
||||||
/// Measured in chars not pixels.
|
/// Measured in chars not pixels.
|
||||||
screen_height: u32,
|
screen_height: u32,
|
||||||
/// 16 pixels tall.
|
|
||||||
text_line: u32,
|
text_line: u32,
|
||||||
/// 8 pixels wide.
|
|
||||||
text_col: u32,
|
text_col: u32,
|
||||||
|
|
||||||
fg_color: Colour,
|
fg_color: Colour,
|
||||||
bg_color: Colour,
|
bg_color: Colour,
|
||||||
}
|
}
|
||||||
@@ -62,6 +60,15 @@ impl Writer {
|
|||||||
self.font = font;
|
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.
|
/// This is sent when the user types a backspace.
|
||||||
const BACKSPACE: u8 = 8;
|
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() {
|
pub fn reset_cursor() {
|
||||||
interrupts::without_interrupts(|| {
|
interrupts::without_interrupts(|| {
|
||||||
let mut writer = WRITER.lock();
|
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_export]
|
||||||
macro_rules! println_log {
|
macro_rules! println_log {
|
||||||
() => ($crate::print_log!("\n"));
|
() => ($crate::print_log!("\n"));
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
|
|||||||
extern "x86-interrupt" fn timer_interrupt_handler(
|
extern "x86-interrupt" fn timer_interrupt_handler(
|
||||||
_stack_frame: InterruptStackFrame,
|
_stack_frame: InterruptStackFrame,
|
||||||
) {
|
) {
|
||||||
debug!("Timer Interrupt");
|
// debug!("Timer Interrupt");
|
||||||
unsafe {
|
unsafe {
|
||||||
PICS.lock()
|
PICS.lock()
|
||||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||||
@@ -138,10 +138,10 @@ extern "x86-interrupt" fn page_fault_handler(
|
|||||||
_stack_frame: InterruptStackFrame,
|
_stack_frame: InterruptStackFrame,
|
||||||
_error_code: PageFaultErrorCode,
|
_error_code: PageFaultErrorCode,
|
||||||
) {
|
) {
|
||||||
serial_println!("Exception: Page Fault");
|
// serial_println!("Exception: Page Fault");
|
||||||
serial_println!("Accessed Address: {:?}", Cr2::read());
|
// serial_println!("Accessed Address: {:?}", Cr2::read());
|
||||||
serial_println!("Error Code: {:?}", _error_code);
|
// serial_println!("Error Code: {:?}", _error_code);
|
||||||
serial_println!("{:#?}", _stack_frame);
|
// serial_println!("{:#?}", _stack_frame);
|
||||||
|
|
||||||
if let Some(frame_allocator) = FRAME_ALLOCATOR.get() {
|
if let Some(frame_allocator) = FRAME_ALLOCATOR.get() {
|
||||||
let mut f = frame_allocator.lock();
|
let mut f = frame_allocator.lock();
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use crate::arch::x86_64::memory::units::MemoryUnits;
|
|||||||
use crate::arch::x86_64::memory::{
|
use crate::arch::x86_64::memory::{
|
||||||
FRAME_ALLOCATOR, HEAP_SIZE, HEAP_VIRTUAL_SPACE,
|
FRAME_ALLOCATOR, HEAP_SIZE, HEAP_VIRTUAL_SPACE,
|
||||||
};
|
};
|
||||||
use crate::serial_println;
|
use crate::debugln;
|
||||||
use crate::{debugln, serial_print};
|
|
||||||
use core::alloc::{GlobalAlloc, Layout};
|
use core::alloc::{GlobalAlloc, Layout};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use spin::{Mutex, MutexGuard};
|
use spin::{Mutex, MutexGuard};
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ use x86_64::{
|
|||||||
structures::paging::{OffsetPageTable, PageTable},
|
structures::paging::{OffsetPageTable, PageTable},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000; // start address of the memory space where we store allocated stacks
|
/// 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 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 const HEAP_SIZE: usize = MiB(1).to_bytes();
|
||||||
|
|
||||||
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
|
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
|
||||||
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
|
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
|
||||||
|
|
||||||
/// Returns a mutable reference to the current level 4 page table.
|
/// Returns a mutable reference to the current level 4 page table.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|||||||
+52
-42
@@ -1,5 +1,5 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(abi_x86_interrupt)]
|
#![feature(abi_x86_interrupt, breakpoint)]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::correctness,
|
clippy::correctness,
|
||||||
clippy::nursery,
|
clippy::nursery,
|
||||||
@@ -14,46 +14,40 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
// TODO: Fix nesting under `arch`. A lot of code does not NEED to run on
|
|
||||||
// x86_64. Note that the panic handler does.
|
|
||||||
arch::x86_64::{
|
arch::x86_64::{
|
||||||
cpu::apic::enable_apic,
|
// cpu::apic::enable_apic,
|
||||||
drivers::{
|
drivers::{
|
||||||
ascii::screensize_chars, framebuffer::display::screensize_px,
|
ascii::screensize_chars, framebuffer::display::screensize_px,
|
||||||
},
|
},
|
||||||
|
gdt,
|
||||||
|
interrupts,
|
||||||
memory::{
|
memory::{
|
||||||
FRAME_ALLOCATOR,
|
FRAME_ALLOCATOR,
|
||||||
allocation::{
|
allocation::{
|
||||||
heap_alloc::init_heap, page_alloc::FoundryOSFrameAllocator,
|
heap_alloc::init_heap, page_alloc::FoundryOSFrameAllocator,
|
||||||
},
|
},
|
||||||
init_page_table,
|
init_page_table, mapping,
|
||||||
units::MemoryUnits,
|
units::MemoryUnits,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use arch::x86_64::memory::mapping;
|
use alloc::{boxed::Box, format};
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use limine::BaseRevision;
|
use limine::BaseRevision;
|
||||||
use std::unwind::UNWINDER;
|
|
||||||
use x86_64::VirtAddr;
|
use x86_64::VirtAddr;
|
||||||
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
|
/// Commonly used re-exports.
|
||||||
|
pub mod prelude;
|
||||||
pub mod resources;
|
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;
|
pub mod std;
|
||||||
|
mod step;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub mod prelude {
|
|
||||||
pub use crate::{
|
|
||||||
debug, debugln, eprint, eprintln, print, print_log, println,
|
|
||||||
println_log, serial_print, serial_println,
|
|
||||||
std::debug::_debug,
|
|
||||||
std::io::{_print, _print_err, _print_log, _serial_write},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the base revision to the latest revision supported by the crate.
|
/// Sets the base revision to the latest revision supported by the crate.
|
||||||
/// See specification for further info.
|
/// See specification for further info.
|
||||||
/// Be sure to mark all limine requests with #[used], otherwise they may be
|
/// Be sure to mark all limine requests with #[used], otherwise they may be
|
||||||
@@ -73,16 +67,28 @@ pub fn hcf() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NoError {}
|
||||||
|
|
||||||
|
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
|
/// Panicking before this is initialised is unwise. We should probably extract
|
||||||
/// very early init into it's own function because Stack Traces may require
|
/// very early init into it's own function because Stack Traces may require
|
||||||
/// allocations etc.
|
/// allocations etc.
|
||||||
pub fn boot() -> Result<(), &'static str> {
|
#[inline(never)]
|
||||||
|
pub fn boot() -> Result<(), Box<dyn core::error::Error>> {
|
||||||
if !BASE_REVISION.is_supported() {
|
if !BASE_REVISION.is_supported() {
|
||||||
return Err("Base revision not supported");
|
return Err("Base revision not supported.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
use arch::x86_64::{gdt, interrupts};
|
|
||||||
|
|
||||||
let memory_map = mapping::get_memory_map();
|
let memory_map = mapping::get_memory_map();
|
||||||
|
|
||||||
print_log!(" Initialising Serial... ");
|
print_log!(" Initialising Serial... ");
|
||||||
@@ -94,12 +100,11 @@ pub fn boot() -> Result<(), &'static str> {
|
|||||||
debugln!("[Success]");
|
debugln!("[Success]");
|
||||||
}
|
}
|
||||||
|
|
||||||
debugln!(" Display...");
|
debugln!(
|
||||||
let dimensions = screensize_chars();
|
"Display is {:?} chars, {:?} px.",
|
||||||
let dimensions2 = screensize_px();
|
screensize_chars(),
|
||||||
debugln!(" => (px) : {}x{} ", dimensions2.0, dimensions2.1);
|
screensize_px()
|
||||||
debugln!(" => (chars) : {}x{} ", dimensions.0, dimensions.1);
|
);
|
||||||
debugln!(" [Success]");
|
|
||||||
|
|
||||||
debug!(" Setting Up Global Descriptor Table... ");
|
debug!(" Setting Up Global Descriptor Table... ");
|
||||||
gdt::init();
|
gdt::init();
|
||||||
@@ -116,36 +121,41 @@ pub fn boot() -> Result<(), &'static str> {
|
|||||||
FoundryOSFrameAllocator::init(memory_map);
|
FoundryOSFrameAllocator::init(memory_map);
|
||||||
let available_bytes =
|
let available_bytes =
|
||||||
FRAME_ALLOCATOR.get().unwrap().lock().available_memory();
|
FRAME_ALLOCATOR.get().unwrap().lock().available_memory();
|
||||||
|
|
||||||
debugln!(
|
debugln!(
|
||||||
" => Available Memory: {}",
|
" => Available Memory: {}",
|
||||||
MemoryUnits::from_bytes(available_bytes as usize)
|
MemoryUnits::from_bytes(available_bytes as usize)
|
||||||
);
|
);
|
||||||
|
|
||||||
debugln!("[Success]");
|
// Allocations should be all fine past this point.
|
||||||
|
|
||||||
debugln!(" Initialising Heap... ");
|
debugln!(" Initialising Heap... ");
|
||||||
if unsafe { init_heap() }.is_err() {
|
match unsafe { init_heap() } {
|
||||||
return Err("Failed to initialise heap: error");
|
Ok(_) => debugln!(" [Success]"),
|
||||||
|
Err(why) => return Err(format!("{:?}", why).into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!(" Enabling PICs... ");
|
||||||
|
interrupts::enable_pic();
|
||||||
debugln!("[Success]");
|
debugln!("[Success]");
|
||||||
|
|
||||||
// debug!(" Enabling PICs... ");
|
// debug!(" Disabling PICs... ");
|
||||||
// interrupts::enable_pic();
|
// interrupts::disable_pic();
|
||||||
// debugln!("[Success]");
|
// debugln!("[Success]");
|
||||||
|
|
||||||
debug!(" Disabling PICs... ");
|
// debug!(" Initialising APIC... ");
|
||||||
interrupts::disable_pic();
|
// enable_apic();
|
||||||
debugln!("[Success]");
|
// debugln!("[Success]");
|
||||||
|
|
||||||
debug!(" Initialising APIC... ");
|
|
||||||
enable_apic();
|
|
||||||
debugln!("[Success]");
|
|
||||||
|
|
||||||
debug!(" Enabling Interrupts... ");
|
debug!(" Enabling Interrupts... ");
|
||||||
x86_64::instructions::interrupts::enable();
|
x86_64::instructions::interrupts::enable();
|
||||||
debugln!("[Success]");
|
debugln!("[Success]");
|
||||||
|
|
||||||
UNWINDER.lock(); // Initialises the Unwinder once and only once :fingers_crossed:.
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &core::panic::PanicInfo<'_>) -> ! {
|
||||||
|
debugln!("{:?}", _info);
|
||||||
|
|
||||||
|
hcf()
|
||||||
|
}
|
||||||
|
|||||||
+6
-13
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use foundry_os::arch::x86_64::processing::async_io::task::{Executor, Task};
|
use foundry_os::{
|
||||||
use foundry_os::prelude::*;
|
arch::x86_64::processing::async_io::task::{Executor, Task},
|
||||||
use foundry_os::util::shell::shell;
|
prelude::*,
|
||||||
|
util::shell::shell,
|
||||||
|
};
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn kmain() -> ! {
|
extern "C" fn kmain() -> ! {
|
||||||
@@ -13,7 +15,6 @@ extern "C" fn kmain() -> ! {
|
|||||||
if let Err(err) = foundry_os::boot() {
|
if let Err(err) = foundry_os::boot() {
|
||||||
panic!("{}", err);
|
panic!("{}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
println_log!(" [ Kernel Initialised Successfully ] ");
|
println_log!(" [ Kernel Initialised Successfully ] ");
|
||||||
|
|
||||||
// println!("TESTING :: Allocation");
|
// println!("TESTING :: Allocation");
|
||||||
@@ -22,17 +23,9 @@ extern "C" fn kmain() -> ! {
|
|||||||
// println!("{}", somevec.len());
|
// println!("{}", somevec.len());
|
||||||
// println!("PASSED!");
|
// println!("PASSED!");
|
||||||
|
|
||||||
test1();
|
// test1();
|
||||||
|
|
||||||
let mut executor = Executor::new();
|
let mut executor = Executor::new();
|
||||||
executor.spawn(Task::new(shell()));
|
executor.spawn(Task::new(shell()));
|
||||||
executor.run()
|
executor.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test1() {
|
|
||||||
test2()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test2() {
|
|
||||||
panic!("Test");
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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},
|
||||||
|
};
|
||||||
+63
-21
@@ -11,14 +11,14 @@
|
|||||||
//! * Add support for loading binary programs (this should probably be written
|
//! * Add support for loading binary programs (this should probably be written
|
||||||
//! in a different module)
|
//! in a different module)
|
||||||
|
|
||||||
use alloc::format;
|
use alloc::{format, vec::Vec};
|
||||||
use elf::{
|
use elf::{
|
||||||
ElfBytes, ParseError,
|
ElfBytes, ParseError,
|
||||||
endian::LittleEndian,
|
endian::LittleEndian,
|
||||||
parse::{ParseAt, ParsingTable},
|
parse::{ParseAt, ParsingTable},
|
||||||
section::{SectionHeader, SectionHeaderTable},
|
section::{SectionHeader, SectionHeaderTable},
|
||||||
string_table::StringTable,
|
string_table::StringTable,
|
||||||
symbol::SymbolTable,
|
symbol::{Symbol, SymbolTable},
|
||||||
};
|
};
|
||||||
use limine::request::KernelFileRequest;
|
use limine::request::KernelFileRequest;
|
||||||
|
|
||||||
@@ -38,6 +38,8 @@ pub static KERNEL_FILE_REQUEST: KernelFileRequest = KernelFileRequest::new();
|
|||||||
pub enum ElfError {
|
pub enum ElfError {
|
||||||
/// Returned if a section did not exist in [ElfReader::get_section_size].
|
/// Returned if a section did not exist in [ElfReader::get_section_size].
|
||||||
SectionNotExists,
|
SectionNotExists,
|
||||||
|
/// Returned if we failed to fetch the symbol table.
|
||||||
|
Symtab,
|
||||||
/// Parse errors returned by the `elf` crate.
|
/// Parse errors returned by the `elf` crate.
|
||||||
OtherParseError(elf::ParseError),
|
OtherParseError(elf::ParseError),
|
||||||
}
|
}
|
||||||
@@ -49,8 +51,14 @@ impl From<elf::ParseError> for ElfError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ElfReader {
|
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.
|
/// 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 {
|
impl ElfReader {
|
||||||
@@ -65,34 +73,35 @@ impl ElfReader {
|
|||||||
/// Both of these should be satisfied, but this function is marked unsafe
|
/// Both of these should be satisfied, but this function is marked unsafe
|
||||||
/// just in case, because we are derefererencing arbitrary pointers.
|
/// just in case, because we are derefererencing arbitrary pointers.
|
||||||
pub unsafe fn new() -> Result<Self, ElfError> {
|
pub unsafe fn new() -> Result<Self, ElfError> {
|
||||||
let response = KERNEL_FILE_REQUEST
|
// Store this for use with other libraries.
|
||||||
.get_response()
|
let bytes = get_elf_slice();
|
||||||
.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> =
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[warn(clippy::unwrap_used)]
|
|
||||||
pub fn get_symbol_table(
|
pub fn get_symbol_table(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Option<(SymbolTable<'static, LittleEndian>, StringTable<'static>)>,
|
Option<(SymbolTable<'static, LittleEndian>, StringTable<'static>)>,
|
||||||
ElfError,
|
ElfError,
|
||||||
> {
|
> {
|
||||||
// TODO: Remove .unwrap().
|
Ok(self.elf.symbol_table()?)
|
||||||
Ok(self.elf.symbol_table().unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the section size of `section_name` in bytes.
|
/// Gets the section size of `section_name` in bytes.
|
||||||
@@ -114,7 +123,7 @@ impl ElfReader {
|
|||||||
/// Gets the section header of `section_name`.
|
/// Gets the section header of `section_name`.
|
||||||
pub fn get_section_header(
|
pub fn get_section_header(
|
||||||
&self,
|
&self,
|
||||||
section_name: &'static str,
|
section_name: &str,
|
||||||
) -> Result<SectionHeader, ElfError> {
|
) -> Result<SectionHeader, ElfError> {
|
||||||
let section_hdr = self
|
let section_hdr = self
|
||||||
.elf
|
.elf
|
||||||
@@ -123,4 +132,37 @@ impl ElfReader {
|
|||||||
|
|
||||||
section_hdr.ok_or(ElfError::SectionNotExists)
|
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 _
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ impl<T: Coordinate> Vec2<T> {
|
|||||||
pub const fn y(&self) -> T {
|
pub const fn y(&self) -> T {
|
||||||
self.y
|
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> {
|
impl<T: Coordinate> AddAssign for Vec2<T> {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
pub mod application;
|
pub mod application;
|
||||||
pub mod ascii;
|
pub mod ascii;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod elf;
|
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod maths;
|
pub mod maths;
|
||||||
pub mod unwind;
|
|
||||||
|
|||||||
@@ -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.
|
|
||||||
pub base_addrs: BaseAddresses,
|
|
||||||
/// The parsed `.eh_frame_hdr` section.
|
|
||||||
pub 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].
|
|
||||||
pub hdr_table: EhHdrTable<'static, EndianSlice<'static, LittleEndian>>,
|
|
||||||
/// The parsed `.eh_frame` containing the CFIs (call frame information).
|
|
||||||
pub 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,23 +0,0 @@
|
|||||||
use core::arch::asm;
|
|
||||||
|
|
||||||
use eh_info::ELF;
|
|
||||||
use spin::{Lazy, Mutex};
|
|
||||||
use unwinder::{RegisterSet, Unwinder};
|
|
||||||
|
|
||||||
pub mod eh_info;
|
|
||||||
pub mod panic;
|
|
||||||
pub mod unwinder;
|
|
||||||
|
|
||||||
/// We should initialise on program start.
|
|
||||||
pub static UNWINDER: Lazy<Mutex<Unwinder>> = Lazy::new(|| {
|
|
||||||
// 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 { eh_info::EhInfo::from_hdr_ptr(eh_frame_ptr) };
|
|
||||||
let mut registers = RegisterSet::default();
|
|
||||||
|
|
||||||
Mutex::new(Unwinder::new(eh_info, registers))
|
|
||||||
});
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
//! Defines a simple panic handler which handles stack traces as required.
|
|
||||||
|
|
||||||
use core::{arch::asm, panic::PanicInfo};
|
|
||||||
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use fallible_iterator::FallibleIterator;
|
|
||||||
use gimli::{Register, X86_64};
|
|
||||||
|
|
||||||
use super::unwinder::Unwinder;
|
|
||||||
use crate::{
|
|
||||||
hcf,
|
|
||||||
prelude::*,
|
|
||||||
std::unwind::{UNWINDER, eh_info::ELF, unwinder::RegisterSet},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
/// A basic panic handler which can produce a helpful stack trace.
|
|
||||||
pub fn panic_handler(info: &PanicInfo<'_>) -> ! {
|
|
||||||
println!("Kernel panic: {}", info);
|
|
||||||
serial_println!("Kernel panic: {}", info);
|
|
||||||
|
|
||||||
let mut rip: u64;
|
|
||||||
let mut rsp: u64;
|
|
||||||
unsafe {
|
|
||||||
asm!("lea {0}, [rip]",
|
|
||||||
"mov {1}, rsp",
|
|
||||||
out(reg) rip, out(reg) rsp);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut unwinder = UNWINDER.lock();
|
|
||||||
unwinder.regs.set_pc(rip);
|
|
||||||
unwinder.regs.set_stack_ptr(rsp);
|
|
||||||
|
|
||||||
while let Some(call_frame) = unwinder.next().unwrap_or_else(|err| {
|
|
||||||
// If an unwind error occurred.
|
|
||||||
eprintln!("{:?}", err);
|
|
||||||
hcf()
|
|
||||||
}) {
|
|
||||||
serial_println!("Got frame: {:x?}", call_frame);
|
|
||||||
let Some((symtab, strtab)) =
|
|
||||||
ELF.get_symbol_table().unwrap_or_else(|e| {
|
|
||||||
serial_println!("{:?}", e);
|
|
||||||
hcf()
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
// TODO: Omit symbol names but just print addresses.
|
|
||||||
serial_println!("Didn't find symtab and strtab!");
|
|
||||||
hcf()
|
|
||||||
};
|
|
||||||
|
|
||||||
// let sym_name = symtab.get(call_frame.pc as usize).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::hcf()
|
|
||||||
}
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
//! Implements the core stack unwinding logic.
|
|
||||||
//!
|
|
||||||
//! # TODOs
|
|
||||||
//!
|
|
||||||
//! * Support evaluation of DWARF expressions, this might not be required
|
|
||||||
//! however, because Rust doesn't tend to use this DWARF feature.
|
|
||||||
|
|
||||||
use fallible_iterator::FallibleIterator;
|
|
||||||
use gimli::{
|
|
||||||
CfaRule, EndianSlice, LittleEndian, Register, RegisterRule, StoreOnHeap,
|
|
||||||
UnwindContext, UnwindSection, X86_64,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::eh_info::EhInfo;
|
|
||||||
|
|
||||||
/// Implements the core stack unwinding logic by parsing the call frame
|
|
||||||
/// information. This also stores current (DWARF) register values.
|
|
||||||
///
|
|
||||||
/// # Sources
|
|
||||||
///
|
|
||||||
/// Taken from [lesenechal.fr](https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way)
|
|
||||||
/// with some additional features to be added soon.
|
|
||||||
pub struct Unwinder {
|
|
||||||
/// The call frame information.
|
|
||||||
eh_info: EhInfo,
|
|
||||||
/// An [UnwindContext] used by Gimli for optimisations.
|
|
||||||
unwind_ctx: UnwindContext<usize, StoreOnHeap>,
|
|
||||||
/// The current values of ABI/architecture independent registers. There are
|
|
||||||
/// used by DWARF.
|
|
||||||
pub regs: RegisterSet,
|
|
||||||
/// The current CFA address.
|
|
||||||
cfa: u64,
|
|
||||||
/// Is this the first iteration?
|
|
||||||
is_first: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Use map_err et al.
|
|
||||||
impl FallibleIterator for Unwinder {
|
|
||||||
type Item = CallFrame;
|
|
||||||
type Error = UnwinderError;
|
|
||||||
|
|
||||||
/// Returns call frames of calling functions. This may be called to produce
|
|
||||||
/// a stack trace or otherwise support physical unwinding.
|
|
||||||
fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
|
|
||||||
// Gets the current program counter from the DWARF register set.
|
|
||||||
let Some(pc) = self.regs.get_pc() else {
|
|
||||||
return Err(UnwinderError::NoPcRegister);
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.is_first {
|
|
||||||
self.is_first = false;
|
|
||||||
|
|
||||||
return Ok(Some(CallFrame { pc, symbol: 0 }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a row in the virtual unwind table AKA the CFI which will help
|
|
||||||
// us find the CFA (canonical frame address) for a given program
|
|
||||||
// counter.
|
|
||||||
let Ok(row) = self.eh_info.hdr_table.unwind_info_for_address(
|
|
||||||
&self.eh_info.eh_frame,
|
|
||||||
&self.eh_info.base_addrs,
|
|
||||||
&mut self.unwind_ctx,
|
|
||||||
pc,
|
|
||||||
|section, bases, offset|
|
|
||||||
// Finds a DWARF CIE using an offset as given.
|
|
||||||
section.cie_from_offset(bases, offset),
|
|
||||||
) else {
|
|
||||||
return Err(UnwinderError::NoUnwindInfo);
|
|
||||||
};
|
|
||||||
|
|
||||||
// We compute the CFA (canonical frame address from its rule).
|
|
||||||
// TODO: Support other rules such as DWARF expressions.
|
|
||||||
match row.cfa() {
|
|
||||||
CfaRule::RegisterAndOffset { register, offset } => {
|
|
||||||
let Some(reg_val) = self.regs.get(*register) else {
|
|
||||||
return Err(UnwinderError::CfaRuleUnknownRegister(
|
|
||||||
*register,
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
self.cfa = (reg_val as i64 + offset) as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support other rules for computing the CFA.
|
|
||||||
_ => return Err(UnwinderError::UnsupportedCfaRule),
|
|
||||||
}
|
|
||||||
|
|
||||||
for reg in RegisterSet::iter() {
|
|
||||||
match row.register(reg) {
|
|
||||||
RegisterRule::Undefined => self.regs.undef(reg),
|
|
||||||
RegisterRule::SameValue => (),
|
|
||||||
RegisterRule::Offset(offset) => {
|
|
||||||
// Adds the given offset to the register contents and
|
|
||||||
// retrieve the value from the stack at address CFA +
|
|
||||||
// offset.
|
|
||||||
let ptr = (self.cfa as i64 + offset) as u64 as *const usize;
|
|
||||||
|
|
||||||
self.regs.set(reg, unsafe { ptr.read() } as u64)?
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
return Err(UnwinderError::UnimplementedRegisterRule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the new value for %rip from the function return value and
|
|
||||||
// subtract one from it, because the address actually points the the
|
|
||||||
// next instruction after the call.
|
|
||||||
let Some(pc) = self.regs.get_ret() else {
|
|
||||||
return Err(UnwinderError::NoReturnAddr);
|
|
||||||
};
|
|
||||||
|
|
||||||
// REVIEWME: Must be a nicer way of doing this.
|
|
||||||
let Some(pc) = pc.checked_sub(1) else {
|
|
||||||
// REVIEWME: This should handle underflow now.
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.regs.set_pc(pc);
|
|
||||||
|
|
||||||
// Set %rsp to the CFA. This simulates returning from the function,
|
|
||||||
// destroying the call frame, so we were able to virtually unwind (to
|
|
||||||
// the caller function).
|
|
||||||
self.regs.set_stack_ptr(self.cfa);
|
|
||||||
|
|
||||||
Ok(Some(CallFrame { pc, symbol: 0 }))
|
|
||||||
}
|
|
||||||
// fn next(&mut self) -> Option<Result<Option<CallFrame>, UnwinderError>> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unwinder {
|
|
||||||
pub fn new(eh_info: EhInfo, regset: RegisterSet) -> Self {
|
|
||||||
Self {
|
|
||||||
eh_info,
|
|
||||||
regs: regset,
|
|
||||||
unwind_ctx: UnwindContext::new(),
|
|
||||||
cfa: 0,
|
|
||||||
is_first: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The set of registers used by DWARF. This struct allows future portability if
|
|
||||||
/// we want to target other architectures than x86_64.
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct RegisterSet {
|
|
||||||
rip: Option<u64>,
|
|
||||||
rsp: Option<u64>,
|
|
||||||
rbp: Option<u64>,
|
|
||||||
/// The return address register.
|
|
||||||
ret: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegisterSet {
|
|
||||||
pub const fn get(&self, reg: Register) -> Option<u64> {
|
|
||||||
match reg {
|
|
||||||
X86_64::RSP => self.rsp,
|
|
||||||
X86_64::RBP => self.rbp,
|
|
||||||
X86_64::RA => self.ret,
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn set(
|
|
||||||
&mut self,
|
|
||||||
reg: Register,
|
|
||||||
val: u64,
|
|
||||||
) -> Result<(), UnwinderError> {
|
|
||||||
*match reg {
|
|
||||||
X86_64::RSP => &mut self.rsp,
|
|
||||||
X86_64::RBP => &mut self.rbp,
|
|
||||||
X86_64::RA => &mut self.ret,
|
|
||||||
_ => return Err(UnwinderError::UnexpectedRegister(reg)),
|
|
||||||
} = Some(val);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn undef(&mut self, reg: Register) {
|
|
||||||
*match reg {
|
|
||||||
X86_64::RSP => &mut self.rsp,
|
|
||||||
X86_64::RBP => &mut self.rbp,
|
|
||||||
X86_64::RA => &mut self.ret,
|
|
||||||
_ => return,
|
|
||||||
} = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn get_pc(&self) -> Option<u64> {
|
|
||||||
self.rip
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn set_pc(&mut self, val: u64) {
|
|
||||||
self.rip = Some(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn get_ret(&self) -> Option<u64> {
|
|
||||||
self.ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn set_stack_ptr(&mut self, val: u64) {
|
|
||||||
self.rsp = Some(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter() -> impl Iterator<Item = Register> {
|
|
||||||
[X86_64::RSP, X86_64::RBP, X86_64::RA].into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The current instruction pointer.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CallFrame {
|
|
||||||
/// The current instruction pointer.
|
|
||||||
pub pc: u64,
|
|
||||||
/// The symbol of the function.
|
|
||||||
pub symbol: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// A list of errors that could occur whilst unwinding the stack.
|
|
||||||
pub enum UnwinderError {
|
|
||||||
UnexpectedRegister(Register),
|
|
||||||
UnsupportedCfaRule,
|
|
||||||
UnimplementedRegisterRule,
|
|
||||||
CfaRuleUnknownRegister(Register),
|
|
||||||
NoUnwindInfo,
|
|
||||||
NoPcRegister,
|
|
||||||
NoReturnAddr,
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -2,3 +2,4 @@ wrap_comments = true
|
|||||||
max_width = 80
|
max_width = 80
|
||||||
comment_width = 80
|
comment_width = 80
|
||||||
format_code_in_doc_comments = true
|
format_code_in_doc_comments = true
|
||||||
|
doc_comment_code_block_width = 80
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Set up test-specific flags
|
# 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"
|
test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"
|
||||||
serial_flags="-serial stdio"
|
serial_flags="-serial stdio"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user