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

View file

@ -5,13 +5,25 @@ pub use x86_64::boot_kernel;
mod x86_64 { mod x86_64 {
use core::{arch::global_asm, slice}; 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::{ use x86_64::{
structures::paging::{PageTable, PageTableFlags}, structures::paging::{PageTable, PageTableFlags},
PhysAddr, PhysAddr,
}; };
use crate::{object::Kernel, staging::allocate_code}; use crate::{
abi::{EFIFramebufferParams, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize},
object::Kernel,
staging::allocate_code,
};
global_asm!( global_asm!(
r#" r#"
@ -67,6 +79,7 @@ trampoline_size:
kernel_base: *const u8, kernel_base: *const u8,
modinfo_base: *const u8, modinfo_base: *const u8,
free_ptr: *const u8, free_ptr: *const u8,
staging: &mut [u8],
) { ) {
let misc_alloc = allocate_code( let misc_alloc = allocate_code(
system_table.boot_services(), system_table.boot_services(),
@ -113,6 +126,9 @@ trampoline_size:
); );
unsafe { unsafe {
// Serialize some final metadata. Calls exit boot services.
add_efi_metadata(system_table, staging);
let trampoline_func: TrampolineFunction = let trampoline_func: TrampolineFunction =
core::mem::transmute(misc_buf.trampoline.as_ptr()); core::mem::transmute(misc_buf.trampoline.as_ptr());
trampoline_func( 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; 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 alloc::{collections::btree_map::BTreeMap, string::ToString};
use log::info; use log::info;
use object::{Kernel, Module}; use object::{Kernel, Module};
use uefi::{fs::FileSystem, prelude::*}; use uefi::{fs::FileSystem, prelude::*, proto::loaded_image::LoadedImage};
#[entry] #[entry]
fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status { 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"); 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 kernel_data = {
let mut esp = FileSystem::new( let mut esp = FileSystem::new(
system_table system_table
@ -42,12 +52,12 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
"kernel".to_string(), "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 let metadata_size: usize = kernel_metadata
.iter() .iter()
.map(|item| item.alloc_size()) .map(|item| item.alloc_size())
.sum::<usize>() .sum::<usize>()
+ 16 * 1024; + 1024 * 1024;
let alloc_size = kernel.alloc_size() + environment.alloc_size() + metadata_size; let alloc_size = kernel.alloc_size() + environment.alloc_size() + metadata_size;
let staging = staging::allocate_code(system_table.boot_services(), alloc_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), 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) }; let free_ptr = unsafe { next.as_ptr().byte_add(metadata_size) };
kernel_metadata.push(ModuleInfoItem { kernel_metadata.push(ModuleInfoItem {
@ -69,11 +82,19 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
value: ModuleInfoValue::Pointer(free_ptr), value: ModuleInfoValue::Pointer(free_ptr),
}); });
let modinfo_base = next.as_ptr();
for item in kernel_metadata.iter() { for item in kernel_metadata.iter() {
next = item.serialize_unaligned(next); 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 // boot_kernel shouldn't return
Status::ABORTED Status::ABORTED

View file

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