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
use RefCell;
use IndexSet;
use crate::;
/// Batches the page faults raised by a sequence of memory reads so they can be
/// injected together, while skipping pages already known to be unserviceable.
///
/// A read that touches a non-resident page returns [`VmiError::Translation`].
/// Instead of aborting an operation on the first such fault, the prober lets
/// you run a sequence of reads: each probed read that faults yields `Ok(None)`
/// and its faulting address is recorded, unless that address is in the
/// `suppressed` set, in which case it is ignored.
///
/// # Protocol
///
/// 1. Construct a prober with [`new`], passing the set of pages already known
/// to be unserviceable (the `suppressed` set).
/// 2. Run one or more reads through [`probe`] or the [`vmi_probe!`] macro. Each
/// returns `Ok(Some(value))` when the read succeeded, or `Ok(None)` when it
/// faulted (the fault is recorded unless it is suppressed).
/// 3. Call [`error_for_page_faults`]. It returns `Err(VmiError::Translation)`
/// carrying every recorded, non-suppressed fault, or `Ok(())` if there were
/// none. Propagate that error to an event loop that injects page faults: for
/// example, returning it from a `vmi-utils` reactor handler makes the
/// reactor inject the fault so the guest pages the memory in, after which
/// the operation is retried.
/// 4. Faults that turn out to be permanently unserviceable are added to the
/// `suppressed` set by the caller (for instance from a `KiDispatchException`
/// handler), so a later attempt skips them instead of re-injecting forever.
///
/// [`new`]: Self::new
/// [`probe`]: Self::probe
/// [`error_for_page_faults`]: Self::error_for_page_faults
/// [`vmi_probe!`]: crate::vmi_probe
///
/// # Examples
///
/// ```ignore
/// // `suppressed` holds pages a previous round found unserviceable.
/// let prober = VmiProber::new(&suppressed);
///
/// // Each probed read is `Ok(Some(_))` if resident, `Ok(None)` if it faulted.
/// let header = vmi_probe!(prober, vmi.read_struct::<Header>(address))?;
/// let name = vmi_probe!(prober, vmi.read_string(name_address))?;
///
/// // Surface the accumulated, non-suppressed faults. Returning this error from
/// // a reactor handler injects the fault and retries the operation.
/// prober.error_for_page_faults()?;
///
/// // No faults pending: `header` and `name` are `Some` (or `None` for a
/// // suppressed page) and safe to use.
/// ```
/// Probes a single read expression through a [`VmiProber`], returning
/// `Ok(None)` if it page-faults.
///
/// Shorthand for [`VmiProber::check_result`] over the expression's result.
///
/// [`VmiProber`]: crate::VmiProber
/// [`VmiProber::check_result`]: crate::VmiProber::check_result