1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4};
5use core::{error::Error, fmt};
6
7use crate::seal;
8
9#[derive(Debug)]
12pub enum VcpuError {
13 Unsupported,
14 InvalidId,
15 BadArchitecture,
16 #[cfg(feature = "std")]
17 Io(std::io::Error),
18}
19
20pub type VcpuResult<T> = Result<T, VcpuError>;
21
22impl fmt::Display for VcpuError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::Unsupported => f.write_str("unsupported operation"),
26 Self::InvalidId => f.write_str("invalid vCPU ID"),
27 Self::BadArchitecture => f.write_str("wrong architecture"),
28 #[cfg(feature = "std")]
29 Self::Io(err) => err.fmt(f),
30 }
31 }
32}
33
34impl Error for VcpuError {
35 #[inline]
36 fn source(&self) -> Option<&(dyn Error + 'static)> {
37 match self {
38 #[cfg(feature = "std")]
39 Self::Io(err) => err.source(),
40 _ => None,
41 }
42 }
43}
44
45#[derive(Debug)]
46#[non_exhaustive]
47pub enum MemoryAccessError {
48 OutOfBounds,
49 Unsupported,
50 #[cfg(feature = "std")]
51 Io(std::io::Error),
52}
53
54impl fmt::Display for MemoryAccessError {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 match self {
57 Self::OutOfBounds => f.write_str("out of bounds memory access"),
58 Self::Unsupported => f.write_str("unsupported operation"),
59 #[cfg(feature = "std")]
60 Self::Io(err) => err.fmt(f),
61 }
62 }
63}
64
65impl Error for MemoryAccessError {
66 fn source(&self) -> Option<&(dyn Error + 'static)> {
67 match self {
68 #[cfg(feature = "std")]
69 Self::Io(err) => err.source(),
70 _ => None,
71 }
72 }
73}
74
75#[cfg(feature = "std")]
76impl From<std::io::Error> for MemoryAccessError {
77 #[cold]
78 #[inline]
79 fn from(error: std::io::Error) -> Self {
80 Self::Io(error)
81 }
82}
83
84#[cfg(feature = "std")]
85impl From<MemoryAccessError> for std::io::Error {
86 fn from(error: MemoryAccessError) -> Self {
87 match error {
88 MemoryAccessError::OutOfBounds => std::io::Error::new(
89 std::io::ErrorKind::UnexpectedEof,
90 "out of bounds memory access",
91 ),
92 MemoryAccessError::Unsupported => std::io::Error::from(std::io::ErrorKind::Unsupported),
93 MemoryAccessError::Io(error) => error,
94 }
95 }
96}
97
98pub type MemoryAccessResult<T> = Result<T, MemoryAccessError>;
99
100#[derive(Debug)]
101pub enum TranslationError {
102 Memory(MemoryAccessError),
103 Invalid(u64),
104}
105
106pub type TranslationResult<T> = Result<T, TranslationError>;
107
108impl From<MemoryAccessError> for TranslationError {
109 fn from(err: MemoryAccessError) -> Self {
110 Self::Memory(err)
111 }
112}
113
114impl From<TranslationError> for VmError {
115 fn from(err: TranslationError) -> Self {
116 match err {
117 TranslationError::Memory(err) => err.into(),
118 TranslationError::Invalid(entry) => VmError::from_repr(Repr::InvalidPage(entry)),
119 }
120 }
121}
122
123impl fmt::Display for TranslationError {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match self {
126 TranslationError::Memory(err) => err.fmt(f),
127 TranslationError::Invalid(_) => f.write_str("invalid MMU entry"),
128 }
129 }
130}
131
132impl Error for TranslationError {
133 fn source(&self) -> Option<&(dyn Error + 'static)> {
134 match self {
135 TranslationError::Memory(err) => err.source(),
136 TranslationError::Invalid(_) => None,
137 }
138 }
139}
140
141#[derive(Debug)]
142enum Repr {
143 Memory(MemoryAccessError),
144 Vcpu(VcpuError),
145 #[allow(dead_code)]
146 InvalidPage(u64),
147
148 UnsupportedArchitecture,
149 Unsupported,
150 Unimplemented,
151
152 MissingModule(Box<str>),
153 MissingSymbol(Box<str>),
154 MissingField(Box<str>, Box<str>),
155
156 NullPtr,
157
158 #[cfg(feature = "std")]
159 Io(std::io::Error),
160 Other(Box<dyn Error + Send + Sync>),
161 Context(Box<str>, Option<Box<dyn Error + Send + Sync>>),
162}
163
164#[derive(Debug)]
165#[repr(transparent)]
166pub struct VmError {
167 repr: Box<Repr>,
168}
169
170pub type VmResult<T> = Result<T, VmError>;
171
172impl VmError {
173 #[inline]
174 fn from_repr(repr: Repr) -> Self {
175 Self {
176 repr: Box::new(repr),
177 }
178 }
179
180 #[cold]
181 pub fn missing_module(sym: &str) -> Self {
182 Self::from_repr(Repr::MissingModule(sym.into()))
183 }
184
185 #[cold]
186 pub fn missing_symbol(sym: &str) -> Self {
187 Self::from_repr(Repr::MissingSymbol(sym.into()))
188 }
189
190 #[cold]
191 pub fn missing_field(field: &str, typ: &str) -> Self {
192 Self::from_repr(Repr::MissingField(field.into(), typ.into()))
193 }
194
195 #[cold]
196 pub fn deref_null_ptr() -> Self {
197 Self::from_repr(Repr::NullPtr)
198 }
199
200 #[cold]
201 pub fn new(err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
202 Self::from_repr(Repr::Other(err.into()))
203 }
204
205 #[cold]
206 pub fn with_context(msg: impl ToString, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
207 Self::from_repr(Repr::Context(msg.to_string().into(), Some(err.into())))
208 }
209
210 #[cold]
211 pub fn unsupported_architecture() -> Self {
212 Self::from_repr(Repr::UnsupportedArchitecture)
213 }
214
215 #[cold]
216 pub fn unsupported() -> Self {
217 Self::from_repr(Repr::Unsupported)
218 }
219
220 #[cold]
221 pub fn unimplemented() -> Self {
222 Self::from_repr(Repr::Unimplemented)
223 }
224
225 pub fn print_backtrace(&self) -> String {
226 let mut trace = String::new();
227 fmt::write(&mut trace, format_args!("{self:#}")).unwrap();
228 trace
229 }
230}
231
232impl fmt::Display for Repr {
233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234 match self {
235 Repr::Memory(_) => f.write_str("failed to access physical memory"),
236 Repr::Vcpu(_) => f.write_str("failed to access registers"),
237 Repr::InvalidPage(_) => f.write_str("encountered invalid page"),
238 Repr::UnsupportedArchitecture => {
239 f.write_str("operation unsupported by the architecture")
240 }
241 Repr::Unsupported => f.write_str("unsupported operation"),
242 Repr::Unimplemented => f.write_str("unimplemented"),
243 Repr::MissingModule(name) => {
244 f.write_fmt(format_args!("missing required module \"{name}\""))
245 }
246 Repr::MissingSymbol(sym) => {
247 f.write_fmt(format_args!("missing required symbol \"{sym}\""))
248 }
249 Repr::MissingField(field, typ) => f.write_fmt(format_args!(
250 "missing required field \"{field}\" in type \"{typ}\""
251 )),
252 Repr::NullPtr => f.write_str("attempted to deref NULL pointer"),
253 #[cfg(feature = "std")]
254 Repr::Io(_) => f.write_str("I/O error"),
255 Repr::Context(msg, _) => f.write_str(msg),
256 Repr::Other(err) => err.fmt(f),
257 }
258 }
259}
260
261impl fmt::Display for VmError {
262 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263 if f.alternate() {
264 f.write_fmt(format_args!("{}", self.repr))?;
265
266 let mut current = self.source();
267
268 if current.is_some() {
269 f.write_str("\n\nCaused by:")?;
270 }
271
272 while let Some(cause) = current {
273 f.write_fmt(format_args!("\n {cause}"))?;
274 current = cause.source();
275 }
276
277 Ok(())
278 } else {
279 self.repr.fmt(f)
280 }
281 }
282}
283
284impl Error for VmError {
285 fn source(&self) -> Option<&(dyn Error + 'static)> {
286 match &*self.repr {
287 Repr::Memory(err) => Some(err),
288 Repr::Vcpu(err) => Some(err),
289 #[cfg(feature = "std")]
290 Repr::Io(err) => Some(err),
291 Repr::Context(_, err) => Some(&**err.as_ref()?),
292 Repr::Other(err) => err.source(),
293 _ => None,
294 }
295 }
296}
297
298impl From<&str> for VmError {
299 #[cold]
300 fn from(msg: &str) -> Self {
301 Self::from_repr(Repr::Context(msg.into(), None))
302 }
303}
304
305impl From<String> for VmError {
306 #[cold]
307 fn from(msg: String) -> Self {
308 Self::from_repr(Repr::Context(msg.into(), None))
309 }
310}
311
312impl From<MemoryAccessError> for VmError {
313 #[cold]
314 fn from(err: MemoryAccessError) -> Self {
315 Self::from_repr(Repr::Memory(err))
316 }
317}
318
319impl From<VcpuError> for VmError {
320 #[cold]
321 fn from(err: VcpuError) -> Self {
322 Self::from_repr(Repr::Vcpu(err))
323 }
324}
325
326impl From<core::str::Utf8Error> for VmError {
327 #[cold]
328 fn from(error: core::str::Utf8Error) -> Self {
329 Self::from(error.to_string())
330 }
331}
332
333#[cfg(feature = "std")]
334impl From<std::io::Error> for VmError {
335 #[cold]
336 fn from(error: std::io::Error) -> Self {
337 Self::from_repr(Repr::Io(error))
338 }
339}
340
341pub trait TranslationResultExt<T>: seal::Sealed {
342 fn maybe_invalid(self) -> MemoryAccessResult<Option<T>>;
343}
344
345impl<T> TranslationResultExt<T> for TranslationResult<T> {
346 fn maybe_invalid(self) -> MemoryAccessResult<Option<T>> {
347 match self {
348 Ok(x) => Ok(Some(x)),
349 Err(TranslationError::Invalid(_)) => Ok(None),
350 Err(TranslationError::Memory(err)) => Err(err),
351 }
352 }
353}
354
355pub trait ResultExt<T>: seal::Sealed {
356 fn context(self, msg: impl ToString) -> VmResult<T>;
357
358 fn with_context<F, S>(self, msg: F) -> VmResult<T>
359 where
360 F: FnOnce() -> S,
361 S: ToString;
362}
363
364impl<T, E> ResultExt<T> for Result<T, E>
365where
366 E: Into<Box<dyn Error + Send + Sync>>,
367{
368 fn context(self, msg: impl ToString) -> VmResult<T> {
369 self.map_err(|err| VmError::with_context(msg.to_string(), err))
370 }
371
372 fn with_context<F, S>(self, msg: F) -> VmResult<T>
373 where
374 F: FnOnce() -> S,
375 S: ToString,
376 {
377 self.map_err(|err| VmError::with_context(msg().to_string(), err))
378 }
379}
380
381impl<T> ResultExt<T> for Option<T> {
382 fn context(self, msg: impl ToString) -> VmResult<T> {
383 self.ok_or_else(|| VmError::new(msg.to_string()))
384 }
385
386 fn with_context<F, S>(self, msg: F) -> VmResult<T>
387 where
388 F: FnOnce() -> S,
389 S: ToString,
390 {
391 self.ok_or_else(|| VmError::new(msg().to_string()))
392 }
393}