|
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