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