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

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