Skip to content

Commit 92dd42d

Browse files
committed
implement UEFI PXE
1 parent 6a014df commit 92dd42d

File tree

4 files changed

+159
-5
lines changed

4 files changed

+159
-5
lines changed

src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ use std::{
7878
mod fat;
7979
mod gpt;
8080
mod mbr;
81+
mod pxe;
8182

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

@@ -121,3 +122,12 @@ pub fn create_bios_disk_image(
121122

122123
Ok(())
123124
}
125+
126+
pub fn create_uefi_pxe_tftp_folder(kernel_binary: &Path, out_path: &Path) -> anyhow::Result<()> {
127+
let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));
128+
129+
pxe::create_uefi_tftp_folder(bootloader_path, kernel_binary, out_path)
130+
.context("failed to create UEFI PXE tftp folder")?;
131+
132+
Ok(())
133+
}

src/pxe.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use std::path::Path;
2+
3+
use anyhow::Context;
4+
5+
pub fn create_uefi_tftp_folder(
6+
bootloader_path: &Path,
7+
kernel_binary: &Path,
8+
out_path: &Path,
9+
) -> anyhow::Result<()> {
10+
std::fs::create_dir_all(out_path)
11+
.with_context(|| format!("failed to create out dir at {}", out_path.display()))?;
12+
13+
let to = out_path.join("bootloader");
14+
std::fs::copy(bootloader_path, &to).with_context(|| {
15+
format!(
16+
"failed to copy bootloader from {} to {}",
17+
bootloader_path.display(),
18+
to.display()
19+
)
20+
})?;
21+
22+
let to = out_path.join("kernel-x86_64");
23+
std::fs::copy(kernel_binary, &to).with_context(|| {
24+
format!(
25+
"failed to copy kernel from {} to {}",
26+
kernel_binary.display(),
27+
to.display()
28+
)
29+
})?;
30+
31+
Ok(())
32+
}

tests/runner/src/lib.rs

+43-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ const QEMU_ARGS: &[&str] = &[
1111
];
1212

1313
pub fn run_test_kernel(kernel_binary_path: &str) {
14+
run_test_kernel_on_uefi(kernel_binary_path);
15+
run_test_kernel_on_uefi_pxe(kernel_binary_path);
16+
// TODO: run tests with BIOS bootloader too
17+
}
18+
19+
pub fn run_test_kernel_on_uefi(kernel_binary_path: &str) {
1420
let kernel_path = Path::new(kernel_binary_path);
1521
let out_fat_path = kernel_path.with_extension("fat");
1622
bootloader::create_boot_partition(kernel_path, &out_fat_path).unwrap();
@@ -19,8 +25,6 @@ pub fn run_test_kernel(kernel_binary_path: &str) {
1925
let out_mbr_path = kernel_path.with_extension("mbr");
2026
bootloader::create_bios_disk_image(&out_fat_path, &out_mbr_path).unwrap();
2127

22-
// TODO: run tests with BIOS bootloader too
23-
2428
let mut run_cmd = Command::new("qemu-system-x86_64");
2529
run_cmd
2630
.arg("-drive")
@@ -42,3 +46,40 @@ pub fn run_test_kernel(kernel_binary_path: &str) {
4246
other => panic!("Test failed with unexpected exit code `{:?}`", other),
4347
}
4448
}
49+
50+
pub fn run_test_kernel_on_uefi_pxe(kernel_binary_path: &str) {
51+
let kernel_path = Path::new(kernel_binary_path);
52+
let out_tftp_path = kernel_path.with_extension(".tftp");
53+
54+
bootloader::create_uefi_pxe_tftp_folder(kernel_path, &out_tftp_path).unwrap();
55+
56+
let out_fat_path = kernel_path.with_extension("fat");
57+
bootloader::create_boot_partition(kernel_path, &out_fat_path).unwrap();
58+
let out_gpt_path = kernel_path.with_extension("gpt");
59+
bootloader::create_uefi_disk_image(&out_fat_path, &out_gpt_path).unwrap();
60+
let out_mbr_path = kernel_path.with_extension("mbr");
61+
bootloader::create_bios_disk_image(&out_fat_path, &out_mbr_path).unwrap();
62+
63+
let mut run_cmd = Command::new("qemu-system-x86_64");
64+
run_cmd.arg("-netdev").arg(format!(
65+
"user,id=net0,net=192.168.17.0/24,tftp={},bootfile=bootloader,id=net0",
66+
out_tftp_path.display()
67+
));
68+
run_cmd.arg("-device").arg("virtio-net-pci,netdev=net0");
69+
run_cmd.args(QEMU_ARGS);
70+
run_cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
71+
72+
let child_output = run_cmd.output().unwrap();
73+
strip_ansi_escapes::Writer::new(std::io::stderr())
74+
.write_all(&child_output.stderr)
75+
.unwrap();
76+
strip_ansi_escapes::Writer::new(std::io::stderr())
77+
.write_all(&child_output.stdout)
78+
.unwrap();
79+
80+
match child_output.status.code() {
81+
Some(33) => {} // success
82+
Some(35) => panic!("Test failed"), // success
83+
other => panic!("Test failed with unexpected exit code `{:?}`", other),
84+
}
85+
}

uefi/src/main.rs

+74-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ use uefi::{
1717
file::{File, FileAttribute, FileInfo, FileMode},
1818
fs::SimpleFileSystem,
1919
},
20+
network::{
21+
pxe::{BaseCode, DhcpV4Packet},
22+
IpAddress,
23+
},
2024
},
2125
table::boot::{AllocateType, MemoryDescriptor, MemoryType},
22-
CStr16,
26+
CStr16, CStr8,
2327
};
2428
use x86_64::{
2529
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
@@ -122,6 +126,16 @@ fn main_inner(image: Handle, mut st: SystemTable<Boot>) -> Status {
122126
}
123127

124128
fn load_kernel(image: Handle, st: &SystemTable<Boot>) -> Kernel<'static> {
129+
let kernel_slice = load_kernel_file(image, st).expect("couldn't find kernel");
130+
Kernel::parse(kernel_slice)
131+
}
132+
133+
fn load_kernel_file(image: Handle, st: &SystemTable<Boot>) -> Option<&'static mut [u8]> {
134+
load_kernel_file_from_disk(image, st)
135+
.or_else(|| load_kernel_file_from_tftp_boot_server(image, st))
136+
}
137+
138+
fn load_kernel_file_from_disk(image: Handle, st: &SystemTable<Boot>) -> Option<&'static mut [u8]> {
125139
let file_system_raw = {
126140
let ref this = st.boot_services();
127141
let loaded_image = this
@@ -138,7 +152,7 @@ fn load_kernel(image: Handle, st: &SystemTable<Boot>) -> Kernel<'static> {
138152

139153
let device_handle = this
140154
.locate_device_path::<SimpleFileSystem>(&mut device_path)
141-
.expect("Failed to locate `SimpleFileSystem` protocol on device path");
155+
.ok()?;
142156

143157
this.handle_protocol::<SimpleFileSystem>(device_handle)
144158
}
@@ -172,7 +186,64 @@ fn load_kernel(image: Handle, st: &SystemTable<Boot>) -> Kernel<'static> {
172186
let kernel_slice = unsafe { slice::from_raw_parts_mut(kernel_ptr, kernel_size) };
173187
kernel_file.read(kernel_slice).unwrap();
174188

175-
Kernel::parse(kernel_slice)
189+
Some(kernel_slice)
190+
}
191+
192+
fn load_kernel_file_from_tftp_boot_server(
193+
image: Handle,
194+
st: &SystemTable<Boot>,
195+
) -> Option<&'static mut [u8]> {
196+
let ref this = st.boot_services();
197+
198+
let file_system_raw = {
199+
let ref this = st.boot_services();
200+
let loaded_image = this
201+
.handle_protocol::<LoadedImage>(image)
202+
.expect("Failed to retrieve `LoadedImage` protocol from handle");
203+
let loaded_image = unsafe { &*loaded_image.get() };
204+
205+
let device_handle = loaded_image.device();
206+
207+
let device_path = this
208+
.handle_protocol::<DevicePath>(device_handle)
209+
.expect("Failed to retrieve `DevicePath` protocol from image's device handle");
210+
let mut device_path = unsafe { &*device_path.get() };
211+
212+
let device_handle = this
213+
.locate_device_path::<BaseCode>(&mut device_path)
214+
.expect("Failed to locate `BaseCode` protocol on device path");
215+
216+
this.handle_protocol::<BaseCode>(device_handle)
217+
}
218+
.unwrap();
219+
let base_code = unsafe { &mut *file_system_raw.get() };
220+
221+
let mode = base_code.mode();
222+
assert!(mode.dhcp_ack_received);
223+
let dhcpv4: &DhcpV4Packet = mode.dhcp_ack.as_ref();
224+
let server_ip = IpAddress::new_v4(dhcpv4.bootp_si_addr);
225+
226+
let filename = CStr8::from_bytes_with_nul(b"kernel-x86_64\0").unwrap();
227+
let file_size = base_code.tftp_get_file_size(&server_ip, filename).unwrap();
228+
229+
let kernel_size = usize::try_from(file_size).unwrap();
230+
231+
let kernel_ptr = st
232+
.boot_services()
233+
.allocate_pages(
234+
AllocateType::AnyPages,
235+
MemoryType::LOADER_DATA,
236+
((kernel_size - 1) / 4096) + 1,
237+
)
238+
.unwrap() as *mut u8;
239+
unsafe { ptr::write_bytes(kernel_ptr, 0, kernel_size) };
240+
let kernel_slice = unsafe { slice::from_raw_parts_mut(kernel_ptr, kernel_size) };
241+
242+
base_code
243+
.tftp_read_file(&server_ip, filename, Some(kernel_slice))
244+
.unwrap();
245+
246+
Some(kernel_slice)
176247
}
177248

178249
/// Creates page table abstraction types for both the bootloader and kernel page tables.

0 commit comments

Comments
 (0)