Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bios and uefi cargo features (closes #287) #304

Merged
merged 9 commits into from
Jan 9, 2023
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ jobs:
- name: Run integration tests
run: cargo test

# test feature gates (only on one OS is enough)
- name: Test with only UEFI feature
if: runner.os == 'Linux'
run: cargo test --no-default-features --features uefi
- name: Test with only BIOS feature
if: runner.os == 'Linux'
run: cargo test --no-default-features --features bios

fmt:
name: Check Formatting
runs-on: ubuntu-latest
Expand Down
9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,17 @@ bootloader_api = { version = "0.11.0", path = "api" }
bootloader-x86_64-common = { version = "0.11.0", path = "common" }
bootloader-x86_64-bios-common = { version = "0.11.0", path = "bios/common" }

[features]
default = ["bios", "uefi"]
bios = ["dep:mbrman", "bootloader_test_runner/bios"]
uefi = ["dep:gpt", "bootloader_test_runner/uefi"]

[dependencies]
anyhow = "1.0.32"
fatfs = "0.3.4"
gpt = "3.0.0"
mbrman = "0.5.1"
tempfile = "3.3.0"
mbrman = { version = "0.5.1", optional = true }
gpt = { version = "3.0.0", optional = true }

[dev-dependencies]
bootloader_test_runner = { path = "tests/runner" }
Expand Down
83 changes: 43 additions & 40 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,49 @@ use std::{
process::Command,
};

const BOOTLOADER_X86_64_UEFI_VERSION: &str = env!("CARGO_PKG_VERSION");

const BOOTLOADER_X86_64_BIOS_BOOT_SECTOR_VERSION: &str = env!("CARGO_PKG_VERSION");
const BOOTLOADER_X86_64_BIOS_STAGE_2_VERSION: &str = env!("CARGO_PKG_VERSION");
const BOOTLOADER_X86_64_BIOS_STAGE_3_VERSION: &str = env!("CARGO_PKG_VERSION");
const BOOTLOADER_X86_64_BIOS_STAGE_4_VERSION: &str = env!("CARGO_PKG_VERSION");
const BOOTLOADER_VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());

let uefi_path = build_uefi_bootloader(&out_dir);
println!(
"cargo:rustc-env=UEFI_BOOTLOADER_PATH={}",
uefi_path.display()
);
#[cfg(feature = "uefi")]
{
let uefi_path = build_uefi_bootloader(&out_dir);
println!(
"cargo:rustc-env=UEFI_BOOTLOADER_PATH={}",
uefi_path.display()
);
}

let bios_boot_sector_path = build_bios_boot_sector(&out_dir);
println!(
"cargo:rustc-env=BIOS_BOOT_SECTOR_PATH={}",
bios_boot_sector_path.display()
);
let bios_stage_2_path = build_bios_stage_2(&out_dir);
println!(
"cargo:rustc-env=BIOS_STAGE_2_PATH={}",
bios_stage_2_path.display()
);
#[cfg(feature = "bios")]
{
let bios_boot_sector_path = build_bios_boot_sector(&out_dir);
println!(
"cargo:rustc-env=BIOS_BOOT_SECTOR_PATH={}",
bios_boot_sector_path.display()
);
let bios_stage_2_path = build_bios_stage_2(&out_dir);
println!(
"cargo:rustc-env=BIOS_STAGE_2_PATH={}",
bios_stage_2_path.display()
);

let bios_stage_3_path = build_bios_stage_3(&out_dir);
println!(
"cargo:rustc-env=BIOS_STAGE_3_PATH={}",
bios_stage_3_path.display()
);
let bios_stage_3_path = build_bios_stage_3(&out_dir);
println!(
"cargo:rustc-env=BIOS_STAGE_3_PATH={}",
bios_stage_3_path.display()
);

let bios_stage_4_path = build_bios_stage_4(&out_dir);
println!(
"cargo:rustc-env=BIOS_STAGE_4_PATH={}",
bios_stage_4_path.display()
);
let bios_stage_4_path = build_bios_stage_4(&out_dir);
println!(
"cargo:rustc-env=BIOS_STAGE_4_PATH={}",
bios_stage_4_path.display()
);
}
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "uefi")]
fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let mut cmd = Command::new(cargo);
Expand All @@ -53,7 +55,7 @@ fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
cmd.arg("--path").arg("uefi");
println!("cargo:rerun-if-changed=uefi");
} else {
cmd.arg("--version").arg(BOOTLOADER_X86_64_UEFI_VERSION);
cmd.arg("--version").arg(BOOTLOADER_VERSION);
}
cmd.arg("--locked");
cmd.arg("--target").arg("x86_64-unknown-uefi");
Expand All @@ -78,6 +80,7 @@ fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let mut cmd = Command::new(cargo);
Expand All @@ -90,8 +93,7 @@ fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
cmd.arg("--path").arg(&local_path);
println!("cargo:rerun-if-changed={}", local_path.display());
} else {
cmd.arg("--version")
.arg(BOOTLOADER_X86_64_BIOS_BOOT_SECTOR_VERSION);
cmd.arg("--version").arg(BOOTLOADER_VERSION);
}
cmd.arg("--locked");
cmd.arg("--target").arg("i386-code16-boot-sector.json");
Expand Down Expand Up @@ -121,6 +123,7 @@ fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let mut cmd = Command::new(cargo);
Expand All @@ -133,8 +136,7 @@ fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
cmd.arg("--path").arg(&local_path);
println!("cargo:rerun-if-changed={}", local_path.display());
} else {
cmd.arg("--version")
.arg(BOOTLOADER_X86_64_BIOS_STAGE_2_VERSION);
cmd.arg("--version").arg(BOOTLOADER_VERSION);
}
cmd.arg("--locked");
cmd.arg("--target").arg("i386-code16-stage-2.json");
Expand Down Expand Up @@ -162,6 +164,7 @@ fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let mut cmd = Command::new(cargo);
Expand All @@ -174,8 +177,7 @@ fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
cmd.arg("--path").arg(&local_path);
println!("cargo:rerun-if-changed={}", local_path.display());
} else {
cmd.arg("--version")
.arg(BOOTLOADER_X86_64_BIOS_STAGE_3_VERSION);
cmd.arg("--version").arg(BOOTLOADER_VERSION);
}
cmd.arg("--locked");
cmd.arg("--target").arg("i686-stage-3.json");
Expand Down Expand Up @@ -203,6 +205,7 @@ fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let mut cmd = Command::new(cargo);
Expand All @@ -215,8 +218,7 @@ fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
cmd.arg("--path").arg(&local_path);
println!("cargo:rerun-if-changed={}", local_path.display());
} else {
cmd.arg("--version")
.arg(BOOTLOADER_X86_64_BIOS_STAGE_4_VERSION);
cmd.arg("--version").arg(BOOTLOADER_VERSION);
}
cmd.arg("--locked");
cmd.arg("--target").arg("x86_64-stage-4.json");
Expand Down Expand Up @@ -244,6 +246,7 @@ fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
convert_elf_to_bin(elf_path)
}

#[cfg(feature = "bios")]
fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
let flat_binary_path = elf_path.with_extension("bin");

Expand Down
File renamed without changes.
67 changes: 67 additions & 0 deletions src/bios/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use crate::fat;
use anyhow::Context;
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};
use tempfile::NamedTempFile;

mod mbr;

const BIOS_STAGE_3: &str = "boot-stage-3";
const BIOS_STAGE_4: &str = "boot-stage-4";

/// Create disk images for booting on legacy BIOS systems.
pub struct BiosBoot {
kernel: PathBuf,
}

impl BiosBoot {
/// Start creating a disk image for the given bootloader ELF executable.
pub fn new(kernel_path: &Path) -> Self {
Self {
kernel: kernel_path.to_owned(),
}
}

/// Create a bootable UEFI disk image at the given path.
pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> {
let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH"));
let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH"));

let fat_partition = self
.create_fat_partition()
.context("failed to create FAT partition")?;

mbr::create_mbr_disk(
bootsector_path,
stage_2_path,
fat_partition.path(),
out_path,
)
.context("failed to create BIOS MBR disk image")?;

fat_partition
.close()
.context("failed to delete FAT partition after disk image creation")?;

Ok(())
}

/// Creates an BIOS-bootable FAT partition with the kernel.
fn create_fat_partition(&self) -> anyhow::Result<NamedTempFile> {
let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH"));
let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH"));

let mut files = BTreeMap::new();
files.insert(crate::KERNEL_FILE_NAME, self.kernel.as_path());
files.insert(BIOS_STAGE_3, stage_3_path);
files.insert(BIOS_STAGE_4, stage_4_path);

let out_file = NamedTempFile::new().context("failed to create temp file")?;
fat::create_fat_filesystem(files, out_file.path())
.context("failed to create BIOS FAT filesystem")?;

Ok(out_file)
}
}
Loading