mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-10-05 07:27:33 +02:00
flash.ps1/debug-server.ps1: Windows fixes (tested)
- debug-server.ps1: look up the core target by name ([lindex [target names] 0]) instead of hard-coding rp2350.dap.core0, which differs across OpenOCD builds. - flash.ps1: resolve the .bin to an absolute, forward-slash, braced path so OpenOCD's Tcl parser does not eat the backslash in \build\... Tested on Windows.
This commit is contained in:
1 parent
1748603ea5
commit
16ece6a119
2 files changed
+411
-405
No files matched your search
+279
-277
@@ -1,277 +1,279 @@
|
|||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Start OpenOCD as a live GDB server for the RP2350 via the Pico Debug Probe.
|
Start OpenOCD as a live GDB server for the RP2350 via the Pico Debug Probe.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Starts OpenOCD in the foreground as a long-running GDB server. It exposes
|
Starts OpenOCD in the foreground as a long-running GDB server. It exposes
|
||||||
rp2350.dap.core0 on 127.0.0.1:3333 and leaves the core RUNNING, so that a
|
rp2350.dap.core0 on 127.0.0.1:3333 and leaves the core RUNNING, so that a
|
||||||
debugger (Binary Ninja, or plain GDB) can attach to a target that is already
|
debugger (Binary Ninja, or plain GDB) can attach to a target that is already
|
||||||
executing and therefore has sane registers.
|
executing and therefore has sane registers.
|
||||||
|
|
||||||
This script is deliberately NOT flash.ps1. flash.ps1 programs flash and
|
This script is deliberately NOT flash.ps1. flash.ps1 programs flash and
|
||||||
exits; this one claims the probe and stays up so you can single-step, read
|
exits; this one claims the probe and stays up so you can single-step, read
|
||||||
memory, and set breakpoints. Do not run both at once -- exactly one process
|
memory, and set breakpoints. Do not run both at once -- exactly one process
|
||||||
may own the debug probe.
|
may own the debug probe.
|
||||||
|
|
||||||
The script ends with "reset run" rather than OpenOCD's default halt. This is
|
The script ends with "reset run" rather than OpenOCD's default halt. This is
|
||||||
the single most important line in the file; see "Why reset run" below.
|
the single most important line in the file; see "Why reset run" below.
|
||||||
|
|
||||||
.PARAMETER OCD
|
.PARAMETER OCD
|
||||||
Optional. Directory containing openocd.exe AND its scripts\ directory.
|
Optional. Directory containing openocd.exe AND its scripts\ directory.
|
||||||
Overrides the PICO_OPENOCD environment variable.
|
Overrides the PICO_OPENOCD environment variable.
|
||||||
|
|
||||||
.ENVIRONMENT
|
.ENVIRONMENT
|
||||||
PICO_OPENOCD Directory containing openocd.exe AND its scripts\ directory.
|
PICO_OPENOCD Directory containing openocd.exe AND its scripts\ directory.
|
||||||
Default: $env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev
|
Default: $env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev
|
||||||
The path is used for BOTH the executable and the -s scripts
|
The path is used for BOTH the executable and the -s scripts
|
||||||
argument. This must be an OpenOCD build matching your machine
|
argument. This must be an OpenOCD build matching your machine
|
||||||
architecture (x64).
|
architecture (x64).
|
||||||
ADAPTER_SPEED SWD clock in kHz. Default: 24000.
|
ADAPTER_SPEED SWD clock in kHz. Default: 24000.
|
||||||
This is a read-mostly debug session, so a fast clock is fine
|
This is a read-mostly debug session, so a fast clock is fine
|
||||||
and makes stepping noticeably smoother. Drop it (10000 or
|
and makes stepping noticeably smoother. Drop it (10000 or
|
||||||
lower) if the link is flaky or you are using long dupont
|
lower) if the link is flaky or you are using long dupont
|
||||||
wires instead of the probe's own connector.
|
wires instead of the probe's own connector.
|
||||||
USE_CORE Which cores to expose to the client. Default: 0 (core0 only).
|
USE_CORE Which cores to expose to the client. Default: 0 (core0 only).
|
||||||
Accepted values:
|
Accepted values:
|
||||||
0 core0 only <- use this with Binary Ninja
|
0 core0 only <- use this with Binary Ninja
|
||||||
1 core1 only <- rarely useful
|
1 core1 only <- rarely useful
|
||||||
SMP both cores as hwthreads <- see "Why USE_CORE=0"
|
SMP both cores as hwthreads <- see "Why USE_CORE=0"
|
||||||
Do not change this to SMP when driving Binary Ninja. The
|
Do not change this to SMP when driving Binary Ninja. The
|
||||||
explanation below is the whole reason this default is 0.
|
explanation below is the whole reason this default is 0.
|
||||||
BP_ADDR OPTIONAL address to halt at during the startup run, as 0x
|
BP_ADDR OPTIONAL address to halt at during the startup run, as 0x
|
||||||
prefixed hex, e.g. 0x10000234 for main. Unset by default,
|
prefixed hex, e.g. 0x10000234 for main. Unset by default,
|
||||||
which is the normal "attach to a running target" behaviour.
|
which is the normal "attach to a running target" behaviour.
|
||||||
Arms a 2-byte hardware execute breakpoint just before the
|
Arms a 2-byte hardware execute breakpoint just before the
|
||||||
final "reset run", so the core runs from the vector table
|
final "reset run", so the core runs from the vector table
|
||||||
and stops there on its own, with no client attached yet.
|
and stops there on its own, with no client attached yet.
|
||||||
See "Why BP_ADDR is one-shot" below for the important
|
See "Why BP_ADDR is one-shot" below for the important
|
||||||
limitation. Arming a length of 2 is mandatory: the Cortex-M33
|
limitation. Arming a length of 2 is mandatory: the Cortex-M33
|
||||||
comparators are halfword-based and reject anything else.
|
comparators are halfword-based and reject anything else.
|
||||||
|
|
||||||
.NOTES
|
.NOTES
|
||||||
Requirements:
|
Requirements:
|
||||||
* Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target.
|
* Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target.
|
||||||
* OpenOCD with the rp2350 target script installed. See PICO_OPENOCD.
|
* OpenOCD with the rp2350 target script installed. See PICO_OPENOCD.
|
||||||
* Exactly one process may own the debug probe.
|
* Exactly one process may own the debug probe.
|
||||||
* A firmware image already programmed into flash (use flash.ps1 first).
|
* A firmware image already programmed into flash (use flash.ps1 first).
|
||||||
* The Debug Probe must use the WinUSB driver. If OpenOCD reports
|
* The Debug Probe must use the WinUSB driver. If OpenOCD reports
|
||||||
"unable to open CMSIS-DAP device", install it with Zadig
|
"unable to open CMSIS-DAP device", install it with Zadig
|
||||||
(https://zadig.akeo.ie/), selecting "Debug Probe (CMSIS-DAP)" -> WinUSB.
|
(https://zadig.akeo.ie/), selecting "Debug Probe (CMSIS-DAP)" -> WinUSB.
|
||||||
|
|
||||||
Why USE_CORE=0 (core1 must be hidden from the client)
|
Why USE_CORE=0 (core1 must be hidden from the client)
|
||||||
The RP2350 has two Cortex-M33 cores. Core1 does not start on its own:
|
The RP2350 has two Cortex-M33 cores. Core1 does not start on its own:
|
||||||
nothing in a Pico SDK application releases it from reset unless the program
|
nothing in a Pico SDK application releases it from reset unless the program
|
||||||
explicitly does so. If you expose it anyway (USE_CORE=SMP), its registers
|
explicitly does so. If you expose it anyway (USE_CORE=SMP), its registers
|
||||||
read back as meaningless reset defaults:
|
read back as meaningless reset defaults:
|
||||||
|
|
||||||
core1: pc 0x000000ec sp 0xf0000000 xpsr 0x09000000 lr 0x00000147
|
core1: pc 0x000000ec sp 0xf0000000 xpsr 0x09000000 lr 0x00000147
|
||||||
core0: pc 0x1000320c sp 0x20081f38 xpsr 0xa9000000 lr 0x100030ab
|
core0: pc 0x1000320c sp 0x20081f38 xpsr 0xa9000000 lr 0x100030ab
|
||||||
|
|
||||||
Binary Ninja's register widget renders a memory preview for every register,
|
Binary Ninja's register widget renders a memory preview for every register,
|
||||||
which means it treats each register value as an ADDRESS and reads it. The
|
which means it treats each register value as an ADDRESS and reads it. The
|
||||||
core1 values above are not real addresses. Each read data-aborts, and
|
core1 values above are not real addresses. Each read data-aborts, and
|
||||||
OpenOCD responds by tearing down and re-establishing the SWD debug port,
|
OpenOCD responds by tearing down and re-establishing the SWD debug port,
|
||||||
logging a pair of lines per fault:
|
logging a pair of lines per fault:
|
||||||
|
|
||||||
Error: Failed to read memory at 0xf0000000
|
Error: Failed to read memory at 0xf0000000
|
||||||
Info : SWD DPIDR 0x4c013477
|
Info : SWD DPIDR 0x4c013477
|
||||||
|
|
||||||
That becomes a self-sustaining loop of roughly 450 fault-and-recover cycles
|
That becomes a self-sustaining loop of roughly 450 fault-and-recover cycles
|
||||||
every two seconds, which makes the session unusable. It looks exactly like a
|
every two seconds, which makes the session unusable. It looks exactly like a
|
||||||
broken debugger or a broken firmware. It is neither: it is a configuration
|
broken debugger or a broken firmware. It is neither: it is a configuration
|
||||||
mismatch, and hiding core1 removes it completely.
|
mismatch, and hiding core1 removes it completely.
|
||||||
|
|
||||||
Why reset run (the target must be released, not halted)
|
Why reset run (the target must be released, not halted)
|
||||||
On RP2350, halting during reset stops the core at the boot ROM stub BEFORE
|
On RP2350, halting during reset stops the core at the boot ROM stub BEFORE
|
||||||
the stack pointer is loaded:
|
the stack pointer is loaded:
|
||||||
|
|
||||||
xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 lr: 0xffffffff
|
xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 lr: 0xffffffff
|
||||||
|
|
||||||
Those values are garbage for every core, including core0. Attaching in that
|
Those values are garbage for every core, including core0. Attaching in that
|
||||||
window triggers the identical fault storm described above. Ending this
|
window triggers the identical fault storm described above. Ending this
|
||||||
script with "reset run" means the core starts from the vector table and is
|
script with "reset run" means the core starts from the vector table and is
|
||||||
executing normally by the time you attach, so registers read back correct.
|
executing normally by the time you attach, so registers read back correct.
|
||||||
|
|
||||||
Consequence for the client: ATTACH WHILE THE TARGET IS RUNNING. Do not
|
Consequence for the client: ATTACH WHILE THE TARGET IS RUNNING. Do not
|
||||||
press Reset/Restart in Binary Ninja -- it performs reset-halt and puts you
|
press Reset/Restart in Binary Ninja -- it performs reset-halt and puts you
|
||||||
back in the garbage window. If you must reset, send "reset run" over the
|
back in the garbage window. If you must reset, send "reset run" over the
|
||||||
OpenOCD telnet port (4444) instead. See WEEK04\WEEK04-BN.md.
|
OpenOCD telnet port (4444) instead. See WEEK04\WEEK04-BN.md.
|
||||||
|
|
||||||
Why BP_ADDR is one-shot (read this before relying on it)
|
Why BP_ADDR is one-shot (read this before relying on it)
|
||||||
A breakpoint armed here DOES fire during the startup "reset run" and halts
|
A breakpoint armed here DOES fire during the startup "reset run" and halts
|
||||||
the core at your address, so the server comes up parked there and your
|
the core at your address, so the server comes up parked there and your
|
||||||
debugger can simply attach and look at it. That part works.
|
debugger can simply attach and look at it. That part works.
|
||||||
|
|
||||||
What you do NOT get is a reusable breakpoint. As soon as any GDB client
|
What you do NOT get is a reusable breakpoint. As soon as any GDB client
|
||||||
connects, OpenOCD unconditionally flushes every breakpoint it is holding:
|
connects, OpenOCD unconditionally flushes every breakpoint it is holding:
|
||||||
|
|
||||||
Info : accepting 'gdb' connection on tcp/3333
|
Info : accepting 'gdb' connection on tcp/3333
|
||||||
Debug: breakpoints.c:328 breakpoint_remove_all_internal():
|
Debug: breakpoints.c:328 breakpoint_remove_all_internal():
|
||||||
[rp2350.dap.core0] Delete all breakpoints
|
[rp2350.dap.core0] Delete all breakpoints
|
||||||
|
|
||||||
So by the time Binary Ninja is up, the comparator is gone -- reading
|
So by the time Binary Ninja is up, the comparator is gone -- reading
|
||||||
0xE0002000 shows zeros, not your address. Consequences:
|
0xE0002000 shows zeros, not your address. Consequences:
|
||||||
|
|
||||||
* The startup stop is single-use. You cannot Resume and re-catch the
|
* The startup stop is single-use. You cannot Resume and re-catch the
|
||||||
same address.
|
same address.
|
||||||
* You cannot use BP_ADDR to stop in a loop that is already running,
|
* You cannot use BP_ADDR to stop in a loop that is already running,
|
||||||
because the core only passes that point once per reset.
|
because the core only passes that point once per reset.
|
||||||
* main (0x10000234) is a good BP_ADDR value precisely because it is
|
* main (0x10000234) is a good BP_ADDR value precisely because it is
|
||||||
reached exactly once, right after reset.
|
reached exactly once, right after reset.
|
||||||
|
|
||||||
To arm anything further, or to re-arm main, do it from the OpenOCD command
|
To arm anything further, or to re-arm main, do it from the OpenOCD command
|
||||||
port AFTER your debugger has connected -- the order is the whole trick:
|
port AFTER your debugger has connected -- the order is the whole trick:
|
||||||
|
|
||||||
nc 127.0.0.1 4444
|
nc 127.0.0.1 4444
|
||||||
> bp 0x1000023e 2 hw
|
> bp 0x1000023e 2 hw
|
||||||
> reset run
|
> reset run
|
||||||
|
|
||||||
Arming before the client connects does not work (flushed above), and in
|
Arming before the client connects does not work (flushed above), and in
|
||||||
plain GDB the equivalent "hbreak" is cleared by "detach" -- both were
|
plain GDB the equivalent "hbreak" is cleared by "detach" -- both were
|
||||||
verified by reading the FPB comparator registers back.
|
verified by reading the FPB comparator registers back.
|
||||||
|
|
||||||
Related Binary Ninja bug: Binary Ninja's own breakpoints cannot be used on
|
Related Binary Ninja bug: Binary Ninja's own breakpoints cannot be used on
|
||||||
this target at all. It sends Z0,<addr>,1 -- a 1-byte packet -- and
|
this target at all. It sends Z0,<addr>,1 -- a 1-byte packet -- and
|
||||||
OpenOCD answers "only breakpoints of two bytes length supported". Both
|
OpenOCD answers "only breakpoints of two bytes length supported". Both
|
||||||
Toggle Breakpoint (F2) and Add Hardware Breakpoint (F3) fail, at every
|
Toggle Breakpoint (F2) and Add Hardware Breakpoint (F3) fail, at every
|
||||||
address, and the dialog's Size field is disabled so there is no UI way
|
address, and the dialog's Size field is disabled so there is no UI way
|
||||||
around it. gdb_breakpoint_override hard does not change this. Hence the
|
around it. gdb_breakpoint_override hard does not change this. Hence the
|
||||||
command-port workflow described above.
|
command-port workflow described above.
|
||||||
|
|
||||||
Why the remaining OpenOCD flags are set
|
Why the remaining OpenOCD flags are set
|
||||||
gdb_breakpoint_override hard
|
gdb_breakpoint_override hard
|
||||||
Force every client breakpoint onto the Cortex-M33 hardware comparators
|
Force every client breakpoint onto the Cortex-M33 hardware comparators
|
||||||
(the RP2350 has 8 breakpoints and 4 watchpoints). Without this, a
|
(the RP2350 has 8 breakpoints and 4 watchpoints). Without this, a
|
||||||
client may try to write a BKPT instruction into flash at 0x10000000,
|
client may try to write a BKPT instruction into flash at 0x10000000,
|
||||||
which is read-only XIP memory, and the write fails.
|
which is read-only XIP memory, and the write fails.
|
||||||
gdb_memory_map disable
|
gdb_memory_map disable
|
||||||
Stops the client probing the entire 32 MiB flash map on connect. Pure
|
Stops the client probing the entire 32 MiB flash map on connect. Pure
|
||||||
Raspberry Pi guidance for suppressing spurious "Failed to read memory"
|
Raspberry Pi guidance for suppressing spurious "Failed to read memory"
|
||||||
reports during target discovery.
|
reports during target discovery.
|
||||||
cortex_m reset_config sysresetreq
|
cortex_m reset_config sysresetreq
|
||||||
The Cortex-M33 in the RP2350 has no VECTRESET. Without this, OpenOCD
|
The Cortex-M33 in the RP2350 has no VECTRESET. Without this, OpenOCD
|
||||||
warns on every reset:
|
warns on every reset:
|
||||||
VECTRESET is not supported on this Cortex-M core, using SYSRESETREQ
|
VECTRESET is not supported on this Cortex-M core, using SYSRESETREQ
|
||||||
instead
|
instead
|
||||||
NOTE: this MUST be the generic "cortex_m" command, not
|
NOTE: this MUST be the generic "cortex_m" command, not
|
||||||
"rp2350.dap.core1 cortex_m ...". With USE_CORE=0 the core1 target does
|
"rp2350.dap.core1 cortex_m ...". With USE_CORE=0 the core1 target does
|
||||||
not exist, so a core1-scoped command aborts OpenOCD before "init" runs,
|
not exist, so a core1-scoped command aborts OpenOCD before "init" runs,
|
||||||
and the script exits silently having printed nothing useful.
|
and the script exits silently having printed nothing useful.
|
||||||
adapter speed
|
adapter speed
|
||||||
See ADAPTER_SPEED above.
|
See ADAPTER_SPEED above.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\debug-server.ps1
|
.\debug-server.ps1
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
$env:ADAPTER_SPEED=10000; .\debug-server.ps1
|
$env:ADAPTER_SPEED=10000; .\debug-server.ps1
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
$env:PICO_OPENOCD="C:\openocd\bin"; .\debug-server.ps1
|
$env:PICO_OPENOCD="C:\openocd\bin"; .\debug-server.ps1
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
$env:BP_ADDR="0x10000234"; .\debug-server.ps1
|
$env:BP_ADDR="0x10000234"; .\debug-server.ps1
|
||||||
Comes up halted at main (one-shot; see "Why BP_ADDR is one-shot").
|
Comes up halted at main (one-shot; see "Why BP_ADDR is one-shot").
|
||||||
|
|
||||||
.NOTES
|
.NOTES
|
||||||
Exit status:
|
Exit status:
|
||||||
* propagated from OpenOCD. A clean shutdown via Ctrl-C exits 0; an
|
* propagated from OpenOCD. A clean shutdown via Ctrl-C exits 0; an
|
||||||
OpenOCD configuration error exits non-zero.
|
OpenOCD configuration error exits non-zero.
|
||||||
|
|
||||||
Related scripts:
|
Related scripts:
|
||||||
flash.ps1 one-shot raw .bin programmer (exits when done)
|
flash.ps1 one-shot raw .bin programmer (exits when done)
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
WEEK04\WEEK04-BN.md the full walkthrough this script belongs to
|
WEEK04\WEEK04-BN.md the full walkthrough this script belongs to
|
||||||
#>
|
#>
|
||||||
|
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[Parameter()]
|
[Parameter()]
|
||||||
[string]$OCD
|
[string]$OCD
|
||||||
)
|
)
|
||||||
|
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
# --- configuration ---------------------------------------------------------
|
# --- configuration ---------------------------------------------------------
|
||||||
|
|
||||||
if (-not $OCD) { $OCD = $env:PICO_OPENOCD }
|
if (-not $OCD) { $OCD = $env:PICO_OPENOCD }
|
||||||
if (-not $OCD) { $OCD = "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev" }
|
if (-not $OCD) { $OCD = "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev" }
|
||||||
|
|
||||||
$SPEED = if ($env:ADAPTER_SPEED) { $env:ADAPTER_SPEED } else { "24000" }
|
$SPEED = if ($env:ADAPTER_SPEED) { $env:ADAPTER_SPEED } else { "24000" }
|
||||||
$USE_CORE = if ($env:USE_CORE) { $env:USE_CORE } else { "0" }
|
$USE_CORE = if ($env:USE_CORE) { $env:USE_CORE } else { "0" }
|
||||||
$BP_ADDR = if ($env:BP_ADDR) { $env:BP_ADDR.Trim() } else { "" }
|
$BP_ADDR = if ($env:BP_ADDR) { $env:BP_ADDR.Trim() } else { "" }
|
||||||
|
|
||||||
if ($BP_ADDR -and $BP_ADDR -notmatch '^0x[0-9a-fA-F]+$') {
|
if ($BP_ADDR -and $BP_ADDR -notmatch '^0x[0-9a-fA-F]+$') {
|
||||||
Write-Error "BP_ADDR must be 0x-prefixed hex, e.g. 0x10000234 (got '$BP_ADDR')"
|
Write-Error "BP_ADDR must be 0x-prefixed hex, e.g. 0x10000234 (got '$BP_ADDR')"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not (Test-Path "$OCD\openocd.exe")) {
|
if (-not (Test-Path "$OCD\openocd.exe")) {
|
||||||
Write-Error "OpenOCD not found at $OCD\openocd.exe (set PICO_OPENOCD or -OCD to the directory containing openocd.exe)"
|
Write-Error "OpenOCD not found at $OCD\openocd.exe (set PICO_OPENOCD or -OCD to the directory containing openocd.exe)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- announce, so the operator can verify intent before the target is touched --
|
# --- announce, so the operator can verify intent before the target is touched --
|
||||||
|
|
||||||
Write-Host "Starting OpenOCD GDB server on 127.0.0.1:3333 using $OCD\openocd.exe"
|
Write-Host "Starting OpenOCD GDB server on 127.0.0.1:3333 using $OCD\openocd.exe"
|
||||||
Write-Host "SWD adapter speed: $SPEED kHz"
|
Write-Host "SWD adapter speed: $SPEED kHz"
|
||||||
Write-Host "Cores exposed to GDB (USE_CORE): $USE_CORE"
|
Write-Host "Cores exposed to GDB (USE_CORE): $USE_CORE"
|
||||||
Write-Host "Target will be reset and released (reset run) - attach while it is running."
|
Write-Host "Target will be reset and released (reset run) - attach while it is running."
|
||||||
|
|
||||||
if ($BP_ADDR) {
|
if ($BP_ADDR) {
|
||||||
Write-Host "Startup breakpoint at $BP_ADDR (2-byte hardware execute, one-shot)."
|
Write-Host "Startup breakpoint at $BP_ADDR (2-byte hardware execute, one-shot)."
|
||||||
Write-Host " It fires during this startup reset run. Any client connecting later"
|
Write-Host " It fires during this startup reset run. Any client connecting later"
|
||||||
Write-Host " causes OpenOCD to delete it, so arm further breakpoints on port 4444"
|
Write-Host " causes OpenOCD to delete it, so arm further breakpoints on port 4444"
|
||||||
Write-Host " after attaching: bp <addr> 2 hw"
|
Write-Host " after attaching: bp <addr> 2 hw"
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- start -----------------------------------------------------------------
|
# --- start -----------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Order matters: USE_CORE must be set BEFORE -f target/rp2350.cfg is read,
|
# Order matters: USE_CORE must be set BEFORE -f target/rp2350.cfg is read,
|
||||||
# because the target script branches on it when creating the DAP targets.
|
# because the target script branches on it when creating the DAP targets.
|
||||||
#
|
#
|
||||||
# "reset run" is last so it happens after init and after the target is
|
# "reset run" is last so it happens after init and after the target is
|
||||||
# examined, releasing the core rather than halting it. See .NOTES for why.
|
# examined, releasing the core rather than halting it. See .NOTES for why.
|
||||||
#
|
#
|
||||||
# Any BP_ADDR breakpoint is inserted after "init" (the target must exist before
|
# Any BP_ADDR breakpoint is inserted after "init" (the target must exist before
|
||||||
# a comparator can be programmed) but before "reset run" (so the core is already
|
# a comparator can be programmed) but before "reset run" (so the core is already
|
||||||
# armed when it starts). The length must be 2: Cortex-M33 comparators reject
|
# armed when it starts). The length must be 2: Cortex-M33 comparators reject
|
||||||
# other widths.
|
# other widths.
|
||||||
|
|
||||||
$ocdArgs = @(
|
$ocdArgs = @(
|
||||||
"-s", "$OCD\scripts",
|
"-s", "$OCD\scripts",
|
||||||
"-f", "interface/cmsis-dap.cfg",
|
"-f", "interface/cmsis-dap.cfg",
|
||||||
"-c", "set USE_CORE $USE_CORE",
|
"-c", "set USE_CORE $USE_CORE",
|
||||||
"-f", "target/rp2350.cfg",
|
"-f", "target/rp2350.cfg",
|
||||||
# Drop the hwthread RTOS the RP2350 target script attaches to core0.
|
# Drop the hwthread RTOS the RP2350 target script attaches to core0.
|
||||||
#
|
#
|
||||||
# target/rp2350.cfg creates core0 with "-rtos hwthread", which registers a
|
# target/rp2350.cfg creates core0 with "-rtos hwthread", which registers a
|
||||||
# fake RTOS whose "current thread" is coreid+1 = 1. Binary Ninja single-steps
|
# fake RTOS whose "current thread" is coreid+1 = 1. Binary Ninja single-steps
|
||||||
# with the GDB packet "vCont;s" and no thread id, i.e. thread 0. OpenOCD's
|
# with the GDB packet "vCont;s" and no thread id, i.e. thread 0. OpenOCD's
|
||||||
# gdb_server sees rtos->current_thread (1) != thread_id (0) and takes its
|
# gdb_server sees rtos->current_thread (1) != thread_id (0) and takes its
|
||||||
# "fake step" path, replying with a stop without ever stepping the core:
|
# "fake step" path, replying with a stop without ever stepping the core:
|
||||||
#
|
#
|
||||||
# gdb_server.c gdb_handle_vcont_packet(): fake step thread 0
|
# gdb_server.c gdb_handle_vcont_packet(): fake step thread 0
|
||||||
#
|
#
|
||||||
# The result is that Step Into / Step Over in Binary Ninja does nothing: the
|
# The result is that Step Into / Step Over in Binary Ninja does nothing: the
|
||||||
# PC never moves. Clearing the RTOS removes the mismatch so the step is real.
|
# PC never moves. Clearing the RTOS removes the mismatch so the step is real.
|
||||||
# Harmless for single-core use, which is all this lab does (USE_CORE=0).
|
# Harmless for single-core use, which is all this lab does (USE_CORE=0).
|
||||||
"-c", "rp2350.dap.core0 configure -rtos none",
|
# The core's name differs between OpenOCD builds (rp2350.dap.core0 vs rp2350.cm0
|
||||||
"-c", "adapter speed $SPEED",
|
# in the Pico SDK's Windows build), so look it up instead of hard-coding it.
|
||||||
"-c", "gdb_memory_map disable",
|
"-c", "[lindex [target names] 0] configure -rtos none",
|
||||||
"-c", "gdb_breakpoint_override hard",
|
"-c", "adapter speed $SPEED",
|
||||||
"-c", "cortex_m reset_config sysresetreq",
|
"-c", "gdb_memory_map disable",
|
||||||
"-c", "init"
|
"-c", "gdb_breakpoint_override hard",
|
||||||
)
|
"-c", "cortex_m reset_config sysresetreq",
|
||||||
|
"-c", "init"
|
||||||
if ($BP_ADDR) {
|
)
|
||||||
$ocdArgs += @("-c", "bp $BP_ADDR 2 hw")
|
|
||||||
}
|
if ($BP_ADDR) {
|
||||||
|
$ocdArgs += @("-c", "bp $BP_ADDR 2 hw")
|
||||||
$ocdArgs += @("-c", "reset run")
|
}
|
||||||
|
|
||||||
& "$OCD\openocd.exe" @ocdArgs
|
$ocdArgs += @("-c", "reset run")
|
||||||
|
|
||||||
exit $LASTEXITCODE
|
& "$OCD\openocd.exe" @ocdArgs
|
||||||
|
|
||||||
|
exit $LASTEXITCODE
|
||||||
@@ -1,128 +1,132 @@
|
|||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Program a raw .bin into RP2350 XIP flash via the Pico Debug Probe and OpenOCD.
|
Program a raw .bin into RP2350 XIP flash via the Pico Debug Probe and OpenOCD.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Writes a headerless raw binary to the RP2350's external XIP flash starting at
|
Writes a headerless raw binary to the RP2350's external XIP flash starting at
|
||||||
physical address 0x10000000, verifies the written bytes by reading them back,
|
physical address 0x10000000, verifies the written bytes by reading them back,
|
||||||
then releases the core so the freshly programmed firmware runs.
|
then releases the core so the freshly programmed firmware runs.
|
||||||
|
|
||||||
A raw .bin carries no address information, no entry point and no section
|
A raw .bin carries no address information, no entry point and no section
|
||||||
table, so the load address MUST be supplied out of band. On the RP2350 the
|
table, so the load address MUST be supplied out of band. On the RP2350 the
|
||||||
only correct value is 0x10000000: that is where the boot ROM jumps after
|
only correct value is 0x10000000: that is where the boot ROM jumps after
|
||||||
pulling the reset vector out of the on-chip XIP window. Flashing anywhere
|
pulling the reset vector out of the on-chip XIP window. Flashing anywhere
|
||||||
else produces a board that enumerates over USB and then does nothing.
|
else produces a board that enumerates over USB and then does nothing.
|
||||||
|
|
||||||
The target must already be running the stock RP2350 bootrom (the default
|
The target must already be running the stock RP2350 bootrom (the default
|
||||||
state after power-up, or after any BOOTSEL + UF2 reflash). OpenOCD attaches
|
state after power-up, or after any BOOTSEL + UF2 reflash). OpenOCD attaches
|
||||||
to the bootrom over SWD, halts it, programs flash, verifies, and resets.
|
to the bootrom over SWD, halts it, programs flash, verifies, and resets.
|
||||||
|
|
||||||
This is the macOS / Linux equivalent of flash.sh. The two must stay in
|
This is the macOS / Linux equivalent of flash.sh. The two must stay in
|
||||||
lockstep: same base address, same verify, same conservative SWD clock.
|
lockstep: same base address, same verify, same conservative SWD clock.
|
||||||
|
|
||||||
.PARAMETER Bin
|
.PARAMETER Bin
|
||||||
Mandatory. Path to the raw .bin image to program.
|
Mandatory. Path to the raw .bin image to program.
|
||||||
|
|
||||||
.ENVIRONMENT
|
.ENVIRONMENT
|
||||||
PICO_OPENOCD Directory containing openocd.exe AND its scripts\ directory.
|
PICO_OPENOCD Directory containing openocd.exe AND its scripts\ directory.
|
||||||
Default: $env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev
|
Default: $env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev
|
||||||
The path is used for BOTH the executable and the -s scripts
|
The path is used for BOTH the executable and the -s scripts
|
||||||
argument.
|
argument.
|
||||||
ADAPTER_SPEED SWD clock in kHz. Default: 5000.
|
ADAPTER_SPEED SWD clock in kHz. Default: 5000.
|
||||||
5000 kHz is deliberately conservative. This is a write path,
|
5000 kHz is deliberately conservative. This is a write path,
|
||||||
not a read-only debug session, and a marginal USB cable or
|
not a read-only debug session, and a marginal USB cable or
|
||||||
long dupont run produces spurious verify failures at higher
|
long dupont run produces spurious verify failures at higher
|
||||||
clocks. Raise it (24000) for read-only work; if
|
clocks. Raise it (24000) for read-only work; if
|
||||||
"Error: target not halted" or verify mismatches appear,
|
"Error: target not halted" or verify mismatches appear,
|
||||||
lower it to 1000.
|
lower it to 1000.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\flash.ps1 -Bin 0x0005_intro-to-variables\build\0x0005_intro-to-variables.bin
|
.\flash.ps1 -Bin 0x0005_intro-to-variables\build\0x0005_intro-to-variables.bin
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
$env:ADAPTER_SPEED=1000; .\flash.ps1 -Bin build\hacked.bin
|
$env:ADAPTER_SPEED=1000; .\flash.ps1 -Bin build\hacked.bin
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
$env:PICO_OPENOCD="C:\openocd\bin"; .\flash.ps1 -Bin build\hacked.bin
|
$env:PICO_OPENOCD="C:\openocd\bin"; .\flash.ps1 -Bin build\hacked.bin
|
||||||
|
|
||||||
.NOTES
|
.NOTES
|
||||||
Requirements:
|
Requirements:
|
||||||
* Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target.
|
* Pico Debug Probe (or any CMSIS-DAP / SWD adapter) connected to the target.
|
||||||
* OpenOCD with the rp2350 target script installed.
|
* OpenOCD with the rp2350 target script installed.
|
||||||
* The Debug Probe must use the WinUSB driver. If OpenOCD reports
|
* The Debug Probe must use the WinUSB driver. If OpenOCD reports
|
||||||
"unable to open CMSIS-DAP device", install it with Zadig
|
"unable to open CMSIS-DAP device", install it with Zadig
|
||||||
(https://zadig.akeo.ie/), selecting "Debug Probe (CMSIS-DAP)" -> WinUSB.
|
(https://zadig.akeo.ie/), selecting "Debug Probe (CMSIS-DAP)" -> WinUSB.
|
||||||
* Exactly one process may own the debug probe. Close any other OpenOCD,
|
* Exactly one process may own the debug probe. Close any other OpenOCD,
|
||||||
GDB, or IDE debug session first.
|
GDB, or IDE debug session first.
|
||||||
|
|
||||||
Exit status:
|
Exit status:
|
||||||
0 flash written, verified, and core released
|
0 flash written, verified, and core released
|
||||||
1 input file missing or OpenOCD not found
|
1 input file missing or OpenOCD not found
|
||||||
* any other status is propagated from OpenOCD, so a failed verify or a
|
* any other status is propagated from OpenOCD, so a failed verify or a
|
||||||
write error is visible to the caller rather than being swallowed.
|
write error is visible to the caller rather than being swallowed.
|
||||||
|
|
||||||
Related scripts:
|
Related scripts:
|
||||||
debug-server.ps1 long-running GDB server for live debugging with
|
debug-server.ps1 long-running GDB server for live debugging with
|
||||||
Binary Ninja. Use that instead of this script when you
|
Binary Ninja. Use that instead of this script when you
|
||||||
need to single-step.
|
need to single-step.
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
WEEK04\WEEK04-BN.md the full walkthrough this script belongs to
|
WEEK04\WEEK04-BN.md the full walkthrough this script belongs to
|
||||||
#>
|
#>
|
||||||
|
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[ValidateNotNullOrEmpty()]
|
[ValidateNotNullOrEmpty()]
|
||||||
[string]$Bin
|
[string]$Bin
|
||||||
)
|
)
|
||||||
|
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
# --- argument validation ---------------------------------------------------
|
# --- argument validation ---------------------------------------------------
|
||||||
|
|
||||||
if (-not (Test-Path -PathType Leaf $Bin)) {
|
if (-not (Test-Path -PathType Leaf $Bin)) {
|
||||||
Write-Error "file not found: $Bin"
|
Write-Error "file not found: $Bin"
|
||||||
Write-Host "build it first: cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ; cmake --build build"
|
Write-Host "build it first: cmake -B build -G Ninja -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ; cmake --build build"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- toolchain resolution --------------------------------------------------
|
# --- toolchain resolution --------------------------------------------------
|
||||||
|
|
||||||
$OCD = if ($env:PICO_OPENOCD) { $env:PICO_OPENOCD } else { "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev" }
|
$OCD = if ($env:PICO_OPENOCD) { $env:PICO_OPENOCD } else { "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev" }
|
||||||
if (-not (Test-Path "$OCD\openocd.exe")) {
|
if (-not (Test-Path "$OCD\openocd.exe")) {
|
||||||
Write-Error "OpenOCD not found at $OCD\openocd.exe (set PICO_OPENOCD to the directory containing openocd.exe)"
|
Write-Error "OpenOCD not found at $OCD\openocd.exe (set PICO_OPENOCD to the directory containing openocd.exe)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$SPEED = if ($env:ADAPTER_SPEED) { $env:ADAPTER_SPEED } else { "5000" }
|
$SPEED = if ($env:ADAPTER_SPEED) { $env:ADAPTER_SPEED } else { "5000" }
|
||||||
|
|
||||||
# --- program ---------------------------------------------------------------
|
# --- program ---------------------------------------------------------------
|
||||||
|
|
||||||
# OpenOCD flag notes:
|
# OpenOCD flag notes:
|
||||||
# -f interface/cmsis-dap.cfg the Pico Debug Probe is a CMSIS-DAP v1 device
|
# -f interface/cmsis-dap.cfg the Pico Debug Probe is a CMSIS-DAP v1 device
|
||||||
# -f target/rp2350.cfg RP2350 dual Cortex-M33 + RP2350B0-style DAP;
|
# -f target/rp2350.cfg RP2350 dual Cortex-M33 + RP2350B0-style DAP;
|
||||||
# sets USE_CORE=SMP by default, which is fine here
|
# sets USE_CORE=SMP by default, which is fine here
|
||||||
# because we never hand uninitialised core1
|
# because we never hand uninitialised core1
|
||||||
# registers to a debugger. For live debugging use
|
# registers to a debugger. For live debugging use
|
||||||
# debug-server.ps1, which forces USE_CORE=0.
|
# debug-server.ps1, which forces USE_CORE=0.
|
||||||
# -c "program BIN 0x10000000 verify reset exit"
|
# -c "program BIN 0x10000000 verify reset exit"
|
||||||
# program the write
|
# program the write
|
||||||
# 0x10000000 base address (see .DESCRIPTION)
|
# 0x10000000 base address (see .DESCRIPTION)
|
||||||
# verify read back and compare every byte; a mismatch aborts
|
# verify read back and compare every byte; a mismatch aborts
|
||||||
# reset reset the core so the new image starts at its vectors
|
# reset reset the core so the new image starts at its vectors
|
||||||
# exit release the probe and return to the shell
|
# exit release the probe and return to the shell
|
||||||
#
|
#
|
||||||
# The adapter speed is deliberately lower here than in debug-server.ps1; see
|
# The adapter speed is deliberately lower here than in debug-server.ps1; see
|
||||||
# ADAPTER_SPEED in .ENVIRONMENT.
|
# ADAPTER_SPEED in .ENVIRONMENT.
|
||||||
Write-Host "Flashing $Bin -> 0x10000000 using $OCD\openocd.exe (SWD $SPEED kHz)"
|
Write-Host "Flashing $Bin -> 0x10000000 using $OCD\openocd.exe (SWD $SPEED kHz)"
|
||||||
|
|
||||||
& "$OCD\openocd.exe" `
|
# OpenOCD parses -c strings as Tcl, which eats backslashes ("\build" -> backspace+"uild").
|
||||||
-s "$OCD\scripts" `
|
# Hand it an absolute path with forward slashes, braced so spaces survive.
|
||||||
-f interface/cmsis-dap.cfg `
|
$BinTcl = (Resolve-Path $Bin).Path -replace '\\', '/'
|
||||||
-f target/rp2350.cfg `
|
|
||||||
-c "adapter speed $SPEED" `
|
& "$OCD\openocd.exe" `
|
||||||
-c "program $Bin 0x10000000 verify reset exit"
|
-s "$OCD\scripts" `
|
||||||
|
-f interface/cmsis-dap.cfg `
|
||||||
exit $LASTEXITCODE
|
-f target/rp2350.cfg `
|
||||||
|
-c "adapter speed $SPEED" `
|
||||||
|
-c "program {$BinTcl} 0x10000000 verify reset exit"
|
||||||
|
|
||||||
|
exit $LASTEXITCODE
|
||||||
Reference in new issue
Block a user