diff options
author | Ard Biesheuvel <ardb@kernel.org> | 2023-11-06 14:51:20 +0100 |
---|---|---|
committer | Ard Biesheuvel <ardb@kernel.org> | 2023-11-06 14:51:20 +0100 |
commit | d5f448f80177e5b8c2bbbe3cee9def6c5a527e6d (patch) | |
tree | fbaee60a8dcbe7bd5ac196452623acbf0f602196 | |
download | efiloadertest-d5f448f80177e5b8c2bbbe3cee9def6c5a527e6d.tar.gz |
Quick and dirty user space rig for efiloader
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Cargo.lock | 222 | ||||
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | src/main.rs | 244 |
4 files changed, 479 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..840486a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,222 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-utf16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90feefab165fe011746e3be2f0708b7b180fcbd9f5054ff81a454d7bd93d8285" + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "efiloader" +version = "0.0.1" +source = "git+https://github.com/ardbiesheuvel/efiloader#6f97bcf558b8fabe8706cbd874138a99df1e8153" +dependencies = [ + "const-utf16", + "crc", + "linked_list_allocator", + "log", + "mmio", + "once_cell", + "spinning_top", + "widestring", +] + +[[package]] +name = "efiloadertest" +version = "0.0.1" +dependencies = [ + "efiloader", + "libc", + "log", + "rand", + "termion", + "widestring", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mmio" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee857bfd0b37394f3507d78ee7bd4b712a2179a2ce50e47d36bbb481672f5408" + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_termios" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +dependencies = [ + "redox_syscall", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + +[[package]] +name = "termion" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..470906c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "efiloadertest" +version = "0.0.1" +edition = "2021" + +[dependencies] +efiloader = { git = "https://github.com/ardbiesheuvel/efiloader", default-features = false } +libc = "*" +log = "*" +rand = "*" +termion = "2.0.1" +widestring = "*" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1a6ef20 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,244 @@ +use core::mem::MaybeUninit; +use core::ops::Range; +use core::slice; +use rand::Rng; +use std::env; +use std::io::{Read, Write}; +use std::io; +use std::io::Stdout; +use std::fs; +use termion::raw::IntoRawMode; +use termion::raw::RawTerminal; + +use efiloader::memorytype::EFI_MEMORY_RO; +use efiloader::memorytype::EFI_MEMORY_XP; +use efiloader::EfiProtocol; +use efiloader::{Guid, guid}; +use efiloader::devicepath::*; +use efiloader::devicepath::DevicePathType::*; +use efiloader::devicepath::DevicePathSubtype::*; + +use efiloader::status::Status; +use efiloader::DumbConsole; +use efiloader::blockio::*; + +struct DumbSerialConsole { + term: Option<RawTerminal<Stdout>>, +} + +static mut CON: DumbSerialConsole = DumbSerialConsole { term: None }; +impl DumbSerialConsole { + fn new() -> Option<&'static (dyn DumbConsole + Send + Sync)> { + unsafe { + CON.term = Some(io::stdout().into_raw_mode().ok()?); + Some(&CON) + } + } +} + +impl efiloader::DumbConsole for DumbSerialConsole { + fn write_string(&self, s: &str) { + for c in s.chars() { + let s = &[c as u8]; + let c: &[u8] = match c { + '\n' => &[b'\r', b'\n'], + _ => s, + }; + io::stdout().write(&c).unwrap(); + io::stdout().flush().unwrap(); + } + } + + fn read_byte(&self) -> Option<u8> { + let mut c = [0u8]; + io::stdin().read(&mut c).ok()?; + Some(c[0]) + } +} + +use log::{LevelFilter, Metadata, Record}; + +static MY_LOGGER: MyLogger = MyLogger; + +struct MyLogger; + +impl log::Log for MyLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + eprintln!("{} - {}", record.level(), record.args()); + } + } + fn flush(&self) {} +} + +struct MemoryMapper; +impl efiloader::MemoryMapper for MemoryMapper { + fn remap_range(&self, range: &Range<usize>, set: u64, clr: u64) -> Result<(), &str> { + let prot = libc::PROT_READ + | match clr & !set { + EFI_MEMORY_RO => libc::PROT_WRITE, + EFI_MEMORY_XP => libc::PROT_EXEC, + 0 => 0, + _ => libc::PROT_WRITE | libc::PROT_EXEC, + }; + + unsafe { libc::mprotect(range.start as *mut _, range.end - range.start, prot) }; + Ok(()) + } + + fn query_range(&self, _range: &Range<usize>) -> Option<u64> { + None + } +} + +struct Random {} +impl efiloader::Random for Random { + fn get_entropy(&self, bytes: &mut [u8], _use_raw: bool) -> bool { + let mut rng = rand::thread_rng(); + bytes.fill_with(|| rng.gen::<u8>()); + true + } +} + +struct File { + contents: Vec<u8>, +} + +impl File { + pub fn new(f: &String) -> Option<Self> { + let contents = fs::read(f).ok()?; + Some(File { contents: contents }) + } +} + +impl efiloader::FileLoader for File { + fn get_size(&self) -> usize { + self.contents.len() + } + + fn load_file<'a>(&self, buf: &'a mut [MaybeUninit<u8>]) -> Result<&'a [u8], &str> { + let size = self.get_size().min(buf.len()); + let out = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut _, buf.len()) }; + + out[..size].copy_from_slice(self.contents.as_slice()); + + if buf.len() > size { + out[size..buf.len()].fill(0u8); + } + Ok(out) + } + + unsafe fn load_range(&self, dst: *mut (), offset: usize, bufsize: usize) -> Result<(), &str> { + let out = unsafe { slice::from_raw_parts_mut(dst as *mut _, bufsize) }; + let size = bufsize.min(self.get_size() - offset); + + out[..size].copy_from_slice(&self.contents.as_slice()[offset..offset + size]); + + if out.len() > bufsize { + out[size..bufsize].fill(0u8); + } + Ok(()) + } +} + +extern "efiapi" fn stall(ms: usize) -> Status { + unsafe { libc::usleep(ms as _) }; + Status::EFI_SUCCESS +} + +fn main() { + log::set_logger(&MY_LOGGER).unwrap(); + log::set_max_level(LevelFilter::Trace); + + let mut files: Vec<_> = env::args().skip(1).map(|f| File::new(&f)).collect(); + + let memmap = efiloader::memmap::MemoryMap::new(); + + let ram = { + let size = 512 * 0x10_0000; + let base = unsafe { + libc::mmap( + core::ptr::null_mut(), + size as _, + libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC, + libc::MAP_ANONYMOUS | libc::MAP_PRIVATE | libc::MAP_32BIT, + -1, + 0, + ) as usize + }; + base..base + size + }; + + memmap + .declare_memory_region(&ram) + .unwrap_or_else(|e| println!("Unsupported RAM region {:x?}", e)); + + // memmap.allocate_region(&(ram.start..ram.start + 0x10_0000), EfiUnacceptedMemory, 0) + // .unwrap(); + + let efi = efiloader::init( + DumbSerialConsole::new(), + memmap, + MemoryMapper {}, + Some(Random {}), + ) + .expect("Failed to init EFI runtime"); + + efi.override_stall_handler(stall); + +// if files.len() > 1 { +// files.remove(1).map(|i| efi.set_initrd_loader(i)); +// } + if files.len() > 1 { + let bio = EfiBlockIoProtocol::new(files.remove(1).unwrap()).unwrap(); + + struct DevPath { + _vendor: VendorMedia, + _end: DevicePath, + } + impl EfiProtocol for DevPath { + fn guid(&self) -> &'static Guid { + &EFI_DEVICE_PATH_PROTOCOL_GUID + } + } + + + let handle = efi.install_protocol(None, bio); + efi.install_protocol(Some(handle), + DevPath { + _vendor: VendorMedia { + header: DevicePath { + _type: EFI_DEV_MEDIA, + subtype: EFI_DEV_MEDIA_VENDOR, + size: core::mem::size_of::<VendorMedia>() as u16, + }, + vendor_guid: guid!( + 0xf42a639c, + 0xc725, + 0x4ef1, + [0x93, 0x00, 0x5b, 0x23, 0x83, 0x8a, 0x39, 0xdf] + ) + }, + _end: DevicePath { + _type: EFI_DEV_END_PATH, + subtype: EFI_DEV_END_ENTIRE, + size: core::mem::size_of::<DevicePath>() as u16, + }, + }, + ); + } + let cmdline = "efi=debug".encode_utf16().collect::<Vec<u16>>(); + let f = files[0].as_ref().unwrap(); + if let Some(mut li) = efi.load_image(f) { + li.set_load_options(cmdline); + let ret = li.start_image(); + eprintln!("EFI program returned {ret:?}"); + } else { + eprintln!("load_image failed"); + } + unsafe { CON.term = None; } +} |