-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Barebones EFI implementation for mach-virt/arm64
This is a minimal implementation in Rust of those parts of the EFI spec that are actually needed to boot a arm64 Linux kernel straight from QEMU's mach-virt. It uses the efiloader crate which implements the bare minimum of EFI's boot and runtime services that are needed to boot a Linux kernel, UKI image or GRUB/shim loader stage. It is intended to be minimal and secure: - file data is never copied around - the kernel image and initrd are DMA'd into their final location using QEMU's fwcfg device, - the MMU is enabled straight out of reset, and all memory accesses and code execution occurs with the appropriate memory and permission attributes, - written in Rust, - only a reduced subset of boot and runtime services is implemented, - ACPI and SMBIOS tables are obtained from QEMU fwcfg directly and not modified, How to use: - build using 'cargo build' (ELF file ends up in target/aarch64-unknown-none/.../efilite) - use 'objcopy -O binary <in> <out>' to convert the ELF file into a raw binary - run using something like the below $ qemu-system-aarch64 \ -M virt -cpu max -smp 4 \ -net none -nographic -m 900m -bios efilite.bin -kernel path/to/Image \ -drive if=virtio,file=path/to/hda.xxx,format=xxx -initrd <initrd>
- Loading branch information
1 parent
f624ec4
commit 4b72a3d
Showing
16 changed files
with
1,914 additions
and
2 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[target.aarch64-unknown-none] | ||
rustflags = [ | ||
"-C", "relocation-model=static", | ||
"-C", "linker-flavor=ld.lld", | ||
"-C", "link-arg=-Tsrc/efilite.lds", | ||
"-C", "link-arg=--orphan-handling=error", | ||
"-C", "target-feature=+rand,+bti", | ||
# "-Z", "branch-protection=bti" | ||
] | ||
|
||
[build] | ||
target = "aarch64-unknown-none" |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: Rust | ||
|
||
on: | ||
push: | ||
branches: [ "mach-virt" ] | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- run: rustup target add aarch64-unknown-none | ||
- run: sudo apt-get update && sudo apt-get install -y lld | ||
- name: Build | ||
run: cargo build --verbose |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
/target | ||
*.bin | ||
.*.swp |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
// Copyright 2022-2023 Google LLC | ||
// Author: Ard Biesheuvel <[email protected]> | ||
|
||
use core::fmt::Write; | ||
use core::mem::MaybeUninit; | ||
use core::ops::Range; | ||
use fdt::node::FdtNode; | ||
use log::{Metadata, Record}; | ||
use mmio::{Allow, Deny, VolBox}; | ||
use spinning_top::Spinlock; | ||
|
||
pub struct DumbSerialConsole { | ||
pub base: usize, | ||
out: Spinlock<VolBox<u32, Deny, Allow>>, | ||
} | ||
|
||
struct DumbSerialConsoleWriter<'a> { | ||
console: &'a DumbSerialConsole, | ||
} | ||
|
||
pub fn init(base: &Range<usize>) -> &'static DumbSerialConsole { | ||
// Statically allocated so we can init the console before the heap | ||
static mut CON: MaybeUninit<DumbSerialConsole> = MaybeUninit::uninit(); | ||
|
||
unsafe { | ||
let v = VolBox::<u32, Deny, Allow>::new(base.start as *mut u32); | ||
CON.write(DumbSerialConsole { | ||
base: base.start, | ||
out: Spinlock::new(v), | ||
}) | ||
} | ||
} | ||
|
||
pub fn init_from_fdt_node(node: FdtNode) -> Option<&'static DumbSerialConsole> { | ||
let reg = node.reg()?.nth(0)?; | ||
let base = reg.starting_address as usize; | ||
let size = reg.size?; | ||
Some(init(&(base..base + size))) | ||
} | ||
|
||
impl DumbSerialConsole { | ||
fn puts(&self, s: &str) { | ||
let mut out = self.out.lock(); | ||
|
||
for b in s.as_bytes().iter() { | ||
if *b == b'\n' { | ||
out.write(b'\r' as u32); | ||
} | ||
out.write(*b as u32) | ||
} | ||
} | ||
} | ||
|
||
impl efiloader::SimpleConsole for DumbSerialConsole { | ||
fn write_string(&self, s: &str) { | ||
self.puts(s) | ||
} | ||
|
||
fn read_byte(&self) -> Option<u8> { | ||
None | ||
} | ||
} | ||
|
||
impl Write for DumbSerialConsoleWriter<'_> { | ||
fn write_str(&mut self, s: &str) -> core::fmt::Result { | ||
Ok(self.console.puts(s)) | ||
} | ||
} | ||
|
||
impl log::Log for DumbSerialConsole { | ||
fn enabled(&self, metadata: &Metadata) -> bool { | ||
metadata.level() <= log::max_level() | ||
} | ||
|
||
fn log(&self, record: &Record) { | ||
if self.enabled(record.metadata()) { | ||
let mut out = DumbSerialConsoleWriter { console: &self }; | ||
write!(&mut out, "efilite {} - {}", record.level(), record.args()).ok(); | ||
} | ||
} | ||
|
||
fn flush(&self) {} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright 2022 Google LLC | ||
* Author: Ard Biesheuvel <[email protected]> | ||
*/ | ||
|
||
MEMORY | ||
{ | ||
flash : ORIGIN = 0, LENGTH = 2M | ||
ram : ORIGIN = 0x40000000, LENGTH = 4M | ||
} | ||
|
||
PROVIDE(_init_base = 0x40000000); | ||
PROVIDE(_init_size = 0x400000); | ||
|
||
PROVIDE(rust_eh_personality = 0x0); | ||
|
||
ENTRY(_entry) | ||
|
||
SECTIONS | ||
{ | ||
.text : { | ||
_entry = .; | ||
KEEP(*(.text.entry)) | ||
*(.rodata.idmap) | ||
|
||
/* | ||
* Omit the first 64k flash region from the runtime mapping | ||
* so that the NOR flash control registers are not mapped | ||
* by the OS | ||
*/ | ||
. = ALIGN(0x10000); | ||
RTSCODE = .; | ||
_rtcode_start = .; | ||
*(SORT_BY_ALIGNMENT(.text*)) | ||
*(SORT_BY_ALIGNMENT(.rodata*)) | ||
*(.got .got.plt) | ||
*(.igot .iplt .igot.plt) | ||
*(.gcc_except_table.*) | ||
. = ALIGN(0x10000); | ||
_rtcode_end = .; | ||
} >flash | ||
|
||
/* | ||
* QEMU passes the DT blob by storing it at the base of DRAM | ||
* before starting the guest | ||
*/ | ||
.dtb (NOLOAD) : { | ||
_bsdata_start = .; | ||
_dtb_start = .; | ||
. += 0x200000; | ||
_dtb_end = .; | ||
} >ram | ||
|
||
/* | ||
* put the stack first so we will notice if we overrun and | ||
* hit the R/O mapping of the DT blob | ||
*/ | ||
.stack (NOLOAD) : { | ||
. += 0x20000; | ||
_stack_end = .; | ||
} >ram | ||
|
||
.data : ALIGN(32) { | ||
_data = .; | ||
*(SORT_BY_ALIGNMENT(.data*)) | ||
. = ALIGN(32); | ||
_edata = .; | ||
} >ram AT >flash | ||
|
||
data_lma = LOADADDR(.data); | ||
|
||
.bss : ALIGN (32) { | ||
_bss_start = .; | ||
*(SORT_BY_ALIGNMENT(.bss*)) | ||
. = ALIGN(32); | ||
_bss_end = .; | ||
_end = .; | ||
} >ram | ||
|
||
.rtdata _init_base + _init_size - 0x10000 (NOLOAD) : { | ||
_bsdata_end = .; | ||
_rtdata_start = .; | ||
_avail = ABSOLUTE(.) - _end; | ||
*(.rtdata .rtdata*) | ||
. = ALIGN(0x10000); | ||
_rtdata_end = .; | ||
} >ram | ||
|
||
.debug_abbrev 0 : { *(.debug_abbrev) } | ||
.debug_info 0 : { *(.debug_info) } | ||
.debug_aranges 0 : { *(.debug_aranges) } | ||
.debug_ranges 0 : { *(.debug_ranges) } | ||
.debug_str 0 : { *(.debug_str) } | ||
.debug_pubnames 0 : { *(.debug_pubnames) } | ||
.debug_pubtypes 0 : { *(.debug_pubtypes) } | ||
.debug_line 0 : { *(.debug_line) } | ||
.debug_frame 0 : { *(.debug_frame) } | ||
.debug_loc 0 : { *(.debug_loc) } | ||
|
||
.symtab 0 : { *(.symtab) } | ||
.strtab 0 : { *(.strtab) } | ||
.shstrtab 0 : { *(.shstrtab) } | ||
|
||
/DISCARD/ : { | ||
*(.note* .comment* .rela.* .eh_frame_hdr .eh_frame .interp) | ||
} | ||
} | ||
ASSERT(SIZEOF(.rtsdata) != 0x10000, ".rtsdata section too big") |
Oops, something went wrong.