Style improvements on the boot code

This commit is contained in:
Zhang Junyang 2023-12-29 17:32:59 +08:00 committed by Tate, Hongliang Tian
parent 501894652f
commit 11ff35d34e
4 changed files with 117 additions and 107 deletions

View File

@ -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<Vec<MemoryRegion>>) {
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;

View File

@ -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<u8> {
let elf = xmas_elf::ElfFile::new(&elf_file).unwrap();
@ -54,7 +141,7 @@ fn to_flat_binary(elf_file: &[u8]) -> Vec<u8> {
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");

View File

@ -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;
}
}

View File

@ -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
}
};