forked from rust-osdev/bootloader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmbr.rs
101 lines (90 loc) · 3.25 KB
/
mbr.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use anyhow::Context;
use mbrman::BOOT_ACTIVE;
use std::{
fs::{self, File},
io::{self, Seek, SeekFrom},
path::Path,
};
const SECTOR_SIZE: u32 = 512;
pub fn create_mbr_disk(
bootsector_path: &Path,
second_stage_path: &Path,
boot_partition_path: &Path,
out_mbr_path: &Path,
) -> anyhow::Result<()> {
let mut boot_sector = File::open(bootsector_path).context("failed to open boot sector")?;
let mut mbr =
mbrman::MBR::read_from(&mut boot_sector, SECTOR_SIZE).context("failed to read MBR")?;
for (index, partition) in mbr.iter() {
if !partition.is_unused() {
anyhow::bail!("partition {index} should be unused");
}
}
let mut second_stage =
File::open(second_stage_path).context("failed to open second stage binary")?;
let second_stage_size = second_stage
.metadata()
.context("failed to read file metadata of second stage")?
.len();
let second_stage_start_sector = 1;
let second_stage_sectors = ((second_stage_size - 1) / u64::from(SECTOR_SIZE) + 1)
.try_into()
.context("size of second stage is larger than u32::MAX")?;
mbr[1] = mbrman::MBRPartitionEntry {
boot: BOOT_ACTIVE,
starting_lba: second_stage_start_sector,
sectors: second_stage_sectors,
// see BOOTLOADER_SECOND_STAGE_PARTITION_TYPE in `boot_sector` crate
sys: 0x20,
first_chs: mbrman::CHS::empty(),
last_chs: mbrman::CHS::empty(),
};
let mut boot_partition =
File::open(boot_partition_path).context("failed to open FAT boot partition")?;
let boot_partition_start_sector = second_stage_start_sector + second_stage_sectors;
let boot_partition_size = boot_partition
.metadata()
.context("failed to read file metadata of FAT boot partition")?
.len();
mbr[2] = mbrman::MBRPartitionEntry {
boot: BOOT_ACTIVE,
starting_lba: boot_partition_start_sector,
sectors: ((boot_partition_size - 1) / u64::from(SECTOR_SIZE) + 1)
.try_into()
.context("size of FAT partition is larger than u32::MAX")?,
//TODO: is this the correct type?
sys: 0x0c, // FAT32 with LBA
first_chs: mbrman::CHS::empty(),
last_chs: mbrman::CHS::empty(),
};
let mut disk = fs::OpenOptions::new()
.create(true)
.truncate(true)
.read(true)
.write(true)
.open(out_mbr_path)
.with_context(|| {
format!(
"failed to create MBR disk image at `{}`",
out_mbr_path.display()
)
})?;
mbr.write_into(&mut disk)
.context("failed to write MBR header to disk image")?;
// second stage
assert_eq!(
disk.stream_position()
.context("failed to get disk image seek position")?,
(second_stage_start_sector * SECTOR_SIZE).into()
);
io::copy(&mut second_stage, &mut disk)
.context("failed to copy second stage binary to MBR disk image")?;
// fat partition
disk.seek(SeekFrom::Start(
(boot_partition_start_sector * SECTOR_SIZE).into(),
))
.context("seek failed")?;
io::copy(&mut boot_partition, &mut disk)
.context("failed to copy FAT image to MBR disk image")?;
Ok(())
}