Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2555127

Browse filesBrowse files
Guard the lower 1MB of memory
This commit fixes the review changes in #317. Most of the credit should go to Jason Couture <jasonc@alertr.info> Co-authored-by: Jason Couture <jasonc@alertr.info>
1 parent 1e52e4c commit 2555127
Copy full SHA for 2555127

File tree

Expand file treeCollapse file tree

7 files changed

+168
-4
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+168
-4
lines changed

‎Cargo.lock

Copy file name to clipboardExpand all lines: Cargo.lock
+10Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

Copy file name to clipboardExpand all lines: Cargo.toml
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ members = [
2626
"tests/test_kernels/lto",
2727
"tests/test_kernels/ramdisk",
2828
"tests/test_kernels/min_stack",
29+
"tests/test_kernels/lower_memory_free",
2930
]
3031
exclude = ["examples/basic", "examples/test_framework"]
3132

@@ -67,6 +68,7 @@ test_kernel_pie = { path = "tests/test_kernels/pie", artifact = "bin", target =
6768
test_kernel_ramdisk = { path = "tests/test_kernels/ramdisk", artifact = "bin", target = "x86_64-unknown-none" }
6869
test_kernel_config_file = { path = "tests/test_kernels/config_file", artifact = "bin", target = "x86_64-unknown-none" }
6970
test_kernel_min_stack = { path = "tests/test_kernels/min_stack", artifact = "bin", target = "x86_64-unknown-none" }
71+
test_kernel_lower_memory_free = { path = "tests/test_kernels/lower_memory_free", artifact = "bin", target = "x86_64-unknown-none" }
7072

7173
[profile.dev]
7274
panic = "abort"

‎common/src/legacy_memory_region.rs

Copy file name to clipboardExpand all lines: common/src/legacy_memory_region.rs
+45-4Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ pub struct LegacyFrameAllocator<I, D> {
2828
memory_map: I,
2929
current_descriptor: Option<D>,
3030
next_frame: PhysFrame,
31+
min_frame: PhysFrame,
3132
}
3233

34+
/// Start address of the first frame that is not part of the lower 1MB of frames
35+
const LOWER_MEMORY_END_PAGE: u64 = 0x100_000;
36+
3337
impl<I, D> LegacyFrameAllocator<I, D>
3438
where
3539
I: ExactSizeIterator<Item = D> + Clone,
@@ -40,20 +44,26 @@ where
4044
/// Skips the frame at physical address zero to avoid potential problems. For example
4145
/// identity-mapping the frame at address zero is not valid in Rust, because Rust's `core`
4246
/// library assumes that references can never point to virtual address `0`.
47+
/// Also skips the lower 1MB of frames, there are use cases that require lower conventional memory access (Such as SMP SIPI).
4348
pub fn new(memory_map: I) -> Self {
4449
// 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));
50+
// Also skip at least the lower 1MB of frames, there are use cases that require lower conventional memory access (Such as SMP SIPI).
51+
let start_frame = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
4652
Self::new_starting_at(start_frame, memory_map)
4753
}
4854

4955
/// Creates a new frame allocator based on the given legacy memory regions. Skips any frames
50-
/// before the given `frame`.
56+
/// before the given `frame` or `0x10000`(1MB) whichever is higher, there are use cases that require
57+
/// lower conventional memory access (Such as SMP SIPI).
5158
pub fn new_starting_at(frame: PhysFrame, memory_map: I) -> Self {
59+
let lower_mem_end = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
60+
let frame = core::cmp::max(frame, lower_mem_end);
5261
Self {
5362
original: memory_map.clone(),
5463
memory_map,
5564
current_descriptor: None,
5665
next_frame: frame,
66+
min_frame: frame,
5767
}
5868
}
5969

@@ -71,6 +81,7 @@ where
7181
if self.next_frame <= end_frame {
7282
let ret = self.next_frame;
7383
self.next_frame += 1;
84+
7485
Some(ret)
7586
} else {
7687
None
@@ -125,14 +136,44 @@ where
125136
let next_free = self.next_frame.start_address();
126137
let kind = match descriptor.kind() {
127138
MemoryRegionKind::Usable => {
128-
if end <= next_free {
139+
if end <= next_free && start >= self.min_frame.start_address() {
129140
MemoryRegionKind::Bootloader
130141
} else if descriptor.start() >= next_free {
131142
MemoryRegionKind::Usable
143+
} else if end <= self.min_frame.start_address() {
144+
// treat regions before min_frame as usable
145+
// this allows for access to the lower 1MB of frames
146+
MemoryRegionKind::Usable
147+
} else if end <= next_free {
148+
// part of the region is used -> add it separately
149+
// first part of the region is in lower 1MB, later part is used
150+
let free_region = MemoryRegion {
151+
start: descriptor.start().as_u64(),
152+
end: self.min_frame.start_address().as_u64(),
153+
kind: MemoryRegionKind::Usable,
154+
};
155+
Self::add_region(free_region, regions, &mut next_index);
156+
157+
// add bootloader part normally
158+
start = self.min_frame.start_address();
159+
MemoryRegionKind::Bootloader
132160
} else {
161+
if start < self.min_frame.start_address() {
162+
// part of the region is in lower memory
163+
let lower_region = MemoryRegion {
164+
start: start.as_u64(),
165+
end: self.min_frame.start_address().as_u64(),
166+
kind: MemoryRegionKind::Usable,
167+
};
168+
Self::add_region(lower_region, regions, &mut next_index);
169+
170+
start = self.min_frame.start_address();
171+
}
172+
133173
// part of the region is used -> add it separately
174+
// first part of the region is used, later part is free
134175
let used_region = MemoryRegion {
135-
start: descriptor.start().as_u64(),
176+
start: start.as_u64(),
136177
end: next_free.as_u64(),
137178
kind: MemoryRegionKind::Bootloader,
138179
};

‎tests/lower_memory_free.rs

Copy file name to clipboard
+7Lines changed: 7 additions & 0 deletions
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+
}
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "test_kernel_lower_memory_free"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bootloader_api = { path = "../../../api" }
8+
x86_64 = { version = "0.14.7", default-features = false, features = [
9+
"instructions",
10+
"inline_asm",
11+
] }
12+
uart_16550 = "0.2.10"
+65Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 test_kernel_lower_memory_free::{exit_qemu, QemuExitCode};
8+
9+
const LOWER_MEMORY_END_PAGE: u64 = 0x0010_0000;
10+
const WRITE_TEST_UNTIL: u64 = 0x4000_0000;
11+
12+
pub const BOOTLOADER_CONFIG: BootloaderConfig = {
13+
let mut config = BootloaderConfig::new_default();
14+
config.mappings.physical_memory = Some(Mapping::FixedAddress(0x0000_4000_0000_0000));
15+
config
16+
};
17+
18+
entry_point!(kernel_main, config = &BOOTLOADER_CONFIG);
19+
20+
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
21+
use core::fmt::Write;
22+
use test_kernel_lower_memory_free::serial;
23+
24+
let phys_mem_offset = boot_info.physical_memory_offset.into_option().unwrap();
25+
26+
let mut count = 0;
27+
for region in boot_info.memory_regions.iter() {
28+
writeln!(
29+
serial(),
30+
"Region: {:016x}-{:016x} - {:?}",
31+
region.start,
32+
region.end,
33+
region.kind
34+
)
35+
.unwrap();
36+
if region.kind == MemoryRegionKind::Usable && region.start < LOWER_MEMORY_END_PAGE {
37+
let end = core::cmp::min(region.end, LOWER_MEMORY_END_PAGE);
38+
let pages = (end - region.start) / 4096;
39+
count += pages;
40+
}
41+
if region.kind == MemoryRegionKind::Usable && region.start < WRITE_TEST_UNTIL {
42+
let end = core::cmp::min(region.end, WRITE_TEST_UNTIL);
43+
// ensure region is actually writable
44+
let addr = phys_mem_offset + region.start;
45+
let size = end - region.start;
46+
unsafe {
47+
core::ptr::write_bytes(addr as *mut u8, 0xff, size as usize);
48+
}
49+
}
50+
}
51+
52+
writeln!(serial(), "Free lower memory page count: {}", count).unwrap();
53+
assert!(count > 0x10); // 0x10 chosen arbirarily, we need _some_ free conventional memory, but not all of it. Some, especially on BIOS, may be reserved for hardware.
54+
exit_qemu(QemuExitCode::Success);
55+
}
56+
57+
/// This function is called on panic.
58+
#[panic_handler]
59+
#[cfg(not(test))]
60+
fn panic(info: &core::panic::PanicInfo) -> ! {
61+
use core::fmt::Write;
62+
63+
let _ = writeln!(test_kernel_lower_memory_free::serial(), "PANIC: {}", info);
64+
exit_qemu(QemuExitCode::Failed);
65+
}
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 fn exit_qemu(exit_code: QemuExitCode) -> ! {
11+
use x86_64::instructions::{nop, port::Port};
12+
13+
unsafe {
14+
let mut port = Port::new(0xf4);
15+
port.write(exit_code as u32);
16+
}
17+
18+
loop {
19+
nop();
20+
}
21+
}
22+
23+
pub fn serial() -> uart_16550::SerialPort {
24+
let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) };
25+
port.init();
26+
port
27+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.