Skip to content

Commit 35db7ba

Browse files
committed
Store the address of the kernel's PML4 for later use
Previously, there was a common problem with how we handled the kernel's page mapping. It was returned by memory::init, but then had to be kept in a variable to be usable. The neat solution would be to store it in a static variable in the memory module, but since OffsetPageTable can't be initialised with a dummy value, there's no safe way to initialise the static variable before the kernel starts and is able to provide the proper value. With that option ruled out, the next best option is to store the PML4's virtual address in a static variable, then provide a function (kernel_pml4) that instantiates the PML4 using that address. This replaces the similar active_level_4_table function, but reading from where we keep the kernel's PML4, rather than whatever is currently stored in the cr3 register. I would love to make this neater. With that change made, memory::init now returns nothing, so when we debug the virtual address space in main, we do so by calling kernel_pml4 to get another instance of the memory map. It's important to note that we run the risk of undefined behaviour if two references to the kernel's PML4 exist at the same time. Frankly, I don't know enough about the nuance to know how risky this is, but in the grand scheme of things it's an acceptable risk. Signed-off-by: SlyMarbo <[email protected]>
1 parent 9c8bad2 commit 35db7ba

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

kernel/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
5050
#[cfg(not(test))]
5151
fn kmain(boot_info: &'static BootInfo) {
5252
// Set up the heap allocator.
53-
let mut mapper = unsafe { memory::init(boot_info) };
53+
unsafe { memory::init(boot_info) };
5454

5555
println!("Kernel ready!");
5656
println!("Kernel booted at {}.", time::boot_time());
@@ -62,7 +62,7 @@ fn kmain(boot_info: &'static BootInfo) {
6262
println!("Kernel running on unknown CPU.");
6363
}
6464

65-
unsafe { memory::vmm::debug(mapper.level_4_table()) };
65+
unsafe { memory::vmm::debug(memory::kernel_pml4().level_4_table()) };
6666
memory::pmm::debug();
6767
}
6868

kernel/src/memory/mod.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
use bootloader::BootInfo;
183183
use x86_64::registers::control::Cr3;
184184
use x86_64::structures::paging::{OffsetPageTable, PageTable};
185+
use x86_64::VirtAddr;
185186

186187
mod constants;
187188
pub mod pmm;
@@ -194,7 +195,14 @@ pub use crate::memory::constants::{
194195

195196
// PML4 functionality.
196197

197-
/// init initialises a new OffsetPageTable.
198+
/// KERNEL_PML4_ADDRESS contains the virtual address of the kernel's
199+
/// level 4 page table. This enables the kernel_pml4 function to
200+
/// construct the structured data.
201+
///
202+
static KERNEL_PML4_ADDRESS: spin::Mutex<VirtAddr> = spin::Mutex::new(VirtAddr::zero());
203+
204+
/// init initialises the kernel's memory, including setting up the
205+
/// heap.
198206
///
199207
/// # Safety
200208
///
@@ -203,38 +211,34 @@ pub use crate::memory::constants::{
203211
/// PHYSICAL_MEMORY_OFFSET. Also, this function must be only called once
204212
/// to avoid aliasing &mut references (which is undefined behavior).
205213
///
206-
pub unsafe fn init(boot_info: &'static BootInfo) -> OffsetPageTable<'static> {
207-
let level_4_table = active_level_4_table();
208-
let mut page_table = OffsetPageTable::new(level_4_table, PHYSICAL_MEMORY_OFFSET);
214+
pub unsafe fn init(boot_info: &'static BootInfo) {
215+
// Prepare the kernel's PML4.
216+
let (level_4_table_frame, _) = Cr3::read();
217+
let phys = level_4_table_frame.start_address();
218+
*KERNEL_PML4_ADDRESS.lock() = phys_to_virt_addr(phys);
219+
220+
let mut page_table = kernel_pml4();
209221
let mut frame_allocator = pmm::bootstrap(&boot_info.memory_map);
210222

211223
vmm::init(&mut page_table, &mut frame_allocator).expect("heap initialization failed");
212224

213225
// Switch over to a more sophisticated physical memory manager.
214226
pmm::init(frame_allocator);
215-
216-
page_table
217227
}
218228

219-
/// active_level_4_table returns a mutable reference
220-
/// to the active level 4 table.
229+
/// kernel_pml4 returns a mutable reference to the
230+
/// kernel's level 4 table.
221231
///
222232
/// # Safety
223233
///
224-
/// This function is unsafe because the caller must
225-
/// guarantee that all physical memory is mapped to
226-
/// virtual memory at PHYSICAL_MEMORY_OFFSET.
227-
///
228-
/// active_level_4_table must only be called once to
234+
/// kernel_pml4 must only be called once at a time to
229235
/// avoid aliasing &mut references (which is undefined
230236
/// behavior).
231237
///
232-
unsafe fn active_level_4_table() -> &'static mut PageTable {
233-
let (level_4_table_frame, _) = Cr3::read();
234-
235-
let phys = level_4_table_frame.start_address();
236-
let virt = phys_to_virt_addr(phys);
238+
pub unsafe fn kernel_pml4() -> OffsetPageTable<'static> {
239+
let virt = KERNEL_PML4_ADDRESS.lock();
237240
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
238241

239-
&mut *page_table_ptr // unsafe
242+
let page_table = &mut *page_table_ptr; // unsafe
243+
OffsetPageTable::new(page_table, PHYSICAL_MEMORY_OFFSET)
240244
}

0 commit comments

Comments
 (0)