diff --git a/Cargo.lock b/Cargo.lock index 5023a397..97352a1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,7 +155,7 @@ dependencies = [ [[package]] name = "bootloader" -version = "0.11.4" +version = "0.11.5" dependencies = [ "anyhow", "async-process", @@ -180,22 +180,22 @@ dependencies = [ [[package]] name = "bootloader-boot-config" -version = "0.11.4" +version = "0.11.5" dependencies = [ "serde", ] [[package]] name = "bootloader-x86_64-bios-boot-sector" -version = "0.11.4" +version = "0.11.5" [[package]] name = "bootloader-x86_64-bios-common" -version = "0.11.4" +version = "0.11.5" [[package]] name = "bootloader-x86_64-bios-stage-2" -version = "0.11.4" +version = "0.11.5" dependencies = [ "bootloader-x86_64-bios-common", "byteorder", @@ -204,7 +204,7 @@ dependencies = [ [[package]] name = "bootloader-x86_64-bios-stage-3" -version = "0.11.4" +version = "0.11.5" dependencies = [ "bootloader-x86_64-bios-common", "noto-sans-mono-bitmap 0.1.6", @@ -212,7 +212,7 @@ dependencies = [ [[package]] name = "bootloader-x86_64-bios-stage-4" -version = "0.11.4" +version = "0.11.5" dependencies = [ "bootloader-boot-config", "bootloader-x86_64-bios-common", @@ -227,7 +227,7 @@ dependencies = [ [[package]] name = "bootloader-x86_64-common" -version = "0.11.4" +version = "0.11.5" dependencies = [ "bootloader-boot-config", "bootloader_api", @@ -246,7 +246,7 @@ dependencies = [ [[package]] name = "bootloader-x86_64-uefi" -version = "0.11.4" +version = "0.11.5" dependencies = [ "bootloader-boot-config", "bootloader-x86_64-common", @@ -259,7 +259,7 @@ dependencies = [ [[package]] name = "bootloader_api" -version = "0.11.4" +version = "0.11.5" dependencies = [ "rand", ] @@ -851,9 +851,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.22" +version = "0.37.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8818fa822adcc98b18fedbb3632a6a33213c070556b5aa7c4c8cc21cff565c4c" +checksum = "84f3f8f960ed3b5a59055428714943298bf3fa2d4a1d53135084e0544829d995" dependencies = [ "bitflags 1.3.2", "errno", diff --git a/Cargo.toml b/Cargo.toml index b6ab32f2..4378584c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,15 +31,15 @@ exclude = ["examples/basic", "examples/test_framework"] [workspace.package] # don't forget to update `workspace.dependencies` below -version = "0.11.4" -license = "MIT/Apache-2.0" +version = "0.11.5" +license = "MIT OR Apache-2.0" repository = "https://github.com/rust-osdev/bootloader" [workspace.dependencies] -bootloader_api = { version = "0.11.4", path = "api" } -bootloader-x86_64-common = { version = "0.11.4", path = "common" } -bootloader-boot-config = { version = "0.11.4", path = "common/config" } -bootloader-x86_64-bios-common = { version = "0.11.4", path = "bios/common" } +bootloader_api = { version = "0.11.5", path = "api" } +bootloader-x86_64-common = { version = "0.11.5", path = "common" } +bootloader-boot-config = { version = "0.11.5", path = "common/config" } +bootloader-x86_64-bios-common = { version = "0.11.5", path = "bios/common" } [features] default = ["bios", "uefi"] diff --git a/Changelog.md b/Changelog.md index b8ecf0b8..bd39899e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,17 @@ # Unreleased +# 0.11.5 – 2023-12-28 + +* [RacyCell: Data race allowed on `T`](https://github.com/rust-osdev/bootloader/pull/390) +* [Update license field following SPDX 2.1 license expression standard](https://github.com/rust-osdev/bootloader/pull/391) +* [kernel image fields & zero out rbp](https://github.com/rust-osdev/bootloader/pull/346) +* [Update `rustix` dependency](https://github.com/rust-osdev/bootloader/pull/398) +* [Add an additional MB of space to the generated FAT partition](https://github.com/rust-osdev/bootloader/pull/397) +* [Fix: Enable test runner again](https://github.com/rust-osdev/bootloader/pull/407) +* [Fix: Mark `ramdisk` as used in memory map](https://github.com/rust-osdev/bootloader/pull/408) + +**Full Changelog**: https://github.com/rust-osdev/bootloader/compare/v0.11.4...v0.11.5 + # 0.11.4 – 2023-07-05 - [Fix bug stemming from treating an exclusive range as an inclusive ranges](https://github.com/rust-osdev/bootloader/pull/362) diff --git a/api/src/info.rs b/api/src/info.rs index cdb48722..965fcf40 100644 --- a/api/src/info.rs +++ b/api/src/info.rs @@ -56,6 +56,12 @@ pub struct BootInfo { pub ramdisk_addr: Optional, /// Ramdisk image size, set to 0 if addr is None pub ramdisk_len: u64, + /// Physical address of the kernel ELF in memory. + pub kernel_addr: u64, + /// Size of the kernel ELF in memory. + pub kernel_len: u64, + /// Virtual address of the loaded kernel image. + pub kernel_image_offset: u64, #[doc(hidden)] pub _test_sentinel: u64, @@ -76,6 +82,9 @@ impl BootInfo { tls_template: Optional::None, ramdisk_addr: Optional::None, ramdisk_len: 0, + kernel_addr: 0, + kernel_len: 0, + kernel_image_offset: 0, _test_sentinel: 0, } } diff --git a/bios/common/src/racy_cell.rs b/bios/common/src/racy_cell.rs index d2797f7b..439d1b18 100644 --- a/bios/common/src/racy_cell.rs +++ b/bios/common/src/racy_cell.rs @@ -18,4 +18,4 @@ impl RacyCell { } unsafe impl Send for RacyCell where T: Send {} -unsafe impl Sync for RacyCell {} +unsafe impl Sync for RacyCell {} diff --git a/common/src/legacy_memory_region.rs b/common/src/legacy_memory_region.rs index bf804e9c..e57c74cd 100644 --- a/common/src/legacy_memory_region.rs +++ b/common/src/legacy_memory_region.rs @@ -110,10 +110,14 @@ where pub fn construct_memory_map( self, regions: &mut [MaybeUninit], - kernel_slice_start: u64, + kernel_slice_start: PhysAddr, kernel_slice_len: u64, + ramdisk_slice_start: Option, + 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(); @@ -156,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 @@ -197,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); diff --git a/common/src/lib.rs b/common/src/lib.rs index ecd0d9cc..2c5aaba4 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -39,7 +39,7 @@ pub mod serial; const PAGE_SIZE: u64 = 4096; -/// Initialize a text-based logger using the given pixel-based framebuffer as output. +/// Initialize a text-based logger using the given pixel-based framebuffer as output. pub fn init_logger( framebuffer: &'static mut [u8], info: FrameBufferInfo, @@ -195,10 +195,10 @@ where enable_write_protect_bit(); let config = kernel.config; - let kernel_slice_start = kernel.start_address as u64; + let kernel_slice_start = PhysAddr::new(kernel.start_address as _); let kernel_slice_len = u64::try_from(kernel.len).unwrap(); - let (entry_point, tls_template) = load_kernel::load_kernel( + let (kernel_image_offset, entry_point, tls_template) = load_kernel::load_kernel( kernel, kernel_page_table, frame_allocator, @@ -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 = PhysFrame::containing_address(physical_address); let ramdisk_page_count = (system_info.ramdisk_len - 1) / Size4KiB::SIZE; @@ -402,6 +402,9 @@ where kernel_slice_start, kernel_slice_len, + kernel_image_offset, + + ramdisk_slice_phys_start, ramdisk_slice_start, ramdisk_slice_len, } @@ -426,9 +429,12 @@ pub struct Mappings { pub tls_template: Option, /// Start address of the kernel slice allocation in memory. - pub kernel_slice_start: u64, + pub kernel_slice_start: PhysAddr, /// Size of the kernel slice allocation in memory. 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, pub ramdisk_slice_start: Option, pub ramdisk_slice_len: u64, } @@ -512,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"); @@ -543,6 +551,9 @@ where .map(|addr| addr.as_u64()) .into(); info.ramdisk_len = mappings.ramdisk_slice_len; + info.kernel_addr = mappings.kernel_slice_start.as_u64(); + info.kernel_len = mappings.kernel_slice_len as _; + info.kernel_image_offset = mappings.kernel_image_offset.as_u64(); info._test_sentinel = boot_config._test_sentinel; info }); @@ -587,7 +598,7 @@ pub struct PageTables { /// /// Must be the page table that the `kernel` field of this struct refers to. /// - /// This frame is loaded into the `CR3` register on the final context switch to the kernel. + /// This frame is loaded into the `CR3` register on the final context switch to the kernel. pub kernel_level_4_frame: PhysFrame, } @@ -595,7 +606,13 @@ pub struct PageTables { unsafe fn context_switch(addresses: Addresses) -> ! { unsafe { asm!( - "mov cr3, {}; mov rsp, {}; push 0; jmp {}", + r#" + xor rbp, rbp + mov cr3, {} + mov rsp, {} + push 0 + jmp {} + "#, in(reg) addresses.page_table.start_address().as_u64(), in(reg) addresses.stack_top.as_u64(), in(reg) addresses.entry_point.as_u64(), diff --git a/common/src/load_kernel.rs b/common/src/load_kernel.rs index a79a404a..68ad9778 100644 --- a/common/src/load_kernel.rs +++ b/common/src/load_kernel.rs @@ -721,11 +721,15 @@ pub fn load_kernel( page_table: &mut (impl MapperAllSizes + Translate), frame_allocator: &mut impl FrameAllocator, used_entries: &mut UsedLevel4Entries, -) -> Result<(VirtAddr, Option), &'static str> { +) -> Result<(VirtAddr, VirtAddr, Option), &'static str> { let mut loader = Loader::new(kernel, page_table, frame_allocator, used_entries)?; let tls_template = loader.load_segments()?; - Ok((loader.entry_point(), tls_template)) + Ok(( + VirtAddr::new(loader.inner.virtual_address_offset.virtual_address_offset() as u64), + loader.entry_point(), + tls_template, + )) } /// A helper type used to offset virtual addresses for position independent diff --git a/src/fat.rs b/src/fat.rs index aa2a450b..c81033b5 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -26,7 +26,7 @@ pub fn create_fat_filesystem( .truncate(true) .open(out_fat_path) .unwrap(); - let fat_size_padded_and_rounded = ((needed_size + 1024 * 64 - 1) / MB + 1) * MB; + let fat_size_padded_and_rounded = ((needed_size + 1024 * 64 - 1) / MB + 1) * MB + MB; fat_file.set_len(fat_size_padded_and_rounded).unwrap(); // choose a file system label diff --git a/tests/ramdisk.rs b/tests/ramdisk.rs index bdd7f9db..08c86f9b 100644 --- a/tests/ramdisk.rs +++ b/tests/ramdisk.rs @@ -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)), + ); +} diff --git a/tests/runner/Cargo.toml b/tests/runner/Cargo.toml index 127184a6..796ffb01 100644 --- a/tests/runner/Cargo.toml +++ b/tests/runner/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +default = ["bios", "uefi"] bios = ["bootloader/bios"] uefi = ["bootloader/uefi", "dep:ovmf-prebuilt"] diff --git a/tests/test_kernels/ramdisk/src/bin/memory_map.rs b/tests/test_kernels/ramdisk/src/bin/memory_map.rs new file mode 100644 index 00000000..b939a420 --- /dev/null +++ b/tests/test_kernels/ramdisk/src/bin/memory_map.rs @@ -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 { + 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 +}