lanzaboote: load kernel and initrd into memory only once
This commit is contained in:
parent
d754a87d5c
commit
7a15bba50b
|
@ -7,20 +7,17 @@
|
||||||
|
|
||||||
use core::{ffi::c_void, ops::Range, pin::Pin, ptr::slice_from_raw_parts_mut};
|
use core::{ffi::c_void, ops::Range, pin::Pin, ptr::slice_from_raw_parts_mut};
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
use uefi::{
|
use uefi::{
|
||||||
prelude::BootServices,
|
prelude::BootServices,
|
||||||
proto::{
|
proto::{
|
||||||
device_path::{DevicePath, FfiDevicePath},
|
device_path::{DevicePath, FfiDevicePath},
|
||||||
media::file::RegularFile,
|
|
||||||
Protocol,
|
Protocol,
|
||||||
},
|
},
|
||||||
table::boot::LoadImageSource,
|
table::boot::LoadImageSource,
|
||||||
unsafe_guid, Handle, Identify, Result, ResultExt, Status,
|
unsafe_guid, Handle, Identify, Result, ResultExt, Status,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::uefi_helpers::read_all;
|
|
||||||
|
|
||||||
/// The Linux kernel's initrd loading device path.
|
/// The Linux kernel's initrd loading device path.
|
||||||
///
|
///
|
||||||
/// The Linux kernel points us to
|
/// The Linux kernel points us to
|
||||||
|
@ -65,23 +62,10 @@ struct LoadFile2Protocol {
|
||||||
) -> Status,
|
) -> Status,
|
||||||
|
|
||||||
// This is not part of the official protocol struct.
|
// This is not part of the official protocol struct.
|
||||||
file: RegularFile,
|
initrd_data: Vec<u8>,
|
||||||
range: Range<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoadFile2Protocol {
|
impl LoadFile2Protocol {
|
||||||
fn initrd_start(&self) -> usize {
|
|
||||||
self.range.start
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initrd_size(&self) -> usize {
|
|
||||||
if self.range.is_empty() {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.range.end - self.range.start
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_file(
|
fn load_file(
|
||||||
&mut self,
|
&mut self,
|
||||||
_file_path: *const FfiDevicePath,
|
_file_path: *const FfiDevicePath,
|
||||||
|
@ -89,24 +73,21 @@ impl LoadFile2Protocol {
|
||||||
buffer_size: *mut usize,
|
buffer_size: *mut usize,
|
||||||
buffer: *mut c_void,
|
buffer: *mut c_void,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if buffer.is_null() || unsafe { *buffer_size } < self.initrd_size() {
|
if buffer.is_null() || unsafe { *buffer_size } < self.initrd_data.len() {
|
||||||
unsafe {
|
unsafe {
|
||||||
*buffer_size = self.initrd_size();
|
*buffer_size = self.initrd_data.len();
|
||||||
}
|
}
|
||||||
return Err(Status::BUFFER_TOO_SMALL.into());
|
return Err(Status::BUFFER_TOO_SMALL.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
self.file
|
|
||||||
.set_position(self.initrd_start().try_into().unwrap())?;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
*buffer_size = self.initrd_size();
|
*buffer_size = self.initrd_data.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_slice: &mut [u8] =
|
let output_slice: &mut [u8] =
|
||||||
unsafe { &mut *slice_from_raw_parts_mut(buffer as *mut u8, *buffer_size) };
|
unsafe { &mut *slice_from_raw_parts_mut(buffer as *mut u8, *buffer_size) };
|
||||||
|
|
||||||
let read_bytes = self.file.read(output_slice).map_err(|e| e.status())?;
|
output_slice.copy_from_slice(&self.initrd_data);
|
||||||
assert_eq!(read_bytes, unsafe { *buffer_size });
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -137,11 +118,8 @@ pub struct InitrdLoader {
|
||||||
/// Returns the data range of the initrd in the PE binary.
|
/// Returns the data range of the initrd in the PE binary.
|
||||||
///
|
///
|
||||||
/// The initrd has to be embedded in the file as a .initrd PE section.
|
/// The initrd has to be embedded in the file as a .initrd PE section.
|
||||||
fn initrd_location(initrd_efi: &mut RegularFile) -> Result<Range<usize>> {
|
fn initrd_location(initrd_efi: &[u8]) -> Result<Range<usize>> {
|
||||||
initrd_efi.set_position(0)?;
|
let pe_binary = goblin::pe::PE::parse(initrd_efi).map_err(|_| Status::INVALID_PARAMETER)?;
|
||||||
|
|
||||||
let file_data = read_all(initrd_efi)?;
|
|
||||||
let pe_binary = goblin::pe::PE::parse(&file_data).map_err(|_| Status::INVALID_PARAMETER)?;
|
|
||||||
|
|
||||||
pe_binary
|
pe_binary
|
||||||
.sections
|
.sections
|
||||||
|
@ -163,14 +141,11 @@ fn initrd_location(initrd_efi: &mut RegularFile) -> Result<Range<usize>> {
|
||||||
///
|
///
|
||||||
/// For this to work, the initrd needs to be a PE binary. We misuse
|
/// For this to work, the initrd needs to be a PE binary. We misuse
|
||||||
/// [`BootServices::load_image`] for this.
|
/// [`BootServices::load_image`] for this.
|
||||||
fn initrd_verify(boot_services: &BootServices, initrd_efi: &mut RegularFile) -> Result<()> {
|
fn initrd_verify(boot_services: &BootServices, initrd_efi: &[u8]) -> Result<()> {
|
||||||
initrd_efi.set_position(0)?;
|
|
||||||
let file_data = read_all(initrd_efi)?;
|
|
||||||
|
|
||||||
let initrd_handle = boot_services.load_image(
|
let initrd_handle = boot_services.load_image(
|
||||||
boot_services.image_handle(),
|
boot_services.image_handle(),
|
||||||
LoadImageSource::FromBuffer {
|
LoadImageSource::FromBuffer {
|
||||||
buffer: &file_data,
|
buffer: &initrd_efi,
|
||||||
file_path: None,
|
file_path: None,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -192,15 +167,21 @@ impl InitrdLoader {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
boot_services: &BootServices,
|
boot_services: &BootServices,
|
||||||
handle: Handle,
|
handle: Handle,
|
||||||
mut file: RegularFile,
|
mut initrd_data: Vec<u8>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
initrd_verify(boot_services, &mut file)?;
|
initrd_verify(boot_services, &initrd_data)?;
|
||||||
|
|
||||||
|
let range = initrd_location(&initrd_data)?;
|
||||||
|
|
||||||
|
// Remove the PE wrapper from the initrd. We do this in place
|
||||||
|
// to avoid having to keep the initrd in memory twice.
|
||||||
|
initrd_data.drain(0..range.start);
|
||||||
|
initrd_data.resize(range.end - range.start, 0);
|
||||||
|
initrd_data.shrink_to_fit();
|
||||||
|
|
||||||
let range = initrd_location(&mut file)?;
|
|
||||||
let mut proto = Box::pin(LoadFile2Protocol {
|
let mut proto = Box::pin(LoadFile2Protocol {
|
||||||
load_file: raw_load_file,
|
load_file: raw_load_file,
|
||||||
file,
|
initrd_data,
|
||||||
range,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Linux finds the right handle by looking for something that
|
// Linux finds the right handle by looking for something that
|
||||||
|
|
|
@ -84,8 +84,8 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
||||||
EmbeddedConfiguration::new(&mut booted_image_file(system_table.boot_services()).unwrap())
|
EmbeddedConfiguration::new(&mut booted_image_file(system_table.boot_services()).unwrap())
|
||||||
.expect("Failed to extract configuration from binary. Did you run lanzatool?");
|
.expect("Failed to extract configuration from binary. Did you run lanzatool?");
|
||||||
|
|
||||||
let mut kernel_file;
|
let kernel_data;
|
||||||
let initrd_file;
|
let initrd_data;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut file_system = system_table
|
let mut file_system = system_table
|
||||||
|
@ -96,7 +96,7 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
||||||
.open_volume()
|
.open_volume()
|
||||||
.expect("Failed to find ESP root directory");
|
.expect("Failed to find ESP root directory");
|
||||||
|
|
||||||
kernel_file = root
|
let mut kernel_file = root
|
||||||
.open(
|
.open(
|
||||||
&config.kernel_filename,
|
&config.kernel_filename,
|
||||||
FileMode::Read,
|
FileMode::Read,
|
||||||
|
@ -106,7 +106,9 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
||||||
.into_regular_file()
|
.into_regular_file()
|
||||||
.expect("Kernel is not a regular file");
|
.expect("Kernel is not a regular file");
|
||||||
|
|
||||||
initrd_file = root
|
kernel_data = read_all(&mut kernel_file).expect("Failed to read kernel file into memory");
|
||||||
|
|
||||||
|
let mut initrd_file = root
|
||||||
.open(
|
.open(
|
||||||
&config.initrd_filename,
|
&config.initrd_filename,
|
||||||
FileMode::Read,
|
FileMode::Read,
|
||||||
|
@ -115,15 +117,14 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
||||||
.expect("Failed to open initrd for reading")
|
.expect("Failed to open initrd for reading")
|
||||||
.into_regular_file()
|
.into_regular_file()
|
||||||
.expect("Initrd is not a regular file");
|
.expect("Initrd is not a regular file");
|
||||||
|
|
||||||
|
initrd_data = read_all(&mut initrd_file).expect("Failed to read kernel file into memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
let kernel_cmdline =
|
let kernel_cmdline =
|
||||||
booted_image_cmdline(system_table.boot_services()).expect("Failed to fetch command line");
|
booted_image_cmdline(system_table.boot_services()).expect("Failed to fetch command line");
|
||||||
|
|
||||||
let kernel_handle = {
|
let kernel_handle = {
|
||||||
let kernel_data =
|
|
||||||
read_all(&mut kernel_file).expect("Failed to read kernel file into memory");
|
|
||||||
|
|
||||||
system_table
|
system_table
|
||||||
.boot_services()
|
.boot_services()
|
||||||
.load_image(
|
.load_image(
|
||||||
|
@ -148,7 +149,7 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_file)
|
let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)
|
||||||
.expect("Failed to load the initrd. It may not be there or it is not signed");
|
.expect("Failed to load the initrd. It may not be there or it is not signed");
|
||||||
let status = system_table
|
let status = system_table
|
||||||
.boot_services()
|
.boot_services()
|
||||||
|
|
Loading…
Reference in New Issue