Skip to content

Commit 471c325

Browse files
committed
refactor: move memory manager and host functions out of HyperlightVm
Remove MemoryManager and FunctionRegistry fields from HyperlightVm, passing them as parameters to run() and dispatch_call_from_host() instead. This eliminates duplicate ownership, simplifies the ownership model and gets rid of a complete category of possible errors. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 955bd4e commit 471c325

File tree

8 files changed

+68
-137
lines changed

8 files changed

+68
-137
lines changed

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 37 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ pub(crate) struct HyperlightVm {
7676
entrypoint: u64,
7777
orig_rsp: GuestPtr,
7878
interrupt_handle: Arc<dyn InterruptHandleImpl>,
79-
mem_mgr: Option<SandboxMemoryManager<HostSharedMemory>>,
80-
host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
8179

8280
sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
8381
mmap_regions: Vec<(u32, MemoryRegion)>, // Later mapped regions (slot number, region)
@@ -193,8 +191,6 @@ impl HyperlightVm {
193191
orig_rsp: rsp_gp,
194192
interrupt_handle,
195193
page_size: 0, // Will be set in `initialise`
196-
mem_mgr: None,
197-
host_funcs: None,
198194

199195
next_slot: mem_regions.len() as u32,
200196
sandbox_regions: mem_regions,
@@ -229,13 +225,11 @@ impl HyperlightVm {
229225
peb_addr: RawPtr,
230226
seed: u64,
231227
page_size: u32,
232-
mem_mgr: SandboxMemoryManager<HostSharedMemory>,
228+
mem_mgr: &mut SandboxMemoryManager<HostSharedMemory>,
233229
host_funcs: Arc<Mutex<FunctionRegistry>>,
234230
max_guest_log_level: Option<LevelFilter>,
235231
#[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<SandboxMemoryManager<HostSharedMemory>>>,
236232
) -> Result<()> {
237-
self.mem_mgr = Some(mem_mgr);
238-
self.host_funcs = Some(host_funcs);
239233
self.page_size = page_size as usize;
240234

241235
let max_guest_log_level: u64 = match max_guest_log_level {
@@ -259,6 +253,8 @@ impl HyperlightVm {
259253
self.vm.set_regs(&regs)?;
260254

261255
self.run(
256+
mem_mgr,
257+
host_funcs,
262258
#[cfg(gdb)]
263259
dbg_mem_access_fn,
264260
)?;
@@ -269,6 +265,8 @@ impl HyperlightVm {
269265
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
270266
pub(crate) fn dispatch_call_from_host(
271267
&mut self,
268+
mem_mgr: &mut SandboxMemoryManager<HostSharedMemory>,
269+
host_funcs: Arc<Mutex<FunctionRegistry>>,
272270
dispatch_func_addr: RawPtr,
273271
#[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<SandboxMemoryManager<HostSharedMemory>>>,
274272
) -> Result<()> {
@@ -285,6 +283,8 @@ impl HyperlightVm {
285283
self.vm.set_fpu(&CommonFpu::default())?;
286284

287285
self.run(
286+
mem_mgr,
287+
host_funcs,
288288
#[cfg(gdb)]
289289
dbg_mem_access_fn,
290290
)?;
@@ -325,61 +325,10 @@ impl HyperlightVm {
325325
&self.mmap_regions
326326
}
327327

328-
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
329-
fn handle_io(&mut self, port: u16, data: Vec<u8>) -> Result<()> {
330-
if data.is_empty() {
331-
log_then_return!("no data was given in IO interrupt");
332-
}
333-
334-
#[allow(clippy::get_first)]
335-
let val = u32::from_le_bytes([
336-
data.get(0).copied().unwrap_or(0),
337-
data.get(1).copied().unwrap_or(0),
338-
data.get(2).copied().unwrap_or(0),
339-
data.get(3).copied().unwrap_or(0),
340-
]);
341-
342-
#[cfg(feature = "mem_profile")]
343-
{
344-
// We need to handle the borrow checker issue where we need both:
345-
// - &mut MemMgrWrapper (from self.mem_mgr.as_mut())
346-
// - &mut dyn Hypervisor (from self)
347-
// We'll use a temporary approach to extract the mem_mgr temporarily
348-
let mem_mgr_option = self.mem_mgr.take();
349-
let mut mem_mgr =
350-
mem_mgr_option.ok_or_else(|| new_error!("mem_mgr not initialized"))?;
351-
let host_funcs = self
352-
.host_funcs
353-
.as_ref()
354-
.ok_or_else(|| new_error!("host_funcs not initialized"))?
355-
.clone();
356-
357-
handle_outb(&mut mem_mgr, host_funcs, port, val, self)?;
358-
359-
// Put the mem_mgr back
360-
self.mem_mgr = Some(mem_mgr);
361-
}
362-
363-
#[cfg(not(feature = "mem_profile"))]
364-
{
365-
let mem_mgr = self
366-
.mem_mgr
367-
.as_mut()
368-
.ok_or_else(|| new_error!("mem_mgr not initialized"))?;
369-
let host_funcs = self
370-
.host_funcs
371-
.as_ref()
372-
.ok_or_else(|| new_error!("host_funcs not initialized"))?
373-
.clone();
374-
375-
handle_outb(mem_mgr, host_funcs, port, val)?;
376-
}
377-
378-
Ok(())
379-
}
380-
381328
fn run(
382329
&mut self,
330+
mem_mgr: &mut SandboxMemoryManager<HostSharedMemory>,
331+
host_funcs: Arc<Mutex<FunctionRegistry>>,
383332
#[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<SandboxMemoryManager<HostSharedMemory>>>,
384333
) -> Result<()> {
385334
// ===== KILL() TIMING POINT 1: Between guest function calls =====
@@ -425,7 +374,7 @@ impl HyperlightVm {
425374

426375
// Handle the guest trace data if any
427376
#[cfg(feature = "trace_guest")]
428-
if let Err(e) = self.handle_trace(&mut tc) {
377+
if let Err(e) = tc.handle_trace(&self.vm.regs()?, mem_mgr) {
429378
// If no trace data is available, we just log a message and continue
430379
// Is this the right thing to do?
431380
log::debug!("Error handling guest trace: {:?}", e);
@@ -469,7 +418,28 @@ impl HyperlightVm {
469418
Ok(VmExit::Halt()) => {
470419
break Ok(());
471420
}
472-
Ok(VmExit::IoOut(port, data)) => self.handle_io(port, data)?,
421+
Ok(VmExit::IoOut(port, data)) => {
422+
if data.is_empty() {
423+
log_then_return!("no data was given in IO interrupt");
424+
}
425+
426+
#[allow(clippy::get_first)]
427+
let val: u32 = u32::from_le_bytes([
428+
data.get(0).copied().unwrap_or(0),
429+
data.get(1).copied().unwrap_or(0),
430+
data.get(2).copied().unwrap_or(0),
431+
data.get(3).copied().unwrap_or(0),
432+
]);
433+
handle_outb(
434+
mem_mgr,
435+
host_funcs.clone(),
436+
port,
437+
val,
438+
&self.vm.regs()?,
439+
#[cfg(feature = "mem_profile")]
440+
&mut self.trace_info,
441+
)?;
442+
}
473443
Ok(VmExit::MmioRead(addr)) => {
474444
let all_regions = self
475445
.sandbox_regions
@@ -491,17 +461,9 @@ impl HyperlightVm {
491461
));
492462
}
493463
None => {
494-
match &self.mem_mgr {
495-
Some(mem_mgr) => {
496-
if !mem_mgr.check_stack_guard()? {
497-
break Err(HyperlightError::StackOverflow());
498-
}
499-
}
500-
None => {
501-
break Err(new_error!("Memory manager not initialized"));
502-
}
503-
}
504-
464+
// if !self.mem_mgr.check_stack_guard()? {
465+
// break Err(HyperlightError::StackOverflow());
466+
// }
505467
break Err(new_error!("MMIO READ access address {:#x}", addr));
506468
}
507469
}
@@ -527,15 +489,8 @@ impl HyperlightVm {
527489
));
528490
}
529491
None => {
530-
match &self.mem_mgr {
531-
Some(mem_mgr) => {
532-
if !mem_mgr.check_stack_guard()? {
533-
break Err(HyperlightError::StackOverflow());
534-
}
535-
}
536-
None => {
537-
break Err(new_error!("Memory manager not initialized"));
538-
}
492+
if mem_mgr.check_stack_guard()? {
493+
break Err(HyperlightError::StackOverflow());
539494
}
540495

541496
break Err(new_error!("MMIO WRITE access address {:#x}", addr));
@@ -803,27 +758,6 @@ impl HyperlightVm {
803758
filename,
804759
))
805760
}
806-
807-
#[cfg(feature = "mem_profile")]
808-
pub(crate) fn vm_regs(&self) -> Result<CommonRegisters> {
809-
self.vm.regs()
810-
}
811-
812-
#[cfg(feature = "trace_guest")]
813-
fn handle_trace(&mut self, tc: &mut crate::sandbox::trace::TraceContext) -> Result<()> {
814-
let regs = self.vm.regs()?;
815-
tc.handle_trace(
816-
&regs,
817-
self.mem_mgr.as_mut().ok_or_else(|| {
818-
new_error!("Memory manager is not initialized before handling trace")
819-
})?,
820-
)
821-
}
822-
823-
#[cfg(feature = "mem_profile")]
824-
pub(crate) fn trace_info_mut(&mut self) -> &mut MemTraceInfo {
825-
&mut self.trace_info
826-
}
827761
}
828762

829763
impl Drop for HyperlightVm {

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,9 @@ pub(crate) mod tests {
492492
let rt_cfg: SandboxRuntimeConfig = Default::default();
493493
let sandbox =
494494
UninitializedSandbox::new(GuestBinary::FilePath(filename.clone()), Some(config))?;
495-
let (mem_mgr, mut gshm) = sandbox.mgr.build();
495+
let (mut hshm, _gshm) = sandbox.mgr.build();
496496
let mut vm = set_up_hypervisor_partition(
497-
&mut gshm,
497+
&mut hshm,
498498
&config,
499499
#[cfg(any(crashdump, gdb))]
500500
&rt_cfg,
@@ -509,14 +509,14 @@ pub(crate) mod tests {
509509
let guest_max_log_level = Some(log::LevelFilter::Error);
510510

511511
#[cfg(gdb)]
512-
let dbg_mem_access_fn = Arc::new(Mutex::new(mem_mgr.clone()));
512+
let dbg_mem_access_fn = Arc::new(Mutex::new(hshm.clone()));
513513

514514
// Test the initialise method
515515
vm.initialise(
516516
peb_addr,
517517
seed,
518518
page_size,
519-
mem_mgr,
519+
&mut hshm,
520520
host_funcs,
521521
guest_max_log_level,
522522
#[cfg(gdb)]

src/hyperlight_host/src/mem/layout.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ use super::memory_region::{
3030
};
3131
#[cfg(feature = "init-paging")]
3232
use super::mgr::AMOUNT_OF_MEMORY_PER_PT;
33-
use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, SharedMemory};
33+
use super::shared_mem::{ExclusiveSharedMemory, SharedMemory};
3434
use crate::error::HyperlightError::{GuestOffsetIsInvalid, MemoryRequestTooBig};
35+
use crate::mem::shared_mem::HostSharedMemory;
3536
use crate::sandbox::SandboxConfiguration;
3637
use crate::{Result, new_error};
3738

@@ -561,7 +562,7 @@ impl SandboxMemoryLayout {
561562

562563
/// Returns the memory regions associated with this memory layout,
563564
/// suitable for passing to a hypervisor for mapping into memory
564-
pub fn get_memory_regions(&self, shared_mem: &GuestSharedMemory) -> Result<Vec<MemoryRegion>> {
565+
pub fn get_memory_regions(&self, shared_mem: &HostSharedMemory) -> Result<Vec<MemoryRegion>> {
565566
let mut builder = MemoryRegionVecBuilder::new(Self::BASE_ADDRESS, shared_mem.base_addr());
566567

567568
cfg_if::cfg_if! {

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ pub struct MultiUseSandbox {
9696
id: u64,
9797
/// Whether this sandbox is poisoned
9898
poisoned: bool,
99-
// We need to keep a reference to the host functions, even if the compiler marks it as unused. The compiler cannot detect our dynamic usages of the host function in `HyperlightFunction::call`.
100-
pub(super) _host_funcs: Arc<Mutex<FunctionRegistry>>,
99+
pub(super) host_funcs: Arc<Mutex<FunctionRegistry>>,
101100
pub(crate) mem_mgr: SandboxMemoryManager<HostSharedMemory>,
102101
vm: HyperlightVm,
103102
dispatch_ptr: RawPtr,
@@ -125,7 +124,7 @@ impl MultiUseSandbox {
125124
Self {
126125
id: SANDBOX_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
127126
poisoned: false,
128-
_host_funcs: host_funcs,
127+
host_funcs,
129128
mem_mgr: mgr,
130129
vm,
131130
dispatch_ptr,
@@ -611,6 +610,8 @@ impl MultiUseSandbox {
611610
self.mem_mgr.write_guest_function_call(buffer)?;
612611

613612
self.vm.dispatch_call_from_host(
613+
&mut self.mem_mgr,
614+
self.host_funcs.clone(),
614615
self.dispatch_ptr.clone(),
615616
#[cfg(gdb)]
616617
self.dbg_mem_access_fn.clone(),

src/hyperlight_host/src/sandbox/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ mod tests {
174174
.pop()
175175
.unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
176176
let host_funcs = sandbox
177-
._host_funcs
177+
.host_funcs
178178
.try_lock()
179179
.map_err(|_| new_error!("Error locking"));
180180

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ use tracing::{Span, instrument};
2525
use tracing_log::format_trace;
2626

2727
use super::host_funcs::FunctionRegistry;
28-
#[cfg(feature = "mem_profile")]
29-
use crate::hypervisor::hyperlight_vm::HyperlightVm;
28+
use crate::hypervisor::regs::CommonRegisters;
3029
use crate::mem::mgr::SandboxMemoryManager;
3130
use crate::mem::shared_mem::HostSharedMemory;
3231
use crate::{HyperlightError, Result, new_error};
3332

33+
#[cfg(feature = "mem_profile")]
34+
use crate::sandbox::trace::MemTraceInfo;
35+
3436
#[instrument(err(Debug), skip_all, parent = Span::current(), level="Trace")]
3537
pub(super) fn outb_log(mgr: &mut SandboxMemoryManager<HostSharedMemory>) -> Result<()> {
3638
// This code will create either a logging record or a tracing record for the GuestLogData depending on if the host has set up a tracing subscriber.
@@ -150,7 +152,8 @@ pub(crate) fn handle_outb(
150152
host_funcs: Arc<Mutex<FunctionRegistry>>,
151153
port: u16,
152154
data: u32,
153-
#[cfg(feature = "mem_profile")] vm: &mut HyperlightVm,
155+
_regs: &CommonRegisters,
156+
#[cfg(feature = "mem_profile")] trace_info: &mut MemTraceInfo,
154157
) -> Result<()> {
155158
match port.try_into()? {
156159
OutBAction::Log => outb_log(mem_mgr),
@@ -185,17 +188,9 @@ pub(crate) fn handle_outb(
185188
#[cfg(feature = "trace_guest")]
186189
OutBAction::TraceBatch => Ok(()),
187190
#[cfg(feature = "mem_profile")]
188-
OutBAction::TraceMemoryAlloc => {
189-
let regs = vm.vm_regs()?;
190-
let trace_info = vm.trace_info_mut();
191-
trace_info.handle_trace_mem_alloc(&regs, mem_mgr)
192-
}
191+
OutBAction::TraceMemoryAlloc => trace_info.handle_trace_mem_alloc(_regs, mem_mgr),
193192
#[cfg(feature = "mem_profile")]
194-
OutBAction::TraceMemoryFree => {
195-
let regs = vm.vm_regs()?;
196-
let trace_info = vm.trace_info_mut();
197-
trace_info.handle_trace_mem_free(&regs, mem_mgr)
198-
}
193+
OutBAction::TraceMemoryFree => trace_info.handle_trace_mem_free(_regs, mem_mgr),
199194
}
200195
}
201196
#[cfg(test)]

0 commit comments

Comments
 (0)