Skip to content
Merged
5 changes: 5 additions & 0 deletions src/hyperlight_guest_bin/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ fn cargo_main() {
// targets will eventually show up.
cfg.flag("--target=x86_64-unknown-linux-none");

// We don't use a different stack for all interrupts, so there
// can be no red zone
cfg.flag("-mno-red-zone");

// We don't support stack protectors at the moment, but Arch Linux clang
// auto-enables them for -linux platforms, so explicitly disable them.
cfg.flag("-fno-stack-protector");
Expand Down Expand Up @@ -245,6 +249,7 @@ fn main() -> std::process::ExitCode {
"-fno-stack-protector",
"-fstack-clash-protection",
"-mstack-probe-size=4096",
"-mno-red-zone",
])
.arg("-nostdinc")
.arg("-isystem")
Expand Down
75 changes: 72 additions & 3 deletions src/hyperlight_guest_bin/src/exceptions/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,89 @@ use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
use hyperlight_common::outb::Exception;
use hyperlight_guest::exit::abort_with_code_and_message;

use crate::paging;

/// See AMD64 Architecture Programmer's Manual, Volume 2
/// §8.9.3 Interrupt Stack Frame, pp. 283--284
/// Figure 8-14: Long-Mode Stack After Interrupt---Same Privilege,
/// Figure 8-15: Long-Mode Stack After Interrupt---Higher Privilege
/// Subject to the proviso that we push a dummy error code of 0 for exceptions
/// for which the processor does not provide one
#[repr(C)]
pub struct ExceptionInfo {
pub error_code: u64,
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub rsp: u64,
pub ss: u64,
}
const _: () = assert!(core::mem::offset_of!(ExceptionInfo, rip) == 8);
const _: () = assert!(core::mem::offset_of!(ExceptionInfo, rsp) == 32);

#[repr(C)]
/// Saved context, pushed onto the stack by exception entry code
pub struct Context {
/// in order: gs, fs, es
pub segments: [u64; 3],
pub fxsave: [u8; 512],
pub ds: u64,
/// no `rsp`, since the processor saved it
/// `rax` is at the top, `r15` the bottom
pub gprs: [u64; 15],
}
const _: () = assert!(size_of::<Context>() == 152 + 512);

// TODO: This will eventually need to end up in a per-thread context,
// when there are threads.
pub static handlers: [core::sync::atomic::AtomicU64; 31] =
[const { core::sync::atomic::AtomicU64::new(0) }; 31];
type handler_t = fn(n: u64, info: *mut ExceptionInfo, ctx: *mut Context, pf_addr: u64) -> bool;

/// Exception handler
#[unsafe(no_mangle)]
pub extern "C" fn hl_exception_handler(
stack_pointer: u64,
exception_number: u64,
page_fault_address: u64,
) {
let ctx = stack_pointer as *mut Context;
let exn_info = (stack_pointer + size_of::<Context>() as u64) as *mut ExceptionInfo;

let exception = Exception::try_from(exception_number as u8).expect("Invalid exception number");

let saved_rip = unsafe { (&raw const (*exn_info).rip).read_volatile() };
let error_code = unsafe { (&raw const (*exn_info).error_code).read_volatile() };

let msg = format!(
"Page Fault Address: {:#x}\n\
Stack Pointer: {:#x}",
page_fault_address, stack_pointer
"Exception vector: {:#}\n\
Faulting Instruction: {:#x}\n\
Page Fault Address: {:#x}\n\
Error code: {:#x}\n\
Stack Pointer: {:#x}",
exception_number, saved_rip, page_fault_address, error_code, stack_pointer
);

// We don't presently have any need for user-defined interrupts,
// so we only support handlers for the architecture-defined
// vectors (0-31)
if exception_number < 31 {
let handler =
handlers[exception_number as usize].load(core::sync::atomic::Ordering::Acquire);
if handler != 0
&& unsafe {
core::mem::transmute::<_, handler_t>(handler)(
exception_number,
exn_info,
ctx,
page_fault_address,
)
}
{
return;
}
}

unsafe {
abort_with_code_and_message(
&[ErrorCode::GuestError as u8, exception as u8],
Expand Down
22 changes: 19 additions & 3 deletions src/hyperlight_guest_bin/src/exceptions/interrupt_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,19 @@ macro_rules! context_save {
" push r13\n",
" push r14\n",
" push r15\n",
// Save segment registers
// Save one of the segment registers to get 16-byte alignment for
// FXSAVE. TODO: consider packing the segment registers
" mov rax, ds\n",
" push rax\n",
// Save floating-point/SSE registers
// TODO: Don't do this unconditionally: get the exn
// handlers compiled without sse
// TODO: Check if we ever generate code with ymm/zmm in
// the handlers and save/restore those as well
" sub rsp, 512\n",
" mov rax, rsp\n",
" fxsave [rax]\n",
// Save the rest of the segment registers
" mov rax, es\n",
" push rax\n",
" mov rax, fs\n",
Expand All @@ -83,13 +93,18 @@ macro_rules! context_save {
macro_rules! context_restore {
() => {
concat!(
// Restore segment registers
// Restore most segment registers
" pop rax\n",
" mov gs, rax\n",
" pop rax\n",
" mov fs, rax\n",
" pop rax\n",
" mov es, rax\n",
// Restore floating-point/SSE registers
" mov rax, rsp\n",
" fxrstor [rax]\n",
" add rsp, 512\n",
// Restore the last segment register
" pop rax\n",
" mov ds, rax\n",
// Restore general-purpose registers
Expand Down Expand Up @@ -123,7 +138,8 @@ macro_rules! generate_exceptions {
" mov rdi, rsp\n",
" call {hl_exception_handler}\n",
context_restore!(),
" iretq\n", // iretq is used to return from exception in x86_64
" add rsp, 8\n", // error code
" iretq\n", // iretq is used to return from exception in x86_64
generate_excp!(0, pusherrcode),
generate_excp!(1, pusherrcode),
generate_excp!(2, pusherrcode),
Expand Down
11 changes: 11 additions & 0 deletions src/hyperlight_guest_bin/src/guest_function/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ fn internal_dispatch_function() -> Result<()> {
// which if it were included in the internal_dispatch_function cause the epilogue to not be called because the halt() would not return
// when running in the hypervisor.
pub(crate) extern "C" fn dispatch_function() {
// The hyperlight host likes to use one partition and reset it in
// various ways; if that has happened, there might stale TLB
// entries hanging around from the former user of the
// partition. Flushing the TLB here is not quite the right thing
// to do, since incorrectly cached entries could make even this
// code not exist, but regrettably there is not a simple way for
// the host to trigger flushing when it ought to happen, so for
// now this works in practice, since the text segment is always
// part of the big identity-mapped region at the base of the
// guest.
crate::paging::flush_tlb();
let _ = internal_dispatch_function();
halt();
}
5 changes: 3 additions & 2 deletions src/hyperlight_guest_bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ use spin::Once;

// === Modules ===
#[cfg(target_arch = "x86_64")]
mod exceptions {
pub mod exceptions {
pub(super) mod gdt;
mod handler;
pub mod handler;
mod idt;
pub(super) mod idtr;
mod interrupt_entry;
Expand All @@ -52,6 +52,7 @@ pub mod guest_function {
pub mod guest_logger;
pub mod host_comm;
pub mod memory;
pub mod paging;

// === Globals ===
#[global_allocator]
Expand Down
Loading
Loading