You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.6 KiB
Rust
227 lines
7.6 KiB
Rust
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
//
|
|
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
|
|
|
|
//! BSP Memory Management Unit.
|
|
|
|
use crate::{
|
|
common,
|
|
memory::{
|
|
mmu as generic_mmu,
|
|
mmu::{
|
|
AccessPermissions, AddressSpace, AssociatedTranslationTable, AttributeFields,
|
|
MemAttributes, Page, PageSliceDescriptor, TranslationGranule,
|
|
},
|
|
Physical, Virtual,
|
|
},
|
|
synchronization::InitStateLock,
|
|
};
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Private Definitions
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
type KernelTranslationTable =
|
|
<KernelVirtAddrSpace as AssociatedTranslationTable>::TableStartFromBottom;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Public Definitions
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
/// The translation granule chosen by this BSP. This will be used everywhere else in the kernel to
|
|
/// derive respective data structures and their sizes. For example, the `crate::memory::mmu::Page`.
|
|
pub type KernelGranule = TranslationGranule<{ 64 * 1024 }>;
|
|
|
|
/// The kernel's virtual address space defined by this BSP.
|
|
pub type KernelVirtAddrSpace = AddressSpace<{ 8 * 1024 * 1024 * 1024 }>;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Global instances
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
/// The kernel translation tables.
|
|
///
|
|
/// It is mandatory that InitStateLock is transparent.
|
|
///
|
|
/// That is, `size_of(InitStateLock<KernelTranslationTable>) == size_of(KernelTranslationTable)`.
|
|
/// There is a unit tests that checks this porperty.
|
|
static KERNEL_TABLES: InitStateLock<KernelTranslationTable> =
|
|
InitStateLock::new(KernelTranslationTable::new());
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Private Code
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
/// Helper function for calculating the number of pages the given parameter spans.
|
|
const fn size_to_num_pages(size: usize) -> usize {
|
|
assert!(size > 0);
|
|
assert!(size % KernelGranule::SIZE == 0);
|
|
|
|
size >> KernelGranule::SHIFT
|
|
}
|
|
|
|
/// The Read+Execute (RX) pages of the kernel binary.
|
|
fn virt_rx_page_desc() -> PageSliceDescriptor<Virtual> {
|
|
let num_pages = size_to_num_pages(super::rx_size());
|
|
|
|
PageSliceDescriptor::from_addr(super::virt_rx_start(), num_pages)
|
|
}
|
|
|
|
/// The Read+Write (RW) pages of the kernel binary.
|
|
fn virt_rw_page_desc() -> PageSliceDescriptor<Virtual> {
|
|
let num_pages = size_to_num_pages(super::rw_size());
|
|
|
|
PageSliceDescriptor::from_addr(super::virt_rw_start(), num_pages)
|
|
}
|
|
|
|
/// The boot core's stack.
|
|
fn virt_boot_core_stack_page_desc() -> PageSliceDescriptor<Virtual> {
|
|
let num_pages = size_to_num_pages(super::boot_core_stack_size());
|
|
|
|
PageSliceDescriptor::from_addr(super::virt_boot_core_stack_start(), num_pages)
|
|
}
|
|
|
|
// The binary is still identity mapped, so we don't need to convert in the following.
|
|
|
|
/// The Read+Execute (RX) pages of the kernel binary.
|
|
fn phys_rx_page_desc() -> PageSliceDescriptor<Physical> {
|
|
virt_rx_page_desc().into()
|
|
}
|
|
|
|
/// The Read+Write (RW) pages of the kernel binary.
|
|
fn phys_rw_page_desc() -> PageSliceDescriptor<Physical> {
|
|
virt_rw_page_desc().into()
|
|
}
|
|
|
|
/// The boot core's stack.
|
|
fn phys_boot_core_stack_page_desc() -> PageSliceDescriptor<Physical> {
|
|
virt_boot_core_stack_page_desc().into()
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Public Code
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
/// Return a reference to the kernel's translation tables.
|
|
pub fn kernel_translation_tables() -> &'static InitStateLock<KernelTranslationTable> {
|
|
&KERNEL_TABLES
|
|
}
|
|
|
|
/// Pointer to the last page of the physical address space.
|
|
pub fn phys_addr_space_end_page() -> *const Page<Physical> {
|
|
common::align_down(
|
|
super::phys_addr_space_end().into_usize(),
|
|
KernelGranule::SIZE,
|
|
) as *const Page<_>
|
|
}
|
|
|
|
/// Map the kernel binary.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// - Any miscalculation or attribute error will likely be fatal. Needs careful manual checking.
|
|
pub unsafe fn kernel_map_binary() -> Result<(), &'static str> {
|
|
generic_mmu::kernel_map_pages_at(
|
|
"Kernel code and RO data",
|
|
&virt_rx_page_desc(),
|
|
&phys_rx_page_desc(),
|
|
&AttributeFields {
|
|
mem_attributes: MemAttributes::CacheableDRAM,
|
|
acc_perms: AccessPermissions::ReadOnly,
|
|
execute_never: false,
|
|
},
|
|
)?;
|
|
|
|
generic_mmu::kernel_map_pages_at(
|
|
"Kernel data and bss",
|
|
&virt_rw_page_desc(),
|
|
&phys_rw_page_desc(),
|
|
&AttributeFields {
|
|
mem_attributes: MemAttributes::CacheableDRAM,
|
|
acc_perms: AccessPermissions::ReadWrite,
|
|
execute_never: true,
|
|
},
|
|
)?;
|
|
|
|
generic_mmu::kernel_map_pages_at(
|
|
"Kernel boot-core stack",
|
|
&virt_boot_core_stack_page_desc(),
|
|
&phys_boot_core_stack_page_desc(),
|
|
&AttributeFields {
|
|
mem_attributes: MemAttributes::CacheableDRAM,
|
|
acc_perms: AccessPermissions::ReadWrite,
|
|
execute_never: true,
|
|
},
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Testing
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use core::{cell::UnsafeCell, ops::Range};
|
|
use test_macros::kernel_test;
|
|
|
|
/// Check alignment of the kernel's virtual memory layout sections.
|
|
#[kernel_test]
|
|
fn virt_mem_layout_sections_are_64KiB_aligned() {
|
|
for i in [
|
|
virt_rx_page_desc,
|
|
virt_rw_page_desc,
|
|
virt_boot_core_stack_page_desc,
|
|
]
|
|
.iter()
|
|
{
|
|
let start: usize = i().start_addr().into_usize();
|
|
let end: usize = i().end_addr().into_usize();
|
|
|
|
assert_eq!(start % KernelGranule::SIZE, 0);
|
|
assert_eq!(end % KernelGranule::SIZE, 0);
|
|
assert!(end >= start);
|
|
}
|
|
}
|
|
|
|
/// Ensure the kernel's virtual memory layout is free of overlaps.
|
|
#[kernel_test]
|
|
fn virt_mem_layout_has_no_overlaps() {
|
|
let layout = [
|
|
virt_rx_page_desc(),
|
|
virt_rw_page_desc(),
|
|
virt_boot_core_stack_page_desc(),
|
|
];
|
|
|
|
for (i, first_range) in layout.iter().enumerate() {
|
|
for second_range in layout.iter().skip(i + 1) {
|
|
assert!(!first_range.contains(second_range.start_addr()));
|
|
assert!(!first_range.contains(second_range.end_addr_inclusive()));
|
|
assert!(!second_range.contains(first_range.start_addr()));
|
|
assert!(!second_range.contains(first_range.end_addr_inclusive()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check if KERNEL_TABLES is in .bss.
|
|
#[kernel_test]
|
|
fn kernel_tables_in_bss() {
|
|
extern "Rust" {
|
|
static __bss_start: UnsafeCell<u64>;
|
|
static __bss_end_exclusive: UnsafeCell<u64>;
|
|
}
|
|
|
|
let bss_range = unsafe {
|
|
Range {
|
|
start: __bss_start.get(),
|
|
end: __bss_end_exclusive.get(),
|
|
}
|
|
};
|
|
let kernel_tables_addr = &KERNEL_TABLES as *const _ as usize as *mut u64;
|
|
|
|
assert!(bss_range.contains(&kernel_tables_addr));
|
|
}
|
|
}
|