diff --git a/.cargo/config.toml b/.cargo/config.toml index 450058d..5b35ff9 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,6 @@ [unstable] -build-std = ["core", "compiler_builtins"] # recompile these -build-std-features = ["compiler-builtins-mem"] +build-std = ["alloc", "core", "compiler_builtins"] # recompile these +build-std-features = [ "compiler-builtins-mem" ] [build] target = "x86_64-rustboot.json" # target OS specifications diff --git a/Cargo.lock b/Cargo.lock index c354281..8d1f1bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" @@ -20,6 +20,27 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "unicode-xid", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -39,6 +60,89 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "multiboot2" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43fcee184de68e344a888bc4f2b9d6b2f2f527cae8cedbb4d62a4df727d1ceae" +dependencies = [ + "bitflags", + "derive_more", + "log", + "multiboot2-common", + "ptr_meta", + "uefi-raw", +] + +[[package]] +name = "multiboot2-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbabf8d9980d55576ba487924fa0b9a467fc0012b996b93d2319904f168ed8ab" +dependencies = [ + "derive_more", + "ptr_meta", +] + +[[package]] +name = "pc-keyboard" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca629cbb3f0d5b699c338f0129ff78c9bfd7ea8b1258ad529bff490dc8ed5a" + +[[package]] +name = "pic8259" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d9a86c292b165f757e47e7fd66855def189b2564609bc4203727b27c33db22" +dependencies = [ + "x86_64", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rlibc" version = "1.0.0" @@ -50,6 +154,9 @@ name = "rustboot" version = "0.1.0" dependencies = [ "lazy_static", + "multiboot2", + "pc-keyboard", + "pic8259", "rlibc", "spin 0.10.0", "volatile 0.3.0", @@ -83,6 +190,57 @@ dependencies = [ "lock_api", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "uefi-raw" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b463030b802e1265a3800fab24df95d3229c202c2e408832a206f05b4d1496ca" +dependencies = [ + "bitflags", + "ptr_meta", + "uguid", +] + +[[package]] +name = "uguid" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8352f8c05e47892e7eaf13b34abd76a7f4aeaf817b716e88789381927f199c" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "volatile" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index c6cfebd..a18a2ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,17 +16,14 @@ test-timeout = 30 # in seconds crate-type = ["staticlib"] # library that contains all dependencies [dependencies] +multiboot2 = "0.23" +pc-keyboard = "0.8" +pic8259 = "0.11" rlibc = "1.0" spin = "0.10" -volatile = "0.3" # newest functional version -x86_64 = "0.15" +volatile = "0.3" # 0.3 is newest functional version +x86_64 = "0.15" # 0.14 is newest functional version [dependencies.lazy_static] version = "1.5" features = ["spin_no_std"] - -[profile.dev] -panic = "abort" - -[profile.release] -panic = "abort" diff --git a/build/os.iso b/build/os.iso index 1168be6..b03452b 100644 Binary files a/build/os.iso and b/build/os.iso differ diff --git a/flake.lock b/flake.lock index a149c1a..87f53a6 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1746576598, - "narHash": "sha256-FshoQvr6Aor5SnORVvh/ZdJ1Sa2U4ZrIMwKBX5k2wu0=", + "lastModified": 1747060738, + "narHash": "sha256-ByfPRQuqj+nhtVV0koinEpmJw0KLzNbgcgi9EF+NVow=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b3582c75c7f21ce0b429898980eddbbf05c68e55", + "rev": "eaeed9530c76ce5f1d2d8232e08bec5e26f18ec1", "type": "github" }, "original": { diff --git a/justfile b/justfile index b97b18a..c05bd04 100644 --- a/justfile +++ b/justfile @@ -1,5 +1,7 @@ all: setup asm rust link image clean +mkrun: all run + setup: mkdir temp diff --git a/link-cfg/linker.ld b/link-cfg/linker.ld index fd54e9e..f03aa3a 100644 --- a/link-cfg/linker.ld +++ b/link-cfg/linker.ld @@ -7,6 +7,14 @@ SECTIONS { } .text : { - *(.text) + *(.text .text.*) + } + .rodata : + { + *(.rodata .rodata.*) + } + .data.rel.ro : + { + *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) } } diff --git a/src/' b/src/' new file mode 100644 index 0000000..b2ce26b --- /dev/null +++ b/src/' @@ -0,0 +1,56 @@ +#![no_std] +#![feature(abi_x86_interrupt)] + +extern crate alloc; +extern crate multiboot2; +extern crate rlibc; + +mod allocator; +mod gdt; +mod interrupt; +mod vga_buffer; + +use core::panic::PanicInfo; +use multiboot2::{BootInformation, BootInformationHeader}; + +#[unsafe(no_mangle)] +pub extern "C" fn rust_main(multiboot_information_address: usize) { + println!("Hello World!"); + // println!( + // "This program booted itself from assembly. This println function manipulates individual bytes in a vga buffer. My power is beyond your understanding\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" + // ); + let boot_info = unsafe { + BootInformation::load(multiboot_information_address as *const BootInformationHeader) + .unwrap() + }; + let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required"); + init(); + println!("Got past"); + halt() +} + +pub fn init() { + allocator::init_heap + + println!("got to gdt::init()"); + gdt::init(); + println!("got to interrupt::init_idt();"); + interrupt::init_idt(); + println!("got to PICS.lock()"); + unsafe { interrupt::PICS.lock().initialize() }; + println!("got to enable inst"); + x86_64::instructions::interrupts::enable(); +} + +#[panic_handler] +pub fn panic(info: &PanicInfo) -> ! { + println!("Panic happened: {}", info); + halt() +} + +pub fn halt() -> ! { + println!("we've halted"); + loop { + x86_64::instructions::hlt(); + } +} diff --git a/src/allocator/linked_list.rs b/src/allocator/linked_list.rs new file mode 100644 index 0000000..83956ec --- /dev/null +++ b/src/allocator/linked_list.rs @@ -0,0 +1,114 @@ +use super::{Locked, align_up}; +use alloc::alloc::{GlobalAlloc, Layout}; +use core::{mem, ptr}; + +pub struct LinkedListAllocator { + head: ListNode, +} + +impl LinkedListAllocator { + pub const fn new() -> Self { + Self { + head: ListNode::new(0), + } + } + + pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { + unsafe { self.add_free_region(heap_start, heap_size) }; + } + + unsafe fn add_free_region(&mut self, addr: usize, size: usize) { + assert_eq!(align_up(addr, mem::align_of::()), addr); + assert!(size >= mem::size_of::()); + let mut node = ListNode::new(size); + node.next = self.head.next.take(); + let node_ptr = addr as *mut ListNode; + unsafe { + node_ptr.write(node); + self.head.next = Some(&mut *node_ptr) + } + } + + fn alloc_from_region(region: &ListNode, size: usize, align: usize) -> Result { + let alloc_start = align_up(region.start_addr(), align); + let alloc_end = alloc_start.checked_add(size).ok_or(())?; + + if alloc_end > region.end_addr() { + return Err(()); + } + + let excess_size = region.end_addr() - alloc_end; + if excess_size > 0 && excess_size < mem::size_of::() { + return Err(()); + } + Ok(alloc_start) + } + + fn find_region(&mut self, size: usize, align: usize) -> Option<(&'static mut ListNode, usize)> { + let mut current = &mut self.head; + while let Some(ref mut region) = current.next { + if let Ok(alloc_start) = Self::alloc_from_region(®ion, size, align) { + let next = region.next.take(); + let ret = Some((current.next.take().unwrap(), alloc_start)); + current.next = next; + return ret; + } else { + current = current.next.as_mut().unwrap(); + } + } + None + } + + fn size_align(layout: Layout) -> (usize, usize) { + let layout = layout + .align_to(mem::align_of::()) + .expect("adjusting alignment failed") + .pad_to_align(); + let size = layout.size().max(mem::align_of::()); + (size, layout.align()) + } +} + +unsafe impl GlobalAlloc for Locked { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let (size, align) = LinkedListAllocator::size_align(layout); + let mut allocator = self.lock(); + + 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 { + unsafe { + allocator.add_free_region(alloc_end, excess_size); + } + } + alloc_start as *mut u8 + } else { + ptr::null_mut() + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let (size, _) = LinkedListAllocator::size_align(layout); + unsafe { self.lock().add_free_region(ptr as usize, size) } + } +} + +struct ListNode { + size: usize, + next: Option<&'static mut ListNode>, +} + +impl ListNode { + const fn new(size: usize) -> Self { + ListNode { size, next: None } + } + + fn start_addr(&self) -> usize { + self as *const Self as usize + } + + fn end_addr(&self) -> usize { + self.start_addr() + self.size + } +} diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs new file mode 100644 index 0000000..22870c8 --- /dev/null +++ b/src/allocator/mod.rs @@ -0,0 +1,62 @@ +use crate::memory::area_frame_allocator; +use linked_list::LinkedListAllocator; +use x86_64::VirtAddr; +use x86_64::structures::paging::mapper::MapToError; +use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}; + +pub mod linked_list; + +pub const HEAP_START: usize = 0x_4444_4444_0000; +pub const HEAP_SIZE: usize = 100 * 1024; // 100KiB + +#[global_allocator] +static ALLOCATOR: Locked = Locked::new(LinkedListAllocator::new()); + +pub struct Locked { + inner: spin::Mutex, +} + +impl Locked { + pub const fn new(inner: A) -> Self { + Locked { + inner: spin::Mutex::new(inner), + } + } + + pub fn lock(&self) -> spin::MutexGuard { + self.inner.lock() + } +} + +pub fn init_heap( + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) -> Result<(), MapToError> { + let page_range = { + let heap_start = VirtAddr::new(HEAP_START as u64); + let heap_end = heap_start + HEAP_SIZE as u64 - 1u64; + let heap_start_page = Page::containing_address(heap_start); + let heap_end_page = Page::containing_address(heap_end); + Page::range_inclusive(heap_start_page, heap_end_page) + }; + for page in page_range { + let frame = frame_allocator + .allocate_frame() + .ok_or(MapToError::FrameAllocationFailed)?; + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; + } + unsafe { + ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); + } + Ok(()) +} + +fn align_up(addr: usize, align: usize) -> usize { + let remainder = addr % align; + if remainder == 0 { + addr + } else { + addr - remainder + align + } +} diff --git a/src/gdt/mod.rs b/src/gdt/mod.rs new file mode 100644 index 0000000..55086c7 --- /dev/null +++ b/src/gdt/mod.rs @@ -0,0 +1,52 @@ +use lazy_static::lazy_static; +use x86_64::VirtAddr; +use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; +use x86_64::structures::tss::TaskStateSegment; + +// selector returned by GDT +// code_selector: where CS register goes in case of DF +// tss_selector: where TSS starts in case of DF +struct Selectors { + code_selector: SegmentSelector, + tss_selector: SegmentSelector, +} + +// location of double fault emergency stac +pub const DOUBLE_FAULT_1ST_INDEX: u16 = 0; + +pub fn init() { + use x86_64::instructions::segmentation::{CS, Segment}; + use x86_64::instructions::tables::load_tss; + GDT.0.load(); + + unsafe { + CS::set_reg(GDT.1.code_selector); + load_tss(GDT.1.tss_selector); + } +} + +lazy_static! { + static ref GDT: (GlobalDescriptorTable, Selectors) = { + let mut gdt = GlobalDescriptorTable::new(); + let code_selector = gdt.append(Descriptor::kernel_code_segment()); + let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); + ( + gdt, + Selectors { + code_selector, + tss_selector, + }, + ) + }; + + static ref TSS: TaskStateSegment = { + let mut tss = TaskStateSegment::new(); + tss.interrupt_stack_table[DOUBLE_FAULT_1ST_INDEX as usize] = { + const STACK_SIZE: usize = 4096 * 5; + static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; // TODO make real stack + let stack_start = VirtAddr::from_ptr(&raw const STACK); + stack_start + STACK_SIZE as u64 // end of stack + }; + tss + }; +} diff --git a/src/interrupt/mod.rs b/src/interrupt/mod.rs new file mode 100644 index 0000000..722e08c --- /dev/null +++ b/src/interrupt/mod.rs @@ -0,0 +1,110 @@ +use crate::{print, println}; +use lazy_static::lazy_static; +use pic8259::ChainedPics; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; + +pub fn init_idt() { + IDT.load(); +} + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: spin::Mutex = + spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, +} + +impl InterruptIndex { + fn as_u8(self) -> u8 { + self as u8 + } + fn as_usize(self) -> usize { + usize::from(self.as_u8()) + } +} + +lazy_static! { + static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + unsafe { + idt.double_fault + .set_handler_fn(double_fault_handler) + .set_stack_index(crate::gdt::DOUBLE_FAULT_1ST_INDEX); + } + idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); + idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); + idt.page_fault.set_handler_fn(page_fault_handler); + idt + }; +} + +extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { + println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); +} + +extern "x86-interrupt" fn double_fault_handler( + stack_frame: InterruptStackFrame, + _error_code: u64, +) -> ! { + panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); +} + +extern "x86-interrupt" fn keyboard_interrupt_handler(_keyboard_stack_frame: InterruptStackFrame) { + use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts}; + use spin::Mutex; + use x86_64::instructions::port::Port; + + lazy_static! { + static ref KEYBOARD: Mutex> = + Mutex::new(Keyboard::new( + ScancodeSet1::new(), + layouts::Us104Key, + HandleControl::Ignore + )); + } + + let mut keyboard = KEYBOARD.lock(); + let mut port = Port::new(0x60); + + let scancode: u8 = unsafe { port.read() }; + if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { + if let Some(key) = keyboard.process_keyevent(key_event) { + match key { + DecodedKey::Unicode(character) => print!("{}", character), + DecodedKey::RawKey(key) => print!("{:?}", key), + } + } + } + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); + } +} + +extern "x86-interrupt" fn page_fault_handler( + stack_frame: InterruptStackFrame, + error_code: PageFaultErrorCode, +) { + use x86_64::registers::control::Cr2; + + println!("EXCEPTION: PAGE FAULT"); + println!("Accessed Address: {:?}", Cr2::read()); + println!("Error Code: {:?}?", error_code); + println!("{:#?}", stack_frame); + crate::halt(); +} + +extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { + print!("."); + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); + } +} diff --git a/src/lib.rs b/src/lib.rs index cbe2755..bfc8ff3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,93 @@ #![no_std] +#![feature(abi_x86_interrupt)] +extern crate alloc; +extern crate multiboot2; extern crate rlibc; +mod allocator; +mod gdt; +mod interrupt; +mod memory; mod vga_buffer; use core::panic::PanicInfo; - -pub fn halt() -> ! { - println!("we've halted"); - loop { - x86_64::instructions::hlt(); - } -} +use multiboot2::{BootInformation, BootInformationHeader}; #[unsafe(no_mangle)] -pub extern "C" fn rust_main(_multiboot_information_address: usize) { +pub extern "C" fn rust_main(multiboot_information_address: usize) { println!("Hello World!"); + + // get multiboot info + let boot_info = unsafe { + BootInformation::load(multiboot_information_address as *const BootInformationHeader) + .unwrap() + }; + let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required"); + println!("memory areas:"); + for area in memory_map_tag.memory_areas() { + println!( + " start: 0x{:x}, length: 0x{:x}", + area.start_address(), + area.size() + ); + } + + let elf_sections_tag = boot_info + .elf_sections_tag() + .expect("Elf sections tag required"); + println!("kernel sections:"); + for section in elf_sections_tag.sections() { + println!( + " addr: 0x{:x}, size: 0x{:x}, flags: 0x{:x}", + section.start_address(), + section.size(), + section.flags() + ); + } + + let kernel_start = elf_sections_tag + .sections() + .map(|s| s.start_address()) + .min() + .unwrap(); + let kernel_end = elf_sections_tag + .sections() + .map(|s| s.end_address()) + .max() + .unwrap(); + let multiboot_start = multiboot_information_address; + let multiboot_end = multiboot_start + boot_info.total_size() as usize; + + println!( + "kernel_start: 0x{:x} kernel_end: 0x{:x} kernel_size: {}B\nmultiboot_start: 0x{:x} multiboot_end: 0x{:x} multiboot_size: {}B", + kernel_start, + kernel_end, + kernel_end - kernel_start, + multiboot_start, + multiboot_end, + multiboot_end - multiboot_start + ); + //init(); halt() } +pub fn init() { + gdt::init(); + interrupt::init_idt(); + unsafe { interrupt::PICS.lock().initialize() }; + x86_64::instructions::interrupts::enable(); +} + #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { println!("Panic happened: {}", info); halt() } + +pub fn halt() -> ! { + println!("we've halted"); + loop { + x86_64::instructions::hlt(); + } +} diff --git a/src/memory/area_frame_allocator.rs b/src/memory/area_frame_allocator.rs new file mode 100644 index 0000000..aa95adf --- /dev/null +++ b/src/memory/area_frame_allocator.rs @@ -0,0 +1,95 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::{mem, ptr}; +use multiboot2::{BootInformation, BootInformationHeader}; + +use super::{Frame, PAGE_SIZE}; +use crate::allocator::{HEAP_SIZE, HEAP_START, Locked}; + +pub struct AreaFrameAllocator { + next_free_frame: Frame, + kernel_start: Frame, + kernel_end: Frame, + multiboot_start: Frame, + multiboot_end: Frame, +} + +impl AreaFrameAllocator { + pub const fn new() -> Self { + Self { + next_free_frame: Frame::new(), + kernel_start: Frame::new(), + kernel_end: Frame::new(), + multiboot_start: Frame::new(), + multiboot_end: Frame::new(), + } + } + + pub unsafe fn init(&mut self, multiboot_information_address: usize) -> Self { + let boot_info = unsafe { + BootInformation::load(multiboot_information_address as *const BootInformationHeader) + .unwrap() + }; + //let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required"); + let elf_sections_tag = boot_info + .elf_sections_tag() + .expect("Elf sections tag required"); + let kernel_start = elf_sections_tag + .sections() + .map(|s| s.start_address()) + .min() + .unwrap(); + let kernel_end = elf_sections_tag + .sections() + .map(|s| s.end_address()) + .max() + .unwrap(); + let multiboot_start = multiboot_information_address; + let multiboot_end = multiboot_start + boot_info.total_size(); + + AreaFrameAllocator { + next_free_frame: Frame { number: 0 }, + kernel_start: Frame { + number: kernel_start as usize, + }, + kernel_end: Frame { + number: kernel_end as usize, + }, + multiboot_start: Frame { + number: multiboot_start, + }, + multiboot_end: Frame { + number: multiboot_end, + }, + } + } + + fn find_frame(&mut self, size: usize, align: usize) -> Option<(Frame, usize)> { + let next_addr = self.next_free_frame.number * PAGE_SIZE + HEAP_START; + if &next_addr + core::mem::align_of::() <= PAGE_SIZE + HEAP_START { + Some((Frame::containing_address(next_addr), next_addr)) + } else { + None + } + } + + fn size_align(layout: Layout) -> (usize, usize) { + let layout = layout + .align_to(mem::align_of::()) + .expect("adjusting alignment failed") + .pad_to_align(); + let size = layout.size().max(mem::align_of::()); + (size, layout.align()) + } +} + +unsafe impl GlobalAlloc for Locked { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let (size, align) = AreaFrameAllocator::size_align(layout); + let mut allocator = self.lock(); + match allocator.find_frame(size, align) { + Some((_, addr)) => addr as *mut u8, + None => ptr::null_mut(), + } + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {} +} diff --git a/src/memory/mod.rs b/src/memory/mod.rs new file mode 100644 index 0000000..9623311 --- /dev/null +++ b/src/memory/mod.rs @@ -0,0 +1,19 @@ +pub mod area_frame_allocator; + +pub const PAGE_SIZE: usize = 4096; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Frame { + number: usize, +} + +impl Frame { + pub const fn new() -> Self { + Self { number: 0 } + } + fn containing_address(address: usize) -> Frame { + Frame { + number: address / PAGE_SIZE, + } + } +} diff --git a/src/vga_buffer.rs b/src/vga_buffer/mod.rs similarity index 100% rename from src/vga_buffer.rs rename to src/vga_buffer/mod.rs