Skip to content

Commit 7136605

Browse files
eulerdiskdaniel5151
authored andcommitted
Flash operations support (#172)
This PR adds support for the flash commands as described in (https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets). I had to change the memory map in the armv4t example providing a real memory map, otherwise GDB would not load a real .elf file. It doesn't have to be a real memory map, but you need at least a region marked a flash containing the address of the elf sections to be loaded. (eg: .vector_table, .text etc..)
1 parent fc23567 commit 7136605

File tree

13 files changed

+311
-3
lines changed

13 files changed

+311
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Of course, most use-cases will want to support additional debugging features as
9393
- Configure tracepoints and actions to perform when hit
9494
- Select and interrogate collected trace frames
9595
- _Note:_ Feature support is not exhaustive, and many feature haven't been implemented yet.
96+
- Flash operations (`load`)
9697

9798
_Note:_ GDB features are implemented on an as-needed basis by `gdbstub`'s contributors. If there's a missing GDB feature that you'd like `gdbstub` to implement, please file an issue and/or open a PR!
9899

examples/armv4t/gdb/flash.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use crate::emu::Emu;
2+
use gdbstub::target;
3+
use gdbstub::target::TargetResult;
4+
use log::info;
5+
6+
impl target::ext::flash::Flash for Emu {
7+
fn flash_erase(&mut self, start_addr: u32, length: u32) -> TargetResult<(), Self> {
8+
info!("flash_erase start_addr: {start_addr:08x}, length: {length:08x}");
9+
Ok(())
10+
}
11+
12+
fn flash_write(&mut self, start_addr: u32, _data: &[u8]) -> TargetResult<(), Self> {
13+
info!("flash_write start_addr: {start_addr:08x}");
14+
Ok(())
15+
}
16+
17+
fn flash_done(&mut self) -> TargetResult<(), Self> {
18+
info!("flash_done");
19+
Ok(())
20+
}
21+
}

examples/armv4t/gdb/memory_map.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@ impl target::ext::memory_map::MemoryMap for Emu {
1010
length: usize,
1111
buf: &mut [u8],
1212
) -> TargetResult<usize, Self> {
13-
// Sample memory map, with RAM coverying the whole
14-
// memory space.
13+
// Sample memory map, modeled on part of STM32F446 memory map.
14+
// A real memory map is necessary to test the flash commands.
1515
let memory_map = r#"<?xml version="1.0"?>
1616
<!DOCTYPE memory-map
1717
PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
1818
"http://sourceware.org/gdb/gdb-memory-map.dtd">
1919
<memory-map>
20-
<memory type="ram" start="0x0" length="0x100000000"/>
20+
<memory type="ram" start="0x20000000" length="0x20000"/>
21+
<memory type="flash" start="0x08000000" length="0x10000">
22+
<property name="blocksize">0x4000</property>
23+
</memory>
24+
<memory type="flash" start="0x08010000" length="0x10000">
25+
<property name="blocksize">0x10000</property>
26+
</memory>
27+
<memory type="flash" start="0x08020000" length="0x60000">
28+
<property name="blocksize">0x20000</property>
29+
</memory>
2130
</memory-map>"#
2231
.trim()
2332
.as_bytes();

examples/armv4t/gdb/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod breakpoints;
1919
mod catch_syscalls;
2020
mod exec_file;
2121
mod extended_mode;
22+
mod flash;
2223
mod host_io;
2324
mod libraries;
2425
mod lldb_register_info_override;
@@ -169,6 +170,11 @@ impl Target for Emu {
169170
) -> Option<target::ext::tracepoints::TracepointsOps<'_, Self>> {
170171
Some(self)
171172
}
173+
174+
#[inline(always)]
175+
fn support_flash_operations(&mut self) -> Option<target::ext::flash::FlashOps<'_, Self>> {
176+
Some(self)
177+
}
172178
}
173179

174180
impl SingleThreadBase for Emu {

src/protocol/commands.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ commands! {
302302
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead<'a>,
303303
}
304304

305+
flash_operations use 'a {
306+
"vFlashErase" => _vFlashErase::vFlashErase<'a>,
307+
"vFlashWrite" => _vFlashWrite::vFlashWrite<'a>,
308+
"vFlashDone" => _vFlashDone::vFlashDone,
309+
}
310+
305311
auxv use 'a {
306312
"qXfer:auxv:read" => _qXfer_auxv_read::qXferAuxvRead<'a>,
307313
}

src/protocol/commands/_vFlashDone.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct vFlashDone;
5+
6+
impl<'a> ParseCommand<'a> for vFlashDone {
7+
#[inline(always)]
8+
fn from_packet(_buf: PacketBuf<'a>) -> Option<Self> {
9+
Some(vFlashDone)
10+
}
11+
}

src/protocol/commands/_vFlashErase.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct vFlashErase<'a> {
5+
pub addr: &'a [u8],
6+
pub length: &'a [u8],
7+
}
8+
9+
impl<'a> ParseCommand<'a> for vFlashErase<'a> {
10+
#[inline(always)]
11+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
12+
let body = buf.into_body();
13+
14+
let mut body = body.splitn_mut(3, |&b| b == b',' || b == b':');
15+
let _first_colon = body.next()?;
16+
let addr = decode_hex_buf(body.next()?).ok()?;
17+
let length = decode_hex_buf(body.next()?)
18+
.ok()
19+
.filter(|l| !l.is_empty())?;
20+
Some(Self { addr, length })
21+
}
22+
}
23+
24+
#[cfg(test)]
25+
mod tests {
26+
use super::*;
27+
28+
macro_rules! test_buf {
29+
($bufname:ident, $body:literal) => {
30+
let mut test = $body.to_vec();
31+
let mut buf = PacketBuf::new_with_raw_body(&mut test).unwrap();
32+
if !buf.strip_prefix(b"vFlashErase") {
33+
panic!("invalid test");
34+
}
35+
let $bufname = buf;
36+
};
37+
}
38+
39+
#[test]
40+
fn valid_vFlashErase() {
41+
test_buf!(buf, b"vFlashErase:08000000,00004000");
42+
43+
let pkt = vFlashErase::from_packet(buf).unwrap();
44+
45+
assert_eq!(pkt.addr, [0x08, 0, 0, 0]);
46+
assert_eq!(pkt.length, [0, 0, 0x40, 0]);
47+
}
48+
49+
#[test]
50+
fn invalid_vFlashErase_wrong_address() {
51+
test_buf!(buf, b"vFlashErase:abcdefg:00004000");
52+
53+
assert!(vFlashErase::from_packet(buf).is_none());
54+
}
55+
56+
#[test]
57+
fn invalid_vFlashErase_wrong_length() {
58+
test_buf!(buf, b"vFlashErase:08000000:abcdefg");
59+
60+
assert!(vFlashErase::from_packet(buf).is_none());
61+
}
62+
63+
#[test]
64+
fn invalid_vFlashErase_missing_address() {
65+
test_buf!(buf, b"vFlashErase:");
66+
67+
assert!(vFlashErase::from_packet(buf).is_none());
68+
}
69+
70+
#[test]
71+
fn invalid_vFlashErase_missing_length() {
72+
test_buf!(buf, b"vFlashErase:08000000:");
73+
74+
assert!(vFlashErase::from_packet(buf).is_none());
75+
}
76+
}

src/protocol/commands/_vFlashWrite.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use super::prelude::*;
2+
use crate::protocol::common::hex::decode_bin_buf;
3+
4+
#[derive(Debug)]
5+
pub struct vFlashWrite<'a> {
6+
pub addr: &'a [u8],
7+
pub val: &'a [u8],
8+
}
9+
10+
impl<'a> ParseCommand<'a> for vFlashWrite<'a> {
11+
#[inline(always)]
12+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
13+
let body = buf.into_body();
14+
15+
let mut body = body.splitn_mut(3, |&b| b == b':');
16+
let _first_colon = body.next()?;
17+
let addr = decode_hex_buf(body.next()?)
18+
.ok()
19+
.filter(|a| !a.is_empty())?;
20+
let val = decode_bin_buf(body.next()?)?;
21+
22+
Some(vFlashWrite { addr, val })
23+
}
24+
}
25+
26+
#[cfg(test)]
27+
mod tests {
28+
use super::*;
29+
30+
macro_rules! test_buf {
31+
($bufname:ident, $body:literal) => {
32+
let mut test = $body.to_vec();
33+
let mut buf = PacketBuf::new_with_raw_body(&mut test).unwrap();
34+
if !buf.strip_prefix(b"vFlashWrite") {
35+
panic!("invalid test");
36+
}
37+
let $bufname = buf;
38+
};
39+
}
40+
41+
#[test]
42+
fn valid_vFlashWrite() {
43+
test_buf!(
44+
buf,
45+
b"vFlashWrite:08000000:\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"
46+
);
47+
48+
let pkt = vFlashWrite::from_packet(buf).unwrap();
49+
50+
assert_eq!(pkt.addr, [0x08, 0, 0, 0]);
51+
assert_eq!(pkt.val, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
52+
}
53+
54+
#[test]
55+
fn invalid_vFlashWrite_wrong_address() {
56+
test_buf!(
57+
buf,
58+
b"vFlashWrite:abcdefg:\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"
59+
);
60+
61+
assert!(vFlashWrite::from_packet(buf).is_none())
62+
}
63+
64+
#[test]
65+
fn invalid_vFlashWrite_missing_data() {
66+
test_buf!(buf, b"vFlashWrite:abcdefg:");
67+
68+
assert!(vFlashWrite::from_packet(buf).is_none())
69+
}
70+
71+
#[test]
72+
fn invalid_vFlashWrite_missing_address() {
73+
test_buf!(buf, b"vFlashWrite:");
74+
75+
assert!(vFlashWrite::from_packet(buf).is_none())
76+
}
77+
}

src/stub/core_impl.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod breakpoints;
3030
mod catch_syscalls;
3131
mod exec_file;
3232
mod extended_mode;
33+
mod flash;
3334
mod host_io;
3435
mod libraries;
3536
mod lldb_register_info;
@@ -213,6 +214,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
213214
Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd),
214215
Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd),
215216
Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd),
217+
Command::FlashOperations(cmd) => self.handle_flash_operations(res, target, cmd),
216218
Command::HostIo(cmd) => self.handle_host_io(res, target, cmd),
217219
Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd),
218220
Command::Auxv(cmd) => self.handle_auxv(res, target, cmd),

src/stub/core_impl/flash.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use super::prelude::*;
2+
use crate::arch::Arch;
3+
use crate::protocol::commands::ext::FlashOperations;
4+
5+
impl<T: Target, C: Connection> GdbStubImpl<T, C> {
6+
pub(crate) fn handle_flash_operations(
7+
&mut self,
8+
_res: &mut ResponseWriter<'_, C>,
9+
target: &mut T,
10+
command: FlashOperations<'_>,
11+
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
12+
let ops = match target.support_flash_operations() {
13+
Some(ops) => ops,
14+
None => return Ok(HandlerStatus::Handled),
15+
};
16+
let handler_status = match command {
17+
FlashOperations::vFlashErase(cmd) => {
18+
let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
19+
.ok_or(Error::TargetMismatch)?;
20+
21+
let length = <T::Arch as Arch>::Usize::from_be_bytes(cmd.length)
22+
.ok_or(Error::TargetMismatch)?;
23+
24+
ops.flash_erase(addr, length).handle_error()?;
25+
HandlerStatus::NeedsOk
26+
}
27+
FlashOperations::vFlashWrite(cmd) => {
28+
let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
29+
.ok_or(Error::TargetMismatch)?;
30+
31+
ops.flash_write(addr, cmd.val).handle_error()?;
32+
HandlerStatus::NeedsOk
33+
}
34+
FlashOperations::vFlashDone(_) => {
35+
ops.flash_done().handle_error()?;
36+
HandlerStatus::NeedsOk
37+
}
38+
};
39+
40+
Ok(handler_status)
41+
}
42+
}

0 commit comments

Comments
 (0)