Got a kinda crappy post-boot services allocator working, hopefully.
parent
1d536edcf6
commit
f2db3d581d
|
@ -3,7 +3,9 @@ name = "bootproof"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["James Martin <james@jtmar.me>"]
|
authors = ["James Martin <james@jtmar.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
repository = "https://github.com/jamestmartin/bootproof"
|
||||||
license = "GPL-3.0+"
|
license = "GPL-3.0+"
|
||||||
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
compiler_builtins = { git = "https://github.com/rust-lang/compiler-builtins" }
|
compiler_builtins = { git = "https://github.com/rust-lang/compiler-builtins" }
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
|
pub mod standard;
|
||||||
|
pub mod uefi;
|
||||||
|
|
||||||
use alloc::alloc::GlobalAlloc;
|
use alloc::alloc::GlobalAlloc;
|
||||||
use core::alloc::Layout;
|
use core::alloc::Layout;
|
||||||
|
|
||||||
pub enum Allocator {
|
pub enum GlobalAllocator {
|
||||||
None,
|
None,
|
||||||
|
Standard(standard::StandardAllocator),
|
||||||
|
Uefi(uefi::UefiAllocator),
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for Allocator {
|
unsafe impl GlobalAlloc for GlobalAllocator {
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
match self {
|
match self {
|
||||||
Allocator::None => panic!("No allocator available!")
|
GlobalAllocator::None => panic!("No allocator available!"),
|
||||||
|
GlobalAllocator::Uefi(alloc) => alloc.alloc(layout),
|
||||||
|
GlobalAllocator::Standard(alloc) => alloc.alloc(layout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||||
match self {
|
match self {
|
||||||
Allocator::None => {
|
GlobalAllocator::None => panic!("No allocator available!"),
|
||||||
panic!("No allocator available!");
|
GlobalAllocator::Uefi(alloc) => alloc.dealloc(ptr, layout),
|
||||||
}
|
GlobalAllocator::Standard(alloc) => alloc.dealloc(ptr, layout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
pub static mut ALLOCATOR: Allocator = Allocator::None;
|
pub static mut ALLOCATOR: GlobalAllocator = GlobalAllocator::None;
|
||||||
|
|
||||||
#[alloc_error_handler]
|
#[alloc_error_handler]
|
||||||
fn handle_error(layout: Layout) -> ! {
|
fn handle_error(layout: Layout) -> ! {
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
use alloc::alloc::GlobalAlloc;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::alloc::Layout;
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use uefi::table::boot::MemoryMapIter;
|
||||||
|
|
||||||
|
// TODO: Support granularity better than pages.
|
||||||
|
// TODO: Use an allocation algorithm that isn't absolute garbage!!
|
||||||
|
|
||||||
|
/// **This allocator only supports page-level granularity.**
|
||||||
|
/// Be careful not to use it for small allocations.
|
||||||
|
pub struct StandardAllocator {
|
||||||
|
pages: UnsafeCell<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
pub fn get_bit(bytes: &[u8], index: usize) -> bool {
|
||||||
|
let (byte, bit) = num_integer::div_rem(index, 8);
|
||||||
|
let mask = 0b10000000u8 >> bit;
|
||||||
|
bytes[byte] & mask > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_bit(bytes: &mut [u8], index: usize, value: bool) {
|
||||||
|
let (byte, bit) = num_integer::div_rem(index, 8);
|
||||||
|
let mask = 0b10000000u8 >> bit;
|
||||||
|
if value {
|
||||||
|
bytes[byte] |= mask;
|
||||||
|
} else {
|
||||||
|
bytes[byte] &= !mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StandardAllocator {
|
||||||
|
/// Allocates a new allocator data structure sufficient
|
||||||
|
/// to use the physical memory provided in the memory map.
|
||||||
|
/// With what allocator does the allocator get allocated? The UEFI allocator.
|
||||||
|
/// This does *not* pre-populate the allocator with usage data;
|
||||||
|
/// by default, it will behave as though every page were allocated.
|
||||||
|
/// Use `populate` to fill the allocator with actual data
|
||||||
|
/// using the map that UEFI provides when you exit boot services.
|
||||||
|
pub fn new(mmap: &mut MemoryMapIter) -> StandardAllocator {
|
||||||
|
// Try to find the largest physical address
|
||||||
|
// and create a bitmap allowing the allocation of that much memory.
|
||||||
|
let greatest_physical_page =
|
||||||
|
mmap.map(|d| num_integer::div_ceil(d.phys_start as usize, PAGE_SIZE) + d.page_count as usize).max()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut pages = Vec::with_capacity(num_integer::div_ceil(greatest_physical_page, 8));
|
||||||
|
// I can fit up to 8 pages in a byte in my bitmap.
|
||||||
|
pages.resize(num_integer::div_ceil(greatest_physical_page, 8), 0xFF);
|
||||||
|
|
||||||
|
StandardAllocator {
|
||||||
|
pages: UnsafeCell::new(pages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn populate(&mut self, mmap: &mut MemoryMapIter) {
|
||||||
|
let self_pages = unsafe { &mut *self.pages.get() };
|
||||||
|
// Mark all unsable memory as free for allocations.
|
||||||
|
for entry in mmap {
|
||||||
|
use uefi::table::boot::MemoryType;
|
||||||
|
if entry.ty == MemoryType::BOOT_SERVICES_CODE
|
||||||
|
|| entry.ty == MemoryType::BOOT_SERVICES_DATA
|
||||||
|
|| entry.ty == MemoryType::CONVENTIONAL {
|
||||||
|
let base = entry.phys_start as usize / PAGE_SIZE;
|
||||||
|
for offset in 0..entry.page_count as usize {
|
||||||
|
set_bit(self_pages.as_mut_slice(), base + offset, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if the zero address is valid memory, we *definitely* don't want to allocate it.
|
||||||
|
set_bit(self_pages.as_mut_slice(), 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(&self) -> usize {
|
||||||
|
let self_pages = unsafe { &mut *self.pages.get() };
|
||||||
|
let mut free = 0;
|
||||||
|
for page in 0..self_pages.len() * 8 {
|
||||||
|
if !get_bit(self_pages.as_slice(), page) {
|
||||||
|
free += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not very accurate because this includes a lot of reserved/unusable memory.
|
||||||
|
pub fn used(&self) -> usize {
|
||||||
|
let free = self.free();
|
||||||
|
// This line of code crashes QEMU for inexplicable reasons.
|
||||||
|
// I tried to figure out why and failed.
|
||||||
|
let self_pages = unsafe { &*self.pages.get() };
|
||||||
|
free - self_pages.as_slice().len() * 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl GlobalAlloc for StandardAllocator {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
let min_pages = num_integer::div_ceil(layout.size(), PAGE_SIZE);
|
||||||
|
let mut pages = None;
|
||||||
|
let self_pages = &mut *self.pages.get();
|
||||||
|
for i in 0..(self_pages.len() * 8) {
|
||||||
|
if !get_bit(&self_pages.as_slice(), i) {
|
||||||
|
pages = match pages {
|
||||||
|
Some((begin, size)) => Some((begin, size + 1)),
|
||||||
|
None => Some((i, 1)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if pages.unwrap().1 >= min_pages {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pages = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pages.is_none() || pages.unwrap().1 < min_pages {
|
||||||
|
panic!("Not enough contiguous memory!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let (begin, size) = pages.unwrap();
|
||||||
|
|
||||||
|
let address = begin * PAGE_SIZE;
|
||||||
|
|
||||||
|
for offset in 0..size {
|
||||||
|
set_bit(self_pages.as_mut_slice(), begin + offset, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
address as *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||||
|
let begin = ptr as usize / PAGE_SIZE;
|
||||||
|
let size = num_integer::div_ceil(layout.size(), PAGE_SIZE);
|
||||||
|
let self_pages = &mut *self.pages.get();
|
||||||
|
|
||||||
|
for offset in 0..size {
|
||||||
|
set_bit(self_pages.as_mut_slice(), begin + offset, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
use alloc::alloc::GlobalAlloc;
|
||||||
|
use core::alloc::Layout;
|
||||||
|
use uefi::prelude::{Boot, SystemTable};
|
||||||
|
use uefi::table::boot::{AllocateType, MemoryType};
|
||||||
|
|
||||||
|
/// **This allocator only supports page-level granularity.**
|
||||||
|
/// Be careful not to use it for small allocations.
|
||||||
|
/// In particular, it should only be used to allocate data structures
|
||||||
|
/// for the purpose of setting up another, better allocator.
|
||||||
|
pub struct UefiAllocator {
|
||||||
|
// We must directly store an owned system table because:
|
||||||
|
// 1. It is impossible to take ownership of ST boot services, and
|
||||||
|
// 2. we cannot store a reference to *anything* here
|
||||||
|
// because the global allocator has to be static even if we know it's really not.
|
||||||
|
st: SystemTable<Boot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UefiAllocator {
|
||||||
|
pub fn new(st: SystemTable<Boot>) -> UefiAllocator {
|
||||||
|
UefiAllocator { st: st }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
unsafe impl GlobalAlloc for UefiAllocator {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
self.st.boot_services()
|
||||||
|
.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA,
|
||||||
|
num_integer::div_ceil(layout.size(), PAGE_SIZE))
|
||||||
|
.expect("Failed to allocate memory!").unwrap()
|
||||||
|
as *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||||
|
self.st.boot_services().free_pages(ptr as u64, num_integer::div_ceil(layout.size(), PAGE_SIZE))
|
||||||
|
.expect("Failed to free memory!").unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,27 +19,27 @@ pub static mut STDERR: Option<SerialTty> = None;
|
||||||
macro_rules! print {
|
macro_rules! print {
|
||||||
// These additional single-argument cases are necessary because `format!` requires allocation,
|
// These additional single-argument cases are necessary because `format!` requires allocation,
|
||||||
// which I don't necessarily *have* (not to mention it's inefficient).
|
// which I don't necessarily *have* (not to mention it's inefficient).
|
||||||
($s:expr) => {
|
($s:expr) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
||||||
}
|
}
|
||||||
tty.puts($s);
|
tty.puts($s);
|
||||||
tty.flush();
|
tty.flush();
|
||||||
};
|
}};
|
||||||
($($arg:expr),*) => {
|
($($arg:expr),*) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
||||||
}
|
}
|
||||||
tty.puts(&alloc::format!($($arg),*));
|
tty.puts(&alloc::format!($($arg),*));
|
||||||
tty.flush();
|
tty.flush();
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! println {
|
macro_rules! println {
|
||||||
($s:expr) => {
|
($s:expr) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
||||||
|
@ -47,8 +47,8 @@ macro_rules! println {
|
||||||
tty.puts($s);
|
tty.puts($s);
|
||||||
tty.putc('\n');
|
tty.putc('\n');
|
||||||
tty.flush();
|
tty.flush();
|
||||||
};
|
}};
|
||||||
($($arg:expr),*) => {
|
($($arg:expr),*) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
tty = crate::graphics::tty::STDOUT.clone().unwrap();
|
||||||
|
@ -56,32 +56,32 @@ macro_rules! println {
|
||||||
tty.puts(&alloc::format!($($arg),*));
|
tty.puts(&alloc::format!($($arg),*));
|
||||||
tty.putc('\n');
|
tty.putc('\n');
|
||||||
tty.flush();
|
tty.flush();
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! eprint {
|
macro_rules! eprint {
|
||||||
($s:expr) => {
|
($s:expr) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
||||||
}
|
}
|
||||||
tty.puts($s);
|
tty.puts($s);
|
||||||
tty.flush();
|
tty.flush();
|
||||||
};
|
}};
|
||||||
($($arg:expr),*) => {
|
($($arg:expr),*) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
||||||
}
|
}
|
||||||
tty.puts(&alloc::format!($($arg),*));
|
tty.puts(&alloc::format!($($arg),*));
|
||||||
tty.flush();
|
tty.flush();
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! eprintln {
|
macro_rules! eprintln {
|
||||||
($s:expr) => {
|
($s:expr) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
||||||
|
@ -89,8 +89,8 @@ macro_rules! eprintln {
|
||||||
tty.puts($s);
|
tty.puts($s);
|
||||||
tty.putc('\n');
|
tty.putc('\n');
|
||||||
tty.flush();
|
tty.flush();
|
||||||
};
|
}};
|
||||||
($($arg:expr),*) => {
|
($($arg:expr),*) => {{
|
||||||
let mut tty;
|
let mut tty;
|
||||||
unsafe {
|
unsafe {
|
||||||
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
tty = crate::graphics::tty::STDERR.clone().unwrap();
|
||||||
|
@ -98,15 +98,15 @@ macro_rules! eprintln {
|
||||||
tty.puts(&alloc::format!($($arg),*));
|
tty.puts(&alloc::format!($($arg),*));
|
||||||
tty.putc('\n');
|
tty.putc('\n');
|
||||||
tty.flush();
|
tty.flush();
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! panic {
|
macro_rules! panic {
|
||||||
($($arg:expr),*) => {
|
($($arg:expr),*) => {{
|
||||||
crate::eprintln!($($arg),*);
|
crate::eprintln!($($arg),*);
|
||||||
crate::arch::x86_64::halt();
|
crate::arch::x86_64::halt()
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
|
|
85
src/main.rs
85
src/main.rs
|
@ -8,27 +8,48 @@
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod allocator;
|
mod allocator;
|
||||||
|
mod arch;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod graphics;
|
mod graphics;
|
||||||
mod arch;
|
|
||||||
|
|
||||||
use core::mem;
|
use alloc::vec::Vec;
|
||||||
use core::slice;
|
|
||||||
use crate::allocator::{Allocator, ALLOCATOR};
|
|
||||||
use crate::graphics::tty::{Tty, STDOUT, STDERR};
|
use crate::graphics::tty::{Tty, STDOUT, STDERR};
|
||||||
use crate::graphics::tty::serial::SerialTty;
|
use crate::graphics::tty::serial::SerialTty;
|
||||||
use uefi::prelude::*;
|
use uefi::prelude::*;
|
||||||
use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType};
|
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
||||||
// Although serial ports don't physically exist on modern devices,
|
use crate::allocator::{ALLOCATOR, GlobalAllocator};
|
||||||
// they're still supposed by emulators (for QEMU you can set `-serial stdio`),
|
|
||||||
// and they're extremely useful for debugging
|
|
||||||
// because they don't require any setup and are trivial to use.
|
|
||||||
unsafe {
|
unsafe {
|
||||||
STDOUT = Some(SerialTty::new(0x3F8));
|
// Generally speaking, we want to depend on UEFI as little as possible,
|
||||||
STDERR = Some(SerialTty::new(0x3F8));
|
// so the need for a UEFI allocator may seem a bit strange.
|
||||||
|
// However, there's this awkward time during booting when we need
|
||||||
|
// to allocate space for our "real" allocator's data structures and the UEFI memory maps,
|
||||||
|
// the result being that we need an allocator for our allocator.
|
||||||
|
// In theory there are probably ways to get around it, but why bother?
|
||||||
|
// Just taking advantage of the UEFI allocator briefly is a lot easier.
|
||||||
|
// (This also lets us use `println!` prior to our main allocator being set up.)
|
||||||
|
use crate::allocator::uefi::UefiAllocator;
|
||||||
|
// ABSOLUTELY DO NOT FORGET TO DISABLE THIS AFTER LEAVING UEFI BOOT SERVICES.
|
||||||
|
// ALL ALLOCATIONS MUST BE STATIC OR BE FREED BEFORE BOOT SERVICES EXITS.
|
||||||
|
// If the're not, Rust still try to free UEFI-allocated data using the new allocator,
|
||||||
|
// which is undefined behavior.
|
||||||
|
ALLOCATOR = GlobalAllocator::Uefi(UefiAllocator::new(st_boot.unsafe_clone()));
|
||||||
|
|
||||||
|
// Although serial ports don't physically exist on modern devices,
|
||||||
|
// they're still supposed by emulators (for QEMU you can set `-serial stdio`),
|
||||||
|
// and they're extremely useful for debugging
|
||||||
|
// because they don't require any setup and are trivial to use.
|
||||||
|
STDOUT = Some({
|
||||||
|
let mut stdout = SerialTty::new(0x3F8);
|
||||||
|
stdout.clear();
|
||||||
|
stdout
|
||||||
|
});
|
||||||
|
STDERR = Some({
|
||||||
|
let mut stderr = SerialTty::new(0x3F8);
|
||||||
|
stderr.clear();
|
||||||
|
stderr
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our first task is to exit the UEFI boot services.
|
// Our first task is to exit the UEFI boot services.
|
||||||
|
@ -40,25 +61,35 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
||||||
// which describes where a bunch of important stuff lies in memory
|
// which describes where a bunch of important stuff lies in memory
|
||||||
// (e.g. memory-mapped devices and the ACPI tables),
|
// (e.g. memory-mapped devices and the ACPI tables),
|
||||||
// and what memory is available for us to use safely.
|
// and what memory is available for us to use safely.
|
||||||
let (st, mmap) = {
|
|
||||||
// We must provide a buffer (mmap_buf) for UEFI to write the memory map to.
|
// We can't let the memory map be de-allocated because it is allocated using the UEFI allocator,
|
||||||
|
// but would end up being freed using the standard allocator, which is undefined behavior.
|
||||||
|
// We must provide a buffer (mmap_buf) for UEFI to write the memory map to.
|
||||||
|
let mut mmap_buf = Vec::new();
|
||||||
|
let (_mmap, st) = {
|
||||||
let bs = st_boot.boot_services();
|
let bs = st_boot.boot_services();
|
||||||
|
|
||||||
// More allocations can happen between the allocation of the buffer and the buffer being filled,
|
// More allocations can happen between the allocation of the buffer and the buffer being filled,
|
||||||
// so add space for 8 more memory descriptors (an arbitrary number) just to make sure there's enough.
|
// so add space for 32 more memory descriptors (an arbitrary number) just to make sure there's enough.
|
||||||
let mmap_buf_size = bs.memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>();
|
mmap_buf.resize(bs.memory_map_size() + 1024, 0);
|
||||||
|
|
||||||
// In the memory map, the OS's own code and data is included as LOADER_CODE and LOADER_DATA.
|
// HACK: I hate having to use the UEFI allocator just to set up another allocator!
|
||||||
// This is good to know so we don't accidentally write over it!
|
// There's got to be a better way.
|
||||||
// We allocate the mmap_buf as LOADER_DATA for this purpose.
|
use crate::allocator::standard::StandardAllocator;
|
||||||
let mmap_buf = bs.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, mmap_buf_size)
|
let mut allocator;
|
||||||
.expect_success("Failed to allocate memory for UEFI memory map buffer.");
|
{
|
||||||
|
let mut mmap = bs.memory_map(mmap_buf.as_mut_slice())
|
||||||
// allocate_pages returns a u64 address, but we need a `&mut [u8]`.
|
.expect_success("Failed to exit the UEFI boot services.").1;
|
||||||
let mmap_buf_slice = unsafe { slice::from_raw_parts_mut(mmap_buf as *mut u8, mmap_buf_size) };
|
allocator = StandardAllocator::new(&mut mmap);
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, we actually exit the UEFI boot services.
|
// Finally, we actually exit the UEFI boot services.
|
||||||
st_boot.exit_boot_services(handle, mmap_buf_slice).expect_success("Failed to exit the UEFI boot services.")
|
let (st, mut mmap) = st_boot.exit_boot_services(handle, mmap_buf.as_mut_slice())
|
||||||
|
.expect_success("Failed to exit the UEFI boot services.");
|
||||||
|
|
||||||
|
allocator.populate(&mut mmap);
|
||||||
|
unsafe { ALLOCATOR = GlobalAllocator::Standard(allocator); }
|
||||||
|
|
||||||
|
(mmap, st)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the stuff I need to handle interrupts, which is necessary to write drivers for most devices.
|
// Set up the stuff I need to handle interrupts, which is necessary to write drivers for most devices.
|
||||||
|
@ -76,10 +107,10 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
||||||
// Everything up to this point has been setting up the CPU state, drivers, etc.
|
// Everything up to this point has been setting up the CPU state, drivers, etc.
|
||||||
// Now we begin running actual programs
|
// Now we begin running actual programs
|
||||||
// (or in this case, since we don't support actual programs yet, whatever debug stuff I want to run).
|
// (or in this case, since we don't support actual programs yet, whatever debug stuff I want to run).
|
||||||
main(st, mmap)
|
main(st)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(_st: SystemTable<uefi::table::Runtime>, _mmap: uefi::table::boot::MemoryMapIter) -> ! {
|
fn main(_st: SystemTable<uefi::table::Runtime>) -> ! {
|
||||||
// Put whatever code you want for debugging/testing purposes here...
|
// Put whatever code you want for debugging/testing purposes here...
|
||||||
arch::x86_64::breakpoint();
|
arch::x86_64::breakpoint();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue