Skip to content

Commit 3b04445

Browse files
committed
[common,guest] added peb and input/output API to common library
- removes old HyperlightPEB structures (making it more idiomatic Rust). - removes input/output structures from guest library, moving it to common as they will need to be used by external guests. - creates new HyperlightPEB API w/ capabilities to address memory regions from the guest. Signed-off-by: danbugs <[email protected]>
1 parent 7acabe3 commit 3b04445

File tree

6 files changed

+429
-282
lines changed

6 files changed

+429
-282
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
use crate::PAGE_SIZE;
2+
3+
/// Hyperlight supports 2 primary modes:
4+
/// 1. Hypervisor mode
5+
/// 2. In-process mode
6+
///
7+
/// When running in process, there's no hypervisor isolation.
8+
/// In-process mode is primarily used for debugging and testing.
9+
#[repr(u64)]
10+
#[derive(Clone, Debug, PartialEq, Default)]
11+
pub enum RunMode {
12+
None = 0,
13+
#[default]
14+
Hypervisor = 1,
15+
InProcessWindows = 2,
16+
InProcessLinux = 3,
17+
Invalid = 4,
18+
}
19+
20+
#[repr(C)]
21+
#[derive(Clone, Default)]
22+
pub struct HyperlightPEB {
23+
// - Host configured fields
24+
/// Hyperlight supports two primary modes:
25+
/// 1. Hypervisor mode
26+
/// 2. In-process mode
27+
///
28+
/// When running in process, there's no hypervisor isolation.
29+
/// It's a mode primarily used for debugging and testing.
30+
pub run_mode: RunMode,
31+
32+
/// On Windows, Hyperlight supports in-process execution.
33+
/// In-process execution means a guest is running in
34+
/// Hyperlight, but with no hypervisor isolation. When we
35+
/// run in-process, we can't rely on the usual mechanism for
36+
/// host function calls (i.e., `outb`). Instead, we call a
37+
/// function directly, which is represented by this pointer.
38+
pub outb_ptr: u64,
39+
40+
/// The base address for the guest memory region.
41+
pub guest_memory_base_address: u64,
42+
43+
/// The size of the guest memory region.
44+
pub guest_memory_size: u64,
45+
46+
// - Guest configured fields
47+
/// The guest function dispatch pointer is what allows
48+
/// a host to call "guest functions". The host can
49+
/// directly set the instruction pointer register to this
50+
/// before re-entering the guest.
51+
pub guest_function_dispatch_ptr: u64,
52+
53+
/// Guest error data can be used to pass guest error information
54+
/// between host and the guest.
55+
pub guest_error_data_ptr: u64,
56+
pub guest_error_data_size: u64,
57+
58+
/// Host error data can be used to pass host error information
59+
/// between the host and the guest.
60+
pub host_error_data_ptr: u64,
61+
pub host_error_data_size: u64,
62+
63+
/// The input data pointer is used to pass data from
64+
/// the host to the guest.
65+
pub input_data_ptr: u64,
66+
pub input_data_size: u64,
67+
68+
/// The output data pointer is used to pass data from
69+
/// the guest to the host.
70+
pub output_data_ptr: u64,
71+
pub output_data_size: u64,
72+
73+
/// The guest panic context pointer can be used to pass
74+
/// panic context data from the guest to the host.
75+
pub guest_panic_context_ptr: u64,
76+
pub guest_panic_context_size: u64,
77+
78+
/// The guest heap data pointer points to a region of
79+
/// memory in the guest that is used for heap allocations.
80+
pub guest_heap_data_ptr: u64,
81+
pub guest_heap_data_size: u64,
82+
83+
/// The guest stack data pointer points to a region of
84+
/// memory in the guest that is used for stack allocations.
85+
pub guest_stack_data_ptr: u64,
86+
pub guest_stack_data_size: u64,
87+
88+
// Host function details may be used in the guest before
89+
// issuing a host function call to validate it before
90+
// ensuing a `VMEXIT`.
91+
pub host_function_details_ptr: u64,
92+
pub host_function_details_size: u64,
93+
}
94+
95+
impl HyperlightPEB {
96+
pub fn set_default_memory_layout(&mut self) {
97+
// we set the guest stack at the start of the guest memory region to leverage
98+
// the stack guard page before it
99+
self.set_guest_stack_data_region(
100+
self.guest_memory_base_address, // start at base of custom guest memory region,
101+
None, // don't override the stack size
102+
);
103+
104+
let guest_stack_size = self.get_guest_stack_data_size();
105+
106+
self.set_guest_heap_data_region(
107+
guest_stack_size, // start at the end of the stack
108+
None, // don't override the heap size
109+
);
110+
111+
let guest_heap_size = self.get_guest_heap_data_size();
112+
113+
self.set_guest_error_data_region(
114+
guest_stack_size + guest_heap_size as u64, // start at the end of the host function details
115+
PAGE_SIZE as u64, // 4KB
116+
);
117+
118+
self.set_host_error_data_region(
119+
guest_stack_size + guest_heap_size + PAGE_SIZE as u64, // start at the end of the guest error data
120+
PAGE_SIZE as u64, // 4KB
121+
);
122+
123+
self.set_input_data_region(
124+
guest_stack_size + guest_heap_size + PAGE_SIZE as u64 * 2, // start at the end of the host error data
125+
PAGE_SIZE as u64 * 4, // 16KB
126+
);
127+
128+
self.set_output_data_region(
129+
guest_stack_size + guest_heap_size + PAGE_SIZE as u64 * 6, // start at the end of the input data
130+
PAGE_SIZE as u64 * 4, // 16KB
131+
);
132+
133+
self.set_guest_panic_context_region(
134+
guest_stack_size + guest_heap_size + PAGE_SIZE as u64 * 10, // start at the end of the output data
135+
PAGE_SIZE as u64, // 4KB
136+
);
137+
138+
self.set_host_function_details_region(
139+
guest_stack_size + guest_heap_size + PAGE_SIZE as u64 * 11, // start at the end of the guest panic context
140+
PAGE_SIZE as u64, // 4KB
141+
);
142+
}
143+
144+
// Sets the guest error data region, where the guest can write errors to.
145+
pub fn set_guest_error_data_region(&mut self, ptr: u64, size: u64) {
146+
self.guest_error_data_ptr = self.guest_memory_base_address + ptr;
147+
self.guest_error_data_size = size;
148+
}
149+
150+
// Sets the host error data region, where the host can write errors to.
151+
pub fn set_host_error_data_region(&mut self, ptr: u64, size: u64) {
152+
self.host_error_data_ptr = self.guest_memory_base_address + ptr;
153+
self.host_error_data_size = size;
154+
}
155+
156+
// Sets the input data region, where the host can write things like function calls to.
157+
pub fn set_input_data_region(&mut self, ptr: u64, size: u64) {
158+
self.input_data_ptr = self.guest_memory_base_address + ptr;
159+
self.input_data_size = size;
160+
}
161+
162+
// Sets the output data region, where the guest can write things like function return values to.
163+
pub fn set_output_data_region(&mut self, ptr: u64, size: u64) {
164+
self.output_data_ptr = self.guest_memory_base_address + ptr;
165+
self.output_data_size = size;
166+
}
167+
168+
// Sets the guest panic context region, where the guest can write panic context data to.
169+
pub fn set_guest_panic_context_region(&mut self, ptr: u64, size: u64) {
170+
self.guest_panic_context_ptr = self.guest_memory_base_address + ptr;
171+
self.guest_panic_context_size = size;
172+
}
173+
174+
// Gets the guest heap size. If not set, this function panics—this is because the host is always
175+
// expected to set the size of the heap data region in accordance to info from the guest binary,
176+
// so, if this is not set, we have a critical error.
177+
pub fn get_guest_heap_data_size(&self) -> u64 {
178+
if self.guest_heap_data_size == 0 {
179+
panic!("Heap data size is not set");
180+
}
181+
182+
self.guest_heap_data_size
183+
}
184+
185+
// Sets the guest heap data region.
186+
pub fn set_guest_heap_data_region(&mut self, ptr: u64, size_override: Option<u64>) {
187+
self.guest_heap_data_ptr = self.guest_memory_base_address + ptr;
188+
// the Hyperlight host always sets the heap data size to a default value, the
189+
// guest has the option to override it.
190+
if let Some(size) = size_override {
191+
self.guest_heap_data_size = size;
192+
}
193+
194+
// If by this point the size is still None, we have a critical error
195+
// and we should panic.
196+
if self.guest_heap_data_size == 0 {
197+
panic!("Heap data size is 0 after setting guest heap data region");
198+
}
199+
}
200+
201+
// Gets the guest heap size. If not set, this function panics—this is because the host is always
202+
// expected to set the size of the heap data region in accordance to info from the guest binary,
203+
// so, if this is not set, we have a critical error.
204+
pub fn get_guest_stack_data_size(&self) -> u64 {
205+
if self.guest_stack_data_size == 0 {
206+
panic!("Stack data size is not set");
207+
}
208+
209+
self.guest_stack_data_size
210+
}
211+
212+
// Sets the guest stack data region.
213+
pub fn set_guest_stack_data_region(&mut self, ptr: u64, size_override: Option<u64>) {
214+
self.guest_stack_data_ptr = self.guest_memory_base_address + ptr;
215+
216+
// the Hyperlight host always sets the stack data size to a default value, the
217+
// guest has the option to override it.
218+
if let Some(size) = size_override {
219+
self.guest_stack_data_size = size;
220+
}
221+
222+
// If by this point the size is still None, we have a critical error
223+
// and we should panic.
224+
if self.guest_stack_data_size == 0 {
225+
panic!("Stack data size is 0 after setting guest stack data region");
226+
}
227+
}
228+
229+
// Sets the host function details region, where the guest can write host function details to.
230+
pub fn set_host_function_details_region(&mut self, ptr: u64, size: u64) {
231+
self.host_function_details_ptr = self.guest_memory_base_address + ptr;
232+
self.host_function_details_size = size;
233+
}
234+
235+
// Gets the input data region, where the host can write things like function calls to.
236+
pub fn get_input_data_region(&self) -> (u64, u64) {
237+
(self.input_data_ptr, self.input_data_size)
238+
}
239+
240+
// Gets the output data region, where the guest can write things like function return values to.
241+
pub fn get_output_data_region(&self) -> (u64, u64) {
242+
(self.output_data_ptr, self.output_data_size)
243+
}
244+
}

0 commit comments

Comments
 (0)