From 92a70582d535abc9701f7e85b2a358adeca54155 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Mon, 28 Oct 2024 20:19:18 -0400 Subject: [PATCH 1/2] xtask: Install a simple logger This currently doesn't do anything, but in the next commit ovmf-prebuilt will be used to download OVMF files, and that library uses the logger. --- Cargo.lock | 1 + xtask/Cargo.toml | 3 ++- xtask/src/main.rs | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8bd770f52..2e6d5f329 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1155,6 +1155,7 @@ dependencies = [ "fs-err", "heck", "itertools", + "log", "lzma-rs", "mbrman", "nix", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index ba0f36c77..757a62bd3 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -12,6 +12,7 @@ fatfs = { version = "0.3.6", default-features = false, features = ["alloc", "std fs-err = "3.0.0" heck = "0.5.0" itertools = "0.13.0" +log.workspace = true lzma-rs = "0.3.0" mbrman = "0.5.1" nix = { version = "0.29.0", default-features = false, features = ["fs"] } @@ -24,5 +25,5 @@ sha2 = "0.10.6" syn = { version = "2.0.0", features = ["full"] } tar = "0.4.38" tempfile = "3.6.0" -walkdir = "2.4.0" ureq = "2.8.0" +walkdir = "2.4.0" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 53e46ded0..8c9a008c2 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -17,6 +17,7 @@ use arch::UefiArch; use cargo::{Cargo, CargoAction, Feature, Package, TargetTypes}; use clap::Parser; use itertools::Itertools; +use log::{LevelFilter, Metadata, Record}; use opt::{Action, BuildOpt, ClippyOpt, CovOpt, DocOpt, Opt, QemuOpt, TpmVersion}; use std::process::Command; use util::run_cmd; @@ -322,9 +323,33 @@ fn has_cmd(target_cmd: &str) -> bool { run_cmd(cmd).is_ok() } +fn install_logger() { + struct Logger; + + impl log::Log for Logger { + fn enabled(&self, _: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + println!("[{}] {}", record.level(), record.args()); + } + + fn flush(&self) {} + } + + static LOGGER: Logger = Logger; + + log::set_logger(&LOGGER) + .map(|()| log::set_max_level(LevelFilter::Info)) + .unwrap(); +} + fn main() -> Result<()> { let opt = Opt::parse(); + install_logger(); + match &opt.action { Action::Build(build_opt) => build(build_opt), Action::CheckRaw(_) => check_raw::check_raw(), From 98d1ed24b31298ae7403d4d5141b1af3d8c1c605 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 22 Oct 2024 21:54:35 -0400 Subject: [PATCH 2/2] xtask: Use ovmf-prebuilt to download OVMF files This allows some code and dependencies to be deleted. --- Cargo.lock | 17 ++-- xtask/Cargo.toml | 6 +- xtask/src/qemu.rs | 201 +++++++++------------------------------------- 3 files changed, 48 insertions(+), 176 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e6d5f329..fe3a7bf93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -452,13 +452,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] -name = "os_info" -version = "3.8.2" +name = "ovmf-prebuilt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +checksum = "e0839ec8849e7a5443268f3369458c2dadc409ef807ab10b7f77d4bb9bf1708d" dependencies = [ "log", - "windows-sys 0.52.0", + "lzma-rs", + "sha2", + "tar", + "ureq", ] [[package]] @@ -1156,19 +1159,15 @@ dependencies = [ "heck", "itertools", "log", - "lzma-rs", "mbrman", "nix", - "os_info", + "ovmf-prebuilt", "proc-macro2", "quote", "regex", "serde_json", - "sha2", "syn 2.0.87", - "tar", "tempfile", - "ureq", "walkdir", ] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 757a62bd3..0c77d25d4 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -13,17 +13,13 @@ fs-err = "3.0.0" heck = "0.5.0" itertools = "0.13.0" log.workspace = true -lzma-rs = "0.3.0" mbrman = "0.5.1" nix = { version = "0.29.0", default-features = false, features = ["fs"] } -os_info = { version = "3.6.0", default-features = false } +ovmf-prebuilt = "0.2.0" proc-macro2 = { version = "1.0.46", features = ["span-locations"] } quote = "1.0.21" regex = "1.10.2" serde_json = "1.0.73" -sha2 = "0.10.6" syn = { version = "2.0.0", features = ["full"] } -tar = "0.4.38" tempfile = "3.6.0" -ureq = "2.8.0" walkdir = "2.4.0" diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index d0ac4cd60..2e3ac4812 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -6,17 +6,15 @@ use crate::tpm::Swtpm; use crate::util::command_to_string; use crate::{net, platform}; use anyhow::{bail, Context, Result}; +use ovmf_prebuilt::{FileType, Prebuilt, Source}; 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, Cursor, Read, Write}; +use std::io::{BufRead, BufReader, 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}; @@ -38,161 +36,41 @@ const ENV_VAR_OVMF_VARS: &str = "OVMF_VARS"; /// Environment variable for overriding the path of the OVMF shell file. const ENV_VAR_OVMF_SHELL: &str = "OVMF_SHELL"; -/// 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 = 5 * 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; +impl From for ovmf_prebuilt::Arch { + fn from(arch: UefiArch) -> Self { + match arch { + UefiArch::AArch64 => Self::Aarch64, + UefiArch::IA32 => Self::Ia32, + UefiArch::X86_64 => Self::X64, } - - 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()); +/// Get a user-provided path for the given OVMF file type. +/// +/// This uses the command-line arg if present, otherwise it falls back to an +/// environment variable. If neither is present, returns `None`. +fn get_user_provided_path(file_type: FileType, opt: &QemuOpt) -> Option { + let opt_path; + let var_name; + match file_type { + FileType::Code => { + opt_path = &opt.ovmf_code; + var_name = ENV_VAR_OVMF_CODE; } - } - - 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, - Vars, - Shell, -} - -impl OvmfFileType { - fn as_str(&self) -> &'static str { - match self { - Self::Code => "code", - Self::Vars => "vars", - Self::Shell => "shell", + FileType::Vars => { + opt_path = &opt.ovmf_vars; + var_name = ENV_VAR_OVMF_VARS; } - } - - fn extension(&self) -> &'static str { - match self { - Self::Code | Self::Vars => "fd", - Self::Shell => "efi", + FileType::Shell => { + opt_path = &None; + var_name = ENV_VAR_OVMF_SHELL; } } - - /// Get a user-provided path for the given OVMF file type. - /// - /// This uses the command-line arg if present, otherwise it falls back to an - /// environment variable. If neither is present, returns `None`. - fn get_user_provided_path(self, opt: &QemuOpt) -> Option { - let opt_path; - let var_name; - match self { - Self::Code => { - opt_path = &opt.ovmf_code; - var_name = ENV_VAR_OVMF_CODE; - } - Self::Vars => { - opt_path = &opt.ovmf_vars; - var_name = ENV_VAR_OVMF_VARS; - } - Self::Shell => { - opt_path = &None; - var_name = ENV_VAR_OVMF_SHELL; - } - } - if let Some(path) = opt_path { - Some(path.clone()) - } else { - env::var_os(var_name).map(PathBuf::from) - } + if let Some(path) = opt_path { + Some(path.clone()) + } else { + env::var_os(var_name).map(PathBuf::from) } } @@ -210,8 +88,8 @@ impl OvmfPaths { /// 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) { + fn find_ovmf_file(file_type: FileType, opt: &QemuOpt, arch: UefiArch) -> Result { + if let Some(path) = get_user_provided_path(file_type, opt) { // The user provided an exact path to use; verify that it // exists. if path.exists() { @@ -224,22 +102,21 @@ impl OvmfPaths { ); } } else { - let prebuilt_dir = update_prebuilt()?; + let prebuilt = Prebuilt::fetch(Source { + tag: OVMF_PREBUILT_TAG, + sha256: OVMF_PREBUILT_HASH, + }, OVMF_PREBUILT_DIR)?; - Ok(prebuilt_dir.join(format!( - "{arch}/{}.{}", - file_type.as_str(), - file_type.extension() - ))) + Ok(prebuilt.get_file(arch.into(), file_type)) } } /// Find path to OVMF files by the strategy documented for /// [`Self::find_ovmf_file`]. fn find(opt: &QemuOpt, arch: UefiArch) -> Result { - let code = Self::find_ovmf_file(OvmfFileType::Code, opt, arch)?; - let vars = Self::find_ovmf_file(OvmfFileType::Vars, opt, arch)?; - let shell = Self::find_ovmf_file(OvmfFileType::Shell, opt, arch)?; + let code = Self::find_ovmf_file(FileType::Code, opt, arch)?; + let vars = Self::find_ovmf_file(FileType::Vars, opt, arch)?; + let shell = Self::find_ovmf_file(FileType::Shell, opt, arch)?; Ok(Self { code, vars, shell }) }