Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ bootloader-x86_64-bios-common = { version = "0.11.4", path = "bios/common" }
default = ["bios", "uefi"]
bios = ["dep:mbrman"]
uefi = ["dep:gpt"]
embedded_binaries = []

[dependencies]
anyhow = "1.0.32"
Expand Down
96 changes: 78 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64";
const RAMDISK_FILE_NAME: &str = "ramdisk";
const CONFIG_FILE_NAME: &str = "boot.json";

#[cfg(all(feature = "embedded_binaries", feature = "uefi"))]
static UEFI_BOOTLOADER: &'static [u8] = include_bytes!(env!("UEFI_BOOTLOADER_PATH"));

#[cfg(all(feature = "embedded_binaries", feature = "bios"))]
static BIOS_BOOT_SECTOR: &'static [u8] = include_bytes!(env!("BIOS_BOOT_SECTOR_PATH"));
#[cfg(all(feature = "embedded_binaries", feature = "bios"))]
static BIOS_STAGE_2: &'static [u8] = include_bytes!(env!("BIOS_STAGE_2_PATH"));
#[cfg(all(feature = "embedded_binaries", feature = "bios"))]
static BIOS_STAGE_3: &'static [u8] = include_bytes!(env!("BIOS_STAGE_3_PATH"));
#[cfg(all(feature = "embedded_binaries", feature = "bios"))]
static BIOS_STAGE_4: &'static [u8] = include_bytes!(env!("BIOS_STAGE_4_PATH"));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of statics, I think we can use consts here. E.g. const BIOS_BOOT_SECTOR: &'static [u8] = ....


/// 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 @@ -95,22 +107,22 @@ impl DiskImageBuilder {
self.set_file_source(destination.into(), FileDataSource::File(file_path))
}

#[cfg(feature = "bios")]
#[cfg(all(feature = "bios", not(feature = "embedded_binaries")))]
/// 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";
const BIOS_STAGE_3_NAME: &str = "boot-stage-3";
const BIOS_STAGE_4_NAME: &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"));
let mut internal_files = BTreeMap::new();
internal_files.insert(
BIOS_STAGE_3,
BIOS_STAGE_3_NAME,
FileDataSource::File(stage_3_path.to_path_buf()),
);
internal_files.insert(
BIOS_STAGE_4,
BIOS_STAGE_4_NAME,
FileDataSource::File(stage_4_path.to_path_buf()),
);

Expand All @@ -131,16 +143,50 @@ impl DiskImageBuilder {
Ok(())
}

#[cfg(all(feature = "bios", feature = "embedded_binaries"))]
/// 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_NAME: &str = "boot-stage-3";
const BIOS_STAGE_4_NAME: &str = "boot-stage-4";
let stage_3 = FileDataSource::Data(BIOS_STAGE_3.to_vec());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The to_vec call copies the data to the heap whenever this function is invoked. I think we could avoid this copy by introducing a new FileDataSource::Bytes(&'static [u8]) variant, which could be used with statics and const data.

let stage_4 = FileDataSource::Data(BIOS_STAGE_4.to_vec());
let mut internal_files = BTreeMap::new();
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(
BIOS_BOOT_SECTOR,
BIOS_STAGE_2,
fat_partition.path(),
image_path,
)
.context("failed to create BIOS MBR disk image")?;

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

#[cfg(feature = "uefi")]
/// 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"));

#[cfg(feature = "embedded_binaries")]
fn get_uefi_bootloader() -> FileDataSource {
FileDataSource::Data(UEFI_BOOTLOADER.to_vec())
}
#[cfg(not(feature = "embedded_binaries"))]
fn get_uefi_bootloader() -> FileDataSource {
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
FileDataSource::File(bootloader_path.to_path_buf())
}

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, get_uefi_bootloader());
let fat_partition = self
.create_fat_filesystem_image(internal_files)
.context("failed to create FAT partition")?;
Expand All @@ -158,19 +204,33 @@ impl DiskImageBuilder {
pub fn create_uefi_tftp_folder(&self, tftp_path: &Path) -> anyhow::Result<()> {
use std::{fs, ops::Deref};

#[cfg(feature = "embedded_binaries")]
fn write_uefi_bootloader(to: &PathBuf) -> anyhow::Result<()> {
fs::write(to, UEFI_BOOTLOADER).with_context(|| {
format!(
"failed to copy bootloader from the embedded binary to {}",
to.display()
)
})
}
#[cfg(not(feature = "embedded_binaries"))]
fn write_uefi_bootloader(to: &PathBuf) -> anyhow::Result<()> {
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
fs::copy(bootloader_path, to).map(|_| ()).with_context(|| {
format!(
"failed to copy bootloader from {} to {}",
bootloader_path.display(),
to.display()
)
})
}

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(|| {
format!(
"failed to copy bootloader from {} to {}",
bootloader_path.display(),
to.display()
)
})?;
write_uefi_bootloader(&to)?;

for f in &self.files {
let to = tftp_path.join(f.0.deref());
Expand Down
62 changes: 54 additions & 8 deletions src/mbr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,67 @@ use anyhow::Context;
use mbrman::BOOT_ACTIVE;
use std::{
fs::{self, File},
io::{self, Seek, SeekFrom},
io::{self, Read, Seek, SeekFrom},
path::Path,
};

const SECTOR_SIZE: u32 = 512;

#[cfg(not(feature = "embedded_binaries"))]
pub fn create_mbr_disk(
bootsector_path: &Path,
second_stage_path: &Path,
boot_partition_path: &Path,
out_mbr_path: &Path,
) -> anyhow::Result<()> {
let mut boot_sector = File::open(bootsector_path).context("failed to open boot sector")?;
let second_stage =
File::open(second_stage_path).context("failed to open second stage binary")?;
create_mbr_disk_with_readers(
File::open(bootsector_path).context("failed to open boot sector")?,
SecondStageData {
size: second_stage
.metadata()
.context("failed to read file metadata of second stage")?
.len(),
reader: second_stage,
},
boot_partition_path,
out_mbr_path,
)
}

#[cfg(feature = "embedded_binaries")]
pub fn create_mbr_disk(
bootsector_binary: &[u8],
second_stage_binary: &[u8],
boot_partition_path: &Path,
out_mbr_path: &Path,
) -> anyhow::Result<()> {
use std::io::Cursor;
create_mbr_disk_with_readers(
Cursor::new(bootsector_binary),
SecondStageData {
size: second_stage_binary.len() as u64,
reader: Cursor::new(second_stage_binary),
},
boot_partition_path,
out_mbr_path,
)
}

struct SecondStageData<R> {
size: u64,
reader: R,
}

fn create_mbr_disk_with_readers<R: Read + Seek>(
bootsector_reader: R,
second_stage_data: SecondStageData<R>,
boot_partition_path: &Path,
out_mbr_path: &Path,
) -> anyhow::Result<()> {
// let mut boot_sector = File::open(bootsector_path).context("failed to open boot sector")?;
let mut boot_sector = bootsector_reader;
let mut mbr =
mbrman::MBR::read_from(&mut boot_sector, SECTOR_SIZE).context("failed to read MBR")?;

Expand All @@ -23,12 +72,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 = second_stage_data.reader;
let second_stage_size = second_stage_data.size;

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