Files
sw_openocd/src/target/target.c
T
Tim Newsome 7079e0ca7d From upstream (#331)
* flash/nor: Add support for TI CC26xx/CC13xx flash

Added cc26xx flash driver to support the TI CC26xx and CC13xx
microcontrollers. Driver is capable of determining which MCU
is connected and configures itself accordingly. Added config
files for four specific variants: CC26x0, CC13x0, CC26x2, and
CC13x2.

Note that the flash loader code is based on the sources used
to support flash in Code Composer Studio and Uniflash from TI.

Removed cc26xx.cfg file made obsolete by this patch.

Change-Id: Ie2b0f74f8af7517a9184704b839677d1c9787862
Signed-off-by: Edward Fewell <efewell@ti.com>
Reviewed-on: http://openocd.zylin.com/4358
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Fredrik Hederstierna <fredrik@hederstierna.com>

* flash/nor/nrf5: remove is_erased setting and autoerase before write

Cached flash erase state in sectors[].is_erased is not reliable as running
target can change the flash.

Autoerase was issued before flash write on condition is_erased != 1
Remove autoerase completely as it is a quite non-standard feature.

Change-Id: I19bef459e6afdc4c5fcaa2ccd194cf05be8a42b6
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4400
Tested-by: jenkins

* src/flash/tms470: remove testing of sectors[].is_erased state

The erase check routine checked sectors only if is_erased != 1

Check sector unconditionally.

While on it fix clang static analyzer warnings.

Change-Id: I9988615fd8530c55a9b0c54b1900f89b550345e9
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4401
Tested-by: jenkins

* tcl/target/stm32f7x: configure faster system clock in reset-init

STM32F7xx devices need faster clock for flash programming
over JTAG transport. Using reset default 16 MHz clock
resulted in lot of DAP WAITs and substantial decrease
of flashing performance.

Adapted to the restructured dap support
(see 2231da8ec4).

Change-Id: Ida6915331dd924c9c0d08822fd94c04ad408cdc5
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4464
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>

* flash/nor/psoc5lp: fix compile issue on GCC 8.1.0

Issue already identified by Alex https://sourceforge.net/u/alexbour/
in ticket #191 https://sourceforge.net/p/openocd/tickets/191/

	src/flash/nor/psoc5lp.c:237:2: error: ‘strncpy’ output
	truncated before terminating nul copying 2 bytes from a
	string of the same length [-Werror=stringop-truncation]

Fix it by assigning the value to the array elements.

Change-Id: I22468e5700efa64ea48ae8cdec930c48b4a7d8fb
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4563
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/arm: Add PLD command to ARM disassembler.

Updates the ARM disassembler to handle PLD (PreLoad Data) commands.
Previously handled by printing a TODO message. There are three forms of
the command: literal, register, and immediate. Simply decode based off
of the A1 encoding for the instructions in the ARM ARM. Also fixes mask
to handle PLDW commands.

Change-Id: I63bf97f16af254e838462c7cfac80f6c4681c556
Signed-off-by: James Marshall <jcmarsh@gwmail.gwu.edu>
Reviewed-on: http://openocd.zylin.com/4348
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* mips_m4k.c: Fix build with --disable-target64

Replace PRIx64 with TARGET_PRIxADDR to avoid build problems
when --disable-target64 is used during configure.

Change-Id: I054a27a491e86c42c9386a0488194320b808ba96
Signed-off-by: Liviu Ionescu <ilg@livius.net>
Reviewed-on: http://openocd.zylin.com/4566
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Tim Newsome <tim@sifive.com>

* target/arm_adi_v5: sync CSW and TAR cache on apreg write

When using apreg to change AP registers CSW or TAR we get internal
cached value not valid anymore.

Reuse the setup functions for CSW and TAR to write them.
Invalidate the cached value before the call to force the write, thus
keeping original apreg behaviour.

Change-Id: Ib14fafd5e584345de94f2e983de55406c588ac1c
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4565
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/arm_adi_v5: keep CSW and TAR cache updated

The call to dap_queue_ap_write() can fail and the value in CSW and
TAR becomes unknown.

Invalidate the OpenOCD cache if dap_queue_ap_write() fails.

Change-Id: Id6ec370b4c5ad07e454464780c1a1c8ae34ac870
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4564
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/target: Add Renesas R-Car R8A7794 E2 target

Add configuration for the Renesas R-Car R8A7794 E2 target.
This is an SoC with two Cortex A7 ARMv7a cores, both A7
cores are supported.

Change-Id: Ic1c81840e3bfcef8ee1de5acedffae5c83612a5e
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4531
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car R8A7790 H2 Stout board

Add configuration for the Renesas R-Car R8A7790 H2
based Stout ADAS board.

Change-Id: Ib880b5d2e1fab5c8c0bc0dbcedcdce8055463fe2
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4497
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car R8A7791 M2W Porter board

Add configuration for the Renesas R-Car R8A7791 M2W
based Porter evaluation board.

Change-Id: Iaadb18f29748f890ebb68519ea9ddbd18e7649af
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4498
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car R8A7794 E2 Silk board

Add configuration for the Renesas R-Car R8A7794 E2
based Silk evaluation board.

Change-Id: I504b5630b1a2791ed6967c6c2af8851ceef9723f
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
---
NOTE: This requires SW7[1] in position 1 (default is 0)
Reviewed-on: http://openocd.zylin.com/4532
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Factor out common R-Car Gen2 code

Factor out the code shared by all R-Car Gen2 boards into a single
file to get rid of the duplication.

Change-Id: I70b302c2e71f4e6fdccb2817dd65a5493bb393d8
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4533
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* jtag/drivers/cmsis-dap: fix connect in cmsis_dap_swd_switch_seq()

The proc cmsis_dap_swd_switch_seq() is part of the SWD API for
this interface driver. It is valid only when the interface is
used in SWD mode.
In this proc there is the need to call, in sequence, first
cmsis_dap_cmd_DAP_Disconnect() then cmsis_dap_cmd_DAP_Connect().
The latter call requires the connection mode as parameter, that
inside cmsis_dap_swd_switch_seq() can only be CONNECT_SWD.

The current implementation is not correct and in some cases can
pass mode CONNECT_JTAG. Moreover, JTAG is optional in CMSIS-DAP
and passing mode CONNECT_JTAG triggers an error with SWD-only
interfaces.

Use mode CONNECT_SWD in SWD specific cmsis_dap_swd_switch_seq().

Change-Id: Ib455bf5b69cb2a2d146a6c8875387b00c27a5690
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4571
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: return error if breakpoint address is out of range

If the "Flash Patch and Breakpoint" unit is rev.1 then it can only
accept breakpoint addresses below 0x1FFFFFFF.
Detailed info in "ARM v7-M Architecture Reference Manual", DDI0403E
at chapter "C1.11 Flash Patch and Breakpoint unit".

Print a message and return error if the address of hardware
breakpoint cannot be handled by the breakpoint unit.

Change-Id: I95c92b1f058f0dfc568bf03015f99e439b27c59b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4535
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* flash/nor/stm32: Report errors in wait_status_busy

Flash operation errors that occur during algorithm programming are
reported via the algorithm return value. However, Flash operation
errors that occur during non-algorithm work (erasing, programming
without a work area, programming the last non-multiple-of-32-bytes on
an H7, etc.) generally end with a call to stm32x_wait_status_busy,
which reads the status register and clears the error flags but fails
to actually report that something went wrong should an error flag
(other than WRPERR) be set. Return an error status from
stm32x_wait_status_busy in those cases. Correct a log message
accordingly.

Change-Id: I09369ea5f924fe58833aec1f45e52320ab4aaf43
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4519
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/stm32: Eliminate working area leak

On a specific early-return path, an allocated working area was not
freed. Free it.

Change-Id: I7c8fe51ff475f191624086996be1c77251780b77
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4520
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/stm32h7: Fix incorrect comment

The name of the bit according to the reference manual is inconsistency
error, not increment error.

Change-Id: Ie3b73c0312db586e35519e03fd1a5cb225673d97
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4521
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>

* target: fix 'bp' command help message

"asid" and "length" are separate arguments of the command.
Put space between them.

Change-Id: I36cfc1e3a01caafef4fc3b26972a0cc192b0b963
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4511
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* Add ARM v8 AArch64 semihosting support

This patch implements semihosting support for AArch64. This picks
code from previously submitted AArch64 semihosting support patch
and rebases on top of reworked semihosting code. Tested in AArch64
mode on a Lemaker Hikey Board with NewLib and GDB.

Change-Id: I228a38f1de24f79e49ba99d8514d822a28c2950b
Signed-off-by: Omair Javaid <omair.javaid@linaro.org>
Reviewed-on: http://openocd.zylin.com/4537
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* GDB fileIO stdout support

This patch fixes gdb fileio support to allow gdb console to be used as stdout.

Now we can do something like
gdb <inferior file>

(gdb) tar ext :3333
(gdb) load
(gdb) monitor arm semihosting enable
(gdb) monitor arm semihosting_fileio enable
(gdb) continue

Here: Output from inferior using puts, printf etc will be routed to gdb console.

Change-Id: I9cb0dddda1de58038c84f5b035c38229828cd744
Signed-off-by: Omair Javaid <omair.javaid@linaro.org>
Reviewed-on: http://openocd.zylin.com/4538
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target: armv8: Avoid semihosting segfault on halt

Avoid a NULL pointer dereference when halting an aarch64 core.

Change-Id: I333d40475ab26e2f0dca5c27302a5fa4d817a12f
Signed-off-by: Andreas Färber <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/4593
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl: target: Add NXP LS1012A config

As seen on the FRDM-LS1012A board.

Change-Id: Ifc9074b3f7535167b9ded5f544501ec2879f5db7
Signed-off-by: Andreas Färber <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/4594
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl: board: Add NXP Freedom FRDM-LS1012A config

An update for the K20 CMSIS-DAP firmware can be found here:
https://community.nxp.com/thread/387080?commentID=840141#comment-840141

Change-Id: I149d7f8610aa56daf1aeb95f14ee1bf88f7cb647
Signed-off-by: Andreas Färber <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/4595
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* gdb_server: only trigger once the event gdb-detach at gdb quit

When GDB quits (e.g. with "quit" command) we first execute
gdb_detach() to reply "OK" then, at GDB disconnect (either TCP
or pipe connection type), we execute gdb_connection_closed().
In case GDB is killed or it crashes, OpenOCD only executes the
latter when detects the disconnection.
Both gdb_detach() and gdb_connection_closed() trigger the event
TARGET_EVENT_GDB_DETACH thus getting it triggered twice on clean
GDB quit.

Do not trigger the event TARGET_EVENT_GDB_DETACH in gdb_detach()
and let only gdb_connection_closed() to handle it.

Change-Id: Iacf035c855b8b3e2239c1c0e259c279688b418ee
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4585
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* gdb_server: set current_target from connection's one

In a multi-target environment we are supposed to have a single
gdb server for each target (or for each group of targets within
a SMP node).
By default, the gdb attached to a server sends its command to
the target (or to the SMP node targets) linked to that server.

This is working fine for the normal gdb commands, but it is
broken for the native OpenOCD commands executed through gdb
"monitor" command. In the latter case, gdb "monitor" commands
will be executed on the current target of OpenOCD configuration
script (that is either the last target created or the target
specified in a "targets" command).

Fixed in gdb_new_connection() by replacing the current target
in the connection's copy of command context.

Change-Id: If7c8f2dce4a3138f0907d3000dd0b15e670cfa80
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4586
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* target/image: make i/j unsigned to avoid ubsan runtime error

	src/target/image.c:1055:15: runtime error: left shift of 128 by 24 places cannot be represented in type 'int'

Change-Id: I322fd391cf3f242beffc8a274824763c8c5e69a4
Signed-off-by: Cody Schafer <openocd@codyps.com>
Reviewed-on: http://openocd.zylin.com/4584
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* target/stm32f7x: Clear stuck HSE clock with CSS

Change-Id: Ica0025ea465910dd664ab546b66f4f25b271f1f5
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4570
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* psoc5lp: fix erase check, add free_driver_priv

psoc5lp_erase_check() was not properly adapted to the new
armv7m_blank_check_memory() in the hot fix 53376dbbed
This change fixes handling of num_sectors in dependecy of ecc_enabled.
Also add comments how ecc_enabled influences num_sectors.

Add pointer to default_flash_free_driver_priv() to all psoc5lp flash
drivers to keep valgrind happy.

Change-Id: Ie1806538becd364fe0efb7a414f0fe6a84b2055b
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4569
Tested-by: jenkins

* target: atmel samd10 xplained mini

cortex m0+ on a tiny board, with an mEDBG (CMSIS-DAP) debug interface.

Change-Id: Iaedfab578b4eb4aa2d923bd80f220f59b34e6ef9
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3402
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/board: add SAMD11 Xplained Pro evaluation board

Change-Id: Id996c4de6dc9f25f71424017bf07689fea7bd3af
Signed-off-by: Peter Lawrence <majbthrd@gmail.com>
Reviewed-on: http://openocd.zylin.com/4507
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* Adds SAMD11D14AU flash support.

Corrects names of SAMD11D14AM and SAMD11D14ASS per datasheet.

Change-Id: I8beb15d5376966a4f8d7de76bfb2cbda2db440dc
Signed-off-by: Christopher Hoover <ch@murgatroid.com>
Reviewed-on: http://openocd.zylin.com/4597
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* nds32: Avoid detected JTAG clock

AICE2 doesn't support scan for the maximum clock frequency of
JTAG chain. It will cause USB command timeout.

Change-Id: I41d1e3be387b6ed5a4dd0be663385a5f053fbcf9
Signed-off-by: Hellosun Wu <wujiheng.tw@gmail.com>
Reviewed-on: http://openocd.zylin.com/4292
Tested-by: jenkins
Reviewed-by: Hsiangkai Wang <hsiangkai@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/tcl: Distinguish between sectors and blocks in status messages

Use the right word in flash protect command status messages based on
whether the target bank defines num_prot_blocks. Minor message style
tidy-up.

Change-Id: I5f40fb5627422536ce737f242fbf80feafe7a1fc
Signed-off-by: Dominik Peklo <dom.peklo@gmail.com>
Reviewed-on: http://openocd.zylin.com/4573
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* drivers: cmsis-dap: pull up common connect code

Just a minor deduplication

Change-Id: Idd256883e5f6d4bd4dcc18462dd5468991f507b3
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3403
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* drivers: cmsis-dap: Print version info when available

No need to wait until after connecting, might help diagnose part information by
printing earlier.

Change-Id: I51eb0d584be306baa811fbeb1ad6a604773e602c
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3404
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor: add support for TI MSP432 devices

Added msp432 flash driver to support the TI MSP432P4x and
MSP432E4x microcontrollers. Implemented the flash algo
helper as used in the TI debug and flash tools. This
implemention supports the MSP432E4, Falcon, and Falcon 2M
variants. The flash driver automatically detects the
connected variant and configures itself appropriately.
Added command to mass erase device for consistency with
TI tools and added command to unlock the protected BSL
region.

Tested using MSP432E401Y, MSP432P401R, and MSP432P4111
LaunchPads.
Tested with embedded XDS110 debug probe in CMSIS-DAP
mode and with external SEGGER J-Link probe.

Removed ti_msp432p4xx.cfg file made obsolete by this
patch.
Change-Id: I3b29d39ccc492524ef2c4a1733f7f9942c2684c0
Signed-off-by: Edward Fewell <efewell@ti.com>
Reviewed-on: http://openocd.zylin.com/4153
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/at91sam4: fix sam4sa16c flash banks and its gpnvms count

There was already a github fork that had this fixed, but as we try
to use the latest, non-modified version of all software we use,
I would like to have this fix in the next releases of OpenOCD so
that if people uses $packagemanager, they will not have issues flashing
the last part of the flash of sam4sa16c chips.

Additionally, I've added some more logging related to the flash
bank that was used, and the chip ID that was detected.

Change-Id: I7ea5970105906e4560b727e46222ae9a91e41559
Signed-off-by: Erwin Oegema <blablaechthema@hotmail.com>
Reviewed-on: http://openocd.zylin.com/4599
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Tested-by: jenkins

* flash/nor/stm32lx: Add revision 'V' for STM32L1xx Cat.3 devices

Change-Id: Ic92b0fb5b738af3bec79ae335876aa9e26f5f4cd
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/4600
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* Avoid null target->semihosting references.

The new common semihosting code introduced a bug,
in certain conditions target->semihosting was
used without semihosting being initialised.

The solution was to explicitly test for
target->semihosting before dereferencing it.

Change-Id: I4c83e596140c68fe4ab32e586e51f7e981a40798
Signed-off-by: Liviu Ionescu <ilg@livius.net>
Reviewed-on: http://openocd.zylin.com/4603
Tested-by: jenkins
Reviewed-by: Jonathan Larmour <jifl@eCosCentric.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* nrf5: Add HWID 0x139 (52832 rev E0)

Change-Id: I71b7471ccfcb8fcc6de30da57ce4165c7fb1f73f
Signed-off-by: James Jacobsson <slowcoder@gmail.com>
Reviewed-on: http://openocd.zylin.com/4604
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target: Fix segfault for 'mem2array'

Call 'mem2array' without arguments to reproduce the segmentation
fault.

Change-Id: I02bf46cc8bd317abbb721a8c75d7cbfac99eb34e
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/4534
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Christopher Head <chead@zaber.com>

* target/armv7m_trace: Fix typo in enum

Change-Id: I6364ee5011ef2d55c59674e3b97504a285de0cb2
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/3904
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/armv7m_trace: Use prefix for enums

Change-Id: I3f199e6053146a1094d96b98ea174b41bb021599
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/3905
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/aarch64: Call aarch64_init_debug_access() earlier in aarch64_deassert_reset()

On Renesas R-Car, calling 'reset halt' and 'reset init' always made DAP inaccessible. Calling 'reset' and 'halt' seperatly worked fine.
The only differences seems to be the point in time when aarch64_init_debug_access() is called. This patch aligns the behaviour.

Change-Id: I2296c65e48414a7d9846f12a395e5eca315b49ca
Signed-off-by: Dennis Ostermann <dennis.ostermann@renesas.com>
Reviewed-on: http://openocd.zylin.com/4607
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* server: Improve signal handling under Linux

Commit 5087a955 added custom signal handlers for the openocd
server process.

Before this commit, when openocd is run as a background process
having the same controlling terminal as gdb, Control-C would be
handled by gdb to stop target execution and return to the gdb prompt.

However, after commit 5087a955, the SIGINT caused by pressing
Control-C also terminates openocd, effectively crashing the
debugging session.  The only way to avoid this is run openocd in
a different controling terminal or to detach openocd from its
controlling terminal,
thus losing all job control for the openocd process.

This patch improves the server's handling of POSIX signals:
1) Keyboard generated signals (INT and QUIT) are ignored
   when server process has is no controlling terminal.
2) SIGHUP and SIGPIPE are handled to ensure that .quit functions
   for each interface are called if user's logs out of X
   session or there is a network failure.

SIG_INT & SIG_QUIT still stop openocd
when it is running in the foreground.

Change-Id: I03ad645e62408fdaf4edc49a3550b89b287eda10
Signed-off-by: Brent Roman <genosensor@gmail.com>
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/3963
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* armv7a: read ttbcr and ttb0/1 at every entry in debug state

Commit bfc5c764df avoids reading
ttbcr and ttb0/1 at every virt2phys translation by caching them,
and it updates the cached values in armv7a_arch_state().
But the purpose of any (*arch_state)() method, thus including
armv7a_arch_state(), is to only print out and inform the user
about some architecture specific status.
Moreover, to reduce the verbosity during a GDB session, the
method (*arch_state)() is not executed anymore at debug state
entry (check use of target->verbose_halt_msg in src/openocd.c),
thus the state of translation table gets out-of-sync triggering
	Error: Address translation failure
or even using a wrong address in the memory R/W operation.

In addition, the commit above breaks the case of armv7r by
calling armv7a_read_ttbcr() unconditionally.

Fixed by moving in cortex_a_post_debug_entry() the call to
armv7a_read_ttbcr() on armv7a case only.
Remove the call to armv7a_read_ttbcr() in armv7a_identify_cache()
since it is (conditionally) called only in the same procedure
cortex_a_post_debug_entry().

Fixes: bfc5c764df ("armv7a: cache ttbcr and ttb0/1 on debug
state entry")
Change-Id: Ifc20eca190111832e339a01b7f85d28c1547c8ba
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4601
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* Avoid dereferencing NULL pointer.

If a NULL pointer is passed, don't attempt to increment it.  This avoids
passing the now not-NULL pointer on and eventually segfaulting.  Also
remove some unnecessary temporary variables.

Change-Id: I268e225121aa283d59179bfae407ebf6959d3a4e
Signed-off-by: Darius Rad <darius@bluespec.com>
Reviewed-on: http://openocd.zylin.com/4550
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* Remove FSF mailing address.

Checkpatch complains about this (FSF_MAILING_ADDRESS).

Change-Id: Ib46a7704f9aed4ed16ce7733d43c58254a094149
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/4559
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>

* drivers: cmsis_dap_usb: implement cmd JTAG_TMS

Simply add a wrapper around cmsis_dap_cmd_DAP_SWJ_Sequence()

Change-Id: Icf86f84b24e9fec56e2f9e155396aac34b0e06d2
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4517
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>

* arm_adi_v5: put SWJ-DP back to JTAG mode at exit

When SWD mode is used, current OpenOCD code left the SWJ-DP in
SWD mode at exit. Also, current code is unable to switch back the
SWJ-DP in JTAG at next run, thus a power cycle of both target and
interface is required in order to run OpenOCD in JTAG mode again.

Put the SWJ-DP back to JTAG mode before exit from OpenOCD.

Use switch_seq(SWD_TO_JTAG) instead of dap_to_jtag(), because the
latter is not implemented on some interfaces. This is aligned
with the use of switch_seq(JTAG_TO_SWD) in swd_connect().

Change-Id: I55d3faebe60d6402037ec39dd9700dc5f17c53b0
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4493
Tested-by: jenkins
Reviewed-by: Bohdan Tymkiv <bhdt@cypress.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* Add RISC-V support.

This supports both 0.11 and 0.13 versions of the debug spec.

Support for `-rtos riscv` will come in a separate commit since it was
easy to separate out, and is likely to be more controversial.

Flash support for the SiFive boards will also come in a later commit.

Change-Id: I1d38fe669c2041b4e21a5c54a091594aac3e2190
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/4578
Tested-by: jenkins
Reviewed-by: Liviu Ionescu <ilg@livius.net>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* usb_blaster: Don't unnecessarily go through DR-/IR-Pause

There is no need to pass through DR-/IR-Pause after a scan if we want to
go to DR-/IR-Update. We just have to skip the first step of the path to
the end state because we already did that step when shifting the last
bit.

v2:
 - Fix comments as remarked in review of v1

Change-Id: I3c10f02794b2233f63d2150934e2768430873caa
Signed-off-by: Daniel Glöckner <daniel-gl@gmx.net>
Reviewed-on: http://openocd.zylin.com/4245
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* cortex_a: fix virt2phys when mmu is disabled

When the MMU is not enabled on debug state entry, virt2phys cannot
perform a translation since it is unknown whether a valid MMU
configuration existed before. In this case, return the virtual
address as physical address.

Change-Id: I6f85a7a5dbc200be1a4b5badf10a1a717f1c79c0
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
Reviewed-on: http://openocd.zylin.com/4480
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* drivers: cmsis-dap: print serial if available

Helpful for sanity checking connections

Change-Id: Ife0d8b4e12d4c03685aac8115c9739a4c1e994fe
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3405
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: make a variable local

The vec_ids variable is not referenced anywhere other than the vector
catch command handler. Make it local to that function.

Change-Id: Ie5865e8f78698c19a09f0b9d58269ced1c9db440
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4606
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_a: fix compile error for uninitialized variable

Commit ad6c71e151 introduced the
variable "mmu_enabled" whose pointer is passed to cortex_a_mmu()
that initialises it.
This initialization is not visible to the compiler that issue
a compile error.
The same situation is common across the same file and the usual
workaround is to initialize it to zero; thus the same fix i
applied here.

Ticket: https://sourceforge.net/p/openocd/tickets/197/
Fixes: commit ad6c71e151 ("cortex_a: fix virt2phys when mmu is disabled")
Change-Id: I77dec41acdf4c715b45ae37b72e36719d96d9283
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4619
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* mips_m4k: add optional reset handler

In some cases by using SRST we can't halt CPU early enough. And
option PrRst is not available too. In this case the only way is
to set BOOT flag over EJTAG and reset CPU or SoC from CPU itself.
For example by writing to some reset register.

This patch is providing possibility to use user defined reset-assert
handler which will be enabled only in case SRST is disabled. It is
needed to be able switch between two different reset variants on run
time.

Change-Id: I6ef98f1871ea657115877190f7cc7a5e8f3233e4
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4404
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target: add config for Qualcomm QCA4531

The QCA4531 is a two stream (2x2) 802.11b/g/n single-band programmable
Wi-Fi System-on-Chip (SoC) for the Internet of Things (IoT).
https://www.qualcomm.com/products/qca4531

Change-Id: I58398c00943b005cfaf0ac1eaad92d1fa4e2cba7
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4405
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/board: add config for 8devices LIMA board

More information about this board can be found here:
https://www.8devices.com/products/lima

Change-Id: Id35a35d3e986630d58d37b47828870afd107cc6a
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4406
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target|board: move common AR9331 code to atheros_ar9331.cfg

The ar9331_25mhz_pll_init and ar9331_ddr1_init routines
can be used not only for TP-Link MR3020 board,
so move them to the common atheros_ar9331.cfg file.

Change-Id: I04090856b08151d6bb0f5ef9cc654efae1c81835
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
Reviewed-on: http://openocd.zylin.com/2999
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target/atheros_ar9331: add DDR2 helper

this helper works on many different boards, so it is
good to have it in target config

Change-Id: I068deac36fdd73dbbcedffc87865cc5b9d992c1d
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4422
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target/atheros_ar9331: add documentation and extra helpers

Sync it with experience gathered on Qualcomm QCA4531 SoC. This
chips are in many ways similar.

Change-Id: I06b9c85e5985a09a9be3cb6cc0ce3b37695d2e54
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4423
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/board: add DPTechnics DPT-Board-v1

it is Atheros AR9331 based IoT dev board.

Change-Id: I6fc3cdea1bef49c53045018ff5acfec4d5610ba6
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4424
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* fpga/altera-10m50: add all device id

add all currently know Intel (Alter) MAX 10 device ids

Change-Id: I6a88fef222c8e206812499d41be863c3d89fa944
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-on: http://openocd.zylin.com/4598
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target|board: Add Intel (Altera) Arria 10 target and related board

Target information about this SoC can be found here:
https://www.altera.com/products/fpga/arria-series/arria-10/overview.html

Achilles Instant-Development Kit Arria 10 SoC SoM:
https://www.reflexces.com/products-solutions/development-kits/arria-10/achilles-instant-development-kit-arria-10-soc-som

Change-Id: Id78c741be6a8b7d3a70f37d41088e47ee61b437a
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-on: http://openocd.zylin.com/4583
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/riscv: fix compile error with gcc 8.1.1

Fix compile error:
src/target/riscv/riscv-011.c: In function ‘slot_offset’:
src/target/riscv/riscv-011.c:238:4: error: this statement may fall through
 [-Werror=implicit-fallthrough=]
    switch (slot) {
    ^~~~~~
src/target/riscv/riscv-011.c:243:3: note: here
   case 64:
   ^~~~

Fixes: a51ab8ddf6 ("Add RISC-V support.")
Change-Id: I7fa86b305bd90cc590fd4359c3698632d44712e5
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4618
Tested-by: jenkins
Reviewed-by: Jiri Kastner <cz172638@gmail.com>
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-by: Tim Newsome <tim@sifive.com>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* server: explicitly call "shutdown" when catch CTRL-C or a signal

Every TCL command can be renamed (or deleted) and then replaced by
a TCL proc that has the same name of the original TCL command.
This can be used either to completely replace an existing command
or to wrap the original command to extend its functionality.
This applies also to the OpenOCD command "shutdown" and can be
useful, for example, to set back some default value to the target
before quitting OpenOCD.
E.g. (TCL code):
	rename shutdown original_shutdown
	proc shutdown {} {
		puts "This is my implementation of shutdown"
		# my own stuff before exit OpenOCD
		original_shutdown
	}

Unfortunately, sending a signal (or pressing CTRL-C) to terminate
OpenOCD doesn't trigger calling the original "shutdown" command
nor its (eventual) replacement.

Detect if the main loop is terminated by an external signal and
in such case execute explicitly the command "shutdown".
Replace with enum the magic numbers assumed by "shutdown_openocd".

Please notice that it's possible to write a custom "shutdown" TCL
proc that does not call the original "shutdown" command. This is
useful, for example, to prevent the user to quit OpenOCD by typing
"shutdown" in the telnet session.
Such case will not prevent OpenOCD to terminate when receiving a
signal; OpenOCD will quit after executing the custom "shutdown"
command.

Change-Id: I86b8f9eab8dbd7a28dad58b8cafd97caa7a82f43
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4551
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* zy1000: fix compile error with gcc 8.1.1

The fall-through comment is not taken in consideration by gcc 8.1.1
because it is inside the braces of a C-code block.

Move the comment outside the C block.

Change-Id: I22d87b2dee109fb8bcf2071ac55fdf7171ffcf4b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4614
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/tcl.c: fix flash bank bounds check in 'flash fill' command handler

Steps to reproduce ( STM32F103 'Blue Pill', 128KiB of flash ):
> flash fillh 0x0801FFFE 00 1
wrote 2 bytes to 0x0801fffe in 0.019088s (0.102 KiB/s)
> flash fillw 0x0801FFFE 00 1
Error: stm32f1x.cpu -- clearing lockup after double fault
Error: error waiting for target flash write algorithm
Error: error writing to flash at address 0x08000000 at offset 0x0001fffe

Change-Id: I145092ec5e45bc586b3df48bf37c38c9226915c1
Signed-off-by: Bohdan Tymkiv <bhdt@cypress.com>
Reviewed-on: http://openocd.zylin.com/4516
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/arm_adi_v5: add command "dpreg"

For very low level debug or development around DAP, it is useful
to have direct access to DP registers.

Add command "dpreg" by mimic the syntax of the existing "apreg"
command:
	$dap_name dpreg reg [value]

Change-Id: Ic4ab451eb5e74453133adee61050b4c6f656ffa3
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4612
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* nrf5: add free_driver_priv

Change-Id: I429a9868deb0c4b51f47a4bbad844bdc348e8d21
Signed-off-by: Jim Paris <jim@jtan.com>
Reviewed-on: http://openocd.zylin.com/4608
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* rtos: add support for NuttX

This patch introduces RTOS support for NuttX. Currently,
only ARM Cortex-M (both FPU and FPU-less) targets are supported.

To use, add the following lines to ~/.gdbinit.

define hookpost-file
  eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid
  eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs
  eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state
  eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name
  eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name)
end

And please make sure the above values are the same as in
src/rtos/nuttx_header.h

Change-Id: I2aaf8644d24dfb84b500516a9685382d5d8fe48f
Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
Signed-off-by: Masatoshi Tateishi <Masatoshi.Tateishi@jp.sony.com>
Signed-off-by: Nobuto Kobayashi <Nobuto.Kobayashi@sony.com>
Reviewed-on: http://openocd.zylin.com/4103
Tested-by: jenkins
Reviewed-by: Alan Carvalho de Assis <acassis@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* server/server: Add ability to remove services

Add the ability to remove services while OpenOCD is running.

Change-Id: I4067916fda6d03485463fa40901b40484d94e24e
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/4054
Tested-by: jenkins
Reviewed-by: Fredrik Hederstierna <fredrik@hederstierna.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: fix incorrect comment

The code sets C_MASKINTS if that bit is not already set (correctly). Fix
the comment to agree.

Change-Id: If4543e2660a9fa2cdabb2d2698427a6c8d9a274c
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4620
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/target/stm32f0x: Allow overriding the Flash bank size

Copy & paste from another stm32 target.

Change-Id: I0f6cbcec974ce70c23c1850526354106caee1172
Signed-off-by: Dominik Peklo <dom.peklo@gmail.com>
Reviewed-on: http://openocd.zylin.com/4575
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/target: add Allwinner V3s SoC support

Change-Id: I2459d2b137050985b7301047f9651951d72d9e9e
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4427
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/arm_adi_v5: allow commands apsel and apcsw during init phase

The current implementation of apsel cannot be executed during the
initialization phase because it queries the DAP AP to retrieve and
print the content of IDR register, and the query is only possible
later on during the exec phase.
But IDR information is already printed by the dedicated command
apid, making redundant printing it by apsel too.
Being unable to run apsel during initialization, makes also apcsw
command (that depends on apsel) not usable in such phase.

Modify the command apsel to only set the current AP, without making
any transfer to the (possibly not initialized yet) DAP. When run
without parameters, just print the current AP number.
Change mode to COMMAND_ANY to apsel and to apcsw.

Change-Id: Ibea6d531e435d1d49d782de1ed8ee6846e91bfdf
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4624
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_a: allow command dacrfixup during init phase

There is no reason to restrict the command "cortex_a dacrfixup"
to the EXEC phase only.
Change the command mode to ANY so the command can be used in
the initialization phase too.

Change-Id: I498cc6b2dbdc48b3b2dd5f0445519a51857b295f
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4623
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/armv7a_cache: add gdb keep-alive and fix a missing dpm finish

Depending on range size, the loop on cache operations can take quite
some time, causing gdb to timeout.

Add keep-alive to prevent gdb to timeout.
Add also a missing dpm->finish() to balance dpm->prepare().

Change-Id: Ia87934b1ec19a0332bb50e3010b582381e5f3685
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4627
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* Add detail to `wrong register size` error.

Signed-off-by: Tim Newsome <tim@sifive.com>
Change-Id: Id31499c94b539969970251145e42c89c943fd87c
Reviewed-on: http://openocd.zylin.com/4577
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* doc: fix typo in cortex_m maskisr command

Change-Id: I37795c320ff7cbf6f2c7434e03b26dbaf6fc6db4
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4621
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: restore C_MASKINTS after reset

The cortex_m maskisr user-facing setting is not changed across a target
reset. However, the in-core C_MASKINTS bit was always cleared as part of
reset processing, meaning that a cortex_m maskisr on setting would not
be respected after a reset. Set C_MASKINTS based on the user-facing
setting value rather than always clearing it after reset.

Change-Id: I5aa5b9dfde04a0fb9c6816fa55b5ef1faf39f8de
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4605
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/board: update all uses of interface/stlink-v2-1 to interface/stlink

Change-Id: I5e27e84d022f73101376e8b4a1bdc65f58fd348a
Signed-off-by: Cody P Schafer <openocd@codyps.com>
Reviewed-on: http://openocd.zylin.com/4456
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/riscv/riscv-011: fix compile warning about uninitialized variable

In MSYS2 MinGW 64-bit
git clone git://git.code.sf.net/p/openocd/code openocd
$ gcc --version
gcc.exe (Rev1, Built by MSYS2 project) 8.2.0
./bootstrap
./configure --prefix=
$ cat config.status | grep CFLAGS
CFLAGS='-g -O2'
make bindir = "bin-x64"

depbase=`echo src/target/riscv/riscv-011.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I.  -D__USE_MINGW_ANSI_STDIO -I./src -I./src -I./src/helper -DPKGDATADIR=\"/mingw64/share/openocd\" -DBINDIR=\"bin-x64\" -I./jimtcl -I./jimtcl  -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Werror -g -O2 -MT src/target/riscv/riscv-011.lo -MD -MP -MF $depbase.Tpo -c -o src/target/riscv/riscv-011.lo src/target/riscv/riscv-011.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -D__USE_MINGW_ANSI_STDIO -I./src -I./src -I./src/helper -DPKGDATADIR=\"/mingw64/share/openocd\" -DBINDIR=\"bin-x64\" -I./jimtcl -I./jimtcl -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Werror -g -O2 -MT src/target/riscv/riscv-011.lo -MD -MP -MF src/target/riscv/.deps/riscv-011.Tpo -c src/target/riscv/riscv-011.c -o src/target/riscv/riscv-011.o
src/target/riscv/riscv-011.c: In function 'poll_target':
src/target/riscv/riscv-011.c:1799:6: error: 'reg' may be used uninitialized in this function [-Werror=maybe-uninitialized]
      reg_cache_set(target, reg, ((data & 0xffffffff) << 32) | value);
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/target/riscv/riscv-011.c:1686:17: note: 'reg' was declared here
    unsigned int reg;
                 ^~~
cc1.exe: all warnings being treated as errors
make[2]: *** [Makefile:3250: src/target/riscv/riscv-011.lo] Error 1

Change-Id: I6996dcb866fbace26817636f4bedba09510a087f
Signed-off-by: Svetoslav Enchev <svetoslav.enchev@gmail.com>
Reviewed-on: http://openocd.zylin.com/4635
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Tim Newsome <tim@sifive.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* max32xxx: Support for MAX32XXX devices.

Adding flash programming support for Maxim Integrated MAX32XXX
devices.

Change-Id: I5b0f57a885f9d813240e4bc2d9f765b743e1cfc3
Signed-off-by: Kevin Gillespie <kgills@gmail.com>
Reviewed-on: http://openocd.zylin.com/3543
Tested-by: jenkins
Reviewed-by: Ismail H. KOSE <ihkose@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* flash/at91sam4: run probe just once

Reread registers in sam4_GetInfo()

Change-Id: I3b023b3e642a9c052b5c41673d196317f7e7f2e3
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4609
Tested-by: jenkins
Reviewed-by: Erwin Oegema <blablaechthema@hotmail.com>
Reviewed-by: Svetoslav Enchev <svetoslav.enchev@gmail.com>

* flash/at91sam4: emit flash bank info

Change related LOG_INFO to LOG_DEBUG

Change-Id: I0c09b1ec83da631b26980dc8632b9031fe2921a3
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4610
Tested-by: jenkins
Reviewed-by: Erwin Oegema <blablaechthema@hotmail.com>
Reviewed-by: Svetoslav Enchev <svetoslav.enchev@gmail.com>

* flash/at91sam4: set wait states only once per write

Read-modify-write setting of FMR register requires an USB turnaround.
Setting FMR before each page write is not necessary and decreases the
write speed.

Change-Id: I67844c898aaf117f155c762c979840b603c767ed
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4611
Tested-by: jenkins
Reviewed-by: Svetoslav Enchev <svetoslav.enchev@gmail.com>

* flash/at91sam4: fix clang static analyzer warning

Change-Id: I5e5319d855c868adfa012f68086f7f809ec5a069
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4639
Tested-by: jenkins

* rtos/linux.c: fix clang static analyzer warning

Remove sizeof(int64_t) from string size computation.

Change-Id: I029b394df5d62a2594a723c4c0e13608b3423b9b
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4640
Tested-by: jenkins

* target: armv8: Ensure target is halted for virt2phys

Othewise the error reported as

    Timeout waiting for dpm prepare

Change-Id: Ieed2fdcd94ae4e877a749df3eec07a01dbf80b10
Closes: https://sourceforge.net/p/openocd/tickets/201/
Found-by: Matthias Welwarsky <matthias@welwarsky.de>
Signed-off-by: Guido Günther <guido.gunther@puri.sm>
Reviewed-on: http://openocd.zylin.com/4647
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* adi_v5: enforce check on AP number value

The AP number value is restricted in 8 bits unsigned by ADI-v5
specification. Nevertheless, an "invalid" value is used by
target cortex-m to force an automatic detection of the AP.

Replace magic numbers by using new macros for AP max number and
for the value of AP invalid.
Check the value passed through -ap-num flag during configuration.

Change-Id: Ic19a367db0ab11c0ebd070750eca0647d25279a5
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4668
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* gdb_server: add per target option "-gdb-port"

The argument passed to global config command "gdb_port" is usually,
but not always, a TCP port number. In case of multiple targets, this
numeric value is used as the first port of a set of consecutive TCP
ports assigned one per target.
If the argument is not a numeric value (e.g. "pipe", "disabled", ...)
then incrementing it for the next target has no sense.

Add the option "-gdb-port number" to the commands "target create" and
"$target_name configure" to override, for the specific target, the
general global configuration.

This permits to use a per target "-gdb-port disabled", when no gdb
port is required for that specific target.

It also makes possible to choose a custom TCP port number for each
target, overriding the usual sequence of consecutive port numbers.

Change-Id: I3b9a1910b28ab4bc757e839d0e5d08ffc29f7ab4
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4530
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* libusb: return oocd error values

keep same return style where possible

Change-Id: I2f9b85dbc307a483609f76a84de77e3c74d346c7
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-on: http://openocd.zylin.com/4588
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* rtos-helpers: fix minor typo in uC/OS-III helper

This patch corrects a spelling error in uCOS-III-openocd.c.

Change-Id: I6d1923ff1f5e6361358c45cec3dd6c08ca9ccef0
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4659
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* flash/stm32f2x: add stm32f7 revision Z identification

Signed-off-by: Cody P Schafer <openocd@codyps.com>
Change-Id: Ia0169514d494bae2a98d92ebc97c8eccc10bc6c4
Reviewed-on: http://openocd.zylin.com/4657
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/mem_ap: generic mem-ap target

This pseudo target allows attaching to any access point on the DAP at the
MEM-AP level and read and write addresses on the connected bus. For
example, one can create a mem_ap target on the APB-AP and read and write
registers of debug components directly. This allows many diagnostic
and other features be programmed entirely using TCL, without necessity
of adding drivers to OpenOCD.

Change-Id: I53229ffd68fb0f96fb68be15b0f3a76cc8843c8e
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
Reviewed-on: http://openocd.zylin.com/4002
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Leonard Crestez <cdleonard@gmail.com>

* interface: adapter configuration for FTDI C232HM

This patch adds support for the C232HM-DDSL-0 and C232HM-EDSL-0
FT232H-based cables from FTDI. For more information, see:
http://www.ftdichip.com/Products/Cables/USBMPSSE.htm

Change-Id: Ic97423eb1e2f6b5ebae04943cd5cce86f38771d5
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4081
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* ftdi: extend ftdi_location format

To existing <bus>:<port>,<port> format add <bus>-<port>.<port> support.
The last format is used by kernel and other drivers.

Change-Id: I6528970d3af4f6a8bf7b27a0f7a763b5957fdf2b
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-on: http://openocd.zylin.com/4631
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/cortex_a: poll all targets in SMP node after halt

The periodic poll scans all the targets in the same order they
have been declared in the configuration file.
When one target in a SMP node halts, the transition is detected
in the following poll and this triggers a halt request to all the
other cores of the SMP node.
The targets that will be polled afterwards will be identified as
"halted", but the targets already scanned will remain as
"running" until the next periodic poll.
This creates a race condition with GDB; GDB sets the breakpoints
when runs the target and removes them as soon as the target is
halted. When it receives the halt event, it starts removing the
breakpoints and fails on the targets that are still reported as
"running".

Fixed by polling all the targets in the SMP node before informing
GDB about the halt event.
This implementation is almost copy/paste from the one in aarch64.

Change-Id: Id2bd99f1e56b014e48e9e34ccb891b4219c518f8
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4622
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Emcraft imx8 SOM BSB support

Tested with Olimex ARM-USB-TINY-H adapter

Simple commands work fine but there are currently issues when attaching
remote gdb or running virt2phys:

    https://sourceforge.net/p/openocd/tickets/201/

Change-Id: I86ccf1d93c5d23870bb522f92b3e2af190d529e8
Signed-off-by: Guido Günther <agx@sigxcpu.org>
Reviewed-on: http://openocd.zylin.com/4646
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* arm_adi_v5: remove useless cast to int

The field ap_num in struct adiv5_private_config is already of type int.
Casting it to type int has no sense.

Change-Id: Ida642e808c02591bb58609425eccd096d404e2c4
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4666
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nrf5: time-based timeout waiting for flash controller

Change-Id: Id214df154dc359ca130c8d8fe1554d106621b9cd
Signed-off-by: Kai Geissdoerfer <kai.geissdoerfer@tu-dresden.de>
Reviewed-on: http://openocd.zylin.com/4648
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nrf5: support for nRF52840 Q1AAC0

Change-Id: Id3280dadece84e1d68544936e44d506c7930a55d
Signed-off-by: Kai Geissdoerfer <kai.geissdoerfer@tu-dresden.de>
Reviewed-on: http://openocd.zylin.com/4649
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* doc: fix use of deprecated config file in the example

Commit 31c58c139d ("jtag: drivers: stlink: handle all versions with
single config") deprecates the use of "interface/stlink-v2-1.cfg" in
favor of a unique config file "interface/stlink.cfg".

Update the example in the documentation.

Change-Id: I1aed7c70e15f4edb4f81a3ee8e3bce575fde873b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4667
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* Added support for STM32L4X option bytes writing.

Enables the programming of Write protection lock bits.

- Updated/re-factored with option_read, option_write and option_load commands.

Change-Id: I86358c7eb1285c3c0baac1564e46da8ced5fd025
Signed-off-by: Thomas Søhus <tls@ceepro.dk>
Reviewed-on: http://openocd.zylin.com/4654
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* Clarify what exactly the RISC-V code supports.

Change-Id: I8da657426cc52c738ab41bfb0164cbc6721c0aef
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/4655
Tested-by: jenkins
Reviewed-by: Philipp Guehring <pg@futureware.at>
Reviewed-by: Liviu Ionescu <ilg@livius.net>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: fix typo

The subunit of the debug unit is called the Flash Patch and Breakpoint
unit, abbreviated (by ARM no less) as FPB, not FBP.

Change-Id: Ia2f08470da705f0f1518feeca878f0f500507308
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4675
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* contrib/60-openocd.rules: provide hint to reload udev rules

No need to reboot the Linux box when new rules are added to udev.
Suggest the command in the script header.

Change-Id: Ie95383bfd73914a3d2e2c05d77fa3eb32e68b7e2
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4665
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* target/stm32: make APCSW cacheable

Change-Id: I7c5c9720ded329848647f17db95f845e46c01c19
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4674
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/atsamv: make APCSW cacheable

Change-Id: Ic00d3192642c682f370a6f7f8b70ae29744eb746
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4678
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* gdb_server: avoid gdb server for virtual targets

Virtual targets, like mem_ap, do not or cannot implement the required
functionality to accept a GDB connection. In the case of mem_ap, the
method get_gdb_reg_list() is missing and a following connection from
gdb causes OpenOCD to segfault.
OpenOCD opens a GDB port for each target; it's always possible to
connect, by mistake, GDB to one virtual target.

Add a method to check if the target supports GDB connections (for the
moment just checking if get_gdb_reg_list is implemented).
Skip opening a gdb server for every targets that don't support GDB
connections.

Change-Id: Ia439a43efe1a9adbb1771cd9d252db8ffa32eb9d
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4676
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/target: Add Renesas R-Car Gen3 targets

Add configuration for the Renesas R-Car Generation 3 targets.
These are SoCs with Cortex A57s, A53s, and R7s. All cores
are supported.

Change-Id: I795233210e4f647a1a2a0adea7c058ae98b5db70
Signed-off-by: Adam Bass <adam.bass@renesas.com>
Reviewed-on: http://openocd.zylin.com/4669
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car Salvator-X(S) boards.

Add configuration for the Renesas R-Car Salvator-X
and Renesas R-Car Salvator-XS boards.

Change-Id: I898008f56adb31908d30760f18217583fabf1c51
Signed-off-by: Adam Bass <adam.bass@renesas.com>
Reviewed-on: http://openocd.zylin.com/4670
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* flash/nrf5: support for nRF52810

Change-Id: I01c430bfa593d20ea7a51c90d67052e374d239b3
Signed-off-by: Anders Westrup <anders@westrup.se>
Reviewed-on: http://openocd.zylin.com/4680
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Slowcoder <slowcoder@gmail.com>

* drivers: cmsis-dap: fix connection in JTAG mode

Commit 5aceec2412 ("drivers:
cmsis-dap: pull up common connect code") breaks the driver and it
cannot connect anymore in JTAG mode.
The issue is caused in cmsis_dap_init() by anticipating the call to
cmsis_dap_usb_open(), which then sets cmsis_dap_handle and makes the
following test to always fail.
Actually the original code was quite tricky:
	if (swd_mode)
		do something that also sets cmsis_dap_handle;
	if (cmsis_dap_handle == NULL)
		do something for !swd_mode;

Convert the sequence of tricky "if"s in a single "if-then-else" to
handle clearly the cases swd_mode and !swd_mode.

Change-Id: I359a23bf26a3edc2461f4352daa0be83e78868f7
Fixes: 5aceec2412 ("drivers: cmsis-dap: pull up common connect code")
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4697
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Tested-by: jenkins

* register: support non-existent registers

This patch fixes a number of bugs caused by incomplete support for
non-existent registers. This is needed for targets that provide optional
registers or non-linear register numbers.

Change-Id: I216196e0051f28887a2c3da410959382369eed80
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4113
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* rtos: support gdb_get_register_packet

This patch adds support for p packet responses by targets configured
with RTOS support. This change required moving to a rtos_reg struct,
which is similar to struct reg used by targets, which resulted in
needing to update each stacking with register numbers. This patch also
allows targets with non-linear register numbers to function with RTOSes
as well.

Change-Id: I5b189d74110d6b6f2fa851a67ab0762ae6b1832f
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4121
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* gdb_server: add support for architecture element

This change adds optional support for a target to report architecture
information in the target description to GDB. This is needed by some GDB
implementations to properly support remote target with custom behavior.
More information on the architecture element can be found here:

    https://sourceware.org/gdb/onlinedocs/gdb/Target-Description-Format.html#Target-Description-Format

Change-Id: I57b19cae5ac3496256e4e5cc52cf6526ca5c322d
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4078
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* jtag: make cmd_queue_scan_field_clone public

This patch makes the cmd_queue_scan_field_clone function public. This
permits targets to insert fields without affecting the submitted
scan_field list. This will be used in an upcoming target implementation
that needs to insert additional padding bits.

Change-Id: I8fbd3b9b4e413432471f4f1444048932c8fa189e
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4082
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* esirisc: support eSi-RISC targets

eSi-RISC is a highly configurable microprocessor architecture for
embedded systems provided by EnSilica. This patch adds support for
32-bit targets and also includes an internal flash driver and
uC/OS-III RTOS support. This is a non-traditional target and required
a number of additional changes to support non-linear register numbers
and the 'p' packet in RTOS support for proper integration into
EnSilica's GDB port.

Change-Id: I59d5c40b3bb2ace1b1a01b2538bfab211adf113f
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4660
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* rtos: check symbol list when updating uCOS-III

This patch corrects a crash in uCOS-III on a new GDB connection when
RTOS autodetection is not used. The crash was caused by not checking if
the symbol list had been loaded prior to updating threads.

Change-Id: I64c5133e02fe22fc8d14584cc40d87b49c935b0b
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4719
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* ftdi: demote unhelpful debug messages

Some protocols make use of empty scan fields for optional padding, which
causes the log to fill with unhelpful messages that a field is empty.
The remaining LOG_DEBUG messages in ftdi_execute_scan have been demoted
to DEBUG_JTAG_IO such that these messages are only seen when debugging
JTAG.

Change-Id: I61fd4551411ce851da34e67d003bca5d7a71cd92
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4112
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* tcl: Add support for the Numato Lab Mimas A7 board

The Mimas A7 FPGA board has FTDI FT2232 whose channel B is connected to
Artix-7 FPGA's JTAG interface. Hence, OpenOCD can easily interface with
it via the its ftdi driver interface. Tested to be working great up to
30 MHz.

Change-Id: Ieda015fbc6135bf95ad5a069cbf38650da45911e
Signed-off-by: Rohit Singh <rohit91.2008@gmail.com>
Reviewed-on: http://openocd.zylin.com/4720
Tested-by: jenkins
Reviewed-by: Tim "mithro" Ansell <me@mith.ro>
Reviewed-by: Robert Jordens <jordens@gmail.com>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/arm_adi_v5: fix sync CSW cache on apreg write

Commit 0057c71ab6 updates the OpenOCD
cached values of CSW and TAR registers if these registers are modified
by an apreg command.
The condition to force the update of CSW cache is incorrect and it will
erase the default CSW value.
Moreover, calling mem_ap_setup_csw() does not honor the value requested
in the apreg command because such value is incorrectly bitwise or-ed
with csw_default.

Fix it by updating csw_value, instead of erasing csw_default, and writing
directly in CSW register the new value from the command line.

Change-Id: I40273cb64d22ccfb9b6d3499bd39b586eb60de38
Fixes: 0057c71ab6 ("target/arm_adi_v5: sync CSW and TAR cache on apreg write")
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4679
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/board: Add Arty-S7 Spartan 7 FPGA Development Board

Change-Id: I8bfe780cb67a1777d5112a68e8a9781bfe4f2038
Signed-off-by: William D. Jones <wjones@wdj-consulting.com>
Reviewed-on: http://openocd.zylin.com/4525
Reviewed-by: Robert Jordens <jordens@gmail.com>
Tested-by: jenkins
Reviewed-by: Rohit Singh <rohit91.2008@gmail.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* xilinx-xc7: Add additional IDCODEs.

Add/detect missing IDCODEs for the Spartan 7 family and Artix 25T
and Artix 12T.

Change-Id: Ib6c83c5592e90df1eb8e715e79b279da9a95f9c6
Signed-off-by: William D. Jones <wjones@wdj-consulting.com>
Reviewed-on: http://openocd.zylin.com/4428
Reviewed-by: Robert Jördens
Tested-by: jenkins
Reviewed-by: Rohit Singh <rohit91.2008@gmail.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/cortex_a: fix temporary breakpoint during step

Commit c8926d1457 introduces the
context and hybrid breakpoint types beside existing SW and HW
types. The new field "asid" is non-zero only for these new types.

The commit above did not properly initialize "asid" to 0 for a
temporarily HW breakpoint used during single step. This causes
cortex_a_unset_breakpoint() to identify this breakpoint as of
type "hybrid".

Identified through valgrind error:
Conditional jump or move depends on uninitialised value(s)
Actually valgrind triggers a total of 10 messages about use of
uninitialized variables, but they are all caused by the first
conditional jump bases on "asid != 0".

Fixed by initializing "asid" to 0 in cortex_a_step().

Fixes: c8926d1457 ("cortex_a hybrid & context breakpoints")
Change-Id: Ib674d8457d1e02e10332fd9d73554d13719ef93d
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4613
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* zynq_7000: Add zynqpl_program command

This allows for programming the PL part of the Xilinx Zynq 7000

Change-Id: I89e86c0f381951091f6948c46802d17d7f1f3500
Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Reviewed-on: http://openocd.zylin.com/4177
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/target.c: adding keep_alive() to while loop.

Adding the call to keep_alive() to suppress warnings when
running the async flash algorithm. Issue observed when
loading large pieces of code on slower debuggers.

Change-Id: I7660fa05f68ebd7be07b2ca0a55b0f3b6ae718f3
Signed-off-by: Kevin Gillespie <kgills@gmail.com>
Reviewed-on: http://openocd.zylin.com/4686
Tested-by: jenkins
Reviewed-by: Jesse Marroquin
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* drivers/cmsis-dap: speed up sending multiple HID requests

The performance of CMSIS-DAP in long data transfers was improved substantially in
ef02b69b14. But it not as good as some
other USB/MCU based adapters. Using HID and therefore interrupt endpoint
is slower than USB bulk transfer.

CMSIS-DAP adapter implements multiple HID buffer handling and OpenOCD already
reads number of buffers from info command.

This change adds capability to sumbit more than one HID requests before
driver waits for a HID response. This scenario is used for long transfers only.
Results show about double speed on USB FS and ~140% speed on USB HS:

                                         | w/o this change | with multi HIDrq
-----------------------------------------+-----------------+-----------------
Open source CMSIS-DAP, USB FS, adapter_khz 1000
dump_image ram32k.bin 0x1fffe000 0x8000  |   23.225 KiB/s  |   45.901 KiB/s
load_image ram32k.bin 0x1fffe000         |   23.324 KiB/s  |   46.552 KiB/s

Cypress' Kitprog in CMSIS-DAP mode, USB FS, adapter_khz 1000 (over firmware limit)
dump_image ram64k.bin 0x20000000 0x10000 |   15.537 KiB/s  |   42.558 KiB/s
load_image ram64k.bin 0x20000000         |   15.605 KiB/s  |   43.291 KiB/s

Atmel's EDBG, USB HS, adapter_khz 10000 (#3945 applied)
dump_image ram384k.bin 0x20400000 0x6000 |  248.402 KiB/s  |  345.250 KiB/s
load_image ram384k.bin 0x20400000        |  256.039 KiB/s  |  365.945 KiB/s

Change-Id: I9edbe018086176d357c6aaba5d6b657a5e5e1c64
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4080
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl: target: omit apcsw for hla

When using stlink for CM7 targets we have to rely on its firmware
to do the right thing as direct DAP access is not possible.

Change-Id: Ieee69f4eeea5c911f89f060f31ce86ed043bdfd0
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Reviewed-on: http://openocd.zylin.com/4732
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/at91samd: add SAMR21E19A DID

While on it correct RAM amount of SAMR21x16A devices

Change-Id: Ie9ab9de1551bdceff17af7597a9a2ee41f5aebe0
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4734
Reviewed-by: Eduardo Montoya
Tested-by: jenkins

* arm_adi_v5: do not deactivate power domains while trying to clear sticky error

At OpenOCD start-up the operation of clearing the sticky error in
CTRL/STAT register ignores the current value of the power domains
bits CDBGPWRUPREQ and CSYSPWRUPREQ in the same register and
incorrectly set them to zero.
This abrupt disable does not follow the requirement in IHI0031 to
wait for the acknowledgment of power disabled before continuing.
The power domains are then re-enabled immediately after; it is
possible that such short disable period has passed undetected or
has been tested only on devices that do not implement the power
domains.
Anyway, this sequence is incorrect and can generate unexpected
and hard-to-debug issues while OpenOCD attaches to a running
target that implements power domains.

Anticipate the initialization of dap->dp_ctrl_stat and use it
while clearing the sticky bit. This has the additional effect of
avoiding a power disable in the error recovery part of the
function dap_dp_read_atomic().
Keep the same sequence of read/write in dap_dp_init() to avoid
breaking the initialization of some problematic target.
Add comments to document these choices.

Change-Id: I8d6da788f2dd11909792b5d6b69bc90fbe4df25d
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4677
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* flash/nor/stm32f1x: Use of protection blocks, improved option bytes handling

Handle write protection status in blocks instead of sectors, removing
unnecessary complexity in the process. Now closer to stm32f2x.
Support sequential modification of option bytes by read/modify/write
directly to option bytes area instead of always starting with the
currently loaded bytes from FLASH_OBR/WRPR registers.
Added new command 'options_load' to force re-load of option bytes w/o
having to power cycle target.

Change-Id: I5c76191e29c17a1e11482df06379d10ca8d6d04d
Signed-off-by: Dominik Peklo <dom.peklo@gmail.com>
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4576
Tested-by: jenkins
Reviewed-by: Jan Vojtěch <honza.vojtech@gmail.com>

* target/cortex_a: remove unused code controlled by "fast_reg_read"

The variable fast_reg_read is always zero, causing some code to
never be executed.
Such code try to read the target registers by dumping them in
memory and then reading back the memory through the debugger. But
it is broken due to lack of cache and MMU management.
This code also uses the broken memory_ap access that is going to
be removed soon.

Remove all the code that depends on fast_reg_read not zero.
Add a missing check on arm_dpm_read_current_registers() return.
Keep the unused function cortex_a_dap_write_coreregister_u32()
to balance the used "read" version.

Change-Id: If2ff28a8c49eb0a87dc85207f5431978efd158db
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4746
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/cortex_a: remove buggy memory AP accesses

The armv7m debug port provides a direct access to the CPU memory
bus, allowing the debugger to bypass the CPU for every memory
operation.
The armv7a debug port doesn't offer the same feature, mainly
because CPU caches and MMU makes the direct memory access more
tricky. Nevertheless most SoC with armv7a provide direct memory
access through an AHB bus available on another DAP access port,
different from the debug port.

The original port of cortex_a in OpenOCD was inspired from the
working cortex_m code, and provided optional memory access
through the AHB, if present.
The code for AHB access is problematic and partially buggy due
to incomplete management of cache coherency and missing check of
page boundary during virtual address operations.

With the commit 5d458cf727
("target/mem_ap: generic mem-ap target") we have a clean support
for memory access through system buses connected to DAP AP, which
obsoletes the buggy memory AP hack in cortex_a.

Remove any code that uses the memory AP accesses in cortex_a.

Change-Id: I7cd1f94885e5817448058953e043d8da90dea3cc
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4748
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/arm_dpm: uniform names of exported functions

The name of the function dpm_modeswitch() does not follow the
common style of the other function names in the same file.

Rename it as arm_dpm_modeswitch().

Change-Id: Idebf3c7bbddcd9b3c7b44f8d0dea1e5f7549b0eb
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4756
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/cortex_a: remove duplicate code to read target registers

The functions cortex_a_dap_{read,write}_coreregister_u32() are
duplicate of the functions dpm_{read,write}_reg().

Remove both duplicated functions in cortex_a.c while export only
dpm_read_reg(), since dpm_write_reg() is currently not used.
Rename dpm_read_reg() as arm_dpm_read_reg() to keep uniform the
naming style.

Change-Id: I501bc99dc402039e630c47917a086a0bb382782c
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4747
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Tested-by: jenkins

* armv7a: ARMv7-A MMU tools

factor out mmu-related code from armv7a.c, add a 'dump' command for
page tables.

Change-Id: Ic1ac3c645d7fd097e9d625c7c8302e7065875dd4
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
Reviewed-on: http://openocd.zylin.com/4327
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* jtag/bitq: array boundary overflow

The for loop inside bitq_path_move function is not correct, this will
overflow the cmd->path array and produces an unpredictable result.

Change-Id: I81e3bc9ee6d1dd948acd2fe4c667103ac22bb26f
Signed-off-by: xuguangxiao <szgxxu@qq.com>
Reviewed-on: http://openocd.zylin.com/4733
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/stm32h7x: Fix documentation of reset_config

The stm32h7x.cfg does not specify connect_assert_srst or
connect_deassert_srst in its reset_config. The comment claims that it
will therefore connect in reset. However, per the manual, the default
configuration is actually connect_deassert_srst, not
connect_assert_srst. In actual fact, connect_assert_srst does not work
on the STM32H7 because, while SRST is asserted, everything on the AXI
bus is inaccessible. The CPU core is accessible, but since the
examine-end event handler also pokes at the DBGMCU peripheral, that will
fail in connect_assert_srst mode. So using connect_deassert_srst is
appropriate, so fix the comment accordingly.

Change-Id: If3e32e871fb19cc61183bdf911b7c5efd80b62e2
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4741
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* docs: fix typo in manual

Change-Id: I28717105eb2a907b0cb4b03f4b5ff1f47194413b
Signed-off-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-on: http://openocd.zylin.com/4751
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* README: fix stlink instructions

Since 31c58c139d there is a unified config
for all stlink versions.

Change-Id: Id736063496ecd96e2024ed69dcb67a22c44b80bb
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Reviewed-on: http://openocd.zylin.com/4672
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* Permit null target on TCL connection

In previous versions of OpenOCD, it was possible to connect to the TCL
RPC interface without a current target. In `tcl_new_connection`, the
curent target would be queried by number, and the possibility of a null
current target was handled properly.

In commit bb9d9c6026, the
`get_target_by_num` call was replaced by a `get_current_target` call,
without noticing that `get_current_target` aborts if there is no current
target, whereas `tcl_new_connection` is perfectly able to handle that
situation.

Provide a `get_current_target_or_null` function for use by consumers who
are OK with a null current target, and use it in `tcl_new_connection`.

Change-Id: I06f7e1e149f1169e23c73ba328c7ad9f9425cc2a
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4730
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Franck Jullien <franck.jullien@gmail.com>

* riscv_get_thread_reg_list matches new prototype.

None of the existing test cases cause this function to be called. I
suspect it never gets called, since it is weird to have a function that
only returns the GPRs as a full list of registers.

Change-Id: Ib54f182c1b2fc4dd711c877cb5c9b3e0af77461d

* Fix conflict resolutions.

Change-Id: I5228c308a08ee54530f8c1cadac2afe1c974d41e
2018-11-19 12:46:40 -08:00

6600 lines
172 KiB
C

/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007-2010 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* Copyright (C) 2008, Duane Ellis *
* openocd@duaneeellis.com *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2008 by Rick Altherr *
* kc8apf@kc8apf.net> *
* *
* Copyright (C) 2011 by Broadcom Corporation *
* Evan Hunter - ehunter@broadcom.com *
* *
* Copyright (C) ST-Ericsson SA 2011 *
* michel.jaouen@stericsson.com : smp minimum support *
* *
* Copyright (C) 2011 Andreas Fritiofson *
* andreas.fritiofson@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <helper/time_support.h>
#include <jtag/jtag.h>
#include <flash/nor/core.h>
#include "target.h"
#include "target_type.h"
#include "target_request.h"
#include "breakpoints.h"
#include "register.h"
#include "trace.h"
#include "image.h"
#include "rtos/rtos.h"
#include "transport/transport.h"
#include "arm_cti.h"
/* default halt wait timeout (ms) */
#define DEFAULT_HALT_TIMEOUT 5000
static int target_read_buffer_default(struct target *target, target_addr_t address,
uint32_t count, uint8_t *buffer);
static int target_write_buffer_default(struct target *target, target_addr_t address,
uint32_t count, const uint8_t *buffer);
static int target_array2mem(Jim_Interp *interp, struct target *target,
int argc, Jim_Obj * const *argv);
static int target_mem2array(Jim_Interp *interp, struct target *target,
int argc, Jim_Obj * const *argv);
static int target_register_user_commands(struct command_context *cmd_ctx);
static int target_get_gdb_fileio_info_default(struct target *target,
struct gdb_fileio_info *fileio_info);
static int target_gdb_fileio_end_default(struct target *target, int retcode,
int fileio_errno, bool ctrl_c);
static int target_profiling_default(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds);
/* targets */
extern struct target_type arm7tdmi_target;
extern struct target_type arm720t_target;
extern struct target_type arm9tdmi_target;
extern struct target_type arm920t_target;
extern struct target_type arm966e_target;
extern struct target_type arm946e_target;
extern struct target_type arm926ejs_target;
extern struct target_type fa526_target;
extern struct target_type feroceon_target;
extern struct target_type dragonite_target;
extern struct target_type xscale_target;
extern struct target_type cortexm_target;
extern struct target_type cortexa_target;
extern struct target_type aarch64_target;
extern struct target_type cortexr4_target;
extern struct target_type arm11_target;
extern struct target_type ls1_sap_target;
extern struct target_type mips_m4k_target;
extern struct target_type avr_target;
extern struct target_type dsp563xx_target;
extern struct target_type dsp5680xx_target;
extern struct target_type testee_target;
extern struct target_type avr32_ap7k_target;
extern struct target_type hla_target;
extern struct target_type nds32_v2_target;
extern struct target_type nds32_v3_target;
extern struct target_type nds32_v3m_target;
extern struct target_type or1k_target;
extern struct target_type quark_x10xx_target;
extern struct target_type quark_d20xx_target;
extern struct target_type stm8_target;
extern struct target_type riscv_target;
extern struct target_type mem_ap_target;
extern struct target_type esirisc_target;
static struct target_type *target_types[] = {
&arm7tdmi_target,
&arm9tdmi_target,
&arm920t_target,
&arm720t_target,
&arm966e_target,
&arm946e_target,
&arm926ejs_target,
&fa526_target,
&feroceon_target,
&dragonite_target,
&xscale_target,
&cortexm_target,
&cortexa_target,
&cortexr4_target,
&arm11_target,
&ls1_sap_target,
&mips_m4k_target,
&avr_target,
&dsp563xx_target,
&dsp5680xx_target,
&testee_target,
&avr32_ap7k_target,
&hla_target,
&nds32_v2_target,
&nds32_v3_target,
&nds32_v3m_target,
&or1k_target,
&quark_x10xx_target,
&quark_d20xx_target,
&stm8_target,
&riscv_target,
&mem_ap_target,
&esirisc_target,
#if BUILD_TARGET64
&aarch64_target,
#endif
NULL,
};
struct target *all_targets;
static struct target_event_callback *target_event_callbacks;
static struct target_timer_callback *target_timer_callbacks;
LIST_HEAD(target_reset_callback_list);
LIST_HEAD(target_trace_callback_list);
static const int polling_interval = 100;
static const Jim_Nvp nvp_assert[] = {
{ .name = "assert", NVP_ASSERT },
{ .name = "deassert", NVP_DEASSERT },
{ .name = "T", NVP_ASSERT },
{ .name = "F", NVP_DEASSERT },
{ .name = "t", NVP_ASSERT },
{ .name = "f", NVP_DEASSERT },
{ .name = NULL, .value = -1 }
};
static const Jim_Nvp nvp_error_target[] = {
{ .value = ERROR_TARGET_INVALID, .name = "err-invalid" },
{ .value = ERROR_TARGET_INIT_FAILED, .name = "err-init-failed" },
{ .value = ERROR_TARGET_TIMEOUT, .name = "err-timeout" },
{ .value = ERROR_TARGET_NOT_HALTED, .name = "err-not-halted" },
{ .value = ERROR_TARGET_FAILURE, .name = "err-failure" },
{ .value = ERROR_TARGET_UNALIGNED_ACCESS , .name = "err-unaligned-access" },
{ .value = ERROR_TARGET_DATA_ABORT , .name = "err-data-abort" },
{ .value = ERROR_TARGET_RESOURCE_NOT_AVAILABLE , .name = "err-resource-not-available" },
{ .value = ERROR_TARGET_TRANSLATION_FAULT , .name = "err-translation-fault" },
{ .value = ERROR_TARGET_NOT_RUNNING, .name = "err-not-running" },
{ .value = ERROR_TARGET_NOT_EXAMINED, .name = "err-not-examined" },
{ .value = -1, .name = NULL }
};
static const char *target_strerror_safe(int err)
{
const Jim_Nvp *n;
n = Jim_Nvp_value2name_simple(nvp_error_target, err);
if (n->name == NULL)
return "unknown";
else
return n->name;
}
static const Jim_Nvp nvp_target_event[] = {
{ .value = TARGET_EVENT_GDB_HALT, .name = "gdb-halt" },
{ .value = TARGET_EVENT_HALTED, .name = "halted" },
{ .value = TARGET_EVENT_RESUMED, .name = "resumed" },
{ .value = TARGET_EVENT_RESUME_START, .name = "resume-start" },
{ .value = TARGET_EVENT_RESUME_END, .name = "resume-end" },
{ .name = "gdb-start", .value = TARGET_EVENT_GDB_START },
{ .name = "gdb-end", .value = TARGET_EVENT_GDB_END },
{ .value = TARGET_EVENT_RESET_START, .name = "reset-start" },
{ .value = TARGET_EVENT_RESET_ASSERT_PRE, .name = "reset-assert-pre" },
{ .value = TARGET_EVENT_RESET_ASSERT, .name = "reset-assert" },
{ .value = TARGET_EVENT_RESET_ASSERT_POST, .name = "reset-assert-post" },
{ .value = TARGET_EVENT_RESET_DEASSERT_PRE, .name = "reset-deassert-pre" },
{ .value = TARGET_EVENT_RESET_DEASSERT_POST, .name = "reset-deassert-post" },
{ .value = TARGET_EVENT_RESET_INIT, .name = "reset-init" },
{ .value = TARGET_EVENT_RESET_END, .name = "reset-end" },
{ .value = TARGET_EVENT_EXAMINE_START, .name = "examine-start" },
{ .value = TARGET_EVENT_EXAMINE_END, .name = "examine-end" },
{ .value = TARGET_EVENT_DEBUG_HALTED, .name = "debug-halted" },
{ .value = TARGET_EVENT_DEBUG_RESUMED, .name = "debug-resumed" },
{ .value = TARGET_EVENT_GDB_ATTACH, .name = "gdb-attach" },
{ .value = TARGET_EVENT_GDB_DETACH, .name = "gdb-detach" },
{ .value = TARGET_EVENT_GDB_FLASH_WRITE_START, .name = "gdb-flash-write-start" },
{ .value = TARGET_EVENT_GDB_FLASH_WRITE_END , .name = "gdb-flash-write-end" },
{ .value = TARGET_EVENT_GDB_FLASH_ERASE_START, .name = "gdb-flash-erase-start" },
{ .value = TARGET_EVENT_GDB_FLASH_ERASE_END , .name = "gdb-flash-erase-end" },
{ .value = TARGET_EVENT_TRACE_CONFIG, .name = "trace-config" },
{ .name = NULL, .value = -1 }
};
static const Jim_Nvp nvp_target_state[] = {
{ .name = "unknown", .value = TARGET_UNKNOWN },
{ .name = "running", .value = TARGET_RUNNING },
{ .name = "halted", .value = TARGET_HALTED },
{ .name = "reset", .value = TARGET_RESET },
{ .name = "debug-running", .value = TARGET_DEBUG_RUNNING },
{ .name = NULL, .value = -1 },
};
static const Jim_Nvp nvp_target_debug_reason[] = {
{ .name = "debug-request" , .value = DBG_REASON_DBGRQ },
{ .name = "breakpoint" , .value = DBG_REASON_BREAKPOINT },
{ .name = "watchpoint" , .value = DBG_REASON_WATCHPOINT },
{ .name = "watchpoint-and-breakpoint", .value = DBG_REASON_WPTANDBKPT },
{ .name = "single-step" , .value = DBG_REASON_SINGLESTEP },
{ .name = "target-not-halted" , .value = DBG_REASON_NOTHALTED },
{ .name = "program-exit" , .value = DBG_REASON_EXIT },
{ .name = "undefined" , .value = DBG_REASON_UNDEFINED },
{ .name = NULL, .value = -1 },
};
static const Jim_Nvp nvp_target_endian[] = {
{ .name = "big", .value = TARGET_BIG_ENDIAN },
{ .name = "little", .value = TARGET_LITTLE_ENDIAN },
{ .name = "be", .value = TARGET_BIG_ENDIAN },
{ .name = "le", .value = TARGET_LITTLE_ENDIAN },
{ .name = NULL, .value = -1 },
};
static const Jim_Nvp nvp_reset_modes[] = {
{ .name = "unknown", .value = RESET_UNKNOWN },
{ .name = "run" , .value = RESET_RUN },
{ .name = "halt" , .value = RESET_HALT },
{ .name = "init" , .value = RESET_INIT },
{ .name = NULL , .value = -1 },
};
const char *debug_reason_name(struct target *t)
{
const char *cp;
cp = Jim_Nvp_value2name_simple(nvp_target_debug_reason,
t->debug_reason)->name;
if (!cp) {
LOG_ERROR("Invalid debug reason: %d", (int)(t->debug_reason));
cp = "(*BUG*unknown*BUG*)";
}
return cp;
}
const char *target_state_name(struct target *t)
{
const char *cp;
cp = Jim_Nvp_value2name_simple(nvp_target_state, t->state)->name;
if (!cp) {
LOG_ERROR("Invalid target state: %d", (int)(t->state));
cp = "(*BUG*unknown*BUG*)";
}
if (!target_was_examined(t) && t->defer_examine)
cp = "examine deferred";
return cp;
}
const char *target_event_name(enum target_event event)
{
const char *cp;
cp = Jim_Nvp_value2name_simple(nvp_target_event, event)->name;
if (!cp) {
LOG_ERROR("Invalid target event: %d", (int)(event));
cp = "(*BUG*unknown*BUG*)";
}
return cp;
}
const char *target_reset_mode_name(enum target_reset_mode reset_mode)
{
const char *cp;
cp = Jim_Nvp_value2name_simple(nvp_reset_modes, reset_mode)->name;
if (!cp) {
LOG_ERROR("Invalid target reset mode: %d", (int)(reset_mode));
cp = "(*BUG*unknown*BUG*)";
}
return cp;
}
/* determine the number of the new target */
static int new_target_number(void)
{
struct target *t;
int x;
/* number is 0 based */
x = -1;
t = all_targets;
while (t) {
if (x < t->target_number)
x = t->target_number;
t = t->next;
}
return x + 1;
}
/* read a uint64_t from a buffer in target memory endianness */
uint64_t target_buffer_get_u64(struct target *target, const uint8_t *buffer)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
return le_to_h_u64(buffer);
else
return be_to_h_u64(buffer);
}
/* read a uint32_t from a buffer in target memory endianness */
uint32_t target_buffer_get_u32(struct target *target, const uint8_t *buffer)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
return le_to_h_u32(buffer);
else
return be_to_h_u32(buffer);
}
/* read a uint24_t from a buffer in target memory endianness */
uint32_t target_buffer_get_u24(struct target *target, const uint8_t *buffer)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
return le_to_h_u24(buffer);
else
return be_to_h_u24(buffer);
}
/* read a uint16_t from a buffer in target memory endianness */
uint16_t target_buffer_get_u16(struct target *target, const uint8_t *buffer)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
return le_to_h_u16(buffer);
else
return be_to_h_u16(buffer);
}
/* read a uint8_t from a buffer in target memory endianness */
static uint8_t target_buffer_get_u8(struct target *target, const uint8_t *buffer)
{
return *buffer & 0x0ff;
}
/* write a uint64_t to a buffer in target memory endianness */
void target_buffer_set_u64(struct target *target, uint8_t *buffer, uint64_t value)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
h_u64_to_le(buffer, value);
else
h_u64_to_be(buffer, value);
}
/* write a uint32_t to a buffer in target memory endianness */
void target_buffer_set_u32(struct target *target, uint8_t *buffer, uint32_t value)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
h_u32_to_le(buffer, value);
else
h_u32_to_be(buffer, value);
}
/* write a uint24_t to a buffer in target memory endianness */
void target_buffer_set_u24(struct target *target, uint8_t *buffer, uint32_t value)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
h_u24_to_le(buffer, value);
else
h_u24_to_be(buffer, value);
}
/* write a uint16_t to a buffer in target memory endianness */
void target_buffer_set_u16(struct target *target, uint8_t *buffer, uint16_t value)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
h_u16_to_le(buffer, value);
else
h_u16_to_be(buffer, value);
}
/* write a uint8_t to a buffer in target memory endianness */
static void target_buffer_set_u8(struct target *target, uint8_t *buffer, uint8_t value)
{
*buffer = value;
}
/* write a uint64_t array to a buffer in target memory endianness */
void target_buffer_get_u64_array(struct target *target, const uint8_t *buffer, uint32_t count, uint64_t *dstbuf)
{
uint32_t i;
for (i = 0; i < count; i++)
dstbuf[i] = target_buffer_get_u64(target, &buffer[i * 8]);
}
/* write a uint32_t array to a buffer in target memory endianness */
void target_buffer_get_u32_array(struct target *target, const uint8_t *buffer, uint32_t count, uint32_t *dstbuf)
{
uint32_t i;
for (i = 0; i < count; i++)
dstbuf[i] = target_buffer_get_u32(target, &buffer[i * 4]);
}
/* write a uint16_t array to a buffer in target memory endianness */
void target_buffer_get_u16_array(struct target *target, const uint8_t *buffer, uint32_t count, uint16_t *dstbuf)
{
uint32_t i;
for (i = 0; i < count; i++)
dstbuf[i] = target_buffer_get_u16(target, &buffer[i * 2]);
}
/* write a uint64_t array to a buffer in target memory endianness */
void target_buffer_set_u64_array(struct target *target, uint8_t *buffer, uint32_t count, const uint64_t *srcbuf)
{
uint32_t i;
for (i = 0; i < count; i++)
target_buffer_set_u64(target, &buffer[i * 8], srcbuf[i]);
}
/* write a uint32_t array to a buffer in target memory endianness */
void target_buffer_set_u32_array(struct target *target, uint8_t *buffer, uint32_t count, const uint32_t *srcbuf)
{
uint32_t i;
for (i = 0; i < count; i++)
target_buffer_set_u32(target, &buffer[i * 4], srcbuf[i]);
}
/* write a uint16_t array to a buffer in target memory endianness */
void target_buffer_set_u16_array(struct target *target, uint8_t *buffer, uint32_t count, const uint16_t *srcbuf)
{
uint32_t i;
for (i = 0; i < count; i++)
target_buffer_set_u16(target, &buffer[i * 2], srcbuf[i]);
}
/* return a pointer to a configured target; id is name or number */
struct target *get_target(const char *id)
{
struct target *target;
/* try as tcltarget name */
for (target = all_targets; target; target = target->next) {
if (target_name(target) == NULL)
continue;
if (strcmp(id, target_name(target)) == 0)
return target;
}
/* It's OK to remove this fallback sometime after August 2010 or so */
/* no match, try as number */
unsigned num;
if (parse_uint(id, &num) != ERROR_OK)
return NULL;
for (target = all_targets; target; target = target->next) {
if (target->target_number == (int)num) {
LOG_WARNING("use '%s' as target identifier, not '%u'",
target_name(target), num);
return target;
}
}
return NULL;
}
/* returns a pointer to the n-th configured target */
struct target *get_target_by_num(int num)
{
struct target *target = all_targets;
while (target) {
if (target->target_number == num)
return target;
target = target->next;
}
return NULL;
}
struct target *get_current_target(struct command_context *cmd_ctx)
{
struct target *target = get_current_target_or_null(cmd_ctx);
if (target == NULL) {
LOG_ERROR("BUG: current_target out of bounds");
exit(-1);
}
return target;
}
struct target *get_current_target_or_null(struct command_context *cmd_ctx)
{
return cmd_ctx->current_target_override
? cmd_ctx->current_target_override
: cmd_ctx->current_target;
}
int target_poll(struct target *target)
{
int retval;
/* We can't poll until after examine */
if (!target_was_examined(target)) {
/* Fail silently lest we pollute the log */
return ERROR_FAIL;
}
retval = target->type->poll(target);
if (retval != ERROR_OK)
return retval;
if (target->halt_issued) {
if (target->state == TARGET_HALTED)
target->halt_issued = false;
else {
int64_t t = timeval_ms() - target->halt_issued_time;
if (t > DEFAULT_HALT_TIMEOUT) {
target->halt_issued = false;
LOG_INFO("Halt timed out, wake up GDB.");
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
}
}
return ERROR_OK;
}
int target_halt(struct target *target)
{
int retval;
/* We can't poll until after examine */
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
retval = target->type->halt(target);
if (retval != ERROR_OK)
return retval;
target->halt_issued = true;
target->halt_issued_time = timeval_ms();
return ERROR_OK;
}
/**
* Make the target (re)start executing using its saved execution
* context (possibly with some modifications).
*
* @param target Which target should start executing.
* @param current True to use the target's saved program counter instead
* of the address parameter
* @param address Optionally used as the program counter.
* @param handle_breakpoints True iff breakpoints at the resumption PC
* should be skipped. (For example, maybe execution was stopped by
* such a breakpoint, in which case it would be counterprodutive to
* let it re-trigger.
* @param debug_execution False if all working areas allocated by OpenOCD
* should be released and/or restored to their original contents.
* (This would for example be true to run some downloaded "helper"
* algorithm code, which resides in one such working buffer and uses
* another for data storage.)
*
* @todo Resolve the ambiguity about what the "debug_execution" flag
* signifies. For example, Target implementations don't agree on how
* it relates to invalidation of the register cache, or to whether
* breakpoints and watchpoints should be enabled. (It would seem wrong
* to enable breakpoints when running downloaded "helper" algorithms
* (debug_execution true), since the breakpoints would be set to match
* target firmware being debugged, not the helper algorithm.... and
* enabling them could cause such helpers to malfunction (for example,
* by overwriting data with a breakpoint instruction. On the other
* hand the infrastructure for running such helpers might use this
* procedure but rely on hardware breakpoint to detect termination.)
*/
int target_resume(struct target *target, int current, target_addr_t address,
int handle_breakpoints, int debug_execution)
{
int retval;
/* We can't poll until after examine */
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
target_call_event_callbacks(target, TARGET_EVENT_RESUME_START);
/* note that resume *must* be asynchronous. The CPU can halt before
* we poll. The CPU can even halt at the current PC as a result of
* a software breakpoint being inserted by (a bug?) the application.
*/
retval = target->type->resume(target, current, address, handle_breakpoints, debug_execution);
if (retval != ERROR_OK)
return retval;
target_call_event_callbacks(target, TARGET_EVENT_RESUME_END);
return retval;
}
static int target_process_reset(struct command_context *cmd_ctx, enum target_reset_mode reset_mode)
{
char buf[100];
int retval;
Jim_Nvp *n;
n = Jim_Nvp_value2name_simple(nvp_reset_modes, reset_mode);
if (n->name == NULL) {
LOG_ERROR("invalid reset mode");
return ERROR_FAIL;
}
struct target *target;
for (target = all_targets; target; target = target->next)
target_call_reset_callbacks(target, reset_mode);
/* disable polling during reset to make reset event scripts
* more predictable, i.e. dr/irscan & pathmove in events will
* not have JTAG operations injected into the middle of a sequence.
*/
bool save_poll = jtag_poll_get_enabled();
jtag_poll_set_enabled(false);
sprintf(buf, "ocd_process_reset %s", n->name);
retval = Jim_Eval(cmd_ctx->interp, buf);
jtag_poll_set_enabled(save_poll);
if (retval != JIM_OK) {
Jim_MakeErrorMessage(cmd_ctx->interp);
command_print(NULL, "%s\n", Jim_GetString(Jim_GetResult(cmd_ctx->interp), NULL));
return ERROR_FAIL;
}
/* We want any events to be processed before the prompt */
retval = target_call_timer_callbacks_now();
for (target = all_targets; target; target = target->next) {
target->type->check_reset(target);
target->running_alg = false;
}
return retval;
}
static int identity_virt2phys(struct target *target,
target_addr_t virtual, target_addr_t *physical)
{
*physical = virtual;
return ERROR_OK;
}
static int no_mmu(struct target *target, int *enabled)
{
*enabled = 0;
return ERROR_OK;
}
static int default_examine(struct target *target)
{
target_set_examined(target);
return ERROR_OK;
}
/* no check by default */
static int default_check_reset(struct target *target)
{
return ERROR_OK;
}
int target_examine_one(struct target *target)
{
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START);
int retval = target->type->examine(target);
if (retval != ERROR_OK)
return retval;
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
return ERROR_OK;
}
static int jtag_enable_callback(enum jtag_event event, void *priv)
{
struct target *target = priv;
if (event != JTAG_TAP_EVENT_ENABLE || !target->tap->enabled)
return ERROR_OK;
jtag_unregister_event_callback(jtag_enable_callback, target);
return target_examine_one(target);
}
/* Targets that correctly implement init + examine, i.e.
* no communication with target during init:
*
* XScale
*/
int target_examine(void)
{
int retval = ERROR_OK;
struct target *target;
for (target = all_targets; target; target = target->next) {
/* defer examination, but don't skip it */
if (!target->tap->enabled) {
jtag_register_event_callback(jtag_enable_callback,
target);
continue;
}
if (target->defer_examine)
continue;
retval = target_examine_one(target);
if (retval != ERROR_OK)
return retval;
}
return retval;
}
const char *target_type_name(struct target *target)
{
return target->type->name;
}
static int target_soft_reset_halt(struct target *target)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (!target->type->soft_reset_halt) {
LOG_ERROR("Target %s does not support soft_reset_halt",
target_name(target));
return ERROR_FAIL;
}
return target->type->soft_reset_halt(target);
}
/**
* Downloads a target-specific native code algorithm to the target,
* and executes it. * Note that some targets may need to set up, enable,
* and tear down a breakpoint (hard or * soft) to detect algorithm
* termination, while others may support lower overhead schemes where
* soft breakpoints embedded in the algorithm automatically terminate the
* algorithm.
*
* @param target used to run the algorithm
* @param arch_info target-specific description of the algorithm.
*/
int target_run_algorithm(struct target *target,
int num_mem_params, struct mem_param *mem_params,
int num_reg_params, struct reg_param *reg_param,
uint32_t entry_point, uint32_t exit_point,
int timeout_ms, void *arch_info)
{
int retval = ERROR_FAIL;
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
goto done;
}
if (!target->type->run_algorithm) {
LOG_ERROR("Target type '%s' does not support %s",
target_type_name(target), __func__);
goto done;
}
target->running_alg = true;
retval = target->type->run_algorithm(target,
num_mem_params, mem_params,
num_reg_params, reg_param,
entry_point, exit_point, timeout_ms, arch_info);
target->running_alg = false;
done:
return retval;
}
/**
* Executes a target-specific native code algorithm and leaves it running.
*
* @param target used to run the algorithm
* @param arch_info target-specific description of the algorithm.
*/
int target_start_algorithm(struct target *target,
int num_mem_params, struct mem_param *mem_params,
int num_reg_params, struct reg_param *reg_params,
uint32_t entry_point, uint32_t exit_point,
void *arch_info)
{
int retval = ERROR_FAIL;
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
goto done;
}
if (!target->type->start_algorithm) {
LOG_ERROR("Target type '%s' does not support %s",
target_type_name(target), __func__);
goto done;
}
if (target->running_alg) {
LOG_ERROR("Target is already running an algorithm");
goto done;
}
target->running_alg = true;
retval = target->type->start_algorithm(target,
num_mem_params, mem_params,
num_reg_params, reg_params,
entry_point, exit_point, arch_info);
done:
return retval;
}
/**
* Waits for an algorithm started with target_start_algorithm() to complete.
*
* @param target used to run the algorithm
* @param arch_info target-specific description of the algorithm.
*/
int target_wait_algorithm(struct target *target,
int num_mem_params, struct mem_param *mem_params,
int num_reg_params, struct reg_param *reg_params,
uint32_t exit_point, int timeout_ms,
void *arch_info)
{
int retval = ERROR_FAIL;
if (!target->type->wait_algorithm) {
LOG_ERROR("Target type '%s' does not support %s",
target_type_name(target), __func__);
goto done;
}
if (!target->running_alg) {
LOG_ERROR("Target is not running an algorithm");
goto done;
}
retval = target->type->wait_algorithm(target,
num_mem_params, mem_params,
num_reg_params, reg_params,
exit_point, timeout_ms, arch_info);
if (retval != ERROR_TARGET_TIMEOUT)
target->running_alg = false;
done:
return retval;
}
/**
* Streams data to a circular buffer on target intended for consumption by code
* running asynchronously on target.
*
* This is intended for applications where target-specific native code runs
* on the target, receives data from the circular buffer, does something with
* it (most likely writing it to a flash memory), and advances the circular
* buffer pointer.
*
* This assumes that the helper algorithm has already been loaded to the target,
* but has not been started yet. Given memory and register parameters are passed
* to the algorithm.
*
* The buffer is defined by (buffer_start, buffer_size) arguments and has the
* following format:
*
* [buffer_start + 0, buffer_start + 4):
* Write Pointer address (aka head). Written and updated by this
* routine when new data is written to the circular buffer.
* [buffer_start + 4, buffer_start + 8):
* Read Pointer address (aka tail). Updated by code running on the
* target after it consumes data.
* [buffer_start + 8, buffer_start + buffer_size):
* Circular buffer contents.
*
* See contrib/loaders/flash/stm32f1x.S for an example.
*
* @param target used to run the algorithm
* @param buffer address on the host where data to be sent is located
* @param count number of blocks to send
* @param block_size size in bytes of each block
* @param num_mem_params count of memory-based params to pass to algorithm
* @param mem_params memory-based params to pass to algorithm
* @param num_reg_params count of register-based params to pass to algorithm
* @param reg_params memory-based params to pass to algorithm
* @param buffer_start address on the target of the circular buffer structure
* @param buffer_size size of the circular buffer structure
* @param entry_point address on the target to execute to start the algorithm
* @param exit_point address at which to set a breakpoint to catch the
* end of the algorithm; can be 0 if target triggers a breakpoint itself
*/
int target_run_flash_async_algorithm(struct target *target,
const uint8_t *buffer, uint32_t count, int block_size,
int num_mem_params, struct mem_param *mem_params,
int num_reg_params, struct reg_param *reg_params,
uint32_t buffer_start, uint32_t buffer_size,
uint32_t entry_point, uint32_t exit_point, void *arch_info)
{
int retval;
int timeout = 0;
const uint8_t *buffer_orig = buffer;
/* Set up working area. First word is write pointer, second word is read pointer,
* rest is fifo data area. */
uint32_t wp_addr = buffer_start;
uint32_t rp_addr = buffer_start + 4;
uint32_t fifo_start_addr = buffer_start + 8;
uint32_t fifo_end_addr = buffer_start + buffer_size;
uint32_t wp = fifo_start_addr;
uint32_t rp = fifo_start_addr;
/* validate block_size is 2^n */
assert(!block_size || !(block_size & (block_size - 1)));
retval = target_write_u32(target, wp_addr, wp);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, rp_addr, rp);
if (retval != ERROR_OK)
return retval;
/* Start up algorithm on target and let it idle while writing the first chunk */
retval = target_start_algorithm(target, num_mem_params, mem_params,
num_reg_params, reg_params,
entry_point,
exit_point,
arch_info);
if (retval != ERROR_OK) {
LOG_ERROR("error starting target flash write algorithm");
return retval;
}
while (count > 0) {
retval = target_read_u32(target, rp_addr, &rp);
if (retval != ERROR_OK) {
LOG_ERROR("failed to get read pointer");
break;
}
LOG_DEBUG("offs 0x%zx count 0x%" PRIx32 " wp 0x%" PRIx32 " rp 0x%" PRIx32,
(size_t) (buffer - buffer_orig), count, wp, rp);
if (rp == 0) {
LOG_ERROR("flash write algorithm aborted by target");
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
if (((rp - fifo_start_addr) & (block_size - 1)) || rp < fifo_start_addr || rp >= fifo_end_addr) {
LOG_ERROR("corrupted fifo read pointer 0x%" PRIx32, rp);
break;
}
/* Count the number of bytes available in the fifo without
* crossing the wrap around. Make sure to not fill it completely,
* because that would make wp == rp and that's the empty condition. */
uint32_t thisrun_bytes;
if (rp > wp)
thisrun_bytes = rp - wp - block_size;
else if (rp > fifo_start_addr)
thisrun_bytes = fifo_end_addr - wp;
else
thisrun_bytes = fifo_end_addr - wp - block_size;
if (thisrun_bytes == 0) {
/* Throttle polling a bit if transfer is (much) faster than flash
* programming. The exact delay shouldn't matter as long as it's
* less than buffer size / flash speed. This is very unlikely to
* run when using high latency connections such as USB. */
alive_sleep(10);
/* to stop an infinite loop on some targets check and increment a timeout
* this issue was observed on a stellaris using the new ICDI interface */
if (timeout++ >= 500) {
LOG_ERROR("timeout waiting for algorithm, a target reset is recommended");
return ERROR_FLASH_OPERATION_FAILED;
}
continue;
}
/* reset our timeout */
timeout = 0;
/* Limit to the amount of data we actually want to write */
if (thisrun_bytes > count * block_size)
thisrun_bytes = count * block_size;
/* Write data to fifo */
retval = target_write_buffer(target, wp, thisrun_bytes, buffer);
if (retval != ERROR_OK)
break;
/* Update counters and wrap write pointer */
buffer += thisrun_bytes;
count -= thisrun_bytes / block_size;
wp += thisrun_bytes;
if (wp >= fifo_end_addr)
wp = fifo_start_addr;
/* Store updated write pointer to target */
retval = target_write_u32(target, wp_addr, wp);
if (retval != ERROR_OK)
break;
/* Avoid GDB timeouts */
keep_alive();
}
if (retval != ERROR_OK) {
/* abort flash write algorithm on target */
target_write_u32(target, wp_addr, 0);
}
int retval2 = target_wait_algorithm(target, num_mem_params, mem_params,
num_reg_params, reg_params,
exit_point,
10000,
arch_info);
if (retval2 != ERROR_OK) {
LOG_ERROR("error waiting for target flash write algorithm");
retval = retval2;
}
if (retval == ERROR_OK) {
/* check if algorithm set rp = 0 after fifo writer loop finished */
retval = target_read_u32(target, rp_addr, &rp);
if (retval == ERROR_OK && rp == 0) {
LOG_ERROR("flash write algorithm aborted by target");
retval = ERROR_FLASH_OPERATION_FAILED;
}
}
return retval;
}
int target_read_memory(struct target *target,
target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (!target->type->read_memory) {
LOG_ERROR("Target %s doesn't support read_memory", target_name(target));
return ERROR_FAIL;
}
return target->type->read_memory(target, address, size, count, buffer);
}
int target_read_phys_memory(struct target *target,
target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (!target->type->read_phys_memory) {
LOG_ERROR("Target %s doesn't support read_phys_memory", target_name(target));
return ERROR_FAIL;
}
return target->type->read_phys_memory(target, address, size, count, buffer);
}
int target_write_memory(struct target *target,
target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (!target->type->write_memory) {
LOG_ERROR("Target %s doesn't support write_memory", target_name(target));
return ERROR_FAIL;
}
return target->type->write_memory(target, address, size, count, buffer);
}
int target_write_phys_memory(struct target *target,
target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (!target->type->write_phys_memory) {
LOG_ERROR("Target %s doesn't support write_phys_memory", target_name(target));
return ERROR_FAIL;
}
return target->type->write_phys_memory(target, address, size, count, buffer);
}
int target_add_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if ((target->state != TARGET_HALTED) && (breakpoint->type != BKPT_HARD)) {
LOG_WARNING("target %s is not halted (add breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_breakpoint(target, breakpoint);
}
int target_add_context_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (add context breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_context_breakpoint(target, breakpoint);
}
int target_add_hybrid_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (add hybrid breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_hybrid_breakpoint(target, breakpoint);
}
int target_remove_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
return target->type->remove_breakpoint(target, breakpoint);
}
int target_add_watchpoint(struct target *target,
struct watchpoint *watchpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (add watchpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_watchpoint(target, watchpoint);
}
int target_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint)
{
return target->type->remove_watchpoint(target, watchpoint);
}
int target_hit_watchpoint(struct target *target,
struct watchpoint **hit_watchpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (hit watchpoint)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
if (target->type->hit_watchpoint == NULL) {
/* For backward compatible, if hit_watchpoint is not implemented,
* return ERROR_FAIL such that gdb_server will not take the nonsense
* information. */
return ERROR_FAIL;
}
return target->type->hit_watchpoint(target, hit_watchpoint);
}
const char *target_get_gdb_arch(struct target *target)
{
if (target->type->get_gdb_arch == NULL)
return NULL;
return target->type->get_gdb_arch(target);
}
int target_get_gdb_reg_list(struct target *target,
struct reg **reg_list[], int *reg_list_size,
enum target_register_class reg_class)
{
return target->type->get_gdb_reg_list(target, reg_list, reg_list_size, reg_class);
}
bool target_supports_gdb_connection(struct target *target)
{
/*
* based on current code, we can simply exclude all the targets that
* don't provide get_gdb_reg_list; this could change with new targets.
*/
return !!target->type->get_gdb_reg_list;
}
int target_step(struct target *target,
int current, target_addr_t address, int handle_breakpoints)
{
return target->type->step(target, current, address, handle_breakpoints);
}
int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (gdb fileio)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->get_gdb_fileio_info(target, fileio_info);
}
int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (gdb fileio end)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
}
int target_profiling(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted (profiling)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->profiling(target, samples, max_num_samples,
num_samples, seconds);
}
/**
* Reset the @c examined flag for the given target.
* Pure paranoia -- targets are zeroed on allocation.
*/
static void target_reset_examined(struct target *target)
{
target->examined = false;
}
static int handle_target(void *priv);
static int target_init_one(struct command_context *cmd_ctx,
struct target *target)
{
target_reset_examined(target);
struct target_type *type = target->type;
if (type->examine == NULL)
type->examine = default_examine;
if (type->check_reset == NULL)
type->check_reset = default_check_reset;
assert(type->init_target != NULL);
int retval = type->init_target(cmd_ctx, target);
if (ERROR_OK != retval) {
LOG_ERROR("target '%s' init failed", target_name(target));
return retval;
}
/* Sanity-check MMU support ... stub in what we must, to help
* implement it in stages, but warn if we need to do so.
*/
if (type->mmu) {
if (type->virt2phys == NULL) {
LOG_ERROR("type '%s' is missing virt2phys", type->name);
type->virt2phys = identity_virt2phys;
}
} else {
/* Make sure no-MMU targets all behave the same: make no
* distinction between physical and virtual addresses, and
* ensure that virt2phys() is always an identity mapping.
*/
if (type->write_phys_memory || type->read_phys_memory || type->virt2phys)
LOG_WARNING("type '%s' has bad MMU hooks", type->name);
type->mmu = no_mmu;
type->write_phys_memory = type->write_memory;
type->read_phys_memory = type->read_memory;
type->virt2phys = identity_virt2phys;
}
if (target->type->read_buffer == NULL)
target->type->read_buffer = target_read_buffer_default;
if (target->type->write_buffer == NULL)
target->type->write_buffer = target_write_buffer_default;
if (target->type->get_gdb_fileio_info == NULL)
target->type->get_gdb_fileio_info = target_get_gdb_fileio_info_default;
if (target->type->gdb_fileio_end == NULL)
target->type->gdb_fileio_end = target_gdb_fileio_end_default;
if (target->type->profiling == NULL)
target->type->profiling = target_profiling_default;
return ERROR_OK;
}
static int target_init(struct command_context *cmd_ctx)
{
struct target *target;
int retval;
for (target = all_targets; target; target = target->next) {
retval = target_init_one(cmd_ctx, target);
if (ERROR_OK != retval)
return retval;
}
if (!all_targets)
return ERROR_OK;
retval = target_register_user_commands(cmd_ctx);
if (ERROR_OK != retval)
return retval;
retval = target_register_timer_callback(&handle_target,
polling_interval, 1, cmd_ctx->interp);
if (ERROR_OK != retval)
return retval;
return ERROR_OK;
}
COMMAND_HANDLER(handle_target_init_command)
{
int retval;
if (CMD_ARGC != 0)
return ERROR_COMMAND_SYNTAX_ERROR;
static bool target_initialized;
if (target_initialized) {
LOG_INFO("'target init' has already been called");
return ERROR_OK;
}
target_initialized = true;
retval = command_run_line(CMD_CTX, "init_targets");
if (ERROR_OK != retval)
return retval;
retval = command_run_line(CMD_CTX, "init_target_events");
if (ERROR_OK != retval)
return retval;
retval = command_run_line(CMD_CTX, "init_board");
if (ERROR_OK != retval)
return retval;
LOG_DEBUG("Initializing targets...");
return target_init(CMD_CTX);
}
int target_register_event_callback(int (*callback)(struct target *target,
enum target_event event, void *priv), void *priv)
{
struct target_event_callback **callbacks_p = &target_event_callbacks;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
if (*callbacks_p) {
while ((*callbacks_p)->next)
callbacks_p = &((*callbacks_p)->next);
callbacks_p = &((*callbacks_p)->next);
}
(*callbacks_p) = malloc(sizeof(struct target_event_callback));
(*callbacks_p)->callback = callback;
(*callbacks_p)->priv = priv;
(*callbacks_p)->next = NULL;
return ERROR_OK;
}
int target_register_reset_callback(int (*callback)(struct target *target,
enum target_reset_mode reset_mode, void *priv), void *priv)
{
struct target_reset_callback *entry;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
entry = malloc(sizeof(struct target_reset_callback));
if (entry == NULL) {
LOG_ERROR("error allocating buffer for reset callback entry");
return ERROR_COMMAND_SYNTAX_ERROR;
}
entry->callback = callback;
entry->priv = priv;
list_add(&entry->list, &target_reset_callback_list);
return ERROR_OK;
}
int target_register_trace_callback(int (*callback)(struct target *target,
size_t len, uint8_t *data, void *priv), void *priv)
{
struct target_trace_callback *entry;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
entry = malloc(sizeof(struct target_trace_callback));
if (entry == NULL) {
LOG_ERROR("error allocating buffer for trace callback entry");
return ERROR_COMMAND_SYNTAX_ERROR;
}
entry->callback = callback;
entry->priv = priv;
list_add(&entry->list, &target_trace_callback_list);
return ERROR_OK;
}
int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv)
{
struct target_timer_callback **callbacks_p = &target_timer_callbacks;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
if (*callbacks_p) {
while ((*callbacks_p)->next)
callbacks_p = &((*callbacks_p)->next);
callbacks_p = &((*callbacks_p)->next);
}
(*callbacks_p) = malloc(sizeof(struct target_timer_callback));
(*callbacks_p)->callback = callback;
(*callbacks_p)->periodic = periodic;
(*callbacks_p)->time_ms = time_ms;
(*callbacks_p)->removed = false;
gettimeofday(&(*callbacks_p)->when, NULL);
timeval_add_time(&(*callbacks_p)->when, 0, time_ms * 1000);
(*callbacks_p)->priv = priv;
(*callbacks_p)->next = NULL;
return ERROR_OK;
}
int target_unregister_event_callback(int (*callback)(struct target *target,
enum target_event event, void *priv), void *priv)
{
struct target_event_callback **p = &target_event_callbacks;
struct target_event_callback *c = target_event_callbacks;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
while (c) {
struct target_event_callback *next = c->next;
if ((c->callback == callback) && (c->priv == priv)) {
*p = next;
free(c);
return ERROR_OK;
} else
p = &(c->next);
c = next;
}
return ERROR_OK;
}
int target_unregister_reset_callback(int (*callback)(struct target *target,
enum target_reset_mode reset_mode, void *priv), void *priv)
{
struct target_reset_callback *entry;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
list_for_each_entry(entry, &target_reset_callback_list, list) {
if (entry->callback == callback && entry->priv == priv) {
list_del(&entry->list);
free(entry);
break;
}
}
return ERROR_OK;
}
int target_unregister_trace_callback(int (*callback)(struct target *target,
size_t len, uint8_t *data, void *priv), void *priv)
{
struct target_trace_callback *entry;
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
list_for_each_entry(entry, &target_trace_callback_list, list) {
if (entry->callback == callback && entry->priv == priv) {
list_del(&entry->list);
free(entry);
break;
}
}
return ERROR_OK;
}
int target_unregister_timer_callback(int (*callback)(void *priv), void *priv)
{
if (callback == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
for (struct target_timer_callback *c = target_timer_callbacks;
c; c = c->next) {
if ((c->callback == callback) && (c->priv == priv)) {
c->removed = true;
return ERROR_OK;
}
}
return ERROR_FAIL;
}
int target_call_event_callbacks(struct target *target, enum target_event event)
{
struct target_event_callback *callback = target_event_callbacks;
struct target_event_callback *next_callback;
if (event == TARGET_EVENT_HALTED) {
/* execute early halted first */
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
LOG_DEBUG("target event %i (%s)", event,
Jim_Nvp_value2name_simple(nvp_target_event, event)->name);
target_handle_event(target, event);
while (callback) {
next_callback = callback->next;
callback->callback(target, event, callback->priv);
callback = next_callback;
}
return ERROR_OK;
}
int target_call_reset_callbacks(struct target *target, enum target_reset_mode reset_mode)
{
struct target_reset_callback *callback;
LOG_DEBUG("target reset %i (%s)", reset_mode,
Jim_Nvp_value2name_simple(nvp_reset_modes, reset_mode)->name);
list_for_each_entry(callback, &target_reset_callback_list, list)
callback->callback(target, reset_mode, callback->priv);
return ERROR_OK;
}
int target_call_trace_callbacks(struct target *target, size_t len, uint8_t *data)
{
struct target_trace_callback *callback;
list_for_each_entry(callback, &target_trace_callback_list, list)
callback->callback(target, len, data, callback->priv);
return ERROR_OK;
}
static int target_timer_callback_periodic_restart(
struct target_timer_callback *cb, struct timeval *now)
{
cb->when = *now;
timeval_add_time(&cb->when, 0, cb->time_ms * 1000L);
return ERROR_OK;
}
static int target_call_timer_callback(struct target_timer_callback *cb,
struct timeval *now)
{
cb->callback(cb->priv);
if (cb->periodic)
return target_timer_callback_periodic_restart(cb, now);
return target_unregister_timer_callback(cb->callback, cb->priv);
}
static int target_call_timer_callbacks_check_time(int checktime)
{
static bool callback_processing;
/* Do not allow nesting */
if (callback_processing)
return ERROR_OK;
callback_processing = true;
keep_alive();
struct timeval now;
gettimeofday(&now, NULL);
/* Store an address of the place containing a pointer to the
* next item; initially, that's a standalone "root of the
* list" variable. */
struct target_timer_callback **callback = &target_timer_callbacks;
while (*callback) {
if ((*callback)->removed) {
struct target_timer_callback *p = *callback;
*callback = (*callback)->next;
free(p);
continue;
}
bool call_it = (*callback)->callback &&
((!checktime && (*callback)->periodic) ||
timeval_compare(&now, &(*callback)->when) >= 0);
if (call_it)
target_call_timer_callback(*callback, &now);
callback = &(*callback)->next;
}
callback_processing = false;
return ERROR_OK;
}
int target_call_timer_callbacks(void)
{
return target_call_timer_callbacks_check_time(1);
}
/* invoke periodic callbacks immediately */
int target_call_timer_callbacks_now(void)
{
return target_call_timer_callbacks_check_time(0);
}
/* Prints the working area layout for debug purposes */
static void print_wa_layout(struct target *target)
{
struct working_area *c = target->working_areas;
while (c) {
LOG_DEBUG("%c%c " TARGET_ADDR_FMT "-" TARGET_ADDR_FMT " (%" PRIu32 " bytes)",
c->backup ? 'b' : ' ', c->free ? ' ' : '*',
c->address, c->address + c->size - 1, c->size);
c = c->next;
}
}
/* Reduce area to size bytes, create a new free area from the remaining bytes, if any. */
static void target_split_working_area(struct working_area *area, uint32_t size)
{
assert(area->free); /* Shouldn't split an allocated area */
assert(size <= area->size); /* Caller should guarantee this */
/* Split only if not already the right size */
if (size < area->size) {
struct working_area *new_wa = malloc(sizeof(*new_wa));
if (new_wa == NULL)
return;
new_wa->next = area->next;
new_wa->size = area->size - size;
new_wa->address = area->address + size;
new_wa->backup = NULL;
new_wa->user = NULL;
new_wa->free = true;
area->next = new_wa;
area->size = size;
/* If backup memory was allocated to this area, it has the wrong size
* now so free it and it will be reallocated if/when needed */
if (area->backup) {
free(area->backup);
area->backup = NULL;
}
}
}
/* Merge all adjacent free areas into one */
static void target_merge_working_areas(struct target *target)
{
struct working_area *c = target->working_areas;
while (c && c->next) {
assert(c->next->address == c->address + c->size); /* This is an invariant */
/* Find two adjacent free areas */
if (c->free && c->next->free) {
/* Merge the last into the first */
c->size += c->next->size;
/* Remove the last */
struct working_area *to_be_freed = c->next;
c->next = c->next->next;
if (to_be_freed->backup)
free(to_be_freed->backup);
free(to_be_freed);
/* If backup memory was allocated to the remaining area, it's has
* the wrong size now */
if (c->backup) {
free(c->backup);
c->backup = NULL;
}
} else {
c = c->next;
}
}
}
int target_alloc_working_area_try(struct target *target, uint32_t size, struct working_area **area)
{
/* Reevaluate working area address based on MMU state*/
if (target->working_areas == NULL) {
int retval;
int enabled;
retval = target->type->mmu(target, &enabled);
if (retval != ERROR_OK)
return retval;
if (!enabled) {
if (target->working_area_phys_spec) {
LOG_DEBUG("MMU disabled, using physical "
"address for working memory " TARGET_ADDR_FMT,
target->working_area_phys);
target->working_area = target->working_area_phys;
} else {
LOG_ERROR("No working memory available. "
"Specify -work-area-phys to target.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
} else {
if (target->working_area_virt_spec) {
LOG_DEBUG("MMU enabled, using virtual "
"address for working memory " TARGET_ADDR_FMT,
target->working_area_virt);
target->working_area = target->working_area_virt;
} else {
LOG_ERROR("No working memory available. "
"Specify -work-area-virt to target.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
/* Set up initial working area on first call */
struct working_area *new_wa = malloc(sizeof(*new_wa));
if (new_wa) {
new_wa->next = NULL;
new_wa->size = target->working_area_size & ~3UL; /* 4-byte align */
new_wa->address = target->working_area;
new_wa->backup = NULL;
new_wa->user = NULL;
new_wa->free = true;
}
target->working_areas = new_wa;
}
/* only allocate multiples of 4 byte */
if (size % 4)
size = (size + 3) & (~3UL);
struct working_area *c = target->working_areas;
/* Find the first large enough working area */
while (c) {
if (c->free && c->size >= size)
break;
c = c->next;
}
if (c == NULL)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
/* Split the working area into the requested size */
target_split_working_area(c, size);
LOG_DEBUG("allocated new working area of %" PRIu32 " bytes at address " TARGET_ADDR_FMT,
size, c->address);
if (target->backup_working_area) {
if (c->backup == NULL) {
c->backup = malloc(c->size);
if (c->backup == NULL)
return ERROR_FAIL;
}
int retval = target_read_memory(target, c->address, 4, c->size / 4, c->backup);
if (retval != ERROR_OK)
return retval;
}
/* mark as used, and return the new (reused) area */
c->free = false;
*area = c;
/* user pointer */
c->user = area;
print_wa_layout(target);
return ERROR_OK;
}
int target_alloc_working_area(struct target *target, uint32_t size, struct working_area **area)
{
int retval;
retval = target_alloc_working_area_try(target, size, area);
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
LOG_WARNING("not enough working area available(requested %"PRIu32")", size);
return retval;
}
static int target_restore_working_area(struct target *target, struct working_area *area)
{
int retval = ERROR_OK;
if (target->backup_working_area && area->backup != NULL) {
retval = target_write_memory(target, area->address, 4, area->size / 4, area->backup);
if (retval != ERROR_OK)
LOG_ERROR("failed to restore %" PRIu32 " bytes of working area at address " TARGET_ADDR_FMT,
area->size, area->address);
}
return retval;
}
/* Restore the area's backup memory, if any, and return the area to the allocation pool */
static int target_free_working_area_restore(struct target *target, struct working_area *area, int restore)
{
int retval = ERROR_OK;
if (area->free)
return retval;
if (restore) {
retval = target_restore_working_area(target, area);
/* REVISIT: Perhaps the area should be freed even if restoring fails. */
if (retval != ERROR_OK)
return retval;
}
area->free = true;
LOG_DEBUG("freed %" PRIu32 " bytes of working area at address " TARGET_ADDR_FMT,
area->size, area->address);
/* mark user pointer invalid */
/* TODO: Is this really safe? It points to some previous caller's memory.
* How could we know that the area pointer is still in that place and not
* some other vital data? What's the purpose of this, anyway? */
*area->user = NULL;
area->user = NULL;
target_merge_working_areas(target);
print_wa_layout(target);
return retval;
}
int target_free_working_area(struct target *target, struct working_area *area)
{
return target_free_working_area_restore(target, area, 1);
}
static void target_destroy(struct target *target)
{
if (target->type->deinit_target)
target->type->deinit_target(target);
if (target->semihosting)
free(target->semihosting);
jtag_unregister_event_callback(jtag_enable_callback, target);
struct target_event_action *teap = target->event_action;
while (teap) {
struct target_event_action *next = teap->next;
Jim_DecrRefCount(teap->interp, teap->body);
free(teap);
teap = next;
}
target_free_all_working_areas(target);
/* Now we have none or only one working area marked as free */
if (target->working_areas) {
free(target->working_areas->backup);
free(target->working_areas);
}
/* release the targets SMP list */
if (target->smp) {
struct target_list *head = target->head;
while (head != NULL) {
struct target_list *pos = head->next;
head->target->smp = 0;
free(head);
head = pos;
}
target->smp = 0;
}
free(target->gdb_port_override);
free(target->type);
free(target->trace_info);
free(target->fileio_info);
free(target->cmd_name);
free(target);
}
void target_quit(void)
{
struct target_event_callback *pe = target_event_callbacks;
while (pe) {
struct target_event_callback *t = pe->next;
free(pe);
pe = t;
}
target_event_callbacks = NULL;
struct target_timer_callback *pt = target_timer_callbacks;
while (pt) {
struct target_timer_callback *t = pt->next;
free(pt);
pt = t;
}
target_timer_callbacks = NULL;
for (struct target *target = all_targets; target;) {
struct target *tmp;
tmp = target->next;
target_destroy(target);
target = tmp;
}
all_targets = NULL;
}
/* free resources and restore memory, if restoring memory fails,
* free up resources anyway
*/
static void target_free_all_working_areas_restore(struct target *target, int restore)
{
struct working_area *c = target->working_areas;
LOG_DEBUG("freeing all working areas");
/* Loop through all areas, restoring the allocated ones and marking them as free */
while (c) {
if (!c->free) {
if (restore)
target_restore_working_area(target, c);
c->free = true;
*c->user = NULL; /* Same as above */
c->user = NULL;
}
c = c->next;
}
/* Run a merge pass to combine all areas into one */
target_merge_working_areas(target);
print_wa_layout(target);
}
void target_free_all_working_areas(struct target *target)
{
target_free_all_working_areas_restore(target, 1);
}
/* Find the largest number of bytes that can be allocated */
uint32_t target_get_working_area_avail(struct target *target)
{
struct working_area *c = target->working_areas;
uint32_t max_size = 0;
if (c == NULL)
return target->working_area_size;
while (c) {
if (c->free && max_size < c->size)
max_size = c->size;
c = c->next;
}
return max_size;
}
int target_arch_state(struct target *target)
{
int retval;
if (target == NULL) {
LOG_WARNING("No target has been configured");
return ERROR_OK;
}
if (target->state != TARGET_HALTED)
return ERROR_OK;
retval = target->type->arch_state(target);
return retval;
}
static int target_get_gdb_fileio_info_default(struct target *target,
struct gdb_fileio_info *fileio_info)
{
/* If target does not support semi-hosting function, target
has no need to provide .get_gdb_fileio_info callback.
It just return ERROR_FAIL and gdb_server will return "Txx"
as target halted every time. */
return ERROR_FAIL;
}
static int target_gdb_fileio_end_default(struct target *target,
int retcode, int fileio_errno, bool ctrl_c)
{
return ERROR_OK;
}
static int target_profiling_default(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
{
struct timeval timeout, now;
gettimeofday(&timeout, NULL);
timeval_add_time(&timeout, seconds, 0);
LOG_INFO("Starting profiling. Halting and resuming the"
" target as often as we can...");
uint32_t sample_count = 0;
/* hopefully it is safe to cache! We want to stop/restart as quickly as possible. */
struct reg *reg = register_get_by_name(target->reg_cache, "pc", 1);
int retval = ERROR_OK;
for (;;) {
target_poll(target);
if (target->state == TARGET_HALTED) {
uint32_t t = buf_get_u32(reg->value, 0, 32);
samples[sample_count++] = t;
/* current pc, addr = 0, do not handle breakpoints, not debugging */
retval = target_resume(target, 1, 0, 0, 0);
target_poll(target);
alive_sleep(10); /* sleep 10ms, i.e. <100 samples/second. */
} else if (target->state == TARGET_RUNNING) {
/* We want to quickly sample the PC. */
retval = target_halt(target);
} else {
LOG_INFO("Target not halted or running");
retval = ERROR_OK;
break;
}
if (retval != ERROR_OK)
break;
gettimeofday(&now, NULL);
if ((sample_count >= max_num_samples) || timeval_compare(&now, &timeout) >= 0) {
LOG_INFO("Profiling completed. %" PRIu32 " samples.", sample_count);
break;
}
}
*num_samples = sample_count;
return retval;
}
/* Single aligned words are guaranteed to use 16 or 32 bit access
* mode respectively, otherwise data is handled as quickly as
* possible
*/
int target_write_buffer(struct target *target, target_addr_t address, uint32_t size, const uint8_t *buffer)
{
LOG_DEBUG("writing buffer of %" PRIi32 " byte at " TARGET_ADDR_FMT,
size, address);
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (size == 0)
return ERROR_OK;
if ((address + size - 1) < address) {
/* GDB can request this when e.g. PC is 0xfffffffc */
LOG_ERROR("address + size wrapped (" TARGET_ADDR_FMT ", 0x%08" PRIx32 ")",
address,
size);
return ERROR_FAIL;
}
return target->type->write_buffer(target, address, size, buffer);
}
static int target_write_buffer_default(struct target *target,
target_addr_t address, uint32_t count, const uint8_t *buffer)
{
uint32_t size;
/* Align up to maximum 4 bytes. The loop condition makes sure the next pass
* will have something to do with the size we leave to it. */
for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) {
if (address & size) {
int retval = target_write_memory(target, address, size, 1, buffer);
if (retval != ERROR_OK)
return retval;
address += size;
count -= size;
buffer += size;
}
}
/* Write the data with as large access size as possible. */
for (; size > 0; size /= 2) {
uint32_t aligned = count - count % size;
if (aligned > 0) {
int retval = target_write_memory(target, address, size, aligned / size, buffer);
if (retval != ERROR_OK)
return retval;
address += aligned;
count -= aligned;
buffer += aligned;
}
}
return ERROR_OK;
}
/* Single aligned words are guaranteed to use 16 or 32 bit access
* mode respectively, otherwise data is handled as quickly as
* possible
*/
int target_read_buffer(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
{
LOG_DEBUG("reading buffer of %" PRIi32 " byte at " TARGET_ADDR_FMT,
size, address);
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (size == 0)
return ERROR_OK;
if ((address + size - 1) < address) {
/* GDB can request this when e.g. PC is 0xfffffffc */
LOG_ERROR("address + size wrapped (" TARGET_ADDR_FMT ", 0x%08" PRIx32 ")",
address,
size);
return ERROR_FAIL;
}
return target->type->read_buffer(target, address, size, buffer);
}
static int target_read_buffer_default(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer)
{
uint32_t size;
/* Align up to maximum 4 bytes. The loop condition makes sure the next pass
* will have something to do with the size we leave to it. */
for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) {
if (address & size) {
int retval = target_read_memory(target, address, size, 1, buffer);
if (retval != ERROR_OK)
return retval;
address += size;
count -= size;
buffer += size;
}
}
/* Read the data with as large access size as possible. */
for (; size > 0; size /= 2) {
uint32_t aligned = count - count % size;
if (aligned > 0) {
int retval = target_read_memory(target, address, size, aligned / size, buffer);
if (retval != ERROR_OK)
return retval;
address += aligned;
count -= aligned;
buffer += aligned;
}
}
return ERROR_OK;
}
int target_checksum_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t* crc)
{
uint8_t *buffer;
int retval;
uint32_t i;
uint32_t checksum = 0;
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
retval = target->type->checksum_memory(target, address, size, &checksum);
if (retval != ERROR_OK) {
buffer = malloc(size);
if (buffer == NULL) {
LOG_ERROR("error allocating buffer for section (%" PRId32 " bytes)", size);
return ERROR_COMMAND_SYNTAX_ERROR;
}
retval = target_read_buffer(target, address, size, buffer);
if (retval != ERROR_OK) {
free(buffer);
return retval;
}
/* convert to target endianness */
for (i = 0; i < (size/sizeof(uint32_t)); i++) {
uint32_t target_data;
target_data = target_buffer_get_u32(target, &buffer[i*sizeof(uint32_t)]);
target_buffer_set_u32(target, &buffer[i*sizeof(uint32_t)], target_data);
}
retval = image_calculate_checksum(buffer, size, &checksum);
free(buffer);
}
*crc = checksum;
return retval;
}
int target_blank_check_memory(struct target *target,
struct target_memory_check_block *blocks, int num_blocks,
uint8_t erased_value)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (target->type->blank_check_memory == NULL)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
return target->type->blank_check_memory(target, blocks, num_blocks, erased_value);
}
int target_read_u64(struct target *target, target_addr_t address, uint64_t *value)
{
uint8_t value_buf[8];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
int retval = target_read_memory(target, address, 8, 1, value_buf);
if (retval == ERROR_OK) {
*value = target_buffer_get_u64(target, value_buf);
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%16.16" PRIx64 "",
address,
*value);
} else {
*value = 0x0;
LOG_DEBUG("address: " TARGET_ADDR_FMT " failed",
address);
}
return retval;
}
int target_read_u32(struct target *target, target_addr_t address, uint32_t *value)
{
uint8_t value_buf[4];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
int retval = target_read_memory(target, address, 4, 1, value_buf);
if (retval == ERROR_OK) {
*value = target_buffer_get_u32(target, value_buf);
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%8.8" PRIx32 "",
address,
*value);
} else {
*value = 0x0;
LOG_DEBUG("address: " TARGET_ADDR_FMT " failed",
address);
}
return retval;
}
int target_read_u16(struct target *target, target_addr_t address, uint16_t *value)
{
uint8_t value_buf[2];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
int retval = target_read_memory(target, address, 2, 1, value_buf);
if (retval == ERROR_OK) {
*value = target_buffer_get_u16(target, value_buf);
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%4.4" PRIx16,
address,
*value);
} else {
*value = 0x0;
LOG_DEBUG("address: " TARGET_ADDR_FMT " failed",
address);
}
return retval;
}
int target_read_u8(struct target *target, target_addr_t address, uint8_t *value)
{
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
int retval = target_read_memory(target, address, 1, 1, value);
if (retval == ERROR_OK) {
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%2.2" PRIx8,
address,
*value);
} else {
*value = 0x0;
LOG_DEBUG("address: " TARGET_ADDR_FMT " failed",
address);
}
return retval;
}
int target_write_u64(struct target *target, target_addr_t address, uint64_t value)
{
int retval;
uint8_t value_buf[8];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%16.16" PRIx64 "",
address,
value);
target_buffer_set_u64(target, value_buf, value);
retval = target_write_memory(target, address, 8, 1, value_buf);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_u32(struct target *target, target_addr_t address, uint32_t value)
{
int retval;
uint8_t value_buf[4];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%8.8" PRIx32 "",
address,
value);
target_buffer_set_u32(target, value_buf, value);
retval = target_write_memory(target, address, 4, 1, value_buf);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_u16(struct target *target, target_addr_t address, uint16_t value)
{
int retval;
uint8_t value_buf[2];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%8.8" PRIx16,
address,
value);
target_buffer_set_u16(target, value_buf, value);
retval = target_write_memory(target, address, 2, 1, value_buf);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_u8(struct target *target, target_addr_t address, uint8_t value)
{
int retval;
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%2.2" PRIx8,
address, value);
retval = target_write_memory(target, address, 1, 1, &value);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_phys_u64(struct target *target, target_addr_t address, uint64_t value)
{
int retval;
uint8_t value_buf[8];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%16.16" PRIx64 "",
address,
value);
target_buffer_set_u64(target, value_buf, value);
retval = target_write_phys_memory(target, address, 8, 1, value_buf);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_phys_u32(struct target *target, target_addr_t address, uint32_t value)
{
int retval;
uint8_t value_buf[4];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%8.8" PRIx32 "",
address,
value);
target_buffer_set_u32(target, value_buf, value);
retval = target_write_phys_memory(target, address, 4, 1, value_buf);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_phys_u16(struct target *target, target_addr_t address, uint16_t value)
{
int retval;
uint8_t value_buf[2];
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%8.8" PRIx16,
address,
value);
target_buffer_set_u16(target, value_buf, value);
retval = target_write_phys_memory(target, address, 2, 1, value_buf);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
int target_write_phys_u8(struct target *target, target_addr_t address, uint8_t value)
{
int retval;
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
LOG_DEBUG("address: " TARGET_ADDR_FMT ", value: 0x%2.2" PRIx8,
address, value);
retval = target_write_phys_memory(target, address, 1, 1, &value);
if (retval != ERROR_OK)
LOG_DEBUG("failed: %i", retval);
return retval;
}
static int find_target(struct command_context *cmd_ctx, const char *name)
{
struct target *target = get_target(name);
if (target == NULL) {
LOG_ERROR("Target: %s is unknown, try one of:\n", name);
return ERROR_FAIL;
}
if (!target->tap->enabled) {
LOG_USER("Target: TAP %s is disabled, "
"can't be the current target\n",
target->tap->dotted_name);
return ERROR_FAIL;
}
cmd_ctx->current_target = target;
if (cmd_ctx->current_target_override)
cmd_ctx->current_target_override = target;
return ERROR_OK;
}
COMMAND_HANDLER(handle_targets_command)
{
int retval = ERROR_OK;
if (CMD_ARGC == 1) {
retval = find_target(CMD_CTX, CMD_ARGV[0]);
if (retval == ERROR_OK) {
/* we're done! */
return retval;
}
}
struct target *target = all_targets;
command_print(CMD_CTX, " TargetName Type Endian TapName State ");
command_print(CMD_CTX, "-- ------------------ ---------- ------ ------------------ ------------");
while (target) {
const char *state;
char marker = ' ';
if (target->tap->enabled)
state = target_state_name(target);
else
state = "tap-disabled";
if (CMD_CTX->current_target == target)
marker = '*';
/* keep columns lined up to match the headers above */
command_print(CMD_CTX,
"%2d%c %-18s %-10s %-6s %-18s %s",
target->target_number,
marker,
target_name(target),
target_type_name(target),
Jim_Nvp_value2name_simple(nvp_target_endian,
target->endianness)->name,
target->tap->dotted_name,
state);
target = target->next;
}
return retval;
}
/* every 300ms we check for reset & powerdropout and issue a "reset halt" if so. */
static int powerDropout;
static int srstAsserted;
static int runPowerRestore;
static int runPowerDropout;
static int runSrstAsserted;
static int runSrstDeasserted;
static int sense_handler(void)
{
static int prevSrstAsserted;
static int prevPowerdropout;
int retval = jtag_power_dropout(&powerDropout);
if (retval != ERROR_OK)
return retval;
int powerRestored;
powerRestored = prevPowerdropout && !powerDropout;
if (powerRestored)
runPowerRestore = 1;
int64_t current = timeval_ms();
static int64_t lastPower;
bool waitMore = lastPower + 2000 > current;
if (powerDropout && !waitMore) {
runPowerDropout = 1;
lastPower = current;
}
retval = jtag_srst_asserted(&srstAsserted);
if (retval != ERROR_OK)
return retval;
int srstDeasserted;
srstDeasserted = prevSrstAsserted && !srstAsserted;
static int64_t lastSrst;
waitMore = lastSrst + 2000 > current;
if (srstDeasserted && !waitMore) {
runSrstDeasserted = 1;
lastSrst = current;
}
if (!prevSrstAsserted && srstAsserted)
runSrstAsserted = 1;
prevSrstAsserted = srstAsserted;
prevPowerdropout = powerDropout;
if (srstDeasserted || powerRestored) {
/* Other than logging the event we can't do anything here.
* Issuing a reset is a particularly bad idea as we might
* be inside a reset already.
*/
}
return ERROR_OK;
}
/* process target state changes */
static int handle_target(void *priv)
{
Jim_Interp *interp = (Jim_Interp *)priv;
int retval = ERROR_OK;
if (!is_jtag_poll_safe()) {
/* polling is disabled currently */
return ERROR_OK;
}
/* we do not want to recurse here... */
static int recursive;
if (!recursive) {
recursive = 1;
sense_handler();
/* danger! running these procedures can trigger srst assertions and power dropouts.
* We need to avoid an infinite loop/recursion here and we do that by
* clearing the flags after running these events.
*/
int did_something = 0;
if (runSrstAsserted) {
LOG_INFO("srst asserted detected, running srst_asserted proc.");
Jim_Eval(interp, "srst_asserted");
did_something = 1;
}
if (runSrstDeasserted) {
Jim_Eval(interp, "srst_deasserted");
did_something = 1;
}
if (runPowerDropout) {
LOG_INFO("Power dropout detected, running power_dropout proc.");
Jim_Eval(interp, "power_dropout");
did_something = 1;
}
if (runPowerRestore) {
Jim_Eval(interp, "power_restore");
did_something = 1;
}
if (did_something) {
/* clear detect flags */
sense_handler();
}
/* clear action flags */
runSrstAsserted = 0;
runSrstDeasserted = 0;
runPowerRestore = 0;
runPowerDropout = 0;
recursive = 0;
}
/* Poll targets for state changes unless that's globally disabled.
* Skip targets that are currently disabled.
*/
for (struct target *target = all_targets;
is_jtag_poll_safe() && target;
target = target->next) {
if (!target_was_examined(target))
continue;
if (!target->tap->enabled)
continue;
if (target->backoff.times > target->backoff.count) {
/* do not poll this time as we failed previously */
target->backoff.count++;
continue;
}
target->backoff.count = 0;
/* only poll target if we've got power and srst isn't asserted */
if (!powerDropout && !srstAsserted) {
/* polling may fail silently until the target has been examined */
retval = target_poll(target);
if (retval != ERROR_OK) {
/* 100ms polling interval. Increase interval between polling up to 5000ms */
if (target->backoff.times * polling_interval < 5000) {
target->backoff.times *= 2;
target->backoff.times++;
}
/* Tell GDB to halt the debugger. This allows the user to
* run monitor commands to handle the situation.
*/
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
if (target->backoff.times > 0) {
LOG_USER("Polling target %s failed, trying to reexamine", target_name(target));
target_reset_examined(target);
retval = target_examine_one(target);
/* Target examination could have failed due to unstable connection,
* but we set the examined flag anyway to repoll it later */
if (retval != ERROR_OK) {
target->examined = true;
LOG_USER("Examination failed, GDB will be halted. Polling again in %dms",
target->backoff.times * polling_interval);
return retval;
}
}
/* Since we succeeded, we reset backoff count */
target->backoff.times = 0;
}
}
return retval;
}
COMMAND_HANDLER(handle_reg_command)
{
struct target *target;
struct reg *reg = NULL;
unsigned count = 0;
char *value;
int retval;
LOG_DEBUG("-");
target = get_current_target(CMD_CTX);
/* list all available registers for the current target */
if (CMD_ARGC == 0) {
struct reg_cache *cache = target->reg_cache;
count = 0;
while (cache) {
unsigned i;
command_print(CMD_CTX, "===== %s", cache->name);
for (i = 0, reg = cache->reg_list;
i < cache->num_regs;
i++, reg++, count++) {
if (reg->exist == false)
continue;
/* only print cached values if they are valid */
if (reg->exist) {
if (reg->valid) {
value = buf_to_str(reg->value,
reg->size, 16);
command_print(CMD_CTX,
"(%i) %s (/%" PRIu32 "): 0x%s%s",
count, reg->name,
reg->size, value,
reg->dirty
? " (dirty)"
: "");
free(value);
} else {
command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")",
count, reg->name,
reg->size) ;
}
}
}
cache = cache->next;
}
return ERROR_OK;
}
/* access a single register by its ordinal number */
if ((CMD_ARGV[0][0] >= '0') && (CMD_ARGV[0][0] <= '9')) {
unsigned num;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], num);
struct reg_cache *cache = target->reg_cache;
count = 0;
while (cache) {
unsigned i;
for (i = 0; i < cache->num_regs; i++) {
if (count++ == num) {
reg = &cache->reg_list[i];
break;
}
}
if (reg)
break;
cache = cache->next;
}
if (!reg) {
command_print(CMD_CTX, "%i is out of bounds, the current target "
"has only %i registers (0 - %i)", num, count, count - 1);
return ERROR_OK;
}
} else {
/* access a single register by its name */
reg = register_get_by_name(target->reg_cache, CMD_ARGV[0], 1);
if (!reg)
goto not_found;
}
assert(reg != NULL); /* give clang a hint that we *know* reg is != NULL here */
if (!reg->exist)
goto not_found;
/* display a register */
if ((CMD_ARGC == 1) || ((CMD_ARGC == 2) && !((CMD_ARGV[1][0] >= '0')
&& (CMD_ARGV[1][0] <= '9')))) {
if ((CMD_ARGC == 2) && (strcmp(CMD_ARGV[1], "force") == 0))
reg->valid = 0;
if (reg->valid == 0) {
retval = reg->type->get(reg);
if (retval != ERROR_OK) {
LOG_DEBUG("Couldn't get register %s.", reg->name);
return retval;
}
}
value = buf_to_str(reg->value, reg->size, 16);
command_print(CMD_CTX, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value);
free(value);
return ERROR_OK;
}
/* set register value */
if (CMD_ARGC == 2) {
uint8_t *buf = malloc(DIV_ROUND_UP(reg->size, 8));
if (buf == NULL)
return ERROR_FAIL;
str_to_buf(CMD_ARGV[1], strlen(CMD_ARGV[1]), buf, reg->size, 0);
retval = reg->type->set(reg, buf);
if (retval != ERROR_OK) {
LOG_DEBUG("Couldn't set register %s.", reg->name);
free(buf);
return retval;
}
value = buf_to_str(reg->value, reg->size, 16);
command_print(CMD_CTX, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value);
free(value);
free(buf);
return ERROR_OK;
}
return ERROR_COMMAND_SYNTAX_ERROR;
not_found:
command_print(CMD_CTX, "register %s not found in current target", CMD_ARGV[0]);
return ERROR_OK;
}
COMMAND_HANDLER(handle_poll_command)
{
int retval = ERROR_OK;
struct target *target = get_current_target(CMD_CTX);
if (CMD_ARGC == 0) {
command_print(CMD_CTX, "background polling: %s",
jtag_poll_get_enabled() ? "on" : "off");
command_print(CMD_CTX, "TAP: %s (%s)",
target->tap->dotted_name,
target->tap->enabled ? "enabled" : "disabled");
if (!target->tap->enabled)
return ERROR_OK;
retval = target_poll(target);
if (retval != ERROR_OK)
return retval;
retval = target_arch_state(target);
if (retval != ERROR_OK)
return retval;
} else if (CMD_ARGC == 1) {
bool enable;
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
jtag_poll_set_enabled(enable);
} else
return ERROR_COMMAND_SYNTAX_ERROR;
return retval;
}
COMMAND_HANDLER(handle_wait_halt_command)
{
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
unsigned ms = DEFAULT_HALT_TIMEOUT;
if (1 == CMD_ARGC) {
int retval = parse_uint(CMD_ARGV[0], &ms);
if (ERROR_OK != retval)
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct target *target = get_current_target(CMD_CTX);
return target_wait_state(target, TARGET_HALTED, ms);
}
/* wait for target state to change. The trick here is to have a low
* latency for short waits and not to suck up all the CPU time
* on longer waits.
*
* After 500ms, keep_alive() is invoked
*/
int target_wait_state(struct target *target, enum target_state state, int ms)
{
int retval;
int64_t then = 0, cur;
bool once = true;
for (;;) {
retval = target_poll(target);
if (retval != ERROR_OK)
return retval;
if (target->state == state)
break;
cur = timeval_ms();
if (once) {
once = false;
then = timeval_ms();
LOG_DEBUG("waiting for target %s...",
Jim_Nvp_value2name_simple(nvp_target_state, state)->name);
}
if (cur-then > 500)
keep_alive();
if ((cur-then) > ms) {
LOG_ERROR("timed out while waiting for target %s",
Jim_Nvp_value2name_simple(nvp_target_state, state)->name);
return ERROR_FAIL;
}
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_halt_command)
{
LOG_DEBUG("-");
struct target *target = get_current_target(CMD_CTX);
target->verbose_halt_msg = true;
int retval = target_halt(target);
if (ERROR_OK != retval)
return retval;
if (CMD_ARGC == 1) {
unsigned wait_local;
retval = parse_uint(CMD_ARGV[0], &wait_local);
if (ERROR_OK != retval)
return ERROR_COMMAND_SYNTAX_ERROR;
if (!wait_local)
return ERROR_OK;
}
return CALL_COMMAND_HANDLER(handle_wait_halt_command);
}
COMMAND_HANDLER(handle_soft_reset_halt_command)
{
struct target *target = get_current_target(CMD_CTX);
LOG_USER("requesting target halt and executing a soft reset");
target_soft_reset_halt(target);
return ERROR_OK;
}
COMMAND_HANDLER(handle_reset_command)
{
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
enum target_reset_mode reset_mode = RESET_RUN;
if (CMD_ARGC == 1) {
const Jim_Nvp *n;
n = Jim_Nvp_name2value_simple(nvp_reset_modes, CMD_ARGV[0]);
if ((n->name == NULL) || (n->value == RESET_UNKNOWN))
return ERROR_COMMAND_SYNTAX_ERROR;
reset_mode = n->value;
}
/* reset *all* targets */
return target_process_reset(CMD_CTX, reset_mode);
}
COMMAND_HANDLER(handle_resume_command)
{
int current = 1;
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct target *target = get_current_target(CMD_CTX);
/* with no CMD_ARGV, resume from current pc, addr = 0,
* with one arguments, addr = CMD_ARGV[0],
* handle breakpoints, not debugging */
target_addr_t addr = 0;
if (CMD_ARGC == 1) {
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr);
current = 0;
}
return target_resume(target, current, addr, 1, 0);
}
COMMAND_HANDLER(handle_step_command)
{
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
LOG_DEBUG("-");
/* with no CMD_ARGV, step from current pc, addr = 0,
* with one argument addr = CMD_ARGV[0],
* handle breakpoints, debugging */
target_addr_t addr = 0;
int current_pc = 1;
if (CMD_ARGC == 1) {
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr);
current_pc = 0;
}
struct target *target = get_current_target(CMD_CTX);
return target->type->step(target, current_pc, addr, 1);
}
static void handle_md_output(struct command_context *cmd_ctx,
struct target *target, target_addr_t address, unsigned size,
unsigned count, const uint8_t *buffer)
{
const unsigned line_bytecnt = 32;
unsigned line_modulo = line_bytecnt / size;
char output[line_bytecnt * 4 + 1];
unsigned output_len = 0;
const char *value_fmt;
switch (size) {
case 8:
value_fmt = "%16.16"PRIx64" ";
break;
case 4:
value_fmt = "%8.8"PRIx64" ";
break;
case 2:
value_fmt = "%4.4"PRIx64" ";
break;
case 1:
value_fmt = "%2.2"PRIx64" ";
break;
default:
/* "can't happen", caller checked */
LOG_ERROR("invalid memory read size: %u", size);
return;
}
for (unsigned i = 0; i < count; i++) {
if (i % line_modulo == 0) {
output_len += snprintf(output + output_len,
sizeof(output) - output_len,
TARGET_ADDR_FMT ": ",
(address + (i * size)));
}
uint64_t value = 0;
const uint8_t *value_ptr = buffer + i * size;
switch (size) {
case 8:
value = target_buffer_get_u64(target, value_ptr);
break;
case 4:
value = target_buffer_get_u32(target, value_ptr);
break;
case 2:
value = target_buffer_get_u16(target, value_ptr);
break;
case 1:
value = *value_ptr;
}
output_len += snprintf(output + output_len,
sizeof(output) - output_len,
value_fmt, value);
if ((i % line_modulo == line_modulo - 1) || (i == count - 1)) {
command_print(cmd_ctx, "%s", output);
output_len = 0;
}
}
}
COMMAND_HANDLER(handle_md_command)
{
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
unsigned size = 0;
switch (CMD_NAME[2]) {
case 'd':
size = 8;
break;
case 'w':
size = 4;
break;
case 'h':
size = 2;
break;
case 'b':
size = 1;
break;
default:
return ERROR_COMMAND_SYNTAX_ERROR;
}
bool physical = strcmp(CMD_ARGV[0], "phys") == 0;
int (*fn)(struct target *target,
target_addr_t address, uint32_t size_value, uint32_t count, uint8_t *buffer);
if (physical) {
CMD_ARGC--;
CMD_ARGV++;
fn = target_read_phys_memory;
} else
fn = target_read_memory;
if ((CMD_ARGC < 1) || (CMD_ARGC > 2))
return ERROR_COMMAND_SYNTAX_ERROR;
target_addr_t address;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
unsigned count = 1;
if (CMD_ARGC == 2)
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], count);
uint8_t *buffer = calloc(count, size);
if (buffer == NULL) {
LOG_ERROR("Failed to allocate md read buffer");
return ERROR_FAIL;
}
struct target *target = get_current_target(CMD_CTX);
int retval = fn(target, address, size, count, buffer);
if (ERROR_OK == retval)
handle_md_output(CMD_CTX, target, address, size, count, buffer);
free(buffer);
return retval;
}
typedef int (*target_write_fn)(struct target *target,
target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer);
static int target_fill_mem(struct target *target,
target_addr_t address,
target_write_fn fn,
unsigned data_size,
/* value */
uint64_t b,
/* count */
unsigned c)
{
/* We have to write in reasonably large chunks to be able
* to fill large memory areas with any sane speed */
const unsigned chunk_size = 16384;
uint8_t *target_buf = malloc(chunk_size * data_size);
if (target_buf == NULL) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
for (unsigned i = 0; i < chunk_size; i++) {
switch (data_size) {
case 8:
target_buffer_set_u64(target, target_buf + i * data_size, b);
break;
case 4:
target_buffer_set_u32(target, target_buf + i * data_size, b);
break;
case 2:
target_buffer_set_u16(target, target_buf + i * data_size, b);
break;
case 1:
target_buffer_set_u8(target, target_buf + i * data_size, b);
break;
default:
exit(-1);
}
}
int retval = ERROR_OK;
for (unsigned x = 0; x < c; x += chunk_size) {
unsigned current;
current = c - x;
if (current > chunk_size)
current = chunk_size;
retval = fn(target, address + x * data_size, data_size, current, target_buf);
if (retval != ERROR_OK)
break;
/* avoid GDB timeouts */
keep_alive();
}
free(target_buf);
return retval;
}
COMMAND_HANDLER(handle_mw_command)
{
if (CMD_ARGC < 2)
return ERROR_COMMAND_SYNTAX_ERROR;
bool physical = strcmp(CMD_ARGV[0], "phys") == 0;
target_write_fn fn;
if (physical) {
CMD_ARGC--;
CMD_ARGV++;
fn = target_write_phys_memory;
} else
fn = target_write_memory;
if ((CMD_ARGC < 2) || (CMD_ARGC > 3))
return ERROR_COMMAND_SYNTAX_ERROR;
target_addr_t address;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
target_addr_t value;
COMMAND_PARSE_ADDRESS(CMD_ARGV[1], value);
unsigned count = 1;
if (CMD_ARGC == 3)
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[2], count);
struct target *target = get_current_target(CMD_CTX);
unsigned wordsize;
switch (CMD_NAME[2]) {
case 'd':
wordsize = 8;
break;
case 'w':
wordsize = 4;
break;
case 'h':
wordsize = 2;
break;
case 'b':
wordsize = 1;
break;
default:
return ERROR_COMMAND_SYNTAX_ERROR;
}
return target_fill_mem(target, address, fn, wordsize, value, count);
}
static COMMAND_HELPER(parse_load_image_command_CMD_ARGV, struct image *image,
target_addr_t *min_address, target_addr_t *max_address)
{
if (CMD_ARGC < 1 || CMD_ARGC > 5)
return ERROR_COMMAND_SYNTAX_ERROR;
/* a base address isn't always necessary,
* default to 0x0 (i.e. don't relocate) */
if (CMD_ARGC >= 2) {
target_addr_t addr;
COMMAND_PARSE_ADDRESS(CMD_ARGV[1], addr);
image->base_address = addr;
image->base_address_set = 1;
} else
image->base_address_set = 0;
image->start_address_set = 0;
if (CMD_ARGC >= 4)
COMMAND_PARSE_ADDRESS(CMD_ARGV[3], *min_address);
if (CMD_ARGC == 5) {
COMMAND_PARSE_ADDRESS(CMD_ARGV[4], *max_address);
/* use size (given) to find max (required) */
*max_address += *min_address;
}
if (*min_address > *max_address)
return ERROR_COMMAND_SYNTAX_ERROR;
return ERROR_OK;
}
COMMAND_HANDLER(handle_load_image_command)
{
uint8_t *buffer;
size_t buf_cnt;
uint32_t image_size;
target_addr_t min_address = 0;
target_addr_t max_address = -1;
int i;
struct image image;
int retval = CALL_COMMAND_HANDLER(parse_load_image_command_CMD_ARGV,
&image, &min_address, &max_address);
if (ERROR_OK != retval)
return retval;
struct target *target = get_current_target(CMD_CTX);
struct duration bench;
duration_start(&bench);
if (image_open(&image, CMD_ARGV[0], (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL) != ERROR_OK)
return ERROR_FAIL;
image_size = 0x0;
retval = ERROR_OK;
for (i = 0; i < image.num_sections; i++) {
buffer = malloc(image.sections[i].size);
if (buffer == NULL) {
command_print(CMD_CTX,
"error allocating buffer for section (%d bytes)",
(int)(image.sections[i].size));
retval = ERROR_FAIL;
break;
}
retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt);
if (retval != ERROR_OK) {
free(buffer);
break;
}
uint32_t offset = 0;
uint32_t length = buf_cnt;
/* DANGER!!! beware of unsigned comparision here!!! */
if ((image.sections[i].base_address + buf_cnt >= min_address) &&
(image.sections[i].base_address < max_address)) {
if (image.sections[i].base_address < min_address) {
/* clip addresses below */
offset += min_address-image.sections[i].base_address;
length -= offset;
}
if (image.sections[i].base_address + buf_cnt > max_address)
length -= (image.sections[i].base_address + buf_cnt)-max_address;
retval = target_write_buffer(target,
image.sections[i].base_address + offset, length, buffer + offset);
if (retval != ERROR_OK) {
free(buffer);
break;
}
image_size += length;
command_print(CMD_CTX, "%u bytes written at address " TARGET_ADDR_FMT "",
(unsigned int)length,
image.sections[i].base_address + offset);
}
free(buffer);
}
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "downloaded %" PRIu32 " bytes "
"in %fs (%0.3f KiB/s)", image_size,
duration_elapsed(&bench), duration_kbps(&bench, image_size));
}
image_close(&image);
return retval;
}
COMMAND_HANDLER(handle_dump_image_command)
{
struct fileio *fileio;
uint8_t *buffer;
int retval, retvaltemp;
target_addr_t address, size;
struct duration bench;
struct target *target = get_current_target(CMD_CTX);
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_ADDRESS(CMD_ARGV[1], address);
COMMAND_PARSE_ADDRESS(CMD_ARGV[2], size);
uint32_t buf_size = (size > 4096) ? 4096 : size;
buffer = malloc(buf_size);
if (!buffer)
return ERROR_FAIL;
retval = fileio_open(&fileio, CMD_ARGV[0], FILEIO_WRITE, FILEIO_BINARY);
if (retval != ERROR_OK) {
free(buffer);
return retval;
}
duration_start(&bench);
while (size > 0) {
size_t size_written;
uint32_t this_run_size = (size > buf_size) ? buf_size : size;
retval = target_read_buffer(target, address, this_run_size, buffer);
if (retval != ERROR_OK)
break;
retval = fileio_write(fileio, this_run_size, buffer, &size_written);
if (retval != ERROR_OK)
break;
size -= this_run_size;
address += this_run_size;
}
free(buffer);
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
size_t filesize;
retval = fileio_size(fileio, &filesize);
if (retval != ERROR_OK)
return retval;
command_print(CMD_CTX,
"dumped %zu bytes in %fs (%0.3f KiB/s)", filesize,
duration_elapsed(&bench), duration_kbps(&bench, filesize));
}
retvaltemp = fileio_close(fileio);
if (retvaltemp != ERROR_OK)
return retvaltemp;
return retval;
}
enum verify_mode {
IMAGE_TEST = 0,
IMAGE_VERIFY = 1,
IMAGE_CHECKSUM_ONLY = 2
};
static COMMAND_HELPER(handle_verify_image_command_internal, enum verify_mode verify)
{
uint8_t *buffer;
size_t buf_cnt;
uint32_t image_size;
int i;
int retval;
uint32_t checksum = 0;
uint32_t mem_checksum = 0;
struct image image;
struct target *target = get_current_target(CMD_CTX);
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
if (!target) {
LOG_ERROR("no target selected");
return ERROR_FAIL;
}
struct duration bench;
duration_start(&bench);
if (CMD_ARGC >= 2) {
target_addr_t addr;
COMMAND_PARSE_ADDRESS(CMD_ARGV[1], addr);
image.base_address = addr;
image.base_address_set = 1;
} else {
image.base_address_set = 0;
image.base_address = 0x0;
}
image.start_address_set = 0;
retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL);
if (retval != ERROR_OK)
return retval;
image_size = 0x0;
int diffs = 0;
retval = ERROR_OK;
for (i = 0; i < image.num_sections; i++) {
buffer = malloc(image.sections[i].size);
if (buffer == NULL) {
command_print(CMD_CTX,
"error allocating buffer for section (%d bytes)",
(int)(image.sections[i].size));
break;
}
retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt);
if (retval != ERROR_OK) {
free(buffer);
break;
}
if (verify >= IMAGE_VERIFY) {
/* calculate checksum of image */
retval = image_calculate_checksum(buffer, buf_cnt, &checksum);
if (retval != ERROR_OK) {
free(buffer);
break;
}
retval = target_checksum_memory(target, image.sections[i].base_address, buf_cnt, &mem_checksum);
if (retval != ERROR_OK) {
free(buffer);
break;
}
if ((checksum != mem_checksum) && (verify == IMAGE_CHECKSUM_ONLY)) {
LOG_ERROR("checksum mismatch");
free(buffer);
retval = ERROR_FAIL;
goto done;
}
if (checksum != mem_checksum) {
/* failed crc checksum, fall back to a binary compare */
uint8_t *data;
if (diffs == 0)
LOG_ERROR("checksum mismatch - attempting binary compare");
data = malloc(buf_cnt);
/* Can we use 32bit word accesses? */
int size = 1;
int count = buf_cnt;
if ((count % 4) == 0) {
size *= 4;
count /= 4;
}
retval = target_read_memory(target, image.sections[i].base_address, size, count, data);
if (retval == ERROR_OK) {
uint32_t t;
for (t = 0; t < buf_cnt; t++) {
if (data[t] != buffer[t]) {
command_print(CMD_CTX,
"diff %d address 0x%08x. Was 0x%02x instead of 0x%02x",
diffs,
(unsigned)(t + image.sections[i].base_address),
data[t],
buffer[t]);
if (diffs++ >= 127) {
command_print(CMD_CTX, "More than 128 errors, the rest are not printed.");
free(data);
free(buffer);
goto done;
}
}
keep_alive();
}
}
free(data);
}
} else {
command_print(CMD_CTX, "address " TARGET_ADDR_FMT " length 0x%08zx",
image.sections[i].base_address,
buf_cnt);
}
free(buffer);
image_size += buf_cnt;
}
if (diffs > 0)
command_print(CMD_CTX, "No more differences found.");
done:
if (diffs > 0)
retval = ERROR_FAIL;
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "verified %" PRIu32 " bytes "
"in %fs (%0.3f KiB/s)", image_size,
duration_elapsed(&bench), duration_kbps(&bench, image_size));
}
image_close(&image);
return retval;
}
COMMAND_HANDLER(handle_verify_image_checksum_command)
{
return CALL_COMMAND_HANDLER(handle_verify_image_command_internal, IMAGE_CHECKSUM_ONLY);
}
COMMAND_HANDLER(handle_verify_image_command)
{
return CALL_COMMAND_HANDLER(handle_verify_image_command_internal, IMAGE_VERIFY);
}
COMMAND_HANDLER(handle_test_image_command)
{
return CALL_COMMAND_HANDLER(handle_verify_image_command_internal, IMAGE_TEST);
}
static int handle_bp_command_list(struct command_context *cmd_ctx)
{
struct target *target = get_current_target(cmd_ctx);
struct breakpoint *breakpoint = target->breakpoints;
while (breakpoint) {
if (breakpoint->type == BKPT_SOFT) {
char *buf = buf_to_str(breakpoint->orig_instr,
breakpoint->length, 16);
command_print(cmd_ctx, "IVA breakpoint: " TARGET_ADDR_FMT ", 0x%x, %i, 0x%s",
breakpoint->address,
breakpoint->length,
breakpoint->set, buf);
free(buf);
} else {
if ((breakpoint->address == 0) && (breakpoint->asid != 0))
command_print(cmd_ctx, "Context breakpoint: 0x%8.8" PRIx32 ", 0x%x, %i",
breakpoint->asid,
breakpoint->length, breakpoint->set);
else if ((breakpoint->address != 0) && (breakpoint->asid != 0)) {
command_print(cmd_ctx, "Hybrid breakpoint(IVA): " TARGET_ADDR_FMT ", 0x%x, %i",
breakpoint->address,
breakpoint->length, breakpoint->set);
command_print(cmd_ctx, "\t|--->linked with ContextID: 0x%8.8" PRIx32,
breakpoint->asid);
} else
command_print(cmd_ctx, "Breakpoint(IVA): " TARGET_ADDR_FMT ", 0x%x, %i",
breakpoint->address,
breakpoint->length, breakpoint->set);
}
breakpoint = breakpoint->next;
}
return ERROR_OK;
}
static int handle_bp_command_set(struct command_context *cmd_ctx,
target_addr_t addr, uint32_t asid, uint32_t length, int hw)
{
struct target *target = get_current_target(cmd_ctx);
int retval;
if (asid == 0) {
retval = breakpoint_add(target, addr, length, hw);
if (ERROR_OK == retval)
command_print(cmd_ctx, "breakpoint set at " TARGET_ADDR_FMT "", addr);
else {
LOG_ERROR("Failure setting breakpoint, the same address(IVA) is already used");
return retval;
}
} else if (addr == 0) {
if (target->type->add_context_breakpoint == NULL) {
LOG_WARNING("Context breakpoint not available");
return ERROR_OK;
}
retval = context_breakpoint_add(target, asid, length, hw);
if (ERROR_OK == retval)
command_print(cmd_ctx, "Context breakpoint set at 0x%8.8" PRIx32 "", asid);
else {
LOG_ERROR("Failure setting breakpoint, the same address(CONTEXTID) is already used");
return retval;
}
} else {
if (target->type->add_hybrid_breakpoint == NULL) {
LOG_WARNING("Hybrid breakpoint not available");
return ERROR_OK;
}
retval = hybrid_breakpoint_add(target, addr, asid, length, hw);
if (ERROR_OK == retval)
command_print(cmd_ctx, "Hybrid breakpoint set at 0x%8.8" PRIx32 "", asid);
else {
LOG_ERROR("Failure setting breakpoint, the same address is already used");
return retval;
}
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_bp_command)
{
target_addr_t addr;
uint32_t asid;
uint32_t length;
int hw = BKPT_SOFT;
switch (CMD_ARGC) {
case 0:
return handle_bp_command_list(CMD_CTX);
case 2:
asid = 0;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
return handle_bp_command_set(CMD_CTX, addr, asid, length, hw);
case 3:
if (strcmp(CMD_ARGV[2], "hw") == 0) {
hw = BKPT_HARD;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
asid = 0;
return handle_bp_command_set(CMD_CTX, addr, asid, length, hw);
} else if (strcmp(CMD_ARGV[2], "hw_ctx") == 0) {
hw = BKPT_HARD;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], asid);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
addr = 0;
return handle_bp_command_set(CMD_CTX, addr, asid, length, hw);
}
/* fallthrough */
case 4:
hw = BKPT_HARD;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], asid);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], length);
return handle_bp_command_set(CMD_CTX, addr, asid, length, hw);
default:
return ERROR_COMMAND_SYNTAX_ERROR;
}
}
COMMAND_HANDLER(handle_rbp_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
target_addr_t addr;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr);
struct target *target = get_current_target(CMD_CTX);
breakpoint_remove(target, addr);
return ERROR_OK;
}
COMMAND_HANDLER(handle_wp_command)
{
struct target *target = get_current_target(CMD_CTX);
if (CMD_ARGC == 0) {
struct watchpoint *watchpoint = target->watchpoints;
while (watchpoint) {
command_print(CMD_CTX, "address: " TARGET_ADDR_FMT
", len: 0x%8.8" PRIx32
", r/w/a: %i, value: 0x%8.8" PRIx32
", mask: 0x%8.8" PRIx32,
watchpoint->address,
watchpoint->length,
(int)watchpoint->rw,
watchpoint->value,
watchpoint->mask);
watchpoint = watchpoint->next;
}
return ERROR_OK;
}
enum watchpoint_rw type = WPT_ACCESS;
uint32_t addr = 0;
uint32_t length = 0;
uint32_t data_value = 0x0;
uint32_t data_mask = 0xffffffff;
switch (CMD_ARGC) {
case 5:
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], data_mask);
/* fall through */
case 4:
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], data_value);
/* fall through */
case 3:
switch (CMD_ARGV[2][0]) {
case 'r':
type = WPT_READ;
break;
case 'w':
type = WPT_WRITE;
break;
case 'a':
type = WPT_ACCESS;
break;
default:
LOG_ERROR("invalid watchpoint mode ('%c')", CMD_ARGV[2][0]);
return ERROR_COMMAND_SYNTAX_ERROR;
}
/* fall through */
case 2:
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], addr);
break;
default:
return ERROR_COMMAND_SYNTAX_ERROR;
}
int retval = watchpoint_add(target, addr, length, type,
data_value, data_mask);
if (ERROR_OK != retval)
LOG_ERROR("Failure setting watchpoints");
return retval;
}
COMMAND_HANDLER(handle_rwp_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
uint32_t addr;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], addr);
struct target *target = get_current_target(CMD_CTX);
watchpoint_remove(target, addr);
return ERROR_OK;
}
/**
* Translate a virtual address to a physical address.
*
* The low-level target implementation must have logged a detailed error
* which is forwarded to telnet/GDB session.
*/
COMMAND_HANDLER(handle_virt2phys_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
target_addr_t va;
COMMAND_PARSE_ADDRESS(CMD_ARGV[0], va);
target_addr_t pa;
struct target *target = get_current_target(CMD_CTX);
int retval = target->type->virt2phys(target, va, &pa);
if (retval == ERROR_OK)
command_print(CMD_CTX, "Physical address " TARGET_ADDR_FMT "", pa);
return retval;
}
static void writeData(FILE *f, const void *data, size_t len)
{
size_t written = fwrite(data, 1, len, f);
if (written != len)
LOG_ERROR("failed to write %zu bytes: %s", len, strerror(errno));
}
static void writeLong(FILE *f, int l, struct target *target)
{
uint8_t val[4];
target_buffer_set_u32(target, val, l);
writeData(f, val, 4);
}
static void writeString(FILE *f, char *s)
{
writeData(f, s, strlen(s));
}
typedef unsigned char UNIT[2]; /* unit of profiling */
/* Dump a gmon.out histogram file. */
static void write_gmon(uint32_t *samples, uint32_t sampleNum, const char *filename, bool with_range,
uint32_t start_address, uint32_t end_address, struct target *target, uint32_t duration_ms)
{
uint32_t i;
FILE *f = fopen(filename, "w");
if (f == NULL)
return;
writeString(f, "gmon");
writeLong(f, 0x00000001, target); /* Version */
writeLong(f, 0, target); /* padding */
writeLong(f, 0, target); /* padding */
writeLong(f, 0, target); /* padding */
uint8_t zero = 0; /* GMON_TAG_TIME_HIST */
writeData(f, &zero, 1);
/* figure out bucket size */
uint32_t min;
uint32_t max;
if (with_range) {
min = start_address;
max = end_address;
} else {
min = samples[0];
max = samples[0];
for (i = 0; i < sampleNum; i++) {
if (min > samples[i])
min = samples[i];
if (max < samples[i])
max = samples[i];
}
/* max should be (largest sample + 1)
* Refer to binutils/gprof/hist.c (find_histogram_for_pc) */
max++;
}
int addressSpace = max - min;
assert(addressSpace >= 2);
/* FIXME: What is the reasonable number of buckets?
* The profiling result will be more accurate if there are enough buckets. */
static const uint32_t maxBuckets = 128 * 1024; /* maximum buckets. */
uint32_t numBuckets = addressSpace / sizeof(UNIT);
if (numBuckets > maxBuckets)
numBuckets = maxBuckets;
int *buckets = malloc(sizeof(int) * numBuckets);
if (buckets == NULL) {
fclose(f);
return;
}
memset(buckets, 0, sizeof(int) * numBuckets);
for (i = 0; i < sampleNum; i++) {
uint32_t address = samples[i];
if ((address < min) || (max <= address))
continue;
long long a = address - min;
long long b = numBuckets;
long long c = addressSpace;
int index_t = (a * b) / c; /* danger!!!! int32 overflows */
buckets[index_t]++;
}
/* append binary memory gmon.out &profile_hist_hdr ((char*)&profile_hist_hdr + sizeof(struct gmon_hist_hdr)) */
writeLong(f, min, target); /* low_pc */
writeLong(f, max, target); /* high_pc */
writeLong(f, numBuckets, target); /* # of buckets */
float sample_rate = sampleNum / (duration_ms / 1000.0);
writeLong(f, sample_rate, target);
writeString(f, "seconds");
for (i = 0; i < (15-strlen("seconds")); i++)
writeData(f, &zero, 1);
writeString(f, "s");
/*append binary memory gmon.out profile_hist_data (profile_hist_data + profile_hist_hdr.hist_size) */
char *data = malloc(2 * numBuckets);
if (data != NULL) {
for (i = 0; i < numBuckets; i++) {
int val;
val = buckets[i];
if (val > 65535)
val = 65535;
data[i * 2] = val&0xff;
data[i * 2 + 1] = (val >> 8) & 0xff;
}
free(buckets);
writeData(f, data, numBuckets * 2);
free(data);
} else
free(buckets);
fclose(f);
}
/* profiling samples the CPU PC as quickly as OpenOCD is able,
* which will be used as a random sampling of PC */
COMMAND_HANDLER(handle_profile_command)
{
struct target *target = get_current_target(CMD_CTX);
if ((CMD_ARGC != 2) && (CMD_ARGC != 4))
return ERROR_COMMAND_SYNTAX_ERROR;
const uint32_t MAX_PROFILE_SAMPLE_NUM = 10000;
uint32_t offset;
uint32_t num_of_samples;
int retval = ERROR_OK;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], offset);
uint32_t *samples = malloc(sizeof(uint32_t) * MAX_PROFILE_SAMPLE_NUM);
if (samples == NULL) {
LOG_ERROR("No memory to store samples.");
return ERROR_FAIL;
}
uint64_t timestart_ms = timeval_ms();
/**
* Some cores let us sample the PC without the
* annoying halt/resume step; for example, ARMv7 PCSR.
* Provide a way to use that more efficient mechanism.
*/
retval = target_profiling(target, samples, MAX_PROFILE_SAMPLE_NUM,
&num_of_samples, offset);
if (retval != ERROR_OK) {
free(samples);
return retval;
}
uint32_t duration_ms = timeval_ms() - timestart_ms;
assert(num_of_samples <= MAX_PROFILE_SAMPLE_NUM);
retval = target_poll(target);
if (retval != ERROR_OK) {
free(samples);
return retval;
}
if (target->state == TARGET_RUNNING) {
retval = target_halt(target);
if (retval != ERROR_OK) {
free(samples);
return retval;
}
}
retval = target_poll(target);
if (retval != ERROR_OK) {
free(samples);
return retval;
}
uint32_t start_address = 0;
uint32_t end_address = 0;
bool with_range = false;
if (CMD_ARGC == 4) {
with_range = true;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], start_address);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], end_address);
}
write_gmon(samples, num_of_samples, CMD_ARGV[1],
with_range, start_address, end_address, target, duration_ms);
command_print(CMD_CTX, "Wrote %s", CMD_ARGV[1]);
free(samples);
return retval;
}
static int new_int_array_element(Jim_Interp *interp, const char *varname, int idx, uint32_t val)
{
char *namebuf;
Jim_Obj *nameObjPtr, *valObjPtr;
int result;
namebuf = alloc_printf("%s(%d)", varname, idx);
if (!namebuf)
return JIM_ERR;
nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
valObjPtr = Jim_NewIntObj(interp, val);
if (!nameObjPtr || !valObjPtr) {
free(namebuf);
return JIM_ERR;
}
Jim_IncrRefCount(nameObjPtr);
Jim_IncrRefCount(valObjPtr);
result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
Jim_DecrRefCount(interp, valObjPtr);
free(namebuf);
/* printf("%s(%d) <= 0%08x\n", varname, idx, val); */
return result;
}
static int jim_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
struct command_context *context;
struct target *target;
context = current_command_context(interp);
assert(context != NULL);
target = get_current_target(context);
if (target == NULL) {
LOG_ERROR("mem2array: no current target");
return JIM_ERR;
}
return target_mem2array(interp, target, argc - 1, argv + 1);
}
static int target_mem2array(Jim_Interp *interp, struct target *target, int argc, Jim_Obj *const *argv)
{
long l;
uint32_t width;
int len;
uint32_t addr;
uint32_t count;
uint32_t v;
const char *varname;
const char *phys;
bool is_phys;
int n, e, retval;
uint32_t i;
/* argv[1] = name of array to receive the data
* argv[2] = desired width
* argv[3] = memory address
* argv[4] = count of times to read
*/
if (argc < 4 || argc > 5) {
Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems [phys]");
return JIM_ERR;
}
varname = Jim_GetString(argv[0], &len);
/* given "foo" get space for worse case "foo(%d)" .. add 20 */
e = Jim_GetLong(interp, argv[1], &l);
width = l;
if (e != JIM_OK)
return e;
e = Jim_GetLong(interp, argv[2], &l);
addr = l;
if (e != JIM_OK)
return e;
e = Jim_GetLong(interp, argv[3], &l);
len = l;
if (e != JIM_OK)
return e;
is_phys = false;
if (argc > 4) {
phys = Jim_GetString(argv[4], &n);
if (!strncmp(phys, "phys", n))
is_phys = true;
else
return JIM_ERR;
}
switch (width) {
case 8:
width = 1;
break;
case 16:
width = 2;
break;
case 32:
width = 4;
break;
default:
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp), "Invalid width param, must be 8/16/32", NULL);
return JIM_ERR;
}
if (len == 0) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: zero width read?", NULL);
return JIM_ERR;
}
if ((addr + (len * width)) < addr) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: addr + len - wraps to zero?", NULL);
return JIM_ERR;
}
/* absurd transfer size? */
if (len > 65536) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: absurd > 64K item request", NULL);
return JIM_ERR;
}
if ((width == 1) ||
((width == 2) && ((addr & 1) == 0)) ||
((width == 4) && ((addr & 3) == 0))) {
/* all is well */
} else {
char buf[100];
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
sprintf(buf, "mem2array address: 0x%08" PRIx32 " is not aligned for %" PRId32 " byte reads",
addr,
width);
Jim_AppendStrings(interp, Jim_GetResult(interp), buf, NULL);
return JIM_ERR;
}
/* Transfer loop */
/* index counter */
n = 0;
size_t buffersize = 4096;
uint8_t *buffer = malloc(buffersize);
if (buffer == NULL)
return JIM_ERR;
/* assume ok */
e = JIM_OK;
while (len) {
/* Slurp... in buffer size chunks */
count = len; /* in objects.. */
if (count > (buffersize / width))
count = (buffersize / width);
if (is_phys)
retval = target_read_phys_memory(target, addr, width, count, buffer);
else
retval = target_read_memory(target, addr, width, count, buffer);
if (retval != ERROR_OK) {
/* BOO !*/
LOG_ERROR("mem2array: Read @ 0x%08" PRIx32 ", w=%" PRId32 ", cnt=%" PRId32 ", failed",
addr,
width,
count);
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: cannot read memory", NULL);
e = JIM_ERR;
break;
} else {
v = 0; /* shut up gcc */
for (i = 0; i < count ; i++, n++) {
switch (width) {
case 4:
v = target_buffer_get_u32(target, &buffer[i*width]);
break;
case 2:
v = target_buffer_get_u16(target, &buffer[i*width]);
break;
case 1:
v = buffer[i] & 0x0ff;
break;
}
new_int_array_element(interp, varname, n, v);
}
len -= count;
addr += count * width;
}
}
free(buffer);
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
return e;
}
static int get_int_array_element(Jim_Interp *interp, const char *varname, int idx, uint32_t *val)
{
char *namebuf;
Jim_Obj *nameObjPtr, *valObjPtr;
int result;
long l;
namebuf = alloc_printf("%s(%d)", varname, idx);
if (!namebuf)
return JIM_ERR;
nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
if (!nameObjPtr) {
free(namebuf);
return JIM_ERR;
}
Jim_IncrRefCount(nameObjPtr);
valObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_ERRMSG);
Jim_DecrRefCount(interp, nameObjPtr);
free(namebuf);
if (valObjPtr == NULL)
return JIM_ERR;
result = Jim_GetLong(interp, valObjPtr, &l);
/* printf("%s(%d) => 0%08x\n", varname, idx, val); */
*val = l;
return result;
}
static int jim_array2mem(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
struct command_context *context;
struct target *target;
context = current_command_context(interp);
assert(context != NULL);
target = get_current_target(context);
if (target == NULL) {
LOG_ERROR("array2mem: no current target");
return JIM_ERR;
}
return target_array2mem(interp, target, argc-1, argv + 1);
}
static int target_array2mem(Jim_Interp *interp, struct target *target,
int argc, Jim_Obj *const *argv)
{
long l;
uint32_t width;
int len;
uint32_t addr;
uint32_t count;
uint32_t v;
const char *varname;
const char *phys;
bool is_phys;
int n, e, retval;
uint32_t i;
/* argv[1] = name of array to get the data
* argv[2] = desired width
* argv[3] = memory address
* argv[4] = count to write
*/
if (argc < 4 || argc > 5) {
Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems [phys]");
return JIM_ERR;
}
varname = Jim_GetString(argv[0], &len);
/* given "foo" get space for worse case "foo(%d)" .. add 20 */
e = Jim_GetLong(interp, argv[1], &l);
width = l;
if (e != JIM_OK)
return e;
e = Jim_GetLong(interp, argv[2], &l);
addr = l;
if (e != JIM_OK)
return e;
e = Jim_GetLong(interp, argv[3], &l);
len = l;
if (e != JIM_OK)
return e;
is_phys = false;
if (argc > 4) {
phys = Jim_GetString(argv[4], &n);
if (!strncmp(phys, "phys", n))
is_phys = true;
else
return JIM_ERR;
}
switch (width) {
case 8:
width = 1;
break;
case 16:
width = 2;
break;
case 32:
width = 4;
break;
default:
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp),
"Invalid width param, must be 8/16/32", NULL);
return JIM_ERR;
}
if (len == 0) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp),
"array2mem: zero width read?", NULL);
return JIM_ERR;
}
if ((addr + (len * width)) < addr) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp),
"array2mem: addr + len - wraps to zero?", NULL);
return JIM_ERR;
}
/* absurd transfer size? */
if (len > 65536) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp),
"array2mem: absurd > 64K item request", NULL);
return JIM_ERR;
}
if ((width == 1) ||
((width == 2) && ((addr & 1) == 0)) ||
((width == 4) && ((addr & 3) == 0))) {
/* all is well */
} else {
char buf[100];
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
sprintf(buf, "array2mem address: 0x%08" PRIx32 " is not aligned for %" PRId32 " byte reads",
addr,
width);
Jim_AppendStrings(interp, Jim_GetResult(interp), buf, NULL);
return JIM_ERR;
}
/* Transfer loop */
/* index counter */
n = 0;
/* assume ok */
e = JIM_OK;
size_t buffersize = 4096;
uint8_t *buffer = malloc(buffersize);
if (buffer == NULL)
return JIM_ERR;
while (len) {
/* Slurp... in buffer size chunks */
count = len; /* in objects.. */
if (count > (buffersize / width))
count = (buffersize / width);
v = 0; /* shut up gcc */
for (i = 0; i < count; i++, n++) {
get_int_array_element(interp, varname, n, &v);
switch (width) {
case 4:
target_buffer_set_u32(target, &buffer[i * width], v);
break;
case 2:
target_buffer_set_u16(target, &buffer[i * width], v);
break;
case 1:
buffer[i] = v & 0x0ff;
break;
}
}
len -= count;
if (is_phys)
retval = target_write_phys_memory(target, addr, width, count, buffer);
else
retval = target_write_memory(target, addr, width, count, buffer);
if (retval != ERROR_OK) {
/* BOO !*/
LOG_ERROR("array2mem: Write @ 0x%08" PRIx32 ", w=%" PRId32 ", cnt=%" PRId32 ", failed",
addr,
width,
count);
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp), "array2mem: cannot read memory", NULL);
e = JIM_ERR;
break;
}
addr += count * width;
}
free(buffer);
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
return e;
}
/* FIX? should we propagate errors here rather than printing them
* and continuing?
*/
void target_handle_event(struct target *target, enum target_event e)
{
struct target_event_action *teap;
for (teap = target->event_action; teap != NULL; teap = teap->next) {
if (teap->event == e) {
LOG_DEBUG("target(%d): %s (%s) event: %d (%s) action: %s",
target->target_number,
target_name(target),
target_type_name(target),
e,
Jim_Nvp_value2name_simple(nvp_target_event, e)->name,
Jim_GetString(teap->body, NULL));
/* Override current target by the target an event
* is issued from (lot of scripts need it).
* Return back to previous override as soon
* as the handler processing is done */
struct command_context *cmd_ctx = current_command_context(teap->interp);
struct target *saved_target_override = cmd_ctx->current_target_override;
cmd_ctx->current_target_override = target;
if (Jim_EvalObj(teap->interp, teap->body) != JIM_OK) {
Jim_MakeErrorMessage(teap->interp);
command_print(NULL, "%s\n", Jim_GetString(Jim_GetResult(teap->interp), NULL));
}
cmd_ctx->current_target_override = saved_target_override;
}
}
}
/**
* Returns true only if the target has a handler for the specified event.
*/
bool target_has_event_action(struct target *target, enum target_event event)
{
struct target_event_action *teap;
for (teap = target->event_action; teap != NULL; teap = teap->next) {
if (teap->event == event)
return true;
}
return false;
}
enum target_cfg_param {
TCFG_TYPE,
TCFG_EVENT,
TCFG_WORK_AREA_VIRT,
TCFG_WORK_AREA_PHYS,
TCFG_WORK_AREA_SIZE,
TCFG_WORK_AREA_BACKUP,
TCFG_ENDIAN,
TCFG_COREID,
TCFG_CHAIN_POSITION,
TCFG_DBGBASE,
TCFG_RTOS,
TCFG_DEFER_EXAMINE,
TCFG_GDB_PORT,
};
static Jim_Nvp nvp_config_opts[] = {
{ .name = "-type", .value = TCFG_TYPE },
{ .name = "-event", .value = TCFG_EVENT },
{ .name = "-work-area-virt", .value = TCFG_WORK_AREA_VIRT },
{ .name = "-work-area-phys", .value = TCFG_WORK_AREA_PHYS },
{ .name = "-work-area-size", .value = TCFG_WORK_AREA_SIZE },
{ .name = "-work-area-backup", .value = TCFG_WORK_AREA_BACKUP },
{ .name = "-endian" , .value = TCFG_ENDIAN },
{ .name = "-coreid", .value = TCFG_COREID },
{ .name = "-chain-position", .value = TCFG_CHAIN_POSITION },
{ .name = "-dbgbase", .value = TCFG_DBGBASE },
{ .name = "-rtos", .value = TCFG_RTOS },
{ .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE },
{ .name = "-gdb-port", .value = TCFG_GDB_PORT },
{ .name = NULL, .value = -1 }
};
static int target_configure(Jim_GetOptInfo *goi, struct target *target)
{
Jim_Nvp *n;
Jim_Obj *o;
jim_wide w;
int e;
/* parse config or cget options ... */
while (goi->argc > 0) {
Jim_SetEmptyResult(goi->interp);
/* Jim_GetOpt_Debug(goi); */
if (target->type->target_jim_configure) {
/* target defines a configure function */
/* target gets first dibs on parameters */
e = (*(target->type->target_jim_configure))(target, goi);
if (e == JIM_OK) {
/* more? */
continue;
}
if (e == JIM_ERR) {
/* An error */
return e;
}
/* otherwise we 'continue' below */
}
e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n);
if (e != JIM_OK) {
Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0);
return e;
}
switch (n->value) {
case TCFG_TYPE:
/* not setable */
if (goi->isconfigure) {
Jim_SetResultFormatted(goi->interp,
"not settable: %s", n->name);
return JIM_ERR;
} else {
no_params:
if (goi->argc != 0) {
Jim_WrongNumArgs(goi->interp,
goi->argc, goi->argv,
"NO PARAMS");
return JIM_ERR;
}
}
Jim_SetResultString(goi->interp,
target_type_name(target), -1);
/* loop for more */
break;
case TCFG_EVENT:
if (goi->argc == 0) {
Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ...");
return JIM_ERR;
}
e = Jim_GetOpt_Nvp(goi, nvp_target_event, &n);
if (e != JIM_OK) {
Jim_GetOpt_NvpUnknown(goi, nvp_target_event, 1);
return e;
}
if (goi->isconfigure) {
if (goi->argc != 1) {
Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ?EVENT-BODY?");
return JIM_ERR;
}
} else {
if (goi->argc != 0) {
Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name?");
return JIM_ERR;
}
}
{
struct target_event_action *teap;
teap = target->event_action;
/* replace existing? */
while (teap) {
if (teap->event == (enum target_event)n->value)
break;
teap = teap->next;
}
if (goi->isconfigure) {
bool replace = true;
if (teap == NULL) {
/* create new */
teap = calloc(1, sizeof(*teap));
replace = false;
}
teap->event = n->value;
teap->interp = goi->interp;
Jim_GetOpt_Obj(goi, &o);
if (teap->body)
Jim_DecrRefCount(teap->interp, teap->body);
teap->body = Jim_DuplicateObj(goi->interp, o);
/*
* FIXME:
* Tcl/TK - "tk events" have a nice feature.
* See the "BIND" command.
* We should support that here.
* You can specify %X and %Y in the event code.
* The idea is: %T - target name.
* The idea is: %N - target number
* The idea is: %E - event name.
*/
Jim_IncrRefCount(teap->body);
if (!replace) {
/* add to head of event list */
teap->next = target->event_action;
target->event_action = teap;
}
Jim_SetEmptyResult(goi->interp);
} else {
/* get */
if (teap == NULL)
Jim_SetEmptyResult(goi->interp);
else
Jim_SetResult(goi->interp, Jim_DuplicateObj(goi->interp, teap->body));
}
}
/* loop for more */
break;
case TCFG_WORK_AREA_VIRT:
if (goi->isconfigure) {
target_free_all_working_areas(target);
e = Jim_GetOpt_Wide(goi, &w);
if (e != JIM_OK)
return e;
target->working_area_virt = w;
target->working_area_virt_spec = true;
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_virt));
/* loop for more */
break;
case TCFG_WORK_AREA_PHYS:
if (goi->isconfigure) {
target_free_all_working_areas(target);
e = Jim_GetOpt_Wide(goi, &w);
if (e != JIM_OK)
return e;
target->working_area_phys = w;
target->working_area_phys_spec = true;
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_phys));
/* loop for more */
break;
case TCFG_WORK_AREA_SIZE:
if (goi->isconfigure) {
target_free_all_working_areas(target);
e = Jim_GetOpt_Wide(goi, &w);
if (e != JIM_OK)
return e;
target->working_area_size = w;
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_size));
/* loop for more */
break;
case TCFG_WORK_AREA_BACKUP:
if (goi->isconfigure) {
target_free_all_working_areas(target);
e = Jim_GetOpt_Wide(goi, &w);
if (e != JIM_OK)
return e;
/* make this exactly 1 or 0 */
target->backup_working_area = (!!w);
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->backup_working_area));
/* loop for more e*/
break;
case TCFG_ENDIAN:
if (goi->isconfigure) {
e = Jim_GetOpt_Nvp(goi, nvp_target_endian, &n);
if (e != JIM_OK) {
Jim_GetOpt_NvpUnknown(goi, nvp_target_endian, 1);
return e;
}
target->endianness = n->value;
} else {
if (goi->argc != 0)
goto no_params;
}
n = Jim_Nvp_value2name_simple(nvp_target_endian, target->endianness);
if (n->name == NULL) {
target->endianness = TARGET_LITTLE_ENDIAN;
n = Jim_Nvp_value2name_simple(nvp_target_endian, target->endianness);
}
Jim_SetResultString(goi->interp, n->name, -1);
/* loop for more */
break;
case TCFG_COREID:
if (goi->isconfigure) {
e = Jim_GetOpt_Wide(goi, &w);
if (e != JIM_OK)
return e;
target->coreid = (int32_t)w;
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_size));
/* loop for more */
break;
case TCFG_CHAIN_POSITION:
if (goi->isconfigure) {
Jim_Obj *o_t;
struct jtag_tap *tap;
if (target->has_dap) {
Jim_SetResultString(goi->interp,
"target requires -dap parameter instead of -chain-position!", -1);
return JIM_ERR;
}
target_free_all_working_areas(target);
e = Jim_GetOpt_Obj(goi, &o_t);
if (e != JIM_OK)
return e;
tap = jtag_tap_by_jim_obj(goi->interp, o_t);
if (tap == NULL)
return JIM_ERR;
target->tap = tap;
target->tap_configured = true;
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResultString(goi->interp, target->tap->dotted_name, -1);
/* loop for more e*/
break;
case TCFG_DBGBASE:
if (goi->isconfigure) {
e = Jim_GetOpt_Wide(goi, &w);
if (e != JIM_OK)
return e;
target->dbgbase = (uint32_t)w;
target->dbgbase_set = true;
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->dbgbase));
/* loop for more */
break;
case TCFG_RTOS:
/* RTOS */
{
int result = rtos_create(goi, target);
if (result != JIM_OK)
return result;
}
/* loop for more */
break;
case TCFG_DEFER_EXAMINE:
/* DEFER_EXAMINE */
target->defer_examine = true;
/* loop for more */
break;
case TCFG_GDB_PORT:
if (goi->isconfigure) {
const char *s;
e = Jim_GetOpt_String(goi, &s, NULL);
if (e != JIM_OK)
return e;
target->gdb_port_override = strdup(s);
} else {
if (goi->argc != 0)
goto no_params;
}
Jim_SetResultString(goi->interp, target->gdb_port_override ? : "undefined", -1);
/* loop for more */
break;
}
} /* while (goi->argc) */
/* done - we return */
return JIM_OK;
}
static int jim_target_configure(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
goi.isconfigure = !strcmp(Jim_GetString(argv[0], NULL), "configure");
if (goi.argc < 1) {
Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
"missing: -option ...");
return JIM_ERR;
}
struct target *target = Jim_CmdPrivData(goi.interp);
return target_configure(&goi, target);
}
static int jim_target_mw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *cmd_name = Jim_GetString(argv[0], NULL);
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
if (goi.argc < 2 || goi.argc > 4) {
Jim_SetResultFormatted(goi.interp,
"usage: %s [phys] <address> <data> [<count>]", cmd_name);
return JIM_ERR;
}
target_write_fn fn;
fn = target_write_memory;
int e;
if (strcmp(Jim_GetString(argv[1], NULL), "phys") == 0) {
/* consume it */
struct Jim_Obj *obj;
e = Jim_GetOpt_Obj(&goi, &obj);
if (e != JIM_OK)
return e;
fn = target_write_phys_memory;
}
jim_wide a;
e = Jim_GetOpt_Wide(&goi, &a);
if (e != JIM_OK)
return e;
jim_wide b;
e = Jim_GetOpt_Wide(&goi, &b);
if (e != JIM_OK)
return e;
jim_wide c = 1;
if (goi.argc == 1) {
e = Jim_GetOpt_Wide(&goi, &c);
if (e != JIM_OK)
return e;
}
/* all args must be consumed */
if (goi.argc != 0)
return JIM_ERR;
struct target *target = Jim_CmdPrivData(goi.interp);
unsigned data_size;
if (strcasecmp(cmd_name, "mww") == 0)
data_size = 4;
else if (strcasecmp(cmd_name, "mwh") == 0)
data_size = 2;
else if (strcasecmp(cmd_name, "mwb") == 0)
data_size = 1;
else {
LOG_ERROR("command '%s' unknown: ", cmd_name);
return JIM_ERR;
}
return (target_fill_mem(target, a, fn, data_size, b, c) == ERROR_OK) ? JIM_OK : JIM_ERR;
}
/**
* @brief Reads an array of words/halfwords/bytes from target memory starting at specified address.
*
* Usage: mdw [phys] <address> [<count>] - for 32 bit reads
* mdh [phys] <address> [<count>] - for 16 bit reads
* mdb [phys] <address> [<count>] - for 8 bit reads
*
* Count defaults to 1.
*
* Calls target_read_memory or target_read_phys_memory depending on
* the presence of the "phys" argument
* Reads the target memory in blocks of max. 32 bytes, and returns an array of ints formatted
* to int representation in base16.
* Also outputs read data in a human readable form using command_print
*
* @param phys if present target_read_phys_memory will be used instead of target_read_memory
* @param address address where to start the read. May be specified in decimal or hex using the standard "0x" prefix
* @param count optional count parameter to read an array of values. If not specified, defaults to 1.
* @returns: JIM_ERR on error or JIM_OK on success and sets the result string to an array of ascii formatted numbers
* on success, with [<count>] number of elements.
*
* In case of little endian target:
* Example1: "mdw 0x00000000" returns "10123456"
* Exmaple2: "mdh 0x00000000 1" returns "3456"
* Example3: "mdb 0x00000000" returns "56"
* Example4: "mdh 0x00000000 2" returns "3456 1012"
* Example5: "mdb 0x00000000 3" returns "56 34 12"
**/
static int jim_target_md(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *cmd_name = Jim_GetString(argv[0], NULL);
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
if ((goi.argc < 1) || (goi.argc > 3)) {
Jim_SetResultFormatted(goi.interp,
"usage: %s [phys] <address> [<count>]", cmd_name);
return JIM_ERR;
}
int (*fn)(struct target *target,
target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer);
fn = target_read_memory;
int e;
if (strcmp(Jim_GetString(argv[1], NULL), "phys") == 0) {
/* consume it */
struct Jim_Obj *obj;
e = Jim_GetOpt_Obj(&goi, &obj);
if (e != JIM_OK)
return e;
fn = target_read_phys_memory;
}
/* Read address parameter */
jim_wide addr;
e = Jim_GetOpt_Wide(&goi, &addr);
if (e != JIM_OK)
return JIM_ERR;
/* If next parameter exists, read it out as the count parameter, if not, set it to 1 (default) */
jim_wide count;
if (goi.argc == 1) {
e = Jim_GetOpt_Wide(&goi, &count);
if (e != JIM_OK)
return JIM_ERR;
} else
count = 1;
/* all args must be consumed */
if (goi.argc != 0)
return JIM_ERR;
jim_wide dwidth = 1; /* shut up gcc */
if (strcasecmp(cmd_name, "mdw") == 0)
dwidth = 4;
else if (strcasecmp(cmd_name, "mdh") == 0)
dwidth = 2;
else if (strcasecmp(cmd_name, "mdb") == 0)
dwidth = 1;
else {
LOG_ERROR("command '%s' unknown: ", cmd_name);
return JIM_ERR;
}
/* convert count to "bytes" */
int bytes = count * dwidth;
struct target *target = Jim_CmdPrivData(goi.interp);
uint8_t target_buf[32];
jim_wide x, y, z;
while (bytes > 0) {
y = (bytes < 16) ? bytes : 16; /* y = min(bytes, 16); */
/* Try to read out next block */
e = fn(target, addr, dwidth, y / dwidth, target_buf);
if (e != ERROR_OK) {
Jim_SetResultFormatted(interp, "error reading target @ 0x%08lx", (long)addr);
return JIM_ERR;
}
command_print_sameline(NULL, "0x%08x ", (int)(addr));
switch (dwidth) {
case 4:
for (x = 0; x < 16 && x < y; x += 4) {
z = target_buffer_get_u32(target, &(target_buf[x]));
command_print_sameline(NULL, "%08x ", (int)(z));
}
for (; (x < 16) ; x += 4)
command_print_sameline(NULL, " ");
break;
case 2:
for (x = 0; x < 16 && x < y; x += 2) {
z = target_buffer_get_u16(target, &(target_buf[x]));
command_print_sameline(NULL, "%04x ", (int)(z));
}
for (; (x < 16) ; x += 2)
command_print_sameline(NULL, " ");
break;
case 1:
default:
for (x = 0 ; (x < 16) && (x < y) ; x += 1) {
z = target_buffer_get_u8(target, &(target_buf[x]));
command_print_sameline(NULL, "%02x ", (int)(z));
}
for (; (x < 16) ; x += 1)
command_print_sameline(NULL, " ");
break;
}
/* ascii-ify the bytes */
for (x = 0 ; x < y ; x++) {
if ((target_buf[x] >= 0x20) &&
(target_buf[x] <= 0x7e)) {
/* good */
} else {
/* smack it */
target_buf[x] = '.';
}
}
/* space pad */
while (x < 16) {
target_buf[x] = ' ';
x++;
}
/* terminate */
target_buf[16] = 0;
/* print - with a newline */
command_print_sameline(NULL, "%s\n", target_buf);
/* NEXT... */
bytes -= 16;
addr += 16;
}
return JIM_OK;
}
static int jim_target_mem2array(Jim_Interp *interp,
int argc, Jim_Obj *const *argv)
{
struct target *target = Jim_CmdPrivData(interp);
return target_mem2array(interp, target, argc - 1, argv + 1);
}
static int jim_target_array2mem(Jim_Interp *interp,
int argc, Jim_Obj *const *argv)
{
struct target *target = Jim_CmdPrivData(interp);
return target_array2mem(interp, target, argc - 1, argv + 1);
}
static int jim_target_tap_disabled(Jim_Interp *interp)
{
Jim_SetResultFormatted(interp, "[TAP is disabled]");
return JIM_ERR;
}
static int jim_target_examine(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
bool allow_defer = false;
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
if (goi.argc > 1) {
const char *cmd_name = Jim_GetString(argv[0], NULL);
Jim_SetResultFormatted(goi.interp,
"usage: %s ['allow-defer']", cmd_name);
return JIM_ERR;
}
if (goi.argc > 0 &&
strcmp(Jim_GetString(argv[1], NULL), "allow-defer") == 0) {
/* consume it */
struct Jim_Obj *obj;
int e = Jim_GetOpt_Obj(&goi, &obj);
if (e != JIM_OK)
return e;
allow_defer = true;
}
struct target *target = Jim_CmdPrivData(interp);
if (!target->tap->enabled)
return jim_target_tap_disabled(interp);
if (allow_defer && target->defer_examine) {
LOG_INFO("Deferring arp_examine of %s", target_name(target));
LOG_INFO("Use arp_examine command to examine it manually!");
return JIM_OK;
}
int e = target->type->examine(target);
if (e != ERROR_OK)
return JIM_ERR;
return JIM_OK;
}
static int jim_target_was_examined(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
struct target *target = Jim_CmdPrivData(interp);
Jim_SetResultBool(interp, target_was_examined(target));
return JIM_OK;
}
static int jim_target_examine_deferred(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
struct target *target = Jim_CmdPrivData(interp);
Jim_SetResultBool(interp, target->defer_examine);
return JIM_OK;
}
static int jim_target_halt_gdb(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "[no parameters]");
return JIM_ERR;
}
struct target *target = Jim_CmdPrivData(interp);
if (target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT) != ERROR_OK)
return JIM_ERR;
return JIM_OK;
}
static int jim_target_poll(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "[no parameters]");
return JIM_ERR;
}
struct target *target = Jim_CmdPrivData(interp);
if (!target->tap->enabled)
return jim_target_tap_disabled(interp);
int e;
if (!(target_was_examined(target)))
e = ERROR_TARGET_NOT_EXAMINED;
else
e = target->type->poll(target);
if (e != ERROR_OK)
return JIM_ERR;
return JIM_OK;
}
static int jim_target_reset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
if (goi.argc != 2) {
Jim_WrongNumArgs(interp, 0, argv,
"([tT]|[fF]|assert|deassert) BOOL");
return JIM_ERR;
}
Jim_Nvp *n;
int e = Jim_GetOpt_Nvp(&goi, nvp_assert, &n);
if (e != JIM_OK) {
Jim_GetOpt_NvpUnknown(&goi, nvp_assert, 1);
return e;
}
/* the halt or not param */
jim_wide a;
e = Jim_GetOpt_Wide(&goi, &a);
if (e != JIM_OK)
return e;
struct target *target = Jim_CmdPrivData(goi.interp);
if (!target->tap->enabled)
return jim_target_tap_disabled(interp);
if (!target->type->assert_reset || !target->type->deassert_reset) {
Jim_SetResultFormatted(interp,
"No target-specific reset for %s",
target_name(target));
return JIM_ERR;
}
if (target->defer_examine)
target_reset_examined(target);
/* determine if we should halt or not. */
target->reset_halt = !!a;
/* When this happens - all workareas are invalid. */
target_free_all_working_areas_restore(target, 0);
/* do the assert */
if (n->value == NVP_ASSERT)
e = target->type->assert_reset(target);
else
e = target->type->deassert_reset(target);
return (e == ERROR_OK) ? JIM_OK : JIM_ERR;
}
static int jim_target_halt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "[no parameters]");
return JIM_ERR;
}
struct target *target = Jim_CmdPrivData(interp);
if (!target->tap->enabled)
return jim_target_tap_disabled(interp);
int e = target->type->halt(target);
return (e == ERROR_OK) ? JIM_OK : JIM_ERR;
}
static int jim_target_wait_state(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
/* params: <name> statename timeoutmsecs */
if (goi.argc != 2) {
const char *cmd_name = Jim_GetString(argv[0], NULL);
Jim_SetResultFormatted(goi.interp,
"%s <state_name> <timeout_in_msec>", cmd_name);
return JIM_ERR;
}
Jim_Nvp *n;
int e = Jim_GetOpt_Nvp(&goi, nvp_target_state, &n);
if (e != JIM_OK) {
Jim_GetOpt_NvpUnknown(&goi, nvp_target_state, 1);
return e;
}
jim_wide a;
e = Jim_GetOpt_Wide(&goi, &a);
if (e != JIM_OK)
return e;
struct target *target = Jim_CmdPrivData(interp);
if (!target->tap->enabled)
return jim_target_tap_disabled(interp);
e = target_wait_state(target, n->value, a);
if (e != ERROR_OK) {
Jim_Obj *eObj = Jim_NewIntObj(interp, e);
Jim_SetResultFormatted(goi.interp,
"target: %s wait %s fails (%#s) %s",
target_name(target), n->name,
eObj, target_strerror_safe(e));
Jim_FreeNewObj(interp, eObj);
return JIM_ERR;
}
return JIM_OK;
}
/* List for human, Events defined for this target.
* scripts/programs should use 'name cget -event NAME'
*/
static int jim_target_event_list(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
struct command_context *cmd_ctx = current_command_context(interp);
assert(cmd_ctx != NULL);
struct target *target = Jim_CmdPrivData(interp);
struct target_event_action *teap = target->event_action;
command_print(cmd_ctx, "Event actions for target (%d) %s\n",
target->target_number,
target_name(target));
command_print(cmd_ctx, "%-25s | Body", "Event");
command_print(cmd_ctx, "------------------------- | "
"----------------------------------------");
while (teap) {
Jim_Nvp *opt = Jim_Nvp_value2name_simple(nvp_target_event, teap->event);
command_print(cmd_ctx, "%-25s | %s",
opt->name, Jim_GetString(teap->body, NULL));
teap = teap->next;
}
command_print(cmd_ctx, "***END***");
return JIM_OK;
}
static int jim_target_current_state(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "[no parameters]");
return JIM_ERR;
}
struct target *target = Jim_CmdPrivData(interp);
Jim_SetResultString(interp, target_state_name(target), -1);
return JIM_OK;
}
static int jim_target_invoke_event(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
if (goi.argc != 1) {
const char *cmd_name = Jim_GetString(argv[0], NULL);
Jim_SetResultFormatted(goi.interp, "%s <eventname>", cmd_name);
return JIM_ERR;
}
Jim_Nvp *n;
int e = Jim_GetOpt_Nvp(&goi, nvp_target_event, &n);
if (e != JIM_OK) {
Jim_GetOpt_NvpUnknown(&goi, nvp_target_event, 1);
return e;
}
struct target *target = Jim_CmdPrivData(interp);
target_handle_event(target, n->value);
return JIM_OK;
}
static const struct command_registration target_instance_command_handlers[] = {
{
.name = "configure",
.mode = COMMAND_CONFIG,
.jim_handler = jim_target_configure,
.help = "configure a new target for use",
.usage = "[target_attribute ...]",
},
{
.name = "cget",
.mode = COMMAND_ANY,
.jim_handler = jim_target_configure,
.help = "returns the specified target attribute",
.usage = "target_attribute",
},
{
.name = "mww",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_mw,
.help = "Write 32-bit word(s) to target memory",
.usage = "address data [count]",
},
{
.name = "mwh",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_mw,
.help = "Write 16-bit half-word(s) to target memory",
.usage = "address data [count]",
},
{
.name = "mwb",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_mw,
.help = "Write byte(s) to target memory",
.usage = "address data [count]",
},
{
.name = "mdw",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_md,
.help = "Display target memory as 32-bit words",
.usage = "address [count]",
},
{
.name = "mdh",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_md,
.help = "Display target memory as 16-bit half-words",
.usage = "address [count]",
},
{
.name = "mdb",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_md,
.help = "Display target memory as 8-bit bytes",
.usage = "address [count]",
},
{
.name = "array2mem",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_array2mem,
.help = "Writes Tcl array of 8/16/32 bit numbers "
"to target memory",
.usage = "arrayname bitwidth address count",
},
{
.name = "mem2array",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_mem2array,
.help = "Loads Tcl array of 8/16/32 bit numbers "
"from target memory",
.usage = "arrayname bitwidth address count",
},
{
.name = "eventlist",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_event_list,
.help = "displays a table of events defined for this target",
},
{
.name = "curstate",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_current_state,
.help = "displays the current state of this target",
},
{
.name = "arp_examine",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_examine,
.help = "used internally for reset processing",
.usage = "['allow-defer']",
},
{
.name = "was_examined",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_was_examined,
.help = "used internally for reset processing",
},
{
.name = "examine_deferred",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_examine_deferred,
.help = "used internally for reset processing",
},
{
.name = "arp_halt_gdb",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_halt_gdb,
.help = "used internally for reset processing to halt GDB",
},
{
.name = "arp_poll",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_poll,
.help = "used internally for reset processing",
},
{
.name = "arp_reset",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_reset,
.help = "used internally for reset processing",
},
{
.name = "arp_halt",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_halt,
.help = "used internally for reset processing",
},
{
.name = "arp_waitstate",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_wait_state,
.help = "used internally for reset processing",
},
{
.name = "invoke-event",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_invoke_event,
.help = "invoke handler for specified event",
.usage = "event_name",
},
COMMAND_REGISTRATION_DONE
};
static int target_create(Jim_GetOptInfo *goi)
{
Jim_Obj *new_cmd;
Jim_Cmd *cmd;
const char *cp;
int e;
int x;
struct target *target;
struct command_context *cmd_ctx;
cmd_ctx = current_command_context(goi->interp);
assert(cmd_ctx != NULL);
if (goi->argc < 3) {
Jim_WrongNumArgs(goi->interp, 1, goi->argv, "?name? ?type? ..options...");
return JIM_ERR;
}
/* COMMAND */
Jim_GetOpt_Obj(goi, &new_cmd);
/* does this command exist? */
cmd = Jim_GetCommand(goi->interp, new_cmd, JIM_ERRMSG);
if (cmd) {
cp = Jim_GetString(new_cmd, NULL);
Jim_SetResultFormatted(goi->interp, "Command/target: %s Exists", cp);
return JIM_ERR;
}
/* TYPE */
e = Jim_GetOpt_String(goi, &cp, NULL);
if (e != JIM_OK)
return e;
struct transport *tr = get_current_transport();
if (tr->override_target) {
e = tr->override_target(&cp);
if (e != ERROR_OK) {
LOG_ERROR("The selected transport doesn't support this target");
return JIM_ERR;
}
LOG_INFO("The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD");
}
/* now does target type exist */
for (x = 0 ; target_types[x] ; x++) {
if (0 == strcmp(cp, target_types[x]->name)) {
/* found */
break;
}
/* check for deprecated name */
if (target_types[x]->deprecated_name) {
if (0 == strcmp(cp, target_types[x]->deprecated_name)) {
/* found */
LOG_WARNING("target name is deprecated use: \'%s\'", target_types[x]->name);
break;
}
}
}
if (target_types[x] == NULL) {
Jim_SetResultFormatted(goi->interp, "Unknown target type %s, try one of ", cp);
for (x = 0 ; target_types[x] ; x++) {
if (target_types[x + 1]) {
Jim_AppendStrings(goi->interp,
Jim_GetResult(goi->interp),
target_types[x]->name,
", ", NULL);
} else {
Jim_AppendStrings(goi->interp,
Jim_GetResult(goi->interp),
" or ",
target_types[x]->name, NULL);
}
}
return JIM_ERR;
}
/* Create it */
target = calloc(1, sizeof(struct target));
/* set target number */
target->target_number = new_target_number();
cmd_ctx->current_target = target;
/* allocate memory for each unique target type */
target->type = calloc(1, sizeof(struct target_type));
memcpy(target->type, target_types[x], sizeof(struct target_type));
/* will be set by "-endian" */
target->endianness = TARGET_ENDIAN_UNKNOWN;
/* default to first core, override with -coreid */
target->coreid = 0;
target->working_area = 0x0;
target->working_area_size = 0x0;
target->working_areas = NULL;
target->backup_working_area = 0;
target->state = TARGET_UNKNOWN;
target->debug_reason = DBG_REASON_UNDEFINED;
target->reg_cache = NULL;
target->breakpoints = NULL;
target->watchpoints = NULL;
target->next = NULL;
target->arch_info = NULL;
target->verbose_halt_msg = true;
target->halt_issued = false;
/* initialize trace information */
target->trace_info = calloc(1, sizeof(struct trace));
target->dbgmsg = NULL;
target->dbg_msg_enabled = 0;
target->endianness = TARGET_ENDIAN_UNKNOWN;
target->rtos = NULL;
target->rtos_auto_detect = false;
target->gdb_port_override = NULL;
/* Do the rest as "configure" options */
goi->isconfigure = 1;
e = target_configure(goi, target);
if (e == JIM_OK) {
if (target->has_dap) {
if (!target->dap_configured) {
Jim_SetResultString(goi->interp, "-dap ?name? required when creating target", -1);
e = JIM_ERR;
}
} else {
if (!target->tap_configured) {
Jim_SetResultString(goi->interp, "-chain-position ?name? required when creating target", -1);
e = JIM_ERR;
}
}
/* tap must be set after target was configured */
if (target->tap == NULL)
e = JIM_ERR;
}
if (e != JIM_OK) {
free(target->gdb_port_override);
free(target->type);
free(target);
return e;
}
if (target->endianness == TARGET_ENDIAN_UNKNOWN) {
/* default endian to little if not specified */
target->endianness = TARGET_LITTLE_ENDIAN;
}
cp = Jim_GetString(new_cmd, NULL);
target->cmd_name = strdup(cp);
if (target->type->target_create) {
e = (*(target->type->target_create))(target, goi->interp);
if (e != ERROR_OK) {
LOG_DEBUG("target_create failed");
free(target->gdb_port_override);
free(target->type);
free(target->cmd_name);
free(target);
return JIM_ERR;
}
}
/* create the target specific commands */
if (target->type->commands) {
e = register_commands(cmd_ctx, NULL, target->type->commands);
if (ERROR_OK != e)
LOG_ERROR("unable to register '%s' commands", cp);
}
/* append to end of list */
{
struct target **tpp;
tpp = &(all_targets);
while (*tpp)
tpp = &((*tpp)->next);
*tpp = target;
}
/* now - create the new target name command */
const struct command_registration target_subcommands[] = {
{
.chain = target_instance_command_handlers,
},
{
.chain = target->type->commands,
},
COMMAND_REGISTRATION_DONE
};
const struct command_registration target_commands[] = {
{
.name = cp,
.mode = COMMAND_ANY,
.help = "target command group",
.usage = "",
.chain = target_subcommands,
},
COMMAND_REGISTRATION_DONE
};
e = register_commands(cmd_ctx, NULL, target_commands);
if (ERROR_OK != e)
return JIM_ERR;
struct command *c = command_find_in_context(cmd_ctx, cp);
assert(c);
command_set_handler_data(c, target);
return (ERROR_OK == e) ? JIM_OK : JIM_ERR;
}
static int jim_target_current(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
return JIM_ERR;
}
struct command_context *cmd_ctx = current_command_context(interp);
assert(cmd_ctx != NULL);
Jim_SetResultString(interp, target_name(get_current_target(cmd_ctx)), -1);
return JIM_OK;
}
static int jim_target_types(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
return JIM_ERR;
}
Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
for (unsigned x = 0; NULL != target_types[x]; x++) {
Jim_ListAppendElement(interp, Jim_GetResult(interp),
Jim_NewStringObj(interp, target_types[x]->name, -1));
}
return JIM_OK;
}
static int jim_target_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
return JIM_ERR;
}
Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
struct target *target = all_targets;
while (target) {
Jim_ListAppendElement(interp, Jim_GetResult(interp),
Jim_NewStringObj(interp, target_name(target), -1));
target = target->next;
}
return JIM_OK;
}
static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
const char *targetname;
int retval, len;
struct target *target = (struct target *) NULL;
struct target_list *head, *curr, *new;
curr = (struct target_list *) NULL;
head = (struct target_list *) NULL;
retval = 0;
LOG_DEBUG("%d", argc);
/* argv[1] = target to associate in smp
* argv[2] = target to assoicate in smp
* argv[3] ...
*/
for (i = 1; i < argc; i++) {
targetname = Jim_GetString(argv[i], &len);
target = get_target(targetname);
LOG_DEBUG("%s ", targetname);
if (target) {
new = malloc(sizeof(struct target_list));
new->target = target;
new->next = (struct target_list *)NULL;
if (head == (struct target_list *)NULL) {
head = new;
curr = head;
} else {
curr->next = new;
curr = new;
}
}
}
/* now parse the list of cpu and put the target in smp mode*/
curr = head;
while (curr != (struct target_list *)NULL) {
target = curr->target;
target->smp = 1;
target->head = head;
curr = curr->next;
}
if (target && target->rtos)
retval = rtos_smp_init(head->target);
return retval;
}
static int jim_target_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_GetOptInfo goi;
Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
if (goi.argc < 3) {
Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
"<name> <target_type> [<target_options> ...]");
return JIM_ERR;
}
return target_create(&goi);
}
static const struct command_registration target_subcommand_handlers[] = {
{
.name = "init",
.mode = COMMAND_CONFIG,
.handler = handle_target_init_command,
.help = "initialize targets",
},
{
.name = "create",
/* REVISIT this should be COMMAND_CONFIG ... */
.mode = COMMAND_ANY,
.jim_handler = jim_target_create,
.usage = "name type '-chain-position' name [options ...]",
.help = "Creates and selects a new target",
},
{
.name = "current",
.mode = COMMAND_ANY,
.jim_handler = jim_target_current,
.help = "Returns the currently selected target",
},
{
.name = "types",
.mode = COMMAND_ANY,
.jim_handler = jim_target_types,
.help = "Returns the available target types as "
"a list of strings",
},
{
.name = "names",
.mode = COMMAND_ANY,
.jim_handler = jim_target_names,
.help = "Returns the names of all targets as a list of strings",
},
{
.name = "smp",
.mode = COMMAND_ANY,
.jim_handler = jim_target_smp,
.usage = "targetname1 targetname2 ...",
.help = "gather several target in a smp list"
},
COMMAND_REGISTRATION_DONE
};
struct FastLoad {
target_addr_t address;
uint8_t *data;
int length;
};
static int fastload_num;
static struct FastLoad *fastload;
static void free_fastload(void)
{
if (fastload != NULL) {
int i;
for (i = 0; i < fastload_num; i++) {
if (fastload[i].data)
free(fastload[i].data);
}
free(fastload);
fastload = NULL;
}
}
COMMAND_HANDLER(handle_fast_load_image_command)
{
uint8_t *buffer;
size_t buf_cnt;
uint32_t image_size;
target_addr_t min_address = 0;
target_addr_t max_address = -1;
int i;
struct image image;
int retval = CALL_COMMAND_HANDLER(parse_load_image_command_CMD_ARGV,
&image, &min_address, &max_address);
if (ERROR_OK != retval)
return retval;
struct duration bench;
duration_start(&bench);
retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL);
if (retval != ERROR_OK)
return retval;
image_size = 0x0;
retval = ERROR_OK;
fastload_num = image.num_sections;
fastload = malloc(sizeof(struct FastLoad)*image.num_sections);
if (fastload == NULL) {
command_print(CMD_CTX, "out of memory");
image_close(&image);
return ERROR_FAIL;
}
memset(fastload, 0, sizeof(struct FastLoad)*image.num_sections);
for (i = 0; i < image.num_sections; i++) {
buffer = malloc(image.sections[i].size);
if (buffer == NULL) {
command_print(CMD_CTX, "error allocating buffer for section (%d bytes)",
(int)(image.sections[i].size));
retval = ERROR_FAIL;
break;
}
retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt);
if (retval != ERROR_OK) {
free(buffer);
break;
}
uint32_t offset = 0;
uint32_t length = buf_cnt;
/* DANGER!!! beware of unsigned comparision here!!! */
if ((image.sections[i].base_address + buf_cnt >= min_address) &&
(image.sections[i].base_address < max_address)) {
if (image.sections[i].base_address < min_address) {
/* clip addresses below */
offset += min_address-image.sections[i].base_address;
length -= offset;
}
if (image.sections[i].base_address + buf_cnt > max_address)
length -= (image.sections[i].base_address + buf_cnt)-max_address;
fastload[i].address = image.sections[i].base_address + offset;
fastload[i].data = malloc(length);
if (fastload[i].data == NULL) {
free(buffer);
command_print(CMD_CTX, "error allocating buffer for section (%" PRIu32 " bytes)",
length);
retval = ERROR_FAIL;
break;
}
memcpy(fastload[i].data, buffer + offset, length);
fastload[i].length = length;
image_size += length;
command_print(CMD_CTX, "%u bytes written at address 0x%8.8x",
(unsigned int)length,
((unsigned int)(image.sections[i].base_address + offset)));
}
free(buffer);
}
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "Loaded %" PRIu32 " bytes "
"in %fs (%0.3f KiB/s)", image_size,
duration_elapsed(&bench), duration_kbps(&bench, image_size));
command_print(CMD_CTX,
"WARNING: image has not been loaded to target!"
"You can issue a 'fast_load' to finish loading.");
}
image_close(&image);
if (retval != ERROR_OK)
free_fastload();
return retval;
}
COMMAND_HANDLER(handle_fast_load_command)
{
if (CMD_ARGC > 0)
return ERROR_COMMAND_SYNTAX_ERROR;
if (fastload == NULL) {
LOG_ERROR("No image in memory");
return ERROR_FAIL;
}
int i;
int64_t ms = timeval_ms();
int size = 0;
int retval = ERROR_OK;
for (i = 0; i < fastload_num; i++) {
struct target *target = get_current_target(CMD_CTX);
command_print(CMD_CTX, "Write to 0x%08x, length 0x%08x",
(unsigned int)(fastload[i].address),
(unsigned int)(fastload[i].length));
retval = target_write_buffer(target, fastload[i].address, fastload[i].length, fastload[i].data);
if (retval != ERROR_OK)
break;
size += fastload[i].length;
}
if (retval == ERROR_OK) {
int64_t after = timeval_ms();
command_print(CMD_CTX, "Loaded image %f kBytes/s", (float)(size/1024.0)/((float)(after-ms)/1000.0));
}
return retval;
}
static const struct command_registration target_command_handlers[] = {
{
.name = "targets",
.handler = handle_targets_command,
.mode = COMMAND_ANY,
.help = "change current default target (one parameter) "
"or prints table of all targets (no parameters)",
.usage = "[target]",
},
{
.name = "target",
.mode = COMMAND_CONFIG,
.help = "configure target",
.chain = target_subcommand_handlers,
},
COMMAND_REGISTRATION_DONE
};
int target_register_commands(struct command_context *cmd_ctx)
{
return register_commands(cmd_ctx, NULL, target_command_handlers);
}
static bool target_reset_nag = true;
bool get_target_reset_nag(void)
{
return target_reset_nag;
}
COMMAND_HANDLER(handle_target_reset_nag)
{
return CALL_COMMAND_HANDLER(handle_command_parse_bool,
&target_reset_nag, "Nag after each reset about options to improve "
"performance");
}
COMMAND_HANDLER(handle_ps_command)
{
struct target *target = get_current_target(CMD_CTX);
char *display;
if (target->state != TARGET_HALTED) {
LOG_INFO("target not halted !!");
return ERROR_OK;
}
if ((target->rtos) && (target->rtos->type)
&& (target->rtos->type->ps_command)) {
display = target->rtos->type->ps_command(target);
command_print(CMD_CTX, "%s", display);
free(display);
return ERROR_OK;
} else {
LOG_INFO("failed");
return ERROR_TARGET_FAILURE;
}
}
static void binprint(struct command_context *cmd_ctx, const char *text, const uint8_t *buf, int size)
{
if (text != NULL)
command_print_sameline(cmd_ctx, "%s", text);
for (int i = 0; i < size; i++)
command_print_sameline(cmd_ctx, " %02x", buf[i]);
command_print(cmd_ctx, " ");
}
COMMAND_HANDLER(handle_test_mem_access_command)
{
struct target *target = get_current_target(CMD_CTX);
uint32_t test_size;
int retval = ERROR_OK;
if (target->state != TARGET_HALTED) {
LOG_INFO("target not halted !!");
return ERROR_FAIL;
}
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], test_size);
/* Test reads */
size_t num_bytes = test_size + 4;
struct working_area *wa = NULL;
retval = target_alloc_working_area(target, num_bytes, &wa);
if (retval != ERROR_OK) {
LOG_ERROR("Not enough working area");
return ERROR_FAIL;
}
uint8_t *test_pattern = malloc(num_bytes);
for (size_t i = 0; i < num_bytes; i++)
test_pattern[i] = rand();
retval = target_write_memory(target, wa->address, 1, num_bytes, test_pattern);
if (retval != ERROR_OK) {
LOG_ERROR("Test pattern write failed");
goto out;
}
for (int host_offset = 0; host_offset <= 1; host_offset++) {
for (int size = 1; size <= 4; size *= 2) {
for (int offset = 0; offset < 4; offset++) {
uint32_t count = test_size / size;
size_t host_bufsiz = (count + 2) * size + host_offset;
uint8_t *read_ref = malloc(host_bufsiz);
uint8_t *read_buf = malloc(host_bufsiz);
for (size_t i = 0; i < host_bufsiz; i++) {
read_ref[i] = rand();
read_buf[i] = read_ref[i];
}
command_print_sameline(CMD_CTX,
"Test read %" PRIu32 " x %d @ %d to %saligned buffer: ", count,
size, offset, host_offset ? "un" : "");
struct duration bench;
duration_start(&bench);
retval = target_read_memory(target, wa->address + offset, size, count,
read_buf + size + host_offset);
duration_measure(&bench);
if (retval == ERROR_TARGET_UNALIGNED_ACCESS) {
command_print(CMD_CTX, "Unsupported alignment");
goto next;
} else if (retval != ERROR_OK) {
command_print(CMD_CTX, "Memory read failed");
goto next;
}
/* replay on host */
memcpy(read_ref + size + host_offset, test_pattern + offset, count * size);
/* check result */
int result = memcmp(read_ref, read_buf, host_bufsiz);
if (result == 0) {
command_print(CMD_CTX, "Pass in %fs (%0.3f KiB/s)",
duration_elapsed(&bench),
duration_kbps(&bench, count * size));
} else {
command_print(CMD_CTX, "Compare failed");
binprint(CMD_CTX, "ref:", read_ref, host_bufsiz);
binprint(CMD_CTX, "buf:", read_buf, host_bufsiz);
}
next:
free(read_ref);
free(read_buf);
}
}
}
out:
free(test_pattern);
if (wa != NULL)
target_free_working_area(target, wa);
/* Test writes */
num_bytes = test_size + 4 + 4 + 4;
retval = target_alloc_working_area(target, num_bytes, &wa);
if (retval != ERROR_OK) {
LOG_ERROR("Not enough working area");
return ERROR_FAIL;
}
test_pattern = malloc(num_bytes);
for (size_t i = 0; i < num_bytes; i++)
test_pattern[i] = rand();
for (int host_offset = 0; host_offset <= 1; host_offset++) {
for (int size = 1; size <= 4; size *= 2) {
for (int offset = 0; offset < 4; offset++) {
uint32_t count = test_size / size;
size_t host_bufsiz = count * size + host_offset;
uint8_t *read_ref = malloc(num_bytes);
uint8_t *read_buf = malloc(num_bytes);
uint8_t *write_buf = malloc(host_bufsiz);
for (size_t i = 0; i < host_bufsiz; i++)
write_buf[i] = rand();
command_print_sameline(CMD_CTX,
"Test write %" PRIu32 " x %d @ %d from %saligned buffer: ", count,
size, offset, host_offset ? "un" : "");
retval = target_write_memory(target, wa->address, 1, num_bytes, test_pattern);
if (retval != ERROR_OK) {
command_print(CMD_CTX, "Test pattern write failed");
goto nextw;
}
/* replay on host */
memcpy(read_ref, test_pattern, num_bytes);
memcpy(read_ref + size + offset, write_buf + host_offset, count * size);
struct duration bench;
duration_start(&bench);
retval = target_write_memory(target, wa->address + size + offset, size, count,
write_buf + host_offset);
duration_measure(&bench);
if (retval == ERROR_TARGET_UNALIGNED_ACCESS) {
command_print(CMD_CTX, "Unsupported alignment");
goto nextw;
} else if (retval != ERROR_OK) {
command_print(CMD_CTX, "Memory write failed");
goto nextw;
}
/* read back */
retval = target_read_memory(target, wa->address, 1, num_bytes, read_buf);
if (retval != ERROR_OK) {
command_print(CMD_CTX, "Test pattern write failed");
goto nextw;
}
/* check result */
int result = memcmp(read_ref, read_buf, num_bytes);
if (result == 0) {
command_print(CMD_CTX, "Pass in %fs (%0.3f KiB/s)",
duration_elapsed(&bench),
duration_kbps(&bench, count * size));
} else {
command_print(CMD_CTX, "Compare failed");
binprint(CMD_CTX, "ref:", read_ref, num_bytes);
binprint(CMD_CTX, "buf:", read_buf, num_bytes);
}
nextw:
free(read_ref);
free(read_buf);
}
}
}
free(test_pattern);
if (wa != NULL)
target_free_working_area(target, wa);
return retval;
}
static const struct command_registration target_exec_command_handlers[] = {
{
.name = "fast_load_image",
.handler = handle_fast_load_image_command,
.mode = COMMAND_ANY,
.help = "Load image into server memory for later use by "
"fast_load; primarily for profiling",
.usage = "filename address ['bin'|'ihex'|'elf'|'s19'] "
"[min_address [max_length]]",
},
{
.name = "fast_load",
.handler = handle_fast_load_command,
.mode = COMMAND_EXEC,
.help = "loads active fast load image to current target "
"- mainly for profiling purposes",
.usage = "",
},
{
.name = "profile",
.handler = handle_profile_command,
.mode = COMMAND_EXEC,
.usage = "seconds filename [start end]",
.help = "profiling samples the CPU PC",
},
/** @todo don't register virt2phys() unless target supports it */
{
.name = "virt2phys",
.handler = handle_virt2phys_command,
.mode = COMMAND_ANY,
.help = "translate a virtual address into a physical address",
.usage = "virtual_address",
},
{
.name = "reg",
.handler = handle_reg_command,
.mode = COMMAND_EXEC,
.help = "display (reread from target with \"force\") or set a register; "
"with no arguments, displays all registers and their values",
.usage = "[(register_number|register_name) [(value|'force')]]",
},
{
.name = "poll",
.handler = handle_poll_command,
.mode = COMMAND_EXEC,
.help = "poll target state; or reconfigure background polling",
.usage = "['on'|'off']",
},
{
.name = "wait_halt",
.handler = handle_wait_halt_command,
.mode = COMMAND_EXEC,
.help = "wait up to the specified number of milliseconds "
"(default 5000) for a previously requested halt",
.usage = "[milliseconds]",
},
{
.name = "halt",
.handler = handle_halt_command,
.mode = COMMAND_EXEC,
.help = "request target to halt, then wait up to the specified"
"number of milliseconds (default 5000) for it to complete",
.usage = "[milliseconds]",
},
{
.name = "resume",
.handler = handle_resume_command,
.mode = COMMAND_EXEC,
.help = "resume target execution from current PC or address",
.usage = "[address]",
},
{
.name = "reset",
.handler = handle_reset_command,
.mode = COMMAND_EXEC,
.usage = "[run|halt|init]",
.help = "Reset all targets into the specified mode."
"Default reset mode is run, if not given.",
},
{
.name = "soft_reset_halt",
.handler = handle_soft_reset_halt_command,
.mode = COMMAND_EXEC,
.usage = "",
.help = "halt the target and do a soft reset",
},
{
.name = "step",
.handler = handle_step_command,
.mode = COMMAND_EXEC,
.help = "step one instruction from current PC or address",
.usage = "[address]",
},
{
.name = "mdd",
.handler = handle_md_command,
.mode = COMMAND_EXEC,
.help = "display memory words",
.usage = "['phys'] address [count]",
},
{
.name = "mdw",
.handler = handle_md_command,
.mode = COMMAND_EXEC,
.help = "display memory words",
.usage = "['phys'] address [count]",
},
{
.name = "mdh",
.handler = handle_md_command,
.mode = COMMAND_EXEC,
.help = "display memory half-words",
.usage = "['phys'] address [count]",
},
{
.name = "mdb",
.handler = handle_md_command,
.mode = COMMAND_EXEC,
.help = "display memory bytes",
.usage = "['phys'] address [count]",
},
{
.name = "mwd",
.handler = handle_mw_command,
.mode = COMMAND_EXEC,
.help = "write memory word",
.usage = "['phys'] address value [count]",
},
{
.name = "mww",
.handler = handle_mw_command,
.mode = COMMAND_EXEC,
.help = "write memory word",
.usage = "['phys'] address value [count]",
},
{
.name = "mwh",
.handler = handle_mw_command,
.mode = COMMAND_EXEC,
.help = "write memory half-word",
.usage = "['phys'] address value [count]",
},
{
.name = "mwb",
.handler = handle_mw_command,
.mode = COMMAND_EXEC,
.help = "write memory byte",
.usage = "['phys'] address value [count]",
},
{
.name = "bp",
.handler = handle_bp_command,
.mode = COMMAND_EXEC,
.help = "list or set hardware or software breakpoint",
.usage = "<address> [<asid>] <length> ['hw'|'hw_ctx']",
},
{
.name = "rbp",
.handler = handle_rbp_command,
.mode = COMMAND_EXEC,
.help = "remove breakpoint",
.usage = "address",
},
{
.name = "wp",
.handler = handle_wp_command,
.mode = COMMAND_EXEC,
.help = "list (no params) or create watchpoints",
.usage = "[address length [('r'|'w'|'a') value [mask]]]",
},
{
.name = "rwp",
.handler = handle_rwp_command,
.mode = COMMAND_EXEC,
.help = "remove watchpoint",
.usage = "address",
},
{
.name = "load_image",
.handler = handle_load_image_command,
.mode = COMMAND_EXEC,
.usage = "filename address ['bin'|'ihex'|'elf'|'s19'] "
"[min_address] [max_length]",
},
{
.name = "dump_image",
.handler = handle_dump_image_command,
.mode = COMMAND_EXEC,
.usage = "filename address size",
},
{
.name = "verify_image_checksum",
.handler = handle_verify_image_checksum_command,
.mode = COMMAND_EXEC,
.usage = "filename [offset [type]]",
},
{
.name = "verify_image",
.handler = handle_verify_image_command,
.mode = COMMAND_EXEC,
.usage = "filename [offset [type]]",
},
{
.name = "test_image",
.handler = handle_test_image_command,
.mode = COMMAND_EXEC,
.usage = "filename [offset [type]]",
},
{
.name = "mem2array",
.mode = COMMAND_EXEC,
.jim_handler = jim_mem2array,
.help = "read 8/16/32 bit memory and return as a TCL array "
"for script processing",
.usage = "arrayname bitwidth address count",
},
{
.name = "array2mem",
.mode = COMMAND_EXEC,
.jim_handler = jim_array2mem,
.help = "convert a TCL array to memory locations "
"and write the 8/16/32 bit values",
.usage = "arrayname bitwidth address count",
},
{
.name = "reset_nag",
.handler = handle_target_reset_nag,
.mode = COMMAND_ANY,
.help = "Nag after each reset about options that could have been "
"enabled to improve performance. ",
.usage = "['enable'|'disable']",
},
{
.name = "ps",
.handler = handle_ps_command,
.mode = COMMAND_EXEC,
.help = "list all tasks ",
.usage = " ",
},
{
.name = "test_mem_access",
.handler = handle_test_mem_access_command,
.mode = COMMAND_EXEC,
.help = "Test the target's memory access functions",
.usage = "size",
},
COMMAND_REGISTRATION_DONE
};
static int target_register_user_commands(struct command_context *cmd_ctx)
{
int retval = ERROR_OK;
retval = target_request_register_commands(cmd_ctx);
if (retval != ERROR_OK)
return retval;
retval = trace_register_commands(cmd_ctx);
if (retval != ERROR_OK)
return retval;
return register_commands(cmd_ctx, NULL, target_exec_command_handlers);
}