Skip to content

Commit 73f599d

Browse files
committed
Replace UefiBoot and BiosBoot with DiskImageBuilder
1 parent 5e42ad0 commit 73f599d

File tree

8 files changed

+208
-254
lines changed

8 files changed

+208
-254
lines changed

Diff for: build.rs

+44-55
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,51 @@ use std::{
66
const BOOTLOADER_VERSION: &str = env!("CARGO_PKG_VERSION");
77

88
fn main() {
9-
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
10-
119
#[cfg(feature = "uefi")]
12-
{
13-
let uefi_path = build_uefi_bootloader(&out_dir);
14-
println!(
15-
"cargo:rustc-env=UEFI_BOOTLOADER_PATH={}",
16-
uefi_path.display()
17-
);
18-
}
19-
10+
uefi_main();
2011
#[cfg(feature = "bios")]
21-
{
22-
let bios_boot_sector_path = build_bios_boot_sector(&out_dir);
23-
println!(
24-
"cargo:rustc-env=BIOS_BOOT_SECTOR_PATH={}",
25-
bios_boot_sector_path.display()
26-
);
27-
let bios_stage_2_path = build_bios_stage_2(&out_dir);
28-
println!(
29-
"cargo:rustc-env=BIOS_STAGE_2_PATH={}",
30-
bios_stage_2_path.display()
31-
);
32-
33-
let bios_stage_3_path = build_bios_stage_3(&out_dir);
34-
println!(
35-
"cargo:rustc-env=BIOS_STAGE_3_PATH={}",
36-
bios_stage_3_path.display()
37-
);
12+
bios_main();
13+
}
3814

39-
let bios_stage_4_path = build_bios_stage_4(&out_dir);
40-
println!(
41-
"cargo:rustc-env=BIOS_STAGE_4_PATH={}",
42-
bios_stage_4_path.display()
43-
);
44-
}
15+
#[cfg(not(docsrs_dummy_build))]
16+
#[cfg(feature = "uefi")]
17+
fn uefi_main() {
18+
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
19+
let uefi_path = build_uefi_bootloader(&out_dir);
20+
println!(
21+
"cargo:rustc-env=UEFI_BOOTLOADER_PATH={}",
22+
uefi_path.display()
23+
);
4524
}
4625

4726
#[cfg(not(docsrs_dummy_build))]
27+
#[cfg(feature = "bios")]
28+
fn bios_main() {
29+
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
30+
let bios_boot_sector_path = build_bios_boot_sector(&out_dir);
31+
println!(
32+
"cargo:rustc-env=BIOS_BOOT_SECTOR_PATH={}",
33+
bios_boot_sector_path.display()
34+
);
35+
let bios_stage_2_path = build_bios_stage_2(&out_dir);
36+
println!(
37+
"cargo:rustc-env=BIOS_STAGE_2_PATH={}",
38+
bios_stage_2_path.display()
39+
);
40+
41+
let bios_stage_3_path = build_bios_stage_3(&out_dir);
42+
println!(
43+
"cargo:rustc-env=BIOS_STAGE_3_PATH={}",
44+
bios_stage_3_path.display()
45+
);
46+
47+
let bios_stage_4_path = build_bios_stage_4(&out_dir);
48+
println!(
49+
"cargo:rustc-env=BIOS_STAGE_4_PATH={}",
50+
bios_stage_4_path.display()
51+
);
52+
}
53+
4854
#[cfg(feature = "uefi")]
4955
fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
5056
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -79,7 +85,6 @@ fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
7985
}
8086
}
8187

82-
#[cfg(not(docsrs_dummy_build))]
8388
#[cfg(feature = "bios")]
8489
fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
8590
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -122,7 +127,6 @@ fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
122127
convert_elf_to_bin(elf_path)
123128
}
124129

125-
#[cfg(not(docsrs_dummy_build))]
126130
#[cfg(feature = "bios")]
127131
fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
128132
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -163,7 +167,6 @@ fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
163167
convert_elf_to_bin(elf_path)
164168
}
165169

166-
#[cfg(not(docsrs_dummy_build))]
167170
#[cfg(feature = "bios")]
168171
fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
169172
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -204,7 +207,6 @@ fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
204207
convert_elf_to_bin(elf_path)
205208
}
206209

207-
#[cfg(not(docsrs_dummy_build))]
208210
#[cfg(feature = "bios")]
209211
fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
210212
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -277,22 +279,9 @@ fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
277279
// dummy implementations because docsrs builds have no network access
278280

279281
#[cfg(docsrs_dummy_build)]
280-
fn build_uefi_bootloader(_out_dir: &Path) -> PathBuf {
281-
PathBuf::new()
282-
}
283-
#[cfg(docsrs_dummy_build)]
284-
fn build_bios_boot_sector(_out_dir: &Path) -> PathBuf {
285-
PathBuf::new()
286-
}
287-
#[cfg(docsrs_dummy_build)]
288-
fn build_bios_stage_2(_out_dir: &Path) -> PathBuf {
289-
PathBuf::new()
290-
}
291-
#[cfg(docsrs_dummy_build)]
292-
fn build_bios_stage_3(_out_dir: &Path) -> PathBuf {
293-
PathBuf::new()
294-
}
282+
#[cfg(feature = "uefi")]
283+
fn uefi_main() {}
284+
295285
#[cfg(docsrs_dummy_build)]
296-
fn build_bios_stage_4(_out_dir: &Path) -> PathBuf {
297-
PathBuf::new()
298-
}
286+
#[cfg(feature = "bios")]
287+
fn bios_main() {}

Diff for: src/bios/mod.rs

-67
This file was deleted.

Diff for: src/uefi/gpt.rs renamed to src/gpt.rs

File renamed without changes.

Diff for: src/lib.rs

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

Diff for: src/bios/mbr.rs renamed to src/mbr.rs

File renamed without changes.

0 commit comments

Comments
 (0)