use crate::arch::pmp::{Pmp, PmpAddrMatch, PmpEntry, PmpResult};
const R: u8 = 1 << 0;
const W: u8 = 1 << 1;
const X: u8 = 1 << 2;
const A_TOR: u8 = 1 << 3; const A_NA4: u8 = 2 << 3; const A_NAPOT: u8 = 3 << 3; const L: u8 = 1 << 7;
#[test]
fn addr_match_off() {
assert_eq!(PmpAddrMatch::from_bits(0), PmpAddrMatch::Off);
}
#[test]
fn addr_match_tor() {
assert_eq!(PmpAddrMatch::from_bits(1), PmpAddrMatch::Tor);
}
#[test]
fn addr_match_na4() {
assert_eq!(PmpAddrMatch::from_bits(2), PmpAddrMatch::Na4);
}
#[test]
fn addr_match_napot() {
assert_eq!(PmpAddrMatch::from_bits(3), PmpAddrMatch::Napot);
}
#[test]
fn machine_mode_full_access_no_entries() {
let pmp = Pmp::new();
let result = pmp.check(0x8000_0000, 4, true, false, false, true);
assert_eq!(result, PmpResult::Allow);
}
#[test]
fn machine_mode_write_no_entries() {
let pmp = Pmp::new();
let result = pmp.check(0x1000, 8, false, true, false, true);
assert_eq!(result, PmpResult::Allow);
}
#[test]
fn machine_mode_exec_no_entries() {
let pmp = Pmp::new();
let result = pmp.check(0x0, 4, false, false, true, true);
assert_eq!(result, PmpResult::Allow);
}
#[test]
fn user_mode_no_match_no_entries() {
let pmp = Pmp::new();
let result = pmp.check(0x8000_0000, 4, true, false, false, false);
assert_eq!(result, PmpResult::NoMatch);
}
#[test]
fn tor_permits_access_in_range() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000); pmp.set_cfg(0, A_TOR | R | W | X);
let result = pmp.check(0x4000, 4, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
}
#[test]
fn tor_denies_access_outside_range() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000); pmp.set_cfg(0, A_TOR | R | W | X);
let result = pmp.check(0x8000, 4, true, false, false, false);
assert_eq!(result, PmpResult::NoMatch);
}
#[test]
fn tor_range_between_entries() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x1000);
pmp.set_cfg(0, 0);
pmp.set_addr(1, 0x2000);
pmp.set_cfg(1, A_TOR | R);
let result = pmp.check(0x5000, 4, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x3000, 4, true, false, false, false);
assert_eq!(result, PmpResult::NoMatch);
}
#[test]
fn tor_denies_write_when_only_read_permitted() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000); pmp.set_cfg(0, A_TOR | R);
let read_result = pmp.check(0x1000, 4, true, false, false, false);
assert_eq!(read_result, PmpResult::Allow);
let write_result = pmp.check(0x1000, 4, false, true, false, false);
assert_eq!(write_result, PmpResult::Deny);
}
#[test]
fn na4_matches_exactly_four_bytes() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x1000);
pmp.set_cfg(0, A_NA4 | R | W);
let result = pmp.check(0x4000, 4, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x4000, 1, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x4004, 1, true, false, false, false);
assert_eq!(result, PmpResult::NoMatch);
let result = pmp.check(0x4002, 4, true, false, false, false);
assert_eq!(result, PmpResult::Deny);
}
#[test]
fn napot_8_byte_region() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x400);
pmp.set_cfg(0, A_NAPOT | R | W | X);
let result = pmp.check(0x1000, 4, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x1004, 4, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x1008, 1, true, false, false, false);
assert_eq!(result, PmpResult::NoMatch);
}
#[test]
fn napot_larger_region() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x403);
pmp.set_cfg(0, A_NAPOT | R);
let result = pmp.check(0x1000, 1, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x101F, 1, true, false, false, false);
assert_eq!(result, PmpResult::Allow);
let result = pmp.check(0x1020, 1, true, false, false, false);
assert_eq!(result, PmpResult::NoMatch);
}
#[test]
fn read_only_denies_write_and_exec() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000);
pmp.set_cfg(0, A_TOR | R);
assert_eq!(pmp.check(0x1000, 4, true, false, false, false), PmpResult::Allow);
assert_eq!(pmp.check(0x1000, 4, false, true, false, false), PmpResult::Deny);
assert_eq!(pmp.check(0x1000, 4, false, false, true, false), PmpResult::Deny);
}
#[test]
fn execute_only_denies_read_and_write() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000);
pmp.set_cfg(0, A_TOR | X);
assert_eq!(pmp.check(0x1000, 4, false, false, true, false), PmpResult::Allow);
assert_eq!(pmp.check(0x1000, 4, true, false, false, false), PmpResult::Deny);
assert_eq!(pmp.check(0x1000, 4, false, true, false, false), PmpResult::Deny);
}
#[test]
fn rwx_permits_all() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000);
pmp.set_cfg(0, A_TOR | R | W | X);
assert_eq!(pmp.check(0x1000, 4, true, false, false, false), PmpResult::Allow);
assert_eq!(pmp.check(0x1000, 4, false, true, false, false), PmpResult::Allow);
assert_eq!(pmp.check(0x1000, 4, false, false, true, false), PmpResult::Allow);
}
#[test]
fn locked_entry_applies_to_machine_mode() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000); pmp.set_cfg(0, A_TOR | R | L);
let result = pmp.check(0x1000, 4, false, true, false, true);
assert_eq!(result, PmpResult::Deny);
let result = pmp.check(0x1000, 4, true, false, false, true);
assert_eq!(result, PmpResult::Allow);
}
#[test]
fn unlocked_entry_m_mode_bypasses() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000); pmp.set_cfg(0, A_TOR | R);
let result = pmp.check(0x1000, 4, false, true, false, true);
assert_eq!(result, PmpResult::Allow);
}
#[test]
fn locked_entry_cannot_be_modified() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000);
pmp.set_cfg(0, A_TOR | R | L);
pmp.set_cfg(0, A_TOR | R | W | X);
assert_eq!(pmp.get_cfg(0), A_TOR | R | L);
pmp.set_addr(0, 0x4000);
assert_eq!(pmp.get_addr(0), 0x2000);
}
#[test]
fn first_matching_entry_wins() {
let mut pmp = Pmp::new();
pmp.set_addr(0, 0x2000);
pmp.set_cfg(0, A_TOR | R);
pmp.set_addr(1, 0x4000);
pmp.set_cfg(1, A_TOR | R | W);
let result = pmp.check(0x1000, 4, false, true, false, false);
assert_eq!(result, PmpResult::Deny);
}
#[test]
fn pmp_entry_permission_accessors() {
let entry = PmpEntry { cfg: R | W | X | L, addr: 0 };
assert!(entry.is_readable());
assert!(entry.is_writable());
assert!(entry.is_executable());
assert!(entry.is_locked());
let entry2 = PmpEntry { cfg: R, addr: 0 };
assert!(entry2.is_readable());
assert!(!entry2.is_writable());
assert!(!entry2.is_executable());
assert!(!entry2.is_locked());
}
#[test]
fn pmp_entry_match_mode_accessor() {
let entry_off = PmpEntry { cfg: 0, addr: 0 };
assert_eq!(entry_off.match_mode(), PmpAddrMatch::Off);
let entry_tor = PmpEntry { cfg: A_TOR, addr: 0 };
assert_eq!(entry_tor.match_mode(), PmpAddrMatch::Tor);
let entry_na4 = PmpEntry { cfg: A_NA4, addr: 0 };
assert_eq!(entry_na4.match_mode(), PmpAddrMatch::Na4);
let entry_napot = PmpEntry { cfg: A_NAPOT, addr: 0 };
assert_eq!(entry_napot.match_mode(), PmpAddrMatch::Napot);
}