| 
 | 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