Skip to content

Fix function ABI #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions BUILDING.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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.
Expand All @@ -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.
73 changes: 48 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,68 @@

[![Build Status](https://travis-ci.org/GabrielMajeri/uefi-rs.svg?branch=master)](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.

<p align="center">
<img width="848px" height="701px" alt="uefi-rs running in QEMU" src="https://imgur.com/SFPSVuO.png"/>
</p>
**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

![uefi-rs running in QEMU](https://imgur.com/SFPSVuO.png)

## 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
Expand Down
6 changes: 3 additions & 3 deletions src/proto/console/gop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/proto/console/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
12 changes: 6 additions & 6 deletions src/proto/console/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
}

Expand Down
4 changes: 2 additions & 2 deletions src/proto/console/text/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 10 additions & 10 deletions src/proto/console/text/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down
2 changes: 1 addition & 1 deletion src/proto/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
///
/// ```rust
/// struct CustomProtocol {
/// function_pointer: extern "C" fn() -> (),
/// function_pointer: extern "win64" fn() -> (),
/// data: usize
/// }
///
Expand Down
18 changes: 10 additions & 8 deletions src/proto/media/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down
2 changes: 1 addition & 1 deletion src/proto/media/file_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
29 changes: 15 additions & 14 deletions src/table/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (),
Expand All @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/table/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion uefi-test-runner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down