Skip to content

Commit 90e7221

Browse files
committed
Add AArch64 guest exception callback
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
1 parent 637bbc4 commit 90e7221

5 files changed

Lines changed: 68 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
55
## [Prerelease] - Unreleased
66

77
### Added
8+
* AArch64 guests can register an exception callback to update ELR and saved
9+
general-purpose registers before Hyperlight aborts.
810
* `SandboxBuilder`, the entry point for creating a sandbox. It gathers machine
911
configuration, host functions, init data and memory mappings, then builds a
1012
`MultiUseSandbox` from a guest binary on disk, a guest binary in memory, or a

src/hyperlight_guest_bin/src/arch/aarch64/exception/handle.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// Copyright 2026 The Hyperlight Authors.
33
use core::fmt::Write;
4+
use core::sync::atomic::{AtomicU64, Ordering};
45

56
use hyperlight_common::arch::exn::{DataFault, DataFaultKind, Exception, decode_syndrome};
67
use hyperlight_common::vmem::{
@@ -10,10 +11,50 @@ use hyperlight_guest::error::ErrorCode;
1011
use hyperlight_guest::exit::write_abort;
1112
use hyperlight_guest::layout::{MAIN_STACK_LIMIT_GVA, MAIN_STACK_TOP_GVA};
1213

13-
use super::super::mrs;
14+
use super::super::{mrs, msr};
1415
use super::types::*;
1516
use crate::HyperlightAbortWriter;
1617

18+
/// Callback for synchronous AArch64 exceptions not handled by Hyperlight.
19+
///
20+
/// The arguments are the decoded exception, raw ESR_EL1, FAR_EL1, mutable
21+
/// ELR_EL1, and saved x0 through x30. Return `true` to resume execution.
22+
/// Call [`crate::paging::barrier::first_valid_same_ctx`] after mapping a page
23+
/// for a translation fault.
24+
pub type ExceptionHandler = fn(Exception, u64, u64, &mut u64, &mut [u64; 31]) -> bool;
25+
26+
static HANDLER: AtomicU64 = AtomicU64::new(0);
27+
28+
/// Register the callback for synchronous AArch64 exceptions.
29+
///
30+
/// A new callback replaces any existing callback.
31+
pub fn register_exception_handler(handler: ExceptionHandler) {
32+
HANDLER.store(handler as usize as u64, Ordering::Release);
33+
}
34+
35+
/// Remove the registered AArch64 exception callback.
36+
pub fn unregister_exception_handler() {
37+
HANDLER.store(0, Ordering::Release);
38+
}
39+
40+
fn try_handle_registered_exception(
41+
exn: Exception,
42+
esr: u64,
43+
far: u64,
44+
elr: &mut u64,
45+
registers: &mut [u64; 31],
46+
) -> bool {
47+
let handler = HANDLER.load(Ordering::Acquire);
48+
if handler == 0 {
49+
return false;
50+
}
51+
52+
// SAFETY: HANDLER contains only function pointers stored by
53+
// register_exception_handler.
54+
let handler = unsafe { core::mem::transmute::<u64, ExceptionHandler>(handler) };
55+
handler(exn, esr, far, elr, registers)
56+
}
57+
1758
fn handle_stack_fault(far: u64) {
1859
// TODO: perhaps we should have a sanity check that the
1960
// stack grows only one page at a time, which should be
@@ -124,7 +165,7 @@ fn handle_internal_fault(exn: Exception, far: u64) -> bool {
124165
pub(super) extern "C" fn handle_exception(
125166
typ: ExceptionType,
126167
from: ExceptionFrom,
127-
_regs: *mut ExceptionContext,
168+
regs: *mut ExceptionContext,
128169
) {
129170
let esr = unsafe { mrs!(ESR_EL1) };
130171
let far = unsafe { mrs!(FAR_EL1) };
@@ -134,6 +175,17 @@ pub(super) extern "C" fn handle_exception(
134175
if handle_internal_fault(exn, far) {
135176
return;
136177
}
178+
179+
let mut elr = unsafe { mrs!(ELR_EL1) };
180+
// SAFETY: Exception entry passes its live, uniquely borrowed save area.
181+
let registers = unsafe { &mut (*regs).x };
182+
if try_handle_registered_exception(exn, esr, far, &mut elr, registers) {
183+
// SAFETY: The callback supplies the resume address for eret.
184+
unsafe {
185+
msr!(ELR_EL1, elr);
186+
}
187+
return;
188+
}
137189
}
138190

139191
// Die with some diagnostic information

src/hyperlight_guest_bin/src/arch/aarch64/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub mod dispatch {
3939
);
4040
}
4141

42-
mod exception;
42+
pub(crate) mod exception;
4343

4444
macro_rules! msr {
4545
($sysreg:ident, $expr:expr) => {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// Copyright 2025 The Hyperlight Authors.
33

4+
#[cfg(target_arch = "x86_64")]
45
pub mod arch {
56
pub use crate::arch::context::Context;
67
pub use crate::arch::exception::handle::HANDLERS;
78
pub use crate::arch::machine::ExceptionInfo;
89
}
10+
11+
#[cfg(target_arch = "aarch64")]
12+
pub mod arch {
13+
pub use hyperlight_common::arch::exn::Exception;
14+
15+
pub use crate::arch::exception::handle::{
16+
ExceptionHandler, register_exception_handler, unregister_exception_handler,
17+
};
18+
}

src/hyperlight_guest_bin/src/lib.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ use hyperlight_guest::guest_handle::handle::GuestHandle;
2323
#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/mod.rs")]
2424
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")]
2525
mod arch;
26-
// temporarily expose the architecture-specific exception interface;
27-
// this should be replaced with something a bit more abstract in the
28-
// near future.
29-
#[cfg(target_arch = "x86_64")]
26+
// Temporarily expose architecture-specific exception interfaces.
3027
pub mod exception;
3128
pub mod guest_function {
3229
pub(super) mod call;

0 commit comments

Comments
 (0)