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 self.core.reset_state()?;
188 }
189
190 Ok(result)
191 }
192}
193
194impl<'a, Os> VmiSession<'a, Os>
199where
200 Os: VmiOs,
201 Os::Driver: VmiQueryRegisters + VmiVmControl,
202{
203 pub fn pause_guard(&self) -> Result<VmiSessionPauseGuard<'_, Os>, VmiError> {
206 VmiSessionPauseGuard::new(self)
207 }
208}
209
210pub struct VmiSessionPauseGuard<'a, Os>
220where
221 Os: VmiOs,
222 Os::Driver: VmiQueryRegisters + VmiVmControl,
223{
224 session: &'a VmiSession<'a, Os>,
225 registers: <<Os::Driver as VmiDriver>::Architecture as Architecture>::Registers,
226}
227
228impl<'a, Os> VmiSessionPauseGuard<'a, Os>
229where
230 Os: VmiOs,
231 Os::Driver: VmiQueryRegisters + VmiVmControl,
232{
233 pub fn new(session: &'a VmiSession<'a, Os>) -> Result<Self, VmiError> {
235 session.driver().pause()?;
236
237 let registers = match session.registers(VcpuId(0)) {
238 Ok(registers) => registers,
239 Err(err) => {
240 if let Err(resume_err) = session.driver().resume() {
241 tracing::error!(
242 err = %resume_err,
243 "failed to resume after register-query failure"
244 );
245 }
246 return Err(err);
247 }
248 };
249
250 Ok(Self { session, registers })
251 }
252
253 pub fn session(&self) -> &VmiSession<'a, Os> {
255 self.session
256 }
257
258 pub fn registers(&self) -> &<Os::Architecture as Architecture>::Registers {
261 &self.registers
262 }
263
264 pub fn state(&self) -> VmiState<'_, Os> {
267 VmiState::new(self.session, &self.registers)
268 }
269}
270
271impl<Os> Drop for VmiSessionPauseGuard<'_, Os>
272where
273 Os: VmiOs,
274 Os::Driver: VmiQueryRegisters + VmiVmControl,
275{
276 fn drop(&mut self) {
277 if let Err(err) = self.session.driver().resume() {
278 tracing::error!(%err, "failed to resume the virtual machine");
279 }
280 }
281}