vmi_utils/ptm/
mod.rs

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