const ATC_ENTRIES: usize = 64;
#[derive(Debug, Clone, Copy, Default)]
struct AtcEntry {
valid: bool,
tag: u32,
phys_page: u32,
write_protected: bool,
supervisor_only: bool,
}
#[derive(Debug, Clone)]
pub struct Atc {
entries: [AtcEntry; ATC_ENTRIES],
}
impl Default for Atc {
fn default() -> Self {
Self {
entries: [AtcEntry::default(); ATC_ENTRIES],
}
}
}
impl Atc {
#[inline]
fn tag(page_frame: u32, supervisor: bool) -> u32 {
(page_frame << 1) | supervisor as u32
}
#[inline]
fn index(page_frame: u32) -> usize {
(page_frame as usize) & (ATC_ENTRIES - 1)
}
#[inline]
pub fn lookup(&self, page_frame: u32, supervisor: bool, write: bool) -> Option<u32> {
let e = &self.entries[Self::index(page_frame)];
if !e.valid || e.tag != Self::tag(page_frame, supervisor) {
return None;
}
if (write && e.write_protected) || (!supervisor && e.supervisor_only) {
return None;
}
Some(e.phys_page)
}
#[inline]
pub fn insert(
&mut self,
page_frame: u32,
supervisor: bool,
phys_page: u32,
write_protected: bool,
supervisor_only: bool,
) {
self.entries[Self::index(page_frame)] = AtcEntry {
valid: true,
tag: Self::tag(page_frame, supervisor),
phys_page,
write_protected,
supervisor_only,
};
}
pub fn flush_all(&mut self) {
for e in &mut self.entries {
e.valid = false;
}
}
pub fn flush_page(&mut self, page_frame: u32) {
let e = &mut self.entries[Self::index(page_frame)];
if e.tag >> 1 == page_frame {
e.valid = false;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hit_after_insert_miss_after_flush() {
let mut atc = Atc::default();
assert_eq!(atc.lookup(0x10, false, false), None);
atc.insert(0x10, false, 0x8000, false, false);
assert_eq!(atc.lookup(0x10, false, false), Some(0x8000));
assert_eq!(atc.lookup(0x10, true, false), None);
atc.flush_all();
assert_eq!(atc.lookup(0x10, false, false), None);
}
#[test]
fn flush_page_drops_only_that_frame() {
let mut atc = Atc::default();
atc.insert(0x10, false, 0x1000, false, false);
atc.flush_page(0x11); assert_eq!(atc.lookup(0x10, false, false), Some(0x1000));
atc.flush_page(0x10);
assert_eq!(atc.lookup(0x10, false, false), None);
}
#[test]
fn permission_violation_does_not_hit() {
let mut atc = Atc::default();
atc.insert(0x10, true, 0x5000, true, true);
assert_eq!(atc.lookup(0x10, true, false), Some(0x5000));
assert_eq!(atc.lookup(0x10, true, true), None);
}
}