diff --git a/src/arch/aarch64/memory/area_frame_allocator.rs b/src/arch/aarch64/memory/area_frame_allocator.rs new file mode 100644 index 0000000..5b236ee --- /dev/null +++ b/src/arch/aarch64/memory/area_frame_allocator.rs @@ -0,0 +1,99 @@ +use memory::{Frame, FrameAllocator}; +use multiboot2::{MemoryArea, MemoryAreaIter}; // replace with DTB? + +pub struct AreaFrameAllocator { + next_free_frame: Frame, + current_area: Option<&'static MemoryArea>, + areas: MemoryAreaIter, + kernel_start: Frame, + kernel_end: Frame, + multiboot_start: Frame, + multiboot_end: Frame, +} + +impl FrameAllocator for AreaFrameAllocator { + fn allocate_frame(&mut self) -> Option { + if let Some(area) = self.current_area { + // "Clone" the frame to return it if it's free. Frame doesn't + // implement Clone, but we can construct an identical frame. + let frame = Frame { + number: self.next_free_frame.number, + }; + + // the last frame of the current area + let current_area_last_frame = { + let address = area.base_addr + area.length - 1; + Frame::containing_address(address as usize) + }; + + if frame > current_area_last_frame { + // all frames of current area are used, switch to next area + self.choose_next_area(); + } else if frame >= self.kernel_start && frame <= self.kernel_end { + // `frame` is used by the kernel + self.next_free_frame = Frame { + number: self.kernel_end.number + 1, + }; + } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + // `frame` is used by the multiboot information structure + self.next_free_frame = Frame { + number: self.multiboot_end.number + 1, + }; + } else { + // frame is unused, increment `next_free_frame` and return it + self.next_free_frame.number += 1; + return Some(frame); + } + // `frame` was not valid, try it again with the updated `next_free_frame` + self.allocate_frame() + } else { + None // no free frames left + } + } + + fn deallocate_frame(&mut self, _frame: Frame) { + unimplemented!() + } +} + +// Fixme: no multiboot, but dtb instead with avail memory regions +// Need dtb parser here! + +impl AreaFrameAllocator { + pub fn new( + kernel_start: usize, + kernel_end: usize, + multiboot_start: usize, + multiboot_end: usize, + memory_areas: MemoryAreaIter, + ) -> AreaFrameAllocator { + let mut allocator = AreaFrameAllocator { + next_free_frame: Frame::containing_address(0), + current_area: None, + areas: memory_areas, + kernel_start: Frame::containing_address(kernel_start), + kernel_end: Frame::containing_address(kernel_end), + multiboot_start: Frame::containing_address(multiboot_start), + multiboot_end: Frame::containing_address(multiboot_end), + }; + allocator.choose_next_area(); + allocator + } + + fn choose_next_area(&mut self) { + self.current_area = self.areas + .clone() + .filter(|area| { + let address = area.base_addr + area.length - 1; + Frame::containing_address(address as usize) >= self.next_free_frame + }) + .min_by_key(|area| area.base_addr); + + if let Some(area) = self.current_area { + let start_frame = Frame::containing_address(area.base_addr as usize); + if self.next_free_frame < start_frame { + self.next_free_frame = start_frame; + } + } + } +} diff --git a/src/arch/aarch64/memory/mod.rs b/src/arch/aarch64/memory/mod.rs new file mode 100644 index 0000000..f306448 --- /dev/null +++ b/src/arch/aarch64/memory/mod.rs @@ -0,0 +1,29 @@ +pub use self::area_frame_allocator::AreaFrameAllocator; +use self::paging::PhysicalAddress; + +mod area_frame_allocator; +mod paging; + +pub const PAGE_SIZE: usize = 4096; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Frame { + number: usize, +} + +impl Frame { + fn containing_address(address: usize) -> Frame { + Frame { + number: address / PAGE_SIZE, + } + } + + fn start_address(&self) -> PhysicalAddress { + self.number * PAGE_SIZE + } +} + +pub trait FrameAllocator { + fn allocate_frame(&mut self) -> Option; + fn deallocate_frame(&mut self, frame: Frame); +} diff --git a/src/arch/aarch64/memory/paging.rs b/src/arch/aarch64/memory/paging.rs deleted file mode 100644 index 1069133..0000000 --- a/src/arch/aarch64/memory/paging.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Paging is mostly based on https://os.phil-opp.com/page-tables/ and ARM ARM - -// AArch64: -// Table D4-8-2021: check supported granule sizes, select alloc policy based on results. -// TTBR_ELx is the pdbr for specific page tables - -// Page 2068 actual page descriptor formats - diff --git a/src/arch/aarch64/memory/paging/entry.rs b/src/arch/aarch64/memory/paging/entry.rs new file mode 100644 index 0000000..84d5f54 --- /dev/null +++ b/src/arch/aarch64/memory/paging/entry.rs @@ -0,0 +1,33 @@ +use memory::Frame; + +pub struct Entry(u64); + +bitflags! { + pub struct EntryFlags: u64 { + + } +} + +impl Entry { + pub fn is_unused(&self) -> bool { + self.0 == 0 + } + + pub fn set_unused(&mut self) { + self.0 = 0; + } + + pub fn flags(&self) -> EntryFlags { + EntryFlags::from_bits_truncate(self.0) + } + + pub fn pointed_frame(&self) -> Option { + if self.flags().contains(PRESENT) { + Some(Frame::containing_address( + self.0 as usize & 0x000fffff_fffff000, + )) + } else { + None + } + } +} diff --git a/src/arch/aarch64/memory/paging/mod.rs b/src/arch/aarch64/memory/paging/mod.rs new file mode 100644 index 0000000..339f992 --- /dev/null +++ b/src/arch/aarch64/memory/paging/mod.rs @@ -0,0 +1,78 @@ +//! Some code was borrowed from [Phil Opp's Blog](https://os.phil-opp.com/page-tables/) +//! Paging is mostly based on https://os.phil-opp.com/page-tables/ and ARM ARM + +// AArch64: +// Table D4-8-2021: check supported granule sizes, select alloc policy based on results. +// TTBR_ELx is the pdbr for specific page tables + +// Page 2068 actual page descriptor formats + +/* + * With 4k page granule, a virtual address is split into 4 lookup parts + * spanning 9 bits each: + * + * _______________________________________________ + * | | | | | | | + * | signx | Lv0 | Lv1 | Lv2 | Lv3 | off | + * |_______|_______|_______|_______|_______|_______| + * 63-48 47-39 38-30 29-21 20-12 11-00 + * + * mask page size + * + * Lv0: FF8000000000 -- + * Lv1: 7FC0000000 1G + * Lv2: 3FE00000 2M + * Lv3: 1FF000 4K + * off: FFF + */ + +pub use self::entry::*; +use core::ptr::Unique; +use memory::{Frame, PAGE_SIZE}; +use self::table::{Level0, Table}; + +mod entry; +mod table; + +const ENTRY_COUNT: usize = 512; + +pub type PhysicalAddress = usize; +pub type VirtualAddress = usize; + +pub struct Page { + number: usize, +} + +impl Page { + pub fn containing_address(address: VirtualAddress) -> Page { + assert!( + address < 0x0000_8000_0000_0000 || address >= 0xffff_8000_0000_0000, + "invalid address: 0x{:x}", + address + ); + Page { + number: address / PAGE_SIZE, + } + } + + fn start_address(&self) -> usize { + self.number * PAGE_SIZE + } + + fn l0_index(&self) -> usize { + (self.number >> 27) & 0o777 + } + fn l1_index(&self) -> usize { + (self.number >> 18) & 0o777 + } + fn l2_index(&self) -> usize { + (self.number >> 9) & 0o777 + } + fn l3_index(&self) -> usize { + (self.number >> 0) & 0o777 + } +} + +pub struct ActivePageTable { + l0: Unique>, +} diff --git a/src/arch/aarch64/memory/paging/table.rs b/src/arch/aarch64/memory/paging/table.rs new file mode 100644 index 0000000..5ef323a --- /dev/null +++ b/src/arch/aarch64/memory/paging/table.rs @@ -0,0 +1,67 @@ +use core::marker::PhantomData; +use memory::paging::entry::*; +use memory::paging::ENTRY_COUNT; + +pub const L0: *mut Table = 0xffff_ffff_ffff_f000 as *mut _; + +pub struct Table { + entries: [Entry; ENTRY_COUNT], + level: PhantomData, +} + +impl Table +where + L: TableLevel, +{ + pub fn zero(&mut self) { + for entry in self.entries.iter_mut() { + entry.set_unused(); + } + } +} + +impl Index for Table +where + L: TableLevel, +{ + type Output = Entry; + + fn index(&self, index: usize) -> &Entry { + &self.entries[index] + } +} + +impl IndexMut for Table +where + L: TableLevel, +{ + fn index_mut(&mut self, index: usize) -> &mut Entry { + &mut self.entries[index] + } +} + +pub trait TableLevel {} + +pub enum Level0 {} +pub enum Level1 {} +pub enum Level2 {} +pub enum Level3 {} + +impl TableLevel for Level0 {} +impl TableLevel for Level1 {} +impl TableLevel for Level2 {} +impl TableLevel for Level3 {} + +pub trait HierarchicalLevel: TableLevel { + type NextLevel: TableLevel; +} + +impl HierarchicalLevel for Level0 { + type NextLevel = Level1; +} +impl HierarchicalLevel for Level1 { + type NextLevel = Level2; +} +impl HierarchicalLevel for Level2 { + type NextLevel = Level3; +}