diff --git a/build.rs b/build.rs index d40373d4..3f543d3f 100644 --- a/build.rs +++ b/build.rs @@ -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), @@ -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() @@ -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={}", @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { diff --git a/src/file_data_source.rs b/src/file_data_source.rs index 3e1128b3..5724d96f 100644 --- a/src/file_data_source.rs +++ b/src/file_data_source.rs @@ -11,6 +11,7 @@ use std::{fs, io}; pub enum FileDataSource { File(PathBuf), Data(Vec), + Bytes(&'static [u8]), } impl Debug for FileDataSource { @@ -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())) + } } } } @@ -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 @@ -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(()) diff --git a/src/lib.rs b/src/lib.rs index 179f1d10..cb3805e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. @@ -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, ) @@ -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")?; @@ -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() ) })?; diff --git a/src/mbr.rs b/src/mbr.rs index 6c7a9f0d..3328a07f 100644 --- a/src/mbr.rs +++ b/src/mbr.rs @@ -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")?; @@ -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()