Skip to content

Commit

Permalink
Guard the lower 1MB of memory
Browse files Browse the repository at this point in the history
This commit fixes the review changes in #317.
Most of the credit should go to Jason Couture <jasonc@alertr.info>
  • Loading branch information
Wasabi375 committed Jun 8, 2024
1 parent e10010e commit fdddff9
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 4 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
"tests/test_kernels/lto",
"tests/test_kernels/ramdisk",
"tests/test_kernels/min_stack",
"tests/test_kernels/lower_memory_free",
]
exclude = ["examples/basic", "examples/test_framework"]

Expand Down Expand Up @@ -67,6 +68,7 @@ test_kernel_pie = { path = "tests/test_kernels/pie", artifact = "bin", target =
test_kernel_ramdisk = { path = "tests/test_kernels/ramdisk", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_config_file = { path = "tests/test_kernels/config_file", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_min_stack = { path = "tests/test_kernels/min_stack", 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"
Expand Down
48 changes: 44 additions & 4 deletions common/src/legacy_memory_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ pub struct LegacyFrameAllocator<I, D> {
memory_map: I,
current_descriptor: Option<D>,
next_frame: PhysFrame,
min_frame: PhysFrame,
}

/// Start address of the first frame that is not part of the lower 1MB of frames
const LOWER_MEMORY_END_PAGE: u64 = 0x10_000;

impl<I, D> LegacyFrameAllocator<I, D>
where
I: ExactSizeIterator<Item = D> + Clone,
Expand All @@ -40,20 +44,25 @@ where
/// Skips the frame at physical address zero to avoid potential problems. For example
/// identity-mapping the frame at address zero is not valid in Rust, because Rust's `core`
/// library assumes that references can never point to virtual address `0`.
/// Also skips the lower 1MB of frames, there are use cases that require lower conventional memory access (Such as SMP SIPI).
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));
let start_frame = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
Self::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 `0x10000`(1MB) whichever is higher, there are use cases that require
/// lower conventional memory access (Such as SMP SIPI).
pub fn new_starting_at(frame: PhysFrame, memory_map: I) -> Self {
let lower_mem_end = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
let frame = core::cmp::max(frame, lower_mem_end);
Self {
original: memory_map.clone(),
memory_map,
current_descriptor: None,
next_frame: frame,
min_frame: frame,
}
}

Expand All @@ -71,6 +80,7 @@ where
if self.next_frame <= end_frame {
let ret = self.next_frame;
self.next_frame += 1;

Some(ret)
} else {
None
Expand Down Expand Up @@ -125,14 +135,44 @@ where
let next_free = self.next_frame.start_address();
let kind = match descriptor.kind() {
MemoryRegionKind::Usable => {
if end <= next_free {
if end <= next_free && start >= self.min_frame.start_address() {
MemoryRegionKind::Bootloader
} else if descriptor.start() >= next_free {
MemoryRegionKind::Usable
} else if end <= self.min_frame.start_address() {
// treat regions before min_frame as usable
// this allows for access to the lower 1MB of frames
MemoryRegionKind::Usable
} else if end <= next_free {
// part of the region is used -> add it separately
// first part of the region is in lower 1MB, later part is used
let free_region = MemoryRegion {
start: descriptor.start().as_u64(),
end: self.min_frame.start_address().as_u64(),
kind: MemoryRegionKind::Usable,
};
Self::add_region(free_region, regions, &mut next_index);

// add bootloader part normally
start = self.min_frame.start_address();
MemoryRegionKind::Bootloader
} else {
if start < self.min_frame.start_address() {
// part of the region is in lower memory
let lower_region = MemoryRegion {
start: start.as_u64(),
end: self.min_frame.start_address().as_u64(),
kind: MemoryRegionKind::Usable,
};
Self::add_region(lower_region, regions, &mut next_index);

start = self.min_frame.start_address();
}

// part of the region is used -> add it separately
// first part of the region is used, later part is free
let used_region = MemoryRegion {
start: descriptor.start().as_u64(),
start: start.as_u64(),
end: next_free.as_u64(),
kind: MemoryRegionKind::Bootloader,
};
Expand Down
7 changes: 7 additions & 0 deletions tests/lower_memory_free.rs
Original file line number Diff line number Diff line change
@@ -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"
));
}
13 changes: 13 additions & 0 deletions tests/test_kernels/lower_memory_free/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "test_kernel_lower_memory_free"
version = "0.1.0"
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
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"
45 changes: 45 additions & 0 deletions tests/test_kernels/lower_memory_free/src/bin/lower_memory_free.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![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.kind == MemoryRegionKind::Usable && region.start < LOWER_MEMORY_END_PAGE {
let end = core::cmp::min(region.end, LOWER_MEMORY_END_PAGE);
let pages = (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);
}
27 changes: 27 additions & 0 deletions tests/test_kernels/lower_memory_free/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![no_std]

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
}

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
}

0 comments on commit fdddff9

Please sign in to comment.