Skip to content

Commit

Permalink
update armv4t example with custom registers + target.xml
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel5151 committed Jun 23, 2021
1 parent ecbbaf7 commit 5562eec
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 30 deletions.
6 changes: 6 additions & 0 deletions examples/armv4t/emu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub enum Event {
pub struct Emu {
start_addr: u32,

// example custom register. only read/written to from the GDB client
pub(crate) custom_reg: u32,

pub(crate) cpu: Cpu,
pub(crate) mem: ExampleMem,

Expand Down Expand Up @@ -61,6 +64,9 @@ impl Emu {

Ok(Emu {
start_addr: elf_header.entry as u32,

custom_reg: 0x12345678,

cpu,
mem,

Expand Down
212 changes: 183 additions & 29 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,16 @@ fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
}

impl Target for Emu {
type Arch = gdbstub_arch::arm::Armv4t;
// As an example, I've defined a custom architecture based off
// `gdbstub_arch::arm::Armv4t`. The implementation is in the `custom_arch`
// module at the bottom of this file.
//
// unless you're working with a particularly funky architecture that uses custom
// registers, you should probably stick to using the simple `target.xml`
// implementations from the `gdbstub_arch` repo (i.e: `target.xml` files that
// only specify the <architecture> and <feature>s of the arch, instead of
// listing out all the registers out manually).
type Arch = custom_arch::Armv4tCustom;
type Error = &'static str;

// --------------- IMPORTANT NOTE ---------------
Expand Down Expand Up @@ -142,34 +151,35 @@ impl SingleThreadOps for Emu {

fn read_registers(
&mut self,
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
regs: &mut custom_arch::ArmCoreRegsCustom,
) -> TargetResult<(), Self> {
let mode = self.cpu.mode();

for i in 0..13 {
regs.r[i] = self.cpu.reg_get(mode, i as u8);
regs.core.r[i] = self.cpu.reg_get(mode, i as u8);
}
regs.sp = self.cpu.reg_get(mode, reg::SP);
regs.lr = self.cpu.reg_get(mode, reg::LR);
regs.pc = self.cpu.reg_get(mode, reg::PC);
regs.cpsr = self.cpu.reg_get(mode, reg::CPSR);
regs.core.sp = self.cpu.reg_get(mode, reg::SP);
regs.core.lr = self.cpu.reg_get(mode, reg::LR);
regs.core.pc = self.cpu.reg_get(mode, reg::PC);
regs.core.cpsr = self.cpu.reg_get(mode, reg::CPSR);

regs.custom = self.custom_reg;

Ok(())
}

fn write_registers(
&mut self,
regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
) -> TargetResult<(), Self> {
fn write_registers(&mut self, regs: &custom_arch::ArmCoreRegsCustom) -> TargetResult<(), Self> {
let mode = self.cpu.mode();

for i in 0..13 {
self.cpu.reg_set(mode, i, regs.r[i as usize]);
self.cpu.reg_set(mode, i, regs.core.r[i as usize]);
}
self.cpu.reg_set(mode, reg::SP, regs.sp);
self.cpu.reg_set(mode, reg::LR, regs.lr);
self.cpu.reg_set(mode, reg::PC, regs.pc);
self.cpu.reg_set(mode, reg::CPSR, regs.cpsr);
self.cpu.reg_set(mode, reg::SP, regs.core.sp);
self.cpu.reg_set(mode, reg::LR, regs.core.lr);
self.cpu.reg_set(mode, reg::PC, regs.core.pc);
self.cpu.reg_set(mode, reg::CPSR, regs.core.cpsr);

self.custom_reg = regs.custom;

Ok(())
}
Expand Down Expand Up @@ -217,33 +227,61 @@ impl target::ext::base::SingleRegisterAccess<()> for Emu {
fn read_register(
&mut self,
_tid: (),
reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
reg_id: custom_arch::ArmCoreRegIdCustom,
dst: &mut [u8],
) -> TargetResult<(), Self> {
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(().into())
match reg_id {
custom_arch::ArmCoreRegIdCustom::Core(reg_id) => {
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(().into())
}
}
custom_arch::ArmCoreRegIdCustom::Custom => {
dst.copy_from_slice(&self.custom_reg.to_le_bytes());
Ok(())
}
custom_arch::ArmCoreRegIdCustom::Time => {
dst.copy_from_slice(
&(std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u32)
.to_le_bytes(),
);
Ok(())
}
}
}

fn write_register(
&mut self,
_tid: (),
reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
reg_id: custom_arch::ArmCoreRegIdCustom,
val: &[u8],
) -> TargetResult<(), Self> {
let w = u32::from_le_bytes(
val.try_into()
.map_err(|_| TargetError::Fatal("invalid data"))?,
);
if let Some(i) = cpu_reg_id(reg_id) {
self.cpu.reg_set(self.cpu.mode(), i, w);
Ok(())
} else {
Err(().into())
match reg_id {
custom_arch::ArmCoreRegIdCustom::Core(reg_id) => {
if let Some(i) = cpu_reg_id(reg_id) {
self.cpu.reg_set(self.cpu.mode(), i, w);
Ok(())
} else {
Err(().into())
}
}
custom_arch::ArmCoreRegIdCustom::Custom => {
self.custom_reg = w;
Ok(())
}
// ignore writes
custom_arch::ArmCoreRegIdCustom::Time => Ok(()),
}
}
}
Expand Down Expand Up @@ -294,3 +332,119 @@ impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu {
}
}
}

mod custom_arch {
use gdbstub::arch::{Arch, RegId, Registers};

use gdbstub_arch::arm::reg::id::ArmCoreRegId;
use gdbstub_arch::arm::reg::ArmCoreRegs;
use gdbstub_arch::arm::ArmBreakpointKind;

/// Implements `Arch` for ARMv4T
pub enum Armv4tCustom {}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct ArmCoreRegsCustom {
pub core: ArmCoreRegs,
pub custom: u32,
}

impl Registers for ArmCoreRegsCustom {
type ProgramCounter = u32;

fn pc(&self) -> Self::ProgramCounter {
self.core.pc
}

fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
self.core.gdb_serialize(&mut write_byte);

macro_rules! write_bytes {
($bytes:expr) => {
for b in $bytes {
write_byte(Some(*b))
}
};
}

write_bytes!(&self.custom.to_le_bytes());
}

fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
// ensure bytes.chunks_exact(4) won't panic
if bytes.len() % 4 != 0 {
return Err(());
}

use core::convert::TryInto;
let mut regs = bytes
.chunks_exact(4)
.map(|c| u32::from_le_bytes(c.try_into().unwrap()));

// copied from ArmCoreRegs
{
for reg in self.core.r.iter_mut() {
*reg = regs.next().ok_or(())?
}
self.core.sp = regs.next().ok_or(())?;
self.core.lr = regs.next().ok_or(())?;
self.core.pc = regs.next().ok_or(())?;

// Floating point registers (unused)
for _ in 0..25 {
regs.next().ok_or(())?;
}

self.core.cpsr = regs.next().ok_or(())?;
}

self.custom = regs.next().ok_or(())?;

if regs.next().is_some() {
return Err(());
}

Ok(())
}
}

#[derive(Debug)]
pub enum ArmCoreRegIdCustom {
Core(ArmCoreRegId),
Custom,
// not sent as part of `struct ArmCoreRegsCustom`, and only accessible via the single
// register read/write functions
Time,
}

impl RegId for ArmCoreRegIdCustom {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
let reg = match id {
26 => Self::Custom,
27 => Self::Time,
_ => {
let (reg, size) = ArmCoreRegId::from_raw_id(id)?;
return Some((Self::Core(reg), size));
}
};
Some((reg, 4))
}
}

impl Arch for Armv4tCustom {
type Usize = u32;
type Registers = ArmCoreRegsCustom;
type RegId = ArmCoreRegIdCustom;
type BreakpointKind = ArmBreakpointKind;

// for _purely demonstrative purposes_, i'll return dummy data from this
// function, as it will be overwritten by TargetDescriptionXmlOverride.
//
// See `examples/armv4t/gdb/target_description_xml_override.rs`
//
// in an actual implementation, you'll want to return an actual string here!
fn target_description_xml() -> Option<&'static str> {
Some("never gets returned")
}
}
}
64 changes: 63 additions & 1 deletion examples/armv4t/gdb/target_description_xml_override.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,68 @@ use crate::emu::Emu;

impl target::ext::target_description_xml_override::TargetDescriptionXmlOverride for Emu {
fn target_description_xml(&self) -> &str {
r#"<target version="1.0"><!-- custom override string --><architecture>armv4t</architecture></target>"#
r#"<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>armv4t</architecture>
<feature name="org.gnu.gdb.arm.core">
<vector id="padding" type="uint32" count="25"/>
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<!--
For some reason, my version of `gdb-multiarch` doesn't seem to
respect "regnum", and will not parse this custom target.xml unless I
manually include the padding bytes in the target description.
On the bright side, AFAIK, there aren't all that many architectures
that use padding bytes. Heck, the only reason armv4t uses padding is
for historical reasons (see comment below).
Odds are if you're defining your own custom arch, you won't run into
this issue, since you can just lay out all the registers in the
correct order.
-->
<reg name="padding" type="padding" bitsize="32"/>
<!-- The CPSR is register 25, rather than register 16, because
the FPA registers historically were placed between the PC
and the CPSR in the "g" packet. -->
<reg name="cpsr" bitsize="32" regnum="25"/>
</feature>
<feature name="custom-armv4t-extension">
<!--
maps to a simple scratch register within the emulator. the GDB
client can read the register using `p $custom` and set it using
`set $custom=1337`
-->
<reg name="custom" bitsize="32" type="uint32"/>
<!--
pseudo-register that return the current time when read.
notably, i've set up the target to NOT send this register as part of
the regular register list, which means that GDB will fetch/update
this register via the 'p' and 'P' packets respectively
-->
<reg name="time" bitsize="32" type="uint32"/>
</feature>
</target>
"#
}
}

0 comments on commit 5562eec

Please sign in to comment.