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