From a532340c65f87e78a074f2d1a82aa9bf501d4803 Mon Sep 17 00:00:00 2001 From: Zhang Junyang Date: Mon, 23 Oct 2023 17:11:22 +0800 Subject: [PATCH] Implement linux x86 32bit legacy boot protocol --- .github/workflows/syscall_test.yml | 12 ++- Cargo.lock | 3 +- framework/jinux-frame/Cargo.toml | 3 - framework/jinux-frame/build.rs | 52 +--------- .../jinux-frame/src/arch/x86/boot/boot.S | 4 +- .../jinux-frame/src/arch/x86/boot/linker.ld | 6 +- .../arch/x86/boot/linux_boot/boot_params.rs | 16 ++- .../src/arch/x86/boot/linux_boot/mod.rs | 23 ++--- .../arch/x86/boot/linux_boot/setup/Cargo.toml | 1 + .../boot/linux_boot/setup/src/boot_params.rs | 12 +++ .../x86/boot/linux_boot/setup/src/console.rs | 6 +- .../x86/boot/linux_boot/setup/src/loader.rs | 29 ++++++ .../x86/boot/linux_boot/setup/src/main.rs | 32 +++++- .../src/arch/x86/boot/multiboot/mod.rs | 16 +-- .../src/arch/x86/kernel/acpi/mod.rs | 39 +++++++- framework/jinux-frame/src/boot/mod.rs | 3 +- runner/Cargo.toml | 1 + .../src/machine/qemu_grub_efi/linux_boot.rs | 98 +++++++++++++++++++ .../mod.rs} | 64 ++---------- 19 files changed, 261 insertions(+), 159 deletions(-) create mode 100644 framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/boot_params.rs create mode 100644 framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/loader.rs create mode 100644 runner/src/machine/qemu_grub_efi/linux_boot.rs rename runner/src/machine/{qemu_grub_efi.rs => qemu_grub_efi/mod.rs} (72%) diff --git a/.github/workflows/syscall_test.yml b/.github/workflows/syscall_test.yml index 2ef049d97..7cba7a5ef 100644 --- a/.github/workflows/syscall_test.yml +++ b/.github/workflows/syscall_test.yml @@ -17,12 +17,16 @@ jobs: - uses: actions/checkout@v3 + - name: Syscall Test (Multiboot) + id: syscall_test_mb + run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=multiboot + - name: Syscall Test (Multiboot2) id: syscall_test_mb2 run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=multiboot2 - # TODO: include the integration tests for Multiboot/MicroVM/Linux boot methods, which are not ready yet. + - name: Syscall Test (Linux Boot Protocol) + id: syscall_test_lbp + run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=linux - # - name: Syscall Test (Linux Boot Protocol) - # id: syscall_test_lbp - # run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=linux + # TODO: include the integration tests for MicroVM, which is not ready yet. diff --git a/Cargo.lock b/Cargo.lock index a3f179a62..9bb3fe158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,7 +647,6 @@ dependencies = [ "volatile", "x86", "x86_64", - "xmas-elf", ] [[package]] @@ -655,6 +654,7 @@ name = "jinux-frame-x86-boot-setup" version = "0.1.0" dependencies = [ "uart_16550", + "xmas-elf", ] [[package]] @@ -729,6 +729,7 @@ dependencies = [ "clap", "glob", "rand", + "xmas-elf", ] [[package]] diff --git a/framework/jinux-frame/Cargo.toml b/framework/jinux-frame/Cargo.toml index c6b915c8f..ca17e207b 100644 --- a/framework/jinux-frame/Cargo.toml +++ b/framework/jinux-frame/Cargo.toml @@ -30,8 +30,5 @@ 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 942999429..24cacd70d 100644 --- a/framework/jinux-frame/build.rs +++ b/framework/jinux-frame/build.rs @@ -1,22 +1,13 @@ use std::{ error::Error, - io::{Seek, Write}, + io::Write, path::{Path, PathBuf}, }; -use xmas_elf::program::{ProgramHeader, SegmentData}; - -// We chose the legacy setup sections to be 7 so that the setup header -// is page-aligned and the legacy setup section size would be 0x1000. -const LEGACY_SETUP_SECS: usize = 7; -const LEGACY_SETUP_SEC_SIZE: usize = 0x200 * (LEGACY_SETUP_SECS + 1); -const SETUP32_LMA: usize = 0x100000; - fn main() -> Result<(), Box> { 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(()) } @@ -68,44 +59,3 @@ fn build_linux_setup_header( 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 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 + LEGACY_SETUP_SEC_SIZE - 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/boot.S b/framework/jinux-frame/src/arch/x86/boot/boot.S index e01f3fcc4..f85abfb3c 100644 --- a/framework/jinux-frame/src/arch/x86/boot/boot.S +++ b/framework/jinux-frame/src/arch/x86/boot/boot.S @@ -15,7 +15,7 @@ MULTIBOOT_ENTRY_MAGIC = 0x2BADB002 MULTIBOOT2_ENTRY_MAGIC = 0x36D76289 // The Linux 32-bit Boot Protocol entry point. -// Must be located at 0x100000, ABI immutable! +// Must be located at 0x8001000, ABI immutable! .code32 .org 0x000 .global __linux32_boot @@ -34,7 +34,7 @@ __linux32_boot: jmp initial_boot_setup // The Linux 64-bit Boot Protocol entry point. -// Must be located at 0x100200, ABI immutable! +// Must be located at 0x8001200, ABI immutable! .code64 .org 0x200 .global __linux64_boot_tag diff --git a/framework/jinux-frame/src/arch/x86/boot/linker.ld b/framework/jinux-frame/src/arch/x86/boot/linker.ld index 48552a15f..ba9549430 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linker.ld +++ b/framework/jinux-frame/src/arch/x86/boot/linker.ld @@ -2,7 +2,8 @@ ENTRY(__multiboot_boot) OUTPUT_ARCH(i386:x86-64) OUTPUT_FORMAT(elf64-x86-64) -KERNEL_LMA = 0x100000; +KERNEL_LMA = 0x8000000; +LINUX_32_ENTRY = 0x8001000; KERNEL_VMA = 0xffffffff80000000; SECTIONS @@ -13,6 +14,9 @@ SECTIONS .multiboot_header : { KEEP(*(.multiboot_header)) } .multiboot2_header : { KEEP(*(.multiboot2_header)) } + + . = LINUX_32_ENTRY; + .boot : { KEEP(*(.boot)) } . += KERNEL_VMA; diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/boot_params.rs b/framework/jinux-frame/src/arch/x86/boot/linux_boot/boot_params.rs index c2fc1b417..5150a19af 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linux_boot/boot_params.rs +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/boot_params.rs @@ -6,6 +6,7 @@ //! currently not needed by Jinux. //! +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct ScreenInfo { pub(super) orig_x: u8, /* 0x00 */ @@ -48,6 +49,7 @@ pub(super) struct ScreenInfo { pub(super) _reserved: [u8; 2], /* 0x3e */ } +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct ApmBiosInfo { pub(super) version: u16, @@ -61,6 +63,7 @@ pub(super) struct ApmBiosInfo { pub(super) dseg_len: u16, } +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct IstInfo { pub(super) signature: u32, @@ -69,12 +72,14 @@ pub(super) struct IstInfo { pub(super) perf_level: u32, } +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct SysDescTable { pub(super) length: u16, pub(super) table: [u8; 14], } +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct OlpcOfwHeader { pub(super) ofw_magic: u32, /* OFW signature */ @@ -83,11 +88,13 @@ pub(super) struct OlpcOfwHeader { pub(super) irq_desc_table: u32, } +#[derive(Copy, Clone, Debug)] #[repr(C)] pub(super) struct EdidInfo { pub(super) dummy: [u8; 128], } +#[derive(Copy, Clone, Debug)] #[repr(C)] pub(super) struct EfiInfo { pub(super) efi_loader_signature: u32, @@ -100,8 +107,6 @@ pub(super) struct EfiInfo { pub(super) efi_memmap_hi: u32, } -/// Magic stored in SetupHeader.boot_flag. -pub(super) const LINUX_BOOT_FLAG_MAGIC: u16 = 0xAA55; /// Magic stored in SetupHeader.header. pub(super) const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448; @@ -109,6 +114,7 @@ pub(super) const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448; /// /// Originally defined in the linux source tree: /// `linux/arch/x86/include/uapi/asm/bootparam.h` +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct SetupHeader { pub(super) setup_sects: u8, @@ -156,7 +162,7 @@ pub(super) struct SetupHeader { /// /// Originally defined in the linux source tree: /// `linux/arch/x86/include/asm/e820/types.h` -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] #[repr(u32)] pub(super) enum E820Type { Ram = 1, @@ -194,6 +200,7 @@ pub(super) enum E820Type { ReservedKern = 128, } +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct BootE820Entry { pub(super) addr: u64, @@ -203,6 +210,7 @@ pub(super) struct BootE820Entry { const E820_MAX_ENTRIES_ZEROPAGE: usize = 128; +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct EddDeviceParams { // TODO: We currently have no plans to support the edd device, @@ -212,6 +220,7 @@ pub(super) struct EddDeviceParams { pub(super) _dummy: [u8; (0xeec - 0xd00) / 6 - 8], } +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct EddInfo { pub(super) device: u8, @@ -230,6 +239,7 @@ const EDDMAXNR: usize = 6; /// /// Originally defined in the linux source tree: /// `linux/arch/x86/include/uapi/asm/bootparam.h` +#[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub(super) struct BootParams { pub(super) screen_info: ScreenInfo, /* 0x000 */ diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/mod.rs b/framework/jinux-frame/src/arch/x86/boot/linux_boot/mod.rs index eeec58e19..3d2657aa2 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linux_boot/mod.rs +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/mod.rs @@ -78,16 +78,14 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) { } fn init_acpi_arg(acpi: &'static Once) { - acpi.call_once(|| { - BootloaderAcpiArg::Rsdp( - BOOT_PARAMS - .get() - .unwrap() - .acpi_rsdp_addr - .try_into() - .unwrap(), - ) - }); + let rsdp = BOOT_PARAMS.get().unwrap().acpi_rsdp_addr; + if rsdp == 0 { + acpi.call_once(|| BootloaderAcpiArg::NotProvided); + } else { + acpi.call_once(|| { + BootloaderAcpiArg::Rsdp(rsdp.try_into().expect("RSDP address overflowed!")) + }); + } } fn init_framebuffer_info(framebuffer_arg: &'static Once) { @@ -133,10 +131,9 @@ extern "Rust" { } /// The entry point of Rust code called by the Linux 64-bit boot compatible bootloader. -/// It is the ELF entrypoint. #[no_mangle] -unsafe extern "sysv64" fn __linux64_boot(params: boot_params::BootParams) -> ! { - assert_eq!({ params.hdr.boot_flag }, boot_params::LINUX_BOOT_FLAG_MAGIC); +unsafe extern "sysv64" fn __linux64_boot(params_ptr: *const boot_params::BootParams) -> ! { + let params = *params_ptr; assert_eq!({ params.hdr.header }, boot_params::LINUX_BOOT_HEADER_MAGIC); BOOT_PARAMS.call_once(|| params); crate::boot::register_boot_init_callbacks( diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/Cargo.toml b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/Cargo.toml index 58eccd06e..1f0481959 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/Cargo.toml +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] uart_16550 = "0.3.0" +xmas-elf = "0.8.0" diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/boot_params.rs b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/boot_params.rs new file mode 100644 index 000000000..a250c53c3 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/boot_params.rs @@ -0,0 +1,12 @@ +const FIELD_PAYLOAD_OFFSET: u32 = 0x248; +const FIELD_PAYLOAD_LENGTH: u32 = 0x24c; + +/// Safty: user must ensure that the boot_params_ptr is valid +pub unsafe fn get_payload_offset(boot_params_ptr: u32) -> u32 { + *((boot_params_ptr + FIELD_PAYLOAD_OFFSET) as *const u32) +} + +/// Safty: user must ensure that the boot_params_ptr is valid +pub unsafe fn get_payload_length(boot_params_ptr: u32) -> u32 { + *((boot_params_ptr + FIELD_PAYLOAD_LENGTH) as *const u32) +} diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/console.rs b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/console.rs index 53913f532..60dc7043e 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/console.rs +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/console.rs @@ -6,11 +6,13 @@ struct Stdout { serial_port: SerialPort, } -static mut STDOUT: Stdout = Stdout { serial_port: unsafe { SerialPort::new(0x0) } }; +static mut STDOUT: Stdout = Stdout { + serial_port: unsafe { SerialPort::new(0x0) }, +}; /// safety: this function must only be called once pub unsafe fn init() { - STDOUT = Stdout::init(); + STDOUT = Stdout::init(); } impl Stdout { diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/loader.rs b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/loader.rs new file mode 100644 index 000000000..d22eec098 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/loader.rs @@ -0,0 +1,29 @@ +use xmas_elf::program::{ProgramHeader, SegmentData}; + +pub fn load_elf(file: &[u8]) -> u32 { + let elf = xmas_elf::ElfFile::new(file).unwrap(); + + for ph in elf.program_iter() { + let ProgramHeader::Ph64(program) = ph else { + panic!("[setup] Unexpected program header type!"); + }; + if program.get_type().unwrap() == xmas_elf::program::Type::Load { + let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else { + panic!("[setup] Unexpected segment data type!"); + }; + // Safety: the physical address from the ELF file is valid + let dst_slice = unsafe { + core::slice::from_raw_parts_mut( + program.physical_addr as *mut u8, + program.mem_size as usize, + ) + }; + dst_slice[..program.file_size as usize].copy_from_slice(header_data); + let zero_slice = &mut dst_slice[program.file_size as usize..]; + zero_slice.fill(0); + } + } + + // Return the Linux 32-bit Boot Protocol entry point defined by Jinux. + 0x8001000 +} diff --git a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/main.rs b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/main.rs index 8608b81ff..22b98a187 100644 --- a/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/main.rs +++ b/framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/main.rs @@ -1,22 +1,44 @@ #![no_std] #![no_main] +mod boot_params; mod console; +mod loader; -use core::arch::global_asm; +use core::arch::{asm, global_asm}; global_asm!(include_str!("header.S")); +unsafe fn call_jinux_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! { + asm!("mov esi, {}", in(reg) boot_params_ptr); + asm!("mov eax, {}", in(reg) entrypoint); + asm!("jmp eax"); + + unreachable!(); +} + #[no_mangle] pub extern "cdecl" fn _rust_setup_entry(boot_params_ptr: u32) -> ! { - // safety: this init function is only called once + // Safety: this init function is only called once. unsafe { console::init() }; println!("[setup] boot_params_ptr: {:#x}", boot_params_ptr); - #[allow(clippy::empty_loop)] - loop {} + + let payload_offset = unsafe { boot_params::get_payload_offset(boot_params_ptr) }; + let payload_length = unsafe { boot_params::get_payload_length(boot_params_ptr) }; + let payload = unsafe { + core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length as usize) + }; + + println!("[setup] loading ELF payload..."); + let entrypoint = loader::load_elf(payload); + println!("[setup] entrypoint: {:#x}", entrypoint); + + // Safety: the entrypoint and the ptr is valid. + unsafe { call_jinux_entrypoint(entrypoint, boot_params_ptr) }; } #[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { +fn panic(info: &core::panic::PanicInfo) -> ! { + println!("panic: {:?}", info); loop {} } diff --git a/framework/jinux-frame/src/arch/x86/boot/multiboot/mod.rs b/framework/jinux-frame/src/arch/x86/boot/multiboot/mod.rs index c64fa6b16..411a29fa0 100644 --- a/framework/jinux-frame/src/arch/x86/boot/multiboot/mod.rs +++ b/framework/jinux-frame/src/arch/x86/boot/multiboot/mod.rs @@ -3,7 +3,6 @@ use multiboot2::MemoryAreaType; use spin::Once; use crate::{ - arch::x86::kernel::acpi::AcpiMemoryHandler, boot::{ kcmdline::KCmdlineArg, memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType}, @@ -88,20 +87,7 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) { fn init_acpi_arg(acpi: &'static Once) { // The multiboot protocol does not contain RSDP address. // TODO: What about UEFI? - let rsdp = unsafe { rsdp::Rsdp::search_for_on_bios(AcpiMemoryHandler {}) }; - match rsdp { - Ok(map) => match map.validate() { - Ok(_) => acpi.call_once(|| { - if map.revision() > 0 { - BootloaderAcpiArg::Xsdt(map.xsdt_address() as usize) - } else { - BootloaderAcpiArg::Rsdt(map.rsdt_address() as usize) - } - }), - Err(_) => acpi.call_once(|| BootloaderAcpiArg::NotExists), - }, - Err(_) => acpi.call_once(|| BootloaderAcpiArg::NotExists), - }; + acpi.call_once(|| BootloaderAcpiArg::NotProvided); } fn init_framebuffer_info(framebuffer_arg: &'static Once) { diff --git a/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs b/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs index 7367d3c32..afc9e10dc 100644 --- a/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs +++ b/framework/jinux-frame/src/arch/x86/kernel/acpi/mod.rs @@ -104,9 +104,42 @@ pub fn init() { BootloaderAcpiArg::Xsdt(addr) => unsafe { AcpiTables::from_rsdt(AcpiMemoryHandler {}, 1, addr).unwrap() }, - BootloaderAcpiArg::NotExists => { - warn!("Not found ACPI table"); - return; + BootloaderAcpiArg::NotProvided => { + // We search by ourselves if the bootloader decides not to provide a rsdp location. + let rsdp = unsafe { rsdp::Rsdp::search_for_on_bios(AcpiMemoryHandler {}) }; + match rsdp { + Ok(map) => match map.validate() { + Ok(_) => { + if map.revision() > 0 { + unsafe { + AcpiTables::from_rsdt( + AcpiMemoryHandler {}, + 1, + map.xsdt_address() as usize, + ) + .unwrap() + } + } else { + unsafe { + AcpiTables::from_rsdt( + AcpiMemoryHandler {}, + 0, + map.rsdt_address() as usize, + ) + .unwrap() + } + } + } + Err(_) => { + warn!("ACPI info not found!"); + return; + } + }, + Err(_) => { + warn!("ACPI info not found!"); + return; + } + } } }; diff --git a/framework/jinux-frame/src/boot/mod.rs b/framework/jinux-frame/src/boot/mod.rs index 1e3ee9fca..084620683 100644 --- a/framework/jinux-frame/src/boot/mod.rs +++ b/framework/jinux-frame/src/boot/mod.rs @@ -18,7 +18,8 @@ use spin::Once; /// This is because bootloaders differ in such behaviors. #[derive(Copy, Clone, Debug)] pub enum BootloaderAcpiArg { - NotExists, + /// The bootloader does not provide one, a manual search is needed. + NotProvided, /// Physical address of the RSDP. Rsdp(usize), /// Address of RSDT provided in RSDP v1. diff --git a/runner/Cargo.toml b/runner/Cargo.toml index fa09b377f..69d11f0e9 100644 --- a/runner/Cargo.toml +++ b/runner/Cargo.toml @@ -9,3 +9,4 @@ anyhow = "1.0.32" clap = { version = "4.3.19", features = ["derive"] } glob = "0.3.1" rand = "0.8.5" +xmas-elf = "0.8.0" diff --git a/runner/src/machine/qemu_grub_efi/linux_boot.rs b/runner/src/machine/qemu_grub_efi/linux_boot.rs new file mode 100644 index 000000000..1f7ab83e9 --- /dev/null +++ b/runner/src/machine/qemu_grub_efi/linux_boot.rs @@ -0,0 +1,98 @@ +use std::{ + fs::File, + io::{Read, Write}, + path::Path, +}; + +use xmas_elf::program::{ProgramHeader, SegmentData}; + +// We chose the legacy setup sections to be 7 so that the setup header +// is page-aligned and the legacy setup section size would be 0x1000. +const LEGACY_SETUP_SECS: usize = 7; +const LEGACY_SETUP_SEC_SIZE: usize = 0x200 * (LEGACY_SETUP_SECS + 1); +const SETUP32_LMA: usize = 0x100000; + +/// 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 header_to_raw_binary(elf_file: &[u8]) -> Vec { + let elf = xmas_elf::ElfFile::new(&elf_file).unwrap(); + let mut bin = Vec::::new(); + + for ph in elf.program_iter() { + let ProgramHeader::Ph32(program) = ph else { + panic!("Unexpected program header type"); + }; + if program.get_type().unwrap() == xmas_elf::program::Type::Load { + let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else { + panic!("Unexpected segment data type"); + }; + let dst_file_offset = + program.virtual_addr as usize + LEGACY_SETUP_SEC_SIZE - SETUP32_LMA; + let dst_file_length = program.file_size as usize; + if bin.len() < dst_file_offset + dst_file_length { + bin.resize(dst_file_offset + dst_file_length, 0); + } + let dest_slice = bin[dst_file_offset..dst_file_offset + dst_file_length].as_mut(); + dest_slice.copy_from_slice(header_data); + } + } + + bin +} + +/// This function sould 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/jinux-frame/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() + ); + header[offset..offset + size].copy_from_slice(value); +} + +pub fn make_bzimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> { + let mut header = Vec::new(); + File::open(header_path)?.read_to_end(&mut header)?; + let mut header = header_to_raw_binary(&header); + // Pad the header to let the payload starts with 8-byte alignment. + header.resize((header.len() + 7) & !7, 0x00); + + let mut kernel = Vec::new(); + File::open(kernel_path)?.read_to_end(&mut kernel)?; + + let header_len = header.len(); + let kernel_len = kernel.len(); + + let payload_offset = header_len - LEGACY_SETUP_SEC_SIZE + SETUP32_LMA; + fill_header_field( + &mut header, + 0x248, /* payload_offset */ + &(payload_offset as u32).to_le_bytes(), + ); + + fill_header_field( + &mut header, + 0x24C, /* payload_length */ + &(kernel_len as u32).to_le_bytes(), + ); + + fill_header_field( + &mut header, + 0x260, /* init_size */ + &((header_len + kernel_len) as u32).to_le_bytes(), + ); + + let mut kernel_image = File::create(path)?; + kernel_image.write_all(&header)?; + kernel_image.write_all(&kernel)?; + + Ok(()) +} diff --git a/runner/src/machine/qemu_grub_efi.rs b/runner/src/machine/qemu_grub_efi/mod.rs similarity index 72% rename from runner/src/machine/qemu_grub_efi.rs rename to runner/src/machine/qemu_grub_efi/mod.rs index 6b609dc75..0638dbbcc 100644 --- a/runner/src/machine/qemu_grub_efi.rs +++ b/runner/src/machine/qemu_grub_efi/mod.rs @@ -1,6 +1,8 @@ +mod linux_boot; + use std::{ - fs::{self, File}, - io::{Read, Write}, + fs, + io::Read, path::{Path, PathBuf}, }; @@ -85,13 +87,14 @@ pub fn create_bootdev_image( BootProtocol::Linux => { // Find the setup header in the build script output directory. let bs_out_dir = glob("target/x86_64-custom/debug/build/jinux-frame-*").unwrap(); - let header_bin = Path::new(bs_out_dir.into_iter().next().unwrap().unwrap().as_path()) + let header_path = Path::new(bs_out_dir.into_iter().next().unwrap().unwrap().as_path()) .join("out") .join("bin") - .join("jinux-frame-x86-boot-setup.bin"); - // Make the `zimage`-compatible kernel image and place it in the boot directory. + .join("jinux-frame-x86-boot-setup"); + // Make the `bzImage`-compatible kernel image and place it in the boot directory. let target_path = iso_root.join("boot").join("jinuz"); - make_zimage(&target_path, &jinux_path.as_path(), &header_bin.as_path()).unwrap(); + linux_boot::make_bzimage(&target_path, &jinux_path.as_path(), &header_path.as_path()) + .unwrap(); target_path } BootProtocol::Multiboot | BootProtocol::Multiboot2 => { @@ -162,52 +165,3 @@ pub fn generate_grub_cfg( buffer } - -/// This function sould 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/jinux-frame/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() - ); - header[offset..offset + size].copy_from_slice(value); -} - -fn make_zimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> { - let mut header = Vec::new(); - File::open(header_path)?.read_to_end(&mut header)?; - // Pad the header to let the payload starts with 8-byte alignment. - header.resize((header.len() + 7) & !7, 0x00); - - let mut kernel = Vec::new(); - File::open(kernel_path)?.read_to_end(&mut kernel)?; - - let header_len = header.len(); - let kernel_len = kernel.len(); - - fill_header_field( - &mut header, - 0x248, /* payload_offset */ - &(header_len as u32).to_le_bytes(), - ); - fill_header_field( - &mut header, - 0x24C, /* payload_length */ - &(kernel_len as u32).to_le_bytes(), - ); - fill_header_field( - &mut header, - 0x260, /* init_size */ - &((kernel_len + header_len) as u32).to_le_bytes(), - ); - - let mut kernel_image = File::create(path)?; - kernel_image.write_all(&header)?; - kernel_image.write_all(&kernel)?; - - Ok(()) -}