2023-01-06 18:57:20 -06:00
|
|
|
use error_stack::{bail, report, Context, IntoReport, Result, ResultExt};
|
|
|
|
use fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek};
|
|
|
|
use nix::fcntl::FallocateFlags;
|
|
|
|
use std::{fmt::Display, fs::File, io, os::fd::AsRawFd, path::Path, process::Command};
|
|
|
|
|
|
|
|
fn main() -> Result<(), Error> {
|
|
|
|
env_logger::init();
|
|
|
|
let mut args = std::env::args();
|
|
|
|
args.next();
|
|
|
|
|
|
|
|
match args.next().as_deref() {
|
|
|
|
Some("build" | "b") => build(
|
|
|
|
args.next()
|
|
|
|
.map(|x| x == "-r" || x == "--release")
|
|
|
|
.unwrap_or_default(),
|
|
|
|
)
|
|
|
|
.change_context(Error::Build),
|
|
|
|
Some("run" | "r") => {
|
|
|
|
build(
|
|
|
|
args.next()
|
|
|
|
.map(|x| x == "-r" || x == "--release")
|
|
|
|
.unwrap_or_default(),
|
|
|
|
)?;
|
|
|
|
run()
|
|
|
|
}
|
|
|
|
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",
|
|
|
|
),
|
|
|
|
);
|
|
|
|
Ok(())
|
2022-08-07 07:35:55 -05:00
|
|
|
}
|
2023-01-06 18:57:20 -06:00
|
|
|
_ => Err(report!(Error::InvalidSubCom)),
|
2022-08-07 07:35:55 -05:00
|
|
|
}
|
2022-08-03 02:11:51 -05:00
|
|
|
}
|
|
|
|
|
2023-01-06 18:57:20 -06: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()
|
2022-08-07 07:35:55 -05:00
|
|
|
}
|
2023-01-06 18:57:20 -06:00
|
|
|
}
|
2022-12-03 10:47:10 -06:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
let mut img = File::options()
|
|
|
|
.read(true)
|
|
|
|
.write(true)
|
|
|
|
.create(true)
|
|
|
|
.open(path)?;
|
|
|
|
|
|
|
|
{
|
|
|
|
#[cfg(unix)]
|
|
|
|
nix::fcntl::fallocate(
|
|
|
|
img.as_raw_fd(),
|
|
|
|
FallocateFlags::from_bits(0).unwrap(),
|
|
|
|
0,
|
|
|
|
1024 * 1024 * 64,
|
|
|
|
)
|
|
|
|
.map_err(|e| std::io::Error::from_raw_os_error(e as i32))?;
|
|
|
|
|
|
|
|
#[cfg(not(any(unix)))]
|
|
|
|
compile_error!("unsupported platform");
|
|
|
|
}
|
2022-12-03 10:47:10 -06:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
fatfs::format_volume(&mut img, FormatVolumeOptions::new())?;
|
2022-11-23 04:02:02 -06:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
let fs = FileSystem::new(img, FsOptions::new())?;
|
|
|
|
let bootdir = fs.root_dir().create_dir("efi")?.create_dir("boot")?;
|
2022-08-18 04:16:37 -05:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
io::copy(
|
|
|
|
&mut File::open("limine/BOOTX64.EFI")?,
|
|
|
|
&mut bootdir.create_file("bootx64.efi")?,
|
|
|
|
)?;
|
2022-08-18 04:16:37 -05:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
io::copy(
|
|
|
|
&mut File::open(Path::new("repbuild/limine.cfg"))?,
|
|
|
|
&mut fs.root_dir().create_file("limine.cfg")?,
|
|
|
|
)?;
|
2022-08-07 07:35:55 -05:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
drop(bootdir);
|
|
|
|
Ok(fs)
|
|
|
|
}
|
2022-08-07 07:35:55 -05:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
fn build(release: bool) -> Result<(), Error> {
|
|
|
|
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");
|
2022-08-07 07:35:55 -05:00
|
|
|
}
|
2022-11-23 17:22:58 -06:00
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
com.status().into_report().change_context(Error::Build)?;
|
|
|
|
|
|
|
|
(|| -> std::io::Result<_> {
|
|
|
|
io::copy(
|
|
|
|
&mut File::open(
|
|
|
|
Path::new("target/x86_64-ableos")
|
|
|
|
.join(if release { "release" } else { "debug" })
|
|
|
|
.join("kernel"),
|
|
|
|
)?,
|
|
|
|
&mut fs.root_dir().create_file("kernel")?,
|
|
|
|
)
|
|
|
|
.map(|_| ())
|
|
|
|
})()
|
|
|
|
.into_report()
|
|
|
|
.change_context(Error::Io)
|
2022-08-03 02:11:51 -05:00
|
|
|
}
|
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
fn run() -> Result<(), Error> {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let args = [
|
|
|
|
"-bios", "/usr/share/OVMF/OVMF_CODE.fd",
|
|
|
|
"-enable-kvm",
|
|
|
|
"-cpu", "host",
|
|
|
|
"-drive", "file=target/disk.img,format=raw",
|
|
|
|
"-m", "4G",
|
|
|
|
"-serial", "stdio",
|
|
|
|
"-smp", "cores=2",
|
|
|
|
];
|
|
|
|
|
|
|
|
match Command::new("qemu-system-x86_64")
|
|
|
|
.args(args)
|
|
|
|
.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-01-06 18:57:20 -06:00
|
|
|
#[derive(Debug)]
|
|
|
|
enum Error {
|
|
|
|
Build,
|
|
|
|
InvalidSubCom,
|
|
|
|
Io,
|
|
|
|
ProcessSpawn,
|
|
|
|
Qemu(Option<i32>),
|
2022-08-03 02:11:51 -05:00
|
|
|
}
|
|
|
|
|
2023-01-06 18:57:20 -06:00
|
|
|
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)")
|
|
|
|
}
|
|
|
|
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"),
|
|
|
|
}
|
|
|
|
}
|
2022-08-03 02:11:51 -05:00
|
|
|
}
|