@@ -5,7 +5,7 @@ use crate::pipe::Pipe;
5
5
use crate :: tpm:: Swtpm ;
6
6
use crate :: util:: command_to_string;
7
7
use crate :: { net, platform} ;
8
- use anyhow:: { bail, Context , Result } ;
8
+ use anyhow:: { anyhow , bail, Context , Result } ;
9
9
use regex:: bytes:: Regex ;
10
10
use serde_json:: { json, Value } ;
11
11
use std:: env;
@@ -14,6 +14,8 @@ use std::io::{BufRead, BufReader, Read, Write};
14
14
use std:: path:: { Path , PathBuf } ;
15
15
use std:: process:: { Child , Command , Stdio } ;
16
16
use tempfile:: TempDir ;
17
+ #[ cfg( target_os = "linux" ) ]
18
+ use { std:: fs:: Permissions , std:: os:: unix:: fs:: PermissionsExt } ;
17
19
18
20
#[ derive( Clone , Copy , Debug ) ]
19
21
enum OvmfFileType {
@@ -60,6 +62,18 @@ struct OvmfPaths {
60
62
}
61
63
62
64
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
+
63
77
fn get_path ( & self , file_type : OvmfFileType ) -> & Path {
64
78
match file_type {
65
79
OvmfFileType :: Code => & self . code ,
@@ -151,6 +165,24 @@ impl OvmfPaths {
151
165
}
152
166
}
153
167
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
+
154
186
/// Get the Windows OVMF paths for the given guest arch.
155
187
fn windows ( arch : UefiArch ) -> Self {
156
188
match arch {
@@ -172,7 +204,7 @@ impl OvmfPaths {
172
204
173
205
/// Get candidate paths where OVMF code/vars might exist for the
174
206
/// 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 > > {
176
208
let mut candidates = Vec :: new ( ) ;
177
209
if platform:: is_linux ( ) {
178
210
candidates. push ( Self :: arch_linux ( arch) ) ;
@@ -181,20 +213,21 @@ impl OvmfPaths {
181
213
}
182
214
candidates. push ( Self :: debian_linux ( arch) ) ;
183
215
candidates. push ( Self :: fedora_linux ( arch) ) ;
216
+ Self :: assist_nixos_users ( ) ?;
184
217
}
185
218
if platform:: is_windows ( ) {
186
219
candidates. push ( Self :: windows ( arch) ) ;
187
220
}
188
- candidates
221
+ Ok ( candidates)
189
222
}
190
223
191
224
/// Search for an OVMF file (either code or vars).
192
225
///
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` .
198
231
fn find_ovmf_file (
199
232
file_type : OvmfFileType ,
200
233
opt : & QemuOpt ,
@@ -231,9 +264,10 @@ impl OvmfPaths {
231
264
}
232
265
}
233
266
234
- /// Find path to OVMF files.
267
+ /// Find path to OVMF files by the strategy documented for
268
+ /// [`Self::find_ovmf_file`].
235
269
fn find ( opt : & QemuOpt , arch : UefiArch ) -> Result < Self > {
236
- let candidates = Self :: get_candidate_paths ( arch) ;
270
+ let candidates = Self :: get_candidate_paths ( arch) ? ;
237
271
238
272
let code = Self :: find_ovmf_file ( OvmfFileType :: Code , opt, & candidates) ?;
239
273
let vars = Self :: find_ovmf_file ( OvmfFileType :: Vars , opt, & candidates) ?;
@@ -521,6 +555,10 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
521
555
// versions of OVMF won't boot if the vars file isn't writeable.
522
556
let ovmf_vars = tmp_dir. join ( "ovmf_vars" ) ;
523
557
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 ) ) ?;
524
562
525
563
add_pflash_args ( & mut cmd, & ovmf_paths. code , PflashMode :: ReadOnly ) ;
526
564
add_pflash_args ( & mut cmd, & ovmf_vars, PflashMode :: ReadWrite ) ;
0 commit comments