Skip to content

Commit 1563a44

Browse files
committed
First working version of dynamic kernel loading in UEFI bootloader
1 parent e76b7e1 commit 1563a44

File tree

1 file changed

+104
-69
lines changed

1 file changed

+104
-69
lines changed

src/bin/uefi.rs

Lines changed: 104 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@
44
#![feature(maybe_uninit_extra)]
55
#![deny(unsafe_op_in_unsafe_fn)]
66

7-
#[repr(align(4096))]
8-
struct PageAligned<T>(T);
9-
107
use bootloader::binary::{legacy_memory_region::LegacyFrameAllocator, Kernel, SystemInfo};
118
use bootloader_api::{info::FrameBufferInfo, BootloaderConfig};
12-
use core::{arch::asm, mem, panic::PanicInfo, ptr, slice};
9+
use core::{arch::asm, cell::UnsafeCell, fmt::Write, mem, panic::PanicInfo, ptr, slice};
1310
use uefi::{
1411
prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable},
1512
proto::{
@@ -22,46 +19,125 @@ use uefi::{
2219
},
2320
},
2421
table::boot::{AllocateType, MemoryDescriptor, MemoryType},
25-
CStr16, Completion,
22+
Completion,
2623
};
2724
use x86_64::{
2825
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
2926
PhysAddr, VirtAddr,
3027
};
3128
use xmas_elf::ElfFile;
3229

30+
static SYSTEM_TABLE: VeryUnsafeCell<Option<SystemTable<Boot>>> = VeryUnsafeCell::new(None);
31+
32+
struct VeryUnsafeCell<T>(UnsafeCell<T>);
33+
34+
impl<T> VeryUnsafeCell<T> {
35+
const fn new(v: T) -> Self {
36+
Self(UnsafeCell::new(v))
37+
}
38+
}
39+
40+
unsafe impl<T> Sync for VeryUnsafeCell<T> {}
41+
42+
impl<T> core::ops::Deref for VeryUnsafeCell<T> {
43+
type Target = UnsafeCell<T>;
44+
45+
fn deref(&self) -> &Self::Target {
46+
&self.0
47+
}
48+
}
49+
3350
#[entry]
3451
fn efi_main(image: Handle, st: SystemTable<Boot>) -> Status {
3552
main_inner(image, st)
3653
}
3754

3855
fn main_inner(image: Handle, mut st: SystemTable<Boot>) -> Status {
39-
let mut buf = [0; 100];
40-
st.stdout()
41-
.output_string(
42-
CStr16::from_str_with_buf("UEFI bootloader started; trying to load kernel", &mut buf)
43-
.unwrap(),
44-
)
45-
.unwrap()
46-
.unwrap();
56+
// temporarily clone the system table for printing panics
57+
unsafe {
58+
*SYSTEM_TABLE.get() = Some(st.unsafe_clone());
59+
}
60+
61+
st.stdout().clear().unwrap().unwrap();
62+
writeln!(
63+
st.stdout(),
64+
"UEFI bootloader started; trying to load kernel"
65+
)
66+
.unwrap();
67+
68+
let kernel = load_kernel(image, &st);
69+
70+
let (framebuffer_addr, framebuffer_info) = init_logger(&st, kernel.config);
71+
72+
// we no longer need the system table for printing panics
73+
unsafe {
74+
*SYSTEM_TABLE.get() = None;
75+
}
76+
77+
log::info!("UEFI bootloader started");
78+
log::info!("Reading kernel and configuration from disk was successful");
79+
log::info!("Using framebuffer at {:#x}", framebuffer_addr);
80+
81+
let mmap_storage = {
82+
let max_mmap_size =
83+
st.boot_services().memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>();
84+
let ptr = st
85+
.boot_services()
86+
.allocate_pool(MemoryType::LOADER_DATA, max_mmap_size)?
87+
.log();
88+
unsafe { slice::from_raw_parts_mut(ptr, max_mmap_size) }
89+
};
90+
91+
log::trace!("exiting boot services");
92+
let (system_table, memory_map) = st
93+
.exit_boot_services(image, mmap_storage)
94+
.expect_success("Failed to exit boot services");
95+
96+
let mut frame_allocator = LegacyFrameAllocator::new(memory_map.copied());
97+
98+
let page_tables = create_page_tables(&mut frame_allocator);
99+
100+
let system_info = SystemInfo {
101+
framebuffer_addr,
102+
framebuffer_info,
103+
rsdp_addr: {
104+
use uefi::table::cfg;
105+
let mut config_entries = system_table.config_table().iter();
106+
// look for an ACPI2 RSDP first
107+
let acpi2_rsdp = config_entries.find(|entry| matches!(entry.guid, cfg::ACPI2_GUID));
108+
// if no ACPI2 RSDP is found, look for a ACPI1 RSDP
109+
let rsdp = acpi2_rsdp
110+
.or_else(|| config_entries.find(|entry| matches!(entry.guid, cfg::ACPI_GUID)));
111+
rsdp.map(|entry| PhysAddr::new(entry.address as u64))
112+
},
113+
};
47114

115+
bootloader::binary::load_and_switch_to_kernel(
116+
kernel,
117+
frame_allocator,
118+
page_tables,
119+
system_info,
120+
);
121+
}
122+
123+
fn load_kernel(image: Handle, st: &SystemTable<Boot>) -> Kernel<'static> {
48124
let file_system_raw = {
49125
let ref this = st.boot_services();
50126
let loaded_image = this
51-
.handle_protocol::<LoadedImage>(image)?
52-
.expect("Failed to retrieve `LoadedImage` protocol from handle");
127+
.handle_protocol::<LoadedImage>(image)
128+
.expect_success("Failed to retrieve `LoadedImage` protocol from handle");
53129
let loaded_image = unsafe { &*loaded_image.get() };
54130

55131
let device_handle = loaded_image.device();
56132

57133
let device_path = this
58-
.handle_protocol::<DevicePath>(device_handle)?
59-
.expect("Failed to retrieve `DevicePath` protocol from image's device handle");
134+
.handle_protocol::<DevicePath>(device_handle)
135+
.expect_success("Failed to retrieve `DevicePath` protocol from image's device handle");
60136
let mut device_path = unsafe { &*device_path.get() };
61137

62138
let device_handle = this
63-
.locate_device_path::<SimpleFileSystem>(&mut device_path)?
64-
.expect("Failed to locate `SimpleFileSystem` protocol on device path");
139+
.locate_device_path::<SimpleFileSystem>(&mut device_path)
140+
.expect_success("Failed to locate `SimpleFileSystem` protocol on device path");
65141

66142
this.handle_protocol::<SimpleFileSystem>(device_handle)
67143
}
@@ -72,14 +148,14 @@ fn main_inner(image: Handle, mut st: SystemTable<Boot>) -> Status {
72148
let mut root = file_system.open_volume().unwrap().unwrap();
73149
let kernel_file_handle = root
74150
.open("kernel-x86_64", FileMode::Read, FileAttribute::empty())
75-
.unwrap()
151+
.expect("Failed to load kernel (expected file named `kernel-x86_64`)")
76152
.unwrap();
77153
let mut kernel_file = match kernel_file_handle.into_type().unwrap().unwrap() {
78154
uefi::proto::media::file::FileType::Regular(f) => f,
79155
uefi::proto::media::file::FileType::Dir(_) => panic!(),
80156
};
81157

82-
let mut buf = [0; 100];
158+
let mut buf = [0; 500];
83159
let kernel_info: &mut FileInfo = kernel_file.get_info(&mut buf).unwrap().unwrap();
84160
let kernel_size = usize::try_from(kernel_info.file_size()).unwrap();
85161

@@ -106,56 +182,10 @@ fn main_inner(image: Handle, mut st: SystemTable<Boot>) -> Status {
106182
BootloaderConfig::deserialize(raw).unwrap()
107183
};
108184

109-
let kernel = Kernel {
185+
Kernel {
110186
elf: kernel_elf,
111187
config,
112-
};
113-
114-
let (framebuffer_addr, framebuffer_info) = init_logger(&st, config);
115-
log::info!("UEFI bootloader started");
116-
log::info!("Reading kernel and configuration from disk was successful");
117-
log::info!("Using framebuffer at {:#x}", framebuffer_addr);
118-
119-
let mmap_storage = {
120-
let max_mmap_size =
121-
st.boot_services().memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>();
122-
let ptr = st
123-
.boot_services()
124-
.allocate_pool(MemoryType::LOADER_DATA, max_mmap_size)?
125-
.log();
126-
unsafe { slice::from_raw_parts_mut(ptr, max_mmap_size) }
127-
};
128-
129-
log::trace!("exiting boot services");
130-
let (system_table, memory_map) = st
131-
.exit_boot_services(image, mmap_storage)
132-
.expect_success("Failed to exit boot services");
133-
134-
let mut frame_allocator = LegacyFrameAllocator::new(memory_map.copied());
135-
136-
let page_tables = create_page_tables(&mut frame_allocator);
137-
138-
let system_info = SystemInfo {
139-
framebuffer_addr,
140-
framebuffer_info,
141-
rsdp_addr: {
142-
use uefi::table::cfg;
143-
let mut config_entries = system_table.config_table().iter();
144-
// look for an ACPI2 RSDP first
145-
let acpi2_rsdp = config_entries.find(|entry| matches!(entry.guid, cfg::ACPI2_GUID));
146-
// if no ACPI2 RSDP is found, look for a ACPI1 RSDP
147-
let rsdp = acpi2_rsdp
148-
.or_else(|| config_entries.find(|entry| matches!(entry.guid, cfg::ACPI_GUID)));
149-
rsdp.map(|entry| PhysAddr::new(entry.address as u64))
150-
},
151-
};
152-
153-
bootloader::binary::load_and_switch_to_kernel(
154-
kernel,
155-
frame_allocator,
156-
page_tables,
157-
system_info,
158-
);
188+
}
159189
}
160190

161191
/// Creates page table abstraction types for both the bootloader and kernel page tables.
@@ -286,12 +316,17 @@ fn init_logger(st: &SystemTable<Boot>, config: BootloaderConfig) -> (PhysAddr, F
286316

287317
#[panic_handler]
288318
fn panic(info: &PanicInfo) -> ! {
319+
if let Some(st) = unsafe { &mut *SYSTEM_TABLE.get() } {
320+
let _ = writeln!(st.stdout(), "{}", info);
321+
}
322+
289323
unsafe {
290324
bootloader::binary::logger::LOGGER
291325
.get()
292326
.map(|l| l.force_unlock())
293327
};
294328
log::error!("{}", info);
329+
295330
loop {
296331
unsafe { asm!("cli; hlt") };
297332
}

0 commit comments

Comments
 (0)