Skip to content

Commit 7d6715e

Browse files
committed
Replace UefiBoot and BiosBoot with DiskImageBuilder
1 parent 6939d4f commit 7d6715e

File tree

9 files changed

+232
-301
lines changed

9 files changed

+232
-301
lines changed

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());
@@ -80,7 +86,6 @@ fn build_uefi_bootloader(out_dir: &Path) -> PathBuf {
8086
}
8187
}
8288

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

126-
#[cfg(not(docsrs_dummy_build))]
127131
#[cfg(feature = "bios")]
128132
fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
129133
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -168,7 +172,6 @@ fn build_bios_stage_2(out_dir: &Path) -> PathBuf {
168172
convert_elf_to_bin(elf_path)
169173
}
170174

171-
#[cfg(not(docsrs_dummy_build))]
172175
#[cfg(feature = "bios")]
173176
fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
174177
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -209,7 +212,6 @@ fn build_bios_stage_3(out_dir: &Path) -> PathBuf {
209212
convert_elf_to_bin(elf_path)
210213
}
211214

212-
#[cfg(not(docsrs_dummy_build))]
213215
#[cfg(feature = "bios")]
214216
fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
215217
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
@@ -282,22 +284,9 @@ fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
282284
// dummy implementations because docsrs builds have no network access
283285

284286
#[cfg(docsrs_dummy_build)]
285-
fn build_uefi_bootloader(_out_dir: &Path) -> PathBuf {
286-
PathBuf::new()
287-
}
288-
#[cfg(docsrs_dummy_build)]
289-
fn build_bios_boot_sector(_out_dir: &Path) -> PathBuf {
290-
PathBuf::new()
291-
}
292-
#[cfg(docsrs_dummy_build)]
293-
fn build_bios_stage_2(_out_dir: &Path) -> PathBuf {
294-
PathBuf::new()
295-
}
296-
#[cfg(docsrs_dummy_build)]
297-
fn build_bios_stage_3(_out_dir: &Path) -> PathBuf {
298-
PathBuf::new()
299-
}
287+
#[cfg(feature = "uefi")]
288+
fn uefi_main() {}
289+
300290
#[cfg(docsrs_dummy_build)]
301-
fn build_bios_stage_4(_out_dir: &Path) -> PathBuf {
302-
PathBuf::new()
303-
}
291+
#[cfg(feature = "bios")]
292+
fn bios_main() {}

src/bios/mod.rs

-78
This file was deleted.
File renamed without changes.

src/lib.rs

+156-7
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.

0 commit comments

Comments
 (0)