use frame_allocator::FrameAllocator;
use x86_64::{PhysAddr, VirtAddr};
use x86_64::structures::paging::{PAGE_SIZE, PageTable, PageTableFlags, PageTableEntry, Page, PhysFrame};
use usize_conversions::usize_from;
use xmas_elf;
use xmas_elf::program::{self, ProgramHeader};
pub(crate) fn map_kernel(kernel_start: PhysAddr, elf_file: &xmas_elf::ElfFile, p4: &mut PageTable,
frame_allocator: &mut FrameAllocator) -> VirtAddr
{
for program_header in elf_file.program_iter() {
map_segment(kernel_start, program_header, p4, frame_allocator);
}
let stack_start = VirtAddr::new(0x57AC_0000_0000);
let stack_size = 1 * 1024 * 1024 / u64::from(PAGE_SIZE); let stack_end = stack_start + stack_size;
let page_size = usize_from(PAGE_SIZE);
let virt_page_iter = (stack_start..(stack_start + stack_size)).step_by(page_size);
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
for virt_page_addr in virt_page_iter {
let page = Page::containing_address(virt_page_addr);
map_page(page, frame_allocator.allocate_frame(), flags, p4, frame_allocator);
}
stack_end
}
pub(crate) fn identity_map(frame: PhysFrame, flags: PageTableFlags, p4: &mut PageTable, frame_allocator: &mut FrameAllocator) {
let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
map_page(page, frame, flags, p4, frame_allocator);
}
pub(crate) fn map_segment(kernel_start: PhysAddr, program_header: ProgramHeader, p4: &mut PageTable,
frame_allocator: &mut FrameAllocator)
{
let typ = program_header.get_type().unwrap();
match typ {
program::Type::Load => {
let offset = program_header.offset();
let phys_start_addr = kernel_start + offset;
let size = program_header.mem_size();
let virt_start_addr = {
let v = program_header.virtual_addr();
VirtAddr::new(v)
};
let flags = program_header.flags();
let mut page_table_flags = PageTableFlags::PRESENT;
if !flags.is_execute() { page_table_flags |= PageTableFlags::NO_EXECUTE };
if flags.is_write() { page_table_flags |= PageTableFlags::WRITABLE };
let page_size = usize_from(PAGE_SIZE);
let virt_page_iter = (virt_start_addr..(virt_start_addr + size)).step_by(page_size);
let phys_page_iter = (phys_start_addr..(phys_start_addr + size)).step_by(page_size);
for (virt_page_addr, phys_frame_addr) in virt_page_iter.zip(phys_page_iter) {
let page = Page::containing_address(virt_page_addr);
let frame = PhysFrame::containing_address(phys_frame_addr);
map_page(page, frame, page_table_flags, p4, frame_allocator);
}
},
_ => {},
}
}
pub(crate) fn map_page(page: Page, phys_frame: PhysFrame, flags: PageTableFlags,
p4: &mut PageTable, frame_allocator: &mut FrameAllocator)
{
fn as_page_table_ptr(addr: PhysAddr) -> *mut PageTable {
usize_from(addr.as_u64()) as *const PageTable as *mut PageTable
}
fn create_and_link_page_table(frame_allocator: &mut FrameAllocator,
parent_table_entry: &mut PageTableEntry) -> &'static mut PageTable
{
let table_frame = frame_allocator.allocate_frame();
let page_table = unsafe { &mut *as_page_table_ptr(table_frame.start_address()) };
page_table.zero();
parent_table_entry.set(table_frame, PageTableFlags::PRESENT);
page_table
}
fn get_or_create_next_page_table(frame_allocator: &mut FrameAllocator,
page_table_entry: &mut PageTableEntry) -> &'static mut PageTable
{
match page_table_entry.points_to() {
Some(addr) => unsafe { &mut *as_page_table_ptr(addr) },
None => create_and_link_page_table(frame_allocator, page_table_entry)
}
}
let virt_page_addr = page.start_address();
let p4_entry = &mut p4[virt_page_addr.p4_index()];
let p3 = get_or_create_next_page_table(frame_allocator, p4_entry);
let p3_entry = &mut p3[virt_page_addr.p3_index()];
let p2 = get_or_create_next_page_table(frame_allocator, p3_entry);
let p2_entry = &mut p2[virt_page_addr.p2_index()];
let p1 = get_or_create_next_page_table(frame_allocator, p2_entry);
let p1_entry = &mut p1[virt_page_addr.p1_index()];
assert!(p1_entry.is_unused(), "page for {:?} already in use", virt_page_addr);
p1_entry.set(phys_frame, flags);
}