diff --git a/BUILDING.md b/BUILDING.md
index 806c15970..e26f8b0e2 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -1,13 +1,14 @@
# Creating UEFI applications
-UEFI applications are simple COFF (Windows) executables, with the special `EFI_Application` subsystem,
-and some limitations (such as no dynamic linking).
+UEFI applications are simple COFF (Windows) executables, with the special
+`EFI_Application` subsystem, and some limitations (such as no dynamic linking).
-The `x86_64-uefi.json` file creates a custom target for building UEFI / Windows apps, and links them using LLD.
+The `x86_64-uefi.json` file describes a custom target for building UEFI apps.
## Prerequisites
-- [cargo-xbuild](https://github.com/rust-osdev/cargo-xbuild): this is essential if you plan to do any sort of cross-platform / bare-bones Rust programming.
+- [cargo-xbuild](https://github.com/rust-osdev/cargo-xbuild): this is essential
+ if you plan to do any sort of cross-platform / bare-bones Rust programming.
## Steps
@@ -18,7 +19,7 @@ The following steps allow you to build a simple UEFI app.
```rust
#[no_mangle]
-pub extern "C" fn uefi_start(handle: Handle, system_table: &'static table::SystemTable) -> Status;
+pub extern "win64" fn uefi_start(handle: Handle, system_table: &'static table::SystemTable) -> Status;
```
- Copy the `tests/x86_64-uefi.json` target file to your project's root.
@@ -34,4 +35,8 @@ pub extern "C" fn uefi_start(handle: Handle, system_table: &'static table::Syste
- Copy the file to the USB drive, to `/EFI/Boot/Bootx64.efi`
- In the UEFI BIOS, choose "Boot from USB" or similar
+- To run this in QEMU:
+ - You will need a recent version of QEMU as well as OVMF to provide UEFI support
+ - Check the `build.py` script for an idea of what arguments to pass to QEMU
+
You can use the `uefi-test-runner` directory as sample code for building a simple UEFI app.
diff --git a/README.md b/README.md
index 70b6219db..0ff956238 100644
--- a/README.md
+++ b/README.md
@@ -2,45 +2,68 @@
[](https://travis-ci.org/GabrielMajeri/uefi-rs)
-This library allows you to write [UEFI][uefi] applications in Rust.
+## Description
-UEFI is the successor to the BIOS. It provides an early boot environment for OS loaders
-and other low-level applications.
+[UEFI] is the successor to the BIOS. It provides an early boot environment for
+OS loaders, hypervisors and other low-level applications. While it started out
+as x86-specific, it has been adopted on other platforms, such as ARM.
-The objective of this library is to provide **safe** and **performant** wrappers for UEFI
-interfaces, and allow developers to write idiomatic Rust code.
+This crates makes it easy to write UEFI applications in Rust.
-[uefi]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
+The objective is to provide **safe** and **performant** wrappers for UEFI interfaces,
+and allow developers to write idiomatic Rust code.
-
-
-
+**Note**: due to some issues with the Rust compiler, this crate currently works
+and has been tested _only_ with **64-bit** UEFI.
+
+[UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
+
+
## Project structure
This project contains multiple sub-crates:
-- `uefi` (top directory): defines the standard UEFI tables / interfaces. The objective is to stay unopionated
- and safely wrap most interfaces.
+- `uefi` (top directory): defines the standard UEFI tables / interfaces.
+ The objective is to stay unopionated and safely wrap most interfaces.
-- `uefi-services`: initializes many convenience crates:
- - `uefi-logger`: wrapper for the standard [logging](https://github.com/rust-lang-nursery/log) crate.
- Prints log output to console. No buffering is done: this is not a high-performance logger.
+- `uefi-services`: provides a panic handler, and initializes some helper crates:
+ - `uefi-logger`: logging implementation for the standard [log] crate.
+ - Prints output to console.
+ - No buffering is done: this is not a high-performance logger.
- `uefi-alloc`: implements a global allocator using UEFI functions.
- This allows you to allocate objects on the heap.
- There's no guarantee of the efficiency of UEFI's allocator.
-
- Since the global logger / allocator **can only be set once** per binary, if you're building
- a real OS you will want to either:
- - provide your own logger / allocator, using _your_ kernel's systems
- - use UEFI for writing an OS-specific boot loader binary, while your kernel is a separate binary, packaged
- together with the boot loader: similar to what the Linux kernel's [EFI stub] does
+ - This allows you to allocate objects on the heap.
+ - There's no guarantee of the efficiency of UEFI's allocator.
+
+- `uefi-exts`: extension traits providing utility functions for common patterns.
+ - Requires the `alloc` crate (either use `uefi-alloc` or your own custom allocator).
+
+- `uefi-test-runner`: a UEFI application that runs unit / integration tests.
+
+[log]: https://github.com/rust-lang-nursery/log
+
+## Building kernels which use UEFI
+
+This crate makes it easy to start buildimg simple applications with UEFI.
+However, there are some limitations you should be aware of:
+
+- The global logger / allocator **can only be set once** per binary.
+ It is useful when just starting out, but if you're building a real OS you will
+ want to write your own specific kernel logger and memory allocator.
+
+- To support advanced features such as [higher half kernel] and [linker scripts]
+ you will want to build your kernel as an ELF binary.
-- `uefi-exts`: extends existing UEFI objects by providing utility functions for common API usage.
- Requires the `alloc` crate (either use `uefi-alloc` or your own custom allocator).
+In other words, the best way to use this crate is to create a small binary which
+wraps your actual kernel, and then use UEFI's convenient functions for loading
+it from disk and booting it.
-- `uefi-test-runner` a UEFI application that runs unit / integration tests.
+This is similar to what the Linux kernel's [EFI stub] does: the compressed kernel
+is an ELF binary which has little knowledge of how it's booted, and the boot loader
+uses UEFI to set up an environment for it.
+[higher half kernel]: https://wiki.osdev.org/Higher_Half_Kernel
+[linker scripts]: https://sourceware.org/binutils/docs/ld/Scripts.html
[EFI stub]: https://www.kernel.org/doc/Documentation/efi-stub.txt
## Documentation
diff --git a/src/proto/console/gop.rs b/src/proto/console/gop.rs
index d5772430c..28fa66628 100644
--- a/src/proto/console/gop.rs
+++ b/src/proto/console/gop.rs
@@ -33,12 +33,12 @@ use crate::{Result, Status};
#[repr(C)]
pub struct GraphicsOutput {
query_mode:
- extern "C" fn(&GraphicsOutput, mode: u32, info_sz: &mut usize, &mut *const ModeInfo)
+ extern "win64" fn(&GraphicsOutput, mode: u32, info_sz: &mut usize, &mut *const ModeInfo)
-> Status,
- set_mode: extern "C" fn(&mut GraphicsOutput, mode: u32) -> Status,
+ set_mode: extern "win64" fn(&mut GraphicsOutput, mode: u32) -> Status,
// Clippy correctly complains that this is too complicated, but we can't change the spec.
#[allow(clippy::type_complexity)]
- blt: extern "C" fn(
+ blt: extern "win64" fn(
this: &mut GraphicsOutput,
buffer: usize,
op: u32,
diff --git a/src/proto/console/pointer/mod.rs b/src/proto/console/pointer/mod.rs
index fad282844..f3583820f 100644
--- a/src/proto/console/pointer/mod.rs
+++ b/src/proto/console/pointer/mod.rs
@@ -6,8 +6,8 @@ use crate::{Result, Status};
/// Provides information about a pointer device.
#[repr(C)]
pub struct Pointer {
- reset: extern "C" fn(this: &mut Pointer, ext_verif: bool) -> Status,
- get_state: extern "C" fn(this: &Pointer, state: &mut PointerState) -> Status,
+ reset: extern "win64" fn(this: &mut Pointer, ext_verif: bool) -> Status,
+ get_state: extern "win64" fn(this: &Pointer, state: &mut PointerState) -> Status,
_wait_for_input: usize,
mode: &'static PointerMode,
}
diff --git a/src/proto/console/serial.rs b/src/proto/console/serial.rs
index 145733250..e6f6b0e7d 100644
--- a/src/proto/console/serial.rs
+++ b/src/proto/console/serial.rs
@@ -15,8 +15,8 @@ pub struct Serial {
// Revision of this protocol, only 1.0 is currently defined.
// Future versions will be backwards compatible.
revision: u32,
- reset: extern "C" fn(&mut Serial) -> Status,
- set_attributes: extern "C" fn(
+ reset: extern "win64" fn(&mut Serial) -> Status,
+ set_attributes: extern "win64" fn(
&Serial,
baud_rate: u64,
receive_fifo_depth: u32,
@@ -25,10 +25,10 @@ pub struct Serial {
data_bits: u8,
stop_bits_type: StopBits,
) -> Status,
- set_control_bits: extern "C" fn(&mut Serial, ControlBits) -> Status,
- get_control_bits: extern "C" fn(&Serial, &mut ControlBits) -> Status,
- write: extern "C" fn(&mut Serial, &mut usize, *const u8) -> Status,
- read: extern "C" fn(&mut Serial, &mut usize, *mut u8) -> Status,
+ set_control_bits: extern "win64" fn(&mut Serial, ControlBits) -> Status,
+ get_control_bits: extern "win64" fn(&Serial, &mut ControlBits) -> Status,
+ write: extern "win64" fn(&mut Serial, &mut usize, *const u8) -> Status,
+ read: extern "win64" fn(&mut Serial, &mut usize, *mut u8) -> Status,
io_mode: &'static IoMode,
}
diff --git a/src/proto/console/text/input.rs b/src/proto/console/text/input.rs
index 2c09aadeb..2fb16d557 100644
--- a/src/proto/console/text/input.rs
+++ b/src/proto/console/text/input.rs
@@ -4,8 +4,8 @@ use crate::{Result, Status};
/// Interface for text-based input devices.
#[repr(C)]
pub struct Input {
- reset: extern "C" fn(this: &mut Input, extended: bool) -> Status,
- read_key_stroke: extern "C" fn(this: &mut Input, key: &mut Key) -> Status,
+ reset: extern "win64" fn(this: &mut Input, extended: bool) -> Status,
+ read_key_stroke: extern "win64" fn(this: &mut Input, key: &mut Key) -> Status,
}
impl Input {
diff --git a/src/proto/console/text/output.rs b/src/proto/console/text/output.rs
index cf8c05c93..c70f053ce 100644
--- a/src/proto/console/text/output.rs
+++ b/src/proto/console/text/output.rs
@@ -7,16 +7,16 @@ use crate::{Result, Status};
/// standard Rust constructs like the write!() and writeln!() macros.
#[repr(C)]
pub struct Output {
- reset: extern "C" fn(this: &Output, extended: bool) -> Status,
- output_string: extern "C" fn(this: &Output, string: *const u16) -> Status,
- test_string: extern "C" fn(this: &Output, string: *const u16) -> Status,
- query_mode:
- extern "C" fn(this: &Output, mode: i32, columns: &mut usize, rows: &mut usize) -> Status,
- set_mode: extern "C" fn(this: &mut Output, mode: i32) -> Status,
- set_attribute: extern "C" fn(this: &mut Output, attribute: usize) -> Status,
- clear_screen: extern "C" fn(this: &mut Output) -> Status,
- set_cursor_position: extern "C" fn(this: &mut Output, column: usize, row: usize) -> Status,
- enable_cursor: extern "C" fn(this: &mut Output, visible: bool) -> Status,
+ reset: extern "win64" fn(this: &Output, extended: bool) -> Status,
+ output_string: extern "win64" fn(this: &Output, string: *const u16) -> Status,
+ test_string: extern "win64" fn(this: &Output, string: *const u16) -> Status,
+ query_mode: extern "win64" fn(this: &Output, mode: i32, columns: &mut usize, rows: &mut usize)
+ -> Status,
+ set_mode: extern "win64" fn(this: &mut Output, mode: i32) -> Status,
+ set_attribute: extern "win64" fn(this: &mut Output, attribute: usize) -> Status,
+ clear_screen: extern "win64" fn(this: &mut Output) -> Status,
+ set_cursor_position: extern "win64" fn(this: &mut Output, column: usize, row: usize) -> Status,
+ enable_cursor: extern "win64" fn(this: &mut Output, visible: bool) -> Status,
data: &'static OutputData,
}
diff --git a/src/proto/macros.rs b/src/proto/macros.rs
index 716ebe089..dcccde002 100644
--- a/src/proto/macros.rs
+++ b/src/proto/macros.rs
@@ -5,7 +5,7 @@
///
/// ```rust
/// struct CustomProtocol {
-/// function_pointer: extern "C" fn() -> (),
+/// function_pointer: extern "win64" fn() -> (),
/// data: usize
/// }
///
diff --git a/src/proto/media/file.rs b/src/proto/media/file.rs
index 2e9b47b83..1cf7df773 100644
--- a/src/proto/media/file.rs
+++ b/src/proto/media/file.rs
@@ -152,22 +152,24 @@ impl<'a> File<'a> {
#[repr(C)]
struct FileImpl {
revision: u64,
- open: extern "C" fn(
+ open: extern "win64" fn(
this: &mut FileImpl,
new_handle: &mut usize,
filename: *const u16,
open_mode: FileMode,
attributes: FileAttribute,
) -> Status,
- close: extern "C" fn(this: &mut FileImpl) -> Status,
- delete: extern "C" fn(this: &mut FileImpl) -> Status,
- read: extern "C" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *mut u8) -> Status,
- write: extern "C" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *const u8) -> Status,
- get_position: extern "C" fn(this: &mut FileImpl, position: &mut u64) -> Status,
- set_position: extern "C" fn(this: &mut FileImpl, position: u64) -> Status,
+ close: extern "win64" fn(this: &mut FileImpl) -> Status,
+ delete: extern "win64" fn(this: &mut FileImpl) -> Status,
+ read:
+ extern "win64" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *mut u8) -> Status,
+ write: extern "win64" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *const u8)
+ -> Status,
+ get_position: extern "win64" fn(this: &mut FileImpl, position: &mut u64) -> Status,
+ set_position: extern "win64" fn(this: &mut FileImpl, position: u64) -> Status,
get_info: usize,
set_info: usize,
- flush: extern "C" fn(this: &mut FileImpl) -> Status,
+ flush: extern "win64" fn(this: &mut FileImpl) -> Status,
}
bitflags! {
diff --git a/src/proto/media/file_system.rs b/src/proto/media/file_system.rs
index 9373cf721..31035ca6f 100644
--- a/src/proto/media/file_system.rs
+++ b/src/proto/media/file_system.rs
@@ -9,7 +9,7 @@ use super::file::File;
#[repr(C)]
pub struct SimpleFileSystem {
revision: u64,
- open_volume: extern "C" fn(this: &mut SimpleFileSystem, root: &mut usize) -> Status,
+ open_volume: extern "win64" fn(this: &mut SimpleFileSystem, root: &mut usize) -> Status,
}
impl SimpleFileSystem {
diff --git a/src/table/boot.rs b/src/table/boot.rs
index bf63b8550..82d2016bb 100644
--- a/src/table/boot.rs
+++ b/src/table/boot.rs
@@ -12,18 +12,19 @@ pub struct BootServices {
header: Header,
// Task Priority services
- raise_tpl: extern "C" fn(Tpl) -> Tpl,
- restore_tpl: extern "C" fn(Tpl),
+ raise_tpl: extern "win64" fn(Tpl) -> Tpl,
+ restore_tpl: extern "win64" fn(Tpl),
// Memory allocation functions
allocate_pages:
- extern "C" fn(alloc_ty: u32, mem_ty: MemoryType, count: usize, addr: &mut u64) -> Status,
- free_pages: extern "C" fn(u64, usize) -> Status,
+ extern "win64" fn(alloc_ty: u32, mem_ty: MemoryType, count: usize, addr: &mut u64)
+ -> Status,
+ free_pages: extern "win64" fn(u64, usize) -> Status,
memory_map:
- extern "C" fn(size: &mut usize, usize, key: &mut MemoryMapKey, &mut usize, &mut u32)
+ extern "win64" fn(size: &mut usize, usize, key: &mut MemoryMapKey, &mut usize, &mut u32)
-> Status,
- allocate_pool: extern "C" fn(MemoryType, usize, addr: &mut usize) -> Status,
- free_pool: extern "C" fn(buffer: usize) -> Status,
+ allocate_pool: extern "win64" fn(MemoryType, usize, addr: &mut usize) -> Status,
+ free_pool: extern "win64" fn(buffer: usize) -> Status,
// Event & timer functions
create_event: usize,
@@ -38,10 +39,10 @@ pub struct BootServices {
reinstall_protocol_interface: usize,
uninstall_protocol_interface: usize,
handle_protocol:
- extern "C" fn(handle: Handle, proto: *const Guid, out_proto: &mut usize) -> Status,
+ extern "win64" fn(handle: Handle, proto: *const Guid, out_proto: &mut usize) -> Status,
_reserved: usize,
register_protocol_notify: usize,
- locate_handle: extern "C" fn(
+ locate_handle: extern "win64" fn(
search_ty: i32,
proto: *const Guid,
key: *mut (),
@@ -56,12 +57,12 @@ pub struct BootServices {
start_image: usize,
exit: usize,
unload_image: usize,
- exit_boot_services: extern "C" fn(Handle, MemoryMapKey) -> Status,
+ exit_boot_services: extern "win64" fn(Handle, MemoryMapKey) -> Status,
// Misc services
get_next_monotonic_count: usize,
- stall: extern "C" fn(usize) -> Status,
- set_watchdog_timer: extern "C" fn(
+ stall: extern "win64" fn(usize) -> Status,
+ set_watchdog_timer: extern "win64" fn(
timeout: usize,
watchdog_code: u64,
data_size: usize,
@@ -88,8 +89,8 @@ pub struct BootServices {
calculate_crc32: usize,
// Misc services
- copy_mem: extern "C" fn(dest: *mut u8, src: *const u8, len: usize),
- set_mem: extern "C" fn(buffer: *mut u8, len: usize, value: u8),
+ copy_mem: extern "win64" fn(dest: *mut u8, src: *const u8, len: usize),
+ set_mem: extern "win64" fn(buffer: *mut u8, len: usize, value: u8),
// New event functions (UEFI 2.0 or newer)
create_event_ex: usize,
diff --git a/src/table/runtime.rs b/src/table/runtime.rs
index 3069102f7..9181dfb5a 100644
--- a/src/table/runtime.rs
+++ b/src/table/runtime.rs
@@ -13,7 +13,7 @@ pub struct RuntimeServices {
header: Header,
// Skip some useless functions.
_pad: [usize; 10],
- reset: extern "C" fn(u32, Status, usize, *const u8) -> !,
+ reset: extern "win64" fn(u32, Status, usize, *const u8) -> !,
}
impl RuntimeServices {
diff --git a/uefi-test-runner/src/main.rs b/uefi-test-runner/src/main.rs
index baecc0b5f..d3a801c56 100644
--- a/uefi-test-runner/src/main.rs
+++ b/uefi-test-runner/src/main.rs
@@ -17,7 +17,7 @@ mod boot;
mod proto;
#[no_mangle]
-pub extern "C" fn uefi_start(_handle: uefi::Handle, st: &'static SystemTable) -> Status {
+pub extern "win64" fn uefi_start(_handle: uefi::Handle, st: &'static SystemTable) -> Status {
// Initialize logging.
uefi_services::init(st);