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}