-
Notifications
You must be signed in to change notification settings - Fork 215
/
Copy pathuefi.rs
185 lines (162 loc) · 6.34 KB
/
uefi.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#![no_std]
#![no_main]
#![feature(abi_efiapi)]
#![feature(asm)]
#![feature(maybe_uninit_extra)]
#![deny(unsafe_op_in_unsafe_fn)]
// Defines the constants `KERNEL_BYTES` (array of `u8`) and `KERNEL_SIZE` (`usize`).
include!(concat!(env!("OUT_DIR"), "/kernel_info.rs"));
static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES);
#[repr(align(4096))]
struct PageAligned<T>(T);
use bootloader::{
binary::{legacy_memory_region::LegacyFrameAllocator, SystemInfo},
boot_info::FrameBufferInfo,
};
use core::{mem, panic::PanicInfo, slice};
use uefi::{
prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable},
proto::console::gop::{GraphicsOutput, PixelFormat},
table::boot::{MemoryDescriptor, MemoryType},
};
use x86_64::{
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
PhysAddr, VirtAddr,
};
#[entry]
fn efi_main(image: Handle, st: SystemTable<Boot>) -> Status {
let (framebuffer_addr, framebuffer_info) = init_logger(&st);
log::info!("Hello World from UEFI bootloader!");
log::info!("Using framebuffer at {:#x}", framebuffer_addr);
let mmap_storage = {
let max_mmap_size =
st.boot_services().memory_map_size() + 8 * mem::size_of::<MemoryDescriptor>();
let ptr = st
.boot_services()
.allocate_pool(MemoryType::LOADER_DATA, max_mmap_size)?
.log();
unsafe { slice::from_raw_parts_mut(ptr, max_mmap_size) }
};
log::trace!("exiting boot services");
let (system_table, memory_map) = st
.exit_boot_services(image, mmap_storage)
.expect_success("Failed to exit boot services");
let mut frame_allocator = LegacyFrameAllocator::new(memory_map.copied());
let page_tables = create_page_tables(&mut frame_allocator);
let system_info = SystemInfo {
framebuffer_addr,
framebuffer_info,
rsdp_addr: {
use uefi::table::cfg;
let mut config_entries = system_table.config_table().iter();
config_entries
.find(|entry| matches!(entry.guid, cfg::ACPI_GUID | cfg::ACPI2_GUID))
.map(|entry| PhysAddr::new(entry.address as u64))
},
};
bootloader::binary::load_and_switch_to_kernel(
&KERNEL.0,
frame_allocator,
page_tables,
system_info,
);
}
/// Creates page table abstraction types for both the bootloader and kernel page tables.
fn create_page_tables(
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> bootloader::binary::PageTables {
// UEFI identity-maps all memory, so the offset between physical and virtual addresses is 0
let phys_offset = VirtAddr::new(0);
// copy the currently active level 4 page table, because it might be read-only
log::trace!("switching to new level 4 table");
let bootloader_page_table = {
let old_table = {
let frame = x86_64::registers::control::Cr3::read().0;
let ptr: *const PageTable = (phys_offset + frame.start_address().as_u64()).as_ptr();
unsafe { &*ptr }
};
let new_frame = frame_allocator
.allocate_frame()
.expect("Failed to allocate frame for new level 4 table");
let new_table: &mut PageTable = {
let ptr: *mut PageTable =
(phys_offset + new_frame.start_address().as_u64()).as_mut_ptr();
// create a new, empty page table
unsafe {
ptr.write(PageTable::new());
&mut *ptr
}
};
// copy the first entry (we don't need to access more than 512 GiB; also, some UEFI
// implementations seem to create an level 4 table entry 0 in all slots)
new_table[0] = old_table[0].clone();
// the first level 4 table entry is now identical, so we can just load the new one
unsafe {
x86_64::registers::control::Cr3::write(
new_frame,
x86_64::registers::control::Cr3Flags::empty(),
);
OffsetPageTable::new(&mut *new_table, phys_offset)
}
};
// create a new page table hierarchy for the kernel
let (kernel_page_table, kernel_level_4_frame) = {
// get an unused frame for new level 4 page table
let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames");
log::info!("New page table at: {:#?}", &frame);
// get the corresponding virtual address
let addr = phys_offset + frame.start_address().as_u64();
// initialize a new page table
let ptr = addr.as_mut_ptr();
unsafe { *ptr = PageTable::new() };
let level_4_table = unsafe { &mut *ptr };
(
unsafe { OffsetPageTable::new(level_4_table, phys_offset) },
frame,
)
};
bootloader::binary::PageTables {
bootloader: bootloader_page_table,
kernel: kernel_page_table,
kernel_level_4_frame,
}
}
fn init_logger(st: &SystemTable<Boot>) -> (PhysAddr, FrameBufferInfo) {
let gop = st
.boot_services()
.locate_protocol::<GraphicsOutput>()
.expect_success("failed to locate gop");
let gop = unsafe { &mut *gop.get() };
let mode_info = gop.current_mode_info();
let mut framebuffer = gop.frame_buffer();
let slice = unsafe { slice::from_raw_parts_mut(framebuffer.as_mut_ptr(), framebuffer.size()) };
let info = FrameBufferInfo {
byte_len: framebuffer.size(),
horizontal_resolution: mode_info.resolution().0,
vertical_resolution: mode_info.resolution().1,
pixel_format: match mode_info.pixel_format() {
PixelFormat::Rgb => bootloader::boot_info::PixelFormat::BGR,
PixelFormat::Bgr => bootloader::boot_info::PixelFormat::BGR,
PixelFormat::Bitmask | PixelFormat::BltOnly => {
panic!("Bitmask and BltOnly framebuffers are not supported")
}
},
bytes_per_pixel: 4,
stride: mode_info.stride(),
};
log::info!("UEFI boot");
bootloader::binary::init_logger(slice, info);
(PhysAddr::new(framebuffer.as_mut_ptr() as u64), info)
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
unsafe {
bootloader::binary::logger::LOGGER
.get()
.map(|l| l.force_unlock())
};
log::error!("{}", info);
loop {
unsafe { asm!("cli; hlt") };
}
}