Skip to content

Commit 0a03f81

Browse files
committed
add support for interrupts
Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent cfaefc3 commit 0a03f81

File tree

4 files changed

+54
-10
lines changed

4 files changed

+54
-10
lines changed

src/hyperlight_host/src/hypervisor/gdb/event_loop.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
use gdbstub::common::Signal;
1718
use gdbstub::conn::ConnectionExt;
1819
use gdbstub::stub::run_blocking::{self, WaitForStopReasonError};
1920
use gdbstub::stub::{BaseStopReason, DisconnectReason, GdbStub, SingleThreadStopReason};
21+
use libc::{pthread_kill, SIGRTMIN};
2022

2123
use super::x86_64_target::HyperlightSandboxTarget;
22-
use super::{DebugResponse, VcpuStopReason};
24+
use super::{DebugResponse, GdbTargetError, VcpuStopReason};
2325

2426
pub struct GdbBlockingEventLoop;
2527

@@ -48,7 +50,15 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
4850
VcpuStopReason::DoneStep => BaseStopReason::DoneStep,
4951
VcpuStopReason::SwBp => BaseStopReason::SwBreak(()),
5052
VcpuStopReason::HwBp => BaseStopReason::HwBreak(()),
53+
// This is a consequence of the GDB client sending an interrupt signal
54+
// to the target thread
55+
VcpuStopReason::Interrupt => BaseStopReason::SignalWithThread {
56+
tid: (),
57+
signal: Signal(SIGRTMIN() as u8),
58+
},
5159
VcpuStopReason::Unknown => {
60+
log::warn!("Unknown stop reason - resuming execution");
61+
5262
target
5363
.resume_vcpu()
5464
.map_err(WaitForStopReasonError::Target)?;
@@ -87,8 +97,20 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
8797
/// This function is called when the GDB client sends an interrupt signal.
8898
/// Passing `None` defers sending a stop reason to later (e.g. when the target stops).
8999
fn on_interrupt(
90-
_target: &mut Self::Target,
100+
target: &mut Self::Target,
91101
) -> Result<Option<Self::StopReason>, <Self::Target as gdbstub::target::Target>::Error> {
102+
log::info!("Received interrupt from GDB client - sending signal to target thread");
103+
104+
// Send a signal to the target thread to interrupt it
105+
let ret = unsafe { pthread_kill(target.get_thread_id(), SIGRTMIN()) };
106+
107+
log::info!("pthread_kill returned {}", ret);
108+
109+
if ret < 0 && ret != libc::ESRCH {
110+
log::error!("Failed to send signal to target thread");
111+
return Err(GdbTargetError::SendSignalError);
112+
}
113+
92114
Ok(None)
93115
}
94116
}

src/hyperlight_host/src/hypervisor/gdb/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub enum GdbTargetError {
3939
CannotReceiveMsg,
4040
#[error("Error encountered when sending message")]
4141
CannotSendMsg,
42+
#[error("Error encountered when sending a signal to the hypervisor thread")]
43+
SendSignalError,
4244
#[error("Encountered an unexpected message over communication channel")]
4345
UnexpectedMessage,
4446
#[error("Unexpected error encountered")]
@@ -93,6 +95,7 @@ pub enum VcpuStopReason {
9395
DoneStep,
9496
HwBp,
9597
SwBp,
98+
Interrupt,
9699
Unknown,
97100
}
98101

@@ -178,6 +181,7 @@ impl<T, U> DebugCommChannel<T, U> {
178181
/// Creates a thread that handles gdb protocol
179182
pub fn create_gdb_thread(
180183
port: u16,
184+
thread_id: u64,
181185
) -> Result<DebugCommChannel<DebugResponse, DebugMsg>, GdbTargetError> {
182186
let (gdb_conn, hyp_conn) = DebugCommChannel::unbounded();
183187
let socket = format!("localhost:{}", port);
@@ -195,7 +199,7 @@ pub fn create_gdb_thread(
195199
let conn: Box<dyn ConnectionExt<Error = io::Error>> = Box::new(conn);
196200
let debugger = GdbStub::new(conn);
197201

198-
let mut target = HyperlightSandboxTarget::new(hyp_conn);
202+
let mut target = HyperlightSandboxTarget::new(hyp_conn, thread_id);
199203

200204
// Waits for vCPU to stop at entrypoint breakpoint
201205
let res = target.recv()?;

src/hyperlight_host/src/hypervisor/gdb/x86_64_target.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,16 @@ use super::{DebugCommChannel, DebugMsg, DebugResponse, GdbTargetError, X86_64Reg
3535
pub struct HyperlightSandboxTarget {
3636
/// Hypervisor communication channels
3737
hyp_conn: DebugCommChannel<DebugMsg, DebugResponse>,
38+
/// Thread ID
39+
thread_id: u64,
3840
}
3941

4042
impl HyperlightSandboxTarget {
41-
pub fn new(hyp_conn: DebugCommChannel<DebugMsg, DebugResponse>) -> Self {
42-
HyperlightSandboxTarget { hyp_conn }
43+
pub fn new(hyp_conn: DebugCommChannel<DebugMsg, DebugResponse>, thread_id: u64) -> Self {
44+
HyperlightSandboxTarget {
45+
hyp_conn,
46+
thread_id,
47+
}
4348
}
4449

4550
/// Sends a command over the communication channel and waits for response
@@ -55,6 +60,11 @@ impl HyperlightSandboxTarget {
5560
self.hyp_conn.send(ev)
5661
}
5762

63+
/// Returns the thread ID
64+
pub fn get_thread_id(&self) -> u64 {
65+
self.thread_id
66+
}
67+
5868
/// Waits for a response over the communication channel
5969
pub fn recv(&self) -> Result<DebugResponse, GdbTargetError> {
6070
self.hyp_conn.recv()
@@ -366,6 +376,8 @@ impl SwBreakpoint for HyperlightSandboxTarget {
366376
}
367377

368378
impl SingleThreadResume for HyperlightSandboxTarget {
379+
/// Resumes the execution of the vCPU
380+
/// Note: We do not handle signals passed to this method
369381
fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
370382
log::debug!("Resume");
371383
self.resume_vcpu()
@@ -376,9 +388,9 @@ impl SingleThreadResume for HyperlightSandboxTarget {
376388
}
377389

378390
impl SingleThreadSingleStep for HyperlightSandboxTarget {
379-
fn step(&mut self, signal: Option<Signal>) -> Result<(), Self::Error> {
380-
assert!(signal.is_none());
381-
391+
/// Steps the vCPU execution by
392+
/// Note: We do not handle signals passed to this method
393+
fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
382394
log::debug!("Step");
383395
match self.send_command(DebugMsg::Step)? {
384396
DebugResponse::Step => Ok(()),
@@ -404,7 +416,7 @@ mod tests {
404416
fn test_gdb_target() {
405417
let (gdb_conn, hyp_conn) = DebugCommChannel::unbounded();
406418

407-
let mut target = HyperlightSandboxTarget::new(hyp_conn);
419+
let mut target = HyperlightSandboxTarget::new(hyp_conn, 0);
408420

409421
// Check response to read registers - send the response first to not be blocked
410422
// by the recv call in the target

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ impl KVMDriver {
634634
#[cfg(gdb)]
635635
let (debug, gdb_conn) = {
636636
if let Some(DebugInfo { port }) = debug_info {
637-
let gdb_conn = create_gdb_thread(*port);
637+
let gdb_conn = create_gdb_thread(*port, unsafe { pthread_self() });
638638

639639
// in case the gdb thread creation fails, we still want to continue
640640
// without gdb
@@ -875,7 +875,13 @@ impl Hypervisor for KVMDriver {
875875
HyperlightExit::Debug(reason)
876876
}
877877
Err(e) => match e.errno() {
878+
// In case of the gdb feature, the timeout is not enabled, this
879+
// exit is because of a signal sent from the gdb thread to the
880+
// hypervisor thread to cancel execution
881+
#[cfg(gdb)]
882+
libc::EINTR => HyperlightExit::Debug(VcpuStopReason::Interrupt),
878883
// we send a signal to the thread to cancel execution this results in EINTR being returned by KVM so we return Cancelled
884+
#[cfg(not(gdb))]
879885
libc::EINTR => HyperlightExit::Cancelled(),
880886
libc::EAGAIN => HyperlightExit::Retry(),
881887
_ => {

0 commit comments

Comments
 (0)