1 //! ------------------------------------------------------------------------ !//
2 //! ------------------------------ DISCLAIMER ------------------------------ !//
3 //! ------------------------------------------------------------------------ !//
4 //!
5 //! This code is absolutely awful, and completely slapped together for the sake
6 //! of example. The watchpoint implementation is particularly awful.
7 //!
8 //! While it technically "gets the job done" and provides a simple multicore
9 //! system that can be debugged, it would really merit a re-write, since it's
10 //! not a good example of "proper Rust coding practices"
11 
12 use crate::mem_sniffer::AccessKind;
13 use crate::mem_sniffer::MemSniffer;
14 use crate::DynResult;
15 use armv4t_emu::reg;
16 use armv4t_emu::Cpu;
17 use armv4t_emu::ExampleMem;
18 use armv4t_emu::Memory;
19 use armv4t_emu::Mode;
20 use std::collections::HashMap;
21 
22 const HLE_RETURN_ADDR: u32 = 0x12345678;
23 
24 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25 pub enum CpuId {
26     Cpu,
27     Cop,
28 }
29 
30 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
31 pub enum Event {
32     DoneStep,
33     Halted,
34     Break,
35     WatchWrite(u32),
36     WatchRead(u32),
37 }
38 
39 pub enum ExecMode {
40     Step,
41     Continue,
42 }
43 
44 /// incredibly barebones armv4t-based emulator
45 pub struct Emu {
46     pub(crate) cpu: Cpu,
47     pub(crate) cop: Cpu,
48     pub(crate) mem: ExampleMem,
49 
50     pub(crate) exec_mode: HashMap<CpuId, ExecMode>,
51 
52     pub(crate) watchpoints: Vec<u32>,
53     /// (read, write)
54     pub(crate) watchpoint_kind: HashMap<u32, (bool, bool)>,
55     pub(crate) breakpoints: Vec<u32>,
56 
57     // GDB seems to get gets very confused if two threads are executing the exact same code at the
58     // exact same time. Maybe this is a bug with `gdbstub`?
59     stall_cop_cycles: usize,
60 }
61 
62 impl Emu {
new(program_elf: &[u8]) -> DynResult<Emu>63     pub fn new(program_elf: &[u8]) -> DynResult<Emu> {
64         // set up emulated system
65         let mut cpu = Cpu::new();
66         let mut mem = ExampleMem::new();
67 
68         // load ELF
69         let elf_header = goblin::elf::Elf::parse(program_elf)?;
70 
71         // copy all in-memory sections from the ELF file into system RAM
72         let sections = elf_header
73             .section_headers
74             .iter()
75             .filter(|h| h.is_alloc() && h.sh_type != goblin::elf::section_header::SHT_NOBITS);
76 
77         for h in sections {
78             eprintln!(
79                 "loading section {:?} into memory from [{:#010x?}..{:#010x?}]",
80                 elf_header.shdr_strtab.get_at(h.sh_name).unwrap(),
81                 h.sh_addr,
82                 h.sh_addr + h.sh_size,
83             );
84 
85             for (i, b) in program_elf[h.file_range().unwrap()].iter().enumerate() {
86                 mem.w8(h.sh_addr as u32 + i as u32, *b);
87             }
88         }
89 
90         // setup execution state
91         eprintln!("Setting PC to {:#010x?}", elf_header.entry);
92         cpu.reg_set(Mode::User, reg::SP, 0x10000000);
93         cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR);
94         cpu.reg_set(Mode::User, reg::PC, elf_header.entry as u32);
95         cpu.reg_set(Mode::User, reg::CPSR, 0x10); // user mode
96         let cop = cpu;
97 
98         Ok(Emu {
99             cpu,
100             cop,
101             mem,
102 
103             exec_mode: HashMap::new(),
104 
105             watchpoints: Vec::new(),
106             watchpoint_kind: HashMap::new(),
107             breakpoints: Vec::new(),
108 
109             stall_cop_cycles: 24,
110         })
111     }
112 
step_core(&mut self, id: CpuId) -> Option<Event>113     pub fn step_core(&mut self, id: CpuId) -> Option<Event> {
114         let cpu = match id {
115             CpuId::Cop if self.stall_cop_cycles != 0 => {
116                 self.stall_cop_cycles -= 1;
117                 return None;
118             }
119             CpuId::Cop => &mut self.cop,
120             CpuId::Cpu => &mut self.cpu,
121         };
122 
123         // set up magic memory location
124         self.mem.w8(
125             0xffff_4200,
126             match id {
127                 CpuId::Cpu => 0xaa,
128                 CpuId::Cop => 0x55,
129             },
130         );
131 
132         let mut hit_watchpoint = None;
133         let mut sniffer = MemSniffer::new(&mut self.mem, &self.watchpoints, |access| {
134             hit_watchpoint = Some(access)
135         });
136 
137         cpu.step(&mut sniffer);
138         let pc = cpu.reg_get(Mode::User, reg::PC);
139 
140         if pc == HLE_RETURN_ADDR {
141             match id {
142                 CpuId::Cpu => return Some(Event::Halted),
143                 CpuId::Cop => return Some(Event::Halted),
144             }
145         }
146 
147         if let Some(access) = hit_watchpoint {
148             // NOTE: this isn't a particularly elegant way to do watchpoints! This works
149             // fine for some example code, but don't use this as inspiration in your own
150             // emulator!
151             match access.kind {
152                 AccessKind::Read => {
153                     if *self
154                         .watchpoint_kind
155                         .get(&access.addr)
156                         .map(|(r, _w)| r)
157                         .unwrap_or(&false)
158                     {
159                         let fixup = if cpu.thumb_mode() { 2 } else { 4 };
160                         cpu.reg_set(Mode::User, reg::PC, pc - fixup);
161                         return Some(Event::WatchRead(access.addr));
162                     }
163                 }
164                 AccessKind::Write => {
165                     if *self
166                         .watchpoint_kind
167                         .get(&access.addr)
168                         .map(|(_r, w)| w)
169                         .unwrap_or(&false)
170                     {
171                         let fixup = if cpu.thumb_mode() { 2 } else { 4 };
172                         cpu.reg_set(Mode::User, reg::PC, pc - fixup);
173                         return Some(Event::WatchWrite(access.addr));
174                     }
175                 }
176             }
177         }
178 
179         if self.breakpoints.contains(&pc) {
180             return Some(Event::Break);
181         }
182 
183         None
184     }
185 
step(&mut self) -> Option<(Event, CpuId)>186     pub fn step(&mut self) -> Option<(Event, CpuId)> {
187         let mut evt = None;
188 
189         for id in [CpuId::Cpu, CpuId::Cop].iter().copied() {
190             if let Some(event) = self.step_core(id) {
191                 if evt.is_none() {
192                     evt = Some((event, id));
193                 }
194             }
195         }
196 
197         evt
198     }
199 
run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent200     pub fn run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent {
201         // the underlying armv4t_multicore emulator runs both cores in lock step, so
202         // when GDB requests a specific core to single-step, all we need to do is jot
203         // down that we want to single-step the system, as there is no way to
204         // single-step a single core while the other runs.
205         //
206         // In more complex emulators / implementations, this simplification is _not_
207         // valid, and you should track which specific TID the GDB client requested to be
208         // single-stepped, and run them appropriately.
209 
210         let should_single_step = matches!(
211             self.exec_mode
212                 .get(&CpuId::Cpu)
213                 .or_else(|| self.exec_mode.get(&CpuId::Cop)),
214             Some(&ExecMode::Step)
215         );
216 
217         match should_single_step {
218             true => match self.step() {
219                 Some((event, id)) => RunEvent::Event(event, id),
220                 None => RunEvent::Event(Event::DoneStep, CpuId::Cpu),
221             },
222             false => {
223                 let mut cycles = 0;
224                 loop {
225                     if cycles % 1024 == 0 {
226                         // poll for incoming data
227                         if poll_incoming_data() {
228                             break RunEvent::IncomingData;
229                         }
230                     }
231                     cycles += 1;
232 
233                     if let Some((event, id)) = self.step() {
234                         break RunEvent::Event(event, id);
235                     };
236                 }
237             }
238         }
239     }
240 }
241 
242 pub enum RunEvent {
243     Event(Event, CpuId),
244     IncomingData,
245 }
246