uefi-rs を使うとかなりサクっと使えて良い。
x86_64-uefi.json
cargo build
をするだけで UEFI Application が吐ける。
まず,cargo-xbuild
をインストール。
$ cargo install xbuild
これは私が以前から使っていた xargo
の fork のようだ。
以下のようにそれぞれ準備する。
x86_64-uefi.json
{
"llvm-target": "x86_64-pc-windows-msvc",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "uefi",
"arch": "x86_64",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"linker": "rust-lld",
"linker-flavor": "lld-link",
"pre-link-args": {
"lld-link": [
"/Subsystem:EFI_Application",
"/Entry:efi_main"
]
},
"panic-strategy": "abort",
"default-hidden-visibility": true,
"executables": true,
"position-independent-executables": true,
"exe-suffix": ".efi",
"is-like-windows": true,
"emit-debug-gdb-scripts": false
}
Cargo.toml
cargo-features = ["edition"]
[package]
name = "uefi_app"
version = "0.1.0"
authors = ["foo "]
[dependencies]
uefi = { path = "../external/uefi-rs/" }
uefi-services = { path = "../external/uefi-rs/uefi-services" }
uefi-exts = { path = "../external/uefi-rs/uefi-exts" }
log = { version = "0.4", default-features = false }
src/main.rs
#![no_std]
#![no_main]
#![feature(slice_patterns)]
#![feature(alloc)]
#![feature(asm)]
#![feature(extern_prelude)]
extern crate uefi;
extern crate uefi_exts;
#[macro_use]
extern crate log;
#[macro_use]
extern crate alloc;
use uefi::prelude::*;
use uefi::table::boot::MemoryDescriptor;
use uefi::proto::console::gop::GraphicsOutput;
use uefi_exts::BootServicesExt;
use core::mem;
use alloc::vec::Vec;
#[no_mangle]
pub extern "C" fn efi_main(handle: uefi::Handle, system_table: &'static SystemTable) -> Status {
uefi_services::init(system_table);
let graphic_mode = system_table.stdout().modes().last().unwrap();
system_table.stdout().set_mode(graphic_mode).expect("Failed");
gop_init(system_table.boot);
memmap(system_table.boot);
loop{}
return uefi::Status::Success;
}
fn gop_init(bt: &BootServices) {
if let Some(mut gop_proto) = bt.find_protocol::() {
let gop = unsafe { gop_proto.as_mut() };
let mode = gop.modes()
.find(|ref mode| {
let mode_info = mode.info();
mode_info.resolution() == (1280, 720)
}).unwrap();
gop.set_mode(&mode).expect("failed");
} else {
warn!("UEFI GOP is not supported");
}
}
fn memmap(bt: &BootServices) {
let map_size = bt.memory_map_size();
let buffer_size = map_size + 8 * mem::size_of::();
let mut buffer = Vec::with_capacity(buffer_size);
unsafe {
buffer.set_len(buffer_size);
}
let (_key, mut descriptor_iter) = bt.memory_map(&mut buffer)
.expect("failed");
assert!(descriptor_iter.len() > 0, "memory map is empty");
let first_descriptor = descriptor_iter.next().unwrap();
let physical_address_start = first_descriptor.phys_start;
assert_eq!(physical_address_start, 0, "memory does not start at address 0x0");
}
以上のファイルが用意できたら次のコマンドでビルド
$ RUST_TARGET_PATH=`pwd` cargo xbuild --target x86_64-uefi
すると,build/target/x86_64-uefi/uefi_app.efi
が生成される。
実行するときは UEFI の image である OVMF について OVMF_CODE.fd と OVMF_VARS.fd をそれぞれ用意し,次のような構成にする
uefi_app
|
+--- Cargo.toml
|
+--- x86_64-uefi.json
|
+--- OVMF_CODE.fd
|
+--- OVMF_VARS.fd
|
+--- src
| |
| +--- main.rs
|
+--- esp
|
+--- efi
|
+--- boot
|
+--- bootx64.efi (uefi_app.efi をコピー)
生成されたバイナリはプロジェクトディレクトリの esp/efi/boot/bootx64.efi として配置する。
qemu-system-x86_64 -nodefaults -vga std -machine q35,accel=kvm:tcg -m 1024 \
-drive if=pflash,format=raw,file=OVMF_CODE.fd ,readonly=on \
-drive if=pflash,format=raw,file=OVMF_VARS.fd ,readonly=on \
-drive format=raw,file=fat:rw:esp