Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'read/write register' commands #22

Merged
merged 8 commits into from
Sep 9, 2020
46 changes: 44 additions & 2 deletions examples/armv4t/gdb.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
use core::convert::TryInto;

use armv4t_emu::{reg, Memory};
use gdbstub::{
arch, Actions, BreakOp, OptResult, ResumeAction, StopReason, Target, Tid, WatchKind,
SINGLE_THREAD_TID,
arch, arch::arm::reg::ArmCoreRegId, Actions, BreakOp, OptResult, ResumeAction, StopReason,
Target, Tid, WatchKind, SINGLE_THREAD_TID,
};

use crate::emu::{Emu, Event};

/// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`.
fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
match id {
ArmCoreRegId::Gpr(i) => Some(i),
ArmCoreRegId::Sp => Some(reg::SP),
ArmCoreRegId::Lr => Some(reg::LR),
ArmCoreRegId::Pc => Some(reg::PC),
ArmCoreRegId::Cpsr => Some(reg::CPSR),
_ => None,
}
}

impl Target for Emu {
type Arch = arch::arm::Armv4t;
type Error = &'static str;
Expand Down Expand Up @@ -56,6 +70,20 @@ impl Target for Emu {
))
}

fn read_register(
&mut self,
reg_id: arch::arm::reg::ArmCoreRegId,
dst: &mut [u8],
) -> OptResult<(), Self::Error> {
if let Some(i) = cpu_reg_id(reg_id) {
let w = self.cpu.reg_get(self.cpu.mode(), i);
dst.copy_from_slice(&w.to_le_bytes());
Ok(())
} else {
Err("unsupported register read".into())
}
}

fn read_registers(
&mut self,
regs: &mut arch::arm::reg::ArmCoreRegs,
Expand All @@ -73,6 +101,20 @@ impl Target for Emu {
Ok(())
}

fn write_register(
daniel5151 marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
reg_id: arch::arm::reg::ArmCoreRegId,
val: &[u8],
) -> OptResult<(), Self::Error> {
let w = u32::from_le_bytes(val.try_into().map_err(|_| "invalid data")?);
if let Some(i) = cpu_reg_id(reg_id) {
self.cpu.reg_set(self.cpu.mode(), i, w);
Ok(())
} else {
Err("unsupported register write".into())
}
}

fn write_registers(&mut self, regs: &arch::arm::reg::ArmCoreRegs) -> Result<(), &'static str> {
let mode = self.cpu.mode();

Expand Down
37 changes: 36 additions & 1 deletion src/arch/arm/reg/arm_core.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
use crate::arch::Registers;
use crate::arch::{RegId, Registers};

/// 32-bit ARM core register identifier.
#[derive(Debug)]
pub enum ArmCoreRegId {
/// General purpose registers (R0-R12)
Gpr(u8),
/// Stack Pointer (R13)
Sp,
/// Link Register (R14)
Lr,
/// Program Counter (R15)
Pc,
/// Floating point registers (F0-F7)
Fpr(u8),
/// Floating point status
Fps,
/// Current Program Status Register (cpsr)
Cpsr,
}

impl RegId for ArmCoreRegId {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
match id {
0..=12 => Some((Self::Gpr(id as u8), 4)),
13 => Some((Self::Sp, 4)),
14 => Some((Self::Lr, 4)),
15 => Some((Self::Pc, 4)),
16..=23 => Some((Self::Fpr(id as u8), 4)),
25 => Some((Self::Cpsr, 4)),
_ => None,
}
}
}

/// 32-bit ARM core registers.
///
Expand All @@ -18,6 +51,8 @@ pub struct ArmCoreRegs {
}

impl Registers for ArmCoreRegs {
type RegId = ArmCoreRegId;

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_bytes {
($bytes:expr) => {
Expand Down
1 change: 1 addition & 0 deletions src/arch/arm/reg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

mod arm_core;

pub use arm_core::ArmCoreRegId;
pub use arm_core::ArmCoreRegs;
2 changes: 2 additions & 0 deletions src/arch/mips/reg/mips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ impl<U> Registers for MipsCoreRegs<U>
where
U: PrimInt + LeBytes + Default,
{
type RegId = ();

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_le_bytes {
($value:expr) => {
Expand Down
2 changes: 2 additions & 0 deletions src/arch/msp430/reg/msp430.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub struct Msp430Regs {
}

impl Registers for Msp430Regs {
type RegId = ();

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_bytes {
($bytes:expr) => {
Expand Down
2 changes: 2 additions & 0 deletions src/arch/ppc/reg/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub struct PowerPcCommonRegs {
}

impl Registers for PowerPcCommonRegs {
type RegId = ();

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_bytes {
($bytes:expr) => {
Expand Down
1 change: 1 addition & 0 deletions src/arch/riscv/reg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
mod riscv;

pub use riscv::RiscvCoreRegs;
pub use riscv::RiscvRegId;
32 changes: 31 additions & 1 deletion src/arch/riscv/reg/riscv.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
use crate::arch::Registers;
use crate::arch::{RegId, Registers};
use crate::internal::LeBytes;
use num_traits::PrimInt;

/// RISC-V Register identifier.
#[derive(Debug, Clone)]
pub enum RiscvRegId {
/// General Purpose Register (x0-x31).
Gpr(u8),
/// Floating Point Register (f0-f31).
Fpr(u8),
/// Program Counter.
Pc,
/// Control and Status Register.
Csr(u16),
/// Privilege level.
Priv,
}

impl RegId for RiscvRegId {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
match id {
0..=31 => Some((Self::Gpr(id as u8), 4)),
32 => Some((Self::Pc, 4)),
33..=64 => Some((Self::Fpr((id - 33) as u8), 4)),
65..=4160 => Some((Self::Csr((id - 65) as u16), 4)),
4161 => Some((Self::Priv, 1)),
_ => None,
}
}
}

/// RISC-V Integer registers.
///
/// The register width is set to `u32` or `u64` based on the `<U>` type.
Expand All @@ -21,6 +49,8 @@ impl<U> Registers for RiscvCoreRegs<U>
where
U: PrimInt + LeBytes + Default,
{
type RegId = RiscvRegId;

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_le_bytes {
($value:expr) => {
Expand Down
26 changes: 26 additions & 0 deletions src/arch/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ use num_traits::{Num, PrimInt, Unsigned};

use crate::internal::BeBytes;

/// Register identifier for target registers.
///
/// These identifiers are used by GDB for single register operations.
pub trait RegId: Sized {
/// Map raw GDB register number corresponding `RegId` and register size.
///
/// Returns `None` if the register is not available.
fn from_raw_id(id: usize) -> Option<(Self, usize)>;
}

impl RegId for () {
fn from_raw_id(_: usize) -> Option<(Self, usize)> {
None
}
}

/// Methods to read/write architecture-specific registers.
///
/// Registers must be de/serialized in the order specified by the architecture's
Expand All @@ -13,6 +29,16 @@ use crate::internal::BeBytes;
// TODO: add (optional?) trait methods for reading/writing specific register
// (via it's GDB index)
pub trait Registers: Default {
/// Register identifier for addressing single registers.
///
/// Architectures that do not support single register accesses can safely
/// use `RegId = ()` as a default.
///
/// **Note**: the use of `RegId = ()` in most architectures is temporary.
/// Contributions to implement `RegId` for architectures are welcome! Feel
/// free to open an issue/PR to get some support.
type RegId: RegId;

/// Serialize `self` into a GDB register bytestream.
///
/// Missing registers are serialized by passing `None` to write_byte.
Expand Down
2 changes: 2 additions & 0 deletions src/arch/x86/reg/core32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub struct X86CoreRegs {
}

impl Registers for X86CoreRegs {
type RegId = ();

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_bytes {
($bytes:expr) => {
Expand Down
2 changes: 2 additions & 0 deletions src/arch/x86/reg/core64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub struct X86_64CoreRegs {
}

impl Registers for X86_64CoreRegs {
type RegId = ();

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_bytes {
($bytes:expr) => {
Expand Down
2 changes: 2 additions & 0 deletions src/arch/x86/reg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub struct X87FpuInternalRegs {
}

impl Registers for X87FpuInternalRegs {
type RegId = ();

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_bytes {
($bytes:expr) => {
Expand Down
32 changes: 29 additions & 3 deletions src/gdbstub_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::marker::PhantomData;
use managed::ManagedSlice;

use crate::{
arch::{Arch, Registers},
arch::{Arch, RegId, Registers},
connection::Connection,
internal::*,
protocol::{Command, ConsoleOutput, Packet, ResponseWriter, Tid, TidSelector},
Expand Down Expand Up @@ -402,6 +402,32 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
Some(false) => res.write_str("E22")?, // value of 22 grafted from QEMU
}
}
Command::p(p) => {
let mut dst = [0u8; 16];
let reg = <<T::Arch as Arch>::Registers as Registers>::RegId::from_raw_id(p.reg_id);
let (reg_id, reg_size) = match reg {
Some(v) => v,
None => return Ok(None),
};
let dst = &mut dst[0..reg_size];
target.read_register(reg_id, dst).maybe_missing_impl()?;
res.write_hex_buf(dst)?;
}
Command::P(p) => {
let reg = <<T::Arch as Arch>::Registers as Registers>::RegId::from_raw_id(p.reg_id);
let supported = match reg {
Some((reg_id, _)) => target
.write_register(reg_id, p.val)
.maybe_missing_impl()?
.is_some(),
None => false,
};
if supported {
res.write_str("OK")?;
} else {
res.write_str("E01")?;
}
}
Command::vCont(cmd) => {
use crate::protocol::_vCont::VContKind;

Expand Down Expand Up @@ -443,14 +469,14 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
res,
target,
&mut core::iter::once((self.current_tid.tid, ResumeAction::Continue)),
)
);
}
Command::s(_) => {
return self.do_vcont(
res,
target,
&mut core::iter::once((self.current_tid.tid, ResumeAction::Step)),
)
);
}

// ------------------- Multi-threading Support ------------------ //
Expand Down
2 changes: 2 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ commands! {
"k" => _k::k,
"m" => _m::m<'a>,
"M" => _m_upcase::M<'a>,
"p" => _p::p,
"P" => _p_upcase::P<'a>,
"qAttached" => _qAttached::qAttached,
"qC" => _qC::qC,
"qfThreadInfo" => _qfThreadInfo::qfThreadInfo,
Expand Down
13 changes: 13 additions & 0 deletions src/protocol/commands/_p.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::prelude::*;

#[derive(PartialEq, Eq, Debug)]
pub struct p {
pub reg_id: usize,
}

impl<'a> ParseCommand<'a> for p {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let reg_id = decode_hex(buf.into_body()).ok()?;
Some(p { reg_id })
}
}
17 changes: 17 additions & 0 deletions src/protocol/commands/_p_upcase.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use super::prelude::*;

#[derive(PartialEq, Eq, Debug)]
pub struct P<'a> {
pub reg_id: usize,
pub val: &'a [u8]
}

impl<'a> ParseCommand<'a> for P<'a> {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
let mut body = body.split_mut(|&b| b == b'=');
let reg_id = decode_hex(body.next()?).ok()?;
let val = decode_hex_buf(body.next()?).ok()?;
Some(P { reg_id, val })
}
}
Loading