Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Mark ramdisk as used in memory map #408

Merged
merged 2 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

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
}
Loading