Skip to main content

ovmf_prebuilt/
lib.rs

1//! Download, cache, and access [OVMF] prebuilts.
2//!
3//! [OVMF]: https://github.com/tianocore/edk2/tree/master/OvmfPkg#readme
4//!
5//! # Example
6//!
7//! ```
8//! use ovmf_prebuilt::{Arch, FileType, Source, Prebuilt};
9//! use std::path::Path;
10//!
11//! let prebuilt = Prebuilt::fetch(Source::LATEST, "target/ovmf")
12//!     .expect("failed to update prebuilt");
13//! assert_eq!(
14//!     prebuilt.get_file(Arch::X64, FileType::Code),
15//!     Path::new("target/ovmf/x64/code.fd")
16//! );
17//! ```
18
19#![warn(missing_docs)]
20
21mod error;
22mod fetch;
23mod source_constants;
24
25use fetch::update_cache;
26use std::path::{Path, PathBuf};
27
28pub use error::Error;
29
30/// Which prebuilt to download.
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct Source {
33    /// Release tag name, e.g. "edk2-stable202408-r1".
34    pub tag: &'static str,
35
36    /// SHA-256 hash of the compressed tarball.
37    pub sha256: &'static str,
38}
39
40/// UEFI architecture.
41///
42/// Note that not all releases contain every architecture:
43/// * `Ia32` is available in `edk2-stable202508` and earlier releases.
44/// * `LoongArch64` is available in `edk2-stable202505` and later releases.
45/// * `Aarch64`, `Riscv64`, and `X64` are available in all releases.
46#[allow(missing_docs)]
47#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
48pub enum Arch {
49    Aarch64,
50    Ia32,
51    LoongArch64,
52    Riscv64,
53    X64,
54}
55
56impl Arch {
57    /// Convert to a string.
58    pub fn as_str(self) -> &'static str {
59        match self {
60            Self::Aarch64 => "aarch64",
61            Self::Ia32 => "ia32",
62            Self::LoongArch64 => "loongarch64",
63            Self::Riscv64 => "riscv64",
64            Self::X64 => "x64",
65        }
66    }
67}
68
69/// Type of file within the prebuilt archive.
70#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
71#[allow(missing_docs)]
72pub enum FileType {
73    Code,
74    Vars,
75    Shell,
76}
77
78impl FileType {
79    /// Convert to a string.
80    pub fn as_str(self) -> &'static str {
81        match self {
82            Self::Code => "code.fd",
83            Self::Vars => "vars.fd",
84            Self::Shell => "shell.efi",
85        }
86    }
87}
88
89/// Cached prebuilt.
90pub struct Prebuilt {
91    dir: PathBuf,
92}
93
94impl Prebuilt {
95    /// Fetch a prebuilt from a local cache. If the cache is out of
96    /// date, the prebuilt is downloaded and the cache is updated.
97    ///
98    /// The SHA-256 hash of the original prebuilt is stored in
99    /// `<prebuilt_dir>/sha256`. This is used to determine whether the
100    /// cache is up-to-date. Note that if some external process modifies
101    /// the cached files but leaves the `sha256` file unmodified, this
102    /// code will not detect that the cache is invalid.
103    ///
104    /// If the cache is updated, the downloaded prebuilt's hash will be
105    /// checked against [`source.sha256`]. An error will be
106    /// returned if the hash does not match, and the filesystem will not
107    /// be modified. This ensures that if you pin this library in
108    /// `Cargo.lock`, and use one of the [`Source`] associated
109    /// constants, the library will never unpack unverified files. This
110    /// provides some protection against a malicious attack modifying
111    /// the release tarballs on Github.
112    ///
113    /// [`source.sha256`]: Source::sha256
114    pub fn fetch<P: AsRef<Path>>(source: Source, prebuilt_dir: P) -> Result<Self, Error> {
115        let prebuilt_dir = prebuilt_dir.as_ref();
116
117        update_cache(source, prebuilt_dir)?;
118
119        Ok(Self {
120            dir: prebuilt_dir.to_owned(),
121        })
122    }
123
124    /// Get the path of a specific file within the cache.
125    pub fn get_file(&self, arch: Arch, file_type: FileType) -> PathBuf {
126        self.dir.join(arch.as_str()).join(file_type.as_str())
127    }
128}