Skip to content

Commit d9a364b

Browse files
committed
add a way to read/write host shared mem when debugging
Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent a1b0a5d commit d9a364b

File tree

12 files changed

+242
-7
lines changed

12 files changed

+242
-7
lines changed

src/hyperlight_host/src/hypervisor/handlers.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,96 @@ impl MemAccessHandlerCaller for MemAccessHandler {
102102
func()
103103
}
104104
}
105+
106+
/// The trait representing custom logic to handle the case when
107+
/// a Hypervisor's virtual CPU (vCPU) informs Hyperlight a debug memory access
108+
/// has been requested.
109+
pub trait DbgMemAccessHandlerCaller: Send {
110+
/// Function that gets called when a read is requested.
111+
fn read(&mut self, addr: usize, data: &mut [u8]) -> Result<()>;
112+
113+
/// Function that gets called when a write is requested.
114+
fn write(&mut self, addr: usize, data: &[u8]) -> Result<()>;
115+
116+
/// Function that gets called for a request to get guest code offset.
117+
fn get_code_offset(&mut self) -> Result<usize>;
118+
}
119+
120+
/// A convenient type representing a common way `DbgMemAccessHandler` implementations
121+
/// are passed as parameters to functions
122+
///
123+
/// Note: This needs to be wrapped in a Mutex to be able to grab a mutable
124+
/// reference to the underlying data
125+
pub type DbgMemAccessHandlerWrapper = Arc<Mutex<dyn DbgMemAccessHandlerCaller>>;
126+
127+
pub(crate) type DbgReadMemAccessHandlerFunction =
128+
Box<dyn FnMut(usize, &mut [u8]) -> Result<()> + Send>;
129+
pub(crate) type DbgWriteMemAccessHandlerFunction =
130+
Box<dyn FnMut(usize, &[u8]) -> Result<()> + Send>;
131+
pub(crate) type DbgGetCodeAddrHandlerFunction = Box<dyn FnMut() -> Result<usize> + Send>;
132+
133+
/// A `DbgMemAccessHandler` implementation using
134+
/// (`DbgReadMemAccessHandlerFunction`, `DbgWriteMemAccessHandlerFunction `, `DbgGetCodeAddrHandlerFunction `).
135+
///
136+
/// Note: This handler must live for as long as its Sandbox or for
137+
/// static in the case of its C API usage.
138+
pub(crate) struct DbgMemAccessHandler (
139+
Arc<Mutex<DbgReadMemAccessHandlerFunction>>,
140+
Arc<Mutex<DbgWriteMemAccessHandlerFunction>>,
141+
Arc<Mutex<DbgGetCodeAddrHandlerFunction>>,
142+
);
143+
144+
impl
145+
From<(
146+
DbgReadMemAccessHandlerFunction,
147+
DbgWriteMemAccessHandlerFunction,
148+
DbgGetCodeAddrHandlerFunction,
149+
)> for DbgMemAccessHandler
150+
{
151+
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
152+
fn from(
153+
(f1, f2, f3): (
154+
DbgReadMemAccessHandlerFunction,
155+
DbgWriteMemAccessHandlerFunction,
156+
DbgGetCodeAddrHandlerFunction,
157+
),
158+
) -> Self {
159+
Self(
160+
Arc::new(Mutex::new(f1)),
161+
Arc::new(Mutex::new(f2)),
162+
Arc::new(Mutex::new(f3)),
163+
)
164+
}
165+
}
166+
167+
impl DbgMemAccessHandlerCaller for DbgMemAccessHandler {
168+
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
169+
fn read(&mut self, addr: usize, data: &mut [u8]) -> Result<()> {
170+
let mut read_func = self
171+
.0
172+
.try_lock()
173+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
174+
175+
read_func(addr, data)
176+
}
177+
178+
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
179+
fn write(&mut self, addr: usize, data: &[u8]) -> Result<()> {
180+
let mut write_func = self
181+
.1
182+
.try_lock()
183+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
184+
185+
write_func(addr, data)
186+
}
187+
188+
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
189+
fn get_code_offset(&mut self) -> Result<usize> {
190+
let mut get_code_offset_func = self
191+
.2
192+
.try_lock()
193+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
194+
195+
get_code_offset_func()
196+
}
197+
}

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ use mshv_ioctls::{Mshv, VcpuFd, VmFd};
4444
use tracing::{instrument, Span};
4545

4646
use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
47+
#[cfg(gdb)]
48+
use super::handlers::DbgMemAccessHandlerWrapper;
4749
use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
4850
use super::{
4951
Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR,
@@ -203,6 +205,7 @@ impl Hypervisor for HypervLinuxDriver {
203205
outb_hdl: OutBHandlerWrapper,
204206
mem_access_hdl: MemAccessHandlerWrapper,
205207
hv_handler: Option<HypervisorHandler>,
208+
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
206209
) -> Result<()> {
207210
let regs = StandardRegisters {
208211
rip: self.entrypoint,
@@ -224,6 +227,8 @@ impl Hypervisor for HypervLinuxDriver {
224227
hv_handler,
225228
outb_hdl,
226229
mem_access_hdl,
230+
#[cfg(gdb)]
231+
dbg_mem_access_fn,
227232
)?;
228233

229234
// reset RSP to what it was before initialise
@@ -242,6 +247,7 @@ impl Hypervisor for HypervLinuxDriver {
242247
outb_handle_fn: OutBHandlerWrapper,
243248
mem_access_fn: MemAccessHandlerWrapper,
244249
hv_handler: Option<HypervisorHandler>,
250+
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
245251
) -> Result<()> {
246252
// Reset general purpose registers except RSP, then set RIP
247253
let rsp_before = self.vcpu_fd.get_regs()?.rsp;
@@ -268,6 +274,8 @@ impl Hypervisor for HypervLinuxDriver {
268274
hv_handler,
269275
outb_handle_fn,
270276
mem_access_fn,
277+
#[cfg(gdb)]
278+
dbg_mem_access_fn,
271279
)?;
272280

273281
// reset RSP to what it was before function call

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ use windows::Win32::System::Hypervisor::{
2828
};
2929

3030
use super::fpu::{FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
31+
#[cfg(gdb)]
32+
use super::handlers::DbgMemAccessHandlerWrapper;
3133
use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
3234
use super::surrogate_process::SurrogateProcess;
3335
use super::surrogate_process_manager::*;
@@ -305,6 +307,7 @@ impl Hypervisor for HypervWindowsDriver {
305307
outb_hdl: OutBHandlerWrapper,
306308
mem_access_hdl: MemAccessHandlerWrapper,
307309
hv_handler: Option<HypervisorHandler>,
310+
#[cfg(gdb)] dbg_mem_access_hdl: DbgMemAccessHandlerWrapper,
308311
) -> Result<()> {
309312
let regs = WHvGeneralRegisters {
310313
rip: self.entrypoint,
@@ -326,6 +329,8 @@ impl Hypervisor for HypervWindowsDriver {
326329
hv_handler,
327330
outb_hdl,
328331
mem_access_hdl,
332+
#[cfg(gdb)]
333+
dbg_mem_access_hdl,
329334
)?;
330335

331336
// reset RSP to what it was before initialise
@@ -344,6 +349,7 @@ impl Hypervisor for HypervWindowsDriver {
344349
outb_hdl: OutBHandlerWrapper,
345350
mem_access_hdl: MemAccessHandlerWrapper,
346351
hv_handler: Option<HypervisorHandler>,
352+
#[cfg(gdb)] dbg_mem_access_hdl: DbgMemAccessHandlerWrapper,
347353
) -> Result<()> {
348354
// Reset general purpose registers except RSP, then set RIP
349355
let rsp_before = self.processor.get_regs()?.rsp;
@@ -368,6 +374,7 @@ impl Hypervisor for HypervWindowsDriver {
368374
hv_handler,
369375
outb_hdl,
370376
mem_access_hdl,
377+
dbg_mem_access_hdl,
371378
)?;
372379

373380
// reset RSP to what it was before function call

src/hyperlight_host/src/hypervisor/hypervisor_handler.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ use windows::Win32::System::Hypervisor::{WHvCancelRunVirtualProcessor, WHV_PARTI
3737

3838
#[cfg(feature = "function_call_metrics")]
3939
use crate::histogram_vec_observe;
40+
#[cfg(gdb)]
41+
use crate::hypervisor::handlers::DbgMemAccessHandlerWrapper;
4042
use crate::hypervisor::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
4143
#[cfg(target_os = "windows")]
4244
use crate::hypervisor::wrappers::HandleWrapper;
@@ -187,6 +189,8 @@ pub(crate) struct HvHandlerConfig {
187189
pub(crate) outb_handler: OutBHandlerWrapper,
188190
pub(crate) mem_access_handler: MemAccessHandlerWrapper,
189191
pub(crate) max_wait_for_cancellation: Duration,
192+
#[cfg(gdb)]
193+
pub(crate) dbg_mem_access_handler: DbgMemAccessHandlerWrapper,
190194
}
191195

192196
impl HypervisorHandler {
@@ -351,6 +355,8 @@ impl HypervisorHandler {
351355
configuration.outb_handler.clone(),
352356
configuration.mem_access_handler.clone(),
353357
Some(hv_handler_clone.clone()),
358+
#[cfg(gdb)]
359+
configuration.dbg_mem_access_handler.clone(),
354360
);
355361
drop(mem_lock_guard);
356362
drop(evar_lock_guard);
@@ -436,6 +442,8 @@ impl HypervisorHandler {
436442
configuration.outb_handler.clone(),
437443
configuration.mem_access_handler.clone(),
438444
Some(hv_handler_clone.clone()),
445+
#[cfg(gdb)]
446+
configuration.dbg_mem_access_handler.clone(),
439447
);
440448
histogram_vec_observe!(
441449
&GuestFunctionCallDurationMicroseconds,
@@ -451,6 +459,8 @@ impl HypervisorHandler {
451459
configuration.outb_handler.clone(),
452460
configuration.mem_access_handler.clone(),
453461
Some(hv_handler_clone.clone()),
462+
#[cfg(gdb)]
463+
configuration.dbg_mem_access_handler.clone(),
454464
)
455465
};
456466
drop(mem_lock_guard);

src/hyperlight_host/src/hypervisor/inprocess.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
use std::fmt::Debug;
1818
use std::os::raw::c_void;
1919

20+
#[cfg(gdb)]
21+
use super::handlers::DbgMemAccessHandlerWrapper;
2022
use super::{HyperlightExit, Hypervisor};
2123
#[cfg(crashdump)]
2224
use crate::mem::memory_region::MemoryRegion;
@@ -73,6 +75,7 @@ impl<'a> Hypervisor for InprocessDriver<'a> {
7375
_outb_handle_fn: super::handlers::OutBHandlerWrapper,
7476
_mem_access_fn: super::handlers::MemAccessHandlerWrapper,
7577
_hv_handler: Option<super::hypervisor_handler::HypervisorHandler>,
78+
#[cfg(gdb)] _dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
7679
) -> crate::Result<()> {
7780
let entrypoint_fn: extern "win64" fn(u64, u64, u64, u64) =
7881
unsafe { std::mem::transmute(self.args.entrypoint_raw as *const c_void) };
@@ -93,6 +96,7 @@ impl<'a> Hypervisor for InprocessDriver<'a> {
9396
_outb_handle_fn: super::handlers::OutBHandlerWrapper,
9497
_mem_access_fn: super::handlers::MemAccessHandlerWrapper,
9598
_hv_handler: Option<super::hypervisor_handler::HypervisorHandler>,
99+
#[cfg(gdb)] _dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
96100
) -> crate::Result<()> {
97101
let ptr: u64 = dispatch_func_addr.into();
98102
let dispatch_func: extern "win64" fn() =

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ limitations under the License.
1616

1717
use std::convert::TryFrom;
1818
use std::fmt::Debug;
19+
#[cfg(gdb)]
20+
use std::sync::{Arc, Mutex};
1921

2022
use kvm_bindings::{kvm_fpu, kvm_regs, kvm_userspace_memory_region, KVM_MEM_READONLY};
2123
use kvm_ioctls::Cap::UserMemory;
@@ -24,7 +26,9 @@ use tracing::{instrument, Span};
2426

2527
use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
2628
#[cfg(gdb)]
27-
use super::gdb::{create_gdb_thread, DebugMsg, DebugResponse, DebugCommChannel, VcpuStopReason};
29+
use super::gdb::{create_gdb_thread, DebugCommChannel, DebugMsg, DebugResponse, VcpuStopReason};
30+
#[cfg(gdb)]
31+
use super::handlers::DbgMemAccessHandlerWrapper;
2832
use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
2933
use super::{
3034
HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP,
@@ -61,6 +65,7 @@ pub(crate) fn is_hypervisor_present() -> bool {
6165

6266
#[cfg(gdb)]
6367
mod debug {
68+
use std::sync::{Arc, Mutex};
6469
use kvm_bindings::{
6570
kvm_guest_debug, kvm_regs, KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP,
6671
KVM_GUESTDBG_USE_HW_BP, KVM_GUESTDBG_USE_SW_BP,
@@ -69,6 +74,7 @@ mod debug {
6974

7075
use super::KVMDriver;
7176
use crate::hypervisor::gdb::{DebugMsg, DebugResponse, VcpuStopReason, X86_64Regs};
77+
use crate::hypervisor::handlers::DbgMemAccessHandlerCaller;
7278
use crate::{new_error, Result};
7379

7480
/// KVM Debug struct
@@ -336,6 +342,7 @@ mod debug {
336342
pub fn process_dbg_request(
337343
&mut self,
338344
req: DebugMsg,
345+
_dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
339346
) -> Result<DebugResponse> {
340347
match req {
341348
DebugMsg::AddHwBreakpoint(addr) => {
@@ -541,6 +548,7 @@ impl Hypervisor for KVMDriver {
541548
outb_hdl: OutBHandlerWrapper,
542549
mem_access_hdl: MemAccessHandlerWrapper,
543550
hv_handler: Option<HypervisorHandler>,
551+
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
544552
) -> Result<()> {
545553
let regs = kvm_regs {
546554
rip: self.entrypoint,
@@ -561,6 +569,8 @@ impl Hypervisor for KVMDriver {
561569
hv_handler,
562570
outb_hdl,
563571
mem_access_hdl,
572+
#[cfg(gdb)]
573+
dbg_mem_access_fn,
564574
)?;
565575

566576
// reset RSP to what it was before initialise
@@ -578,6 +588,7 @@ impl Hypervisor for KVMDriver {
578588
outb_handle_fn: OutBHandlerWrapper,
579589
mem_access_fn: MemAccessHandlerWrapper,
580590
hv_handler: Option<HypervisorHandler>,
591+
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
581592
) -> Result<()> {
582593
// Reset general purpose registers except RSP, then set RIP
583594
let rsp_before = self.vcpu_fd.get_regs()?.rsp;
@@ -603,6 +614,8 @@ impl Hypervisor for KVMDriver {
603614
hv_handler,
604615
outb_handle_fn,
605616
mem_access_fn,
617+
#[cfg(gdb)]
618+
dbg_mem_access_fn,
606619
)?;
607620

608621
// reset RSP to what it was before function call
@@ -714,6 +727,7 @@ impl Hypervisor for KVMDriver {
714727
#[cfg(gdb)]
715728
fn handle_debug(
716729
&mut self,
730+
dbg_mem_access_fn: Arc<Mutex<dyn super::handlers::DbgMemAccessHandlerCaller>>,
717731
stop_reason: VcpuStopReason,
718732
) -> Result<()> {
719733
self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
@@ -724,7 +738,7 @@ impl Hypervisor for KVMDriver {
724738
// Wait for a message from gdb
725739
let req = self.recv_dbg_msg()?;
726740

727-
let response = self.process_dbg_request(req)?;
741+
let response = self.process_dbg_request(req, dbg_mem_access_fn.clone())?;
728742

729743
// If the command was either step or continue, we need to run the vcpu
730744
let cont = matches!(
@@ -748,6 +762,8 @@ impl Hypervisor for KVMDriver {
748762
mod tests {
749763
use std::sync::{Arc, Mutex};
750764

765+
#[cfg(gdb)]
766+
use crate::hypervisor::handlers::DbgMemAccessHandler;
751767
use crate::hypervisor::handlers::{MemAccessHandler, OutBHandler};
752768
use crate::hypervisor::tests::test_initialise;
753769
use crate::Result;
@@ -767,6 +783,28 @@ mod tests {
767783
let func: Box<dyn FnMut() -> Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) });
768784
Arc::new(Mutex::new(MemAccessHandler::from(func)))
769785
};
770-
test_initialise(outb_handler, mem_access_handler).unwrap();
786+
#[cfg(gdb)]
787+
#[allow(clippy::type_complexity)]
788+
let dbg_mem_access_handler = {
789+
let read: Box<dyn FnMut(usize, &mut [u8]) -> Result<()> + Send> =
790+
Box::new(|_: usize, _: &mut [u8]| -> Result<()> { Ok(()) });
791+
let write: Box<dyn FnMut(usize, &[u8]) -> Result<()> + Send> =
792+
Box::new(|_: usize, _: &[u8]| -> Result<()> { Ok(()) });
793+
let get_code_offset: Box<dyn FnMut() -> Result<usize> + Send> =
794+
Box::new(|| -> Result<usize> { Ok(0) });
795+
Arc::new(Mutex::new(DbgMemAccessHandler::from((
796+
read,
797+
write,
798+
get_code_offset,
799+
))))
800+
};
801+
802+
test_initialise(
803+
outb_handler,
804+
mem_access_handler,
805+
#[cfg(gdb)]
806+
dbg_mem_access_handler,
807+
)
808+
.unwrap();
771809
}
772810
}

0 commit comments

Comments
 (0)