Skip to content

Commit a7d77da

Browse files
WIP: UEFI shell protocol testing
1 parent 7a1aa01 commit a7d77da

File tree

2 files changed

+115
-15
lines changed

2 files changed

+115
-15
lines changed
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
extern crate alloc;
5+
6+
use alloc::vec::Vec;
7+
use log::info;
8+
use uefi::prelude::*;
9+
use uefi::proto::device_path::build::{self, DevicePathBuilder};
10+
use uefi::proto::device_path::{DeviceSubType, DeviceType, LoadedImageDevicePath};
11+
use uefi::proto::loaded_image::LoadedImage;
12+
use uefi::table::boot::LoadImageSource;
13+
use uefi::Status;
14+
15+
// This launcher attempts to launch the shell app and run the main
16+
// uefi-test-running app inside that shell. This allows testing of protocols
17+
// that require the shell.
18+
//
19+
// Launching the shell programmatically allows us to avoid the built-in five
20+
// second delay when starting the shell.
21+
#[entry]
22+
fn efi_main(image: Handle, mut st: SystemTable<Boot>) -> Status {
23+
uefi_services::init(&mut st).unwrap();
24+
25+
let boot_services = st.boot_services();
26+
27+
let loaded_image_device_path = boot_services
28+
.open_protocol_exclusive::<LoadedImageDevicePath>(image)
29+
.unwrap();
30+
31+
let mut storage = Vec::new();
32+
let mut builder = DevicePathBuilder::with_vec(&mut storage);
33+
for node in loaded_image_device_path.node_iter() {
34+
if node.full_type() == (DeviceType::MEDIA, DeviceSubType::MEDIA_FILE_PATH) {
35+
break;
36+
}
37+
builder = builder.push(&node).unwrap();
38+
}
39+
builder = builder
40+
.push(&build::media::FilePath {
41+
path_name: cstr16!(r"efi\boot\shell.efi"),
42+
})
43+
.unwrap();
44+
let new_image_path = builder.finalize().unwrap();
45+
46+
// TODO
47+
let new_image = boot_services
48+
.load_image(
49+
image,
50+
LoadImageSource::FromFilePath {
51+
file_path: new_image_path,
52+
from_boot_manager: false,
53+
},
54+
)
55+
.expect("failed to load shell app");
56+
57+
let mut new_loaded_image = boot_services
58+
.open_protocol_exclusive::<LoadedImage>(new_image)
59+
.unwrap();
60+
let load_options = cstr16!(r"shell.efi test_runner.efi");
61+
unsafe {
62+
new_loaded_image.set_load_options(
63+
load_options.as_ptr().cast(),
64+
load_options.num_bytes() as u32,
65+
);
66+
}
67+
68+
info!("launching the UEFI shell");
69+
boot_services.start_image(new_image).unwrap();
70+
71+
Status::SUCCESS
72+
}

xtask/src/qemu.rs

+43-15
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,22 @@ fn update_prebuilt() -> Result<PathBuf> {
135135
enum OvmfFileType {
136136
Code,
137137
Vars,
138+
Shell,
138139
}
139140

140141
impl OvmfFileType {
141142
fn as_str(&self) -> &'static str {
142143
match self {
143144
Self::Code => "code",
144145
Self::Vars => "vars",
146+
Self::Shell => "shell",
147+
}
148+
}
149+
150+
fn extension(&self) -> &'static str {
151+
match self {
152+
Self::Code | Self::Vars => "fd",
153+
Self::Shell => "efi",
145154
}
146155
}
147156

@@ -161,6 +170,10 @@ impl OvmfFileType {
161170
opt_path = &opt.ovmf_vars;
162171
var_name = "OVMF_VARS";
163172
}
173+
Self::Shell => {
174+
opt_path = &None;
175+
var_name = "OVMF_SHELL";
176+
}
164177
}
165178
if let Some(path) = opt_path {
166179
Some(path.clone())
@@ -173,6 +186,7 @@ impl OvmfFileType {
173186
struct OvmfPaths {
174187
code: PathBuf,
175188
vars: PathBuf,
189+
shell: PathBuf,
176190
}
177191

178192
impl OvmfPaths {
@@ -199,7 +213,11 @@ impl OvmfPaths {
199213
} else {
200214
let prebuilt_dir = update_prebuilt()?;
201215

202-
Ok(prebuilt_dir.join(format!("{arch}/{}.fd", file_type.as_str())))
216+
Ok(prebuilt_dir.join(format!(
217+
"{arch}/{}.{}",
218+
file_type.as_str(),
219+
file_type.extension()
220+
)))
203221
}
204222
}
205223

@@ -208,8 +226,9 @@ impl OvmfPaths {
208226
fn find(opt: &QemuOpt, arch: UefiArch) -> Result<Self> {
209227
let code = Self::find_ovmf_file(OvmfFileType::Code, opt, arch)?;
210228
let vars = Self::find_ovmf_file(OvmfFileType::Vars, opt, arch)?;
229+
let shell = Self::find_ovmf_file(OvmfFileType::Shell, opt, arch)?;
211230

212-
Ok(Self { code, vars })
231+
Ok(Self { code, vars, shell })
213232
}
214233
}
215234

@@ -352,7 +371,7 @@ fn process_qemu_io(mut monitor_io: Io, mut serial_io: Io, tmp_dir: &Path) -> Res
352371
}
353372

354373
/// Create an EFI boot directory to pass into QEMU.
355-
fn build_esp_dir(opt: &QemuOpt) -> Result<PathBuf> {
374+
fn build_esp_dir(opt: &QemuOpt, ovmf_paths: &OvmfPaths) -> Result<PathBuf> {
356375
let build_mode = if opt.build_mode.release {
357376
"release"
358377
} else {
@@ -362,21 +381,31 @@ fn build_esp_dir(opt: &QemuOpt) -> Result<PathBuf> {
362381
.join(opt.target.as_triple())
363382
.join(build_mode);
364383
let esp_dir = build_dir.join("esp");
384+
385+
// Create boot dir.
365386
let boot_dir = esp_dir.join("EFI").join("Boot");
366-
let built_file = if let Some(example) = &opt.example {
367-
build_dir.join("examples").join(format!("{example}.efi"))
368-
} else {
369-
build_dir.join("uefi-test-runner.efi")
370-
};
371-
let output_file = match *opt.target {
387+
if !boot_dir.exists() {
388+
fs_err::create_dir_all(&boot_dir)?;
389+
}
390+
391+
let boot_file_name = match *opt.target {
372392
UefiArch::AArch64 => "BootAA64.efi",
373393
UefiArch::IA32 => "BootIA32.efi",
374394
UefiArch::X86_64 => "BootX64.efi",
375395
};
376-
if !boot_dir.exists() {
377-
fs_err::create_dir_all(&boot_dir)?;
378-
}
379-
fs_err::copy(built_file, boot_dir.join(output_file))?;
396+
397+
if let Some(example) = &opt.example {
398+
let src_path = build_dir.join("examples").join(format!("{example}.efi"));
399+
fs_err::copy(src_path, boot_dir.join(boot_file_name))?;
400+
} else {
401+
let shell_launcher = build_dir.join("shell_launcher.efi");
402+
fs_err::copy(shell_launcher, boot_dir.join(boot_file_name))?;
403+
404+
let test_runner = build_dir.join("uefi-test-runner.efi");
405+
fs_err::copy(test_runner, boot_dir.join("test_runner.efi"))?;
406+
407+
fs_err::copy(&ovmf_paths.shell, boot_dir.join("shell.efi"))?;
408+
};
380409

381410
Ok(esp_dir)
382411
}
@@ -403,8 +432,6 @@ impl Drop for ChildWrapper {
403432
}
404433

405434
pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
406-
let esp_dir = build_esp_dir(opt)?;
407-
408435
let qemu_exe = match arch {
409436
UefiArch::AArch64 => "qemu-system-aarch64",
410437
UefiArch::IA32 | UefiArch::X86_64 => "qemu-system-x86_64",
@@ -506,6 +533,7 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
506533
// Mount a local directory as a FAT partition.
507534
cmd.arg("-drive");
508535
let mut drive_arg = OsString::from("format=raw,file=fat:rw:");
536+
let esp_dir = build_esp_dir(opt, &ovmf_paths)?;
509537
drive_arg.push(esp_dir);
510538
cmd.arg(drive_arg);
511539

0 commit comments

Comments
 (0)