Skip to content

Commit 2ac0c82

Browse files
committed
Create FAT file system image from .efi image
Can be run in QEMU through `qemu-system-x86_64 -drive file=bootimage-uefi-blog_os.fat -bios ovmf/OVMF_CODE.fd`.
1 parent dc1267b commit 2ac0c82

File tree

3 files changed

+195
-4
lines changed

3 files changed

+195
-4
lines changed

Cargo.lock

Lines changed: 102 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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ llvm-tools = { version = "0.1.1", optional = true }
4646
thiserror = { version = "1.0.20", optional = true }
4747
json = { version = "0.12.4", optional = true }
4848
rsdp = { version = "1.0.0", optional = true }
49+
fatfs = { version = "0.3.4", optional = true }
4950

5051
[dependencies.font8x8]
5152
version = "0.2.5"
@@ -60,7 +61,7 @@ serde = { version = "1.0", features = ["derive"], optional = true}
6061

6162
[features]
6263
default = []
63-
builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"]
64+
builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "fatfs"]
6465
runner = ["anyhow"]
6566
bios_bin = ["binary", "vga_320x200", "rsdp"]
6667
uefi_bin = ["binary", "uefi", "font8x8"]

src/bin/builder.rs

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Context};
22
use argh::FromArgs;
33
use bootloader::disk_image::create_disk_image;
44
use std::{
5-
fs,
5+
fs, io,
66
path::{Path, PathBuf},
77
process::Command,
88
str::FromStr,
@@ -66,14 +66,30 @@ impl FromStr for Firmware {
6666
}
6767
}
6868

69+
impl Firmware {
70+
fn uefi(&self) -> bool {
71+
match self {
72+
Firmware::Bios => false,
73+
Firmware::Uefi | Firmware::All => true,
74+
}
75+
}
76+
77+
fn bios(&self) -> bool {
78+
match self {
79+
Firmware::Bios | Firmware::All => true,
80+
Firmware::Uefi => false,
81+
}
82+
}
83+
}
84+
6985
/// Firmware must be one of `uefi`, `bios`, or `all`.
7086
#[derive(Debug, displaydoc::Display, Eq, PartialEq, Copy, Clone)]
7187
struct FirmwareParseError;
7288

7389
fn main() -> anyhow::Result<()> {
7490
let args: BuildArguments = argh::from_env();
7591

76-
if args.firmware == Firmware::Uefi || args.firmware == Firmware::All {
92+
if args.firmware.uefi() {
7793
let build_or_run = if args.run { "run" } else { "build" };
7894
let mut cmd = Command::new(env!("CARGO"));
7995
cmd.arg(build_or_run).arg("--bin").arg("uefi");
@@ -91,9 +107,81 @@ fn main() -> anyhow::Result<()> {
91107
cmd.env("KERNEL", &args.kernel_binary);
92108
cmd.env("KERNEL_MANIFEST", &args.kernel_manifest);
93109
assert!(cmd.status()?.success());
110+
111+
// Retrieve binary paths
112+
cmd.arg("--message-format").arg("json");
113+
let output = cmd
114+
.output()
115+
.context("failed to execute kernel build with json output")?;
116+
if !output.status.success() {
117+
return Err(anyhow!("{}", String::from_utf8_lossy(&output.stderr)));
118+
}
119+
let mut executables = Vec::new();
120+
for line in String::from_utf8(output.stdout)
121+
.context("build JSON output is not valid UTF-8")?
122+
.lines()
123+
{
124+
let mut artifact = json::parse(line).context("build JSON output is not valid JSON")?;
125+
if let Some(executable) = artifact["executable"].take_string() {
126+
executables.push(PathBuf::from(executable));
127+
}
128+
}
129+
130+
assert_eq!(executables.len(), 1);
131+
let executable_path = executables.pop().unwrap();
132+
let executable_name = executable_path.file_stem().unwrap().to_str().unwrap();
133+
let kernel_name = args.kernel_binary.file_name().unwrap().to_str().unwrap();
134+
135+
if let Some(out_dir) = &args.out_dir {
136+
let efi_file =
137+
out_dir.join(format!("bootimage-{}-{}.efi", executable_name, kernel_name));
138+
fs::copy(&executable_path, &efi_file).context("failed to copy efi file to out dir")?;
139+
140+
let efi_size = fs::metadata(&efi_file)
141+
.context("failed to read metadata of efi file")?
142+
.len();
143+
144+
// create fat partition
145+
{
146+
const MB: u64 = 1024 * 1024;
147+
148+
let fat_path = efi_file.with_extension("fat");
149+
dbg!(&fat_path);
150+
let fat_file = fs::OpenOptions::new()
151+
.read(true)
152+
.write(true)
153+
.create(true)
154+
.truncate(true)
155+
.open(&fat_path)
156+
.context("Failed to create UEFI FAT file")?;
157+
let efi_size_rounded = ((efi_size - 1) / MB + 1) * MB;
158+
fat_file
159+
.set_len(dbg!(efi_size_rounded))
160+
.context("failed to set UEFI FAT file length")?;
161+
162+
// create new FAT partition
163+
fatfs::format_volume(&fat_file, fatfs::FormatVolumeOptions::new())
164+
.context("Failed to format UEFI FAT file")?;
165+
166+
// copy EFI file to FAT filesystem
167+
let partition = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new())
168+
.context("Failed to open FAT file system of UEFI FAT file")?;
169+
let root_dir = partition.root_dir();
170+
root_dir.create_dir("efi")?;
171+
root_dir.create_dir("efi/boot")?;
172+
let mut bootx64 = root_dir.create_file("efi/boot/bootx64.efi")?;
173+
bootx64.truncate()?;
174+
io::copy(&mut fs::File::open(&executable_path)?, &mut bootx64)?;
175+
}
176+
177+
// create gpt disk
178+
{
179+
//todo!()
180+
}
181+
}
94182
}
95183

96-
if args.firmware == Firmware::Bios || args.firmware == Firmware::All {
184+
if args.firmware.bios() {
97185
let mut cmd = Command::new(env!("CARGO"));
98186
cmd.arg("build").arg("--bin").arg("bios");
99187
cmd.arg("--profile").arg("release");

0 commit comments

Comments
 (0)