Skip to content

Commit

Permalink
boot: new efi module to handle efi interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
sdemos committed Aug 24, 2018
1 parent 3910eaa commit 8f76abc
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 38 deletions.
75 changes: 75 additions & 0 deletions boot/src/efi.rs
@@ -0,0 +1,75 @@
//! the efi module handles all the uefi interactions

use core::{mem, slice};
use uefi::{self, table, table::boot};
use uefi_services;

pub fn alloc_addr<'a>(bytes: usize) -> Result<(&'a mut [u8], usize), uefi::Status> {
let size = bytes / 4096;
trace!("allocating {} pages for {} bytes", size, bytes);
let pages = uefi_services::system_table().boot.allocate_pages(
boot::AllocateType::AnyPages,
boot::MemoryType::LoaderData,
size,
)?;
unsafe {
let ptr = mem::transmute::<_, *mut u8>(pages);
Ok((slice::from_raw_parts_mut(ptr, bytes), pages))
}
}

pub fn alloc<'a>(bytes: usize) -> Result<&'a mut [u8], uefi::Status> {
alloc_addr(bytes).map(|(b, _)| b)
}

pub struct Efi {
handle: uefi::Handle,
st: &'static table::SystemTable,
}

impl Efi {
/// init calls necessary initialization functions to set up our uefi
/// environment.
pub fn init(handle: uefi::Handle, st: &'static table::SystemTable) -> Self {
// initialize uefi_services. this sets up logging and allocation and
// initializes a globally accessible reference to the system table.
uefi_services::init(st);

// get the handle to stdout, and reset it
let stdout = st.stdout();
stdout.reset(false)
.expect("failed to reset stdout");

// Switch to the maximum supported graphics mode. should be safe to
// unwrap here because we are getting the last mode, and there should be
// /any/ modes available.
let best_mode = stdout.modes().last()
.expect("failed to get the best mode");
stdout.set_mode(best_mode)
.expect("failed to set stdout mode the best mode");

Efi {handle, st}
}

pub fn get_memory_map<'a>(&self) -> (boot::MemoryMapKey, boot::MemoryMapIter<'a>) {
trace!("getting the memory map");
let map_size = self.st.boot.memory_map_size();
// just in case allocating these additional pages increases the size of the
// memory map, we allocate more space than we need. importantly, this is
// okay because we aren't using the size of the slice to inform how many
// entries are in it. the underlying uefi function returns this information
// explicitly, and the iterator stores it.
let map_buffer = alloc(map_size + 4096)
.expect("failed to allocate memory for memory map");
self.st.boot.memory_map(map_buffer)
.expect("failed to get memory map")
}

pub fn exit_boot_services(self, key: boot::MemoryMapKey) {
// retrieving an updated memory map again.
unsafe {
self.st.boot.exit_boot_services(self.handle, key)
.expect("failed to exit boot services");
}
}
}
52 changes: 14 additions & 38 deletions boot/src/main.rs
Expand Up @@ -12,10 +12,12 @@ extern crate uefi_services;
extern crate uefi_utils;

use core::{mem, slice};
mod efi;

use goblin::elf;
use uefi::{Handle, Status, table, table::boot, proto::media};
use uefi_utils::proto::find_protocol;
use efi::Efi;

/// uefi_start is the entrypoint called by the uefi firmware. It is defined as
/// the entrypoint as part of the target spec. It is responsible for setting up
Expand All @@ -27,54 +29,28 @@ use uefi_utils::proto::find_protocol;
/// different logger, and we want to write our own implementation of panic, oom,
/// and eh_personality.
#[no_mangle]
pub extern "C" fn uefi_start(handle: Handle, st: &'static table::SystemTable) -> Status {
// initialize uefi_services. this sets up logging and allocation and
// initializes a globally accessible reference to the system table.
uefi_services::init(st);

// get the handle to stdout, and reset it
let stdout = st.stdout();
stdout.reset(false).unwrap();

// Switch to the maximum supported graphics mode.
let best_mode = stdout.modes().last().unwrap();
stdout.set_mode(best_mode).unwrap();

// try printing something!
pub extern "C" fn uefi_start(
handle: Handle,
st: &'static table::SystemTable,
) -> Status {
// initialize our runtime environment
let efi = Efi::init(handle, st);

// welcome! to the bootloader
info!("# DemOS #");
info!("Image handle: {:?}", handle);

let entry = load_kernel();

info!("Grabbing the memory map");
let bt = st.boot;
let map_size = bt.memory_map_size();
// in case allocating our buffer requires an additional page, we allocate
// more space than we need.
let buf_size = (map_size / 4096) + 1;
let pages = bt.allocate_pages(
boot::AllocateType::AnyPages,
boot::MemoryType::LoaderData,
buf_size,
).expect("failed to allocate memory for memory map");
let buffer = unsafe {
let ptr = mem::transmute::<_, *mut u8>(pages);
slice::from_raw_parts_mut(ptr, buf_size * 4096)
};
// grab the memory map from the firmware
let (key, desc) = efi.get_memory_map();

let (key, descs) = bt.memory_map(buffer).expect("failed to get memory map");
// in my experience, using logging functions changes the memory map key,
// once we get the memory map, we don't log anymore. either way, once we
// exit boot services, we can't use the uefi logging functionality anyway.

// exit boot services
// info!("exiting boot services and starting the kernel");
unsafe {
bt.exit_boot_services(handle, key)
.expect("failed to exit boot services");
}
// TODO: check the return and possibly attempt to exit again, perhaps after
// retrieving an updated memory map again.
// exit boot services.
efi.exit_boot_services(key);

remap_kernel();

Expand Down

0 comments on commit 8f76abc

Please sign in to comment.