Skip to content

Commit

Permalink
Embed bios and uefi binaries (#395)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysteriouslyseeing committed Dec 28, 2023
1 parent 0fec6fd commit 7d2a579
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 48 deletions.
113 changes: 100 additions & 13 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ async fn bios_main() {
// BIOS crates don't have enough dependencies to utilize all cores on modern
// CPUs. So by running the build commands in parallel, we increase the number
// of utilized cores.)
#[cfg(not(docsrs_dummy_build))]
let (bios_boot_sector_path, bios_stage_2_path, bios_stage_3_path, bios_stage_4_path) = (
build_bios_boot_sector(&out_dir),
build_bios_stage_2(&out_dir),
Expand All @@ -30,14 +29,6 @@ async fn bios_main() {
)
.join()
.await;
// dummy implementations because docsrs builds have no network access
#[cfg(docsrs_dummy_build)]
let (bios_boot_sector_path, bios_stage_2_path, bios_stage_3_path, bios_stage_4_path) = (
PathBuf::new(),
PathBuf::new(),
PathBuf::new(),
PathBuf::new(),
);
println!(
"cargo:rustc-env=BIOS_BOOT_SECTOR_PATH={}",
bios_boot_sector_path.display()
Expand All @@ -60,11 +51,7 @@ async fn bios_main() {
async fn uefi_main() {
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());

#[cfg(not(docsrs_dummy_build))]
let uefi_path = build_uefi_bootloader(&out_dir).await;
// dummy implementation because docsrs builds have no network access
#[cfg(docsrs_dummy_build)]
let uefi_path = PathBuf::new();

println!(
"cargo:rustc-env=UEFI_BOOTLOADER_PATH={}",
Expand Down Expand Up @@ -109,6 +96,26 @@ async fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
}
}

// dummy implementation because docsrs builds have no network access.
// This will put an empty file in out_dir and return its path.
#[cfg(docsrs_dummy_build)]
#[cfg(feature = "uefi")]
async fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
use std::fs::File;

let path = out_dir.join("bootloader-dummy-bootloader-uefi");

if File::create(&path).is_err() {
panic!("Failed to create dummy uefi bootloader");
}
assert!(
path.exists(),
"uefi bootloader dummy file does not exist after file creation"
);

path
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
async fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
Expand Down Expand Up @@ -153,6 +160,26 @@ async fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
convert_elf_to_bin(elf_path).await
}

// dummy implementation because docsrs builds have no network access.
// This will put an empty file in out_dir and return its path.
#[cfg(docsrs_dummy_build)]
#[cfg(feature = "bios")]
async fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
use std::fs::File;

let path = out_dir.join("bootloader-dummy-bios-boot-sector");

if File::create(&path).is_err() {
panic!("Failed to create dummy bios boot sector");
}
assert!(
path.exists(),
"bios boot sector dummy file does not exist after file creation"
);

path
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
async fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
Expand Down Expand Up @@ -199,6 +226,26 @@ async fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
convert_elf_to_bin(elf_path).await
}

// dummy implementation because docsrs builds have no network access.
// This will put an empty file in out_dir and return its path.
#[cfg(docsrs_dummy_build)]
#[cfg(feature = "bios")]
async fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
use std::fs::File;

let path = out_dir.join("bootloader-dummy-bios-stage-2");

if File::create(&path).is_err() {
panic!("Failed to create dummy bios second stage");
}
assert!(
path.exists(),
"bios second stage dummy file does not exist after file creation"
);

path
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
async fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
Expand Down Expand Up @@ -241,6 +288,26 @@ async fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
convert_elf_to_bin(elf_path).await
}

// dummy implementation because docsrs builds have no network access.
// This will put an empty file in out_dir and return its path.
#[cfg(docsrs_dummy_build)]
#[cfg(feature = "bios")]
async fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
use std::fs::File;

let path = out_dir.join("bootloader-dummy-bios-stage-3");

if File::create(&path).is_err() {
panic!("Failed to create dummy bios stage-3");
}
assert!(
path.exists(),
"bios stage-3 dummy file does not exist after file creation"
);

path
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
async fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
Expand Down Expand Up @@ -284,6 +351,26 @@ async fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
convert_elf_to_bin(elf_path).await
}

// dummy implementation because docsrs builds have no network access.
// This will put an empty file in out_dir and return its path.
#[cfg(docsrs_dummy_build)]
#[cfg(feature = "bios")]
async fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
use std::fs::File;

let path = out_dir.join("bootloader-dummy-bios-stage-4");

if File::create(&path).is_err() {
panic!("Failed to create dummy bios stage-4");
}
assert!(
path.exists(),
"bios stage-4 dummy file does not exist after file creation"
);

path
}

#[cfg(not(docsrs_dummy_build))]
#[cfg(feature = "bios")]
async fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
Expand Down
9 changes: 9 additions & 0 deletions src/file_data_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{fs, io};
pub enum FileDataSource {
File(PathBuf),
Data(Vec<u8>),
Bytes(&'static [u8]),
}

impl Debug for FileDataSource {
Expand All @@ -22,6 +23,9 @@ impl Debug for FileDataSource {
FileDataSource::Data(d) => {
f.write_fmt(format_args!("data source: {} raw bytes ", d.len()))
}
FileDataSource::Bytes(b) => {
f.write_fmt(format_args!("data source: {} raw bytes ", b.len()))
}
}
}
}
Expand All @@ -34,6 +38,7 @@ impl FileDataSource {
.with_context(|| format!("failed to read metadata of file `{}`", path.display()))?
.len(),
FileDataSource::Data(v) => v.len() as u64,
FileDataSource::Bytes(s) => s.len() as u64,
})
}
/// Copy this data source to the specified target that implements io::Write
Expand All @@ -51,6 +56,10 @@ impl FileDataSource {
let mut cursor = Cursor::new(contents);
io::copy(&mut cursor, target)?;
}
FileDataSource::Bytes(contents) => {
let mut cursor = Cursor::new(contents);
io::copy(&mut cursor, target)?;
}
};

Ok(())
Expand Down
49 changes: 23 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64";
const RAMDISK_FILE_NAME: &str = "ramdisk";
const CONFIG_FILE_NAME: &str = "boot.json";

#[cfg(feature = "uefi")]
const UEFI_BOOTLOADER: &[u8] = include_bytes!(env!("UEFI_BOOTLOADER_PATH"));
#[cfg(feature = "bios")]
const BIOS_BOOT_SECTOR: &[u8] = include_bytes!(env!("BIOS_BOOT_SECTOR_PATH"));
#[cfg(feature = "bios")]
const BIOS_STAGE_2: &[u8] = include_bytes!(env!("BIOS_STAGE_2_PATH"));
#[cfg(feature = "bios")]
const BIOS_STAGE_3: &[u8] = include_bytes!(env!("BIOS_STAGE_3_PATH"));
#[cfg(feature = "bios")]
const BIOS_STAGE_4: &[u8] = include_bytes!(env!("BIOS_STAGE_4_PATH"));

/// Allows creating disk images for a specified set of files.
///
/// It can currently create `MBR` (BIOS), `GPT` (UEFI), and `TFTP` (UEFI) images.
Expand Down Expand Up @@ -98,28 +109,19 @@ impl DiskImageBuilder {
#[cfg(feature = "bios")]
/// Create an MBR disk image for booting on BIOS systems.
pub fn create_bios_image(&self, image_path: &Path) -> anyhow::Result<()> {
const BIOS_STAGE_3: &str = "boot-stage-3";
const BIOS_STAGE_4: &str = "boot-stage-4";
let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH"));
let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH"));
let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH"));
let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH"));
const BIOS_STAGE_3_NAME: &str = "boot-stage-3";
const BIOS_STAGE_4_NAME: &str = "boot-stage-4";
let stage_3 = FileDataSource::Bytes(BIOS_STAGE_3);
let stage_4 = FileDataSource::Bytes(BIOS_STAGE_4);
let mut internal_files = BTreeMap::new();
internal_files.insert(
BIOS_STAGE_3,
FileDataSource::File(stage_3_path.to_path_buf()),
);
internal_files.insert(
BIOS_STAGE_4,
FileDataSource::File(stage_4_path.to_path_buf()),
);

internal_files.insert(BIOS_STAGE_3_NAME, stage_3);
internal_files.insert(BIOS_STAGE_4_NAME, stage_4);
let fat_partition = self
.create_fat_filesystem_image(internal_files)
.context("failed to create FAT partition")?;
mbr::create_mbr_disk(
bootsector_path,
stage_2_path,
BIOS_BOOT_SECTOR,
BIOS_STAGE_2,
fat_partition.path(),
image_path,
)
Expand All @@ -135,12 +137,9 @@ impl DiskImageBuilder {
/// Create a GPT disk image for booting on UEFI systems.
pub fn create_uefi_image(&self, image_path: &Path) -> anyhow::Result<()> {
const UEFI_BOOT_FILENAME: &str = "efi/boot/bootx64.efi";
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));

let mut internal_files = BTreeMap::new();
internal_files.insert(
UEFI_BOOT_FILENAME,
FileDataSource::File(bootloader_path.to_path_buf()),
);
internal_files.insert(UEFI_BOOT_FILENAME, FileDataSource::Bytes(UEFI_BOOTLOADER));
let fat_partition = self
.create_fat_filesystem_image(internal_files)
.context("failed to create FAT partition")?;
Expand All @@ -159,15 +158,13 @@ impl DiskImageBuilder {
use std::{fs, ops::Deref};

const UEFI_TFTP_BOOT_FILENAME: &str = "bootloader";
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
fs::create_dir_all(tftp_path)
.with_context(|| format!("failed to create out dir at {}", tftp_path.display()))?;

let to = tftp_path.join(UEFI_TFTP_BOOT_FILENAME);
fs::copy(bootloader_path, &to).with_context(|| {
fs::write(&to, UEFI_BOOTLOADER).with_context(|| {
format!(
"failed to copy bootloader from {} to {}",
bootloader_path.display(),
"failed to copy bootloader from the embedded binary to {}",
to.display()
)
})?;
Expand Down
17 changes: 8 additions & 9 deletions src/mbr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ use std::{
io::{self, Seek, SeekFrom},
path::Path,
};

const SECTOR_SIZE: u32 = 512;

pub fn create_mbr_disk(
bootsector_path: &Path,
second_stage_path: &Path,
bootsector_binary: &[u8],
second_stage_binary: &[u8],
boot_partition_path: &Path,
out_mbr_path: &Path,
) -> anyhow::Result<()> {
let mut boot_sector = File::open(bootsector_path).context("failed to open boot sector")?;
use std::io::Cursor;
let mut boot_sector = Cursor::new(bootsector_binary);
let mut mbr =
mbrman::MBR::read_from(&mut boot_sector, SECTOR_SIZE).context("failed to read MBR")?;

Expand All @@ -23,12 +25,9 @@ pub fn create_mbr_disk(
}
}

let mut second_stage =
File::open(second_stage_path).context("failed to open second stage binary")?;
let second_stage_size = second_stage
.metadata()
.context("failed to read file metadata of second stage")?
.len();
let mut second_stage = Cursor::new(second_stage_binary);
let second_stage_size = second_stage_binary.len() as u64;

let second_stage_start_sector = 1;
let second_stage_sectors = ((second_stage_size - 1) / u64::from(SECTOR_SIZE) + 1)
.try_into()
Expand Down

0 comments on commit 7d2a579

Please sign in to comment.