diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4f6e03a91..4ef616e3f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,10 +22,10 @@ jobs: - uses: Swatinem/rust-cache@v2 - - name: Install qemu and OVMF + - name: Install qemu run: | sudo apt-get update - sudo apt-get install qemu-system-arm qemu-efi-aarch64 -y + sudo apt-get install qemu-system-arm -y - name: Run VM tests run: cargo xtask run --target aarch64 --headless --ci @@ -40,10 +40,10 @@ jobs: - uses: Swatinem/rust-cache@v2 - - name: Install qemu and OVMF + - name: Install qemu run: | sudo apt-get update - sudo apt-get install qemu-system-x86 ovmf swtpm -y + sudo apt-get install qemu-system-x86 swtpm -y - name: Run VM tests run: cargo xtask run --target x86_64 --headless --ci --tpm=v1 @@ -58,10 +58,10 @@ jobs: - uses: Swatinem/rust-cache@v2 - - name: Install qemu and OVMF + - name: Install qemu run: | sudo apt-get update - sudo apt-get install qemu-system-x86 ovmf-ia32 swtpm -y + sudo apt-get install qemu-system-x86 swtpm -y - name: Run VM tests run: cargo xtask run --target ia32 --headless --ci --tpm=v2 @@ -168,10 +168,10 @@ jobs: - name: Checkout sources uses: actions/checkout@v3 - - name: Install qemu and OVMF + - name: Install qemu run: | sudo apt-get update - sudo apt-get install qemu-system-x86 ovmf -y + sudo apt-get install qemu-system-x86 -y - name: Enable nightly toolchain run: cp .github/workflows/nightly_toolchain.toml rust-toolchain.toml diff --git a/Cargo.lock b/Cargo.lock index 963a21e2e..36fba5dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.0.1" @@ -23,6 +29,12 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "basic-toml" version = "0.1.2" @@ -71,6 +83,21 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "byteorder" version = "1.4.3" @@ -129,6 +156,59 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.8.1" @@ -176,6 +256,37 @@ dependencies = [ "log", ] +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs-err" version = "2.9.0" @@ -188,6 +299,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "glob" version = "0.3.1" @@ -206,6 +327,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "instant" version = "0.1.12" @@ -241,6 +372,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.144" @@ -262,6 +402,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "mbrman" version = "0.5.2" @@ -281,6 +431,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "nix" version = "0.26.2" @@ -309,6 +468,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "proc-macro2" version = "1.0.56" @@ -359,6 +524,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -385,6 +559,21 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rustix" version = "0.37.19" @@ -399,6 +588,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "ryu" version = "1.0.13" @@ -414,6 +615,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.162" @@ -454,6 +665,23 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "static_assertions" version = "1.1.0" @@ -488,6 +716,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.5.0" @@ -496,7 +735,7 @@ checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys 0.45.0", ] @@ -530,6 +769,21 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "trybuild" version = "1.0.80" @@ -545,6 +799,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "ucs2" version = "0.3.2" @@ -621,12 +881,66 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "594cc87e268a7b43d625d46c63cf1605d0e61bf66e4b1cd58c058ec0191e1f81" +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "ureq" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "url", + "webpki", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.3.3" @@ -637,6 +951,89 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.15", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + [[package]] name = "winapi" version = "0.3.9" @@ -809,6 +1206,15 @@ dependencies = [ "tap", ] +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + [[package]] name = "xtask" version = "0.0.0" @@ -819,6 +1225,7 @@ dependencies = [ "fs-err", "heck", "itertools", + "lzma-rs", "mbrman", "nix", "os_info", @@ -826,7 +1233,10 @@ dependencies = [ "quote", "regex", "serde_json", + "sha2", "syn 2.0.15", + "tar", "tempfile", + "ureq", "walkdir", ] diff --git a/Cargo.toml b/Cargo.toml index d8a49f9b8..5f7d241ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,8 @@ uefi = { path = "uefi" } uefi-macros = { path = "uefi-macros" } uefi-raw = { path = "uefi-raw" } uefi-services = { path = "uefi-services" } + +# Enable optimization for xtask itself, not for its dependencies. This speeds up +# OVMF prebuilt decompression without much increase in compilation time. +[profile.dev.package.xtask] +opt-level = 3 diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 311ef2243..13bd2b8ed 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -11,6 +11,7 @@ fatfs = { version = "0.3.6", default-features = false, features = ["alloc", "std fs-err = "2.6.0" heck = "0.4.0" itertools = "0.10.5" +lzma-rs = "0.3.0" mbrman = "0.5.1" nix = { version = "0.26.1", default-features = false, features = ["fs"] } os_info = { version = "3.6.0", default-features = false } @@ -18,6 +19,9 @@ proc-macro2 = { version = "1.0.46", features = ["span-locations"] } quote = "1.0.21" regex = "1.8.1" serde_json = "1.0.73" +sha2 = "0.10.6" syn = { version = "2.0.0", features = ["full"] } +tar = "0.4.38" tempfile = "3.5.0" walkdir = "2.3.3" +ureq = "2.6.2" diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index 44bf38b39..a63f1116a 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -5,18 +5,142 @@ use crate::pipe::Pipe; use crate::tpm::Swtpm; use crate::util::command_to_string; use crate::{net, platform}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use regex::bytes::Regex; use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; use std::env; use std::ffi::OsString; -use std::io::{BufRead, BufReader, Read, Write}; +use std::io::{BufRead, BufReader, Cursor, Read, Write}; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Stdio}; +use tar::Archive; use tempfile::TempDir; +use ureq::Agent; #[cfg(target_os = "linux")] use {std::fs::Permissions, std::os::unix::fs::PermissionsExt}; +/// Name of the ovmf-prebuilt release tag. +const OVMF_PREBUILT_TAG: &str = "edk2-stable202211-r1"; + +/// SHA-256 hash of the release tarball. +const OVMF_PREBUILT_HASH: &str = "b085cfe18fd674bf70a31af1dc3e991bcd25cb882981c6d3523d81260f1e0d12"; + +/// Directory into which the prebuilts will be download (relative to the repo root). +const OVMF_PREBUILT_DIR: &str = "target/ovmf"; + +/// Environment variable for overriding the path of the OVMF code file. +const ENV_VAR_OVMF_CODE: &str = "OVMF_CODE"; + +/// Environment variable for overriding the path of the OVMF vars file. +const ENV_VAR_OVMF_VARS: &str = "OVMF_VARS"; + +/// Download `url` and return the raw data. +fn download_url(url: &str) -> Result> { + let agent: Agent = ureq::AgentBuilder::new() + .user_agent("uefi-rs-ovmf-downloader") + .build(); + + // Limit the size of the download. + let max_size_in_bytes = 4 * 1024 * 1024; + + // Download the file. + println!("downloading {url}"); + let resp = agent.get(url).call()?; + let mut data = Vec::with_capacity(max_size_in_bytes); + resp.into_reader() + .take(max_size_in_bytes.try_into().unwrap()) + .read_to_end(&mut data)?; + println!("received {} bytes", data.len()); + + Ok(data) +} + +// Extract the tarball's files into `prebuilt_dir`. +// +// `tarball_data` is raw decompressed tar data. +fn extract_prebuilt(tarball_data: &[u8], prebuilt_dir: &Path) -> Result<()> { + let cursor = Cursor::new(tarball_data); + let mut archive = Archive::new(cursor); + + // Extract each file entry. + for entry in archive.entries()? { + let mut entry = entry?; + + // Skip directories. + if entry.size() == 0 { + continue; + } + + let path = entry.path()?; + // Strip the leading directory, which is the release name. + let path: PathBuf = path.components().skip(1).collect(); + + let dir = path.parent().unwrap(); + let dst_dir = prebuilt_dir.join(dir); + let dst_path = prebuilt_dir.join(path); + println!("unpacking to {}", dst_path.display()); + fs_err::create_dir_all(dst_dir)?; + entry.unpack(dst_path)?; + } + + Ok(()) +} + +/// Update the local copy of the prebuilt OVMF files. Does nothing if the local +/// copy is already up to date. +fn update_prebuilt() -> Result { + let prebuilt_dir = Path::new(OVMF_PREBUILT_DIR); + let hash_path = prebuilt_dir.join("sha256"); + + // Check if the hash file already has the expected hash in it. If so, assume + // that we've already got the correct prebuilt downloaded and unpacked. + if let Ok(current_hash) = fs_err::read_to_string(&hash_path) { + if current_hash == OVMF_PREBUILT_HASH { + return Ok(prebuilt_dir.to_path_buf()); + } + } + + let base_url = "https://github.com/rust-osdev/ovmf-prebuilt/releases/download"; + let url = format!( + "{base_url}/{release}/{release}-bin.tar.xz", + release = OVMF_PREBUILT_TAG + ); + + let data = download_url(&url)?; + + // Validate the hash. + let actual_hash = format!("{:x}", Sha256::digest(&data)); + if actual_hash != OVMF_PREBUILT_HASH { + bail!( + "file hash {actual_hash} does not match {}", + OVMF_PREBUILT_HASH + ); + } + + // Unpack the tarball. + println!("decompressing tarball"); + let mut decompressed = Vec::new(); + let mut compressed = Cursor::new(data); + lzma_rs::xz_decompress(&mut compressed, &mut decompressed)?; + + // Clear out the existing prebuilt dir, if present. + let _ = fs_err::remove_dir_all(prebuilt_dir); + + // Extract the files. + extract_prebuilt(&decompressed, prebuilt_dir)?; + + // Rename the x64 directory to x86_64, to match `Arch::as_str`. + fs_err::rename(prebuilt_dir.join("x64"), prebuilt_dir.join("x86_64"))?; + + // Write out the hash file. When we upgrade to a new release of + // ovmf-prebuilt, the hash will no longer match, triggering a fresh + // download. + fs_err::write(&hash_path, actual_hash)?; + + Ok(prebuilt_dir.to_path_buf()) +} + #[derive(Clone, Copy, Debug)] enum OvmfFileType { Code, @@ -41,11 +165,11 @@ impl OvmfFileType { match self { Self::Code => { opt_path = &opt.ovmf_code; - var_name = "OVMF_CODE"; + var_name = ENV_VAR_OVMF_CODE; } Self::Vars => { opt_path = &opt.ovmf_vars; - var_name = "OVMF_VARS"; + var_name = ENV_VAR_OVMF_VARS; } } if let Some(path) = opt_path { @@ -62,177 +186,14 @@ struct OvmfPaths { } impl OvmfPaths { - /// If OVMF files can not or should not be found at well-known locations, - /// this optional environment variable can point to it. - /// - /// This variable points to the `_CODE.fd` file. - - const ENV_VAR_OVMF_CODE: &'static str = "OVMF_CODE"; - /// If OVMF files can not or should not be found at well-known locations, - /// this optional environment variable can point to it. - /// - /// This variable points to the `_VARS.fd` file. - const ENV_VAR_OVMF_VARS: &'static str = "OVMF_VARS"; - - fn get_path(&self, file_type: OvmfFileType) -> &Path { - match file_type { - OvmfFileType::Code => &self.code, - OvmfFileType::Vars => &self.vars, - } - } - - /// Get the Arch Linux OVMF paths for the given guest arch. - fn arch_linux(arch: UefiArch) -> Self { - match arch { - // Package "edk2-armvirt". - UefiArch::AArch64 => Self { - code: "/usr/share/edk2-armvirt/aarch64/QEMU_CODE.fd".into(), - vars: "/usr/share/edk2-armvirt/aarch64/QEMU_VARS.fd".into(), - }, - // Package "edk2-ovmf". - UefiArch::IA32 => Self { - code: "/usr/share/edk2-ovmf/ia32/OVMF_CODE.fd".into(), - vars: "/usr/share/edk2-ovmf/ia32/OVMF_VARS.fd".into(), - }, - // Package "edk2-ovmf". - UefiArch::X86_64 => Self { - code: "/usr/share/edk2-ovmf/x64/OVMF_CODE.fd".into(), - vars: "/usr/share/edk2-ovmf/x64/OVMF_VARS.fd".into(), - }, - } - } - - /// Get the CentOS OVMF paths for the given guest arch. - fn centos_linux(arch: UefiArch) -> Option { - match arch { - // Package "edk2-aarch64". - UefiArch::AArch64 => Some(Self { - code: "/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw".into(), - vars: "/usr/share/edk2/aarch64/vars-template-pflash.raw".into(), - }), - // There's no official ia32 package. - UefiArch::IA32 => None, - // Package "edk2-ovmf". - UefiArch::X86_64 => Some(Self { - // Use the `.secboot` variant because the CentOS package - // doesn't have a plain "OVMF_CODE.fd". - code: "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd".into(), - vars: "/usr/share/edk2/ovmf/OVMF_VARS.fd".into(), - }), - } - } - - /// Get the Debian OVMF paths for the given guest arch. These paths - /// also work on Ubuntu. - fn debian_linux(arch: UefiArch) -> Self { - match arch { - // Package "qemu-efi-aarch64". - UefiArch::AArch64 => Self { - code: "/usr/share/AAVMF/AAVMF_CODE.fd".into(), - vars: "/usr/share/AAVMF/AAVMF_VARS.fd".into(), - }, - // Package "ovmf-ia32". - UefiArch::IA32 => Self { - code: "/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd".into(), - vars: "/usr/share/OVMF/OVMF32_VARS_4M.fd".into(), - }, - // Package "ovmf". - UefiArch::X86_64 => Self { - code: "/usr/share/OVMF/OVMF_CODE.fd".into(), - vars: "/usr/share/OVMF/OVMF_VARS.fd".into(), - }, - } - } - - /// Get the Fedora OVMF paths for the given guest arch. - fn fedora_linux(arch: UefiArch) -> Self { - match arch { - // Package "edk2-aarch64". - UefiArch::AArch64 => Self { - code: "/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw".into(), - vars: "/usr/share/edk2/aarch64/vars-template-pflash.raw".into(), - }, - // Package "edk2-ovmf-ia32". - UefiArch::IA32 => Self { - code: "/usr/share/edk2/ovmf-ia32/OVMF_CODE.fd".into(), - vars: "/usr/share/edk2/ovmf-ia32/OVMF_VARS.fd".into(), - }, - // Package "edk2-ovmf". - UefiArch::X86_64 => Self { - code: "/usr/share/edk2/ovmf/OVMF_CODE.fd".into(), - vars: "/usr/share/edk2/ovmf/OVMF_VARS.fd".into(), - }, - } - } - - /// If a user uses NixOS, this function returns an error if the user didn't - /// set the environment variables `OVMF_CODE` and `OVMF_VARS`. - /// - /// It returns nothing as the environment variables are resolved at a - /// higher level. NixOS doesn't have globally installed software (without - /// hacky and non-idiomatic workarounds). - fn assist_nixos_users() -> Result<()> { - let os_info = os_info::get(); - if os_info.os_type() == os_info::Type::NixOS { - let code = env::var_os(Self::ENV_VAR_OVMF_CODE); - let vars = env::var_os(Self::ENV_VAR_OVMF_VARS); - if !matches!((code, vars), (Some(_), Some(_))) { - return Err(anyhow!("Run `$ nix-shell` for OVMF files.")); - } - } - Ok(()) - } - - /// Get the Windows OVMF paths for the given guest arch. - fn windows(arch: UefiArch) -> Self { - match arch { - UefiArch::AArch64 => Self { - code: r"C:\Program Files\qemu\share\edk2-aarch64-code.fd".into(), - vars: r"C:\Program Files\qemu\share\edk2-arm-vars.fd".into(), - }, - UefiArch::IA32 => Self { - code: r"C:\Program Files\qemu\share\edk2-i386-code.fd".into(), - vars: r"C:\Program Files\qemu\share\edk2-i386-vars.fd".into(), - }, - UefiArch::X86_64 => Self { - code: r"C:\Program Files\qemu\share\edk2-x86_64-code.fd".into(), - // There's no x86_64 vars file, but the i386 one works. - vars: r"C:\Program Files\qemu\share\edk2-i386-vars.fd".into(), - }, - } - } - - /// Get candidate paths where OVMF code/vars might exist for the - /// given guest arch and host platform. - fn get_candidate_paths(arch: UefiArch) -> Result> { - let mut candidates = Vec::new(); - if platform::is_linux() { - candidates.push(Self::arch_linux(arch)); - if let Some(candidate) = Self::centos_linux(arch) { - candidates.push(candidate); - } - candidates.push(Self::debian_linux(arch)); - candidates.push(Self::fedora_linux(arch)); - Self::assist_nixos_users()?; - } - if platform::is_windows() { - candidates.push(Self::windows(arch)); - } - Ok(candidates) - } - /// Search for an OVMF file (either code or vars). /// /// There are multiple locations where a file is searched at in the following /// priority: - /// 1. User-defined location: See [`OvmfFileType::get_user_provided_path`] - /// 2. Well-known location of common Linux distributions by using the - /// paths in `candidates`. - fn find_ovmf_file( - file_type: OvmfFileType, - opt: &QemuOpt, - candidates: &[Self], - ) -> Result { + /// 1. Command-line arg + /// 2. Environment variable + /// 3. Prebuilt file (automatically downloaded) + fn find_ovmf_file(file_type: OvmfFileType, opt: &QemuOpt, arch: UefiArch) -> Result { if let Some(path) = file_type.get_user_provided_path(opt) { // The user provided an exact path to use; verify that it // exists. @@ -246,31 +207,17 @@ impl OvmfPaths { ); } } else { - for candidate in candidates { - let path = candidate.get_path(file_type); - if path.exists() { - return Ok(path.to_owned()); - } - } + let prebuilt_dir = update_prebuilt()?; - bail!( - "no ovmf {} file found in candidates: {:?}", - file_type.as_str(), - candidates - .iter() - .map(|c| c.get_path(file_type)) - .collect::>(), - ); + Ok(prebuilt_dir.join(format!("{arch}/{}.fd", file_type.as_str()))) } } /// Find path to OVMF files by the strategy documented for /// [`Self::find_ovmf_file`]. fn find(opt: &QemuOpt, arch: UefiArch) -> Result { - let candidates = Self::get_candidate_paths(arch)?; - - let code = Self::find_ovmf_file(OvmfFileType::Code, opt, &candidates)?; - let vars = Self::find_ovmf_file(OvmfFileType::Vars, opt, &candidates)?; + let code = Self::find_ovmf_file(OvmfFileType::Code, opt, arch)?; + let vars = Self::find_ovmf_file(OvmfFileType::Vars, opt, arch)?; Ok(Self { code, vars }) }