From 3b383d319ff2e02bf00f23d217f21d6392a59df5 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 21:56:43 -0500 Subject: [PATCH] Guard the lower 1MB of memory This also adds a new fuction to ignore the attempts to guard the lower 1MB if it's really required. --- Cargo.lock | 10 +++++ Cargo.toml | 4 +- api/src/info.rs | 4 +- common/src/legacy_memory_region.rs | 29 +++++++++--- tests/lower_memory_free.rs | 7 +++ .../test_kernels/lower_memory_free/Cargo.toml | 13 ++++++ .../src/bin/lower_memory_free.rs | 44 +++++++++++++++++++ .../test_kernels/lower_memory_free/src/lib.rs | 29 ++++++++++++ 8 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 tests/lower_memory_free.rs create mode 100644 tests/test_kernels/lower_memory_free/Cargo.toml create mode 100644 tests/test_kernels/lower_memory_free/src/bin/lower_memory_free.rs create mode 100644 tests/test_kernels/lower_memory_free/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 415b0b55..86ae1d38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,7 @@ dependencies = [ "tempfile", "test_kernel_default_settings", "test_kernel_higher_half", + "test_kernel_lower_memory_free", "test_kernel_map_phys_mem", "test_kernel_pie", "test_kernel_ramdisk", @@ -571,6 +572,15 @@ dependencies = [ "x86_64", ] +[[package]] +name = "test_kernel_lower_memory_free" +version = "0.1.0" +dependencies = [ + "bootloader_api", + "uart_16550", + "x86_64", +] + [[package]] name = "test_kernel_lto" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 335407b2..c13f7b89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ members = [ "tests/test_kernels/higher_half", "tests/test_kernels/pie", "tests/test_kernels/lto", - "tests/test_kernels/ramdisk" + "tests/test_kernels/ramdisk", + "tests/test_kernels/lower_memory_free" ] exclude = ["examples/basic", "examples/test_framework"] @@ -57,6 +58,7 @@ test_kernel_higher_half = { path = "tests/test_kernels/higher_half", artifact = test_kernel_map_phys_mem = { path = "tests/test_kernels/map_phys_mem", artifact = "bin", target = "x86_64-unknown-none" } test_kernel_pie = { path = "tests/test_kernels/pie", artifact = "bin", target = "x86_64-unknown-none" } test_kernel_ramdisk = { path = "tests/test_kernels/ramdisk", artifact = "bin", target = "x86_64-unknown-none" } +test_kernel_lower_memory_free = { path = "tests/test_kernels/lower_memory_free", artifact = "bin", target = "x86_64-unknown-none" } [profile.dev] panic = "abort" diff --git a/api/src/info.rs b/api/src/info.rs index 771948de..14aab0ba 100644 --- a/api/src/info.rs +++ b/api/src/info.rs @@ -149,12 +149,12 @@ impl MemoryRegion { #[non_exhaustive] #[repr(C)] pub enum MemoryRegionKind { - /// Unused conventional memory, can be used by the kernel. - Usable, /// Memory mappings created by the bootloader, including the page table and boot info mappings. /// /// This memory should _not_ be used by the kernel. Bootloader, + /// Unused conventional memory, can be used by the kernel. + Usable, /// An unknown memory region reported by the UEFI firmware. /// /// Contains the UEFI memory type tag. diff --git a/common/src/legacy_memory_region.rs b/common/src/legacy_memory_region.rs index 17999c0b..ca981e9b 100644 --- a/common/src/legacy_memory_region.rs +++ b/common/src/legacy_memory_region.rs @@ -28,8 +28,11 @@ pub struct LegacyFrameAllocator { memory_map: I, current_descriptor: Option, next_frame: PhysFrame, + start_frame: PhysFrame, } +const LOWER_MEMORY_END_PAGE: u64 = 0x10_0000; + impl LegacyFrameAllocator where I: ExactSizeIterator + Clone, @@ -42,18 +45,32 @@ where /// library assumes that references can never point to virtual address `0`. pub fn new(memory_map: I) -> Self { // skip frame 0 because the rust core library does not see 0 as a valid address - let start_frame = PhysFrame::containing_address(PhysAddr::new(0x1000)); - Self::new_starting_at(start_frame, memory_map) + // also skip the first 1MB of frames, there are use cases that require lower conventional memory access. (Such as SMP SIPI) + let start_frame = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE)); + unsafe { Self::unsafe_new_starting_at(start_frame, memory_map) } } /// Creates a new frame allocator based on the given legacy memory regions. Skips any frames - /// before the given `frame`. + /// before the given `frame`, or 0x100000, whichever is higher. pub fn new_starting_at(frame: PhysFrame, memory_map: I) -> Self { + if frame.start_address().as_u64() < LOWER_MEMORY_END_PAGE { + // Skip the first 1MB of frames, regardless of what was requested. + // there are use cases that require lower conventional memory access. (Such as SMP SIPI) + return Self::new(memory_map); + } + + return unsafe { Self::unsafe_new_starting_at(frame, memory_map) }; + } + + /// Creates a new frame allocator based on the given legacy memory regions. Skips any frames + /// before the given `frame`, without attempting to protect the lower 1MB of memory. + pub unsafe fn unsafe_new_starting_at(frame: PhysFrame, memory_map: I) -> Self { Self { original: memory_map.clone(), memory_map, current_descriptor: None, next_frame: frame, + start_frame: frame, } } @@ -121,9 +138,11 @@ where let next_free = self.next_frame.start_address(); let kind = match descriptor.kind() { MemoryRegionKind::Usable => { - if end <= next_free { + if end <= next_free && end > self.start_frame.start_address() { MemoryRegionKind::Bootloader - } else if descriptor.start() >= next_free { + } else if descriptor.start() >= next_free + || end <= self.start_frame.start_address() + { MemoryRegionKind::Usable } else { // part of the region is used -> add it separately diff --git a/tests/lower_memory_free.rs b/tests/lower_memory_free.rs new file mode 100644 index 00000000..aedaf061 --- /dev/null +++ b/tests/lower_memory_free.rs @@ -0,0 +1,7 @@ +use bootloader_test_runner::run_test_kernel; +#[test] +fn lower_memory_free() { + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_LOWER_MEMORY_FREE_lower_memory_free" + )); +} diff --git a/tests/test_kernels/lower_memory_free/Cargo.toml b/tests/test_kernels/lower_memory_free/Cargo.toml new file mode 100644 index 00000000..d2af9d17 --- /dev/null +++ b/tests/test_kernels/lower_memory_free/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test_kernel_lower_memory_free" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2021" + +[dependencies] +bootloader_api = { path = "../../../api" } +x86_64 = { version = "0.14.7", default-features = false, features = [ + "instructions", + "inline_asm", +] } +uart_16550 = "0.2.10" diff --git a/tests/test_kernels/lower_memory_free/src/bin/lower_memory_free.rs b/tests/test_kernels/lower_memory_free/src/bin/lower_memory_free.rs new file mode 100644 index 00000000..f443b768 --- /dev/null +++ b/tests/test_kernels/lower_memory_free/src/bin/lower_memory_free.rs @@ -0,0 +1,44 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader_api::{entry_point, info::MemoryRegionKind, BootInfo}; +use test_kernel_lower_memory_free::{exit_qemu, QemuExitCode}; + +const LOWER_MEMORY_END_PAGE: u64 = 0x0010_0000; + +entry_point!(kernel_main); + +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + use core::fmt::Write; + use test_kernel_lower_memory_free::serial; + + let mut count = 0; + for region in boot_info.memory_regions.iter() { + writeln!( + serial(), + "Region: {:016x}-{:016x} - {:?}", + region.start, + region.end, + region.kind + ) + .unwrap(); + if region.end <= LOWER_MEMORY_END_PAGE && region.kind == MemoryRegionKind::Usable { + let pages = (region.end - region.start) / 4096; + count += pages; + } + } + + writeln!(serial(), "Free lower memory page count: {}", count).unwrap(); + assert!(count > 0x10); // 0x20 chosen arbirarily, we need _some_ free conventional memory, but not all of it. Some, especially on BIOS, may be reserved for hardware. + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[panic_handler] +#[cfg(not(test))] +fn panic(info: &core::panic::PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(test_kernel_lower_memory_free::serial(), "PANIC: {}", info); + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/lower_memory_free/src/lib.rs b/tests/test_kernels/lower_memory_free/src/lib.rs new file mode 100644 index 00000000..00ea92a6 --- /dev/null +++ b/tests/test_kernels/lower_memory_free/src/lib.rs @@ -0,0 +1,29 @@ +#![no_std] + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub static RAMDISK_CONTENTS: &[u8] = include_bytes!("../../../ramdisk.txt"); + +pub fn exit_qemu(exit_code: QemuExitCode) -> ! { + use x86_64::instructions::{nop, port::Port}; + + unsafe { + let mut port = Port::new(0xf4); + port.write(exit_code as u32); + } + + loop { + nop(); + } +} + +pub fn serial() -> uart_16550::SerialPort { + let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; + port.init(); + port +}