From 10926ce547f60cc78c86e9eaa52b1dd6fda29da1 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Sun, 9 Mar 2025 22:47:27 +0800 Subject: [PATCH] Include payload as bytes in EFI stub --- osdk/src/commands/build/bin.rs | 50 ++++++++++--- ostd/libs/linux-bzimage/builder/src/lib.rs | 74 +------------------ .../linux-bzimage/setup/.cargo/config.toml | 4 + ostd/libs/linux-bzimage/setup/src/main.rs | 14 ---- .../setup/src/x86/amd64_efi/efi.rs | 3 +- .../libs/linux-bzimage/setup/src/x86/header.S | 12 ++- .../setup/src/x86/legacy_i386/mod.rs | 8 +- .../linux-bzimage/setup/src/x86/linker.ld | 8 ++ ostd/libs/linux-bzimage/setup/src/x86/mod.rs | 22 ++++++ 9 files changed, 83 insertions(+), 112 deletions(-) diff --git a/osdk/src/commands/build/bin.rs b/osdk/src/commands/build/bin.rs index f58369517..6e1d5c0f7 100644 --- a/osdk/src/commands/build/bin.rs +++ b/osdk/src/commands/build/bin.rs @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MPL-2.0 use std::{ - fs::OpenOptions, - io::{Seek, SeekFrom, Write}, + fs::{File, OpenOptions}, + io::{Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}, process::Command, }; use linux_bzimage_builder::{ - legacy32_rust_target_json, make_bzimage, BzImageType, PayloadEncoding, + encode_kernel, legacy32_rust_target_json, make_bzimage, BzImageType, PayloadEncoding, }; use crate::{ @@ -42,13 +42,21 @@ pub fn make_install_bzimage( let gen_target_json_path = target_dir.as_ref().join("x86_64-i386_pm-none.json"); std::fs::write(&gen_target_json_path, target_json).unwrap(); let arch = SetupInstallArch::Other(gen_target_json_path.canonicalize().unwrap()); - install_setup_with_arch(setup_install_dir, setup_target_dir, &arch); + install_setup_with_arch( + setup_install_dir, + setup_target_dir, + &arch, + aster_elf, + encoding, + ); } BzImageType::Efi64 => { install_setup_with_arch( setup_install_dir, setup_target_dir, &SetupInstallArch::X86_64, + aster_elf, + encoding, ); } }; @@ -58,13 +66,7 @@ pub fn make_install_bzimage( let install_path = install_dir.as_ref().join(target_name); info!("Building bzImage"); println!("install_path: {:?}", install_path); - make_bzimage( - &install_path, - image_type, - aster_elf.path(), - &setup_bin, - encoding, - ); + make_bzimage(&install_path, image_type, &setup_bin); AsterBin::new( &install_path, @@ -160,6 +162,8 @@ fn install_setup_with_arch( install_dir: impl AsRef, target_dir: impl AsRef, arch: &SetupInstallArch, + aster_elf: &AsterBin, + encoding: PayloadEncoding, ) { if !target_dir.as_ref().exists() { std::fs::create_dir_all(&target_dir).unwrap(); @@ -198,6 +202,7 @@ fn install_setup_with_arch( }; rustflags.push(target_feature_args); cmd.env("RUSTFLAGS", rustflags.join(" ")); + cmd.env("PAYLOAD_FILE", encode_kernel_to_file(aster_elf, encoding)); cmd.arg("install").arg("linux-bzimage-setup"); cmd.arg("--force"); cmd.arg("--root").arg(install_dir.as_ref()); @@ -226,3 +231,26 @@ fn install_setup_with_arch( ); } } + +fn encode_kernel_to_file(aster_elf: &AsterBin, encoding: PayloadEncoding) -> PathBuf { + let kernel_path = aster_elf.path(); + let encoded_path = { + let mut filename = kernel_path.file_name().unwrap().to_os_string(); + filename.push(".compressed"); + kernel_path.with_file_name(filename) + }; + + let mut kernel = Vec::new(); + File::open(kernel_path) + .unwrap() + .read_to_end(&mut kernel) + .unwrap(); + + let encoded = encode_kernel(kernel, encoding); + File::create(&encoded_path) + .unwrap() + .write_all(&encoded) + .unwrap(); + + encoded_path +} diff --git a/ostd/libs/linux-bzimage/builder/src/lib.rs b/ostd/libs/linux-bzimage/builder/src/lib.rs index a010291ae..7d92d2136 100644 --- a/ostd/libs/linux-bzimage/builder/src/lib.rs +++ b/ostd/libs/linux-bzimage/builder/src/lib.rs @@ -23,8 +23,7 @@ use std::{ path::Path, }; -use encoder::encode_kernel; -pub use encoder::PayloadEncoding; +pub use encoder::{encode_kernel, PayloadEncoding}; use mapping::{SetupFileOffset, SetupVA}; use xmas_elf::program::SegmentData; @@ -41,17 +40,9 @@ pub enum BzImageType { /// 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_elf_path`: The path to the setup ELF; -/// - `encoding`: The encoding format for compressing the kernel ELF. /// -pub fn make_bzimage( - target_image_path: &Path, - image_type: BzImageType, - kernel_path: &Path, - setup_elf_path: &Path, - encoding: PayloadEncoding, -) { +pub fn make_bzimage(target_image_path: &Path, image_type: BzImageType, setup_elf_path: &Path) { let mut setup_elf = Vec::new(); File::open(setup_elf_path) .unwrap() @@ -61,32 +52,14 @@ pub fn make_bzimage( // 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 = match image_type { - BzImageType::Legacy32 => kernel, - BzImageType::Efi64 => encode_kernel(kernel, encoding), - }; - - 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); + let pe_header = pe_header::make_pe_coff_header(&setup_elf, setup.len()); assert!( pe_header.header_at_zero.len() <= 0x1f1, "PE/COFF header is too large" @@ -141,44 +114,3 @@ fn to_flat_binary(elf_file: &[u8]) -> Vec { bin } - -/// 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 -/// `ostd/src/arch/x86/boot/linux_boot/setup/src/header.S` for more -/// info on this mechanism. -fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) { - let size = value.len(); - assert_eq!( - &header[offset..offset + size], - vec![0xABu8; size].as_slice(), - "The field {:#x} to be filled must be marked with 0xAB", - offset - ); - header[offset..offset + size].copy_from_slice(value); -} - -fn fill_legacy_header_fields( - header: &mut [u8], - kernel_len: usize, - setup_len: usize, - payload_offset: SetupVA, -) { - fill_header_field( - header, - 0x248, /* payload_offset */ - &(usize::from(payload_offset) as u32).to_le_bytes(), - ); - - fill_header_field( - header, - 0x24C, /* payload_length */ - &(kernel_len as u32).to_le_bytes(), - ); - - fill_header_field( - header, - 0x260, /* init_size */ - &((setup_len + kernel_len) as u32).to_le_bytes(), - ); -} diff --git a/ostd/libs/linux-bzimage/setup/.cargo/config.toml b/ostd/libs/linux-bzimage/setup/.cargo/config.toml index e02744b99..710da6359 100644 --- a/ostd/libs/linux-bzimage/setup/.cargo/config.toml +++ b/ostd/libs/linux-bzimage/setup/.cargo/config.toml @@ -1,3 +1,7 @@ +[env] +# Provide a default value. Otherwise `Cargo check` won't work. +PAYLOAD_FILE = "/PAYLOAD_FILE_is_not_defined" + [target.x86_64-unknown-none] rustflags = [ "-Ccode-model=kernel", diff --git a/ostd/libs/linux-bzimage/setup/src/main.rs b/ostd/libs/linux-bzimage/setup/src/main.rs index 2e411b987..ae665b935 100644 --- a/ostd/libs/linux-bzimage/setup/src/main.rs +++ b/ostd/libs/linux-bzimage/setup/src/main.rs @@ -22,22 +22,8 @@ #![no_std] #![no_main] -use linux_boot_params::BootParams; - mod console; mod loader; // The entry points are defined in `x86/*/setup.S`. mod x86; - -fn get_payload(boot_params: &BootParams) -> &'static [u8] { - let hdr = &boot_params.hdr; - // The payload_offset field is not recorded in the relocation table, so we need to - // calculate the loaded offset manually. - let loaded_offset = x86::image_load_offset(); - let payload_offset = (loaded_offset + hdr.payload_offset as isize) as usize; - let payload_length = hdr.payload_length as usize; - // SAFETY: the payload_offset and payload_length is valid if we assume that the - // boot_params struct is correct. - unsafe { core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length) } -} diff --git a/ostd/libs/linux-bzimage/setup/src/x86/amd64_efi/efi.rs b/ostd/libs/linux-bzimage/setup/src/x86/amd64_efi/efi.rs index 071f5a226..80a6795e9 100644 --- a/ostd/libs/linux-bzimage/setup/src/x86/amd64_efi/efi.rs +++ b/ostd/libs/linux-bzimage/setup/src/x86/amd64_efi/efi.rs @@ -67,8 +67,7 @@ fn efi_phase_boot(boot_params: &mut BootParams) -> ! { } // Load the kernel payload to memory. - let payload = crate::get_payload(boot_params); - let kernel = decode_payload(payload); + let kernel = decode_payload(crate::x86::payload()); uefi::println!("[EFI stub] Loading payload."); crate::loader::load_elf(&kernel); diff --git a/ostd/libs/linux-bzimage/setup/src/x86/header.S b/ostd/libs/linux-bzimage/setup/src/x86/header.S index c7d262410..332559c7f 100644 --- a/ostd/libs/linux-bzimage/setup/src/x86/header.S +++ b/ostd/libs/linux-bzimage/setup/src/x86/header.S @@ -4,10 +4,8 @@ // See https://www.kernel.org/doc/html/v5.6/x86/boot.html for // more information on the Linux x86 Boot Protocol. -// Some of the fields filled with a 0xab* values should be filled -// by the torjan builder. -// Asterinas will use only a few of these fields, and some of them -// are filled by the loader and will be read by Asterinas. +// The bootloader may fill some fields at runtime, which can +// be read by the kernel (via `boot_params.hdr`). .section ".header", "a" @@ -63,11 +61,11 @@ xloadflags: .word 0 cmdline_size: .long 4096 - 1 hardware_subarch: .long 0 hardware_subarch_data: .quad 0 -payload_offset: .long 0xabababab # at 0x248/4, to be filled by the builder -payload_length: .long 0xabababab # at 0x24c/4, to be filled by the builder +payload_offset: .long 0 # Not used. +payload_length: .long 0 # Not used. setup_data: .quad 0 pref_address: .quad CODE32_START -init_size: .long 0xabababab # at 0x260/4, to be filled by the builder +init_size: .long __executable_size .if {CFG_TARGET_ARCH_X86_64} handover_offset: .long (entry_efi_handover32 - entry_legacy32) diff --git a/ostd/libs/linux-bzimage/setup/src/x86/legacy_i386/mod.rs b/ostd/libs/linux-bzimage/setup/src/x86/legacy_i386/mod.rs index 8f276c009..32f548823 100644 --- a/ostd/libs/linux-bzimage/setup/src/x86/legacy_i386/mod.rs +++ b/ostd/libs/linux-bzimage/setup/src/x86/legacy_i386/mod.rs @@ -2,8 +2,6 @@ use core::arch::{asm, global_asm}; -use linux_boot_params::BootParams; - global_asm!(include_str!("setup.S")); use crate::console::{print_hex, print_str}; @@ -22,11 +20,7 @@ extern "cdecl" fn main_legacy32(boot_params_ptr: u32) -> ! { print_str("\n"); } - // SAFETY: the boot_params_ptr is a valid pointer to be borrowed. - let boot_params = unsafe { &*(boot_params_ptr as *const BootParams) }; - // SAFETY: the payload_offset and payload_length is valid. - let payload = crate::get_payload(boot_params); - crate::loader::load_elf(payload); + crate::loader::load_elf(crate::x86::payload()); // SAFETY: the entrypoint and the ptr is valid. unsafe { call_aster_entrypoint(ASTER_ENTRY_POINT, boot_params_ptr.try_into().unwrap()) }; diff --git a/ostd/libs/linux-bzimage/setup/src/x86/linker.ld b/ostd/libs/linux-bzimage/setup/src/x86/linker.ld index 47d5223b6..d763d785e 100644 --- a/ostd/libs/linux-bzimage/setup/src/x86/linker.ld +++ b/ostd/libs/linux-bzimage/setup/src/x86/linker.ld @@ -38,11 +38,17 @@ SECTIONS // Text segment. . = SETUP32_LMA; + __executable_start = .; .setup : { KEEP(*(.setup)) } : text .text : { *(.text .text.*) } : text // Rodata segment. . = ALIGN(4096); + .payload : { + PROVIDE(__payload_start = .); + KEEP(*(.payload)) + PROVIDE(__payload_end = .); + } : rodata .rodata : { *(.rodata .rodata.*) } : rodata .rela : { PROVIDE(__rela_start = .); @@ -59,6 +65,8 @@ SECTIONS *(.bss .bss.*) *(COMMON) PROVIDE(__bss_end = .); } : data + __executable_end = .; + __executable_size = __executable_end - __executable_start; . = ALIGN(4096); // Section names. ELF files must have them. diff --git a/ostd/libs/linux-bzimage/setup/src/x86/mod.rs b/ostd/libs/linux-bzimage/setup/src/x86/mod.rs index 6b085a398..78601c35e 100644 --- a/ostd/libs/linux-bzimage/setup/src/x86/mod.rs +++ b/ostd/libs/linux-bzimage/setup/src/x86/mod.rs @@ -32,3 +32,25 @@ pub fn image_load_offset() -> isize { (entry_legacy32 as usize as isize) - CODE32_START } + +global_asm!( + ".section \".payload\", \"a\"", + concat!(".incbin \"", env!("PAYLOAD_FILE"), "\""), +); + +/// Returns an immutable slice containing the payload (i.e., the kernel). +fn payload() -> &'static [u8] { + extern "C" { + fn __payload_start(); + fn __payload_end(); + } + + // SAFETY: The memory region is part of the "rodata" segment, which is initialized, live for + // `'static`, and never mutated. + unsafe { + core::slice::from_raw_parts( + __payload_start as *const u8, + __payload_end as usize - __payload_start as usize, + ) + } +}