Skip to content

Commit 81f8380

Browse files
xtask: Rework the OVMF search to make it more robust
Prior to this commit we assumed that OVMF files for a given guest arch have consistent file names across host operating systems, varying only in the directory path. This turns out not to be a good assumption though; the locations and names of OVMF files installed by system packages very a lot between operating systems. To make it easier to figure out where stuff is, add a number of OS-specific functions that provide the fulls OVMF code and vars paths. For now these functions cover Arch, CentOS, Debian/Ubuntu, Fedora, and Windows. A selection of candidate paths is then collected (one set of paths for Linux, a different set for Windows) and searched. Along with the above changes, the `--ovmf-dir` option has been replaced with separate `--ovmf-code` and `--ovmf-vars` options. When set, these paths will be used instead of searching the candidate paths. The `uefi-test-runner` directory is no longer automatically searched for OVMF files since we no longer assume we know what these files would be named. Hopefully the improved candidate search makes this moot, and the `--ovmf-code`/`--ovmf-vars` options can still be used to search there if desired. CI changes: * The aarch64 job has been simplified a bit since the system location of the files is now sufficient for `cargo xtask run` to work. * The ia32 job now explicitly sets the location of the downloaded OVMF files. Fixes #469
1 parent 6a57854 commit 81f8380

File tree

4 files changed

+184
-43
lines changed

4 files changed

+184
-43
lines changed

.github/workflows/rust.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ jobs:
2323
sudo add-apt-repository ppa:canonical-server/server-backports
2424
sudo apt-get update
2525
sudo apt-get install qemu-system-arm qemu-efi-aarch64 -y
26-
# Copy the files so that the vars file isn't read-only.
27-
cp /usr/share/AAVMF/AAVMF_CODE.fd uefi-test-runner/QEMU_EFI-pflash.raw
28-
cp /usr/share/AAVMF/AAVMF_VARS.fd uefi-test-runner/vars-template-pflash.raw
2926
3027
- name: Install stable
3128
uses: actions-rs/toolchain@v1
@@ -102,8 +99,8 @@ jobs:
10299
# guard against external changes breaking the CI.
103100
EDK2_NIGHTLY_COMMIT: 'ebb83e5475d49418afc32857f66111949928bcdc'
104101
run: |
105-
curl -o uefi-test-runner/OVMF32_CODE.fd https://raw.githubusercontent.com/retrage/edk2-nightly/${EDK2_NIGHTLY_COMMIT}/bin/RELEASEIa32_OVMF_CODE.fd
106-
curl -o uefi-test-runner/OVMF32_VARS.fd https://raw.githubusercontent.com/retrage/edk2-nightly/${EDK2_NIGHTLY_COMMIT}/bin/RELEASEIa32_OVMF_VARS.fd
102+
curl -o OVMF32_CODE.fd https://raw.githubusercontent.com/retrage/edk2-nightly/${EDK2_NIGHTLY_COMMIT}/bin/RELEASEIa32_OVMF_CODE.fd
103+
curl -o OVMF32_VARS.fd https://raw.githubusercontent.com/retrage/edk2-nightly/${EDK2_NIGHTLY_COMMIT}/bin/RELEASEIa32_OVMF_VARS.fd
107104
108105
- name: Install stable
109106
uses: actions-rs/toolchain@v1
@@ -121,7 +118,7 @@ jobs:
121118
run: cargo xtask build --target ia32
122119

123120
- name: Run VM tests
124-
run: cargo xtask run --target ia32 --headless --ci
121+
run: cargo xtask run --target ia32 --headless --ci --ovmf-code OVMF32_CODE.fd --ovmf-vars OVMF32_VARS.fd
125122
timeout-minutes: 2
126123

127124
test:

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ Available commands:
8787
Especially useful if you want to run the tests under
8888
[WSL](https://docs.microsoft.com/en-us/windows/wsl) on Windows.
8989
- `--headless`: run QEMU without a GUI
90-
- `--ovmf-dir <PATH>`: directory in which to look for OVMF files
90+
- `--ovmf-code <PATH>`: path of an OVMF code file
91+
- `--ovmf-vars <PATH>`: path of an OVMF vars file
9192
- `--release`: build in release mode
9293
- `--target {x86_64,ia32,aarch64}`: choose target UEFI arch
9394
- `test`: run unit tests and doctests on the host

xtask/src/opt.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,13 @@ pub struct QemuOpt {
140140
#[clap(long, action)]
141141
pub headless: bool,
142142

143-
/// Directory in which to look for OVMF files.
143+
/// Path of an OVMF code file.
144144
#[clap(long, action)]
145-
pub ovmf_dir: Option<PathBuf>,
145+
pub ovmf_code: Option<PathBuf>,
146+
147+
/// Path of an OVMF vars file.
148+
#[clap(long, action)]
149+
pub ovmf_vars: Option<PathBuf>,
146150
}
147151

148152
/// Build uefi-test-runner and run it in QEMU.

xtask/src/qemu.rs

Lines changed: 173 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,67 +14,206 @@ use std::process::{Child, Command, Stdio};
1414
use std::{env, thread};
1515
use tempfile::TempDir;
1616

17+
#[derive(Clone, Copy, Debug)]
18+
enum OvmfFileType {
19+
Code,
20+
Vars,
21+
}
22+
23+
impl OvmfFileType {
24+
fn as_str(&self) -> &'static str {
25+
match self {
26+
Self::Code => "code",
27+
Self::Vars => "vars",
28+
}
29+
}
30+
}
31+
1732
struct OvmfPaths {
1833
code: PathBuf,
1934
vars: PathBuf,
2035
}
2136

2237
impl OvmfPaths {
23-
fn from_dir(dir: &Path, arch: UefiArch) -> Self {
38+
fn get_path(&self, file_type: OvmfFileType) -> &Path {
39+
match file_type {
40+
OvmfFileType::Code => &self.code,
41+
OvmfFileType::Vars => &self.vars,
42+
}
43+
}
44+
45+
/// Get the Arch Linux OVMF paths for the given guest arch.
46+
fn arch_linux(arch: UefiArch) -> Self {
2447
match arch {
48+
// Package "edk2-armvirt".
2549
UefiArch::AArch64 => Self {
26-
code: dir.join("QEMU_EFI-pflash.raw"),
27-
vars: dir.join("vars-template-pflash.raw"),
50+
code: "/usr/share/edk2-armvirt/aarch64/QEMU_CODE.fd".into(),
51+
vars: "/usr/share/edk2-armvirt/aarch64/QEMU_VARS.fd".into(),
2852
},
53+
// Package "edk2-ovmf".
2954
UefiArch::IA32 => Self {
30-
code: dir.join("OVMF32_CODE.fd"),
31-
vars: dir.join("OVMF32_VARS.fd"),
55+
code: "/usr/share/edk2-ovmf/ia32/OVMF_CODE.fd".into(),
56+
vars: "/usr/share/edk2-ovmf/ia32/OVMF_VARS.fd".into(),
3257
},
58+
// Package "edk2-ovmf".
3359
UefiArch::X86_64 => Self {
34-
code: dir.join("OVMF_CODE.fd"),
35-
vars: dir.join("OVMF_VARS.fd"),
60+
code: "/usr/share/edk2-ovmf/x64/OVMF_CODE.fd".into(),
61+
vars: "/usr/share/edk2-ovmf/x64/OVMF_VARS.fd".into(),
3662
},
3763
}
3864
}
3965

40-
fn exists(&self) -> bool {
41-
self.code.exists() && self.vars.exists()
66+
/// Get the CentOS OVMF paths for the given guest arch.
67+
fn centos_linux(arch: UefiArch) -> Option<Self> {
68+
match arch {
69+
// Package "edk2-aarch64".
70+
UefiArch::AArch64 => Some(Self {
71+
code: "/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw".into(),
72+
vars: "/usr/share/edk2/aarch64/vars-template-pflash.raw".into(),
73+
}),
74+
// There's no official ia32 package.
75+
UefiArch::IA32 => None,
76+
// Package "edk2-ovmf".
77+
UefiArch::X86_64 => Some(Self {
78+
// Use the `.secboot` variant because the CentOS package
79+
// doesn't have a plain "OVMF_CODE.fd".
80+
code: "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd".into(),
81+
vars: "/usr/share/edk2/ovmf/OVMF_VARS.fd".into(),
82+
}),
83+
}
4284
}
4385

44-
/// Find path to OVMF files.
45-
fn find(opt: &QemuOpt, arch: UefiArch) -> Result<Self> {
46-
// If the path is specified in the settings, use it.
47-
if let Some(ovmf_dir) = &opt.ovmf_dir {
48-
let ovmf_paths = Self::from_dir(ovmf_dir, arch);
49-
if ovmf_paths.exists() {
50-
return Ok(ovmf_paths);
51-
}
52-
bail!("OVMF files not found in {}", ovmf_dir.display());
86+
/// Get the Debian OVMF paths for the given guest arch. These paths
87+
/// also work on Ubuntu.
88+
fn debian_linux(arch: UefiArch) -> Self {
89+
match arch {
90+
// Package "qemu-efi-aarch64".
91+
UefiArch::AArch64 => Self {
92+
code: "/usr/share/AAVMF/AAVMF_CODE.fd".into(),
93+
vars: "/usr/share/AAVMF/AAVMF_VARS.fd".into(),
94+
},
95+
// Package "ovmf-ia32".
96+
UefiArch::IA32 => Self {
97+
code: "/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd".into(),
98+
vars: "/usr/share/OVMF/OVMF32_VARS_4M.fd".into(),
99+
},
100+
// Package "ovmf".
101+
UefiArch::X86_64 => Self {
102+
code: "/usr/share/OVMF/OVMF_CODE.fd".into(),
103+
vars: "/usr/share/OVMF/OVMF_VARS.fd".into(),
104+
},
105+
}
106+
}
107+
108+
/// Get the Fedora OVMF paths for the given guest arch.
109+
fn fedora_linux(arch: UefiArch) -> Self {
110+
match arch {
111+
// Package "edk2-aarch64".
112+
UefiArch::AArch64 => Self {
113+
code: "/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw".into(),
114+
vars: "/usr/share/edk2/aarch64/vars-template-pflash.raw".into(),
115+
},
116+
// Package "edk2-ovmf-ia32".
117+
UefiArch::IA32 => Self {
118+
code: "/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd".into(),
119+
vars: "/usr/share/edk2/ovmf-ia32/OVMF_VARS.fd".into(),
120+
},
121+
// Package "edk2-ovmf".
122+
UefiArch::X86_64 => Self {
123+
code: "/usr/share/edk2/ovmf/OVMF_CODE.fd".into(),
124+
vars: "/usr/share/edk2/ovmf/OVMF_VARS.fd".into(),
125+
},
53126
}
127+
}
54128

55-
// Check whether the test runner directory contains the files.
56-
let ovmf_dir = Path::new("uefi-test-runner");
57-
let ovmf_paths = Self::from_dir(ovmf_dir, arch);
58-
if ovmf_paths.exists() {
59-
return Ok(ovmf_paths);
129+
/// Get the Windows OVMF paths for the given guest arch.
130+
fn windows(arch: UefiArch) -> Self {
131+
match arch {
132+
UefiArch::AArch64 => Self {
133+
code: r"C:\Program Files\qemu\share\edk2-aarch64-code.fd".into(),
134+
vars: r"C:\Program Files\qemu\share\edk2-arm-vars.fd".into(),
135+
},
136+
UefiArch::IA32 => Self {
137+
code: r"C:\Program Files\qemu\share\edk2-i386-code.fd".into(),
138+
vars: r"C:\Program Files\qemu\share\edk2-i386-vars.fd".into(),
139+
},
140+
UefiArch::X86_64 => Self {
141+
code: r"C:\Program Files\qemu\share\edk2-x86_64-code.fd".into(),
142+
// There's no x86_64 vars file, but the i386 one works.
143+
vars: r"C:\Program Files\qemu\share\edk2-i386-vars.fd".into(),
144+
},
60145
}
146+
}
61147

148+
/// Get candidate paths where OVMF code/vars might exist for the
149+
/// given guest arch and host platform.
150+
fn get_candidate_paths(arch: UefiArch) -> Vec<Self> {
151+
let mut candidates = Vec::new();
62152
if platform::is_linux() {
63-
let possible_paths = [
64-
// Most distros, including CentOS, Fedora, Debian, and Ubuntu.
65-
Path::new("/usr/share/OVMF"),
66-
// Arch Linux.
67-
Path::new("/usr/share/ovmf/x64"),
68-
];
69-
for path in possible_paths {
70-
let ovmf_paths = Self::from_dir(path, arch);
71-
if ovmf_paths.exists() {
72-
return Ok(ovmf_paths);
153+
candidates.push(Self::arch_linux(arch));
154+
if let Some(candidate) = Self::centos_linux(arch) {
155+
candidates.push(candidate);
156+
}
157+
candidates.push(Self::debian_linux(arch));
158+
candidates.push(Self::fedora_linux(arch));
159+
}
160+
if platform::is_windows() {
161+
candidates.push(Self::windows(arch));
162+
}
163+
candidates
164+
}
165+
166+
/// Search for an OVMF file (either code or vars).
167+
///
168+
/// If `user_provided_path` is not None, it is always used. An error
169+
/// is returned if the path does not exist.
170+
///
171+
/// Otherwise, the paths in `candidates` are searched to find one
172+
/// that exists. If none of them exist, an error is returned.
173+
fn find_ovmf_file(
174+
file_type: OvmfFileType,
175+
user_provided_path: &Option<PathBuf>,
176+
candidates: &[Self],
177+
) -> Result<PathBuf> {
178+
if let Some(path) = user_provided_path {
179+
// The user provided an exact path to use; verify that it
180+
// exists.
181+
if path.exists() {
182+
Ok(path.to_owned())
183+
} else {
184+
bail!(
185+
"ovmf {} file does not exist: {}",
186+
file_type.as_str(),
187+
path.display()
188+
);
189+
}
190+
} else {
191+
for candidate in candidates {
192+
let path = candidate.get_path(file_type);
193+
if path.exists() {
194+
return Ok(path.to_owned());
73195
}
74196
}
197+
198+
bail!(
199+
"no ovmf {} file found in candidates: {:?}",
200+
file_type.as_str(),
201+
candidates
202+
.iter()
203+
.map(|c| c.get_path(file_type))
204+
.collect::<Vec<_>>(),
205+
);
75206
}
207+
}
208+
209+
/// Find path to OVMF files.
210+
fn find(opt: &QemuOpt, arch: UefiArch) -> Result<Self> {
211+
let candidates = Self::get_candidate_paths(arch);
212+
213+
let code = Self::find_ovmf_file(OvmfFileType::Code, &opt.ovmf_code, &candidates)?;
214+
let vars = Self::find_ovmf_file(OvmfFileType::Vars, &opt.ovmf_vars, &candidates)?;
76215

77-
bail!("OVMF files not found anywhere");
216+
Ok(Self { code, vars })
78217
}
79218
}
80219

0 commit comments

Comments
 (0)