#![no_std]
#![no_main]
#![feature(abi_efiapi)]
#![feature(asm)]
#![feature(maybe_uninit_extra)]
#![deny(unsafe_op_in_unsafe_fn)]
include!(concat!(env!("OUT_DIR"), "/kernel_info.rs"));
static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES);
#[repr(align(4096))]
struct PageAligned<T>(T);
use bootloader::{
binary::{legacy_memory_region::LegacyFrameAllocator, SystemInfo},
boot_info::FrameBufferInfo,
};
use core::{mem, panic::PanicInfo, slice};
use uefi::{
prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable},
proto::console::gop::{GraphicsOutput, PixelFormat},
table::boot::{MemoryDescriptor, MemoryType},
};
use x86_64::{
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
PhysAddr, VirtAddr,
};
#[entry]
fn efi_main(image: Handle, st: SystemTable<Boot>) -> Status {
let (framebuffer_addr, framebuffer_info) = init_logger(&st);
log::info!("Hello World from UEFI bootloader!");
log::info!("Using framebuffer at {:#x}", framebuffer_addr);
let mmap_storage = {
let max_mmap_size =
st.boot_services().memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>();
let ptr = st
.boot_services()
.allocate_pool(MemoryType::LOADER_DATA, max_mmap_size)?
.log();
unsafe { slice::from_raw_parts_mut(ptr, max_mmap_size) }
};
log::trace!("exiting boot services");
let (system_table, memory_map) = st
.exit_boot_services(image, mmap_storage)
.expect_success("Failed to exit boot services");
let mut frame_allocator = LegacyFrameAllocator::new(memory_map.copied());
let page_tables = create_page_tables(&mut frame_allocator);
let system_info = SystemInfo {
framebuffer_addr,
framebuffer_info,
rsdp_addr: {
use uefi::table::cfg;
let mut config_entries = system_table.config_table().iter();
config_entries
.find(|entry| matches!(entry.guid, cfg::ACPI_GUID | cfg::ACPI2_GUID))
.map(|entry| PhysAddr::new(entry.address as u64))
},
};
bootloader::binary::load_and_switch_to_kernel(
&KERNEL.0,
frame_allocator,
page_tables,
system_info,
);
}
fn create_page_tables(
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> bootloader::binary::PageTables {
let phys_offset = VirtAddr::new(0);
log::trace!("switching to new level 4 table");
let bootloader_page_table = {
let old_table = {
let frame = x86_64::registers::control::Cr3::read().0;
let ptr: *const PageTable = (phys_offset + frame.start_address().as_u64()).as_ptr();
unsafe { &*ptr }
};
let new_frame = frame_allocator
.allocate_frame()
.expect("Failed to allocate frame for new level 4 table");
let new_table: &mut PageTable = {
let ptr: *mut PageTable =
(phys_offset + new_frame.start_address().as_u64()).as_mut_ptr();
unsafe {
ptr.write(PageTable::new());
&mut *ptr
}
};
new_table[0] = old_table[0].clone();
unsafe {
x86_64::registers::control::Cr3::write(
new_frame,
x86_64::registers::control::Cr3Flags::empty(),
);
OffsetPageTable::new(&mut *new_table, phys_offset)
}
};
let (kernel_page_table, kernel_level_4_frame) = {
let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames");
log::info!("New page table at: {:#?}", &frame);
let addr = phys_offset + frame.start_address().as_u64();
let ptr = addr.as_mut_ptr();
unsafe { *ptr = PageTable::new() };
let level_4_table = unsafe { &mut *ptr };
(
unsafe { OffsetPageTable::new(level_4_table, phys_offset) },
frame,
)
};
bootloader::binary::PageTables {
bootloader: bootloader_page_table,
kernel: kernel_page_table,
kernel_level_4_frame,
}
}
fn init_logger(st: &SystemTable<Boot>) -> (PhysAddr, FrameBufferInfo) {
let gop = st
.boot_services()
.locate_protocol::<GraphicsOutput>()
.expect_success("failed to locate gop");
let gop = unsafe { &mut *gop.get() };
let mode_info = gop.current_mode_info();
let mut framebuffer = gop.frame_buffer();
let slice = unsafe { slice::from_raw_parts_mut(framebuffer.as_mut_ptr(), framebuffer.size()) };
let info = FrameBufferInfo {
byte_len: framebuffer.size(),
horizontal_resolution: mode_info.resolution().0,
vertical_resolution: mode_info.resolution().1,
pixel_format: match mode_info.pixel_format() {
PixelFormat::RGB => bootloader::boot_info::PixelFormat::BGR,
PixelFormat::BGR => bootloader::boot_info::PixelFormat::BGR,
PixelFormat::Bitmask | PixelFormat::BltOnly => {
panic!("Bitmask and BltOnly framebuffers are not supported")
}
},
bytes_per_pixel: 4,
stride: mode_info.stride(),
};
log::info!("UEFI boot");
bootloader::binary::init_logger(slice, info);
(PhysAddr::new(framebuffer.as_mut_ptr() as u64), info)
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
unsafe {
bootloader::binary::logger::LOGGER
.get()
.map(|l| l.force_unlock())
};
log::error!("{}", info);
loop {
unsafe { asm!("cli; hlt") };
}
}