mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
WIP: Add paging and section remapping code
This commit is contained in:
77
src/memory/mod.rs
Normal file
77
src/memory/mod.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
use multiboot2::Multiboot;
|
||||||
|
use core::iter::range_inclusive;
|
||||||
|
use core::cmp::max;
|
||||||
|
|
||||||
|
mod paging;
|
||||||
|
|
||||||
|
pub fn init(multiboot: &Multiboot) {
|
||||||
|
// ATTENTION: we have a very small stack and no guard page
|
||||||
|
|
||||||
|
let kernel_end = multiboot.elf_tag().unwrap().sections().map(|s| s.addr + s.size).max()
|
||||||
|
.unwrap() as usize;
|
||||||
|
let multiboot_end = multiboot as *const _ as usize + multiboot.total_size as usize;
|
||||||
|
let mut allocator = FrameAllocator::new(max(kernel_end, multiboot_end));
|
||||||
|
let mut c = unsafe{paging::Controller::new(allocator)};
|
||||||
|
|
||||||
|
c.begin_new_table();
|
||||||
|
c.identity_map(Page{number: 0xb8}, true, false);
|
||||||
|
|
||||||
|
|
||||||
|
for section in multiboot.elf_tag().unwrap().sections() {
|
||||||
|
let in_memory = section.flags & 0x2 != 0;
|
||||||
|
let writable = section.flags & 0x1 != 0;
|
||||||
|
let executable = section.flags & 0x4 != 0;
|
||||||
|
if !in_memory {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!("section at 0x{:x}, allocated: {}, writable: {}, executable: {}", section.addr,
|
||||||
|
in_memory, writable, executable);
|
||||||
|
let start_page = Page::containing_address(section.addr as usize);
|
||||||
|
let end_page = Page::containing_address((section.addr + section.size) as usize);
|
||||||
|
for page in range_inclusive(start_page.number, end_page.number).map(|n| Page{number: n}) {
|
||||||
|
c.identity_map(page, writable, executable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.activate_new_table();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VirtualAddress(*const u8);
|
||||||
|
|
||||||
|
struct FrameAllocator {
|
||||||
|
next_free_frame: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameAllocator {
|
||||||
|
fn new(kernel_end: usize) -> FrameAllocator {
|
||||||
|
assert!(kernel_end > 0x100000);
|
||||||
|
FrameAllocator {
|
||||||
|
next_free_frame: ((kernel_end - 1) >> 12) + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate_frame(&mut self) -> Option<Frame> {
|
||||||
|
let page_number = self.next_free_frame;
|
||||||
|
self.next_free_frame += 1;
|
||||||
|
Some(Frame {
|
||||||
|
number: page_number
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
|
||||||
|
struct Frame {
|
||||||
|
number: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
|
||||||
|
struct Page {
|
||||||
|
number: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
fn containing_address(address: usize) -> Page {
|
||||||
|
Page {
|
||||||
|
number: address >> 12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
223
src/memory/paging.rs
Normal file
223
src/memory/paging.rs
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
use super::{VirtualAddress, Frame};
|
||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
pub type FrameAllocator = super::FrameAllocator;
|
||||||
|
pub type Page = super::Page;
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: u64 = 4096;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
flags PageTableFieldFlags: u64 {
|
||||||
|
const PRESENT = 1 << 0,
|
||||||
|
const WRITABLE = 1 << 1,
|
||||||
|
const USER_ACCESSIBLE = 1 << 2,
|
||||||
|
const WRITE_THROUGH = 1 << 3,
|
||||||
|
const NO_CACHE = 1 << 4,
|
||||||
|
const ACCESSED = 1 << 5,
|
||||||
|
const DIRTY = 1 << 6,
|
||||||
|
const OTHER1 = 1 << 9,
|
||||||
|
const OTHER2 = 1 << 10,
|
||||||
|
const NO_EXECUTE = 1 << 63,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Controller {
|
||||||
|
allocator: FrameAllocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Controller {
|
||||||
|
pub unsafe fn new(allocator: FrameAllocator) -> Controller {
|
||||||
|
Controller {
|
||||||
|
allocator: allocator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identity_map(&mut self, page: Page, writable: bool, executable: bool) {
|
||||||
|
let mut flags = PRESENT;
|
||||||
|
if writable {
|
||||||
|
flags = flags | WRITABLE;
|
||||||
|
}
|
||||||
|
if !executable {
|
||||||
|
flags = flags | NO_EXECUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
page.map_to(Frame{number: page.number}, flags, &mut self.allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn begin_new_table(&mut self) {
|
||||||
|
let new_p4_frame = self.allocator.allocate_frame().unwrap();
|
||||||
|
let new_p4_page = &mut PageTablePage(Page{number: new_p4_frame.number});
|
||||||
|
unsafe{new_p4_page.zero()};
|
||||||
|
new_p4_page.field(511).set(new_p4_frame, PRESENT | WRITABLE);
|
||||||
|
|
||||||
|
let old_p4_page = &mut PageTablePage(Page{number: 0o777_777_777_777});
|
||||||
|
old_p4_page.field(511).set(new_p4_frame, PRESENT | WRITABLE);
|
||||||
|
|
||||||
|
Self::flush_tlb();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate_new_table(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let p4_address: u64 = {
|
||||||
|
let field = *(0xfffffffffffffff8 as *const u64);
|
||||||
|
field & !0xfff
|
||||||
|
};
|
||||||
|
|
||||||
|
asm!("mov cr3, $0" :: "r"(p4_address) :: "intel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_tlb() {
|
||||||
|
unsafe{asm!("mov rax, cr3
|
||||||
|
mov cr3, rax" ::: "{rax}" : "intel")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first allocated address starts on second P4-Page
|
||||||
|
pub const FIRST_PAGE : Page = Page {
|
||||||
|
number: 0o_001_000_000_000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// a page containing a page table
|
||||||
|
struct PageTablePage(Page);
|
||||||
|
|
||||||
|
struct PageIter(Page);
|
||||||
|
|
||||||
|
struct PageTableField(*const u64);
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
fn from_address(address: &VirtualAddress) -> Page {
|
||||||
|
Page {
|
||||||
|
number: address.0 as usize >> 12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_address(&self) -> VirtualAddress {
|
||||||
|
if self.number >= 0o400_000_000_000 {
|
||||||
|
//sign extension
|
||||||
|
VirtualAddress(((self.number << 12) | 0o177777_000_000_000_000_0000) as *const u8)
|
||||||
|
} else {
|
||||||
|
VirtualAddress((self.number << 12) as *const u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p4_index(&self) -> usize {(self.number >> 27) & 0o777}
|
||||||
|
fn p3_index(&self) -> usize {(self.number >> 18) & 0o777}
|
||||||
|
fn p2_index(&self) -> usize {(self.number >> 9) & 0o777}
|
||||||
|
fn p1_index(&self) -> usize {(self.number >> 0) & 0o777}
|
||||||
|
|
||||||
|
fn p4_page(&self) -> PageTablePage {
|
||||||
|
PageTablePage(Page {
|
||||||
|
number: 0o_777_777_777_777,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn p3_page(&self) -> PageTablePage {
|
||||||
|
PageTablePage(Page {
|
||||||
|
number: 0o_777_777_777_000 | self.p4_index(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn p2_page(&self) -> PageTablePage {
|
||||||
|
PageTablePage(Page {
|
||||||
|
number: 0o_777_777_000_000 | (self.p4_index() << 9) | self.p3_index(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn p1_page(&self) -> PageTablePage {
|
||||||
|
PageTablePage(Page {
|
||||||
|
number: 0o_777_000_000_000 | (self.p4_index() << 18) | (self.p3_index() << 9)
|
||||||
|
| self.p2_index(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_to(&self, frame: Frame, flags: PageTableFieldFlags, allocator: &mut FrameAllocator) {
|
||||||
|
let p4_field = self.p4_page().field(self.p4_index());
|
||||||
|
if p4_field.is_free() {
|
||||||
|
p4_field.set(allocator.allocate_frame().expect("no frame allocated"), PRESENT | WRITABLE);
|
||||||
|
unsafe{self.p3_page().zero()};
|
||||||
|
}
|
||||||
|
let p3_field = self.p3_page().field(self.p3_index());
|
||||||
|
if p3_field.is_free() {
|
||||||
|
p3_field.set(allocator.allocate_frame().expect("no frame allocated"), PRESENT | WRITABLE);
|
||||||
|
unsafe{self.p2_page().zero()};
|
||||||
|
}
|
||||||
|
let p2_field = self.p2_page().field(self.p2_index());
|
||||||
|
if p2_field.is_free() {
|
||||||
|
p2_field.set(allocator.allocate_frame().expect("no frame allocated"), PRESENT | WRITABLE);
|
||||||
|
unsafe{self.p1_page().zero()};
|
||||||
|
}
|
||||||
|
let p1_field = self.p1_page().field(self.p1_index());
|
||||||
|
//TODOassert!(p1_field.is_free());
|
||||||
|
p1_field.set(frame, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn zero(&self) {
|
||||||
|
let page = self.start_address().0 as *mut [u64; (PAGE_SIZE/64) as usize];
|
||||||
|
*page = [0; (PAGE_SIZE/64) as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_pages(self) -> PageIter {
|
||||||
|
PageIter(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PageIter {
|
||||||
|
type Item = Page;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Page> {
|
||||||
|
self.0.number += 1;
|
||||||
|
Some(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTablePage {
|
||||||
|
fn field(&self, index: usize) -> PageTableField {
|
||||||
|
//print!("index: {} pointer: {:o}\n", index, self.0.start_address().0 as usize + (index * 8));
|
||||||
|
PageTableField((self.0.start_address().0 as usize + (index * 8)) as *const u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for PageTablePage {
|
||||||
|
type Target = Page;
|
||||||
|
fn deref(&self) -> &Page { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtualAddress {
|
||||||
|
pub fn page(&self) -> Page {
|
||||||
|
Page::from_address(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_offset(&self) -> u32 {
|
||||||
|
self.0 as u32 & 0xfff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTableField {
|
||||||
|
fn is(&self, flags: PageTableFieldFlags) -> bool {
|
||||||
|
PageTableFieldFlags::from_bits_truncate(unsafe{*(self.0)}).contains(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_flag(&self, flags: PageTableFieldFlags) {
|
||||||
|
unsafe{*(self.0 as *mut u64) |= flags.bits};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_flag(&self, flags: PageTableFieldFlags) {
|
||||||
|
unsafe{*(self.0 as *mut u64) &= !flags.bits};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_free(&self) -> bool {
|
||||||
|
//TODO
|
||||||
|
let f = unsafe{*self.0};
|
||||||
|
let free = f == 0;
|
||||||
|
free
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointed_frame(&self) -> Frame {
|
||||||
|
Frame {
|
||||||
|
number: ((unsafe{(*self.0)} & 0x000fffff_fffff000) >> 12) as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&self, frame: Frame, flags: PageTableFieldFlags) {
|
||||||
|
let new = (((frame.number as u64) << 12) & 0x000fffff_fffff000) | flags.bits();
|
||||||
|
unsafe{*(self.0 as *mut u64) = new};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user