use super::*; use types::address::{PhysicalAddress, VirtualAddress}; use types::id::{ProcessId, RegionId}; use types::mapping::PagePerms; const BASE: usize = 0x8400_0000; fn table() -> FrameTable<8> { FrameTable::new(PhysicalAddress::from_raw(BASE), 8) } fn pid(n: u16) -> ProcessId { ProcessId::from_raw(n) } fn va(addr: usize) -> VirtualAddress { VirtualAddress::from_raw(addr) } fn user(owner: u16, addr: usize) -> FrameUse { FrameUse::user(pid(owner), va(addr), PagePerms::ReadWrite).unwrap() } #[test] fn new_table_has_every_frame_free() { let t = table(); assert_eq!(t.free_count(), 8); assert_eq!(t.capacity(), 8); } #[test] fn total_is_clamped_to_capacity() { let t: FrameTable<4> = FrameTable::new(PhysicalAddress::from_raw(BASE), 100); assert_eq!(t.free_count(), 4); } #[test] fn user_use_requires_page_aligned_va() { assert!(FrameUse::user(pid(0), va(0x1234), PagePerms::ReadOnly).is_none()); assert!(FrameUse::user(pid(0), va(0x1000), PagePerms::ReadOnly).is_some()); } #[test] fn alloc_hands_out_distinct_frames_inside_the_pool() { let mut t = table(); let a = t.alloc(user(0, 0x1000)).unwrap(); let b = t.alloc(user(0, 0x2000)).unwrap(); assert_ne!(a, b); for pa in [a, b] { assert!(pa.raw() >= BASE && pa.raw() < BASE + 8 * 4096); assert_eq!(pa.raw() % 4096, 0); } assert_eq!(t.free_count(), 6); } #[test] fn alloc_fails_when_exhausted() { let mut t = table(); for i in 0..8 { t.alloc(user(0, (i + 1) * 0x1000)).unwrap(); } assert!(t.alloc(user(0, 0x9000)).is_none()); } #[test] fn free_returns_the_frame_and_its_use() { let mut t = table(); let pa = t.alloc(user(3, 0x5000)).unwrap(); assert_eq!(t.free(pa), Some(user(3, 0x5000))); assert_eq!(t.free_count(), 8); } #[test] fn freeing_a_free_frame_is_rejected() { let mut t = table(); let pa = t.alloc(user(0, 0x1000)).unwrap(); t.free(pa).unwrap(); assert_eq!(t.free(pa), None); assert_eq!(t.free_count(), 8); } #[test] fn freeing_outside_the_pool_or_unaligned_is_rejected() { let mut t = table(); t.alloc(user(0, 0x1000)).unwrap(); assert_eq!(t.free(PhysicalAddress::from_raw(BASE - 4096)), None); assert_eq!(t.free(PhysicalAddress::from_raw(BASE + 8 * 4096)), None); assert_eq!(t.free(PhysicalAddress::from_raw(BASE + 1)), None); assert_eq!(t.free_count(), 7); } #[test] fn find_user_matches_owner_and_va() { let mut t = table(); let pa = t.alloc(user(1, 0x3000)).unwrap(); assert_eq!( t.find_user(pid(1), va(0x3000)), Some((pa, PagePerms::ReadWrite)) ); assert_eq!(t.find_user(pid(2), va(0x3000)), None); assert_eq!(t.find_user(pid(1), va(0x4000)), None); } #[test] fn retag_moves_a_frame_to_a_new_use() { let mut t = table(); let pa = t.alloc(user(1, 0x3000)).unwrap(); assert!(t.retag(pa, user(2, 0x7000))); assert_eq!(t.find_user(pid(1), va(0x3000)), None); assert_eq!(t.find_user(pid(2), va(0x7000)).map(|(p, _)| p), Some(pa)); } #[test] fn retag_of_a_free_frame_is_rejected() { let mut t = table(); let pa = t.alloc(user(1, 0x3000)).unwrap(); t.free(pa); assert!(!t.retag(pa, user(2, 0x7000))); assert_eq!(t.free_count(), 8); } #[test] fn alloc_contiguous_returns_adjacent_frames() { let mut t = table(); let base = t .alloc_contiguous(3, |_| FrameUse::Region { region: RegionId::from_raw(0), }) .unwrap(); assert_eq!(t.free_count(), 5); for i in 0..3 { assert_eq!( t.get(base.offset(i)), Some(&FrameUse::Region { region: RegionId::from_raw(0) }) ); } } #[test] fn alloc_contiguous_fails_without_a_long_enough_run() { let mut t: FrameTable<4> = FrameTable::new(PhysicalAddress::from_raw(BASE), 4); let a = t.alloc(user(0, 0x1000)).unwrap(); let _b = t.alloc(user(0, 0x2000)).unwrap(); let _c = t.alloc(user(0, 0x3000)).unwrap(); t.free(a); // free frames: 0 and 3 — no run of 2 assert!( t.alloc_contiguous(2, |_| FrameUse::PageTable { owner: pid(0) }) .is_none() ); assert_eq!(t.free_count(), 2); } #[test] fn free_owned_by_releases_only_that_processes_frames() { let mut t = table(); t.alloc(user(1, 0x1000)).unwrap(); t.alloc(FrameUse::PageTable { owner: pid(1) }).unwrap(); let other = t.alloc(user(2, 0x1000)).unwrap(); let region = t .alloc(FrameUse::Region { region: RegionId::from_raw(1), }) .unwrap(); assert_eq!(t.free_owned_by(pid(1)), 2); assert_eq!(t.free_count(), 6); assert!(t.get(other).is_some()); assert!(t.get(region).is_some()); } #[test] fn user_frames_of_lists_mappings_of_one_process() { let mut t = table(); t.alloc(user(1, 0x1000)).unwrap(); t.alloc(user(2, 0x2000)).unwrap(); t.alloc(user(1, 0x3000)).unwrap(); t.alloc(FrameUse::PageTable { owner: pid(1) }).unwrap(); let mut vas = [0usize; 4]; let mut n = 0; for (_, v) in t.user_frames_of(pid(1)) { vas[n] = v.raw(); n += 1; } assert_eq!(&vas[..n], &[0x1000, 0x3000]); }