Skip to content

Commit 6879809

Browse files
committed
Allow the guest to manage its own stack
This moves management of the guest stack into the guest, allowing the guest to flexibly allocate pages from the scratch region to the stack. This also makes the stack dynamically growable and essentially unbounded, as is common on other architectures. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent 3460991 commit 6879809

File tree

24 files changed

+205
-366
lines changed

24 files changed

+205
-366
lines changed

src/hyperlight_common/src/arch/amd64/layout.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@ pub const SNAPSHOT_PT_GVA_MAX: usize = 0xffff_80ff_ffff_ffff;
3030
/// bits, so we could consider bumping this in the future if we were
3131
/// ever memory-constrained.
3232
pub const MAX_GPA: usize = 0x0000_000f_ffff_ffff;
33+
34+
/// On amd64, this is:
35+
/// - Two pages for the TSS and IDT
36+
/// - (up to) 4 pages for the PTEs for mapping that (including CoW'ing the root PT)
37+
/// - A page for the smallest possible non-exception stack
38+
/// - (up to) 3 pages for mapping that
39+
/// - Two pages for the exception stack and metadata
40+
pub fn min_scratch_size() -> usize {
41+
12 * crate::vmem::PAGE_SIZE
42+
}

src/hyperlight_common/src/arch/i686/layout.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ pub const MAX_GVA: usize = 0xffff_efff;
2121
pub const SNAPSHOT_PT_GVA_MIN: usize = 0xef00_0000;
2222
pub const SNAPSHOT_PT_GVA_MAX: usize = 0xefff_efff;
2323
pub const MAX_GPA: usize = 0xffff_ffff;
24+
25+
pub fn min_scratch_size() -> usize {
26+
1 * crate::vmem::PAGE_SIZE
27+
}

src/hyperlight_common/src/layout.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ pub fn scratch_base_gpa(size: usize) -> u64 {
3232
pub fn scratch_base_gva(size: usize) -> u64 {
3333
(MAX_GVA - size + 1) as u64
3434
}
35+
36+
/// Compute the minimum scratch region size needed for a sandbox.
37+
pub use arch::min_scratch_size;

src/hyperlight_common/src/mem.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,4 @@ pub struct HyperlightPEB {
4646
pub output_stack: GuestMemoryRegion,
4747
pub init_data: GuestMemoryRegion,
4848
pub guest_heap: GuestMemoryRegion,
49-
pub guest_stack: GuestStack,
5049
}

src/hyperlight_guest/src/arch/amd64/layout.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ limitations under the License.
1818
// src/hyperlight_common/src/arch/amd64/layout.rs and
1919
// src/hyperlight_guest_bin/src/arch/amd64/layout.rs
2020

21-
pub const MAIN_STACK_TOP_GVA: u64 = 0xffff_feff_ffff_f000;
21+
/// Note that the x86-64 ELF psABI requires that the stack be 16-byte
22+
/// aligned before a call instruction; we use the aligned version
23+
/// here, even though this requires adjusting the pointer by 8 bytes
24+
/// when entering the guest without a call instruction to push a
25+
/// return address.
26+
pub const MAIN_STACK_TOP_GVA: u64 = 0xffff_ff00_0000_0000;
27+
pub const MAIN_STACK_LIMIT_GVA: u64 = 0xffff_fe00_0000_0000;
2228

2329
pub fn scratch_size() -> u64 {
2430
let addr = crate::layout::scratch_size_gva();

src/hyperlight_guest/src/arch/i686/layout.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ limitations under the License.
1818
// allow compiling the guest for real mode boot scenarios.
1919

2020
pub const MAIN_STACK_TOP_GVA: usize = 0xdfff_efff;
21+
pub const MAIN_STACK_LIMIT_GVA: usize = 0xdf00_0000;
2122

2223
pub fn scratch_size() -> u64 {
2324
hyperlight_common::vmem::PAGE_SIZE as u64

src/hyperlight_guest/src/layout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ limitations under the License.
1818
#[cfg_attr(target_arch = "x86", path = "arch/i686/layout.rs")]
1919
mod arch;
2020

21-
pub use arch::MAIN_STACK_TOP_GVA;
21+
pub use arch::{MAIN_STACK_LIMIT_GVA, MAIN_STACK_TOP_GVA};
2222
pub fn scratch_size_gva() -> *mut u64 {
2323
use hyperlight_common::layout::{MAX_GVA, SCRATCH_TOP_SIZE_OFFSET};
2424
(MAX_GVA as u64 - SCRATCH_TOP_SIZE_OFFSET + 1) as *mut u64

src/hyperlight_guest_bin/src/arch/amd64/exn/handle.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use core::fmt::Write;
1818

1919
use hyperlight_common::outb::Exception;
2020
use hyperlight_guest::exit::write_abort;
21+
use hyperlight_guest::layout::{MAIN_STACK_LIMIT_GVA, MAIN_STACK_TOP_GVA};
2122

2223
use super::super::context::Context;
2324
use super::super::machine::ExceptionInfo;
@@ -77,6 +78,23 @@ pub(crate) extern "C" fn hl_exception_handler(
7778
let saved_rip = unsafe { (&raw const (*exn_info).rip).read_volatile() };
7879
let error_code = unsafe { (&raw const (*exn_info).error_code).read_volatile() };
7980

81+
if exception_number == 14
82+
&& (MAIN_STACK_LIMIT_GVA..MAIN_STACK_TOP_GVA).contains(&page_fault_address)
83+
{
84+
// TODO: perhaps we should have a sanity check that the
85+
// stack grows only one page at a time, which should be
86+
// ensured by our stack probing discipline?
87+
unsafe {
88+
let new_page = hyperlight_guest::prim_alloc::alloc_phys_pages(1);
89+
crate::paging::map_region(
90+
new_page,
91+
(page_fault_address & !0xfff) as *mut u8,
92+
hyperlight_common::vmem::PAGE_SIZE as u64,
93+
);
94+
return;
95+
}
96+
}
97+
8098
// Check for registered user handlers (only for architecture-defined vectors 0-30)
8199
if exception_number < 31 {
82100
let handler =

src/hyperlight_guest_bin/src/arch/amd64/init.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ unsafe fn init_tss(pc: *mut ProcCtrl) {
8686
}
8787
}
8888

89+
unsafe fn init_stack() -> u64 {
90+
use hyperlight_guest::layout::MAIN_STACK_TOP_GVA;
91+
let stack_top_page_base = (MAIN_STACK_TOP_GVA - 1) & !0xfff;
92+
unsafe {
93+
crate::paging::map_region(
94+
hyperlight_guest::prim_alloc::alloc_phys_pages(1),
95+
stack_top_page_base as *mut u8,
96+
hyperlight_common::vmem::PAGE_SIZE as u64,
97+
);
98+
}
99+
MAIN_STACK_TOP_GVA
100+
}
101+
89102
/// Machine-specific initialisation; calls [`crate::generic_init`]
90103
/// once stack, CoW, etc have been set up.
91104
#[unsafe(no_mangle)]
@@ -95,18 +108,25 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve
95108
init_gdt(pc);
96109
init_tss(pc);
97110
init_idt(pc);
98-
call_generic_init(peb_address, seed, ops, max_log_level);
111+
let stack_top = init_stack();
112+
pivot_stack(peb_address, seed, ops, max_log_level, stack_top);
99113
}
100114
}
101115

102116
unsafe extern "C" {
103-
unsafe fn call_generic_init(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) -> !;
117+
unsafe fn pivot_stack(
118+
peb_address: u64,
119+
seed: u64,
120+
ops: u64,
121+
max_log_level: u64,
122+
stack_top: u64,
123+
) -> !;
104124
}
105125

106126
core::arch::global_asm!("
107-
.global call_generic_init\n
108-
call_generic_init:\n
109-
sub rsp, 0x8\n
127+
.global pivot_stack\n
128+
pivot_stack:\n
129+
mov rsp, r8\n
110130
call {generic_init}\n
111131
hlt\n
112132
", generic_init = sym crate::generic_init);

src/hyperlight_guest_bin/src/lib.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,10 @@ pub static mut GUEST_HANDLE: GuestHandle = GuestHandle::new();
119119
pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister =
120120
GuestFunctionRegister::new();
121121

122-
pub static mut MIN_STACK_ADDRESS: u64 = 0;
123-
122+
/// The size of one page in the host OS, which may have some impacts
123+
/// on how buffers for host consumption should be aligned. Code only
124+
/// working with the guest page tables should use
125+
/// [`hyperlight_common::vm::PAGE_SIZE`] instead.
124126
pub static mut OS_PAGE_SIZE: u32 = 0;
125127

126128
// === Panic Handler ===
@@ -202,12 +204,6 @@ pub(crate) extern "C" fn generic_init(peb_address: u64, seed: u64, ops: u64, max
202204
unsafe {
203205
(*peb_ptr).guest_function_dispatch_ptr = dispatch_function as usize as u64;
204206

205-
// This static is to make it easier to implement the __chkstk
206-
// function in assembly. It also means that should we change
207-
// the layout of the struct in the future, we don't have to
208-
// change the assembly code.
209-
MIN_STACK_ADDRESS = (*peb_ptr).guest_stack.min_user_stack_address;
210-
211207
let srand_seed = (((peb_address << 8) ^ (seed >> 4)) >> 32) as u32;
212208
// Set the seed for the random number generator for C code using rand;
213209
srand(srand_seed);

0 commit comments

Comments
 (0)