Skip to content

Commit 922cb09

Browse files
Use new UEFI bootloader and UEFI bootloader API crate (theseus-os#792)
* Removes old assumptions/requirements that all sections in the kernel base image are loaded into physically-contiguous memory, especially the stack and bootloader info. * IOW, we stop relying upon a fixed `KERNEL_OFFSET` to calculate virtual addresses from physical addresses and vice versa; instead, we actually translate addresses via the initial page table. * Thus, we must obtain the address of the GDT used to boot secondary CPUs (APs on x86) and pass it through the various init routines so that it can be used when booting secondary CPUs. * `PageRange` and `FrameRange` constructors will now return an empty range if invoked with a size of 0 bytes, instead of panicking. * The major changes in this commit is to introduce new crates that support building Theseus for and booting it on UEFI bootloaders. * `tools/uefi-builder` is now multi-architecture, but aarch64 support is a WIP. * The separate `theseus-os/uefi-bootloader` repo is a fork of `rust-osdev/bootloader` but heavily changed to support Theseus's needs and additional architectures. * aarch64 is still a WIP here too. * Currently we manually ensure that the same version of the `uefi-bootloader*` crates are used in the Theseus workspace and in the `tools/uefi-builder/*` crates. Ideally this would be ensured automatically in the future. * `uefi-builder` consists of separate, per-arch crates; we can combine them once <rust-lang/cargo#10030> is fixed. Co-authored-by: Kevin Boos <kevinaboos@gmail.com>
1 parent 0569939 commit 922cb09

File tree

33 files changed

+2481
-370
lines changed

33 files changed

+2481
-370
lines changed

Cargo.lock

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ ifeq ($(boot_spec), uefi)
193193
--release \
194194
-Z bindeps \
195195
--manifest-path \
196-
$(ROOT_DIR)/tools/uefi_builder/Cargo.toml -- \
196+
$(ROOT_DIR)/tools/uefi_builder/x86_64/Cargo.toml -- \
197197
--kernel $(nano_core_binary) \
198198
--modules $(OBJECT_FILES_BUILD_DIR) \
199199
--efi-image $(iso) \

kernel/boot_info/Cargo.toml

+3-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ kernel_config = { path = "../kernel_config" }
1111
memory_structs = { path = "../memory_structs" }
1212
multiboot2 = { version = "0.14", optional = true }
1313

14-
[dependencies.bootloader_api]
15-
git = "https://github.com/theseus-os/bootloader"
16-
branch = "theseus"
14+
[dependencies.uefi-bootloader-api]
15+
git = "https://github.com/theseus-os/uefi-bootloader"
1716
optional = true
1817

1918
[features]
20-
uefi = ["dep:bootloader_api"]
19+
uefi = ["dep:uefi-bootloader-api"]
2120
multiboot2 = ["dep:multiboot2"]

kernel/boot_info/src/lib.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ pub trait ElfSection {
3838
/// Returns the section's starting virtual address.
3939
fn start(&self) -> VirtualAddress;
4040

41-
/// Returns the section's length.
41+
/// Returns the section's length in memory, as opposed to its length in the
42+
/// ELF file.
4243
fn len(&self) -> usize;
4344

4445
/// Returns whether the section is empty.
@@ -121,8 +122,8 @@ pub trait BootInformation: 'static {
121122
&self,
122123
) -> Result<Self::AdditionalReservedMemoryRegions, &'static str>;
123124

124-
/// Returns the end of the kernel's image in physical memory.
125-
fn kernel_end(&self) -> Result<PhysicalAddress, &'static str>;
125+
/// Returns the end of the kernel's image in memory.
126+
fn kernel_end(&self) -> Result<VirtualAddress, &'static str>;
126127

127128
/// Returns the RSDP if it was provided by the bootloader.
128129
fn rsdp(&self) -> Option<PhysicalAddress>;

kernel/boot_info/src/multiboot2.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,12 @@ impl crate::BootInformation for multiboot2::BootInformation {
196196
.into_iter())
197197
}
198198

199-
fn kernel_end(&self) -> Result<PhysicalAddress, &'static str> {
200-
let reserved_region = kernel_memory_region(self)?;
201-
Ok(reserved_region.start + reserved_region.len)
199+
fn kernel_end(&self) -> Result<VirtualAddress, &'static str> {
200+
use crate::ElfSection;
201+
self.elf_sections()?
202+
.map(|section| section.start() + section.len())
203+
.max()
204+
.ok_or("no elf sections")
202205
}
203206

204207
fn rsdp(&self) -> Option<PhysicalAddress> {

kernel/boot_info/src/uefi.rs

+37-54
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::ElfSectionFlags;
2-
use bootloader_api::info;
32
use core::iter::{Iterator, Peekable};
4-
use kernel_config::memory::{KERNEL_OFFSET, KERNEL_STACK_SIZE_IN_PAGES, PAGE_SIZE};
3+
use kernel_config::memory::{KERNEL_STACK_SIZE_IN_PAGES, PAGE_SIZE};
54
use memory_structs::{PhysicalAddress, VirtualAddress};
65

76
// TODO: Ideally this would be defined in nano_core. However, that would
@@ -11,66 +10,50 @@ use memory_structs::{PhysicalAddress, VirtualAddress};
1110
pub const STACK_SIZE: usize = (KERNEL_STACK_SIZE_IN_PAGES + 2) * PAGE_SIZE;
1211

1312
/// A custom memory region kind used by the bootloader for the modules.
14-
const MODULES_MEMORY_KIND: info::MemoryRegionKind = info::MemoryRegionKind::UnknownUefi(0x80000000);
13+
const MODULES_MEMORY_KIND: uefi_bootloader_api::MemoryRegionKind =
14+
uefi_bootloader_api::MemoryRegionKind::UnknownUefi(0x80000000);
1515

16-
pub struct MemoryRegion {
17-
start: PhysicalAddress,
18-
len: usize,
19-
is_usable: bool,
20-
}
21-
22-
impl From<info::MemoryRegion> for MemoryRegion {
23-
fn from(info::MemoryRegion { start, end, kind }: info::MemoryRegion) -> Self {
24-
Self {
25-
start: PhysicalAddress::new_canonical(start as usize),
26-
len: (end - start) as usize,
27-
is_usable: matches!(kind, info::MemoryRegionKind::Usable),
28-
}
29-
}
30-
}
31-
32-
impl crate::MemoryRegion for MemoryRegion {
16+
impl crate::MemoryRegion for uefi_bootloader_api::MemoryRegion {
3317
fn start(&self) -> PhysicalAddress {
34-
self.start
18+
PhysicalAddress::new_canonical(self.start)
3519
}
3620

3721
fn len(&self) -> usize {
3822
self.len
3923
}
4024

4125
fn is_usable(&self) -> bool {
42-
self.is_usable
26+
matches!(self.kind, uefi_bootloader_api::MemoryRegionKind::Usable)
4327
}
4428
}
4529

46-
pub struct MemoryRegions {
47-
inner: Peekable<core::slice::Iter<'static, info::MemoryRegion>>,
30+
pub struct MemoryRegions<'a> {
31+
inner: Peekable<core::slice::Iter<'a, uefi_bootloader_api::MemoryRegion>>,
4832
}
4933

50-
impl Iterator for MemoryRegions {
51-
type Item = MemoryRegion;
34+
impl<'a> Iterator for MemoryRegions<'a> {
35+
type Item = uefi_bootloader_api::MemoryRegion;
5236

5337
fn next(&mut self) -> Option<Self::Item> {
54-
let mut area: MemoryRegion = (*self.inner.next()?).into();
38+
let mut region = *self.inner.next()?;
5539

5640
// UEFI often separates contiguous memory into separate memory regions. We
5741
// consolidate them to minimise the number of entries in the frame allocator's
5842
// reserved and available lists.
59-
while let Some(next) = self.inner.next_if(|next| {
60-
let next = MemoryRegion::from(**next);
61-
area.is_usable == next.is_usable && (area.start + area.len) == next.start
62-
}) {
63-
let next = MemoryRegion::from(*next);
64-
area.len += next.len;
43+
while let Some(next) = self
44+
.inner
45+
.next_if(|next| region.kind == next.kind && (region.start + region.len) == next.start)
46+
{
47+
region.len += next.len;
6548
}
6649

67-
Some(area)
50+
Some(region)
6851
}
6952
}
7053

71-
impl<'a> crate::ElfSection for &'a info::ElfSection {
54+
impl<'a> crate::ElfSection for &'a uefi_bootloader_api::ElfSection {
7255
fn name(&self) -> &str {
73-
info::ElfSection::name(self)
56+
uefi_bootloader_api::ElfSection::name(self)
7457
}
7558

7659
fn start(&self) -> VirtualAddress {
@@ -86,14 +69,15 @@ impl<'a> crate::ElfSection for &'a info::ElfSection {
8669
}
8770
}
8871

72+
#[derive(Debug)]
8973
pub struct Module {
90-
inner: info::Module,
91-
regions: &'static info::MemoryRegions,
74+
inner: uefi_bootloader_api::Module,
75+
regions: &'static uefi_bootloader_api::MemoryRegions,
9276
}
9377

9478
impl crate::Module for Module {
9579
fn name(&self) -> Result<&str, &'static str> {
96-
Ok(info::Module::name(&self.inner))
80+
Ok(uefi_bootloader_api::Module::name(&self.inner))
9781
}
9882

9983
fn start(&self) -> PhysicalAddress {
@@ -113,8 +97,8 @@ impl crate::Module for Module {
11397
}
11498

11599
pub struct Modules {
116-
inner: &'static info::Modules,
117-
regions: &'static info::MemoryRegions,
100+
inner: &'static uefi_bootloader_api::Modules,
101+
regions: &'static uefi_bootloader_api::MemoryRegions,
118102
index: usize,
119103
}
120104

@@ -132,12 +116,12 @@ impl Iterator for Modules {
132116
}
133117
}
134118

135-
impl crate::BootInformation for &'static bootloader_api::BootInfo {
136-
type MemoryRegion<'a> = MemoryRegion;
137-
type MemoryRegions<'a> = MemoryRegions;
119+
impl crate::BootInformation for &'static uefi_bootloader_api::BootInformation {
120+
type MemoryRegion<'a> = uefi_bootloader_api::MemoryRegion;
121+
type MemoryRegions<'a> = MemoryRegions<'a>;
138122

139-
type ElfSection<'a> = &'a info::ElfSection;
140-
type ElfSections<'a> = core::slice::Iter<'a, info::ElfSection>;
123+
type ElfSection<'a> = &'a uefi_bootloader_api::ElfSection;
124+
type ElfSections<'a> = core::slice::Iter<'a, uefi_bootloader_api::ElfSection>;
141125

142126
type Module<'a> = Module;
143127
type Modules<'a> = Modules;
@@ -176,24 +160,23 @@ impl crate::BootInformation for &'static bootloader_api::BootInfo {
176160
Ok(core::iter::empty())
177161
}
178162

179-
fn kernel_end(&self) -> Result<PhysicalAddress, &'static str> {
163+
fn kernel_end(&self) -> Result<VirtualAddress, &'static str> {
180164
use crate::ElfSection;
181165

182-
PhysicalAddress::new(
166+
VirtualAddress::new(
183167
self.elf_sections()?
184168
.filter(|section| section.flags().contains(ElfSectionFlags::ALLOCATED))
169+
.filter(|section| section.size > 0)
185170
.map(|section| section.start + section.size)
186171
.max()
187-
.ok_or("couldn't find kernel end address")? as usize
188-
- KERNEL_OFFSET,
172+
.ok_or("couldn't find kernel end address")? as usize,
189173
)
190-
.ok_or("kernel physical end address was invalid")
174+
.ok_or("kernel virtual end address was invalid")
191175
}
192176

193177
fn rsdp(&self) -> Option<PhysicalAddress> {
194-
self.rsdp_addr
195-
.into_option()
196-
.map(|address| PhysicalAddress::new_canonical(address as usize))
178+
self.rsdp_address
179+
.map(|address| PhysicalAddress::new_canonical(address))
197180
}
198181

199182
fn stack_size(&self) -> Result<usize, &'static str> {

kernel/captain/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pub fn init(
8585
bsp_initial_stack: NoDrop<Stack>,
8686
ap_start_realmode_begin: VirtualAddress,
8787
ap_start_realmode_end: VirtualAddress,
88+
ap_gdt: VirtualAddress,
8889
rsdp_address: Option<PhysicalAddress>,
8990
) -> Result<(), &'static str> {
9091
#[cfg(mirror_log_to_vga)]
@@ -128,6 +129,7 @@ pub fn init(
128129
&kernel_mmi_ref,
129130
ap_start_realmode_begin,
130131
ap_start_realmode_end,
132+
ap_gdt,
131133
Some(kernel_config::display::FRAMEBUFFER_MAX_RESOLUTION),
132134
)?;
133135
let cpu_count = ap_count + 1;

kernel/memory/src/lib.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod paging;
2121
pub use self::paging::{
2222
PageTable, Mapper, Mutability, Mutable, Immutable,
2323
MappedPages, BorrowedMappedPages, BorrowedSliceMappedPages,
24+
translate,
2425
};
2526

2627
pub use memory_structs::{Frame, Page, FrameRange, PageRange, VirtualAddress, PhysicalAddress};
@@ -220,7 +221,18 @@ pub fn init(
220221
debug!("Initialized new frame allocator!");
221222
frame_allocator::dump_frame_allocator_state();
222223

223-
page_allocator::init(VirtualAddress::new_canonical(boot_info.kernel_end()?.value()))?;
224+
page_allocator::init(
225+
VirtualAddress::new(
226+
// We subtract 1 when translating because `kernel_end` returns an exclusive
227+
// upper bound, which can cause problems if the kernel ends on a page boundary.
228+
// We then add it back later to get the correct identity virtual address.
229+
translate(boot_info.kernel_end()? - 1)
230+
.ok_or("couldn't translate kernel end virtual address")?
231+
.value()
232+
+ 1,
233+
)
234+
.ok_or("couldn't convert kernel end physical address into virtual address")?,
235+
)?;
224236
debug!("Initialized new page allocator!");
225237
page_allocator::dump_page_allocator_state();
226238

kernel/memory/src/paging/mapper.rs

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ use owned_borrowed_trait::{OwnedOrBorrowed, Owned, Borrowed};
5050
/// that it is only invoked for `UnmappedFrames`.
5151
pub(super) static INTO_ALLOCATED_FRAMES_FUNC: Once<fn(FrameRange) -> AllocatedFrames> = Once::new();
5252

53+
/// A convenience function to translate the given virtual address into a
54+
/// physical address using the currently-active page table.
55+
pub fn translate(virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
56+
Mapper::from_current().translate(virtual_address)
57+
}
5358

5459
pub struct Mapper {
5560
p4: Unique<Table<Level4>>,

0 commit comments

Comments
 (0)