• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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