Auto-fetch OVMF
This commit is contained in:
parent
3b6dce674d
commit
aa5ea3fb8f
1645
Cargo.lock
generated
1645
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,16 @@ version = "0.2.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.10"
|
||||
cpio_reader = "0.1"
|
||||
derive_more = "0.99"
|
||||
env_logger = "0.10"
|
||||
error-stack = "0.2"
|
||||
fatfs = "0.3"
|
||||
log = "0.4"
|
||||
fatfs = "0.3"
|
||||
log = "0.4"
|
||||
rpm = "0.11"
|
||||
zstd = "0.12"
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.11"
|
||||
default-features = false
|
||||
features = ["rustls-tls", "blocking"]
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
use std::fs;
|
||||
|
||||
use {
|
||||
error_stack::{bail, report, Context, IntoReport, Result, ResultExt},
|
||||
derive_more::{Deref, DerefMut, Display},
|
||||
error_stack::{bail, ensure, report, Context, IntoReport, Result, ResultExt},
|
||||
fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek},
|
||||
std::{fmt::Display, fs::File, io, path::Path, process::Command},
|
||||
std::{
|
||||
fmt::Display,
|
||||
fs::File,
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
process::Command,
|
||||
},
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
|
@ -116,6 +121,8 @@ fn get_fs() -> Result<FileSystem<impl ReadWriteSeek>, io::Error> {
|
|||
}
|
||||
|
||||
fn build(release: bool, target: Target) -> Result<(), Error> {
|
||||
fetch_ovmf().change_context(Error::OvmfFetch)?;
|
||||
|
||||
let fs = get_fs().change_context(Error::Io)?;
|
||||
let mut com = Command::new("cargo");
|
||||
com.current_dir("kernel");
|
||||
|
@ -124,11 +131,8 @@ fn build(release: bool, target: Target) -> Result<(), Error> {
|
|||
com.arg("-r");
|
||||
}
|
||||
|
||||
match target {
|
||||
Target::Riscv64Virt => {
|
||||
com.args(["--target", "targets/riscv64-virt-ableos.json"]);
|
||||
}
|
||||
_ => {}
|
||||
if target == Target::Riscv64Virt {
|
||||
com.args(["--target", "targets/riscv64-virt-ableos.json"]);
|
||||
}
|
||||
|
||||
match com.status() {
|
||||
|
@ -165,10 +169,7 @@ fn run(release: bool, target: Target) -> Result<(), Error> {
|
|||
if target == Target::X86_64 {
|
||||
#[rustfmt::skip]
|
||||
com.args([
|
||||
"-bios",
|
||||
std::env::var("REPBUILD_QEMU_FIRMWARE_PATH")
|
||||
.as_deref()
|
||||
.unwrap_or("/usr/share/ovmf/x64/OVMF_CODE.fd"),
|
||||
"-bios", "target/OVMF_CODE.fd",
|
||||
"-drive", "file=target/disk.img,format=raw",
|
||||
"-m", "4G",
|
||||
// "-serial", "stdio",
|
||||
|
@ -217,33 +218,121 @@ fn run(release: bool, target: Target) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fetch_ovmf() -> Result<(), OvmfFetchError> {
|
||||
const OVMF_RPM_URL: &str = "https://kojipkgs.fedoraproject.org/packages/edk2/20230524/3.fc38/noarch/edk2-ovmf-20230524-3.fc38.noarch.rpm";
|
||||
|
||||
let mut file = match std::fs::metadata("target/OVMF_CODE.fd") {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open("target/OVMF_CODE.fd")
|
||||
.into_report()
|
||||
.change_context(OvmfFetchError::Io)?,
|
||||
Ok(_) => return Ok(()),
|
||||
Err(e) => return Err(report!(e).change_context(OvmfFetchError::Io)),
|
||||
};
|
||||
|
||||
log::info!("No OVMF found, downloading…");
|
||||
let rpm = rpm::RPMPackage::parse(
|
||||
&mut std::convert::identity::<reqwest::Result<_>>((|| {
|
||||
reqwest::blocking::get(OVMF_RPM_URL)?.bytes()
|
||||
})())
|
||||
.into_report()
|
||||
.change_context(OvmfFetchError::Fetch)?
|
||||
.as_ref(),
|
||||
)
|
||||
.map_err(RpmError)
|
||||
.into_report()
|
||||
.change_context(OvmfFetchError::RpmParse)?;
|
||||
|
||||
ensure!(
|
||||
rpm.metadata
|
||||
.get_payload_compressor()
|
||||
.map_err(RpmError)
|
||||
.into_report()
|
||||
.change_context(OvmfFetchError::RpmParse)?
|
||||
== rpm::CompressionType::Zstd,
|
||||
OvmfFetchError::UnsupportedCompression,
|
||||
);
|
||||
|
||||
file.write_all(
|
||||
cpio_reader::iter_files(
|
||||
&zstd::decode_all(std::io::Cursor::new(rpm.content))
|
||||
.into_report()
|
||||
.change_context(OvmfFetchError::Zstd)?,
|
||||
)
|
||||
.find(|file| file.name() == "./usr/share/edk2/ovmf/OVMF_CODE.fd")
|
||||
.ok_or_else(|| report!(OvmfFetchError::NoFileFound))?
|
||||
.file(),
|
||||
)
|
||||
.into_report()
|
||||
.change_context(OvmfFetchError::Io)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Deref, DerefMut)]
|
||||
struct RpmError(rpm::RPMError);
|
||||
impl Context for RpmError {}
|
||||
|
||||
// Ehhh?? I am pretty sure they just forgot :ferrisClueless:
|
||||
unsafe impl Sync for RpmError {}
|
||||
unsafe impl Send for RpmError {}
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
enum OvmfFetchError {
|
||||
#[display(fmt = "Failed to fetch OVMF package")]
|
||||
Fetch,
|
||||
#[display(fmt = "RPM parse error")]
|
||||
RpmParse,
|
||||
#[display(fmt = "Unsupported compression (ZSTD is the only supported one)")]
|
||||
UnsupportedCompression,
|
||||
#[display(fmt = "Decompression error")]
|
||||
Zstd,
|
||||
#[display(fmt = "Requested file not found in package")]
|
||||
NoFileFound,
|
||||
#[display(fmt = "IO Error")]
|
||||
Io,
|
||||
}
|
||||
|
||||
impl Context for OvmfFetchError {}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Target {
|
||||
X86_64,
|
||||
Riscv64Virt,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
enum Error {
|
||||
#[display(fmt = "Failed to build the kernel")]
|
||||
Build,
|
||||
#[display(fmt = "Missing or invalid subcommand (available: build, run)")]
|
||||
InvalidSubCom,
|
||||
#[display(fmt = "IO Error")]
|
||||
Io,
|
||||
#[display(fmt = "Failed to spawn a process")]
|
||||
ProcessSpawn,
|
||||
#[display(fmt = "Failed to fetch UEFI firmware")]
|
||||
OvmfFetch,
|
||||
#[display(fmt = "QEMU Error: {}", "fmt_qemu_err(*_0)")]
|
||||
Qemu(Option<i32>),
|
||||
}
|
||||
|
||||
impl Context for Error {}
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Build => f.write_str("failed to build the kernel"),
|
||||
Self::InvalidSubCom => {
|
||||
f.write_str("missing or invalid subcommand (available: build, run)")
|
||||
|
||||
fn fmt_qemu_err(e: Option<i32>) -> impl Display {
|
||||
struct W(Option<i32>);
|
||||
impl Display for W {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(c) = self.0 {
|
||||
c.fmt(f)
|
||||
} else {
|
||||
f.write_str("Interrupted by signal")
|
||||
}
|
||||
Self::Io => f.write_str("IO error"),
|
||||
Self::ProcessSpawn => f.write_str("failed to spawn a process"),
|
||||
Self::Qemu(Some(c)) => write!(f, "QEMU Error: {c}"),
|
||||
Self::Qemu(None) => write!(f, "QEMU Error: interrupted by signal"),
|
||||
}
|
||||
}
|
||||
|
||||
W(e)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue