Skip to content

Commit

Permalink
Merge pull request #408 from rust-osdev/fix-ramdisk
Browse files Browse the repository at this point in the history
Fix: Mark `ramdisk` as used in memory map
  • Loading branch information
phil-opp authored Dec 28, 2023
2 parents 1b84c46 + 7c74627 commit 22d116f
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 3 deletions.
47 changes: 46 additions & 1 deletion common/src/legacy_memory_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,12 @@ where
regions: &mut [MaybeUninit<MemoryRegion>],
kernel_slice_start: PhysAddr,
kernel_slice_len: u64,
ramdisk_slice_start: Option<PhysAddr>,
ramdisk_slice_len: u64,
) -> &mut [MemoryRegion] {
let mut next_index = 0;
let kernel_slice_start = kernel_slice_start.as_u64();
let ramdisk_slice_start = ramdisk_slice_start.map(|a| a.as_u64());

for descriptor in self.original {
let mut start = descriptor.start();
Expand Down Expand Up @@ -157,8 +160,9 @@ where
kind,
};

// check if region overlaps with kernel
// check if region overlaps with kernel or ramdisk
let kernel_slice_end = kernel_slice_start + kernel_slice_len;
let ramdisk_slice_end = ramdisk_slice_start.map(|s| s + ramdisk_slice_len);
if region.kind == MemoryRegionKind::Usable
&& kernel_slice_start < region.end
&& kernel_slice_end > region.start
Expand Down Expand Up @@ -198,6 +202,47 @@ where
Self::add_region(before_kernel, regions, &mut next_index);
Self::add_region(kernel, regions, &mut next_index);
Self::add_region(after_kernel, regions, &mut next_index);
} else if region.kind == MemoryRegionKind::Usable
&& ramdisk_slice_start.map(|s| s < region.end).unwrap_or(false)
&& ramdisk_slice_end.map(|e| e > region.start).unwrap_or(false)
{
// region overlaps with ramdisk -> we might need to split it
let ramdisk_slice_start = ramdisk_slice_start.unwrap();
let ramdisk_slice_end = ramdisk_slice_end.unwrap();

// ensure that the ramdisk allocation does not span multiple regions
assert!(
ramdisk_slice_start >= region.start,
"region overlaps with ramdisk, but ramdisk begins before region \
(ramdisk_start: {ramdisk_slice_start:#x}, region_start: {:#x})",
region.start
);
assert!(
ramdisk_slice_end <= region.end,
"region overlaps with ramdisk, but region ends before ramdisk \
(ramdisk_end: {ramdisk_slice_end:#x}, region_end: {:#x})",
region.end,
);

// split the region into three parts
let before_ramdisk = MemoryRegion {
end: ramdisk_slice_start,
..region
};
let ramdisk = MemoryRegion {
start: ramdisk_slice_start,
end: ramdisk_slice_end,
kind: MemoryRegionKind::Bootloader,
};
let after_ramdisk = MemoryRegion {
start: ramdisk_slice_end,
..region
};

// add the three regions (empty regions are ignored in `add_region`)
Self::add_region(before_ramdisk, regions, &mut next_index);
Self::add_region(ramdisk, regions, &mut next_index);
Self::add_region(after_ramdisk, regions, &mut next_index);
} else {
// add the region normally
Self::add_region(region, regions, &mut next_index);
Expand Down
8 changes: 6 additions & 2 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,14 @@ where
None
};
let ramdisk_slice_len = system_info.ramdisk_len;
let ramdisk_slice_start = if let Some(ramdisk_address) = system_info.ramdisk_addr {
let ramdisk_slice_phys_start = system_info.ramdisk_addr.map(PhysAddr::new);
let ramdisk_slice_start = if let Some(physical_address) = ramdisk_slice_phys_start {
let start_page = mapping_addr_page_aligned(
config.mappings.ramdisk_memory,
system_info.ramdisk_len,
&mut used_entries,
"ramdisk start",
);
let physical_address = PhysAddr::new(ramdisk_address);
let ramdisk_physical_start_page: PhysFrame<Size4KiB> =
PhysFrame::containing_address(physical_address);
let ramdisk_page_count = (system_info.ramdisk_len - 1) / Size4KiB::SIZE;
Expand Down Expand Up @@ -404,6 +404,7 @@ where
kernel_slice_len,
kernel_image_offset,

ramdisk_slice_phys_start,
ramdisk_slice_start,
ramdisk_slice_len,
}
Expand Down Expand Up @@ -433,6 +434,7 @@ pub struct Mappings {
pub kernel_slice_len: u64,
/// Relocation offset of the kernel image in virtual memory.
pub kernel_image_offset: VirtAddr,
pub ramdisk_slice_phys_start: Option<PhysAddr>,
pub ramdisk_slice_start: Option<VirtAddr>,
pub ramdisk_slice_len: u64,
}
Expand Down Expand Up @@ -516,6 +518,8 @@ where
memory_regions,
mappings.kernel_slice_start,
mappings.kernel_slice_len,
mappings.ramdisk_slice_phys_start,
mappings.ramdisk_slice_len,
);

log::info!("Create bootinfo");
Expand Down
8 changes: 8 additions & 0 deletions tests/ramdisk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ fn check_ramdisk() {
Some(Path::new(RAMDISK_PATH)),
);
}

#[test]
fn memory_map() {
run_test_kernel_with_ramdisk(
env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_memory_map"),
Some(Path::new(RAMDISK_PATH)),
);
}
88 changes: 88 additions & 0 deletions tests/test_kernels/ramdisk/src/bin/memory_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points

use bootloader_api::{
config::Mapping, entry_point, info::MemoryRegionKind, BootInfo, BootloaderConfig,
};
use core::{fmt::Write, ptr::slice_from_raw_parts};
use test_kernel_ramdisk::{exit_qemu, serial, QemuExitCode, RAMDISK_CONTENTS};
use x86_64::{
structures::paging::{OffsetPageTable, PageTable, PageTableFlags, Translate},
VirtAddr,
};

pub const BOOTLOADER_CONFIG: BootloaderConfig = {
let mut config = BootloaderConfig::new_default();
config.mappings.physical_memory = Some(Mapping::FixedAddress(0x0000_6000_0000_0000));
config
};

entry_point!(kernel_main, config = &BOOTLOADER_CONFIG);

fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
writeln!(serial(), "Boot info: {boot_info:?}").unwrap();

let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset.into_option().unwrap());
let level_4_table = unsafe { active_level_4_table(phys_mem_offset) };
let page_table = unsafe { OffsetPageTable::new(level_4_table, phys_mem_offset) };

let ramdisk_start_addr = VirtAddr::new(boot_info.ramdisk_addr.into_option().unwrap());
assert_eq!(boot_info.ramdisk_len as usize, RAMDISK_CONTENTS.len());
let ramdisk_end_addr = ramdisk_start_addr + boot_info.ramdisk_len;

let mut next_addr = ramdisk_start_addr;
while next_addr < ramdisk_end_addr {
let phys_addr = match page_table.translate(next_addr) {
x86_64::structures::paging::mapper::TranslateResult::Mapped {
frame,
offset: _,
flags,
} => {
assert!(flags.contains(PageTableFlags::PRESENT));
assert!(flags.contains(PageTableFlags::WRITABLE));

next_addr += frame.size();

frame.start_address()
}
other => panic!("invalid result: {other:?}"),
};
let region = boot_info
.memory_regions
.iter()
.find(|r| r.start <= phys_addr.as_u64() && r.end > phys_addr.as_u64())
.unwrap();
assert_eq!(region.kind, MemoryRegionKind::Bootloader);
}

let actual_ramdisk = unsafe {
&*slice_from_raw_parts(
boot_info.ramdisk_addr.into_option().unwrap() as *const u8,
boot_info.ramdisk_len as usize,
)
};
writeln!(serial(), "Actual contents: {actual_ramdisk:?}").unwrap();
assert_eq!(RAMDISK_CONTENTS, actual_ramdisk);

exit_qemu(QemuExitCode::Success);
}

/// This function is called on panic.
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
let _ = writeln!(test_kernel_ramdisk::serial(), "PANIC: {info}");
exit_qemu(QemuExitCode::Failed);
}

pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {

Check warning on line 78 in tests/test_kernels/ramdisk/src/bin/memory_map.rs

View workflow job for this annotation

GitHub Actions / Clippy

unsafe function's docs miss `# Safety` section
use x86_64::registers::control::Cr3;

let (level_4_table_frame, _) = Cr3::read();

let phys = level_4_table_frame.start_address();
let virt = physical_memory_offset + phys.as_u64();
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();

&mut *page_table_ptr // unsafe
}

0 comments on commit 22d116f

Please sign in to comment.