Compare commits
2 Commits
34034dd5db
...
9f8e9ec380
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f8e9ec380 | |||
| a64fcaa3b5 |
149
Cargo.lock
generated
149
Cargo.lock
generated
@@ -2,6 +2,56 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
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]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.100"
|
version = "1.0.100"
|
||||||
@@ -26,6 +76,52 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
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]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "goblin"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -37,6 +133,12 @@ dependencies = [
|
|||||||
"scroll",
|
"scroll",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "int-enum"
|
name = "int-enum"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -49,6 +151,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.176"
|
version = "0.2.176"
|
||||||
@@ -82,6 +190,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "plain"
|
name = "plain"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -138,6 +258,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.111"
|
version = "2.0.111"
|
||||||
@@ -154,10 +280,12 @@ name = "trve"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
"goblin",
|
"goblin",
|
||||||
"int-enum",
|
"int-enum",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"nix",
|
"nix",
|
||||||
|
"oneshot",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -166,8 +294,29 @@ version = "1.0.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
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",
|
||||||
|
]
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
goblin = "0.10.4"
|
goblin = "0.10.4"
|
||||||
int-enum = "1.2.0"
|
int-enum = "1.2.0"
|
||||||
memmap2 = "0.9.8"
|
memmap2 = "0.9.8"
|
||||||
nix = { version = "0.30.1", features = ["fs"] }
|
nix = { version = "0.30.1", features = ["fs"] }
|
||||||
|
oneshot = "0.1.11"
|
||||||
|
|||||||
127
src/core.rs
127
src/core.rs
@@ -4,10 +4,14 @@
|
|||||||
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
|
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
|
||||||
// See LICENSE file in the project root for full license text.
|
// See LICENSE file in the project root for full license text.
|
||||||
|
|
||||||
|
use std::{collections::HashSet, sync::mpsc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{Addr, RegId, RegValue},
|
consts::{Addr, RegId, RegValue},
|
||||||
|
core::commands::CoreCmd,
|
||||||
decode::Instruction,
|
decode::Instruction,
|
||||||
exceptions::ExceptionType,
|
exceptions::ExceptionType,
|
||||||
|
gdb::{self, DebugCommand, StopReason},
|
||||||
instructions::find_and_exec,
|
instructions::find_and_exec,
|
||||||
mem::MemConfig,
|
mem::MemConfig,
|
||||||
};
|
};
|
||||||
@@ -16,51 +20,152 @@ pub struct Core {
|
|||||||
pub(crate) x_regs: [RegValue; 32],
|
pub(crate) x_regs: [RegValue; 32],
|
||||||
pub(crate) pc: Addr,
|
pub(crate) pc: Addr,
|
||||||
pub(crate) mem: MemConfig,
|
pub(crate) mem: MemConfig,
|
||||||
|
command_stream: mpsc::Receiver<CoreCmd>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new(mem: MemConfig) -> Self {
|
pub fn new(mem: MemConfig, command_stream: mpsc::Receiver<CoreCmd>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
x_regs: [0; 32],
|
x_regs: [0; 32],
|
||||||
pc: 0,
|
pc: 0,
|
||||||
mem,
|
mem,
|
||||||
|
command_stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("Command processed");
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(_) = stopper.try_recv() {
|
||||||
|
return StopReason::Interrupted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = self.step() {
|
||||||
|
self.throw_exception(e);
|
||||||
|
return StopReason::Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn step(&mut self) -> Result<(), ExceptionType> {
|
||||||
if !self.pc.is_multiple_of(4) {
|
if !self.pc.is_multiple_of(4) {
|
||||||
self.throw_exception(ExceptionType::InstructionAddressMisaligned);
|
self.throw_exception(ExceptionType::InstructionAddressMisaligned);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let instr = match self.mem.read_word(self.pc) {
|
let instr = match self.mem.read_word(self.pc) {
|
||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.throw_exception(e.to_exception_instr());
|
return Err(e.to_exception_instr());
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if instr == 0 {
|
if instr == 0 {
|
||||||
self.throw_exception(ExceptionType::IllegalInstruction);
|
return Err(ExceptionType::IllegalInstruction);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if instr & 3 != 3 {
|
if instr & 3 != 3 {
|
||||||
// Compressed instruction - (currently) unsupported
|
// Compressed instruction - (currently) unsupported
|
||||||
self.throw_exception(ExceptionType::IllegalInstruction);
|
return Err(ExceptionType::IllegalInstruction);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let instr = Instruction(instr);
|
let instr = Instruction(instr);
|
||||||
|
|
||||||
if let Err(e) = find_and_exec(instr, self) {
|
if let Err(e) = find_and_exec(instr, self) {
|
||||||
self.throw_exception(e);
|
|
||||||
eprintln!("instr: {:08x}", instr.0);
|
eprintln!("instr: {:08x}", instr.0);
|
||||||
break;
|
return Err(e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn throw_exception(&mut self, exception_type: ExceptionType) {
|
fn throw_exception(&mut self, exception_type: ExceptionType) {
|
||||||
|
|||||||
7
src/core/commands.rs
Normal file
7
src/core/commands.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use crate::gdb;
|
||||||
|
|
||||||
|
pub enum CoreCmd {
|
||||||
|
EnterDbgMode(mpsc::Receiver<gdb::DebugCommand>),
|
||||||
|
}
|
||||||
@@ -57,7 +57,9 @@ impl Instruction {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn imm_s(self) -> DWord {
|
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]
|
#[inline]
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub enum ExceptionType {
|
|||||||
HardwareError = 19,
|
HardwareError = 19,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum MemoryExceptionType {
|
pub enum MemoryExceptionType {
|
||||||
AddressMisaligned,
|
AddressMisaligned,
|
||||||
AccessFault,
|
AccessFault,
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use goblin::{
|
|||||||
program_header::PT_LOAD,
|
program_header::PT_LOAD,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use trve::consts::Addr;
|
use trve::{consts::Addr, mem::RAM_START};
|
||||||
|
|
||||||
pub fn load<P: AsRef<Path>>(path: P, ram: &mut [u8], ram_start: Addr) -> Result<Addr> {
|
pub fn load<P: AsRef<Path>>(path: P, ram: &mut [u8]) -> Result<Addr> {
|
||||||
let buf = fs::read(path)?;
|
let buf = fs::read(path)?;
|
||||||
|
|
||||||
match Object::parse(&buf)? {
|
match Object::parse(&buf)? {
|
||||||
@@ -36,11 +36,11 @@ pub fn load<P: AsRef<Path>>(path: P, ram: &mut [u8], ram_start: Addr) -> Result<
|
|||||||
|
|
||||||
for ph in elf.program_headers {
|
for ph in elf.program_headers {
|
||||||
if ph.p_type == PT_LOAD {
|
if ph.p_type == PT_LOAD {
|
||||||
let start = (ph.p_vaddr - ram_start) as usize;
|
let start = (ph.p_vaddr - RAM_START) as usize;
|
||||||
let end = start + ph.p_memsz as usize;
|
let end = start + ph.p_memsz as usize;
|
||||||
let file_end = start + ph.p_filesz as usize;
|
let file_end = start + ph.p_filesz as usize;
|
||||||
|
|
||||||
if end > ram_start as usize + ram.len() {
|
if end > RAM_START as usize + ram.len() {
|
||||||
bail!("Segment at 0x{:x} does not fit in RAM", ph.p_vaddr);
|
bail!("Segment at 0x{:x} does not fit in RAM", ph.p_vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ pub fn load<P: AsRef<Path>>(path: P, ram: &mut [u8], ram_start: Addr) -> Result<
|
|||||||
bail!("Program too large for RAM");
|
bail!("Program too large for RAM");
|
||||||
}
|
}
|
||||||
ram[..buf.len()].copy_from_slice(&buf);
|
ram[..buf.len()].copy_from_slice(&buf);
|
||||||
Ok(ram_start)
|
Ok(RAM_START)
|
||||||
}
|
}
|
||||||
_ => bail!("Unsupported executable format"),
|
_ => bail!("Unsupported executable format"),
|
||||||
}
|
}
|
||||||
|
|||||||
315
src/gdb.rs
Normal file
315
src/gdb.rs
Normal file
@@ -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 ® in ®s.x_regs {
|
||||||
|
hex.push_str(
|
||||||
|
®.to_le_bytes()
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("{b:02x}"))
|
||||||
|
.collect::<String>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hex.push_str(
|
||||||
|
®s
|
||||||
|
.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(())
|
||||||
|
}
|
||||||
@@ -2,5 +2,6 @@ pub mod consts;
|
|||||||
pub mod core;
|
pub mod core;
|
||||||
mod decode;
|
mod decode;
|
||||||
pub mod exceptions;
|
pub mod exceptions;
|
||||||
|
pub mod gdb;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
|
|||||||
36
src/main.rs
36
src/main.rs
@@ -4,33 +4,38 @@
|
|||||||
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
|
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
|
||||||
// See LICENSE file in the project root for full license text.
|
// 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::{
|
use trve::{
|
||||||
consts::{Addr, Byte, DWord, HWord, Word},
|
consts::{Addr, Byte, DWord, HWord, Word},
|
||||||
core::Core,
|
core::Core,
|
||||||
exceptions::MemoryExceptionType,
|
exceptions::MemoryExceptionType,
|
||||||
|
gdb,
|
||||||
mem::{MemConfig, MemDeviceInterface, MmioRoot, Ram},
|
mem::{MemConfig, MemDeviceInterface, MmioRoot, Ram},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::basic_uart::BasicUart;
|
use crate::basic_uart::BasicUart;
|
||||||
|
|
||||||
mod execload;
|
mod execload;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
struct Args {
|
||||||
|
executable: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
wait: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
let mut ram = Ram::try_new(16 * 1024 * 1024)?;
|
let mut ram = Ram::try_new(16 * 1024 * 1024)?;
|
||||||
let buf = ram.buf_mut();
|
let buf = ram.buf_mut();
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let entry_point = execload::load(args.executable, buf)?;
|
||||||
|
|
||||||
if args.len() != 2 {
|
|
||||||
eprintln!("USAGE: trve <ram_image>");
|
|
||||||
bail!("Wrong number of arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
let entry_point = execload::load(&args[1], buf, 0x8000_0000)?;
|
|
||||||
|
|
||||||
let mut mmio_root = MmioRoot::default();
|
let mut mmio_root = MmioRoot::default();
|
||||||
mmio_root.insert(0, Arc::new(DbgOut));
|
mmio_root.insert(0, Arc::new(DbgOut));
|
||||||
@@ -44,9 +49,18 @@ fn main() -> Result<()> {
|
|||||||
mmio_root,
|
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.reset(entry_point);
|
||||||
|
|
||||||
|
if args.wait {
|
||||||
|
core.run_waiting_for_cmd();
|
||||||
|
} else {
|
||||||
core.run();
|
core.run();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user