Skip to content

Commit 4b98b71

Browse files
committed
Replace UefiBoot and BiosBoot with DiskImageBuilder
1 parent 85c3536 commit 4b98b71

File tree

8 files changed

+171
-253
lines changed

8 files changed

+171
-253
lines changed

build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ async fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
266266
convert_elf_to_bin(elf_path).await
267267
}
268268

269+
#[cfg(not(docsrs_dummy_build))]
269270
#[cfg(feature = "bios")]
270271
async fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
271272
let flat_binary_path = elf_path.with_extension("bin");

src/bios/mod.rs

Lines changed: 0 additions & 98 deletions
This file was deleted.
File renamed without changes.

src/lib.rs

Lines changed: 156 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,169 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems.
44

55
#![warn(missing_docs)]
66

7+
#[cfg(feature = "uefi")]
8+
mod gpt;
79
#[cfg(feature = "bios")]
8-
mod bios;
10+
mod mbr;
11+
912
mod fat;
10-
#[cfg(feature = "uefi")]
11-
mod uefi;
1213

13-
#[cfg(feature = "bios")]
14-
pub use bios::BiosBoot;
14+
use std::{
15+
collections::BTreeMap,
16+
path::{Path, PathBuf},
17+
};
1518

16-
#[cfg(feature = "uefi")]
17-
pub use uefi::UefiBoot;
19+
use anyhow::Context;
20+
21+
use tempfile::NamedTempFile;
1822

1923
pub use bootloader_boot_config::BootConfig;
2024

2125
const KERNEL_FILE_NAME: &str = "kernel-x86_64";
2226
const RAMDISK_FILE_NAME: &str = "ramdisk";
2327
const CONFIG_FILE_NAME: &str = "boot.json";
28+
29+
struct DiskImageFile<'a> {
30+
source: &'a PathBuf,
31+
destination: &'a str,
32+
}
33+
34+
/// DiskImageBuilder helps create disk images for a specified set of files.
35+
/// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images.
36+
pub struct DiskImageBuilder<'a> {
37+
files: Vec<DiskImageFile<'a>>,
38+
}
39+
40+
impl<'a> DiskImageBuilder<'a> {
41+
/// Create a new instance of DiskImageBuilder, with the specified kernel.
42+
pub fn new(kernel: &'a PathBuf) -> Self {
43+
let mut obj = Self::empty();
44+
obj.set_kernel(kernel);
45+
obj
46+
}
47+
48+
/// Create a new, empty instance of DiskImageBuilder
49+
pub fn empty() -> Self {
50+
Self { files: Vec::new() }
51+
}
52+
53+
/// Add or replace a kernel to be included in the final image.
54+
pub fn set_kernel(&mut self, path: &'a PathBuf) -> &mut Self {
55+
self.add_or_replace_file(path, KERNEL_FILE_NAME)
56+
}
57+
58+
/// Add or replace a ramdisk to be included in the final image.
59+
pub fn set_ramdisk(&mut self, path: &'a PathBuf) -> &mut Self {
60+
self.add_or_replace_file(&path, RAMDISK_FILE_NAME)
61+
}
62+
63+
/// Add or replace arbitrary files.
64+
/// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI
65+
/// This can be useful in situations where you want to generate an image, but not use the provided bootloader.
66+
pub fn add_or_replace_file(&mut self, path: &'a PathBuf, target: &'a str) -> &mut Self {
67+
self.files.insert(
68+
0,
69+
DiskImageFile::<'a> {
70+
source: &path,
71+
destination: &target,
72+
},
73+
);
74+
self
75+
}
76+
fn create_fat_filesystem_image(
77+
&self,
78+
internal_files: BTreeMap<&'a str, &'a Path>,
79+
) -> anyhow::Result<NamedTempFile> {
80+
let mut local_map = BTreeMap::new();
81+
82+
for k in internal_files {
83+
local_map.insert(k.0, k.1);
84+
}
85+
86+
for f in self.files.as_slice() {
87+
local_map.insert(f.destination, &f.source.as_path());
88+
}
89+
90+
let out_file = NamedTempFile::new().context("failed to create temp file")?;
91+
fat::create_fat_filesystem(local_map, out_file.path())
92+
.context("failed to create BIOS FAT filesystem")?;
93+
94+
Ok(out_file)
95+
}
96+
#[cfg(feature = "bios")]
97+
/// Create an MBR disk image for booting on BIOS systems.
98+
pub fn create_bios_image(&self, image_filename: &Path) -> anyhow::Result<()> {
99+
const BIOS_STAGE_3: &str = "boot-stage-3";
100+
const BIOS_STAGE_4: &str = "boot-stage-4";
101+
let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH"));
102+
let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH"));
103+
let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH"));
104+
let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH"));
105+
let mut internal_files = BTreeMap::new();
106+
internal_files.insert(BIOS_STAGE_3, stage_3_path);
107+
internal_files.insert(BIOS_STAGE_4, stage_4_path);
108+
109+
let fat_partition = self
110+
.create_fat_filesystem_image(internal_files)
111+
.context("failed to create FAT partition")?;
112+
mbr::create_mbr_disk(
113+
bootsector_path,
114+
stage_2_path,
115+
fat_partition.path(),
116+
image_filename,
117+
)
118+
.context("failed to create BIOS MBR disk image")?;
119+
120+
fat_partition
121+
.close()
122+
.context("failed to delete FAT partition after disk image creation")?;
123+
Ok(())
124+
}
125+
126+
#[cfg(feature = "uefi")]
127+
/// Create a GPT disk image for booting on UEFI systems.
128+
pub fn create_uefi_image(&self, image_filename: &Path) -> anyhow::Result<()> {
129+
const UEFI_BOOT_FILENAME: &str = "efi/boot/bootx64.efi";
130+
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
131+
let mut internal_files = BTreeMap::new();
132+
internal_files.insert(UEFI_BOOT_FILENAME, bootloader_path);
133+
let fat_partition = self
134+
.create_fat_filesystem_image(internal_files)
135+
.context("failed to create FAT partition")?;
136+
gpt::create_gpt_disk(fat_partition.path(), image_filename)
137+
.context("failed to create UEFI GPT disk image")?;
138+
fat_partition
139+
.close()
140+
.context("failed to delete FAT partition after disk image creation")?;
141+
142+
Ok(())
143+
}
144+
145+
#[cfg(feature = "uefi")]
146+
/// Create a folder containing the needed files for UEFI TFTP/PXE booting.
147+
pub fn create_uefi_tftp_folder(&self, tftp_path: &Path) -> anyhow::Result<()> {
148+
const UEFI_TFTP_BOOT_FILENAME: &str = "bootloader";
149+
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
150+
std::fs::create_dir_all(tftp_path)
151+
.with_context(|| format!("failed to create out dir at {}", tftp_path.display()))?;
152+
153+
let to = tftp_path.join(UEFI_TFTP_BOOT_FILENAME);
154+
std::fs::copy(bootloader_path, &to).with_context(|| {
155+
format!(
156+
"failed to copy bootloader from {} to {}",
157+
bootloader_path.display(),
158+
to.display()
159+
)
160+
})?;
161+
162+
for f in self.files.as_slice() {
163+
let to = tftp_path.join(f.destination);
164+
let result = std::fs::copy(f.source, to);
165+
if result.is_err() {
166+
return Err(anyhow::Error::from(result.unwrap_err()));
167+
}
168+
}
169+
170+
Ok(())
171+
}
172+
}
File renamed without changes.

src/uefi/mod.rs

Lines changed: 0 additions & 105 deletions
This file was deleted.

0 commit comments

Comments
 (0)