@@ -5,7 +5,7 @@ use crate::pipe::Pipe;
55use crate :: tpm:: Swtpm ;
66use crate :: util:: command_to_string;
77use crate :: { net, platform} ;
8- use anyhow:: { bail, Context , Result } ;
8+ use anyhow:: { anyhow , bail, Context , Result } ;
99use regex:: bytes:: Regex ;
1010use serde_json:: { json, Value } ;
1111use std:: env;
@@ -14,6 +14,8 @@ use std::io::{BufRead, BufReader, Read, Write};
1414use std:: path:: { Path , PathBuf } ;
1515use std:: process:: { Child , Command , Stdio } ;
1616use tempfile:: TempDir ;
17+ #[ cfg( target_os = "linux" ) ]
18+ use { std:: fs:: Permissions , std:: os:: unix:: fs:: PermissionsExt } ;
1719
1820#[ derive( Clone , Copy , Debug ) ]
1921enum OvmfFileType {
@@ -60,6 +62,18 @@ struct OvmfPaths {
6062}
6163
6264impl 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