use patina_paging::{MemoryAttributes, PageTable, PagingType, PtError, aarch64::AArch64PageTable};
use crate::paging::{CacheAttributeValue, PatinaPageTable};
use patina::pi::protocol::cpu_arch::CpuFlushType;
use patina::standard::efi;
use patina_paging::page_allocator::PageAllocator;
const CACHED_ATTRS: MemoryAttributes = MemoryAttributes::Writeback.union(MemoryAttributes::WriteThrough);
const UNCACHED_ATTRS: MemoryAttributes = MemoryAttributes::Uncached.union(MemoryAttributes::WriteCombining);
#[derive(Debug)]
pub struct EfiCpuPagingAArch64<P>
where
P: PageTable,
{
paging: P,
}
impl<P> PatinaPageTable for EfiCpuPagingAArch64<P>
where
P: PageTable,
{
fn map_memory_region(&mut self, address: u64, size: u64, attributes: MemoryAttributes) -> Result<(), PtError> {
self.paging.map_memory_region(address, size, attributes)
}
fn map_aliased_memory_region(
&mut self,
virtual_address: u64,
physical_address: u64,
size: u64,
attributes: MemoryAttributes,
) -> Result<(), PtError> {
self.paging.map_aliased_memory_region(virtual_address, physical_address, size, attributes)
}
fn unmap_memory_region(&mut self, address: u64, size: u64) -> Result<(), PtError> {
self.paging.unmap_memory_region(address, size)
}
fn install_page_table(&mut self) -> Result<(), PtError> {
self.paging.install_page_table()
}
fn query_memory_region(&self, address: u64, size: u64) -> Result<MemoryAttributes, (PtError, CacheAttributeValue)> {
self.paging.query_memory_region(address, size).map_err(|e| (e, CacheAttributeValue::NotSupported))
}
fn dump_page_tables(&self, address: u64, size: u64) -> Result<(), PtError> {
self.paging.dump_page_tables(address, size)
}
fn handle_cacheability_change(
&self,
address: u64,
size: u64,
old_cache_attributes: MemoryAttributes,
new_cache_attributes: MemoryAttributes,
) {
if old_cache_attributes.intersects(CACHED_ATTRS) && new_cache_attributes.intersects(UNCACHED_ATTRS) {
let _ = patina::arch::flush_data_cache(address, size, CpuFlushType::EfiCpuFlushTypeWriteBackInvalidate);
}
}
}
#[cfg_attr(coverage, coverage(off))]
pub fn create_cpu_aarch64_paging<A: PageAllocator + 'static>(
page_allocator: A,
) -> Result<impl PatinaPageTable, efi::Status> {
Ok(EfiCpuPagingAArch64 { paging: AArch64PageTable::new(page_allocator, PagingType::Paging4Level).unwrap() })
}
#[cfg_attr(coverage, coverage(off))]
pub unsafe fn open_active_cpu_aarch64_paging<A: PageAllocator + 'static>(
page_allocator: A,
) -> Result<impl PatinaPageTable, PtError> {
let page_table = unsafe { AArch64PageTable::open_active(page_allocator)? };
Ok(EfiCpuPagingAArch64 { paging: page_table })
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::*;
use patina_paging::MockPageTable;
#[test]
fn test_map_memory_region() {
let mut mock_page_table = MockPageTable::new();
mock_page_table.expect_map_memory_region().returning(|_, _, _| Ok(()));
let mut paging = EfiCpuPagingAArch64 { paging: mock_page_table };
let result = paging.map_memory_region(0x1000, 0x1000, MemoryAttributes::Uncached);
assert!(result.is_ok());
}
#[test]
fn test_map_aliased_memory_region() {
let mut mock_page_table = MockPageTable::new();
mock_page_table.expect_map_aliased_memory_region().returning(
|virtual_address, physical_address, size, attributes| {
assert_eq!(virtual_address, 0x2000);
assert_eq!(physical_address, 0x1000);
assert_eq!(size, 0x1000);
assert_eq!(attributes, MemoryAttributes::Writeback | MemoryAttributes::ReadOnly);
Ok(())
},
);
let mut paging = EfiCpuPagingAArch64 { paging: mock_page_table };
let result = paging.map_aliased_memory_region(
0x2000,
0x1000,
0x1000,
MemoryAttributes::Writeback | MemoryAttributes::ReadOnly,
);
assert!(result.is_ok());
}
#[test]
fn test_unmap_memory_region() {
let mut mock_page_table = MockPageTable::new();
mock_page_table.expect_unmap_memory_region().returning(|_, _| Ok(()));
let mut paging = EfiCpuPagingAArch64 { paging: mock_page_table };
let result = paging.unmap_memory_region(0x1000, 0x1000);
assert!(result.is_ok());
}
#[test]
fn test_remap_memory_region() {
let mut mock_page_table = MockPageTable::new();
mock_page_table.expect_map_memory_region().returning(|_, _, _| Ok(()));
let mut paging = EfiCpuPagingAArch64 { paging: mock_page_table };
let result = paging.map_memory_region(0x1000, 0x1000, MemoryAttributes::Uncached);
assert!(result.is_ok());
}
#[test]
fn test_query_memory_region() {
let mut mock_page_table = MockPageTable::new();
mock_page_table
.expect_query_memory_region()
.returning(|_, _| Ok(MemoryAttributes::Writeback | MemoryAttributes::Uncached));
let paging = EfiCpuPagingAArch64 { paging: mock_page_table };
let result = paging.query_memory_region(0x1000, 0x1000);
assert!(result.is_ok());
assert_eq!(result.unwrap(), MemoryAttributes::Writeback | MemoryAttributes::Uncached);
}
#[test]
fn test_handle_cacheability_change() {
let paging = EfiCpuPagingAArch64 { paging: MockPageTable::new() };
paging.handle_cacheability_change(0x1000, 0x1000, MemoryAttributes::Writeback, MemoryAttributes::Uncached);
}
}