|
1 | 1 | mod event_loop; |
2 | 2 | pub mod target; |
3 | 3 |
|
| 4 | +use std::io::{self, ErrorKind}; |
4 | 5 | use std::net::TcpListener; |
5 | 6 | use std::thread; |
6 | 7 |
|
7 | 8 | use crossbeam_channel::{Receiver, Sender, TryRecvError}; |
8 | 9 | use event_loop::event_loop_thread; |
| 10 | +use gdbstub::arch::Arch; |
9 | 11 | use gdbstub::conn::ConnectionExt; |
10 | | -use gdbstub::stub::GdbStub; |
11 | | -use target::HyperlightKvmSandboxTarget; |
| 12 | +use gdbstub::stub::{BaseStopReason, GdbStub}; |
| 13 | +use gdbstub::target::Target; |
12 | 14 |
|
13 | 15 | #[derive(Debug)] |
14 | 16 | pub enum GdbTargetError { |
15 | 17 | BindError, |
16 | 18 | InstructionPointerError, |
17 | 19 | ListenerError, |
18 | | - QueueError, |
19 | 20 | ReadRegistersError, |
20 | 21 | ReceiveMsgError, |
21 | 22 | CannotResume, |
| 23 | + QueueError, |
22 | 24 | SendMsgError, |
23 | 25 | SetGuestDebugError, |
24 | | - SpawnThreadError, |
25 | 26 | InvalidGva, |
26 | 27 | UnexpectedMessageError, |
27 | 28 | WriteRegistersError, |
| 29 | + UnexpectedError, |
| 30 | +} |
| 31 | + |
| 32 | +impl From<io::Error> for GdbTargetError { |
| 33 | + fn from(err: io::Error) -> Self { |
| 34 | + match err.kind() { |
| 35 | + ErrorKind::AddrInUse => Self::BindError, |
| 36 | + ErrorKind::AddrNotAvailable => Self::BindError, |
| 37 | + ErrorKind::ConnectionReset |
| 38 | + | ErrorKind::ConnectionAborted |
| 39 | + | ErrorKind::ConnectionRefused => Self::ListenerError, |
| 40 | + _ => Self::UnexpectedError, |
| 41 | + } |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +impl From<DebugMessage> for GdbTargetError { |
| 46 | + fn from(value: DebugMessage) -> Self { |
| 47 | + match value { |
| 48 | + DebugMessage::VcpuStoppedEv => GdbTargetError::UnexpectedMessageError, |
| 49 | + _ => GdbTargetError::UnexpectedMessageError, |
| 50 | + } |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +impl From<TryRecvError> for GdbTargetError { |
| 55 | + fn from(_value: TryRecvError) -> Self { |
| 56 | + GdbTargetError::QueueError |
| 57 | + } |
28 | 58 | } |
29 | 59 |
|
30 | 60 | /// Trait that provides common communication methods for targets |
31 | | -pub trait GdbDebug { |
| 61 | +pub trait GdbDebug: Target { |
32 | 62 | /// Sends a message to the Hypervisor |
33 | | - fn send(&self, ev: DebugMessage) -> Result<(), GdbTargetError>; |
| 63 | + fn send(&self, ev: DebugMessage) -> Result<(), <Self as Target>::Error>; |
34 | 64 | /// Waits for a message from the Hypervisor |
35 | | - fn recv(&self) -> Result<DebugMessage, GdbTargetError>; |
| 65 | + fn recv(&self) -> Result<DebugMessage, <Self as Target>::Error>; |
36 | 66 | /// Checks for a pending message from the Hypervisor |
37 | 67 | fn try_recv(&self) -> Result<DebugMessage, TryRecvError>; |
| 68 | + |
| 69 | + /// Marks the vCPU as paused |
| 70 | + fn pause_vcpu(&mut self); |
| 71 | + /// Resumes the vCPU |
| 72 | + fn resume_vcpu(&mut self) -> Result<(), <Self as Target>::Error>; |
| 73 | + /// Returns the reason why vCPU stopped |
| 74 | + #[allow(clippy::type_complexity)] |
| 75 | + fn get_stop_reason( |
| 76 | + &self, |
| 77 | + ) -> Result<Option<BaseStopReason<(), <Self::Arch as Arch>::Usize>>, Self::Error>; |
38 | 78 | } |
39 | 79 |
|
40 | 80 | /// Event sent to the VCPU execution loop |
@@ -93,46 +133,52 @@ impl GdbConnection { |
93 | 133 | } |
94 | 134 |
|
95 | 135 | /// Creates a thread that handles gdb protocol |
96 | | -pub fn create_gdb_thread(mut target: HyperlightKvmSandboxTarget) -> Result<(), GdbTargetError> { |
| 136 | +pub fn create_gdb_thread<T: GdbDebug + Send + 'static>( |
| 137 | + mut target: T, |
| 138 | +) -> Result<(), <T as Target>::Error> |
| 139 | +where |
| 140 | + <T as Target>::Error: |
| 141 | + std::fmt::Debug + Send + From<io::Error> + From<DebugMessage> + From<TryRecvError>, |
| 142 | +{ |
97 | 143 | // TODO: Address multiple sandboxes scenario |
98 | 144 | let socket = format!("localhost:{}", 8081); |
99 | 145 |
|
100 | 146 | log::info!("Listening on {:?}", socket); |
101 | | - let listener = TcpListener::bind(socket).map_err(|_| GdbTargetError::BindError)?; |
| 147 | + let listener = TcpListener::bind(socket)?; |
102 | 148 |
|
103 | 149 | log::info!("Starting GDB thread"); |
104 | 150 | let _handle = thread::Builder::new() |
105 | 151 | .name("GDB handler".to_string()) |
106 | | - .spawn(move || -> Result<(), GdbTargetError> { |
107 | | - let mut initial_conn = true; |
108 | | - let result = loop { |
109 | | - log::info!("Waiting for GDB connection ... "); |
110 | | - let (conn, _) = listener |
111 | | - .accept() |
112 | | - .map_err(|_| GdbTargetError::ListenerError)?; |
113 | | - |
114 | | - let conn: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(conn); |
115 | | - let debugger = GdbStub::new(conn); |
116 | | - |
117 | | - if initial_conn { |
118 | | - // Waits for vCPU to stop at entrypoint breakpoint |
119 | | - if let DebugMessage::VcpuStoppedEv = target.recv()? { |
120 | | - target.pause_vcpu(); |
121 | | - |
122 | | - event_loop_thread(debugger, &mut target); |
123 | | - initial_conn = false; |
| 152 | + .spawn( |
| 153 | + move || -> Result<(), <T as gdbstub::target::Target>::Error> { |
| 154 | + let mut initial_conn = true; |
| 155 | + let result = loop { |
| 156 | + log::info!("Waiting for GDB connection ... "); |
| 157 | + let (conn, _) = listener.accept().map_err(<T as Target>::Error::from)?; |
| 158 | + |
| 159 | + let conn: Box<dyn ConnectionExt<Error = io::Error>> = Box::new(conn); |
| 160 | + let debugger = GdbStub::new(conn); |
| 161 | + |
| 162 | + if initial_conn { |
| 163 | + // Waits for vCPU to stop at entrypoint breakpoint |
| 164 | + let res = target.recv()?; |
| 165 | + if let DebugMessage::VcpuStoppedEv = res { |
| 166 | + target.pause_vcpu(); |
| 167 | + |
| 168 | + event_loop_thread(debugger, &mut target); |
| 169 | + initial_conn = false; |
| 170 | + } else { |
| 171 | + break Err(res)?; |
| 172 | + } |
124 | 173 | } else { |
125 | | - break Err(GdbTargetError::UnexpectedMessageError); |
| 174 | + log::info!("Reattaching GDB connection ... "); |
| 175 | + event_loop_thread(debugger, &mut target); |
126 | 176 | } |
127 | | - } else { |
128 | | - log::info!("Reattaching GDB connection ... "); |
129 | | - event_loop_thread(debugger, &mut target); |
130 | | - } |
131 | | - }; |
132 | | - |
133 | | - result |
134 | | - }) |
135 | | - .map_err(|_| GdbTargetError::SpawnThreadError)?; |
| 177 | + }; |
| 178 | + |
| 179 | + result |
| 180 | + }, |
| 181 | + ); |
136 | 182 |
|
137 | 183 | Ok(()) |
138 | 184 | } |
0 commit comments