hyperlight_host/hypervisor/
handlers.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
/*
Copyright 2024 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use std::sync::{Arc, Mutex};

use tracing::{instrument, Span};

use crate::{new_error, Result};

/// The trait representing custom logic to handle the case when
/// a Hypervisor's virtual CPU (vCPU) informs Hyperlight the guest
/// has initiated an outb operation.
pub trait OutBHandlerCaller: Sync + Send {
    /// Function that gets called when an outb operation has occurred.
    fn call(&mut self, port: u16, payload: u64) -> Result<()>;
}

/// A convenient type representing a common way `OutBHandler` implementations
/// are passed as parameters to functions
///
/// Note: This needs to be wrapped in a Mutex to be able to grab a mutable
/// reference to the underlying data (i.e., handle_outb in `Sandbox` takes
/// a &mut self).
pub type OutBHandlerWrapper = Arc<Mutex<dyn OutBHandlerCaller>>;

pub(crate) type OutBHandlerFunction = Box<dyn FnMut(u16, u64) -> Result<()> + Send>;

/// A `OutBHandler` implementation using a `OutBHandlerFunction`
///
/// Note: This handler must live no longer than the `Sandbox` to which it belongs
pub(crate) struct OutBHandler(Arc<Mutex<OutBHandlerFunction>>);

impl From<OutBHandlerFunction> for OutBHandler {
    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
    fn from(func: OutBHandlerFunction) -> Self {
        Self(Arc::new(Mutex::new(func)))
    }
}

impl OutBHandlerCaller for OutBHandler {
    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
    fn call(&mut self, port: u16, payload: u64) -> Result<()> {
        let mut func = self
            .0
            .try_lock()
            .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
        func(port, payload)
    }
}

/// The trait representing custom logic to handle the case when
/// a Hypervisor's virtual CPU (vCPU) informs Hyperlight a memory access
/// outside the designated address space has occurred.
pub trait MemAccessHandlerCaller: Send {
    /// Function that gets called when unexpected memory access has occurred.
    fn call(&mut self) -> Result<()>;
}

/// A convenient type representing a common way `MemAccessHandler` implementations
/// are passed as parameters to functions
///
/// Note: This needs to be wrapped in a Mutex to be able to grab a mutable
/// reference to the underlying data (i.e., handle_mmio_exit in `Sandbox` takes
/// a &mut self).
pub type MemAccessHandlerWrapper = Arc<Mutex<dyn MemAccessHandlerCaller>>;

pub(crate) type MemAccessHandlerFunction = Box<dyn FnMut() -> Result<()> + Send>;

/// A `MemAccessHandler` implementation using `MemAccessHandlerFunction`.
///
/// Note: This handler must live for as long as its Sandbox or for
/// static in the case of its C API usage.
pub(crate) struct MemAccessHandler(Arc<Mutex<MemAccessHandlerFunction>>);

impl From<MemAccessHandlerFunction> for MemAccessHandler {
    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
    fn from(func: MemAccessHandlerFunction) -> Self {
        Self(Arc::new(Mutex::new(func)))
    }
}

impl MemAccessHandlerCaller for MemAccessHandler {
    #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
    fn call(&mut self) -> Result<()> {
        let mut func = self
            .0
            .try_lock()
            .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
        func()
    }
}