#![allow(unused)]
use core::arch::asm;
use core::marker::PhantomData;
use core::{fmt, mem, ptr, usize};
use align_address::Align;
use crate::arch::aarch64::kernel::{
get_base_address, get_boot_info_address, get_image_size, get_ram_address, processor,
};
use crate::arch::aarch64::mm::{physicalmem, virtualmem, PhysAddr, VirtAddr};
use crate::env::is_uhyve;
use crate::{mm, scheduler, KERNEL_STACK_SIZE};
const L0TABLE_ADDRESS: VirtAddr = VirtAddr(0x0000_FFFF_FFFF_F000u64);
const PAGE_BITS: usize = 12;
const PAGE_MAP_BITS: usize = 9;
const PAGE_MAP_MASK: usize = 0x1FF;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PageTableEntryFlags: u64 {
const PRESENT = 1 << 0;
const TABLE_OR_4KIB_PAGE = 1 << 1;
const DEVICE_NGNRNE = 0;
const DEVICE_NGNRE = 1 << 2;
const DEVICE_GRE = 1 << 3;
const NORMAL_NC = 1 << 3 | 1 << 2;
const NORMAL = 1 << 4;
const READ_ONLY = 1 << 7;
const INNER_SHAREABLE = 1 << 8 | 1 << 9;
const ACCESSED = 1 << 10;
const PRIVILEGED_EXECUTE_NEVER = 1 << 53;
const UNPRIVILEGED_EXECUTE_NEVER = 1 << 54;
const SELF = 1 << 55;
}
}
impl PageTableEntryFlags {
const BLANK: PageTableEntryFlags = PageTableEntryFlags::empty();
pub fn present(&mut self) -> &mut Self {
self.insert(PageTableEntryFlags::PRESENT);
self
}
pub fn device(&mut self) -> &mut Self {
self.remove(PageTableEntryFlags::NORMAL);
self.remove(PageTableEntryFlags::NORMAL_NC);
self.remove(PageTableEntryFlags::DEVICE_NGNRE);
self.remove(PageTableEntryFlags::DEVICE_GRE);
self.insert(PageTableEntryFlags::DEVICE_NGNRNE);
self
}
pub fn normal(&mut self) -> &mut Self {
self.remove(PageTableEntryFlags::NORMAL_NC);
self.remove(PageTableEntryFlags::DEVICE_NGNRE);
self.remove(PageTableEntryFlags::DEVICE_GRE);
self.insert(PageTableEntryFlags::NORMAL);
self
}
pub fn read_only(&mut self) -> &mut Self {
self.insert(PageTableEntryFlags::READ_ONLY);
self
}
pub fn writable(&mut self) -> &mut Self {
self.remove(PageTableEntryFlags::READ_ONLY);
self
}
pub fn execute_disable(&mut self) -> &mut Self {
self.insert(PageTableEntryFlags::PRIVILEGED_EXECUTE_NEVER);
self.insert(PageTableEntryFlags::UNPRIVILEGED_EXECUTE_NEVER);
self
}
}
#[derive(Clone, Copy, Debug)]
pub struct PageTableEntry {
physical_address_and_flags: PhysAddr,
}
impl PageTableEntry {
pub fn address(&self) -> PhysAddr {
PhysAddr(
self.physical_address_and_flags.as_u64()
& !(BasePageSize::SIZE - 1u64)
& !(u64::MAX << 48),
)
}
fn is_present(&self) -> bool {
(self.physical_address_and_flags & PageTableEntryFlags::PRESENT.bits()) != 0
}
fn set(&mut self, physical_address: PhysAddr, flags: PageTableEntryFlags) {
assert_eq!(
physical_address % BasePageSize::SIZE,
0,
"Physical address is not on a 4 KiB page boundary (physical_address = {physical_address:p})"
);
let mut flags_to_set = flags;
flags_to_set.insert(PageTableEntryFlags::PRESENT);
flags_to_set.insert(PageTableEntryFlags::INNER_SHAREABLE);
flags_to_set.insert(PageTableEntryFlags::ACCESSED);
self.physical_address_and_flags = PhysAddr(physical_address.as_u64() | flags_to_set.bits());
}
}
pub trait PageSize: Copy {
const SIZE: u64;
const MAP_LEVEL: usize;
const MAP_EXTRA_FLAG: PageTableEntryFlags;
}
#[derive(Clone, Copy)]
pub enum BasePageSize {}
impl PageSize for BasePageSize {
const SIZE: u64 = 4096;
const MAP_LEVEL: usize = 3;
const MAP_EXTRA_FLAG: PageTableEntryFlags = PageTableEntryFlags::TABLE_OR_4KIB_PAGE;
}
#[derive(Clone, Copy)]
pub enum LargePageSize {}
impl PageSize for LargePageSize {
const SIZE: u64 = 2 * 1024 * 1024;
const MAP_LEVEL: usize = 2;
const MAP_EXTRA_FLAG: PageTableEntryFlags = PageTableEntryFlags::BLANK;
}
#[derive(Clone, Copy)]
pub enum HugePageSize {}
impl PageSize for HugePageSize {
const SIZE: u64 = 1024 * 1024 * 1024;
const MAP_LEVEL: usize = 1;
const MAP_EXTRA_FLAG: PageTableEntryFlags = PageTableEntryFlags::BLANK;
}
#[derive(Clone, Copy)]
struct Page<S: PageSize> {
virtual_address: VirtAddr,
size: PhantomData<S>,
}
impl<S: PageSize> Page<S> {
fn address(&self) -> VirtAddr {
self.virtual_address
}
fn flush_from_tlb(&self) {
unsafe {
asm!(
"dsb ishst",
"tlbi vale1is, {}",
"dsb ish",
"isb",
in(reg) self.virtual_address.as_u64(),
options(nostack),
);
}
}
fn is_valid_address(virtual_address: VirtAddr) -> bool {
virtual_address < VirtAddr(0x1_0000_0000_0000)
}
fn including_address(virtual_address: VirtAddr) -> Self {
assert!(
Self::is_valid_address(virtual_address),
"Virtual address {virtual_address:p} is invalid"
);
Self {
virtual_address: VirtAddr(virtual_address.0.align_down(S::SIZE)),
size: PhantomData,
}
}
fn range(first: Self, last: Self) -> PageIter<S> {
assert!(first.virtual_address <= last.virtual_address);
PageIter {
current: first,
last,
}
}
fn table_index<L: PageTableLevel>(&self) -> usize {
assert!(L::LEVEL <= S::MAP_LEVEL);
self.virtual_address.as_usize() >> PAGE_BITS >> ((3 - L::LEVEL) * PAGE_MAP_BITS)
& PAGE_MAP_MASK
}
}
struct PageIter<S: PageSize> {
current: Page<S>,
last: Page<S>,
}
impl<S: PageSize> Iterator for PageIter<S> {
type Item = Page<S>;
fn next(&mut self) -> Option<Page<S>> {
if self.current.virtual_address <= self.last.virtual_address {
let p = self.current;
self.current.virtual_address += S::SIZE;
Some(p)
} else {
None
}
}
}
trait PageTableLevel {
const LEVEL: usize;
}
trait PageTableLevelWithSubtables: PageTableLevel {
type SubtableLevel;
}
enum L0Table {}
impl PageTableLevel for L0Table {
const LEVEL: usize = 0;
}
impl PageTableLevelWithSubtables for L0Table {
type SubtableLevel = L1Table;
}
enum L1Table {}
impl PageTableLevel for L1Table {
const LEVEL: usize = 1;
}
impl PageTableLevelWithSubtables for L1Table {
type SubtableLevel = L2Table;
}
enum L2Table {}
impl PageTableLevel for L2Table {
const LEVEL: usize = 2;
}
impl PageTableLevelWithSubtables for L2Table {
type SubtableLevel = L3Table;
}
enum L3Table {}
impl PageTableLevel for L3Table {
const LEVEL: usize = 3;
}
struct PageTable<L> {
entries: [PageTableEntry; 1 << PAGE_MAP_BITS],
level: PhantomData<L>,
}
trait PageTableMethods {
fn get_page_table_entry<S: PageSize>(&self, page: Page<S>) -> Option<PageTableEntry>;
fn map_page_in_this_table<S: PageSize>(
&mut self,
page: Page<S>,
physical_address: PhysAddr,
flags: PageTableEntryFlags,
);
fn map_page<S: PageSize>(
&mut self,
page: Page<S>,
physical_address: PhysAddr,
flags: PageTableEntryFlags,
);
}
impl<L: PageTableLevel> PageTableMethods for PageTable<L> {
fn map_page_in_this_table<S: PageSize>(
&mut self,
page: Page<S>,
physical_address: PhysAddr,
flags: PageTableEntryFlags,
) {
assert_eq!(L::LEVEL, S::MAP_LEVEL);
let index = page.table_index::<L>();
let flush = self.entries[index].is_present();
if flags == PageTableEntryFlags::BLANK {
self.entries[index].set(physical_address, flags);
} else {
self.entries[index].set(physical_address, S::MAP_EXTRA_FLAG | flags);
}
if flush {
page.flush_from_tlb();
}
}
default fn get_page_table_entry<S: PageSize>(&self, page: Page<S>) -> Option<PageTableEntry> {
assert_eq!(L::LEVEL, S::MAP_LEVEL);
let index = page.table_index::<L>();
if self.entries[index].is_present() {
Some(self.entries[index])
} else {
None
}
}
default fn map_page<S: PageSize>(
&mut self,
page: Page<S>,
physical_address: PhysAddr,
flags: PageTableEntryFlags,
) {
self.map_page_in_this_table::<S>(page, physical_address, flags)
}
}
impl<L: PageTableLevelWithSubtables> PageTableMethods for PageTable<L>
where
L::SubtableLevel: PageTableLevel,
{
fn get_page_table_entry<S: PageSize>(&self, page: Page<S>) -> Option<PageTableEntry> {
assert!(L::LEVEL <= S::MAP_LEVEL);
let index = page.table_index::<L>();
if self.entries[index].is_present() {
if L::LEVEL < S::MAP_LEVEL {
let subtable = self.subtable::<S>(page);
subtable.get_page_table_entry::<S>(page)
} else {
Some(self.entries[index])
}
} else {
None
}
}
fn map_page<S: PageSize>(
&mut self,
page: Page<S>,
physical_address: PhysAddr,
flags: PageTableEntryFlags,
) {
assert!(L::LEVEL <= S::MAP_LEVEL);
if L::LEVEL < S::MAP_LEVEL {
let index = page.table_index::<L>();
if !self.entries[index].is_present() {
let physical_address = physicalmem::allocate(BasePageSize::SIZE as usize)
.expect("Unable to allocate physical memory");
self.entries[index].set(
physical_address,
PageTableEntryFlags::NORMAL | PageTableEntryFlags::TABLE_OR_4KIB_PAGE,
);
let subtable = self.subtable::<S>(page);
for entry in subtable.entries.iter_mut() {
entry.physical_address_and_flags = PhysAddr::zero();
}
}
let subtable = self.subtable::<S>(page);
subtable.map_page::<S>(page, physical_address, flags)
} else {
self.map_page_in_this_table::<S>(page, physical_address, flags)
}
}
}
impl<L: PageTableLevelWithSubtables> PageTable<L>
where
L::SubtableLevel: PageTableLevel,
{
#[allow(clippy::mut_from_ref)]
fn subtable<S: PageSize>(&self, page: Page<S>) -> &mut PageTable<L::SubtableLevel> {
assert!(L::LEVEL < S::MAP_LEVEL);
let index = page.table_index::<L>();
let table_address = self as *const PageTable<L> as usize;
let subtable_address =
(table_address << PAGE_MAP_BITS) & !(usize::MAX << 48) | (index << PAGE_BITS);
unsafe { &mut *(subtable_address as *mut PageTable<L::SubtableLevel>) }
}
fn map_pages<S: PageSize>(
&mut self,
range: PageIter<S>,
physical_address: PhysAddr,
flags: PageTableEntryFlags,
) {
let mut current_physical_address = physical_address;
for page in range {
self.map_page::<S>(page, current_physical_address, flags);
current_physical_address += S::SIZE;
}
}
}
#[inline]
fn get_page_range<S: PageSize>(virtual_address: VirtAddr, count: usize) -> PageIter<S> {
let first_page = Page::<S>::including_address(virtual_address);
let last_page = Page::<S>::including_address(virtual_address + (count - 1) * S::SIZE as usize);
Page::range(first_page, last_page)
}
pub fn get_page_table_entry<S: PageSize>(virtual_address: VirtAddr) -> Option<PageTableEntry> {
trace!("Looking up Page Table Entry for {:p}", virtual_address);
let page = Page::<S>::including_address(virtual_address);
let root_pagetable = unsafe { &mut *(L0TABLE_ADDRESS.as_mut_ptr::<PageTable<L0Table>>()) };
root_pagetable.get_page_table_entry(page)
}
pub fn get_physical_address<S: PageSize>(virtual_address: VirtAddr) -> Option<PhysAddr> {
trace!("Getting physical address for {:p}", virtual_address);
let page = Page::<S>::including_address(virtual_address);
let root_pagetable = unsafe { &mut *(L0TABLE_ADDRESS.as_mut_ptr::<PageTable<L0Table>>()) };
let address = root_pagetable.get_page_table_entry(page)?.address();
let offset = virtual_address & (S::SIZE - 1);
Some(PhysAddr(address.as_u64() | offset.as_u64()))
}
pub fn virtual_to_physical(virtual_address: VirtAddr) -> Option<PhysAddr> {
get_physical_address::<BasePageSize>(virtual_address)
}
#[no_mangle]
pub extern "C" fn virt_to_phys(virtual_address: VirtAddr) -> PhysAddr {
virtual_to_physical(virtual_address).unwrap()
}
pub fn map<S: PageSize>(
virtual_address: VirtAddr,
physical_address: PhysAddr,
count: usize,
flags: PageTableEntryFlags,
) {
trace!(
"Mapping virtual address {:p} to physical address {:p} ({} pages)",
virtual_address,
physical_address,
count
);
let range = get_page_range::<S>(virtual_address, count);
let root_pagetable = unsafe { &mut *(L0TABLE_ADDRESS.as_mut_ptr::<PageTable<L0Table>>()) };
root_pagetable.map_pages(range, physical_address, flags);
}
pub fn map_heap<S: PageSize>(virt_addr: VirtAddr, count: usize) -> Result<(), usize> {
let flags = {
let mut flags = PageTableEntryFlags::empty();
flags.normal().writable().execute_disable();
flags
};
let virt_addrs = (0..count).map(|n| virt_addr + n * S::SIZE as usize);
for (map_counter, virt_addr) in virt_addrs.enumerate() {
let phys_addr = physicalmem::allocate_aligned(S::SIZE as usize, S::SIZE as usize)
.map_err(|_| map_counter)?;
map::<S>(virt_addr, phys_addr, 1, flags);
}
Ok(())
}
pub fn unmap<S: PageSize>(virtual_address: VirtAddr, count: usize) {
trace!(
"Unmapping virtual address {:p} ({} pages)",
virtual_address,
count
);
let range = get_page_range::<S>(virtual_address, count);
let root_pagetable = unsafe { &mut *(L0TABLE_ADDRESS.as_mut_ptr::<PageTable<L0Table>>()) };
root_pagetable.map_pages(range, PhysAddr::zero(), PageTableEntryFlags::BLANK);
}
#[inline]
pub fn get_application_page_size() -> usize {
BasePageSize::SIZE as usize
}
pub unsafe fn init() {
let aa64mmfr0: u64;
let ram_start = get_ram_address();
info!("RAM starts at physical address {:p}", ram_start);
unsafe {
asm!(
"mrs {}, id_aa64mmfr0_el1",
out(reg) aa64mmfr0,
options(nostack),
);
}
let pa_range: u64 = match aa64mmfr0 & 0b1111 {
0b0000 => 32,
0b0001 => 36,
0b0010 => 40,
0b0011 => 42,
0b0100 => 44,
0b0101 => 48,
0b0110 => 52,
_ => panic!("Invalid physical address range"),
};
info!("Physical address range: {}GB", 1 << (pa_range - 30));
let tgran16: u64 = (aa64mmfr0 >> 20) & 0b1111;
let tgran64: u64 = (aa64mmfr0 >> 24) & 0b1111;
let tgran4: u64 = (aa64mmfr0 >> 28) & 0b1111;
info!("Support of 4KB pages: {}", tgran4 == 0);
info!("Support of 16KB pages: {}", tgran16 == 0b0001);
info!("Support of 64KB pages: {}", tgran64 == 0);
assert!(tgran4 == 0);
}