Skip to content

Commit

Permalink
read_addrs() can return fewer bytes than requested (#115)
Browse files Browse the repository at this point in the history
The `read_addrs()` method for targets now returns a number of bytes that
were read. This allows a target to gracefully signal the client that
memory at an address range is not accessible.

Signed-off-by: Thomas Scholtes <geigerzaehler@axiom.fm>
  • Loading branch information
geigerzaehler authored and daniel5151 committed Apr 26, 2023
1 parent 36f166e commit dfdb1d3
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 20 deletions.
4 changes: 2 additions & 2 deletions example_no_std/src/gdb.rs
Expand Up @@ -77,10 +77,10 @@ impl MultiThreadBase for DummyTarget {
_start_addr: u32,
data: &mut [u8],
_tid: Tid, // same address space for each core
) -> TargetResult<(), Self> {
) -> TargetResult<usize, Self> {
print_str("> read_addrs");
data.iter_mut().for_each(|b| *b = 0x55);
Ok(())
Ok(data.len())
}

#[inline(never)]
Expand Down
17 changes: 14 additions & 3 deletions examples/armv4t/gdb/mod.rs
Expand Up @@ -196,11 +196,22 @@ impl SingleThreadBase for Emu {
Some(self)
}

fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
for (addr, val) in (start_addr..).zip(data.iter_mut()) {
fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<usize, Self> {
// These values are taken from the link script.
const MEMORY_ORIGIN: u32 = 0x5555_0000;
const MEMORY_LENGTH: u32 = 0x1000_0000;
const MEMORY_END: u32 = MEMORY_ORIGIN + MEMORY_LENGTH;

if !(MEMORY_ORIGIN..MEMORY_END).contains(&start_addr) {
return Err(TargetError::NonFatal);
}

let end_addr = std::cmp::min(start_addr + data.len() as u32, MEMORY_END);

for (addr, val) in (start_addr..end_addr).zip(data.iter_mut()) {
*val = self.mem.r8(addr)
}
Ok(())
Ok((end_addr - start_addr) as usize)
}

fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> {
Expand Down
4 changes: 2 additions & 2 deletions examples/armv4t_multicore/gdb.rs
Expand Up @@ -92,11 +92,11 @@ impl MultiThreadBase for Emu {
start_addr: u32,
data: &mut [u8],
_tid: Tid, // same address space for each core
) -> TargetResult<(), Self> {
) -> TargetResult<usize, Self> {
for (addr, val) in (start_addr..).zip(data.iter_mut()) {
*val = self.mem.r8(addr)
}
Ok(())
Ok(data.len())
}

fn write_addrs(
Expand Down
4 changes: 3 additions & 1 deletion src/stub/core_impl/base.rs
Expand Up @@ -266,7 +266,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {

let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
let data = &mut buf[..chunk_size];
match target.base_ops() {
let data_len = match target.base_ops() {
BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
BaseOps::MultiThread(ops) => {
ops.read_addrs(addr, data, self.current_mem_tid)
Expand All @@ -277,6 +277,8 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
n -= chunk_size;
i += chunk_size;

// TODO: add more specific error variant?
let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?;
res.write_hex_buf(data)?;
}
HandlerStatus::Handled
Expand Down
18 changes: 13 additions & 5 deletions src/target/ext/base/multithread.rs
Expand Up @@ -43,17 +43,25 @@ pub trait MultiThreadBase: Target {
None
}

/// Read bytes from the specified address range.
/// Read bytes from the specified address range and return the number of
/// bytes that were read.
///
/// If the requested address range could not be accessed (e.g: due to
/// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
/// error should be returned.
/// Implementations may return a number `n` that is less than `data.len()`
/// to indicate that memory starting at `start_addr + n` cannot be
/// accessed.
///
/// Implemenations may also return an appropriate non-fatal error if the
/// requested address range could not be accessed (e.g: due to MMU
/// protection, unhanded page fault, etc...).
///
/// Implementations must guarantee that the returned number is less than or
/// equal `data.len()`.
fn read_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8],
tid: Tid,
) -> TargetResult<(), Self>;
) -> TargetResult<usize, Self>;

/// Write bytes to the specified address range.
///
Expand Down
18 changes: 13 additions & 5 deletions src/target/ext/base/singlethread.rs
Expand Up @@ -32,16 +32,24 @@ pub trait SingleThreadBase: Target {
None
}

/// Read bytes from the specified address range.
/// Read bytes from the specified address range and return the number of
/// bytes that were read.
///
/// If the requested address range could not be accessed (e.g: due to
/// MMU protection, unhanded page fault, etc...), an appropriate
/// non-fatal error should be returned.
/// Implementations may return a number `n` that is less than `data.len()`
/// to indicate that memory starting at `start_addr + n` cannot be
/// accessed.
///
/// Implemenations may also return an appropriate non-fatal error if the
/// requested address range could not be accessed (e.g: due to MMU
/// protection, unhanded page fault, etc...).
///
/// Implementations must guarantee that the returned number is less than or
/// equal `data.len()`.
fn read_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8],
) -> TargetResult<(), Self>;
) -> TargetResult<usize, Self>;

/// Write bytes to the specified address range.
///
Expand Down
4 changes: 2 additions & 2 deletions src/target/mod.rs
Expand Up @@ -151,7 +151,7 @@
//! &mut self,
//! start_addr: u32,
//! data: &mut [u8],
//! ) -> TargetResult<(), Self> { todo!() }
//! ) -> TargetResult<usize, Self> { todo!() }
//!
//! fn write_addrs(
//! &mut self,
Expand Down Expand Up @@ -409,7 +409,7 @@ pub trait Target {
/// # &mut self,
/// # start_addr: u32,
/// # data: &mut [u8],
/// # ) -> TargetResult<(), Self> { todo!() }
/// # ) -> TargetResult<usize, Self> { todo!() }
/// #
/// # fn write_addrs(
/// # &mut self,
Expand Down

0 comments on commit dfdb1d3

Please sign in to comment.