From 82ebfc839a5ebde76a330dbef9daf61b9badd17f Mon Sep 17 00:00:00 2001 From: Mathieu Strypsteen Date: Sat, 6 Jul 2024 11:00:25 +0200 Subject: [PATCH] Improve paging --- kernel/Cargo.lock | 7 ++++ kernel/Cargo.toml | 1 + kernel/link.ld | 1 + kernel/src/cpu/gdt.rs | 8 +++- kernel/src/main.rs | 2 +- loader/src/elf.rs | 22 +++++++++- loader/src/main.rs | 9 ++-- loader/src/paging.rs | 97 ++++++++++++++++++++++++------------------- 8 files changed, 96 insertions(+), 51 deletions(-) diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index cd775fc..a9a16fa 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -8,6 +8,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" +[[package]] +name = "bitfield" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1" + [[package]] name = "bitvec" version = "1.0.1" @@ -38,6 +44,7 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" name = "kernel" version = "0.0.1" dependencies = [ + "bitfield", "bitvec", "com_logger", "log", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index b8b3780..3b24351 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" edition = "2021" [dependencies] +bitfield = "0.15.0" bitvec = {version = "1.0.1", default-features = false, features = ["alloc", "atomic"]} com_logger = { git = "https://git.strypsteen.com/mathieu/com_logger", version = "0.1.1" } log = "0.4.22" diff --git a/kernel/link.ld b/kernel/link.ld index 7cb4add..81c1ad2 100644 --- a/kernel/link.ld +++ b/kernel/link.ld @@ -8,6 +8,7 @@ SECTIONS { } .data ALIGN(0x1000) : { *(.data*) + *(.got) } .bss ALIGN(0x1000) : { *(.bss*) diff --git a/kernel/src/cpu/gdt.rs b/kernel/src/cpu/gdt.rs index a44efc5..68b0b3d 100644 --- a/kernel/src/cpu/gdt.rs +++ b/kernel/src/cpu/gdt.rs @@ -22,7 +22,7 @@ struct GDTDescriptor { base: u64, } -const GDT: GDT = GDT { +static mut GDT: GDT = GDT { entries: [ GDTEntry { limit_low: 0, @@ -52,9 +52,13 @@ const GDT: GDT = GDT { }; pub fn setup_gdt() { + let ptr; + unsafe { + ptr = GDT.entries.as_ptr() as u64; + } let gdt_descriptor = Box::leak(Box::new(GDTDescriptor { limit: (size_of::() - 1) as u16, - base: GDT.entries.as_ptr() as u64, + base: ptr, })); unsafe { asm!(" diff --git a/kernel/src/main.rs b/kernel/src/main.rs index ae9b04c..3e3f56e 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -17,7 +17,7 @@ mod cpu; mod misc; #[global_allocator] -static A: Bump<[u8; 8 * 1024 * 1024]> = Bump::uninit(); +static ALLOC: Bump<[u8; 8 * 1024 * 1024]> = Bump::uninit(); global_asm!( " diff --git a/loader/src/elf.rs b/loader/src/elf.rs index 165eb8c..0ea7cd7 100644 --- a/loader/src/elf.rs +++ b/loader/src/elf.rs @@ -6,9 +6,11 @@ use uefi::table::{ Boot, SystemTable, }; +use crate::paging::{map, PageTable}; + pub const KERNEL_VIRT_START: u64 = 0xffffffff80000000; -pub fn load_kernel(kernel: &[u8], system_table: &SystemTable) -> (u64, usize, u64) { +pub fn load_kernel(kernel: &[u8], system_table: &SystemTable) -> (u64, u64) { let file: ElfBytes = ElfBytes::minimal_parse(kernel).unwrap(); let mut kernel_size = 0; for i in file.segments().unwrap() { @@ -18,6 +20,9 @@ pub fn load_kernel(kernel: &[u8], system_table: &SystemTable) -> (u64, usi if i.p_vaddr < KERNEL_VIRT_START { panic!("Invalid kernel segments") } + if i.p_vaddr % 0x1000 != 0 { + panic!("Invalid alignment"); + } let end_addr = i.p_vaddr - KERNEL_VIRT_START + i.p_memsz; if end_addr > kernel_size { kernel_size = end_addr; @@ -37,5 +42,18 @@ pub fn load_kernel(kernel: &[u8], system_table: &SystemTable) -> (u64, usi ptr::copy(kernel[i.p_offset.try_into().unwrap()..].as_ptr(), start_addr as *mut u8, i.p_filesz.try_into().unwrap()); } } - (phys_start, kernel_size as usize, file.ehdr.e_entry) + (phys_start, file.ehdr.e_entry) +} +pub fn map_kernel(kernel: &[u8], pml4: &mut PageTable, kernel_start: u64) { + let file: ElfBytes = ElfBytes::minimal_parse(kernel).unwrap(); + for i in file.segments().unwrap() { + if i.p_type != PT_LOAD { + continue; + } + for j in 0..(i.p_memsz + 0xfff) / 0x1000 { + let virt = i.p_vaddr + j * 0x1000; + let phys = virt - KERNEL_VIRT_START + kernel_start; + map(pml4, virt, phys, i.p_flags & 2 != 0, i.p_flags & 1 != 0); + } + } } diff --git a/loader/src/main.rs b/loader/src/main.rs index a6def14..312d8dd 100644 --- a/loader/src/main.rs +++ b/loader/src/main.rs @@ -5,7 +5,7 @@ extern crate alloc; use core::{arch::asm, mem, panic::PanicInfo}; -use elf::load_kernel; +use elf::{load_kernel, map_kernel}; use loader_struct::{generate_loader_struct, LoaderStruct}; use log::{error, info}; use paging::setup_paging; @@ -21,7 +21,7 @@ mod loader_struct; mod paging; #[global_allocator] -static A: Bump<[u8; 8 * 1024 * 1024]> = Bump::uninit(); +static ALLOC: Bump<[u8; 8 * 1024 * 1024]> = Bump::uninit(); const KERNEL: &[u8] = include_bytes!("../../kernel/target/x86_64-unknown-none/release/kernel"); @@ -30,12 +30,13 @@ fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { com_logger::init(); info!("Starting bootloader..."); uefi::helpers::init(&mut system_table).unwrap(); - let (kernel_start, kernel_size, kernel_entry) = load_kernel(KERNEL, &system_table); + let (kernel_start, kernel_entry) = load_kernel(KERNEL, &system_table); let memory_map; unsafe { memory_map = system_table.exit_boot_services(MemoryType::LOADER_DATA).1; } - setup_paging(&memory_map, kernel_start, kernel_size); + let pml4 = setup_paging(&memory_map); + map_kernel(KERNEL, pml4, kernel_start); let loader_struct = generate_loader_struct(&memory_map); info!("Jumping to kernel..."); unsafe { diff --git a/loader/src/paging.rs b/loader/src/paging.rs index 373f156..f83bf3d 100644 --- a/loader/src/paging.rs +++ b/loader/src/paging.rs @@ -4,76 +4,89 @@ use alloc::boxed::Box; use bitfield::bitfield; use uefi::table::boot::{MemoryMap, MemoryType}; -use crate::elf; - bitfield! { #[derive(Clone, Copy)] struct PageEntry(u64); - present, set_present: 1, 0; - write, set_write: 2, 1; - user, set_user: 3, 2; - write_through, set_write_through: 4, 3; - cache_disable, set_cache_disable: 5, 4; - accessed, set_accessed: 6, 5; - dirty, set_dirty: 7, 6; - page_size, set_page_size: 8, 7; - global, set_global: 9, 8; + present, set_present: 0, 0; + write, set_write: 1, 1; + user, set_user: 2, 2; + write_through, set_write_through: 3, 3; + cache_disable, set_cache_disable: 4, 4; + accessed, set_accessed: 5, 5; + dirty, set_dirty: 6, 6; + page_size, set_page_size: 7, 7; + global, set_global: 8, 8; available, set_available: 11, 9; - address, set_address: 63, 12; + address, set_address: 62, 12; + execute_disable, set_execute_disable: 63, 63; } - #[repr(C, align(0x1000))] -struct PageTable { - entries: [PageEntry; 512], +pub struct PageTable { + entries_phys: [PageEntry; 512], + entries_virt: [Option>; 512], } fn get_table_entry(table: &mut PageTable, i: usize) -> &mut PageTable { - if table.entries[i].present() == 0 { - let new_table = Box::leak(Box::new(PageTable { entries: [PageEntry(0); 512] })); - table.entries[i].set_address(new_table as *const PageTable as u64 / 0x1000); - table.entries[i].set_present(1); - table.entries[i].set_write(1); - } - let ptr = (table.entries[i].address() * 0x1000) as *mut PageTable; - unsafe { - return ptr.as_mut().unwrap(); + if table.entries_virt[i].is_none() { + const NONE: Option> = None; + let new_table = Box::new(PageTable { + entries_phys: [PageEntry(0); 512], + entries_virt: [NONE; 512], + }); + table.entries_phys[i].set_address(new_table.as_ref() as *const PageTable as u64 / 0x1000); + table.entries_virt[i] = Some(new_table); + table.entries_phys[i].set_write(1); + table.entries_phys[i].set_present(1); } + return table.entries_virt[i].as_mut().unwrap(); } -fn map(pml4: &mut PageTable, mut virt: usize, phys: usize) { - virt /= 0x1000; - let table_i = virt % 512; - let directory_i = virt / 512 % 512; - let pdpt_i = virt / 512 / 512 % 512; - let pml4_i = virt / 512 / 512 / 512 % 512; +pub fn map(pml4: &mut PageTable, virt: u64, phys: u64, write: bool, exec: bool) { + let virt_page = virt as usize / 0x1000; + let table_i = virt_page % 512; + let directory_i = virt_page / 512 % 512; + let pdpt_i = virt_page / 512 / 512 % 512; + let pml4_i = virt_page / 512 / 512 / 512 % 512; let pdpt = get_table_entry(pml4, pml4_i); let directory = get_table_entry(pdpt, pdpt_i); let table = get_table_entry(directory, directory_i); - table.entries[table_i].set_address(phys as u64 / 0x1000); - table.entries[table_i].set_present(1); - table.entries[table_i].set_write(1); + table.entries_phys[table_i].set_address(phys / 0x1000); + table.entries_phys[table_i].set_write(write as u64); + table.entries_phys[table_i].set_execute_disable(!exec as u64); + table.entries_phys[table_i].set_present(1); } -pub fn setup_paging(memory_map: &MemoryMap, kernel_start: u64, kernel_size: usize) { - let pml4 = Box::leak(Box::new(PageTable { entries: [PageEntry(0); 512] })); +pub fn setup_paging(memory_map: &MemoryMap) -> &mut PageTable { + const NONE: Option> = None; + let pml4 = Box::leak(Box::new(PageTable { + entries_phys: [PageEntry(0); 512], + entries_virt: [NONE; 512], + })); for i in memory_map.entries() { if i.ty == MemoryType::LOADER_CODE || i.ty == MemoryType::LOADER_DATA || i.ty == MemoryType::RUNTIME_SERVICES_CODE || i.ty == MemoryType::RUNTIME_SERVICES_DATA - || i.ty == MemoryType::BOOT_SERVICES_CODE || i.ty == MemoryType::BOOT_SERVICES_DATA { for j in 0..i.page_count { let addr = i.phys_start + j * 0x1000; - map(pml4, addr as usize, addr as usize); + let mut write = true; + let mut exec = false; + if i.ty == MemoryType::LOADER_CODE || i.ty == MemoryType::RUNTIME_SERVICES_CODE { + exec = true; + } + if i.ty == MemoryType::RUNTIME_SERVICES_CODE { + write = false; + } + map(pml4, addr, addr, write, exec); } } } - for i in 0..((kernel_size + 0xfff) / 0x1000) { - let virt = elf::KERNEL_VIRT_START as usize + i * 0x1000; - let phys = kernel_start as usize + i * 0x1000; - map(pml4, virt, phys); - } unsafe { asm!("mov cr3, {}", in(reg) pml4 as *const PageTable as u64); + // Write Protect + asm!("mov rax, cr0; bts rax, 16; mov cr0, rax", out("rax") _); + // No-Execute Enable + asm!("rdmsr; bts rax, 11; wrmsr", in("rcx") 0xc0000080 as u64, out("rax") _, out("rdx") _); } + pml4 }