use memory_addr::{va_range, MemoryAddr, VirtAddr};
use crate::{MappingBackend, MappingError, MemoryArea, MemorySet};
const MAX_ADDR: usize = 0x10000;
type MockFlags = u8;
type MockPageTable = [MockFlags; MAX_ADDR];
#[derive(Clone)]
struct MockBackend;
type MockMemorySet = MemorySet<MockBackend>;
impl MappingBackend for MockBackend {
type Addr = VirtAddr;
type Flags = MockFlags;
type PageTable = MockPageTable;
fn map(&self, start: VirtAddr, size: usize, flags: MockFlags, pt: &mut MockPageTable) -> bool {
for entry in pt.iter_mut().skip(start.as_usize()).take(size) {
if *entry != 0 {
return false;
}
*entry = flags;
}
true
}
fn unmap(&self, start: VirtAddr, size: usize, pt: &mut MockPageTable) -> bool {
for entry in pt.iter_mut().skip(start.as_usize()).take(size) {
if *entry == 0 {
return false;
}
*entry = 0;
}
true
}
fn protect(
&self,
start: VirtAddr,
size: usize,
new_flags: MockFlags,
pt: &mut MockPageTable,
) -> bool {
for entry in pt.iter_mut().skip(start.as_usize()).take(size) {
if *entry == 0 {
return false;
}
*entry = new_flags;
}
true
}
}
macro_rules! assert_ok {
($expr: expr) => {
assert!(($expr).is_ok())
};
}
macro_rules! assert_err {
($expr: expr) => {
assert!(($expr).is_err())
};
($expr: expr, $err: ident) => {
assert_eq!(($expr).err(), Some(MappingError::$err))
};
}
fn dump_memory_set(set: &MockMemorySet) {
use std::sync::Mutex;
static DUMP_LOCK: Mutex<()> = Mutex::new(());
let _lock = DUMP_LOCK.lock().unwrap();
println!("Number of areas: {}", set.len());
for area in set.iter() {
println!("{:?}", area);
}
}
#[test]
fn test_map_unmap() {
let mut set = MockMemorySet::new();
let mut pt = [0; MAX_ADDR];
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.map(
MemoryArea::new(start.into(), 0x1000, 1, MockBackend),
&mut pt,
false,
));
}
for start in (0x1000..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.map(
MemoryArea::new(start.into(), 0x1000, 2, MockBackend),
&mut pt,
false,
));
}
dump_memory_set(&set);
assert_eq!(set.len(), 16);
for &e in &pt[0..MAX_ADDR] {
assert!(e == 1 || e == 2);
}
let area = set.find(0x4100.into()).unwrap();
assert_eq!(area.start(), 0x4000.into());
assert_eq!(area.end(), 0x5000.into());
assert_eq!(area.flags(), 1);
assert_eq!(pt[0x4200], 1);
assert_err!(
set.map(
MemoryArea::new(0x4000.into(), 0x4000, 3, MockBackend),
&mut pt,
false
),
AlreadyExists
);
assert_ok!(set.map(
MemoryArea::new(0x4000.into(), 0x4000, 3, MockBackend),
&mut pt,
true
));
dump_memory_set(&set);
assert_eq!(set.len(), 13);
let area = set.find(0x4100.into()).unwrap();
assert_eq!(area.start(), 0x4000.into());
assert_eq!(area.end(), 0x8000.into());
assert_eq!(area.flags(), 3);
for &e in &pt[0x4000..0x8000] {
assert_eq!(e, 3);
}
assert_ok!(set.unmap(0x4000.into(), 0x8000, &mut pt));
assert_eq!(set.len(), 8);
assert_ok!(set.unmap(0.into(), MAX_ADDR * 2, &mut pt));
assert_eq!(set.len(), 0);
for &e in &pt[0..MAX_ADDR] {
assert_eq!(e, 0);
}
}
#[test]
fn test_unmap_split() {
let mut set = MockMemorySet::new();
let mut pt = [0; MAX_ADDR];
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.map(
MemoryArea::new(start.into(), 0x1000, 1, MockBackend),
&mut pt,
false,
));
}
assert_eq!(set.len(), 8);
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.unmap((start + 0xc00).into(), 0x1800, &mut pt));
}
dump_memory_set(&set);
assert_eq!(set.len(), 8);
for area in set.iter() {
if area.start().as_usize() == 0 {
assert_eq!(area.size(), 0xc00);
} else {
assert_eq!(area.start().align_offset_4k(), 0x400);
assert_eq!(area.end().align_offset_4k(), 0xc00);
assert_eq!(area.size(), 0x800);
}
for &e in &pt[area.start().as_usize()..area.end().as_usize()] {
assert_eq!(e, 1);
}
}
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.unmap((start + 0x800).into(), 0x100, &mut pt));
}
dump_memory_set(&set);
assert_eq!(set.len(), 16);
for area in set.iter() {
let off = area.start().align_offset_4k();
if off == 0 {
assert_eq!(area.size(), 0x800);
} else if off == 0x400 {
assert_eq!(area.size(), 0x400);
} else if off == 0x900 {
assert_eq!(area.size(), 0x300);
} else {
unreachable!();
}
for &e in &pt[area.start().as_usize()..area.end().as_usize()] {
assert_eq!(e, 1);
}
}
let mut iter = set.iter();
while let Some(area) = iter.next() {
if let Some(next) = iter.next() {
for &e in &pt[area.end().as_usize()..next.start().as_usize()] {
assert_eq!(e, 0);
}
}
}
drop(iter);
assert_ok!(set.unmap(0.into(), MAX_ADDR, &mut pt));
assert_eq!(set.len(), 0);
for &e in &pt[0..MAX_ADDR] {
assert_eq!(e, 0);
}
}
#[test]
fn test_protect() {
let mut set = MockMemorySet::new();
let mut pt = [0; MAX_ADDR];
let update_flags = |new_flags: MockFlags| {
move |old_flags: MockFlags| -> Option<MockFlags> {
if (old_flags & 0x7) == (new_flags & 0x7) {
return None;
}
let flags = (new_flags & 0x7) | (old_flags & !0x7);
Some(flags)
}
};
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.map(
MemoryArea::new(start.into(), 0x1000, 0x7, MockBackend),
&mut pt,
false,
));
}
assert_eq!(set.len(), 8);
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.protect((start + 0xc00).into(), 0x1800, update_flags(0x1), &mut pt));
}
dump_memory_set(&set);
assert_eq!(set.len(), 23);
for area in set.iter() {
let off = area.start().align_offset_4k();
if area.start().as_usize() == 0 {
assert_eq!(area.size(), 0xc00);
assert_eq!(area.flags(), 0x7);
} else if off == 0 {
assert_eq!(area.size(), 0x400);
assert_eq!(area.flags(), 0x1);
} else if off == 0x400 {
assert_eq!(area.size(), 0x800);
assert_eq!(area.flags(), 0x7);
} else if off == 0xc00 {
assert_eq!(area.size(), 0x400);
assert_eq!(area.flags(), 0x1);
}
}
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.protect((start + 0x800).into(), 0x100, update_flags(0x13), &mut pt));
}
dump_memory_set(&set);
assert_eq!(set.len(), 39);
for area in set.iter() {
let off = area.start().align_offset_4k();
if area.start().as_usize() == 0 {
assert_eq!(area.size(), 0x800);
assert_eq!(area.flags(), 0x7);
} else if off == 0 {
assert_eq!(area.size(), 0x400);
assert_eq!(area.flags(), 0x1);
} else if off == 0x400 {
assert_eq!(area.size(), 0x400);
assert_eq!(area.flags(), 0x7);
} else if off == 0x800 {
assert_eq!(area.size(), 0x100);
assert_eq!(area.flags(), 0x3);
} else if off == 0x900 {
assert_eq!(area.size(), 0x300);
assert_eq!(area.flags(), 0x7);
} else if off == 0xc00 {
assert_eq!(area.size(), 0x400);
assert_eq!(area.flags(), 0x1);
}
}
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.protect((start + 0x880).into(), 0x80, update_flags(0x3), &mut pt));
}
assert_eq!(set.len(), 39);
assert_ok!(set.unmap(0.into(), MAX_ADDR, &mut pt));
assert_eq!(set.len(), 0);
for &e in &pt[0..MAX_ADDR] {
assert_eq!(e, 0);
}
}
#[test]
fn test_find_free_area() {
let mut set = MockMemorySet::new();
let mut pt = [0; MAX_ADDR];
for start in (0..MAX_ADDR).step_by(0x2000) {
assert_ok!(set.map(
MemoryArea::new(start.into(), 0x1000, 1, MockBackend),
&mut pt,
false,
));
}
let addr = set.find_free_area(0.into(), 0x1000, va_range!(0..MAX_ADDR), 1);
assert_eq!(addr, Some(0x1000.into()));
let addr = set.find_free_area(0x800.into(), 0x800, va_range!(0..MAX_ADDR), 0x800);
assert_eq!(addr, Some(0x1000.into()));
let addr = set.find_free_area(0x1800.into(), 0x800, va_range!(0..MAX_ADDR), 0x800);
assert_eq!(addr, Some(0x1800.into()));
let addr = set.find_free_area(0x1800.into(), 0x1000, va_range!(0..MAX_ADDR), 0x1000);
assert_eq!(addr, Some(0x3000.into()));
let addr = set.find_free_area(0x2000.into(), 0x1000, va_range!(0..MAX_ADDR), 0x1000);
assert_eq!(addr, Some(0x3000.into()));
let addr = set.find_free_area(0xf000.into(), 0x1000, va_range!(0..MAX_ADDR), 0x1000);
assert_eq!(addr, Some(0xf000.into()));
let addr = set.find_free_area(0xf001.into(), 0x1000, va_range!(0..MAX_ADDR), 0x1000);
assert_eq!(addr, None);
}