1 use armv4t_emu::{reg, Memory};
2
3 use gdbstub::common::Tid;
4 use gdbstub::target;
5 use gdbstub::target::ext::base::multithread::{
6 GdbInterrupt, MultiThreadOps, ResumeAction, ThreadStopReason,
7 };
8 use gdbstub::target::ext::breakpoints::WatchKind;
9 use gdbstub::target::{Target, TargetError, TargetResult};
10
11 use crate::emu::{CpuId, Emu, Event};
12
event_to_stopreason(e: Event, id: CpuId) -> ThreadStopReason<u32>13 fn event_to_stopreason(e: Event, id: CpuId) -> ThreadStopReason<u32> {
14 let tid = cpuid_to_tid(id);
15 match e {
16 Event::Halted => ThreadStopReason::Terminated(19), // SIGSTOP
17 Event::Break => ThreadStopReason::SwBreak(tid),
18 Event::WatchWrite(addr) => ThreadStopReason::Watch {
19 tid,
20 kind: WatchKind::Write,
21 addr,
22 },
23 Event::WatchRead(addr) => ThreadStopReason::Watch {
24 tid,
25 kind: WatchKind::Read,
26 addr,
27 },
28 }
29 }
30
cpuid_to_tid(id: CpuId) -> Tid31 fn cpuid_to_tid(id: CpuId) -> Tid {
32 match id {
33 CpuId::Cpu => Tid::new(1).unwrap(),
34 CpuId::Cop => Tid::new(2).unwrap(),
35 }
36 }
37
tid_to_cpuid(tid: Tid) -> Result<CpuId, &'static str>38 fn tid_to_cpuid(tid: Tid) -> Result<CpuId, &'static str> {
39 match tid.get() {
40 1 => Ok(CpuId::Cpu),
41 2 => Ok(CpuId::Cop),
42 _ => Err("specified invalid core"),
43 }
44 }
45
46 impl Target for Emu {
47 type Arch = gdbstub_arch::arm::Armv4t;
48 type Error = &'static str;
49
50 #[inline(always)]
base_ops(&mut self) -> target::ext::base::BaseOps<Self::Arch, Self::Error>51 fn base_ops(&mut self) -> target::ext::base::BaseOps<Self::Arch, Self::Error> {
52 target::ext::base::BaseOps::MultiThread(self)
53 }
54
55 #[inline(always)]
breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>>56 fn breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>> {
57 Some(self)
58 }
59 }
60
61 impl MultiThreadOps for Emu {
resume( &mut self, default_resume_action: ResumeAction, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<ThreadStopReason<u32>, Self::Error>62 fn resume(
63 &mut self,
64 default_resume_action: ResumeAction,
65 gdb_interrupt: GdbInterrupt<'_>,
66 ) -> Result<ThreadStopReason<u32>, Self::Error> {
67 // In general, the behavior of multi-threaded systems during debugging is
68 // determined by the system scheduler. On certain systems, this behavior can be
69 // configured using the GDB command `set scheduler-locking _mode_`, but at the
70 // moment, `gdbstub` doesn't plumb-through that configuration command.
71
72 let default_resume_action_is_step = match default_resume_action {
73 ResumeAction::Step => true,
74 ResumeAction::Continue => false,
75 _ => return Err("no support for resuming with signal"),
76 };
77
78 match self
79 .resume_action_is_step
80 .unwrap_or(default_resume_action_is_step)
81 {
82 true => match self.step() {
83 Some((event, id)) => Ok(event_to_stopreason(event, id)),
84 None => Ok(ThreadStopReason::DoneStep),
85 },
86 false => {
87 let mut gdb_interrupt = gdb_interrupt.no_async();
88 let mut cycles: usize = 0;
89 loop {
90 // check for GDB interrupt every 1024 instructions
91 if cycles % 1024 == 0 && gdb_interrupt.pending() {
92 return Ok(ThreadStopReason::GdbInterrupt);
93 }
94 cycles += 1;
95
96 if let Some((event, id)) = self.step() {
97 return Ok(event_to_stopreason(event, id));
98 };
99 }
100 }
101 }
102 }
103
104 // FIXME: properly handle multiple actions
clear_resume_actions(&mut self) -> Result<(), Self::Error>105 fn clear_resume_actions(&mut self) -> Result<(), Self::Error> {
106 self.resume_action_is_step = None;
107 Ok(())
108 }
109
110 // FIXME: properly handle multiple actions
set_resume_action(&mut self, _tid: Tid, action: ResumeAction) -> Result<(), Self::Error>111 fn set_resume_action(&mut self, _tid: Tid, action: ResumeAction) -> Result<(), Self::Error> {
112 // in this emulator, each core runs in lock-step, so we don't actually care
113 // about the specific tid. In real integrations, you very much should!
114
115 if self.resume_action_is_step.is_some() {
116 return Ok(());
117 }
118
119 self.resume_action_is_step = match action {
120 ResumeAction::Step => Some(true),
121 ResumeAction::Continue => Some(false),
122 _ => return Err("no support for resuming with signal"),
123 };
124
125 Ok(())
126 }
127
read_registers( &mut self, regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self>128 fn read_registers(
129 &mut self,
130 regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
131 tid: Tid,
132 ) -> TargetResult<(), Self> {
133 let cpu = match tid_to_cpuid(tid).map_err(TargetError::Fatal)? {
134 CpuId::Cpu => &mut self.cpu,
135 CpuId::Cop => &mut self.cop,
136 };
137
138 let mode = cpu.mode();
139
140 for i in 0..13 {
141 regs.r[i] = cpu.reg_get(mode, i as u8);
142 }
143 regs.sp = cpu.reg_get(mode, reg::SP);
144 regs.lr = cpu.reg_get(mode, reg::LR);
145 regs.pc = cpu.reg_get(mode, reg::PC);
146 regs.cpsr = cpu.reg_get(mode, reg::CPSR);
147
148 Ok(())
149 }
150
write_registers( &mut self, regs: &gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self>151 fn write_registers(
152 &mut self,
153 regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
154 tid: Tid,
155 ) -> TargetResult<(), Self> {
156 let cpu = match tid_to_cpuid(tid).map_err(TargetError::Fatal)? {
157 CpuId::Cpu => &mut self.cpu,
158 CpuId::Cop => &mut self.cop,
159 };
160
161 let mode = cpu.mode();
162
163 for i in 0..13 {
164 cpu.reg_set(mode, i, regs.r[i as usize]);
165 }
166 cpu.reg_set(mode, reg::SP, regs.sp);
167 cpu.reg_set(mode, reg::LR, regs.lr);
168 cpu.reg_set(mode, reg::PC, regs.pc);
169 cpu.reg_set(mode, reg::CPSR, regs.cpsr);
170
171 Ok(())
172 }
173
read_addrs( &mut self, start_addr: u32, data: &mut [u8], _tid: Tid, ) -> TargetResult<(), Self>174 fn read_addrs(
175 &mut self,
176 start_addr: u32,
177 data: &mut [u8],
178 _tid: Tid, // same address space for each core
179 ) -> TargetResult<(), Self> {
180 for (addr, val) in (start_addr..).zip(data.iter_mut()) {
181 *val = self.mem.r8(addr)
182 }
183 Ok(())
184 }
185
write_addrs( &mut self, start_addr: u32, data: &[u8], _tid: Tid, ) -> TargetResult<(), Self>186 fn write_addrs(
187 &mut self,
188 start_addr: u32,
189 data: &[u8],
190 _tid: Tid, // same address space for each core
191 ) -> TargetResult<(), Self> {
192 for (addr, val) in (start_addr..).zip(data.iter().copied()) {
193 self.mem.w8(addr, val)
194 }
195 Ok(())
196 }
197
list_active_threads( &mut self, register_thread: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>198 fn list_active_threads(
199 &mut self,
200 register_thread: &mut dyn FnMut(Tid),
201 ) -> Result<(), Self::Error> {
202 register_thread(cpuid_to_tid(CpuId::Cpu));
203 register_thread(cpuid_to_tid(CpuId::Cop));
204 Ok(())
205 }
206 }
207
208 impl target::ext::breakpoints::Breakpoints for Emu {
sw_breakpoint(&mut self) -> Option<target::ext::breakpoints::SwBreakpointOps<Self>>209 fn sw_breakpoint(&mut self) -> Option<target::ext::breakpoints::SwBreakpointOps<Self>> {
210 Some(self)
211 }
212
hw_watchpoint(&mut self) -> Option<target::ext::breakpoints::HwWatchpointOps<Self>>213 fn hw_watchpoint(&mut self) -> Option<target::ext::breakpoints::HwWatchpointOps<Self>> {
214 Some(self)
215 }
216 }
217
218 impl target::ext::breakpoints::SwBreakpoint for Emu {
add_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult<bool, Self>219 fn add_sw_breakpoint(
220 &mut self,
221 addr: u32,
222 _kind: gdbstub_arch::arm::ArmBreakpointKind,
223 ) -> TargetResult<bool, Self> {
224 self.breakpoints.push(addr);
225 Ok(true)
226 }
227
remove_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult<bool, Self>228 fn remove_sw_breakpoint(
229 &mut self,
230 addr: u32,
231 _kind: gdbstub_arch::arm::ArmBreakpointKind,
232 ) -> TargetResult<bool, Self> {
233 match self.breakpoints.iter().position(|x| *x == addr) {
234 None => return Ok(false),
235 Some(pos) => self.breakpoints.remove(pos),
236 };
237
238 Ok(true)
239 }
240 }
241
242 impl target::ext::breakpoints::HwWatchpoint for Emu {
add_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self>243 fn add_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self> {
244 self.watchpoints.push(addr);
245
246 let entry = self.watchpoint_kind.entry(addr).or_insert((false, false));
247 match kind {
248 WatchKind::Write => entry.1 = true,
249 WatchKind::Read => entry.0 = true,
250 WatchKind::ReadWrite => entry.0 = true, // arbitrary
251 };
252
253 Ok(true)
254 }
255
remove_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self>256 fn remove_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self> {
257 let entry = self.watchpoint_kind.entry(addr).or_insert((false, false));
258 match kind {
259 WatchKind::Write => entry.1 = false,
260 WatchKind::Read => entry.0 = false,
261 WatchKind::ReadWrite => entry.0 = false, // arbitrary
262 };
263
264 if !self.watchpoint_kind.contains_key(&addr) {
265 let pos = match self.watchpoints.iter().position(|x| *x == addr) {
266 None => return Ok(false),
267 Some(pos) => pos,
268 };
269 self.watchpoints.remove(pos);
270 }
271
272 Ok(true)
273 }
274 }
275