Created
June 17, 2020 21:42
-
-
Save IronOxidizer/888e7351c6001d435b16f2836c6b9b20 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![no_std] | |
#![no_main] | |
#![feature(asm)] | |
#![feature(abi_efiapi)] | |
extern crate compiler_builtins; | |
#[macro_use] | |
extern crate log; | |
#[macro_use] | |
extern crate alloc; | |
use core::mem; | |
use uefi::prelude::*; | |
use uefi::proto::console::serial::Serial; | |
use uefi::table::boot::MemoryDescriptor; | |
mod boot; | |
mod proto; | |
#[entry] | |
fn efi_main(image: Handle, st: SystemTable<Boot>) -> Status { | |
// Initialize utilities (logging, memory allocation...) | |
uefi_services::init(&st).expect_success("Failed to initialize utilities"); | |
// Reset the console before running all the other tests. | |
st.stdout() | |
.reset(false) | |
.expect_success("Failed to reset stdout"); | |
// Ensure the tests are run on a version of UEFI we support. | |
check_revision(st.uefi_revision()); | |
// Test all the boot services. | |
let bt = st.boot_services(); | |
boot::test(bt); | |
// Test all the supported protocols. | |
proto::test(&st); | |
// TODO: test the runtime services. | |
// These work before boot services are exited, but we'd probably want to | |
// test them after exit_boot_services... | |
shutdown(image, st); | |
} | |
fn check_revision(rev: uefi::table::Revision) { | |
let (major, minor) = (rev.major(), rev.minor()); | |
info!("UEFI {}.{}", major, minor / 10); | |
assert!(major >= 2, "Running on an old, unsupported version of UEFI"); | |
assert!( | |
minor >= 30, | |
"Old version of UEFI 2, some features might not be available." | |
); | |
} | |
/// Ask the test runner to check the current screen output against a reference | |
/// | |
/// This functionality is very specific to our QEMU-based test runner. Outside | |
/// of it, we just pause the tests for a couple of seconds to allow visual | |
/// inspection of the output. | |
fn check_screenshot(bt: &BootServices, name: &str) { | |
if cfg!(feature = "qemu") { | |
// Access the serial port (in a QEMU environment, it should always be there) | |
let serial = bt | |
.locate_protocol::<Serial>() | |
.expect_success("Could not find serial port"); | |
let serial = unsafe { &mut *serial.get() }; | |
// Set a large timeout to avoid problems with Travis | |
let mut io_mode = *serial.io_mode(); | |
io_mode.timeout = 10_000_000; | |
serial | |
.set_attributes(&io_mode) | |
.expect_success("Failed to configure serial port timeout"); | |
// Send a screenshot request to the host | |
serial | |
.write(b"SCREENSHOT: ") | |
.expect_success("Failed to send request"); | |
let name_bytes = name.as_bytes(); | |
serial | |
.write(name_bytes) | |
.expect_success("Failed to send request"); | |
serial.write(b"\n").expect_success("Failed to send request"); | |
// Wait for the host's acknowledgement before moving forward | |
let mut reply = [0; 3]; | |
serial | |
.read(&mut reply[..]) | |
.expect_success("Failed to read host reply"); | |
assert_eq!(&reply[..], b"OK\n", "Unexpected screenshot request reply"); | |
} else { | |
// Outside of QEMU, give the user some time to inspect the output | |
bt.stall(3_000_000); | |
} | |
} | |
fn shutdown(image: uefi::Handle, st: SystemTable<Boot>) -> ! { | |
use uefi::table::runtime::ResetType; | |
// Get our text output back. | |
st.stdout().reset(false).unwrap_success(); | |
// Inform the user, and give him time to read on real hardware | |
if cfg!(not(feature = "qemu")) { | |
info!("Testing complete, shutting down in 3 seconds..."); | |
st.boot_services().stall(3_000_000); | |
} else { | |
info!("Testing complete, shutting down..."); | |
} | |
// Exit boot services as a proof that it works :) | |
let max_mmap_size = | |
st.boot_services().memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>(); | |
let mut mmap_storage = vec![0; max_mmap_size].into_boxed_slice(); | |
let (st, _iter) = st | |
.exit_boot_services(image, &mut mmap_storage[..]) | |
.expect_success("Failed to exit boot services"); | |
// Shut down the system | |
let rt = unsafe { st.runtime_services() }; | |
rt.reset(ResetType::Shutdown, Status::SUCCESS, None); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment