From 11ff35d34e8d3d1bf88bd17b774d53b128a27b7d Mon Sep 17 00:00:00 2001 From: Zhang Junyang Date: Fri, 29 Dec 2023 17:32:59 +0800 Subject: [PATCH] Style improvements on the boot code --- .../src/arch/x86/boot/linux_boot/mod.rs | 3 +- .../libs/linux-bzimage/builder/src/lib.rs | 180 +++++++++--------- .../libs/linux-bzimage/setup/src/loader.rs | 25 +-- runner/src/machine/qemu_grub_efi.rs | 16 +- 4 files changed, 117 insertions(+), 107 deletions(-) diff --git a/framework/aster-frame/src/arch/x86/boot/linux_boot/mod.rs b/framework/aster-frame/src/arch/x86/boot/linux_boot/mod.rs index 62159c1ec..1e94c0d64 100644 --- a/framework/aster-frame/src/arch/x86/boot/linux_boot/mod.rs +++ b/framework/aster-frame/src/arch/x86/boot/linux_boot/mod.rs @@ -1,7 +1,6 @@ //! The Linux 64-bit Boot Protocol supporting module. //! -extern crate linux_boot_params; use linux_boot_params::{BootParams, E820Type, LINUX_BOOT_HEADER_MAGIC}; use crate::boot::{ @@ -138,7 +137,7 @@ fn init_memory_regions(memory_regions: &'static Once>) { memory_regions.call_once(|| non_overlapping_regions_from(regions.as_ref())); } -/// The entry point of of the Rust code portion of Asterinas. +/// The entry point of the Rust code portion of Asterinas. #[no_mangle] unsafe extern "sysv64" fn __linux_boot(params_ptr: *const BootParams) -> ! { let params = *params_ptr; diff --git a/framework/libs/linux-bzimage/builder/src/lib.rs b/framework/libs/linux-bzimage/builder/src/lib.rs index d0f95e7dc..b62c925af 100644 --- a/framework/libs/linux-bzimage/builder/src/lib.rs +++ b/framework/libs/linux-bzimage/builder/src/lib.rs @@ -24,11 +24,98 @@ use xmas_elf::program::SegmentData; use mapping::{SetupFileOffset, SetupVA}; +/// The type of the bzImage that we are building through `make_bzimage`. +/// +/// Currently, Legacy32 and Efi64 are mutually exclusive. +pub enum BzImageType { + Legacy32, + Efi64, +} + +/// Making a bzImage given the kernel ELF and setup source. +/// +/// Explanations for the arguments: +/// - `target_image_path`: The path to the target bzImage. +/// - `image_type`: The type of the bzImage that we are building. +/// - `kernel_path`: The path to the kernel ELF. +/// - `setup_src`: The path to the setup crate. +/// - `setup_tmp_out_dir`: The path to the temporary output directory for the setup binary. +pub fn make_bzimage( + target_image_path: &Path, + image_type: BzImageType, + kernel_path: &Path, + setup_src: &Path, + setup_tmp_out_dir: &Path, +) { + let setup = match image_type { + BzImageType::Legacy32 => { + let arch = setup_src + .join("x86_64-i386_pm-none.json") + .canonicalize() + .unwrap(); + build_setup_with_arch(setup_src, setup_tmp_out_dir, &SetupBuildArch::Other(arch)) + } + BzImageType::Efi64 => { + build_setup_with_arch(setup_src, setup_tmp_out_dir, &SetupBuildArch::X86_64) + } + }; + + let mut setup_elf = Vec::new(); + File::open(setup) + .unwrap() + .read_to_end(&mut setup_elf) + .unwrap(); + let mut setup = to_flat_binary(&setup_elf); + // Pad the header with 8-byte alignment. + setup.resize((setup.len() + 7) & !7, 0x00); + + let mut kernel = Vec::new(); + File::open(kernel_path) + .unwrap() + .read_to_end(&mut kernel) + .unwrap(); + let payload = kernel; + + let setup_len = setup.len(); + let payload_len = payload.len(); + let payload_offset = SetupFileOffset::from(setup_len); + fill_legacy_header_fields(&mut setup, payload_len, setup_len, payload_offset.into()); + + let mut kernel_image = File::create(target_image_path).unwrap(); + kernel_image.write_all(&setup).unwrap(); + kernel_image.write_all(&payload).unwrap(); + + let image_size = setup_len + payload_len; + + if matches!(image_type, BzImageType::Efi64) { + // Write the PE/COFF header to the start of the file. + // Since the Linux boot header starts at 0x1f1, we can write the PE/COFF header directly to the + // start of the file without overwriting the Linux boot header. + let pe_header = pe_header::make_pe_coff_header(&setup_elf, image_size); + assert!( + pe_header.header_at_zero.len() <= 0x1f1, + "PE/COFF header is too large" + ); + + kernel_image.seek(SeekFrom::Start(0)).unwrap(); + kernel_image.write_all(&pe_header.header_at_zero).unwrap(); + kernel_image + .seek(SeekFrom::Start(usize::from(pe_header.relocs.0) as u64)) + .unwrap(); + kernel_image.write_all(&pe_header.relocs.1).unwrap(); + } +} + +enum SetupBuildArch { + X86_64, + Other(PathBuf), +} + /// We need a flat binary which satisfies PA delta == File offset delta, /// and objcopy does not satisfy us well, so we should parse the ELF and /// do our own objcopy job. /// -/// Interstingly, the resulting binary should be the same as the memory +/// Interestingly, the resulting binary should be the same as the memory /// dump of the kernel setup header when it's loaded by the bootloader. fn to_flat_binary(elf_file: &[u8]) -> Vec { let elf = xmas_elf::ElfFile::new(&elf_file).unwrap(); @@ -54,7 +141,7 @@ fn to_flat_binary(elf_file: &[u8]) -> Vec { bin } -/// This function sould be used when generating the Linux x86 Boot setup header. +/// This function should be used when generating the Linux x86 Boot setup header. /// Some fields in the Linux x86 Boot setup header should be filled after assembled. /// And the filled fields must have the bytes with values of 0xAB. See /// `framework/aster-frame/src/arch/x86/boot/linux_boot/setup/src/header.S` for more @@ -95,91 +182,14 @@ fn fill_legacy_header_fields( ); } -/// The type of the bzImage that we are building through `make_bzimage`. -/// -/// Currently, Legacy32 and Efi64 are mutually exclusive. -pub enum BzImageType { - Legacy32, - Efi64, -} - -pub fn make_bzimage( - image_path: &Path, - kernel_path: &Path, - image_type: BzImageType, - setup_src: &Path, - setup_out: &Path, -) { - let setup = match image_type { - BzImageType::Legacy32 => { - let arch = setup_src - .join("x86_64-i386_pm-none.json") - .canonicalize() - .unwrap(); - build_setup_with_arch(setup_src, setup_out, &SetupBuildArch::Other(arch)) - } - BzImageType::Efi64 => build_setup_with_arch(setup_src, setup_out, &SetupBuildArch::X86_64), - }; - - let mut setup_elf = Vec::new(); - File::open(setup) - .unwrap() - .read_to_end(&mut setup_elf) - .unwrap(); - let mut setup = to_flat_binary(&setup_elf); - // Pad the header with 8-byte alignment. - setup.resize((setup.len() + 7) & !7, 0x00); - - let mut kernel = Vec::new(); - File::open(kernel_path) - .unwrap() - .read_to_end(&mut kernel) - .unwrap(); - let payload = kernel; - - let setup_len = setup.len(); - let payload_len = payload.len(); - let payload_offset = SetupFileOffset::from(setup_len); - fill_legacy_header_fields(&mut setup, payload_len, setup_len, payload_offset.into()); - - let mut kernel_image = File::create(image_path).unwrap(); - kernel_image.write_all(&setup).unwrap(); - kernel_image.write_all(&payload).unwrap(); - - let image_size = setup_len + payload_len; - - if matches!(image_type, BzImageType::Efi64) { - // Write the PE/COFF header to the start of the file. - // Since the Linux boot header starts at 0x1f1, we can write the PE/COFF header directly to the - // start of the file without overwriting the Linux boot header. - let pe_header = pe_header::make_pe_coff_header(&setup_elf, image_size); - assert!( - pe_header.header_at_zero.len() <= 0x1f1, - "PE/COFF header is too large" - ); - - kernel_image.seek(SeekFrom::Start(0)).unwrap(); - kernel_image.write_all(&pe_header.header_at_zero).unwrap(); - kernel_image - .seek(SeekFrom::Start(usize::from(pe_header.relocs.0) as u64)) - .unwrap(); - kernel_image.write_all(&pe_header.relocs.1).unwrap(); - } -} - -enum SetupBuildArch { - X86_64, - Other(PathBuf), -} - /// Build the setup binary. /// /// It will return the path to the built setup binary. -fn build_setup_with_arch(source_dir: &Path, out_dir: &Path, arch: &SetupBuildArch) -> PathBuf { - if !out_dir.exists() { - std::fs::create_dir_all(&out_dir).unwrap(); +fn build_setup_with_arch(source_dir: &Path, tmp_out_dir: &Path, arch: &SetupBuildArch) -> PathBuf { + if !tmp_out_dir.exists() { + std::fs::create_dir_all(&tmp_out_dir).unwrap(); } - let out_dir = std::fs::canonicalize(out_dir).unwrap(); + let tmp_out_dir = std::fs::canonicalize(tmp_out_dir).unwrap(); // Relocations are fewer in release mode. That's why the release mode is more stable than // the debug mode. @@ -201,7 +211,7 @@ fn build_setup_with_arch(source_dir: &Path, out_dir: &Path, arch: &SetupBuildArc cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); // Specify the build target directory to avoid cargo running // into a deadlock reading the workspace files. - cmd.arg("--target-dir").arg(out_dir.as_os_str()); + cmd.arg("--target-dir").arg(tmp_out_dir.as_os_str()); cmd.env_remove("RUSTFLAGS"); cmd.env_remove("CARGO_ENCODED_RUSTFLAGS"); @@ -220,7 +230,7 @@ fn build_setup_with_arch(source_dir: &Path, out_dir: &Path, arch: &SetupBuildArc SetupBuildArch::Other(path) => path.file_stem().unwrap().to_str().unwrap(), }; - let setup_artifact = out_dir + let setup_artifact = tmp_out_dir .join(arch_name) .join(profile) .join("linux-bzimage-setup"); diff --git a/framework/libs/linux-bzimage/setup/src/loader.rs b/framework/libs/linux-bzimage/setup/src/loader.rs index 167a2ec1c..6f43a3d85 100644 --- a/framework/libs/linux-bzimage/setup/src/loader.rs +++ b/framework/libs/linux-bzimage/setup/src/loader.rs @@ -1,17 +1,5 @@ use xmas_elf::program::{ProgramHeader, SegmentData}; -/// TODO: remove this and use copy_from_slice instead -/// -/// We use a custom memcpy because the standard library's compiler's builtin memcpy -/// fails for some unknown reason. -unsafe fn memcpy(dst: *mut u8, src: *const u8, size: usize) { - let mut i = 0; - while i < size { - *dst.add(i) = *src.add(i); - i += 1; - } -} - /// Load the kernel ELF payload to memory. pub fn load_elf(file: &[u8]) { let elf = xmas_elf::ElfFile::new(file).unwrap(); @@ -62,3 +50,16 @@ fn load_segment(file: &xmas_elf::ElfFile, program: &xmas_elf::program::ProgramHe let zero_slice = &mut dst_slice[program.file_size as usize..]; zero_slice.fill(0); } + +/// TODO: remove this and use copy_from_slice instead +/// +/// We use a custom memcpy because the standard library's compiler's builtin memcpy +/// fails for some unknown reason. Sometimes that will result in "Unknown OPCode" +/// machine error. +unsafe fn memcpy(dst: *mut u8, src: *const u8, size: usize) { + let mut i = 0; + while i < size { + *dst.add(i) = *src.add(i); + i += 1; + } +} diff --git a/runner/src/machine/qemu_grub_efi.rs b/runner/src/machine/qemu_grub_efi.rs index ad6c5531a..f2051d99b 100644 --- a/runner/src/machine/qemu_grub_efi.rs +++ b/runner/src/machine/qemu_grub_efi.rs @@ -74,12 +74,12 @@ pub const GRUB_PREFIX_EFI_AND_LEGACY: &str = "/usr/local/grub"; pub const GRUB_VERSION: &str = "x86_64-efi"; pub fn create_bootdev_image( - aster_path: PathBuf, + kernel_elf_path: PathBuf, initramfs_path: PathBuf, grub_cfg: String, protocol: BootProtocol, ) -> PathBuf { - let target_dir = aster_path.parent().unwrap(); + let target_dir = kernel_elf_path.parent().unwrap(); let iso_root = target_dir.join("iso_root"); // Clear or make the iso dir. @@ -102,24 +102,24 @@ pub fn create_bootdev_image( BootProtocol::LinuxEfiHandover64 => BzImageType::Efi64, _ => unreachable!(), }; - let wrapper_src = Path::new("framework/libs/linux-bzimage/setup"); - let wrapper_out = Path::new("target/linux-bzimage-setup"); + let setup_src = Path::new("framework/libs/linux-bzimage/setup"); + let setup_out_dir = Path::new("target/linux-bzimage-setup"); // Make the `bzImage`-compatible kernel image and place it in the boot directory. let target_path = iso_root.join("boot").join("asterinaz"); println!("[aster-runner] Building bzImage."); make_bzimage( &target_path, - &aster_path.as_path(), image_type, - &wrapper_src, - &wrapper_out, + &kernel_elf_path.as_path(), + &setup_src, + &setup_out_dir, ); target_path } BootProtocol::Multiboot | BootProtocol::Multiboot2 => { // Copy the kernel image to the boot directory. let target_path = iso_root.join("boot").join("atserinas"); - fs::copy(&aster_path, &target_path).unwrap(); + fs::copy(&kernel_elf_path, &target_path).unwrap(); target_path } };