Initial commit

This commit is contained in:
Mark Poliakov 2023-08-22 09:47:05 +03:00
commit 7e1770e591
10 changed files with 959 additions and 0 deletions

8
.cargo/config.toml Normal file
View File

@ -0,0 +1,8 @@
[build]
target = "x86_64-unknown-uefi"
[unstable]
build-std = ["core", "compiler_builtins"]
[target.x86_64-unknown-uefi]
rustflags = ["-Ccode-model=small", "-Clink-arg=/debug:none"]

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

194
Cargo.lock generated Normal file
View File

@ -0,0 +1,194 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bytemuck"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.27",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "quote"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "ucs2"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad643914094137d475641b6bab89462505316ec2ce70907ad20102d28a79ab8"
dependencies = [
"bit_field",
]
[[package]]
name = "uefi"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b63e82686b4bdb0db74f18b2abbd60a0470354fb640aa69e115598d714d0a10"
dependencies = [
"bitflags",
"log",
"ptr_meta",
"ucs2",
"uefi-macros",
"uefi-raw",
"uguid",
]
[[package]]
name = "uefi-macros"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "023d94ef8e135d068b9a3bd94614ef2610b2b0419ade0a9d8f3501fa9cd08e95"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.27",
]
[[package]]
name = "uefi-raw"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62642516099c6441a5f41b0da8486d5fc3515a0603b0fdaea67b31600e22082e"
dependencies = [
"bitflags",
"ptr_meta",
"uguid",
]
[[package]]
name = "uefi-services"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44b32954ebbb4be5ebfde0df6699c2091f04e9f9c3762c65f3435dfb1a90a668"
dependencies = [
"cfg-if",
"log",
"uefi",
]
[[package]]
name = "uguid"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16dfbd255defbd727b3a30e8950695d2e6d045841ee250ff0f1f7ced17917f8d"
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "yboot"
version = "0.1.0"
dependencies = [
"bytemuck",
"log",
"uefi",
"uefi-services",
"yboot-proto",
]
[[package]]
name = "yboot-proto"
version = "0.1.0"
dependencies = [
"bytemuck",
]

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "yboot"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bytemuck = { version = "1.13.1", features = ["derive"] }
log = "0.4.19"
uefi = "0.24.0"
uefi-services = "0.21.0"
yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" }
[patch.'https://git.alnyan.me/yggdrasil/yboot-proto.git']
yboot-proto = { path = "../yboot-proto" }

33
qemu.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/sh
ARCH=x86_64-unknown-uefi
PROFILE=debug
O=target/${ARCH}/${PROFILE}
BIOS=/usr/share/edk2-ovmf/x64/OVMF_CODE.fd
IMAGE=${O}/image.fat32
set -e
mkdir -p ${O}/image
cargo build
dd if=/dev/zero of=${IMAGE} bs=1M count=64
mkfs.vfat -F32 ${IMAGE}
mcopy -i ${IMAGE} ${O}/yboot.efi ::yboot.efi
if [ "${KERNEL_FILE}" != "" ]; then
mcopy -i ${IMAGE} ${KERNEL_FILE} ::kernel.elf
fi
qemu-system-x86_64 \
-s \
-serial mon:stdio \
-m 256 \
-drive format=raw,file=${BIOS},readonly=on,if=pflash \
-drive format=raw,file=${IMAGE} \
-net none \
-enable-kvm \
-M q35 \
-cpu host

329
src/elf.rs Normal file
View File

@ -0,0 +1,329 @@
use core::mem::size_of;
use bytemuck::Zeroable;
use log::{debug, error, info};
// TODO use 'elf' crate
use uefi::{
proto::media::file::{File, FileAttribute, FileMode, RegularFile},
table::boot::MemoryMap,
CStr16, Error, Status,
};
use yboot_proto::LoadProtocolV1;
use crate::{
elf::types::{PT_LOAD, SHF_ALLOC, SHF_WRITE, SHT_PROGBITS},
mem::MemoryMapExt,
};
use self::types::{Ehdr, Phdr, Shdr};
mod types {
use bytemuck::{Pod, Zeroable};
pub type Off = u64;
pub type Addr = u64;
pub type Half = u16;
pub type Word = u32;
pub type XWord = u64;
pub const PT_LOAD: Word = 1;
pub const SHT_PROGBITS: Word = 1;
pub const SHF_WRITE: XWord = 1 << 0;
pub const SHF_ALLOC: XWord = 1 << 1;
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Ehdr {
pub ident: [u8; 16],
pub type_: Half,
pub machine: Half,
pub version: Word,
pub entry: Addr,
pub phoff: Off,
pub shoff: Off,
pub flags: Word,
pub ehsize: Half,
pub phentsize: Half,
pub phnum: Half,
pub shentsize: Half,
pub shnum: Half,
pub shstrndx: Half,
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Shdr {
pub name: Word,
pub type_: Word,
pub flags: XWord,
pub addr: Addr,
pub offset: Off,
pub size: XWord,
pub link: Word,
pub info: Word,
pub addralign: XWord,
pub entsize: XWord,
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Phdr {
pub type_: Word,
pub flags: Word,
pub offset: Off,
pub vaddr: Addr,
pub paddr: Addr,
pub filesz: XWord,
pub memsz: XWord,
pub align: XWord,
}
}
// Maximum address this loader can map in the target kernel
pub const MAX_KERNEL_PHYS: u64 = 0x100000000;
pub struct Object {
file: RegularFile,
ehdr: Ehdr,
}
#[derive(Debug)]
pub struct LoadedObject {
pub image_start: u64,
pub image_end: u64,
pub entry: u64,
pub protocol_struct_paddr: u64,
pub protocol_version: u32,
}
#[derive(Debug)]
struct LocatedProtocol {
address: usize,
offset: u64,
size: usize,
}
trait ReadExact {
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>;
}
impl ReadExact for RegularFile {
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> {
if self.read(buf)? != buf.len() {
todo!();
} else {
Ok(())
}
}
}
impl Object {
pub fn open<D: File>(root: &mut D, path: &CStr16) -> Result<Self, Error> {
let file = root.open(path, FileMode::Read, FileAttribute::empty())?;
let mut file = file.into_regular_file().unwrap();
let mut ehdr = Ehdr::zeroed();
file.set_position(0)?;
file.read_exact(bytemuck::bytes_of_mut(&mut ehdr))?;
// Validate the the image is indeed ELF
if &ehdr.ident[..4] != b"\x7FELF" {
error!("Image has invalid ELF magic");
return Err(Error::new(Status::LOAD_ERROR, ()));
}
// Validate that we're loading a x86_64 image
if ehdr.ident[4] != 2 {
error!("Image is not 64-bit");
return Err(Error::new(Status::LOAD_ERROR, ()));
}
// Check that the entry point is set
if ehdr.entry == 0 {
error!("Image does not have a valid entry point");
return Err(Error::new(Status::LOAD_ERROR, ()));
}
Ok(Self { file, ehdr })
}
// Returns the image entry point
pub fn load(&mut self, mmap: &MemoryMap) -> Result<LoadedObject, Error> {
// 1. Locate the protocol data structure
let Some(loc_proto) = self.locate_protocol_data()? else {
error!("The image does not have a valid protocol structure");
todo!();
};
info!("Protocol structure @ {:#x?}", loc_proto);
// No other protocol versions currently implemented, so just try to interpret this as V1
if loc_proto.size < size_of::<LoadProtocolV1>() {
error!("The image's protocol structure has invalid size");
todo!();
}
// TODO avoid reading the whole protocol struct just to obtain the virtual offset
let mut proto_data = LoadProtocolV1::zeroed();
self.file.set_position(loc_proto.offset)?;
self.file
.read_exact(bytemuck::bytes_of_mut(&mut proto_data))?;
info!(
"Kernel is virtually mapped at {:#x}",
proto_data.kernel_virt_offset
);
// 2. Find the kernel's range and check that the loaded physical addresses are actually
// usable from UEFI
let mut image_start = u64::MAX;
let mut image_end = 0;
for i in 0..self.ehdr.phnum {
let phdr = self.read_phdr(i as _)?;
if phdr.type_ != PT_LOAD {
continue;
}
if phdr.paddr + phdr.memsz >= MAX_KERNEL_PHYS {
error!(
"Kernel segment cannot be mapped (above 4GiB): {:#x?}",
phdr.paddr..phdr.paddr + phdr.memsz
);
todo!();
}
let aligned_start = phdr.paddr & !0xFFF;
let aligned_end = (phdr.paddr + phdr.memsz + 0xFFF) & !0xFFF;
if aligned_start < image_start {
image_start = aligned_start;
}
if aligned_end > image_end {
image_end = aligned_end;
}
// Check all pages
for page in (aligned_start..aligned_end).step_by(0x1000) {
if !mmap.is_usable(page) {
error!(
"Cannot load segment {:#x?}: crosses loader-used or reserved memory",
phdr.paddr..phdr.paddr + phdr.memsz
);
todo!();
}
}
}
// TODO maybe reserve the kernel memory in the boot services?
// 3. Load the segments
for i in 0..self.ehdr.phnum {
let phdr = self.read_phdr(i as _)?;
if phdr.type_ != PT_LOAD {
continue;
}
info!(
"Load segment {}: {:#x?}",
i,
phdr.paddr..phdr.paddr + phdr.memsz
);
if phdr.filesz > 0 {
// The section has load data
let dst = unsafe {
core::slice::from_raw_parts_mut(phdr.paddr as *mut u8, phdr.filesz as usize)
};
debug!(
"Load {:#x?} from ELF offset {:#x}",
phdr.paddr..phdr.paddr + phdr.filesz,
phdr.offset
);
self.file.set_position(phdr.offset)?;
self.file.read_exact(dst)?;
}
if phdr.memsz > 0 {
let dst = unsafe {
core::slice::from_raw_parts_mut(
(phdr.paddr + phdr.filesz) as *mut u8,
(phdr.memsz - phdr.filesz) as usize,
)
};
debug!(
"Zero data {:#x?}",
phdr.paddr + phdr.filesz..phdr.paddr + phdr.memsz
);
dst.fill(0);
}
}
// Now that the image is in memory, protocol structure can be written in the further steps
let protocol_struct_paddr = (loc_proto.address as u64) - proto_data.kernel_virt_offset;
let protocol_version = proto_data.header.version;
let entry = self.ehdr.entry;
Ok(LoadedObject {
image_start,
image_end,
entry,
protocol_struct_paddr,
protocol_version,
})
}
fn read_phdr(&mut self, index: usize) -> Result<Phdr, Error> {
let mut phdr = Phdr::zeroed();
self.file
.set_position(self.ehdr.phoff + self.ehdr.phentsize as u64 * index as u64)?;
self.file.read_exact(bytemuck::bytes_of_mut(&mut phdr))?;
Ok(phdr)
}
fn read_shdr(&mut self, index: usize) -> Result<Shdr, Error> {
let mut shdr = Shdr::zeroed();
self.file
.set_position(self.ehdr.shoff + self.ehdr.shentsize as u64 * index as u64)?;
self.file.read_exact(bytemuck::bytes_of_mut(&mut shdr))?;
Ok(shdr)
}
fn locate_protocol_data(&mut self) -> Result<Option<LocatedProtocol>, Error> {
for i in 0..self.ehdr.shnum {
let shdr = self.read_shdr(i as _)?;
if shdr.type_ != SHT_PROGBITS {
continue;
}
if (shdr.flags & (SHF_ALLOC | SHF_WRITE)) != SHF_ALLOC | SHF_WRITE {
continue;
}
// The protocol structure must be located at the start of the section
// Read the magic field from the ELF section data
let mut magic = 0;
self.file.set_position(shdr.offset)?;
self.file.read_exact(bytemuck::bytes_of_mut(&mut magic))?;
if magic != yboot_proto::KERNEL_MAGIC {
continue;
}
return Ok(Some(LocatedProtocol {
offset: shdr.offset,
address: shdr.addr as _,
size: shdr.size as _,
}));
}
Ok(None)
}
}

63
src/initrd.rs Normal file
View File

@ -0,0 +1,63 @@
use uefi::{
proto::media::file::{Directory, File, FileAttribute, FileInfo, FileMode, RegularFile},
table::boot::MemoryMap,
CStr16, Error,
};
use crate::{elf::LoadedObject, mem::MemoryMapExt};
fn can_place(mmap: &MemoryMap, base: u64, size: u64) -> bool {
let start_aligned = base & !0xFFF;
let end_aligned = (base + size + 0xFFF) & !0xFFF;
for page in (start_aligned..end_aligned).step_by(0x1000) {
if !mmap.is_usable(page) {
return false;
}
}
true
}
fn do_load(file: &mut RegularFile, base: u64, size: u64) -> Result<(), Error> {
let buffer = unsafe { core::slice::from_raw_parts_mut(base as *mut u8, size as usize) };
file.read(buffer)?;
Ok(())
}
pub fn load_somewhere(
root: &mut Directory,
filename: &CStr16,
mmap: &MemoryMap,
obj: &LoadedObject,
) -> Result<(u64, u64), Error> {
const IMAGE_END_GAP: u64 = 0x3000;
const MAXIMUM_ADDRESS: u64 = 0x100000000;
let mut info_buffer: [u8; 1024] = [0; 1024];
let file = root.open(filename, FileMode::Read, FileAttribute::empty())?;
let mut file = file.into_regular_file().unwrap();
let file_info: &FileInfo = file.get_info(&mut info_buffer).unwrap();
let size = file_info.file_size();
// 1. Try loading below the kernel
if obj.image_start >= size {
let start = (obj.image_start - size) & !0xFFF;
if can_place(mmap, start, size) {
do_load(&mut file, start, size)?;
return Ok((start, size));
}
}
// 2. Try any location above the kernel
let start = ((obj.image_end + 0xFFF) & !0xFFF) + IMAGE_END_GAP;
for base in (start..MAXIMUM_ADDRESS).step_by(0x1000) {
if can_place(mmap, base, size) {
do_load(&mut file, base, size)?;
return Ok((base, size));
}
}
panic!("Could not place initrd");
}

195
src/main.rs Normal file
View File

@ -0,0 +1,195 @@
#![feature(asm_const)]
#![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, device_path::DevicePath, loaded_image::LoadedImage,
media::fs::SimpleFileSystem,
},
table::{
boot::{AllocateType, MemoryType, ScopedProtocol},
cfg,
},
Error,
};
use yboot_proto::{
v1::{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();
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 _;
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, 'b>(
ih: Handle,
st: &'a SystemTable<Boot>,
) -> Result<(u64, u64, &'b mut LoadProtocolV1), Error> {
let bs = st.boot_services();
// Obtain the memory map
let mmap = mem::memory_map(bs)?;
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(&mmap)?;
debug!("Loaded object: {:#x?}", loaded_obj);
// Load initrd
let (initrd_start, initrd_size) =
initrd::load_somewhere(&mut root, cstr16!("initrd.img"), &mmap, &loaded_obj)?;
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!("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);
}
}

100
src/mem.rs Normal file
View File

@ -0,0 +1,100 @@
use uefi::{
prelude::BootServices,
table::boot::{MemoryDescriptor, MemoryMap, MemoryType},
Error,
};
const PTE_PRESENT: u64 = 1 << 0;
const PTE_WRITABLE: u64 = 1 << 1;
const PTE_BLOCK: u64 = 1 << 7;
const MMAP_BUFFER_SIZE: usize = 32768;
#[repr(C, align(0x10))]
struct MmapBuffer {
data: [u8; MMAP_BUFFER_SIZE],
}
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))]
struct Table {
data: [u64; 512],
}
static mut MMAP_BUFFER: MmapBuffer = MmapBuffer {
data: [0; MMAP_BUFFER_SIZE],
};
static mut PML4: Table = Table { data: [0; 512] };
static mut PDPT: Table = Table { data: [0; 512] };
static mut PDS: [Table; 4] = [Table { data: [0; 512] }; 4];
pub trait MemoryMapExt {
fn is_usable(&self, page: u64) -> bool;
}
pub trait MemoryDescriptorExt {
fn is_runtime_usable(&self) -> bool;
}
impl MemoryMapExt for MemoryMap<'_> {
fn is_usable(&self, page: u64) -> bool {
assert_eq!(page & 0xFFF, 0);
for entry in self.entries() {
let range = entry.phys_start..entry.phys_start + entry.page_count * 0x1000;
if range.contains(&page) {
return entry.ty == MemoryType::CONVENTIONAL;
}
}
// Not found in the memory map
false
}
}
impl MemoryDescriptorExt for MemoryDescriptor {
fn is_runtime_usable(&self) -> bool {
self.ty == MemoryType::LOADER_DATA
|| self.ty == MemoryType::LOADER_CODE
|| self.ty == MemoryType::BOOT_SERVICES_CODE
|| self.ty == MemoryType::BOOT_SERVICES_DATA
|| self.ty == MemoryType::RUNTIME_SERVICES_DATA
|| self.ty == MemoryType::RUNTIME_SERVICES_CODE
|| self.ty == MemoryType::CONVENTIONAL
}
}
pub fn memory_map(bs: &BootServices) -> Result<MemoryMap, Error> {
bs.memory_map(unsafe { &mut MMAP_BUFFER.data })
}
// TODO handle other offsets
pub unsafe fn map_image() -> u64 {
// Load the original cr3 to obtain the first 512GiB, these are still in use by UEFI
let mut cr3: usize;
core::arch::asm!("mov %cr3, {0}", out(reg) cr3, options(att_syntax));
let orig_pml4 = core::slice::from_raw_parts(cr3 as *const u64, 512);
// Setup the mapping tables
for i in 0..512 * PDS.len() {
let pd_index = i / 512;
let pd_offset = i % 512;
PDS[pd_index].data[pd_offset] = (i << 21) as u64 | PTE_BLOCK | PTE_WRITABLE | PTE_PRESENT;
}
for i in 0..PDS.len() {
let addr = PDS[i].data.as_mut_ptr() as u64;
PDPT.data[i] = addr | PTE_WRITABLE | PTE_PRESENT;
}
let addr = PDPT.data.as_mut_ptr() as u64;
// Clone the lower mapping from the UEFI's table
PML4.data[0] = orig_pml4[0];
// Set up upper mapping for the kernel
PML4.data[511] = addr | PTE_WRITABLE | PTE_PRESENT;
PML4.data.as_mut_ptr() as u64
}

20
src/protocol_ext.rs Normal file
View File

@ -0,0 +1,20 @@
use uefi::proto::console::gop::{GraphicsOutput, Mode};
pub trait GraphicsOutputExt {
fn match_mode(&self, width: u32, height: u32) -> Option<Mode>;
}
impl GraphicsOutputExt for GraphicsOutput {
fn match_mode(&self, width: u32, height: u32) -> Option<Mode> {
for mode in self.modes() {
let mode_info = mode.info();
let (mode_w, mode_h) = mode_info.resolution();
if (mode_w as u32 == width) && (mode_h as u32 == height) {
return Some(mode);
}
}
None
}
}