1use std::{io::ErrorKind, time::Duration};
2
3use super::{VmiState, context::VmiContext};
4use crate::{
5 Architecture, VcpuId, VmiCore, VmiError, VmiHandler,
6 driver::{VmiDriver, VmiEventControl, VmiQueryRegisters, VmiVmControl},
7 os::{NoOS, VmiOs},
8};
9
10pub struct VmiSession<'a, Os>
16where
17 Os: VmiOs,
18{
19 core: &'a VmiCore<Os::Driver>,
21
22 os: &'a Os,
24}
25
26impl<Os> Clone for VmiSession<'_, Os>
27where
28 Os: VmiOs,
29{
30 fn clone(&self) -> Self {
31 *self
32 }
33}
34
35impl<Os> Copy for VmiSession<'_, Os> where Os: VmiOs {}
36
37impl<Os> std::ops::Deref for VmiSession<'_, Os>
38where
39 Os: VmiOs,
40{
41 type Target = VmiCore<Os::Driver>;
42
43 fn deref(&self) -> &Self::Target {
44 self.core
45 }
46}
47
48impl<'a, Os> VmiSession<'a, Os>
49where
50 Os: VmiOs,
51{
52 pub fn new(core: &'a VmiCore<Os::Driver>, os: &'a Os) -> Self {
54 Self { core, os }
55 }
56
57 pub fn with_registers(
59 &'a self,
60 registers: &'a <Os::Architecture as Architecture>::Registers,
61 ) -> VmiState<'a, Os> {
62 VmiState::new(self, registers)
63 }
64
65 pub fn without_os(&self) -> VmiSession<'a, NoOS<Os::Driver>> {
67 VmiSession {
68 core: self.core,
69 os: const { &NoOS(std::marker::PhantomData) },
70 }
71 }
72
73 pub fn core(&self) -> &'a VmiCore<Os::Driver> {
75 self.core
76 }
77
78 pub fn underlying_os(&self) -> &'a Os {
80 self.os
81 }
82}
83
84impl<'a, Os> VmiSession<'a, Os>
89where
90 Os: VmiOs,
91 Os::Driver: VmiEventControl,
92{
93 pub fn wait_for_event(
99 &self,
100 timeout: Duration,
101 handler: &mut impl VmiHandler<Os>,
102 ) -> Result<(), VmiError> {
103 self.core.wait_for_event(timeout, |event| {
104 let state = VmiState::new(self, event.registers());
105 handler.handle_event(VmiContext::new(&state, event))
106 })
107 }
108}
109
110impl<'a, Os> VmiSession<'a, Os>
115where
116 Os: VmiOs,
117 Os::Driver: VmiEventControl + VmiVmControl,
118{
119 pub fn handle<Handler>(
122 &self,
123 handler_factory: impl FnOnce(&VmiSession<Os>) -> Result<Handler, VmiError>,
124 ) -> Result<Option<Handler::Output>, VmiError>
125 where
126 Handler: VmiHandler<Os>,
127 {
128 self.handle_with_timeout(Duration::from_millis(5000), handler_factory)
129 }
130
131 pub fn handle_with_timeout<Handler>(
134 &self,
135 timeout: Duration,
136 handler_factory: impl FnOnce(&VmiSession<Os>) -> Result<Handler, VmiError>,
137 ) -> Result<Option<Handler::Output>, VmiError>
138 where
139 Handler: VmiHandler<Os>,
140 {
141 let mut result;
142 let mut handler = handler_factory(self)?;
143
144 loop {
145 result = handler.poll();
146
147 if result.is_some() {
148 break;
149 }
150
151 match self.wait_for_event(timeout, &mut handler) {
152 Ok(_) => {}
153 Err(VmiError::Timeout) => {
154 tracing::trace!("timeout");
155 handler.handle_timeout(self);
156 }
157 Err(VmiError::Io(err)) if err.kind() == ErrorKind::Interrupted => {
158 tracing::trace!("interrupted");
159 handler.handle_interrupted(self);
160 break;
161 }
162 Err(err) => return Err(err),
163 }
164 }
165
166 tracing::trace!("disabling monitor");
167 handler.cleanup(self);
168 self.core.reset_state()?;
169 tracing::trace!(pending_events = self.events_pending());
170
171 let _pause_guard = self.core().pause_guard()?;
172 if self.events_pending() > 0 {
173 match self.wait_for_event(Duration::from_millis(0), &mut handler) {
174 Err(VmiError::Timeout) => {
175 tracing::trace!("timeout");
176 }
177 Err(err) => return Err(err),
178 Ok(_) => {}
179 }
180 }
181
182 Ok(result)
183 }
184}
185
186impl<'a, Os> VmiSession<'a, Os>
191where
192 Os: VmiOs,
193 Os::Driver: VmiQueryRegisters + VmiVmControl,
194{
195 pub fn pause_guard(&self) -> Result<VmiSessionPauseGuard<'_, Os>, VmiError> {
198 VmiSessionPauseGuard::new(self)
199 }
200}
201
202pub struct VmiSessionPauseGuard<'a, Os>
212where
213 Os: VmiOs,
214 Os::Driver: VmiQueryRegisters + VmiVmControl,
215{
216 session: &'a VmiSession<'a, Os>,
217 registers: <<Os::Driver as VmiDriver>::Architecture as Architecture>::Registers,
218}
219
220impl<'a, Os> VmiSessionPauseGuard<'a, Os>
221where
222 Os: VmiOs,
223 Os::Driver: VmiQueryRegisters + VmiVmControl,
224{
225 pub fn new(session: &'a VmiSession<'a, Os>) -> Result<Self, VmiError> {
227 session.driver().pause()?;
228
229 let registers = match session.registers(VcpuId(0)) {
230 Ok(registers) => registers,
231 Err(err) => {
232 if let Err(resume_err) = session.driver().resume() {
233 tracing::error!(
234 err = %resume_err,
235 "failed to resume after register-query failure"
236 );
237 }
238 return Err(err);
239 }
240 };
241
242 Ok(Self { session, registers })
243 }
244
245 pub fn session(&self) -> &VmiSession<'a, Os> {
247 self.session
248 }
249
250 pub fn registers(&self) -> &<Os::Architecture as Architecture>::Registers {
253 &self.registers
254 }
255
256 pub fn state(&self) -> VmiState<'_, Os> {
259 VmiState::new(self.session, &self.registers)
260 }
261}
262
263impl<Os> Drop for VmiSessionPauseGuard<'_, Os>
264where
265 Os: VmiOs,
266 Os::Driver: VmiQueryRegisters + VmiVmControl,
267{
268 fn drop(&mut self) {
269 if let Err(err) = self.session.driver().resume() {
270 tracing::error!(%err, "failed to resume the virtual machine");
271 }
272 }
273}