Include payload as bytes in EFI stub

This commit is contained in:
Ruihan Li
2025-03-09 22:47:27 +08:00
committed by Tate, Hongliang Tian
parent 1488219c4f
commit 10926ce547
9 changed files with 83 additions and 112 deletions

View File

@ -1,14 +1,14 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use std::{ use std::{
fs::OpenOptions, fs::{File, OpenOptions},
io::{Seek, SeekFrom, Write}, io::{Read, Seek, SeekFrom, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
}; };
use linux_bzimage_builder::{ use linux_bzimage_builder::{
legacy32_rust_target_json, make_bzimage, BzImageType, PayloadEncoding, encode_kernel, legacy32_rust_target_json, make_bzimage, BzImageType, PayloadEncoding,
}; };
use crate::{ 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"); 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(); std::fs::write(&gen_target_json_path, target_json).unwrap();
let arch = SetupInstallArch::Other(gen_target_json_path.canonicalize().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 => { BzImageType::Efi64 => {
install_setup_with_arch( install_setup_with_arch(
setup_install_dir, setup_install_dir,
setup_target_dir, setup_target_dir,
&SetupInstallArch::X86_64, &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); let install_path = install_dir.as_ref().join(target_name);
info!("Building bzImage"); info!("Building bzImage");
println!("install_path: {:?}", install_path); println!("install_path: {:?}", install_path);
make_bzimage( make_bzimage(&install_path, image_type, &setup_bin);
&install_path,
image_type,
aster_elf.path(),
&setup_bin,
encoding,
);
AsterBin::new( AsterBin::new(
&install_path, &install_path,
@ -160,6 +162,8 @@ fn install_setup_with_arch(
install_dir: impl AsRef<Path>, install_dir: impl AsRef<Path>,
target_dir: impl AsRef<Path>, target_dir: impl AsRef<Path>,
arch: &SetupInstallArch, arch: &SetupInstallArch,
aster_elf: &AsterBin,
encoding: PayloadEncoding,
) { ) {
if !target_dir.as_ref().exists() { if !target_dir.as_ref().exists() {
std::fs::create_dir_all(&target_dir).unwrap(); std::fs::create_dir_all(&target_dir).unwrap();
@ -198,6 +202,7 @@ fn install_setup_with_arch(
}; };
rustflags.push(target_feature_args); rustflags.push(target_feature_args);
cmd.env("RUSTFLAGS", rustflags.join(" ")); 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("install").arg("linux-bzimage-setup");
cmd.arg("--force"); cmd.arg("--force");
cmd.arg("--root").arg(install_dir.as_ref()); 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
}

View File

@ -23,8 +23,7 @@ use std::{
path::Path, path::Path,
}; };
use encoder::encode_kernel; pub use encoder::{encode_kernel, PayloadEncoding};
pub use encoder::PayloadEncoding;
use mapping::{SetupFileOffset, SetupVA}; use mapping::{SetupFileOffset, SetupVA};
use xmas_elf::program::SegmentData; use xmas_elf::program::SegmentData;
@ -41,17 +40,9 @@ pub enum BzImageType {
/// Explanations for the arguments: /// Explanations for the arguments:
/// - `target_image_path`: The path to the target bzImage; /// - `target_image_path`: The path to the target bzImage;
/// - `image_type`: The type of the bzImage that we are building; /// - `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; /// - `setup_elf_path`: The path to the setup ELF;
/// - `encoding`: The encoding format for compressing the kernel ELF.
/// ///
pub fn make_bzimage( pub fn make_bzimage(target_image_path: &Path, image_type: BzImageType, setup_elf_path: &Path) {
target_image_path: &Path,
image_type: BzImageType,
kernel_path: &Path,
setup_elf_path: &Path,
encoding: PayloadEncoding,
) {
let mut setup_elf = Vec::new(); let mut setup_elf = Vec::new();
File::open(setup_elf_path) File::open(setup_elf_path)
.unwrap() .unwrap()
@ -61,32 +52,14 @@ pub fn make_bzimage(
// Pad the header with 8-byte alignment. // Pad the header with 8-byte alignment.
setup.resize((setup.len() + 7) & !7, 0x00); 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(); let mut kernel_image = File::create(target_image_path).unwrap();
kernel_image.write_all(&setup).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) { if matches!(image_type, BzImageType::Efi64) {
// Write the PE/COFF header to the start of the file. // 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 // 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. // 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!( assert!(
pe_header.header_at_zero.len() <= 0x1f1, pe_header.header_at_zero.len() <= 0x1f1,
"PE/COFF header is too large" "PE/COFF header is too large"
@ -141,44 +114,3 @@ fn to_flat_binary(elf_file: &[u8]) -> Vec<u8> {
bin 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(),
);
}

View File

@ -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] [target.x86_64-unknown-none]
rustflags = [ rustflags = [
"-Ccode-model=kernel", "-Ccode-model=kernel",

View File

@ -22,22 +22,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use linux_boot_params::BootParams;
mod console; mod console;
mod loader; mod loader;
// The entry points are defined in `x86/*/setup.S`. // The entry points are defined in `x86/*/setup.S`.
mod x86; 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) }
}

View File

@ -67,8 +67,7 @@ fn efi_phase_boot(boot_params: &mut BootParams) -> ! {
} }
// Load the kernel payload to memory. // Load the kernel payload to memory.
let payload = crate::get_payload(boot_params); let kernel = decode_payload(crate::x86::payload());
let kernel = decode_payload(payload);
uefi::println!("[EFI stub] Loading payload."); uefi::println!("[EFI stub] Loading payload.");
crate::loader::load_elf(&kernel); crate::loader::load_elf(&kernel);

View File

@ -4,10 +4,8 @@
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for // See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
// more information on the Linux x86 Boot Protocol. // more information on the Linux x86 Boot Protocol.
// Some of the fields filled with a 0xab* values should be filled // The bootloader may fill some fields at runtime, which can
// by the torjan builder. // be read by the kernel (via `boot_params.hdr`).
// Asterinas will use only a few of these fields, and some of them
// are filled by the loader and will be read by Asterinas.
.section ".header", "a" .section ".header", "a"
@ -63,11 +61,11 @@ xloadflags: .word 0
cmdline_size: .long 4096 - 1 cmdline_size: .long 4096 - 1
hardware_subarch: .long 0 hardware_subarch: .long 0
hardware_subarch_data: .quad 0 hardware_subarch_data: .quad 0
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the builder payload_offset: .long 0 # Not used.
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the builder payload_length: .long 0 # Not used.
setup_data: .quad 0 setup_data: .quad 0
pref_address: .quad CODE32_START 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} .if {CFG_TARGET_ARCH_X86_64}
handover_offset: .long (entry_efi_handover32 - entry_legacy32) handover_offset: .long (entry_efi_handover32 - entry_legacy32)

View File

@ -2,8 +2,6 @@
use core::arch::{asm, global_asm}; use core::arch::{asm, global_asm};
use linux_boot_params::BootParams;
global_asm!(include_str!("setup.S")); global_asm!(include_str!("setup.S"));
use crate::console::{print_hex, print_str}; use crate::console::{print_hex, print_str};
@ -22,11 +20,7 @@ extern "cdecl" fn main_legacy32(boot_params_ptr: u32) -> ! {
print_str("\n"); print_str("\n");
} }
// SAFETY: the boot_params_ptr is a valid pointer to be borrowed. crate::loader::load_elf(crate::x86::payload());
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);
// SAFETY: the entrypoint and the ptr is valid. // SAFETY: the entrypoint and the ptr is valid.
unsafe { call_aster_entrypoint(ASTER_ENTRY_POINT, boot_params_ptr.try_into().unwrap()) }; unsafe { call_aster_entrypoint(ASTER_ENTRY_POINT, boot_params_ptr.try_into().unwrap()) };

View File

@ -38,11 +38,17 @@ SECTIONS
// Text segment. // Text segment.
. = SETUP32_LMA; . = SETUP32_LMA;
__executable_start = .;
.setup : { KEEP(*(.setup)) } : text .setup : { KEEP(*(.setup)) } : text
.text : { *(.text .text.*) } : text .text : { *(.text .text.*) } : text
// Rodata segment. // Rodata segment.
. = ALIGN(4096); . = ALIGN(4096);
.payload : {
PROVIDE(__payload_start = .);
KEEP(*(.payload))
PROVIDE(__payload_end = .);
} : rodata
.rodata : { *(.rodata .rodata.*) } : rodata .rodata : { *(.rodata .rodata.*) } : rodata
.rela : { .rela : {
PROVIDE(__rela_start = .); PROVIDE(__rela_start = .);
@ -59,6 +65,8 @@ SECTIONS
*(.bss .bss.*) *(COMMON) *(.bss .bss.*) *(COMMON)
PROVIDE(__bss_end = .); PROVIDE(__bss_end = .);
} : data } : data
__executable_end = .;
__executable_size = __executable_end - __executable_start;
. = ALIGN(4096); . = ALIGN(4096);
// Section names. ELF files must have them. // Section names. ELF files must have them.

View File

@ -32,3 +32,25 @@ pub fn image_load_offset() -> isize {
(entry_legacy32 as usize as isize) - CODE32_START (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,
)
}
}