1 use crate::emu::CpuId;
2 use crate::emu::Emu;
3 use crate::emu::ExecMode;
4 use armv4t_emu::reg;
5 use armv4t_emu::Memory;
6 use gdbstub::common::Signal;
7 use gdbstub::common::Tid;
8 use gdbstub::target;
9 use gdbstub::target::ext::base::multithread::MultiThreadBase;
10 use gdbstub::target::ext::base::multithread::MultiThreadResume;
11 use gdbstub::target::ext::breakpoints::WatchKind;
12 use gdbstub::target::Target;
13 use gdbstub::target::TargetError;
14 use gdbstub::target::TargetResult;
15
cpuid_to_tid(id: CpuId) -> Tid16 pub fn cpuid_to_tid(id: CpuId) -> Tid {
17 match id {
18 CpuId::Cpu => Tid::new(1).unwrap(),
19 CpuId::Cop => Tid::new(2).unwrap(),
20 }
21 }
22
tid_to_cpuid(tid: Tid) -> Result<CpuId, &'static str>23 fn tid_to_cpuid(tid: Tid) -> Result<CpuId, &'static str> {
24 match tid.get() {
25 1 => Ok(CpuId::Cpu),
26 2 => Ok(CpuId::Cop),
27 _ => Err("specified invalid core"),
28 }
29 }
30
31 impl Target for Emu {
32 type Arch = gdbstub_arch::arm::Armv4t;
33 type Error = &'static str;
34
35 #[inline(always)]
base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error>36 fn base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error> {
37 target::ext::base::BaseOps::MultiThread(self)
38 }
39
40 #[inline(always)]
support_breakpoints( &mut self, ) -> Option<target::ext::breakpoints::BreakpointsOps<'_, Self>>41 fn support_breakpoints(
42 &mut self,
43 ) -> Option<target::ext::breakpoints::BreakpointsOps<'_, Self>> {
44 Some(self)
45 }
46 }
47
48 impl MultiThreadBase for Emu {
read_registers( &mut self, regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self>49 fn read_registers(
50 &mut self,
51 regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
52 tid: Tid,
53 ) -> TargetResult<(), Self> {
54 let cpu = match tid_to_cpuid(tid).map_err(TargetError::Fatal)? {
55 CpuId::Cpu => &mut self.cpu,
56 CpuId::Cop => &mut self.cop,
57 };
58
59 let mode = cpu.mode();
60
61 for i in 0..13 {
62 regs.r[i] = cpu.reg_get(mode, i as u8);
63 }
64 regs.sp = cpu.reg_get(mode, reg::SP);
65 regs.lr = cpu.reg_get(mode, reg::LR);
66 regs.pc = cpu.reg_get(mode, reg::PC);
67 regs.cpsr = cpu.reg_get(mode, reg::CPSR);
68
69 Ok(())
70 }
71
write_registers( &mut self, regs: &gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self>72 fn write_registers(
73 &mut self,
74 regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
75 tid: Tid,
76 ) -> TargetResult<(), Self> {
77 let cpu = match tid_to_cpuid(tid).map_err(TargetError::Fatal)? {
78 CpuId::Cpu => &mut self.cpu,
79 CpuId::Cop => &mut self.cop,
80 };
81
82 let mode = cpu.mode();
83
84 for i in 0..13 {
85 cpu.reg_set(mode, i, regs.r[i as usize]);
86 }
87 cpu.reg_set(mode, reg::SP, regs.sp);
88 cpu.reg_set(mode, reg::LR, regs.lr);
89 cpu.reg_set(mode, reg::PC, regs.pc);
90 cpu.reg_set(mode, reg::CPSR, regs.cpsr);
91
92 Ok(())
93 }
94
read_addrs( &mut self, start_addr: u32, data: &mut [u8], _tid: Tid, ) -> TargetResult<usize, Self>95 fn read_addrs(
96 &mut self,
97 start_addr: u32,
98 data: &mut [u8],
99 _tid: Tid, // same address space for each core
100 ) -> TargetResult<usize, Self> {
101 for (addr, val) in (start_addr..).zip(data.iter_mut()) {
102 *val = self.mem.r8(addr)
103 }
104 Ok(data.len())
105 }
106
write_addrs( &mut self, start_addr: u32, data: &[u8], _tid: Tid, ) -> TargetResult<(), Self>107 fn write_addrs(
108 &mut self,
109 start_addr: u32,
110 data: &[u8],
111 _tid: Tid, // same address space for each core
112 ) -> TargetResult<(), Self> {
113 for (addr, val) in (start_addr..).zip(data.iter().copied()) {
114 self.mem.w8(addr, val)
115 }
116 Ok(())
117 }
118
list_active_threads( &mut self, register_thread: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>119 fn list_active_threads(
120 &mut self,
121 register_thread: &mut dyn FnMut(Tid),
122 ) -> Result<(), Self::Error> {
123 register_thread(cpuid_to_tid(CpuId::Cpu));
124 register_thread(cpuid_to_tid(CpuId::Cop));
125 Ok(())
126 }
127
128 #[inline(always)]
support_resume( &mut self, ) -> Option<target::ext::base::multithread::MultiThreadResumeOps<'_, Self>>129 fn support_resume(
130 &mut self,
131 ) -> Option<target::ext::base::multithread::MultiThreadResumeOps<'_, Self>> {
132 Some(self)
133 }
134
135 #[inline(always)]
support_thread_extra_info( &mut self, ) -> Option<gdbstub::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>>136 fn support_thread_extra_info(
137 &mut self,
138 ) -> Option<gdbstub::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>> {
139 Some(self)
140 }
141 }
142
143 impl MultiThreadResume for Emu {
resume(&mut self) -> Result<(), Self::Error>144 fn resume(&mut self) -> Result<(), Self::Error> {
145 // Upon returning from the `resume` method, the target being debugged should be
146 // configured to run according to whatever resume actions the GDB client has
147 // specified (as specified by `set_resume_action`, `set_resume_range_step`,
148 // `set_reverse_{step, continue}`, etc...)
149 //
150 // In this basic `armv4t_multicore` example, the `resume` method is actually a
151 // no-op, as the execution mode of the emulator's interpreter loop has already
152 // been modified via the various `set_X` methods.
153 //
154 // In more complex implementations, it's likely that the target being debugged
155 // will be running in another thread / process, and will require some kind of
156 // external "orchestration" to set it's execution mode (e.g: modifying the
157 // target's process state via platform specific debugging syscalls).
158
159 Ok(())
160 }
161
clear_resume_actions(&mut self) -> Result<(), Self::Error>162 fn clear_resume_actions(&mut self) -> Result<(), Self::Error> {
163 self.exec_mode.clear();
164 Ok(())
165 }
166
167 #[inline(always)]
support_single_step( &mut self, ) -> Option<target::ext::base::multithread::MultiThreadSingleStepOps<'_, Self>>168 fn support_single_step(
169 &mut self,
170 ) -> Option<target::ext::base::multithread::MultiThreadSingleStepOps<'_, Self>> {
171 Some(self)
172 }
173
set_resume_action_continue( &mut self, tid: Tid, signal: Option<Signal>, ) -> Result<(), Self::Error>174 fn set_resume_action_continue(
175 &mut self,
176 tid: Tid,
177 signal: Option<Signal>,
178 ) -> Result<(), Self::Error> {
179 if signal.is_some() {
180 return Err("no support for continuing with signal");
181 }
182
183 self.exec_mode
184 .insert(tid_to_cpuid(tid)?, ExecMode::Continue);
185
186 Ok(())
187 }
188 }
189
190 impl target::ext::base::multithread::MultiThreadSingleStep for Emu {
set_resume_action_step( &mut self, tid: Tid, signal: Option<Signal>, ) -> Result<(), Self::Error>191 fn set_resume_action_step(
192 &mut self,
193 tid: Tid,
194 signal: Option<Signal>,
195 ) -> Result<(), Self::Error> {
196 if signal.is_some() {
197 return Err("no support for stepping with signal");
198 }
199
200 self.exec_mode.insert(tid_to_cpuid(tid)?, ExecMode::Step);
201
202 Ok(())
203 }
204 }
205
206 impl target::ext::breakpoints::Breakpoints for Emu {
support_sw_breakpoint( &mut self, ) -> Option<target::ext::breakpoints::SwBreakpointOps<'_, Self>>207 fn support_sw_breakpoint(
208 &mut self,
209 ) -> Option<target::ext::breakpoints::SwBreakpointOps<'_, Self>> {
210 Some(self)
211 }
212
support_hw_watchpoint( &mut self, ) -> Option<target::ext::breakpoints::HwWatchpointOps<'_, Self>>213 fn support_hw_watchpoint(
214 &mut self,
215 ) -> Option<target::ext::breakpoints::HwWatchpointOps<'_, Self>> {
216 Some(self)
217 }
218 }
219
220 impl target::ext::breakpoints::SwBreakpoint for Emu {
add_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult<bool, Self>221 fn add_sw_breakpoint(
222 &mut self,
223 addr: u32,
224 _kind: gdbstub_arch::arm::ArmBreakpointKind,
225 ) -> TargetResult<bool, Self> {
226 self.breakpoints.push(addr);
227 Ok(true)
228 }
229
remove_sw_breakpoint( &mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult<bool, Self>230 fn remove_sw_breakpoint(
231 &mut self,
232 addr: u32,
233 _kind: gdbstub_arch::arm::ArmBreakpointKind,
234 ) -> TargetResult<bool, Self> {
235 match self.breakpoints.iter().position(|x| *x == addr) {
236 None => return Ok(false),
237 Some(pos) => self.breakpoints.remove(pos),
238 };
239
240 Ok(true)
241 }
242 }
243
244 impl target::ext::breakpoints::HwWatchpoint for Emu {
add_hw_watchpoint( &mut self, addr: u32, _len: u32, kind: WatchKind, ) -> TargetResult<bool, Self>245 fn add_hw_watchpoint(
246 &mut self,
247 addr: u32,
248 _len: u32, // TODO: properly handle `len` parameter
249 kind: WatchKind,
250 ) -> TargetResult<bool, Self> {
251 self.watchpoints.push(addr);
252
253 let entry = self.watchpoint_kind.entry(addr).or_insert((false, false));
254 match kind {
255 WatchKind::Write => entry.1 = true,
256 WatchKind::Read => entry.0 = true,
257 WatchKind::ReadWrite => entry.0 = true, // arbitrary
258 };
259
260 Ok(true)
261 }
262
remove_hw_watchpoint( &mut self, addr: u32, _len: u32, kind: WatchKind, ) -> TargetResult<bool, Self>263 fn remove_hw_watchpoint(
264 &mut self,
265 addr: u32,
266 _len: u32, // TODO: properly handle `len` parameter
267 kind: WatchKind,
268 ) -> TargetResult<bool, Self> {
269 let entry = self.watchpoint_kind.entry(addr).or_insert((false, false));
270 match kind {
271 WatchKind::Write => entry.1 = false,
272 WatchKind::Read => entry.0 = false,
273 WatchKind::ReadWrite => entry.0 = false, // arbitrary
274 };
275
276 if !self.watchpoint_kind.contains_key(&addr) {
277 let pos = match self.watchpoints.iter().position(|x| *x == addr) {
278 None => return Ok(false),
279 Some(pos) => pos,
280 };
281 self.watchpoints.remove(pos);
282 }
283
284 Ok(true)
285 }
286 }
287
288 impl target::ext::thread_extra_info::ThreadExtraInfo for Emu {
thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result<usize, Self::Error>289 fn thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result<usize, Self::Error> {
290 let cpu_id = tid_to_cpuid(tid)?;
291 let info = format!("CPU {:?}", cpu_id);
292
293 Ok(copy_to_buf(info.as_bytes(), buf))
294 }
295 }
296
297 /// Copy all bytes of `data` to `buf`.
298 /// Return the size of data copied.
copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize299 pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize {
300 let len = buf.len().min(data.len());
301 buf[..len].copy_from_slice(&data[..len]);
302 len
303 }
304