Skip to content

Commit 37e3d6c

Browse files
committed
Replace UefiBoot and BiosBoot with DiskImageBuilder
1 parent 0f6453c commit 37e3d6c

File tree

9 files changed

+188
-250
lines changed

9 files changed

+188
-250
lines changed

build.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ async fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
9191
}
9292
}
9393

94-
#[cfg(not(docsrs_dummy_build))]
9594
#[cfg(feature = "bios")]
9695
async fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
9796
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -135,7 +134,6 @@ async fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
135134
convert_elf_to_bin(elf_path).await
136135
}
137136

138-
#[cfg(not(docsrs_dummy_build))]
139137
#[cfg(feature = "bios")]
140138
async fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
141139
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -181,7 +179,6 @@ async fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
181179
convert_elf_to_bin(elf_path).await
182180
}
183181

184-
#[cfg(not(docsrs_dummy_build))]
185182
#[cfg(feature = "bios")]
186183
async fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
187184
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -223,7 +220,6 @@ async fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
223220
convert_elf_to_bin(elf_path).await
224221
}
225222

226-
#[cfg(not(docsrs_dummy_build))]
227223
#[cfg(feature = "bios")]
228224
async fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
229225
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());

src/bios/mod.rs

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

src/uefi/mod.rs

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

0 commit comments

Comments
 (0)