bootloader_x86_64_common/
level_4_entries.rsuse crate::{entropy, load_kernel::VirtualAddressOffset, BootInfo, RawFrameBufferInfo};
use bootloader_api::{config, info::MemoryRegion, BootloaderConfig};
use core::{alloc::Layout, iter::Step};
use rand::{
distributions::{Distribution, Uniform},
seq::IteratorRandom,
};
use rand_hc::Hc128Rng;
use usize_conversions::IntoUsize;
use x86_64::{
structures::paging::{Page, PageTableIndex, Size4KiB},
PhysAddr, VirtAddr,
};
use xmas_elf::program::ProgramHeader;
pub struct UsedLevel4Entries {
entry_state: [bool; 512],
rng: Option<Hc128Rng>,
}
impl UsedLevel4Entries {
pub fn new(
max_phys_addr: PhysAddr,
regions_len: usize,
framebuffer: Option<&RawFrameBufferInfo>,
config: &BootloaderConfig,
) -> Self {
let mut used = UsedLevel4Entries {
entry_state: [false; 512],
rng: config.mappings.aslr.then(entropy::build_rng),
};
used.entry_state[0] = true; if let Some(config::Mapping::FixedAddress(physical_memory_offset)) =
config.mappings.physical_memory
{
used.mark_range_as_used(physical_memory_offset, max_phys_addr.as_u64().into_usize());
}
if let Some(config::Mapping::FixedAddress(recursive_address)) =
config.mappings.page_table_recursive
{
let recursive_index = VirtAddr::new(recursive_address).p4_index();
used.mark_p4_index_as_used(recursive_index);
}
if let config::Mapping::FixedAddress(kernel_stack_address) = config.mappings.kernel_stack {
used.mark_range_as_used(kernel_stack_address, config.kernel_stack_size);
}
if let config::Mapping::FixedAddress(boot_info_address) = config.mappings.boot_info {
let boot_info_layout = Layout::new::<BootInfo>();
let regions = regions_len + 1; let memory_regions_layout = Layout::array::<MemoryRegion>(regions).unwrap();
let (combined, _) = boot_info_layout.extend(memory_regions_layout).unwrap();
used.mark_range_as_used(boot_info_address, combined.size());
}
if let config::Mapping::FixedAddress(framebuffer_address) = config.mappings.framebuffer {
if let Some(framebuffer) = framebuffer {
used.mark_range_as_used(framebuffer_address, framebuffer.info.byte_len);
}
}
if let Some(dynamic_range_start) = config.mappings.dynamic_range_start {
let dynamic_range_start = VirtAddr::new(dynamic_range_start);
let start_page: Page = Page::containing_address(dynamic_range_start);
if let Some(unusable_page) = Step::backward_checked(start_page, 1) {
for i in 0..=u16::from(unusable_page.p4_index()) {
used.mark_p4_index_as_used(PageTableIndex::new(i));
}
}
}
if let Some(dynamic_range_end) = config.mappings.dynamic_range_end {
let dynamic_range_end = VirtAddr::new(dynamic_range_end);
let end_page: Page = Page::containing_address(dynamic_range_end);
if let Some(unusable_page) = Step::forward_checked(end_page, 1) {
for i in u16::from(unusable_page.p4_index())..512 {
used.mark_p4_index_as_used(PageTableIndex::new(i));
}
}
}
used
}
fn mark_range_as_used<S>(&mut self, address: u64, size: S)
where
VirtAddr: core::ops::Add<S, Output = VirtAddr>,
{
let start = VirtAddr::new(address);
let end_inclusive = (start + size) - 1usize;
let start_page = Page::<Size4KiB>::containing_address(start);
let end_page_inclusive = Page::<Size4KiB>::containing_address(end_inclusive);
for p4_index in u16::from(start_page.p4_index())..=u16::from(end_page_inclusive.p4_index())
{
self.mark_p4_index_as_used(PageTableIndex::new(p4_index));
}
}
fn mark_p4_index_as_used(&mut self, p4_index: PageTableIndex) {
self.entry_state[usize::from(p4_index)] = true;
}
pub fn mark_segments<'a>(
&mut self,
segments: impl Iterator<Item = ProgramHeader<'a>>,
virtual_address_offset: VirtualAddressOffset,
) {
for segment in segments.filter(|s| s.mem_size() > 0) {
self.mark_range_as_used(
virtual_address_offset + segment.virtual_addr(),
segment.mem_size(),
);
}
}
pub fn get_free_entries(&mut self, num: u64) -> PageTableIndex {
let mut free_entries = self
.entry_state
.windows(num.into_usize())
.enumerate()
.filter(|(_, entries)| entries.iter().all(|used| !used))
.map(|(idx, _)| idx);
let idx_opt = if let Some(rng) = self.rng.as_mut() {
free_entries.choose(rng)
} else {
free_entries.next()
};
let Some(idx) = idx_opt else {
panic!("no usable level 4 entries found ({num} entries requested)");
};
for i in 0..num.into_usize() {
self.entry_state[idx + i] = true;
}
PageTableIndex::new(idx.try_into().unwrap())
}
pub fn get_free_address(&mut self, size: u64, alignment: u64) -> VirtAddr {
assert!(alignment.is_power_of_two());
const LEVEL_4_SIZE: u64 = 4096 * 512 * 512 * 512;
let level_4_entries = (size + (LEVEL_4_SIZE - 1)) / LEVEL_4_SIZE;
let base = Page::from_page_table_indices_1gib(
self.get_free_entries(level_4_entries),
PageTableIndex::new(0),
)
.start_address();
let offset = if let Some(rng) = self.rng.as_mut() {
let max_offset = LEVEL_4_SIZE - (size % LEVEL_4_SIZE);
let uniform_range = Uniform::from(0..max_offset / alignment);
uniform_range.sample(rng) * alignment
} else {
0
};
base + offset
}
}