Skip to content

Commit f525b86

Browse files
GabrielMajeriHadrienG2
authored andcommitted
Fix function ABI (#39)
* Fix function ABI and drop support for 32-bit * Update README to describe limitations
1 parent 5431761 commit f525b86

File tree

13 files changed

+110
-79
lines changed

13 files changed

+110
-79
lines changed

BUILDING.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# Creating UEFI applications
22

3-
UEFI applications are simple COFF (Windows) executables, with the special `EFI_Application` subsystem,
4-
and some limitations (such as no dynamic linking).
3+
UEFI applications are simple COFF (Windows) executables, with the special
4+
`EFI_Application` subsystem, and some limitations (such as no dynamic linking).
55

6-
The `x86_64-uefi.json` file creates a custom target for building UEFI / Windows apps, and links them using LLD.
6+
The `x86_64-uefi.json` file describes a custom target for building UEFI apps.
77

88
## Prerequisites
99

10-
- [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.
10+
- [cargo-xbuild](https://github.com/rust-osdev/cargo-xbuild): this is essential
11+
if you plan to do any sort of cross-platform / bare-bones Rust programming.
1112

1213
## Steps
1314

@@ -18,7 +19,7 @@ The following steps allow you to build a simple UEFI app.
1819

1920
```rust
2021
#[no_mangle]
21-
pub extern "C" fn uefi_start(handle: Handle, system_table: &'static table::SystemTable) -> Status;
22+
pub extern "win64" fn uefi_start(handle: Handle, system_table: &'static table::SystemTable) -> Status;
2223
```
2324

2425
- 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
3435
- Copy the file to the USB drive, to `/EFI/Boot/Bootx64.efi`
3536
- In the UEFI BIOS, choose "Boot from USB" or similar
3637

38+
- To run this in QEMU:
39+
- You will need a recent version of QEMU as well as OVMF to provide UEFI support
40+
- Check the `build.py` script for an idea of what arguments to pass to QEMU
41+
3742
You can use the `uefi-test-runner` directory as sample code for building a simple UEFI app.

README.md

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,68 @@
22

33
[![Build Status](https://travis-ci.org/GabrielMajeri/uefi-rs.svg?branch=master)](https://travis-ci.org/GabrielMajeri/uefi-rs)
44

5-
This library allows you to write [UEFI][uefi] applications in Rust.
5+
## Description
66

7-
UEFI is the successor to the BIOS. It provides an early boot environment for OS loaders
8-
and other low-level applications.
7+
[UEFI] is the successor to the BIOS. It provides an early boot environment for
8+
OS loaders, hypervisors and other low-level applications. While it started out
9+
as x86-specific, it has been adopted on other platforms, such as ARM.
910

10-
The objective of this library is to provide **safe** and **performant** wrappers for UEFI
11-
interfaces, and allow developers to write idiomatic Rust code.
11+
This crates makes it easy to write UEFI applications in Rust.
1212

13-
[uefi]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
13+
The objective is to provide **safe** and **performant** wrappers for UEFI interfaces,
14+
and allow developers to write idiomatic Rust code.
1415

15-
<p align="center">
16-
<img width="848px" height="701px" alt="uefi-rs running in QEMU" src="https://imgur.com/SFPSVuO.png"/>
17-
</p>
16+
**Note**: due to some issues with the Rust compiler, this crate currently works
17+
and has been tested _only_ with **64-bit** UEFI.
18+
19+
[UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
20+
21+
![uefi-rs running in QEMU](https://imgur.com/SFPSVuO.png)
1822

1923
## Project structure
2024

2125
This project contains multiple sub-crates:
2226

23-
- `uefi` (top directory): defines the standard UEFI tables / interfaces. The objective is to stay unopionated
24-
and safely wrap most interfaces.
27+
- `uefi` (top directory): defines the standard UEFI tables / interfaces.
28+
The objective is to stay unopionated and safely wrap most interfaces.
2529

26-
- `uefi-services`: initializes many convenience crates:
27-
- `uefi-logger`: wrapper for the standard [logging](https://github.com/rust-lang-nursery/log) crate.
28-
Prints log output to console. No buffering is done: this is not a high-performance logger.
30+
- `uefi-services`: provides a panic handler, and initializes some helper crates:
31+
- `uefi-logger`: logging implementation for the standard [log] crate.
32+
- Prints output to console.
33+
- No buffering is done: this is not a high-performance logger.
2934
- `uefi-alloc`: implements a global allocator using UEFI functions.
30-
This allows you to allocate objects on the heap.
31-
There's no guarantee of the efficiency of UEFI's allocator.
32-
33-
Since the global logger / allocator **can only be set once** per binary, if you're building
34-
a real OS you will want to either:
35-
- provide your own logger / allocator, using _your_ kernel's systems
36-
- use UEFI for writing an OS-specific boot loader binary, while your kernel is a separate binary, packaged
37-
together with the boot loader: similar to what the Linux kernel's [EFI stub] does
35+
- This allows you to allocate objects on the heap.
36+
- There's no guarantee of the efficiency of UEFI's allocator.
37+
38+
- `uefi-exts`: extension traits providing utility functions for common patterns.
39+
- Requires the `alloc` crate (either use `uefi-alloc` or your own custom allocator).
40+
41+
- `uefi-test-runner`: a UEFI application that runs unit / integration tests.
42+
43+
[log]: https://github.com/rust-lang-nursery/log
44+
45+
## Building kernels which use UEFI
46+
47+
This crate makes it easy to start buildimg simple applications with UEFI.
48+
However, there are some limitations you should be aware of:
49+
50+
- The global logger / allocator **can only be set once** per binary.
51+
It is useful when just starting out, but if you're building a real OS you will
52+
want to write your own specific kernel logger and memory allocator.
53+
54+
- To support advanced features such as [higher half kernel] and [linker scripts]
55+
you will want to build your kernel as an ELF binary.
3856

39-
- `uefi-exts`: extends existing UEFI objects by providing utility functions for common API usage.
40-
Requires the `alloc` crate (either use `uefi-alloc` or your own custom allocator).
57+
In other words, the best way to use this crate is to create a small binary which
58+
wraps your actual kernel, and then use UEFI's convenient functions for loading
59+
it from disk and booting it.
4160

42-
- `uefi-test-runner` a UEFI application that runs unit / integration tests.
61+
This is similar to what the Linux kernel's [EFI stub] does: the compressed kernel
62+
is an ELF binary which has little knowledge of how it's booted, and the boot loader
63+
uses UEFI to set up an environment for it.
4364

65+
[higher half kernel]: https://wiki.osdev.org/Higher_Half_Kernel
66+
[linker scripts]: https://sourceware.org/binutils/docs/ld/Scripts.html
4467
[EFI stub]: https://www.kernel.org/doc/Documentation/efi-stub.txt
4568

4669
## Documentation

src/proto/console/gop.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ use crate::{Result, Status};
3333
#[repr(C)]
3434
pub struct GraphicsOutput {
3535
query_mode:
36-
extern "C" fn(&GraphicsOutput, mode: u32, info_sz: &mut usize, &mut *const ModeInfo)
36+
extern "win64" fn(&GraphicsOutput, mode: u32, info_sz: &mut usize, &mut *const ModeInfo)
3737
-> Status,
38-
set_mode: extern "C" fn(&mut GraphicsOutput, mode: u32) -> Status,
38+
set_mode: extern "win64" fn(&mut GraphicsOutput, mode: u32) -> Status,
3939
// Clippy correctly complains that this is too complicated, but we can't change the spec.
4040
#[allow(clippy::type_complexity)]
41-
blt: extern "C" fn(
41+
blt: extern "win64" fn(
4242
this: &mut GraphicsOutput,
4343
buffer: usize,
4444
op: u32,

src/proto/console/pointer/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::{Result, Status};
66
/// Provides information about a pointer device.
77
#[repr(C)]
88
pub struct Pointer {
9-
reset: extern "C" fn(this: &mut Pointer, ext_verif: bool) -> Status,
10-
get_state: extern "C" fn(this: &Pointer, state: &mut PointerState) -> Status,
9+
reset: extern "win64" fn(this: &mut Pointer, ext_verif: bool) -> Status,
10+
get_state: extern "win64" fn(this: &Pointer, state: &mut PointerState) -> Status,
1111
_wait_for_input: usize,
1212
mode: &'static PointerMode,
1313
}

src/proto/console/serial.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ pub struct Serial {
1515
// Revision of this protocol, only 1.0 is currently defined.
1616
// Future versions will be backwards compatible.
1717
revision: u32,
18-
reset: extern "C" fn(&mut Serial) -> Status,
19-
set_attributes: extern "C" fn(
18+
reset: extern "win64" fn(&mut Serial) -> Status,
19+
set_attributes: extern "win64" fn(
2020
&Serial,
2121
baud_rate: u64,
2222
receive_fifo_depth: u32,
@@ -25,10 +25,10 @@ pub struct Serial {
2525
data_bits: u8,
2626
stop_bits_type: StopBits,
2727
) -> Status,
28-
set_control_bits: extern "C" fn(&mut Serial, ControlBits) -> Status,
29-
get_control_bits: extern "C" fn(&Serial, &mut ControlBits) -> Status,
30-
write: extern "C" fn(&mut Serial, &mut usize, *const u8) -> Status,
31-
read: extern "C" fn(&mut Serial, &mut usize, *mut u8) -> Status,
28+
set_control_bits: extern "win64" fn(&mut Serial, ControlBits) -> Status,
29+
get_control_bits: extern "win64" fn(&Serial, &mut ControlBits) -> Status,
30+
write: extern "win64" fn(&mut Serial, &mut usize, *const u8) -> Status,
31+
read: extern "win64" fn(&mut Serial, &mut usize, *mut u8) -> Status,
3232
io_mode: &'static IoMode,
3333
}
3434

src/proto/console/text/input.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use crate::{Result, Status};
44
/// Interface for text-based input devices.
55
#[repr(C)]
66
pub struct Input {
7-
reset: extern "C" fn(this: &mut Input, extended: bool) -> Status,
8-
read_key_stroke: extern "C" fn(this: &mut Input, key: &mut Key) -> Status,
7+
reset: extern "win64" fn(this: &mut Input, extended: bool) -> Status,
8+
read_key_stroke: extern "win64" fn(this: &mut Input, key: &mut Key) -> Status,
99
}
1010

1111
impl Input {

src/proto/console/text/output.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ use crate::{Result, Status};
77
/// standard Rust constructs like the write!() and writeln!() macros.
88
#[repr(C)]
99
pub struct Output {
10-
reset: extern "C" fn(this: &Output, extended: bool) -> Status,
11-
output_string: extern "C" fn(this: &Output, string: *const u16) -> Status,
12-
test_string: extern "C" fn(this: &Output, string: *const u16) -> Status,
13-
query_mode:
14-
extern "C" fn(this: &Output, mode: i32, columns: &mut usize, rows: &mut usize) -> Status,
15-
set_mode: extern "C" fn(this: &mut Output, mode: i32) -> Status,
16-
set_attribute: extern "C" fn(this: &mut Output, attribute: usize) -> Status,
17-
clear_screen: extern "C" fn(this: &mut Output) -> Status,
18-
set_cursor_position: extern "C" fn(this: &mut Output, column: usize, row: usize) -> Status,
19-
enable_cursor: extern "C" fn(this: &mut Output, visible: bool) -> Status,
10+
reset: extern "win64" fn(this: &Output, extended: bool) -> Status,
11+
output_string: extern "win64" fn(this: &Output, string: *const u16) -> Status,
12+
test_string: extern "win64" fn(this: &Output, string: *const u16) -> Status,
13+
query_mode: extern "win64" fn(this: &Output, mode: i32, columns: &mut usize, rows: &mut usize)
14+
-> Status,
15+
set_mode: extern "win64" fn(this: &mut Output, mode: i32) -> Status,
16+
set_attribute: extern "win64" fn(this: &mut Output, attribute: usize) -> Status,
17+
clear_screen: extern "win64" fn(this: &mut Output) -> Status,
18+
set_cursor_position: extern "win64" fn(this: &mut Output, column: usize, row: usize) -> Status,
19+
enable_cursor: extern "win64" fn(this: &mut Output, visible: bool) -> Status,
2020
data: &'static OutputData,
2121
}
2222

src/proto/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
///
66
/// ```rust
77
/// struct CustomProtocol {
8-
/// function_pointer: extern "C" fn() -> (),
8+
/// function_pointer: extern "win64" fn() -> (),
99
/// data: usize
1010
/// }
1111
///

src/proto/media/file.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,22 +152,24 @@ impl<'a> File<'a> {
152152
#[repr(C)]
153153
struct FileImpl {
154154
revision: u64,
155-
open: extern "C" fn(
155+
open: extern "win64" fn(
156156
this: &mut FileImpl,
157157
new_handle: &mut usize,
158158
filename: *const u16,
159159
open_mode: FileMode,
160160
attributes: FileAttribute,
161161
) -> Status,
162-
close: extern "C" fn(this: &mut FileImpl) -> Status,
163-
delete: extern "C" fn(this: &mut FileImpl) -> Status,
164-
read: extern "C" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *mut u8) -> Status,
165-
write: extern "C" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *const u8) -> Status,
166-
get_position: extern "C" fn(this: &mut FileImpl, position: &mut u64) -> Status,
167-
set_position: extern "C" fn(this: &mut FileImpl, position: u64) -> Status,
162+
close: extern "win64" fn(this: &mut FileImpl) -> Status,
163+
delete: extern "win64" fn(this: &mut FileImpl) -> Status,
164+
read:
165+
extern "win64" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *mut u8) -> Status,
166+
write: extern "win64" fn(this: &mut FileImpl, buffer_size: &mut usize, buffer: *const u8)
167+
-> Status,
168+
get_position: extern "win64" fn(this: &mut FileImpl, position: &mut u64) -> Status,
169+
set_position: extern "win64" fn(this: &mut FileImpl, position: u64) -> Status,
168170
get_info: usize,
169171
set_info: usize,
170-
flush: extern "C" fn(this: &mut FileImpl) -> Status,
172+
flush: extern "win64" fn(this: &mut FileImpl) -> Status,
171173
}
172174

173175
bitflags! {

src/proto/media/file_system.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::file::File;
99
#[repr(C)]
1010
pub struct SimpleFileSystem {
1111
revision: u64,
12-
open_volume: extern "C" fn(this: &mut SimpleFileSystem, root: &mut usize) -> Status,
12+
open_volume: extern "win64" fn(this: &mut SimpleFileSystem, root: &mut usize) -> Status,
1313
}
1414

1515
impl SimpleFileSystem {

0 commit comments

Comments
 (0)