Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AArch64: Add direct kernel boot support #297

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions aarch64-unknown-none.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
66 changes: 59 additions & 7 deletions src/efi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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::<u8>() 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,
Expand Down
53 changes: 53 additions & 0 deletions src/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64>,
fdt_entry: MemoryEntry,
Expand Down Expand Up @@ -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<KernelInfo> {
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<'_> {
Expand Down
41 changes: 40 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) -> ! {
Expand Down Expand Up @@ -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);
}
Expand Down
Loading