ableos/repbuild/src/main.rs

340 lines
9.7 KiB
Rust
Raw Normal View History

2023-05-06 06:50:24 -05:00
use {
2023-07-17 09:36:39 -05:00
derive_more::Display,
error_stack::{bail, report, Context, IntoReport, Result, ResultExt},
2023-05-06 06:50:24 -05:00
fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek},
2023-07-12 20:21:33 -05:00
std::{
fmt::Display,
fs::File,
io::{self, Write},
path::Path,
process::Command,
},
2023-05-06 06:50:24 -05:00
};
2023-03-30 16:43:04 -05:00
fn main() -> Result<(), Error> {
env_logger::init();
let mut args = std::env::args();
args.next();
2023-06-13 21:03:09 -05:00
2023-07-08 23:21:27 -05:00
// let disk_meta = fs::metadata("target/disk.img").unwrap();
// let config_meta = fs::metadata("system.toml").unwrap();
2023-06-13 21:03:09 -05:00
2023-07-08 23:21:27 -05:00
// if disk_meta.modified().unwrap() < config_meta.modified().unwrap() {
// // TODO: work on adding in system.toml support
// // TODO: rebuild the disk
// }
2023-03-30 16:43:04 -05:00
match args.next().as_deref() {
Some("build" | "b") => {
let mut release = false;
let mut target = Target::X86_64;
for arg in args {
if arg == "-r" || arg == "--release" {
release = true;
2023-07-17 09:36:39 -05:00
} else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
2023-03-30 16:43:04 -05:00
target = Target::Riscv64Virt;
2023-07-17 09:36:39 -05:00
} else if arg == "aarch" || arg == "arm64" {
2023-07-13 22:41:09 -05:00
target = Target::Aarch64;
2023-07-17 09:36:39 -05:00
} else {
return Err(report!(Error::InvalidSubCom));
2023-07-13 22:41:09 -05:00
}
2023-03-30 16:43:04 -05:00
}
2022-08-03 02:11:51 -05:00
2023-03-30 16:43:04 -05:00
build(release, target).change_context(Error::Build)
}
Some("run" | "r") => {
let mut release = false;
let mut target = Target::X86_64;
for arg in args {
if arg == "-r" || arg == "--release" {
release = true;
2023-07-17 09:36:39 -05:00
} else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
2023-03-30 16:43:04 -05:00
target = Target::Riscv64Virt;
2023-07-17 09:36:39 -05:00
} else if arg == "aarch" || arg == "arm64" {
2023-07-13 22:41:09 -05:00
target = Target::Aarch64;
2023-07-17 09:36:39 -05:00
} else {
return Err(report!(Error::InvalidSubCom));
2023-07-13 22:41:09 -05:00
}
2023-03-30 16:43:04 -05:00
}
2022-08-03 02:11:51 -05:00
2023-03-30 16:43:04 -05:00
build(release, target)?;
run(release, target)
}
Some("help" | "h") => {
println!(concat!(
"AbleOS RepBuild\n",
"Subcommands:\n",
" build (b): Build a bootable disk image\n",
" help (h): Print this message\n",
" run (r): Build and run AbleOS in QEMU\n\n",
"Options for build and run:\n",
" -r: build in release mode",
" [target]: sets target"
),);
Ok(())
}
_ => Err(report!(Error::InvalidSubCom)),
}
2022-08-03 02:11:51 -05:00
}
2023-03-30 16:43:04 -05:00
fn get_fs() -> Result<FileSystem<impl ReadWriteSeek>, io::Error> {
let path = Path::new("target/disk.img");
match std::fs::metadata(path) {
Err(e) if e.kind() == io::ErrorKind::NotFound => (),
Err(e) => bail!(e),
Ok(_) => {
return FileSystem::new(
File::options().read(true).write(true).open(path)?,
FsOptions::new(),
)
.into_report()
}
}
let mut img = File::options()
.read(true)
.write(true)
.create(true)
.open(path)?;
img.set_len(1024 * 1024 * 64)?;
fatfs::format_volume(&mut img, FormatVolumeOptions::new())?;
let fs = FileSystem::new(img, FsOptions::new())?;
let bootdir = fs.root_dir().create_dir("efi")?.create_dir("boot")?;
io::copy(
&mut File::open("limine/BOOTX64.EFI")
.into_report()
.attach_printable("copying Limine bootloader (have you pulled the submodule?)")?,
&mut bootdir.create_file("bootx64.efi")?,
)?;
io::copy(
&mut File::open("limine/BOOTAA64.EFI")
.into_report()
.attach_printable(
"copying Limine bootloader arm version (have you pulled the submodule?)",
)?,
&mut bootdir.create_file("bootaa64.efi")?,
)?;
2023-03-30 16:43:04 -05:00
io::copy(
&mut File::open("repbuild/limine.cfg")?,
&mut fs.root_dir().create_file("limine.cfg")?,
)?;
io::copy(
&mut File::open("repbuild/background.bmp")?,
&mut fs.root_dir().create_file("background.bmp")?,
)?;
io::copy(
&mut File::open("repbuild/holeybytes/inf_loop.hb")?,
&mut fs.root_dir().create_file("inf_loop.hb")?,
)?;
2023-03-30 16:43:04 -05:00
io::copy(
&mut File::open("repbuild/holeybytes/ecall.hb")?,
&mut fs.root_dir().create_file("ecall.hb")?,
)?;
2023-03-30 16:43:04 -05:00
drop(bootdir);
Ok(fs)
2022-08-03 02:11:51 -05:00
}
2023-03-30 16:43:04 -05:00
fn build(release: bool, target: Target) -> Result<(), Error> {
2023-07-12 20:21:33 -05:00
fetch_ovmf().change_context(Error::OvmfFetch)?;
2023-03-30 16:43:04 -05:00
let fs = get_fs().change_context(Error::Io)?;
let mut com = Command::new("cargo");
com.current_dir("kernel");
com.args(["b"]);
if release {
com.arg("-r");
}
2023-07-12 20:21:33 -05:00
if target == Target::Riscv64Virt {
com.args(["--target", "targets/riscv64-virt-ableos.json"]);
2022-08-07 07:35:55 -05:00
}
2023-07-13 22:41:09 -05:00
if target == Target::Aarch64 {
com.args(["--target", "targets/aarch64-virt-ableos.json"]);
}
2022-08-03 02:11:51 -05:00
2023-03-30 16:43:04 -05:00
match com.status() {
Ok(s) if s.code() != Some(0) => bail!(Error::Build),
Err(e) => bail!(report!(e).change_context(Error::Build)),
_ => (),
}
2022-08-03 02:11:51 -05:00
2023-07-17 09:36:39 -05:00
if target != Target::Aarch64 {
todo!()
2023-03-30 16:43:04 -05:00
}
2022-08-07 07:35:55 -05:00
2023-03-30 16:43:04 -05:00
(|| -> std::io::Result<_> {
io::copy(
&mut File::open(
Path::new("target/aarch64-virt-ableos")
2023-03-30 16:43:04 -05:00
.join(if release { "release" } else { "debug" })
.join("kernel"),
)?,
&mut fs.root_dir().create_file("kernel")?,
)
.map(|_| ())
})()
.into_report()
.change_context(Error::Io)
}
2022-08-07 07:35:55 -05:00
2023-03-30 16:43:04 -05:00
fn run(release: bool, target: Target) -> Result<(), Error> {
let mut com = match target {
Target::X86_64 => Command::new("qemu-system-x86_64"),
Target::Riscv64Virt => Command::new("qemu-system-riscv64"),
2023-07-13 22:41:09 -05:00
Target::Aarch64 => Command::new("qemu-system-aarch64"),
2023-03-30 16:43:04 -05:00
};
if target == Target::X86_64 {
#[rustfmt::skip]
com.args([
2023-07-12 20:21:33 -05:00
"-bios", "target/OVMF_CODE.fd",
2023-03-30 16:43:04 -05:00
"-drive", "file=target/disk.img,format=raw",
"-m", "4G",
2023-05-06 06:50:24 -05:00
// "-serial", "stdio",
"-smp", "cores=4",
2023-07-12 06:03:06 -05:00
// "-vga", "cirrus",
2023-05-23 05:16:14 -05:00
// "-device", "ati-vga",
2023-07-12 06:03:06 -05:00
// "-device", "virtio-gpu-pci",
2023-05-06 06:50:24 -05:00
2023-07-12 06:03:06 -05:00
// "-device", "virtio-serial,id=virtio-serial0",
// "-chardev", "stdio,id=char0,mux=on",
// "-device", "virtconsole,chardev=char0",
// "-device", "virtio-mouse-pci",
2023-05-06 06:50:24 -05:00
// "-device", "ati-vga", "model=rage128p"
2023-03-30 16:43:04 -05:00
]);
#[cfg(target_os = "linux")]
{
2023-07-17 09:36:39 -05:00
// com.args(["-enable-kvm", "-cpu", "host"]);
2022-08-07 07:35:55 -05:00
}
2023-03-30 16:43:04 -05:00
}
if target == Target::Riscv64Virt {
#[rustfmt::skip]
com.args([
"-M", "virt",
"-m", "128M",
"-serial", "stdio",
"-kernel",
if release {
"target/riscv64-virt-ableos/release/kernel"
} else {
"target/riscv64-virt-ableos/debug/kernel"
2022-08-03 02:11:51 -05:00
}
2023-03-30 16:43:04 -05:00
]);
}
2023-07-13 22:41:09 -05:00
if target == Target::Aarch64 {
2023-07-17 09:36:39 -05:00
#[rustfmt::skip]
2023-07-13 22:41:09 -05:00
com.args([
2023-07-17 09:36:39 -05:00
"-M", "virt",
"-cpu", "cortex-a72",
"-device", "ramfb",
"-device", "qemu-xhci",
"-device", "usb-kbd",
"-m", "2G",
"-bios", "target/AARCH64_OVMF.fd",
"-drive", "file=target/disk.img,format=raw",
2023-07-13 22:41:09 -05:00
]);
}
2023-03-30 16:43:04 -05:00
match com
.status()
.into_report()
.change_context(Error::ProcessSpawn)?
{
s if s.success() => Ok(()),
s => Err(report!(Error::Qemu(s.code()))),
2022-08-07 07:35:55 -05:00
}
2022-08-03 02:11:51 -05:00
}
2023-07-12 20:21:33 -05:00
fn fetch_ovmf() -> Result<(), OvmfFetchError> {
2023-07-17 09:36:39 -05:00
const OVMF_AARCH64_URL: &str =
"https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd";
2023-07-12 20:21:33 -05:00
2023-07-17 09:36:39 -05:00
let mut file = match std::fs::metadata("target/AARCH64_OVMF.fd") {
2023-07-12 20:21:33 -05:00
Err(e) if e.kind() == std::io::ErrorKind::NotFound => std::fs::OpenOptions::new()
.create(true)
.write(true)
.read(true)
2023-07-17 09:36:39 -05:00
.open("target/AARCH64_OVMF.fd")
2023-07-12 20:21:33 -05:00
.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…");
2023-07-17 09:36:39 -05:00
let bytes = std::convert::identity((|| reqwest::blocking::get(OVMF_AARCH64_URL)?.bytes())())
2023-07-12 20:21:33 -05:00
.into_report()
2023-07-17 09:36:39 -05:00
.change_context(OvmfFetchError::Fetch)?;
2023-07-12 20:21:33 -05:00
2023-07-17 09:36:39 -05:00
file.write_all(&bytes)
.into_report()
.change_context(OvmfFetchError::Io)?;
2023-07-12 20:21:33 -05:00
Ok(())
}
#[derive(Debug, Display)]
enum OvmfFetchError {
#[display(fmt = "Failed to fetch OVMF package")]
Fetch,
#[display(fmt = "IO Error")]
Io,
}
impl Context for OvmfFetchError {}
2023-03-30 16:43:04 -05:00
#[derive(Clone, Copy, PartialEq, Eq)]
enum Target {
X86_64,
Riscv64Virt,
2023-07-13 22:41:09 -05:00
Aarch64,
2023-03-30 16:43:04 -05:00
}
2022-08-03 02:11:51 -05:00
2023-07-12 20:21:33 -05:00
#[derive(Debug, Display)]
2023-03-30 16:43:04 -05:00
enum Error {
2023-07-12 20:21:33 -05:00
#[display(fmt = "Failed to build the kernel")]
2023-03-30 16:43:04 -05:00
Build,
2023-07-12 20:21:33 -05:00
#[display(fmt = "Missing or invalid subcommand (available: build, run)")]
2023-03-30 16:43:04 -05:00
InvalidSubCom,
2023-07-12 20:21:33 -05:00
#[display(fmt = "IO Error")]
2023-03-30 16:43:04 -05:00
Io,
2023-07-12 20:21:33 -05:00
#[display(fmt = "Failed to spawn a process")]
2023-03-30 16:43:04 -05:00
ProcessSpawn,
2023-07-12 20:21:33 -05:00
#[display(fmt = "Failed to fetch UEFI firmware")]
OvmfFetch,
#[display(fmt = "QEMU Error: {}", "fmt_qemu_err(*_0)")]
2023-03-30 16:43:04 -05:00
Qemu(Option<i32>),
2022-08-03 02:11:51 -05:00
}
2023-03-30 16:43:04 -05:00
impl Context for Error {}
2023-07-12 20:21:33 -05:00
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")
2023-03-30 16:43:04 -05:00
}
2022-08-07 07:35:55 -05:00
}
}
2023-07-12 20:21:33 -05:00
W(e)
2022-08-03 02:11:51 -05:00
}