Skip to content

Commit 22d116f

Browse files
authored
Merge pull request #408 from rust-osdev/fix-ramdisk
Fix: Mark `ramdisk` as used in memory map
2 parents 1b84c46 + 7c74627 commit 22d116f

File tree

4 files changed

+148
-3
lines changed

4 files changed

+148
-3
lines changed

Diff for: common/src/legacy_memory_region.rs

+46-1
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,12 @@ where
112112
regions: &mut [MaybeUninit<MemoryRegion>],
113113
kernel_slice_start: PhysAddr,
114114
kernel_slice_len: u64,
115+
ramdisk_slice_start: Option<PhysAddr>,
116+
ramdisk_slice_len: u64,
115117
) -> &mut [MemoryRegion] {
116118
let mut next_index = 0;
117119
let kernel_slice_start = kernel_slice_start.as_u64();
120+
let ramdisk_slice_start = ramdisk_slice_start.map(|a| a.as_u64());
118121

119122
for descriptor in self.original {
120123
let mut start = descriptor.start();
@@ -157,8 +160,9 @@ where
157160
kind,
158161
};
159162

160-
// check if region overlaps with kernel
163+
// check if region overlaps with kernel or ramdisk
161164
let kernel_slice_end = kernel_slice_start + kernel_slice_len;
165+
let ramdisk_slice_end = ramdisk_slice_start.map(|s| s + ramdisk_slice_len);
162166
if region.kind == MemoryRegionKind::Usable
163167
&& kernel_slice_start < region.end
164168
&& kernel_slice_end > region.start
@@ -198,6 +202,47 @@ where
198202
Self::add_region(before_kernel, regions, &mut next_index);
199203
Self::add_region(kernel, regions, &mut next_index);
200204
Self::add_region(after_kernel, regions, &mut next_index);
205+
} else if region.kind == MemoryRegionKind::Usable
206+
&& ramdisk_slice_start.map(|s| s < region.end).unwrap_or(false)
207+
&& ramdisk_slice_end.map(|e| e > region.start).unwrap_or(false)
208+
{
209+
// region overlaps with ramdisk -> we might need to split it
210+
let ramdisk_slice_start = ramdisk_slice_start.unwrap();
211+
let ramdisk_slice_end = ramdisk_slice_end.unwrap();
212+
213+
// ensure that the ramdisk allocation does not span multiple regions
214+
assert!(
215+
ramdisk_slice_start >= region.start,
216+
"region overlaps with ramdisk, but ramdisk begins before region \
217+
(ramdisk_start: {ramdisk_slice_start:#x}, region_start: {:#x})",
218+
region.start
219+
);
220+
assert!(
221+
ramdisk_slice_end <= region.end,
222+
"region overlaps with ramdisk, but region ends before ramdisk \
223+
(ramdisk_end: {ramdisk_slice_end:#x}, region_end: {:#x})",
224+
region.end,
225+
);
226+
227+
// split the region into three parts
228+
let before_ramdisk = MemoryRegion {
229+
end: ramdisk_slice_start,
230+
..region
231+
};
232+
let ramdisk = MemoryRegion {
233+
start: ramdisk_slice_start,
234+
end: ramdisk_slice_end,
235+
kind: MemoryRegionKind::Bootloader,
236+
};
237+
let after_ramdisk = MemoryRegion {
238+
start: ramdisk_slice_end,
239+
..region
240+
};
241+
242+
// add the three regions (empty regions are ignored in `add_region`)
243+
Self::add_region(before_ramdisk, regions, &mut next_index);
244+
Self::add_region(ramdisk, regions, &mut next_index);
245+
Self::add_region(after_ramdisk, regions, &mut next_index);
201246
} else {
202247
// add the region normally
203248
Self::add_region(region, regions, &mut next_index);

Diff for: common/src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -293,14 +293,14 @@ where
293293
None
294294
};
295295
let ramdisk_slice_len = system_info.ramdisk_len;
296-
let ramdisk_slice_start = if let Some(ramdisk_address) = system_info.ramdisk_addr {
296+
let ramdisk_slice_phys_start = system_info.ramdisk_addr.map(PhysAddr::new);
297+
let ramdisk_slice_start = if let Some(physical_address) = ramdisk_slice_phys_start {
297298
let start_page = mapping_addr_page_aligned(
298299
config.mappings.ramdisk_memory,
299300
system_info.ramdisk_len,
300301
&mut used_entries,
301302
"ramdisk start",
302303
);
303-
let physical_address = PhysAddr::new(ramdisk_address);
304304
let ramdisk_physical_start_page: PhysFrame<Size4KiB> =
305305
PhysFrame::containing_address(physical_address);
306306
let ramdisk_page_count = (system_info.ramdisk_len - 1) / Size4KiB::SIZE;
@@ -404,6 +404,7 @@ where
404404
kernel_slice_len,
405405
kernel_image_offset,
406406

407+
ramdisk_slice_phys_start,
407408
ramdisk_slice_start,
408409
ramdisk_slice_len,
409410
}
@@ -433,6 +434,7 @@ pub struct Mappings {
433434
pub kernel_slice_len: u64,
434435
/// Relocation offset of the kernel image in virtual memory.
435436
pub kernel_image_offset: VirtAddr,
437+
pub ramdisk_slice_phys_start: Option<PhysAddr>,
436438
pub ramdisk_slice_start: Option<VirtAddr>,
437439
pub ramdisk_slice_len: u64,
438440
}
@@ -516,6 +518,8 @@ where
516518
memory_regions,
517519
mappings.kernel_slice_start,
518520
mappings.kernel_slice_len,
521+
mappings.ramdisk_slice_phys_start,
522+
mappings.ramdisk_slice_len,
519523
);
520524

521525
log::info!("Create bootinfo");

Diff for: tests/ramdisk.rs

+8
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,11 @@ fn check_ramdisk() {
1818
Some(Path::new(RAMDISK_PATH)),
1919
);
2020
}
21+
22+
#[test]
23+
fn memory_map() {
24+
run_test_kernel_with_ramdisk(
25+
env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_memory_map"),
26+
Some(Path::new(RAMDISK_PATH)),
27+
);
28+
}

Diff for: tests/test_kernels/ramdisk/src/bin/memory_map.rs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
44+
next_addr += frame.size();
45+
46+
frame.start_address()
47+
}
48+
other => panic!("invalid result: {other:?}"),
49+
};
50+
let region = boot_info
51+
.memory_regions
52+
.iter()
53+
.find(|r| r.start <= phys_addr.as_u64() && r.end > phys_addr.as_u64())
54+
.unwrap();
55+
assert_eq!(region.kind, MemoryRegionKind::Bootloader);
56+
}
57+
58+
let actual_ramdisk = unsafe {
59+
&*slice_from_raw_parts(
60+
boot_info.ramdisk_addr.into_option().unwrap() as *const u8,
61+
boot_info.ramdisk_len as usize,
62+
)
63+
};
64+
writeln!(serial(), "Actual contents: {actual_ramdisk:?}").unwrap();
65+
assert_eq!(RAMDISK_CONTENTS, actual_ramdisk);
66+
67+
exit_qemu(QemuExitCode::Success);
68+
}
69+
70+
/// This function is called on panic.
71+
#[cfg(not(test))]
72+
#[panic_handler]
73+
fn panic(info: &core::panic::PanicInfo) -> ! {
74+
let _ = writeln!(test_kernel_ramdisk::serial(), "PANIC: {info}");
75+
exit_qemu(QemuExitCode::Failed);
76+
}
77+
78+
pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
79+
use x86_64::registers::control::Cr3;
80+
81+
let (level_4_table_frame, _) = Cr3::read();
82+
83+
let phys = level_4_table_frame.start_address();
84+
let virt = physical_memory_offset + phys.as_u64();
85+
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
86+
87+
&mut *page_table_ptr // unsafe
88+
}

0 commit comments

Comments
 (0)