Skip to content

Commit 5310e4b

Browse files
committed
Integrate BIOS bootsector into build system
1 parent 3812a86 commit 5310e4b

File tree

7 files changed

+237
-10
lines changed

7 files changed

+237
-10
lines changed

Cargo.lock

Lines changed: 108 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ exclude = ["examples/basic", "examples/test_framework"]
2929
anyhow = "1.0.32"
3030
fatfs = "0.3.4"
3131
gpt = "3.0.0"
32+
mbrman = "0.4.2"
3233

3334
[dependencies.bootloader-x86_64-bios]
3435
version = "0.1.0-alpha.0"
@@ -70,6 +71,9 @@ rustflags = [
7071
"code-model=large",
7172
]
7273

74+
[build-dependencies]
75+
llvm-tools = "0.1.1"
76+
7377
[package.metadata.docs.rs]
7478
default-target = "x86_64-unknown-linux-gnu"
7579

bios/boot_sector/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub extern "C" fn first_stage(disk_number: u16) {
6363
print_char(b'4');
6464
let boot_sector = fat::BootSector::deserialize(fat_slice);
6565

66+
print_char(b'5');
67+
6668
// TODO: get root dir
6769

6870
// TODO: get offset of `second_stage` file

build.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
7676
let status = cmd
7777
.status()
7878
.expect("failed to run cargo install for bios boot sector");
79-
if status.success() {
79+
let elf_path = if status.success() {
8080
let path = out_dir
8181
.join("bin")
8282
.join("bootloader-x86_64-bios-boot-sector");
@@ -87,5 +87,33 @@ fn build_bios_boot_sector(out_dir: &Path) -> PathBuf {
8787
path
8888
} else {
8989
panic!("failed to build bios boot sector");
90+
};
91+
convert_elf_to_bin(elf_path)
92+
}
93+
94+
fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
95+
let flat_binary_path = elf_path.with_extension("bin");
96+
97+
let llvm_tools = llvm_tools::LlvmTools::new().expect("failed to get llvm tools");
98+
let objcopy = llvm_tools
99+
.tool(&llvm_tools::exe("llvm-objcopy"))
100+
.expect("LlvmObjcopyNotFound");
101+
102+
// convert first stage to binary
103+
let mut cmd = Command::new(objcopy);
104+
cmd.arg("-I").arg("elf64-x86-64");
105+
cmd.arg("-O").arg("binary");
106+
cmd.arg("--binary-architecture=i386:x86-64");
107+
cmd.arg(&elf_path);
108+
cmd.arg(&flat_binary_path);
109+
let output = cmd
110+
.output()
111+
.expect("failed to execute llvm-objcopy command");
112+
if !output.status.success() {
113+
panic!(
114+
"objcopy failed: {}",
115+
String::from_utf8_lossy(&output.stderr)
116+
);
90117
}
118+
flat_binary_path
91119
}

src/lib.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,41 @@ use std::{
7777

7878
mod fat;
7979
mod gpt;
80+
mod mbr;
8081

8182
const KERNEL_FILE_NAME: &str = "kernel-x86_64";
8283

83-
pub fn create_uefi_disk_image(
84-
kernel_binary: &Path,
85-
out_fat_path: &Path,
86-
out_gpt_path: &Path,
87-
) -> anyhow::Result<()> {
84+
/// Creates a bootable FAT partition at the given path.
85+
pub fn create_boot_partition(kernel_binary: &Path, out_path: &Path) -> anyhow::Result<()> {
8886
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
8987

9088
let mut files = BTreeMap::new();
9189
files.insert("efi/boot/bootx64.efi", bootloader_path);
9290
files.insert(KERNEL_FILE_NAME, kernel_binary);
9391

94-
fat::create_fat_filesystem(files, &out_fat_path)
95-
.context("failed to create UEFI FAT filesystem")?;
96-
gpt::create_gpt_disk(out_fat_path, out_gpt_path)
92+
fat::create_fat_filesystem(files, &out_path).context("failed to create UEFI FAT filesystem")?;
93+
94+
Ok(())
95+
}
96+
97+
pub fn create_uefi_disk_image(
98+
boot_partition_path: &Path,
99+
out_gpt_path: &Path,
100+
) -> anyhow::Result<()> {
101+
gpt::create_gpt_disk(boot_partition_path, out_gpt_path)
97102
.context("failed to create UEFI GPT disk image")?;
98103

99104
Ok(())
100105
}
101106

107+
pub fn create_bios_disk_image(
108+
boot_partition_path: &Path,
109+
out_mbr_path: &Path,
110+
) -> anyhow::Result<()> {
111+
let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH"));
112+
113+
mbr::create_mbr_disk(bootsector_path, boot_partition_path, out_mbr_path)
114+
.context("failed to create BIOS MBR disk image")?;
115+
116+
Ok(())
117+
}

src/mbr.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use anyhow::Context;
2+
use std::{
3+
fs::{self, File},
4+
io,
5+
path::Path,
6+
};
7+
const SECTOR_SIZE: u32 = 512;
8+
9+
pub fn create_mbr_disk(
10+
bootsector_path: &Path,
11+
boot_partition_path: &Path,
12+
out_mbr_path: &Path,
13+
) -> anyhow::Result<()> {
14+
let mut boot_sector = File::open(bootsector_path).context("failed to open boot sector")?;
15+
let mut mbr =
16+
mbrman::MBR::read_from(&mut boot_sector, SECTOR_SIZE).context("failed to read MBR")?;
17+
18+
for (index, partition) in mbr.iter() {
19+
if !partition.is_unused() {
20+
anyhow::bail!("partition {index} should be unused");
21+
}
22+
}
23+
24+
let mut boot_partition =
25+
File::open(boot_partition_path).context("failed to open FAT boot partition")?;
26+
let boot_partition_size = boot_partition
27+
.metadata()
28+
.context("failed to read file metadata of FAT boot partition")?
29+
.len();
30+
31+
mbr[1] = mbrman::MBRPartitionEntry {
32+
boot: true,
33+
starting_lba: 1,
34+
sectors: (boot_partition_size / u64::from(SECTOR_SIZE))
35+
.try_into()
36+
.context("size of FAT partition is larger than u32::MAX")?,
37+
//TODO: is this the correct type?
38+
sys: 0x0c, // FAT32 with LBA
39+
40+
first_chs: mbrman::CHS::empty(),
41+
last_chs: mbrman::CHS::empty(),
42+
};
43+
44+
let mut disk = fs::OpenOptions::new()
45+
.create(true)
46+
.truncate(true)
47+
.read(true)
48+
.write(true)
49+
.open(&out_mbr_path)
50+
.with_context(|| {
51+
format!(
52+
"failed to create MBR disk image at `{}`",
53+
out_mbr_path.display()
54+
)
55+
})?;
56+
57+
mbr.write_into(&mut disk)
58+
.context("failed to write MBR header to disk image")?;
59+
60+
io::copy(&mut boot_partition, &mut disk)
61+
.context("failed to copy FAT image to MBR disk image")?;
62+
63+
Ok(())
64+
}

tests/runner/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ const QEMU_ARGS: &[&str] = &[
1313
pub fn run_test_kernel(kernel_binary_path: &str) {
1414
let kernel_path = Path::new(kernel_binary_path);
1515
let out_fat_path = kernel_path.with_extension("fat");
16+
bootloader::create_boot_partition(kernel_path, &out_fat_path).unwrap();
1617
let out_gpt_path = kernel_path.with_extension("gpt");
17-
bootloader::create_uefi_disk_image(kernel_path, &out_fat_path, &out_gpt_path).unwrap();
18+
bootloader::create_uefi_disk_image(&out_fat_path, &out_gpt_path).unwrap();
19+
let out_mbr_path = kernel_path.with_extension("mbr");
20+
bootloader::create_bios_disk_image(&out_fat_path, &out_mbr_path).unwrap();
21+
22+
// TODO: run tests with BIOS bootloader too
1823

1924
let mut run_cmd = Command::new("qemu-system-x86_64");
2025
run_cmd

0 commit comments

Comments
 (0)