blob: b5bd42c5557bf6bb540786936701e7e8bc3eae74 [file] [log] [blame]
use core::convert::TryInto;
use gdbstub::arch::Registers;
use gdbstub::internal::LeBytes;
use num_traits::PrimInt;
/// MIPS registers.
///
/// The register width is set to `u32` or `u64` based on the `<U>` type.
///
/// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-cpu.xml>
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct MipsCoreRegs<U> {
/// General purpose registers (R0-R31)
pub r: [U; 32],
/// Low register (regnum 33)
pub lo: U,
/// High register (regnum 34)
pub hi: U,
/// Program Counter (regnum 37)
pub pc: U,
/// CP0 registers
pub cp0: MipsCp0Regs<U>,
/// FPU registers
pub fpu: MipsFpuRegs<U>,
}
/// MIPS CP0 (coprocessor 0) registers.
///
/// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-cp0.xml>
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct MipsCp0Regs<U> {
/// Status register (regnum 32)
pub status: U,
/// Bad Virtual Address register (regnum 35)
pub badvaddr: U,
/// Exception Cause register (regnum 36)
pub cause: U,
}
/// MIPS FPU registers.
///
/// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-fpu.xml>
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct MipsFpuRegs<U> {
/// FP registers (F0-F31) starting at regnum 38
pub r: [U; 32],
/// Floating-point Control Status register
pub fcsr: U,
/// Floating-point Implementation Register
pub fir: U,
}
/// MIPS DSP registers.
///
/// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-dsp.xml>
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct MipsDspRegs<U> {
/// High 1 register (regnum 72)
pub hi1: U,
/// Low 1 register (regnum 73)
pub lo1: U,
/// High 2 register (regnum 74)
pub hi2: U,
/// Low 2 register (regnum 75)
pub lo2: U,
/// High 3 register (regnum 76)
pub hi3: U,
/// Low 3 register (regnum 77)
pub lo3: U,
/// DSP Control register (regnum 78)
/// Note: This register will always be 32-bit regardless of the target
/// <https://sourceware.org/gdb/current/onlinedocs/gdb/MIPS-Features.html#MIPS-Features>
pub dspctl: u32,
/// Restart register (regnum 79)
pub restart: U,
}
/// MIPS core and DSP registers.
///
/// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-dsp-linux.xml>
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct MipsCoreRegsWithDsp<U> {
/// Core registers
pub core: MipsCoreRegs<U>,
/// DSP registers
pub dsp: MipsDspRegs<U>,
}
impl<U> Registers for MipsCoreRegs<U>
where
U: PrimInt + LeBytes + Default + core::fmt::Debug,
{
type ProgramCounter = U;
fn pc(&self) -> Self::ProgramCounter {
self.pc
}
fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_le_bytes {
($value:expr) => {
let mut buf = [0; 16];
// infallible (unless digit is a >128 bit number)
let len = $value.to_le_bytes(&mut buf).unwrap();
let buf = &buf[..len];
for b in buf {
write_byte(Some(*b));
}
};
}
// Write GPRs
for reg in self.r.iter() {
write_le_bytes!(reg);
}
// Status register is regnum 32
write_le_bytes!(&self.cp0.status);
// Low and high registers are regnums 33 and 34
write_le_bytes!(&self.lo);
write_le_bytes!(&self.hi);
// Badvaddr and Cause registers are regnums 35 and 36
write_le_bytes!(&self.cp0.badvaddr);
write_le_bytes!(&self.cp0.cause);
// Program Counter is regnum 37
write_le_bytes!(&self.pc);
// Write FPRs
for reg in self.fpu.r.iter() {
write_le_bytes!(reg);
}
// Write FCSR and FIR registers
write_le_bytes!(&self.fpu.fcsr);
write_le_bytes!(&self.fpu.fir);
}
fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
let ptrsize = core::mem::size_of::<U>();
// Ensure bytes contains enough data for all 72 registers
if bytes.len() < ptrsize * 72 {
return Err(());
}
// All core registers are the same size
let mut regs = bytes
.chunks_exact(ptrsize)
.map(|c| U::from_le_bytes(c).unwrap());
// Read GPRs
for reg in self.r.iter_mut() {
*reg = regs.next().ok_or(())?
}
// Read Status register
self.cp0.status = regs.next().ok_or(())?;
// Read Low and High registers
self.lo = regs.next().ok_or(())?;
self.hi = regs.next().ok_or(())?;
// Read Badvaddr and Cause registers
self.cp0.badvaddr = regs.next().ok_or(())?;
self.cp0.cause = regs.next().ok_or(())?;
// Read the Program Counter
self.pc = regs.next().ok_or(())?;
// Read FPRs
for reg in self.fpu.r.iter_mut() {
*reg = regs.next().ok_or(())?
}
// Read FCSR and FIR registers
self.fpu.fcsr = regs.next().ok_or(())?;
self.fpu.fir = regs.next().ok_or(())?;
Ok(())
}
}
impl<U> Registers for MipsCoreRegsWithDsp<U>
where
U: PrimInt + LeBytes + Default + core::fmt::Debug,
{
type ProgramCounter = U;
fn pc(&self) -> Self::ProgramCounter {
self.core.pc
}
fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
macro_rules! write_le_bytes {
($value:expr) => {
let mut buf = [0; 16];
// infallible (unless digit is a >128 bit number)
let len = $value.to_le_bytes(&mut buf).unwrap();
let buf = &buf[..len];
for b in buf {
write_byte(Some(*b));
}
};
}
// Serialize the core registers first
self.core.gdb_serialize(&mut write_byte);
// Write the DSP registers
write_le_bytes!(&self.dsp.hi1);
write_le_bytes!(&self.dsp.lo1);
write_le_bytes!(&self.dsp.hi2);
write_le_bytes!(&self.dsp.lo2);
write_le_bytes!(&self.dsp.hi3);
write_le_bytes!(&self.dsp.lo3);
for b in &self.dsp.dspctl.to_le_bytes() {
write_byte(Some(*b));
}
write_le_bytes!(&self.dsp.restart);
}
fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
// Deserialize the core registers first
self.core.gdb_deserialize(bytes)?;
// Ensure bytes contains enough data for all 79 registers of target-width
// and the dspctl register which is always 4 bytes
let ptrsize = core::mem::size_of::<U>();
if bytes.len() < (ptrsize * 79) + 4 {
return Err(());
}
// Calculate the offsets to the DSP registers based on the ptrsize
let dspregs_start = ptrsize * 72;
let dspctl_start = ptrsize * 78;
// Read up until the dspctl register
let mut regs = bytes[dspregs_start..dspctl_start]
.chunks_exact(ptrsize)
.map(|c| U::from_le_bytes(c).unwrap());
self.dsp.hi1 = regs.next().ok_or(())?;
self.dsp.lo1 = regs.next().ok_or(())?;
self.dsp.hi2 = regs.next().ok_or(())?;
self.dsp.lo2 = regs.next().ok_or(())?;
self.dsp.hi3 = regs.next().ok_or(())?;
self.dsp.lo3 = regs.next().ok_or(())?;
// Dspctl will always be a u32
self.dsp.dspctl =
u32::from_le_bytes(bytes[dspctl_start..dspctl_start + 4].try_into().unwrap());
// Only 4 or 8 bytes should remain to be read
self.dsp.restart = U::from_le_bytes(
bytes[dspctl_start + 4..]
.chunks_exact(ptrsize)
.next()
.ok_or(())?,
)
.unwrap();
Ok(())
}
}