vmi_utils/ptm/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
//! Page table monitoring.
//!
//! Page table monitoring system that tracks changes in page table hierarchies.
//! It provides architecture-independent monitoring of virtual address
//! translations and automatically detects when monitored addresses become
//! available or unavailable in physical memory.
//!
//! The monitor tracks page table entries across the entire hierarchy
//! (e.g., PML4 through PT on AMD64) and generates events when pages are mapped
//! (page-in) or unmapped (page-out). Each monitored address can be associated
//! with a custom tag, allowing users to easily track monitored addresses during
//! debugging.
//!
//! The system is architecture-aware through its `ArchAdapter` trait system,
//! which allows for architecture-specific implementations while maintaining
//! a common interface.
//!
//! The AMD64 implementation serves as a reference, supporting the complete
//! 4-level paging structure.
//!
//! # Events
//!
//! The monitor generates two types of events through [`PageTableMonitorEvent`]:
//! - [`PageTableMonitorEvent::PageIn`]: When a monitored virtual address
//! becomes backed by physical memory.
//! - [`PageTableMonitorEvent::PageOut`]: When a monitored virtual address is
//! no longer backed by physical memory.
//!
//! Each event includes the affected virtual address context, physical address,
//! and associated view.
//!
//! # Limitations
//!
//! The monitor currently does not support monitoring of large pages (e.g., 2MB
//! or 1GB pages on AMD64).
mod arch;
use std::{fmt::Debug, hash::Hash};
use vmi_core::{AddressContext, Pa, VcpuId, View, VmiCore, VmiDriver, VmiError};
use self::arch::{ArchAdapter, PageTableMonitorArchAdapter};
/// Tag Type.
pub trait TagType: Debug + Copy + Eq + Hash {}
impl<T> TagType for T where T: Debug + Copy + Eq + Hash {}
/// Page Entry Update.
///
/// Page entry update that represents a change in a page table entry.
#[derive(Debug, Clone, Copy)]
pub struct PageEntryUpdate {
/// View in which the update occurred.
pub view: View,
/// Virtual address context.
pub ctx: AddressContext,
/// Physical address.
pub pa: Pa,
}
/// Page Table Monitor Event.
///
/// Page table monitor event that represents a change in the page table
/// hierarchy.
#[derive(Debug)]
pub enum PageTableMonitorEvent {
/// Page In.
///
/// This event is generated when a monitored virtual address becomes backed
/// by physical memory.
PageIn(PageEntryUpdate),
/// Page Out.
///
/// This event is generated when a monitored virtual address is no longer
/// backed by physical memory.
PageOut(PageEntryUpdate),
}
/// Page Table Monitor.
pub struct PageTableMonitor<Driver, Tag = &'static str>
where
Driver: VmiDriver,
Driver::Architecture: ArchAdapter<Driver, Tag>,
Tag: TagType,
{
inner: <Driver::Architecture as ArchAdapter<Driver, Tag>>::Impl,
}
impl<Driver, Tag> PageTableMonitor<Driver, Tag>
where
Driver: VmiDriver,
Driver::Architecture: ArchAdapter<Driver, Tag>,
Tag: TagType,
{
#[expect(clippy::new_without_default)]
/// Creates a new page table monitor.
pub fn new() -> Self {
Self {
inner: <Driver::Architecture as ArchAdapter<Driver, Tag>>::Impl::new(),
}
}
/// Returns the number of monitored tables.
pub fn monitored_tables(&self) -> usize {
self.inner.monitored_tables()
}
/// Returns the number of monitored entries.
pub fn monitored_entries(&self) -> usize {
self.inner.monitored_entries()
}
/// Returns the number of paged-in entries.
pub fn paged_in_entries(&self) -> usize {
self.inner.paged_in_entries()
}
/// Dumps the monitor state.
pub fn dump(&self) {
self.inner.dump();
}
/// Monitors a virtual address.
pub fn monitor(
&mut self,
vmi: &VmiCore<Driver>,
ctx: impl Into<AddressContext>,
view: View,
tag: Tag,
) -> Result<(), VmiError> {
self.inner.monitor(vmi, ctx, view, tag)
}
/// Unmonitors a virtual address.
pub fn unmonitor(
&mut self,
vmi: &VmiCore<Driver>,
ctx: impl Into<AddressContext>,
view: View,
) -> Result<(), VmiError> {
self.inner.unmonitor(vmi, ctx, view)
}
/// Unmonitors all virtual addresses.
pub fn unmonitor_all(&mut self, vmi: &VmiCore<Driver>) {
self.inner.unmonitor_all(vmi);
}
/// Unmonitors all virtual addresses associated with a view.
pub fn unmonitor_view(&mut self, vmi: &VmiCore<Driver>, view: View) {
self.inner.unmonitor_view(vmi, view);
}
/// Marks a page table entry as dirty.
pub fn mark_dirty_entry(&mut self, entry_pa: Pa, view: View, vcpu_id: VcpuId) -> bool {
self.inner.mark_dirty_entry(entry_pa, view, vcpu_id)
}
/// Processes dirty entries.
pub fn process_dirty_entries(
&mut self,
vmi: &VmiCore<Driver>,
vcpu_id: VcpuId,
) -> Result<Vec<PageTableMonitorEvent>, VmiError> {
self.inner.process_dirty_entries(vmi, vcpu_id)
}
}