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}