201 lines
5.7 KiB
Rust
201 lines
5.7 KiB
Rust
#![allow(static_mut_refs)]
|
|
#![no_std]
|
|
#![no_main]
|
|
|
|
pub mod elf;
|
|
pub mod initrd;
|
|
pub mod mem;
|
|
pub mod protocol_ext;
|
|
|
|
use core::{arch::asm, mem::size_of, ops::Deref};
|
|
|
|
use elf::Object;
|
|
use log::{debug, error, info};
|
|
use uefi::{
|
|
prelude::*,
|
|
proto::{
|
|
console::gop::{GraphicsOutput, PixelFormat},
|
|
device_path::DevicePath,
|
|
loaded_image::LoadedImage,
|
|
media::fs::SimpleFileSystem,
|
|
},
|
|
table::{
|
|
boot::{AllocateType, MemoryType, ScopedProtocol},
|
|
cfg,
|
|
},
|
|
Error,
|
|
};
|
|
use yboot_proto::{
|
|
v1::{self, AvailableMemoryRegion, FramebufferOption},
|
|
LoadProtocolV1, LOADER_MAGIC,
|
|
};
|
|
|
|
use crate::{mem::MemoryDescriptorExt, protocol_ext::GraphicsOutputExt};
|
|
|
|
fn setup_framebuffer(bs: &BootServices, fb: &mut FramebufferOption) -> Result<(), Error> {
|
|
let gop_handle = bs.get_handle_for_protocol::<GraphicsOutput>()?;
|
|
let mut gop = bs.open_protocol_exclusive::<GraphicsOutput>(gop_handle)?;
|
|
|
|
// Find the requested mode
|
|
let mode = gop.match_mode(fb.req_width, fb.req_height).ok_or_else(|| {
|
|
error!(
|
|
"Requested mode is not supported: {}x{}",
|
|
fb.req_width, fb.req_height
|
|
);
|
|
Error::new(Status::INVALID_PARAMETER, ())
|
|
})?;
|
|
|
|
gop.set_mode(&mode)?;
|
|
|
|
let mut result = gop.frame_buffer();
|
|
|
|
let format = match mode.info().pixel_format() {
|
|
PixelFormat::Bgr => v1::PIXEL_B8G8R8A8,
|
|
PixelFormat::Rgb => v1::PIXEL_R8G8B8A8,
|
|
_ => 0,
|
|
};
|
|
|
|
fb.res_width = fb.req_width;
|
|
fb.res_height = fb.req_height;
|
|
fb.res_address = result.as_mut_ptr() as _;
|
|
fb.res_stride = mode.info().stride() as u64 * 4;
|
|
fb.res_size = result.size() as _;
|
|
fb.res_format = format;
|
|
|
|
info!(
|
|
"Framebuffer: {}x{} @ {:#x}",
|
|
fb.res_width, fb.res_height, fb.res_address
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn locate_rsdp(st: &SystemTable<Boot>) -> Option<u64> {
|
|
for entry in st.config_table() {
|
|
if entry.guid == cfg::ACPI_GUID {
|
|
return Some(entry.address as u64);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn boot_partition(
|
|
image: Handle,
|
|
bs: &BootServices,
|
|
) -> Result<ScopedProtocol<SimpleFileSystem>, Error> {
|
|
let loaded_image = bs.open_protocol_exclusive::<LoadedImage>(image)?;
|
|
let device_handle = loaded_image.device();
|
|
|
|
let device_path = bs.open_protocol_exclusive::<DevicePath>(device_handle)?;
|
|
let mut device_path = device_path.deref();
|
|
|
|
let fs_handle = bs.locate_device_path::<SimpleFileSystem>(&mut device_path)?;
|
|
bs.open_protocol_exclusive::<SimpleFileSystem>(fs_handle)
|
|
}
|
|
|
|
fn load_kernel<'a>(
|
|
ih: Handle,
|
|
st: &SystemTable<Boot>,
|
|
) -> Result<(u64, u64, &'a mut LoadProtocolV1), Error> {
|
|
let bs = st.boot_services();
|
|
|
|
let mut fs = boot_partition(ih, bs)?;
|
|
let mut root = fs.open_volume()?;
|
|
|
|
let mut kernel_obj = Object::open(&mut root, cstr16!("kernel.elf"))?;
|
|
let loaded_obj = kernel_obj.load(bs)?;
|
|
|
|
debug!("Loaded object: {:#x?}", loaded_obj);
|
|
|
|
// Load initrd
|
|
let (initrd_start, initrd_size) = initrd::load_somewhere(bs, &mut root, cstr16!("initrd.img"))?;
|
|
debug!(
|
|
"Loaded initrd: {:#x?}",
|
|
initrd_start..initrd_start + initrd_size
|
|
);
|
|
|
|
// Other versions are not existent yet
|
|
assert_eq!(loaded_obj.protocol_version, 1);
|
|
let proto_data = unsafe { &mut *(loaded_obj.protocol_struct_paddr as *mut LoadProtocolV1) };
|
|
|
|
let rsdp = locate_rsdp(st).ok_or_else(|| {
|
|
error!("Failed to find a RSDP config entry");
|
|
Error::new(Status::NOT_FOUND, ())
|
|
})?;
|
|
info!("RSDP at {:#x}", rsdp);
|
|
|
|
proto_data.rsdp_address = rsdp;
|
|
proto_data.initrd_address = initrd_start;
|
|
proto_data.initrd_size = initrd_size;
|
|
|
|
// 32768 bytes should be enough
|
|
let mmap_memory = bs.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, 8)?;
|
|
info!("Memory map is placed at {:#x}", mmap_memory);
|
|
|
|
// Setup framebuffer if needed
|
|
if proto_data.opt_framebuffer.req_width != 0 && proto_data.opt_framebuffer.req_height != 0 {
|
|
setup_framebuffer(bs, &mut proto_data.opt_framebuffer)?;
|
|
}
|
|
|
|
// Not yet supported
|
|
if proto_data.kernel_virt_offset == 0 {
|
|
todo!();
|
|
}
|
|
|
|
// TODO handle other offsets
|
|
assert_eq!(proto_data.kernel_virt_offset, 0xFFFFFF8000000000);
|
|
info!(
|
|
"Kernel entry will be at {:#x}",
|
|
loaded_obj.entry + proto_data.kernel_virt_offset
|
|
);
|
|
|
|
let entry = loaded_obj.entry + proto_data.kernel_virt_offset;
|
|
|
|
Ok((entry, mmap_memory, proto_data))
|
|
}
|
|
|
|
unsafe fn map_and_enter_kernel(
|
|
st: SystemTable<Boot>,
|
|
proto_data: &mut LoadProtocolV1,
|
|
mmap_memory: u64,
|
|
entry: u64,
|
|
) -> ! {
|
|
let (_, mmap) = st.exit_boot_services();
|
|
let mut index = 0;
|
|
|
|
let mmap_data = core::slice::from_raw_parts_mut(
|
|
mmap_memory as *mut AvailableMemoryRegion,
|
|
8 * 0x1000 / size_of::<AvailableMemoryRegion>(),
|
|
);
|
|
|
|
for entry in mmap.entries() {
|
|
if entry.is_runtime_usable() {
|
|
mmap_data[index] = AvailableMemoryRegion {
|
|
start_address: entry.phys_start,
|
|
page_count: entry.page_count,
|
|
};
|
|
index += 1;
|
|
}
|
|
}
|
|
|
|
proto_data.memory_map.address = mmap_memory;
|
|
proto_data.memory_map.len = index as _;
|
|
|
|
let cr3 = mem::map_image();
|
|
asm!("cli; wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax));
|
|
|
|
asm!("jmp *{0}", in(reg) entry, in("eax") LOADER_MAGIC, options(noreturn, att_syntax));
|
|
}
|
|
|
|
#[entry]
|
|
fn efi_main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
|
uefi_services::init(&mut system_table).unwrap();
|
|
|
|
let (entry, mmap_memory, proto_data) = load_kernel(image_handle, &system_table).unwrap();
|
|
|
|
unsafe {
|
|
map_and_enter_kernel(system_table, proto_data, mmap_memory, entry);
|
|
}
|
|
}
|