docs: Add book

This is a first pass at adding a uefi-rs book. It is not a replacement
for the reference documentation on docs.rs, but more of a companion.

See [book/README.md] for a quick intro on how to build the book locally.

https://github.com/rust-osdev/uefi-rs/issues/459
This commit is contained in:
Nicholas Bishop
2022-07-03 20:31:42 -04:00
parent 47b7e58244
commit 3455e11330
21 changed files with 660 additions and 0 deletions
+3
View File
@@ -3,3 +3,6 @@
# Libraries should not commit their .lock files.
Cargo.lock
# Files generated by mdBook.
/book/book/
+14
View File
@@ -0,0 +1,14 @@
# UEFI Book
To build the book locally, first install [mdBook].
Next, launch a server that will automatically update as changes are made
to the markdown sources:
```console
mdbook serve book/
```
Then open <http://localhost:3000> in a web browser.
[mdBook]: https://rust-lang.github.io/mdBook/guide/installation.html
+5
View File
@@ -0,0 +1,5 @@
[book]
language = "en"
multilingual = false
src = "src"
title = "Rust UEFI Book"
+8
View File
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Redirecting to latest documentation</title>
<meta http-equiv="refresh" content="5; URL=HEAD">
</head>
Redirecting to <a href="HEAD">./HEAD</a>...
+18
View File
@@ -0,0 +1,18 @@
# Summary
- [Introduction](introduction.md)
- [Tutorial](tutorial/introduction.md)
- [Creating a UEFI Application](tutorial/app.md)
- [Building](tutorial/building.md)
- [Running in a VM](tutorial/vm.md)
- [How-to](how_to/introduction.md)
- [Using Protocols](how_to/protocols.md)
- [Concepts](concepts/introduction.md)
- [Boot Stages](concepts/boot_stages.md)
- [Tables](concepts/tables.md)
- [GUID](concepts/guid.md)
- [Handles and Protocols](concepts/handles_and_protocols.md)
- [Device Paths](concepts/device_paths.md)
- [Variables](concepts/variables.md)
- [GPT](concepts/gpt.md)
- [Reference](reference.md)
+24
View File
@@ -0,0 +1,24 @@
# Boot Stages
A UEFI system goes through several distinct phases during the boot process.
1. **Platform Initialization.** This early-boot phase is mostly outside
the scope of `uefi-rs`. It is described by the [UEFI Platform
Initialization Specification], which is separate from the main UEFI
Specification.
2. **Boot Services.** This is when UEFI drivers and applications are
loaded. Both the [`BootServices`] and [`RuntimeServices`] tables are
accessible. This stage typically culminates in running a bootloader
that loads an operating system. The stage ends when
[`SystemTable::exit_boot_services`] is called, putting the system in
Runtime mode.
3. **Runtime.** This stage is typically active when running an operating
system such as Linux or Windows. UEFI functionality is much more
limited in the Runtime mode. The [`BootServices`] table is no longer
accessible, but the [`RuntimeServices`] table is still
available. Once the system is in Runtime mode, it cannot return to
the Boot Services stage until after a system reset.
[UEFI Platform Initialization Specification]: https://uefi.org/specifications
[`BootServices`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html
[`RuntimeServices`]: https://docs.rs/uefi/latest/uefi/table/runtime/struct.RuntimeServices.html
[`SystemTable::exit_boot_services`]: https://docs.rs/uefi/latest/uefi/table/struct.SystemTable.html#method.exit_boot_services
+24
View File
@@ -0,0 +1,24 @@
# Device Paths
A device path is a very flexible packed data structure for storing paths
to many kinds of device. Note that these device paths are not the same
thing as file system paths, although they can include file system
paths. Like [handles], device paths can be used to uniquely identify
resources such as consoles, mice, disks, partitions, and more. Unlike
[handles], which are essentially opaque pointers, device paths are
variable-length structures that contain parseable information.
The [`uefi::proto::device_path`] module documentation describes the
details of how device paths are encoded.
Device paths can also be converted to and from human-readable text
representations that look like this:
```text
PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
```
See [`uefi::proto::device_path::text`] for details.
[handles]: handles.md
[`uefi::proto::device_path`]: https://docs.rs/uefi/latest/uefi/proto/device_path/index.html
[`uefi::proto::device_path::text`]: https://docs.rs/uefi/latest/uefi/proto/device_path/text/index.html
+47
View File
@@ -0,0 +1,47 @@
# GPT
[GPT] is short for [GUID] Partition Table. It's a more modern
alternative to MBR (master boot record) partition tables. Although it's
defined in the UEFI specification, it often gets used on non-UEFI
systems too. There are a couple big advantages of using GPT over MBR:
- It has a relatively clear and precise standard, unlike MBR where
implementations often just try to match what other implementations do.
- It supports very large disks and very large numbers of partitions.
A GPT disk contains a primary header near the beginning of the disk,
followed by a partition entry array. The header and partition entry
array have a secondary copy at the end of the disk for redundency. The
partition entry arrays contain structures that describe each partition,
including a GUID to identify the individual partition, a partition type
GUID to indicate the purpose of the partition, and start/end block
addresses. In between the entry arrays is the actual partition data.
## System partition
The system partition is UEFI's version of a bootable partition. The
system partition is sometimes called the ESP, or EFI System
Partition. It is identified by a partition type of
`c12a7328-f81f-11d2-ba4b-00a0c93ec93b`. The system partition always
contains a FAT file system. There are various standardized paths that
can exist within the file system, and of particular importance are the
boot files. These are the files that UEFI will try to boot from by
default (in the absence of a different boot configuration set through
special [UEFI variables]).
Boot files are under `\EFI\BOOT`, and are named `BOOT<ARCH>.efi`, where
`<ARCH>` is a short architecture name.
|Architecture |File name |
|--------------|----------------|
|Intel 32-bit |BOOTIA32.EFI |
|X86_64 |BOOTX64.EFI |
|Itanium |BOOTIA64.EFI |
|AArch32 |BOOTARM.EFI |
|AArch64 |BOOTAA64.EFI |
|RISC-V 32-bit |BOOTRISCV32.EFI |
|RISC-V 64-bit |BOOTRISCV64.EFI |
|RISC-V 128-bit|BOOTRISCV128.EFI|
[GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table
[GUID]: guid.md
[UEFI variables]: variables.md
+15
View File
@@ -0,0 +1,15 @@
# GUID
GUID is short for Globally Unique Identifier. A GUID is always 16 bytes,
and has a standard string representation format that looks like this:
`313b0d7c-fed4-4de7-99ed-2fe48874a410`. The details of the GUID format
aren't too important, but be aware that the actual byte representation
is not in the same order as the string representation because the first
three fields are little-endian. For the most part you can treat GUIDs as
opaque identifiers.
The UEFI specification uses GUIDs all over the place. GUIDs are used to
identify protocols, disk partitions, variable groupings, and much
more. In `uefi-rs`, GUIDs are represented by the [`Guid`] type.
[`Guid`]: https://docs.rs/uefi/latest/uefi/struct.Guid.html
@@ -0,0 +1,40 @@
# Handles and Protocols
Handles and protocols are at the core of what makes UEFI
extensible. Together they are the mechanism by which UEFI can adapt to a
wide array of hardware and boot conditions, while still providing a
consistent interface to drivers and applications.
### Handles
Handles represent resources. A resource might be a physical device such
as a disk drive or USB device, or something less tangible like a loaded
executable.
A [Handle] is an opaque pointer, so you can't do anything with it
directly. To operate on a handle you have to open a protocol.
### Protocols
Protocols are interfaces that provide functions to interact with a
resource. For example, the [BlockIO] protocol provides functions to read
and write to block IO devices.
Protocols are only available during the Boot Services [stage]; you can't
access them during the Runtime stage.
The UEFI Specification defines a very large number of protocols. Because
protocols are inherently very diverse, the best place to learn about
individual protocols is the [UEFI Specification]. There are many
chapters covering various protocols. Not all of these protocols are
wrapped by `uefi-rs` yet (contributions welcome!) but many of the most
commonly useful ones are.
See the [Using Protocols] how-to for details of the `uefi-rs` API for
interacting with protocols.
[UEFI Specification]: https://uefi.org/specifications
[stage]: boot_stages.md
[Handle]: https://docs.rs/uefi/latest/uefi/data_types/struct.Handle.html
[BlockIO]: https://docs.rs/uefi/latest/uefi/proto/media/block/struct.BlockIO.html
[Using Protocols]: ../how_to/protocols.md
+10
View File
@@ -0,0 +1,10 @@
# Concepts
The canonical source of information about UEFI is the [UEFI specification].
The specification is huge (currently nearly 2500 pages). Much of that
content relates to optional services, understanding of which is not
critical to understanding UEFI as a whole. This chapter summarizes some
of the more important UEFI concepts and links to the relevant `uefi-rs`
documentation.
[UEFI specification]: https://uefi.org/specifications
+37
View File
@@ -0,0 +1,37 @@
# Tables
UEFI has a few table structures. These tables are how you get access to
UEFI services.
[`SystemTable`] (`EFI_SYSTEM_TABLE` in the specification) is the
top-level table that provides access to the other tables.
[`BootServices`] (`EFI_BOOT_SERVICES` in the specification) provides
access to a wide array of services such as memory allocation, executable
loading, and optional extension interfaces called protocols. This table
is only accessible while in the Boot Services stage.
[`RuntimeServices`] (`EFI_RUNTIME_SERVICES` in the specification)
provides access to a fairly limited set of services, including variable
storage, system time, and virtual-memory mapping. This table is
accessible during both the Boot Services and Runtime stages.
When writing a UEFI application, you get access to the system table from
one of the arguments to the `main` entry point:
```rust,ignore
fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status;
```
Then use [`SystemTable::boot_services`] and
[`SystemTable::runtime_services`] to get access to the other
tables. Once [`SystemTable::exit_boot_services`] is called, the original
system table is consumed and a new system table is returned that only
provides access to the [`RuntimeServices`] table.
[`BootServices`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html
[`RuntimeServices`]: https://docs.rs/uefi/latest/uefi/table/runtime/struct.RuntimeServices.html
[`SystemTable::boot_services`]: https://docs.rs/uefi/latest/uefi/table/struct.SystemTable.html#method.boot_services
[`SystemTable::exit_boot_services`]: https://docs.rs/uefi/latest/uefi/table/struct.SystemTable.html#method.exit_boot_services
[`SystemTable::runtime_services`]: https://docs.rs/uefi/latest/uefi/table/struct.SystemTable.html#method.runtime_services
[`SystemTable`]: https://docs.rs/uefi/latest/uefi/table/struct.SystemTable.html
+39
View File
@@ -0,0 +1,39 @@
# Variables
UEFI provides fairly flexible key/value variable storage.
Each variable is identified by a key consisting of a UCS-2
null-terminated name plus a vendor [GUID]. The vendor GUID serves as a
namespace for variables so that different vendors don't accidentally
overwrite or misinterpret another vendor's variable if they happen to
have the same name.
The data stored in each variable is an arbitrary byte array.
## Attributes
Each variable has attributes (represented as bit flags) associated with
it that affect how it is stored and how it can be accessed.
If the `BOOTSERVICE_ACCESS` and `RUNTIME_ACCESS` bits are set, the
variable can be accessed during both the Boot Services and Runtime
[stages]. If only `BOOTSERVICE_ACCESS` is set then the variable can
neither be read nor written to after exiting boot services.
Another important attribute is the `NON_VOLATILE` bit. If this bit is
_not_ set, the variable will be stored in normal memory and will not
persist across a power cycle. If this bit _is_ set, the variable will be
stored in special non-volatile memory. You should be careful about
writing variables of this type, because the non-volatile storage can be
very limited in size. There have been cases where a vendor's poor UEFI
implementation caused the machine not too boot once the storage became
too full. Even figuring out how much space is in use can be tricky due
to deletion being implemented via garbage collection. Matthew Garret's
article ["Dealing with UEFI non-volatile memory quirks"] has more details.
Most of the other attributes relate to authenticated variables, which
can be used to prevent changes to a variable by unauthorized programs.
[GUID]: guid.md
[stages]: boot_stages.md
["Dealing with UEFI non-volatile memory quirks"]: https://mjg59.dreamwidth.org/25091.html
+3
View File
@@ -0,0 +1,3 @@
# How-to
This chapter contains practical how-to guides.
+122
View File
@@ -0,0 +1,122 @@
# Using Protocols
The open a protocol, you must first get a handle, then open a protocol
on that handle. See [Handles and Protocols] for an overview of what
these terms mean.
To get a handle you can use:
* [`BootServices::locate_handle_buffer`]: this can be used to get _all_
available handles, or just the handles that support a particular
protocol.
* [`BootServices::locate_handle`]: the same as `locate_handle_buffer`,
but you provide the slice that stores the handles.
* [`BootServices::locate_device_path`]: find a handle by [Device Path].
Once you have obtained a handle, use
[`BootServices::open_protocol_exclusive`] to open a protocol on that
handle. This returns a [`ScopedProtocol`], which automatically closes
the protocol when dropped.
Using [`BootServices::open_protocol_exclusive`] is the safest way to
open a protocol, but in some cases a protocol cannot be opened in
exclusive mode. The `unsafe` [`BootServices::open_protocol`] can be used
in that case.
## Example
For this example we'll look at a program that opens a couple different
protocols. This program opens the [`LoadedImage`] protocol to get
information about an executable (the currently-running program in this
case). It also opens the [`DevicePathToText`] protocol to get the file
system path that the program was launched from.
We'll walk through the details of this program shortly, but first here's
the whole thing:
```rust
{{#include ../../../uefi-test-runner/examples/loaded_image.rs:all}}
```
When the program is run it will print something like this:
```text
[ INFO]: example.rs@058: Image path: \EFI\BOOT\BOOTX64.EFI
```
## Walkthrough
The `main` function looks much like the ["Hello world!" example]. It
sets up logging, calls `print_image_path`, and pauses for ten seconds to
give you time to read the output. Let's look at `print_image_path`:
```rust
{{#include ../../../uefi-test-runner/examples/loaded_image.rs:print_image_path}}
```
The return type is a [`uefi::Result`], which is a `Result` alias that
combines [`uefi::Status`] with the error data. Both the success and
error data types are `()` by default.
The function starts by opening the [`LoadedImage`] protocol:
```rust
{{#include ../../../uefi-test-runner/examples/loaded_image.rs:loaded_image}}
```
The [`open_protocol_exclusive`] method takes a type parameter, which is
the type of [`Protocol`] you want to open ([`LoadedImage`] in this
case). It also takes one regular argument of type [`Handle`]. For this
example we want the handle of the currently-running image, which was
passed in as the first argument to `main`. The handle is conveniently
accessible through [`BootServices::image_handle`], so we use that here.
Next the program opens the [`DevicePathToText`] protocol:
```rust
{{#include ../../../uefi-test-runner/examples/loaded_image.rs:device_path}}
```
This protocol isn't available for the `image_handle`, so we start by
using [`locate_handle_buffer`] to find all handles that support
`DevicePathToText`. We only need one handle though, so we call `first()`
and discard the rest. Then we call [`open_protocol_exclusive`] again. It
looks more or less like the previous time, but with [`DevicePathToText`]
as the type parameter and `device_path_to_text_handle` as the handle.
Now that we have both protocols open, we can use them together to get
the program's path and convert it to text:
```rust
{{#include ../../../uefi-test-runner/examples/loaded_image.rs:text}}
```
Since protocols do a wide range of different things, the methods
available to call are very specific to each individual protocol. The
best places to find out what each protocol can do are the [uefi-rs
reference documentation] and the [UEFI Specification].
[Device Path]: ../concepts/device_paths.md
[Handles and Protocols]: ../concepts/handles_and_protocols.md
[UEFI Specification]: https://uefi.org/specifications
[`BootServices::image_handle`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.image_handle
[`BootServices::locate_device_path`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.locate_device_path
[`BootServices::locate_handle_buffer`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.locate_handle_buffer
[`BootServices::locate_handle`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.locate_handle
[`BootServices::open_protocol`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.open_protocol
[`BootServices::open_protocol_exclusive`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.open_protocol_exclusive
[`BootServices`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html
[`DevicePathToText`]: https://docs.rs/uefi/latest/uefi/proto/device_path/text/struct.DevicePathToText.html
["Hello world!" example]: ../tutorial/app.html
[`Handle`]: https://docs.rs/uefi/latest/uefi/data_types/struct.Handle.html
[`LoadedImage`]: https://docs.rs/uefi/latest/uefi/proto/loaded_image/struct.LoadedImage.html
[`OpenProtocolAttributes::Exclusive`]: https://docs.rs/uefi/latest/uefi/table/boot/enum.OpenProtocolAttributes.html#variant.Exclusive
[`OpenProtocolAttributes`]: https://docs.rs/uefi/latest/uefi/table/boot/enum.OpenProtocolAttributes.html
[`OpenProtocolParams`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.OpenProtocolParams.html
[`Protocol`]: https://docs.rs/uefi/latest/uefi/proto/trait.Protocol.html
[`ScopedProtocol`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.ScopedProtocol.html
[`locate_handle_buffer`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.locate_handle_buffer
[`open_protocol`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.open_protocol
[`open_protocol_exclusive`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.open_protocol_exclusive
[uefi-rs reference documentation]: https://docs.rs/uefi/latest/uefi/proto/index.html
[`uefi::Result`]: https://docs.rs/uefi/latest/uefi/type.Result.html
[`uefi::Status`]: https://docs.rs/uefi/latest/uefi/struct.Status.html
+7
View File
@@ -0,0 +1,7 @@
# Introduction
Welcome to the Rust UEFI Book. The focus of this book is how to use
[`uefi-rs`] to build UEFI applications in Rust, but it also describes
some general UEFI concepts, as well as relevant tools such as QEMU.
[`uefi-rs`]: https://github.com/rust-osdev/uefi-rs
+6
View File
@@ -0,0 +1,6 @@
# Reference
* [`uefi` crate reference](https://docs.rs/uefi)
* [`uefi-macros` crate reference](https://docs.rs/uefi-macros)
* [`uefi-services` crate reference](https://docs.rs/uefi-services)
* [UEFI Specifications](https://uefi.org/specifications)
+99
View File
@@ -0,0 +1,99 @@
# Creating a UEFI application
## Install dependencies
Follow the [Rust installation instructions] to set up Rust.
## Create a minimal application
Create an empty application and change to that directory:
```sh
cargo new my-uefi-app
cd my-uefi-app
```
In `cargo.toml`, add a few dependencies:
```toml
[dependencies]
log = "0.4"
uefi = "0.17"
uefi-services = "0.14"
```
Replace the contents of `src/main.rs` with this:
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:all}}
```
## Walkthrough
Let's look a quick look at what each part of the program is doing,
starting with the `#![...]` lines at the top:
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:features}}
```
This is some boilerplate that all Rust UEFI applications will
need. `no_main` is needed because the UEFI application entry point is
different from the standard Rust `main` function. `no_std` is needed to
turn off the `std` library; the `core` and `alloc` crates can still be
used. And `feature(abi_efiapi)` is needed because UEFI applications have
a special calling convention that is not yet stabilized in the Rust
compiler.
Next up are some `use` lines. Nothing too exciting here; the
`uefi::prelude` module is intended to be glob-imported, and exports a
number of commonly-used types.
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:use}}
```
Now we get to the UEFI application `main` function, and here things look
a little different from a standard Rust program.
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:entry}}
```
The `main` function in a Uefi application always takes two arguments,
the image handle and the system table. The image [handle] represents the
currently-running executable, and the system [table] provides access to
many different UEFI services. The `main` function returns a [`Status`],
which is essentially a numeric error (or success) code defined by UEFI.
The first thing we do inside of `main` is initialize `uefi_services`:
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:services}}
```
The `uefi_services` crate is not strictly required to make a UEFI
application with the `uefi` crate, but it makes things much simpler by
setting a simple memory allocator, initializing the logger, and
providing a panic handler.
Next we use the standard `log` crate to output "Hello world!". Then we
call `stall` to make the system pause for 10 seconds. This just ensures
you have enough time to see the output.
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:log}}
```
Finally we return `Status::SUCCESS` indicating that everything completed
successfully:
```rust
{{#include ../../../uefi-test-runner/examples/hello_world.rs:return}}
```
[Rust installation instructions]: https://www.rust-lang.org/tools/install
[`Status`]: https://docs.rs/uefi/latest/uefi/struct.Status.html
[`log`]: https://crates.io/crates/log
[handle]: ../concepts/handles_and_protocols.md
[table]: ../concepts/tables.md
+64
View File
@@ -0,0 +1,64 @@
# Building
## Nightly toolchain
Rust's nightly toolchain is currently required because uefi-rs uses some
unstable features. The [`build-std`] feature we use to build the
standard libraries is also unstable.
The easiest way to set this up is using a [rustup toolchain file]. In
the root of your repository, add `rust-toolchain.toml`:
```toml
[toolchain]
channel = "nightly"
components = ["rust-src"]
```
Note that nightly releases can sometimes break, so you might opt to pin
to a specific release. For example, `channel = "nightly-2022-09-01"`.
## Build the application
Run this command to build the application:
```sh
cargo build --target x86_64-unknown-uefi \
-Zbuild-std=core,compiler_builtins,alloc \
-Zbuild-std-features=compiler-builtins-mem
```
This will produce an x86-64 executable:
`target/x86_64-unknown-uefi/debug/my-uefi-app.efi`.
## Simplifying the build command
The above build command is verbose and not easy to remember. With a bit
of configuration we can simplify it a lot.
Create a `.cargo` directory in the root of the project:
```sh
mkdir .cargo
```
Create `.cargo/config.toml` with these contents:
```toml
[build]
target = "x86_64-unknown-uefi"
[unstable]
build-std = ["core", "compiler_builtins", "alloc"]
build-std-features = ["compiler-builtins-mem"]
```
Now you can build much more simply:
```sh
cargo build
```
[`build-std`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
[`rust-toolchain.toml`]: https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file
[rustup toolchain file]: https://rust-lang.github.io/rustup/concepts/toolchains.html
+5
View File
@@ -0,0 +1,5 @@
# Tutorial
This tutorial describes the process of creating and running a simple
x86_64 UEFI application in Rust. The application will print "Hello
World", pause for 10 seconds, then exit.
+70
View File
@@ -0,0 +1,70 @@
# Running in a VM
## Install dependencies
Two dependencies are needed: [QEMU], which implements the virtual
machine itself, and [OVMF], which provides UEFI firmware that QEMU can
run.
The details of how to install QEMU and OVMF will vary depending on your
operating system.
Debian/Ubuntu:
```sh
sudo apt-get install qemu ovmf
```
Fedora:
```sh
sudo dnf install qemu-kvm edk2-ovmf
```
### Firmware files
The OVMF package provides two firmware files, one for the executable
code and one for variable storage. (The package may provide multiple
variations of these files; refer to the package's documentation for
details of the files it includes.)
For ease of access we'll copy the OVMF code and vars files to the
project directory. The location where OVMF is installed depends on your
operating system; for Debian, Ubuntu and Fedora the files are under
`/usr/share/OVMF`.
Copy the files to your project directory:
```sh
cp /usr/share/OVMF/OVMF_CODE.fd .
cp /usr/share/OVMF/OVMF_VARS.fd .
```
## System partition
Now create a directory structure containing the executable to imitate a
[UEFI System Partition]:
```sh
mkdir -p esp/efi/boot
cp target/x86_64-unknown-uefi/debug/my-uefi-app.efi esp/efi/boot/bootx64.efi
```
## Launch the VM
Now we can launch QEMU, using [VVFAT] to access the `esp` directory created above.
```sh
qemu-system-x86_64 -enable-kvm \
-drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.fd \
-drive if=pflash,format=raw,readonly=on,file=OVMF_VARS.fd \
-drive format=raw,file=fat:rw:esp
```
A QEMU window should appear, and after a few seconds you should see the
log message:
```text
[ INFO]: src/main.rs@011: Hello world!
```
[QEMU]: https://www.qemu.org
[OVMF]: https://github.com/tianocore/tianocore.github.io/wiki/OVMF
[VVFAT]: https://en.m.wikibooks.org/wiki/QEMU/Devices/Storage#Virtual_FAT_filesystem_(VVFAT)
[UEFI System Partition]: ../concepts/gpt.md#system-partition