diff --git a/Cargo.lock b/Cargo.lock index d389fe8..ec24ebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "adler" @@ -43,8 +43,9 @@ dependencies = [ [[package]] name = "armv4t_emu" -version = "0.1.0" -source = "git+https://github.com/daniel5151/armv4t_emu.git#9647abdaebc5be076fe5ce92f4fb4254d305c577" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "041e88e25b5fd858fc0091f44a8981aed83d4c29141820b46c9f56f94e42e816" dependencies = [ "log", ] @@ -206,6 +207,7 @@ dependencies = [ "futures", "futures-executor", "gdbstub", + "gdbstub_arch", "log", "num_enum", "pin-utils", @@ -502,9 +504,9 @@ dependencies = [ [[package]] name = "gdbstub" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79a8fc10e9c4a4009deacc1d1e632cc2860c63c5169abc737f838e07534ab1a" +checksum = "7e135587d3f6eee6fa02c4ba174270c2337424e6d852c156942c0840b3c0f5cc" dependencies = [ "cfg-if 0.1.10", "log", @@ -513,6 +515,16 @@ dependencies = [ "paste", ] +[[package]] +name = "gdbstub_arch" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e358b9c0e1468eae66099062e47bb502849308b987b74b5e72f1936397c33c16" +dependencies = [ + "gdbstub", + "num-traits", +] + [[package]] name = "gloo-timers" version = "0.2.6" diff --git a/clicky-core/Cargo.toml b/clicky-core/Cargo.toml index 2b6c343..37a6bc5 100644 --- a/clicky-core/Cargo.toml +++ b/clicky-core/Cargo.toml @@ -24,8 +24,9 @@ static_assertions = "1.1" thiserror = "1.0" # emulation related -armv4t_emu = { git = "https://github.com/daniel5151/armv4t_emu.git" } -gdbstub = "0.4" +armv4t_emu = "0.1.2" +gdbstub = "0.5" +gdbstub_arch = "0.1.0" # async/await async-channel = "1.4" diff --git a/clicky-core/src/sys/ipod4g/gdb.rs b/clicky-core/src/sys/ipod4g/gdb.rs index b29a311..573227d 100644 --- a/clicky-core/src/sys/ipod4g/gdb.rs +++ b/clicky-core/src/sys/ipod4g/gdb.rs @@ -1,11 +1,12 @@ use std::collections::HashMap; use armv4t_emu::reg; -use gdbstub::arch; +use gdbstub_arch; use gdbstub::common::Tid; use gdbstub::target; +use gdbstub::target::ext::base::GdbInterrupt; use gdbstub::target::ext::base::multithread::{ - Actions, MultiThreadOps, ResumeAction, ThreadStopReason, + MultiThreadOps, ResumeAction, ThreadStopReason, }; use gdbstub::target::ext::breakpoints::WatchKind; use gdbstub::target::ext::monitor_cmd::{outputln, ConsoleOutput}; @@ -13,7 +14,7 @@ use gdbstub::target::{Target, TargetResult}; use crate::devices::Device; use crate::error::*; -use crate::memory::{MemAccessKind, Memory}; +use crate::memory::{MemAccess, MemAccessKind, MemAccessVal, Memory}; use super::{BlockMode, CpuId, Ipod4g}; @@ -32,6 +33,7 @@ pub struct Ipod4gGdb { breakpoints: Vec, single_step_irq: bool, + resume_action_is_step: Option, } impl Ipod4gGdb { @@ -42,6 +44,7 @@ impl Ipod4gGdb { watchpoint_kinds: HashMap::new(), breakpoints: Vec::new(), single_step_irq: false, + resume_action_is_step: None, } } @@ -205,22 +208,28 @@ fn event_to_stopreason(e: Event, id: CpuId) -> ThreadStopReason { } impl Target for Ipod4gGdb { - type Arch = arch::arm::Armv4t; + type Arch = gdbstub_arch::arm::Armv4t; type Error = FatalMemException; fn base_ops(&mut self) -> target::ext::base::BaseOps { target::ext::base::BaseOps::MultiThread(self) } - fn sw_breakpoint(&mut self) -> Option> { + fn breakpoints(&mut self) -> Option> { Some(self) } - fn hw_watchpoint(&mut self) -> Option> { + fn monitor_cmd(&mut self) -> Option> { Some(self) } +} - fn monitor_cmd(&mut self) -> Option> { +impl target::ext::breakpoints::Breakpoints for Ipod4gGdb { + fn sw_breakpoint(&mut self) -> Option> { + Some(self) + } + + fn hw_watchpoint(&mut self) -> Option> { Some(self) } } @@ -228,15 +237,20 @@ impl Target for Ipod4gGdb { impl MultiThreadOps for Ipod4gGdb { fn resume( &mut self, - actions: Actions, - check_gdb_interrupt: &mut dyn FnMut() -> bool, + default_resume_action: ResumeAction, + gdb_interrupt: GdbInterrupt<'_>, ) -> Result, Self::Error> { - // FIXME: properly handle multiple actions... - let actions = actions.collect::>(); - let (_, action) = actions[0]; + let default_resume_action_is_step = match default_resume_action { + ResumeAction::Step => true, + ResumeAction::Continue => false, + _ => return Ok(ThreadStopReason::DoneStep), // should be an error + }; - match action { - ResumeAction::Step => { + match self + .resume_action_is_step + .unwrap_or(default_resume_action_is_step) + { + true => { if !self.single_step_irq { self.sys.skip_irq_check = true; } @@ -248,12 +262,13 @@ impl MultiThreadOps for Ipod4gGdb { self.sys.skip_irq_check = false; } res - } - ResumeAction::Continue => { + }, + false => { + let mut gdb_interrupt = gdb_interrupt.no_async(); let mut cycles: usize = 0; loop { // check for GDB interrupt every 1024 instructions - if cycles % 1024 == 0 && check_gdb_interrupt() { + if cycles % 1024 == 0 && gdb_interrupt.pending() { return Ok(ThreadStopReason::GdbInterrupt); } cycles += 1; @@ -266,9 +281,47 @@ impl MultiThreadOps for Ipod4gGdb { } } + fn clear_resume_actions(&mut self) -> Result<(), Self::Error> { + self.resume_action_is_step = None; + Ok(()) + } + + fn set_resume_action(&mut self, tid: Tid, action: ResumeAction) -> Result<(), Self::Error> { + // in this emulator, each core runs in lock-step, so we don't actually care + // about the specific tid. In real integrations, you very much should! + + if self.resume_action_is_step.is_some() { + return Ok(()); + } + + let cpu = match tid_to_cpuid(tid).unwrap() { + CpuId::Cpu => &mut self.sys.cpu, + CpuId::Cop => &mut self.sys.cop, + }; + + self.resume_action_is_step = match action { + ResumeAction::Step => Some(true), + ResumeAction::Continue => Some(false), + _ => return MemException::Fatal("no support for resuming with signal".to_owned()).resolve( + "Debug", + MemExceptionCtx { + pc: cpu.reg_get(cpu.mode(), reg::PC), + access: MemAccess { + kind: MemAccessKind::Read, + offset: 0, + val: MemAccessVal::U8(0), + }, + in_device: format!("{}", tid_to_cpuid(tid).unwrap()), + }, + ), + }; + + Ok(()) + } + fn read_registers( &mut self, - regs: &mut arch::arm::reg::ArmCoreRegs, + regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self> { let cpu = match tid_to_cpuid(tid).unwrap() { @@ -291,7 +344,7 @@ impl MultiThreadOps for Ipod4gGdb { fn write_registers( &mut self, - regs: &arch::arm::reg::ArmCoreRegs, + regs: &gdbstub_arch::arm::reg::ArmCoreRegs, tid: Tid, ) -> TargetResult<(), Self> { let cpu = match tid_to_cpuid(tid).unwrap() { @@ -351,12 +404,12 @@ impl MultiThreadOps for Ipod4gGdb { } impl target::ext::breakpoints::SwBreakpoint for Ipod4gGdb { - fn add_sw_breakpoint(&mut self, addr: u32) -> TargetResult { + fn add_sw_breakpoint(&mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind) -> TargetResult { self.breakpoints.push(addr); Ok(true) } - fn remove_sw_breakpoint(&mut self, addr: u32) -> TargetResult { + fn remove_sw_breakpoint(&mut self, addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind) -> TargetResult { match self.breakpoints.iter().position(|x| *x == addr) { None => return Ok(false), Some(pos) => self.breakpoints.remove(pos), diff --git a/clicky-desktop/Cargo.toml b/clicky-desktop/Cargo.toml index e498fe9..b514952 100644 --- a/clicky-desktop/Cargo.toml +++ b/clicky-desktop/Cargo.toml @@ -11,7 +11,7 @@ default = ["minifb"] clicky-core = { path = "../clicky-core/" } cfg-if = "0.1" -gdbstub = "0.4" +gdbstub = "0.5" human-size = "0.4" log = "0.4" pretty_env_logger = "0.3" diff --git a/clicky-desktop/src/main.rs b/clicky-desktop/src/main.rs index 7a8944c..a3aad1f 100644 --- a/clicky-desktop/src/main.rs +++ b/clicky-desktop/src/main.rs @@ -175,8 +175,12 @@ fn main() -> DynResult<()> { info!("Target is still running. Resuming execution..."); system.run() } - DisconnectReason::TargetHalted => { - info!("Target halted!"); + DisconnectReason::TargetExited(_) => { + info!("Target exited!"); + Ok(()) + } + DisconnectReason::TargetTerminated(_) => { + info!("Target terminated!"); Ok(()) } DisconnectReason::Kill => {