summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2023-11-06 14:51:20 +0100
committerArd Biesheuvel <ardb@kernel.org>2023-11-06 14:51:20 +0100
commitd5f448f80177e5b8c2bbbe3cee9def6c5a527e6d (patch)
treefbaee60a8dcbe7bd5ac196452623acbf0f602196
downloadefiloadertest-d5f448f80177e5b8c2bbbe3cee9def6c5a527e6d.tar.gz
Quick and dirty user space rig for efiloader
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock222
-rw-r--r--Cargo.toml12
-rw-r--r--src/main.rs244
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; }
+}