Skip to content

Commit aefa4aa

Browse files
committed
Add function to create UEFI disk image to library
1 parent ee9bf20 commit aefa4aa

File tree

1 file changed

+135
-4
lines changed

1 file changed

+135
-4
lines changed

src/lib.rs

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,138 @@ for all possible configuration options.
6767

6868
#![warn(missing_docs)]
6969

70-
/// Provides a function to turn a bootloader executable into a disk image.
71-
///
72-
/// Used by the `builder` binary. Only available when the `builder` feature is enabled.
73-
pub mod disk_image;
70+
use std::{
71+
fs::{self, File},
72+
io::{self, Seek},
73+
path::Path,
74+
};
75+
76+
use anyhow::Context;
77+
78+
pub fn create_uefi_disk_image(
79+
kernel_binary: &Path,
80+
out_fat_path: &Path,
81+
out_gpt_path: &Path,
82+
) -> anyhow::Result<()> {
83+
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
84+
85+
create_fat_filesystem(bootloader_path, kernel_binary, &out_fat_path)
86+
.context("failed to create UEFI FAT filesystem")?;
87+
create_gpt_disk(out_fat_path, out_gpt_path);
88+
89+
Ok(())
90+
}
91+
92+
fn create_fat_filesystem(
93+
bootloader_efi_file: &Path,
94+
kernel_binary: &Path,
95+
out_fat_path: &Path,
96+
) -> anyhow::Result<()> {
97+
const MB: u64 = 1024 * 1024;
98+
99+
// retrieve size of `.efi` file and round it up
100+
let efi_size = fs::metadata(&bootloader_efi_file).unwrap().len();
101+
// size of a megabyte
102+
// round it to next megabyte
103+
let efi_size_rounded = ((efi_size - 1) / MB + 1) * MB;
104+
105+
// create new filesystem image file at the given path and set its length
106+
let fat_file = fs::OpenOptions::new()
107+
.read(true)
108+
.write(true)
109+
.create(true)
110+
.truncate(true)
111+
.open(&out_fat_path)
112+
.unwrap();
113+
fat_file.set_len(efi_size_rounded).unwrap();
114+
115+
// create new FAT file system and open it
116+
let label = {
117+
if let Some(name) = bootloader_efi_file.file_stem() {
118+
let converted = name.to_string_lossy();
119+
let name = converted.as_bytes();
120+
let mut label = [0u8; 11];
121+
let name = &name[..label.len()];
122+
let slice = &mut label[..name.len()];
123+
slice.copy_from_slice(name);
124+
label
125+
} else {
126+
*b"MY_RUST_OS!"
127+
}
128+
};
129+
let format_options = fatfs::FormatVolumeOptions::new().volume_label(label);
130+
fatfs::format_volume(&fat_file, format_options).context("Failed to format UEFI FAT file")?;
131+
let filesystem = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new())
132+
.context("Failed to open FAT file system of UEFI FAT file")?;
133+
134+
// copy EFI file to FAT filesystem
135+
let root_dir = filesystem.root_dir();
136+
root_dir.create_dir("efi").unwrap();
137+
root_dir.create_dir("efi/boot").unwrap();
138+
let mut bootx64 = root_dir.create_file("efi/boot/bootx64.efi").unwrap();
139+
bootx64.truncate().unwrap();
140+
io::copy(
141+
&mut fs::File::open(&bootloader_efi_file).unwrap(),
142+
&mut bootx64,
143+
)
144+
.unwrap();
145+
146+
// copy kernel to FAT filesystem
147+
let mut kernel_file = root_dir.create_file("kernel-x86_64")?;
148+
kernel_file.truncate()?;
149+
io::copy(&mut fs::File::open(&kernel_binary)?, &mut kernel_file)?;
150+
151+
Ok(())
152+
}
153+
154+
fn create_gpt_disk(fat_image: &Path, out_gpt_path: &Path) {
155+
// create new file
156+
let mut disk = fs::OpenOptions::new()
157+
.create(true)
158+
.truncate(true)
159+
.read(true)
160+
.write(true)
161+
.open(&out_gpt_path)
162+
.unwrap();
163+
164+
// set file size
165+
let partition_size: u64 = fs::metadata(&fat_image).unwrap().len();
166+
let disk_size = partition_size + 1024 * 64; // for GPT headers
167+
disk.set_len(disk_size).unwrap();
168+
169+
// create a protective MBR at LBA0 so that disk is not considered
170+
// unformatted on BIOS systems
171+
let mbr = gpt::mbr::ProtectiveMBR::with_lb_size(
172+
u32::try_from((disk_size / 512) - 1).unwrap_or(0xFF_FF_FF_FF),
173+
);
174+
mbr.overwrite_lba0(&mut disk).unwrap();
175+
176+
// create new GPT structure
177+
let block_size = gpt::disk::LogicalBlockSize::Lb512;
178+
let mut gpt = gpt::GptConfig::new()
179+
.writable(true)
180+
.initialized(false)
181+
.logical_block_size(block_size)
182+
.create_from_device(Box::new(&mut disk), None)
183+
.unwrap();
184+
gpt.update_partitions(Default::default()).unwrap();
185+
186+
// add new EFI system partition and get its byte offset in the file
187+
let partition_id = gpt
188+
.add_partition("boot", partition_size, gpt::partition_types::EFI, 0, None)
189+
.unwrap();
190+
let partition = gpt.partitions().get(&partition_id).unwrap();
191+
let start_offset = partition.bytes_start(block_size).unwrap();
192+
193+
// close the GPT structure and write out changes
194+
gpt.write().unwrap();
195+
196+
// place the FAT filesystem in the newly created partition
197+
disk.seek(io::SeekFrom::Start(start_offset)).unwrap();
198+
io::copy(&mut File::open(&fat_image).unwrap(), &mut disk).unwrap();
199+
}
200+
201+
// Provides a function to turn a bootloader executable into a disk image.
202+
//
203+
// Used by the `builder` binary. Only available when the `builder` feature is enabled.
204+
// pub mod disk_image;

0 commit comments

Comments
 (0)