mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 01:43:22 +00:00
Include payload as bytes in EFI stub
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
1488219c4f
commit
10926ce547
@ -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<Path>,
|
||||
target_dir: impl AsRef<Path>,
|
||||
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
|
||||
}
|
||||
|
@ -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<u8> {
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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) }
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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()) };
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user