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 {

src/table/boot.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@ pub struct BootServices {
1212
header: Header,
1313

1414
// Task Priority services
15-
raise_tpl: extern "C" fn(Tpl) -> Tpl,
16-
restore_tpl: extern "C" fn(Tpl),
15+
raise_tpl: extern "win64" fn(Tpl) -> Tpl,
16+
restore_tpl: extern "win64" fn(Tpl),
1717

1818
// Memory allocation functions
1919
allocate_pages:
20-
extern "C" fn(alloc_ty: u32, mem_ty: MemoryType, count: usize, addr: &mut u64) -> Status,
21-
free_pages: extern "C" fn(u64, usize) -> Status,
20+
extern "win64" fn(alloc_ty: u32, mem_ty: MemoryType, count: usize, addr: &mut u64)
21+
-> Status,
22+
free_pages: extern "win64" fn(u64, usize) -> Status,
2223
memory_map:
23-
extern "C" fn(size: &mut usize, usize, key: &mut MemoryMapKey, &mut usize, &mut u32)
24+
extern "win64" fn(size: &mut usize, usize, key: &mut MemoryMapKey, &mut usize, &mut u32)
2425
-> Status,
25-
allocate_pool: extern "C" fn(MemoryType, usize, addr: &mut usize) -> Status,
26-
free_pool: extern "C" fn(buffer: usize) -> Status,
26+
allocate_pool: extern "win64" fn(MemoryType, usize, addr: &mut usize) -> Status,
27+
free_pool: extern "win64" fn(buffer: usize) -> Status,
2728

2829
// Event & timer functions
2930
create_event: usize,
@@ -38,10 +39,10 @@ pub struct BootServices {
3839
reinstall_protocol_interface: usize,
3940
uninstall_protocol_interface: usize,
4041
handle_protocol:
41-
extern "C" fn(handle: Handle, proto: *const Guid, out_proto: &mut usize) -> Status,
42+
extern "win64" fn(handle: Handle, proto: *const Guid, out_proto: &mut usize) -> Status,
4243
_reserved: usize,
4344
register_protocol_notify: usize,
44-
locate_handle: extern "C" fn(
45+
locate_handle: extern "win64" fn(
4546
search_ty: i32,
4647
proto: *const Guid,
4748
key: *mut (),
@@ -56,12 +57,12 @@ pub struct BootServices {
5657
start_image: usize,
5758
exit: usize,
5859
unload_image: usize,
59-
exit_boot_services: extern "C" fn(Handle, MemoryMapKey) -> Status,
60+
exit_boot_services: extern "win64" fn(Handle, MemoryMapKey) -> Status,
6061

6162
// Misc services
6263
get_next_monotonic_count: usize,
63-
stall: extern "C" fn(usize) -> Status,
64-
set_watchdog_timer: extern "C" fn(
64+
stall: extern "win64" fn(usize) -> Status,
65+
set_watchdog_timer: extern "win64" fn(
6566
timeout: usize,
6667
watchdog_code: u64,
6768
data_size: usize,
@@ -88,8 +89,8 @@ pub struct BootServices {
8889
calculate_crc32: usize,
8990

9091
// Misc services
91-
copy_mem: extern "C" fn(dest: *mut u8, src: *const u8, len: usize),
92-
set_mem: extern "C" fn(buffer: *mut u8, len: usize, value: u8),
92+
copy_mem: extern "win64" fn(dest: *mut u8, src: *const u8, len: usize),
93+
set_mem: extern "win64" fn(buffer: *mut u8, len: usize, value: u8),
9394

9495
// New event functions (UEFI 2.0 or newer)
9596
create_event_ex: usize,

src/table/runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub struct RuntimeServices {
1313
header: Header,
1414
// Skip some useless functions.
1515
_pad: [usize; 10],
16-
reset: extern "C" fn(u32, Status, usize, *const u8) -> !,
16+
reset: extern "win64" fn(u32, Status, usize, *const u8) -> !,
1717
}
1818

1919
impl RuntimeServices {

uefi-test-runner/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod boot;
1717
mod proto;
1818

1919
#[no_mangle]
20-
pub extern "C" fn uefi_start(_handle: uefi::Handle, st: &'static SystemTable) -> Status {
20+
pub extern "win64" fn uefi_start(_handle: uefi::Handle, st: &'static SystemTable) -> Status {
2121
// Initialize logging.
2222
uefi_services::init(st);
2323

0 commit comments

Comments
 (0)