From f3fac5e3b87c18abcf560be38e210c3903d22e93 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Thu, 8 Sep 2022 23:39:47 +0300 Subject: [PATCH] Implement new gdbstub target for arm7tdmi, add a small example & test as well Former-commit-id: 16ba6dada28f8beb5971413acbbe153a26aa9ec3 Former-commit-id: f0661ad20245110797fd99fe16d3fb11382bb78a --- Cargo.lock | 130 ++++++++++++++--- arm7tdmi/Cargo.toml | 6 + arm7tdmi/examples/simple_emulator.rs | 129 +++++++++++++++++ arm7tdmi/examples/test_program/.gitignore | 2 + arm7tdmi/examples/test_program/Makefile | 37 +++++ arm7tdmi/examples/test_program/reset.s | 40 ++++++ arm7tdmi/examples/test_program/test.bin | Bin 0 -> 432 bytes arm7tdmi/examples/test_program/test.c | 17 +++ arm7tdmi/examples/test_program/test.elf | Bin 0 -> 68996 bytes arm7tdmi/examples/test_program/test.ld | 32 +++++ arm7tdmi/examples/test_program/test.map | 162 ++++++++++++++++++++++ arm7tdmi/src/cpu.rs | 40 +++--- arm7tdmi/src/gdb/breakpoints.rs | 56 ++++++++ arm7tdmi/src/gdb/mod.rs | 45 ++++++ arm7tdmi/src/gdb/target.rs | 113 +++++++++++++++ arm7tdmi/src/lib.rs | 3 + arm7tdmi/src/memory.rs | 9 +- arm7tdmi/src/simple_memory.rs | 83 +++++++++++ core/Cargo.toml | 3 +- core/src/cartridge/loader.rs | 74 ++-------- core/src/gba.rs | 10 +- core/src/gdb.rs | 2 +- core/src/lib.rs | 3 - platform/rustboyadvance-sdl2/Cargo.toml | 1 + platform/rustboyadvance-sdl2/src/main.rs | 12 +- utils/Cargo.toml | 3 + utils/src/elf.rs | 67 +++++++++ utils/src/lib.rs | 2 + 28 files changed, 964 insertions(+), 117 deletions(-) create mode 100644 arm7tdmi/examples/simple_emulator.rs create mode 100644 arm7tdmi/examples/test_program/.gitignore create mode 100644 arm7tdmi/examples/test_program/Makefile create mode 100644 arm7tdmi/examples/test_program/reset.s create mode 100644 arm7tdmi/examples/test_program/test.bin create mode 100644 arm7tdmi/examples/test_program/test.c create mode 100644 arm7tdmi/examples/test_program/test.elf create mode 100644 arm7tdmi/examples/test_program/test.ld create mode 100644 arm7tdmi/examples/test_program/test.map create mode 100644 arm7tdmi/src/gdb/breakpoints.rs create mode 100644 arm7tdmi/src/gdb/mod.rs create mode 100644 arm7tdmi/src/gdb/target.rs create mode 100644 arm7tdmi/src/simple_memory.rs create mode 100644 utils/src/elf.rs diff --git a/Cargo.lock b/Cargo.lock index d6c1786..2348093 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,12 +77,16 @@ dependencies = [ "bit", "byteorder", "cfg-if 1.0.0", - "colored", + "colored 1.9.3", "enum-primitive-derive", + "gdbstub 0.6.3", + "gdbstub_arch", + "log 0.4.11", "num 0.2.1", "num-traits 0.2.12", "rustboyadvance-utils", "serde", + "simple_logger", ] [[package]] @@ -201,9 +205,9 @@ checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake2b_simd" @@ -333,7 +337,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term 0.11.0", "atty", - "bitflags 1.2.1", + "bitflags 1.3.2", "strsim", "textwrap", "unicode-width", @@ -352,6 +356,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static 1.4.0", + "winapi 0.3.9", +] + [[package]] name = "combine" version = "4.3.2" @@ -485,7 +500,7 @@ checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" dependencies = [ "bstr", "csv-core", - "itoa", + "itoa 0.4.6", "ryu", "serde", ] @@ -654,6 +669,30 @@ dependencies = [ "num-traits 0.2.12", ] +[[package]] +name = "gdbstub" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c95766e0414f8bfc1d07055574c621b67739466d6ba516c4fef8e99d30d2e6" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "log 0.4.11", + "managed", + "num-traits 0.2.12", + "paste", +] + +[[package]] +name = "gdbstub_arch" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eecb536c55c43593a00dde9074dbbdb0e81ce5f20dbca921400f8779c21dea9c" +dependencies = [ + "gdbstub 0.6.3", + "num-traits 0.2.12", +] + [[package]] name = "gdi32-sys" version = "0.1.2" @@ -793,6 +832,12 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + [[package]] name = "jni" version = "0.17.0" @@ -851,7 +896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" dependencies = [ "arrayvec 0.5.2", - "bitflags 1.2.1", + "bitflags 1.3.2", "cfg-if 0.1.10", "ryu", "static_assertions", @@ -859,9 +904,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.77" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libretro-backend" @@ -906,6 +951,12 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -980,7 +1031,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" dependencies = [ - "bitflags 1.2.1", + "bitflags 1.3.2", "cc", "cfg-if 0.1.10", "libc", @@ -1110,6 +1161,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.20.0" @@ -1144,6 +1204,12 @@ dependencies = [ "sdl2 0.31.0", ] +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + [[package]] name = "pin-project-lite" version = "0.1.9" @@ -1374,18 +1440,17 @@ dependencies = [ "bit-set", "bit_reverse", "bitfield", - "bitflags 1.2.1", + "bitflags 1.3.2", "byteorder", "bytesize", "cfg-if 1.0.0", "chrono", - "colored", + "colored 1.9.3", "criterion", "debug_stub_derive", "enum-primitive-derive", "fuzzy-matcher", - "gdbstub", - "goblin", + "gdbstub 0.1.2", "hex-literal", "hexdump", "lazy_static 1.4.0", @@ -1461,7 +1526,9 @@ dependencies = [ name = "rustboyadvance-utils" version = "0.1.0" dependencies = [ + "goblin", "instant", + "log 0.4.11", "ringbuf", ] @@ -1583,7 +1650,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f74124048ea86b5cd50236b2443f6f57cf4625a8e8818009b4e50dbb8729a43" dependencies = [ - "bitflags 1.2.1", + "bitflags 1.3.2", "lazy_static 1.4.0", "libc", "sdl2-sys 0.33.0", @@ -1659,7 +1726,7 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" dependencies = [ - "itoa", + "itoa 0.4.6", "ryu", "serde", ] @@ -1682,6 +1749,19 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "simple_logger" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48047e77b528151aaf841a10a9025f9459da80ba820e425ff7eb005708a76dc7" +dependencies = [ + "atty", + "colored 2.0.0", + "log 0.4.11", + "time 0.3.14", + "winapi 0.3.9", +] + [[package]] name = "smart-default" version = "0.6.0" @@ -1872,11 +1952,23 @@ dependencies = [ "libc", "standback", "stdweb", - "time-macros", + "time-macros 0.1.1", "version_check", "winapi 0.3.9", ] +[[package]] +name = "time" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" +dependencies = [ + "itoa 1.0.3", + "libc", + "num_threads", + "time-macros 0.2.4", +] + [[package]] name = "time-macros" version = "0.1.1" @@ -1887,6 +1979,12 @@ dependencies = [ "time-macros-impl", ] +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + [[package]] name = "time-macros-impl" version = "0.1.1" diff --git a/arm7tdmi/Cargo.toml b/arm7tdmi/Cargo.toml index 5304536..78d4db4 100644 --- a/arm7tdmi/Cargo.toml +++ b/arm7tdmi/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] rustboyadvance-utils = {"path" = "../utils" } +log = "0.4.8" bit = "^0.1" cfg-if = "1.0.0" serde = { version = "1.0.104", features = ["derive", "rc"] } @@ -16,6 +17,11 @@ byteorder = "1" num = "0.2.1" num-traits = "0.2" enum-primitive-derive = "^0.1" +gdbstub = "0.6.3" +gdbstub_arch = "0.2.4" + +[dev-dependencies] +simple_logger = "2.3.0" # For the examples [build-dependencies] bit = "^0.1" \ No newline at end of file diff --git a/arm7tdmi/examples/simple_emulator.rs b/arm7tdmi/examples/simple_emulator.rs new file mode 100644 index 0000000..a12b1d1 --- /dev/null +++ b/arm7tdmi/examples/simple_emulator.rs @@ -0,0 +1,129 @@ +use log::info; +use simple_logger::SimpleLogger; + +use gdbstub::common::Signal; +use gdbstub::conn::{Connection, ConnectionExt}; +use gdbstub::stub::{run_blocking, GdbStub, SingleThreadStopReason}; +use gdbstub::target::Target; + +use arm7tdmi::gdb::wait_for_connection; +use arm7tdmi::{Arm7tdmiCore, SimpleMemory}; + +use rustboyadvance_utils::Shared; + +struct SimpleEmulator { + cpu: Arm7tdmiCore, +} + +impl SimpleEmulator { + fn new(program: &[u8]) -> SimpleEmulator { + let mut memory = SimpleMemory::new(0x4000); + memory.load_program(program); + + let bus = Shared::new(memory); + let mut cpu = Arm7tdmiCore::new(bus); + cpu.reset(); + + SimpleEmulator { cpu } + } +} + +impl run_blocking::BlockingEventLoop for SimpleEmulator { + type Target = Arm7tdmiCore; + type Connection = Box>; + type StopReason = SingleThreadStopReason; + + fn wait_for_stop_reason( + target: &mut Self::Target, + conn: &mut Self::Connection, + ) -> Result< + run_blocking::Event>, + run_blocking::WaitForStopReasonError< + ::Error, + ::Error, + >, + > { + let mut steps = 0; + loop { + if conn.peek().map(|b| b.is_some()).unwrap_or(true) { + let byte = conn + .read() + .map_err(run_blocking::WaitForStopReasonError::Connection)?; + return Ok(run_blocking::Event::IncomingData(byte)); + } else { + target.step(); + if target.check_breakpoint().is_some() { + return Ok(run_blocking::Event::TargetStopped( + SingleThreadStopReason::SwBreak(()), + )); + } + + steps += 1; + if steps % 1024 == 0 { + return Ok(run_blocking::Event::TargetStopped( + SingleThreadStopReason::SwBreak(()), + )); + } + } + } + } + + fn on_interrupt( + _target: &mut Arm7tdmiCore, + ) -> Result>, as Target>::Error> + { + // Because this emulator runs as part of the GDB stub loop, there isn't any + // special action that needs to be taken to interrupt the underlying target. It + // is implicitly paused whenever the stub isn't within the + // `wait_for_stop_reason` callback. + Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT))) + } +} + +fn main() -> Result<(), Box> { + SimpleLogger::new().env().init().unwrap(); + + let mut emulator = SimpleEmulator::new(include_bytes!("test_program/example.bin")); + + let conn: Box> = Box::new(wait_for_connection(1337)?); + let gdb = GdbStub::new(conn); + let result = gdb.run_blocking::(&mut emulator.cpu); + + info!("emulator stopped, gdb result {:?}", result); + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + use arm7tdmi::memory::DebugRead; + use rustboyadvance_utils::elf::read_symbols; + + #[test] + fn test_breakpoint() -> Result<(), Box> { + let mut emulator = SimpleEmulator::new(include_bytes!("test_program/example.bin")); + let symbol_map = read_symbols(include_bytes!("test_program/example.elf"))?; + let breakpoint_addr = *symbol_map.get("breakpoint_on_me").unwrap(); + println!("breakpoint_addr = {:08x}", breakpoint_addr); + let breakpoint_counter_addr = *symbol_map.get("breakpoint_count").unwrap(); + emulator.cpu.breakpoints.push(breakpoint_addr); + + for x in 0..10 { + println!("{}", x); + let timeout = std::time::Instant::now() + std::time::Duration::from_secs(1); + loop { + emulator.cpu.step(); + if let Some(addr) = emulator.cpu.check_breakpoint() { + emulator.cpu.step(); + assert_eq!(addr, breakpoint_addr); + assert_eq!(emulator.cpu.bus.debug_read_32(breakpoint_counter_addr), x); + break; + } + assert!(std::time::Instant::now() < timeout); + } + } + + Ok(()) + } +} diff --git a/arm7tdmi/examples/test_program/.gitignore b/arm7tdmi/examples/test_program/.gitignore new file mode 100644 index 0000000..b2c3624 --- /dev/null +++ b/arm7tdmi/examples/test_program/.gitignore @@ -0,0 +1,2 @@ +/*.o +/*.gdb_history diff --git a/arm7tdmi/examples/test_program/Makefile b/arm7tdmi/examples/test_program/Makefile new file mode 100644 index 0000000..4af0c0d --- /dev/null +++ b/arm7tdmi/examples/test_program/Makefile @@ -0,0 +1,37 @@ +# Easiest to build this using `docker run -it --rm -v $(pwd):/code -w /code devkitpro/devkitarm:latest make +TOOLCHAIN := ${DEVKITARM}/bin/arm-none-eabi +CC := ${TOOLCHAIN}-gcc +OBJCOPY := ${TOOLCHAIN}-objcopy +OBJDUMP := ${TOOLCHAIN}-objdump + +TARGET := test + +CFLAGS := -O0 -g -std=c11 -march=armv4t -mthumb +LDFLAGS := -Wl,-static + + +SOURCES := $(wildcard *.[cs]) +OBJECTS := $(filter %.o,$(SOURCES:%.c=%.o)) +OBJECTS += $(filter %.o,$(SOURCES:%.s=%.o)) + +all: ${TARGET}.bin objdump + +${OBJECTS}: ${SOURCES} + ${CC} -c ${CFLAGS} $^ + +${TARGET}.elf ${TARGET}.map: ${OBJECTS} ${TARGET}.ld + ${CC} ${LDFLAGS} -Wl,-Map=${TARGET}.map -Wl,-T${TARGET}.ld ${OBJECTS} -o $@ + +${TARGET}.bin: ${TARGET}.elf + $(OBJCOPY) -v -O binary $^ $@ + +objdump: ${TARGET}.elf + ${OBJDUMP} -d $^ + +clean: + rm -f ${TARGET}.elf + rm -f ${TARGET}.bin + rm -f ${TARGET}.map + rm -f *.o + +.PHONY: all clean diff --git a/arm7tdmi/examples/test_program/reset.s b/arm7tdmi/examples/test_program/reset.s new file mode 100644 index 0000000..5b6f045 --- /dev/null +++ b/arm7tdmi/examples/test_program/reset.s @@ -0,0 +1,40 @@ +.section ".init" + +.global _interrupt_vector +.align 4 +.arm + +.section ".init.vector" +_interrupt_vector: + b _reset + b _not_implemented + b _not_implemented + b _not_implemented + b _not_implemented + b _not_implemented + b _not_implemented + b _not_implemented + +.section ".init.text" +.global _reset +_reset: + mrs r1, cpsr @ save the mode bits from CPSR + bic r0, r1, #0x1F + orr r0, r0, #0x13 @ supervisor + ldr sp, =_stack_top + mov r0, #0 + mov r1, #0 + mov r2, #0 + mov r3, #0 + mov r4, #0 + mov r5, #0 + mov r6, #0 + mov r7, #0 + mov r9, #0 + mov r10, #0 + mov r11, #0 + mov r12, #0 + b main + +_not_implemented: + b _not_implemented diff --git a/arm7tdmi/examples/test_program/test.bin b/arm7tdmi/examples/test_program/test.bin new file mode 100644 index 0000000000000000000000000000000000000000..771427008f378f788cb65f11412d916d18e3453c GIT binary patch literal 432 zcma*jKTE?<6b0~`+WMzjebuNj26a)g6a(tuAY2?G7zd%7ICSi$dzauR5S&W_et>4M zTMH2!9V&E}Gz4uY!NE?(^9qHI27U=V4)>j#Jkdah4bWg6)?gJ>D1?4Ad@uF0!9`D1 zh)66&+w>f-kQ*O%yoAV)Nx%+Rz;Fo_xP^Q0A%M`27clcTO$R}d$m}@1ubcYkR=OX# zO={$v&FYnrb@WPTYt**gR;D$37TT}4*KOVf)R=KpF literal 0 HcmV?d00001 diff --git a/arm7tdmi/examples/test_program/test.c b/arm7tdmi/examples/test_program/test.c new file mode 100644 index 0000000..1fdd624 --- /dev/null +++ b/arm7tdmi/examples/test_program/test.c @@ -0,0 +1,17 @@ +int breakpoint_count = 0; +volatile int breakpoint_on_me() { + breakpoint_count++; +} + +int main() { + int x = 1337; + for (;;) { + x += 1; + if (x == 7331) { + breakpoint_on_me(); + x = 1337; + } + } + return 0; +} + diff --git a/arm7tdmi/examples/test_program/test.elf b/arm7tdmi/examples/test_program/test.elf new file mode 100644 index 0000000000000000000000000000000000000000..e27ad3d9f6b1d8a49b71191b3ca8bd1263a2fe71 GIT binary patch literal 68996 zcmeI!TWlOx83*w1%y@mt#+&set#fJFh$I+MZ|vfVG{|&J5=X^JC`tQ3RcSoy9otL1 zyVmYFP1*<|ghY7&ae1haiegcOQmJbFK%YpZRN|ozNElS{N)juA_5o4xKoJt=|IN(V zOihsb$ix3gXXku#zH{cxZ*Kdolc#5lF=C4&qoUC`>LhA)2O>vulTyNFREA|J?w9me z$E_n}1GPs3>_;<>bzeLYHn(<(bYq(x7l-_z00k&O0SZun0u-PC1t>rP3Q&Lo6rcbF zC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epy zpa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+ zfC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O z0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC z1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo z6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)U zP=Epypa2CZK!N`YfgK_r>_y#!I)eHj>I0~|QHN3cZ?4_`=uczscdehkGi4&VD|r3hA#czd^&D@+>+^PapDah`dJlE_5PpI%sMN&lLo+w| zv~)YYrq2v^4V&HOL3hOLHK&drKUNy8h8Hi?qUm#IO094?4BBDov7?_YO*~qjD38kv zI#^5>Hew#e&qKKMf~z7gp`J$7;+)5iF}b7Ih@`8k9o=!qbu=H*@RdbP zyCkNkPX{$)aI_6)6ew?|%DGRERC~QaNc|g+S6;}a92xC zzcdxJ>K7-Y(%52Y?5pFYv369QnxB}E%#`!ZRa}aCP-_UDE7?LZ;#?{0>=!V^#hOnS zdcW)Zz)bh|BnsMj9aXQJ?JE@HZteP?#Z?RxomY#SdY(vjBX;Bc`dnynQ|Y-*jHeOh z^-dh$A!46DSFk{ojZS}W9NQmP`fM}ax`H=Yrnr*NM0$LcxmR52zZan8N?}{PYg=4Q z&p6rLB_D2md<7;xLev>$;uVf(V!fX(W#Uq&t@mLv-@Sdwc5EcS%wlIe~_a9b8Y%@9pF}@+@L|eGIOT_S@@+r%e_zu|cf! z$KMKU^?Cb#Cbsh*&CLH(^y|A+i=AKZ%cL0jIT|)L$k5~4^+>uNJ6_{=(viBczE?W? z65ppf$9Lu3PG6F*^XscFDLwK#H0=GZ=cE0 z-D^a_muj%Q94FPns=rXFMQ!o@YSUj_ZdQV2zZx}LZ9iDO zB=b$33DcA(?w{c=)EYHEXtjdNK91MAEDNom9{SbQdi}CAE8hs`BYgaPKeOA%kmzG$ zu(Adgn7;83M#!HzH+|-$fAZ`J-_O2*lb1SXh{9HDbtUpI;sDK-F0S45mx4xhIh6PS z;JhSCTh&yi~il(u|^JUHn)sdw}E>FXi^-dK6T!jatce$+mR$QjWq)_|>zQ za=4V7o|LOW6eN3-^OJI=-PTF45-H=KK{<$`R;{v%Q6b&F%a;({ zz}A-J+b;2Y)8^^@d;%YH`(4yrn>U8G=IK8JeeRw^p6$b~PWuaJYo31L=Fpf!-f6UL zv16vNL%$-Lr|-$v?=zp~K8f0!=IQ%v9eE|hw%Ga3Vu#Js=Wqjgw=$n8?25}X_{A|- z(lDOyunn-ad12bN&+s2H->0|bX>KDkM!Wxsyl-kFEp|TrD{0%>^}J)``7}p2wok8> zZR`HOBCn*4wAj9v5!<$Q74Sm)TAHI9+xKePw&&p^ueL2ubKl5}(e8JVcfaeVxyipn y&B8aV%ZP`IFo^asRJ|U05B8zzwYBAGd@Rq|MPa9H>MJ&z_sc#bzqW%idH(@0g8pj& literal 0 HcmV?d00001 diff --git a/arm7tdmi/examples/test_program/test.ld b/arm7tdmi/examples/test_program/test.ld new file mode 100644 index 0000000..277585b --- /dev/null +++ b/arm7tdmi/examples/test_program/test.ld @@ -0,0 +1,32 @@ +ENTRY(_reset) + +MEMORY { + RAM : ORIGIN = 0x00000000, LENGTH = 16K +} + +SECTIONS { + . = ORIGIN(RAM); + + .init : { + *(.init.vector); + *(.init*); + } > RAM + + .text : ALIGN(4) { + *(.text) + *(.text.*) + } > RAM + .data : ALIGN(4) { + *(.rodata*); + *(.data*); + } > RAM + .bss : ALIGN(4) { + *(.bss*) + } > RAM + + .stack : AT (0x00003000) { + _stack_bottom = .; + . += 0xfe0; + _stack_top = .; + } > RAM +} \ No newline at end of file diff --git a/arm7tdmi/examples/test_program/test.map b/arm7tdmi/examples/test_program/test.map new file mode 100644 index 0000000..7efb850 --- /dev/null +++ b/arm7tdmi/examples/test_program/test.map @@ -0,0 +1,162 @@ + +Memory Configuration + +Name Origin Length Attributes +RAM 0x0000000000000000 0x0000000000004000 +*default* 0x0000000000000000 0xffffffffffffffff + +Linker script and memory map + +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + 0x0000000000000000 . = ORIGIN (RAM) + +.init 0x0000000000000000 0x88 + *(.init.vector) + .init.vector 0x0000000000000000 0x20 reset.o + 0x0000000000000000 _interrupt_vector + *(.init*) + .init 0x0000000000000020 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o + 0x0000000000000020 _init + .init_array 0x000000000000002c 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .init 0x0000000000000030 0x0 reset.o + .init.text 0x0000000000000030 0x4c reset.o + 0x0000000000000030 _reset + .init 0x000000000000007c 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o + +.text 0x0000000000000088 0xbc + *(.text) + .text 0x0000000000000088 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o + .text 0x0000000000000088 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .text 0x0000000000000088 0x4c test.o + 0x0000000000000088 breakpoint_on_me + 0x00000000000000a8 main + .text 0x00000000000000d4 0x0 reset.o + .text 0x00000000000000d4 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + .text 0x00000000000000d4 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o + *(.text.*) + .text.__do_global_dtors_aux + 0x00000000000000d4 0x40 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .text.frame_dummy + 0x0000000000000114 0x30 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + +.glue_7 0x0000000000000144 0x0 + .glue_7 0x0000000000000144 0x0 linker stubs + +.glue_7t 0x0000000000000144 0x0 + .glue_7t 0x0000000000000144 0x0 linker stubs + +.vfp11_veneer 0x0000000000000144 0x0 + .vfp11_veneer 0x0000000000000144 0x0 linker stubs + +.v4_bx 0x0000000000000144 0x0 + .v4_bx 0x0000000000000144 0x0 linker stubs + +.fini 0x0000000000000144 0x18 + .fini 0x0000000000000144 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o + 0x0000000000000144 _fini + .fini 0x0000000000000150 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o + +.iplt 0x000000000000015c 0x0 + .iplt 0x000000000000015c 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + +.eh_frame 0x000000000000015c 0x4 + .eh_frame 0x000000000000015c 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .eh_frame 0x000000000000015c 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + +.rel.dyn 0x0000000000000160 0x0 + .rel.iplt 0x0000000000000160 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + +.data 0x0000000000000160 0x4c + *(.rodata*) + .rodata.all_implied_fbits + 0x0000000000000160 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .rodata.all_implied_fbits + 0x0000000000000184 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + *(.data*) + .data 0x00000000000001a8 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o + .data 0x00000000000001a8 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .data.__dso_handle + 0x00000000000001a8 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + 0x00000000000001a8 __dso_handle + .data 0x00000000000001ac 0x0 test.o + .data 0x00000000000001ac 0x0 reset.o + .data 0x00000000000001ac 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + .data 0x00000000000001ac 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o + +.fini_array 0x00000000000001ac 0x4 + .fini_array 0x00000000000001ac 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + +.igot.plt 0x00000000000001b0 0x0 + .igot.plt 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + +.bss 0x00000000000001b0 0x20 + *(.bss*) + .bss 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o + .bss 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .bss.completed.1 + 0x00000000000001b0 0x1 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + *fill* 0x00000000000001b1 0x3 + .bss.object.0 0x00000000000001b4 0x18 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .bss 0x00000000000001cc 0x4 test.o + 0x00000000000001cc breakpoint_count + .bss 0x00000000000001d0 0x0 reset.o + .bss 0x00000000000001d0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + .bss 0x00000000000001d0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o + +.stack 0x00000000000001d0 0xfe0 load address 0x0000000000003000 + 0x00000000000001d0 _stack_bottom = . + 0x00000000000011b0 . = (. + 0xfe0) + *fill* 0x00000000000001d0 0xfe0 + 0x00000000000011b0 _stack_top = . +LOAD test.o +LOAD reset.o +START GROUP +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/libgcc.a +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/lib/libc.a +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/lib/libsysbase.a +END GROUP +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o +LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o +OUTPUT(test.elf elf32-littlearm) +LOAD linker stubs + +.ARM.attributes + 0x0000000000000000 0x26 + .ARM.attributes + 0x0000000000000000 0x1c /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o + .ARM.attributes + 0x000000000000001c 0x2a /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + .ARM.attributes + 0x0000000000000046 0x2a test.o + .ARM.attributes + 0x0000000000000070 0x1a reset.o + .ARM.attributes + 0x000000000000008a 0x2a /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + .ARM.attributes + 0x00000000000000b4 0x1c /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o + +.comment 0x0000000000000000 0x23 + .comment 0x0000000000000000 0x23 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o + 0x24 (size before relaxing) + .comment 0x0000000000000023 0x24 test.o + .comment 0x0000000000000023 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o + +.debug_info 0x0000000000000000 0x7e + .debug_info 0x0000000000000000 0x7e test.o + +.debug_abbrev 0x0000000000000000 0x77 + .debug_abbrev 0x0000000000000000 0x77 test.o + +.debug_aranges 0x0000000000000000 0x20 + .debug_aranges + 0x0000000000000000 0x20 test.o + +.debug_line 0x0000000000000000 0x51 + .debug_line 0x0000000000000000 0x51 test.o + +.debug_str 0x0000000000000000 0x69 + .debug_str 0x0000000000000000 0x69 test.o + +.debug_frame 0x0000000000000000 0x4c + .debug_frame 0x0000000000000000 0x4c test.o diff --git a/arm7tdmi/src/cpu.rs b/arm7tdmi/src/cpu.rs index 033834a..da74b1c 100644 --- a/arm7tdmi/src/cpu.rs +++ b/arm7tdmi/src/cpu.rs @@ -54,14 +54,14 @@ pub enum CpuAction { } #[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub(super) struct BankedRegisters { +pub struct BankedRegisters { // r13 and r14 are banked for all modes. System&User mode share them - pub(super) gpr_banked_r13: [u32; 6], - pub(super) gpr_banked_r14: [u32; 6], + pub gpr_banked_r13: [u32; 6], + pub gpr_banked_r14: [u32; 6], // r8-r12 are banked for fiq mode - pub(super) gpr_banked_old_r8_12: [u32; 5], - pub(super) gpr_banked_fiq_r8_12: [u32; 5], - pub(super) spsr_bank: [RegPSR; 6], + pub gpr_banked_old_r8_12: [u32; 5], + pub gpr_banked_fiq_r8_12: [u32; 5], + pub spsr_bank: [RegPSR; 6], } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -106,17 +106,21 @@ impl Default for DebuggerState { #[derive(Clone, Debug)] pub struct Arm7tdmiCore { pub pc: u32, - pub(super) bus: Shared, + pub bus: Shared, next_fetch_access: MemoryAccess, pipeline: [u32; 2], pub gpr: [u32; 15], pub cpsr: RegPSR, - pub(super) spsr: RegPSR, + pub spsr: RegPSR, - pub(super) banks: BankedRegisters, + pub banks: BankedRegisters, + /// Hardware breakpoints for use by gdb + pub breakpoints: Vec, + + /// Deprecated in-house debugger state #[cfg(feature = "debugger")] pub dbg: DebuggerState, } @@ -134,6 +138,8 @@ impl Arm7tdmiCore { spsr: Default::default(), banks: BankedRegisters::default(), + breakpoints: Vec::new(), + #[cfg(feature = "debugger")] dbg: DebuggerState::default(), } @@ -156,6 +162,8 @@ impl Arm7tdmiCore { pipeline: state.pipeline, next_fetch_access: state.next_fetch_access, + breakpoints: Vec::new(), // TODO include breakpoints in saved state + // savestate does not keep debugger related information, so just reinitialize to default #[cfg(feature = "debugger")] dbg: DebuggerState::default(), @@ -473,20 +481,6 @@ impl Arm7tdmiCore { pub fn get_cpu_state(&self) -> CpuState { self.cpsr.state() } - - pub fn skip_bios(&mut self) { - self.banks.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS - self.banks.gpr_banked_r13[1] = 0x0300_7f00; // FIQ - self.banks.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ - self.banks.gpr_banked_r13[3] = 0x0300_7fe0; // SVC - self.banks.gpr_banked_r13[4] = 0x0300_7f00; // ABT - self.banks.gpr_banked_r13[5] = 0x0300_7f00; // UND - - self.gpr[13] = 0x0300_7f00; - self.pc = 0x0800_0000; - - self.cpsr.set(0x5f); - } } #[cfg(feature = "debugger")] diff --git a/arm7tdmi/src/gdb/breakpoints.rs b/arm7tdmi/src/gdb/breakpoints.rs new file mode 100644 index 0000000..8c23476 --- /dev/null +++ b/arm7tdmi/src/gdb/breakpoints.rs @@ -0,0 +1,56 @@ +use gdbstub::target; +use gdbstub::target::TargetResult; +use log::debug; + +use crate::Arm7tdmiCore; + +use super::target::MemoryGdbInterface; + +impl target::ext::breakpoints::Breakpoints for Arm7tdmiCore { + // there are several kinds of breakpoints - this target uses software breakpoints + #[inline(always)] + fn support_sw_breakpoint( + &mut self, + ) -> Option> { + Some(self) + } +} + +impl target::ext::breakpoints::SwBreakpoint for Arm7tdmiCore { + fn add_sw_breakpoint( + &mut self, + addr: u32, + _kind: gdbstub_arch::arm::ArmBreakpointKind, + ) -> TargetResult { + debug!("adding breakpoint {:08x}", addr); + self.breakpoints.push(addr); + Ok(true) + } + + fn remove_sw_breakpoint( + &mut self, + addr: u32, + _kind: gdbstub_arch::arm::ArmBreakpointKind, + ) -> TargetResult { + match self.breakpoints.iter().position(|x| *x == addr) { + None => Ok(false), + Some(pos) => { + debug!("deleting breakpoint {:08x}", addr); + self.breakpoints.remove(pos); + Ok(true) + } + } + } +} + +impl Arm7tdmiCore { + pub fn check_breakpoint(&self) -> Option { + let next_pc = self.get_next_pc(); + for bp in &self.breakpoints { + if (*bp & !1) == next_pc { + return Some(*bp); + } + } + None + } +} diff --git a/arm7tdmi/src/gdb/mod.rs b/arm7tdmi/src/gdb/mod.rs new file mode 100644 index 0000000..7927d91 --- /dev/null +++ b/arm7tdmi/src/gdb/mod.rs @@ -0,0 +1,45 @@ +use std::io; +use std::net::{TcpListener, TcpStream}; + +use log::info; + +mod breakpoints; +pub mod target; + +/// Wait for tcp connection on port +pub fn wait_for_connection(port: u16) -> io::Result { + let bind_addr = format!("localhost:{port}"); + let sock = TcpListener::bind(bind_addr)?; + + info!("waiting for connection"); + // Blocks until a GDB client connects via TCP. + // i.e: Running `target remote localhost:` from the GDB prompt. + let (stream, addr) = sock.accept()?; + + info!("gdb connected from {:?}", addr); + + Ok(stream) +} + +/// Copy all bytes of `data` to `buf`. +/// Return the size of data copied. +pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize { + let len = buf.len().min(data.len()); + buf[..len].copy_from_slice(&data[..len]); + len +} + +/// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`. +/// Return the size of data copied. Returns 0 if `offset >= buf.len()`. +/// +/// Mainly used by qXfer:_object_:read commands. +pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize { + let offset = offset as usize; + if offset > data.len() { + return 0; + } + + let start = offset; + let end = (offset + length).min(data.len()); + copy_to_buf(&data[start..end], buf) +} diff --git a/arm7tdmi/src/gdb/target.rs b/arm7tdmi/src/gdb/target.rs new file mode 100644 index 0000000..c6f7b88 --- /dev/null +++ b/arm7tdmi/src/gdb/target.rs @@ -0,0 +1,113 @@ +/// Implementing the Target trait for gdbstub +use gdbstub::common::Signal; +use gdbstub::target::ext::base::singlethread::{ + SingleThreadBase, SingleThreadResume, SingleThreadSingleStep, +}; +use gdbstub::target::ext::base::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps}; +use gdbstub::target::ext::base::BaseOps; +use gdbstub::target::ext::breakpoints::BreakpointsOps; +use gdbstub::target::{self, Target, TargetError, TargetResult}; + +use crate::memory::{DebugRead, MemoryInterface}; +use crate::registers_consts::*; +use crate::Arm7tdmiCore; + +pub trait MemoryGdbInterface: MemoryInterface + DebugRead { + fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize; +} + +impl Target for Arm7tdmiCore { + type Error = (); + type Arch = gdbstub_arch::arm::Armv4t; // as an example + + #[inline(always)] + fn base_ops(&mut self) -> BaseOps { + BaseOps::SingleThread(self) + } + + // opt-in to support for setting/removing breakpoints + #[inline(always)] + fn support_breakpoints(&mut self) -> Option> { + Some(self) + } + + fn support_memory_map(&mut self) -> Option> { + Some(self) + } +} + +impl SingleThreadBase for Arm7tdmiCore { + fn read_registers( + &mut self, + regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, + ) -> TargetResult<(), Self> { + regs.pc = self.get_reg(REG_PC); + regs.lr = self.get_reg(REG_LR); + regs.sp = self.get_reg(REG_SP); + regs.r[..].copy_from_slice(&self.gpr[..13]); + regs.cpsr = self.cpsr.get(); + Ok(()) + } + + fn write_registers( + &mut self, + regs: &gdbstub_arch::arm::reg::ArmCoreRegs, + ) -> TargetResult<(), Self> { + self.set_reg(REG_PC, regs.pc); + self.set_reg(REG_LR, regs.lr); + self.set_reg(REG_SP, regs.sp); + self.gpr.copy_from_slice(®s.r); + self.cpsr.set(regs.cpsr); + Ok(()) + } + + fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> { + self.bus.debug_get_into_bytes(start_addr, data); + Ok(()) + } + + fn write_addrs(&mut self, _start_addr: u32, _data: &[u8]) -> TargetResult<(), Self> { + // todo!("implement DebugWrite bus extention") + Err(TargetError::NonFatal) + } + + // most targets will want to support at resumption as well... + + #[inline(always)] + fn support_resume(&mut self) -> Option> { + Some(self) + } +} + +impl SingleThreadResume for Arm7tdmiCore { + fn resume(&mut self, _signal: Option) -> Result<(), Self::Error> { + // do nothing + Ok(()) + } + + // ...and if the target supports resumption, it'll likely want to support + // single-step resume as well + + #[inline(always)] + fn support_single_step(&mut self) -> Option> { + Some(self) + } +} + +impl SingleThreadSingleStep for Arm7tdmiCore { + fn step(&mut self, _signal: Option) -> Result<(), Self::Error> { + self.step(); + Ok(()) + } +} + +impl target::ext::memory_map::MemoryMap for Arm7tdmiCore { + fn memory_map_xml( + &self, + offset: u64, + length: usize, + buf: &mut [u8], + ) -> TargetResult { + Ok(self.bus.memory_map_xml(offset, length, buf)) + } +} diff --git a/arm7tdmi/src/lib.rs b/arm7tdmi/src/lib.rs index f9b8b6b..4d477d6 100644 --- a/arm7tdmi/src/lib.rs +++ b/arm7tdmi/src/lib.rs @@ -23,7 +23,10 @@ pub use alu::*; use memory::Addr; pub mod disass; pub mod exception; +pub mod gdb; pub mod psr; +mod simple_memory; +pub use simple_memory::SimpleMemory; pub mod registers_consts { pub const REG_PC: usize = 15; diff --git a/arm7tdmi/src/memory.rs b/arm7tdmi/src/memory.rs index 0048c11..d39cac2 100644 --- a/arm7tdmi/src/memory.rs +++ b/arm7tdmi/src/memory.rs @@ -225,13 +225,20 @@ pub trait DebugRead: BusIO { fn debug_read_8(&mut self, addr: Addr) -> u8; - fn debug_get_bytes(&mut self, range: std::ops::Range) -> Vec { + fn debug_get_bytes(&mut self, range: std::ops::Range) -> Vec { let mut bytes = Vec::new(); for b in range { bytes.push(self.debug_read_8(b)); } bytes } + + fn debug_get_into_bytes(&mut self, start_addr: Addr, bytes: &mut [u8]) { + bytes + .iter_mut() + .enumerate() + .for_each(|(idx, byte)| *byte = self.debug_read_8(start_addr + (idx as Addr))); + } } /// The caller is assumed to handle out of bound accesses, diff --git a/arm7tdmi/src/simple_memory.rs b/arm7tdmi/src/simple_memory.rs new file mode 100644 index 0000000..9e63fa1 --- /dev/null +++ b/arm7tdmi/src/simple_memory.rs @@ -0,0 +1,83 @@ +use crate::gdb::{copy_range_to_buf, target::MemoryGdbInterface}; +use crate::memory::{Addr, BusIO, DebugRead, MemoryAccess, MemoryInterface}; + +/// Simple wrapper around a bytearray for memory access +/// For use by tests and examples of this crate. +pub struct SimpleMemory { + data: Box<[u8]>, +} + +impl SimpleMemory { + pub fn new(capacity: usize) -> SimpleMemory { + SimpleMemory { + data: vec![0; capacity].into_boxed_slice(), + } + } + pub fn load_program(&mut self, program: &[u8]) { + self.data[..program.len()].copy_from_slice(program); + } +} + +impl MemoryInterface for SimpleMemory { + #[inline] + fn load_8(&mut self, addr: u32, _access: MemoryAccess) -> u8 { + self.read_8(addr) + } + + #[inline] + fn load_16(&mut self, addr: u32, _access: MemoryAccess) -> u16 { + self.read_16(addr & !1) + } + + #[inline] + fn load_32(&mut self, addr: u32, _access: MemoryAccess) -> u32 { + self.read_32(addr & !3) + } + + fn store_8(&mut self, addr: u32, value: u8, _access: MemoryAccess) { + self.write_8(addr, value); + } + + fn store_16(&mut self, addr: u32, value: u16, _access: MemoryAccess) { + self.write_16(addr & !1, value); + } + + fn store_32(&mut self, addr: u32, value: u32, _access: MemoryAccess) { + self.write_32(addr & !3, value); + } + + fn idle_cycle(&mut self) {} +} + +impl BusIO for SimpleMemory { + fn read_8(&mut self, addr: Addr) -> u8 { + *self.data.get(addr as usize).unwrap_or(&0) + } + + fn write_8(&mut self, addr: Addr, value: u8) { + if let 0..=0x3FFF = addr { + self.data[addr as usize] = value; + } + } +} + +impl DebugRead for SimpleMemory { + fn debug_read_8(&mut self, addr: Addr) -> u8 { + *self.data.get(addr as usize).unwrap_or(&0) + } +} + +impl MemoryGdbInterface for SimpleMemory { + fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize { + let memory_map = r#" + + + + "# + .trim() + .as_bytes(); + copy_range_to_buf(memory_map, offset, length, buf) + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 6e4fe36..e6c65d7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -37,7 +37,6 @@ hex-literal = "0.2.1" rustyline = { version = "6.0.0", optional = true } nom = { version = "5.0.0", optional = true } gdbstub = { version = "0.1.2", optional = true, features = ["std"] } -goblin = { version = "0.2", optional = true } fuzzy-matcher = { version = "0.3.4", optional = true } bit_reverse = "0.1.8" yaml-rust = "0.4" @@ -53,7 +52,7 @@ harness = false [features] default = [] -elf_support = ["goblin"] +elf_support = [] debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"] gdb = ["gdbstub"] # For use for ports where VideoInterface is not needed like wasm & jni diff --git a/core/src/cartridge/loader.rs b/core/src/cartridge/loader.rs index ec9fc28..e55589e 100644 --- a/core/src/cartridge/loader.rs +++ b/core/src/cartridge/loader.rs @@ -6,11 +6,11 @@ use std::io::prelude::*; use std::io::Cursor; use std::path::Path; +#[cfg(feature = "elf_support")] +use rustboyadvance_utils::elf::{load_elf, GoblinError}; use rustboyadvance_utils::read_bin_file; use zip::ZipArchive; -#[cfg(feature = "elf_support")] -use goblin; pub enum LoadRom { #[cfg(feature = "elf_support")] @@ -23,12 +23,22 @@ pub enum LoadRom { type LoadRomResult = GBAResult; #[cfg(feature = "elf_support")] -impl From for GBAError { - fn from(err: goblin::error::Error) -> GBAError { +impl From for GBAError { + fn from(err: GoblinError) -> GBAError { GBAError::CartridgeLoadError(format!("elf parsing error: {}", err)) } } +#[cfg(feature = "elf_support")] +pub(super) fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult { + const CART_BASE: usize = 0x0800_0000; + let elf = load_elf(elf_bytes, CART_BASE)?; + Ok(LoadRom::Elf { + data: elf.data, + symbols: elf.symbols, + }) +} + fn try_load_zip(data: &[u8]) -> LoadRomResult { let reader = Cursor::new(data); let mut archive = ZipArchive::new(reader)?; @@ -45,62 +55,6 @@ fn try_load_zip(data: &[u8]) -> LoadRomResult { )) } -#[cfg(feature = "elf_support")] -fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult { - const CART_BASE: usize = 0x0800_0000; - - let elf = goblin::elf::Elf::parse(&elf_bytes)?; - - let entry = elf.entry; - if entry != (CART_BASE as u64) { - return Err(GBAError::CartridgeLoadError( - "bad elf entry point, maybe multiboot rom ?".to_owned(), - )); - } - - let mut rom = vec![0; 0x200_0000]; - for phdr in &elf.program_headers { - if phdr.p_type == goblin::elf::program_header::PT_LOAD { - let file_range = phdr.file_range(); - let phys_range = - (phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize); - let phys_range_adjusted = (phdr.p_paddr as usize - CART_BASE) - ..(phdr.p_paddr as usize + phdr.p_memsz as usize - CART_BASE); - - if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() { - warn!("ELF: skipping program header {:?}", phdr); - continue; - } - - info!( - "ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}", - phdr, file_range, phys_range, - ); - - let src = &elf_bytes[file_range]; - let dst = &mut rom[phys_range_adjusted]; - dst.copy_from_slice(src); - } - } - - let mut symbols = HashMap::new(); - - let strtab = elf.strtab; - for sym in elf.syms.iter() { - if let Some(Ok(name)) = strtab.get(sym.st_name) { - // TODO do I also want to save the symbol size ? - symbols.insert(name.to_owned(), sym.st_value as u32); - } else { - warn!("failed to parse symbol name sym {:?}", sym); - } - } - - Ok(LoadRom::Elf { - data: rom, - symbols: symbols, - }) -} - pub(super) fn load_from_file(path: &Path) -> LoadRomResult { let bytes = read_bin_file(path)?; diff --git a/core/src/gba.rs b/core/src/gba.rs index 868fa99..baff346 100644 --- a/core/src/gba.rs +++ b/core/src/gba.rs @@ -331,7 +331,15 @@ impl GameBoyAdvance { } pub fn skip_bios(&mut self) { - self.cpu.skip_bios(); + self.cpu.banks.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS + self.cpu.banks.gpr_banked_r13[1] = 0x0300_7f00; // FIQ + self.cpu.banks.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ + self.cpu.banks.gpr_banked_r13[3] = 0x0300_7fe0; // SVC + self.cpu.banks.gpr_banked_r13[4] = 0x0300_7f00; // ABT + self.cpu.banks.gpr_banked_r13[5] = 0x0300_7f00; // UND + self.cpu.gpr[13] = 0x0300_7f00; + self.cpu.pc = 0x0800_0000; + self.cpu.cpsr.set(0x5f); self.sysbus.io.gpu.skip_bios(); } diff --git a/core/src/gdb.rs b/core/src/gdb.rs index f0a1fd2..c91d5b6 100644 --- a/core/src/gdb.rs +++ b/core/src/gdb.rs @@ -1,5 +1,5 @@ use super::arm7tdmi::CpuState; -use super::core::GameBoyAdvance; +use super::GameBoyAdvance; use super::interrupt::*; use super::iodev::IoDevices; use super::sysbus::SysBus; diff --git a/core/src/lib.rs b/core/src/lib.rs index 512af24..1d20eaf 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -52,9 +52,6 @@ pub use bus::*; mod mgba_debug; pub(crate) mod overrides; -#[cfg(feature = "gdb")] -pub mod gdb; - #[cfg(feature = "debugger")] pub mod debugger; diff --git a/platform/rustboyadvance-sdl2/Cargo.toml b/platform/rustboyadvance-sdl2/Cargo.toml index e3e6180..e4136f5 100644 --- a/platform/rustboyadvance-sdl2/Cargo.toml +++ b/platform/rustboyadvance-sdl2/Cargo.toml @@ -20,5 +20,6 @@ spin_sleep = "0.3.7" winres = "0.1" [features] +default = ["gdb"] debugger = ["rustboyadvance-core/debugger"] gdb = ["rustboyadvance-core/gdb"] \ No newline at end of file diff --git a/platform/rustboyadvance-sdl2/src/main.rs b/platform/rustboyadvance-sdl2/src/main.rs index 82604c1..ad9e01b 100644 --- a/platform/rustboyadvance-sdl2/src/main.rs +++ b/platform/rustboyadvance-sdl2/src/main.rs @@ -41,14 +41,11 @@ use input::create_input; use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH}; use rustboyadvance_core::cartridge::BackupType; -#[cfg(feature = "gdb")] -use rustboyadvance_core::gdb::spawn_and_run_gdb_server; use rustboyadvance_core::prelude::*; use rustboyadvance_utils::FpsCounter; const LOG_DIR: &str = ".logs"; -#[cfg(feature = "gdb")] const DEFAULT_GDB_SERVER_ADDR: &'static str = "localhost:1337"; const CANVAS_WIDTH: u32 = SCREEN_WIDTH; @@ -240,13 +237,8 @@ fn main() -> Result<(), Box> { } } - #[cfg(feature = "gdb")] if with_gdbserver { - spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?; - } - #[cfg(not(feature = "gdb"))] - if with_gdbserver { - panic!("Please compile me with 'gdb' feature") + todo!("gdb") } let mut fps_counter = FpsCounter::default(); @@ -277,7 +269,7 @@ fn main() -> Result<(), Box> { info!("ending debugger...") } #[cfg(feature = "gdb")] - Scancode::F2 => spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?, + Scancode::F2 => todo!("gdb"), Scancode::F5 => { info!("Saving state ..."); let save = gba.save_state()?; diff --git a/utils/Cargo.toml b/utils/Cargo.toml index e046852..0eb9f53 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" [dependencies] ringbuf = "0.2.2" +log = "0.4.8" +goblin = "0.2" + [target.'cfg(target_arch="wasm32")'.dependencies] instant = { version = "0.1.2", features = ["wasm-bindgen"] } diff --git a/utils/src/elf.rs b/utils/src/elf.rs new file mode 100644 index 0000000..4daace2 --- /dev/null +++ b/utils/src/elf.rs @@ -0,0 +1,67 @@ +use goblin::elf; +use log::{info, warn}; +use std::collections::HashMap; + +pub use goblin::error::Error as GoblinError; + +pub type SymbolMap = HashMap; + +pub struct LoadedElf { + pub data: Vec, + pub entry: u64, + pub symbols: SymbolMap, +} + +fn read_symbols_from_elf(elf: &elf::Elf) -> SymbolMap { + let mut symbols = SymbolMap::new(); + let strtab = &elf.strtab; + for sym in elf.syms.iter() { + if let Some(Ok(name)) = strtab.get(sym.st_name) { + // TODO do I also want to save the symbol size ? + symbols.insert(name.to_owned(), sym.st_value as u32); + } else { + warn!("failed to parse symbol name sym {:?}", sym); + } + } + symbols +} + +pub fn read_symbols(elf_bytes: &[u8]) -> goblin::error::Result { + let elf = elf::Elf::parse(elf_bytes)?; + Ok(read_symbols_from_elf(&elf)) +} + +pub fn load_elf(elf_bytes: &[u8], base: usize) -> goblin::error::Result { + let elf = elf::Elf::parse(elf_bytes)?; + + let mut rom = vec![0; 0x200_0000]; + for phdr in &elf.program_headers { + if phdr.p_type == elf::program_header::PT_LOAD { + let file_range = phdr.file_range(); + let phys_range = + (phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize); + let phys_range_adjusted = (phdr.p_paddr as usize - base) + ..(phdr.p_paddr as usize + phdr.p_memsz as usize - base); + + if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() { + warn!("ELF: skipping program header {:?}", phdr); + continue; + } + + info!( + "ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}", + phdr, file_range, phys_range, + ); + + let src = &elf_bytes[file_range]; + let dst = &mut rom[phys_range_adjusted]; + dst.copy_from_slice(src); + } + } + + Ok(LoadedElf { + data: rom, + entry: elf.entry, + symbols: read_symbols_from_elf(&elf), + }) +} diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 07927d3..6c1dc35 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -6,6 +6,8 @@ use std::path::Path; use std::ptr; use std::time; +pub mod elf; + #[cfg(not(target_arch = "wasm32"))] type Instant = time::Instant; #[cfg(not(target_arch = "wasm32"))]