diff --git a/Cargo.lock b/Cargo.lock index 4dfee7289..7f4d31f17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -392,9 +392,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitvec" @@ -1028,7 +1028,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b624a7b3f704734d98d21455b617607eb7043d4509d1c34bf9e7ff7dd47b31a" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "derive_more", "log", "ptr_meta", @@ -1515,7 +1515,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07ead9f748a4646479b850add36b527113a80e80a7e0f44d7b0334291850dcc5" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "log", "ptr_meta", "ucs2", @@ -1541,7 +1541,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62642516099c6441a5f41b0da8486d5fc3515a0603b0fdaea67b31600e22082e" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "ptr_meta", "uguid", ] @@ -1552,7 +1552,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864ac69eadd877bfb34e7814be1928122ed0057d9f975169a56ee496aa7bdfd7" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "ptr_meta", "uguid", ] @@ -1737,12 +1737,12 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.14.10" +version = "0.14.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" +checksum = "3b835097a84e4457323331ec5d6eb23d096066cbfb215d54096dcb4b2e85f500" dependencies = [ "bit_field", - "bitflags 1.3.2", + "bitflags 2.4.1", "rustversion", "volatile", ] diff --git a/framework/aster-frame/src/arch/x86/boot/boot.S b/framework/aster-frame/src/arch/x86/boot/boot.S index f85abfb3c..6507924f2 100644 --- a/framework/aster-frame/src/arch/x86/boot/boot.S +++ b/framework/aster-frame/src/arch/x86/boot/boot.S @@ -39,9 +39,14 @@ __linux32_boot: .org 0x200 .global __linux64_boot_tag __linux64_boot_tag: - lea rax, [rip + __linux64_boot] // jump into Rust code - call rax - jmp halt // unreachable here + // We switch back to 32-bit mode to call the 32-bit entry point. + lgdt [boot_gdtr] + mov eax, 0xb002b002 // magic for boot_params + mov ebx, esi // struct boot_params * + sub rsp, 8 + mov dword ptr [rsp], offset __linux32_boot + mov dword ptr [rsp + 4], 24 + retf // The multiboot & multiboot2 entry point. .code32 diff --git a/framework/libs/boot-trojan/trojan/Cargo.toml b/framework/libs/boot-trojan/trojan/Cargo.toml index b1311e131..e20351d53 100644 --- a/framework/libs/boot-trojan/trojan/Cargo.toml +++ b/framework/libs/boot-trojan/trojan/Cargo.toml @@ -12,10 +12,12 @@ uart_16550 = "0.3.0" xmas-elf = "0.8.0" [target.x86_64-unknown-none.dependencies] +bitflags = "2.4.1" log = "0.4.20" uefi = "0.26.0" uefi-services = "0.23.0" +x86_64 = "0.14.11" [features] -default = ["debug_print"] +default = [] debug_print = [] diff --git a/framework/libs/boot-trojan/trojan/build.rs b/framework/libs/boot-trojan/trojan/build.rs index 9ef845512..af5b57d4b 100644 --- a/framework/libs/boot-trojan/trojan/build.rs +++ b/framework/libs/boot-trojan/trojan/build.rs @@ -6,7 +6,7 @@ fn main() { let linker_script = if target_arch == "x86_64-unknown-none" { source_dir.join("src/x86/amd64_efi/linker.ld") } else if target_arch == "x86_64-i386_pm-none" { - source_dir.join("src/arch/legacy_i386/linker.ld") + source_dir.join("src/x86/legacy_i386/linker.ld") } else { panic!("Unsupported target_arch: {}", target_arch); }; diff --git a/framework/libs/boot-trojan/trojan/src/loader.rs b/framework/libs/boot-trojan/trojan/src/loader.rs index e3a392765..e4a0558c5 100644 --- a/framework/libs/boot-trojan/trojan/src/loader.rs +++ b/framework/libs/boot-trojan/trojan/src/loader.rs @@ -1,43 +1,61 @@ use xmas_elf::program::{ProgramHeader, SegmentData}; -pub fn load_elf(file: &[u8]) -> u32 { +/// 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; + } +} + +pub fn load_elf(file: &[u8]) { 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!"); + panic!("[setup] Unexpected program header type! Jinux should be 64-bit ELF binary."); }; 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, - ) - }; - /* crate::println!( - "[setup loader debug] loading ELF segment at {:#x}, size = {:#x}", - program.physical_addr, - program.mem_size, - ); */ - #[cfg(feature = "debug_print")] - unsafe { - use crate::console::{print_hex, print_str}; - print_str("[setup loader debug] loading ELF segment at "); - print_hex(program.physical_addr as u64); - print_str(", size = "); - print_hex(program.mem_size as u64); - print_str("\n"); - } - 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); + load_segment(&elf, program); } } - - // Return the Linux Boot Protocol entry point defined by Asterinas. - crate::x86::ASTER_ENTRY_POINT +} + +fn load_segment(file: &xmas_elf::ElfFile, program: &xmas_elf::program::ProgramHeader64) { + let SegmentData::Undefined(header_data) = program.get_data(&file).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) + }; + /* crate::println!( + "[setup loader debug] loading ELF segment at {:#x}, size = {:#x}", + program.physical_addr, + program.mem_size, + ); */ + #[cfg(feature = "debug_print")] + unsafe { + use crate::console::{print_hex, print_str}; + print_str("[setup loader debug] loading ELF segment at "); + print_hex(program.physical_addr as u64); + print_str(", size = "); + print_hex(program.mem_size as u64); + print_str("\n"); + } + // Safety: the ELF file is valid + // dst_slice[..program.file_size as usize].copy_from_slice(header_data); + unsafe { + memcpy( + dst_slice.as_mut_ptr(), + header_data.as_ptr(), + program.file_size as usize, + ); + } + let zero_slice = &mut dst_slice[program.file_size as usize..]; + zero_slice.fill(0); } diff --git a/framework/libs/boot-trojan/trojan/src/main.rs b/framework/libs/boot-trojan/trojan/src/main.rs index b7f685941..f8739a514 100644 --- a/framework/libs/boot-trojan/trojan/src/main.rs +++ b/framework/libs/boot-trojan/trojan/src/main.rs @@ -5,47 +5,19 @@ use linux_boot_params::BootParams; mod console; mod loader; + +// Unfortunately, the entrypoint is not defined here in the main.rs file. +// See the exported functions in the x86 module for details. mod x86; -use console::{print_hex, print_str}; - -/// The entrypoint of the trojan. The architecture-specific entrypoint will call this function. -fn trojan_entry(boot_params_ptr: usize) -> ! { - // println!("[setup] bzImage loaded at {:#x}", x86::relocation::get_image_loaded_offset()); - unsafe { - print_str("[setup] bzImage loaded at "); - print_hex(x86::relocation::get_image_loaded_offset() as u64); - 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 = unsafe { - 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::relocation::get_image_loaded_offset(); - let payload_offset = (loaded_offset + hdr.payload_offset as isize) as usize; - let payload_length = hdr.payload_length as usize; - core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length as usize) - }; - - // println!("[setup] loading ELF payload at {:#x}", payload as *const _ as *const u8 as usize); - unsafe { - print_str("[setup] loading ELF payload at "); - print_hex(payload as *const _ as *const u8 as u64); - print_str("\n"); - } - let entrypoint = loader::load_elf(payload); - - // println!("[setup] jumping to payload entrypoint at {:#x}", entrypoint); - unsafe { - print_str("[setup] jumping to payload entrypoint at "); - print_hex(entrypoint as u64); - print_str("\n"); - } - - // Safety: the entrypoint and the ptr is valid. - unsafe { x86::call_aster_entrypoint(entrypoint.into(), boot_params_ptr.try_into().unwrap()) }; +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::relocation::get_image_loaded_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 as usize) } } diff --git a/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/efi.rs b/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/efi.rs index ac3b89661..60c78a261 100644 --- a/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/efi.rs +++ b/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/efi.rs @@ -1,4 +1,3 @@ -use core::fmt::Write; use uefi::{ data_types::Handle, proto::loaded_image::LoadedImage, @@ -7,7 +6,7 @@ use uefi::{ use linux_boot_params::BootParams; -#[no_mangle] +#[export_name = "efi_stub_entry"] extern "sysv64" fn efi_stub_entry(handle: Handle, mut system_table: SystemTable) -> ! { unsafe { system_table.boot_services().set_image_handle(handle); @@ -23,7 +22,7 @@ extern "sysv64" fn efi_stub_entry(handle: Handle, mut system_table: SystemTable< ); } -#[no_mangle] +#[export_name = "efi_handover_entry"] extern "sysv64" fn efi_handover_entry( handle: Handle, mut system_table: SystemTable, @@ -39,16 +38,21 @@ extern "sysv64" fn efi_handover_entry( fn efi_phase_boot( handle: Handle, - mut system_table: SystemTable, + system_table: SystemTable, boot_params: *mut BootParams, ) -> ! { // Safety: this init function is only called once. unsafe { crate::console::init() }; - - // Safety: this is a right place to call this function. + + // Safety: this is the right time to apply relocations. unsafe { crate::x86::relocation::apply_rela_dyn_relocations() }; uefi_services::println!("[EFI stub] Relocations applied."); + + uefi_services::println!("[EFI stub] Loading payload."); + let payload = unsafe { crate::get_payload(&*boot_params) }; + crate::loader::load_elf(payload); + uefi_services::println!("[EFI stub] Exiting EFI boot services."); let memory_type = { let boot_services = system_table.boot_services(); @@ -63,14 +67,40 @@ fn efi_phase_boot( } fn efi_phase_runtime( - mut system_table: SystemTable, + _system_table: SystemTable, memory_map: MemoryMap<'static>, - boot_params: *mut BootParams, + boot_params_ptr: *mut BootParams, ) -> ! { unsafe { crate::console::print_str("[EFI stub] Entered runtime services.\n"); } + let boot_params = unsafe { &mut *boot_params_ptr }; + + // Write memory map to e820 table in boot_params. + let e820_table = &mut boot_params.e820_table; + let mut e820_entries = 0; + for md in memory_map.entries() { + if e820_entries >= e820_table.len() || e820_entries >= 128 { + break; + } + e820_table[e820_entries] = linux_boot_params::BootE820Entry { + addr: md.phys_start as u64, + size: md.page_count as u64 * 4096, + typ: match md.ty { + uefi::table::boot::MemoryType::CONVENTIONAL => linux_boot_params::E820Type::Ram, + uefi::table::boot::MemoryType::RESERVED => linux_boot_params::E820Type::Reserved, + uefi::table::boot::MemoryType::ACPI_RECLAIM => linux_boot_params::E820Type::Acpi, + uefi::table::boot::MemoryType::ACPI_NON_VOLATILE => { + linux_boot_params::E820Type::Nvs + } + _ => linux_boot_params::E820Type::Reserved, + }, + }; + e820_entries += 1; + } + boot_params.e820_entries = e820_entries as u8; + #[cfg(feature = "debug_print")] unsafe { use crate::console::{print_hex, print_str}; @@ -90,5 +120,5 @@ fn efi_phase_runtime( } } - crate::trojan_entry(boot_params as usize); + unsafe { super::call_jinux_entrypoint(super::JINUX_ENTRY_POINT, boot_params_ptr as u64) } } diff --git a/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/mod.rs b/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/mod.rs index 48590d0e5..7f1cd2172 100644 --- a/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/mod.rs +++ b/framework/libs/boot-trojan/trojan/src/x86/amd64_efi/mod.rs @@ -8,7 +8,7 @@ global_asm!(include_str!("setup.S")); pub const ASTER_ENTRY_POINT: u32 = 0x8001200; -pub unsafe fn call_aster_entrypoint(entrypoint: u64, boot_params_ptr: u64) -> ! { +unsafe fn call_aster_entrypoint(entrypoint: u64, boot_params_ptr: u64) -> ! { asm!("mov rsi, {}", in(reg) boot_params_ptr as u64); asm!("mov rax, {}", in(reg) entrypoint as u64); asm!("jmp rax"); diff --git a/framework/libs/boot-trojan/trojan/src/x86/legacy_i386/mod.rs b/framework/libs/boot-trojan/trojan/src/x86/legacy_i386/mod.rs index 0006b227f..8854bde52 100644 --- a/framework/libs/boot-trojan/trojan/src/x86/legacy_i386/mod.rs +++ b/framework/libs/boot-trojan/trojan/src/x86/legacy_i386/mod.rs @@ -1,19 +1,43 @@ +use linux_boot_params::BootParams; + use core::arch::{asm, global_asm}; global_asm!(include_str!("header.S")); global_asm!(include_str!("setup.S")); -#[no_mangle] -extern "cdecl" fn _trojan_entry_32(boot_params_ptr: u32) -> ! { +use crate::console::{print_hex, print_str}; + +#[export_name = "_trojan_entry_32"] +extern "cdecl" fn trojan_entry(boot_params_ptr: u32) -> ! { // Safety: this init function is only called once. unsafe { crate::console::init() }; - crate::trojan_entry(0, boot_params_ptr.try_into().unwrap()); + + // println!("[setup] bzImage loaded at {:#x}", x86::relocation::get_image_loaded_offset()); + unsafe { + print_str("[setup] bzImage loaded at "); + print_hex(crate::x86::relocation::get_image_loaded_offset() as u64); + 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); + + // Safety: the entrypoint and the ptr is valid. + unsafe { + call_jinux_entrypoint( + super::JINUX_ENTRY_POINT, + boot_params_ptr.try_into().unwrap(), + ) + }; } pub const ASTER_ENTRY_POINT: u32 = 0x8001000; -pub unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! { +unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! { asm!("mov esi, {}", in(reg) boot_params_ptr); asm!("mov eax, {}", in(reg) entrypoint); asm!("jmp eax"); @@ -22,6 +46,6 @@ pub unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! } #[panic_handler] -fn panic(info: &core::panic::PanicInfo) -> ! { +fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } diff --git a/framework/libs/boot-trojan/trojan/src/x86/relocation.rs b/framework/libs/boot-trojan/trojan/src/x86/relocation.rs index ac18c0c55..535f91f36 100644 --- a/framework/libs/boot-trojan/trojan/src/x86/relocation.rs +++ b/framework/libs/boot-trojan/trojan/src/x86/relocation.rs @@ -11,12 +11,14 @@ pub fn get_image_loaded_offset() -> isize { start_of_setup32 as isize - START_OF_SETUP32_VA as isize } +#[allow(unused)] struct Elf64Rela { r_offset: u64, r_info: u64, r_addend: i64, } +#[allow(unused)] fn get_rela_array() -> &'static [Elf64Rela] { extern "C" { fn __rela_dyn_start(); @@ -59,6 +61,7 @@ const R_X86_64_RELATIVE: u32 = 8; /// This function will modify the memory pointed by the relocations. And the Rust memory safety /// mechanisms are not aware of these kind of modification. Failure to do relocations will cause /// dyn Trait objects to break. +#[allow(unused)] pub unsafe fn apply_rela_dyn_relocations() { let image_loaded_offset = get_image_loaded_offset(); let relas = get_rela_array();