Add EFI stuff to the metadata

This commit is contained in:
Artemis Tosini 2024-08-04 22:43:56 +00:00
parent 9e36a39444
commit d53eb73e76
Signed by: artemist
GPG key ID: EE5227935FE3FF18
5 changed files with 111 additions and 47 deletions

View file

@ -34,7 +34,10 @@
in
{
default = pkgs.mkShell {
packages = [ rust-uefi ];
packages = [
rust-uefi
pkgs.lldb
];
RUST_SRC_PATH = "${rust-uefi}/lib/rustlib/src/rust/library";
};

View file

@ -2,8 +2,7 @@ extern crate alloc;
use alloc::vec::Vec;
use alloc::{collections::BTreeMap, string::String};
use bitflags::bitflags;
use uefi::prelude::BootServices;
use uefi::table::boot::MemoryType;
use uefi::table::boot::MemoryMap;
use uefi::{
proto::console::gop::{GraphicsOutput, PixelFormat},
table::boot::PAGE_SIZE,
@ -123,53 +122,33 @@ struct EFIMemoryMapHeader {
descriptor_version: u32,
}
#[derive(Debug, Clone)]
pub struct EFIMemoryMapParams {
header: EFIMemoryMapHeader,
map: Vec<u8>,
impl Serialize for MemoryMap {
fn alignment() -> usize {
4
}
impl EFIMemoryMapParams {
pub fn from_boot_services(bs: &BootServices) -> Self {
let map = bs
.memory_map(MemoryType::LOADER_DATA)
.expect("Failed to get EFI memory map");
fn size(&self) -> usize {
core::mem::size_of::<EFIMemoryMapHeader>() + self.as_raw().0.len()
}
let (raw, meta) = map.as_raw();
fn serialize_raw(&self, out: &mut [u8]) {
let (raw, meta) = self.as_raw();
let header = EFIMemoryMapHeader {
memory_size: meta.map_size,
descriptor_size: meta.desc_size,
descriptor_version: meta.desc_version,
};
Self {
header,
map: raw.to_vec(),
}
}
}
impl Serialize for EFIMemoryMapParams {
fn alignment() -> usize {
4
}
fn size(&self) -> usize {
core::mem::size_of::<EFIMemoryMapHeader>() + self.map.len()
}
fn serialize_raw(&self, out: &mut [u8]) {
let header_size = core::mem::size_of_val(&self.header);
let header_size = core::mem::size_of_val(&header);
let header_bytes = unsafe {
core::slice::from_raw_parts(
&self.header as *const EFIMemoryMapHeader as *const u8,
&header as *const EFIMemoryMapHeader as *const u8,
header_size,
)
};
out[..header_size].copy_from_slice(header_bytes);
out[header_size..].copy_from_slice(&self.map);
out[header_size..].copy_from_slice(raw);
}
}
@ -279,7 +258,6 @@ pub enum ModuleInfoType {
}
/// Value in the module info TLV
#[derive(Clone)]
pub enum ModuleInfoValue {
StaticString(&'static str),
String(String),
@ -288,7 +266,7 @@ pub enum ModuleInfoValue {
Buffer(Vec<u8>),
Howto(Howto),
EFIFrameBuffer(EFIFramebufferParams),
EFIMemoryMap(EFIMemoryMapParams),
EFIMemoryMap(MemoryMap),
}
impl ModuleInfoValue {
@ -349,7 +327,7 @@ impl core::fmt::Debug for ModuleInfoValue {
/// Key and value in the module info TLV
/// TODO: figure out how to make this properly typesafe
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct ModuleInfoItem {
pub tag: ModuleInfoType,
pub value: ModuleInfoValue,

View file

@ -5,13 +5,25 @@ pub use x86_64::boot_kernel;
mod x86_64 {
use core::{arch::global_asm, slice};
use uefi::table::{boot::PAGE_SIZE, Boot, SystemTable};
use log::warn;
use uefi::{
prelude::BootServices,
proto::console::gop::GraphicsOutput,
table::{
boot::{MemoryType, PAGE_SIZE},
Boot, SystemTable,
},
};
use x86_64::{
structures::paging::{PageTable, PageTableFlags},
PhysAddr,
};
use crate::{object::Kernel, staging::allocate_code};
use crate::{
abi::{EFIFramebufferParams, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize},
object::Kernel,
staging::allocate_code,
};
global_asm!(
r#"
@ -67,6 +79,7 @@ trampoline_size:
kernel_base: *const u8,
modinfo_base: *const u8,
free_ptr: *const u8,
staging: &mut [u8],
) {
let misc_alloc = allocate_code(
system_table.boot_services(),
@ -113,6 +126,9 @@ trampoline_size:
);
unsafe {
// Serialize some final metadata. Calls exit boot services.
add_efi_metadata(system_table, staging);
let trampoline_func: TrampolineFunction =
core::mem::transmute(misc_buf.trampoline.as_ptr());
trampoline_func(
@ -124,4 +140,50 @@ trampoline_size:
);
}
}
unsafe fn add_efi_metadata(boot_system_table: SystemTable<Boot>, mut staging: &mut [u8]) {
staging = add_efi_framebuffer_metadata(boot_system_table.boot_services(), staging);
let (runtime_system_table, memory_map) =
boot_system_table.exit_boot_services(MemoryType::LOADER_DATA);
let memory_map_item = ModuleInfoItem {
tag: ModuleInfoType::EFIMemoryMap,
value: ModuleInfoValue::EFIMemoryMap(memory_map),
};
staging = memory_map_item.serialize_unaligned(staging);
let firmware_handle_item = ModuleInfoItem {
tag: ModuleInfoType::FirmwareHandle,
value: ModuleInfoValue::Pointer(runtime_system_table.as_ptr() as *const u8),
};
let _staging = firmware_handle_item.serialize_unaligned(staging);
}
fn add_efi_framebuffer_metadata<'a>(
boot_services: &BootServices,
staging: &'a mut [u8],
) -> &'a mut [u8] {
let params = match parse_efi_framebuffer_params(boot_services) {
Ok(params) => params,
Err(err) => {
warn!("Unable to parse framebuffer parameters: {}", err);
return staging;
}
};
let item = ModuleInfoItem {
tag: ModuleInfoType::EFIFrameBuffer,
value: ModuleInfoValue::EFIFrameBuffer(params),
};
item.serialize_unaligned(staging)
}
fn parse_efi_framebuffer_params<'a>(
boot_services: &BootServices,
) -> Result<EFIFramebufferParams, uefi::Error> {
let gop_handle = boot_services.get_handle_for_protocol::<GraphicsOutput>()?;
let mut gop = boot_services.open_protocol_exclusive::<GraphicsOutput>(gop_handle)?;
EFIFramebufferParams::from_graphics_output(&mut gop)
.ok_or_else(|| uefi::Error::new(uefi::Status::INVALID_PARAMETER, ()))
}
}

View file

@ -8,11 +8,11 @@ mod staging;
extern crate alloc;
use abi::{Environment, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize};
use abi::{Environment, Howto, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize};
use alloc::{collections::btree_map::BTreeMap, string::ToString};
use log::info;
use object::{Kernel, Module};
use uefi::{fs::FileSystem, prelude::*};
use uefi::{fs::FileSystem, prelude::*, proto::loaded_image::LoadedImage};
#[entry]
fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
@ -20,6 +20,16 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
info!("Starting freeloader");
{
let loaded_image_protocol = system_table
.boot_services()
.open_protocol_exclusive::<LoadedImage>(image_handle)
.expect("Failed to open LoadedImage protocol");
let (base, size) = loaded_image_protocol.info();
log::debug!("Image base: {:#x}, size: {:#x}", base as usize, size);
log::debug!("Main is at {:#x}", &main as *const _ as usize)
}
let kernel_data = {
let mut esp = FileSystem::new(
system_table
@ -42,12 +52,12 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
"kernel".to_string(),
)]));
// Allocate an extra 16KiB for items we have to add to metadata
// Allocate an extra 1 MiB for items we have to add to metadata, like memory map
let metadata_size: usize = kernel_metadata
.iter()
.map(|item| item.alloc_size())
.sum::<usize>()
+ 16 * 1024;
+ 1024 * 1024;
let alloc_size = kernel.alloc_size() + environment.alloc_size() + metadata_size;
let staging = staging::allocate_code(system_table.boot_services(), alloc_size);
@ -61,7 +71,10 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
value: ModuleInfoValue::Pointer(environ_base),
});
let modinfo_base = next.as_ptr();
kernel_metadata.push(abi::ModuleInfoItem {
tag: ModuleInfoType::Howto,
value: ModuleInfoValue::Howto(Howto::VERBOSE | Howto::SERIAL),
});
let free_ptr = unsafe { next.as_ptr().byte_add(metadata_size) };
kernel_metadata.push(ModuleInfoItem {
@ -69,11 +82,19 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
value: ModuleInfoValue::Pointer(free_ptr),
});
let modinfo_base = next.as_ptr();
for item in kernel_metadata.iter() {
next = item.serialize_unaligned(next);
}
boot::boot_kernel(system_table, kernel, kernel_base, modinfo_base, free_ptr);
boot::boot_kernel(
system_table,
kernel,
kernel_base,
modinfo_base,
free_ptr,
next,
);
// boot_kernel shouldn't return
Status::ABORTED

View file

@ -91,7 +91,7 @@ impl<'a> Kernel<'a> {
.copy_from_slice(&self.elf[in_offset..in_offset + copy_size]);
debug!(
"kernel: {:x}@{:x} -> {:x}",
"kernel: {:#x}@{:#x} -> {:#x}",
copy_size,
in_offset,
(&out[out_offset] as *const u8) as usize