From 3262d16a7471543079920051fa35243d1f9508d1 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 29 Dec 2022 19:53:27 -0500 Subject: [PATCH 01/17] Add ramdisk support This adds support for loading a ramdisk/initrd, and an API to add one to the boot image. --- Cargo.lock | 18 +- Cargo.toml | 2 + api/build.rs | 7 +- api/src/config.rs | 22 +- api/src/info.rs | 6 + bios/common/src/lib.rs | 1 + bios/stage-2/src/main.rs | 37 +- bios/stage-4/src/main.rs | 15 +- common/src/lib.rs | 47 ++- src/lib.rs | 46 ++- src/pxe.rs | 11 + tests/default_settings.rs | 21 +- tests/higher_half.rs | 24 +- tests/lto.rs | 2 +- tests/map_phys_mem.rs | 14 +- tests/pie.rs | 8 +- tests/ramdisk.rs | 18 + tests/ramdisk.txt | 1 + tests/runner/src/lib.rs | 22 +- .../default_settings/src/bin/basic_boot.rs | 10 +- tests/test_kernels/ramdisk/Cargo.toml | 13 + .../ramdisk/src/bin/basic_boot.rs | 21 ++ tests/test_kernels/ramdisk/src/bin/ramdisk.rs | 34 ++ tests/test_kernels/ramdisk/src/lib.rs | 29 ++ uefi/Cargo.toml | 2 +- uefi/src/main.rs | 351 +++++++++++------- 26 files changed, 586 insertions(+), 196 deletions(-) create mode 100644 tests/ramdisk.rs create mode 100644 tests/ramdisk.txt create mode 100644 tests/test_kernels/ramdisk/Cargo.toml create mode 100644 tests/test_kernels/ramdisk/src/bin/basic_boot.rs create mode 100644 tests/test_kernels/ramdisk/src/bin/ramdisk.rs create mode 100644 tests/test_kernels/ramdisk/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a9a70725..415b0b55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,7 @@ dependencies = [ "test_kernel_higher_half", "test_kernel_map_phys_mem", "test_kernel_pie", + "test_kernel_ramdisk", ] [[package]] @@ -597,6 +598,15 @@ dependencies = [ "x86_64", ] +[[package]] +name = "test_kernel_ramdisk" +version = "0.1.0" +dependencies = [ + "bootloader_api", + "uart_16550", + "x86_64", +] + [[package]] name = "thiserror" version = "1.0.30" @@ -649,9 +659,9 @@ dependencies = [ [[package]] name = "uefi" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705535cf386e4b033cc7acdea55ec8710f3dde2f07457218791aac35c83be21f" +checksum = "07b87700863d65dd4841556be3374d8d4f9f8dbb577ad93a39859e70b3b91f35" dependencies = [ "bitflags", "log", @@ -661,9 +671,9 @@ dependencies = [ [[package]] name = "uefi-macros" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9917831bc5abb78c2e6a0f4fba2be165105ed53d288718c999e0efbd433bb7" +checksum = "275f054a1d9fd7e43a2ce91cc24298a87b281117dea8afc120ae95faa0e96b94" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4bea9b23..3046cec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "tests/test_kernels/higher_half", "tests/test_kernels/pie", "tests/test_kernels/lto", + "tests/test_kernels/ramdisk" ] exclude = ["examples/basic", "examples/test_framework"] @@ -50,6 +51,7 @@ test_kernel_default_settings = { path = "tests/test_kernels/default_settings", a test_kernel_higher_half = { path = "tests/test_kernels/higher_half", artifact = "bin", target = "x86_64-unknown-none" } test_kernel_map_phys_mem = { path = "tests/test_kernels/map_phys_mem", artifact = "bin", target = "x86_64-unknown-none" } test_kernel_pie = { path = "tests/test_kernels/pie", artifact = "bin", target = "x86_64-unknown-none" } +test_kernel_ramdisk = { path = "tests/test_kernels/ramdisk", artifact = "bin", target = "x86_64-unknown-none" } [profile.dev] panic = "abort" diff --git a/api/build.rs b/api/build.rs index f2eb9548..99235653 100644 --- a/api/build.rs +++ b/api/build.rs @@ -22,9 +22,10 @@ fn main() { (88, 9), (97, 9), (106, 9), - (115, 1), - (116, 1), - (117, 1), + (115, 9), + (124, 1), + (125, 1), + (126, 1), ]; let mut code = String::new(); diff --git a/api/src/config.rs b/api/src/config.rs index a94ebdaf..7b91a4bd 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -49,7 +49,7 @@ impl BootloaderConfig { 0x3D, ]; #[doc(hidden)] - pub const SERIALIZED_LEN: usize = 118; + pub const SERIALIZED_LEN: usize = 127; /// Creates a new default configuration with the following values: /// @@ -97,6 +97,7 @@ impl BootloaderConfig { aslr, dynamic_range_start, dynamic_range_end, + ramdisk_memory, } = mappings; let FrameBuffer { minimum_framebuffer_height, @@ -145,7 +146,9 @@ impl BootloaderConfig { }, ); - let buf = concat_97_9( + let buf = concat_97_9(buf, ramdisk_memory.serialize()); + + let buf = concat_106_9( buf, match minimum_framebuffer_height { Option::None => [0; 9], @@ -153,7 +156,7 @@ impl BootloaderConfig { }, ); - let buf = concat_106_9( + let buf = concat_115_9( buf, match minimum_framebuffer_width { Option::None => [0; 9], @@ -161,12 +164,12 @@ impl BootloaderConfig { }, ); - let log_level = concat_115_1(buf, (*log_level as u8).to_le_bytes()); + let log_level = concat_124_1(buf, (*log_level as u8).to_le_bytes()); let frame_buffer_logger_status = - concat_116_1(log_level, (*frame_buffer_logger_status as u8).to_le_bytes()); + concat_125_1(log_level, (*frame_buffer_logger_status as u8).to_le_bytes()); - concat_117_1( + concat_126_1( frame_buffer_logger_status, (*serial_logger_status as u8).to_le_bytes(), ) @@ -227,6 +230,7 @@ impl BootloaderConfig { let (&dynamic_range_start, s) = split_array_ref(s); let (&dynamic_range_end_some, s) = split_array_ref(s); let (&dynamic_range_end, s) = split_array_ref(s); + let (&ramdisk_memory, s) = split_array_ref(s); let mappings = Mappings { kernel_stack: Mapping::deserialize(&kernel_stack)?, @@ -257,6 +261,7 @@ impl BootloaderConfig { [1] => Option::Some(u64::from_le_bytes(dynamic_range_end)), _ => return Err("invalid dynamic range end value"), }, + ramdisk_memory: Mapping::deserialize(&ramdisk_memory)?, }; (mappings, s) }; @@ -439,6 +444,9 @@ pub struct Mappings { /// /// Defaults to `0xffff_ffff_ffff_f000`. pub dynamic_range_end: Option, + /// Virtual address to map ramdisk image, if present on disk + /// Defaults to dynamic + pub ramdisk_memory: Mapping, } impl Mappings { @@ -455,6 +463,7 @@ impl Mappings { aslr: false, dynamic_range_start: None, dynamic_range_end: None, + ramdisk_memory: Mapping::new_default(), } } @@ -487,6 +496,7 @@ impl Mappings { } else { Option::None }, + ramdisk_memory: Mapping::random(), } } } diff --git a/api/src/info.rs b/api/src/info.rs index 248362ab..020644bd 100644 --- a/api/src/info.rs +++ b/api/src/info.rs @@ -52,6 +52,10 @@ pub struct BootInfo { pub rsdp_addr: Optional, /// The thread local storage (TLS) template of the kernel executable, if present. pub tls_template: Optional, + /// Ramdisk address, if loaded + pub ramdisk_addr: Optional, + /// Ramdisk image size, set to 0 if addr is None + pub ramdisk_len: u64, } impl BootInfo { @@ -67,6 +71,8 @@ impl BootInfo { recursive_index: Optional::None, rsdp_addr: Optional::None, tls_template: Optional::None, + ramdisk_addr: Optional::None, + ramdisk_len: 0, } } } diff --git a/bios/common/src/lib.rs b/bios/common/src/lib.rs index c55c6d31..59a7737d 100644 --- a/bios/common/src/lib.rs +++ b/bios/common/src/lib.rs @@ -7,6 +7,7 @@ pub mod racy_cell; pub struct BiosInfo { pub stage_4: Region, pub kernel: Region, + pub ramdisk: Region, pub framebuffer: BiosFramebufferInfo, pub memory_map_addr: u32, pub memory_map_len: u16, diff --git a/bios/stage-2/src/main.rs b/bios/stage-2/src/main.rs index b8c85bd7..ca439af5 100644 --- a/bios/stage-2/src/main.rs +++ b/bios/stage-2/src/main.rs @@ -27,6 +27,7 @@ const BOOTLOADER_SECOND_STAGE_PARTITION_TYPE: u8 = 0x20; const STAGE_3_DST: *mut u8 = 0x0010_0000 as *mut u8; // 1MiB (typically 14MiB accessible here) const STAGE_4_DST: *mut u8 = 0x0020_0000 as *mut u8; // 2MiB (typically still 13MiB accessible here) const KERNEL_DST: *mut u8 = 0x0100_0000 as *mut u8; // 16MiB +const RAMDISK_DST: *mut u8 = 0x0400_0000 as *mut u8; // 64MiB static mut DISK_BUFFER: AlignedArrayBuffer<0x4000> = AlignedArrayBuffer { buffer: [0; 0x4000], @@ -98,6 +99,17 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { writeln!(screen::Writer, "loading kernel...").unwrap(); let kernel_len = load_file("kernel-x86_64", KERNEL_DST, &mut fs, &mut disk, disk_buffer); writeln!(screen::Writer, "kernel loaded at {KERNEL_DST:#p}").unwrap(); + writeln!(screen::Writer, "Loading ramdisk...").unwrap(); + let ramdisk_len = match try_load_file("ramdisk", RAMDISK_DST, &mut fs, &mut disk, disk_buffer) { + Some(s) => s, + None => 0u64, + }; + + if ramdisk_len == 0 { + writeln!(screen::Writer, "No ramdisk found, skipping.").unwrap(); + } else { + writeln!(screen::Writer, "Loaded ramdisk at {RAMDISK_DST:#p}").unwrap(); + } let memory_map = unsafe { memory_map::query_memory_map() }.unwrap(); writeln!(screen::Writer, "{memory_map:x?}").unwrap(); @@ -129,6 +141,10 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { start: KERNEL_DST as u64, len: kernel_len, }, + ramdisk: Region { + start: RAMDISK_DST as u64, + len: ramdisk_len, + }, memory_map_addr: memory_map.as_mut_ptr() as u32, memory_map_len: memory_map.len().try_into().unwrap(), framebuffer: BiosFramebufferInfo { @@ -151,17 +167,16 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { } } -fn load_file( +fn try_load_file( file_name: &str, dst: *mut u8, fs: &mut fat::FileSystem, disk: &mut disk::DiskAccess, disk_buffer: &mut AlignedArrayBuffer<16384>, -) -> u64 { +) -> Option { let disk_buffer_size = disk_buffer.buffer.len(); - let file = fs - .find_file_in_root_dir(file_name, disk_buffer) - .expect("file not found"); + let file = fs.find_file_in_root_dir(file_name, disk_buffer)?; + let file_size = file.file_size().into(); let mut total_offset = 0; @@ -195,7 +210,17 @@ fn load_file( total_offset += usize::try_from(len).unwrap(); } } - file_size + Some(file_size) +} + +fn load_file( + file_name: &str, + dst: *mut u8, + fs: &mut fat::FileSystem, + disk: &mut disk::DiskAccess, + disk_buffer: &mut AlignedArrayBuffer<16384>, +) -> u64 { + try_load_file(file_name, dst, fs, disk, disk_buffer).expect("file not found") } /// Taken from https://github.com/rust-lang/rust/blob/e100ec5bc7cd768ec17d75448b29c9ab4a39272b/library/core/src/slice/mod.rs#L1673-L1677 diff --git a/bios/stage-4/src/main.rs b/bios/stage-4/src/main.rs index d5685e9d..ddc45eef 100644 --- a/bios/stage-4/src/main.rs +++ b/bios/stage-4/src/main.rs @@ -56,13 +56,21 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { PhysAddr::new(info.kernel.start) }; let kernel_size = info.kernel.len; - let mut frame_allocator = { + let mut frame_allocator = if info.ramdisk.start == 0 { let kernel_end = PhysFrame::containing_address(kernel_start + kernel_size - 1u64); let next_free = kernel_end + 1; LegacyFrameAllocator::new_starting_at( next_free, memory_map.iter().copied().map(MemoryRegion), ) + } else { + let ramdisk_end = + PhysFrame::containing_address(PhysAddr::new(info.ramdisk.start + info.ramdisk.len)); + let next_free = ramdisk_end + 1; + LegacyFrameAllocator::new_starting_at( + next_free, + memory_map.iter().copied().map(MemoryRegion), + ) }; // We identity-mapped all memory, so the offset between physical and virtual addresses is 0 @@ -126,6 +134,11 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { info: framebuffer_info, }), rsdp_addr: detect_rsdp(), + ramdisk_addr: match info.ramdisk.len { + 0 => None, + _ => Some(info.ramdisk.start), + }, + ramdisk_len: info.ramdisk.len, }; load_and_switch_to_kernel(kernel, frame_allocator, page_tables, system_info); diff --git a/common/src/lib.rs b/common/src/lib.rs index b431215e..93c63fb6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -77,6 +77,8 @@ pub struct SystemInfo { pub framebuffer: Option, /// Address of the _Root System Description Pointer_ structure of the ACPI standard. pub rsdp_addr: Option, + pub ramdisk_addr: Option, + pub ramdisk_len: u64, } /// The physical address of the framebuffer and information about the framebuffer. @@ -137,6 +139,7 @@ where &mut page_tables, system_info.framebuffer.as_ref(), &config, + &system_info, ); let boot_info = create_boot_info( &config, @@ -168,6 +171,7 @@ pub fn set_up_mappings( page_tables: &mut PageTables, framebuffer: Option<&RawFrameBufferInfo>, config: &BootloaderConfig, + system_info: &SystemInfo, ) -> Mappings where I: ExactSizeIterator + Clone, @@ -199,7 +203,6 @@ where ) .expect("no entry point"); log::info!("Entry point at: {:#x}", entry_point.as_u64()); - // create a stack let stack_start_addr = mapping_addr( config.mappings.kernel_stack, @@ -284,6 +287,39 @@ where } else { None }; + let ramdisk_slice_len = system_info.ramdisk_len; + let ramdisk_slice_start = if let Some(ramdisk_address) = system_info.ramdisk_addr { + let ramdisk_address_start = mapping_addr( + config.mappings.ramdisk_memory, + system_info.ramdisk_len, + 8, + &mut used_entries, + ); + 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) + 1; + let ramdisk_physical_end_page = ramdisk_physical_start_page + ramdisk_page_count; + let start_page = Page::containing_address(ramdisk_address_start); + + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + for (i, frame) in + PhysFrame::range_inclusive(ramdisk_physical_start_page, ramdisk_physical_end_page) + .enumerate() + { + let page = start_page + i as u64; + match unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } { + Ok(tlb) => tlb.ignore(), + Err(err) => panic!( + "Failed to map page {:?} to frame {:?}: {:?}", + page, frame, err + ), + }; + } + Some(ramdisk_address_start) + } else { + None + }; let physical_memory_offset = if let Some(mapping) = config.mappings.physical_memory { log::info!("Map physical memory"); @@ -358,6 +394,8 @@ where kernel_slice_start, kernel_slice_len, + ramdisk_slice_start, + ramdisk_slice_len, } } @@ -383,6 +421,8 @@ pub struct Mappings { pub kernel_slice_start: u64, /// Size of the kernel slice allocation in memory. pub kernel_slice_len: u64, + pub ramdisk_slice_start: Option, + pub ramdisk_slice_len: u64, } /// Allocates and initializes the boot info struct and the memory map. @@ -492,6 +532,11 @@ where info.recursive_index = mappings.recursive_index.map(Into::into).into(); info.rsdp_addr = system_info.rsdp_addr.map(|addr| addr.as_u64()).into(); info.tls_template = mappings.tls_template.into(); + info.ramdisk_addr = mappings + .ramdisk_slice_start + .map(|addr| addr.as_u64()) + .into(); + info.ramdisk_len = mappings.ramdisk_slice_len; info }); diff --git a/src/lib.rs b/src/lib.rs index 11d1ebf0..a650d6f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,14 @@ mod mbr; mod pxe; const KERNEL_FILE_NAME: &str = "kernel-x86_64"; +const RAMDISK_FILE_NAME: &str = "ramdisk"; const BIOS_STAGE_3: &str = "boot-stage-3"; const BIOS_STAGE_4: &str = "boot-stage-4"; /// Create disk images for booting on legacy BIOS systems. pub struct BiosBoot { kernel: PathBuf, + ramdisk: Option, } impl BiosBoot { @@ -30,9 +32,16 @@ impl BiosBoot { pub fn new(kernel_path: &Path) -> Self { Self { kernel: kernel_path.to_owned(), + ramdisk: None, } } + /// Add a ramdisk file to the image + pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { + self.ramdisk = Some(ramdisk_path.to_owned()); + self + } + /// Create a bootable UEFI disk image at the given path. pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH")); @@ -61,12 +70,15 @@ impl BiosBoot { fn create_fat_partition(&self) -> anyhow::Result { let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH")); let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH")); + let kernel_path = self.kernel.as_path(); let mut files = BTreeMap::new(); - files.insert(KERNEL_FILE_NAME, self.kernel.as_path()); + files.insert(KERNEL_FILE_NAME, kernel_path); files.insert(BIOS_STAGE_3, stage_3_path); files.insert(BIOS_STAGE_4, stage_4_path); - + if let Some(ramdisk_path) = &self.ramdisk { + files.insert(RAMDISK_FILE_NAME, ramdisk_path); + } let out_file = NamedTempFile::new().context("failed to create temp file")?; fat::create_fat_filesystem(files, out_file.path()) .context("failed to create BIOS FAT filesystem")?; @@ -78,6 +90,7 @@ impl BiosBoot { /// Create disk images for booting on UEFI systems. pub struct UefiBoot { kernel: PathBuf, + ramdisk: Option, } impl UefiBoot { @@ -85,9 +98,16 @@ impl UefiBoot { pub fn new(kernel_path: &Path) -> Self { Self { kernel: kernel_path.to_owned(), + ramdisk: None, } } + /// Add a ramdisk file to the disk image + pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { + self.ramdisk = Some(ramdisk_path.to_owned()); + self + } + /// Create a bootable BIOS disk image at the given path. pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { let fat_partition = self @@ -111,9 +131,17 @@ impl UefiBoot { /// bootloader won't be found. pub fn create_pxe_tftp_folder(&self, out_path: &Path) -> anyhow::Result<()> { let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); - - pxe::create_uefi_tftp_folder(bootloader_path, self.kernel.as_path(), out_path) - .context("failed to create UEFI PXE tftp folder")?; + let ramdisk_path = match self.ramdisk.as_ref() { + Some(rd) => Some(rd.as_path()), + None => None, + }; + pxe::create_uefi_tftp_folder( + bootloader_path, + self.kernel.as_path(), + ramdisk_path, + out_path, + ) + .context("failed to create UEFI PXE tftp folder")?; Ok(()) } @@ -121,10 +149,14 @@ impl UefiBoot { /// Creates an UEFI-bootable FAT partition with the kernel. fn create_fat_partition(&self) -> anyhow::Result { let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); - + let kernel_path = self.kernel.as_path(); let mut files = BTreeMap::new(); files.insert("efi/boot/bootx64.efi", bootloader_path); - files.insert(KERNEL_FILE_NAME, self.kernel.as_path()); + files.insert(KERNEL_FILE_NAME, kernel_path); + + if let Some(ramdisk_path) = &self.ramdisk { + files.insert(RAMDISK_FILE_NAME, ramdisk_path); + } let out_file = NamedTempFile::new().context("failed to create temp file")?; fat::create_fat_filesystem(files, out_file.path()) diff --git a/src/pxe.rs b/src/pxe.rs index 9329cec2..84c7b0d6 100644 --- a/src/pxe.rs +++ b/src/pxe.rs @@ -5,6 +5,7 @@ use anyhow::Context; pub fn create_uefi_tftp_folder( bootloader_path: &Path, kernel_binary: &Path, + ramdisk_path: Option<&Path>, out_path: &Path, ) -> anyhow::Result<()> { std::fs::create_dir_all(out_path) @@ -27,6 +28,16 @@ pub fn create_uefi_tftp_folder( to.display() ) })?; + let to = out_path.join("ramdisk"); + if let Some(rp) = ramdisk_path { + std::fs::copy(rp, &to).with_context(|| { + format!( + "failed to copy ramdisk from {} to {}", + rp.display(), + to.display() + ) + })?; + } Ok(()) } diff --git a/tests/default_settings.rs b/tests/default_settings.rs index d610508c..4fa5f5f3 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -2,21 +2,24 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_basic_boot" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_basic_boot"), + Some("tests/ramdisk.txt"), + ); } #[test] fn should_panic() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_should_panic" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_should_panic"), + None, + ); } #[test] fn check_boot_info() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_check_boot_info" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_check_boot_info"), + None, + ); } diff --git a/tests/higher_half.rs b/tests/higher_half.rs index c2b9ac91..ca5b2a47 100644 --- a/tests/higher_half.rs +++ b/tests/higher_half.rs @@ -2,24 +2,32 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_basic_boot")); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_basic_boot"), + None, + ); } #[test] fn should_panic() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_should_panic")); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_should_panic"), + None, + ); } #[test] fn check_boot_info() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_check_boot_info" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_check_boot_info"), + None, + ); } #[test] fn verify_higher_half() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_verify_higher_half" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_verify_higher_half"), + None, + ); } diff --git a/tests/lto.rs b/tests/lto.rs index 00cfe60f..fd3854f5 100644 --- a/tests/lto.rs +++ b/tests/lto.rs @@ -21,5 +21,5 @@ fn basic_boot() { .join("basic_boot"); assert!(kernel_path.exists()); - run_test_kernel(kernel_path.as_path().to_str().unwrap()); + run_test_kernel(kernel_path.as_path().to_str().unwrap(), None); } diff --git a/tests/map_phys_mem.rs b/tests/map_phys_mem.rs index b19ba987..97252764 100644 --- a/tests/map_phys_mem.rs +++ b/tests/map_phys_mem.rs @@ -2,14 +2,16 @@ use bootloader_test_runner::run_test_kernel; #[test] fn check_boot_info() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_check_boot_info" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_check_boot_info"), + None, + ); } #[test] fn access_phys_mem() { - run_test_kernel(env!( - "CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_access_phys_mem" - )); + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_access_phys_mem"), + None, + ); } diff --git a/tests/pie.rs b/tests/pie.rs index c2d30d80..0801d3e1 100644 --- a/tests/pie.rs +++ b/tests/pie.rs @@ -2,20 +2,20 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_basic_boot")); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_basic_boot"), None); } #[test] fn should_panic() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_should_panic")); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_should_panic"), None); } #[test] fn check_boot_info() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_check_boot_info")); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_check_boot_info"), None); } #[test] fn global_variable() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_global_variable")); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_global_variable"), None); } diff --git a/tests/ramdisk.rs b/tests/ramdisk.rs new file mode 100644 index 00000000..7aa46a1a --- /dev/null +++ b/tests/ramdisk.rs @@ -0,0 +1,18 @@ +use bootloader_test_runner::run_test_kernel; +static RAMDISK_PATH: &str = "tests/ramdisk.txt"; + +#[test] +fn basic_boot() { + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_basic_boot"), + Some(RAMDISK_PATH), + ); +} + +#[test] +fn check_ramdisk() { + run_test_kernel( + env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_ramdisk"), + Some(RAMDISK_PATH), + ); +} diff --git a/tests/ramdisk.txt b/tests/ramdisk.txt new file mode 100644 index 00000000..09e6dbc1 --- /dev/null +++ b/tests/ramdisk.txt @@ -0,0 +1 @@ +Test ramdisk. \ No newline at end of file diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index 7f50cd42..a9c785fc 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -8,20 +8,32 @@ const QEMU_ARGS: &[&str] = &[ "-display", "none", "--no-reboot", + "-m", + "size=2048", ]; -pub fn run_test_kernel(kernel_binary_path: &str) { +pub fn run_test_kernel(kernel_binary_path: &str, ramdisk_path: Option<&str>) { let kernel_path = Path::new(kernel_binary_path); + let ramdisk_path = match ramdisk_path { + Some(rdp) => Some(Path::new(rdp)), + None => None, + }; // create an MBR disk image for legacy BIOS booting let mbr_path = kernel_path.with_extension("mbr"); - bootloader::BiosBoot::new(kernel_path) - .create_disk_image(&mbr_path) - .unwrap(); + let mut bios_builder = bootloader::BiosBoot::new(kernel_path); // create a GPT disk image for UEFI booting let gpt_path = kernel_path.with_extension("gpt"); - let uefi_builder = bootloader::UefiBoot::new(kernel_path); + let mut uefi_builder = bootloader::UefiBoot::new(kernel_path); + + // Set ramdisk for test, if supplied. + if let Some(rdp) = ramdisk_path { + bios_builder.set_ramdisk(rdp); + uefi_builder.set_ramdisk(rdp); + } + + bios_builder.create_disk_image(&mbr_path).unwrap(); uefi_builder.create_disk_image(&gpt_path).unwrap(); // create a TFTP folder with the kernel executable and UEFI bootloader for diff --git a/tests/test_kernels/default_settings/src/bin/basic_boot.rs b/tests/test_kernels/default_settings/src/bin/basic_boot.rs index e6bd3a0b..8924e1c0 100644 --- a/tests/test_kernels/default_settings/src/bin/basic_boot.rs +++ b/tests/test_kernels/default_settings/src/bin/basic_boot.rs @@ -2,11 +2,13 @@ #![no_main] // disable all Rust-level entry points use bootloader_api::{entry_point, BootInfo}; -use test_kernel_default_settings::{exit_qemu, QemuExitCode}; +use core::fmt::Write; +use test_kernel_default_settings::{exit_qemu, serial, QemuExitCode}; entry_point!(kernel_main); -fn kernel_main(_boot_info: &'static mut BootInfo) -> ! { +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + writeln!(serial(), "Entered kernel with boot info: {:?}", boot_info).unwrap(); exit_qemu(QemuExitCode::Success); } @@ -14,8 +16,6 @@ fn kernel_main(_boot_info: &'static mut BootInfo) -> ! { #[panic_handler] #[cfg(not(test))] fn panic(info: &core::panic::PanicInfo) -> ! { - use core::fmt::Write; - - let _ = writeln!(test_kernel_default_settings::serial(), "PANIC: {}", info); + let _ = writeln!(serial(), "PANIC: {}", info); exit_qemu(QemuExitCode::Failed); } diff --git a/tests/test_kernels/ramdisk/Cargo.toml b/tests/test_kernels/ramdisk/Cargo.toml new file mode 100644 index 00000000..4258b0a5 --- /dev/null +++ b/tests/test_kernels/ramdisk/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test_kernel_ramdisk" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2021" + +[dependencies] +bootloader_api = { path = "../../../api" } +x86_64 = { version = "0.14.7", default-features = false, features = [ + "instructions", + "inline_asm", +] } +uart_16550 = "0.2.10" diff --git a/tests/test_kernels/ramdisk/src/bin/basic_boot.rs b/tests/test_kernels/ramdisk/src/bin/basic_boot.rs new file mode 100644 index 00000000..515cd22d --- /dev/null +++ b/tests/test_kernels/ramdisk/src/bin/basic_boot.rs @@ -0,0 +1,21 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader_api::{entry_point, BootInfo}; +use test_kernel_ramdisk::{exit_qemu, QemuExitCode}; + +entry_point!(kernel_main); + +fn kernel_main(_boot_info: &'static mut BootInfo) -> ! { + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[panic_handler] +#[cfg(not(test))] +fn panic(info: &core::panic::PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(test_kernel_ramdisk::serial(), "PANIC: {}", info); + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/ramdisk/src/bin/ramdisk.rs b/tests/test_kernels/ramdisk/src/bin/ramdisk.rs new file mode 100644 index 00000000..d64d5592 --- /dev/null +++ b/tests/test_kernels/ramdisk/src/bin/ramdisk.rs @@ -0,0 +1,34 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader_api::{entry_point, BootInfo}; +use core::{fmt::Write, ptr::slice_from_raw_parts}; +use test_kernel_ramdisk::{exit_qemu, serial, QemuExitCode, RAMDISK_CONTENTS}; + +entry_point!(kernel_main); + +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + writeln!(serial(), "Boot info: {:?}", boot_info).unwrap(); + assert!(boot_info.ramdisk_addr.into_option().is_some()); + assert_eq!(boot_info.ramdisk_len as usize, RAMDISK_CONTENTS.len()); + 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) -> ! { + use core::fmt::Write; + + let _ = writeln!(test_kernel_ramdisk::serial(), "PANIC: {}", info); + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/ramdisk/src/lib.rs b/tests/test_kernels/ramdisk/src/lib.rs new file mode 100644 index 00000000..00ea92a6 --- /dev/null +++ b/tests/test_kernels/ramdisk/src/lib.rs @@ -0,0 +1,29 @@ +#![no_std] + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub static RAMDISK_CONTENTS: &[u8] = include_bytes!("../../../ramdisk.txt"); + +pub fn exit_qemu(exit_code: QemuExitCode) -> ! { + use x86_64::instructions::{nop, port::Port}; + + unsafe { + let mut port = Port::new(0xf4); + port.write(exit_code as u32); + } + + loop { + nop(); + } +} + +pub fn serial() -> uart_16550::SerialPort { + let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; + port.init(); + port +} diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index a2dfe632..a637db6e 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -12,5 +12,5 @@ repository.workspace = true bootloader_api = { workspace = true } bootloader-x86_64-common = { workspace = true } log = "0.4.14" -uefi = "0.16.0" +uefi = "0.18.0" x86_64 = "0.14.8" diff --git a/uefi/src/main.rs b/uefi/src/main.rs index 0f7cd469..f23df5ff 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -8,7 +8,12 @@ use bootloader_api::{info::FrameBufferInfo, BootloaderConfig}; use bootloader_x86_64_common::{ legacy_memory_region::LegacyFrameAllocator, Kernel, RawFrameBufferInfo, SystemInfo, }; -use core::{cell::UnsafeCell, fmt::Write, mem, ptr, slice}; +use core::{ + cell::UnsafeCell, + fmt::Write, + ops::{Deref, DerefMut}, + ptr, slice, +}; use uefi::{ prelude::{entry, Boot, Handle, Status, SystemTable}, proto::{ @@ -23,9 +28,10 @@ use uefi::{ pxe::{BaseCode, DhcpV4Packet}, IpAddress, }, + ProtocolPointer, }, table::boot::{ - AllocateType, MemoryDescriptor, MemoryType, OpenProtocolAttributes, OpenProtocolParams, + AllocateType, MemoryType, OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, }, CStr16, CStr8, }; @@ -63,39 +69,77 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { fn main_inner(image: Handle, mut st: SystemTable) -> Status { // temporarily clone the y table for printing panics + unsafe { *SYSTEM_TABLE.get() = Some(st.unsafe_clone()); } + let stdout = unsafe { &mut *SYSTEM_TABLE.get() }; + let stdout = stdout.as_mut().unwrap(); + let stdout = stdout.stdout(); + stdout.clear().unwrap(); + writeln!(stdout, "UEFI bootloader started; trying to load kernel").unwrap(); + + let mut boot_mode = BootMode::Disk; + let mut kernel = load_kernel(image, &mut st, boot_mode); + if kernel.is_none() { + writeln!( + stdout, + "Failed to load kernel via {:?}, trying TFTP", + boot_mode + ) + .unwrap(); + // Try TFTP boot + boot_mode = BootMode::Tftp; + kernel = load_kernel(image, &mut st, boot_mode); + } + let kernel = kernel.expect("Failed to load kernel"); + writeln!(stdout, "Trying to load ramdisk via {:?}", boot_mode).unwrap(); + // Ramdisk must load from same source, or not at all. + let ramdisk = load_ramdisk(image, &mut st, boot_mode); - st.stdout().clear().unwrap(); writeln!( - st.stdout(), - "UEFI bootloader started; trying to load kernel" + stdout, + "{}", + match ramdisk { + Some(_) => "Loaded ramdisk", + None => "Ramdisk not found.", + } ) .unwrap(); - let kernel = load_kernel(image, &st); - let framebuffer = init_logger(&st, kernel.config); - - // we no longer need the system table for printing panics unsafe { *SYSTEM_TABLE.get() = None; } - log::info!("UEFI bootloader started"); log::info!("Reading kernel and configuration from disk was successful"); if let Some(framebuffer) = framebuffer { log::info!("Using framebuffer at {:#x}", framebuffer.addr); } - let mmap_storage = { - let max_mmap_size = - st.boot_services().memory_map_size().map_size + 8 * mem::size_of::(); - let ptr = st - .boot_services() - .allocate_pool(MemoryType::LOADER_DATA, max_mmap_size)?; - unsafe { slice::from_raw_parts_mut(ptr, max_mmap_size) } + let mut memory_map_size = st.boot_services().memory_map_size(); + let mut target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); + let mut storage: &mut [u8]; + loop { + let ptr = st + .boot_services() + .allocate_pool(MemoryType::LOADER_DATA, target_size) + .expect("Failed to allocate memory for mmap storage"); + storage = unsafe { slice::from_raw_parts_mut(ptr, target_size) }; + if let Err(_) = st.boot_services().memory_map(storage) { + memory_map_size = st.boot_services().memory_map_size(); + // By measuring the size here, we can find out exactly how much we need. + // We may hit this code twice, if the map allocation ends up spanning more pages. + let next_target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); + target_size = next_target_size; + st.boot_services() + .free_pool(ptr) + .expect("Failed to free temporary memory for memory map!"); + continue; + } + break; + } + storage }; log::trace!("exiting boot services"); @@ -107,7 +151,13 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { LegacyFrameAllocator::new(memory_map.copied().map(UefiMemoryDescriptor)); let page_tables = create_page_tables(&mut frame_allocator); - + let mut ramdisk_len = 0u64; + let ramdisk_addr = if let Some(rd) = ramdisk { + ramdisk_len = rd.len() as u64; + Some(rd.as_ptr() as usize as u64) + } else { + None + }; let system_info = SystemInfo { framebuffer, rsdp_addr: { @@ -120,6 +170,8 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { .or_else(|| config_entries.find(|entry| matches!(entry.guid, cfg::ACPI_GUID))); rsdp.map(|entry| PhysAddr::new(entry.address as u64)) }, + ramdisk_addr: ramdisk_addr, + ramdisk_len: ramdisk_len, }; bootloader_x86_64_common::load_and_switch_to_kernel( @@ -130,51 +182,101 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { ); } -fn load_kernel(image: Handle, st: &SystemTable) -> Kernel<'static> { - let kernel_slice = load_kernel_file(image, st).expect("couldn't find kernel"); - Kernel::parse(kernel_slice) +#[derive(Clone, Copy, Debug)] +pub enum BootMode { + Disk, + Tftp, } -/// Try to load a kernel file from the boot device. -fn load_kernel_file(image: Handle, st: &SystemTable) -> Option<&'static mut [u8]> { - load_kernel_file_from_disk(image, st) - .or_else(|| load_kernel_file_from_tftp_boot_server(image, st)) +fn load_ramdisk( + image: Handle, + st: &mut SystemTable, + boot_mode: BootMode, +) -> Option<&'static mut [u8]> { + load_file_from_boot_method(image, st, "ramdisk\0", boot_mode) } -fn load_kernel_file_from_disk(image: Handle, st: &SystemTable) -> Option<&'static mut [u8]> { - let file_system_raw = { - let this = st.boot_services(); - let loaded_image = this - .open_protocol::( - OpenProtocolParams { - handle: image, - agent: image, - controller: None, - }, - OpenProtocolAttributes::Exclusive, - ) - .expect("Failed to retrieve `LoadedImage` protocol from handle"); - let loaded_image = unsafe { &*loaded_image.interface.get() }; +fn load_kernel( + image: Handle, + st: &mut SystemTable, + boot_mode: BootMode, +) -> Option> { + let kernel_slice = load_file_from_boot_method(image, st, "kernel-x86_64\0", boot_mode)?; + Some(Kernel::parse(kernel_slice)) +} - let device_handle = loaded_image.device(); +fn load_file_from_boot_method( + image: Handle, + st: &mut SystemTable, + filename: &str, + boot_mode: BootMode, +) -> Option<&'static mut [u8]> { + match boot_mode { + BootMode::Disk => load_file_from_disk(filename, image, st), + BootMode::Tftp => load_file_from_tftp_boot_server(filename, image, st), + } +} - let device_path = this - .open_protocol::( - OpenProtocolParams { - handle: device_handle, - agent: image, - controller: None, - }, - OpenProtocolAttributes::Exclusive, - ) - .expect("Failed to retrieve `DevicePath` protocol from image's device handle"); - let mut device_path = unsafe { &*device_path.interface.get() }; +fn open_device_path_protocol( + image: Handle, + st: &SystemTable, +) -> Option> { + let this = st.boot_services(); + let loaded_image = unsafe { + this.open_protocol::( + OpenProtocolParams { + handle: image, + agent: image, + controller: None, + }, + OpenProtocolAttributes::Exclusive, + ) + }; - let fs_handle = this - .locate_device_path::(&mut device_path) - .ok()?; + if loaded_image.is_err() { + log::error!("Failed to open protocol LoadedImage"); + return None; + } + let loaded_image = loaded_image.unwrap(); + let loaded_image = loaded_image.deref(); + + let device_handle = loaded_image.device(); - this.open_protocol::( + let device_path = unsafe { + this.open_protocol::( + OpenProtocolParams { + handle: device_handle, + agent: image, + controller: None, + }, + OpenProtocolAttributes::Exclusive, + ) + }; + if device_path.is_err() { + log::error!("Failed to open protocol DevicePath"); + return None; + } + Some(device_path.unwrap()) +} + +fn locate_and_open_protocol( + image: Handle, + st: &SystemTable, +) -> Option> { + let this = st.boot_services(); + let mut device_path = open_device_path_protocol(image, st)?; + let mut device_path = device_path.deref(); + + let fs_handle = this.locate_device_path::

(&mut device_path); + if fs_handle.is_err() { + log::error!("Failed to open device path"); + return None; + } + + let fs_handle = fs_handle.unwrap(); + + let opened_handle = unsafe { + this.open_protocol::

( OpenProtocolParams { handle: fs_handle, agent: image, @@ -182,121 +284,101 @@ fn load_kernel_file_from_disk(image: Handle, st: &SystemTable) -> Option<& }, OpenProtocolAttributes::Exclusive, ) + }; + + if opened_handle.is_err() { + log::error!("Failed to open protocol {}", core::any::type_name::

()); + return None; } - .unwrap(); - let file_system = unsafe { &mut *file_system_raw.interface.get() }; + Some(opened_handle.unwrap()) +} + +fn load_file_from_disk( + name: &str, + image: Handle, + st: &SystemTable, +) -> Option<&'static mut [u8]> { + let mut file_system_raw = locate_and_open_protocol::(image, st)?; + let file_system = file_system_raw.deref_mut(); let mut root = file_system.open_volume().unwrap(); - let mut buf = [0; 14 * 2]; - let filename = CStr16::from_str_with_buf("kernel-x86_64", &mut buf).unwrap(); - let kernel_file_handle = root - .open(filename, FileMode::Read, FileAttribute::empty()) - .expect("Failed to load kernel (expected file named `kernel-x86_64`)"); - let mut kernel_file = match kernel_file_handle.into_type().unwrap() { + let mut buf = [0u16; 256]; + assert!(name.len() < 256); + let filename = CStr16::from_str_with_buf(name.trim_end_matches('\0'), &mut buf) + .expect("Failed to convert string to utf16"); + + let file_handle_result = root.open(filename, FileMode::Read, FileAttribute::empty()); + + if file_handle_result.is_err() { + return None; + } + + let file_handle = file_handle_result.unwrap(); + + let mut file = match file_handle.into_type().unwrap() { uefi::proto::media::file::FileType::Regular(f) => f, uefi::proto::media::file::FileType::Dir(_) => panic!(), }; let mut buf = [0; 500]; - let kernel_info: &mut FileInfo = kernel_file.get_info(&mut buf).unwrap(); - let kernel_size = usize::try_from(kernel_info.file_size()).unwrap(); + let file_info: &mut FileInfo = file.get_info(&mut buf).unwrap(); + let file_size = usize::try_from(file_info.file_size()).unwrap(); - let kernel_ptr = st + let file_ptr = st .boot_services() .allocate_pages( AllocateType::AnyPages, MemoryType::LOADER_DATA, - ((kernel_size - 1) / 4096) + 1, + ((file_size - 1) / 4096) + 1, ) .unwrap() as *mut u8; - unsafe { ptr::write_bytes(kernel_ptr, 0, kernel_size) }; - let kernel_slice = unsafe { slice::from_raw_parts_mut(kernel_ptr, kernel_size) }; - kernel_file.read(kernel_slice).unwrap(); + unsafe { ptr::write_bytes(file_ptr, 0, file_size) }; + let file_slice = unsafe { slice::from_raw_parts_mut(file_ptr, file_size) }; + file.read(file_slice).unwrap(); - Some(kernel_slice) + Some(file_slice) } /// Try to load a kernel from a TFTP boot server. -fn load_kernel_file_from_tftp_boot_server( +fn load_file_from_tftp_boot_server( + name: &str, image: Handle, st: &SystemTable, ) -> Option<&'static mut [u8]> { - let this = st.boot_services(); - - // Try to locate a `BaseCode` protocol on the boot device. - - let loaded_image = this - .open_protocol::( - OpenProtocolParams { - handle: image, - agent: image, - controller: None, - }, - OpenProtocolAttributes::Exclusive, - ) - .expect("Failed to retrieve `LoadedImage` protocol from handle"); - let loaded_image = unsafe { &*loaded_image.interface.get() }; - - let device_handle = loaded_image.device(); - - let device_path = this - .open_protocol::( - OpenProtocolParams { - handle: device_handle, - agent: image, - controller: None, - }, - OpenProtocolAttributes::Exclusive, - ) - .expect("Failed to retrieve `DevicePath` protocol from image's device handle"); - let mut device_path = unsafe { &*device_path.interface.get() }; - - let base_code_handle = this.locate_device_path::(&mut device_path).ok()?; - - let base_code_raw = this - .open_protocol::( - OpenProtocolParams { - handle: base_code_handle, - agent: image, - controller: None, - }, - OpenProtocolAttributes::Exclusive, - ) - .unwrap(); - let base_code = unsafe { &mut *base_code_raw.interface.get() }; + let mut base_code_raw = locate_and_open_protocol::(image, st)?; + let base_code = base_code_raw.deref_mut(); // Find the TFTP boot server. let mode = base_code.mode(); assert!(mode.dhcp_ack_received); let dhcpv4: &DhcpV4Packet = mode.dhcp_ack.as_ref(); let server_ip = IpAddress::new_v4(dhcpv4.bootp_si_addr); + let mut buf = [0u8; 256]; + assert!(name.len() < 256); - let filename = CStr8::from_bytes_with_nul(b"kernel-x86_64\0").unwrap(); + let filename = CStr8::from_bytes_with_nul(name.as_bytes()).unwrap(); // Determine the kernel file size. - let file_size = base_code - .tftp_get_file_size(&server_ip, filename) - .expect("Failed to query the kernel file size"); - let kernel_size = - usize::try_from(file_size).expect("The kernel file size should fit into usize"); + let file_size = base_code.tftp_get_file_size(&server_ip, &filename).ok()?; + let kernel_size = usize::try_from(file_size).expect("The file size should fit into usize"); // Allocate some memory for the kernel file. - let kernel_ptr = st + let ptr = st .boot_services() .allocate_pages( AllocateType::AnyPages, MemoryType::LOADER_DATA, ((kernel_size - 1) / 4096) + 1, ) - .expect("Failed to allocate memory for the kernel file") as *mut u8; - let kernel_slice = unsafe { slice::from_raw_parts_mut(kernel_ptr, kernel_size) }; + .expect("Failed to allocate memory for the file") as *mut u8; + let slice = unsafe { slice::from_raw_parts_mut(ptr, kernel_size) }; // Load the kernel file. base_code - .tftp_read_file(&server_ip, filename, Some(kernel_slice)) + .tftp_read_file(&server_ip, &filename, Some(slice)) .expect("Failed to read kernel file from the TFTP boot server"); - Some(kernel_slice) + Some(slice) } /// Creates page table abstraction types for both the bootloader and kernel page tables. @@ -366,11 +448,22 @@ fn create_page_tables( } fn init_logger(st: &SystemTable, config: BootloaderConfig) -> Option { - let gop = st + let gop_handle = st .boot_services() - .locate_protocol::() + .get_handle_for_protocol::() .ok()?; - let gop = unsafe { &mut *gop.get() }; + let mut gop = unsafe { + st.boot_services() + .open_protocol::( + OpenProtocolParams { + handle: gop_handle, + agent: st.boot_services().image_handle(), + controller: None, + }, + OpenProtocolAttributes::Exclusive, + ) + .ok()? + }; let mode = { let modes = gop.modes(); From fecec3369d0cccba3fdc0e64e0cde7bb88a2420a Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Wed, 4 Jan 2023 10:44:01 -0500 Subject: [PATCH 02/17] Apply review comments Co-authored-by: Philipp Oppermann --- common/src/lib.rs | 7 ++++--- src/lib.rs | 7 ++----- uefi/src/main.rs | 39 +++++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index 93c63fb6..a7e08429 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -292,15 +292,16 @@ where let ramdisk_address_start = mapping_addr( config.mappings.ramdisk_memory, system_info.ramdisk_len, - 8, + Size4KiB::SIZE, &mut used_entries, ); 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) + 1; + let ramdisk_page_count = ((system_info.ramdisk_len - 1) / Size4KiB::SIZE) + 1; let ramdisk_physical_end_page = ramdisk_physical_start_page + ramdisk_page_count; - let start_page = Page::containing_address(ramdisk_address_start); + let start_page = Page::from_start_address(ramdisk_address_start) + .expect("the ramdisk start address must be page aligned"); let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; for (i, frame) in diff --git a/src/lib.rs b/src/lib.rs index a650d6f2..907a39ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,7 +108,7 @@ impl UefiBoot { self } - /// Create a bootable BIOS disk image at the given path. + /// Create a bootable UEFI disk image at the given path. pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { let fat_partition = self .create_fat_partition() @@ -131,10 +131,7 @@ impl UefiBoot { /// bootloader won't be found. pub fn create_pxe_tftp_folder(&self, out_path: &Path) -> anyhow::Result<()> { let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); - let ramdisk_path = match self.ramdisk.as_ref() { - Some(rd) => Some(rd.as_path()), - None => None, - }; + let ramdisk_path = self.ramdisk.as_deref(); pxe::create_uefi_tftp_folder( bootloader_path, self.kernel.as_path(), diff --git a/uefi/src/main.rs b/uefi/src/main.rs index f23df5ff..b7ee3a2c 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -119,27 +119,28 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { let mmap_storage = { let mut memory_map_size = st.boot_services().memory_map_size(); let mut target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); - let mut storage: &mut [u8]; loop { let ptr = st .boot_services() .allocate_pool(MemoryType::LOADER_DATA, target_size) .expect("Failed to allocate memory for mmap storage"); - storage = unsafe { slice::from_raw_parts_mut(ptr, target_size) }; - if let Err(_) = st.boot_services().memory_map(storage) { - memory_map_size = st.boot_services().memory_map_size(); - // By measuring the size here, we can find out exactly how much we need. - // We may hit this code twice, if the map allocation ends up spanning more pages. - let next_target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); - target_size = next_target_size; - st.boot_services() - .free_pool(ptr) - .expect("Failed to free temporary memory for memory map!"); - continue; + let storage = unsafe { slice::from_raw_parts_mut(ptr, target_size) }; + if st.boot_services().memory_map(storage).is_ok() { + break storage; } - break; + // allocated memory region was not big enough -> free it again + st.boot_services() + .free_pool(ptr) + .expect("Failed to free temporary memory for memory map!"); + + // By measuring the size here, we can find out exactly how much we need. + // We may hit this code twice, if the map allocation ends up spanning more pages. + memory_map_size = st.boot_services().memory_map_size(); + + let next_target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); + target_size = next_target_size; + } - storage }; log::trace!("exiting boot services"); @@ -309,11 +310,10 @@ fn load_file_from_disk( let file_handle_result = root.open(filename, FileMode::Read, FileAttribute::empty()); - if file_handle_result.is_err() { - return None; - } - - let file_handle = file_handle_result.unwrap(); + let file_handle = match file_handle_result { + Err(_) => return None, + Ok(handle) => handle, + }; let mut file = match file_handle.into_type().unwrap() { uefi::proto::media::file::FileType::Regular(f) => f, @@ -353,7 +353,6 @@ fn load_file_from_tftp_boot_server( assert!(mode.dhcp_ack_received); let dhcpv4: &DhcpV4Packet = mode.dhcp_ack.as_ref(); let server_ip = IpAddress::new_v4(dhcpv4.bootp_si_addr); - let mut buf = [0u8; 256]; assert!(name.len() < 256); let filename = CStr8::from_bytes_with_nul(name.as_bytes()).unwrap(); From 67819348f6660ae56af0717649bbbfcd50ba461b Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Wed, 4 Jan 2023 15:41:59 -0500 Subject: [PATCH 03/17] Measure memory map size before freeing memory to avoid an endless loop, and remove the extra unnecessary variable --- uefi/src/main.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/uefi/src/main.rs b/uefi/src/main.rs index b7ee3a2c..b0c3c00b 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -69,7 +69,6 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { fn main_inner(image: Handle, mut st: SystemTable) -> Status { // temporarily clone the y table for printing panics - unsafe { *SYSTEM_TABLE.get() = Some(st.unsafe_clone()); } @@ -118,28 +117,25 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { } let mmap_storage = { let mut memory_map_size = st.boot_services().memory_map_size(); - let mut target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); loop { let ptr = st .boot_services() - .allocate_pool(MemoryType::LOADER_DATA, target_size) + .allocate_pool(MemoryType::LOADER_DATA, memory_map_size.map_size) .expect("Failed to allocate memory for mmap storage"); - let storage = unsafe { slice::from_raw_parts_mut(ptr, target_size) }; + + let storage = unsafe { slice::from_raw_parts_mut(ptr, memory_map_size.map_size) }; + if st.boot_services().memory_map(storage).is_ok() { break storage; } - // allocated memory region was not big enough -> free it again - st.boot_services() - .free_pool(ptr) - .expect("Failed to free temporary memory for memory map!"); // By measuring the size here, we can find out exactly how much we need. // We may hit this code twice, if the map allocation ends up spanning more pages. memory_map_size = st.boot_services().memory_map_size(); - - let next_target_size = memory_map_size.map_size + (8 * memory_map_size.entry_size); - target_size = next_target_size; - + // allocated memory region was not big enough -> free it again + st.boot_services() + .free_pool(ptr) + .expect("Failed to free temporary memory for memory map!"); } }; @@ -265,7 +261,7 @@ fn locate_and_open_protocol( st: &SystemTable, ) -> Option> { let this = st.boot_services(); - let mut device_path = open_device_path_protocol(image, st)?; + let device_path = open_device_path_protocol(image, st)?; let mut device_path = device_path.deref(); let fs_handle = this.locate_device_path::

(&mut device_path); From 492f75ad1b9916f8f41d53275ef7038f9bb0948b Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Wed, 4 Jan 2023 16:01:14 -0500 Subject: [PATCH 04/17] Simplify math to caluclate the page count for the ramdisk Rather than replace range_inclusive, we can remove the nesting doll math from ramdisk_page_count instead. IMO this makes the code a bit more readable. --- common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index a7e08429..59d12e35 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -298,7 +298,7 @@ where 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) + 1; + let ramdisk_page_count = system_info.ramdisk_len / Size4KiB::SIZE; let ramdisk_physical_end_page = ramdisk_physical_start_page + ramdisk_page_count; let start_page = Page::from_start_address(ramdisk_address_start) .expect("the ramdisk start address must be page aligned"); From e0c3584b143631e64f48193a156b2c5e28dea016 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 07:35:23 -0500 Subject: [PATCH 05/17] Make end page exclusive when computing the end page for the ramdisk Co-authored-by: Philipp Oppermann --- common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index 59d12e35..ad92556f 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -298,7 +298,7 @@ where 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 / Size4KiB::SIZE; + let ramdisk_page_count = (system_info.ramdisk_len - 1) / Size4KiB::SIZE; let ramdisk_physical_end_page = ramdisk_physical_start_page + ramdisk_page_count; let start_page = Page::from_start_address(ramdisk_address_start) .expect("the ramdisk start address must be page aligned"); From ebf88b3e9d8c79a3843569b0cde5e59c7d30a8ca Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 07:35:43 -0500 Subject: [PATCH 06/17] Remove increased memory used to debug test failures Co-authored-by: Philipp Oppermann --- tests/runner/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index a9c785fc..02c6c5b8 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -8,8 +8,6 @@ const QEMU_ARGS: &[&str] = &[ "-display", "none", "--no-reboot", - "-m", - "size=2048", ]; pub fn run_test_kernel(kernel_binary_path: &str, ramdisk_path: Option<&str>) { From 2ed842cc2f2344d2cd3c4169c749b318be2b1df3 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 20:36:29 -0500 Subject: [PATCH 07/17] Update test runner to take an Option<&Path> instead of Option<&str> for the ramdisk path Co-authored-by: Philipp Oppermann --- tests/ramdisk.rs | 6 ++++-- tests/runner/src/lib.rs | 6 +----- tests/test_kernels/ramdisk/src/bin/ramdisk.rs | 2 -- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/ramdisk.rs b/tests/ramdisk.rs index 7aa46a1a..a32f0d66 100644 --- a/tests/ramdisk.rs +++ b/tests/ramdisk.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use bootloader_test_runner::run_test_kernel; static RAMDISK_PATH: &str = "tests/ramdisk.txt"; @@ -5,7 +7,7 @@ static RAMDISK_PATH: &str = "tests/ramdisk.txt"; fn basic_boot() { run_test_kernel( env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_basic_boot"), - Some(RAMDISK_PATH), + Some(&Path::new(RAMDISK_PATH)), ); } @@ -13,6 +15,6 @@ fn basic_boot() { fn check_ramdisk() { run_test_kernel( env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_ramdisk"), - Some(RAMDISK_PATH), + Some(&Path::new(RAMDISK_PATH)), ); } diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index 02c6c5b8..f5099f79 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -10,12 +10,8 @@ const QEMU_ARGS: &[&str] = &[ "--no-reboot", ]; -pub fn run_test_kernel(kernel_binary_path: &str, ramdisk_path: Option<&str>) { +pub fn run_test_kernel(kernel_binary_path: &str, ramdisk_path: Option<&Path>) { let kernel_path = Path::new(kernel_binary_path); - let ramdisk_path = match ramdisk_path { - Some(rdp) => Some(Path::new(rdp)), - None => None, - }; // create an MBR disk image for legacy BIOS booting let mbr_path = kernel_path.with_extension("mbr"); diff --git a/tests/test_kernels/ramdisk/src/bin/ramdisk.rs b/tests/test_kernels/ramdisk/src/bin/ramdisk.rs index d64d5592..d2bbda87 100644 --- a/tests/test_kernels/ramdisk/src/bin/ramdisk.rs +++ b/tests/test_kernels/ramdisk/src/bin/ramdisk.rs @@ -27,8 +27,6 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { #[cfg(not(test))] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - use core::fmt::Write; - let _ = writeln!(test_kernel_ramdisk::serial(), "PANIC: {}", info); exit_qemu(QemuExitCode::Failed); } From 89415ab9be3a46545cf22a08c50c6031c1019255 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 20:50:38 -0500 Subject: [PATCH 08/17] Remove test debugging changes. --- tests/default_settings.rs | 2 +- uefi/src/main.rs | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/default_settings.rs b/tests/default_settings.rs index 4fa5f5f3..5d5a6395 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -4,7 +4,7 @@ use bootloader_test_runner::run_test_kernel; fn basic_boot() { run_test_kernel( env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_basic_boot"), - Some("tests/ramdisk.txt"), + None ); } diff --git a/uefi/src/main.rs b/uefi/src/main.rs index b0c3c00b..2baa87ba 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -72,17 +72,14 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { unsafe { *SYSTEM_TABLE.get() = Some(st.unsafe_clone()); } - let stdout = unsafe { &mut *SYSTEM_TABLE.get() }; - let stdout = stdout.as_mut().unwrap(); - let stdout = stdout.stdout(); - stdout.clear().unwrap(); - writeln!(stdout, "UEFI bootloader started; trying to load kernel").unwrap(); + st.stdout().clear().unwrap(); + writeln!(st.stdout(), "UEFI bootloader started; trying to load kernel").unwrap(); let mut boot_mode = BootMode::Disk; let mut kernel = load_kernel(image, &mut st, boot_mode); if kernel.is_none() { writeln!( - stdout, + st.stdout(), "Failed to load kernel via {:?}, trying TFTP", boot_mode ) @@ -92,12 +89,12 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { kernel = load_kernel(image, &mut st, boot_mode); } let kernel = kernel.expect("Failed to load kernel"); - writeln!(stdout, "Trying to load ramdisk via {:?}", boot_mode).unwrap(); + writeln!(st.stdout(), "Trying to load ramdisk via {:?}", boot_mode).unwrap(); // Ramdisk must load from same source, or not at all. let ramdisk = load_ramdisk(image, &mut st, boot_mode); writeln!( - stdout, + st.stdout(), "{}", match ramdisk { Some(_) => "Loaded ramdisk", From e9d5eae31d3811bf439192bf37ee24049f2d0c84 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 20:53:03 -0500 Subject: [PATCH 09/17] Load ramdisk starting at the page following the kernel when booting via bios --- bios/stage-2/src/main.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bios/stage-2/src/main.rs b/bios/stage-2/src/main.rs index ca439af5..b2bc5e6a 100644 --- a/bios/stage-2/src/main.rs +++ b/bios/stage-2/src/main.rs @@ -27,7 +27,6 @@ const BOOTLOADER_SECOND_STAGE_PARTITION_TYPE: u8 = 0x20; const STAGE_3_DST: *mut u8 = 0x0010_0000 as *mut u8; // 1MiB (typically 14MiB accessible here) const STAGE_4_DST: *mut u8 = 0x0020_0000 as *mut u8; // 2MiB (typically still 13MiB accessible here) const KERNEL_DST: *mut u8 = 0x0100_0000 as *mut u8; // 16MiB -const RAMDISK_DST: *mut u8 = 0x0400_0000 as *mut u8; // 64MiB static mut DISK_BUFFER: AlignedArrayBuffer<0x4000> = AlignedArrayBuffer { buffer: [0; 0x4000], @@ -99,8 +98,10 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { writeln!(screen::Writer, "loading kernel...").unwrap(); let kernel_len = load_file("kernel-x86_64", KERNEL_DST, &mut fs, &mut disk, disk_buffer); writeln!(screen::Writer, "kernel loaded at {KERNEL_DST:#p}").unwrap(); + let kernel_page_size = (((kernel_len - 1) / 4096) + 1) as usize; + let ramdisk_start = KERNEL_DST.wrapping_add(kernel_page_size * 4096); writeln!(screen::Writer, "Loading ramdisk...").unwrap(); - let ramdisk_len = match try_load_file("ramdisk", RAMDISK_DST, &mut fs, &mut disk, disk_buffer) { + let ramdisk_len = match try_load_file("ramdisk", KERNEL_DST.wrapping_add(kernel_page_size * 4096), &mut fs, &mut disk, disk_buffer) { Some(s) => s, None => 0u64, }; @@ -108,7 +109,7 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { if ramdisk_len == 0 { writeln!(screen::Writer, "No ramdisk found, skipping.").unwrap(); } else { - writeln!(screen::Writer, "Loaded ramdisk at {RAMDISK_DST:#p}").unwrap(); + writeln!(screen::Writer, "Loaded ramdisk at {ramdisk_start:#p}").unwrap(); } let memory_map = unsafe { memory_map::query_memory_map() }.unwrap(); @@ -142,7 +143,7 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { len: kernel_len, }, ramdisk: Region { - start: RAMDISK_DST as u64, + start: ramdisk_start as u64, len: ramdisk_len, }, memory_map_addr: memory_map.as_mut_ptr() as u32, From 0b4a415095798c1735757abe532e72cf9258631b Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 20:57:30 -0500 Subject: [PATCH 10/17] Base ramdisk presence on length, not address. --- bios/stage-4/src/main.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/bios/stage-4/src/main.rs b/bios/stage-4/src/main.rs index ddc45eef..5fc29933 100644 --- a/bios/stage-4/src/main.rs +++ b/bios/stage-4/src/main.rs @@ -56,22 +56,14 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { PhysAddr::new(info.kernel.start) }; let kernel_size = info.kernel.len; - let mut frame_allocator = if info.ramdisk.start == 0 { - let kernel_end = PhysFrame::containing_address(kernel_start + kernel_size - 1u64); - let next_free = kernel_end + 1; - LegacyFrameAllocator::new_starting_at( - next_free, - memory_map.iter().copied().map(MemoryRegion), - ) - } else { - let ramdisk_end = - PhysFrame::containing_address(PhysAddr::new(info.ramdisk.start + info.ramdisk.len)); - let next_free = ramdisk_end + 1; - LegacyFrameAllocator::new_starting_at( - next_free, - memory_map.iter().copied().map(MemoryRegion), - ) + let next_free_frame = match info.ramdisk.len { + 0 => PhysFrame::containing_address(kernel_start + kernel_size - 1u64) + 1, + _ => PhysFrame::containing_address(PhysAddr::new(info.ramdisk.start + info.ramdisk.len)) + 1 }; + let mut frame_allocator = LegacyFrameAllocator::new_starting_at( + next_free_frame, + memory_map.iter().copied().map(MemoryRegion), + ); // We identity-mapped all memory, so the offset between physical and virtual addresses is 0 let phys_offset = VirtAddr::new(0); From 9a398184debf010fd1129ebc57f40641844b59ea Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 20:58:41 -0500 Subject: [PATCH 11/17] Subtract 1 from ramdisk end address to get an inclusive address --- bios/stage-4/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bios/stage-4/src/main.rs b/bios/stage-4/src/main.rs index 5fc29933..6dbc23f5 100644 --- a/bios/stage-4/src/main.rs +++ b/bios/stage-4/src/main.rs @@ -58,7 +58,7 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { let kernel_size = info.kernel.len; let next_free_frame = match info.ramdisk.len { 0 => PhysFrame::containing_address(kernel_start + kernel_size - 1u64) + 1, - _ => PhysFrame::containing_address(PhysAddr::new(info.ramdisk.start + info.ramdisk.len)) + 1 + _ => PhysFrame::containing_address(PhysAddr::new(info.ramdisk.start + info.ramdisk.len - 1u64)) + 1 }; let mut frame_allocator = LegacyFrameAllocator::new_starting_at( next_free_frame, From f5a4433acfc4ddd388f0e024706ead22411e379d Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 21:12:02 -0500 Subject: [PATCH 12/17] Pass image handle, instead of fetching from boot services. --- uefi/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uefi/src/main.rs b/uefi/src/main.rs index 2baa87ba..a11f90b7 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -103,7 +103,7 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { ) .unwrap(); - let framebuffer = init_logger(&st, kernel.config); + let framebuffer = init_logger(image, &st, kernel.config); unsafe { *SYSTEM_TABLE.get() = None; } @@ -439,7 +439,7 @@ fn create_page_tables( } } -fn init_logger(st: &SystemTable, config: BootloaderConfig) -> Option { +fn init_logger(image_handle: Handle, st: &SystemTable, config: BootloaderConfig) -> Option { let gop_handle = st .boot_services() .get_handle_for_protocol::() @@ -449,7 +449,7 @@ fn init_logger(st: &SystemTable, config: BootloaderConfig) -> Option( OpenProtocolParams { handle: gop_handle, - agent: st.boot_services().image_handle(), + agent: image_handle, controller: None, }, OpenProtocolAttributes::Exclusive, From 0a4b9c719506774cbbbe2a4379589b0d0445aa9b Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Thu, 5 Jan 2023 21:16:04 -0500 Subject: [PATCH 13/17] Run cargo fmt --- bios/stage-2/src/main.rs | 8 +++++++- bios/stage-4/src/main.rs | 6 +++++- tests/default_settings.rs | 2 +- uefi/src/main.rs | 12 ++++++++++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/bios/stage-2/src/main.rs b/bios/stage-2/src/main.rs index b2bc5e6a..a1ba8566 100644 --- a/bios/stage-2/src/main.rs +++ b/bios/stage-2/src/main.rs @@ -101,7 +101,13 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { let kernel_page_size = (((kernel_len - 1) / 4096) + 1) as usize; let ramdisk_start = KERNEL_DST.wrapping_add(kernel_page_size * 4096); writeln!(screen::Writer, "Loading ramdisk...").unwrap(); - let ramdisk_len = match try_load_file("ramdisk", KERNEL_DST.wrapping_add(kernel_page_size * 4096), &mut fs, &mut disk, disk_buffer) { + let ramdisk_len = match try_load_file( + "ramdisk", + KERNEL_DST.wrapping_add(kernel_page_size * 4096), + &mut fs, + &mut disk, + disk_buffer, + ) { Some(s) => s, None => 0u64, }; diff --git a/bios/stage-4/src/main.rs b/bios/stage-4/src/main.rs index 6dbc23f5..d9e10843 100644 --- a/bios/stage-4/src/main.rs +++ b/bios/stage-4/src/main.rs @@ -58,7 +58,11 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { let kernel_size = info.kernel.len; let next_free_frame = match info.ramdisk.len { 0 => PhysFrame::containing_address(kernel_start + kernel_size - 1u64) + 1, - _ => PhysFrame::containing_address(PhysAddr::new(info.ramdisk.start + info.ramdisk.len - 1u64)) + 1 + _ => { + PhysFrame::containing_address(PhysAddr::new( + info.ramdisk.start + info.ramdisk.len - 1u64, + )) + 1 + } }; let mut frame_allocator = LegacyFrameAllocator::new_starting_at( next_free_frame, diff --git a/tests/default_settings.rs b/tests/default_settings.rs index 5d5a6395..0a7e6c47 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -4,7 +4,7 @@ use bootloader_test_runner::run_test_kernel; fn basic_boot() { run_test_kernel( env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_basic_boot"), - None + None, ); } diff --git a/uefi/src/main.rs b/uefi/src/main.rs index a11f90b7..e6da4b50 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -73,7 +73,11 @@ fn main_inner(image: Handle, mut st: SystemTable) -> Status { *SYSTEM_TABLE.get() = Some(st.unsafe_clone()); } st.stdout().clear().unwrap(); - writeln!(st.stdout(), "UEFI bootloader started; trying to load kernel").unwrap(); + writeln!( + st.stdout(), + "UEFI bootloader started; trying to load kernel" + ) + .unwrap(); let mut boot_mode = BootMode::Disk; let mut kernel = load_kernel(image, &mut st, boot_mode); @@ -439,7 +443,11 @@ fn create_page_tables( } } -fn init_logger(image_handle: Handle, st: &SystemTable, config: BootloaderConfig) -> Option { +fn init_logger( + image_handle: Handle, + st: &SystemTable, + config: BootloaderConfig, +) -> Option { let gop_handle = st .boot_services() .get_handle_for_protocol::() From 1834c8495a501ebb466e4af0dadecc2b774477bc Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 9 Jan 2023 19:01:13 +0100 Subject: [PATCH 14/17] Fix method docs --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 907a39ef..5cf1ca0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,7 @@ impl BiosBoot { self } - /// Create a bootable UEFI disk image at the given path. + /// Create a bootable BIOS disk image at the given path. pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH")); let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH")); From 26fcf07835267638738e78ae272c36aad6f8f130 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 9 Jan 2023 19:08:13 +0100 Subject: [PATCH 15/17] Simplify test function --- tests/default_settings.rs | 21 +++++++++------------ tests/higher_half.rs | 24 ++++++++---------------- tests/lto.rs | 2 +- tests/map_phys_mem.rs | 14 ++++++-------- tests/pie.rs | 8 ++++---- tests/ramdisk.rs | 10 +++++----- tests/runner/src/lib.rs | 6 +++++- 7 files changed, 38 insertions(+), 47 deletions(-) diff --git a/tests/default_settings.rs b/tests/default_settings.rs index 0a7e6c47..d610508c 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -2,24 +2,21 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_basic_boot"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_basic_boot" + )); } #[test] fn should_panic() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_should_panic"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_should_panic" + )); } #[test] fn check_boot_info() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_check_boot_info"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_DEFAULT_SETTINGS_check_boot_info" + )); } diff --git a/tests/higher_half.rs b/tests/higher_half.rs index ca5b2a47..c2b9ac91 100644 --- a/tests/higher_half.rs +++ b/tests/higher_half.rs @@ -2,32 +2,24 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_basic_boot"), - None, - ); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_basic_boot")); } #[test] fn should_panic() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_should_panic"), - None, - ); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_should_panic")); } #[test] fn check_boot_info() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_check_boot_info"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_check_boot_info" + )); } #[test] fn verify_higher_half() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_verify_higher_half"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_HIGHER_HALF_verify_higher_half" + )); } diff --git a/tests/lto.rs b/tests/lto.rs index fd3854f5..00cfe60f 100644 --- a/tests/lto.rs +++ b/tests/lto.rs @@ -21,5 +21,5 @@ fn basic_boot() { .join("basic_boot"); assert!(kernel_path.exists()); - run_test_kernel(kernel_path.as_path().to_str().unwrap(), None); + run_test_kernel(kernel_path.as_path().to_str().unwrap()); } diff --git a/tests/map_phys_mem.rs b/tests/map_phys_mem.rs index 97252764..b19ba987 100644 --- a/tests/map_phys_mem.rs +++ b/tests/map_phys_mem.rs @@ -2,16 +2,14 @@ use bootloader_test_runner::run_test_kernel; #[test] fn check_boot_info() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_check_boot_info"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_check_boot_info" + )); } #[test] fn access_phys_mem() { - run_test_kernel( - env!("CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_access_phys_mem"), - None, - ); + run_test_kernel(env!( + "CARGO_BIN_FILE_TEST_KERNEL_MAP_PHYS_MEM_access_phys_mem" + )); } diff --git a/tests/pie.rs b/tests/pie.rs index 0801d3e1..c2d30d80 100644 --- a/tests/pie.rs +++ b/tests/pie.rs @@ -2,20 +2,20 @@ use bootloader_test_runner::run_test_kernel; #[test] fn basic_boot() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_basic_boot"), None); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_basic_boot")); } #[test] fn should_panic() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_should_panic"), None); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_should_panic")); } #[test] fn check_boot_info() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_check_boot_info"), None); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_check_boot_info")); } #[test] fn global_variable() { - run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_global_variable"), None); + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_PIE_global_variable")); } diff --git a/tests/ramdisk.rs b/tests/ramdisk.rs index a32f0d66..bdd7f9db 100644 --- a/tests/ramdisk.rs +++ b/tests/ramdisk.rs @@ -1,20 +1,20 @@ use std::path::Path; -use bootloader_test_runner::run_test_kernel; +use bootloader_test_runner::run_test_kernel_with_ramdisk; static RAMDISK_PATH: &str = "tests/ramdisk.txt"; #[test] fn basic_boot() { - run_test_kernel( + run_test_kernel_with_ramdisk( env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_basic_boot"), - Some(&Path::new(RAMDISK_PATH)), + Some(Path::new(RAMDISK_PATH)), ); } #[test] fn check_ramdisk() { - run_test_kernel( + run_test_kernel_with_ramdisk( env!("CARGO_BIN_FILE_TEST_KERNEL_RAMDISK_ramdisk"), - Some(&Path::new(RAMDISK_PATH)), + Some(Path::new(RAMDISK_PATH)), ); } diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index f5099f79..8f87485c 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -10,7 +10,11 @@ const QEMU_ARGS: &[&str] = &[ "--no-reboot", ]; -pub fn run_test_kernel(kernel_binary_path: &str, ramdisk_path: Option<&Path>) { +pub fn run_test_kernel(kernel_binary_path: &str) { + run_test_kernel_with_ramdisk(kernel_binary_path, None) +} + +pub fn run_test_kernel_with_ramdisk(kernel_binary_path: &str, ramdisk_path: Option<&Path>) { let kernel_path = Path::new(kernel_binary_path); // create an MBR disk image for legacy BIOS booting From 3fa940be7ad5df73c2668a237d1b349f028b802b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 9 Jan 2023 19:16:04 +0100 Subject: [PATCH 16/17] Avoid code duplication by using binding --- bios/stage-2/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bios/stage-2/src/main.rs b/bios/stage-2/src/main.rs index a1ba8566..d37e7a81 100644 --- a/bios/stage-2/src/main.rs +++ b/bios/stage-2/src/main.rs @@ -103,7 +103,7 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { writeln!(screen::Writer, "Loading ramdisk...").unwrap(); let ramdisk_len = match try_load_file( "ramdisk", - KERNEL_DST.wrapping_add(kernel_page_size * 4096), + ramdisk_start, &mut fs, &mut disk, disk_buffer, From baed6014a431ff01a0a6586f015e025d81950e2a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 9 Jan 2023 19:33:43 +0100 Subject: [PATCH 17/17] Run cargo fmt --- bios/stage-2/src/main.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/bios/stage-2/src/main.rs b/bios/stage-2/src/main.rs index d37e7a81..d8335e14 100644 --- a/bios/stage-2/src/main.rs +++ b/bios/stage-2/src/main.rs @@ -101,13 +101,8 @@ fn start(disk_number: u16, partition_table_start: *const u8) -> ! { let kernel_page_size = (((kernel_len - 1) / 4096) + 1) as usize; let ramdisk_start = KERNEL_DST.wrapping_add(kernel_page_size * 4096); writeln!(screen::Writer, "Loading ramdisk...").unwrap(); - let ramdisk_len = match try_load_file( - "ramdisk", - ramdisk_start, - &mut fs, - &mut disk, - disk_buffer, - ) { + let ramdisk_len = match try_load_file("ramdisk", ramdisk_start, &mut fs, &mut disk, disk_buffer) + { Some(s) => s, None => 0u64, };