• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use core::convert::TryInto;
2 
3 use armv4t_emu::{reg, Memory};
4 use gdbstub::target;
5 use gdbstub::target::ext::base::singlethread::{
6     GdbInterrupt, ResumeAction, SingleThreadOps, SingleThreadReverseContOps,
7     SingleThreadReverseStepOps, StopReason,
8 };
9 use gdbstub::target::ext::breakpoints::WatchKind;
10 use gdbstub::target::{Target, TargetError, TargetResult};
11 use gdbstub_arch::arm::reg::id::ArmCoreRegId;
12 
13 use crate::emu::{Emu, Event};
14 
15 // Additional GDB extensions
16 
17 mod breakpoints;
18 mod extended_mode;
19 mod monitor_cmd;
20 mod section_offsets;
21 mod target_description_xml_override;
22 
23 /// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`.
cpu_reg_id(id: ArmCoreRegId) -> Option<u8>24 fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
25     match id {
26         ArmCoreRegId::Gpr(i) => Some(i),
27         ArmCoreRegId::Sp => Some(reg::SP),
28         ArmCoreRegId::Lr => Some(reg::LR),
29         ArmCoreRegId::Pc => Some(reg::PC),
30         ArmCoreRegId::Cpsr => Some(reg::CPSR),
31         _ => None,
32     }
33 }
34 
35 impl Target for Emu {
36     type Arch = gdbstub_arch::arm::Armv4t;
37     type Error = &'static str;
38 
39     // --------------- IMPORTANT NOTE ---------------
40     // Always remember to annotate IDET enable methods with `inline(always)`!
41     // Without this annotation, LLVM might fail to dead-code-eliminate nested IDET
42     // implementations, resulting in unnecessary binary bloat.
43 
44     #[inline(always)]
base_ops(&mut self) -> target::ext::base::BaseOps<Self::Arch, Self::Error>45     fn base_ops(&mut self) -> target::ext::base::BaseOps<Self::Arch, Self::Error> {
46         target::ext::base::BaseOps::SingleThread(self)
47     }
48 
49     #[inline(always)]
breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>>50     fn breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>> {
51         Some(self)
52     }
53 
54     #[inline(always)]
extended_mode(&mut self) -> Option<target::ext::extended_mode::ExtendedModeOps<Self>>55     fn extended_mode(&mut self) -> Option<target::ext::extended_mode::ExtendedModeOps<Self>> {
56         Some(self)
57     }
58 
59     #[inline(always)]
monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<Self>>60     fn monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<Self>> {
61         Some(self)
62     }
63 
64     #[inline(always)]
section_offsets(&mut self) -> Option<target::ext::section_offsets::SectionOffsetsOps<Self>>65     fn section_offsets(&mut self) -> Option<target::ext::section_offsets::SectionOffsetsOps<Self>> {
66         Some(self)
67     }
68 
69     #[inline(always)]
target_description_xml_override( &mut self, ) -> Option<target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<Self>>70     fn target_description_xml_override(
71         &mut self,
72     ) -> Option<target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<Self>>
73     {
74         Some(self)
75     }
76 }
77 
78 impl Emu {
inner_resume( &mut self, action: ResumeAction, mut check_gdb_interrupt: impl FnMut() -> bool, ) -> Result<StopReason<u32>, &'static str>79     fn inner_resume(
80         &mut self,
81         action: ResumeAction,
82         mut check_gdb_interrupt: impl FnMut() -> bool,
83     ) -> Result<StopReason<u32>, &'static str> {
84         let event = match action {
85             ResumeAction::Step => match self.step() {
86                 Some(e) => e,
87                 None => return Ok(StopReason::DoneStep),
88             },
89             ResumeAction::Continue => {
90                 let mut cycles = 0;
91                 loop {
92                     if let Some(event) = self.step() {
93                         break event;
94                     };
95 
96                     // check for GDB interrupt every 1024 instructions
97                     cycles += 1;
98                     if cycles % 1024 == 0 && check_gdb_interrupt() {
99                         return Ok(StopReason::GdbInterrupt);
100                     }
101                 }
102             }
103             _ => return Err("cannot resume with signal"),
104         };
105 
106         Ok(match event {
107             Event::Halted => StopReason::Terminated(19), // SIGSTOP
108             Event::Break => StopReason::SwBreak,
109             Event::WatchWrite(addr) => StopReason::Watch {
110                 kind: WatchKind::Write,
111                 addr,
112             },
113             Event::WatchRead(addr) => StopReason::Watch {
114                 kind: WatchKind::Read,
115                 addr,
116             },
117         })
118     }
119 }
120 
121 impl SingleThreadOps for Emu {
resume( &mut self, action: ResumeAction, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>122     fn resume(
123         &mut self,
124         action: ResumeAction,
125         gdb_interrupt: GdbInterrupt<'_>,
126     ) -> Result<StopReason<u32>, Self::Error> {
127         let mut gdb_interrupt = gdb_interrupt.no_async();
128         self.inner_resume(action, || gdb_interrupt.pending())
129     }
130 
read_registers( &mut self, regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, ) -> TargetResult<(), Self>131     fn read_registers(
132         &mut self,
133         regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
134     ) -> TargetResult<(), Self> {
135         let mode = self.cpu.mode();
136 
137         for i in 0..13 {
138             regs.r[i] = self.cpu.reg_get(mode, i as u8);
139         }
140         regs.sp = self.cpu.reg_get(mode, reg::SP);
141         regs.lr = self.cpu.reg_get(mode, reg::LR);
142         regs.pc = self.cpu.reg_get(mode, reg::PC);
143         regs.cpsr = self.cpu.reg_get(mode, reg::CPSR);
144 
145         Ok(())
146     }
147 
write_registers( &mut self, regs: &gdbstub_arch::arm::reg::ArmCoreRegs, ) -> TargetResult<(), Self>148     fn write_registers(
149         &mut self,
150         regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
151     ) -> TargetResult<(), Self> {
152         let mode = self.cpu.mode();
153 
154         for i in 0..13 {
155             self.cpu.reg_set(mode, i, regs.r[i as usize]);
156         }
157         self.cpu.reg_set(mode, reg::SP, regs.sp);
158         self.cpu.reg_set(mode, reg::LR, regs.lr);
159         self.cpu.reg_set(mode, reg::PC, regs.pc);
160         self.cpu.reg_set(mode, reg::CPSR, regs.cpsr);
161 
162         Ok(())
163     }
164 
read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self>165     fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
166         for (addr, val) in (start_addr..).zip(data.iter_mut()) {
167             *val = self.mem.r8(addr)
168         }
169         Ok(())
170     }
171 
write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self>172     fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> {
173         for (addr, val) in (start_addr..).zip(data.iter().copied()) {
174             self.mem.w8(addr, val)
175         }
176         Ok(())
177     }
178 
179     #[inline(always)]
single_register_access( &mut self, ) -> Option<target::ext::base::SingleRegisterAccessOps<(), Self>>180     fn single_register_access(
181         &mut self,
182     ) -> Option<target::ext::base::SingleRegisterAccessOps<(), Self>> {
183         Some(self)
184     }
185 
186     #[inline(always)]
support_reverse_cont(&mut self) -> Option<SingleThreadReverseContOps<Self>>187     fn support_reverse_cont(&mut self) -> Option<SingleThreadReverseContOps<Self>> {
188         Some(self)
189     }
190 
191     #[inline(always)]
support_reverse_step(&mut self) -> Option<SingleThreadReverseStepOps<Self>>192     fn support_reverse_step(&mut self) -> Option<SingleThreadReverseStepOps<Self>> {
193         Some(self)
194     }
195 
196     #[inline(always)]
support_resume_range_step( &mut self, ) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<Self>>197     fn support_resume_range_step(
198         &mut self,
199     ) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<Self>> {
200         Some(self)
201     }
202 }
203 
204 impl target::ext::base::SingleRegisterAccess<()> for Emu {
read_register( &mut self, _tid: (), reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId, dst: &mut [u8], ) -> TargetResult<(), Self>205     fn read_register(
206         &mut self,
207         _tid: (),
208         reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
209         dst: &mut [u8],
210     ) -> TargetResult<(), Self> {
211         if let Some(i) = cpu_reg_id(reg_id) {
212             let w = self.cpu.reg_get(self.cpu.mode(), i);
213             dst.copy_from_slice(&w.to_le_bytes());
214             Ok(())
215         } else {
216             Err(().into())
217         }
218     }
219 
write_register( &mut self, _tid: (), reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId, val: &[u8], ) -> TargetResult<(), Self>220     fn write_register(
221         &mut self,
222         _tid: (),
223         reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
224         val: &[u8],
225     ) -> TargetResult<(), Self> {
226         let w = u32::from_le_bytes(
227             val.try_into()
228                 .map_err(|_| TargetError::Fatal("invalid data"))?,
229         );
230         if let Some(i) = cpu_reg_id(reg_id) {
231             self.cpu.reg_set(self.cpu.mode(), i, w);
232             Ok(())
233         } else {
234             Err(().into())
235         }
236     }
237 }
238 
239 impl target::ext::base::singlethread::SingleThreadReverseCont for Emu {
reverse_cont( &mut self, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>240     fn reverse_cont(
241         &mut self,
242         gdb_interrupt: GdbInterrupt<'_>,
243     ) -> Result<StopReason<u32>, Self::Error> {
244         // FIXME: actually implement reverse step
245         eprintln!(
246             "FIXME: Not actually reverse-continuing. Performing forwards continue instead..."
247         );
248         self.resume(ResumeAction::Continue, gdb_interrupt)
249     }
250 }
251 
252 impl target::ext::base::singlethread::SingleThreadReverseStep for Emu {
reverse_step( &mut self, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>253     fn reverse_step(
254         &mut self,
255         gdb_interrupt: GdbInterrupt<'_>,
256     ) -> Result<StopReason<u32>, Self::Error> {
257         // FIXME: actually implement reverse step
258         eprintln!(
259             "FIXME: Not actually reverse-stepping. Performing single forwards step instead..."
260         );
261         self.resume(ResumeAction::Step, gdb_interrupt)
262     }
263 }
264 
265 impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu {
resume_range_step( &mut self, start: u32, end: u32, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>266     fn resume_range_step(
267         &mut self,
268         start: u32,
269         end: u32,
270         gdb_interrupt: GdbInterrupt<'_>,
271     ) -> Result<StopReason<u32>, Self::Error> {
272         let mut gdb_interrupt = gdb_interrupt.no_async();
273         loop {
274             match self.inner_resume(ResumeAction::Step, || gdb_interrupt.pending())? {
275                 StopReason::DoneStep => {}
276                 stop_reason => return Ok(stop_reason),
277             }
278 
279             if !(start..end).contains(&self.cpu.reg_get(self.cpu.mode(), reg::PC)) {
280                 return Ok(StopReason::DoneStep);
281             }
282         }
283     }
284 }
285