Skip to content

Commit c5d0d13

Browse files
authored
Merge pull request #690 from phip1611/ovmf-nixos
OVMF: enable "cargo xtask run" under NixOS
2 parents 349f7de + 5cb9bf5 commit c5d0d13

File tree

5 files changed

+82
-11
lines changed

5 files changed

+82
-11
lines changed

.editorconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ trim_trailing_whitespace = true
1212
indent_style = space
1313
indent_size = 4
1414

15-
[*.{md,json}]
15+
[*.{json,md,nix,yml}]
1616
indent_style = space
1717
indent_size = 2

nix/nixpkgs.nix

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Pinned nixpkgs version.
2+
3+
let
4+
# Picked a recent commit from the nixos-22.11-small branch.
5+
# https://github.com/NixOS/nixpkgs/tree/nixos-22.11-small
6+
#
7+
# When you change this, also change the sha256 hash!
8+
rev = "a45745ac9e4e1eb86397ab22e2a8823120ab9a4c";
9+
in
10+
builtins.fetchTarball {
11+
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
12+
sha256 = "sha256:1acllp8yxp1rwncxsxnxl9cwkm97wxfnd6ryclmvll3sa39j9b1z";
13+
}

shell.nix

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
let
2+
pkgsSrc = import ./nix/nixpkgs.nix;
3+
pkgs = import pkgsSrc {};
4+
in
5+
pkgs.mkShell rec {
6+
nativeBuildInputs = with pkgs; [
7+
rustup
8+
qemu
9+
];
10+
11+
buildInputs = with pkgs; [
12+
];
13+
14+
# Set ENV vars.
15+
# These are automatically the right files for the current CPU (if available).
16+
# https://github.com/NixOS/nixpkgs/blob/nixos-22.11/pkgs/applications/virtualization/OVMF/default.nix#L80
17+
OVMF_CODE="${pkgs.OVMF.firmware}";
18+
OVMF_VARS="${pkgs.OVMF.variables}";
19+
}

xtask/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ heck = "0.4.0"
1313
itertools = "0.10.5"
1414
mbrman = "0.5.1"
1515
nix = "0.26.1"
16+
os_info = { version = "3.6.0", default-features = false }
1617
proc-macro2 = "1.0.46"
1718
quote = "1.0.21"
1819
regex = "1.5.4"

xtask/src/qemu.rs

+48-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::pipe::Pipe;
55
use crate::tpm::Swtpm;
66
use crate::util::command_to_string;
77
use crate::{net, platform};
8-
use anyhow::{bail, Context, Result};
8+
use anyhow::{anyhow, bail, Context, Result};
99
use regex::bytes::Regex;
1010
use serde_json::{json, Value};
1111
use std::env;
@@ -14,6 +14,8 @@ use std::io::{BufRead, BufReader, Read, Write};
1414
use std::path::{Path, PathBuf};
1515
use std::process::{Child, Command, Stdio};
1616
use tempfile::TempDir;
17+
#[cfg(target_os = "linux")]
18+
use {std::fs::Permissions, std::os::unix::fs::PermissionsExt};
1719

1820
#[derive(Clone, Copy, Debug)]
1921
enum OvmfFileType {
@@ -60,6 +62,18 @@ struct OvmfPaths {
6062
}
6163

6264
impl OvmfPaths {
65+
/// If OVMF files can not or should not be found at well-known locations,
66+
/// this optional environment variable can point to it.
67+
///
68+
/// This variable points to the `_CODE.fd` file.
69+
70+
const ENV_VAR_OVMF_CODE: &'static str = "OVMF_CODE";
71+
/// If OVMF files can not or should not be found at well-known locations,
72+
/// this optional environment variable can point to it.
73+
///
74+
/// This variable points to the `_VARS.fd` file.
75+
const ENV_VAR_OVMF_VARS: &'static str = "OVMF_VARS";
76+
6377
fn get_path(&self, file_type: OvmfFileType) -> &Path {
6478
match file_type {
6579
OvmfFileType::Code => &self.code,
@@ -151,6 +165,24 @@ impl OvmfPaths {
151165
}
152166
}
153167

168+
/// If a user uses NixOS, this function returns an error if the user didn't
169+
/// set the environment variables `OVMF_CODE` and `OVMF_VARS`.
170+
///
171+
/// It returns nothing as the environment variables are resolved at a
172+
/// higher level. NixOS doesn't have globally installed software (without
173+
/// hacky and non-idiomatic workarounds).
174+
fn assist_nixos_users() -> Result<()> {
175+
let os_info = os_info::get();
176+
if os_info.os_type() == os_info::Type::NixOS {
177+
let code = env::var_os(Self::ENV_VAR_OVMF_CODE);
178+
let vars = env::var_os(Self::ENV_VAR_OVMF_VARS);
179+
if !matches!((code, vars), (Some(_), Some(_))) {
180+
return Err(anyhow!("Run `$ nix-shell` for OVMF files."));
181+
}
182+
}
183+
Ok(())
184+
}
185+
154186
/// Get the Windows OVMF paths for the given guest arch.
155187
fn windows(arch: UefiArch) -> Self {
156188
match arch {
@@ -172,7 +204,7 @@ impl OvmfPaths {
172204

173205
/// Get candidate paths where OVMF code/vars might exist for the
174206
/// given guest arch and host platform.
175-
fn get_candidate_paths(arch: UefiArch) -> Vec<Self> {
207+
fn get_candidate_paths(arch: UefiArch) -> Result<Vec<Self>> {
176208
let mut candidates = Vec::new();
177209
if platform::is_linux() {
178210
candidates.push(Self::arch_linux(arch));
@@ -181,20 +213,21 @@ impl OvmfPaths {
181213
}
182214
candidates.push(Self::debian_linux(arch));
183215
candidates.push(Self::fedora_linux(arch));
216+
Self::assist_nixos_users()?;
184217
}
185218
if platform::is_windows() {
186219
candidates.push(Self::windows(arch));
187220
}
188-
candidates
221+
Ok(candidates)
189222
}
190223

191224
/// Search for an OVMF file (either code or vars).
192225
///
193-
/// If `user_provided_path` is not None, it is always used. An error
194-
/// is returned if the path does not exist.
195-
///
196-
/// Otherwise, the paths in `candidates` are searched to find one
197-
/// that exists. If none of them exist, an error is returned.
226+
/// There are multiple locations where a file is searched at in the following
227+
/// priority:
228+
/// 1. User-defined location: See [`OvmfFileType::get_user_provided_path`]
229+
/// 2. Well-known location of common Linux distributions by using the
230+
/// paths in `candidates`.
198231
fn find_ovmf_file(
199232
file_type: OvmfFileType,
200233
opt: &QemuOpt,
@@ -231,9 +264,10 @@ impl OvmfPaths {
231264
}
232265
}
233266

234-
/// Find path to OVMF files.
267+
/// Find path to OVMF files by the strategy documented for
268+
/// [`Self::find_ovmf_file`].
235269
fn find(opt: &QemuOpt, arch: UefiArch) -> Result<Self> {
236-
let candidates = Self::get_candidate_paths(arch);
270+
let candidates = Self::get_candidate_paths(arch)?;
237271

238272
let code = Self::find_ovmf_file(OvmfFileType::Code, opt, &candidates)?;
239273
let vars = Self::find_ovmf_file(OvmfFileType::Vars, opt, &candidates)?;
@@ -521,6 +555,10 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
521555
// versions of OVMF won't boot if the vars file isn't writeable.
522556
let ovmf_vars = tmp_dir.join("ovmf_vars");
523557
fs_err::copy(&ovmf_paths.vars, &ovmf_vars)?;
558+
// Necessary, as for example on NixOS, the files are read-only inside
559+
// the Nix store.
560+
#[cfg(target_os = "linux")]
561+
fs_err::set_permissions(&ovmf_vars, Permissions::from_mode(0o666))?;
524562

525563
add_pflash_args(&mut cmd, &ovmf_paths.code, PflashMode::ReadOnly);
526564
add_pflash_args(&mut cmd, &ovmf_vars, PflashMode::ReadWrite);

0 commit comments

Comments
 (0)