Skip to content

Commit 2b84ae0

Browse files
committed
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.
1 parent 6939d4f commit 2b84ae0

File tree

8 files changed

+130
-7
lines changed

8 files changed

+130
-7
lines changed

Diff for: Cargo.lock

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ test_kernel_higher_half = { path = "tests/test_kernels/higher_half", artifact =
5757
test_kernel_map_phys_mem = { path = "tests/test_kernels/map_phys_mem", artifact = "bin", target = "x86_64-unknown-none" }
5858
test_kernel_pie = { path = "tests/test_kernels/pie", artifact = "bin", target = "x86_64-unknown-none" }
5959
test_kernel_ramdisk = { path = "tests/test_kernels/ramdisk", artifact = "bin", target = "x86_64-unknown-none" }
60+
test_kernel_lower_memory_free = { path = "tests/test_kernels/lower_memory_free", artifact = "bin", target = "x86_64-unknown-none" }
6061

6162
[profile.dev]
6263
panic = "abort"

Diff for: api/src/info.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,12 @@ impl MemoryRegion {
149149
#[non_exhaustive]
150150
#[repr(C)]
151151
pub enum MemoryRegionKind {
152-
/// Unused conventional memory, can be used by the kernel.
153-
Usable,
154152
/// Memory mappings created by the bootloader, including the page table and boot info mappings.
155153
///
156154
/// This memory should _not_ be used by the kernel.
157155
Bootloader,
156+
/// Unused conventional memory, can be used by the kernel.
157+
Usable,
158158
/// An unknown memory region reported by the UEFI firmware.
159159
///
160160
/// Contains the UEFI memory type tag.

Diff for: common/src/legacy_memory_region.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ pub struct LegacyFrameAllocator<I, D> {
2828
memory_map: I,
2929
current_descriptor: Option<D>,
3030
next_frame: PhysFrame,
31+
start_frame: PhysFrame,
3132
}
3233

34+
const LOWER_MEMORY_END_PAGE: u64 = 0x10_0000;
35+
3336
impl<I, D> LegacyFrameAllocator<I, D>
3437
where
3538
I: ExactSizeIterator<Item = D> + Clone,
@@ -42,18 +45,32 @@ where
4245
/// library assumes that references can never point to virtual address `0`.
4346
pub fn new(memory_map: I) -> Self {
4447
// skip frame 0 because the rust core library does not see 0 as a valid address
45-
let start_frame = PhysFrame::containing_address(PhysAddr::new(0x1000));
46-
Self::new_starting_at(start_frame, memory_map)
48+
// also skip the first 1MB of frames, there are use cases that require lower conventional memory access. (Such as SMP SIPI)
49+
let start_frame = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
50+
unsafe { Self::unsafe_new_starting_at(start_frame, memory_map) }
4751
}
4852

4953
/// Creates a new frame allocator based on the given legacy memory regions. Skips any frames
50-
/// before the given `frame`.
54+
/// before the given `frame`, or 0x100000, whichever is higher.
5155
pub fn new_starting_at(frame: PhysFrame, memory_map: I) -> Self {
56+
if frame.start_address().as_u64() < LOWER_MEMORY_END_PAGE {
57+
// Skip the first 1MB of frames, regardless of what was requested.
58+
// there are use cases that require lower conventional memory access. (Such as SMP SIPI)
59+
return Self::new(memory_map);
60+
}
61+
62+
return unsafe { Self::unsafe_new_starting_at(frame, memory_map) };
63+
}
64+
65+
/// Creates a new frame allocator based on the given legacy memory regions. Skips any frames
66+
/// before the given `frame`, without attempting to protect the lower 1MB of memory.
67+
pub unsafe fn unsafe_new_starting_at(frame: PhysFrame, memory_map: I) -> Self {
5268
Self {
5369
original: memory_map.clone(),
5470
memory_map,
5571
current_descriptor: None,
5672
next_frame: frame,
73+
start_frame: frame,
5774
}
5875
}
5976

@@ -121,9 +138,11 @@ where
121138
let next_free = self.next_frame.start_address();
122139
let kind = match descriptor.kind() {
123140
MemoryRegionKind::Usable => {
124-
if end <= next_free {
141+
if end <= next_free && end > self.start_frame.start_address() {
125142
MemoryRegionKind::Bootloader
126-
} else if descriptor.start() >= next_free {
143+
} else if descriptor.start() >= next_free
144+
|| end <= self.start_frame.start_address()
145+
{
127146
MemoryRegionKind::Usable
128147
} else {
129148
// part of the region is used -> add it separately

Diff for: tests/lower_memory_free.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use bootloader_test_runner::run_test_kernel;
2+
#[test]
3+
fn lower_memory_free() {
4+
run_test_kernel(env!(
5+
"CARGO_BIN_FILE_TEST_KERNEL_LOWER_MEMORY_FREE_lower_memory_free"
6+
));
7+
}

Diff for: tests/test_kernels/lower_memory_free/Cargo.toml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "test_kernel_lower_memory_free"
3+
version = "0.1.0"
4+
authors = ["Philipp Oppermann <[email protected]>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
bootloader_api = { path = "../../../api" }
9+
x86_64 = { version = "0.14.7", default-features = false, features = [
10+
"instructions",
11+
"inline_asm",
12+
] }
13+
uart_16550 = "0.2.10"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#![no_std] // don't link the Rust standard library
2+
#![no_main] // disable all Rust-level entry points
3+
4+
use bootloader_api::{entry_point, info::MemoryRegionKind, BootInfo};
5+
use test_kernel_lower_memory_free::{exit_qemu, QemuExitCode};
6+
7+
const LOWER_MEMORY_END_PAGE: u64 = 0x0010_0000;
8+
9+
entry_point!(kernel_main);
10+
11+
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
12+
use core::fmt::Write;
13+
use test_kernel_lower_memory_free::serial;
14+
15+
let mut count = 0;
16+
for region in boot_info.memory_regions.iter() {
17+
writeln!(
18+
serial(),
19+
"Region: {:016x}-{:016x} - {:?}",
20+
region.start,
21+
region.end,
22+
region.kind
23+
)
24+
.unwrap();
25+
if region.end <= LOWER_MEMORY_END_PAGE && region.kind == MemoryRegionKind::Usable {
26+
let pages = (region.end - region.start) / 4096;
27+
count += pages;
28+
}
29+
}
30+
31+
writeln!(serial(), "Free lower memory page count: {}", count).unwrap();
32+
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.
33+
exit_qemu(QemuExitCode::Success);
34+
}
35+
36+
/// This function is called on panic.
37+
#[panic_handler]
38+
#[cfg(not(test))]
39+
fn panic(info: &core::panic::PanicInfo) -> ! {
40+
use core::fmt::Write;
41+
42+
let _ = writeln!(test_kernel_lower_memory_free::serial(), "PANIC: {}", info);
43+
exit_qemu(QemuExitCode::Failed);
44+
}

Diff for: tests/test_kernels/lower_memory_free/src/lib.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![no_std]
2+
3+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4+
#[repr(u32)]
5+
pub enum QemuExitCode {
6+
Success = 0x10,
7+
Failed = 0x11,
8+
}
9+
10+
pub static RAMDISK_CONTENTS: &[u8] = include_bytes!("../../../ramdisk.txt");
11+
12+
pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
13+
use x86_64::instructions::{nop, port::Port};
14+
15+
unsafe {
16+
let mut port = Port::new(0xf4);
17+
port.write(exit_code as u32);
18+
}
19+
20+
loop {
21+
nop();
22+
}
23+
}
24+
25+
pub fn serial() -> uart_16550::SerialPort {
26+
let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) };
27+
port.init();
28+
port
29+
}

0 commit comments

Comments
 (0)