summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortaitep <taitep@taitep.se>2025-12-27 11:48:36 +0100
committertaitep <taitep@taitep.se>2025-12-27 11:48:36 +0100
commit9f8e9ec380ad679fe714222345a46ebf77d063d6 (patch)
treebe4a990b220e52c9fcbd5c0abc95b45e2246d73c
parenta64fcaa3b557d3e7f611f3997a0b4c6990347d9b (diff)
Implement a GDB stub and fix another huge issue in S-type immediate decoding
-rw-r--r--Cargo.lock149
-rw-r--r--Cargo.toml2
-rw-r--r--src/core.rs145
-rw-r--r--src/core/commands.rs7
-rw-r--r--src/decode.rs4
-rw-r--r--src/exceptions.rs1
-rw-r--r--src/gdb.rs315
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs38
9 files changed, 629 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c50db93..1dfb25c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,56 @@
version = 4
[[package]]
+name = "anstream"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -27,6 +77,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
+name = "clap"
+version = "4.5.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
name = "goblin"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -38,6 +134,12 @@ dependencies = [
]
[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
name = "int-enum"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -50,6 +152,12 @@ dependencies = [
]
[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
name = "libc"
version = "0.2.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -83,6 +191,18 @@ dependencies = [
]
[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "oneshot"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea"
+
+[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -139,6 +259,12 @@ dependencies = [
]
[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
name = "syn"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -154,10 +280,12 @@ name = "trve"
version = "0.0.0"
dependencies = [
"anyhow",
+ "clap",
"goblin",
"int-enum",
"memmap2",
"nix",
+ "oneshot",
]
[[package]]
@@ -167,7 +295,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
diff --git a/Cargo.toml b/Cargo.toml
index f76ab77..35ad73b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,9 @@ edition = "2024"
[dependencies]
anyhow = "1.0.100"
+clap = { version = "4.5.53", features = ["derive"] }
goblin = "0.10.4"
int-enum = "1.2.0"
memmap2 = "0.9.8"
nix = { version = "0.30.1", features = ["fs"] }
+oneshot = "0.1.11"
diff --git a/src/core.rs b/src/core.rs
index acc52f2..dde8fd7 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -4,10 +4,14 @@
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
// See LICENSE file in the project root for full license text.
+use std::{collections::HashSet, sync::mpsc};
+
use crate::{
consts::{Addr, RegId, RegValue},
+ core::commands::CoreCmd,
decode::Instruction,
exceptions::ExceptionType,
+ gdb::{self, DebugCommand, StopReason},
instructions::find_and_exec,
mem::MemConfig,
};
@@ -16,53 +20,154 @@ pub struct Core {
pub(crate) x_regs: [RegValue; 32],
pub(crate) pc: Addr,
pub(crate) mem: MemConfig,
+ command_stream: mpsc::Receiver<CoreCmd>,
}
+pub mod commands;
+
impl Core {
- pub fn new(mem: MemConfig) -> Self {
+ pub fn new(mem: MemConfig, command_stream: mpsc::Receiver<CoreCmd>) -> Self {
Self {
x_regs: [0; 32],
pc: 0,
mem,
+ command_stream,
}
}
pub fn run(&mut self) {
loop {
- if !self.pc.is_multiple_of(4) {
- self.throw_exception(ExceptionType::InstructionAddressMisaligned);
+ if let Ok(cmd) = self.command_stream.try_recv() {
+ match cmd {
+ CoreCmd::EnterDbgMode(dbg_stream) => {
+ let _ = self.debug_loop(dbg_stream);
+ }
+ };
+ }
+
+ if let Err(e) = self.step() {
+ self.throw_exception(e);
break;
}
+ }
+ }
- let instr = match self.mem.read_word(self.pc) {
- Ok(i) => i,
- Err(e) => {
- self.throw_exception(e.to_exception_instr());
- break;
+ pub fn run_waiting_for_cmd(&mut self) {
+ eprintln!("Waiting for any core command...");
+ if let Ok(cmd) = self.command_stream.recv() {
+ eprintln!("Recieved a command");
+ match cmd {
+ CoreCmd::EnterDbgMode(dbg_stream) => {
+ let _ = self.debug_loop(dbg_stream);
}
};
+ } else {
+ eprintln!("Error recieving command, starting anyway");
+ }
- if instr == 0 {
- self.throw_exception(ExceptionType::IllegalInstruction);
- break;
- }
+ eprintln!("Command processed");
- if instr & 3 != 3 {
- // Compressed instruction - (currently) unsupported
- self.throw_exception(ExceptionType::IllegalInstruction);
- break;
+ self.run();
+ }
+
+ fn debug_loop(&mut self, dbg_stream: mpsc::Receiver<gdb::DebugCommand>) -> anyhow::Result<()> {
+ let mut breakpoints = HashSet::new();
+
+ loop {
+ match dbg_stream.recv()? {
+ DebugCommand::GetRegs(sender) => sender.send(gdb::RegsResponse {
+ x_regs: self.x_regs.clone(),
+ pc: self.pc,
+ })?,
+ DebugCommand::ReadMem {
+ addr,
+ len,
+ responder,
+ } => {
+ let data = (0..len)
+ .map(|offset| self.mem.read_byte(addr + offset))
+ .collect();
+
+ responder.send(data)?;
+ }
+ DebugCommand::SetBreakpoint(addr) => {
+ breakpoints.insert(addr);
+ }
+ DebugCommand::RemoveBreakpoint(addr) => {
+ breakpoints.remove(&addr);
+ }
+ DebugCommand::Step(responder) => {
+ responder.send(match self.step() {
+ Ok(_) => gdb::StopReason::Step,
+ Err(e) => {
+ self.throw_exception(e);
+ gdb::StopReason::Exception(e)
+ }
+ })?;
+ }
+ DebugCommand::Continue(responder, stopper) => {
+ responder.send(self.continue_loop(&breakpoints, stopper))?;
+ }
+ DebugCommand::ExitDebugMode => {
+ eprintln!("exitdbgmode");
+ break Ok(());
+ }
+ };
+ }
+ }
+
+ fn continue_loop(
+ &mut self,
+ breakpoints: &HashSet<Addr>,
+ stopper: oneshot::Receiver<()>,
+ ) -> StopReason {
+ loop {
+ if breakpoints.contains(&self.pc) {
+ return StopReason::Exception(ExceptionType::Breakpoint);
}
- let instr = Instruction(instr);
+ if let Ok(_) = stopper.try_recv() {
+ return StopReason::Interrupted;
+ }
- if let Err(e) = find_and_exec(instr, self) {
+ if let Err(e) = self.step() {
self.throw_exception(e);
- eprintln!("instr: {:08x}", instr.0);
- break;
+ return StopReason::Exception(e);
}
}
}
+ pub(crate) fn step(&mut self) -> Result<(), ExceptionType> {
+ if !self.pc.is_multiple_of(4) {
+ self.throw_exception(ExceptionType::InstructionAddressMisaligned);
+ }
+
+ let instr = match self.mem.read_word(self.pc) {
+ Ok(i) => i,
+ Err(e) => {
+ return Err(e.to_exception_instr());
+ }
+ };
+
+ if instr == 0 {
+ return Err(ExceptionType::IllegalInstruction);
+ }
+
+ if instr & 3 != 3 {
+ // Compressed instruction - (currently) unsupported
+ return Err(ExceptionType::IllegalInstruction);
+ }
+
+ let instr = Instruction(instr);
+
+ if let Err(e) = find_and_exec(instr, self) {
+ eprintln!("instr: {:08x}", instr.0);
+ return Err(e);
+ }
+
+ Ok(())
+ }
+
fn throw_exception(&mut self, exception_type: ExceptionType) {
eprintln!("Exception: {exception_type:?}");
dbg!(self.pc, self.x_regs);
diff --git a/src/core/commands.rs b/src/core/commands.rs
new file mode 100644
index 0000000..93bdd1e
--- /dev/null
+++ b/src/core/commands.rs
@@ -0,0 +1,7 @@
+use std::sync::mpsc;
+
+use crate::gdb;
+
+pub enum CoreCmd {
+ EnterDbgMode(mpsc::Receiver<gdb::DebugCommand>),
+}
diff --git a/src/decode.rs b/src/decode.rs
index 4258a6f..d2e8a80 100644
--- a/src/decode.rs
+++ b/src/decode.rs
@@ -57,7 +57,9 @@ impl Instruction {
#[inline]
pub fn imm_s(self) -> DWord {
- (self.0 as i32 as i64 >> (25 - 5) & (0x7f << 5)) as DWord | (self.0 >> 7 & 0b11111) as DWord
+ let imm_11_5 = (self.0 as i32 as i64 >> 25 << 5) as DWord;
+ let imm_4_0 = (self.0 >> 7 & 0x1f) as DWord;
+ imm_11_5 | imm_4_0
}
#[inline]
diff --git a/src/exceptions.rs b/src/exceptions.rs
index 7f00e5c..3c1d4bd 100644
--- a/src/exceptions.rs
+++ b/src/exceptions.rs
@@ -29,6 +29,7 @@ pub enum ExceptionType {
HardwareError = 19,
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryExceptionType {
AddressMisaligned,
AccessFault,
diff --git a/src/gdb.rs b/src/gdb.rs
new file mode 100644
index 0000000..17e2f71
--- /dev/null
+++ b/src/gdb.rs
@@ -0,0 +1,315 @@
+use std::{
+ io::{self, BufRead, ErrorKind, Write},
+ net::{TcpListener, TcpStream},
+ sync::mpsc,
+};
+
+use crate::{
+ consts::{Addr, RegValue},
+ core::commands::CoreCmd,
+ exceptions::{ExceptionType, MemoryExceptionType},
+};
+
+pub(crate) enum DebugCommand {
+ GetRegs(oneshot::Sender<RegsResponse>),
+ ReadMem {
+ addr: Addr,
+ len: u64,
+ responder: oneshot::Sender<Result<Vec<u8>, MemoryExceptionType>>,
+ },
+ Step(oneshot::Sender<StopReason>),
+ Continue(oneshot::Sender<StopReason>, oneshot::Receiver<()>),
+ SetBreakpoint(Addr),
+ RemoveBreakpoint(Addr),
+ ExitDebugMode,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum StopReason {
+ Exception(ExceptionType),
+ Step,
+ Interrupted,
+}
+
+impl StopReason {
+ fn to_rsp(self) -> &'static str {
+ match self {
+ StopReason::Step => "S05",
+ StopReason::Exception(e) => {
+ use ExceptionType::*;
+ match e {
+ IllegalInstruction => "S04",
+ InstructionAddressMisaligned
+ | InstructionAccessFault
+ | InstructionPageFault
+ | LoadAddressMisaligned
+ | LoadAccessFault
+ | LoadPageFault
+ | StoreAmoAddressMisaligned
+ | StoreAmoAccessFault
+ | StoreAmoPageFault => "S0b",
+ _ => "S05",
+ }
+ }
+ StopReason::Interrupted => "S02",
+ }
+ }
+}
+
+pub(crate) struct RegsResponse {
+ pub x_regs: [RegValue; 32],
+ pub pc: Addr,
+}
+
+pub fn run_stub(cmd_sender: mpsc::Sender<CoreCmd>) {
+ std::thread::spawn(move || {
+ let listener = TcpListener::bind("127.0.0.1:1234").expect("couldnt start tcp listener");
+
+ for stream_res in listener.incoming() {
+ if let Ok(stream) = stream_res {
+ let (dbg_tx, dbg_rx) = mpsc::channel();
+
+ stream
+ .set_nonblocking(true)
+ .expect("Couldnt set TCP stream to nonblocking");
+
+ cmd_sender
+ .send(CoreCmd::EnterDbgMode(dbg_rx))
+ .expect("couldnt ask core to enter debug mode");
+
+ handle_gdb_connection(stream, dbg_tx).expect("failure during connection");
+ }
+ }
+ });
+}
+
+fn handle_gdb_connection(
+ gdb_stream: TcpStream,
+ dbg_tx: mpsc::Sender<DebugCommand>,
+) -> io::Result<()> {
+ eprintln!("gdb connected");
+ let mut reader = io::BufReader::new(gdb_stream.try_clone()?);
+ let mut writer = gdb_stream;
+
+ loop {
+ match read_rsp_packet(&mut reader) {
+ Ok(packet) => match handle_packet(
+ &packet[..packet.len() - 1],
+ &mut writer,
+ &dbg_tx,
+ &mut reader,
+ ) {
+ Err(_) => {
+ let _ = dbg_tx.send(DebugCommand::ExitDebugMode);
+ break;
+ }
+ _ => {}
+ },
+ Err(ref e) if e.kind() == ErrorKind::WouldBlock => {
+ std::thread::yield_now();
+ }
+ Err(_) => {
+ let _ = dbg_tx.send(DebugCommand::ExitDebugMode);
+ break;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn read_rsp_packet<R: BufRead>(reader: &mut R) -> io::Result<String> {
+ let mut buf = Vec::new();
+
+ // Wait for leading '$'
+ loop {
+ let mut byte = [0u8];
+ let n = reader.read(&mut byte)?;
+ if n == 0 {
+ return Err(io::Error::new(ErrorKind::UnexpectedEof, "Disconnected"));
+ }
+ if byte[0] == b'$' {
+ break;
+ }
+ }
+
+ // Read until '#'
+ reader.read_until(b'#', &mut buf)?;
+
+ let mut checksum = [0u8; 2];
+ reader.read_exact(&mut checksum)?;
+
+ String::from_utf8(buf).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
+}
+
+fn handle_packet<W: Write, R: BufRead>(
+ packet: &str,
+ writer: &mut W,
+ dbg_tx: &mpsc::Sender<DebugCommand>,
+ reader: &mut R,
+) -> io::Result<()> {
+ writer.write_all(b"+")?;
+ if packet.is_empty() {
+ send_packet("", writer)?;
+ return Ok(());
+ }
+
+ match &packet[0..1] {
+ "?" => {
+ send_packet("S05", writer)?;
+ }
+
+ "g" => {
+ let (regs_tx, regs_rx) = oneshot::channel();
+ dbg_tx.send(DebugCommand::GetRegs(regs_tx)).unwrap();
+ let regs = regs_rx.recv().unwrap();
+ let mut hex = String::with_capacity(32 * 8 * 2 + 8 * 2);
+ for &reg in &regs.x_regs {
+ hex.push_str(
+ &reg.to_le_bytes()
+ .iter()
+ .map(|b| format!("{b:02x}"))
+ .collect::<String>(),
+ );
+ }
+ hex.push_str(
+ &regs
+ .pc
+ .to_le_bytes()
+ .iter()
+ .map(|b| format!("{b:02x}"))
+ .collect::<String>(),
+ );
+ send_packet(&hex, writer)?;
+ }
+
+ "m" => {
+ if let Some((addr_str, len_str)) = packet[1..].split_once(',') {
+ if let (Ok(addr), Ok(len)) = (
+ u64::from_str_radix(addr_str, 16),
+ u64::from_str_radix(len_str, 16),
+ ) {
+ let (responder, response) = oneshot::channel();
+ dbg_tx
+ .send(DebugCommand::ReadMem {
+ addr,
+ len,
+ responder,
+ })
+ .unwrap();
+ let response = response.recv().unwrap();
+
+ match response {
+ Ok(data) => {
+ let hex: String = data.iter().map(|b| format!("{b:02x}")).collect();
+ send_packet(&hex, writer)?;
+ }
+ Err(e) => send_packet(&format!("E.{e:?}"), writer)?,
+ };
+ } else {
+ send_packet("", writer)?;
+ }
+ } else {
+ send_packet("", writer)?;
+ }
+ }
+
+ "s" => {
+ let (responder, stop_reason_rx) = oneshot::channel();
+ dbg_tx.send(DebugCommand::Step(responder)).unwrap();
+ let stop_reason = stop_reason_rx.recv().unwrap();
+ send_packet(&stop_reason.to_rsp(), writer)?;
+ }
+
+ "c" => {
+ let (responder, stop_reason_rx) = oneshot::channel();
+ let (stopper, stop_listener) = oneshot::channel();
+ dbg_tx
+ .send(DebugCommand::Continue(responder, stop_listener))
+ .unwrap();
+
+ loop {
+ let mut byte = [0u8];
+ match reader.read(&mut byte) {
+ Ok(0) => {
+ stopper.send(()).unwrap();
+ break;
+ }
+ Ok(1) if byte[0] == 0x03 => {
+ stopper.send(()).unwrap();
+ break;
+ }
+ Ok(_) => {}
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ Err(e) => {
+ return Err(e);
+ }
+ }
+
+ if let Ok(stop_reason) = stop_reason_rx.try_recv() {
+ send_packet(&stop_reason.to_rsp(), writer)?;
+ return Ok(());
+ }
+
+ std::thread::yield_now();
+ }
+
+ let stop_reason = stop_reason_rx.recv().unwrap();
+ send_packet(stop_reason.to_rsp(), writer)?;
+ }
+
+ "Z" if packet.chars().nth(1) == Some('0') => {
+ if let Some((addr_str, size_str)) = packet[3..].split_once(',') {
+ if let (Ok(addr), Ok(size)) = (
+ u64::from_str_radix(addr_str, 16),
+ u64::from_str_radix(size_str, 16),
+ ) {
+ if size != 4 {
+ send_packet("", writer)?;
+ return Ok(());
+ }
+
+ dbg_tx.send(DebugCommand::SetBreakpoint(addr)).unwrap();
+ send_packet("OK", writer)?;
+ return Ok(());
+ }
+ }
+ send_packet("", writer)?;
+ }
+
+ "z" if packet.chars().nth(1) == Some('0') => {
+ if let Some((addr_str, size_str)) = packet[3..].split_once(',') {
+ if let (Ok(addr), Ok(size)) = (
+ u64::from_str_radix(addr_str, 16),
+ u64::from_str_radix(size_str, 16),
+ ) {
+ if size != 4 {
+ send_packet("", writer)?;
+ return Ok(());
+ }
+
+ dbg_tx.send(DebugCommand::RemoveBreakpoint(addr)).unwrap();
+ send_packet("OK", writer)?;
+ return Ok(());
+ }
+ }
+ send_packet("", writer)?;
+ }
+
+ _ => {
+ send_packet("", writer)?;
+ }
+ }
+
+ Ok(())
+}
+
+fn send_packet<W: Write>(packet: &str, writer: &mut W) -> io::Result<()> {
+ writer.write_all(b"$")?;
+ writer.write_all(packet.as_bytes())?;
+ writer.write_all(b"#")?;
+ let checksum = packet.bytes().fold(0u8, |acc, b| acc.wrapping_add(b));
+ write!(writer, "{checksum:02x}")?;
+ // eprintln!("successfully sent packet {packet:?}");
+ Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
index 7a9ac37..6eda620 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,5 +2,6 @@ pub mod consts;
pub mod core;
mod decode;
pub mod exceptions;
+pub mod gdb;
mod instructions;
pub mod mem;
diff --git a/src/main.rs b/src/main.rs
index 03072f6..274ef02 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,33 +4,38 @@
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
// See LICENSE file in the project root for full license text.
-use std::{env, sync::Arc, time::Duration};
+use std::{env, path::PathBuf, sync::Arc, time::Duration};
+
+use clap::Parser;
use trve::{
consts::{Addr, Byte, DWord, HWord, Word},
core::Core,
exceptions::MemoryExceptionType,
+ gdb,
mem::{MemConfig, MemDeviceInterface, MmioRoot, Ram},
};
-use anyhow::{Result, bail};
+use anyhow::Result;
use crate::basic_uart::BasicUart;
mod execload;
+#[derive(Parser)]
+struct Args {
+ executable: PathBuf,
+ #[arg(long)]
+ wait: bool,
+}
+
fn main() -> Result<()> {
+ let args = Args::parse();
+
let mut ram = Ram::try_new(16 * 1024 * 1024)?;
let buf = ram.buf_mut();
- let args: Vec<String> = env::args().collect();
-
- if args.len() != 2 {
- eprintln!("USAGE: trve <ram_image>");
- bail!("Wrong number of arguments");
- }
-
- let entry_point = execload::load(&args[1], buf)?;
+ let entry_point = execload::load(args.executable, buf)?;
let mut mmio_root = MmioRoot::default();
mmio_root.insert(0, Arc::new(DbgOut));
@@ -44,9 +49,18 @@ fn main() -> Result<()> {
mmio_root,
};
- let mut core = Core::new(mem_cfg);
+ let (cmd_sender, cmd_reciever) = std::sync::mpsc::channel();
+
+ gdb::run_stub(cmd_sender.clone());
+
+ let mut core = Core::new(mem_cfg, cmd_reciever);
core.reset(entry_point);
- core.run();
+
+ if args.wait {
+ core.run_waiting_for_cmd();
+ } else {
+ core.run();
+ }
Ok(())
}