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)
    }
}