diff --git a/Cargo.lock b/Cargo.lock index 359676ac7..9eec1e387 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,6 +647,7 @@ dependencies = [ "volatile", "x86", "x86_64", + "xmas-elf", ] [[package]] diff --git a/Makefile b/Makefile index 2c5c80930..6a8756240 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,6 @@ export # Toolchain variables that are used when building the Linux setup header export CARGO := cargo -export OBJCOPY := objcopy .PHONY: all setup build tools run test docs check clean diff --git a/framework/jinux-frame/Cargo.toml b/framework/jinux-frame/Cargo.toml index ca17e207b..c6b915c8f 100644 --- a/framework/jinux-frame/Cargo.toml +++ b/framework/jinux-frame/Cargo.toml @@ -30,5 +30,8 @@ aml = "0.16.3" multiboot2 = "0.16.0" rsdp = "2.0.0" +[build-dependencies] +xmas-elf = "0.8.0" + [features] intel_tdx = ["dep:tdx-guest"] diff --git a/framework/jinux-frame/build.rs b/framework/jinux-frame/build.rs index 2b87f4d80..de3e8f8a2 100644 --- a/framework/jinux-frame/build.rs +++ b/framework/jinux-frame/build.rs @@ -1,14 +1,26 @@ -use std::{error::Error, io::Write, path::PathBuf}; +use std::{ + error::Error, + io::{Seek, Write}, + path::{Path, PathBuf}, +}; + +use xmas_elf::program::{ProgramHeader, SegmentData}; + +const SETUP32_LMA: usize = 0x100000; fn main() -> Result<(), Box> { - build_linux_setup_header()?; + let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + build_linux_setup_header(&source_dir, &out_dir)?; + copy_to_raw_binary(&out_dir)?; Ok(()) } -fn build_linux_setup_header() -> Result<(), Box> { - // Build the setup header to raw binary. - let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); +fn build_linux_setup_header( + source_dir: &Path, + out_dir: &Path, +) -> Result<(), Box> { + // Build the setup header to ELF. let setup_crate_dir = source_dir .join("src") .join("arch") @@ -26,12 +38,17 @@ fn build_linux_setup_header() -> Result<(), Box> { let cargo = std::env::var("CARGO").unwrap(); let mut cmd = std::process::Command::new(cargo); cmd.arg("install").arg("jinux-frame-x86-boot-setup"); + cmd.arg("--debug"); cmd.arg("--locked"); cmd.arg("--path").arg(setup_crate_dir.to_str().unwrap()); cmd.arg("--target").arg(target_json.as_os_str()); cmd.arg("-Zbuild-std=core,compiler_builtins"); cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); + // Specify the installation root. cmd.arg("--root").arg(out_dir.as_os_str()); + // 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.env_remove("RUSTFLAGS"); cmd.env_remove("CARGO_ENCODED_RUSTFLAGS"); let output = cmd.output()?; @@ -45,31 +62,46 @@ fn build_linux_setup_header() -> Result<(), Box> { .into()); } + Ok(()) +} + +/// We need a binary which satisfies `LMA == File_Offset`, 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 +/// dump of the kernel setup header when it's loaded by the bootloader. +fn copy_to_raw_binary(out_dir: &Path) -> Result<(), Box> { // Strip the elf header to get the raw header. let elf_path = out_dir.join("bin").join("jinux-frame-x86-boot-setup"); let bin_path = out_dir.join("bin").join("jinux-frame-x86-boot-setup.bin"); - let objcopy = std::env::var("OBJCOPY").unwrap(); - let mut cmd = std::process::Command::new(objcopy); - cmd.arg("-O").arg("binary"); - cmd.arg("-j").arg(".header"); - cmd.arg("-j").arg(".text"); - cmd.arg("-j").arg(".rodata"); - cmd.arg("-j").arg(".data"); - cmd.arg("-j").arg(".bss"); - cmd.arg("-j").arg(".eh_frame"); - cmd.arg("-j").arg(".eh_frame_hdr"); - cmd.arg(elf_path.to_str().unwrap()); - cmd.arg(bin_path.to_str().unwrap()); - let output = cmd.output()?; - if !output.status.success() { - std::io::stdout().write_all(&output.stdout).unwrap(); - std::io::stderr().write_all(&output.stderr).unwrap(); - return Err(format!( - "Failed to strip linux boot header:\n\tcommand `{:?}`\n\treturned {}", - cmd, output.status - ) - .into()); + let elf_file = std::fs::read(elf_path)?; + let elf = xmas_elf::ElfFile::new(&elf_file)?; + + let bin_file = std::fs::File::create(bin_path)?; + let mut bin_writer = std::io::BufWriter::new(bin_file); + + for ph in elf.program_iter() { + let ProgramHeader::Ph32(program) = ph else { + return Err("Unexpected program header type".into()); + }; + if program.get_type().unwrap() == xmas_elf::program::Type::Load { + let dest_file_offset = program.virtual_addr as usize - SETUP32_LMA; + bin_writer.seek(std::io::SeekFrom::End(0))?; + let cur_file_offset = bin_writer.stream_position().unwrap() as usize; + if cur_file_offset < dest_file_offset { + let padding = vec![0; dest_file_offset - cur_file_offset]; + bin_writer.write_all(&padding)?; + } else { + bin_writer.seek(std::io::SeekFrom::Start(dest_file_offset as u64))?; + } + let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else { + return Err("Unexpected segment data type".into()); + }; + bin_writer.write_all(header_data)?; + } } + Ok(()) } diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/header.S b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/header.S index 177319a57..ddfbf8cc8 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/header.S +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/header.S @@ -16,7 +16,7 @@ .code16 .org 0x01f1 -hdr: +hdr_start: SETUP_SECTS = 4 setup_sects: .byte SETUP_SECTS root_flags: .word 1 @@ -26,7 +26,7 @@ vid_mode: .word 0xfffd root_dev: .word 0 boot_flag: .word 0xAA55 jump: .byte 0xeb -jump_addr: .byte start_of_setup32-jump_addr +jump_addr: .byte hdr_end-jump_addr magic: .ascii "HdrS" .word 0x020f realmode_swtch: .word 0, 0 @@ -58,12 +58,12 @@ pref_address: .quad 0 init_size: .long 0xabababab # at 0x260/4, to be filled by the runner handover_offset: .long 0 kernel_info_offset: .long 0 - +hdr_end: // End of header. -// 32-bit setup code starts here. +// 32-bit setup code starts here, and will be loaded at code32_start (0x100000). .code32 -start_of_setup32: .org 0x200 * (SETUP_SECTS + 1) +start_of_setup32: .extern _rust_setup_entry jmp _rust_setup_entry