diff --git a/aarch64-unknown-none.ld b/aarch64-unknown-none.ld index 2cbab38c..2a8e7015 100644 --- a/aarch64-unknown-none.ld +++ b/aarch64-unknown-none.ld @@ -4,11 +4,11 @@ ENTRY(ram64_start) DRAM: [0x4000_0000-0xfc00_0000] FDT: [0x4000_0000-0x401f_ffff) ACPI: [0x4020_0000-0x403f_ffff) - payload:[0x4040_0000-0x405f_ffff) - RHF: [0x40600000-] + payload:[0x4040_0000-0x443f_ffff) + RHF: [0x44400000-] Assuming 2MB is enough to load payload. The stack start is at the end of the RHF region. */ -ram_min = 0x40600000; +ram_min = 0x44400000; /* This value must be identical with arch::aarch64::layout::map::dram::KERNEL_START. */ PAYLOAD_START = 0x40400000; diff --git a/src/efi/mod.rs b/src/efi/mod.rs index c4cf434a..af6e3423 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -10,6 +10,7 @@ use core::{ ptr::null_mut, }; +use crate::efi::file::FileSystemWrapper; use atomic_refcell::AtomicRefCell; use linked_list_allocator::LockedHeap; use r_efi::{ @@ -693,6 +694,8 @@ pub extern "efiapi" fn load_image( path, parent_image_handle, wrapped_fs_ref as *const _ as Handle, + null_mut(), + 0, load_addr, load_size, entry_addr, @@ -1033,10 +1036,13 @@ struct LoadedImageWrapper { type DevicePaths = [file::FileDevicePathProtocol; 2]; +#[allow(clippy::too_many_arguments)] fn new_image_handle( path: &str, parent_handle: Handle, device_handle: Handle, + load_options: *mut core::ffi::c_void, + load_options_size: u32, load_addr: u64, load_size: u64, entry_addr: u64, @@ -1088,8 +1094,8 @@ fn new_image_handle( system_table: unsafe { &mut ST }, device_handle, file_path: &mut file_paths[0].device_path, // Pointer to first path entry - load_options_size: 0, - load_options: null_mut(), + load_options_size, + load_options, image_base: load_addr as *mut _, image_size: load_size, image_code_type: efi::LOADER_CODE, @@ -1102,13 +1108,46 @@ fn new_image_handle( image } +#[cfg(target_arch = "aarch64")] +fn prepare_cmdline(info: &dyn bootinfo::Info) -> (*mut c_void, u32) { + let cmdline = info.cmdline(); + let mut cmdline_size = cmdline.len(); + let mut cmd_addr = null_mut(); + // Allocate memory for cmdline + // cmdline will be converted to [u16], so size must be double + let status = allocate_pool( + efi::LOADER_DATA, + cmdline_size * 2, + &mut cmd_addr as *mut *mut c_void, + ); + + assert!(status == Status::SUCCESS); + + let cmd_addr = cmd_addr as *mut u16; + // Linux asks for cmdline to be in format of utf-16. + for (i, p) in cmdline.iter().enumerate().take(cmdline_size) { + unsafe { + let tmp_addr = cmd_addr.add(i); + *tmp_addr = *p as u16; + } + } + cmdline_size *= 2; + + (cmd_addr as *mut c_void, cmdline_size as u32) +} + +#[cfg(not(target_arch = "aarch64"))] +fn prepare_cmdline(_info: &dyn bootinfo::Info) -> (*mut c_void, u32) { + (null_mut(), 0) +} + pub fn efi_exec( address: u64, loaded_address: u64, loaded_size: u64, info: &dyn bootinfo::Info, - fs: &crate::fat::Filesystem, - block: *const crate::block::VirtioBlockDevice, + fs: Option<&crate::fat::Filesystem>, + block: Option<*const crate::block::VirtioBlockDevice>, ) { let vendor_data = 0u32; @@ -1178,14 +1217,27 @@ pub fn efi_exec( populate_allocator(info, loaded_address, loaded_size); - let efi_part_id = unsafe { block::populate_block_wrappers(&mut BLOCK_WRAPPERS, block) }; + let efi_part_id = if let Some(b) = block { + unsafe { block::populate_block_wrappers(&mut BLOCK_WRAPPERS, b) } + } else { + None + }; + + let wrapfs: FileSystemWrapper; + let mut wrapped_fs = core::ptr::null::() as Handle; + if let Some(f) = fs { + wrapfs = file::FileSystemWrapper::new(f, efi_part_id); + wrapped_fs = &wrapfs as *const _ as Handle; + }; - let wrapped_fs = file::FileSystemWrapper::new(fs, efi_part_id); + let (cmd_addr, cmdline_size) = prepare_cmdline(info); let image = new_image_handle( crate::efi::EFI_BOOT_PATH, 0 as Handle, - &wrapped_fs as *const _ as Handle, + wrapped_fs, + cmd_addr, + cmdline_size, loaded_address, loaded_size, address, diff --git a/src/fdt.rs b/src/fdt.rs index b823c555..530ed69c 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -8,6 +8,13 @@ use crate::{ layout::MemoryDescriptor, }; +// Container of kernel image location address and size +#[cfg(target_arch = "aarch64")] +pub struct KernelInfo { + pub address: u64, + pub size: u64, +} + pub struct StartInfo<'a> { acpi_rsdp_addr: Option, fdt_entry: MemoryEntry, @@ -55,6 +62,52 @@ impl StartInfo<'_> { } None } + + // kernel info is a self-defind item that lays inside Chosen node which should be guaranteed by VMM + #[cfg(target_arch = "aarch64")] + pub fn find_kernel_info(&self) -> Option { + let chosen = self.fdt.find_node("/chosen").unwrap(); + let address = chosen + .properties() + .find(|n| n.name == "linux,kernel-start") + .map(|n| n.value); + + let addr = match address { + Some(addr) => { + let mut a: u64 = 0; + for p in addr.iter().take(8) { + a = (a << 8) + *p as u64; + } + a + } + None => { + return None; + } + }; + + let size = chosen + .properties() + .find(|n| n.name == "linux,kernel-size") + .map(|n| n.value); + + let sz = match size { + Some(sz) => { + let mut s: u64 = 0; + for p in sz.iter().take(8) { + s = (s << 8) + *p as u64; + } + s + } + None => { + return None; + } + }; + + Some(KernelInfo { + address: addr, + size: sz, + }) + } } impl Info for StartInfo<'_> { diff --git a/src/main.rs b/src/main.rs index 23c4362e..b7e67cde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,8 @@ use x86_64::instructions::hlt; #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::layout::code_range; +#[cfg(target_arch = "aarch64")] +use crate::fdt::KernelInfo; #[macro_use] mod serial; @@ -156,10 +158,42 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice, info: &dyn bootinfo:: } log!("Executable loaded"); - efi::efi_exec(entry_addr, load_addr, size, info, &f, device); + efi::efi_exec(entry_addr, load_addr, size, info, Some(&f), Some(device)); true } +#[cfg(target_arch = "aarch64")] +fn boot_from_kernel(k: KernelInfo, info: &dyn bootinfo::Info) { + let load_addr = info.kernel_load_addr(); + let dsc = load_addr as *mut u8; + let src = k.address as *const u8; + unsafe { + core::ptr::copy(src, dsc, k.size as usize); + } + // Get pe_header_offset + let pe_header_offset = unsafe { + let addr = (dsc.wrapping_add(60_usize)) as *const u32; + *addr + }; + let entry_addr = unsafe { + let addr = (dsc.wrapping_add((pe_header_offset + 40).try_into().unwrap())) as *const u32; + *addr + }; + let image_size = unsafe { + let addr = (dsc.wrapping_add((pe_header_offset + 80).try_into().unwrap())) as *const u32; + *addr + }; + + efi::efi_exec( + load_addr + entry_addr as u64, + load_addr, + image_size.into(), + info, + None, + None, + ); +} + #[cfg(target_arch = "x86_64")] #[no_mangle] pub extern "C" fn rust64_start(#[cfg(not(feature = "coreboot"))] pvh_info: &pvh::StartInfo) -> ! { @@ -194,6 +228,11 @@ pub extern "C" fn rust64_start(x0: *const u8) -> ! { None, ); + if let Some(kernel_info) = info.find_kernel_info() { + log!("Boot with direct kernel"); + boot_from_kernel(kernel_info, &info); + } + if let Some((base, length)) = info.find_compatible_region(&["pci-host-ecam-generic"]) { pci::init(base as u64, length as u64); }