#[cfg(test)]
pub mod mock {
use ax_memory_addr::{PhysAddr, VirtAddr};
use axvisor_api::{api_impl, memory::MemoryIf};
use spin::Mutex;
static GLOBAL_LOCK: Mutex<MockMmHalState> = Mutex::new(MockMmHalState::new());
struct MockMmHalState {
memory_pool: [[u8; 4096]; 16],
alloc_mask: u16,
reset_counter: usize,
}
impl MockMmHalState {
const fn new() -> Self {
Self {
memory_pool: [[0; 4096]; 16],
alloc_mask: 0,
reset_counter: 0,
}
}
}
#[derive(Debug)]
pub struct MockMmHal;
#[api_impl]
impl MemoryIf for MockMmHal {
fn alloc_frame() -> Option<PhysAddr> {
let mut state = GLOBAL_LOCK.lock();
for i in 0..16 {
let bit = 1 << i;
if (state.alloc_mask & bit) == 0 {
state.alloc_mask |= bit;
let phys_addr = 0x1000 + (i * 4096);
return Some(ax_memory_addr::PhysAddr::from(phys_addr));
}
}
None
}
fn alloc_contiguous_frames(_num_frames: usize, _frame_align: usize) -> Option<PhysAddr> {
unimplemented!()
}
fn dealloc_frame(paddr: PhysAddr) {
let mut state = GLOBAL_LOCK.lock();
let addr = paddr.as_usize();
if addr >= 0x1000 && addr < 0x1000 + (16 * 4096) && (addr - 0x1000) % 4096 == 0 {
let page_index = (addr - 0x1000) / 4096;
let bit = 1 << page_index;
state.alloc_mask &= !bit;
}
}
fn dealloc_contiguous_frames(_first_addr: PhysAddr, _num_frames: usize) {
unimplemented!()
}
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
let state = GLOBAL_LOCK.lock();
let addr = paddr.as_usize();
if addr >= 0x1000 && addr < 0x1000 + (16 * 4096) {
let page_index = (addr - 0x1000) / 4096;
let offset = (addr - 0x1000) % 4096;
let page_ptr = state.memory_pool[page_index].as_ptr();
ax_memory_addr::VirtAddr::from(unsafe { page_ptr.add(offset) as usize })
} else {
ax_memory_addr::VirtAddr::from(addr)
}
}
fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
let state = GLOBAL_LOCK.lock();
let pool_start = state.memory_pool.as_ptr() as usize;
let pool_end = pool_start + (16 * 4096);
if vaddr.as_usize() >= pool_start && vaddr.as_usize() < pool_end {
let offset = vaddr.as_usize() - pool_start;
ax_memory_addr::PhysAddr::from(0x1000 + offset)
} else {
ax_memory_addr::PhysAddr::from(vaddr.as_usize())
}
}
}
impl MockMmHal {
#[allow(dead_code)]
pub fn reset() {
let mut state = GLOBAL_LOCK.lock();
state.memory_pool = [[0; 4096]; 16];
state.alloc_mask = 0;
state.reset_counter += 1;
}
#[allow(dead_code)]
pub fn allocated_count() -> usize {
let state = GLOBAL_LOCK.lock();
state.alloc_mask.count_ones() as usize
}
#[allow(dead_code)]
pub fn is_allocated(paddr: ax_memory_addr::PhysAddr) -> bool {
let state = GLOBAL_LOCK.lock();
let addr = paddr.as_usize();
if addr >= 0x1000 && addr < 0x1000 + (16 * 4096) && (addr - 0x1000) % 4096 == 0 {
let page_index = (addr - 0x1000) / 4096;
let bit = 1 << page_index;
(state.alloc_mask & bit) != 0
} else {
false
}
}
#[allow(dead_code)]
pub fn reset_count() -> usize {
let state = GLOBAL_LOCK.lock();
state.reset_counter
}
}
}
#[cfg(test)]
mod tests {
use axvisor_api::memory::MemoryIf;
use crate::test_utils::mock::MockMmHal;
#[test]
fn test_mock_allocator() {
MockMmHal::reset();
let addr1 = MockMmHal::alloc_frame().unwrap();
let addr2 = MockMmHal::alloc_frame().unwrap();
let addr3 = MockMmHal::alloc_frame().unwrap();
assert_ne!(addr1.as_usize(), addr2.as_usize());
assert_ne!(addr2.as_usize(), addr3.as_usize());
assert_ne!(addr1.as_usize(), addr3.as_usize());
assert_eq!(addr1.as_usize() % 0x1000, 0);
assert_eq!(addr2.as_usize() % 0x1000, 0);
assert_eq!(addr3.as_usize() % 0x1000, 0);
}
}