diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2225fd9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore index 9a48bcf..4d156d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /drive +*~ diff --git a/Cargo.lock b/Cargo.lock index 2db43e5..40073d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" + [[package]] name = "bit_field" version = "0.10.0" @@ -18,6 +24,7 @@ version = "0.1.0" dependencies = [ "compiler_builtins", "uefi", + "x86_64", ] [[package]] @@ -75,7 +82,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" dependencies = [ - "bit_field", + "bit_field 0.10.0", ] [[package]] @@ -106,3 +113,13 @@ name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "x86_64" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9144cdef072afe8afcbc511be8f421de5b3eb9f4b8ff7abff369bee38f91e7e3" +dependencies = [ + "bit_field 0.9.0", + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml index 32fe9aa..55308a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" [dependencies] compiler_builtins = { git = "https://github.com/rust-lang/compiler-builtins" } uefi = "0.4.6" +x86_64 = "0.11.1" diff --git a/README.md b/README.md index 490cca9..0a4c390 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,11 @@ rather than through a traditional operating system. I don't seriously expect to ever accomplish that, so for now I'm probably just going to... make a forth or something. +## System Requirements +Other configurations may work, but only these systems are regularly tested. +* CPU: x86_64 QEMU, OVMF UEFI. +* Memory: 128 MB. (64 MB appears to be the minimum required to load OVMF at all. Real hardware might require less?) + ## Running bootproof runs on x86_64 UEFI. You may either boot the program directly on your own computer or use an emulator. @@ -15,7 +20,7 @@ Make sure you have the `cargo-xbuild` crate installed and nightly Rust so you ca First, build with: ``` -cargo +nightly xbuild --target x86_64-unknown-uefi +cargo xbuild --release --target x86_64-unknown-uefi ``` -And to run, simply `./run.sh`. +And to run, `./run.sh` will launch bootproof in QEMU. diff --git a/run.sh b/run.sh index 79de8d2..16b631e 100755 --- a/run.sh +++ b/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +profile=${1:-"debug"} mkdir -p drive/EFI/Boot -cp target/x86_64-unknown-uefi/debug/bootproof.efi drive/EFI/Boot/BootX64.efi +cp "target/x86_64-unknown-uefi/$profile/bootproof.efi" drive/EFI/Boot/BootX64.efi qemu-system-x86_64 -nodefaults -machine "q35,accel=kvm:tcg" -smp 8 -m 128M -drive "if=pflash,format=raw,file=/usr/share/OVMF/OVMF_CODE.fd,readonly=on" -drive "if=pflash,format=raw,file=/usr/share/OVMF/OVMF_VARS.fd,readonly=on" -drive "format=raw,file=fat:rw:drive" -serial stdio -display none diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/src/main.rs b/src/main.rs index 733b0de..308be4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,93 @@ #![no_std] #![no_main] #![feature(abi_efiapi)] +#![feature(asm)] use core::fmt::Write; use core::panic::PanicInfo; +use core::mem; +use core::slice; use core::writeln; use uefi::prelude::*; +use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType}; +use x86_64::instructions; + +enum LoggerStdio { + // It is impossible to get ownership of an Output, + // so instead we must pass in the entire boot system table. + Boot(SystemTable), + None +} + +fn halt() -> ! { + instructions::interrupts::disable(); + loop { instructions::hlt(); } +} // Required for use by the panic handler. // This should not be used anywhere else. -static mut global_st: Option<*mut SystemTable> = None; +static mut LOGGER_STDIO: LoggerStdio = LoggerStdio::None; #[panic_handler] fn panic(info: &PanicInfo) -> ! { - let st = unsafe { &global_st.unwrap().read() }; - writeln!(st.stderr(), "stderr: {}", info); - writeln!(st.stdout(), "stdout: {}", info); - - loop {} + match unsafe { &LOGGER_STDIO } { + LoggerStdio::Boot(st) => { + writeln!(st.stderr(), "{}", info).unwrap(); + }, + LoggerStdio::None => { + // There's pretty much nothing we can do in this case. + // What are we supposed to do-- panic? + } + } + + halt(); +} + +fn setup(st: &SystemTable, _handle: Handle) { + let stdout = st.stdout(); + + stdout.reset(false).expect_success("Failed to reset UEFI stdout."); + writeln!(stdout, "Booting...").unwrap(); + + writeln!(stdout, "Exiting the UEFI boot services.").unwrap(); +} + +fn main(st: SystemTable, mmap: uefi::table::boot::MemoryMapIter) -> ! { + halt(); } #[entry] -fn efi_main(handle: Handle, st: SystemTable) -> Status { - let mut g_st = unsafe { st.unsafe_clone() }; +fn efi_main(handle: Handle, st_boot: SystemTable) -> Status { + // Tasks that require the UEFI boot services. + unsafe { - global_st = Some(&mut g_st); + LOGGER_STDIO = LoggerStdio::Boot(st_boot.unsafe_clone()); } - - st.stdout().reset(false).expect_success("Failed to reset UEFI stdout."); - writeln!(st.stdout(), "Hello, world!"); - - loop {} - - Status::SUCCESS -} \ No newline at end of file + + setup(&st_boot, handle); + + // Exit the UEFI boot services. + + // The memory map buffer must live at least as long as the memory map iterator. + let mmap_buf; + let (st_runtime, mmap) = { + let bs = st_boot.boot_services(); + + // More allocations can happen between the allocation of the buffer and the buffer being filled, + // so arbitrarily add space for 8 more memory descriptors just to make sure there's enough. + let mmap_buf_size = bs.memory_map_size() + 8 * mem::size_of::(); + mmap_buf = bs.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, mmap_buf_size) + .expect_success("Could not allocate space for UEFI memory map."); + let mmap_buf_slice = unsafe { slice::from_raw_parts_mut(mmap_buf as *mut u8, mmap_buf_size) }; + st_boot.exit_boot_services(handle, mmap_buf_slice).expect_success("Failed to exit the UEFI boot services.") + }; + + // Tasks that do not require the UEFI boot services. + + // I do not currently have an adequate stdout for post-UEFI, but the UEFI one is now invalid. + unsafe { + LOGGER_STDIO = LoggerStdio::None; + } + + main(st_runtime, mmap); +}