Add process page table (#454)

* Refactor kernel page table

* Add mapper and frame_allocator

* Set physical memory offset in bootloader crate

* Load process page table

* Set stack and boot-info addresses

* Fix alloc error

* Remove unused code

* Add comment
This commit is contained in:
Vincent Ollivier 2023-11-18 09:34:06 +01:00 committed by GitHub
parent e43d2e3c71
commit bcdfd713ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 45 deletions

View File

@ -42,5 +42,10 @@ uart_16550 = "0.2.19"
vte = "0.13.0"
x86_64 = "0.14.11"
[package.metadata.bootloader]
physical-memory-offset = "0xFFFF800000000000"
kernel-stack-address = "0xFFFFFF8000000000"
boot-info-address = "0xFFFFFFFF80000000"
[package.metadata.bootimage]
test-success-exit-code = 33 # (0x10 << 1) | 1

View File

@ -8,6 +8,7 @@ use core::cmp;
use core::ops::{Index, IndexMut};
use linked_list_allocator::LockedHeap;
use spin::Mutex;
use x86_64::structures::paging::OffsetPageTable;
use x86_64::structures::paging::mapper::MapToError;
use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB};
use x86_64::VirtAddr;
@ -21,7 +22,10 @@ fn max_memory() -> u64 {
option_env!("MOROS_MEMORY").unwrap_or("32").parse::<u64>().unwrap() << 20 // MB
}
pub fn init_heap(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Result<(), MapToError<Size4KiB>> {
pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
let mapper = sys::mem::mapper();
let mut frame_allocator = sys::mem::frame_allocator();
// Use half of the memory for the heap caped to 16MB by default because the
// allocator is slow.
let heap_size = cmp::min(sys::mem::memory_size(), max_memory()) / 2;
@ -39,7 +43,7 @@ pub fn init_heap(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut impl
for page in pages {
let frame = frame_allocator.allocate_frame().ok_or(MapToError::FrameAllocationFailed)?;
unsafe {
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
mapper.map_to(page, frame, flags, &mut frame_allocator)?.flush();
}
}
@ -50,10 +54,9 @@ pub fn init_heap(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut impl
Ok(())
}
pub fn alloc_pages(addr: u64, size: usize) -> Result<(), ()> {
pub fn alloc_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) -> Result<(), ()> {
//debug!("Alloc pages (addr={:#x}, size={})", addr, size);
let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(sys::mem::PHYS_MEM_OFFSET)) };
let mut frame_allocator = unsafe { sys::mem::BootInfoFrameAllocator::init(sys::mem::MEMORY_MAP.unwrap()) };
let mut frame_allocator = sys::mem::frame_allocator();
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE;
let pages = {
let start_page = Page::containing_address(VirtAddr::new(addr));
@ -84,8 +87,7 @@ pub fn alloc_pages(addr: u64, size: usize) -> Result<(), ()> {
use x86_64::structures::paging::page::PageRangeInclusive;
// TODO: Replace `free` by `dealloc`
pub fn free_pages(addr: u64, size: usize) {
let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(sys::mem::PHYS_MEM_OFFSET)) };
pub fn free_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) {
let pages: PageRangeInclusive<Size4KiB> = {
let start_page = Page::containing_address(VirtAddr::new(addr));
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));

View File

@ -9,6 +9,8 @@ use x86_64::instructions::interrupts;
use x86_64::instructions::port::Port;
use x86_64::registers::control::Cr2;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue, PageFaultErrorCode};
use x86_64::structures::paging::OffsetPageTable;
use x86_64::VirtAddr;
const PIC1: u16 = 0x21;
const PIC2: u16 = 0xA1;
@ -110,7 +112,12 @@ extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame,
extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode) {
//debug!("EXCEPTION: PAGE FAULT ({:?})", error_code);
let addr = Cr2::read().as_u64();
if sys::allocator::alloc_pages(addr, 1).is_err() {
let page_table = unsafe { sys::process::page_table() };
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
if sys::allocator::alloc_pages(&mut mapper, addr, 1).is_err() {
let csi_color = api::console::Style::color("LightRed");
let csi_reset = api::console::Style::reset();
printk!("{}Error:{} Could not allocate address {:#x}\n", csi_color, csi_reset, addr);
@ -161,7 +168,7 @@ macro_rules! wrap {
"push r11",
"mov rsi, rsp", // Arg #2: register list
"mov rdi, rsp", // Arg #1: interupt frame
"add rdi, 9 * 8",
"add rdi, 9 * 8", // 9 registers * 8 bytes
"call {}",
"pop r11",
"pop r10",

View File

@ -6,9 +6,10 @@ use x86_64::registers::control::Cr3;
use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, Translate};
use x86_64::{PhysAddr, VirtAddr};
// NOTE: mutable but changed only once during initialization
pub static mut PHYS_MEM_OFFSET: u64 = 0;
pub static mut PHYS_MEM_OFFSET: Option<u64> = None;
pub static mut MEMORY_MAP: Option<&MemoryMap> = None;
pub static mut MAPPER: Option<OffsetPageTable<'static>> = None;
pub static MEMORY_SIZE: AtomicU64 = AtomicU64::new(0);
static ALLOCATED_FRAMES: AtomicUsize = AtomicUsize::new(0);
@ -25,41 +26,45 @@ pub fn init(boot_info: &'static BootInfo) {
log!("MEM {} KB\n", memory_size >> 10);
MEMORY_SIZE.store(memory_size, Ordering::Relaxed);
unsafe { PHYS_MEM_OFFSET = boot_info.physical_memory_offset };
let phys_mem_offset = boot_info.physical_memory_offset;
unsafe { PHYS_MEM_OFFSET.replace(phys_mem_offset) };
unsafe { MEMORY_MAP.replace(&boot_info.memory_map) };
unsafe { MAPPER.replace(OffsetPageTable::new(active_page_table(), VirtAddr::new(phys_mem_offset))) };
let mut mapper = unsafe { mapper(VirtAddr::new(PHYS_MEM_OFFSET)) };
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) };
sys::allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
sys::allocator::init_heap().expect("heap initialization failed");
});
}
pub fn mapper() -> &'static mut OffsetPageTable<'static> {
unsafe { sys::mem::MAPPER.as_mut().unwrap() }
}
pub fn memory_size() -> u64 {
MEMORY_SIZE.load(Ordering::Relaxed)
}
pub fn phys_to_virt(addr: PhysAddr) -> VirtAddr {
VirtAddr::new(addr.as_u64() + unsafe { PHYS_MEM_OFFSET })
let phys_mem_offset = unsafe { PHYS_MEM_OFFSET.unwrap() };
VirtAddr::new(addr.as_u64() + phys_mem_offset)
}
pub fn virt_to_phys(addr: VirtAddr) -> Option<PhysAddr> {
let mapper = unsafe { mapper(VirtAddr::new(PHYS_MEM_OFFSET)) };
mapper.translate_addr(addr)
mapper().translate_addr(addr)
}
pub unsafe fn mapper(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> {
let level_4_table = active_level_4_table(physical_memory_offset);
OffsetPageTable::new(level_4_table, physical_memory_offset)
pub unsafe fn active_page_table() -> &'static mut PageTable {
let (frame, _) = Cr3::read();
let phys_addr = frame.start_address();
let virt_addr = phys_to_virt(phys_addr);
let page_table_ptr: *mut PageTable = virt_addr.as_mut_ptr();
&mut *page_table_ptr // unsafe
}
unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
let (level_4_table_frame, _) = Cr3::read();
let phys = level_4_table_frame.start_address();
let virt = physical_memory_offset + phys.as_u64();
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
pub unsafe fn create_page_table(frame: PhysFrame) -> &'static mut PageTable {
let phys_addr = frame.start_address();
let virt_addr = phys_to_virt(phys_addr);
let page_table_ptr: *mut PageTable = virt_addr.as_mut_ptr();
&mut *page_table_ptr // unsafe
}
@ -91,3 +96,7 @@ unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
self.usable_frames().nth(next)
}
}
pub fn frame_allocator() -> BootInfoFrameAllocator {
unsafe { BootInfoFrameAllocator::init(MEMORY_MAP.unwrap()) }
}

View File

@ -12,6 +12,11 @@ use lazy_static::lazy_static;
use object::{Object, ObjectSegment};
use spin::RwLock;
use x86_64::structures::idt::InterruptStackFrameValue;
use x86_64::registers::control::Cr3;
use x86_64::structures::paging::FrameAllocator;
use x86_64::structures::paging::OffsetPageTable;
use x86_64::structures::paging::PhysFrame;
use x86_64::structures::paging::PageTable;
const MAX_HANDLES: usize = 64;
const MAX_PROCS: usize = 2; // TODO: Update this when more than one process can run at once
@ -183,9 +188,29 @@ pub fn set_stack_frame(stack_frame: InterruptStackFrameValue) {
pub fn exit() {
let table = PROCESS_TABLE.read();
let proc = &table[id()];
sys::allocator::free_pages(proc.code_addr, MAX_PROC_SIZE);
let page_table = unsafe { sys::mem::create_page_table(proc.page_table_frame) };
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
sys::allocator::free_pages(&mut mapper, proc.code_addr, MAX_PROC_SIZE);
MAX_PID.fetch_sub(1, Ordering::SeqCst);
set_id(0); // FIXME: No process manager so we switch back to process 0
unsafe {
let (_, flags) = Cr3::read();
Cr3::write(page_table_frame(), flags);
}
}
unsafe fn page_table_frame() -> PhysFrame {
let table = PROCESS_TABLE.read();
let proc = &table[id()];
proc.page_table_frame
}
pub unsafe fn page_table() -> &'static mut PageTable {
sys::mem::create_page_table(page_table_frame())
}
/************************
@ -209,7 +234,7 @@ pub fn init_process_addr(addr: u64) {
#[repr(align(8), C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Registers {
pub struct Registers { // Saved scratch registers
pub r11: usize,
pub r10: usize,
pub r9: usize,
@ -229,7 +254,8 @@ pub struct Process {
id: usize,
code_addr: u64,
stack_addr: u64,
entry_point: u64,
entry_point_addr: u64,
page_table_frame: PhysFrame,
stack_frame: InterruptStackFrameValue,
registers: Registers,
data: ProcessData,
@ -248,8 +274,9 @@ impl Process {
id,
code_addr: 0,
stack_addr: 0,
entry_point: 0,
entry_point_addr: 0,
stack_frame: isf,
page_table_frame: Cr3::read().0,
registers: Registers::default(),
data: ProcessData::new("/", None),
}
@ -269,18 +296,32 @@ impl Process {
}
fn create(bin: &[u8]) -> Result<usize, ()> {
let page_table_frame = sys::mem::frame_allocator().allocate_frame().expect("frame allocation failed");
let page_table = unsafe { sys::mem::create_page_table(page_table_frame) };
let kernel_page_table = unsafe { sys::mem::active_page_table() };
// FIXME: for now we just copy everything
for (user_page, kernel_page) in page_table.iter_mut().zip(kernel_page_table.iter()) {
*user_page = kernel_page.clone();
}
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
let proc_size = MAX_PROC_SIZE as u64;
let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst);
let stack_addr = code_addr + proc_size;
//debug!("code_addr: {:#x}", code_addr);
//debug!("stack_addr: {:#x}", stack_addr);
let mut entry_point = 0;
let mut entry_point_addr = 0;
let code_ptr = code_addr as *mut u8;
let code_size = bin.len();
sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).expect("proc mem alloc");
if bin[0..4] == ELF_MAGIC { // ELF binary
if let Ok(obj) = object::File::parse(bin) {
//sys::allocator::alloc_pages(code_addr, proc_size as usize).expect("proc mem alloc");
entry_point = obj.entry();
entry_point_addr = obj.entry();
sys::allocator::alloc_pages(&mut mapper, code_addr + entry_point_addr, code_size).expect("proc mem alloc");
for segment in obj.segments() {
let addr = segment.address() as usize;
if let Ok(data) = segment.data() {
@ -292,7 +333,7 @@ impl Process {
}
}
} else if bin[0..4] == BIN_MAGIC { // Flat binary
//sys::allocator::alloc_pages(code_addr, proc_size as usize).expect("proc mem alloc");
//sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).expect("proc mem alloc");
for (i, b) in bin.iter().skip(4).enumerate() {
unsafe { core::ptr::write(code_ptr.add(i), *b) };
}
@ -301,8 +342,8 @@ impl Process {
}
let parent = {
let table = PROCESS_TABLE.read();
table[id()].clone()
let process_table = PROCESS_TABLE.read();
process_table[id()].clone()
};
let data = parent.data.clone();
@ -310,19 +351,24 @@ impl Process {
let stack_frame = parent.stack_frame;
let id = MAX_PID.fetch_add(1, Ordering::SeqCst);
let proc = Process { id, code_addr, stack_addr, entry_point, data, stack_frame, registers };
let proc = Process {
id, code_addr, stack_addr, entry_point_addr, page_table_frame, data, stack_frame, registers
};
let mut table = PROCESS_TABLE.write();
table[id] = Box::new(proc);
let mut process_table = PROCESS_TABLE.write();
process_table[id] = Box::new(proc);
Ok(id)
}
// Switch to user mode and execute the program
fn exec(&self, args_ptr: usize, args_len: usize) {
let page_table = unsafe { sys::process::page_table() };
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2;
//debug!("heap_addr: {:#x}", heap_addr);
sys::allocator::alloc_pages(heap_addr, 1).expect("proc heap alloc");
sys::allocator::alloc_pages(&mut mapper, heap_addr, 1).expect("proc heap alloc");
let args_ptr = ptr_from_addr(args_ptr as u64) as usize;
let args: &[&str] = unsafe { core::slice::from_raw_parts(args_ptr as *const &str, args_len) };
@ -348,7 +394,11 @@ impl Process {
let args_ptr = args.as_ptr() as u64;
set_id(self.id); // Change PID
unsafe {
let (_, flags) = Cr3::read();
Cr3::write(self.page_table_frame, flags);
asm!(
"cli", // Disable interrupts
"push {:r}", // Stack segment (SS)
@ -360,7 +410,7 @@ impl Process {
in(reg) GDT.1.user_data.0,
in(reg) self.stack_addr,
in(reg) GDT.1.user_code.0,
in(reg) self.code_addr + self.entry_point,
in(reg) self.code_addr + self.entry_point_addr,
in("rdi") args_ptr,
in("rsi") args_len,
);