From 7079e0ca7d14c7cb4e897aec0e343c932248125f Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 19 Nov 2018 12:46:40 -0800 Subject: [PATCH] From upstream (#331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 Reviewed-on: http://openocd.zylin.com/4358 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Fredrik Hederstierna * 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 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 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 2231da8ec4e7d7ae9b652f3dd1a7104f5a110f3f). Change-Id: Ida6915331dd924c9c0d08822fd94c04ad408cdc5 Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/4464 Tested-by: jenkins Reviewed-by: Christopher Head * 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 Reviewed-on: http://openocd.zylin.com/4563 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4348 Tested-by: jenkins Reviewed-by: Andreas Fritiofson * 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 Reviewed-on: http://openocd.zylin.com/4566 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Tim Newsome * 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 Reviewed-on: http://openocd.zylin.com/4565 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4564 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4531 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4497 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4498 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 --- 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 * 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 Reviewed-on: http://openocd.zylin.com/4533 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4571 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4535 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Christopher Head * 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 Reviewed-on: http://openocd.zylin.com/4519 Tested-by: jenkins Reviewed-by: Spencer Oliver Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4520 Tested-by: jenkins Reviewed-by: Spencer Oliver Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4521 Tested-by: jenkins Reviewed-by: Spencer Oliver * 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 Reviewed-on: http://openocd.zylin.com/4511 Tested-by: jenkins Reviewed-by: Christopher Head Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4537 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 (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 Reviewed-on: http://openocd.zylin.com/4538 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4593 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * tcl: target: Add NXP LS1012A config As seen on the FRDM-LS1012A board. Change-Id: Ifc9074b3f7535167b9ded5f544501ec2879f5db7 Signed-off-by: Andreas Färber Reviewed-on: http://openocd.zylin.com/4594 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4595 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4585 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4586 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Christopher Head * 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 Reviewed-on: http://openocd.zylin.com/4584 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Christopher Head * target/stm32f7x: Clear stuck HSE clock with CSS Change-Id: Ica0025ea465910dd664ab546b66f4f25b271f1f5 Signed-off-by: Christopher Head Reviewed-on: http://openocd.zylin.com/4570 Tested-by: jenkins Reviewed-by: Andreas Fritiofson * 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 53376dbbede4f0bf42e724ff 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 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 Reviewed-on: http://openocd.zylin.com/3402 Tested-by: jenkins Reviewed-by: Tomas Vanek * tcl/board: add SAMD11 Xplained Pro evaluation board Change-Id: Id996c4de6dc9f25f71424017bf07689fea7bd3af Signed-off-by: Peter Lawrence Reviewed-on: http://openocd.zylin.com/4507 Tested-by: jenkins Reviewed-by: Tomas Vanek * Adds SAMD11D14AU flash support. Corrects names of SAMD11D14AM and SAMD11D14ASS per datasheet. Change-Id: I8beb15d5376966a4f8d7de76bfb2cbda2db440dc Signed-off-by: Christopher Hoover Reviewed-on: http://openocd.zylin.com/4597 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4292 Tested-by: jenkins Reviewed-by: Hsiangkai Wang Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4573 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Christopher Head * drivers: cmsis-dap: pull up common connect code Just a minor deduplication Change-Id: Idd256883e5f6d4bd4dcc18462dd5468991f507b3 Signed-off-by: Karl Palsson Reviewed-on: http://openocd.zylin.com/3403 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/3404 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4153 Tested-by: jenkins Reviewed-by: Matthias Welwarsky Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4599 Reviewed-by: Tomas Vanek Tested-by: jenkins * flash/nor/stm32lx: Add revision 'V' for STM32L1xx Cat.3 devices Change-Id: Ic92b0fb5b738af3bec79ae335876aa9e26f5f4cd Signed-off-by: Marc Schink Reviewed-on: http://openocd.zylin.com/4600 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Antonio Borneo * 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 Reviewed-on: http://openocd.zylin.com/4603 Tested-by: jenkins Reviewed-by: Jonathan Larmour Reviewed-by: Tomas Vanek * nrf5: Add HWID 0x139 (52832 rev E0) Change-Id: I71b7471ccfcb8fcc6de30da57ce4165c7fb1f73f Signed-off-by: James Jacobsson Reviewed-on: http://openocd.zylin.com/4604 Tested-by: jenkins Reviewed-by: Tomas Vanek * target: Fix segfault for 'mem2array' Call 'mem2array' without arguments to reproduce the segmentation fault. Change-Id: I02bf46cc8bd317abbb721a8c75d7cbfac99eb34e Signed-off-by: Marc Schink Reviewed-on: http://openocd.zylin.com/4534 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Christopher Head * target/armv7m_trace: Fix typo in enum Change-Id: I6364ee5011ef2d55c59674e3b97504a285de0cb2 Signed-off-by: Marc Schink Reviewed-on: http://openocd.zylin.com/3904 Tested-by: jenkins Reviewed-by: Paul Fertser * target/armv7m_trace: Use prefix for enums Change-Id: I3f199e6053146a1094d96b98ea174b41bb021599 Signed-off-by: Marc Schink Reviewed-on: http://openocd.zylin.com/3905 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4607 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/3963 Tested-by: jenkins Reviewed-by: Antonio Borneo * armv7a: read ttbcr and ttb0/1 at every entry in debug state Commit bfc5c764df145f68835543119865eabe462e19c2 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: bfc5c764df14 ("armv7a: cache ttbcr and ttb0/1 on debug state entry") Change-Id: Ifc20eca190111832e339a01b7f85d28c1547c8ba Signed-off-by: Antonio Borneo Reviewed-on: http://openocd.zylin.com/4601 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4550 Tested-by: jenkins Reviewed-by: Andreas Fritiofson * Remove FSF mailing address. Checkpatch complains about this (FSF_MAILING_ADDRESS). Change-Id: Ib46a7704f9aed4ed16ce7733d43c58254a094149 Signed-off-by: Tim Newsome Reviewed-on: http://openocd.zylin.com/4559 Tested-by: jenkins Reviewed-by: Spencer Oliver * 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 Reviewed-on: http://openocd.zylin.com/4517 Tested-by: jenkins Reviewed-by: Spencer Oliver * 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 Reviewed-on: http://openocd.zylin.com/4493 Tested-by: jenkins Reviewed-by: Bohdan Tymkiv Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4578 Tested-by: jenkins Reviewed-by: Liviu Ionescu Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4245 Tested-by: jenkins Reviewed-by: Christopher Head Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4480 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Matthias Welwarsky * drivers: cmsis-dap: print serial if available Helpful for sanity checking connections Change-Id: Ife0d8b4e12d4c03685aac8115c9739a4c1e994fe Signed-off-by: Karl Palsson Reviewed-on: http://openocd.zylin.com/3405 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4606 Tested-by: jenkins Reviewed-by: Tomas Vanek * target/cortex_a: fix compile error for uninitialized variable Commit ad6c71e151590f9d07eb07eda978a8d2a845259c 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 ad6c71e15159 ("cortex_a: fix virt2phys when mmu is disabled") Change-Id: I77dec41acdf4c715b45ae37b72e36719d96d9283 Signed-off-by: Antonio Borneo Reviewed-on: http://openocd.zylin.com/4619 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4404 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4405 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4406 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/2999 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4422 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4423 Tested-by: jenkins Reviewed-by: Paul Fertser * tcl/board: add DPTechnics DPT-Board-v1 it is Atheros AR9331 based IoT dev board. Change-Id: I6fc3cdea1bef49c53045018ff5acfec4d5610ba6 Signed-off-by: Oleksij Rempel Reviewed-on: http://openocd.zylin.com/4424 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4598 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4583 Tested-by: jenkins Reviewed-by: Paul Fertser * 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: a51ab8ddf63a ("Add RISC-V support.") Change-Id: I7fa86b305bd90cc590fd4359c3698632d44712e5 Signed-off-by: Antonio Borneo Reviewed-on: http://openocd.zylin.com/4618 Tested-by: jenkins Reviewed-by: Jiri Kastner Reviewed-by: Oleksij Rempel Reviewed-by: Tim Newsome Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4551 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4614 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4516 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4612 Tested-by: jenkins Reviewed-by: Tomas Vanek * nrf5: add free_driver_priv Change-Id: I429a9868deb0c4b51f47a4bbad844bdc348e8d21 Signed-off-by: Jim Paris Reviewed-on: http://openocd.zylin.com/4608 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Signed-off-by: Masatoshi Tateishi Signed-off-by: Nobuto Kobayashi Reviewed-on: http://openocd.zylin.com/4103 Tested-by: jenkins Reviewed-by: Alan Carvalho de Assis Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4054 Tested-by: jenkins Reviewed-by: Fredrik Hederstierna Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4620 Tested-by: jenkins Reviewed-by: Tomas Vanek * tcl/target/stm32f0x: Allow overriding the Flash bank size Copy & paste from another stm32 target. Change-Id: I0f6cbcec974ce70c23c1850526354106caee1172 Signed-off-by: Dominik Peklo Reviewed-on: http://openocd.zylin.com/4575 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * tcl/target: add Allwinner V3s SoC support Change-Id: I2459d2b137050985b7301047f9651951d72d9e9e Signed-off-by: Oleksij Rempel Reviewed-on: http://openocd.zylin.com/4427 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4624 Tested-by: jenkins Reviewed-by: Matthias Welwarsky Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4623 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4627 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * Add detail to `wrong register size` error. Signed-off-by: Tim Newsome Change-Id: Id31499c94b539969970251145e42c89c943fd87c Reviewed-on: http://openocd.zylin.com/4577 Tested-by: jenkins Reviewed-by: Tomas Vanek * doc: fix typo in cortex_m maskisr command Change-Id: I37795c320ff7cbf6f2c7434e03b26dbaf6fc6db4 Signed-off-by: Christopher Head Reviewed-on: http://openocd.zylin.com/4621 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4605 Tested-by: jenkins Reviewed-by: Tomas Vanek * tcl/board: update all uses of interface/stlink-v2-1 to interface/stlink Change-Id: I5e27e84d022f73101376e8b4a1bdc65f58fd348a Signed-off-by: Cody P Schafer Reviewed-on: http://openocd.zylin.com/4456 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4635 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Tim Newsome Reviewed-by: Tomas Vanek * max32xxx: Support for MAX32XXX devices. Adding flash programming support for Maxim Integrated MAX32XXX devices. Change-Id: I5b0f57a885f9d813240e4bc2d9f765b743e1cfc3 Signed-off-by: Kevin Gillespie Reviewed-on: http://openocd.zylin.com/3543 Tested-by: jenkins Reviewed-by: Ismail H. KOSE Reviewed-by: Tomas Vanek Reviewed-by: Andreas Fritiofson * flash/at91sam4: run probe just once Reread registers in sam4_GetInfo() Change-Id: I3b023b3e642a9c052b5c41673d196317f7e7f2e3 Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/4609 Tested-by: jenkins Reviewed-by: Erwin Oegema Reviewed-by: Svetoslav Enchev * flash/at91sam4: emit flash bank info Change related LOG_INFO to LOG_DEBUG Change-Id: I0c09b1ec83da631b26980dc8632b9031fe2921a3 Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/4610 Tested-by: jenkins Reviewed-by: Erwin Oegema Reviewed-by: Svetoslav Enchev * 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 Reviewed-on: http://openocd.zylin.com/4611 Tested-by: jenkins Reviewed-by: Svetoslav Enchev * flash/at91sam4: fix clang static analyzer warning Change-Id: I5e5319d855c868adfa012f68086f7f809ec5a069 Signed-off-by: Tomas Vanek 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 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 Signed-off-by: Guido Günther Reviewed-on: http://openocd.zylin.com/4647 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4668 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4530 Tested-by: jenkins Reviewed-by: Christopher Head Reviewed-by: Matthias Welwarsky * libusb: return oocd error values keep same return style where possible Change-Id: I2f9b85dbc307a483609f76a84de77e3c74d346c7 Signed-off-by: Oleksij Rempel Reviewed-on: http://openocd.zylin.com/4588 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4659 Tested-by: jenkins Reviewed-by: Antonio Borneo * flash/stm32f2x: add stm32f7 revision Z identification Signed-off-by: Cody P Schafer Change-Id: Ia0169514d494bae2a98d92ebc97c8eccc10bc6c4 Reviewed-on: http://openocd.zylin.com/4657 Tested-by: jenkins Reviewed-by: Christopher Head Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4002 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Leonard Crestez * 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 Reviewed-on: http://openocd.zylin.com/4081 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * ftdi: extend ftdi_location format To existing :, format add -. support. The last format is used by kernel and other drivers. Change-Id: I6528970d3af4f6a8bf7b27a0f7a763b5957fdf2b Signed-off-by: Oleksij Rempel Reviewed-on: http://openocd.zylin.com/4631 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4622 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4646 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4666 Tested-by: jenkins Reviewed-by: Tomas Vanek * flash/nrf5: time-based timeout waiting for flash controller Change-Id: Id214df154dc359ca130c8d8fe1554d106621b9cd Signed-off-by: Kai Geissdoerfer Reviewed-on: http://openocd.zylin.com/4648 Tested-by: jenkins Reviewed-by: Tomas Vanek * flash/nrf5: support for nRF52840 Q1AAC0 Change-Id: Id3280dadece84e1d68544936e44d506c7930a55d Signed-off-by: Kai Geissdoerfer Reviewed-on: http://openocd.zylin.com/4649 Tested-by: jenkins Reviewed-by: Tomas Vanek * doc: fix use of deprecated config file in the example Commit 31c58c139d85 ("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 Reviewed-on: http://openocd.zylin.com/4667 Tested-by: jenkins Reviewed-by: Andreas Fritiofson * 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 Reviewed-on: http://openocd.zylin.com/4654 Tested-by: jenkins Reviewed-by: Tomas Vanek * Clarify what exactly the RISC-V code supports. Change-Id: I8da657426cc52c738ab41bfb0164cbc6721c0aef Signed-off-by: Tim Newsome Reviewed-on: http://openocd.zylin.com/4655 Tested-by: jenkins Reviewed-by: Philipp Guehring Reviewed-by: Liviu Ionescu Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4675 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Antonio Borneo * 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 Reviewed-on: http://openocd.zylin.com/4665 Tested-by: jenkins Reviewed-by: Andreas Fritiofson * target/stm32: make APCSW cacheable Change-Id: I7c5c9720ded329848647f17db95f845e46c01c19 Signed-off-by: Christopher Head Reviewed-on: http://openocd.zylin.com/4674 Tested-by: jenkins Reviewed-by: Tomas Vanek * target/atsamv: make APCSW cacheable Change-Id: Ic00d3192642c682f370a6f7f8b70ae29744eb746 Signed-off-by: Christopher Head Reviewed-on: http://openocd.zylin.com/4678 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4676 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4669 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4670 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * flash/nrf5: support for nRF52810 Change-Id: I01c430bfa593d20ea7a51c90d67052e374d239b3 Signed-off-by: Anders Westrup Reviewed-on: http://openocd.zylin.com/4680 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Slowcoder * drivers: cmsis-dap: fix connection in JTAG mode Commit 5aceec24122bc222896cfcfd91f7f082f630ac83 ("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: 5aceec24122b ("drivers: cmsis-dap: pull up common connect code") Signed-off-by: Antonio Borneo Reviewed-on: http://openocd.zylin.com/4697 Reviewed-by: Tomas Vanek 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 Reviewed-on: http://openocd.zylin.com/4113 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4121 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4078 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4082 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4660 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4719 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4112 Tested-by: jenkins Reviewed-by: Andreas Fritiofson * 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 Reviewed-on: http://openocd.zylin.com/4720 Tested-by: jenkins Reviewed-by: Tim "mithro" Ansell Reviewed-by: Robert Jordens Reviewed-by: Paul Fertser * target/arm_adi_v5: fix sync CSW cache on apreg write Commit 0057c71ab6b81d0679b232318fc5f84b4becc471 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: 0057c71ab6b8 ("target/arm_adi_v5: sync CSW and TAR cache on apreg write") Signed-off-by: Antonio Borneo Reviewed-on: http://openocd.zylin.com/4679 Tested-by: jenkins Reviewed-by: Christopher Head Reviewed-by: Tomas Vanek * tcl/board: Add Arty-S7 Spartan 7 FPGA Development Board Change-Id: I8bfe780cb67a1777d5112a68e8a9781bfe4f2038 Signed-off-by: William D. Jones Reviewed-on: http://openocd.zylin.com/4525 Reviewed-by: Robert Jordens Tested-by: jenkins Reviewed-by: Rohit Singh Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4428 Reviewed-by: Robert Jördens Tested-by: jenkins Reviewed-by: Rohit Singh Reviewed-by: Matthias Welwarsky * target/cortex_a: fix temporary breakpoint during step Commit c8926d14579528bfcead1e179baf7cb846513db4 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: c8926d145795 ("cortex_a hybrid & context breakpoints") Change-Id: Ib674d8457d1e02e10332fd9d73554d13719ef93d Signed-off-by: Antonio Borneo Reviewed-on: http://openocd.zylin.com/4613 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4177 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4686 Tested-by: jenkins Reviewed-by: Jesse Marroquin Reviewed-by: Tomas Vanek * drivers/cmsis-dap: speed up sending multiple HID requests The performance of CMSIS-DAP in long data transfers was improved substantially in ef02b69b14d133b061217a91add5a028a77e86bc. 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 Reviewed-on: http://openocd.zylin.com/4080 Tested-by: jenkins Reviewed-by: Paul Fertser * 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 Reviewed-on: http://openocd.zylin.com/4732 Tested-by: jenkins Reviewed-by: Matthias Welwarsky Reviewed-by: Tomas Vanek * flash/nor/at91samd: add SAMR21E19A DID While on it correct RAM amount of SAMR21x16A devices Change-Id: Ie9ab9de1551bdceff17af7597a9a2ee41f5aebe0 Signed-off-by: Tomas Vanek 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 Reviewed-on: http://openocd.zylin.com/4677 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Matthias Welwarsky * 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 Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/4576 Tested-by: jenkins Reviewed-by: Jan Vojtěch * 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 Reviewed-on: http://openocd.zylin.com/4746 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 5d458cf72734a4474f38bbed10eea4d9acfe93a2 ("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 Reviewed-on: http://openocd.zylin.com/4748 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4756 Tested-by: jenkins Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4747 Reviewed-by: Matthias Welwarsky 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 Reviewed-on: http://openocd.zylin.com/4327 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Matthias Welwarsky * 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 Reviewed-on: http://openocd.zylin.com/4733 Tested-by: jenkins Reviewed-by: Tomas Vanek * 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 Reviewed-on: http://openocd.zylin.com/4741 Tested-by: jenkins Reviewed-by: Tomas Vanek * docs: fix typo in manual Change-Id: I28717105eb2a907b0cb4b03f4b5ff1f47194413b Signed-off-by: Spencer Oliver Reviewed-on: http://openocd.zylin.com/4751 Tested-by: jenkins Reviewed-by: Antonio Borneo * README: fix stlink instructions Since 31c58c139d85c35cc8ebce4196edb2c5eb157c7a there is a unified config for all stlink versions. Change-Id: Id736063496ecd96e2024ed69dcb67a22c44b80bb Signed-off-by: Paul Fertser Reviewed-on: http://openocd.zylin.com/4672 Tested-by: jenkins Reviewed-by: Antonio Borneo * 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 bb9d9c60264a905926e0d15f84842858d0de80b7, 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 Reviewed-on: http://openocd.zylin.com/4730 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Franck Jullien * 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 --- HACKING | 2 +- README | 17 +- contrib/60-openocd.rules | 2 + contrib/loaders/Makefile | 1 + contrib/loaders/flash/max32xxx/Makefile | 19 + contrib/loaders/flash/max32xxx/max32xxx.inc | 6 + contrib/loaders/flash/max32xxx/max32xxx.s | 70 + contrib/rtos-helpers/uCOS-III-openocd.c | 4 +- doc/openocd.texi | 122 +- src/flash/nor/Makefile.am | 2 + src/flash/nor/at91sam4.c | 82 +- src/flash/nor/at91samd.c | 5 +- src/flash/nor/drivers.c | 4 + src/flash/nor/esirisc_flash.c | 621 +++++++ src/flash/nor/max32xxx.c | 997 +++++++++++ src/flash/nor/nrf5.c | 16 +- src/flash/nor/stm32f1x.c | 405 ++--- src/flash/nor/stm32f2x.c | 3 + src/flash/nor/stm32l4x.c | 434 +++-- src/jtag/Makefile.am | 5 +- src/jtag/commands.c | 12 + src/jtag/commands.h | 1 + src/jtag/drivers/bitq.c | 2 +- src/jtag/drivers/cmsis_dap_usb.c | 200 ++- src/jtag/drivers/driver.c | 16 +- src/jtag/drivers/ftdi.c | 4 +- src/jtag/drivers/libusb1_common.c | 6 +- src/jtag/drivers/mpsse.c | 4 +- src/rtos/ChibiOS.c | 9 +- src/rtos/FreeRTOS.c | 13 +- src/rtos/ThreadX.c | 81 +- src/rtos/eCos.c | 10 +- src/rtos/embKernel.c | 9 +- src/rtos/linux.c | 173 +- src/rtos/mqx.c | 6 +- src/rtos/nuttx.c | 75 +- src/rtos/riscv_debug.c | 50 +- src/rtos/rtos.c | 116 +- src/rtos/rtos.h | 14 +- src/rtos/rtos_chibios_stackings.c | 68 +- src/rtos/rtos_ecos_stackings.c | 34 +- src/rtos/rtos_embkernel_stackings.c | 34 +- src/rtos/rtos_mqx_stackings.c | 34 +- src/rtos/rtos_standard_stackings.c | 226 +-- src/rtos/rtos_ucos_iii_stackings.c | 64 +- src/rtos/rtos_ucos_iii_stackings.h | 1 + src/rtos/uCOS-III.c | 25 +- src/server/gdb_server.c | 54 +- src/server/tcl_server.c | 2 +- src/target/Makefile.am | 15 +- src/target/arm_adi_v5.c | 51 +- src/target/arm_adi_v5.h | 3 + src/target/arm_cti.c | 4 + src/target/arm_dap.c | 4 +- src/target/arm_dpm.c | 30 +- src/target/arm_dpm.h | 3 +- src/target/armv7a.c | 169 +- src/target/armv7a.h | 5 - src/target/armv7a_mmu.c | 456 +++++ src/target/armv7a_mmu.h | 28 + src/target/armv8.c | 5 + src/target/cortex_a.c | 440 +---- src/target/cortex_a.h | 3 - src/target/cortex_m.c | 8 +- src/target/esirisc.c | 1787 +++++++++++++++++++ src/target/esirisc.h | 129 ++ src/target/esirisc_jtag.c | 514 ++++++ src/target/esirisc_jtag.h | 104 ++ src/target/esirisc_regs.h | 184 ++ src/target/mem_ap.c | 181 ++ src/target/register.c | 4 + src/target/target.c | 71 +- src/target/target.h | 24 + src/target/target_type.h | 9 + tcl/board/arty_s7.cfg | 35 + tcl/board/emcraft_imx8m-som-bsb.cfg | 22 + tcl/board/numato_mimas_a7.cfg | 36 + tcl/board/renesas_salvator-xs.cfg | 23 + tcl/cpld/xilinx-xc7.cfg | 8 + tcl/interface/ftdi/c232hm.cfg | 15 + tcl/target/atsamv.cfg | 8 + tcl/target/esi32xx.cfg | 36 + tcl/target/max32620.cfg | 28 + tcl/target/max32625.cfg | 28 + tcl/target/max3263x.cfg | 28 + tcl/target/renesas_rcar_gen3.cfg | 169 ++ tcl/target/stm32f7x.cfg | 9 + tcl/target/stm32h7x.cfg | 21 +- tcl/target/zynq_7000.cfg | 19 + 89 files changed, 7241 insertions(+), 1635 deletions(-) create mode 100644 contrib/loaders/flash/max32xxx/Makefile create mode 100644 contrib/loaders/flash/max32xxx/max32xxx.inc create mode 100644 contrib/loaders/flash/max32xxx/max32xxx.s create mode 100644 src/flash/nor/esirisc_flash.c create mode 100644 src/flash/nor/max32xxx.c create mode 100644 src/target/armv7a_mmu.c create mode 100644 src/target/armv7a_mmu.h create mode 100644 src/target/esirisc.c create mode 100644 src/target/esirisc.h create mode 100644 src/target/esirisc_jtag.c create mode 100644 src/target/esirisc_jtag.h create mode 100644 src/target/esirisc_regs.h create mode 100644 src/target/mem_ap.c create mode 100644 tcl/board/arty_s7.cfg create mode 100644 tcl/board/emcraft_imx8m-som-bsb.cfg create mode 100644 tcl/board/numato_mimas_a7.cfg create mode 100644 tcl/board/renesas_salvator-xs.cfg create mode 100644 tcl/interface/ftdi/c232hm.cfg create mode 100644 tcl/target/esi32xx.cfg create mode 100644 tcl/target/max32620.cfg create mode 100644 tcl/target/max32625.cfg create mode 100644 tcl/target/max3263x.cfg create mode 100644 tcl/target/renesas_rcar_gen3.cfg diff --git a/HACKING b/HACKING index 0d24957e1..b7ef0705b 100644 --- a/HACKING +++ b/HACKING @@ -201,7 +201,7 @@ Further reading: http://www.coreboot.org/Git The code review is intended to take as long as a week or two to allow maintainers and contributors who work on OpenOCD only in their spare -time oportunity to perform a review and raise objections. +time opportunity to perform a review and raise objections. With Gerrit much of the urgency of getting things committed has been removed as the work in progress is safely stored in Gerrit and diff --git a/README b/README index 985e39a98..30443d37c 100644 --- a/README +++ b/README @@ -42,7 +42,7 @@ e.g.: openocd -f interface/ftdi/jtagkey2.cfg -c "transport select jtag" \ -f target/ti_calypso.cfg - openocd -f interface/stlink-v2-1.cfg -c "transport select hla_swd" \ + openocd -f interface/stlink.cfg -c "transport select hla_swd" \ -f target/stm32l0.cfg After OpenOCD startup, connect GDB with @@ -117,17 +117,18 @@ Debug targets ------------- ARM11, ARM7, ARM9, AVR32, Cortex-A, Cortex-R, Cortex-M, LS102x-SAP, -Feroceon/Dragonite, DSP563xx, DSP5680xx, FA526, MIPS EJTAG, NDS32, -XScale, Intel Quark. +Feroceon/Dragonite, DSP563xx, DSP5680xx, EnSilica eSi-RISC, FA526, MIPS +EJTAG, NDS32, XScale, Intel Quark. Flash drivers ------------- -ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis, -LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI, -Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x, Stellaris, STM32, -STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx, -i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC1xxx, XMC4xxx. +ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, eSi-TSMC, +FM3, FM4, Kinetis, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, +Marvell QSPI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x, +Stellaris, STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of +AT91SAM9, LPC3180, LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood, +S3C24xx, S3C6400, XMC1xxx, XMC4xxx. ================== diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules index af092c18b..692e1b10b 100644 --- a/contrib/60-openocd.rules +++ b/contrib/60-openocd.rules @@ -1,4 +1,6 @@ # Copy this file to /etc/udev/rules.d/ +# If rules fail to reload automatically, you can refresh udev rules +# with the command "udevadm control --reload" ACTION!="add|change", GOTO="openocd_rules_end" SUBSYSTEM!="usb|tty|hidraw", GOTO="openocd_rules_end" diff --git a/contrib/loaders/Makefile b/contrib/loaders/Makefile index a9a27706d..0a637aff5 100644 --- a/contrib/loaders/Makefile +++ b/contrib/loaders/Makefile @@ -12,6 +12,7 @@ ARM_CROSS_COMPILE ?= arm-none-eabi- arm_dirs = \ flash/fm4 \ flash/kinetis_ke \ + flash/max32xxx \ flash/xmc1xxx \ debug/xscale diff --git a/contrib/loaders/flash/max32xxx/Makefile b/contrib/loaders/flash/max32xxx/Makefile new file mode 100644 index 000000000..8f3f9242e --- /dev/null +++ b/contrib/loaders/flash/max32xxx/Makefile @@ -0,0 +1,19 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= arm-none-eabi- +AS = $(CROSS_COMPILE)as +OBJCOPY = $(CROSS_COMPILE)objcopy + +all: max32xxx.inc + +%.elf: %.s + $(AS) $< -o $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.bin *.inc diff --git a/contrib/loaders/flash/max32xxx/max32xxx.inc b/contrib/loaders/flash/max32xxx/max32xxx.inc new file mode 100644 index 000000000..442165d0d --- /dev/null +++ b/contrib/loaders/flash/max32xxx/max32xxx.inc @@ -0,0 +1,6 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0xdf,0xf8,0x44,0x40,0xd0,0xf8,0x00,0x80,0xb8,0xf1,0x00,0x0f,0x1a,0xd0,0x47,0x68, +0x47,0x45,0xf7,0xd0,0x22,0x60,0x02,0xf1,0x04,0x02,0x57,0xf8,0x04,0x8b,0xc4,0xf8, +0x30,0x80,0xa5,0x68,0x45,0xf0,0x01,0x05,0xa5,0x60,0xd4,0xf8,0x08,0x80,0x18,0xf0, +0x01,0x0f,0xfa,0xd1,0x8f,0x42,0x28,0xbf,0x00,0xf1,0x08,0x07,0x47,0x60,0x01,0x3b, +0x03,0xb1,0xdf,0xe7,0x00,0xbe,0x00,0xbf,0x00,0x00,0x00,0x40, diff --git a/contrib/loaders/flash/max32xxx/max32xxx.s b/contrib/loaders/flash/max32xxx/max32xxx.s new file mode 100644 index 000000000..f5306d6c5 --- /dev/null +++ b/contrib/loaders/flash/max32xxx/max32xxx.s @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2016 by Maxim Integrated * + * Kevin Gillespie . * + ***************************************************************************/ + +.text +.syntax unified +.cpu cortex-m3 +.thumb +.thumb_func + +/* + * Params : + * r0 = workarea start + * r1 = workarea end + * r2 = target address + * r3 = count (32bit words) + * r4 = pFLASH_CTRL_BASE + * + * Clobbered: + * r5 = FLASHWRITECMD + * r7 - rp + * r8 - wp, tmp + */ + +write: + +wait_fifo: +ldr r8, [r0, #0] /* read wp */ +cmp r8, #0 /* abort if wp == 0 */ +beq exit +ldr r7, [r0, #4] /* read rp */ +cmp r7, r8 /* wait until rp != wp */ +beq wait_fifo + +mainloop: +str r2, [r4, #0x00] /* FLSH_ADDR - write address */ +add r2, r2, #4 /* increment target address */ +ldr r8, [r7], #4 +str r8, [r4, #0x30] /* FLSH_DATA0 - write data */ +ldr r5, [r4, #0x08] /* FLSH_CN */ +orr r5, r5, #1 +str r5, [r4, #0x08] /* FLSH_CN - enable write */ +busy: +ldr r8, [r4, #0x08] /* FLSH_CN */ +tst r8, #1 +bne busy + +cmp r7, r1 /* wrap rp at end of buffer */ +it cs +addcs r7, r0, #8 /* skip loader args */ +str r7, [r0, #4] /* store rp */ +subs r3, r3, #1 /* decrement word count */ +cbz r3, exit /* loop if not done */ +b wait_fifo +exit: +bkpt diff --git a/contrib/rtos-helpers/uCOS-III-openocd.c b/contrib/rtos-helpers/uCOS-III-openocd.c index 9037334f2..5a37bd4c5 100644 --- a/contrib/rtos-helpers/uCOS-III-openocd.c +++ b/contrib/rtos-helpers/uCOS-III-openocd.c @@ -3,8 +3,8 @@ * impossible to determine the appropriate offsets within the structure * unaided. A priori knowledge of offsets based on os_dbg.c is tied to a * specific release and thusly, brittle. The constants defined below - * provide the neccessary information OpenOCD needs to provide support - * in the most robust manner possible. + * provide the necessary information OpenOCD needs to provide support in + * the most robust manner possible. * * This file should be linked along with the project to enable RTOS * support for uC/OS-III. diff --git a/doc/openocd.texi b/doc/openocd.texi index 3932db3c3..6c5cb502e 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2114,6 +2114,7 @@ In such cases, just specify the relevant port number as "disabled". If you disable all access through TCP/IP, you will need to use the command line @option{-pipe} option. +@anchor{gdb_port} @deffn {Command} gdb_port [number] @cindex GDB server Normally gdb listens to a TCP/IP port, but GDB can also @@ -2139,11 +2140,15 @@ The GDB port for the first target will be the base port, the second target will listen on gdb_port + 1, and so on. When not specified during the configuration stage, the port @var{number} defaults to 3333. +When @var{number} is not a numeric value, incrementing it to compute +the next port number does not work. In this case, specify the proper +@var{number} for each target by using the option @code{-gdb-port} of the +commands @command{target create} or @command{$target_name configure}. +@xref{gdbportoverride,,option -gdb-port}. Note: when using "gdb_port pipe", increasing the default remote timeout in gdb (with 'set remotetimeout') is recommended. An insufficient timeout may cause initialization to fail with "Unknown remote qXfer reply: OK". - @end deffn @deffn {Command} tcl_port [number] @@ -4270,6 +4275,8 @@ compact Thumb2 instruction set. @item @code{dragonite} -- resembles arm966e @item @code{dsp563xx} -- implements Freescale's 24-bit DSP. (Support for this is still incomplete.) +@item @code{esirisc} -- this is an EnSilica eSi-RISC core. +The current implementation supports eSi-32xx cores. @item @code{fa526} -- resembles arm920 (w/o Thumb) @item @code{feroceon} -- resembles arm926 @item @code{mips_m4k} -- a MIPS core @@ -4458,6 +4465,13 @@ to the target. Currently, only the @code{aarch64} target makes use of this optio where it is a mandatory configuration for the target run control. @xref{armcrosstrigger,,ARM Cross-Trigger Interface}, for instruction on how to declare and control a CTI instance. + +@anchor{gdbportoverride} +@item @code{-gdb-port} @var{number} -- see command @command{gdb_port} for the +possible values of the parameter @var{number}, which are not only numeric values. +Use this option to override, for this target only, the global parameter set with +command @command{gdb_port}. +@xref{gdb_port,,command gdb_port}. @end itemize @end deffn @@ -5635,6 +5649,27 @@ Note that in order for this command to take effect, the target needs to be reset supported.} @end deffn +@deffn {Flash Driver} esirisc +Members of the eSi-RISC family may optionally include internal flash programmed +via the eSi-TSMC Flash interface. Additional parameters are required to +configure the driver: @option{cfg_address} is the base address of the +configuration register interface, @option{clock_hz} is the expected clock +frequency, and @option{wait_states} is the number of configured read wait states. + +@example +flash bank $_FLASHNAME esirisc base_address size_bytes 0 0 $_TARGETNAME cfg_address clock_hz wait_states +@end example + +@deffn Command {esirisc_flash mass_erase} (bank_id) +Erases all pages in data memory for the bank identified by @option{bank_id}. +@end deffn + +@deffn Command {esirisc_flash ref_erase} (bank_id) +Erases the reference cell for the bank identified by @option{bank_id}. This is +an uncommon operation. +@end deffn +@end deffn + @deffn {Flash Driver} fm3 All members of the FM3 microcontroller family from Fujitsu include internal flash and use ARM Cortex-M3 cores. @@ -6394,23 +6429,24 @@ flash bank $_FLASHNAME stm32f1x 0x08080000 0 0 0 $_TARGETNAME Some stm32f1x-specific commands are defined: @deffn Command {stm32f1x lock} num -Locks the entire stm32 device. +Locks the entire stm32 device against reading. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn Command {stm32f1x unlock} num -Unlocks the entire stm32 device. +Unlocks the entire stm32 device for reading. This command will cause +a mass erase of the entire stm32 device if previously locked. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn Command {stm32f1x mass_erase} num -Mass erases the entire stm32f1x device. +Mass erases the entire stm32 device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn Command {stm32f1x options_read} num -Read and display the stm32 option bytes written by -the @command{stm32f1x options_write} command. +Reads and displays active stm32 option bytes loaded during POR +or upon executing the @command{stm32f1x options_load} command. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @@ -6418,6 +6454,13 @@ The @var{num} parameter is a value shown by @command{flash banks}. Writes the stm32 option byte with the specified values. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn + +@deffn Command {stm32f1x options_load} num +Generates a special kind of reset to re-load the stm32 option bytes written +by the @command{stm32f1x options_write} or @command{flash protect} commands +without having to power cycle the target. Not applicable to stm32f1x devices. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn @end deffn @deffn {Flash Driver} stm32f2x @@ -6584,6 +6627,42 @@ The @var{num} parameter is a value shown by @command{flash banks}. Mass erases the entire stm32l4x device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn + +@deffn Command {stm32l4x option_read} num reg_offset +Reads an option byte register from the stm32l4x device. +The @var{num} parameter is a value shown by @command{flash banks}, @var{reg_offset} +is the register offset of the Option byte to read. + +For example to read the FLASH_OPTR register: +@example +stm32l4x option_read 0 0x20 +# Option Register: <0x40022020> = 0xffeff8aa +@end example + +The above example will read out the FLASH_OPTR register which contains the RDP +option byte, Watchdog configuration, BOR level etc. +@end deffn + +@deffn Command {stm32l4x option_write} num reg_offset reg_mask +Write an option byte register of the stm32l4x device. +The @var{num} parameter is a value shown by @command{flash banks}, @var{reg_offset} +is the register offset of the Option byte to write, and @var{reg_mask} is the mask +to apply when writing the register (only bits with a '1' will be touched). + +For example to write the WRP1AR option bytes: +@example +stm32l4x option_write 0 0x28 0x00FF0000 0x00FF00FF +@end example + +The above example will write the WRP1AR option register configuring the Write protection +Area A for bank 1. The above example set WRP1AR_END=255, WRP1AR_START=0. +This will effectively write protect all sectors in flash bank 1. +@end deffn + +@deffn Command {stm32l4x option_load} num +Forces a re-load of the option byte registers. Will cause a reset of the device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn @end deffn @deffn {Flash Driver} str7x @@ -8687,6 +8766,12 @@ Selects whether interrupts will be processed when single stepping configure l2x cache @end deffn +@deffn Command {cortex_a mmu dump} [@option{0}|@option{1}|@option{addr} address [@option{num_entries}]] +Dump the MMU translation table from TTB0 or TTB1 register, or from physical +memory location @var{address}. When dumping the table from @var{address}, print at most +@var{num_entries} page table entries. @var{num_entries} is optional, if omitted, the maximum +possible (4096) entries are printed. +@end deffn @subsection ARMv7-R specific commands @cindex Cortex-R @@ -8777,7 +8862,7 @@ baud with our custom divisor to get 12MHz) @item @code{itmdump -f /dev/ttyUSB1 -d1} @item OpenOCD invocation line: @example -openocd -f interface/stlink-v2-1.cfg \ +openocd -f interface/stlink.cfg \ -c "transport select hla_swd" \ -f target/stm32l1.cfg \ -c "tpiu config external uart off 24000000 12000000" @@ -8885,6 +8970,29 @@ Selects whether interrupts will be processed when single stepping. The default c @option{on}. @end deffn +@section EnSilica eSi-RISC Architecture + +eSi-RISC is a highly configurable microprocessor architecture for embedded systems +provided by EnSilica. (See: @url{http://www.ensilica.com/risc-ip/}.) + +@subsection esirisc specific commands +@deffn Command {esirisc cache_arch} (@option{harvard}|@option{von_neumann}) +Configure the caching architecture. Targets with the @code{UNIFIED_ADDRESS_SPACE} +option disabled employ a Harvard architecture. By default, @option{von_neumann} is assumed. +@end deffn + +@deffn Command {esirisc flush_caches} +Flush instruction and data caches. This command requires that the target is halted +when the command is issued and configured with an instruction or data cache. +@end deffn + +@deffn Command {esirisc hwdc} (@option{all}|@option{none}|mask ...) +Configure hardware debug control. The HWDC register controls which exceptions return +control back to the debugger. Possible masks are @option{all}, @option{none}, +@option{reset}, @option{interrupt}, @option{syscall}, @option{error}, and @option{debug}. +By default, @option{reset}, @option{error}, and @option{debug} are enabled. +@end deffn + @section Intel Architecture Intel Quark X10xx is the first product in the Quark family of SoCs. It is an IA-32 diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 9a58239eb..639330549 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -25,6 +25,7 @@ NOR_DRIVERS = \ %D%/dsp5680xx_flash.c \ %D%/efm32.c \ %D%/em357.c \ + %D%/esirisc_flash.c \ %D%/faux.c \ %D%/fespi.c \ %D%/fm3.c \ @@ -36,6 +37,7 @@ NOR_DRIVERS = \ %D%/lpc288x.c \ %D%/lpc2900.c \ %D%/lpcspifi.c \ + %D%/max32xxx.c \ %D%/mdr.c \ %D%/msp432.c \ %D%/mrvlqspi.c \ diff --git a/src/flash/nor/at91sam4.c b/src/flash/nor/at91sam4.c index c5b31e964..2eec15e2a 100644 --- a/src/flash/nor/at91sam4.c +++ b/src/flash/nor/at91sam4.c @@ -2376,6 +2376,11 @@ static int sam4_GetInfo(struct sam4_chip *pChip) { const struct sam4_reg_list *pReg; uint32_t regval; + int r; + + r = sam4_ReadAllRegs(pChip); + if (r != ERROR_OK) + return r; pReg = &(sam4_all_regs[0]); while (pReg->name) { @@ -2545,7 +2550,7 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate) sam4_explain_chipid_cidr(pPrivate->pChip); return ERROR_FAIL; } else { - LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr); + LOG_DEBUG("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr); } /* DANGER: THERE ARE DRAGONS HERE */ @@ -2581,14 +2586,35 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate) return ERROR_OK; } -static int _sam4_probe(struct flash_bank *bank, int noise) +static int sam4_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct sam4_bank_private *pPrivate; + int k = bank->size / 1024; + + pPrivate = get_sam4_bank_private(bank); + if (pPrivate == NULL) { + buf[0] = '\0'; + return ERROR_FAIL; + } + + snprintf(buf, buf_size, + "%s bank %d: %d kB at 0x%08" PRIx32, + pPrivate->pChip->details.name, + pPrivate->bank_number, + k, + bank->base); + + return ERROR_OK; +} + +static int sam4_probe(struct flash_bank *bank) { unsigned x; int r; struct sam4_bank_private *pPrivate; - LOG_DEBUG("Begin: Bank: %d, Noise: %d", bank->bank_number, noise); + LOG_DEBUG("Begin: Bank: %d", bank->bank_number); if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -2616,7 +2642,7 @@ static int _sam4_probe(struct flash_bank *bank, int noise) for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) { if (bank->base == pPrivate->pChip->details.bank[x].base_address) { bank->size = pPrivate->pChip->details.bank[x].size_bytes; - LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x); + LOG_DEBUG("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x); break; } } @@ -2655,14 +2681,15 @@ static int _sam4_probe(struct flash_bank *bank, int noise) return r; } -static int sam4_probe(struct flash_bank *bank) -{ - return _sam4_probe(bank, 1); -} - static int sam4_auto_probe(struct flash_bank *bank) { - return _sam4_probe(bank, 0); + struct sam4_bank_private *pPrivate; + + pPrivate = get_sam4_bank_private(bank); + if (pPrivate && pPrivate->probed) + return ERROR_OK; + + return sam4_probe(bank); } static int sam4_erase(struct flash_bank *bank, int first, int last) @@ -2762,20 +2789,17 @@ static int sam4_page_read(struct sam4_bank_private *pPrivate, unsigned pagenum, return r; } -static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, const uint8_t *buf) +static int sam4_set_wait(struct sam4_bank_private *pPrivate) { - uint32_t adr; - uint32_t status; uint32_t fmr; /* EEFC Flash Mode Register */ int r; - adr = pagenum * pPrivate->page_size; - adr = (adr + pPrivate->base_address); - /* Get flash mode register value */ r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address, &fmr); - if (r != ERROR_OK) - LOG_DEBUG("Error Read failed: read flash mode register"); + if (r != ERROR_OK) { + LOG_ERROR("Error Read failed: read flash mode register"); + return r; + } /* Clear flash wait state field */ fmr &= 0xfffff0ff; @@ -2786,7 +2810,19 @@ static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, LOG_DEBUG("Flash Mode: 0x%08x", ((unsigned int)(fmr))); r = target_write_u32(pPrivate->pBank->target, pPrivate->controller_address, fmr); if (r != ERROR_OK) - LOG_DEBUG("Error Write failed: set flash mode register"); + LOG_ERROR("Error Write failed: set flash mode register"); + + return r; +} + +static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, const uint8_t *buf) +{ + uint32_t adr; + uint32_t status; + int r; + + adr = pagenum * pPrivate->page_size; + adr = (adr + pPrivate->base_address); /* 1st sector 8kBytes - page 0 - 15*/ /* 2nd sector 8kBytes - page 16 - 30*/ @@ -2874,6 +2910,10 @@ static int sam4_write(struct flash_bank *bank, goto done; } + r = sam4_set_wait(pPrivate); + if (r != ERROR_OK) + goto done; + /* what page do we start & end in? */ page_cur = offset / pPrivate->page_size; page_end = (offset + count - 1) / pPrivate->page_size; @@ -3091,7 +3131,8 @@ showall: } if ((who >= 0) && (((unsigned)(who)) < pChip->details.n_gpnvms)) { r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), who, &v); - command_print(CMD_CTX, "sam4-gpnvm%u: %u", who, v); + if (r == ERROR_OK) + command_print(CMD_CTX, "sam4-gpnvm%u: %u", who, v); return r; } else { command_print(CMD_CTX, "sam4-gpnvm invalid GPNVM: %u", who); @@ -3203,5 +3244,6 @@ struct flash_driver at91sam4_flash = { .auto_probe = sam4_auto_probe, .erase_check = default_flash_blank_check, .protect_check = sam4_protect_check, + .info = sam4_info, .free_driver_priv = sam4_free_driver_priv, }; diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index 017d1441e..5f172d118 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -165,12 +165,13 @@ static const struct samd_part samd21_parts[] = { { 0xE, "SAMD21E14A", 16, 2 }, /* SAMR21 parts have integrated SAMD21 with a radio */ + { 0x18, "SAMR21G19A", 256, 32 }, /* with 512k of serial flash */ { 0x19, "SAMR21G18A", 256, 32 }, { 0x1A, "SAMR21G17A", 128, 32 }, - { 0x1B, "SAMR21G16A", 64, 32 }, + { 0x1B, "SAMR21G16A", 64, 16 }, { 0x1C, "SAMR21E18A", 256, 32 }, { 0x1D, "SAMR21E17A", 128, 32 }, - { 0x1E, "SAMR21E16A", 64, 32 }, + { 0x1E, "SAMR21E16A", 64, 16 }, /* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */ { 0x20, "SAMD21J16B", 64, 8 }, diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 2b3146da9..62db3feea 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -38,6 +38,7 @@ extern struct flash_driver cfi_flash; extern struct flash_driver dsp5680xx_flash; extern struct flash_driver efm32_flash; extern struct flash_driver em357_flash; +extern struct flash_driver esirisc_flash; extern struct flash_driver faux_flash; extern struct flash_driver fm3_flash; extern struct flash_driver fm4_flash; @@ -49,6 +50,7 @@ extern struct flash_driver lpc2000_flash; extern struct flash_driver lpc288x_flash; extern struct flash_driver lpc2900_flash; extern struct flash_driver lpcspifi_flash; +extern struct flash_driver max32xxx_flash; extern struct flash_driver mdr_flash; extern struct flash_driver mrvlqspi_flash; extern struct flash_driver msp432_flash; @@ -103,6 +105,7 @@ static struct flash_driver *flash_drivers[] = { &dsp5680xx_flash, &efm32_flash, &em357_flash, + &esirisc_flash, &faux_flash, &fm3_flash, &fm4_flash, @@ -114,6 +117,7 @@ static struct flash_driver *flash_drivers[] = { &lpc288x_flash, &lpc2900_flash, &lpcspifi_flash, + &max32xxx_flash, &mdr_flash, &mrvlqspi_flash, &msp432_flash, diff --git a/src/flash/nor/esirisc_flash.c b/src/flash/nor/esirisc_flash.c new file mode 100644 index 000000000..f3833df1c --- /dev/null +++ b/src/flash/nor/esirisc_flash.c @@ -0,0 +1,621 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * 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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +/* eSi-TSMC Flash Registers */ +#define CONTROL 0x00 /* Control Register */ +#define TIMING0 0x04 /* Timing Register 0 */ +#define TIMING1 0x08 /* Timing Register 1 */ +#define TIMING2 0x0c /* Timing Register 2 */ +#define UNLOCK1 0x18 /* Unlock 1 */ +#define UNLOCK2 0x1c /* Unlock 2 */ +#define ADDRESS 0x20 /* Erase/Program Address */ +#define PB_DATA 0x24 /* Program Buffer Data */ +#define PB_INDEX 0x28 /* Program Buffer Index */ +#define STATUS 0x2c /* Status Register */ +#define REDUN_0 0x30 /* Redundant Address 0 */ +#define REDUN_1 0x34 /* Redundant Address 1 */ + +/* Control Fields */ +#define CONTROL_SLM (1<<0) /* Sleep Mode */ +#define CONTROL_WP (1<<1) /* Register Write Protect */ +#define CONTROL_E (1<<3) /* Erase */ +#define CONTROL_EP (1<<4) /* Erase Page */ +#define CONTROL_P (1<<5) /* Program Flash */ +#define CONTROL_ERC (1<<6) /* Erase Reference Cell */ +#define CONTROL_R (1<<7) /* Recall Trim Code */ +#define CONTROL_AP (1<<8) /* Auto-Program */ + +/* Timing Fields */ +#define TIMING0_R(x) (((x) << 0) & 0x3f) /* Read Wait States */ +#define TIMING0_F(x) (((x) << 16) & 0xffff0000) /* Tnvh Clock Cycles */ +#define TIMING1_E(x) (((x) << 0) & 0xffffff) /* Tme/Terase/Tre Clock Cycles */ +#define TIMING2_P(x) (((x) << 0) & 0xffff) /* Tprog Clock Cycles */ +#define TIMING2_H(x) (((x) << 16) & 0xff0000) /* Clock Cycles in 100ns */ +#define TIMING2_T(x) (((x) << 24) & 0xf000000) /* Clock Cycles in 10ns */ + +/* Status Fields */ +#define STATUS_BUSY (1<<0) /* Busy (Erase/Program) */ +#define STATUS_WER (1<<1) /* Write Protect Error */ +#define STATUS_DR (1<<2) /* Disable Redundancy */ +#define STATUS_DIS (1<<3) /* Discharged */ +#define STATUS_BO (1<<4) /* Brown Out */ + +/* Redundant Address Fields */ +#define REDUN_R (1<<0) /* Used */ +#define REDUN_P(x) (((x) << 12) & 0x7f000) /* Redundant Page Address */ + +/* + * The eSi-TSMC Flash manual provides two sets of timings based on the + * underlying flash process. By default, 90nm is assumed. + */ +#if 0 /* 55nm */ +#define TNVH 5000 /* 5us */ +#define TME 80000000 /* 80ms */ +#define TERASE 160000000 /* 160ms */ +#define TRE 100000000 /* 100ms */ +#define TPROG 8000 /* 8us */ +#else /* 90nm */ +#define TNVH 5000 /* 5us */ +#define TME 20000000 /* 20ms */ +#define TERASE 40000000 /* 40ms */ +#define TRE 40000000 /* 40ms */ +#define TPROG 40000 /* 40us */ +#endif + +#define CONTROL_TIMEOUT 5000 /* 5s */ +#define PAGE_SIZE 4096 +#define PB_MAX 32 + +#define NUM_NS_PER_S 1000000000ULL + +struct esirisc_flash_bank { + bool probed; + uint32_t cfg; + uint32_t clock; + uint32_t wait_states; +}; + +FLASH_BANK_COMMAND_HANDLER(esirisc_flash_bank_command) +{ + struct esirisc_flash_bank *esirisc_info; + + if (CMD_ARGC < 9) + return ERROR_COMMAND_SYNTAX_ERROR; + + esirisc_info = calloc(1, sizeof(struct esirisc_flash_bank)); + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], esirisc_info->cfg); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], esirisc_info->clock); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], esirisc_info->wait_states); + + bank->driver_priv = esirisc_info; + + return ERROR_OK; +} + +/* + * Register writes are ignored if the control.WP flag is set; the + * following sequence is required to modify this flag even when + * protection is disabled. + */ +static int esirisc_flash_unlock(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + + target_write_u32(target, esirisc_info->cfg + UNLOCK1, 0x7123); + target_write_u32(target, esirisc_info->cfg + UNLOCK2, 0x812a); + target_write_u32(target, esirisc_info->cfg + UNLOCK1, 0xbee1); + + return ERROR_OK; +} + +static int esirisc_flash_disable_protect(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t control; + + target_read_u32(target, esirisc_info->cfg + CONTROL, &control); + if (!(control & CONTROL_WP)) + return ERROR_OK; + + esirisc_flash_unlock(bank); + + control &= ~CONTROL_WP; + + target_write_u32(target, esirisc_info->cfg + CONTROL, control); + + return ERROR_OK; +} + +static int esirisc_flash_enable_protect(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t control; + + target_read_u32(target, esirisc_info->cfg + CONTROL, &control); + if (control & CONTROL_WP) + return ERROR_OK; + + esirisc_flash_unlock(bank); + + control |= CONTROL_WP; + + target_write_u32(target, esirisc_info->cfg + CONTROL, control); + + return ERROR_OK; +} + +static int esirisc_flash_check_status(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t status; + + target_read_u32(target, esirisc_info->cfg + STATUS, &status); + if (status & STATUS_WER) { + LOG_ERROR("%s: bad status: 0x%" PRIx32, bank->name, status); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int esirisc_flash_clear_status(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + + target_write_u32(target, esirisc_info->cfg + STATUS, STATUS_WER); + + return ERROR_OK; +} + +static int esirisc_flash_wait(struct flash_bank *bank, int ms) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t status; + int64_t t; + + t = timeval_ms(); + for (;;) { + target_read_u32(target, esirisc_info->cfg + STATUS, &status); + if (!(status & STATUS_BUSY)) + return ERROR_OK; + + if ((timeval_ms() - t) > ms) + return ERROR_TARGET_TIMEOUT; + + keep_alive(); + } +} + +static int esirisc_flash_control(struct flash_bank *bank, uint32_t control) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + + esirisc_flash_clear_status(bank); + + target_write_u32(target, esirisc_info->cfg + CONTROL, control); + + int retval = esirisc_flash_wait(bank, CONTROL_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: control timed out: 0x%" PRIx32, bank->name, control); + return retval; + } + + return esirisc_flash_check_status(bank); +} + +static int esirisc_flash_recall(struct flash_bank *bank) +{ + return esirisc_flash_control(bank, CONTROL_R); +} + +static int esirisc_flash_erase(struct flash_bank *bank, int first, int last) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval = ERROR_OK; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + for (int page = first; page < last; ++page) { + uint32_t address = page * PAGE_SIZE; + + target_write_u32(target, esirisc_info->cfg + ADDRESS, address); + + retval = esirisc_flash_control(bank, CONTROL_EP); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to erase address: 0x%" PRIx32, bank->name, address); + break; + } + } + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static int esirisc_flash_mass_erase(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + target_write_u32(target, esirisc_info->cfg + ADDRESS, 0); + + retval = esirisc_flash_control(bank, CONTROL_E); + if (retval != ERROR_OK) + LOG_ERROR("%s: failed to mass erase", bank->name); + + esirisc_flash_enable_protect(bank); + + return retval; +} + +/* + * Per TSMC, the reference cell should be erased once per sample. This + * is typically done during wafer sort, however we include support for + * those that may need to calibrate flash at a later time. + */ +static int esirisc_flash_ref_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + retval = esirisc_flash_control(bank, CONTROL_ERC); + if (retval != ERROR_OK) + LOG_ERROR("%s: failed to erase reference cell", bank->name); + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static int esirisc_flash_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + if (set) + esirisc_flash_enable_protect(bank); + else + esirisc_flash_disable_protect(bank); + + return ERROR_OK; +} + +static int esirisc_flash_fill_pb(struct flash_bank *bank, + const uint8_t *buffer, uint32_t count) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + struct esirisc_common *esirisc = target_to_esirisc(target); + + /* + * The pb_index register is auto-incremented when pb_data is written + * and should be cleared before each operation. + */ + target_write_u32(target, esirisc_info->cfg + PB_INDEX, 0); + + /* + * The width of the pb_data register depends on the underlying + * target; writing one byte at a time incurs a significant + * performance penalty and should be avoided. + */ + while (count > 0) { + uint32_t max_bytes = DIV_ROUND_UP(esirisc->num_bits, 8); + uint32_t num_bytes = MIN(count, max_bytes); + + target_write_buffer(target, esirisc_info->cfg + PB_DATA, num_bytes, buffer); + + buffer += num_bytes; + count -= num_bytes; + } + + return ERROR_OK; +} + +static int esirisc_flash_write(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval = ERROR_OK; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + /* + * The address register is auto-incremented based on the contents of + * the pb_index register after each operation completes. It can be + * set once provided pb_index is cleared before each operation. + */ + target_write_u32(target, esirisc_info->cfg + ADDRESS, offset); + + /* + * Care must be taken when filling the program buffer; a maximum of + * 32 bytes may be written at a time and may not cross a 32-byte + * boundary based on the current offset. + */ + while (count > 0) { + uint32_t max_bytes = PB_MAX - (offset & 0x1f); + uint32_t num_bytes = MIN(count, max_bytes); + + esirisc_flash_fill_pb(bank, buffer, num_bytes); + + retval = esirisc_flash_control(bank, CONTROL_P); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to program address: 0x%" PRIx32, bank->name, offset); + break; + } + + buffer += num_bytes; + offset += num_bytes; + count -= num_bytes; + } + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static uint32_t esirisc_flash_num_cycles(struct flash_bank *bank, uint64_t ns) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + + /* apply scaling factor to avoid truncation */ + uint64_t hz = (uint64_t)esirisc_info->clock * 1000; + uint64_t num_cycles = ((hz / NUM_NS_PER_S) * ns) / 1000; + + if (hz % NUM_NS_PER_S > 0) + num_cycles++; + + return num_cycles; +} + +static int esirisc_flash_init(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t value; + int retval; + + esirisc_flash_disable_protect(bank); + + /* initialize timing registers */ + value = TIMING0_F(esirisc_flash_num_cycles(bank, TNVH)) | + TIMING0_R(esirisc_info->wait_states); + + LOG_DEBUG("TIMING0: 0x%" PRIx32, value); + target_write_u32(target, esirisc_info->cfg + TIMING0, value); + + value = TIMING1_E(esirisc_flash_num_cycles(bank, TERASE)); + + LOG_DEBUG("TIMING1: 0x%" PRIx32, value); + target_write_u32(target, esirisc_info->cfg + TIMING1, value); + + value = TIMING2_T(esirisc_flash_num_cycles(bank, 10)) | + TIMING2_H(esirisc_flash_num_cycles(bank, 100)) | + TIMING2_P(esirisc_flash_num_cycles(bank, TPROG)); + + LOG_DEBUG("TIMING2: 0x%" PRIx32, value); + target_write_u32(target, esirisc_info->cfg + TIMING2, value); + + /* recall trim code */ + retval = esirisc_flash_recall(bank); + if (retval != ERROR_OK) + LOG_ERROR("%s: failed to recall trim code", bank->name); + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static int esirisc_flash_probe(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + bank->num_sectors = bank->size / PAGE_SIZE; + bank->sectors = alloc_block_array(0, PAGE_SIZE, bank->num_sectors); + + /* + * Register write protection is enforced using a single protection + * block for the entire bank. This is as good as it gets. + */ + bank->num_prot_blocks = 1; + bank->prot_blocks = alloc_block_array(0, bank->size, bank->num_prot_blocks); + + retval = esirisc_flash_init(bank); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to initialize bank", bank->name); + return retval; + } + + esirisc_info->probed = true; + + return ERROR_OK; +} + +static int esirisc_flash_auto_probe(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + + if (esirisc_info->probed) + return ERROR_OK; + + return esirisc_flash_probe(bank); +} + +static int esirisc_flash_protect_check(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t control; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + target_read_u32(target, esirisc_info->cfg + CONTROL, &control); + + /* single protection block (also see: esirisc_flash_probe()) */ + bank->prot_blocks[0].is_protected = !!(control & CONTROL_WP); + + return ERROR_OK; +} + +static int esirisc_flash_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + + snprintf(buf, buf_size, + "%4s cfg at 0x%" PRIx32 ", clock %" PRId32 ", wait_states %" PRId32, + "", /* align with first line */ + esirisc_info->cfg, + esirisc_info->clock, + esirisc_info->wait_states); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_esirisc_flash_mass_erase_command) +{ + struct flash_bank *bank; + int retval; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = esirisc_flash_mass_erase(bank); + + command_print(CMD_CTX, "mass erase %s", + (retval == ERROR_OK) ? "successful" : "failed"); + + return retval; +} + +COMMAND_HANDLER(handle_esirisc_flash_ref_erase_command) +{ + struct flash_bank *bank; + int retval; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = esirisc_flash_ref_erase(bank); + + command_print(CMD_CTX, "erase reference cell %s", + (retval == ERROR_OK) ? "successful" : "failed"); + + return retval; +} + +static const struct command_registration esirisc_flash_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = handle_esirisc_flash_mass_erase_command, + .mode = COMMAND_EXEC, + .help = "erases all pages in data memory", + .usage = "bank_id", + }, + { + .name = "ref_erase", + .handler = handle_esirisc_flash_ref_erase_command, + .mode = COMMAND_EXEC, + .help = "erases reference cell (uncommon)", + .usage = "bank_id", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration esirisc_flash_command_handlers[] = { + { + .name = "esirisc_flash", + .mode = COMMAND_ANY, + .help = "eSi-RISC flash command group", + .usage = "", + .chain = esirisc_flash_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver esirisc_flash = { + .name = "esirisc", + .commands = esirisc_flash_command_handlers, + .usage = "flash bank bank_id 'esirisc' base_address size_bytes 0 0 target " + "cfg_address clock_hz wait_states", + .flash_bank_command = esirisc_flash_bank_command, + .erase = esirisc_flash_erase, + .protect = esirisc_flash_protect, + .write = esirisc_flash_write, + .read = default_flash_read, + .probe = esirisc_flash_probe, + .auto_probe = esirisc_flash_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = esirisc_flash_protect_check, + .info = esirisc_flash_info, +}; diff --git a/src/flash/nor/max32xxx.c b/src/flash/nor/max32xxx.c new file mode 100644 index 000000000..ca037de4d --- /dev/null +++ b/src/flash/nor/max32xxx.c @@ -0,0 +1,997 @@ +/*************************************************************************** + * Copyright (C) 2016 by Maxim Integrated * + * Kevin Gillespie * + * * + * 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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include + +/* Register Addresses */ +#define FLSH_ADDR 0x000 +#define FLSH_CLKDIV 0x004 +#define FLSH_CN 0x008 +#define PR1E_ADDR 0x00C +#define PR2S_ADDR 0x010 +#define PR2E_ADDR 0x014 +#define PR3S_ADDR 0x018 +#define PR3E_ADDR 0x01C +#define FLSH_MD 0x020 +#define FLSH_INT 0x024 +#define FLSH_DATA0 0x030 +#define FLSH_DATA1 0x034 +#define FLSH_DATA2 0x038 +#define FLSH_DATA3 0x03C +#define FLSH_BL_CTRL 0x170 +#define FLSH_PROT 0x300 + +#define ARM_PID_REG 0xE00FFFE0 +#define MAX326XX_ID_REG 0x40000838 + +/* Register settings */ +#define FLSH_INT_AF 0x00000002 + +#define FLSH_CN_UNLOCK_MASK 0xF0000000 +#define FLSH_CN_UNLOCK_VALUE 0x20000000 + +#define FLSH_CN_PEND 0x01000000 + +#define FLSH_CN_ERASE_CODE_MASK 0x0000FF00 +#define FLSH_CN_ERASE_CODE_PGE 0x00005500 +#define FLSH_CN_ERASE_CODE_ME 0x0000AA00 + +#define FLSH_CN_PGE 0x00000004 +#define FLSH_CN_ME 0x00000002 +#define FLSH_CN_WR 0x00000001 +#define FLASH_BL_CTRL_23 0x00020000 +#define FLASH_BL_CTRL_IFREN 0x00000001 + +#define ARM_PID_DEFAULT_CM3 0xB4C3 +#define ARM_PID_DEFAULT_CM4 0xB4C4 +#define MAX326XX_ID 0x4D + +static int max32xxx_mass_erase(struct flash_bank *bank); + +struct max32xxx_flash_bank { + int probed; + int max326xx; + unsigned int flash_size; + unsigned int flc_base; + unsigned int sector_size; + unsigned int clkdiv_value; + unsigned int int_state; + unsigned int burst_size_bits; +}; + +/* see contib/loaders/flash/max32xxx/max32xxx.s for src */ +static const uint8_t write_code[] = { +#include "../../contrib/loaders/flash/max32xxx/max32xxx.inc" +}; + +/* Config Command: flash bank name driver base size chip_width bus_width target [driver_option] + flash bank max32xxx 0 0 [burst_bits] + */ +FLASH_BANK_COMMAND_HANDLER(max32xxx_flash_bank_command) +{ + struct max32xxx_flash_bank *info; + + if (CMD_ARGC < 9) { + LOG_WARNING("incomplete flash bank max32xxx configuration: 0 0 [burst_bits]"); + return ERROR_FLASH_BANK_INVALID; + } + + info = calloc(sizeof(struct max32xxx_flash_bank), 1); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], info->flash_size); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->flc_base); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->sector_size); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], info->clkdiv_value); + + if (CMD_ARGC > 9) + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[9], info->burst_size_bits); + else + info->burst_size_bits = 32; + + info->int_state = 0; + bank->driver_priv = info; + return ERROR_OK; +} + +static int get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct max32xxx_flash_bank *info = bank->driver_priv; + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + printed = snprintf(buf, buf_size, "\nMaxim Integrated max32xxx flash driver\n"); + buf += printed; + buf_size -= printed; + return ERROR_OK; +} + +/*************************************************************************** +* flash operations +***************************************************************************/ + +static int max32xxx_flash_op_pre(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct max32xxx_flash_bank *info = bank->driver_priv; + uint32_t flsh_cn; + uint32_t bootloader; + + /* Check if the flash controller is busy */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + if (flsh_cn & (FLSH_CN_PEND | FLSH_CN_ERASE_CODE_MASK | FLSH_CN_PGE | + FLSH_CN_ME | FLSH_CN_WR)) + return ERROR_FLASH_BUSY; + + /* Refresh flash controller timing */ + target_write_u32(target, info->flc_base + FLSH_CLKDIV, info->clkdiv_value); + + /* Clear and disable flash programming interrupts */ + target_read_u32(target, info->flc_base + FLSH_INT, &info->int_state); + target_write_u32(target, info->flc_base + FLSH_INT, 0x00000000); + + /* Clear the lower bit in the bootloader configuration register in case flash page 0 has been replaced */ + if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { + LOG_ERROR("Read failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (bootloader & FLASH_BL_CTRL_23) { + LOG_WARNING("FLSH_BL_CTRL indicates BL mode 2 or mode 3."); + if (bootloader & FLASH_BL_CTRL_IFREN) { + LOG_WARNING("Flash page 0 swapped out, attempting to swap back in for programming"); + bootloader &= ~(FLASH_BL_CTRL_IFREN); + if (target_write_u32(target, info->flc_base + FLSH_BL_CTRL, bootloader) != ERROR_OK) { + LOG_ERROR("Write failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { + LOG_ERROR("Read failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (bootloader & FLASH_BL_CTRL_IFREN) { + /* Bummer */ + LOG_ERROR("Unable to swap flash page 0 back in. Writes to page 0 will fail."); + } + } + } + + /* Unlock flash */ + flsh_cn &= ~FLSH_CN_UNLOCK_MASK; + flsh_cn |= FLSH_CN_UNLOCK_VALUE; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Confirm flash is unlocked */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + if ((flsh_cn & FLSH_CN_UNLOCK_VALUE) != FLSH_CN_UNLOCK_VALUE) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_flash_op_post(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct max32xxx_flash_bank *info = bank->driver_priv; + uint32_t flsh_cn; + + /* Restore flash programming interrupts */ + target_write_u32(target, info->flc_base + FLSH_INT, info->int_state); + + /* Lock flash */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= ~FLSH_CN_UNLOCK_MASK; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + return ERROR_OK; +} + +static int max32xxx_protect_check(struct flash_bank *bank) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int i; + uint32_t temp_reg; + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (!info->max326xx) { + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = -1; + + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + /* Check the protection */ + for (i = 0; i < bank->num_sectors; i++) { + if (i%32 == 0) + target_read_u32(target, info->flc_base + FLSH_PROT + ((i/32)*4), &temp_reg); + + if (temp_reg & (0x1 << i%32)) + bank->sectors[i].is_protected = 1; + else + bank->sectors[i].is_protected = 0; + } + return ERROR_OK; +} + +static int max32xxx_erase(struct flash_bank *bank, int first, int last) +{ + int banknr; + uint32_t flsh_cn, flsh_int; + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int retval; + int retry; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + if ((first == 0) && (last == (bank->num_sectors - 1))) + return max32xxx_mass_erase(bank); + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + int erased = 0; + for (banknr = first; banknr <= last; banknr++) { + + /* Check the protection */ + if (bank->sectors[banknr].is_protected == 1) { + LOG_WARNING("Flash sector %d is protected", banknr); + continue; + } else + erased = 1; + + /* Address is first word in page */ + target_write_u32(target, info->flc_base + FLSH_ADDR, banknr * info->sector_size); + + /* Write page erase code */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn |= FLSH_CN_ERASE_CODE_PGE; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Issue page erase command */ + flsh_cn |= 0x4; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Wait until erase complete */ + retry = 1000; + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash page erase @ 0x%08x", + banknr * info->sector_size); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Error erasing flash page %i", banknr); + target_write_u32(target, info->flc_base + FLSH_INT, 0); + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + bank->sectors[banknr].is_erased = 1; + } + + if (!erased) { + LOG_ERROR("All pages protected %d to %d", first, last); + max32xxx_flash_op_post(bank); + return ERROR_FAIL; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int page; + uint32_t temp_reg; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (!info->max326xx) + return ERROR_FLASH_OPER_UNSUPPORTED; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + /* Setup the protection on the pages given */ + for (page = first; page <= last; page++) { + if (set) { + /* Set the write/erase bit for this page */ + target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); + temp_reg |= (0x1 << page%32); + target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); + bank->sectors[page].is_protected = 1; + } else { + /* Clear the write/erase bit for this page */ + target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); + temp_reg &= ~(0x1 << page%32); + target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); + bank->sectors[page].is_protected = 0; + } + } + + return ERROR_OK; +} + +static int max32xxx_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t wcount) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *source; + struct working_area *write_algorithm; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + /* power of two, and multiple of word size */ + static const unsigned buf_min = 128; + + /* for small buffers it's faster not to download an algorithm */ + if (wcount * 4 < buf_min) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "", + bank, buffer, offset, wcount); + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(write_code), &write_algorithm) != ERROR_OK) { + LOG_DEBUG("no working area for block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* plus a buffer big enough for this data */ + if (wcount * 4 < buffer_size) + buffer_size = wcount * 4; + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + buffer_size /= 2; + + if (buffer_size <= buf_min) { + target_free_working_area(target, write_algorithm); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + LOG_DEBUG("retry target_alloc_working_area(%s, size=%u)", + target_name(target), (unsigned) buffer_size); + } + + target_write_buffer(target, write_algorithm->address, sizeof(write_code), + write_code); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[2].value, 0, 32, address); + buf_set_u32(reg_params[3].value, 0, 32, wcount); + buf_set_u32(reg_params[4].value, 0, 32, info->flc_base); + retval = target_run_flash_async_algorithm(target, buffer, wcount, 4, 0, NULL, + 5, reg_params, source->address, source->size, write_algorithm->address, 0, &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) + LOG_ERROR("error %d executing max32xxx flash write algorithm", retval); + + target_free_working_area(target, write_algorithm); + target_free_working_area(target, source); + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + return retval; +} + +static int max32xxx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t flsh_cn, flsh_int; + uint32_t address = offset; + uint32_t remaining = count; + uint32_t words_remaining; + int retval; + int retry; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_DEBUG("bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", + bank, buffer, offset, count); + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (offset & 0x3) { + LOG_WARNING("offset size must be word aligned"); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + if (remaining >= 4) { + /* write in 32-bit units */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* try using a block write */ + words_remaining = remaining / 4; + retval = max32xxx_write_block(bank, buffer, offset, words_remaining); + + if (retval != ERROR_OK) { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + LOG_DEBUG("writing flash word-at-a-time"); + else { + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + } else { + /* all 32-bit words have been written */ + buffer += words_remaining * 4; + address += words_remaining * 4; + remaining -= words_remaining * 4; + } + } + + if ((remaining >= 4) && ((address & 0x1F) != 0)) { + /* write in 32-bit units until we are 128-bit aligned */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + while ((remaining >= 4) && ((address & 0x1F) != 0)) { + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 4; + address += 4; + remaining -= 4; + } + } + + if ((info->burst_size_bits == 128) && (remaining >= 16)) { + /* write in 128-bit bursts while we can */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + + flsh_cn &= 0xFFFFFFEF; + flsh_cn |= 0x08000000; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + + while (remaining >= 16) { + if ((address & 0xFFF) == 0) + LOG_DEBUG("Writing @ 0x%08x", address); + + target_write_buffer(target, info->flc_base + FLSH_DATA0, 16, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 16; + address += 16; + remaining -= 16; + } + } + + if (remaining >= 4) { + + /* write in 32-bit units while we can */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + while (remaining >= 4) { + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 4; + address += 4; + remaining -= 4; + } + } + + if (remaining > 0) { + /* write remaining bytes in a 32-bit unit */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff}; + int i = 0; + + while (remaining > 0) { + last_word[i++] = *buffer; + buffer++; + remaining--; + } + + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, last_word); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Flash Error writing 0x%x bytes at 0x%08x", count, offset); + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_probe(struct flash_bank *bank) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t arm_id[2]; + uint16_t arm_pid; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + /* provide this for the benefit of the NOR flash framework */ + bank->size = info->flash_size; + bank->num_sectors = info->flash_size / info->sector_size; + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + + for (int i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * info->sector_size; + bank->sectors[i].size = info->sector_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + /* Probe to determine if this part is in the max326xx family */ + info->max326xx = 0; + target_read_u32(target, ARM_PID_REG, &arm_id[0]); + target_read_u32(target, ARM_PID_REG+4, &arm_id[1]); + arm_pid = (arm_id[1] << 8) + arm_id[0]; + LOG_DEBUG("arm_pid = 0x%x", arm_pid); + + if ((arm_pid == ARM_PID_DEFAULT_CM3) || arm_pid == ARM_PID_DEFAULT_CM4) { + uint32_t max326xx_id; + target_read_u32(target, MAX326XX_ID_REG, &max326xx_id); + LOG_DEBUG("max326xx_id = 0x%x", max326xx_id); + max326xx_id = ((max326xx_id & 0xFF000000) >> 24); + if (max326xx_id == MAX326XX_ID) + info->max326xx = 1; + } + LOG_DEBUG("info->max326xx = %d", info->max326xx); + + /* Initialize the protection bits for each flash page */ + if (max32xxx_protect_check(bank) == ERROR_FLASH_OPER_UNSUPPORTED) + LOG_WARNING("Flash protection not supported on this device"); + + info->probed = 1; + return ERROR_OK; +} + +static int max32xxx_mass_erase(struct flash_bank *bank) +{ + struct target *target = NULL; + struct max32xxx_flash_bank *info = NULL; + uint32_t flsh_cn, flsh_int; + int retval; + int retry; + info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + int not_protected = 0; + for (int i = 0; i < bank->num_sectors; i++) { + if (bank->sectors[i].is_protected == 1) + LOG_WARNING("Flash sector %d is protected", i); + else + not_protected = 1; + } + + if (!not_protected) { + LOG_ERROR("All pages protected"); + return ERROR_FAIL; + } + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + /* Write mass erase code */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn |= FLSH_CN_ERASE_CODE_ME; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Issue mass erase command */ + flsh_cn |= 0x2; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Wait until erase complete */ + retry = 1000; + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash mass erase"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Error mass erasing"); + target_write_u32(target, info->flc_base + FLSH_INT, 0); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_mass_erase_command) +{ + int i; + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "max32xxx mass_erase "); + return ERROR_OK; + } + + if (ERROR_OK != retval) + return retval; + + if (max32xxx_mass_erase(bank) == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD_CTX, "max32xxx mass erase complete"); + } else + command_print(CMD_CTX, "max32xxx mass erase failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_set_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + uint32_t addr, len; + + if (CMD_ARGC != 3) { + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Convert the range to the page numbers */ + if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { + LOG_WARNING("Error parsing address"); + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_FAIL; + } + /* Mask off the top portion on the address */ + addr = (addr & 0x0FFFFFFF); + + if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { + LOG_WARNING("Error parsing length"); + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_FAIL; + } + + /* Check the address is in the range of the flash */ + if ((addr+len) >= info->flash_size) + return ERROR_FLASH_SECTOR_INVALID; + + if (len == 0) + return ERROR_OK; + + /* Convert the address and length to the page boundaries */ + addr = addr - (addr % info->sector_size); + if (len % info->sector_size) + len = len + info->sector_size - (len % info->sector_size); + + /* Convert the address and length to page numbers */ + addr = (addr / info->sector_size); + len = addr + (len / info->sector_size) - 1; + + if (max32xxx_protect(bank, 1, addr, len) == ERROR_OK) + command_print(CMD_CTX, "max32xxx protection set complete"); + else + command_print(CMD_CTX, "max32xxx protection set failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_clr_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + uint32_t addr, len; + + if (CMD_ARGC != 3) { + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Convert the range to the page numbers */ + if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { + LOG_WARNING("Error parsing address"); + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_FAIL; + } + /* Mask off the top portion on the address */ + addr = (addr & 0x0FFFFFFF); + + if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { + LOG_WARNING("Error parsing length"); + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_FAIL; + } + + /* Check the address is in the range of the flash */ + if ((addr+len) >= info->flash_size) + return ERROR_FLASH_SECTOR_INVALID; + + if (len == 0) + return ERROR_OK; + + /* Convert the address and length to the page boundaries */ + addr = addr - (addr % info->sector_size); + if (len % info->sector_size) + len = len + info->sector_size - (len % info->sector_size); + + /* Convert the address and length to page numbers */ + addr = (addr / info->sector_size); + len = addr + (len / info->sector_size) - 1; + + if (max32xxx_protect(bank, 0, addr, len) == ERROR_OK) + command_print(CMD_CTX, "max32xxx protection clear complete"); + else + command_print(CMD_CTX, "max32xxx protection clear failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_check_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + int i; + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "max32xxx protection_check "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Update the protection array */ + retval = max32xxx_protect_check(bank); + if (ERROR_OK != retval) { + LOG_WARNING("Error updating the protection array"); + return retval; + } + + LOG_WARNING("s: a:
p:"); + for (i = 0; i < bank->num_sectors; i += 4) { + LOG_WARNING("s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d", + (i+0), (i+0)*info->sector_size, bank->sectors[(i+0)].is_protected, + (i+1), (i+1)*info->sector_size, bank->sectors[(i+1)].is_protected, + (i+2), (i+2)*info->sector_size, bank->sectors[(i+2)].is_protected, + (i+3), (i+3)*info->sector_size, bank->sectors[(i+3)].is_protected); + } + + return ERROR_OK; +} + +static const struct command_registration max32xxx_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = max32xxx_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "mass erase flash", + }, + { + .name = "protection_set", + .handler = max32xxx_handle_protection_set_command, + .mode = COMMAND_EXEC, + .usage = "bank_id addr size", + .help = "set flash protection for address range", + }, + { + .name = "protection_clr", + .handler = max32xxx_handle_protection_clr_command, + .mode = COMMAND_EXEC, + .usage = "bank_id addr size", + .help = "clear flash protection for address range", + }, + { + .name = "protection_check", + .handler = max32xxx_handle_protection_check_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "check flash protection", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration max32xxx_command_handlers[] = { + { + .name = "max32xxx", + .mode = COMMAND_EXEC, + .help = "max32xxx flash command group", + .chain = max32xxx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver max32xxx_flash = { + .name = "max32xxx", + .commands = max32xxx_command_handlers, + .flash_bank_command = max32xxx_flash_bank_command, + .erase = max32xxx_erase, + .protect = max32xxx_protect, + .write = max32xxx_write, + .read = default_flash_read, + .probe = max32xxx_probe, + .auto_probe = max32xxx_probe, + .erase_check = default_flash_blank_check, + .protect_check = max32xxx_protect_check, + .info = get_info, +}; diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c index 16459c7f8..de5c23051 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -26,6 +26,7 @@ #include #include #include +#include enum { NRF5_FLASH_BASE = 0x00000000, @@ -203,9 +204,16 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { NRF5_DEVICE_DEF(0x007A, "51422", "CEAA", "C0", 256), NRF5_DEVICE_DEF(0x0088, "51422", "CFAC", "A0", 256), + /* nRF52810 Devices */ + NRF5_DEVICE_DEF(0x0142, "52810", "QFAA", "B0", 192), + NRF5_DEVICE_DEF(0x0143, "52810", "QCAA", "C0", 192), + /* nRF52832 Devices */ NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512), NRF5_DEVICE_DEF(0x0139, "52832", "QFAA", "E0", 512), + + /* nRF52840 Devices */ + NRF5_DEVICE_DEF(0x0150, "52840", "QIAA", "C0", 1024), }; static int nrf5_bank_is_probed(struct flash_bank *bank) @@ -240,7 +248,8 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip) { uint32_t ready; int res; - int timeout = 100; + int timeout_ms = 200; + int64_t ts_start = timeval_ms(); do { res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready); @@ -252,8 +261,9 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip) if (ready == 0x00000001) return ERROR_OK; - alive_sleep(1); - } while (timeout--); + keep_alive(); + + } while ((timeval_ms()-ts_start) < timeout_ms); LOG_DEBUG("Timed out waiting for NVMC_READY"); return ERROR_FLASH_BUSY; diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c index faada9a61..fbd759828 100644 --- a/src/flash/nor/stm32f1x.c +++ b/src/flash/nor/stm32f1x.c @@ -70,14 +70,15 @@ /* FLASH_CR register bits */ -#define FLASH_PG (1 << 0) -#define FLASH_PER (1 << 1) -#define FLASH_MER (1 << 2) -#define FLASH_OPTPG (1 << 4) -#define FLASH_OPTER (1 << 5) -#define FLASH_STRT (1 << 6) -#define FLASH_LOCK (1 << 7) -#define FLASH_OPTWRE (1 << 9) +#define FLASH_PG (1 << 0) +#define FLASH_PER (1 << 1) +#define FLASH_MER (1 << 2) +#define FLASH_OPTPG (1 << 4) +#define FLASH_OPTER (1 << 5) +#define FLASH_STRT (1 << 6) +#define FLASH_LOCK (1 << 7) +#define FLASH_OPTWRE (1 << 9) +#define FLASH_OBL_LAUNCH (1 << 13) /* except stm32f1x series */ /* FLASH_SR register bits */ @@ -106,10 +107,10 @@ #define FLASH_ERASE_TIMEOUT 100 struct stm32x_options { - uint16_t RDP; - uint16_t user_options; - uint16_t user_data; - uint16_t protection[4]; + uint8_t rdp; + uint8_t user; + uint16_t data; + uint32_t protection; }; struct stm32x_flash_bank { @@ -119,8 +120,9 @@ struct stm32x_flash_bank { bool has_dual_banks; /* used to access dual flash bank stm32xl */ + bool can_load_options; uint32_t register_base; - uint16_t default_rdp; + uint8_t default_rdp; int user_data_offset; int option_offset; uint32_t user_bank_size; @@ -145,6 +147,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) bank->driver_priv = stm32x_info; stm32x_info->probed = 0; stm32x_info->has_dual_banks = false; + stm32x_info->can_load_options = false; stm32x_info->register_base = FLASH_REG_BASE_B0; stm32x_info->user_bank_size = bank->size; @@ -221,44 +224,47 @@ static int stm32x_check_operation_supported(struct flash_bank *bank) static int stm32x_read_options(struct flash_bank *bank) { - uint32_t optiondata; - struct stm32x_flash_bank *stm32x_info = NULL; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; struct target *target = bank->target; + uint32_t option_bytes; + int retval; - stm32x_info = bank->driver_priv; - - /* read current option bytes */ - int retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optiondata); + /* read user and read protection option bytes */ + retval = target_read_u32(target, STM32_OB_RDP, &option_bytes); if (retval != ERROR_OK) return retval; - stm32x_info->option_bytes.user_options = (optiondata >> stm32x_info->option_offset >> 2) & 0xffff; - stm32x_info->option_bytes.user_data = (optiondata >> stm32x_info->user_data_offset) & 0xffff; - stm32x_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5; + stm32x_info->option_bytes.rdp = option_bytes & 0xFF; + stm32x_info->option_bytes.user = (option_bytes >> 16) & 0xFF; - if (optiondata & (1 << OPT_READOUT)) - LOG_INFO("Device Security Bit Set"); - - /* each bit refers to a 4bank protection */ - retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &optiondata); + /* read user data option bytes */ + retval = target_read_u32(target, STM32_OB_DATA0, &option_bytes); if (retval != ERROR_OK) return retval; - stm32x_info->option_bytes.protection[0] = (uint16_t)optiondata; - stm32x_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); - stm32x_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); - stm32x_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); + stm32x_info->option_bytes.data = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF); + + /* read write protection option bytes */ + retval = target_read_u32(target, STM32_OB_WRP0, &option_bytes); + if (retval != ERROR_OK) + return retval; + + stm32x_info->option_bytes.protection = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF); + + retval = target_read_u32(target, STM32_OB_WRP2, &option_bytes); + if (retval != ERROR_OK) + return retval; + + stm32x_info->option_bytes.protection |= (((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF)) << 16; return ERROR_OK; } static int stm32x_erase_options(struct flash_bank *bank) { - struct stm32x_flash_bank *stm32x_info = NULL; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; struct target *target = bank->target; - stm32x_info = bank->driver_priv; - /* read current options */ stm32x_read_options(bank); @@ -291,9 +297,9 @@ static int stm32x_erase_options(struct flash_bank *bank) if (retval != ERROR_OK) return retval; - /* clear readout protection and complementary option bytes + /* clear read protection option byte * this will also force a device unlock if set */ - stm32x_info->option_bytes.RDP = stm32x_info->default_rdp; + stm32x_info->option_bytes.rdp = stm32x_info->default_rdp; return ERROR_OK; } @@ -328,14 +334,14 @@ static int stm32x_write_options(struct flash_bank *bank) uint8_t opt_bytes[16]; - target_buffer_set_u16(target, opt_bytes, stm32x_info->option_bytes.RDP); - target_buffer_set_u16(target, opt_bytes + 2, stm32x_info->option_bytes.user_options); - target_buffer_set_u16(target, opt_bytes + 4, stm32x_info->option_bytes.user_data & 0xff); - target_buffer_set_u16(target, opt_bytes + 6, (stm32x_info->option_bytes.user_data >> 8) & 0xff); - target_buffer_set_u16(target, opt_bytes + 8, stm32x_info->option_bytes.protection[0]); - target_buffer_set_u16(target, opt_bytes + 10, stm32x_info->option_bytes.protection[1]); - target_buffer_set_u16(target, opt_bytes + 12, stm32x_info->option_bytes.protection[2]); - target_buffer_set_u16(target, opt_bytes + 14, stm32x_info->option_bytes.protection[3]); + target_buffer_set_u16(target, opt_bytes, stm32x_info->option_bytes.rdp); + target_buffer_set_u16(target, opt_bytes + 2, stm32x_info->option_bytes.user); + target_buffer_set_u16(target, opt_bytes + 4, stm32x_info->option_bytes.data & 0xff); + target_buffer_set_u16(target, opt_bytes + 6, (stm32x_info->option_bytes.data >> 8) & 0xff); + target_buffer_set_u16(target, opt_bytes + 8, stm32x_info->option_bytes.protection & 0xff); + target_buffer_set_u16(target, opt_bytes + 10, (stm32x_info->option_bytes.protection >> 8) & 0xff); + target_buffer_set_u16(target, opt_bytes + 12, (stm32x_info->option_bytes.protection >> 16) & 0xff); + target_buffer_set_u16(target, opt_bytes + 14, (stm32x_info->option_bytes.protection >> 24) & 0xff); uint32_t offset = STM32_OB_RDP - bank->base; retval = stm32x_write_block(bank, opt_bytes, offset, sizeof(opt_bytes) / 2); @@ -355,64 +361,21 @@ static int stm32x_write_options(struct flash_bank *bank) static int stm32x_protect_check(struct flash_bank *bank) { struct target *target = bank->target; - struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - uint32_t protection; - int i, s; - int num_bits; - int set; int retval = stm32x_check_operation_supported(bank); if (ERROR_OK != retval) return retval; - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ + /* medium density - each bit refers to a 4 sector protection block + * high density - each bit refers to a 2 sector protection block + * bit 31 refers to all remaining sectors in a bank */ retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection); if (retval != ERROR_OK) return retval; - /* medium density - each protection bit is for 4 * 1K pages - * high density - each protection bit is for 2 * 2K pages */ - num_bits = (bank->num_sectors / stm32x_info->ppage_size); - - if (stm32x_info->ppage_size == 2) { - /* high density flash/connectivity line protection */ - - set = 1; - - if (protection & (1 << 31)) - set = 0; - - /* bit 31 controls sector 62 - 255 protection for high density - * bit 31 controls sector 62 - 127 protection for connectivity line */ - for (s = 62; s < bank->num_sectors; s++) - bank->sectors[s].is_protected = set; - - if (bank->num_sectors > 61) - num_bits = 31; - - for (i = 0; i < num_bits; i++) { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (s = 0; s < stm32x_info->ppage_size; s++) - bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; - } - } else { - /* low/medium density flash protection */ - for (i = 0; i < num_bits; i++) { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (s = 0; s < stm32x_info->ppage_size; s++) - bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; - } - } + for (int i = 0; i < bank->num_prot_blocks; i++) + bank->prot_blocks[i].is_protected = (protection & (1 << i)) ? 0 : 1; return ERROR_OK; } @@ -467,14 +430,8 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last) static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) { - struct stm32x_flash_bank *stm32x_info = NULL; struct target *target = bank->target; - uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; - int i, reg, bit; - int status; - uint32_t protection; - - stm32x_info = bank->driver_priv; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -482,80 +439,20 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) } int retval = stm32x_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - if ((first % stm32x_info->ppage_size) != 0) { - LOG_WARNING("aligned start protect sector to a %d sector boundary", - stm32x_info->ppage_size); - first = first - (first % stm32x_info->ppage_size); - } - if (((last + 1) % stm32x_info->ppage_size) != 0) { - LOG_WARNING("aligned end protect sector to a %d sector boundary", - stm32x_info->ppage_size); - last++; - last = last - (last % stm32x_info->ppage_size); - last--; - } - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection); if (retval != ERROR_OK) return retval; - prot_reg[0] = (uint16_t)protection; - prot_reg[1] = (uint16_t)(protection >> 8); - prot_reg[2] = (uint16_t)(protection >> 16); - prot_reg[3] = (uint16_t)(protection >> 24); + retval = stm32x_erase_options(bank); + if (retval != ERROR_OK) + return retval; - if (stm32x_info->ppage_size == 2) { - /* high density flash */ - - /* bit 7 controls sector 62 - 255 protection */ - if (last > 61) { - if (set) - prot_reg[3] &= ~(1 << 7); - else - prot_reg[3] |= (1 << 7); - } - - if (first > 61) - first = 62; - if (last > 61) - last = 61; - - for (i = first; i <= last; i++) { - reg = (i / stm32x_info->ppage_size) / 8; - bit = (i / stm32x_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } else { - /* medium density flash */ - for (i = first; i <= last; i++) { - reg = (i / stm32x_info->ppage_size) / 8; - bit = (i / stm32x_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } + for (int i = first; i <= last; i++) { + if (set) + stm32x_info->option_bytes.protection &= ~(1 << i); + else + stm32x_info->option_bytes.protection |= (1 << i); } - status = stm32x_erase_options(bank); - if (status != ERROR_OK) - return status; - - stm32x_info->option_bytes.protection[0] = prot_reg[0]; - stm32x_info->option_bytes.protection[1] = prot_reg[1]; - stm32x_info->option_bytes.protection[2] = prot_reg[2]; - stm32x_info->option_bytes.protection[3] = prot_reg[3]; - return stm32x_write_options(bank); } @@ -808,7 +705,6 @@ static int stm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_i static int stm32x_probe(struct flash_bank *bank) { struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - int i; uint16_t flash_size_in_kb; uint16_t max_flash_size_in_kb; uint32_t device_id; @@ -820,8 +716,8 @@ static int stm32x_probe(struct flash_bank *bank) stm32x_info->user_data_offset = 10; stm32x_info->option_offset = 0; - /* default factory protection level */ - stm32x_info->default_rdp = 0x5AA5; + /* default factory read protection level 0 */ + stm32x_info->default_rdp = 0xA5; /* read stm32 device id register */ int retval = stm32x_get_device_id(bank, &device_id); @@ -863,7 +759,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 256; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x446: /* stm32f303xD/E */ page_size = 2048; @@ -871,7 +768,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 512; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x428: /* value line High density */ page_size = 2048; @@ -890,7 +788,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 256; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x438: /* stm32f33x */ case 0x439: /* stm32f302x6/8 */ @@ -899,7 +798,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 64; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x440: /* stm32f05x */ case 0x444: /* stm32f03x */ @@ -909,7 +809,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 64; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x448: /* stm32f07x */ case 0x442: /* stm32f09x */ @@ -918,7 +819,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 256; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; default: LOG_WARNING("Cannot identify target as a STM32 family."); @@ -972,17 +874,31 @@ static int stm32x_probe(struct flash_bank *bank) bank->sectors = NULL; } + if (bank->prot_blocks) { + free(bank->prot_blocks); + bank->prot_blocks = NULL; + } + bank->base = base_address; bank->size = (num_pages * page_size); - bank->num_sectors = num_pages; - bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - for (i = 0; i < num_pages; i++) { - bank->sectors[i].offset = i * page_size; - bank->sectors[i].size = page_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } + bank->num_sectors = num_pages; + bank->sectors = alloc_block_array(0, page_size, num_pages); + if (!bank->sectors) + return ERROR_FAIL; + + /* calculate number of write protection blocks */ + int num_prot_blocks = num_pages / stm32x_info->ppage_size; + if (num_prot_blocks > 32) + num_prot_blocks = 32; + + bank->num_prot_blocks = num_prot_blocks; + bank->prot_blocks = alloc_block_array(0, stm32x_info->ppage_size * page_size, num_prot_blocks); + if (!bank->prot_blocks) + return ERROR_FAIL; + + if (num_prot_blocks == 32) + bank->prot_blocks[31].size = (num_pages - (31 * stm32x_info->ppage_size)) * page_size; stm32x_info->probed = 1; @@ -1275,7 +1191,7 @@ COMMAND_HANDLER(stm32x_handle_lock_command) } /* set readout protection */ - stm32x_info->option_bytes.RDP = 0; + stm32x_info->option_bytes.rdp = 0; if (stm32x_write_options(bank) != ERROR_OK) { command_print(CMD_CTX, "stm32x failed to lock device"); @@ -1329,7 +1245,7 @@ COMMAND_HANDLER(stm32x_handle_unlock_command) COMMAND_HANDLER(stm32x_handle_options_read_command) { - uint32_t optionbyte; + uint32_t optionbyte, protection; struct target *target = NULL; struct stm32x_flash_bank *stm32x_info = NULL; @@ -1357,47 +1273,38 @@ COMMAND_HANDLER(stm32x_handle_options_read_command) retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optionbyte); if (retval != ERROR_OK) return retval; - command_print(CMD_CTX, "Option Byte: 0x%" PRIx32 "", optionbyte); - int user_data = optionbyte; + uint16_t user_data = optionbyte >> stm32x_info->user_data_offset; - if (optionbyte >> OPT_ERROR & 1) - command_print(CMD_CTX, "Option Byte Complement Error"); + retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection); + if (retval != ERROR_OK) + return retval; - if (optionbyte >> OPT_READOUT & 1) - command_print(CMD_CTX, "Readout Protection On"); - else - command_print(CMD_CTX, "Readout Protection Off"); + if (optionbyte & (1 << OPT_ERROR)) + command_print(CMD_CTX, "option byte complement error"); + + command_print(CMD_CTX, "option byte register = 0x%" PRIx32 "", optionbyte); + command_print(CMD_CTX, "write protection register = 0x%" PRIx32 "", protection); + + command_print(CMD_CTX, "read protection: %s", + (optionbyte & (1 << OPT_READOUT)) ? "on" : "off"); /* user option bytes are offset depending on variant */ optionbyte >>= stm32x_info->option_offset; - if (optionbyte >> OPT_RDWDGSW & 1) - command_print(CMD_CTX, "Software Watchdog"); - else - command_print(CMD_CTX, "Hardware Watchdog"); + command_print(CMD_CTX, "watchdog: %sware", + (optionbyte & (1 << OPT_RDWDGSW)) ? "soft" : "hard"); - if (optionbyte >> OPT_RDRSTSTOP & 1) - command_print(CMD_CTX, "Stop: No reset generated"); - else - command_print(CMD_CTX, "Stop: Reset generated"); + command_print(CMD_CTX, "stop mode: %sreset generated upon entry", + (optionbyte & (1 << OPT_RDRSTSTOP)) ? "no " : ""); - if (optionbyte >> OPT_RDRSTSTDBY & 1) - command_print(CMD_CTX, "Standby: No reset generated"); - else - command_print(CMD_CTX, "Standby: Reset generated"); + command_print(CMD_CTX, "standby mode: %sreset generated upon entry", + (optionbyte & (1 << OPT_RDRSTSTDBY)) ? "no " : ""); - if (stm32x_info->has_dual_banks) { - if (optionbyte >> OPT_BFB2 & 1) - command_print(CMD_CTX, "Boot: Bank 0"); - else - command_print(CMD_CTX, "Boot: Bank 1"); - } + if (stm32x_info->has_dual_banks) + command_print(CMD_CTX, "boot: bank %d", (optionbyte & (1 << OPT_BFB2)) ? 0 : 1); - command_print(CMD_CTX, "User Option0: 0x%02" PRIx8, - (uint8_t)((user_data >> stm32x_info->user_data_offset) & 0xff)); - command_print(CMD_CTX, "User Option1: 0x%02" PRIx8, - (uint8_t)((user_data >> (stm32x_info->user_data_offset + 8)) & 0xff)); + command_print(CMD_CTX, "user data = 0x%02" PRIx16 "", user_data); return ERROR_OK; } @@ -1434,7 +1341,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command) return retval; /* start with current options */ - optionbyte = stm32x_info->option_bytes.user_options; + optionbyte = stm32x_info->option_bytes.user; /* skip over flash bank */ CMD_ARGC--; @@ -1471,7 +1378,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command) return ERROR_OK; } - stm32x_info->option_bytes.user_options = optionbyte; + stm32x_info->option_bytes.user = optionbyte; if (stm32x_write_options(bank) != ERROR_OK) { command_print(CMD_CTX, "stm32x failed to write options"); @@ -1479,8 +1386,55 @@ COMMAND_HANDLER(stm32x_handle_options_write_command) } command_print(CMD_CTX, "stm32x write options complete.\n" - "INFO: a reset or power cycle is required " - "for the new settings to take effect."); + "INFO: %spower cycle is required " + "for the new settings to take effect.", + stm32x_info->can_load_options + ? "'stm32f1x options_load' command or " : ""); + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_options_load_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + + if (!stm32x_info->can_load_options) { + LOG_ERROR("Command not applicable to stm32f1x devices - power cycle is " + "required instead."); + return ERROR_FAIL; + } + + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = stm32x_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + /* unlock option flash registers */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + return retval; + + /* force re-load of option bytes - generates software reset */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBL_LAUNCH); + if (retval != ERROR_OK) + return retval; return ERROR_OK; } @@ -1574,7 +1528,7 @@ static const struct command_registration stm32x_exec_command_handlers[] = { .handler = stm32x_handle_options_read_command, .mode = COMMAND_EXEC, .usage = "bank_id", - .help = "Read and display device option byte.", + .help = "Read and display device option bytes.", }, { .name = "options_write", @@ -1583,7 +1537,14 @@ static const struct command_registration stm32x_exec_command_handlers[] = { .usage = "bank_id ('SWWDG'|'HWWDG') " "('RSTSTNDBY'|'NORSTSTNDBY') " "('RSTSTOP'|'NORSTSTOP')", - .help = "Replace bits in device option byte.", + .help = "Replace bits in device option bytes.", + }, + { + .name = "options_load", + .handler = stm32x_handle_options_load_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Force re-load of device option bytes.", }, COMMAND_REGISTRATION_DONE }; diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index 413d04d77..b46fb0786 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -1190,6 +1190,9 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) case 0x1000: rev_str = "A"; break; + case 0x1001: + rev_str = "Z"; + break; } break; diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 4fb7e039a..ad179216d 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -57,8 +57,8 @@ #define STM32_FLASH_CR 0x40022014 #define STM32_FLASH_OPTR 0x40022020 #define STM32_FLASH_WRP1AR 0x4002202c -#define STM32_FLASH_WRP2AR 0x40022030 -#define STM32_FLASH_WRP1BR 0x4002204c +#define STM32_FLASH_WRP1BR 0x40022030 +#define STM32_FLASH_WRP2AR 0x4002204c #define STM32_FLASH_WRP2BR 0x40022050 /* FLASH_CR register bits */ @@ -70,8 +70,10 @@ #define FLASH_CR_BKER (1 << 11) #define FLASH_MER2 (1 << 15) #define FLASH_STRT (1 << 16) +#define FLASH_OPTSTRT (1 << 17) #define FLASH_EOPIE (1 << 24) #define FLASH_ERRIE (1 << 25) +#define FLASH_OBLLAUNCH (1 << 27) #define FLASH_OPTLOCK (1 << 30) #define FLASH_LOCK (1 << 31) @@ -102,28 +104,17 @@ #define OPTKEY1 0x08192A3B #define OPTKEY2 0x4C5D6E7F +#define RDP_LEVEL_0 0xAA +#define RDP_LEVEL_1 0xBB +#define RDP_LEVEL_2 0xCC + /* other registers */ #define DBGMCU_IDCODE 0xE0042000 #define FLASH_SIZE_REG 0x1FFF75E0 -struct stm32l4_options { - uint8_t RDP; - uint16_t bank_b_start; - uint8_t user_options; - uint8_t wpr1a_start; - uint8_t wpr1a_end; - uint8_t wpr1b_start; - uint8_t wpr1b_end; - uint8_t wpr2a_start; - uint8_t wpr2a_end; - uint8_t wpr2b_start; - uint8_t wpr2b_end; - /* Fixme: Handle PCROP */ -}; - struct stm32l4_flash_bank { - struct stm32l4_options option_bytes; + uint16_t bank2_start; int probed; }; @@ -265,97 +256,80 @@ static int stm32l4_unlock_option_reg(struct target *target) return ERROR_OK; } -static int stm32l4_read_options(struct flash_bank *bank) +static int stm32l4_read_option(struct flash_bank *bank, uint32_t address, uint32_t* value) { - uint32_t optiondata; - struct stm32l4_flash_bank *stm32l4_info = NULL; struct target *target = bank->target; - - stm32l4_info = bank->driver_priv; - - /* read current option bytes */ - int retval = target_read_u32(target, STM32_FLASH_OPTR, &optiondata); - if (retval != ERROR_OK) - return retval; - - stm32l4_info->option_bytes.user_options = (optiondata >> 8) & 0x3ffff; - stm32l4_info->option_bytes.RDP = optiondata & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP1AR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr1a_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr1a_end = (optiondata >> 16) & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP2AR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr2a_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr2a_end = (optiondata >> 16) & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP1BR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr1b_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr1b_end = (optiondata >> 16) & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP2BR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr2b_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr2b_end = (optiondata >> 16) & 0xff; - - if (stm32l4_info->option_bytes.RDP != 0xAA) - LOG_INFO("Device Security Bit Set"); - - return ERROR_OK; + return target_read_u32(target, address, value); } -static int stm32l4_write_options(struct flash_bank *bank) +static int stm32l4_write_option(struct flash_bank *bank, uint32_t address, uint32_t value, uint32_t mask) { - struct stm32l4_flash_bank *stm32l4_info = NULL; struct target *target = bank->target; uint32_t optiondata; - stm32l4_info = bank->driver_priv; - - (void) optiondata; - (void) stm32l4_info; - - int retval = stm32l4_unlock_option_reg(target); + int retval = target_read_u32(target, address, &optiondata); if (retval != ERROR_OK) return retval; - /* FIXME: Implement Option writing!*/ - return ERROR_OK; + + retval = stm32l4_unlock_reg(target); + if (retval != ERROR_OK) + return retval; + + retval = stm32l4_unlock_option_reg(target); + if (retval != ERROR_OK) + return retval; + + optiondata = (optiondata & ~mask) | (value & mask); + + retval = target_write_u32(target, address, optiondata); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OPTSTRT); + if (retval != ERROR_OK) + return retval; + + retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + return retval; } static int stm32l4_protect_check(struct flash_bank *bank) { struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br; + stm32l4_read_option(bank, STM32_FLASH_WRP1AR, &wrp1ar); + stm32l4_read_option(bank, STM32_FLASH_WRP1BR, &wrp1br); + stm32l4_read_option(bank, STM32_FLASH_WRP2AR, &wrp2ar); + stm32l4_read_option(bank, STM32_FLASH_WRP2BR, &wrp2br); - /* read write protection settings */ - int retval = stm32l4_read_options(bank); - if (retval != ERROR_OK) { - LOG_DEBUG("unable to read option bytes"); - return retval; - } + const uint8_t wrp1a_start = wrp1ar & 0xFF; + const uint8_t wrp1a_end = (wrp1ar >> 16) & 0xFF; + const uint8_t wrp1b_start = wrp1br & 0xFF; + const uint8_t wrp1b_end = (wrp1br >> 16) & 0xFF; + const uint8_t wrp2a_start = wrp2ar & 0xFF; + const uint8_t wrp2a_end = (wrp2ar >> 16) & 0xFF; + const uint8_t wrp2b_start = wrp2br & 0xFF; + const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF; for (int i = 0; i < bank->num_sectors; i++) { - if (i < stm32l4_info->option_bytes.bank_b_start) { - if (((i >= stm32l4_info->option_bytes.wpr1a_start) && - (i <= stm32l4_info->option_bytes.wpr1a_end)) || - ((i >= stm32l4_info->option_bytes.wpr2a_start) && - (i <= stm32l4_info->option_bytes.wpr2a_end))) + if (i < stm32l4_info->bank2_start) { + if (((i >= wrp1a_start) && + (i <= wrp1a_end)) || + ((i >= wrp1b_start) && + (i <= wrp1b_end))) bank->sectors[i].is_protected = 1; else bank->sectors[i].is_protected = 0; } else { uint8_t snb; - snb = i - stm32l4_info->option_bytes.bank_b_start + 256; - if (((snb >= stm32l4_info->option_bytes.wpr1b_start) && - (snb <= stm32l4_info->option_bytes.wpr1b_end)) || - ((snb >= stm32l4_info->option_bytes.wpr2b_start) && - (snb <= stm32l4_info->option_bytes.wpr2b_end))) + snb = i - stm32l4_info->bank2_start + 256; + if (((snb >= wrp2a_start) && + (snb <= wrp2a_end)) || + ((snb >= wrp2b_start) && + (snb <= wrp2b_end))) bank->sectors[i].is_protected = 1; else bank->sectors[i].is_protected = 0; @@ -398,9 +372,9 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last) uint32_t erase_flags; erase_flags = FLASH_PER | FLASH_STRT; - if (i >= stm32l4_info->option_bytes.bank_b_start) { + if (i >= stm32l4_info->bank2_start) { uint8_t snb; - snb = (i - stm32l4_info->option_bytes.bank_b_start) + 256; + snb = (i - stm32l4_info->bank2_start) + 256; erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER; } else erase_flags |= i << FLASH_PAGE_SHIFT; @@ -434,20 +408,29 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last return ERROR_TARGET_NOT_HALTED; } - /* read protection settings */ - int retval = stm32l4_read_options(bank); - if (retval != ERROR_OK) { - LOG_DEBUG("unable to read option bytes"); - return retval; + int ret = ERROR_OK; + /* Bank 2 */ + uint32_t reg_value = 0xFF; /* Default to bank un-protected */ + if (last >= stm32l4_info->bank2_start) { + if (set == 1) { + uint8_t begin = first > stm32l4_info->bank2_start ? first : 0x00; + reg_value = ((last & 0xFF) << 16) | begin; + } + + ret = stm32l4_write_option(bank, STM32_FLASH_WRP2AR, reg_value, 0xffffffff); + } + /* Bank 1 */ + reg_value = 0xFF; /* Default to bank un-protected */ + if (first < stm32l4_info->bank2_start) { + if (set == 1) { + uint8_t end = last >= stm32l4_info->bank2_start ? 0xFF : last; + reg_value = (end << 16) | (first & 0xFF); + } + + ret = stm32l4_write_option(bank, STM32_FLASH_WRP1AR, reg_value, 0xffffffff); } - (void)stm32l4_info; - /* FIXME: Write First and last in a valid WRPxx_start/end combo*/ - retval = stm32l4_write_options(bank); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; + return ret; } /* Count is in halfwords */ @@ -650,9 +633,9 @@ static int stm32l4_probe(struct flash_bank *bank) /* only devices with < 1024 kiB may be set to single bank dual banks */ if ((flash_size_in_kb == 1024) || !(options & OPT_DUALBANK)) - stm32l4_info->option_bytes.bank_b_start = 256; + stm32l4_info->bank2_start = 256; else - stm32l4_info->option_bytes.bank_b_start = flash_size_in_kb << 9; + stm32l4_info->bank2_start = flash_size_in_kb << 9; /* did we assign flash size? */ assert((flash_size_in_kb != 0xffff) && flash_size_in_kb); @@ -747,89 +730,6 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size) return ERROR_OK; } -COMMAND_HANDLER(stm32l4_handle_lock_command) -{ - struct target *target = NULL; - struct stm32l4_flash_bank *stm32l4_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32l4_info = bank->driver_priv; - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stm32l4_read_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to read options", - bank->driver->name); - return ERROR_OK; - } - - /* set readout protection */ - stm32l4_info->option_bytes.RDP = 0; - - if (stm32l4_write_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to lock device", bank->driver->name); - return ERROR_OK; - } - - command_print(CMD_CTX, "%s locked", bank->driver->name); - - return ERROR_OK; -} - -COMMAND_HANDLER(stm32l4_handle_unlock_command) -{ - struct target *target = NULL; - struct stm32l4_flash_bank *stm32l4_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32l4_info = bank->driver_priv; - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stm32l4_read_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to read options", bank->driver->name); - return ERROR_OK; - } - - /* clear readout protection and complementary option bytes - * this will also force a device unlock if set */ - stm32l4_info->option_bytes.RDP = 0xAA; - - if (stm32l4_write_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to unlock device", - bank->driver->name); - return ERROR_OK; - } - - command_print(CMD_CTX, "%s unlocked.\n" - "INFO: a reset or power cycle is required " - "for the new settings to take effect.", bank->driver->name); - - return ERROR_OK; -} - static int stm32l4_mass_erase(struct flash_bank *bank, uint32_t action) { int retval; @@ -873,7 +773,7 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command) uint32_t action; if (CMD_ARGC < 1) { - command_print(CMD_CTX, "stm32x mass_erase "); + command_print(CMD_CTX, "stm32l4x mass_erase "); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -889,14 +789,151 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command) for (i = 0; i < bank->num_sectors; i++) bank->sectors[i].is_erased = 1; - command_print(CMD_CTX, "stm32x mass erase complete"); + command_print(CMD_CTX, "stm32l4x mass erase complete"); } else { - command_print(CMD_CTX, "stm32x mass erase failed"); + command_print(CMD_CTX, "stm32l4x mass erase failed"); } return retval; } +COMMAND_HANDLER(stm32l4_handle_option_read_command) +{ + if (CMD_ARGC < 2) { + command_print(CMD_CTX, "stm32l4x option_read "); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + uint32_t reg_addr = STM32_FLASH_BASE; + uint32_t value = 0; + + reg_addr += strtoul(CMD_ARGV[1], NULL, 16); + + retval = stm32l4_read_option(bank, reg_addr, &value); + if (ERROR_OK != retval) + return retval; + + command_print(CMD_CTX, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value); + + return retval; +} + +COMMAND_HANDLER(stm32l4_handle_option_write_command) +{ + if (CMD_ARGC < 3) { + command_print(CMD_CTX, "stm32l4x option_write [mask]"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + uint32_t reg_addr = STM32_FLASH_BASE; + uint32_t value = 0; + uint32_t mask = 0xFFFFFFFF; + + reg_addr += strtoul(CMD_ARGV[1], NULL, 16); + value = strtoul(CMD_ARGV[2], NULL, 16); + if (CMD_ARGC > 3) + mask = strtoul(CMD_ARGV[3], NULL, 16); + + command_print(CMD_CTX, "%s Option written.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect.", bank->driver->name); + + retval = stm32l4_write_option(bank, reg_addr, value, mask); + return retval; +} + +COMMAND_HANDLER(stm32l4_handle_option_load_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct target *target = bank->target; + + retval = stm32l4_unlock_reg(target); + if (ERROR_OK != retval) + return retval; + + retval = stm32l4_unlock_option_reg(target); + if (ERROR_OK != retval) + return retval; + + /* Write the OBLLAUNCH bit in CR -> Cause device "POR" and option bytes reload */ + retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBLLAUNCH); + + command_print(CMD_CTX, "stm32l4x option load (POR) completed."); + return retval; +} + +COMMAND_HANDLER(stm32l4_handle_lock_command) +{ + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* set readout protection level 1 by erasing the RDP option byte */ + if (stm32l4_write_option(bank, STM32_FLASH_OPTR, 0, 0x000000FF) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to lock device", bank->driver->name); + return ERROR_OK; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32l4_handle_unlock_command) +{ + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name); + return ERROR_OK; + } + + return ERROR_OK; +} + static const struct command_registration stm32l4_exec_command_handlers[] = { { .name = "lock", @@ -919,6 +956,27 @@ static const struct command_registration stm32l4_exec_command_handlers[] = { .usage = "bank_id", .help = "Erase entire flash device.", }, + { + .name = "option_read", + .handler = stm32l4_handle_option_read_command, + .mode = COMMAND_EXEC, + .usage = "bank_id reg_offset", + .help = "Read & Display device option bytes.", + }, + { + .name = "option_write", + .handler = stm32l4_handle_option_write_command, + .mode = COMMAND_EXEC, + .usage = "bank_id reg_offset value mask", + .help = "Write device option bit fields with provided value.", + }, + { + .name = "option_load", + .handler = stm32l4_handle_option_load_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Force re-load of device options (will cause device reset).", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/jtag/Makefile.am b/src/jtag/Makefile.am index 50ee263d0..a76486399 100644 --- a/src/jtag/Makefile.am +++ b/src/jtag/Makefile.am @@ -1,6 +1,6 @@ noinst_LTLIBRARIES += %D%/libjtag.la -JTAG_SRCS = +JTAG_SRCS = %D%/commands.c %C%_libjtag_la_LIBADD = BUILT_SOURCES += %D%/minidriver_imp.h @@ -13,7 +13,7 @@ JTAG_SRCS += %D%/zy1000/zy1000.c JTAG_MINIDRIVER_DIR = %D%/zy1000 endif if MINIDRIVER_DUMMY -JTAG_SRCS += %D%/minidummy/minidummy.c %D%/commands.c +JTAG_SRCS += %D%/minidummy/minidummy.c JTAG_MINIDRIVER_DIR = %D%/minidummy endif @@ -29,7 +29,6 @@ CLEANFILES += %D%/jtag_minidriver.h else MINIDRIVER_IMP_DIR = %D%/drivers -JTAG_SRCS += %D%/commands.c if HLADAPTER include %D%/hla/Makefile.am diff --git a/src/jtag/commands.c b/src/jtag/commands.c index ed40755b7..e2d22cc94 100644 --- a/src/jtag/commands.c +++ b/src/jtag/commands.c @@ -144,6 +144,18 @@ void jtag_command_queue_reset(void) next_command_pointer = &jtag_command_queue; } +/** + * Copy a struct scan_field for insertion into the queue. + * + * This allocates a new copy of out_value using cmd_queue_alloc. + */ +void jtag_scan_field_clone(struct scan_field *dst, const struct scan_field *src) +{ + dst->num_bits = src->num_bits; + dst->out_value = buf_cpy(src->out_value, cmd_queue_alloc(DIV_ROUND_UP(src->num_bits, 8)), src->num_bits); + dst->in_value = src->in_value; +} + enum scan_type jtag_scan_type(const struct scan_command *cmd) { int i; diff --git a/src/jtag/commands.h b/src/jtag/commands.h index 947c94725..c0375964c 100644 --- a/src/jtag/commands.h +++ b/src/jtag/commands.h @@ -168,6 +168,7 @@ void *cmd_queue_alloc(size_t size); void jtag_queue_command(struct jtag_command *cmd); void jtag_command_queue_reset(void); +void jtag_scan_field_clone(struct scan_field *dst, const struct scan_field *src); enum scan_type jtag_scan_type(const struct scan_command *cmd); int jtag_scan_size(const struct scan_command *cmd); int jtag_read_buffer(uint8_t *buffer, const struct scan_command *cmd); diff --git a/src/jtag/drivers/bitq.c b/src/jtag/drivers/bitq.c index 66285f700..55dfe0aa4 100644 --- a/src/jtag/drivers/bitq.c +++ b/src/jtag/drivers/bitq.c @@ -123,7 +123,7 @@ static void bitq_path_move(struct pathmove_command *cmd) { int i; - for (i = 0; i <= cmd->num_states; i++) { + for (i = 0; i < cmd->num_states; i++) { if (tap_state_transition(tap_get_state(), false) == cmd->path[i]) bitq_io(0, 0, 0); else if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap_usb.c index 4ee483659..035cc446e 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap_usb.c @@ -166,7 +166,7 @@ static const char * const info_caps_str[] = { struct cmsis_dap { hid_device *dev_handle; uint16_t packet_size; - uint16_t packet_count; + int packet_count; uint8_t *packet_buffer; uint8_t caps; uint8_t mode; @@ -178,6 +178,11 @@ struct pending_transfer_result { void *buffer; }; +struct pending_request_block { + struct pending_transfer_result *transfers; + int transfer_count; +}; + struct pending_scan_result { /** Offset in bytes in the CMD_DAP_JTAG_SEQ response buffer. */ unsigned first; @@ -189,8 +194,16 @@ struct pending_scan_result { unsigned buffer_offset; }; -static int pending_transfer_count, pending_queue_len; -static struct pending_transfer_result *pending_transfers; +/* Up to MIN(packet_count, MAX_PENDING_REQUESTS) requests may be issued + * until the first response arrives */ +#define MAX_PENDING_REQUESTS 3 + +/* Pending requests are organized as a FIFO - circular buffer */ +/* Each block in FIFO can contain up to pending_queue_len transfers */ +static int pending_queue_len; +static struct pending_request_block pending_fifo[MAX_PENDING_REQUESTS]; +static int pending_fifo_put_idx, pending_fifo_get_idx; +static int pending_fifo_block_count; /* pointers to buffers that will receive jtag scan results on the next flush */ #define MAX_PENDING_SCAN_RESULTS 256 @@ -346,14 +359,16 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap) cmsis_dap_handle = NULL; free(cmsis_dap_serial); cmsis_dap_serial = NULL; - free(pending_transfers); - pending_transfers = NULL; + + for (int i = 0; i < MAX_PENDING_REQUESTS; i++) { + free(pending_fifo[i].transfers); + pending_fifo[i].transfers = NULL; + } return; } -/* Send a message and receive the reply */ -static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) +static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen) { #ifdef CMSIS_DAP_JTAG_DEBUG LOG_DEBUG("cmsis-dap usb xfer cmd=%02X", dap->packet_buffer[1]); @@ -368,6 +383,26 @@ static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) return ERROR_FAIL; } + return ERROR_OK; +} + +/* Send a message and receive the reply */ +static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) +{ + if (pending_fifo_block_count) { + LOG_ERROR("pending %d blocks, flushing", pending_fifo_block_count); + while (pending_fifo_block_count) { + hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, 10); + pending_fifo_block_count--; + } + pending_fifo_put_idx = 0; + pending_fifo_get_idx = 0; + } + + int retval = cmsis_dap_usb_write(dap, txlen); + if (retval != ERROR_OK) + return retval; + /* get reply */ retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, USB_TIMEOUT); if (retval == -1 || retval == 0) { @@ -594,29 +629,31 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us) } #endif -static int cmsis_dap_swd_run_queue(void) +static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) { - uint8_t *buffer = cmsis_dap_handle->packet_buffer; + uint8_t *buffer = dap->packet_buffer; + struct pending_request_block *block = &pending_fifo[pending_fifo_put_idx]; - LOG_DEBUG_IO("Executing %d queued transactions", pending_transfer_count); + LOG_DEBUG_IO("Executing %d queued transactions from FIFO index %d", block->transfer_count, pending_fifo_put_idx); if (queued_retval != ERROR_OK) { LOG_DEBUG("Skipping due to previous errors: %d", queued_retval); goto skip; } - if (!pending_transfer_count) + if (block->transfer_count == 0) goto skip; size_t idx = 0; buffer[idx++] = 0; /* report number */ buffer[idx++] = CMD_DAP_TFER; buffer[idx++] = 0x00; /* DAP Index */ - buffer[idx++] = pending_transfer_count; + buffer[idx++] = block->transfer_count; - for (int i = 0; i < pending_transfer_count; i++) { - uint8_t cmd = pending_transfers[i].cmd; - uint32_t data = pending_transfers[i].data; + for (int i = 0; i < block->transfer_count; i++) { + struct pending_transfer_result *transfer = &(block->transfers[i]); + uint8_t cmd = transfer->cmd; + uint32_t data = transfer->data; LOG_DEBUG_IO("%s %s reg %x %"PRIx32, cmd & SWD_CMD_APnDP ? "AP" : "DP", @@ -651,26 +688,62 @@ static int cmsis_dap_swd_run_queue(void) } } - queued_retval = cmsis_dap_usb_xfer(cmsis_dap_handle, idx); + queued_retval = cmsis_dap_usb_write(dap, idx); if (queued_retval != ERROR_OK) goto skip; - idx = 2; - uint8_t ack = buffer[idx] & 0x07; - if (ack != SWD_ACK_OK || (buffer[idx] & 0x08)) { - LOG_DEBUG("SWD ack not OK: %d %s", buffer[idx-1], + pending_fifo_put_idx = (pending_fifo_put_idx + 1) % dap->packet_count; + pending_fifo_block_count++; + if (pending_fifo_block_count > dap->packet_count) + LOG_ERROR("too much pending writes %d", pending_fifo_block_count); + + return; + +skip: + block->transfer_count = 0; +} + +static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) +{ + uint8_t *buffer = dap->packet_buffer; + struct pending_request_block *block = &pending_fifo[pending_fifo_get_idx]; + + if (pending_fifo_block_count == 0) + LOG_ERROR("no pending write"); + + /* get reply */ + int retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms); + if (retval == 0 && timeout_ms < USB_TIMEOUT) + return; + + if (retval == -1 || retval == 0) { + LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle)); + queued_retval = ERROR_FAIL; + goto skip; + } + + if (buffer[2] & 0x08) { + LOG_DEBUG("CMSIS-DAP Protocol Error @ %d (wrong parity)", buffer[1]); + queued_retval = ERROR_FAIL; + goto skip; + } + uint8_t ack = buffer[2] & 0x07; + if (ack != SWD_ACK_OK) { + LOG_DEBUG("SWD ack not OK @ %d %s", buffer[1], ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK"); queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL; goto skip; } - idx++; - if (pending_transfer_count != buffer[1]) + if (block->transfer_count != buffer[1]) LOG_ERROR("CMSIS-DAP transfer count mismatch: expected %d, got %d", - pending_transfer_count, buffer[1]); + block->transfer_count, buffer[1]); + LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %d", buffer[1], pending_fifo_get_idx); + size_t idx = 3; for (int i = 0; i < buffer[1]; i++) { - if (pending_transfers[i].cmd & SWD_CMD_RnW) { + struct pending_transfer_result *transfer = &(block->transfers[i]); + if (transfer->cmd & SWD_CMD_RnW) { static uint32_t last_read; uint32_t data = le_to_h_u32(&buffer[idx]); uint32_t tmp = data; @@ -679,19 +752,36 @@ static int cmsis_dap_swd_run_queue(void) LOG_DEBUG_IO("Read result: %"PRIx32, data); /* Imitate posted AP reads */ - if ((pending_transfers[i].cmd & SWD_CMD_APnDP) || - ((pending_transfers[i].cmd & SWD_CMD_A32) >> 1 == DP_RDBUFF)) { + if ((transfer->cmd & SWD_CMD_APnDP) || + ((transfer->cmd & SWD_CMD_A32) >> 1 == DP_RDBUFF)) { tmp = last_read; last_read = data; } - if (pending_transfers[i].buffer) - *(uint32_t *)pending_transfers[i].buffer = tmp; + if (transfer->buffer) + *(uint32_t *)(transfer->buffer) = tmp; } } skip: - pending_transfer_count = 0; + block->transfer_count = 0; + pending_fifo_get_idx = (pending_fifo_get_idx + 1) % dap->packet_count; + pending_fifo_block_count--; +} + +static int cmsis_dap_swd_run_queue(void) +{ + if (pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, 0); + + cmsis_dap_swd_write_from_queue(cmsis_dap_handle); + + while (pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, USB_TIMEOUT); + + pending_fifo_put_idx = 0; + pending_fifo_get_idx = 0; + int retval = queued_retval; queued_retval = ERROR_OK; @@ -700,21 +790,29 @@ skip: static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) { - if (pending_transfer_count == pending_queue_len) { + if (pending_fifo[pending_fifo_put_idx].transfer_count == pending_queue_len) { + if (pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, 0); + /* Not enough room in the queue. Run the queue. */ - queued_retval = cmsis_dap_swd_run_queue(); + cmsis_dap_swd_write_from_queue(cmsis_dap_handle); + + if (pending_fifo_block_count >= cmsis_dap_handle->packet_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, USB_TIMEOUT); } if (queued_retval != ERROR_OK) return; - pending_transfers[pending_transfer_count].data = data; - pending_transfers[pending_transfer_count].cmd = cmd; + struct pending_request_block *block = &pending_fifo[pending_fifo_put_idx]; + struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]); + transfer->data = data; + transfer->cmd = cmd; if (cmd & SWD_CMD_RnW) { /* Queue a read transaction */ - pending_transfers[pending_transfer_count].buffer = dst; + transfer->buffer = dst; } - pending_transfer_count++; + block->transfer_count++; } static void cmsis_dap_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) @@ -895,9 +993,7 @@ static int cmsis_dap_init(void) retval = cmsis_dap_swd_open(); if (retval != ERROR_OK) return retval; - } - - if (cmsis_dap_handle == NULL) { + } else { /* Connect in JTAG mode */ if (!(cmsis_dap_handle->caps & INFO_CAPS_JTAG)) { LOG_ERROR("CMSIS-DAP: JTAG not supported"); @@ -911,6 +1007,11 @@ static int cmsis_dap_init(void) LOG_INFO("CMSIS-DAP: Interface Initialised (JTAG)"); } + /* Be conservative and supress submiting multiple HID requests + * until we get packet count info from the adaptor */ + cmsis_dap_handle->packet_count = 1; + pending_queue_len = 12; + /* INFO_ID_PKT_SZ - short */ retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data); if (retval != ERROR_OK) @@ -923,11 +1024,6 @@ static int cmsis_dap_init(void) * write. For bulk read sequences just 4 bytes are * needed per transfer, so this is suboptimal. */ pending_queue_len = (pkt_sz - 4) / 5; - pending_transfers = malloc(pending_queue_len * sizeof(*pending_transfers)); - if (!pending_transfers) { - LOG_ERROR("Unable to allocate memory for CMSIS-DAP queue"); - return ERROR_FAIL; - } if (cmsis_dap_handle->packet_size != pkt_sz + 1) { /* reallocate buffer */ @@ -949,11 +1045,23 @@ static int cmsis_dap_init(void) return retval; if (data[0] == 1) { /* byte */ - uint16_t pkt_cnt = data[1]; - cmsis_dap_handle->packet_count = pkt_cnt; - LOG_DEBUG("CMSIS-DAP: Packet Count = %" PRId16, pkt_cnt); + int pkt_cnt = data[1]; + if (pkt_cnt > 1) + cmsis_dap_handle->packet_count = MIN(MAX_PENDING_REQUESTS, pkt_cnt); + + LOG_DEBUG("CMSIS-DAP: Packet Count = %d", pkt_cnt); } + LOG_DEBUG("Allocating FIFO for %d pending HID requests", cmsis_dap_handle->packet_count); + for (int i = 0; i < cmsis_dap_handle->packet_count; i++) { + pending_fifo[i].transfers = malloc(pending_queue_len * sizeof(struct pending_transfer_result)); + if (!pending_fifo[i].transfers) { + LOG_ERROR("Unable to allocate memory for CMSIS-DAP queue"); + return ERROR_FAIL; + } + } + + retval = cmsis_dap_get_status(); if (retval != ERROR_OK) return ERROR_FAIL; diff --git a/src/jtag/drivers/driver.c b/src/jtag/drivers/driver.c index daf7cd426..4923f96eb 100644 --- a/src/jtag/drivers/driver.c +++ b/src/jtag/drivers/driver.c @@ -55,18 +55,6 @@ static void jtag_callback_queue_reset(void) jtag_callback_queue_tail = NULL; } -/** - * Copy a struct scan_field for insertion into the queue. - * - * This allocates a new copy of out_value using cmd_queue_alloc. - */ -static void cmd_queue_scan_field_clone(struct scan_field *dst, const struct scan_field *src) -{ - dst->num_bits = src->num_bits; - dst->out_value = buf_cpy(src->out_value, cmd_queue_alloc(DIV_ROUND_UP(src->num_bits, 8)), src->num_bits); - dst->in_value = src->in_value; -} - /** * see jtag_add_ir_scan() * @@ -101,7 +89,7 @@ int interface_jtag_add_ir_scan(struct jtag_tap *active, /* if TAP is listed in input fields, copy the value */ tap->bypass = 0; - cmd_queue_scan_field_clone(field, in_fields); + jtag_scan_field_clone(field, in_fields); } else { /* if a TAP isn't listed in input fields, set it to BYPASS */ @@ -168,7 +156,7 @@ int interface_jtag_add_dr_scan(struct jtag_tap *active, int in_num_fields, #endif /* NDEBUG */ for (int j = 0; j < in_num_fields; j++) { - cmd_queue_scan_field_clone(field, in_fields + j); + jtag_scan_field_clone(field, in_fields + j); field++; } diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 6c1e50492..7deb1c493 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -506,11 +506,11 @@ static void ftdi_execute_scan(struct jtag_command *cmd) while (cmd->cmd.scan->num_fields > 0 && cmd->cmd.scan->fields[cmd->cmd.scan->num_fields - 1].num_bits == 0) { cmd->cmd.scan->num_fields--; - LOG_DEBUG("discarding trailing empty field"); + DEBUG_JTAG_IO("discarding trailing empty field"); } if (cmd->cmd.scan->num_fields == 0) { - LOG_DEBUG("empty scan, doing nothing"); + DEBUG_JTAG_IO("empty scan, doing nothing"); return; } diff --git a/src/jtag/drivers/libusb1_common.c b/src/jtag/drivers/libusb1_common.c index 89f809271..a1db86f4b 100644 --- a/src/jtag/drivers/libusb1_common.c +++ b/src/jtag/drivers/libusb1_common.c @@ -71,11 +71,11 @@ int jtag_libusb_open(const uint16_t vids[], const uint16_t pids[], struct jtag_libusb_device_handle **out) { int cnt, idx, errCode; - int retval = -ENODEV; + int retval = ERROR_FAIL; struct jtag_libusb_device_handle *libusb_handle = NULL; if (libusb_init(&jtag_libusb_context) < 0) - return -ENODEV; + return ERROR_FAIL; cnt = libusb_get_device_list(jtag_libusb_context, &devs); @@ -105,7 +105,7 @@ int jtag_libusb_open(const uint16_t vids[], const uint16_t pids[], /* Success. */ *out = libusb_handle; - retval = 0; + retval = ERROR_OK; break; } if (cnt >= 0) diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c index 6c70be6ad..43e924e03 100644 --- a/src/jtag/drivers/mpsse.c +++ b/src/jtag/drivers/mpsse.c @@ -121,7 +121,7 @@ static bool device_location_equal(libusb_device *device, const char *location) LOG_DEBUG("device path has %i steps", path_len); - ptr = strtok(loc, ":"); + ptr = strtok(loc, "-:"); if (ptr == NULL) { LOG_DEBUG("no ':' in path"); goto done; @@ -133,7 +133,7 @@ static bool device_location_equal(libusb_device *device, const char *location) path_step = 0; while (path_step < 7) { - ptr = strtok(NULL, ","); + ptr = strtok(NULL, ".,"); if (ptr == NULL) { LOG_DEBUG("no more tokens in path at step %i", path_step); break; diff --git a/src/rtos/ChibiOS.c b/src/rtos/ChibiOS.c index 312fc4d99..8839acc9a 100644 --- a/src/rtos/ChibiOS.c +++ b/src/rtos/ChibiOS.c @@ -105,7 +105,8 @@ static struct ChibiOS_params ChibiOS_params_list[] = { static bool ChibiOS_detect_rtos(struct target *target); static int ChibiOS_create(struct target *target); static int ChibiOS_update_threads(struct rtos *rtos); -static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type ChibiOS_rtos = { @@ -464,13 +465,13 @@ static int ChibiOS_update_threads(struct rtos *rtos) return 0; } -static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct ChibiOS_params *param; uint32_t stack_ptr = 0; - *hex_reg_list = NULL; if ((rtos == NULL) || (thread_id == 0) || (rtos->rtos_specific_params == NULL)) return -1; @@ -495,7 +496,7 @@ static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, cha return retval; } - return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs); } static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c index 6027d6739..9d89974cc 100644 --- a/src/rtos/FreeRTOS.c +++ b/src/rtos/FreeRTOS.c @@ -102,7 +102,8 @@ static const struct FreeRTOS_params FreeRTOS_params_list[] = { static bool FreeRTOS_detect_rtos(struct target *target); static int FreeRTOS_create(struct target *target); static int FreeRTOS_update_threads(struct rtos *rtos); -static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); static int FreeRTOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type FreeRTOS_rtos = { @@ -395,13 +396,13 @@ static int FreeRTOS_update_threads(struct rtos *rtos) return 0; } -static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct FreeRTOS_params *param; int64_t stack_ptr = 0; - *hex_reg_list = NULL; if (rtos == NULL) return -1; @@ -460,11 +461,11 @@ static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, ch return retval; } if ((LR_svc & 0x10) == 0) - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs); else - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs); } else - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs); } static int FreeRTOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/ThreadX.c b/src/rtos/ThreadX.c index b7dbe6d11..9605533aa 100644 --- a/src/rtos/ThreadX.c +++ b/src/rtos/ThreadX.c @@ -38,7 +38,7 @@ static int is_thread_id_valid_arm926ejs(const struct rtos *rtos, int64_t thread_ static bool ThreadX_detect_rtos(struct target *target); static int ThreadX_create(struct target *target); static int ThreadX_update_threads(struct rtos *rtos); -static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs); static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); @@ -69,45 +69,45 @@ static const struct ThreadX_thread_state ThreadX_thread_states[] = { #define ARM926EJS_REGISTERS_SIZE_SOLICITED (11 * 4) static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_solicited[] = { - { -1, 32 }, /* r0 */ - { -1, 32 }, /* r1 */ - { -1, 32 }, /* r2 */ - { -1, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0C, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1C, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { -1, 32 }, /* r12 */ - { -2, 32 }, /* sp (r13) */ - { 0x28, 32 }, /* lr (r14) */ - { -1, 32 }, /* pc (r15) */ - /*{ -1, 32 },*/ /* lr (r14) */ - /*{ 0x28, 32 },*/ /* pc (r15) */ - { 0x04, 32 }, /* xPSR */ + { 0, -1, 32 }, /* r0 */ + { 1, -1, 32 }, /* r1 */ + { 2, -1, 32 }, /* r2 */ + { 3, -1, 32 }, /* r3 */ + { 4, 0x08, 32 }, /* r4 */ + { 5, 0x0C, 32 }, /* r5 */ + { 6, 0x10, 32 }, /* r6 */ + { 7, 0x14, 32 }, /* r7 */ + { 8, 0x18, 32 }, /* r8 */ + { 9, 0x1C, 32 }, /* r9 */ + { 10, 0x20, 32 }, /* r10 */ + { 11, 0x24, 32 }, /* r11 */ + { 12, -1, 32 }, /* r12 */ + { 13, -2, 32 }, /* sp (r13) */ + { 14, 0x28, 32 }, /* lr (r14) */ + { 15, -1, 32 }, /* pc (r15) */ + /*{ 16, -1, 32 },*/ /* lr (r14) */ + /*{ 17, 0x28, 32 },*/ /* pc (r15) */ + { 16, 0x04, 32 }, /* xPSR */ }; #define ARM926EJS_REGISTERS_SIZE_INTERRUPT (17 * 4) static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_interrupt[] = { - { 0x08, 32 }, /* r0 */ - { 0x0C, 32 }, /* r1 */ - { 0x10, 32 }, /* r2 */ - { 0x14, 32 }, /* r3 */ - { 0x18, 32 }, /* r4 */ - { 0x1C, 32 }, /* r5 */ - { 0x20, 32 }, /* r6 */ - { 0x24, 32 }, /* r7 */ - { 0x28, 32 }, /* r8 */ - { 0x2C, 32 }, /* r9 */ - { 0x30, 32 }, /* r10 */ - { 0x34, 32 }, /* r11 */ - { 0x38, 32 }, /* r12 */ - { -2, 32 }, /* sp (r13) */ - { 0x3C, 32 }, /* lr (r14) */ - { 0x40, 32 }, /* pc (r15) */ - { 0x04, 32 }, /* xPSR */ + { 0, 0x08, 32 }, /* r0 */ + { 1, 0x0C, 32 }, /* r1 */ + { 2, 0x10, 32 }, /* r2 */ + { 3, 0x14, 32 }, /* r3 */ + { 4, 0x18, 32 }, /* r4 */ + { 5, 0x1C, 32 }, /* r5 */ + { 6, 0x20, 32 }, /* r6 */ + { 7, 0x24, 32 }, /* r7 */ + { 8, 0x28, 32 }, /* r8 */ + { 9, 0x2C, 32 }, /* r9 */ + { 10, 0x30, 32 }, /* r10 */ + { 11, 0x34, 32 }, /* r11 */ + { 12, 0x38, 32 }, /* r12 */ + { 13, -2, 32 }, /* sp (r13) */ + { 14, 0x3C, 32 }, /* lr (r14) */ + { 15, 0x40, 32 }, /* pc (r15) */ + { 16, 0x04, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = { @@ -433,13 +433,12 @@ static int ThreadX_update_threads(struct rtos *rtos) return 0; } -static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct ThreadX_params *param; - *hex_reg_list = NULL; - if (rtos == NULL) return -1; @@ -477,7 +476,7 @@ static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, cha return -6; } - return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, reg_list, num_regs); } static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/eCos.c b/src/rtos/eCos.c index 9e41030ee..e6b70730b 100644 --- a/src/rtos/eCos.c +++ b/src/rtos/eCos.c @@ -30,7 +30,7 @@ static bool eCos_detect_rtos(struct target *target); static int eCos_create(struct target *target); static int eCos_update_threads(struct rtos *rtos); -static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs); static int eCos_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct eCos_thread_state { @@ -285,13 +285,12 @@ static int eCos_update_threads(struct rtos *rtos) return 0; } -static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct eCos_params *param; - *hex_reg_list = NULL; - if (rtos == NULL) return -1; @@ -345,7 +344,8 @@ static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char * return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, - hex_reg_list); + reg_list, + num_regs); } return -1; diff --git a/src/rtos/embKernel.c b/src/rtos/embKernel.c index a40c86c3f..8a307f198 100644 --- a/src/rtos/embKernel.c +++ b/src/rtos/embKernel.c @@ -34,7 +34,8 @@ static bool embKernel_detect_rtos(struct target *target); static int embKernel_create(struct target *target); static int embKernel_update_threads(struct rtos *rtos); -static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); static int embKernel_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type embKernel_rtos = { @@ -300,13 +301,13 @@ static int embKernel_update_threads(struct rtos *rtos) return 0; } -static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct embKernel_params *param; int64_t stack_ptr = 0; - *hex_reg_list = NULL; if (rtos == NULL) return -1; @@ -326,7 +327,7 @@ static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, c return retval; } - return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs); } static int embKernel_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/linux.c b/src/rtos/linux.c index 3b2a2b0c3..74172b70a 100644 --- a/src/rtos/linux.c +++ b/src/rtos/linux.c @@ -144,16 +144,6 @@ static int linux_read_memory(struct target *target, return ERROR_OK; } -static char *reg_converter(char *buffer, void *reg, int size) -{ - int i; - - for (i = 0; i < size; i++) - buffer += sprintf(buffer, "%02x", ((uint8_t *) reg)[i]); - - return buffer; -} - int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer) { @@ -174,15 +164,13 @@ uint32_t get_buffer(struct target *target, const uint8_t *buffer) } static int linux_os_thread_reg_list(struct rtos *rtos, - int64_t thread_id, char **hex_reg_list) + int64_t thread_id, struct rtos_reg **reg_list, int *num_regs) { struct target *target = rtos->target; struct linux_os *linux_os = (struct linux_os *) target->rtos->rtos_specific_params; - int i = 0; struct current_thread *tmp = linux_os->current_threads; struct current_thread *next; - char *hex_string; int found = 0; int retval; /* check if a current thread is requested */ @@ -195,117 +183,52 @@ static int linux_os_thread_reg_list(struct rtos *rtos, next = next->next; } while ((found == 0) && (next != tmp) && (next != NULL)); - if (found == 1) { - /* search target to perfom the access */ - struct reg **reg_list; - int reg_list_size, reg_packet_size = 0; - struct target_list *head; - head = target->head; - found = 0; - do { - if (head->target->coreid == next->core_id) { - - target = head->target; - found = 1; - } else - head = head->next; - - } while ((head != (struct target_list *)NULL) && (found == 0)); - - if (found == 0) { - LOG_ERROR - ( - "current thread %" PRIx64 ": no target to perform access of core id %" PRIx32, - thread_id, - next->core_id); - return ERROR_FAIL; - } - - /*LOG_INFO("thread %lx current on core %x",thread_id, - * target->coreid);*/ - retval = - target_get_gdb_reg_list(target, ®_list, ®_list_size, - REG_CLASS_GENERAL); - - if (retval != ERROR_OK) - return retval; - - for (i = 0; i < reg_list_size; i++) - reg_packet_size += reg_list[i]->size; - - assert(reg_packet_size > 0); - - *hex_reg_list = malloc(DIV_ROUND_UP(reg_packet_size, 8) * 2); - - hex_string = *hex_reg_list; - - for (i = 0; i < reg_list_size; i++) { - if (!reg_list[i]->valid) - reg_list[i]->type->get(reg_list[i]); - - hex_string = reg_converter(hex_string, - reg_list[i]->value, - (reg_list[i]->size) / 8); - } - - free(reg_list); - - } else { - struct threads *temp = linux_os->thread_list; - *hex_reg_list = calloc(1, 500 * sizeof(char)); - hex_string = *hex_reg_list; - - for (i = 0; i < 16; i++) - hex_string += sprintf(hex_string, "%02x", 0); - - while ((temp != NULL) && - (temp->threadid != target->rtos->current_threadid)) - temp = temp->next; - - if (temp != NULL) { - if (temp->context == NULL) - temp->context = cpu_context_read(target, - temp-> - base_addr, - &temp-> - thread_info_addr); - - hex_string = - reg_converter(hex_string, &temp->context->R4, 4); - hex_string = - reg_converter(hex_string, &temp->context->R5, 4); - hex_string = - reg_converter(hex_string, &temp->context->R6, 4); - hex_string = - reg_converter(hex_string, &temp->context->R7, 4); - hex_string = - reg_converter(hex_string, &temp->context->R8, 4); - hex_string = - reg_converter(hex_string, &temp->context->R9, 4); - - for (i = 0; i < 4; i++) /*R10 = 0x0 */ - hex_string += sprintf(hex_string, "%02x", 0); - - hex_string = - reg_converter(hex_string, &temp->context->FP, 4); - hex_string = - reg_converter(hex_string, &temp->context->IP, 4); - hex_string = - reg_converter(hex_string, &temp->context->SP, 4); - - for (i = 0; i < 4; i++) - hex_string += sprintf(hex_string, "%02x", 0); - - hex_string = - reg_converter(hex_string, &temp->context->PC, 4); - - for (i = 0; i < 100; i++) /*100 */ - hex_string += sprintf(hex_string, "%02x", 0); - - uint32_t cpsr = 0x00000000; - reg_converter(hex_string, &cpsr, 4); - } + if (found == 0) { + LOG_ERROR("could not find thread: %" PRIx64, thread_id); + return ERROR_FAIL; } + + /* search target to perfom the access */ + struct reg **gdb_reg_list; + struct target_list *head; + head = target->head; + found = 0; + do { + if (head->target->coreid == next->core_id) { + + target = head->target; + found = 1; + } else + head = head->next; + + } while ((head != (struct target_list *)NULL) && (found == 0)); + + if (found == 0) { + LOG_ERROR + ( + "current thread %" PRIx64 ": no target to perform access of core id %" PRIx32, + thread_id, + next->core_id); + return ERROR_FAIL; + } + + /*LOG_INFO("thread %lx current on core %x",thread_id, target->coreid);*/ + retval = target_get_gdb_reg_list(target, &gdb_reg_list, num_regs, REG_CLASS_GENERAL); + if (retval != ERROR_OK) + return retval; + + *reg_list = calloc(*num_regs, sizeof(struct rtos_reg)); + + for (int i = 0; i < *num_regs; ++i) { + if (!gdb_reg_list[i]->valid) + gdb_reg_list[i]->type->get(gdb_reg_list[i]); + + (*reg_list)[i].number = gdb_reg_list[i]->number; + (*reg_list)[i].size = gdb_reg_list[i]->size; + + buf_cpy(gdb_reg_list[i]->value, (*reg_list)[i].value, (*reg_list)[i].size); + } + return ERROR_OK; } @@ -1134,7 +1057,7 @@ int linux_gdb_thread_packet(struct target *target, if (retval != ERROR_OK) return ERROR_TARGET_FAILURE; - char *out_str = calloc(1, 350 * sizeof(int64_t)); + char *out_str = calloc(MAX_THREADS * 17 + 10, 1); char *tmp_str = out_str; tmp_str += sprintf(tmp_str, "m"); struct threads *temp = linux_os->thread_list; @@ -1171,7 +1094,7 @@ int linux_gdb_thread_update(struct target *target, if (found == 1) { /*LOG_INFO("INTO GDB THREAD UPDATE FOUNDING START TASK");*/ - char *out_strr = calloc(1, 350 * sizeof(int64_t)); + char *out_strr = calloc(MAX_THREADS * 17 + 10, 1); char *tmp_strr = out_strr; tmp_strr += sprintf(tmp_strr, "m"); /*LOG_INFO("CHAR MALLOC & M DONE");*/ diff --git a/src/rtos/mqx.c b/src/rtos/mqx.c index 531b03b57..6646ad4de 100644 --- a/src/rtos/mqx.c +++ b/src/rtos/mqx.c @@ -456,7 +456,8 @@ static int mqx_update_threads( static int mqx_get_thread_reg_list( struct rtos *rtos, int64_t thread_id, - char **hex_reg_list + struct rtos_reg **reg_list, + int *num_regs ) { int64_t stack_ptr = 0; @@ -465,7 +466,6 @@ static int mqx_get_thread_reg_list( uint32_t task_queue_size = 0; uint32_t kernel_data_addr = 0; - *hex_reg_list = NULL; if (thread_id == 0) { LOG_ERROR("MQX RTOS - invalid threadid: 0x%X", (int)thread_id); return ERROR_FAIL; @@ -535,7 +535,7 @@ static int mqx_get_thread_reg_list( return ERROR_FAIL; } return rtos_generic_stack_read( - rtos->target, ((struct mqx_params *)rtos->rtos_specific_params)->stacking_info, stack_ptr, hex_reg_list + rtos->target, ((struct mqx_params *)rtos->rtos_specific_params)->stacking_info, stack_ptr, reg_list, num_regs ); } diff --git a/src/rtos/nuttx.c b/src/rtos/nuttx.c index 284b968cc..61fd9aa6c 100644 --- a/src/rtos/nuttx.c +++ b/src/rtos/nuttx.c @@ -98,23 +98,23 @@ static char *task_state_str[] = { /* see arch/arm/include/armv7-m/irq_cmnvector.h */ static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = { - { 0x28, 32 }, /* r0 */ - { 0x2c, 32 }, /* r1 */ - { 0x30, 32 }, /* r2 */ - { 0x34, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0c, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1c, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { 0x38, 32 }, /* r12 */ - { 0, 32 }, /* sp */ - { 0x3c, 32 }, /* lr */ - { 0x40, 32 }, /* pc */ - { 0x44, 32 }, /* xPSR */ + { ARMV7M_R0, 0x28, 32 }, /* r0 */ + { ARMV7M_R1, 0x2c, 32 }, /* r1 */ + { ARMV7M_R2, 0x30, 32 }, /* r2 */ + { ARMV7M_R3, 0x34, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0c, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1c, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x38, 32 }, /* r12 */ + { ARMV7M_R13, 0, 32 }, /* sp */ + { ARMV7M_R14, 0x3c, 32 }, /* lr */ + { ARMV7M_PC, 0x40, 32 }, /* pc */ + { ARMV7M_xPSR, 0x44, 32 }, /* xPSR */ }; @@ -127,23 +127,23 @@ static const struct rtos_register_stacking nuttx_stacking_cortex_m = { }; static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = { - { 0x6c, 32 }, /* r0 */ - { 0x70, 32 }, /* r1 */ - { 0x74, 32 }, /* r2 */ - { 0x78, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0c, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1c, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { 0x7c, 32 }, /* r12 */ - { 0, 32 }, /* sp */ - { 0x80, 32 }, /* lr */ - { 0x84, 32 }, /* pc */ - { 0x88, 32 }, /* xPSR */ + { ARMV7M_R0, 0x6c, 32 }, /* r0 */ + { ARMV7M_R1, 0x70, 32 }, /* r1 */ + { ARMV7M_R2, 0x74, 32 }, /* r2 */ + { ARMV7M_R3, 0x78, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0c, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1c, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x7c, 32 }, /* r12 */ + { ARMV7M_R13, 0, 32 }, /* sp */ + { ARMV7M_R14, 0x80, 32 }, /* lr */ + { ARMV7M_PC, 0x84, 32 }, /* pc */ + { ARMV7M_xPSR, 0x88, 32 }, /* xPSR */ }; static const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = { @@ -344,11 +344,10 @@ static int nuttx_update_threads(struct rtos *rtos) * thread_id = tcb address; */ static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, - char **hex_reg_list) { + struct rtos_reg **reg_list, int *num_regs) +{ int retval; - *hex_reg_list = NULL; - /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ bool cm4_fpu_enabled = false; struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); @@ -378,7 +377,7 @@ static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, stacking = &nuttx_stacking_cortex_m; return rtos_generic_stack_read(rtos->target, stacking, - (uint32_t)thread_id + xcpreg_offset, hex_reg_list); + (uint32_t)thread_id + xcpreg_offset, reg_list, num_regs); } static int nuttx_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c index 36500218e..d5cd4a1bf 100644 --- a/src/rtos/riscv_debug.c +++ b/src/rtos/riscv_debug.c @@ -270,39 +270,35 @@ static int riscv_gdb_v_packet(struct connection *connection, const char *packet, return GDB_THREAD_PACKET_NOT_CONSUMED; } -static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1)); - size_t n_regs = 32; - size_t xlen = 64; - size_t reg_chars = xlen / 8 * 2; + /* We return just the GPRs here. */ - ssize_t hex_reg_list_length = n_regs * reg_chars + 2; - *hex_reg_list = malloc(hex_reg_list_length); - *hex_reg_list[0] = '\0'; - char *p = hex_reg_list[0]; - for (size_t i = 0; i < n_regs; ++i) { - assert(p - hex_reg_list[0] > 3); - if (riscv_has_register(rtos->target, thread_id, i)) { - uint64_t reg_value; - int result = riscv_get_register_on_hart(rtos->target, ®_value, - thread_id - 1, i); - if (result != ERROR_OK) - return JIM_ERR; + *num_regs = 32; + int xlen = riscv_xlen_of_hart(rtos->target, thread_id - 1); - for (size_t byte = 0; byte < xlen / 8; ++byte) { - uint8_t reg_byte = reg_value >> (byte * 8); - p += snprintf(p, 3, "%02x", reg_byte); - } - } else { - for (size_t byte = 0; byte < xlen / 8; ++byte) { - strcpy(p, "xx"); - p += 2; - } - } + *reg_list = calloc(*num_regs, sizeof(struct rtos_reg)); + *reg_list = 0; + for (int i = 0; i < *num_regs; ++i) { + uint64_t reg_value; + if (riscv_get_register_on_hart(rtos->target, ®_value, thread_id - 1, + i) != ERROR_OK) + return JIM_ERR; + + (*reg_list)[i].number = i; + (*reg_list)[i].size = xlen; + (*reg_list)[i].value[0] = reg_value & 0xff; + (*reg_list)[i].value[1] = (reg_value >> 8) & 0xff; + (*reg_list)[i].value[2] = (reg_value >> 16) & 0xff; + (*reg_list)[i].value[3] = (reg_value >> 24) & 0xff; + (*reg_list)[i].value[4] = (reg_value >> 32) & 0xff; + (*reg_list)[i].value[5] = (reg_value >> 40) & 0xff; + (*reg_list)[i].value[6] = (reg_value >> 48) & 0xff; + (*reg_list)[i].value[7] = (reg_value >> 56) & 0xff; } - LOG_DEBUG("%s", *hex_reg_list); return JIM_OK; } diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index bd9da0d2a..9b05ab5c4 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -438,6 +438,68 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa return GDB_THREAD_PACKET_NOT_CONSUMED; } +static int rtos_put_gdb_reg_list(struct connection *connection, + struct rtos_reg *reg_list, int num_regs) +{ + size_t num_bytes = 1; /* NUL */ + for (int i = 0; i < num_regs; ++i) + num_bytes += DIV_ROUND_UP(reg_list[i].size, 8) * 2; + + char *hex = malloc(num_bytes); + char *hex_p = hex; + + for (int i = 0; i < num_regs; ++i) { + size_t count = DIV_ROUND_UP(reg_list[i].size, 8); + size_t n = hexify(hex_p, reg_list[i].value, count, num_bytes); + hex_p += n; + num_bytes -= n; + } + + gdb_put_packet(connection, hex, strlen(hex)); + free(hex); + + return ERROR_OK; +} + +int rtos_get_gdb_reg(struct connection *connection, int reg_num) +{ + struct target *target = get_target_from_connection(connection); + int64_t current_threadid = target->rtos->current_threadid; + if ((target->rtos != NULL) && (current_threadid != -1) && + (current_threadid != 0) && + ((current_threadid != target->rtos->current_thread) || + (target->smp))) { /* in smp several current thread are possible */ + struct rtos_reg *reg_list; + int num_regs; + + LOG_DEBUG("RTOS: getting register %d for thread 0x%" PRIx64 + ", target->rtos->current_thread=0x%" PRIx64 "\r\n", + reg_num, + current_threadid, + target->rtos->current_thread); + + int retval = target->rtos->type->get_thread_reg_list(target->rtos, + current_threadid, + ®_list, + &num_regs); + if (retval != ERROR_OK) { + LOG_ERROR("RTOS: failed to get register list"); + return retval; + } + + for (int i = 0; i < num_regs; ++i) { + if (reg_list[i].number == (uint32_t)reg_num) { + rtos_put_gdb_reg_list(connection, reg_list + i, 1); + free(reg_list); + return ERROR_OK; + } + } + + free(reg_list); + } + return ERROR_FAIL; +} + int rtos_get_gdb_reg_list(struct connection *connection) { struct target *target = get_target_from_connection(connection); @@ -446,7 +508,8 @@ int rtos_get_gdb_reg_list(struct connection *connection) (current_threadid != 0) && ((current_threadid != target->rtos->current_thread) || (target->smp))) { /* in smp several current thread are possible */ - char *hex_reg_list; + struct rtos_reg *reg_list; + int num_regs; LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64 ", target->rtos->current_thread=0x%" PRIx64 "\r\n", @@ -455,17 +518,17 @@ int rtos_get_gdb_reg_list(struct connection *connection) int retval = target->rtos->type->get_thread_reg_list(target->rtos, current_threadid, - &hex_reg_list); + ®_list, + &num_regs); if (retval != ERROR_OK) { LOG_ERROR("RTOS: failed to get register list"); return retval; } - if (hex_reg_list != NULL) { - gdb_put_packet(connection, hex_reg_list, strlen(hex_reg_list)); - free(hex_reg_list); - return ERROR_OK; - } + rtos_put_gdb_reg_list(connection, reg_list, num_regs); + free(reg_list); + + return ERROR_OK; } return ERROR_FAIL; } @@ -473,12 +536,9 @@ int rtos_get_gdb_reg_list(struct connection *connection) int rtos_generic_stack_read(struct target *target, const struct rtos_register_stacking *stacking, int64_t stack_ptr, - char **hex_reg_list) + struct rtos_reg **reg_list, + int *num_regs) { - int list_size = 0; - char *tmp_str_ptr; - int64_t new_stack_ptr; - int i; int retval; if (stack_ptr == 0) { @@ -505,10 +565,8 @@ int rtos_generic_stack_read(struct target *target, LOG_OUTPUT("%02X", stack_data[i]); LOG_OUTPUT("\r\n"); #endif - for (i = 0; i < stacking->num_output_registers; i++) - list_size += stacking->register_offsets[i].width_bits/8; - *hex_reg_list = malloc(list_size*2 + 1); - tmp_str_ptr = *hex_reg_list; + + int64_t new_stack_ptr; if (stacking->calculate_process_stack != NULL) { new_stack_ptr = stacking->calculate_process_stack(target, stack_data, stacking, stack_ptr); @@ -516,19 +574,21 @@ int rtos_generic_stack_read(struct target *target, new_stack_ptr = stack_ptr - stacking->stack_growth_direction * stacking->stack_registers_size; } - for (i = 0; i < stacking->num_output_registers; i++) { - int j; - for (j = 0; j < stacking->register_offsets[i].width_bits/8; j++) { - if (stacking->register_offsets[i].offset == -1) - tmp_str_ptr += sprintf(tmp_str_ptr, "%02x", 0); - else if (stacking->register_offsets[i].offset == -2) - tmp_str_ptr += sprintf(tmp_str_ptr, "%02x", - ((uint8_t *)&new_stack_ptr)[j]); - else - tmp_str_ptr += sprintf(tmp_str_ptr, "%02x", - stack_data[stacking->register_offsets[i].offset + j]); - } + + *reg_list = calloc(stacking->num_output_registers, sizeof(struct rtos_reg)); + *num_regs = stacking->num_output_registers; + + for (int i = 0; i < stacking->num_output_registers; ++i) { + (*reg_list)[i].number = stacking->register_offsets[i].number; + (*reg_list)[i].size = stacking->register_offsets[i].width_bits; + + int offset = stacking->register_offsets[i].offset; + if (offset == -2) + buf_cpy(&new_stack_ptr, (*reg_list)[i].value, (*reg_list)[i].size); + else if (offset != -1) + buf_cpy(stack_data + offset, (*reg_list)[i].value, (*reg_list)[i].size); } + free(stack_data); /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */ return ERROR_OK; diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h index c9ac1e555..22de33fe2 100644 --- a/src/rtos/rtos.h +++ b/src/rtos/rtos.h @@ -59,19 +59,27 @@ struct rtos { void *rtos_specific_params; }; +struct rtos_reg { + uint32_t number; + uint32_t size; + uint8_t value[8]; +}; + struct rtos_type { const char *name; bool (*detect_rtos)(struct target *target); int (*create)(struct target *target); int (*smp_init)(struct target *target); int (*update_threads)(struct rtos *rtos); - int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); + int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); int (*get_symbol_list_to_lookup)(symbol_table_elem_t *symbol_list[]); int (*clean)(struct target *target); char * (*ps_command)(struct target *target); }; struct stack_register_offset { + unsigned short number; /* register number */ signed short offset; /* offset in bytes from stack head, or -1 to indicate * register is not stacked, or -2 to indicate this is the * stack pointer register */ @@ -100,9 +108,11 @@ int rtos_create(Jim_GetOptInfo *goi, struct target *target); int rtos_generic_stack_read(struct target *target, const struct rtos_register_stacking *stacking, int64_t stack_ptr, - char **hex_reg_list); + struct rtos_reg **reg_list, + int *num_regs); int rtos_try_next(struct target *target); int gdb_thread_packet(struct connection *connection, char const *packet, int packet_size); +int rtos_get_gdb_reg(struct connection *connection, int reg_num); int rtos_get_gdb_reg_list(struct connection *connection); int rtos_update_threads(struct target *target); void rtos_free_threadlist(struct rtos *rtos); diff --git a/src/rtos/rtos_chibios_stackings.c b/src/rtos/rtos_chibios_stackings.c index 3651c49a9..2887930bd 100644 --- a/src/rtos/rtos_chibios_stackings.c +++ b/src/rtos/rtos_chibios_stackings.c @@ -27,23 +27,23 @@ #include "target/armv7m.h" static const struct stack_register_offset rtos_chibios_arm_v7m_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { -1, 32 }, /* r0 */ - { -1, 32 }, /* r1 */ - { -1, 32 }, /* r2 */ - { -1, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { -1, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { -1, 32 }, /* lr */ - { 0x20, 32 }, /* pc */ - { -1, 32 }, /* xPSR */ + { ARMV7M_R0, -1, 32 }, /* r0 */ + { ARMV7M_R1, -1, 32 }, /* r1 */ + { ARMV7M_R2, -1, 32 }, /* r2 */ + { ARMV7M_R3, -1, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, -1, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, -1, 32 }, /* lr */ + { ARMV7M_PC, 0x20, 32 }, /* pc */ + { ARMV7M_xPSR, -1, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking = { @@ -55,23 +55,23 @@ const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking = { }; static const struct stack_register_offset rtos_chibios_arm_v7m_stack_offsets_w_fpu[ARMV7M_NUM_CORE_REGS] = { - { -1, 32 }, /* r0 */ - { -1, 32 }, /* r1 */ - { -1, 32 }, /* r2 */ - { -1, 32 }, /* r3 */ - { 0x40, 32 }, /* r4 */ - { 0x44, 32 }, /* r5 */ - { 0x48, 32 }, /* r6 */ - { 0x4c, 32 }, /* r7 */ - { 0x50, 32 }, /* r8 */ - { 0x54, 32 }, /* r9 */ - { 0x58, 32 }, /* r10 */ - { 0x5c, 32 }, /* r11 */ - { -1, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { -1, 32 }, /* lr */ - { 0x60, 32 }, /* pc */ - { -1, 32 }, /* xPSR */ + { ARMV7M_R0, -1, 32 }, /* r0 */ + { ARMV7M_R1, -1, 32 }, /* r1 */ + { ARMV7M_R2, -1, 32 }, /* r2 */ + { ARMV7M_R3, -1, 32 }, /* r3 */ + { ARMV7M_R4, 0x40, 32 }, /* r4 */ + { ARMV7M_R5, 0x44, 32 }, /* r5 */ + { ARMV7M_R6, 0x48, 32 }, /* r6 */ + { ARMV7M_R7, 0x4c, 32 }, /* r7 */ + { ARMV7M_R8, 0x50, 32 }, /* r8 */ + { ARMV7M_R9, 0x54, 32 }, /* r9 */ + { ARMV7M_R10, 0x58, 32 }, /* r10 */ + { ARMV7M_R11, 0x5c, 32 }, /* r11 */ + { ARMV7M_R12, -1, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, -1, 32 }, /* lr */ + { ARMV7M_PC, 0x60, 32 }, /* pc */ + { ARMV7M_xPSR, -1, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking_w_fpu = { diff --git a/src/rtos/rtos_ecos_stackings.c b/src/rtos/rtos_ecos_stackings.c index 43d97a605..ca98d9417 100644 --- a/src/rtos/rtos_ecos_stackings.c +++ b/src/rtos/rtos_ecos_stackings.c @@ -23,23 +23,23 @@ #include "target/armv7m.h" static const struct stack_register_offset rtos_eCos_Cortex_M3_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x0c, 32 }, /* r0 */ - { 0x10, 32 }, /* r1 */ - { 0x14, 32 }, /* r2 */ - { 0x18, 32 }, /* r3 */ - { 0x1c, 32 }, /* r4 */ - { 0x20, 32 }, /* r5 */ - { 0x24, 32 }, /* r6 */ - { 0x28, 32 }, /* r7 */ - { 0x2c, 32 }, /* r8 */ - { 0x30, 32 }, /* r9 */ - { 0x34, 32 }, /* r10 */ - { 0x38, 32 }, /* r11 */ - { 0x3c, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { -1, 32 }, /* lr */ - { 0x40, 32 }, /* pc */ - { -1, 32 }, /* xPSR */ + { ARMV7M_R0, 0x0c, 32 }, /* r0 */ + { ARMV7M_R1, 0x10, 32 }, /* r1 */ + { ARMV7M_R2, 0x14, 32 }, /* r2 */ + { ARMV7M_R3, 0x18, 32 }, /* r3 */ + { ARMV7M_R4, 0x1c, 32 }, /* r4 */ + { ARMV7M_R5, 0x20, 32 }, /* r5 */ + { ARMV7M_R6, 0x24, 32 }, /* r6 */ + { ARMV7M_R7, 0x28, 32 }, /* r7 */ + { ARMV7M_R8, 0x2c, 32 }, /* r8 */ + { ARMV7M_R9, 0x30, 32 }, /* r9 */ + { ARMV7M_R10, 0x34, 32 }, /* r10 */ + { ARMV7M_R11, 0x38, 32 }, /* r11 */ + { ARMV7M_R12, 0x3c, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, -1, 32 }, /* lr */ + { ARMV7M_PC, 0x40, 32 }, /* pc */ + { ARMV7M_xPSR, -1, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_eCos_Cortex_M3_stacking = { diff --git a/src/rtos/rtos_embkernel_stackings.c b/src/rtos/rtos_embkernel_stackings.c index 2a3062955..4ee79af93 100644 --- a/src/rtos/rtos_embkernel_stackings.c +++ b/src/rtos/rtos_embkernel_stackings.c @@ -25,23 +25,23 @@ #include "rtos_standard_stackings.h" static const struct stack_register_offset rtos_embkernel_Cortex_M_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x24, 32 }, /* r0 */ - { 0x28, 32 }, /* r1 */ - { 0x2c, 32 }, /* r2 */ - { 0x30, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x34, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x38, 32 }, /* lr */ - { 0x3c, 32 }, /* pc */ - { 0x40, 32 }, /* xPSR */ + { ARMV7M_R0, 0x24, 32 }, /* r0 */ + { ARMV7M_R1, 0x28, 32 }, /* r1 */ + { ARMV7M_R2, 0x2c, 32 }, /* r2 */ + { ARMV7M_R3, 0x30, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x34, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x38, 32 }, /* lr */ + { ARMV7M_PC, 0x3c, 32 }, /* pc */ + { ARMV7M_xPSR, 0x40, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_embkernel_Cortex_M_stacking = { diff --git a/src/rtos/rtos_mqx_stackings.c b/src/rtos/rtos_mqx_stackings.c index 5db2f8ec3..1a8bdfcc7 100644 --- a/src/rtos/rtos_mqx_stackings.c +++ b/src/rtos/rtos_mqx_stackings.c @@ -51,23 +51,23 @@ */ static const struct stack_register_offset rtos_mqx_arm_v7m_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x2C, 32 }, /* r0 */ - { 0x30, 32 }, /* r1 */ - { 0x34, 32 }, /* r2 */ - { 0x38, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0C, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1C, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { 0x3C, 32 }, /* r12 */ - { -2 , 32 }, /* sp */ - { 0x28, 32 }, /* lr */ - { 0x44, 32 }, /* pc */ - { 0x48, 32 }, /* xPSR */ + { ARMV7M_R0, 0x2C, 32 }, /* r0 */ + { ARMV7M_R1, 0x30, 32 }, /* r1 */ + { ARMV7M_R2, 0x34, 32 }, /* r2 */ + { ARMV7M_R3, 0x38, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0C, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1C, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x3C, 32 }, /* r12 */ + { ARMV7M_R13, -2 , 32 }, /* sp */ + { ARMV7M_R14, 0x28, 32 }, /* lr */ + { ARMV7M_PC, 0x44, 32 }, /* pc */ + { ARMV7M_xPSR, 0x48, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_mqx_arm_v7m_stacking = { diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c index 931cfc7ed..7b054cbbc 100644 --- a/src/rtos/rtos_standard_stackings.c +++ b/src/rtos/rtos_standard_stackings.c @@ -24,132 +24,132 @@ #include "target/armv7m.h" static const struct stack_register_offset rtos_standard_Cortex_M3_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x20, 32 }, /* r0 */ - { 0x24, 32 }, /* r1 */ - { 0x28, 32 }, /* r2 */ - { 0x2c, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x30, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x34, 32 }, /* lr */ - { 0x38, 32 }, /* pc */ - { 0x3c, 32 }, /* xPSR */ + { ARMV7M_R0, 0x20, 32 }, /* r0 */ + { ARMV7M_R1, 0x24, 32 }, /* r1 */ + { ARMV7M_R2, 0x28, 32 }, /* r2 */ + { ARMV7M_R3, 0x2c, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x30, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x34, 32 }, /* lr */ + { ARMV7M_PC, 0x38, 32 }, /* pc */ + { ARMV7M_xPSR, 0x3c, 32 }, /* xPSR */ }; static const struct stack_register_offset rtos_standard_Cortex_M4F_stack_offsets[] = { - { 0x24, 32 }, /* r0 */ - { 0x28, 32 }, /* r1 */ - { 0x2c, 32 }, /* r2 */ - { 0x30, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x34, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x38, 32 }, /* lr */ - { 0x3c, 32 }, /* pc */ - { 0x40, 32 }, /* xPSR */ + { ARMV7M_R0, 0x24, 32 }, /* r0 */ + { ARMV7M_R1, 0x28, 32 }, /* r1 */ + { ARMV7M_R2, 0x2c, 32 }, /* r2 */ + { ARMV7M_R3, 0x30, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x34, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x38, 32 }, /* lr */ + { ARMV7M_PC, 0x3c, 32 }, /* pc */ + { ARMV7M_xPSR, 0x40, 32 }, /* xPSR */ }; static const struct stack_register_offset rtos_standard_Cortex_M4F_FPU_stack_offsets[] = { - { 0x64, 32 }, /* r0 */ - { 0x68, 32 }, /* r1 */ - { 0x6c, 32 }, /* r2 */ - { 0x70, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x74, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x78, 32 }, /* lr */ - { 0x7c, 32 }, /* pc */ - { 0x80, 32 }, /* xPSR */ + { ARMV7M_R0, 0x64, 32 }, /* r0 */ + { ARMV7M_R1, 0x68, 32 }, /* r1 */ + { ARMV7M_R2, 0x6c, 32 }, /* r2 */ + { ARMV7M_R3, 0x70, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x74, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x78, 32 }, /* lr */ + { ARMV7M_PC, 0x7c, 32 }, /* pc */ + { ARMV7M_xPSR, 0x80, 32 }, /* xPSR */ }; static const struct stack_register_offset rtos_standard_Cortex_R4_stack_offsets[] = { - { 0x08, 32 }, /* r0 (a1) */ - { 0x0c, 32 }, /* r1 (a2) */ - { 0x10, 32 }, /* r2 (a3) */ - { 0x14, 32 }, /* r3 (a4) */ - { 0x18, 32 }, /* r4 (v1) */ - { 0x1c, 32 }, /* r5 (v2) */ - { 0x20, 32 }, /* r6 (v3) */ - { 0x24, 32 }, /* r7 (v4) */ - { 0x28, 32 }, /* r8 (a1) */ - { 0x2c, 32 }, /* r9 (sb) */ - { 0x30, 32 }, /* r10 (sl) */ - { 0x34, 32 }, /* r11 (fp) */ - { 0x38, 32 }, /* r12 (ip) */ - { -2, 32 }, /* sp */ - { 0x3c, 32 }, /* lr */ - { 0x40, 32 }, /* pc */ - { -1, 96 }, /* FPA1 */ - { -1, 96 }, /* FPA2 */ - { -1, 96 }, /* FPA3 */ - { -1, 96 }, /* FPA4 */ - { -1, 96 }, /* FPA5 */ - { -1, 96 }, /* FPA6 */ - { -1, 96 }, /* FPA7 */ - { -1, 96 }, /* FPA8 */ - { -1, 32 }, /* FPS */ - { 0x04, 32 }, /* CSPR */ + { 0, 0x08, 32 }, /* r0 (a1) */ + { 1, 0x0c, 32 }, /* r1 (a2) */ + { 2, 0x10, 32 }, /* r2 (a3) */ + { 3, 0x14, 32 }, /* r3 (a4) */ + { 4, 0x18, 32 }, /* r4 (v1) */ + { 5, 0x1c, 32 }, /* r5 (v2) */ + { 6, 0x20, 32 }, /* r6 (v3) */ + { 7, 0x24, 32 }, /* r7 (v4) */ + { 8, 0x28, 32 }, /* r8 (a1) */ + { 10, 0x2c, 32 }, /* r9 (sb) */ + { 11, 0x30, 32 }, /* r10 (sl) */ + { 12, 0x34, 32 }, /* r11 (fp) */ + { 13, 0x38, 32 }, /* r12 (ip) */ + { 14, -2, 32 }, /* sp */ + { 15, 0x3c, 32 }, /* lr */ + { 16, 0x40, 32 }, /* pc */ + { 17, -1, 96 }, /* FPA1 */ + { 18, -1, 96 }, /* FPA2 */ + { 19, -1, 96 }, /* FPA3 */ + { 20, -1, 96 }, /* FPA4 */ + { 21, -1, 96 }, /* FPA5 */ + { 22, -1, 96 }, /* FPA6 */ + { 23, -1, 96 }, /* FPA7 */ + { 24, -1, 96 }, /* FPA8 */ + { 25, -1, 32 }, /* FPS */ + { 26, 0x04, 32 }, /* CSPR */ }; static const struct stack_register_offset rtos_standard_NDS32_N1068_stack_offsets[] = { - { 0x88, 32 }, /* R0 */ - { 0x8C, 32 }, /* R1 */ - { 0x14, 32 }, /* R2 */ - { 0x18, 32 }, /* R3 */ - { 0x1C, 32 }, /* R4 */ - { 0x20, 32 }, /* R5 */ - { 0x24, 32 }, /* R6 */ - { 0x28, 32 }, /* R7 */ - { 0x2C, 32 }, /* R8 */ - { 0x30, 32 }, /* R9 */ - { 0x34, 32 }, /* R10 */ - { 0x38, 32 }, /* R11 */ - { 0x3C, 32 }, /* R12 */ - { 0x40, 32 }, /* R13 */ - { 0x44, 32 }, /* R14 */ - { 0x48, 32 }, /* R15 */ - { 0x4C, 32 }, /* R16 */ - { 0x50, 32 }, /* R17 */ - { 0x54, 32 }, /* R18 */ - { 0x58, 32 }, /* R19 */ - { 0x5C, 32 }, /* R20 */ - { 0x60, 32 }, /* R21 */ - { 0x64, 32 }, /* R22 */ - { 0x68, 32 }, /* R23 */ - { 0x6C, 32 }, /* R24 */ - { 0x70, 32 }, /* R25 */ - { 0x74, 32 }, /* R26 */ - { 0x78, 32 }, /* R27 */ - { 0x7C, 32 }, /* R28 */ - { 0x80, 32 }, /* R29 */ - { 0x84, 32 }, /* R30 (LP) */ - { 0x00, 32 }, /* R31 (SP) */ - { 0x04, 32 }, /* PSW */ - { 0x08, 32 }, /* IPC */ - { 0x0C, 32 }, /* IPSW */ - { 0x10, 32 }, /* IFC_LP */ + { 0, 0x88, 32 }, /* R0 */ + { 1, 0x8C, 32 }, /* R1 */ + { 2, 0x14, 32 }, /* R2 */ + { 3, 0x18, 32 }, /* R3 */ + { 4, 0x1C, 32 }, /* R4 */ + { 5, 0x20, 32 }, /* R5 */ + { 6, 0x24, 32 }, /* R6 */ + { 7, 0x28, 32 }, /* R7 */ + { 8, 0x2C, 32 }, /* R8 */ + { 9, 0x30, 32 }, /* R9 */ + { 10, 0x34, 32 }, /* R10 */ + { 11, 0x38, 32 }, /* R11 */ + { 12, 0x3C, 32 }, /* R12 */ + { 13, 0x40, 32 }, /* R13 */ + { 14, 0x44, 32 }, /* R14 */ + { 15, 0x48, 32 }, /* R15 */ + { 16, 0x4C, 32 }, /* R16 */ + { 17, 0x50, 32 }, /* R17 */ + { 18, 0x54, 32 }, /* R18 */ + { 19, 0x58, 32 }, /* R19 */ + { 20, 0x5C, 32 }, /* R20 */ + { 21, 0x60, 32 }, /* R21 */ + { 22, 0x64, 32 }, /* R22 */ + { 23, 0x68, 32 }, /* R23 */ + { 24, 0x6C, 32 }, /* R24 */ + { 25, 0x70, 32 }, /* R25 */ + { 26, 0x74, 32 }, /* R26 */ + { 27, 0x78, 32 }, /* R27 */ + { 28, 0x7C, 32 }, /* R28 */ + { 29, 0x80, 32 }, /* R29 */ + { 30, 0x84, 32 }, /* R30 (LP) */ + { 31, 0x00, 32 }, /* R31 (SP) */ + { 32, 0x04, 32 }, /* PSW */ + { 33, 0x08, 32 }, /* IPC */ + { 34, 0x0C, 32 }, /* IPSW */ + { 35, 0x10, 32 }, /* IFC_LP */ }; static int64_t rtos_generic_stack_align(struct target *target, diff --git a/src/rtos/rtos_ucos_iii_stackings.c b/src/rtos/rtos_ucos_iii_stackings.c index c260b7f80..d093563ba 100644 --- a/src/rtos/rtos_ucos_iii_stackings.c +++ b/src/rtos/rtos_ucos_iii_stackings.c @@ -24,25 +24,47 @@ #include #include #include +#include static const struct stack_register_offset rtos_uCOS_III_Cortex_M_stack_offsets[] = { - { 0x20, 32 }, /* r0 */ - { 0x24, 32 }, /* r1 */ - { 0x28, 32 }, /* r2 */ - { 0x2c, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x30, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x34, 32 }, /* lr */ - { 0x38, 32 }, /* pc */ - { 0x3c, 32 }, /* xPSR */ + { ARMV7M_R0, 0x20, 32 }, /* r0 */ + { ARMV7M_R1, 0x24, 32 }, /* r1 */ + { ARMV7M_R2, 0x28, 32 }, /* r2 */ + { ARMV7M_R3, 0x2c, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x30, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x34, 32 }, /* lr */ + { ARMV7M_PC, 0x38, 32 }, /* pc */ + { ARMV7M_xPSR, 0x3c, 32 }, /* xPSR */ +}; + +static const struct stack_register_offset rtos_uCOS_III_eSi_RISC_stack_offsets[] = { + { ESIRISC_SP, -2, 32 }, /* sp */ + { ESIRISC_RA, 0x48, 32 }, /* ra */ + { ESIRISC_R2, 0x44, 32 }, /* r2 */ + { ESIRISC_R3, 0x40, 32 }, /* r3 */ + { ESIRISC_R4, 0x3c, 32 }, /* r4 */ + { ESIRISC_R5, 0x38, 32 }, /* r5 */ + { ESIRISC_R6, 0x34, 32 }, /* r6 */ + { ESIRISC_R7, 0x30, 32 }, /* r7 */ + { ESIRISC_R8, 0x2c, 32 }, /* r8 */ + { ESIRISC_R9, 0x28, 32 }, /* r9 */ + { ESIRISC_R10, 0x24, 32 }, /* r10 */ + { ESIRISC_R11, 0x20, 32 }, /* r11 */ + { ESIRISC_R12, 0x1c, 32 }, /* r12 */ + { ESIRISC_R13, 0x18, 32 }, /* r13 */ + { ESIRISC_R14, 0x14, 32 }, /* r14 */ + { ESIRISC_R15, 0x10, 32 }, /* r15 */ + { ESIRISC_PC, 0x04, 32 }, /* PC */ + { ESIRISC_CAS, 0x08, 32 }, /* CAS */ }; const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking = { @@ -52,3 +74,11 @@ const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking = { rtos_generic_stack_align8, /* stack_alignment */ rtos_uCOS_III_Cortex_M_stack_offsets /* register_offsets */ }; + +const struct rtos_register_stacking rtos_uCOS_III_eSi_RISC_stacking = { + 0x4c, /* stack_registers_size */ + -1, /* stack_growth_direction */ + ARRAY_SIZE(rtos_uCOS_III_eSi_RISC_stack_offsets), /* num_output_registers */ + NULL, /* stack_alignment */ + rtos_uCOS_III_eSi_RISC_stack_offsets /* register_offsets */ +}; diff --git a/src/rtos/rtos_ucos_iii_stackings.h b/src/rtos/rtos_ucos_iii_stackings.h index f4703da37..a9398138b 100644 --- a/src/rtos/rtos_ucos_iii_stackings.h +++ b/src/rtos/rtos_ucos_iii_stackings.h @@ -26,5 +26,6 @@ #include extern const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking; +extern const struct rtos_register_stacking rtos_uCOS_III_eSi_RISC_stacking; #endif /* OPENOCD_RTOS_RTOS_UCOS_III_STACKINGS_H */ diff --git a/src/rtos/uCOS-III.c b/src/rtos/uCOS-III.c index 8e63ea4e6..304d07c59 100644 --- a/src/rtos/uCOS-III.c +++ b/src/rtos/uCOS-III.c @@ -68,6 +68,20 @@ static const struct uCOS_III_params uCOS_III_params_list[] = { &rtos_uCOS_III_Cortex_M_stacking, /* stacking_info */ 0, /* num_threads */ }, + { + "esirisc", /* target_name */ + sizeof(uint32_t), /* pointer_width */ + 0, /* thread_stack_offset */ + 0, /* thread_name_offset */ + 0, /* thread_state_offset */ + 0, /* thread_priority_offset */ + 0, /* thread_prev_offset */ + 0, /* thread_next_offset */ + false, /* thread_offsets_updated */ + 1, /* threadid_start */ + &rtos_uCOS_III_eSi_RISC_stacking, /* stacking_info */ + 0, /* num_threads */ + }, }; static const char * const uCOS_III_symbol_list[] = { @@ -286,6 +300,11 @@ static int uCOS_III_update_threads(struct rtos *rtos) struct uCOS_III_params *params = rtos->rtos_specific_params; int retval; + if (rtos->symbols == NULL) { + LOG_ERROR("uCOS-III: symbol list not loaded"); + return ERROR_FAIL; + } + /* free previous thread details */ rtos_free_threadlist(rtos); @@ -454,7 +473,8 @@ static int uCOS_III_update_threads(struct rtos *rtos) return ERROR_OK; } -static int uCOS_III_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, char **hex_reg_list) +static int uCOS_III_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, + struct rtos_reg **reg_list, int *num_regs) { struct uCOS_III_params *params = rtos->rtos_specific_params; int retval; @@ -484,7 +504,8 @@ static int uCOS_III_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, return rtos_generic_stack_read(rtos->target, params->stacking_info, stack_address, - hex_reg_list); + reg_list, + num_regs); } static int uCOS_III_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 3ea324260..3548f166c 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -1179,8 +1179,11 @@ static int gdb_get_registers_packet(struct connection *connection, if (retval != ERROR_OK) return gdb_error(connection, retval); - for (i = 0; i < reg_list_size; i++) + for (i = 0; i < reg_list_size; i++) { + if (reg_list[i] == NULL || reg_list[i]->exist == false) + continue; reg_packet_size += DIV_ROUND_UP(reg_list[i]->size, 8) * 2; + } assert(reg_packet_size > 0); @@ -1191,6 +1194,8 @@ static int gdb_get_registers_packet(struct connection *connection, reg_packet_p = reg_packet; for (i = 0; i < reg_list_size; i++) { + if (reg_list[i] == NULL || reg_list[i]->exist == false) + continue; if (!reg_list[i]->valid) { retval = reg_list[i]->type->get(reg_list[i]); if (retval != ERROR_OK && gdb_report_register_access_error) { @@ -1296,6 +1301,9 @@ static int gdb_get_register_packet(struct connection *connection, LOG_DEBUG("-"); #endif + if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num))) + return ERROR_OK; + retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) @@ -2188,6 +2196,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o int retval = ERROR_OK; struct reg **reg_list = NULL; int reg_list_size; + char const *architecture; char const **features = NULL; char const **arch_defined_types = NULL; int feature_list_size = 0; @@ -2229,6 +2238,12 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o "\n" "\n"); + /* generate architecture element if supported by target */ + architecture = target_get_gdb_arch(target); + if (architecture != NULL) + xml_printf(&retval, &tdesc, &pos, &size, + "%s\n", architecture); + /* generate target description according to register list */ if (features != NULL) { while (features[current_feature]) { @@ -2378,6 +2393,8 @@ static int gdb_target_description_supported(struct target *target, int *supporte char const **features = NULL; int feature_list_size = 0; + char const *architecture = target_get_gdb_arch(target); + retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) { @@ -2399,7 +2416,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte } if (supported) { - if (feature_list_size) + if (architecture || feature_list_size) *supported = 1; else *supported = 0; @@ -3377,6 +3394,8 @@ static int gdb_target_start(struct target *target, const char *port) if (NULL == gdb_service) return -ENOMEM; + LOG_DEBUG("starting gdb server for %s on %s", target_name(target), port); + gdb_service->target = target; gdb_service->core[0] = -1; gdb_service->core[1] = -1; @@ -3402,16 +3421,36 @@ static int gdb_target_start(struct target *target, const char *port) static int gdb_target_add_one(struct target *target) { + /* one gdb instance per smp list */ + if ((target->smp) && (target->gdb_service)) + return ERROR_OK; + + /* skip targets that cannot handle a gdb connections (e.g. mem_ap) */ + if (!target_supports_gdb_connection(target)) { + LOG_DEBUG("skip gdb server for target %s", target_name(target)); + return ERROR_OK; + } + + if (target->gdb_port_override) { + if (strcmp(target->gdb_port_override, "disabled") == 0) { + LOG_INFO("gdb port disabled"); + return ERROR_OK; + } + return gdb_target_start(target, target->gdb_port_override); + } + if (strcmp(gdb_port, "disabled") == 0) { LOG_INFO("gdb port disabled"); return ERROR_OK; } - /* one gdb instance per smp list */ - if ((target->smp) && (target->gdb_service)) - return ERROR_OK; int retval = gdb_target_start(target, gdb_port_next); if (retval == ERROR_OK) { + /* save the port number so can be queried with + * $target_name cget -gdb-port + */ + target->gdb_port_override = strdup(gdb_port_next); + long portnumber; /* If we can parse the port number * then we increment the port number for the next target. @@ -3436,11 +3475,6 @@ static int gdb_target_add_one(struct target *target) int gdb_target_add_all(struct target *target) { - if (strcmp(gdb_port, "disabled") == 0) { - LOG_INFO("gdb server disabled"); - return ERROR_OK; - } - if (NULL == target) { LOG_WARNING("gdb services need one or more targets defined"); return ERROR_OK; diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index 3cb63a275..0676c883b 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -157,7 +157,7 @@ static int tcl_new_connection(struct connection *connection) connection->priv = tclc; - struct target *target = get_current_target(connection->cmd_ctx); + struct target *target = get_current_target_or_null(connection->cmd_ctx); if (target != NULL) tclc->tc_laststate = target->state; diff --git a/src/target/Makefile.am b/src/target/Makefile.am index b1119e7df..8e9fcb27e 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -23,6 +23,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(NDS32_SRC) \ $(STM8_SRC) \ $(INTEL_IA32_SRC) \ + $(ESIRISC_SRC) \ %D%/avrt.c \ %D%/dsp563xx.c \ %D%/dsp563xx_once.c \ @@ -74,8 +75,10 @@ ARMV7_SRC = \ %D%/armv7m_trace.c \ %D%/cortex_m.c \ %D%/armv7a.c \ + %D%/armv7a_mmu.c \ %D%/cortex_a.c \ - %D%/ls1_sap.c + %D%/ls1_sap.c \ + %D%/mem_ap.c ARMV8_SRC = \ %D%/armv8_dpm.c \ @@ -138,6 +141,10 @@ INTEL_IA32_SRC = \ %D%/lakemont.c \ %D%/x86_32_common.c +ESIRISC_SRC = \ + %D%/esirisc.c \ + %D%/esirisc_jtag.c + %C%_libtarget_la_SOURCES += \ %D%/algorithm.h \ %D%/arm.h \ @@ -146,6 +153,7 @@ INTEL_IA32_SRC = \ %D%/arm_adi_v5.h \ %D%/armv7a_cache.h \ %D%/armv7a_cache_l2x.h \ + %D%/armv7a_mmu.h \ %D%/arm_disassembler.h \ %D%/arm_opcodes.h \ %D%/arm_simulator.h \ @@ -217,7 +225,10 @@ INTEL_IA32_SRC = \ %D%/stm8.h \ %D%/lakemont.h \ %D%/x86_32_common.h \ - %D%/arm_cti.h + %D%/arm_cti.h \ + %D%/esirisc.h \ + %D%/esirisc_jtag.h \ + %D%/esirisc_regs.h include %D%/openrisc/Makefile.am include %D%/riscv/Makefile.am diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 302ea7891..03e642bfe 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -652,6 +652,15 @@ int dap_dp_init(struct adiv5_dap *dap) dap_invalidate_cache(dap); + /* + * Early initialize dap->dp_ctrl_stat. + * In jtag mode only, if the following atomic reads fail and set the + * sticky error, it will trigger the clearing of the sticky. Without this + * initialization system and debug power would be disabled while clearing + * the sticky error bit. + */ + dap->dp_ctrl_stat = CDBGPWRUPREQ | CSYSPWRUPREQ; + for (size_t i = 0; i < 30; i++) { /* DP initialization */ @@ -660,7 +669,18 @@ int dap_dp_init(struct adiv5_dap *dap) break; } - retval = dap_queue_dp_write(dap, DP_CTRL_STAT, SSTICKYERR); + /* + * This write operation clears the sticky error bit in jtag mode only and + * is ignored in swd mode. It also powers-up system and debug domains in + * both jtag and swd modes, if not done before. + * Actually we do not need to clear the sticky error here because it has + * been already cleared (if it was set) in the previous atomic read. This + * write could be removed, but this initial part of dap_dp_init() is the + * result of years of fine tuning and there are strong concerns about any + * unnecessary code change. It doesn't harm, so let's keep it here and + * preserve the historical sequence of read/write operations! + */ + retval = dap_queue_dp_write(dap, DP_CTRL_STAT, dap->dp_ctrl_stat | SSTICKYERR); if (retval != ERROR_OK) return retval; @@ -668,7 +688,6 @@ int dap_dp_init(struct adiv5_dap *dap) if (retval != ERROR_OK) return retval; - dap->dp_ctrl_stat = CDBGPWRUPREQ | CSYSPWRUPREQ; retval = dap_queue_dp_write(dap, DP_CTRL_STAT, dap->dp_ctrl_stat); if (retval != ERROR_OK) return retval; @@ -793,7 +812,7 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a int ap_num; /* Maximum AP number is 255 since the SELECT register is 8 bits */ - for (ap_num = 0; ap_num <= 255; ap_num++) { + for (ap_num = 0; ap_num <= DP_APSEL_MAX; ap_num++) { /* read the IDR register of the Access Port */ uint32_t id_val = 0; @@ -1429,7 +1448,7 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) pc = (struct adiv5_private_config *)target->private_config; if (pc == NULL) { pc = calloc(1, sizeof(struct adiv5_private_config)); - pc->ap_num = -1; + pc->ap_num = DP_APSEL_INVALID; target->private_config = pc; } @@ -1498,6 +1517,10 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) e = Jim_GetOpt_Wide(goi, &ap_num); if (e != JIM_OK) return e; + if (ap_num < 0 || ap_num > DP_APSEL_MAX) { + Jim_SetResultString(goi->interp, "Invalid AP number!", -1); + return JIM_ERR; + } pc->ap_num = ap_num; } else { if (goi->argc != 0) { @@ -1507,11 +1530,11 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) return JIM_ERR; } - if (pc->ap_num < 0) { + if (pc->ap_num == DP_APSEL_INVALID) { Jim_SetResultString(goi->interp, "AP number not configured", -1); return JIM_ERR; } - Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, (int)pc->ap_num)); + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, pc->ap_num)); } break; } @@ -1543,7 +1566,7 @@ COMMAND_HANDLER(handle_dap_info_command) break; case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1566,7 +1589,7 @@ COMMAND_HANDLER(dap_baseaddr_command) case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1625,7 +1648,7 @@ COMMAND_HANDLER(dap_apsel_command) case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1691,7 +1714,7 @@ COMMAND_HANDLER(dap_apid_command) case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1722,7 +1745,7 @@ COMMAND_HANDLER(dap_apreg_command) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; ap = dap_ap(dap, apsel); @@ -1734,8 +1757,10 @@ COMMAND_HANDLER(dap_apreg_command) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); switch (reg) { case MEM_AP_REG_CSW: - ap->csw_default = 0; /* invalid, force write */ - retval = mem_ap_setup_csw(ap, value); + ap->csw_value = 0; /* invalid, in case write fails */ + retval = dap_queue_ap_write(ap, reg, value); + if (retval == ERROR_OK) + ap->csw_value = value; break; case MEM_AP_REG_TAR: ap->tar_valid = false; /* invalid, force write */ diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index 883ac8b5d..a340b76f0 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -136,6 +136,9 @@ #define DP_SELECT_DPBANK 0x0000000F #define DP_SELECT_INVALID 0x00FFFF00 /* Reserved bits one */ +#define DP_APSEL_MAX (255) +#define DP_APSEL_INVALID (-1) + /** * This represents an ARM Debug Interface (v5) Access Port (AP). * Most common is a MEM-AP, for memory access. diff --git a/src/target/arm_cti.c b/src/target/arm_cti.c index 0d117e76d..dcaf21e50 100644 --- a/src/target/arm_cti.c +++ b/src/target/arm_cti.c @@ -431,6 +431,10 @@ static int cti_configure(Jim_GetOptInfo *goi, struct arm_cti_object *cti) e = Jim_GetOpt_Wide(goi, &w); if (e != JIM_OK) return e; + if (w < 0 || w > DP_APSEL_MAX) { + Jim_SetResultString(goi->interp, "-ap-num is invalid", -1); + return JIM_ERR; + } cti->ap_num = (uint32_t)w; } } diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c index 3be4d7199..3adb4ed26 100644 --- a/src/target/arm_dap.c +++ b/src/target/arm_dap.c @@ -48,7 +48,7 @@ static void dap_instance_init(struct adiv5_dap *dap) { int i; /* Set up with safe defaults */ - for (i = 0; i <= 255; i++) { + for (i = 0; i <= DP_APSEL_MAX; i++) { dap->ap[i].dap = dap; dap->ap[i].ap_num = i; /* memaccess_tck max is 255 */ @@ -319,7 +319,7 @@ COMMAND_HANDLER(handle_dap_info_command) break; case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c index f9b30c187..8b9957033 100644 --- a/src/target/arm_dpm.c +++ b/src/target/arm_dpm.c @@ -108,7 +108,7 @@ static int dpm_mcr(struct target *target, int cpnum, /* Toggles between recorded core mode (USR, SVC, etc) and a temporary one. * Routines *must* restore the original mode before returning!! */ -int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) +int arm_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) { int retval; uint32_t cpsr; @@ -168,7 +168,7 @@ static int dpm_read_reg_u64(struct arm_dpm *dpm, struct reg *r, unsigned regnum) } /* just read the register -- rely on the core mode being right */ -static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) +int arm_dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) { uint32_t value; int retval; @@ -352,7 +352,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) for (unsigned i = 0; i < 2; i++) { r = arm->core_cache->reg_list + i; if (!r->valid) { - retval = dpm_read_reg(dpm, r, i); + retval = arm_dpm_read_reg(dpm, r, i); if (retval != ERROR_OK) goto fail; } @@ -372,7 +372,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) if (r->valid) continue; - retval = dpm_read_reg(dpm, r, i); + retval = arm_dpm_read_reg(dpm, r, i); if (retval != ERROR_OK) goto fail; } @@ -543,7 +543,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) /* REVISIT error checks */ if (tmode != ARM_MODE_ANY) { - retval = dpm_modeswitch(dpm, tmode); + retval = arm_dpm_modeswitch(dpm, tmode); if (retval != ERROR_OK) goto done; } @@ -564,7 +564,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) * or it's dirty. Must write PC to ensure the return address is * defined, and must not write it before CPSR. */ - retval = dpm_modeswitch(dpm, ARM_MODE_ANY); + retval = arm_dpm_modeswitch(dpm, ARM_MODE_ANY); if (retval != ERROR_OK) goto done; arm->cpsr->dirty = false; @@ -671,18 +671,18 @@ static int arm_dpm_read_core_reg(struct target *target, struct reg *r, return retval; if (mode != ARM_MODE_ANY) { - retval = dpm_modeswitch(dpm, mode); + retval = arm_dpm_modeswitch(dpm, mode); if (retval != ERROR_OK) goto fail; } - retval = dpm_read_reg(dpm, r, regnum); + retval = arm_dpm_read_reg(dpm, r, regnum); if (retval != ERROR_OK) goto fail; /* always clean up, regardless of error */ if (mode != ARM_MODE_ANY) - /* (void) */ dpm_modeswitch(dpm, ARM_MODE_ANY); + /* (void) */ arm_dpm_modeswitch(dpm, ARM_MODE_ANY); fail: /* (void) */ dpm->finish(dpm); @@ -715,7 +715,7 @@ static int arm_dpm_write_core_reg(struct target *target, struct reg *r, return retval; if (mode != ARM_MODE_ANY) { - retval = dpm_modeswitch(dpm, mode); + retval = arm_dpm_modeswitch(dpm, mode); if (retval != ERROR_OK) goto fail; } @@ -724,7 +724,7 @@ static int arm_dpm_write_core_reg(struct target *target, struct reg *r, /* always clean up, regardless of error */ if (mode != ARM_MODE_ANY) - /* (void) */ dpm_modeswitch(dpm, ARM_MODE_ANY); + /* (void) */ arm_dpm_modeswitch(dpm, ARM_MODE_ANY); fail: /* (void) */ dpm->finish(dpm); @@ -773,9 +773,9 @@ static int arm_dpm_full_context(struct target *target) * in FIQ mode we need to patch mode. */ if (mode != ARM_MODE_ANY) - retval = dpm_modeswitch(dpm, mode); + retval = arm_dpm_modeswitch(dpm, mode); else - retval = dpm_modeswitch(dpm, ARM_MODE_USR); + retval = arm_dpm_modeswitch(dpm, ARM_MODE_USR); if (retval != ERROR_OK) goto done; @@ -784,7 +784,7 @@ static int arm_dpm_full_context(struct target *target) continue; /* CPSR was read, so "R16" must mean SPSR */ - retval = dpm_read_reg(dpm, + retval = arm_dpm_read_reg(dpm, &cache->reg_list[i], (r->num == 16) ? 17 : r->num); if (retval != ERROR_OK) @@ -793,7 +793,7 @@ static int arm_dpm_full_context(struct target *target) } while (did_read); - retval = dpm_modeswitch(dpm, ARM_MODE_ANY); + retval = arm_dpm_modeswitch(dpm, ARM_MODE_ANY); /* (void) */ dpm->finish(dpm); done: return retval; diff --git a/src/target/arm_dpm.h b/src/target/arm_dpm.h index f8d124813..d05c66c43 100644 --- a/src/target/arm_dpm.h +++ b/src/target/arm_dpm.h @@ -152,8 +152,9 @@ struct arm_dpm { int arm_dpm_setup(struct arm_dpm *dpm); int arm_dpm_initialize(struct arm_dpm *dpm); +int arm_dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum); int arm_dpm_read_current_registers(struct arm_dpm *); -int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode); +int arm_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode); int arm_dpm_write_dirty_registers(struct arm_dpm *, bool bpwp); diff --git a/src/target/armv7a.c b/src/target/armv7a.c index eecfa7097..f9594c9c3 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -24,6 +24,7 @@ #include #include "armv7a.h" +#include "armv7a_mmu.h" #include "arm_disassembler.h" #include "register.h" @@ -188,174 +189,6 @@ done: return retval; } -/* method adapted to Cortex-A : reused ARM v4 v5 method */ -int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) -{ - uint32_t first_lvl_descriptor = 0x0; - uint32_t second_lvl_descriptor = 0x0; - int retval; - struct armv7a_common *armv7a = target_to_armv7a(target); - uint32_t ttbidx = 0; /* default to ttbr0 */ - uint32_t ttb_mask; - uint32_t va_mask; - uint32_t ttb; - - if (target->state != TARGET_HALTED) - LOG_INFO("target not halted, using cached values for translation table!"); - - /* if va is above the range handled by ttbr0, select ttbr1 */ - if (va > armv7a->armv7a_mmu.ttbr_range[0]) { - /* select ttb 1 */ - ttbidx = 1; - } - - ttb = armv7a->armv7a_mmu.ttbr[ttbidx]; - ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx]; - va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx]; - - LOG_DEBUG("ttb_mask %" PRIx32 " va_mask %" PRIx32 " ttbidx %i", - ttb_mask, va_mask, ttbidx); - retval = armv7a->armv7a_mmu.read_physical_memory(target, - (ttb & ttb_mask) | ((va & va_mask) >> 18), - 4, 1, (uint8_t *)&first_lvl_descriptor); - if (retval != ERROR_OK) - return retval; - first_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) - &first_lvl_descriptor); - /* reuse armv4_5 piece of code, specific armv7a changes may come later */ - LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor); - - if ((first_lvl_descriptor & 0x3) == 0) { - LOG_ERROR("Address translation failure"); - return ERROR_TARGET_TRANSLATION_FAULT; - } - - - if ((first_lvl_descriptor & 0x40002) == 2) { - /* section descriptor */ - *val = (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); - return ERROR_OK; - } else if ((first_lvl_descriptor & 0x40002) == 0x40002) { - /* supersection descriptor */ - if (first_lvl_descriptor & 0x00f001e0) { - LOG_ERROR("Physical address does not fit into 32 bits"); - return ERROR_TARGET_TRANSLATION_FAULT; - } - *val = (first_lvl_descriptor & 0xff000000) | (va & 0x00ffffff); - return ERROR_OK; - } - - /* page table */ - retval = armv7a->armv7a_mmu.read_physical_memory(target, - (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), - 4, 1, (uint8_t *)&second_lvl_descriptor); - if (retval != ERROR_OK) - return retval; - - second_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) - &second_lvl_descriptor); - - LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor); - - if ((second_lvl_descriptor & 0x3) == 0) { - LOG_ERROR("Address translation failure"); - return ERROR_TARGET_TRANSLATION_FAULT; - } - - if ((second_lvl_descriptor & 0x3) == 1) { - /* large page descriptor */ - *val = (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); - } else { - /* small page descriptor */ - *val = (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); - } - - return ERROR_OK; -} - -/* V7 method VA TO PA */ -int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, - uint32_t *val, int meminfo) -{ - int retval = ERROR_FAIL; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct arm_dpm *dpm = armv7a->arm.dpm; - uint32_t virt = va & ~0xfff; - uint32_t NOS, NS, INNER, OUTER; - *val = 0xdeadbeef; - retval = dpm->prepare(dpm); - if (retval != ERROR_OK) - goto done; - /* mmu must be enable in order to get a correct translation - * use VA to PA CP15 register for conversion */ - retval = dpm->instr_write_data_r0(dpm, - ARMV4_5_MCR(15, 0, 0, 7, 8, 0), - virt); - if (retval != ERROR_OK) - goto done; - retval = dpm->instr_read_data_r0(dpm, - ARMV4_5_MRC(15, 0, 0, 7, 4, 0), - val); - /* decode memory attribute */ - NOS = (*val >> 10) & 1; /* Not Outer shareable */ - NS = (*val >> 9) & 1; /* Non secure */ - INNER = (*val >> 4) & 0x7; - OUTER = (*val >> 2) & 0x3; - - if (retval != ERROR_OK) - goto done; - *val = (*val & ~0xfff) + (va & 0xfff); - if (*val == va) - LOG_WARNING("virt = phys : MMU disable !!"); - if (meminfo) { - LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured", - va, *val, - NOS == 1 ? "not" : " ", - NS == 1 ? "not" : ""); - switch (OUTER) { - case 0: - LOG_INFO("outer: Non-Cacheable"); - break; - case 1: - LOG_INFO("outer: Write-Back, Write-Allocate"); - break; - case 2: - LOG_INFO("outer: Write-Through, No Write-Allocate"); - break; - case 3: - LOG_INFO("outer: Write-Back, no Write-Allocate"); - break; - } - switch (INNER) { - case 0: - LOG_INFO("inner: Non-Cacheable"); - break; - case 1: - LOG_INFO("inner: Strongly-ordered"); - break; - case 3: - LOG_INFO("inner: Device"); - break; - case 5: - LOG_INFO("inner: Write-Back, Write-Allocate"); - break; - case 6: - LOG_INFO("inner: Write-Through"); - break; - case 7: - LOG_INFO("inner: Write-Back, no Write-Allocate"); - break; - default: - LOG_INFO("inner: %" PRIx32 " ???", INNER); - } - } - -done: - dpm->finish(dpm); - - return retval; -} - /* FIXME: remove it */ static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way) { diff --git a/src/target/armv7a.h b/src/target/armv7a.h index 57779c61a..1e88c98cf 100644 --- a/src/target/armv7a.h +++ b/src/target/armv7a.h @@ -106,8 +106,6 @@ struct armv7a_common { struct arm_dpm dpm; uint32_t debug_base; struct adiv5_ap *debug_ap; - struct adiv5_ap *memory_ap; - bool memory_ap_available; /* mdir */ uint8_t multi_processor_system; uint8_t cluster_id; @@ -188,9 +186,6 @@ static inline bool is_armv7a(struct armv7a_common *armv7a) int armv7a_arch_state(struct target *target); int armv7a_identify_cache(struct target *target); int armv7a_init_arch_info(struct target *target, struct armv7a_common *armv7a); -int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, - uint32_t *val, int meminfo); -int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val); int armv7a_handle_cache_info_command(struct command_context *cmd_ctx, struct armv7a_cache_common *armv7a_cache); diff --git a/src/target/armv7a_mmu.c b/src/target/armv7a_mmu.c new file mode 100644 index 000000000..eed73ee58 --- /dev/null +++ b/src/target/armv7a_mmu.c @@ -0,0 +1,456 @@ +/*************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * matthias.welwarsky@sysgo.com * + * * + * Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "jtag/interface.h" +#include "arm.h" +#include "armv7a.h" +#include "armv7a_mmu.h" +#include "arm_opcodes.h" +#include "cortex_a.h" + +#define SCTLR_BIT_AFE (1 << 29) + +/* method adapted to Cortex-A : reused ARM v4 v5 method */ +int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) +{ + uint32_t first_lvl_descriptor = 0x0; + uint32_t second_lvl_descriptor = 0x0; + int retval; + struct armv7a_common *armv7a = target_to_armv7a(target); + uint32_t ttbidx = 0; /* default to ttbr0 */ + uint32_t ttb_mask; + uint32_t va_mask; + uint32_t ttb; + + if (target->state != TARGET_HALTED) + LOG_INFO("target not halted, using cached values for translation table!"); + + /* if va is above the range handled by ttbr0, select ttbr1 */ + if (va > armv7a->armv7a_mmu.ttbr_range[0]) { + /* select ttb 1 */ + ttbidx = 1; + } + + ttb = armv7a->armv7a_mmu.ttbr[ttbidx]; + ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx]; + va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx]; + + LOG_DEBUG("ttb_mask %" PRIx32 " va_mask %" PRIx32 " ttbidx %i", + ttb_mask, va_mask, ttbidx); + retval = armv7a->armv7a_mmu.read_physical_memory(target, + (ttb & ttb_mask) | ((va & va_mask) >> 18), + 4, 1, (uint8_t *)&first_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + first_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) + &first_lvl_descriptor); + /* reuse armv4_5 piece of code, specific armv7a changes may come later */ + LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor); + + if ((first_lvl_descriptor & 0x3) == 0) { + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if ((first_lvl_descriptor & 0x40002) == 2) { + /* section descriptor */ + *val = (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); + return ERROR_OK; + } else if ((first_lvl_descriptor & 0x40002) == 0x40002) { + /* supersection descriptor */ + if (first_lvl_descriptor & 0x00f001e0) { + LOG_ERROR("Physical address does not fit into 32 bits"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + *val = (first_lvl_descriptor & 0xff000000) | (va & 0x00ffffff); + return ERROR_OK; + } + + /* page table */ + retval = armv7a->armv7a_mmu.read_physical_memory(target, + (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), + 4, 1, (uint8_t *)&second_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + + second_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) + &second_lvl_descriptor); + + LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor); + + if ((second_lvl_descriptor & 0x3) == 0) { + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if ((second_lvl_descriptor & 0x3) == 1) { + /* large page descriptor */ + *val = (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); + } else { + /* small page descriptor */ + *val = (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); + } + + return ERROR_OK; +} + +/* V7 method VA TO PA */ +int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, + uint32_t *val, int meminfo) +{ + int retval = ERROR_FAIL; + struct armv7a_common *armv7a = target_to_armv7a(target); + struct arm_dpm *dpm = armv7a->arm.dpm; + uint32_t virt = va & ~0xfff; + uint32_t NOS, NS, INNER, OUTER; + *val = 0xdeadbeef; + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + /* mmu must be enable in order to get a correct translation + * use VA to PA CP15 register for conversion */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MCR(15, 0, 0, 7, 8, 0), + virt); + if (retval != ERROR_OK) + goto done; + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 7, 4, 0), + val); + /* decode memory attribute */ + NOS = (*val >> 10) & 1; /* Not Outer shareable */ + NS = (*val >> 9) & 1; /* Non secure */ + INNER = (*val >> 4) & 0x7; + OUTER = (*val >> 2) & 0x3; + + if (retval != ERROR_OK) + goto done; + *val = (*val & ~0xfff) + (va & 0xfff); + if (*val == va) + LOG_WARNING("virt = phys : MMU disable !!"); + if (meminfo) { + LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured", + va, *val, + NOS == 1 ? "not" : " ", + NS == 1 ? "not" : ""); + switch (OUTER) { + case 0: + LOG_INFO("outer: Non-Cacheable"); + break; + case 1: + LOG_INFO("outer: Write-Back, Write-Allocate"); + break; + case 2: + LOG_INFO("outer: Write-Through, No Write-Allocate"); + break; + case 3: + LOG_INFO("outer: Write-Back, no Write-Allocate"); + break; + } + switch (INNER) { + case 0: + LOG_INFO("inner: Non-Cacheable"); + break; + case 1: + LOG_INFO("inner: Strongly-ordered"); + break; + case 3: + LOG_INFO("inner: Device"); + break; + case 5: + LOG_INFO("inner: Write-Back, Write-Allocate"); + break; + case 6: + LOG_INFO("inner: Write-Through"); + break; + case 7: + LOG_INFO("inner: Write-Back, no Write-Allocate"); + break; + default: + LOG_INFO("inner: %" PRIx32 " ???", INNER); + } + } + +done: + dpm->finish(dpm); + + return retval; +} + +static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe) +{ + static char bits_string[64]; + unsigned int len; + + if (afe) { + bool acc_r = true; + bool acc_w = !ap2; + bool priv = !(ap10 & 2); + len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access%s: %s%s", + s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", + priv ? "(priv)" : "", acc_r ? "R" : "N", acc_w ? "W " : "O "); + } else { + bool priv_acc_w = !ap2; + bool priv_acc_r = true; + bool unpriv_acc_w = priv_acc_w; + bool unpriv_acc_r = priv_acc_r; + + switch (ap10) { + case 0: + priv_acc_r = priv_acc_w = false; + unpriv_acc_r = unpriv_acc_w = false; + break; + case 1: + unpriv_acc_r = unpriv_acc_w = false; + break; + case 2: + unpriv_acc_w = false; + break; + default: + break; + } + + len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access(priv): %s%s access(unpriv): %s%s", + s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", priv_acc_r ? "R" : "N", priv_acc_w ? "W" : "O", + unpriv_acc_r ? "R" : "N", unpriv_acc_w ? "W" : "O"); + } + + if (len >= sizeof(bits_string)) + bits_string[63] = 0; + + return bits_string; +} + +static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe) +{ + bool c_bit = !!(l2_desc & (1 << 3)); + bool b_bit = !!(l2_desc & (1 << 2)); + bool s_bit = !!(l2_desc & (1 << 10)); + bool ap2 = !!(l2_desc & (1 << 9)); + int ap10 = (l2_desc >> 4) & 3; + + return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe); +} + +static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe) +{ + bool c_bit = !!(l1_desc & (1 << 3)); + bool b_bit = !!(l1_desc & (1 << 2)); + bool s_bit = !!(l1_desc & (1 << 16)); + bool ap2 = !!(l1_desc & (1 << 15)); + int ap10 = (l1_desc >> 10) & 3; + + return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe); +} + +COMMAND_HANDLER(armv7a_mmu_dump_table) +{ + struct target *target = get_current_target(CMD_CTX); + struct cortex_a_common *cortex_a = target_to_cortex_a(target); + struct armv7a_common *armv7a = target_to_armv7a(target); + struct armv7a_mmu_common *mmu = &armv7a->armv7a_mmu; + struct armv7a_cache_common *cache = &mmu->armv7a_cache; + uint32_t *first_lvl_ptbl; + target_addr_t ttb; + int ttbidx = 0; + int retval; + int pt_idx; + int max_pt_idx = 4095; + bool afe; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!strcmp(CMD_ARGV[0], "addr")) { + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb); + + if (CMD_ARGC > 2) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx); + + if (max_pt_idx < 1 || max_pt_idx > 4096) + return ERROR_COMMAND_ARGUMENT_INVALID; + max_pt_idx -= 1; + } + } else { + if (mmu->cached != 1) { + LOG_ERROR("TTB not cached!"); + return ERROR_FAIL; + } + + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx); + if (ttbidx < 0 || ttbidx > 1) + return ERROR_COMMAND_ARGUMENT_INVALID; + + ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx]; + + if (ttbidx == 0) { + int ttbcr_n = mmu->ttbcr & 0x7; + max_pt_idx = 0x0fff >> ttbcr_n; + } + } + + LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb); + + first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1)); + if (first_lvl_ptbl == NULL) + return ERROR_FAIL; + + /* + * this may or may not be necessary depending on whether + * the table walker is configured to use the cache or not. + */ + cache->flush_all_data_cache(target); + + retval = mmu->read_physical_memory(target, ttb, 4, max_pt_idx+1, (uint8_t *)first_lvl_ptbl); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read first-level page table!"); + return retval; + } + + afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE); + + for (pt_idx = 0; pt_idx <= max_pt_idx;) { + uint32_t first_lvl_descriptor = target_buffer_get_u32(target, + (uint8_t *)&first_lvl_ptbl[pt_idx]); + + LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor); + + /* skip empty entries in the first level table */ + if ((first_lvl_descriptor & 3) == 0) { + pt_idx++; + } else + if ((first_lvl_descriptor & 0x40002) == 2) { + /* section descriptor */ + uint32_t va_range = 1024*1024-1; /* 1MB range */ + uint32_t va_start = pt_idx << 20; + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (first_lvl_descriptor & 0xfff00000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("SECT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe)); + pt_idx++; + } else + if ((first_lvl_descriptor & 0x40002) == 0x40002) { + /* supersection descriptor */ + uint32_t va_range = 16*1024*1024-1; /* 16MB range */ + uint32_t va_start = pt_idx << 20; + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (first_lvl_descriptor & 0xff000000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("SSCT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe)); + + /* skip next 15 entries, they're duplicating the first entry */ + pt_idx += 16; + } else { + target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00; + uint32_t second_lvl_descriptor; + uint32_t *pt2; + int pt2_idx; + + /* page table, always 1KB long */ + pt2 = malloc(1024); + retval = mmu->read_physical_memory(target, second_lvl_ptbl, + 4, 256, (uint8_t *)pt2); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read second-level page table!"); + return ERROR_FAIL; + } + + for (pt2_idx = 0; pt2_idx < 256; ) { + second_lvl_descriptor = target_buffer_get_u32(target, + (uint8_t *)&pt2[pt2_idx]); + + if ((second_lvl_descriptor & 3) == 0) { + /* skip entry */ + pt2_idx++; + } else + if ((second_lvl_descriptor & 3) == 1) { + /* large page */ + uint32_t va_range = 64*1024-1; /* 64KB range */ + uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12); + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (second_lvl_descriptor & 0xffff0000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("LPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe)); + + pt2_idx += 16; + } else { + /* small page */ + uint32_t va_range = 4*1024-1; /* 4KB range */ + uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12); + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (second_lvl_descriptor & 0xfffff000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("SPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe)); + + pt2_idx++; + } + } + free(pt2); + pt_idx++; + } + } + + free(first_lvl_ptbl); + return ERROR_OK; +} + +static const struct command_registration armv7a_mmu_group_handlers[] = { + { + .name = "dump", + .handler = armv7a_mmu_dump_table, + .mode = COMMAND_ANY, + .help = "dump translation table 0, 1 or from
", + .usage = "(0|1|addr
[num_entries])", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct command_registration armv7a_mmu_command_handlers[] = { + { + .name = "mmu", + .mode = COMMAND_ANY, + .help = "mmu command group", + .usage = "", + .chain = armv7a_mmu_group_handlers, + }, + COMMAND_REGISTRATION_DONE +}; diff --git a/src/target/armv7a_mmu.h b/src/target/armv7a_mmu.h new file mode 100644 index 000000000..4372aa8eb --- /dev/null +++ b/src/target/armv7a_mmu.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * matthias.welwarsky@sysgo.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 . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ARMV7A_MMU_H +#define OPENOCD_TARGET_ARMV7A_MMU_H + +extern int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val); +extern int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, + uint32_t *val, int meminfo); + +extern const struct command_registration armv7a_mmu_command_handlers[]; + +#endif /* OPENOCD_TARGET_ARMV7A_MMU_H */ diff --git a/src/target/armv8.c b/src/target/armv8.c index dfa2c67a5..75ada896d 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -936,6 +936,11 @@ int armv8_mmu_translate_va_pa(struct target *target, target_addr_t va, "Secure", "Not Secure" }; + if (target->state != TARGET_HALTED) { + LOG_WARNING("target %s not halted", target_name(target)); + return ERROR_TARGET_NOT_HALTED; + } + retval = dpm->prepare(dpm); if (retval != ERROR_OK) return retval; diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index 4aae5e473..92ba54789 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -50,6 +50,7 @@ #include "breakpoints.h" #include "cortex_a.h" #include "register.h" +#include "armv7a_mmu.h" #include "target_request.h" #include "target_type.h" #include "arm_opcodes.h" @@ -57,6 +58,9 @@ #include "transport/transport.h" #include +#define foreach_smp_target(pos, head) \ + for (pos = head; (pos != NULL); pos = pos->next) + static int cortex_a_poll(struct target *target); static int cortex_a_debug_entry(struct target *target); static int cortex_a_restore_context(struct target *target, bool bpwp); @@ -68,10 +72,6 @@ static int cortex_a_set_hybrid_breakpoint(struct target *target, struct breakpoint *breakpoint); static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *breakpoint); -static int cortex_a_dap_read_coreregister_u32(struct target *target, - uint32_t *value, int regnum); -static int cortex_a_dap_write_coreregister_u32(struct target *target, - uint32_t value, int regnum); static int cortex_a_mmu(struct target *target, int *enabled); static int cortex_a_mmu_modify(struct target *target, int enable); static int cortex_a_virt2phys(struct target *target, @@ -110,7 +110,7 @@ static int cortex_a_prep_memaccess(struct target *target, int phys_access) int mmu_enabled = 0; if (phys_access == 0) { - dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); cortex_a_mmu(target, &mmu_enabled); if (mmu_enabled) cortex_a_mmu_modify(target, 1); @@ -145,7 +145,7 @@ static int cortex_a_post_memaccess(struct target *target, int phys_access) 0, 0, 3, 0, cortex_a->cp15_dacr_reg); } - dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); } else { int mmu_enabled = 0; cortex_a_mmu(target, &mmu_enabled); @@ -304,172 +304,6 @@ static int cortex_a_exec_opcode(struct target *target, return retval; } -/************************************************************************** -Read core register with very few exec_opcode, fast but needs work_area. -This can cause problems with MMU active. -**************************************************************************/ -static int cortex_a_read_regs_through_mem(struct target *target, uint32_t address, - uint32_t *regfile) -{ - int retval = ERROR_OK; - struct armv7a_common *armv7a = target_to_armv7a(target); - - retval = cortex_a_dap_read_coreregister_u32(target, regfile, 0); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_dap_write_coreregister_u32(target, address, 0); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, ARMV4_5_STMIA(0, 0xFFFE, 0, 0), NULL); - if (retval != ERROR_OK) - return retval; - - retval = mem_ap_read_buf(armv7a->memory_ap, - (uint8_t *)(®file[1]), 4, 15, address); - - return retval; -} - -static int cortex_a_dap_read_coreregister_u32(struct target *target, - uint32_t *value, int regnum) -{ - int retval = ERROR_OK; - uint8_t reg = regnum&0xFF; - uint32_t dscr = 0; - struct armv7a_common *armv7a = target_to_armv7a(target); - - if (reg > 17) - return retval; - - if (reg < 15) { - /* Rn to DCCTX, "MCR p14, 0, Rn, c0, c5, 0" 0xEE00nE15 */ - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(14, 0, reg, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } else if (reg == 15) { - /* "MOV r0, r15"; then move r0 to DCCTX */ - retval = cortex_a_exec_opcode(target, 0xE1A0000F, &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } else { - /* "MRS r0, CPSR" or "MRS r0, SPSR" - * then move r0 to DCCTX - */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRS(0, reg & 1), &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } - - /* Wait for DTRRXfull then read DTRRTX */ - int64_t then = timeval_ms(); - while ((dscr & DSCR_DTR_TX_FULL) == 0) { - retval = mem_ap_read_atomic_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DSCR, &dscr); - if (retval != ERROR_OK) - return retval; - if (timeval_ms() > then + 1000) { - LOG_ERROR("Timeout waiting for cortex_a_exec_opcode"); - return ERROR_FAIL; - } - } - - retval = mem_ap_read_atomic_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DTRTX, value); - LOG_DEBUG("read DCC 0x%08" PRIx32, *value); - - return retval; -} - -static int cortex_a_dap_write_coreregister_u32(struct target *target, - uint32_t value, int regnum) -{ - int retval = ERROR_OK; - uint8_t Rd = regnum&0xFF; - uint32_t dscr; - struct armv7a_common *armv7a = target_to_armv7a(target); - - LOG_DEBUG("register %i, value 0x%08" PRIx32, regnum, value); - - /* Check that DCCRX is not full */ - retval = mem_ap_read_atomic_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DSCR, &dscr); - if (retval != ERROR_OK) - return retval; - if (dscr & DSCR_DTR_RX_FULL) { - LOG_ERROR("DSCR_DTR_RX_FULL, dscr 0x%08" PRIx32, dscr); - /* Clear DCCRX with MRC(p14, 0, Rd, c0, c5, 0), opcode 0xEE100E15 */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } - - if (Rd > 17) - return retval; - - /* Write DTRRX ... sets DSCR.DTRRXfull but exec_opcode() won't care */ - LOG_DEBUG("write DCC 0x%08" PRIx32, value); - retval = mem_ap_write_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DTRRX, value); - if (retval != ERROR_OK) - return retval; - - if (Rd < 15) { - /* DCCRX to Rn, "MRC p14, 0, Rn, c0, c5, 0", 0xEE10nE15 */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, Rd, 0, 5, 0), - &dscr); - - if (retval != ERROR_OK) - return retval; - } else if (Rd == 15) { - /* DCCRX to R0, "MRC p14, 0, R0, c0, c5, 0", 0xEE100E15 - * then "mov r15, r0" - */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, 0xE1A0F000, &dscr); - if (retval != ERROR_OK) - return retval; - } else { - /* DCCRX to R0, "MRC p14, 0, R0, c0, c5, 0", 0xEE100E15 - * then "MSR CPSR_cxsf, r0" or "MSR SPSR_cxsf, r0" (all fields) - */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, ARMV4_5_MSR_GP(0, 0xF, Rd & 1), - &dscr); - if (retval != ERROR_OK) - return retval; - - /* "Prefetch flush" after modifying execution status in CPSR */ - if (Rd == 16) { - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(15, 0, 0, 7, 5, 4), - &dscr); - if (retval != ERROR_OK) - return retval; - } - } - - return retval; -} - /* Write to memory mapped registers directly with no cache or mmu handling */ static int cortex_a_dap_write_memap_register_u32(struct target *target, uint32_t address, @@ -806,12 +640,43 @@ static int cortex_a_halt_smp(struct target *target) static int update_halt_gdb(struct target *target) { + struct target *gdb_target = NULL; + struct target_list *head; + struct target *curr; int retval = 0; + if (target->gdb_service && target->gdb_service->core[0] == -1) { target->gdb_service->target = target; target->gdb_service->core[0] = target->coreid; retval += cortex_a_halt_smp(target); } + + if (target->gdb_service) + gdb_target = target->gdb_service->target; + + foreach_smp_target(head, target->head) { + curr = head->target; + /* skip calling context */ + if (curr == target) + continue; + if (!target_was_examined(curr)) + continue; + /* skip targets that were already halted */ + if (curr->state == TARGET_HALTED) + continue; + /* Skip gdb_target; it alerts GDB so has to be polled as last one */ + if (curr == gdb_target) + continue; + + /* avoid recursion in cortex_a_poll() */ + curr->smp = 0; + cortex_a_poll(curr); + curr->smp = 1; + } + + /* after all targets were updated, poll the gdb serving target */ + if (gdb_target != NULL && gdb_target != target) + cortex_a_poll(gdb_target); return retval; } @@ -1002,7 +867,7 @@ static int cortex_a_internal_restore(struct target *target, int current, arm->pc->valid = 1; /* restore dpm_mode at system halt */ - dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); /* called it now before restoring context because it uses cpu * register r0 for restoring cp15 control register */ retval = cortex_a_restore_cp15_control_reg(target); @@ -1149,14 +1014,11 @@ static int cortex_a_resume(struct target *target, int current, static int cortex_a_debug_entry(struct target *target) { - int i; - uint32_t regfile[16], cpsr, spsr, dscr; + uint32_t dscr; int retval = ERROR_OK; - struct working_area *regfile_working_area = NULL; struct cortex_a_common *cortex_a = target_to_cortex_a(target); struct armv7a_common *armv7a = target_to_armv7a(target); struct arm *arm = &armv7a->arm; - struct reg *reg; LOG_DEBUG("dscr = 0x%08" PRIx32, cortex_a->cpudbg_dscr); @@ -1193,68 +1055,16 @@ static int cortex_a_debug_entry(struct target *target) arm_dpm_report_wfar(&armv7a->dpm, wfar); } - /* REVISIT fast_reg_read is never set ... */ - - /* Examine target state and mode */ - if (cortex_a->fast_reg_read) - target_alloc_working_area(target, 64, ®file_working_area); - - - /* First load register acessible through core debug port*/ - if (!regfile_working_area) - retval = arm_dpm_read_current_registers(&armv7a->dpm); - else { - retval = cortex_a_read_regs_through_mem(target, - regfile_working_area->address, regfile); - - target_free_working_area(target, regfile_working_area); - if (retval != ERROR_OK) - return retval; - - /* read Current PSR */ - retval = cortex_a_dap_read_coreregister_u32(target, &cpsr, 16); - /* store current cpsr */ - if (retval != ERROR_OK) - return retval; - - LOG_DEBUG("cpsr: %8.8" PRIx32, cpsr); - - arm_set_cpsr(arm, cpsr); - - /* update cache */ - for (i = 0; i <= ARM_PC; i++) { - reg = arm_reg_current(arm, i); - - buf_set_u32(reg->value, 0, 32, regfile[i]); - reg->valid = 1; - reg->dirty = 0; - } - - /* Fixup PC Resume Address */ - if (cpsr & (1 << 5)) { - /* T bit set for Thumb or ThumbEE state */ - regfile[ARM_PC] -= 4; - } else { - /* ARM state */ - regfile[ARM_PC] -= 8; - } - - reg = arm->pc; - buf_set_u32(reg->value, 0, 32, regfile[ARM_PC]); - reg->dirty = reg->valid; - } + /* First load register accessible through core debug port */ + retval = arm_dpm_read_current_registers(&armv7a->dpm); + if (retval != ERROR_OK) + return retval; if (arm->spsr) { - /* read Saved PSR */ - retval = cortex_a_dap_read_coreregister_u32(target, &spsr, 17); - /* store current spsr */ + /* read SPSR */ + retval = arm_dpm_read_reg(&armv7a->dpm, arm->spsr, 17); if (retval != ERROR_OK) return retval; - - reg = arm->spsr; - buf_set_u32(reg->value, 0, 32, spsr); - reg->valid = 1; - reg->dirty = 0; } #if 0 @@ -1316,7 +1126,7 @@ static int cortex_a_post_debug_entry(struct target *target) cortex_a->curr_mode = armv7a->arm.core_mode; /* switch to SVC mode to read DACR */ - dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); armv7a->arm.mrc(target, 15, 0, 0, 3, 0, &cortex_a->cp15_dacr_reg); @@ -1324,7 +1134,7 @@ static int cortex_a_post_debug_entry(struct target *target) LOG_DEBUG("cp15_dacr_reg: %8.8" PRIx32, cortex_a->cp15_dacr_reg); - dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); return ERROR_OK; } @@ -1386,6 +1196,7 @@ static int cortex_a_step(struct target *target, int current, target_addr_t addre /* Setup single step breakpoint */ stepbreakpoint.address = address; + stepbreakpoint.asid = 0; stepbreakpoint.length = (arm->core_state == ARM_STATE_THUMB) ? 2 : 4; stepbreakpoint.type = BKPT_HARD; @@ -2660,9 +2471,6 @@ static int cortex_a_read_phys_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; int retval; if (!count || !buffer) @@ -2671,9 +2479,6 @@ static int cortex_a_read_phys_memory(struct target *target, LOG_DEBUG("Reading memory at real address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, address, size, count); - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) - return mem_ap_read_buf(armv7a->memory_ap, buffer, size, count, address); - /* read memory through the CPU */ cortex_a_prep_memaccess(target, 1); retval = cortex_a_read_cpu_memory(target, address, size, count, buffer); @@ -2698,57 +2503,10 @@ static int cortex_a_read_memory(struct target *target, target_addr_t address, return retval; } -static int cortex_a_read_memory_ahb(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer) -{ - int mmu_enabled = 0; - target_addr_t virt, phys; - int retval; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; - - if (!armv7a->memory_ap_available || (apsel != armv7a->memory_ap->ap_num)) - return target_read_memory(target, address, size, count, buffer); - - /* cortex_a handles unaligned memory access */ - LOG_DEBUG("Reading memory at address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, - address, size, count); - - /* determine if MMU was enabled on target stop */ - if (!armv7a->is_armv7r) { - retval = cortex_a_mmu(target, &mmu_enabled); - if (retval != ERROR_OK) - return retval; - } - - if (mmu_enabled) { - virt = address; - retval = cortex_a_virt2phys(target, virt, &phys); - if (retval != ERROR_OK) - return retval; - - LOG_DEBUG("Reading at virtual address. " - "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT, - virt, phys); - address = phys; - } - - if (!count || !buffer) - return ERROR_COMMAND_SYNTAX_ERROR; - - retval = mem_ap_read_buf(armv7a->memory_ap, buffer, size, count, address); - - return retval; -} - static int cortex_a_write_phys_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; int retval; if (!count || !buffer) @@ -2757,9 +2515,6 @@ static int cortex_a_write_phys_memory(struct target *target, LOG_DEBUG("Writing memory to real address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, address, size, count); - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) - return mem_ap_write_buf(armv7a->memory_ap, buffer, size, count, address); - /* write memory through the CPU */ cortex_a_prep_memaccess(target, 1); retval = cortex_a_write_cpu_memory(target, address, size, count, buffer); @@ -2786,51 +2541,6 @@ static int cortex_a_write_memory(struct target *target, target_addr_t address, return retval; } -static int cortex_a_write_memory_ahb(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) -{ - int mmu_enabled = 0; - target_addr_t virt, phys; - int retval; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; - - if (!armv7a->memory_ap_available || (apsel != armv7a->memory_ap->ap_num)) - return target_write_memory(target, address, size, count, buffer); - - /* cortex_a handles unaligned memory access */ - LOG_DEBUG("Writing memory at address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, - address, size, count); - - /* determine if MMU was enabled on target stop */ - if (!armv7a->is_armv7r) { - retval = cortex_a_mmu(target, &mmu_enabled); - if (retval != ERROR_OK) - return retval; - } - - if (mmu_enabled) { - virt = address; - retval = cortex_a_virt2phys(target, virt, &phys); - if (retval != ERROR_OK) - return retval; - - LOG_DEBUG("Writing to virtual address. " - "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT, - virt, - phys); - address = phys; - } - - if (!count || !buffer) - return ERROR_COMMAND_SYNTAX_ERROR; - - retval = mem_ap_write_buf(armv7a->memory_ap, buffer, size, count, address); - - return retval; -} - static int cortex_a_read_buffer(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer) { @@ -2840,7 +2550,7 @@ static int cortex_a_read_buffer(struct target *target, target_addr_t address, * 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 = cortex_a_read_memory_ahb(target, address, size, 1, buffer); + int retval = target_read_memory(target, address, size, 1, buffer); if (retval != ERROR_OK) return retval; address += size; @@ -2853,7 +2563,7 @@ static int cortex_a_read_buffer(struct target *target, target_addr_t address, for (; size > 0; size /= 2) { uint32_t aligned = count - count % size; if (aligned > 0) { - int retval = cortex_a_read_memory_ahb(target, address, size, aligned / size, buffer); + int retval = target_read_memory(target, address, size, aligned / size, buffer); if (retval != ERROR_OK) return retval; address += aligned; @@ -2874,7 +2584,7 @@ static int cortex_a_write_buffer(struct target *target, target_addr_t address, * 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 = cortex_a_write_memory_ahb(target, address, size, 1, buffer); + int retval = target_write_memory(target, address, size, 1, buffer); if (retval != ERROR_OK) return retval; address += size; @@ -2887,7 +2597,7 @@ static int cortex_a_write_buffer(struct target *target, target_addr_t address, for (; size > 0; size /= 2) { uint32_t aligned = count - count % size; if (aligned > 0) { - int retval = cortex_a_write_memory_ahb(target, address, size, aligned / size, buffer); + int retval = target_write_memory(target, address, size, aligned / size, buffer); if (retval != ERROR_OK) return retval; address += aligned; @@ -2965,21 +2675,6 @@ static int cortex_a_examine_first(struct target *target) armv7a->debug_ap->memaccess_tck = 80; - /* Search for the AHB-AB. - * REVISIT: We should search for AXI-AP as well and make sure the AP's MEMTYPE says it - * can access system memory. */ - armv7a->memory_ap_available = false; - retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7a->memory_ap); - if (retval == ERROR_OK) { - retval = mem_ap_init(armv7a->memory_ap); - if (retval == ERROR_OK) - armv7a->memory_ap_available = true; - } - if (retval != ERROR_OK) { - /* AHB-AP not found or unavailable - use the CPU */ - LOG_DEBUG("No AHB-AP available for memory access"); - } - if (!target->dbgbase_set) { uint32_t dbgbase; /* Get ROM Table base */ @@ -3139,8 +2834,6 @@ static int cortex_a_init_arch_info(struct target *target, cortex_a->common_magic = CORTEX_A_COMMON_MAGIC; armv7a->arm.dap = dap; - cortex_a->fast_reg_read = 0; - /* register arch-specific functions */ armv7a->examine_debug_reason = NULL; @@ -3225,10 +2918,7 @@ static int cortex_a_mmu(struct target *target, int *enabled) static int cortex_a_virt2phys(struct target *target, target_addr_t virt, target_addr_t *phys) { - int retval = ERROR_FAIL; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; + int retval; int mmu_enabled = 0; /* @@ -3243,23 +2933,12 @@ static int cortex_a_virt2phys(struct target *target, return ERROR_OK; } - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) { - uint32_t ret; - retval = armv7a_mmu_translate_va(target, - virt, &ret); - if (retval != ERROR_OK) - goto done; - *phys = ret; - } else {/* use this method if armv7a->memory_ap not selected - * mmu must be enable in order to get a correct translation */ - retval = cortex_a_mmu_modify(target, 1); - if (retval != ERROR_OK) - goto done; - retval = armv7a_mmu_translate_va_pa(target, (uint32_t)virt, + /* mmu must be enable in order to get a correct translation */ + retval = cortex_a_mmu_modify(target, 1); + if (retval != ERROR_OK) + return retval; + return armv7a_mmu_translate_va_pa(target, (uint32_t)virt, (uint32_t *)phys, 1); - } -done: - return retval; } COMMAND_HANDLER(cortex_a_handle_cache_info_command) @@ -3443,6 +3122,9 @@ static const struct command_registration cortex_a_exec_command_handlers[] = { "on memory access", .usage = "['on'|'off']", }, + { + .chain = armv7a_mmu_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/cortex_a.h b/src/target/cortex_a.h index ff0343208..197a5992d 100644 --- a/src/target/cortex_a.h +++ b/src/target/cortex_a.h @@ -93,9 +93,6 @@ struct cortex_a_common { int brp_num_available; struct cortex_a_brp *brp_list; - /* Use cortex_a_read_regs_through_mem for fast register reads */ - int fast_reg_read; - uint32_t cpuid; uint32_t didr; diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index ca3dbec78..07fea5137 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -42,7 +42,7 @@ /* NOTE: most of this should work fine for the Cortex-M1 and * Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M. - * Some differences: M0/M1 doesn't have FBP remapping or the + * Some differences: M0/M1 doesn't have FPB remapping or the * DWT tracing/profiling support. (So the cycle counter will * not be usable; the other stuff isn't currently used here.) * @@ -255,7 +255,7 @@ static int cortex_m_endreset_event(struct target *target) return retval; /* Paranoia: evidently some (early?) chips don't preserve all the - * debug state (including FBP, DWT, etc) across reset... + * debug state (including FPB, DWT, etc) across reset... */ /* Enable FPB */ @@ -1277,7 +1277,7 @@ int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpo { struct cortex_m_common *cortex_m = target_to_cm(target); - /* REVISIT why check? FBP can be updated with core running ... */ + /* REVISIT why check? FPB can be updated with core running ... */ if (target->state != TARGET_HALTED) { LOG_WARNING("target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -1986,7 +1986,7 @@ int cortex_m_examine(struct target *target) /* stlink shares the examine handler but does not support * all its calls */ if (!armv7m->stlink) { - if (cortex_m->apsel < 0) { + if (cortex_m->apsel == DP_APSEL_INVALID) { /* Search for the MEM-AP */ retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7m->debug_ap); if (retval != ERROR_OK) { diff --git a/src/target/esirisc.c b/src/target/esirisc.c new file mode 100644 index 000000000..38100e834 --- /dev/null +++ b/src/target/esirisc.c @@ -0,0 +1,1787 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * 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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esirisc.h" + +#define RESET_TIMEOUT 5000 /* 5s */ +#define STEP_TIMEOUT 1000 /* 1s */ + +/* + * eSi-RISC targets support a configurable number of interrupts; + * up to 32 interrupts are supported. + */ +static const char * const esirisc_exceptions[] = { + "Reset", "HardwareFailure", "NMI", "InstBreakpoint", "DataBreakpoint", + "Unsupported", "PrivilegeViolation", "InstBusError", "DataBusError", + "AlignmentError", "ArithmeticError", "SystemCall", "MemoryManagement", + "Unrecoverable", "Reserved", + + "Interrupt0", "Interrupt1", "Interrupt2", "Interrupt3", + "Interrupt4", "Interrupt5", "Interrupt6", "Interrupt7", + "Interrupt8", "Interrupt9", "Interrupt10", "Interrupt11", + "Interrupt12", "Interrupt13", "Interrupt14", "Interrupt15", + "Interrupt16", "Interrupt17", "Interrupt18", "Interrupt19", + "Interrupt20", "Interrupt21", "Interrupt22", "Interrupt23", + "Interrupt24", "Interrupt25", "Interrupt26", "Interrupt27", + "Interrupt28", "Interrupt29", "Interrupt30", "Interrupt31", +}; + +/* + * eSi-RISC targets support a configurable number of general purpose + * registers; 8, 16, and 32 registers are supported. + */ +static const struct { + enum esirisc_reg_num number; + const char *name; + enum reg_type type; + const char *group; +} esirisc_regs[] = { + { ESIRISC_SP, "sp", REG_TYPE_DATA_PTR, "general" }, + { ESIRISC_RA, "ra", REG_TYPE_INT, "general" }, + { ESIRISC_R2, "r2", REG_TYPE_INT, "general" }, + { ESIRISC_R3, "r3", REG_TYPE_INT, "general" }, + { ESIRISC_R4, "r4", REG_TYPE_INT, "general" }, + { ESIRISC_R5, "r5", REG_TYPE_INT, "general" }, + { ESIRISC_R6, "r6", REG_TYPE_INT, "general" }, + { ESIRISC_R7, "r7", REG_TYPE_INT, "general" }, + { ESIRISC_R8, "r8", REG_TYPE_INT, "general" }, + { ESIRISC_R9, "r9", REG_TYPE_INT, "general" }, + { ESIRISC_R10, "r10", REG_TYPE_INT, "general" }, + { ESIRISC_R11, "r11", REG_TYPE_INT, "general" }, + { ESIRISC_R12, "r12", REG_TYPE_INT, "general" }, + { ESIRISC_R13, "r13", REG_TYPE_INT, "general" }, + { ESIRISC_R14, "r14", REG_TYPE_INT, "general" }, + { ESIRISC_R15, "r15", REG_TYPE_INT, "general" }, + { ESIRISC_R16, "r16", REG_TYPE_INT, "general" }, + { ESIRISC_R17, "r17", REG_TYPE_INT, "general" }, + { ESIRISC_R18, "r18", REG_TYPE_INT, "general" }, + { ESIRISC_R19, "r19", REG_TYPE_INT, "general" }, + { ESIRISC_R20, "r20", REG_TYPE_INT, "general" }, + { ESIRISC_R21, "r21", REG_TYPE_INT, "general" }, + { ESIRISC_R22, "r22", REG_TYPE_INT, "general" }, + { ESIRISC_R23, "r23", REG_TYPE_INT, "general" }, + { ESIRISC_R24, "r24", REG_TYPE_INT, "general" }, + { ESIRISC_R25, "r25", REG_TYPE_INT, "general" }, + { ESIRISC_R26, "r26", REG_TYPE_INT, "general" }, + { ESIRISC_R27, "r27", REG_TYPE_INT, "general" }, + { ESIRISC_R28, "r28", REG_TYPE_INT, "general" }, + { ESIRISC_R29, "r29", REG_TYPE_INT, "general" }, + { ESIRISC_R30, "r30", REG_TYPE_INT, "general" }, + { ESIRISC_R31, "r31", REG_TYPE_INT, "general" }, +}; + +/* + * Control and Status Registers (CSRs) are largely defined as belonging + * to the system register group. The exception to this rule are the PC + * and CAS registers, which belong to the general group. While debug is + * active, EPC, ECAS, and ETC must be used to read and write the PC, + * CAS, and TC CSRs, respectively. + */ +static const struct { + enum esirisc_reg_num number; + uint8_t bank; + uint8_t csr; + const char *name; + enum reg_type type; + const char *group; +} esirisc_csrs[] = { + { ESIRISC_PC, CSR_THREAD, CSR_THREAD_EPC, "PC", REG_TYPE_CODE_PTR, "general" }, /* PC -> EPC */ + { ESIRISC_CAS, CSR_THREAD, CSR_THREAD_ECAS, "CAS", REG_TYPE_INT, "general" }, /* CAS -> ECAS */ + { ESIRISC_TC, CSR_THREAD, CSR_THREAD_ETC, "TC", REG_TYPE_INT, "system" }, /* TC -> ETC */ + { ESIRISC_ETA, CSR_THREAD, CSR_THREAD_ETA, "ETA", REG_TYPE_INT, "system" }, + { ESIRISC_ETC, CSR_THREAD, CSR_THREAD_ETC, "ETC", REG_TYPE_INT, "system" }, + { ESIRISC_EPC, CSR_THREAD, CSR_THREAD_EPC, "EPC", REG_TYPE_CODE_PTR, "system" }, + { ESIRISC_ECAS, CSR_THREAD, CSR_THREAD_ECAS, "ECAS", REG_TYPE_INT, "system" }, + { ESIRISC_EID, CSR_THREAD, CSR_THREAD_EID, "EID", REG_TYPE_INT, "system" }, + { ESIRISC_ED, CSR_THREAD, CSR_THREAD_ED, "ED", REG_TYPE_INT, "system" }, + { ESIRISC_IP, CSR_INTERRUPT, CSR_INTERRUPT_IP, "IP", REG_TYPE_INT, "system"}, + { ESIRISC_IM, CSR_INTERRUPT, CSR_INTERRUPT_IM, "IM", REG_TYPE_INT, "system"}, + { ESIRISC_IS, CSR_INTERRUPT, CSR_INTERRUPT_IS, "IS", REG_TYPE_INT, "system"}, + { ESIRISC_IT, CSR_INTERRUPT, CSR_INTERRUPT_IT, "IT", REG_TYPE_INT, "system"}, +}; + +static int esirisc_disable_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t etc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETC", target_name(target)); + return retval; + } + + etc &= ~(1<<0); /* TC.I */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +#if 0 +static int esirisc_enable_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t etc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETC", target_name(target)); + return retval; + } + + etc |= (1<<0); /* TC.I */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} +#endif + +static int esirisc_save_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, + &esirisc->etc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_restore_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, + esirisc->etc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +#if 0 +static int esirisc_save_hwdc(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC, + &esirisc->hwdc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: HWDC", target_name(target)); + return retval; + } + + return ERROR_OK; +} +#endif + +static int esirisc_restore_hwdc(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC, + esirisc->hwdc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: HWDC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_save_context(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + for (unsigned i = 0; i < esirisc->reg_cache->num_regs; ++i) { + struct reg *reg = esirisc->reg_cache->reg_list + i; + struct esirisc_reg *reg_info = reg->arch_info; + + if (reg->exist && !reg->valid) + reg_info->read(reg); + } + + return ERROR_OK; +} + +static int esirisc_restore_context(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + for (unsigned i = 0; i < esirisc->reg_cache->num_regs; ++i) { + struct reg *reg = esirisc->reg_cache->reg_list + i; + struct esirisc_reg *reg_info = reg->arch_info; + + if (reg->exist && reg->dirty) + reg_info->write(reg); + } + + return ERROR_OK; +} + +static int esirisc_flush_caches(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + int retval = esirisc_jtag_flush_caches(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to flush caches", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_wait_debug_active(struct esirisc_common *esirisc, int ms) +{ + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int64_t t; + + LOG_DEBUG("-"); + + t = timeval_ms(); + for (;;) { + int retval = esirisc_jtag_enable_debug(jtag_info); + if (retval == ERROR_OK && esirisc_jtag_is_debug_active(jtag_info)) + return retval; + + if ((timeval_ms() - t) > ms) + return ERROR_TARGET_TIMEOUT; + + alive_sleep(100); + } +} + +static int esirisc_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + int num_bits = 8 * size; + for (uint32_t i = 0; i < count; ++i) { + union esirisc_memory value; + void *value_p; + + switch (size) { + case sizeof(value.word): + value_p = &value.word; + retval = esirisc_jtag_read_word(jtag_info, address, value_p); + break; + + case sizeof(value.hword): + value_p = &value.hword; + retval = esirisc_jtag_read_hword(jtag_info, address, value_p); + break; + + case sizeof(value.byte): + value_p = &value.byte; + retval = esirisc_jtag_read_byte(jtag_info, address, value_p); + break; + + default: + LOG_ERROR("%s: unsupported size: %" PRIu32, target_name(target), size); + return ERROR_FAIL; + } + + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read address: 0x%" TARGET_PRIxADDR, target_name(target), + address); + return retval; + } + + buf_cpy(value_p, buffer, num_bits); + address += size; + buffer += size; + } + + return ERROR_OK; +} + +static int esirisc_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + int num_bits = 8 * size; + for (uint32_t i = 0; i < count; ++i) { + union esirisc_memory value; + + switch (size) { + case sizeof(value.word): + value.word = buf_get_u32(buffer, 0, num_bits); + retval = esirisc_jtag_write_word(jtag_info, address, value.word); + break; + + case sizeof(value.hword): + value.hword = buf_get_u32(buffer, 0, num_bits); + retval = esirisc_jtag_write_hword(jtag_info, address, value.hword); + break; + + case sizeof(value.byte): + value.byte = buf_get_u32(buffer, 0, num_bits); + retval = esirisc_jtag_write_byte(jtag_info, address, value.byte); + break; + + default: + LOG_ERROR("%s: unsupported size: %" PRIu32, target_name(target), size); + return ERROR_FAIL; + } + + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write address: 0x%" TARGET_PRIxADDR, target_name(target), + address); + return retval; + } + + address += size; + buffer += size; + } + + return ERROR_OK; +} + +static int esirisc_checksum_memory(struct target *target, target_addr_t address, + uint32_t count, uint32_t *checksum) +{ + return ERROR_FAIL; /* not supported */ +} + +static int esirisc_next_breakpoint(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct breakpoint **breakpoints_p = esirisc->breakpoints_p; + struct breakpoint **breakpoints_e = breakpoints_p + esirisc->num_breakpoints; + + LOG_DEBUG("-"); + + for (int bp_index = 0; breakpoints_p < breakpoints_e; ++breakpoints_p, ++bp_index) + if (*breakpoints_p == NULL) + return bp_index; + + return -1; +} + +static int esirisc_add_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int bp_index; + uint32_t ibc; + int retval; + + LOG_DEBUG("-"); + + /* + * The default linker scripts provided by the eSi-RISC toolchain do + * not specify attributes on memory regions, which results in + * incorrect application of software breakpoints by GDB. Targets + * must be configured with `gdb_breakpoint_override hard` as + * software breakpoints are not supported. + */ + if (breakpoint->type != BKPT_HARD) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + bp_index = esirisc_next_breakpoint(target); + if (bp_index < 0) { + LOG_ERROR("%s: out of hardware breakpoints", target_name(target)); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + breakpoint->set = bp_index + 1; + esirisc->breakpoints_p[bp_index] = breakpoint; + + /* specify instruction breakpoint address */ + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBAn + bp_index, + breakpoint->address); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBA", target_name(target)); + return retval; + } + + /* enable instruction breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: IBC", target_name(target)); + return retval; + } + + ibc |= (1 << bp_index); /* IBC.In */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_add_breakpoints(struct target *target) +{ + struct breakpoint *breakpoint = target->breakpoints; + + LOG_DEBUG("-"); + + while (breakpoint != NULL) { + if (breakpoint->set == 0) + esirisc_add_breakpoint(target, breakpoint); + + breakpoint = breakpoint->next; + } + + return ERROR_OK; +} + +static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int bp_index = breakpoint->set - 1; + uint32_t ibc; + int retval; + + LOG_DEBUG("-"); + + /* disable instruction breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: IBC", target_name(target)); + return retval; + } + + ibc &= ~(1 << bp_index); /* IBC.In */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBC", target_name(target)); + return retval; + } + + esirisc->breakpoints_p[bp_index] = NULL; + breakpoint->set = 0; + + return ERROR_OK; +} + +static int esirisc_remove_breakpoints(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + /* clear instruction breakpoints */ + int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, 0); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBC", target_name(target)); + return retval; + } + + memset(esirisc->breakpoints_p, 0, sizeof(esirisc->breakpoints_p)); + + return ERROR_OK; +} + +static int esirisc_next_watchpoint(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct watchpoint **watchpoints_p = esirisc->watchpoints_p; + struct watchpoint **watchpoints_e = watchpoints_p + esirisc->num_watchpoints; + + LOG_DEBUG("-"); + + for (int wp_index = 0; watchpoints_p < watchpoints_e; ++watchpoints_p, ++wp_index) + if (*watchpoints_p == NULL) + return wp_index; + + return -1; +} + +static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watchpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int wp_index; + uint32_t dbs, dbc; + int retval; + + LOG_DEBUG("-"); + + wp_index = esirisc_next_watchpoint(target); + if (wp_index < 0) { + LOG_ERROR("%s: out of hardware watchpoints", target_name(target)); + return ERROR_FAIL; + } + + watchpoint->set = wp_index + 1; + esirisc->watchpoints_p[wp_index] = watchpoint; + + /* specify data breakpoint address */ + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBAn + wp_index, + watchpoint->address); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBA", target_name(target)); + return retval; + } + + /* specify data breakpoint size */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, &dbs); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBS", target_name(target)); + return retval; + } + + uint32_t sn; + switch (watchpoint->length) { + case sizeof(uint64_t): + sn = 0x3; + break; + case sizeof(uint32_t): + sn = 0x2; + break; + + case sizeof(uint16_t): + sn = 0x1; + break; + + case sizeof(uint8_t): + sn = 0x0; + break; + + default: + LOG_ERROR("%s: unsupported length: %" PRIu32, target_name(target), + watchpoint->length); + return ERROR_FAIL; + } + + dbs |= (sn << (2 * wp_index)); /* DBS.Sn */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, dbs); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBS", target_name(target)); + return retval; + } + + /* enable data breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBC", target_name(target)); + return retval; + } + + uint32_t dn; + switch (watchpoint->rw) { + case WPT_READ: + dn = 0x1; + break; + + case WPT_WRITE: + dn = 0x2; + break; + + case WPT_ACCESS: + dn = 0x3; + break; + + default: + LOG_ERROR("%s: unsupported rw: %" PRId32, target_name(target), + watchpoint->rw); + return ERROR_FAIL; + } + + dbc |= (dn << (2 * wp_index)); /* DBC.Dn */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_add_watchpoints(struct target *target) +{ + struct watchpoint *watchpoint = target->watchpoints; + + LOG_DEBUG("-"); + + while (watchpoint != NULL) { + if (watchpoint->set == 0) + esirisc_add_watchpoint(target, watchpoint); + + watchpoint = watchpoint->next; + } + + return ERROR_OK; +} + +static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *watchpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int wp_index = watchpoint->set - 1; + uint32_t dbc; + int retval; + + LOG_DEBUG("-"); + + /* disable data breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBC", target_name(target)); + return retval; + } + + dbc &= ~(0x3 << (2 * wp_index)); /* DBC.Dn */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBC", target_name(target)); + return retval; + } + + esirisc->watchpoints_p[wp_index] = NULL; + watchpoint->set = 0; + + return ERROR_OK; +} + +static int esirisc_remove_watchpoints(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + /* clear data breakpoints */ + int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, 0); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBC", target_name(target)); + return retval; + } + + memset(esirisc->watchpoints_p, 0, sizeof(esirisc->watchpoints_p)); + + return ERROR_OK; +} + +static int esirisc_halt(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + if (target->state == TARGET_HALTED) + return ERROR_OK; + + int retval = esirisc_jtag_break(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to halt target", target_name(target)); + return retval; + } + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +static int esirisc_disable_step(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t dc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DC", target_name(target)); + return retval; + } + + dc &= ~(1<<0); /* DC.S */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_enable_step(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t dc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DC", target_name(target)); + return retval; + } + + dc |= (1<<0); /* DC.S */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_resume_or_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution, bool step) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct breakpoint *breakpoint = NULL; + int retval; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + if (!debug_execution) { + target_free_all_working_areas(target); + esirisc_add_breakpoints(target); + esirisc_add_watchpoints(target); + } + + if (current) + address = buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size); + else { + buf_set_u32(esirisc->epc->value, 0, esirisc->epc->size, address); + esirisc->epc->dirty = true; + esirisc->epc->valid = true; + } + + esirisc_restore_context(target); + + if (esirisc_has_cache(esirisc)) + esirisc_flush_caches(target); + + if (handle_breakpoints) { + breakpoint = breakpoint_find(target, address); + if (breakpoint != NULL) + esirisc_remove_breakpoint(target, breakpoint); + } + + if (step) { + esirisc_disable_interrupts(target); + esirisc_enable_step(target); + target->debug_reason = DBG_REASON_SINGLESTEP; + } else { + esirisc_disable_step(target); + esirisc_restore_interrupts(target); + target->debug_reason = DBG_REASON_NOTHALTED; + } + + esirisc_restore_hwdc(target); + + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + + register_cache_invalidate(esirisc->reg_cache); + + if (!debug_execution) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } else { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + } + + return ERROR_OK; +} + +static int esirisc_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + LOG_DEBUG("-"); + + return esirisc_resume_or_step(target, current, address, + handle_breakpoints, debug_execution, false); +} + +static int esirisc_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints) +{ + LOG_DEBUG("-"); + + return esirisc_resume_or_step(target, current, address, + handle_breakpoints, 0, true); +} + +static int esirisc_debug_step(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + esirisc_disable_interrupts(target); + esirisc_enable_step(target); + + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + + retval = esirisc_wait_debug_active(esirisc, STEP_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: step timed out", target_name(target)); + return retval; + } + + esirisc_disable_step(target); + esirisc_restore_interrupts(target); + + return ERROR_OK; +} + +static int esirisc_debug_reset(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_assert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to assert reset", target_name(target)); + return retval; + } + + retval = esirisc_jtag_deassert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to deassert reset", target_name(target)); + return retval; + } + + retval = esirisc_wait_debug_active(esirisc, RESET_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: reset timed out", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_debug_enable(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_enable_debug(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to enable debug mode", target_name(target)); + return retval; + } + + /* + * The debug clock is inactive until the first command is sent. + * If the target is stopped, we must first issue a reset before + * attempting further communication. This also handles unpowered + * targets, which will respond with all ones and appear active. + */ + if (esirisc_jtag_is_stopped(jtag_info)) { + LOG_INFO("%s: debug clock inactive; attempting debug reset", target_name(target)); + retval = esirisc_debug_reset(target); + if (retval != ERROR_OK) + return retval; + + if (esirisc_jtag_is_stopped(jtag_info)) { + LOG_ERROR("%s: target unresponsive; giving up", target_name(target)); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int esirisc_debug_entry(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct breakpoint *breakpoint; + + LOG_DEBUG("-"); + + esirisc_save_context(target); + + if (esirisc_has_cache(esirisc)) + esirisc_flush_caches(target); + + if (target->debug_reason != DBG_REASON_SINGLESTEP) { + esirisc_save_interrupts(target); + + uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size); + switch (eid) { + /* + * InstBreakpoint exceptions are also raised when a core is + * halted for debugging. The following is required to + * determine if a breakpoint was encountered. + */ + case EID_INST_BREAKPOINT: + breakpoint = breakpoint_find(target, + buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size)); + target->debug_reason = (breakpoint != NULL) ? + DBG_REASON_BREAKPOINT : DBG_REASON_DBGRQ; + break; + + /* + * eSi-RISC treats watchpoints similarly to breakpoints, + * however GDB will not request to step over the current + * instruction when a watchpoint fires. The following is + * required to resume the target. + */ + case EID_DATA_BREAKPOINT: + esirisc_remove_watchpoints(target); + esirisc_debug_step(target); + esirisc_add_watchpoints(target); + target->debug_reason = DBG_REASON_WATCHPOINT; + break; + + default: + target->debug_reason = DBG_REASON_DBGRQ; + } + } + + return ERROR_OK; +} + +static int esirisc_poll(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + retval = esirisc_jtag_enable_debug(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to poll target", target_name(target)); + return retval; + } + + if (esirisc_jtag_is_stopped(jtag_info)) { + LOG_ERROR("%s: target has stopped; reset required", target_name(target)); + target->state = TARGET_UNKNOWN; + return ERROR_TARGET_FAILURE; + } + + if (esirisc_jtag_is_debug_active(jtag_info)) { + if (target->state == TARGET_RUNNING || target->state == TARGET_RESET) { + target->state = TARGET_HALTED; + + retval = esirisc_debug_entry(target); + if (retval != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + + } else if (target->state == TARGET_HALTED || target->state == TARGET_RESET) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } + + return ERROR_OK; +} + +static int esirisc_assert_reset(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + if (jtag_get_reset_config() & RESET_HAS_SRST) { + jtag_add_reset(1, 1); + if ((jtag_get_reset_config() & RESET_SRST_PULLS_TRST) == 0) + jtag_add_reset(0, 1); + } else { + esirisc_remove_breakpoints(target); + esirisc_remove_watchpoints(target); + + retval = esirisc_jtag_assert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to assert reset", target_name(target)); + return retval; + } + } + + target->state = TARGET_RESET; + + register_cache_invalidate(esirisc->reg_cache); + + return ERROR_OK; +} + +static int esirisc_reset_entry(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t eta, epc; + int retval; + + LOG_DEBUG("-"); + + /* read exception table address */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETA, &eta); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETA", target_name(target)); + return retval; + } + + /* read reset entry point */ + retval = esirisc_jtag_read_word(jtag_info, eta + ENTRY_RESET, &epc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read address: 0x%" TARGET_PRIxADDR, target_name(target), + (target_addr_t)epc); + return retval; + } + + /* write reset entry point */ + retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_EPC, epc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: EPC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_deassert_reset(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + if (jtag_get_reset_config() & RESET_HAS_SRST) { + jtag_add_reset(0, 0); + + retval = esirisc_debug_enable(target); + if (retval != ERROR_OK) + return retval; + + retval = esirisc_debug_reset(target); + if (retval != ERROR_OK) + return retval; + + } else { + retval = esirisc_jtag_deassert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to deassert reset", target_name(target)); + return retval; + } + } + + retval = esirisc_wait_debug_active(esirisc, RESET_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: reset timed out", target_name(target)); + return retval; + } + + retval = esirisc_reset_entry(target); + if (retval != ERROR_OK) + return retval; + + esirisc_add_breakpoints(target); + esirisc_add_watchpoints(target); + + esirisc_restore_hwdc(target); + + if (!target->reset_halt) { + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + } + + return ERROR_OK; +} + +static int esirisc_arch_state(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + uint32_t epc = buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size); + uint32_t ecas = buf_get_u32(esirisc->ecas->value, 0, esirisc->ecas->size); + uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size); + uint32_t ed = buf_get_u32(esirisc->ed->value, 0, esirisc->ed->size); + + LOG_DEBUG("-"); + + const char *exception = "Unknown"; + if (eid < ARRAY_SIZE(esirisc_exceptions)) + exception = esirisc_exceptions[eid]; + + LOG_USER("target halted due to %s, exception: %s\n" + "EPC: 0x%" PRIx32 " ECAS: 0x%" PRIx32 " EID: 0x%" PRIx32 " ED: 0x%" PRIx32, + debug_reason_name(target), exception, epc, ecas, eid, ed); + + return ERROR_OK; +} + +static const char *esirisc_get_gdb_arch(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + /* + * Targets with the UNIFIED_ADDRESS_SPACE option disabled employ a + * Harvard architecture. This option is not exposed in a CSR, which + * requires additional configuration to properly interact with these + * targets in GDB (also see: `esirisc cache_arch`). + */ + if (esirisc->gdb_arch == NULL && target_was_examined(target)) + esirisc->gdb_arch = alloc_printf("esirisc:%d_bit_%d_reg_%s", + esirisc->num_bits, esirisc->num_regs, esirisc_cache_arch(esirisc)); + + return esirisc->gdb_arch; +} + +static int esirisc_get_gdb_reg_list(struct target *target, struct reg **reg_list[], + int *reg_list_size, enum target_register_class reg_class) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + *reg_list_size = ESIRISC_NUM_REGS; + + *reg_list = calloc(*reg_list_size, sizeof(struct reg *)); + if (*reg_list == NULL) + return ERROR_FAIL; + + if (reg_class == REG_CLASS_ALL) + for (int i = 0; i < *reg_list_size; ++i) + (*reg_list)[i] = esirisc->reg_cache->reg_list + i; + else { + for (int i = 0; i < esirisc->num_regs; ++i) + (*reg_list)[i] = esirisc->reg_cache->reg_list + i; + + (*reg_list)[ESIRISC_PC] = esirisc->reg_cache->reg_list + ESIRISC_PC; + (*reg_list)[ESIRISC_CAS] = esirisc->reg_cache->reg_list + ESIRISC_CAS; + } + + return ERROR_OK; +} + +static int esirisc_read_reg(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_reg(jtag_info, reg->number, &data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read register: %s", target_name(target), reg->name); + return retval; + } + + buf_set_u32(reg->value, 0, reg->size, data); + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_write_reg(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data = buf_get_u32(reg->value, 0, reg->size); + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_reg(jtag_info, reg->number, data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write register: %s", target_name(target), reg->name); + return retval; + } + + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_read_csr(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_csr(jtag_info, reg_info->bank, reg_info->csr, &data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: %s", target_name(target), reg->name); + return retval; + } + + buf_set_u32(reg->value, 0, reg->size, data); + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_write_csr(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data = buf_get_u32(reg->value, 0, reg->size); + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_csr(jtag_info, reg_info->bank, reg_info->csr, data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: %s", target_name(target), reg->name); + return retval; + } + + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_get_reg(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct target *target = esirisc->target; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + return reg_info->read(reg); +} + +static int esirisc_set_reg(struct reg *reg, uint8_t *buf) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct target *target = esirisc->target; + uint32_t value = buf_get_u32(buf, 0, reg->size); + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + buf_set_u32(reg->value, 0, reg->size, value); + reg->dirty = true; + reg->valid = true; + + return ERROR_OK; +} + +static const struct reg_arch_type esirisc_reg_type = { + .get = esirisc_get_reg, + .set = esirisc_set_reg, +}; + +static struct reg_cache *esirisc_build_reg_cache(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache); + struct reg_cache *cache = malloc(sizeof(struct reg_cache)); + struct reg *reg_list = calloc(ESIRISC_NUM_REGS, sizeof(struct reg)); + + LOG_DEBUG("-"); + + cache->name = "eSi-RISC registers"; + cache->next = NULL; + cache->reg_list = reg_list; + cache->num_regs = ESIRISC_NUM_REGS; + (*cache_p) = cache; + + esirisc->reg_cache = cache; + esirisc->epc = reg_list + ESIRISC_EPC; + esirisc->ecas = reg_list + ESIRISC_ECAS; + esirisc->eid = reg_list + ESIRISC_EID; + esirisc->ed = reg_list + ESIRISC_ED; + + for (int i = 0; i < esirisc->num_regs; ++i) { + struct reg *reg = reg_list + esirisc_regs[i].number; + struct esirisc_reg *reg_info = calloc(1, sizeof(struct esirisc_reg)); + + reg->name = esirisc_regs[i].name; + reg->number = esirisc_regs[i].number; + reg->value = calloc(1, DIV_ROUND_UP(esirisc->num_bits, 8)); + reg->size = esirisc->num_bits; + reg->reg_data_type = calloc(1, sizeof(struct reg_data_type)); + reg->reg_data_type->type = esirisc_regs[i].type; + reg->group = esirisc_regs[i].group; + reg_info->esirisc = esirisc; + reg_info->read = esirisc_read_reg; + reg_info->write = esirisc_write_reg; + reg->arch_info = reg_info; + reg->type = &esirisc_reg_type; + reg->exist = true; + } + + for (size_t i = 0; i < ARRAY_SIZE(esirisc_csrs); ++i) { + struct reg *reg = reg_list + esirisc_csrs[i].number; + struct esirisc_reg *reg_info = calloc(1, sizeof(struct esirisc_reg)); + + reg->name = esirisc_csrs[i].name; + reg->number = esirisc_csrs[i].number; + reg->value = calloc(1, DIV_ROUND_UP(esirisc->num_bits, 8)); + reg->size = esirisc->num_bits; + reg->reg_data_type = calloc(1, sizeof(struct reg_data_type)); + reg->reg_data_type->type = esirisc_csrs[i].type; + reg->group = esirisc_csrs[i].group; + reg_info->esirisc = esirisc; + reg_info->bank = esirisc_csrs[i].bank; + reg_info->csr = esirisc_csrs[i].csr; + reg_info->read = esirisc_read_csr; + reg_info->write = esirisc_write_csr; + reg->arch_info = reg_info; + reg->type = &esirisc_reg_type; + reg->exist = true; + } + + return cache; +} + +static int esirisc_identify(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t csr; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_ARCH0, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ARCH0", target_name(target)); + return retval; + } + + esirisc->num_bits = (csr >> 0) & 0x3f; /* ARCH0.B */ + esirisc->num_regs = (csr >> 10) & 0x3f; /* ARCH0.R */ + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_MEM, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: MEM", target_name(target)); + return retval; + } + + target->endianness = (csr & 1<<0) ? /* MEM.E */ + TARGET_BIG_ENDIAN : TARGET_LITTLE_ENDIAN; + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_IC, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: IC", target_name(target)); + return retval; + } + + esirisc->has_icache = !!(csr & 1<<0); /* IC.E */ + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DC, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DC", target_name(target)); + return retval; + } + + esirisc->has_dcache = !!(csr & 1<<0); /* DC.E */ + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DBG, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBG", target_name(target)); + return retval; + } + + esirisc->num_breakpoints = (csr >> 7) & 0xf; /* DBG.BP */ + esirisc->num_watchpoints = (csr >> 12) & 0xf; /* DBG.WP */ + + return ERROR_OK; +} + +static int esirisc_target_create(struct target *target, Jim_Interp *interp) +{ + struct jtag_tap *tap = target->tap; + struct esirisc_common *esirisc; + + if (tap == NULL) + return ERROR_FAIL; + + if (tap->ir_length != INSTR_LENGTH) { + LOG_ERROR("%s: invalid IR length; expected %d", target_name(target), + INSTR_LENGTH); + return ERROR_FAIL; + } + + esirisc = calloc(1, sizeof(struct esirisc_common)); + if (esirisc == NULL) + return ERROR_FAIL; + + esirisc->target = target; + esirisc->jtag_info.tap = tap; + target->arch_info = esirisc; + + return ERROR_OK; +} + +static int esirisc_init_target(struct command_context *cmd_ctx, struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + /* trap reset, error, and debug exceptions */ + esirisc->hwdc_save = HWDC_R | HWDC_E | HWDC_D; + + return ERROR_OK; +} + +static int esirisc_examine(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + if (!target_was_examined(target)) { + retval = esirisc_debug_enable(target); + if (retval != ERROR_OK) + return retval; + + /* + * In order to identify the target we must first halt the core. + * We quietly resume once identification has completed for those + * targets that were running when target_examine was called. + */ + if (esirisc_jtag_is_debug_active(jtag_info)) { + if (target->state == TARGET_UNKNOWN) + target->debug_reason = DBG_REASON_DBGRQ; + + target->state = TARGET_HALTED; + } else { + retval = esirisc_jtag_break(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to halt target", target_name(target)); + return retval; + } + + target->state = TARGET_RUNNING; + } + + retval = esirisc_identify(target); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to identify target", target_name(target)); + return retval; + } + + esirisc_build_reg_cache(target); + + esirisc_remove_breakpoints(target); + esirisc_remove_watchpoints(target); + + esirisc_disable_step(target); + esirisc_restore_hwdc(target); + + if (target->state == TARGET_HALTED) + esirisc_save_interrupts(target); + else { + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + } + + target_set_examined(target); + + LOG_INFO("%s: %d bit, %d registers, %s%s%s", target_name(target), + esirisc->num_bits, esirisc->num_regs, + target_endianness(target), + esirisc->has_icache ? ", icache" : "", + esirisc->has_dcache ? ", dcache" : ""); + + LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints", target_name(target), + esirisc->num_breakpoints, esirisc->num_watchpoints); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_esirisc_cache_arch_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct esirisc_common *esirisc = target_to_esirisc(target); + + if (CMD_ARGC > 0) { + if (strcmp(*CMD_ARGV, "harvard") == 0) + esirisc->cache_arch = ESIRISC_CACHE_HARVARD; + else if (strcmp(*CMD_ARGV, "von_neumann") == 0) + esirisc->cache_arch = ESIRISC_CACHE_VON_NEUMANN; + else { + LOG_ERROR("invalid cache_arch: %s", *CMD_ARGV); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + command_print(CMD_CTX, "esirisc cache_arch %s", esirisc_cache_arch(esirisc)); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_esirisc_flush_caches_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct esirisc_common *esirisc = target_to_esirisc(target); + int retval; + + if (!esirisc_has_cache(esirisc)) { + LOG_ERROR("target does not support caching"); + return ERROR_FAIL; + } + + retval = esirisc_flush_caches(target); + + command_print(CMD_CTX, "cache flush %s", + (retval == ERROR_OK) ? "successful" : "failed"); + + return retval; +} + +static const struct { + const char *name; + int mask; +} esirisc_hwdc_masks[] = { + { "reset", HWDC_R }, + { "interrupt", HWDC_I }, + { "syscall", HWDC_S }, + { "error", HWDC_E }, + { "debug", HWDC_D }, +}; + +static int esirisc_find_hwdc_mask(const char *name) +{ + for (size_t i = 0; i < ARRAY_SIZE(esirisc_hwdc_masks); ++i) + if (strcmp(esirisc_hwdc_masks[i].name, name) == 0) + return esirisc_hwdc_masks[i].mask; + + return -1; +} + +COMMAND_HANDLER(handle_esirisc_hwdc_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct esirisc_common *esirisc = target_to_esirisc(target); + + if (CMD_ARGC > 0) { + if (strcmp(CMD_ARGV[0], "all") == 0) + esirisc->hwdc_save = HWDC_R | HWDC_I | HWDC_S | HWDC_E | HWDC_D; + else { + esirisc->hwdc_save = 0; + if (strcmp(CMD_ARGV[0], "none") != 0) { + while (CMD_ARGC-- > 0) { + int mask = esirisc_find_hwdc_mask(CMD_ARGV[CMD_ARGC]); + if (mask < 0) { + LOG_ERROR("invalid mask: %s", CMD_ARGV[CMD_ARGC]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + esirisc->hwdc_save |= mask; + } + } + } + } + + for (size_t i = 0; i < ARRAY_SIZE(esirisc_hwdc_masks); ++i) + command_print(CMD_CTX, "%9s: %s", esirisc_hwdc_masks[i].name, + (esirisc->hwdc_save & esirisc_hwdc_masks[i].mask) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +static const struct command_registration esirisc_exec_command_handlers[] = { + { + .name = "cache_arch", + .handler = handle_esirisc_cache_arch_command, + .mode = COMMAND_ANY, + .help = "configure cache architecture", + .usage = "['harvard'|'von_neumann']", + }, + { + .name = "flush_caches", + .handler = handle_esirisc_flush_caches_command, + .mode = COMMAND_EXEC, + .help = "flush instruction and data caches", + .usage = "", + }, + { + .name = "hwdc", + .handler = handle_esirisc_hwdc_command, + .mode = COMMAND_ANY, + .help = "configure hardware debug control", + .usage = "['all'|'none'|mask ...]", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration esirisc_command_handlers[] = { + { + .name = "esirisc", + .mode = COMMAND_ANY, + .help = "eSi-RISC command group", + .usage = "", + .chain = esirisc_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct target_type esirisc_target = { + .name = "esirisc", + + .poll = esirisc_poll, + .arch_state = esirisc_arch_state, + + .halt = esirisc_halt, + .resume = esirisc_resume, + .step = esirisc_step, + + .assert_reset = esirisc_assert_reset, + .deassert_reset = esirisc_deassert_reset, + + .get_gdb_arch = esirisc_get_gdb_arch, + .get_gdb_reg_list = esirisc_get_gdb_reg_list, + + .read_memory = esirisc_read_memory, + .write_memory = esirisc_write_memory, + .checksum_memory = esirisc_checksum_memory, + + .add_breakpoint = esirisc_add_breakpoint, + .remove_breakpoint = esirisc_remove_breakpoint, + .add_watchpoint = esirisc_add_watchpoint, + .remove_watchpoint = esirisc_remove_watchpoint, + + .commands = esirisc_command_handlers, + + .target_create = esirisc_target_create, + .init_target = esirisc_init_target, + .examine = esirisc_examine, +}; diff --git a/src/target/esirisc.h b/src/target/esirisc.h new file mode 100644 index 000000000..bb50652aa --- /dev/null +++ b/src/target/esirisc.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * 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 . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESIRISC_H +#define OPENOCD_TARGET_ESIRISC_H + +#include +#include +#include + +#include "esirisc_jtag.h" +#include "esirisc_regs.h" + +#define MAX_BREAKPOINTS 8 +#define MAX_WATCHPOINTS 8 + +/* Exception IDs */ +#define EID_RESET 0x00 +#define EID_HARDWARE_FAILURE 0x01 +#define EID_NMI 0x02 +#define EID_INST_BREAKPOINT 0x03 +#define EID_DATA_BREAKPOINT 0x04 +#define EID_UNSUPPORTED 0x05 +#define EID_PRIVILEGE_VIOLATION 0x06 +#define EID_INST_BUS_ERROR 0x07 +#define EID_DATA_BUS_ERROR 0x08 +#define EID_ALIGNMENT_ERROR 0x09 +#define EID_ARITHMETIC_ERROR 0x0a +#define EID_SYSTEM_CALL 0x0b +#define EID_MEMORY_MANAGEMENT 0x0c +#define EID_UNRECOVERABLE 0x0d +#define EID_INTERRUPTn 0x20 + +/* Exception Entry Points */ +#define ENTRY_RESET 0x00 +#define ENTRY_UNRECOVERABLE 0x01 +#define ENTRY_HARDWARE_FAILURE 0x02 +#define ENTRY_RUNTIME 0x03 +#define ENTRY_MEMORY 0x04 +#define ENTRY_SYSCALL 0x05 +#define ENTRY_DEBUG 0x06 +#define ENTRY_NMI 0x07 +#define ENTRY_INTERRUPTn 0x08 + +/* Hardware Debug Control */ +#define HWDC_R (1<<4) /* Reset & Hardware Failure */ +#define HWDC_I (1<<3) /* Interrupts */ +#define HWDC_S (1<<2) /* System Calls */ +#define HWDC_E (1<<1) /* Program Errors */ +#define HWDC_D (1<<0) /* Debug Exceptions */ + +enum esirisc_cache { + ESIRISC_CACHE_VON_NEUMANN, + ESIRISC_CACHE_HARVARD, +}; + +struct esirisc_common { + struct target *target; + struct esirisc_jtag jtag_info; + enum esirisc_cache cache_arch; + char *gdb_arch; + + struct reg_cache *reg_cache; + struct reg *epc; + struct reg *ecas; + struct reg *eid; + struct reg *ed; + uint32_t etc_save; + uint32_t hwdc_save; + + int num_bits; + int num_regs; + bool has_icache; + bool has_dcache; + int num_breakpoints; + int num_watchpoints; + + struct breakpoint *breakpoints_p[MAX_BREAKPOINTS]; + struct watchpoint *watchpoints_p[MAX_WATCHPOINTS]; +}; + +union esirisc_memory { + uint32_t word; + uint16_t hword; + uint8_t byte; +}; + +struct esirisc_reg { + struct esirisc_common *esirisc; + + uint8_t bank; + uint8_t csr; + + int (*read)(struct reg *reg); + int (*write)(struct reg *reg); +}; + +static inline struct esirisc_common *target_to_esirisc(struct target *target) +{ + return (struct esirisc_common *)target->arch_info; +} + +static inline char *esirisc_cache_arch(struct esirisc_common *esirisc) +{ + return esirisc->cache_arch == ESIRISC_CACHE_HARVARD ? "harvard" : "von_neumann"; +} + +static inline bool esirisc_has_cache(struct esirisc_common *esirisc) +{ + return esirisc->has_icache || esirisc->has_dcache; +} + +#endif /* OPENOCD_TARGET_ESIRISC_H */ diff --git a/src/target/esirisc_jtag.c b/src/target/esirisc_jtag.c new file mode 100644 index 000000000..8ab47fa8f --- /dev/null +++ b/src/target/esirisc_jtag.c @@ -0,0 +1,514 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * 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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "esirisc_jtag.h" + +static void esirisc_jtag_set_instr(struct esirisc_jtag *jtag_info, uint32_t new_instr) +{ + struct jtag_tap *tap = jtag_info->tap; + + if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) { + struct scan_field field; + uint8_t t[4]; + + field.num_bits = tap->ir_length; + field.out_value = t; + buf_set_u32(t, 0, field.num_bits, new_instr); + field.in_value = NULL; + + jtag_add_ir_scan(tap, &field, TAP_IDLE); + } +} + +/* + * The data register is latched every 8 bits while in the Shift-DR state + * (Update-DR is not supported). This necessitates prepending padding + * bits to ensure data is aligned when multiple TAPs are present. + */ +static int esirisc_jtag_get_padding(void) +{ + int padding = 0; + int bypass_devices = 0; + + for (struct jtag_tap *tap = jtag_tap_next_enabled(NULL); tap != NULL; + tap = jtag_tap_next_enabled(tap)) + if (tap->bypass) + bypass_devices++; + + int num_bits = bypass_devices % 8; + if (num_bits > 0) + padding = 8 - num_bits; + + return padding; +} + +static int esirisc_jtag_count_bits(int num_fields, struct scan_field *fields) +{ + int bit_count = 0; + + for (int i = 0; i < num_fields; ++i) + bit_count += fields[i].num_bits; + + return bit_count; +} + +/* + * Data received from the target will be byte-stuffed if it contains + * either the pad byte (0xAA) or stuffing marker (0x55). Buffers should + * be sized twice the expected length to account for stuffing overhead. + */ +static void esirisc_jtag_unstuff(uint8_t *data, size_t len) +{ + uint8_t *r, *w; + uint8_t *end; + + r = w = data; + end = data + len; + while (r < end) { + if (*r == STUFF_MARKER) { + r++; /* skip stuffing marker */ + assert(r < end); + *w++ = *r++ ^ STUFF_MARKER; + } else + *w++ = *r++; + } +} + +/* + * The eSi-Debug protocol defines a byte-oriented command/response + * channel that operates over serial or JTAG. While not strictly + * required, separate DR scans are used for sending and receiving data. + * This allows the TAP to recover gracefully if the byte stream is + * corrupted at the expense of sending additional padding bits. + */ + +static int esirisc_jtag_send(struct esirisc_jtag *jtag_info, uint8_t command, + int num_out_fields, struct scan_field *out_fields) +{ + int num_fields = 2 + num_out_fields; + struct scan_field *fields = cmd_queue_alloc(num_fields * sizeof(struct scan_field)); + + esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG); + + fields[0].num_bits = esirisc_jtag_get_padding(); + fields[0].out_value = NULL; + fields[0].in_value = NULL; + + fields[1].num_bits = 8; + fields[1].out_value = &command; + fields[1].in_value = NULL; + + /* append command data */ + for (int i = 0; i < num_out_fields; ++i) + jtag_scan_field_clone(&fields[2+i], &out_fields[i]); + + jtag_add_dr_scan(jtag_info->tap, num_fields, fields, TAP_IDLE); + + return jtag_execute_queue(); +} + +static int esirisc_jtag_recv(struct esirisc_jtag *jtag_info, + int num_in_fields, struct scan_field *in_fields) +{ + int num_in_bits = esirisc_jtag_count_bits(num_in_fields, in_fields); + int num_in_bytes = DIV_ROUND_UP(num_in_bits, 8); + + struct scan_field fields[3]; + uint8_t r[num_in_bytes * 2]; + + esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG); + + fields[0].num_bits = esirisc_jtag_get_padding() + 1; + fields[0].out_value = NULL; + fields[0].in_value = NULL; + + fields[1].num_bits = 8; + fields[1].out_value = NULL; + fields[1].in_value = &jtag_info->status; + + fields[2].num_bits = num_in_bits * 2; + fields[2].out_value = NULL; + fields[2].in_value = r; + + jtag_add_dr_scan(jtag_info->tap, ARRAY_SIZE(fields), fields, TAP_IDLE); + + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + /* unstuff response data and write back to caller */ + if (num_in_fields > 0) { + esirisc_jtag_unstuff(r, ARRAY_SIZE(r)); + + int bit_count = 0; + for (int i = 0; i < num_in_fields; ++i) { + buf_set_buf(r, bit_count, in_fields[i].in_value, 0, in_fields[i].num_bits); + bit_count += in_fields[i].num_bits; + } + } + + return ERROR_OK; +} + +static int esirisc_jtag_check_status(struct esirisc_jtag *jtag_info) +{ + uint8_t eid = esirisc_jtag_get_eid(jtag_info); + if (eid != EID_NONE) { + LOG_ERROR("esirisc_jtag: bad status: 0x%02" PRIx32 " (DA: %" PRId32 ", " + "S: %" PRId32 ", EID: 0x%02" PRIx32 ")", + jtag_info->status, esirisc_jtag_is_debug_active(jtag_info), + esirisc_jtag_is_stopped(jtag_info), eid); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int esirisc_jtag_send_and_recv(struct esirisc_jtag *jtag_info, uint8_t command, + int num_out_fields, struct scan_field *out_fields, + int num_in_fields, struct scan_field *in_fields) +{ + int retval; + + jtag_info->status = 0; /* clear status */ + + retval = esirisc_jtag_send(jtag_info, command, num_out_fields, out_fields); + if (retval != ERROR_OK) { + LOG_ERROR("esirisc_jtag: send failed (command: 0x%02" PRIx32 ")", command); + return ERROR_FAIL; + } + + retval = esirisc_jtag_recv(jtag_info, num_in_fields, in_fields); + if (retval != ERROR_OK) { + LOG_ERROR("esirisc_jtag: recv failed (command: 0x%02" PRIx32 ")", command); + return ERROR_FAIL; + } + + return esirisc_jtag_check_status(jtag_info); +} + +/* + * Status is automatically updated after each command completes; + * these functions make each field available to the caller. + */ + +bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info) +{ + return !!(jtag_info->status & 1<<7); /* DA */ +} + +bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info) +{ + return !!(jtag_info->status & 1<<6); /* S */ +} + +uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info) +{ + return jtag_info->status & 0x3f; /* EID */ +} + +/* + * Most commands manipulate target data (eg. memory and registers); each + * command returns a status byte that indicates success. Commands must + * transmit multibyte values in big-endian order, however response + * values are in little-endian order. Target endianness does not have an + * effect on this ordering. + */ + +int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t *data) +{ + struct scan_field out_fields[1]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[1]; + + in_fields[0].num_bits = 8; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_BYTE, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = *d; + + return ERROR_OK; +} + +int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t *data) +{ + struct scan_field out_fields[1]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[2]; + + in_fields[0].num_bits = 16; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_HWORD, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u16(d); + + return ERROR_OK; +} + +int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t *data) +{ + struct scan_field out_fields[1]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[4]; + + in_fields[0].num_bits = 32; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_WORD, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u32(d); + + return ERROR_OK; +} + +int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t data) +{ + struct scan_field out_fields[2]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 8; + out_fields[1].out_value = &data; + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_BYTE, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t data) +{ + struct scan_field out_fields[2]; + uint8_t a[4], d[2]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 16; + out_fields[1].out_value = d; + h_u16_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_HWORD, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t data) +{ + struct scan_field out_fields[2]; + uint8_t a[4], d[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 32; + out_fields[1].out_value = d; + h_u32_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_WORD, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t *data) +{ + struct scan_field out_fields[1]; + + out_fields[0].num_bits = 8; + out_fields[0].out_value = ® + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[4]; + + in_fields[0].num_bits = 32; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_REG, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u32(d); + + return ERROR_OK; +} + +int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t data) +{ + struct scan_field out_fields[2]; + uint8_t d[4]; + + out_fields[0].num_bits = 8; + out_fields[0].out_value = ® + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 32; + out_fields[1].out_value = d; + h_u32_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_REG, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t *data) +{ + struct scan_field out_fields[1]; + uint8_t c[2]; + + out_fields[0].num_bits = 16; + out_fields[0].out_value = c; + h_u16_to_be(c, (csr << 5) | bank); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[4]; + + in_fields[0].num_bits = 32; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_CSR, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u32(d); + + return ERROR_OK; +} + +int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t data) +{ + struct scan_field out_fields[2]; + uint8_t c[2], d[4]; + + out_fields[0].num_bits = 16; + out_fields[0].out_value = c; + h_u16_to_be(c, (csr << 5) | bank); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 32; + out_fields[1].out_value = d; + h_u32_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_CSR, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +/* + * Control commands affect CPU operation; these commands send no data + * and return a status byte. + */ + +static inline int esirisc_jtag_send_ctrl(struct esirisc_jtag *jtag_info, uint8_t command) +{ + return esirisc_jtag_send_and_recv(jtag_info, command, 0, NULL, 0, NULL); +} + +int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ENABLE_DEBUG); +} + +int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DISABLE_DEBUG); +} + +int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ASSERT_RESET); +} + +int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DEASSERT_RESET); +} + +int esirisc_jtag_break(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_BREAK); +} + +int esirisc_jtag_continue(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_CONTINUE); +} + +int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_FLUSH_CACHES); +} diff --git a/src/target/esirisc_jtag.h b/src/target/esirisc_jtag.h new file mode 100644 index 000000000..8189ddc6c --- /dev/null +++ b/src/target/esirisc_jtag.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * 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 . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESIRISC_JTAG_H +#define OPENOCD_TARGET_ESIRISC_JTAG_H + +#include + +/* TAP Instructions */ +#define INSTR_IDCODE 0x8 +#define INSTR_DEBUG 0x9 +#define INSTR_BYPASS 0xf +#define INSTR_LENGTH 4 + +/* eSi-Debug Commands */ +#define DEBUG_NOP 0x00 +#define DEBUG_READ_BYTE 0x10 +#define DEBUG_READ_HWORD 0x20 +#define DEBUG_READ_WORD 0x30 +#define DEBUG_WRITE_BYTE 0x60 +#define DEBUG_WRITE_HWORD 0x70 +#define DEBUG_WRITE_WORD 0x80 +#define DEBUG_READ_REG 0xb0 +#define DEBUG_WRITE_REG 0xc0 +#define DEBUG_READ_CSR 0xd0 +#define DEBUG_WRITE_CSR 0xe0 +#define DEBUG_ENABLE_DEBUG 0xf0 +#define DEBUG_DISABLE_DEBUG 0xf2 +#define DEBUG_ASSERT_RESET 0xf4 +#define DEBUG_DEASSERT_RESET 0xf6 +#define DEBUG_BREAK 0xf8 +#define DEBUG_CONTINUE 0xfa +#define DEBUG_FLUSH_CACHES 0xfc + +/* Exception IDs */ +#define EID_OVERFLOW 0x3d +#define EID_CANT_DEBUG 0x3e +#define EID_NONE 0x3f + +/* Byte Stuffing */ +#define STUFF_MARKER 0x55 +#define PAD_BYTE 0xaa + +struct esirisc_jtag { + struct jtag_tap *tap; + uint8_t status; +}; + +bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info); +bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info); +uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, + uint32_t address, uint8_t *data); +int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, + uint32_t address, uint16_t *data); +int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, + uint32_t address, uint32_t *data); + +int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, + uint32_t address, uint8_t data); +int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, + uint32_t address, uint16_t data); +int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, + uint32_t address, uint32_t data); + +int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, + uint8_t reg, uint32_t *data); +int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, + uint8_t reg, uint32_t data); + +int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, + uint8_t bank, uint8_t csr, uint32_t *data); +int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, + uint8_t bank, uint8_t csr, uint32_t data); + +int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info); +int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info); +int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_break(struct esirisc_jtag *jtag_info); +int esirisc_jtag_continue(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info); + +#endif /* OPENOCD_TARGET_ESIRISC_JTAG_H */ diff --git a/src/target/esirisc_regs.h b/src/target/esirisc_regs.h new file mode 100644 index 000000000..ad3385819 --- /dev/null +++ b/src/target/esirisc_regs.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * 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 . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESIRISC_REGS_H +#define OPENOCD_TARGET_ESIRISC_REGS_H + +enum esirisc_reg_num { + ESIRISC_SP, + ESIRISC_RA, + ESIRISC_R2, + ESIRISC_R3, + ESIRISC_R4, + ESIRISC_R5, + ESIRISC_R6, + ESIRISC_R7, + ESIRISC_R8, + ESIRISC_R9, + ESIRISC_R10, + ESIRISC_R11, + ESIRISC_R12, + ESIRISC_R13, + ESIRISC_R14, + ESIRISC_R15, + ESIRISC_R16, + ESIRISC_R17, + ESIRISC_R18, + ESIRISC_R19, + ESIRISC_R20, + ESIRISC_R21, + ESIRISC_R22, + ESIRISC_R23, + ESIRISC_R24, + ESIRISC_R25, + ESIRISC_R26, + ESIRISC_R27, + ESIRISC_R28, + ESIRISC_R29, + ESIRISC_R30, + ESIRISC_R31, + + ESIRISC_V0, + ESIRISC_V1, + ESIRISC_V2, + ESIRISC_V3, + ESIRISC_V4, + ESIRISC_V5, + ESIRISC_V6, + ESIRISC_V7, + ESIRISC_V8, + ESIRISC_V9, + ESIRISC_V10, + ESIRISC_V11, + ESIRISC_V12, + ESIRISC_V13, + ESIRISC_V14, + ESIRISC_V15, + ESIRISC_V16, + ESIRISC_V17, + ESIRISC_V18, + ESIRISC_V19, + ESIRISC_V20, + ESIRISC_V21, + ESIRISC_V22, + ESIRISC_V23, + ESIRISC_V24, + ESIRISC_V25, + ESIRISC_V26, + ESIRISC_V27, + ESIRISC_V28, + ESIRISC_V29, + ESIRISC_V30, + ESIRISC_V31, + + ESIRISC_A0, + ESIRISC_A1, + ESIRISC_A2, + ESIRISC_A3, + ESIRISC_A4, + ESIRISC_A5, + ESIRISC_A6, + ESIRISC_A7, + + ESIRISC_PC, + ESIRISC_CAS, + ESIRISC_TC, + ESIRISC_ETA, + ESIRISC_ETC, + ESIRISC_EPC, + ESIRISC_ECAS, + ESIRISC_EID, + ESIRISC_ED, + ESIRISC_IP, + ESIRISC_IM, + ESIRISC_IS, + ESIRISC_IT, + + ESIRISC_NUM_REGS, +}; + +/* CSR Banks */ +#define CSR_THREAD 0x00 +#define CSR_INTERRUPT 0x01 +#define CSR_DEBUG 0x04 +#define CSR_CONFIG 0x05 +#define CSR_TRACE 0x09 + +/* Thread CSRs */ +#define CSR_THREAD_TC 0x00 /* Thread Control */ +#define CSR_THREAD_PC 0x01 /* Program Counter */ +#define CSR_THREAD_CAS 0x02 /* Comparison & Arithmetic Status */ +#define CSR_THREAD_AC 0x03 /* Arithmetic Control */ +#define CSR_THREAD_LF 0x04 /* Locked Flag */ +#define CSR_THREAD_LA 0x05 /* Locked Address */ +#define CSR_THREAD_ETA 0x07 /* Exception Table Address */ +#define CSR_THREAD_ETC 0x08 /* Exception TC */ +#define CSR_THREAD_EPC 0x09 /* Exception PC */ +#define CSR_THREAD_ECAS 0x0a /* Exception CAS */ +#define CSR_THREAD_EID 0x0b /* Exception ID */ +#define CSR_THREAD_ED 0x0c /* Exception Data */ + +/* Interrupt CSRs */ +#define CSR_INTERRUPT_IP 0x00 /* Interrupt Pending */ +#define CSR_INTERRUPT_IA 0x01 /* Interrupt Acknowledge */ +#define CSR_INTERRUPT_IM 0x02 /* Interrupt Mask */ +#define CSR_INTERRUPT_IS 0x03 /* Interrupt Sense */ +#define CSR_INTERRUPT_IT 0x04 /* Interrupt Trigger */ + +/* Debug CSRs */ +#define CSR_DEBUG_DC 0x00 /* Debug Control */ +#define CSR_DEBUG_IBC 0x01 /* Instruction Breakpoint Control */ +#define CSR_DEBUG_DBC 0x02 /* Data Breakpoint Control */ +#define CSR_DEBUG_HWDC 0x03 /* Hardware Debug Control */ +#define CSR_DEBUG_DBS 0x04 /* Data Breakpoint Size */ +#define CSR_DEBUG_DBR 0x05 /* Data Breakpoint Range */ +#define CSR_DEBUG_IBAn 0x08 /* Instruction Breakpoint Address [0..7] */ +#define CSR_DEBUG_DBAn 0x10 /* Data Breakpoint Address [0..7] */ + +/* Configuration CSRs */ +#define CSR_CONFIG_ARCH0 0x00 /* Architectural Configuration 0 */ +#define CSR_CONFIG_ARCH1 0x01 /* Architectural Configuration 1 */ +#define CSR_CONFIG_ARCH2 0x02 /* Architectural Configuration 2 */ +#define CSR_CONFIG_ARCH3 0x03 /* Architectural Configuration 3 */ +#define CSR_CONFIG_MEM 0x04 /* Memory Configuration */ +#define CSR_CONFIG_IC 0x05 /* Instruction Cache Configuration */ +#define CSR_CONFIG_DC 0x06 /* Data Cache Configuration */ +#define CSR_CONFIG_INT 0x07 /* Interrupt Configuration */ +#define CSR_CONFIG_ISAn 0x08 /* Instruction Set Configuration [0..6] */ +#define CSR_CONFIG_DBG 0x0f /* Debug Configuration */ +#define CSR_CONFIG_MID 0x10 /* Manufacturer ID */ +#define CSR_CONFIG_REV 0x11 /* Revision Number */ +#define CSR_CONFIG_MPID 0x12 /* Mulitprocessor ID */ +#define CSR_CONFIG_FREQn 0x13 /* Frequency [0..2] */ +#define CSR_CONFIG_TRACE 0x16 /* Trace Configuration */ + +/* Trace CSRs */ +#define CSR_TRACE_CONTROL 0x00 +#define CSR_TRACE_STATUS 0x01 +#define CSR_TRACE_BUFFER_START 0x02 +#define CSR_TRACE_BUFFER_END 0x03 +#define CSR_TRACE_BUFFER_CUR 0x04 +#define CSR_TRACE_TRIGGER 0x05 +#define CSR_TRACE_START_DATA 0x06 +#define CSR_TRACE_START_MASK 0x07 +#define CSR_TRACE_STOP_DATA 0x08 +#define CSR_TRACE_STOP_MASK 0x09 +#define CSR_TRACE_DELAY 0x0a + +#endif /* OPENOCD_TARGET_ESIRISC_REGS_H */ diff --git a/src/target/mem_ap.c b/src/target/mem_ap.c new file mode 100644 index 000000000..3a2d4b7c5 --- /dev/null +++ b/src/target/mem_ap.c @@ -0,0 +1,181 @@ +/***************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * * + * 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. * + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "target.h" +#include "target_type.h" +#include "arm.h" +#include "arm_adi_v5.h" + +#include + +struct mem_ap { + struct arm arm; + struct adiv5_ap *ap; + int ap_num; +}; + +static int mem_ap_target_create(struct target *target, Jim_Interp *interp) +{ + struct mem_ap *mem_ap = calloc(1, sizeof(struct mem_ap)); + struct adiv5_private_config *pc; + + pc = (struct adiv5_private_config *)target->private_config; + if (pc == NULL) + return ERROR_FAIL; + + if (pc->ap_num == DP_APSEL_INVALID) { + LOG_ERROR("AP number not specified"); + return ERROR_FAIL; + } + + mem_ap->ap_num = pc->ap_num; + mem_ap->arm.common_magic = ARM_COMMON_MAGIC; + mem_ap->arm.dap = pc->dap; + + target->arch_info = mem_ap; + + return ERROR_OK; +} + +static int mem_ap_init_target(struct command_context *cmd_ctx, struct target *target) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_UNKNOWN; + return ERROR_OK; +} + +static int mem_ap_arch_state(struct target *target) +{ + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int mem_ap_poll(struct target *target) +{ + if (target->state == TARGET_UNKNOWN) + target->state = TARGET_RUNNING; + + return ERROR_OK; +} + +static int mem_ap_halt(struct target *target) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_HALTED; + return ERROR_OK; +} + +static int mem_ap_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_RUNNING; + return ERROR_OK; +} + +static int mem_ap_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_HALTED; + return ERROR_OK; +} + +static int mem_ap_assert_reset(struct target *target) +{ + target->state = TARGET_RESET; + + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int mem_ap_examine(struct target *target) +{ + struct mem_ap *mem_ap = target->arch_info; + + if (!target_was_examined(target)) { + mem_ap->ap = dap_ap(mem_ap->arm.dap, mem_ap->ap_num); + target_set_examined(target); + target->state = TARGET_UNKNOWN; + return mem_ap_init(mem_ap->ap); + } + + return ERROR_OK; +} + +static int mem_ap_deassert_reset(struct target *target) +{ + if (target->reset_halt) + target->state = TARGET_HALTED; + else + target->state = TARGET_RUNNING; + + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int mem_ap_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + struct mem_ap *mem_ap = target->arch_info; + + LOG_DEBUG("Reading memory at physical address 0x" TARGET_ADDR_FMT + "; size %" PRId32 "; count %" PRId32, address, size, count); + + if (count == 0 || buffer == NULL) + return ERROR_COMMAND_SYNTAX_ERROR; + + return mem_ap_read_buf(mem_ap->ap, buffer, size, count, address); +} + +static int mem_ap_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, + const uint8_t *buffer) +{ + struct mem_ap *mem_ap = target->arch_info; + + LOG_DEBUG("Writing memory at physical address 0x" TARGET_ADDR_FMT + "; size %" PRId32 "; count %" PRId32, address, size, count); + + if (count == 0 || buffer == NULL) + return ERROR_COMMAND_SYNTAX_ERROR; + + return mem_ap_write_buf(mem_ap->ap, buffer, size, count, address); +} + +struct target_type mem_ap_target = { + .name = "mem_ap", + + .target_create = mem_ap_target_create, + .init_target = mem_ap_init_target, + .examine = mem_ap_examine, + .target_jim_configure = adiv5_jim_configure, + + .poll = mem_ap_poll, + .arch_state = mem_ap_arch_state, + + .halt = mem_ap_halt, + .resume = mem_ap_resume, + .step = mem_ap_step, + + .assert_reset = mem_ap_assert_reset, + .deassert_reset = mem_ap_deassert_reset, + + .read_memory = mem_ap_read_memory, + .write_memory = mem_ap_write_memory, +}; diff --git a/src/target/register.c b/src/target/register.c index 1d63e12f7..850641448 100644 --- a/src/target/register.c +++ b/src/target/register.c @@ -44,6 +44,8 @@ struct reg *register_get_by_name(struct reg_cache *first, while (cache) { for (i = 0; i < cache->num_regs; i++) { + if (cache->reg_list[i].exist == false) + continue; if (strcmp(cache->reg_list[i].name, name) == 0) return &(cache->reg_list[i]); } @@ -84,6 +86,8 @@ void register_cache_invalidate(struct reg_cache *cache) struct reg *reg = cache->reg_list; for (unsigned n = cache->num_regs; n != 0; n--, reg++) { + if (reg->exist == false) + continue; reg->valid = 0; reg->dirty = 0; } diff --git a/src/target/target.c b/src/target/target.c index 8240e6544..7de3e7615 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -108,6 +108,8 @@ 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, @@ -141,6 +143,8 @@ static struct target_type *target_types[] = { &quark_d20xx_target, &stm8_target, &riscv_target, + &mem_ap_target, + &esirisc_target, #if BUILD_TARGET64 &aarch64_target, #endif @@ -513,9 +517,7 @@ struct target *get_target_by_num(int num) struct target *get_current_target(struct command_context *cmd_ctx) { - struct target *target = cmd_ctx->current_target_override - ? cmd_ctx->current_target_override - : cmd_ctx->current_target; + struct target *target = get_current_target_or_null(cmd_ctx); if (target == NULL) { LOG_ERROR("BUG: current_target out of bounds"); @@ -525,6 +527,13 @@ struct target *get_current_target(struct command_context *cmd_ctx) 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; @@ -1043,6 +1052,9 @@ int target_run_flash_async_algorithm(struct target *target, retval = target_write_u32(target, wp_addr, wp); if (retval != ERROR_OK) break; + + /* Avoid GDB timeouts */ + keep_alive(); } if (retval != ERROR_OK) { @@ -1197,12 +1209,29 @@ int target_hit_watchpoint(struct target *target, 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) { @@ -1927,6 +1956,7 @@ static void target_destroy(struct target *target) target->smp = 0; } + free(target->gdb_port_override); free(target->type); free(target->trace_info); free(target->fileio_info); @@ -2792,6 +2822,8 @@ COMMAND_HANDLER(handle_reg_command) 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) { @@ -2847,14 +2879,15 @@ COMMAND_HANDLER(handle_reg_command) /* access a single register by its name */ reg = register_get_by_name(target->reg_cache, CMD_ARGV[0], 1); - if (!reg) { - command_print(CMD_CTX, "register %s not found in current target", CMD_ARGV[0]); - return ERROR_OK; - } + 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')))) { @@ -2898,6 +2931,10 @@ COMMAND_HANDLER(handle_reg_command) } 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) @@ -4550,6 +4587,7 @@ enum target_cfg_param { TCFG_DBGBASE, TCFG_RTOS, TCFG_DEFER_EXAMINE, + TCFG_GDB_PORT, }; static Jim_Nvp nvp_config_opts[] = { @@ -4565,6 +4603,7 @@ static Jim_Nvp nvp_config_opts[] = { { .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 } }; @@ -4852,6 +4891,20 @@ no_params: /* 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) */ @@ -5626,6 +5679,8 @@ static int target_create(Jim_GetOptInfo *goi) 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); @@ -5648,6 +5703,7 @@ static int target_create(Jim_GetOptInfo *goi) } if (e != JIM_OK) { + free(target->gdb_port_override); free(target->type); free(target); return e; @@ -5665,6 +5721,7 @@ static int target_create(Jim_GetOptInfo *goi) 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); diff --git a/src/target/target.h b/src/target/target.h index 51a5b6935..fb9d71465 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -206,6 +206,8 @@ struct target { /* file-I/O information for host to do syscall */ struct gdb_fileio_info *fileio_info; + char *gdb_port_override; /* target-specific override for gdb_port */ + /* The semihosting information, extracted from the target. */ struct semihosting *semihosting; }; @@ -223,6 +225,13 @@ struct gdb_fileio_info { uint64_t param_4; }; +/** Returns a description of the endianness for the specified target. */ +static inline const char *target_endianness(struct target *target) +{ + return (target->endianness == TARGET_ENDIAN_UNKNOWN) ? "unknown" : + (target->endianness == TARGET_BIG_ENDIAN) ? "big endian" : "little endian"; +} + /** Returns the instance-specific name of the specified target. */ static inline const char *target_name(struct target *target) { @@ -387,6 +396,7 @@ int target_call_timer_callbacks_now(void); struct target *get_target_by_num(int num); struct target *get_current_target(struct command_context *cmd_ctx); +struct target *get_current_target_or_null(struct command_context *cmd_ctx); struct target *get_target(const char *id); /** @@ -470,6 +480,13 @@ int target_remove_watchpoint(struct target *target, int target_hit_watchpoint(struct target *target, struct watchpoint **watchpoint); +/** + * Obtain the architecture for GDB. + * + * This routine is a wrapper for target->type->get_gdb_arch. + */ +const char *target_get_gdb_arch(struct target *target); + /** * Obtain the registers for GDB. * @@ -479,6 +496,13 @@ int target_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class); +/** + * Check if @a target allows GDB connections. + * + * Some target do not implement the necessary code required by GDB. + */ +bool target_supports_gdb_connection(struct target *target); + /** * Step the target. * diff --git a/src/target/target_type.h b/src/target/target_type.h index fbbd57d98..a8928911f 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -88,6 +88,15 @@ struct target_type { int (*deassert_reset)(struct target *target); int (*soft_reset_halt)(struct target *target); + /** + * Target architecture for GDB. + * + * The string returned by this function will not be automatically freed; + * if dynamic allocation is used for this value, it must be managed by + * the target, ideally by caching the result for subsequent calls. + */ + const char *(*get_gdb_arch)(struct target *target); + /** * Target register access for GDB. Do @b not call this function * directly, use target_get_gdb_reg_list() instead. diff --git a/tcl/board/arty_s7.cfg b/tcl/board/arty_s7.cfg new file mode 100644 index 000000000..ca7d3f1c4 --- /dev/null +++ b/tcl/board/arty_s7.cfg @@ -0,0 +1,35 @@ +# +# Arty S7: Spartan7 25/50 FPGA Board for Makers and Hobbyists +# +# https://www.xilinx.com/products/boards-and-kits/1-pnziih.html +# https://store.digilentinc.com/arty-s7-spartan-7-fpga-board-for-makers-and-hobbyists/ + +source [find interface/ftdi/digilent-hs1.cfg] + +# Xilinx Spartan7-25/50 FPGA (XC7S{25,50}-CSGA324) +source [find cpld/xilinx-xc7.cfg] +source [find cpld/jtagspi.cfg] + +adapter_khz 25000 + +# Usage: +# +# Load Bitstream into FPGA: +# openocd -f board/arty_s7.cfg -c "init;\ +# pld load 0 bitstream.bit;\ +# shutdown" +# +# Write Bitstream to Flash: +# openocd -f board/arty_s7.cfg -c "init;\ +# jtagspi_init 0 bscan_spi_xc7s??.bit;\ +# jtagspi_program bitstream.bin 0;\ +# xc7s_program xc7s.tap;\ +# shutdown" +# +# jtagspi flash proxies can be found at: +# https://github.com/quartiq/bscan_spi_bitstreams +# +# For the Spartan 50 variant, use +# - https://github.com/quartiq/bscan_spi_bitstreams/raw/master/bscan_spi_xc7s50.bit +# For the Spartan 25 variant, use +# - https://github.com/quartiq/bscan_spi_bitstreams/raw/master/bscan_spi_xc7s25.bit diff --git a/tcl/board/emcraft_imx8m-som-bsb.cfg b/tcl/board/emcraft_imx8m-som-bsb.cfg new file mode 100644 index 000000000..5571d0ecb --- /dev/null +++ b/tcl/board/emcraft_imx8m-som-bsb.cfg @@ -0,0 +1,22 @@ +# +# configuration file for Emcraft IMX8M-SOM-BSB +# + +# only JTAG supported +transport select jtag + +# set a safe JTAG clock speed, can be overridden +adapter_khz 1000 + +# SRST and TRST are wired up +reset_config trst_and_srst + +# delay after SRST goes inactive +adapter_nsrst_delay 70 + +# board has an i.MX8MQ with 4 Cortex-A53 cores +set CHIPNAME imx8mq +set CHIPCORES 4 + +# source SoC configuration +source [find target/imx8m.cfg] diff --git a/tcl/board/numato_mimas_a7.cfg b/tcl/board/numato_mimas_a7.cfg new file mode 100644 index 000000000..1261feacc --- /dev/null +++ b/tcl/board/numato_mimas_a7.cfg @@ -0,0 +1,36 @@ +# +# Numato Mimas A7 - Artix 7 FPGA Board +# +# https://numato.com/product/mimas-a7-artix-7-fpga-development-board-with-ddr-sdram-and-gigabit-ethernet +# +# Note: Connect external DC power supply if programming a heavy design onto FPGA. +# Programming while powering via USB may lead to programming failure. +# Therefore, prefer external power supply. + +interface ftdi +ftdi_device_desc "Mimas Artix 7 FPGA Module" +ftdi_vid_pid 0x2a19 0x1009 + +# channel 0 is for custom purpose by users (like uart, fifo etc) +# channel 1 is reserved for JTAG (by-default) or SPI (possible via changing solder jumpers) +ftdi_channel 1 +ftdi_tdo_sample_edge falling + + +# FTDI Pin Layout +# +# +--------+-------+-------+-------+-------+-------+-------+-------+ +# | DBUS7 | DBUS6 | DBUS5 | DBUS4 | DBUS3 | DBUS2 | DBUS1 | DBUS0 | +# +--------+-------+-------+-------+-------+-------+-------+-------+ +# | PROG_B | OE_N | NC | NC | TMS | TDO | TDI | TCK | +# +--------+-------+-------+-------+-------+-------+-------+-------+ +# +# OE_N is JTAG buffer output enable signal (active-low) +# PROG_B is not used, so left as input to FTDI. +# +ftdi_layout_init 0x0008 0x004b +reset_config none +adapter_khz 30000 + +source [find cpld/xilinx-xc7.cfg] +source [find cpld/jtagspi.cfg] diff --git a/tcl/board/renesas_salvator-xs.cfg b/tcl/board/renesas_salvator-xs.cfg new file mode 100644 index 000000000..1558a5274 --- /dev/null +++ b/tcl/board/renesas_salvator-xs.cfg @@ -0,0 +1,23 @@ +# Renesas R-Car Gen3 Salvator-X(S) Board Config + +# The Salvator-X(S) boards come with either an H3, M3W, or M3N SOC. + +echo "\nSalvator-X(S):" +if { ![info exists SOC] } { + set SOC H3 +} +source [find target/renesas_rcar_gen3.cfg] + +reset_config trst_and_srst srst_nogate + +proc init_reset {mode} { + # Assert both resets: equivalent to a power-on reset + jtag_reset 1 1 + + # Deassert TRST to begin TAP communication + jtag_reset 0 1 + + # TAP should now be responsive, validate the scan-chain + jtag arp_init +} + diff --git a/tcl/cpld/xilinx-xc7.cfg b/tcl/cpld/xilinx-xc7.cfg index d5824f8a1..4c0502c5d 100644 --- a/tcl/cpld/xilinx-xc7.cfg +++ b/tcl/cpld/xilinx-xc7.cfg @@ -9,7 +9,15 @@ if { [info exists CHIPNAME] } { # the 4 top bits (28:31) are the die stepping/revisions. ignore it. jtag newtap $_CHIPNAME tap -irlen 6 -ignore-version \ + -expected-id 0x03622093 \ + -expected-id 0x03620093 \ + -expected-id 0x037C4093 \ + -expected-id 0x0362F093 \ + -expected-id 0x037C8093 \ + -expected-id 0x037C7093 \ + -expected-id 0x037C3093 \ -expected-id 0x0362E093 \ + -expected-id 0x037C2093 \ -expected-id 0x0362D093 \ -expected-id 0x0362C093 \ -expected-id 0x03632093 \ diff --git a/tcl/interface/ftdi/c232hm.cfg b/tcl/interface/ftdi/c232hm.cfg new file mode 100644 index 000000000..387abbb05 --- /dev/null +++ b/tcl/interface/ftdi/c232hm.cfg @@ -0,0 +1,15 @@ +# +# FTDI USB Hi-Speed to MPSSE Cable +# +# http://www.ftdichip.com/Products/Cables/USBMPSSE.htm +# +# C232HM-DDHSL-0 and C232HM-EDSL-0 provide 3.3V and 5V on pin 1 (Red), +# respectively. +# + +interface ftdi +#ftdi_device_desc "C232HM-DDHSL-0" +#ftdi_device_desc "C232HM-EDHSL-0" +ftdi_vid_pid 0x0403 0x6014 + +ftdi_layout_init 0x0008 0x000b diff --git a/tcl/target/atsamv.cfg b/tcl/target/atsamv.cfg index d1f8454d5..43962de31 100644 --- a/tcl/target/atsamv.cfg +++ b/tcl/target/atsamv.cfg @@ -45,6 +45,14 @@ if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to # perform a soft reset cortex_m reset_config sysresetreq + + # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal + # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3 + # makes the data access cacheable. This allows reading and writing data in the + # CPU cache from the debugger, which is far more useful than going straight to + # RAM when operating on typical variables, and is generally no worse when + # operating on special memory locations. + $_CHIPNAME.dap apcsw 0x08000000 0x08000000 } set _FLASHNAME $_CHIPNAME.flash diff --git a/tcl/target/esi32xx.cfg b/tcl/target/esi32xx.cfg new file mode 100644 index 000000000..d32af39bd --- /dev/null +++ b/tcl/target/esi32xx.cfg @@ -0,0 +1,36 @@ +# +# EnSilica eSi-32xx SoC (eSi-RISC Family) +# http://www.ensilica.com/risc-ip/ +# + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME esi32xx +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x11234001 +} + +jtag newtap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME esirisc -chain-position $_CHIPNAME.cpu + +# Targets with the UNIFIED_ADDRESS_SPACE option disabled should set +# CACHEARCH to 'harvard'. By default, 'von_neumann' is assumed. +if { [info exists CACHEARCH] } { + $_TARGETNAME esirisc cache_arch $CACHEARCH +} + +adapter_khz 2000 + +reset_config none + +# The default linker scripts provided by the eSi-RISC toolchain do not +# specify attributes on memory regions, which results in incorrect +# application of software breakpoints by GDB. +gdb_breakpoint_override hard diff --git a/tcl/target/max32620.cfg b/tcl/target/max32620.cfg new file mode 100644 index 000000000..80cb25a47 --- /dev/null +++ b/tcl/target/max32620.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX32620 OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max32620 cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -ignore-version +} else { + swd newdap max32620 cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max32620.cpu cortex_m -chain-position max32620.cpu +max32620.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max32620 flash base address 0x00000000 +# max32620 flash size 0x200000 (2MB) +# max32620 FLC base address 0x40002000 +# max32620 sector (page) size 0x2000 (8kB) +# max32620 clock speed 96 (MHz) +flash bank max32620.flash max32xxx 0x00000000 0x200000 0 0 max32620.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/max32625.cfg b/tcl/target/max32625.cfg new file mode 100644 index 000000000..7182b235f --- /dev/null +++ b/tcl/target/max32625.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX32625 OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max32625 cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -expected-id 0x07f71197 -ignore-version +} else { + swd newdap max32625 cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max32625.cpu cortex_m -chain-position max32625.cpu +max32625.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max32625 flash base address 0x00000000 +# max32625 flash size 0x80000 (512k) +# max32625 FLC base address 0x40002000 +# max32625 sector (page) size 0x2000 (8kB) +# max32625 clock speed 96 (MHz) +flash bank max32625.flash max32xxx 0x00000000 0x80000 0 0 max32625.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/max3263x.cfg b/tcl/target/max3263x.cfg new file mode 100644 index 000000000..f23b0b64d --- /dev/null +++ b/tcl/target/max3263x.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX3263X OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max3263x cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -expected-id 0x07f76197 -ignore-version +} else { + swd newdap max3263x cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max3263x.cpu cortex_m -chain-position max3263x.cpu +max3263x.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max3263x flash base address 0x00000000 +# max3263x flash size 0x200000 (2MB) +# max3263x FLC base address 0x40002000 +# max3263x sector (page) size 0x2000 (8kB) +# max3263x clock speed 96 (MHz) +flash bank max3263x.flash max32xxx 0x00000000 0x200000 0 0 max3263x.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/renesas_rcar_gen3.cfg b/tcl/target/renesas_rcar_gen3.cfg new file mode 100644 index 000000000..a6eef674b --- /dev/null +++ b/tcl/target/renesas_rcar_gen3.cfg @@ -0,0 +1,169 @@ +# Renesas R-Car Generation 3 SOCs +# - There are a combination of Cortex-A57s, Cortex-A53s, and Cortex-R7 for each Gen3 SOC +# - Each SOC can boot through any of the, up to 3, core types that it has +# e.g. H3 can boot through Cortex-A57, Cortex-A53, or Cortex-R7 + +# Supported Gen3 SOCs and their cores: +# H3: Cortex-A57 x 4, Cortex-A53 x 4, Cortex-R7 x 2 (Lock-Step) +# M3W: Cortex-A57 x 2, Cortex-A53 x 4, Cortex-R7 x 2 (Lock-Step) +# M3N: Cortex-A57 x 2, Cortex-R7 x 2 (Lock-Step) +# V3H: Cortex-A53 x 4, Cortex-R7 x 2 (Lock-Step) +# V3M: Cortex-A53 x 2, Cortex-R7 x 2 (Lock-Step) +# E3: Cortex-A53 x 1, Cortex-R7 x 2 (Lock-Step) +# D3: Cortex-A53 x 1 + +# Usage: +# There are 2 configuration options: +# SOC: Selects the supported SOC. (Default 'H3') +# BOOT_CORE: Selects the booting core. 'CA57', 'CA53', or 'CR7' +# Defaults to 'CA57' if the SOC has one, else defaults to 'CA53' + +if { [info exists SOC] } { + set _soc $SOC +} else { + set _soc H3 +} + +# Set configuration for each SOC and the default 'BOOT_CORE' +switch $_soc { + H3 { + set _CHIPNAME r8a77950 + set _num_ca57 4 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA57 + } + M3W { + set _CHIPNAME r8a77960 + set _num_ca57 2 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA57 + } + M3N { + set _CHIPNAME r8a77965 + set _num_ca57 2 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA57 + } + V3H { + set _CHIPNAME r8a77970 + set _num_ca57 0 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA53 + } + V3M { + set _CHIPNAME r8a77980 + set _num_ca57 0 + set _num_ca53 2 + set _num_cr7 1 + set _boot_core CA53 + } + E3 { + set _CHIPNAME r8a77990 + set _num_ca57 0 + set _num_ca53 1 + set _num_cr7 1 + set _boot_core CA53 + } + D3 { + set _CHIPNAME r8a77995 + set _num_ca57 0 + set _num_ca53 1 + set _num_cr7 0 + set _boot_core CA53 + } + default { + echo "'$_soc' is invalid!" + } +} + +# If configured, override the default 'CHIPNAME' +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} + +# If configured, override the default 'BOOT_CORE' +if { [info exists BOOT_CORE] } { + set _boot_core $BOOT_CORE +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x5ba00477 +} + +echo "\t$_soc - $_num_ca57 CA57(s), $_num_ca53 CA53(s), $_num_cr7 CR7(s)" +echo "\tBoot Core - $_boot_core\n" + +set _DAPNAME $_CHIPNAME.dap + +# TAP and DAP +jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x01 -irmask 0x0f -expected-id $_DAP_TAPID +dap create $_DAPNAME -chain-position $_CHIPNAME.cpu + +set CA57_DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000} +set CA57_CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000} +set CA53_DBGBASE {0x80C10000 0x80D10000 0x80E10000 0x80F10000} +set CA53_CTIBASE {0x80C20000 0x80D20000 0x80E20000 0x80F20000} +set CR7_DBGBASE 0x80910000 +set CR7_CTIBASE 0x80918000 + +set smp_targets "" + +proc setup_a5x {core_name dbgbase ctibase num boot} { + global _CHIPNAME + global _DAPNAME + global smp_targets + for { set _core 0 } { $_core < $num } { incr _core } { + set _TARGETNAME $_CHIPNAME.$core_name.$_core + set _CTINAME $_TARGETNAME.cti + cti create $_CTINAME -dap $_DAPNAME -ap-num 1 \ + -ctibase [lindex $ctibase $_core] + set _command "target create $_TARGETNAME aarch64 -dap $_DAPNAME \ + -ap-num 1 -dbgbase [lindex $dbgbase $_core] -cti $_CTINAME" + if { $_core == 0 && $boot == 1 } { + set _targets "$_TARGETNAME" + } else { + set _command "$_command -defer-examine" + } + set smp_targets "$smp_targets $_TARGETNAME" + eval $_command + } +} + +proc setup_cr7 {dbgbase ctibase boot} { + global _CHIPNAME + global _DAPNAME + set _TARGETNAME $_CHIPNAME.r7 + set _CTINAME $_TARGETNAME.cti + cti create $_CTINAME -dap $_DAPNAME -ap-num 1 -ctibase $ctibase + set _command "target create $_TARGETNAME cortex_r4 -dap $_DAPNAME \ + -ap-num 1 -dbgbase $dbgbase" + if { $boot == 1 } { + set _targets "$_TARGETNAME" + } else { + set _command "$_command -defer-examine" + } + eval $_command +} + +# Organize target list based on the boot core +if { [string equal $_boot_core CA57] } { + setup_a5x a57 $CA57_DBGBASE $CA57_CTIBASE $_num_ca57 1 + setup_a5x a53 $CA53_DBGBASE $CA53_CTIBASE $_num_ca53 0 + setup_cr7 $CR7_DBGBASE $CR7_CTIBASE 0 +} elseif { [string equal $_boot_core CA53] } { + setup_a5x a53 $CA53_DBGBASE $CA53_CTIBASE $_num_ca53 1 + setup_a5x a57 $CA57_DBGBASE $CA57_CTIBASE $_num_ca57 0 + setup_cr7 $CR7_DBGBASE $CR7_CTIBASE 0 +} else { + setup_cr7 $CR7_DBGBASE $CR7_CTIBASE 1 + setup_a5x a57 $CA57_DBGBASE $CA57_CTIBASE $_num_ca57 0 + setup_a5x a53 $CA53_DBGBASE $CA53_CTIBASE $_num_ca53 0 +} + +eval "target smp $smp_targets" diff --git a/tcl/target/stm32f7x.cfg b/tcl/target/stm32f7x.cfg index 562de30f6..b0468e21e 100755 --- a/tcl/target/stm32f7x.cfg +++ b/tcl/target/stm32f7x.cfg @@ -65,6 +65,14 @@ if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to # perform a soft reset cortex_m reset_config sysresetreq + + # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal + # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3 + # makes the data access cacheable. This allows reading and writing data in the + # CPU cache from the debugger, which is far more useful than going straight to + # RAM when operating on typical variables, and is generally no worse when + # operating on special memory locations. + $_CHIPNAME.dap apcsw 0x08000000 0x08000000 } $_TARGETNAME configure -event examine-end { @@ -145,3 +153,4 @@ $_TARGETNAME configure -event reset-start { # Reduce speed since CPU speed will slow down to 16MHz with the reset adapter_khz 2000 } + diff --git a/tcl/target/stm32h7x.cfg b/tcl/target/stm32h7x.cfg index 10477a5a7..0bfc43dfd 100644 --- a/tcl/target/stm32h7x.cfg +++ b/tcl/target/stm32h7x.cfg @@ -56,13 +56,31 @@ if {[using_jtag]} { jtag_ntrst_delay 100 } -# use hardware reset, connect under reset +# use hardware reset +# +# The STM32H7 does not support connect_assert_srst mode because the AXI is +# unavailable while SRST is asserted, and that is used to access the DBGMCU +# component at 0x5C001000 in the examine-end event handler. +# +# It is possible to access the DBGMCU component at 0xE00E1000 via AP2 instead +# of the default AP0, and that works with SRST asserted; however, nonzero AP +# usage does not work with HLA, so is not done by default. That change could be +# made in a local configuration file if connect_assert_srst mode is needed for +# a specific application and a non-HLA adapter is in use. reset_config srst_only srst_nogate if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to # perform a soft reset cortex_m reset_config sysresetreq + + # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal + # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3 + # makes the data access cacheable. This allows reading and writing data in the + # CPU cache from the debugger, which is far more useful than going straight to + # RAM when operating on typical variables, and is generally no worse when + # operating on special memory locations. + $_CHIPNAME.dap apcsw 0x08000000 0x08000000 } $_TARGETNAME configure -event examine-end { @@ -92,3 +110,4 @@ $_TARGETNAME configure -event reset-init { # Clock after reset is HSI at 64 MHz, no need of PLL adapter_khz 4000 } + diff --git a/tcl/target/zynq_7000.cfg b/tcl/target/zynq_7000.cfg index 07a6c8352..1562768c5 100644 --- a/tcl/target/zynq_7000.cfg +++ b/tcl/target/zynq_7000.cfg @@ -27,3 +27,22 @@ adapter_khz 1000 ${_TARGETNAME}0 configure -event reset-assert-post "cortex_a dbginit" ${_TARGETNAME}1 configure -event reset-assert-post "cortex_a dbginit" + +pld device virtex2 zynq_pl.bs 1 + +set XC7_JSHUTDOWN 0x0d +set XC7_JPROGRAM 0x0b +set XC7_JSTART 0x0c +set XC7_BYPASS 0x3f + +proc zynqpl_program {tap} { + global XC7_JSHUTDOWN XC7_JPROGRAM XC7_JSTART XC7_BYPASS + irscan $tap $XC7_JSHUTDOWN + irscan $tap $XC7_JPROGRAM + runtest 60000 + #JSTART prevents this from working... + #irscan $tap $XC7_JSTART + runtest 2000 + irscan $tap $XC7_BYPASS + runtest 2000 +}