Unwrap initrd from PE image for Linux

This commit is contained in:
Julian Stecklina 2022-11-23 13:51:07 +01:00
parent 60b269b69c
commit 568fe1d499
2 changed files with 60 additions and 28 deletions

View File

@ -1,19 +1,21 @@
//! This module implements the protocols to hand an initrd to the //! This module implements the protocols to hand an initrd to the
//! Linux kernel. //! Linux kernel.
use core::{ffi::c_void, 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, vec}; use alloc::boxed::Box;
use uefi::{ use uefi::{
prelude::BootServices, prelude::BootServices,
proto::{ proto::{
device_path::{DevicePath, FfiDevicePath}, device_path::{DevicePath, FfiDevicePath},
media::file::{File, FileInfo, RegularFile}, media::file::RegularFile,
Protocol, Protocol,
}, },
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
@ -56,9 +58,22 @@ struct LoadFile2Protocol {
// This is not part of the official protocol struct. // This is not part of the official protocol struct.
file: RegularFile, file: RegularFile,
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,
@ -66,20 +81,19 @@ impl LoadFile2Protocol {
buffer_size: *mut usize, buffer_size: *mut usize,
buffer: *mut c_void, buffer: *mut c_void,
) -> Result<()> { ) -> Result<()> {
let mut fs_info_buf = vec![0; 128]; if buffer.is_null() || unsafe { *buffer_size } < self.initrd_size() {
let fs_info = self
.file
.get_info::<FileInfo>(&mut fs_info_buf)
.map_err(|_| Status::INVALID_PARAMETER)?;
let fs_size = usize::try_from(fs_info.file_size()).unwrap();
if buffer.is_null() || unsafe { *buffer_size } < fs_size {
unsafe { unsafe {
*buffer_size = fs_size; *buffer_size = self.initrd_size();
} }
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 {
*buffer_size = self.initrd_size();
}
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) };
@ -107,11 +121,38 @@ pub struct InitrdLoader {
registered: bool, registered: bool,
} }
/// Returns the data range of the initrd in the PE binary.
fn initrd_location(initrd_efi: &mut RegularFile) -> Result<Range<usize>> {
let file_data = read_all(initrd_efi)?;
let pe_binary = goblin::pe::PE::parse(&file_data).map_err(|_| Status::INVALID_PARAMETER)?;
pe_binary
.sections
.iter()
.find(|s| s.name().unwrap() == ".initrd")
.and_then(|s| {
let section_start: usize = s.pointer_to_raw_data.try_into().unwrap();
let section_size: usize = s.size_of_raw_data.try_into().unwrap();
Some(Range {
start: section_start,
end: section_start + section_size,
})
})
.ok_or(Status::END_OF_FILE.into())
}
impl InitrdLoader { impl InitrdLoader {
pub fn new(boot_services: &BootServices, handle: Handle, file: RegularFile) -> Result<Self> { pub fn new(
boot_services: &BootServices,
handle: Handle,
mut file: RegularFile,
) -> Result<Self> {
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, file,
range,
}); });
unsafe { unsafe {

View File

@ -9,7 +9,6 @@ mod linux_loader;
mod pe_section; mod pe_section;
mod uefi_helpers; mod uefi_helpers;
use log::{debug, info};
use uefi::{ use uefi::{
prelude::*, prelude::*,
proto::{ proto::{
@ -21,8 +20,7 @@ use uefi::{
use crate::{ use crate::{
linux_loader::InitrdLoader, linux_loader::InitrdLoader,
pe_section::pe_section, uefi_helpers::{booted_image_cmdline, read_all},
uefi_helpers::{booted_image_cmdline, booted_image_file, read_all},
}; };
fn print_logo(output: &mut Output) { fn print_logo(output: &mut Output) {
@ -49,15 +47,6 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
print_logo(system_table.stdout()); print_logo(system_table.stdout());
{
let image_data =
read_all(&mut booted_image_file(system_table.boot_services()).unwrap()).unwrap();
if let Some(data) = pe_section(&image_data, ".osrel") {
info!("osrel = {}", core::str::from_utf8(data).unwrap_or("???"))
}
}
let mut file_system = system_table let mut file_system = system_table
.boot_services() .boot_services()
.get_image_file_system(handle) .get_image_file_system(handle)
@ -71,7 +60,11 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
.unwrap(); .unwrap();
let initrd = root let initrd = root
.open(cstr16!("initrd"), FileMode::Read, FileAttribute::empty()) .open(
cstr16!("initrd.efi"),
FileMode::Read,
FileAttribute::empty(),
)
.unwrap() .unwrap()
.into_regular_file() .into_regular_file()
.unwrap(); .unwrap();
@ -80,8 +73,6 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
drop(root); drop(root);
drop(file_system); drop(file_system);
debug!("Opened file");
let kernel_cmdline = booted_image_cmdline(system_table.boot_services()).unwrap(); let kernel_cmdline = booted_image_cmdline(system_table.boot_services()).unwrap();
let kernel_data = read_all(&mut file).unwrap(); let kernel_data = read_all(&mut file).unwrap();