Add updated paging module based on phil-opp code

This commit is contained in:
Berkus Decker 2018-02-19 14:44:39 +02:00 committed by Berkus Decker
parent e75770a335
commit ef2796a583
6 changed files with 306 additions and 8 deletions

View File

@ -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<Frame> {
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;
}
}
}
}

View File

@ -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<Frame>;
fn deallocate_frame(&mut self, frame: Frame);
}

View File

@ -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

View File

@ -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<Frame> {
if self.flags().contains(PRESENT) {
Some(Frame::containing_address(
self.0 as usize & 0x000fffff_fffff000,
))
} else {
None
}
}
}

View File

@ -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<Table<Level0>>,
}

View File

@ -0,0 +1,67 @@
use core::marker::PhantomData;
use memory::paging::entry::*;
use memory::paging::ENTRY_COUNT;
pub const L0: *mut Table<Level0> = 0xffff_ffff_ffff_f000 as *mut _;
pub struct Table<L: TableLevel> {
entries: [Entry; ENTRY_COUNT],
level: PhantomData<L>,
}
impl<L> Table<L>
where
L: TableLevel,
{
pub fn zero(&mut self) {
for entry in self.entries.iter_mut() {
entry.set_unused();
}
}
}
impl<L> Index<usize> for Table<L>
where
L: TableLevel,
{
type Output = Entry;
fn index(&self, index: usize) -> &Entry {
&self.entries[index]
}
}
impl<L> IndexMut<usize> for Table<L>
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;
}