use core::marker::PhantomData;
use core::sync::atomic::{AtomicU64, Ordering};
use crate::tt::page::PageBox;
use super::descriptor::{Descriptor, DescriptorBuilder, DescriptorRefMut};
use super::Level0;
#[derive(Debug)]
#[repr(C, align(0x1000))]
pub struct TranslationTable<L> {
descriptors: [AtomicU64; 512],
phantom: PhantomData<L>,
}
impl<L> TranslationTable<L> {
pub fn new() -> Self {
const INVALID: AtomicU64 = AtomicU64::new(Descriptor::<()>::INVALID_BITS);
Self {
descriptors: [INVALID; 512],
phantom: PhantomData,
}
}
fn get_mut_or_set<'tt, B, D>(&'tt mut self, index: usize, build: B) -> DescriptorRefMut<'tt, L>
where
B: FnOnce(DescriptorBuilder<L>) -> D,
D: Into<Descriptor<L>>,
{
let bits = self.descriptors[index].load(Ordering::SeqCst);
let descriptor = DescriptorRefMut::from_bits(bits);
if let Some(descriptor) = descriptor {
descriptor
} else {
let descriptor = build(DescriptorBuilder::default()).into();
let bits = descriptor.into_inner();
self.descriptors[index].store(bits, Ordering::SeqCst);
unsafe { DescriptorRefMut::from_bits_unchecked(bits) }
}
}
fn replace<F, D>(&mut self, index: usize, build: F) -> Option<Descriptor<L>>
where
F: FnOnce(DescriptorBuilder<L>) -> D,
D: Into<Descriptor<L>>,
{
let descriptor = build(DescriptorBuilder::default()).into();
let new_bits = descriptor.into_inner();
let old_bits = self.descriptors[index].swap(new_bits, Ordering::SeqCst);
Descriptor::from_bits(old_bits)
}
}
impl TranslationTable<Level0> {
pub fn map_contiguous(&mut self, va_start: usize, va_end: usize, pa_start: usize, flags: &str) {
let mut va = va_start;
let mut pa = pa_start;
while va < va_end {
self.map_page(va, pa, flags);
va += 0x1000;
pa += 0x1000;
}
}
fn map_page(&mut self, virtual_address: usize, physical_address: usize, flags: &str) {
const MASK: usize = 0b1_1111_1111;
let level0_index = (virtual_address >> 39) & MASK;
let level1_index = (virtual_address >> 30) & MASK;
let level2_index = (virtual_address >> 21) & MASK;
let level3_index = (virtual_address >> 12) & MASK;
let mut level0_descriptor = self.get_mut_or_set(level0_index, |builder| {
builder.table(PageBox::new(TranslationTable::new())).build()
});
let level1 = level0_descriptor
.table_mut()
.expect("level 0 descriptor should be a table descriptor")
.translation_table_mut();
let mut level1_descriptor = level1.get_mut_or_set(level1_index, |builder| {
builder.table(PageBox::new(TranslationTable::new())).build()
});
let level2 = level1_descriptor
.table_mut()
.expect("level 1 descriptor should be a table descriptor")
.translation_table_mut();
let mut level2_descriptor = level2.get_mut_or_set(level2_index, |builder| {
builder.table(PageBox::new(TranslationTable::new())).build()
});
let level3 = level2_descriptor
.table_mut()
.expect("level 2 descriptor should be a table descriptor")
.translation_table_mut();
let old_level3_descriptor = level3.replace(level3_index, |builder| {
builder.page(physical_address).access_flag(true).build()
});
core::mem::forget(old_level3_descriptor);
}
}