From 8f601b72556376ef3d8a8249bda2ced1606c3982 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 17 Sep 2020 15:27:01 +0300 Subject: [PATCH] Add initrd loading --- crates/efi/src/proto/fp.rs | 34 ++++++++++++++++++++++++- qemu.sh | 2 +- src/elf.rs | 25 +++++++++++++----- src/initrd.rs | 52 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 11 ++++++-- 5 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 src/initrd.rs diff --git a/crates/efi/src/proto/fp.rs b/crates/efi/src/proto/fp.rs index d39f0c8..a127048 100644 --- a/crates/efi/src/proto/fp.rs +++ b/crates/efi/src/proto/fp.rs @@ -1,8 +1,14 @@ -use crate::{CStr16, Status}; +use crate::{CStr16, Status, Guid}; use core::ptr::null_mut; use core::ffi::c_void; pub const OPEN_MODE_READ: u64 = 1; +pub const FILE_INFO_GUID: Guid = Guid { + data1: 0x09576e92, + data2: 0x6d3f, + data3: 0x11d2, + data4: [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b] +}; #[repr(C)] pub struct FileProtocol { @@ -17,6 +23,17 @@ pub struct FileProtocol { write: *mut c_void, get_position: *mut c_void, set_position: unsafe fn (*mut FileProtocol, u64) -> u64, + get_info: unsafe fn (*mut FileProtocol, + *const Guid, + *mut usize, + *mut c_void) -> u64, +} + +#[repr(C)] +pub struct Stat { + pub size: u64, + pub file_size: u64, + // ... } pub struct File { @@ -81,4 +98,19 @@ impl File { ) }).into() } + + pub fn stat(&mut self, statbuf: &mut [u8]) -> Result<&Stat, Status> { + let mut len = statbuf.len(); + match Status::from(unsafe { + ((*self.inner).get_info)( + self.inner, + &FILE_INFO_GUID, + &mut len, + statbuf.as_mut_ptr() as *mut _ + ) + }) { + Status::Success => Ok(unsafe {core::mem::transmute(statbuf.as_ptr())}), + err => Err(err) + } + } } diff --git a/qemu.sh b/qemu.sh index b71968d..92f742c 100755 --- a/qemu.sh +++ b/qemu.sh @@ -14,7 +14,7 @@ cargo build -Z build-std=core dd if=/dev/zero of=${IMAGE} bs=1M count=64 mkfs.vfat -F32 ${IMAGE} mcopy -i ${IMAGE} target/${TARGET}/${CONFIG}/yboot2.efi ::app.efi -mcopy -i ${IMAGE} image/config.txt ::config.txt +mcopy -i ${IMAGE} image/initrd.img ::initrd.img mcopy -i ${IMAGE} image/kernel.elf ::kernel.elf qemu-system-x86_64 \ diff --git a/src/elf.rs b/src/elf.rs index 900c74d..cb0006b 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -50,18 +50,21 @@ struct Shdr { #[repr(C)] struct Phdr { _type: Word, + flags: Word, offset: Off, vaddr: Addr, paddr: Addr, - filesz: Word, - memsz: Word, - flags: Word, - align: Word + filesz: XWord, + memsz: XWord, + align: XWord } pub struct Object { - file: File, - ehdr: Ehdr, + file: File, + ehdr: Ehdr, + + pub start: usize, + pub end: usize } unsafe fn any_as_u8_slice(p: &mut T) -> &mut [u8] { @@ -77,6 +80,8 @@ impl Object { let mut obj = Object { file: root.open(path, efi::proto::fp::OPEN_MODE_READ, 0)?, ehdr: unsafe { MaybeUninit::uninit().assume_init() }, + start: 0xFFFFFFFFFFFFFFFF, + end: 0, }; // Load header @@ -148,6 +153,7 @@ impl Object { let mut phdr = unsafe { MaybeUninit::::uninit().assume_init() }; // 1. Check that all pages in load segments are usable + // Also find out kernel's lowest and highest physical addresses for i in 0 .. self.ehdr.phnum { self.read_phdr(&mut phdr, i as usize)?; @@ -155,6 +161,13 @@ impl Object { let start = phdr.paddr & !0xFFF; let end = (phdr.paddr + phdr.memsz as u64 + 0xFFF) & !0xFFF; + if (start as usize) < self.start { + self.start = start as usize; + } + if (end as usize) > self.end { + self.end = end as usize; + } + for addr in (start .. end).step_by(0x1000) { if !mmap.is_usable_now(addr as usize) { return Err(Status::InvalidParameter); diff --git a/src/initrd.rs b/src/initrd.rs new file mode 100644 index 0000000..e9dc0c0 --- /dev/null +++ b/src/initrd.rs @@ -0,0 +1,52 @@ +use efi::{File, CStr16, Status}; +use core::mem::MaybeUninit; +use crate::elf; + +fn check_placement(mmap: &efi::MemoryMap, base: usize, size: usize) -> bool { + for page in (base & !0xFFF .. (base + size + 0xFFF) & !0xFFF).step_by(0x1000) { + if !mmap.is_usable_now(page) { + return false; + } + } + true +} + +fn do_load(file: &mut File, base: usize, size: usize) -> efi::Result<()> { + file.read(unsafe {core::slice::from_raw_parts_mut(base as *mut u8, size)})?; + Ok(()) +} + +pub fn load_somewhere(root: &mut File, + filename: &CStr16, + mmap: &efi::MemoryMap, + obj: &elf::Object) -> efi::Result<(usize, usize)> { + let mut statbuf: [u8; 1024] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut file = root.open(filename, + efi::proto::fp::OPEN_MODE_READ, + 0)?; + let stat = file.stat(&mut statbuf)?; + let size = stat.file_size as usize; + + // 1. Try loading right below the kernel + if obj.start >= size { + let start = (obj.start - size) & !0xFFF; + + if check_placement(mmap, start, size) { + println!("Loading initrd below the kernel at 0x{:016x}", start); + do_load(&mut file, start, size)?; + return Ok((start, size)); + } + } + + // 2. Any location above the kernel + for start in ((obj.end + 0x3FFF) & !0xFFF .. 0x100000000).step_by(0x1000) { + if check_placement(mmap, start, size) { + println!("Loading initrd at 0x{:016x}", start); + do_load(&mut file, start, size)?; + return Ok((start, size)); + } + } + + Err(Status::InvalidParameter) +} + diff --git a/src/main.rs b/src/main.rs index 0d75b15..5fe3199 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use efi::{ #[macro_use] mod println; +mod initrd; mod proto; mod elf; @@ -39,11 +40,17 @@ fn main() -> efi::Result<()> { .unwrap(); let mut root = image_handle().get_boot_path()?.open_partition()?; + + // Load kernel let mut obj = elf::Object::open(&mut root, CStr16::from_literal(cstr16!(r"\kernel.elf")))?; let entry = obj.load(&mmap)?; let data = obj.locate_protocol_data::()?; - // TODO: load initrd + // Load initrd + let (initrd_base, initrd_size) = initrd::load_somewhere(&mut root, + CStr16::from_literal(cstr16!(r"\initrd.img")), + &mmap, + &obj)?; // Get the new memory map and terminate boot services bs.get_memory_map(&mut mmap)?; @@ -51,7 +58,7 @@ fn main() -> efi::Result<()> { use proto::LoadProtocol; data.set_mmap(&mmap); - data.set_initrd(0, 0); + data.set_initrd(initrd_base, initrd_size); data.set_acpi_rsdp(rsdp as usize); data.set_loader_magic();