|
| 1 | +#![no_std] // don't link the Rust standard library |
| 2 | +#![no_main] // disable all Rust-level entry points |
| 3 | + |
| 4 | +use bootloader_api::{ |
| 5 | + config::Mapping, entry_point, info::MemoryRegionKind, BootInfo, BootloaderConfig, |
| 6 | +}; |
| 7 | +use core::{fmt::Write, ptr::slice_from_raw_parts}; |
| 8 | +use test_kernel_ramdisk::{exit_qemu, serial, QemuExitCode, RAMDISK_CONTENTS}; |
| 9 | +use x86_64::{ |
| 10 | + structures::paging::{OffsetPageTable, PageTable, PageTableFlags, Translate}, |
| 11 | + VirtAddr, |
| 12 | +}; |
| 13 | + |
| 14 | +pub const BOOTLOADER_CONFIG: BootloaderConfig = { |
| 15 | + let mut config = BootloaderConfig::new_default(); |
| 16 | + config.mappings.physical_memory = Some(Mapping::FixedAddress(0x0000_6000_0000_0000)); |
| 17 | + config |
| 18 | +}; |
| 19 | + |
| 20 | +entry_point!(kernel_main, config = &BOOTLOADER_CONFIG); |
| 21 | + |
| 22 | +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { |
| 23 | + writeln!(serial(), "Boot info: {boot_info:?}").unwrap(); |
| 24 | + |
| 25 | + let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset.into_option().unwrap()); |
| 26 | + let level_4_table = unsafe { active_level_4_table(phys_mem_offset) }; |
| 27 | + let page_table = unsafe { OffsetPageTable::new(level_4_table, phys_mem_offset) }; |
| 28 | + |
| 29 | + let ramdisk_start_addr = VirtAddr::new(boot_info.ramdisk_addr.into_option().unwrap()); |
| 30 | + assert_eq!(boot_info.ramdisk_len as usize, RAMDISK_CONTENTS.len()); |
| 31 | + let ramdisk_end_addr = ramdisk_start_addr + boot_info.ramdisk_len; |
| 32 | + |
| 33 | + let mut next_addr = ramdisk_start_addr; |
| 34 | + while next_addr < ramdisk_end_addr { |
| 35 | + let phys_addr = match page_table.translate(next_addr) { |
| 36 | + x86_64::structures::paging::mapper::TranslateResult::Mapped { |
| 37 | + frame, |
| 38 | + offset: _, |
| 39 | + flags, |
| 40 | + } => { |
| 41 | + assert!(flags.contains(PageTableFlags::PRESENT)); |
| 42 | + assert!(flags.contains(PageTableFlags::WRITABLE)); |
| 43 | + assert!(flags.contains(PageTableFlags::NO_EXECUTE)); |
| 44 | + |
| 45 | + next_addr += frame.size(); |
| 46 | + |
| 47 | + frame.start_address() |
| 48 | + } |
| 49 | + other => panic!("invalid result: {other:?}"), |
| 50 | + }; |
| 51 | + let region = boot_info |
| 52 | + .memory_regions |
| 53 | + .iter() |
| 54 | + .find(|r| r.start <= phys_addr.as_u64() && r.end > phys_addr.as_u64()) |
| 55 | + .unwrap(); |
| 56 | + assert_eq!(region.kind, MemoryRegionKind::Bootloader); |
| 57 | + } |
| 58 | + |
| 59 | + let actual_ramdisk = unsafe { |
| 60 | + &*slice_from_raw_parts( |
| 61 | + boot_info.ramdisk_addr.into_option().unwrap() as *const u8, |
| 62 | + boot_info.ramdisk_len as usize, |
| 63 | + ) |
| 64 | + }; |
| 65 | + writeln!(serial(), "Actual contents: {actual_ramdisk:?}").unwrap(); |
| 66 | + assert_eq!(RAMDISK_CONTENTS, actual_ramdisk); |
| 67 | + |
| 68 | + exit_qemu(QemuExitCode::Success); |
| 69 | +} |
| 70 | + |
| 71 | +/// This function is called on panic. |
| 72 | +#[cfg(not(test))] |
| 73 | +#[panic_handler] |
| 74 | +fn panic(info: &core::panic::PanicInfo) -> ! { |
| 75 | + let _ = writeln!(test_kernel_ramdisk::serial(), "PANIC: {info}"); |
| 76 | + exit_qemu(QemuExitCode::Failed); |
| 77 | +} |
| 78 | + |
| 79 | +pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { |
| 80 | + use x86_64::registers::control::Cr3; |
| 81 | + |
| 82 | + let (level_4_table_frame, _) = Cr3::read(); |
| 83 | + |
| 84 | + let phys = level_4_table_frame.start_address(); |
| 85 | + let virt = physical_memory_offset + phys.as_u64(); |
| 86 | + let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); |
| 87 | + |
| 88 | + &mut *page_table_ptr // unsafe |
| 89 | +} |
0 commit comments