This repository demonstrates how to integrate Patina into a UEFI platform build.
This repository is a permanent fork of mu_tiano_platforms which is itself a permanent fork of the OvmfPkg in edk2. The reason for a permanent fork is to allow this repository to be independently used within the ownership of the Patina project and, in doing so, optimized for Rust UEFI development.
This repository is meant to be a "first stop" for developers exploring Patina in practice - how it would be integrated in a UEFI platform. Depending on your background, you may find the following entry points to Patina useful:
- All:
- Interested in the Patina DXE Core?
- Developers:
As Rust is integrated into more production platforms, it is important to understand the options available and where this repository fits in. Recognizing that UEFI inherently supports dynamic integration, at the highest level, there are two approaches for writing UEFI firmware in Rust:
- Build the code using Rust tools in a Pure Rust workspace against the UEFI target triple and integrate the resulting
.efi binary into the platform firmware build process.
- This is the primary approach taken in this repository.
- Support for building Rust source code in the firmware build process. If using the EDK II build process, this means compiling Rust source code alongside the C source code when building code specified in a DSC file.
We generally recommend (1) as the preferred approach for the following reasons:
- It allows for a more natural Rust development experience directly using Rust tools and processes for Rust code.
- It greatly simplifies the firmware build process as it does not need to be enlightened for building Rust code.
This repository integrates Patina code using (1) and demonstrates how (2) can be used for Rust code external to Patina to provide a complete example of how to integrate Rust into a UEFI platform build.
In the diagram below, (1) is ".efi binary" and (2) is either of the other blocks in the diagram depending on which method is selected.
Ease of integration decreases when not directly using a .efi binary because the EDK II build process must be updated to support build rules for Rust code. The coupling of the EDK II build system with the Rust/Cargo build system introduces additional complexity alongside EDK II workspace requirements causing friction with Rust workspace conventions. EDK II customizations such as PCDs are not natively supported in Rust. Conversely, Rust feature flags are not supported in the EDK II build process. This further increases integration complexity as consumers must understand multiple types of customization and how they may cooperate with each other. When Pure Rust code is built into a .efi binary in (1), that best ensures consumers they are using a "Rust implementation". It is possible that Rust build may have had C code linked into the binary with a FFI, but that is not a practice in Patina.
Since (1) is the preferred approach, we will focus on that in this document. However, it is important to note that (2) is also a valid approach and can be used in conjunction with (1). More details on (2) are provided in the collapsed section below if you are interested in understanding more about that approach.
Click to expand for more details on (2)
As of today, [tianocore/edk2](https://github.com/tianocore/edk2) does not support **(2)**. The [Patina](https://github.com/OpenDevicePartnership/patina?tab=readme-ov-file#patina) open-source project does.(2) is particularly useful for linking Rust code with C code in a given module. However, several combinations are possible with today's support:
- C source + Rust source mixed in INF (Library or Module)
- Rust source code is recognized and supported by an EDK II build rule β Rust-To-Lib-File (.rs => .lib)
- Pure Rust Module only.
- A Cargo.toml file is added to INF file as source.
- Rust Module build is supported by EDK II build rule β Toml-File.RUST_MODULE (.toml => .efi)
- Pure Rust Module + Pure Rust Library with Cargo Dependency.
- The cargo dependency means the rust lib dependency declared in Cargo.toml.
- Pure Rust Module + C Library with EDK II Dependency.
- Rust Module build is supported by EDK II build rule β Toml-File (.toml => .lib)
- The EDK II dependency means the EDK II lib dependency declared in INF.
- If a rust module is built with C, the cargo must use
staticlib. Or,rlibshould be used.
- If a rust module is built with C, the cargo must use
- C Module + Pure Rust Library with EDK II Dependency.
- Rust Lib build is supported by EDK II build rule β Toml-File. (.toml => .lib)
- Pure Rust Module + Pure Rust Library with EDK II Dependency.
- Same as combining others from above.
After experimenting with (2), we have found that while it is how most projects initially consider integrating Rust into their codebase but in practice the integration complexity is high due to the ability to cointegrate the Rust/Carg build system with the EDK II build system and it naturally leads to Rust source code being maintained in the C codebase which is not ideal due to language and tooling differences.
There are two platforms currently supported in this repository - QemuQ35Pkg and QemuSbsaPkg.
-
- Intel Q35 chipset with ICH9 south bridge and AMD Cpu
- This demonstrates x86/x64 UEFI firmware development with Patina.
- QemuQ35Pkg Detailed Info
-
- ARM Server Base System Architecture
- This demonstrates AARCH64 UEFI firmware development with Patina.
- QemuSbsaPkg Detailed Info
Supported Host Platforms and Target Architectures
Host Platform Target Architectures Supported Windows x64, AArch64 WSL x64, AArch64 Linux x64, AArch64
If this is your first time using this repository and you're not familiar with the build process, it is recommended
that you start with QemuQ35Pkg and use the workspace setup wizard to get started. You will need to install Python
(Python downloads), and then run the following command to start the setup
process:
Linux:
>python workspace_setup.pyWindows:
>py -3 workspace_setup.pyUsing this script, you will be guided through the process of setting up the workspace and installing the required dependencies. The manual steps are described below.
This is a more detailed, step-by-step manual setup process.
One-time tools and packages required to set up Patina development.
πͺ Windows 11 - 24H2
| Tool | Install Command |
|---|---|
| Chocolatey | winget install --id Chocolatey.Chocolatey -e |
| Python 3 | winget install --id Python.Python.3.12 -e Note: Disable any app execution alias defined for python.exe and python3.exe from Windows settings(Apps > Advanced app settings > App execution alias) |
| Git | winget install --id Git.Git -e |
| Rust | winget install --id Rustlang.Rustup -e
|
| LLVM | winget install --id LLVM.LLVM -e --override "/S /D=C:\LLVM"
|
| GNU Make | choco install make
|
| MSVC BuildTools | winget install --id Microsoft.VisualStudio.2022.BuildTools -e --override "--quiet --wait --norestart --add Microsoft.VisualStudio.Component.VC.CoreBuildTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.22621 --add Microsoft.VisualStudio.Component.VC.Tools.ARM --add Microsoft.VisualStudio.Component.VC.Tools.ARM64" Note: Only required when building for std |
| Node | winget install --id OpenJS.NodeJS.LTS -e
|
| QEMU | winget install --id SoftwareFreedomConservancy.QEMU -e -v 10.0.0 |
| Optional | |
| WinDBG | winget install --id Microsoft.WinDbg -e |
| VSCode | winget install --id Microsoft.VisualStudioCode -e |
| Rust Analyzer | code --install-extension rust-lang.rust-analyzer |
Note: Add the LLVM bin directory (C:\LLVM\bin) and the QEMU bin directory
(C:\Program Files\qemu) to the PATH environment variable.
π§ Linux/WSL - Ubuntu 24.04 LTS - Bash
| Tool | Install Command |
|---|---|
| Build Essentials | sudo apt update && sudo apt install -y build-essential git nasm m4 bison flex curl wget uuid-dev python3 python3-venv python-is-python3 unzip acpica-tools gcc-multilib mono-complete pkg-config libssl-dev mtools dosfstools device-tree-compiler |
| Rust | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh Note: Might have to reopen the terminal
|
| Node | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrcnvm install --lts
|
| QEMU | sudo apt install -y qemu-system |
| LLVM | sudo apt install -y clang llvm lld |
| Optional | |
| VSCode | wget https://go.microsoft.com/fwlink/?LinkID=760868 -O code.deb sudo apt install ./code.deb |
| Rust Analyzer | code --install-extension rust-lang.rust-analyzer |
| Repo | Clone | About |
|---|---|---|
| patina | git clone https://github.com/OpenDevicePartnership/patina |
Patina Firmware. Contains all crates published to crates.io |
| patina-qemu | git clone https://github.com/OpenDevicePartnership/patina-qemu |
Repository to produce Patina firmware image for QEMU |
| patina-dxe-core-qemu | git clone https://github.com/OpenDevicePartnership/patina-dxe-core-qemu |
Repository to produce Patina DXE Core Binary for QEMU |
Note: Prefer short paths on Windows(C:\r\) or Linux(/home/<username>/r/)
π₯οΈ X64 Target
| Repo | Build Instructions |
|---|---|
| patina-dxe-core-qemu | cd <patina-dxe-core-qemu> Build dxe core efi binary: cargo make q35 |
| patina-qemu | cd <patina-qemu> Setup and Activate Virtual Env: python -m venv q35env πͺ q35env\Scripts\activate.bat π§ source q35env/bin/activate Build Perquisites: pip install --upgrade -r pip-requirements.txt Stuart Setup: stuart_setup -c Platforms/QemuQ35Pkg/PlatformBuild.py Stuart Update: stuart_update -c Platforms/QemuQ35Pkg/PlatformBuild.py Note: Retry the command if failed with Filename too long error Stuart Build and Launch Uefi Shell: πͺ stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py --flashrom π§ stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py TOOL_CHAIN_TAG=CLANGPDB --flashrom Stuart Build and Launch OS(Optional): πͺ stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py --flashrom PATH_TO_OS="C:\OS\Windows11.qcow2" π§ stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py TOOL_CHAIN_TAG=CLANGPDB --flashrom PATH_TO_OS="$HOME/OS/Windows11.qcow2" |
| patina | No need to build this(except for local development). Crates from this repo are consumed directly from crates.io by patina-dxe-core-qemu repo |
Note: Please note that the paths C:\r\patina-dxe-core-qemu and $HOME/r/patina-dxe-core-qemu
in C:\r\patina-dxe-core-qemu\target\x86_64-unknown-uefi and
$HOME/r/patina-dxe-core-qemu/target/x86_64-unknown-uefi, respectively, are user-specific and
depend on the location where the repositories are cloned.
π± AArch64 Target
| Repo | Build Instructions |
|---|---|
| patina-dxe-core-qemu | cd <patina-dxe-core-qemu> Build dxe core efi binary: cargo make sbsa |
| patina-qemu | cd <patina-qemu> Setup and Activate Virtual Env: python -m venv sbsaenv πͺ sbsaenv\Scripts\activate.bat π§ source sbsaenv/bin/activate Build Perquisites: pip install --upgrade -r pip-requirements.txt Stuart Setup: stuart_setup -c Platforms/QemuSbsaPkg/PlatformBuild.py Stuart Update: stuart_update -c Platforms/QemuSbsaPkg/PlatformBuild.py Stuart Build and Launch Uefi Shell: πͺ stuart_build -c Platforms/QemuSbsaPkg/PlatformBuild.py TOOL_CHAIN_TAG=CLANGPDB --flashrom π§ stuart_build -c Platforms/QemuSbsaPkg/PlatformBuild.py TOOL_CHAIN_TAG=CLANGPDB --flashrom Stuart Build and Launch OS(Optional): πͺ stuart_build -c Platforms/QemuSbsaPkg/PlatformBuild.py TOOL_CHAIN_TAG=CLANGPDB --flashrom PATH_TO_OS="C:\OS\Windows11.qcow2" π§ stuart_build -c Platforms/QemuSbsaPkg/PlatformBuild.py TOOL_CHAIN_TAG=CLANGPDB --flashrom PATH_TO_OS="$HOME/OS/Windows11.qcow2" |
| patina | No need to build this(except for local development). Crates from this repo are consumed directly from crates.io by patina-dxe-core-qemu repo |
Note: Please note that the paths C:\r\patina-dxe-core-qemu and $HOME/r/patina-dxe-core-qemu
in C:\r\patina-dxe-core-qemu\target\x86_64-unknown-uefi and
$HOME/r/patina-dxe-core-qemu/target/x86_64-unknown-uefi, respectively, are user-specific and
depend on the location where the repositories are cloned.
The above steps will help you build and test the vanilla code, with dependencies fetched from
crates.io. For local development, you should modify the relevant crates within
the patina repository and update the dependencies using appropriate local path.
- WinDbg + QEMU + Patina UEFI - Debugging Guide
- WinDbg + QEMU + Patina UEFI + Windows OS - Debugging Guide
This repository was originally created to demonstrate using Patina modules with an emphasis on ingesting the Patina DXE Core. To modify the build to consume a new DXE Core binary instead of the pre-built .EFI file from the nuget feed, there are several methods supported.
The easiest way to inject a new Patina DXE Core driver is to update the platform FDF file (/Platforms/QemuQ35Pkg/QemuQ35Pkg.fdf
or /Platforms/QemuQ35Pkg/QemuQ35Pkg.fdf) to point to the new binary driver file as typically done in UEFI builds
that ingest pre-compiled binaries. Modify the SECTION definition in the DXE_CORE file declaration as follows:
FILE DXE_CORE = 23C9322F-2AF2-476A-BC4C-26BC88266C71 {
SECTION PE32 = "<new dxe core file path>"
SECTION UI = "DxeCore"
}This repository's platform FDF files support defining a build variable to override the default binary without needing to modify the FDF file. This can be set from the stuart_build command line by defining `BLD_*_DXE_CORE_BINARY_OVERRIDE':
stuart_build -c Platforms\QemuQ35Pkg\PlatformBuild.py --FLASHROM BLD_*_DXE_CORE_BINARY_OVERRIDE="<new dxe core file path>"If multiple iterations of the DXE core are to be tested, the fastest way to integrate each to a bootable FD image is using the Patina FW Patcher. This tool will open an existing UEFI FD binary, find and replace the current DXE Core driver with a new file, and launch QEMU with the patched ROM image.
A build_and_run_rust_binary.py
script is provided in the root of this repository to perform all steps necessary to compile the Patina DXE core driver,
call the patcher, and start QEMU. For more details, run it with the --help command line parameter:
python build_and_run_rust_binary.py --help- Note 1: This tool is not a general FW patcher to be used on any UEFI FD image. It relies on specific features implemented in this UEFI build.
- Note 2: Because this tool is patching an existing QEMU ROM image, only changes to the Rust DXE Core code will be merged. Any changes to the C code will require running a full stuart_build process to build a new ROM image.
- Note 3: The tool can be enhanced to patch more than the Patina DXE Core. If there is interest in new features, please start a discussion in the tool's repo discussions area.
By default, this repository automates the process of choosing a known working QEMU version and downloading that version
into the workspace for you. If you want to use a custom QEMU installation, you can do so by passing the path to the
Stuart build command with theQEMU_PATH argument. For example:
stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py --flashonly QEMU_PATH="<path to qemu executable>"You can also specify the directory where the QEMU binary is located by passing the QEMU_DIR argument. For example:
stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py --flashonly QEMU_DIR="<path to qemu bin directory>"Refer to the Self Certification Test documentation for information on how to configure and run the Self Certification Tests (SCTs).
