aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2018-06-11 12:08:08 -0700
committerTim Newsome <tim@sifive.com>2018-06-11 12:08:08 -0700
commit17a0523736e25b352f82d62e1b16bd1c2d87646b (patch)
tree9969e4575f44f9f9f0cd3b64e33a2679dc730ed4 /src
parent6766fa1dda84e5b9243a2ab60bca77de92ea178d (diff)
parent06123153f38280608b1e92dcb766b31ade7e4668 (diff)
downloadriscv-openocd-17a0523736e25b352f82d62e1b16bd1c2d87646b.zip
riscv-openocd-17a0523736e25b352f82d62e1b16bd1c2d87646b.tar.gz
riscv-openocd-17a0523736e25b352f82d62e1b16bd1c2d87646b.tar.bz2
Merge branch 'master' into from_upstream
Diffstat (limited to 'src')
-rw-r--r--src/flash/nor/Makefile.am3
-rw-r--r--src/flash/nor/cc3220sf.c529
-rw-r--r--src/flash/nor/cc3220sf.h45
-rw-r--r--src/flash/nor/core.c12
-rw-r--r--src/flash/nor/drivers.c8
-rw-r--r--src/flash/nor/psoc5lp.c1574
-rw-r--r--src/flash/nor/virtual.c5
-rw-r--r--src/jtag/drivers/Makefile.am4
-rw-r--r--src/jtag/drivers/sysfsgpio.c8
-rw-r--r--src/jtag/drivers/xds110.c1973
-rw-r--r--src/jtag/interfaces.c6
-rw-r--r--src/server/gdb_server.c28
-rw-r--r--src/target/Makefile.am2
-rw-r--r--src/target/aarch64.c153
-rw-r--r--src/target/arm.h25
-rw-r--r--src/target/arm_semihosting.c580
-rw-r--r--src/target/arm_semihosting.h2
-rw-r--r--src/target/armv4_5.c141
-rw-r--r--src/target/armv7m.c10
-rw-r--r--src/target/armv8.c6
-rw-r--r--src/target/breakpoints.c7
-rw-r--r--src/target/cortex_m.c8
-rw-r--r--src/target/nds32.c73
-rw-r--r--src/target/semihosting_common.c1579
-rw-r--r--src/target/semihosting_common.h163
-rw-r--r--src/target/target.c7
-rw-r--r--src/target/target.h11
27 files changed, 6196 insertions, 766 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 7121412..db936b8 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -19,6 +19,7 @@ NOR_DRIVERS = \
%D%/atsamv.c \
%D%/avrf.c \
%D%/bluenrg-x.c \
+ %D%/cc3220sf.c \
%D%/cfi.c \
%D%/dsp5680xx_flash.c \
%D%/efm32.c \
@@ -43,6 +44,7 @@ NOR_DRIVERS = \
%D%/ocl.c \
%D%/pic32mx.c \
%D%/psoc4.c \
+ %D%/psoc5lp.c \
%D%/psoc6.c \
%D%/sim3x.c \
%D%/spi.c \
@@ -64,6 +66,7 @@ NOR_DRIVERS = \
NORHEADERS = \
%D%/core.h \
+ %D%/cc3220sf.h \
%D%/cfi.h \
%D%/driver.h \
%D%/imp.h \
diff --git a/src/flash/nor/cc3220sf.c b/src/flash/nor/cc3220sf.c
new file mode 100644
index 0000000..af45743
--- /dev/null
+++ b/src/flash/nor/cc3220sf.c
@@ -0,0 +1,529 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Texas Instruments, Inc. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "cc3220sf.h"
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+#define FLASH_TIMEOUT 5000
+
+struct cc3220sf_bank {
+ bool probed;
+ struct armv7m_algorithm armv7m_info;
+};
+
+static int cc3220sf_mass_erase(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ bool done;
+ long long start_ms;
+ long long elapsed_ms;
+ uint32_t value;
+
+ int retval = ERROR_OK;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* Set starting address to erase to zero */
+ retval = target_write_u32(target, FMA_REGISTER_ADDR, 0);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Write the MERASE bit of the FMC register */
+ retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_MERASE_VALUE);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Poll the MERASE bit until the mass erase is complete */
+ done = false;
+ start_ms = timeval_ms();
+ while (!done) {
+ retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
+ if (ERROR_OK != retval)
+ return retval;
+
+ if ((value & FMC_MERASE_BIT) == 0) {
+ /* Bit clears when mass erase is finished */
+ done = true;
+ } else {
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ if (elapsed_ms > FLASH_TIMEOUT)
+ break;
+ }
+ }
+
+ if (!done) {
+ /* Mass erase timed out waiting for confirmation */
+ return ERROR_FAIL;
+ }
+
+ return retval;
+}
+
+FLASH_BANK_COMMAND_HANDLER(cc3220sf_flash_bank_command)
+{
+ struct cc3220sf_bank *cc3220sf_bank;
+
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ cc3220sf_bank = malloc(sizeof(struct cc3220sf_bank));
+ if (NULL == cc3220sf_bank)
+ return ERROR_FAIL;
+
+ /* Initialize private flash information */
+ cc3220sf_bank->probed = false;
+
+ /* Finish initialization of flash bank */
+ bank->driver_priv = cc3220sf_bank;
+ bank->next = NULL;
+
+ return ERROR_OK;
+}
+
+static int cc3220sf_erase(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ bool done;
+ long long start_ms;
+ long long elapsed_ms;
+ uint32_t address;
+ uint32_t value;
+
+ int retval = ERROR_OK;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* Do a mass erase if user requested all sectors of flash */
+ if ((first == 0) && (last == (bank->num_sectors - 1))) {
+ /* Request mass erase of flash */
+ return cc3220sf_mass_erase(bank);
+ }
+
+ /* Erase requested sectors one by one */
+ for (int i = first; i <= last; i++) {
+
+ /* Determine address of sector to erase */
+ address = FLASH_BASE_ADDR + i * FLASH_SECTOR_SIZE;
+
+ /* Set starting address to erase */
+ retval = target_write_u32(target, FMA_REGISTER_ADDR, address);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Write the ERASE bit of the FMC register */
+ retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_ERASE_VALUE);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Poll the ERASE bit until the erase is complete */
+ done = false;
+ start_ms = timeval_ms();
+ while (!done) {
+ retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
+ if (ERROR_OK != retval)
+ return retval;
+
+ if ((value & FMC_ERASE_BIT) == 0) {
+ /* Bit clears when mass erase is finished */
+ done = true;
+ } else {
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ if (elapsed_ms > FLASH_TIMEOUT)
+ break;
+ }
+ }
+
+ if (!done) {
+ /* Sector erase timed out waiting for confirmation */
+ return ERROR_FAIL;
+ }
+ }
+
+ return retval;
+}
+
+static int cc3220sf_protect(struct flash_bank *bank, int set, int first,
+ int last)
+{
+ return ERROR_OK;
+}
+
+static int cc3220sf_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
+ struct working_area *algo_working_area;
+ struct working_area *buffer_working_area;
+ struct reg_param reg_params[3];
+ uint32_t algo_base_address;
+ uint32_t algo_buffer_address;
+ uint32_t algo_buffer_size;
+ uint32_t address;
+ uint32_t remaining;
+ uint32_t words;
+ uint32_t result;
+
+ int retval = ERROR_OK;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* Obtain working area to use for flash helper algorithm */
+ retval = target_alloc_working_area(target, sizeof(cc3220sf_algo),
+ &algo_working_area);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Obtain working area to use for flash buffer */
+ retval = target_alloc_working_area(target,
+ target_get_working_area_avail(target), &buffer_working_area);
+ if (ERROR_OK != retval) {
+ target_free_working_area(target, algo_working_area);
+ return retval;
+ }
+
+ algo_base_address = algo_working_area->address;
+ algo_buffer_address = buffer_working_area->address;
+ algo_buffer_size = buffer_working_area->size;
+
+ /* Make sure buffer size is a multiple of 32 word (0x80 byte) chunks */
+ /* (algo runs more efficiently if it operates on 32 words at a time) */
+ if (algo_buffer_size > 0x80)
+ algo_buffer_size &= ~0x7f;
+
+ /* Write flash helper algorithm into target memory */
+ retval = target_write_buffer(target, algo_base_address,
+ sizeof(cc3220sf_algo), cc3220sf_algo);
+ if (ERROR_OK != retval) {
+ target_free_working_area(target, algo_working_area);
+ target_free_working_area(target, buffer_working_area);
+ return retval;
+ }
+
+ /* Initialize the ARMv7m specific info to run the algorithm */
+ cc3220sf_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ cc3220sf_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
+
+ /* Initialize register params for flash helper algorithm */
+ init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+ init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+ init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT);
+
+ /* Prepare to write to flash */
+ address = FLASH_BASE_ADDR + offset;
+ remaining = count;
+
+ /* The flash hardware can only write complete words to flash. If
+ * an unaligned address is passed in, we must do a read-modify-write
+ * on a word with enough bytes to align the rest of the buffer. And
+ * if less than a whole word remains at the end, we must also do a
+ * read-modify-write on a final word to finish up.
+ */
+
+ /* Do one word write to align address on 32-bit boundary if needed */
+ if (0 != (address & 0x3)) {
+ uint8_t head[4];
+
+ /* Get starting offset for data to write (will be 1 to 3) */
+ uint32_t head_offset = address & 0x03;
+
+ /* Get the aligned address to write this first word to */
+ uint32_t head_address = address & 0xfffffffc;
+
+ /* Retrieve what is already in flash at the head address */
+ retval = target_read_buffer(target, head_address, sizeof(head), head);
+
+ if (ERROR_OK == retval) {
+ /* Substitute in the new data to write */
+ while ((remaining > 0) && (head_offset < 4)) {
+ head[head_offset] = *buffer;
+ head_offset++;
+ address++;
+ buffer++;
+ remaining--;
+ }
+ }
+
+ if (ERROR_OK == retval) {
+ /* Helper parameters are passed in registers R0-R2 */
+ /* Set start of data buffer, address to write to, and word count */
+ buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
+ buf_set_u32(reg_params[1].value, 0, 32, head_address);
+ buf_set_u32(reg_params[2].value, 0, 32, 1);
+
+ /* Write head value into buffer to flash */
+ retval = target_write_buffer(target, algo_buffer_address,
+ sizeof(head), head);
+ }
+
+ if (ERROR_OK == retval) {
+ /* Execute the flash helper algorithm */
+ retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
+ algo_base_address, 0, FLASH_TIMEOUT,
+ &cc3220sf_bank->armv7m_info);
+ if (ERROR_OK != retval)
+ LOG_ERROR("cc3220sf: Flash algorithm failed to run");
+
+ /* Check that the head value was written to flash */
+ result = buf_get_u32(reg_params[2].value, 0, 32);
+ if (0 != result) {
+ retval = ERROR_FAIL;
+ LOG_ERROR("cc3220sf: Flash operation failed");
+ }
+ }
+ }
+
+ /* Check if there's data at end of buffer that isn't a full word */
+ uint32_t tail_count = remaining & 0x03;
+ /* Adjust remaining so it is a multiple of whole words */
+ remaining -= tail_count;
+
+ while ((ERROR_OK == retval) && (remaining > 0)) {
+ /* Set start of data buffer and address to write to */
+ buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+
+ /* Download data to write into memory buffer */
+ if (remaining >= algo_buffer_size) {
+ /* Fill up buffer with data to flash */
+ retval = target_write_buffer(target, algo_buffer_address,
+ algo_buffer_size, buffer);
+ if (ERROR_OK != retval)
+ break;
+
+ /* Count to write is in 32-bit words */
+ words = algo_buffer_size / 4;
+
+ /* Bump variables to next data */
+ address += algo_buffer_size;
+ buffer += algo_buffer_size;
+ remaining -= algo_buffer_size;
+ } else {
+ /* Fill buffer with what's left of the data */
+ retval = target_write_buffer(target, algo_buffer_address,
+ remaining, buffer);
+ if (ERROR_OK != retval)
+ break;
+
+ /* Calculate the final word count to write */
+ words = remaining / 4;
+ if (0 != (remaining % 4))
+ words++;
+
+ /* Bump variables to any final data */
+ address += remaining;
+ buffer += remaining;
+ remaining = 0;
+ }
+
+ /* Set number of words to write */
+ buf_set_u32(reg_params[2].value, 0, 32, words);
+
+ /* Execute the flash helper algorithm */
+ retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
+ algo_base_address, 0, FLASH_TIMEOUT,
+ &cc3220sf_bank->armv7m_info);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("cc3220sf: Flash algorithm failed to run");
+ break;
+ }
+
+ /* Check that all words were written to flash */
+ result = buf_get_u32(reg_params[2].value, 0, 32);
+ if (0 != result) {
+ retval = ERROR_FAIL;
+ LOG_ERROR("cc3220sf: Flash operation failed");
+ break;
+ }
+ }
+
+ /* Do one word write for any final bytes less than a full word */
+ if ((ERROR_OK == retval) && (0 != tail_count)) {
+ uint8_t tail[4];
+
+ /* Set starting byte offset for data to write */
+ uint32_t tail_offset = 0;
+
+ /* Retrieve what is already in flash at the tail address */
+ retval = target_read_buffer(target, address, sizeof(tail), tail);
+
+ if (ERROR_OK == retval) {
+ /* Substitute in the new data to write */
+ while (tail_count > 0) {
+ tail[tail_offset] = *buffer;
+ tail_offset++;
+ buffer++;
+ tail_count--;
+ }
+ }
+
+ if (ERROR_OK == retval) {
+ /* Set start of data buffer, address to write to, and word count */
+ buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+ buf_set_u32(reg_params[2].value, 0, 32, 1);
+
+ /* Write tail value into buffer to flash */
+ retval = target_write_buffer(target, algo_buffer_address,
+ sizeof(tail), tail);
+ }
+
+ if (ERROR_OK == retval) {
+ /* Execute the flash helper algorithm */
+ retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
+ algo_base_address, 0, FLASH_TIMEOUT,
+ &cc3220sf_bank->armv7m_info);
+ if (ERROR_OK != retval)
+ LOG_ERROR("cc3220sf: Flash algorithm failed to run");
+
+ /* Check that the tail was written to flash */
+ result = buf_get_u32(reg_params[2].value, 0, 32);
+ if (0 != result) {
+ retval = ERROR_FAIL;
+ LOG_ERROR("cc3220sf: Flash operation failed");
+ }
+ }
+ }
+
+ /* Free resources */
+ destroy_reg_param(&reg_params[0]);
+ destroy_reg_param(&reg_params[1]);
+ destroy_reg_param(&reg_params[2]);
+ target_free_working_area(target, algo_working_area);
+ target_free_working_area(target, buffer_working_area);
+
+ return retval;
+}
+
+static int cc3220sf_probe(struct flash_bank *bank)
+{
+ struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
+
+ uint32_t base;
+ uint32_t size;
+ int num_sectors;
+ int bank_id;
+
+ bank_id = bank->bank_number;
+
+ if (0 == bank_id) {
+ base = FLASH_BASE_ADDR;
+ size = FLASH_NUM_SECTORS * FLASH_SECTOR_SIZE;
+ num_sectors = FLASH_NUM_SECTORS;
+ } else {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+
+ if (NULL != bank->sectors) {
+ free(bank->sectors);
+ bank->sectors = NULL;
+ }
+
+ bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+ if (NULL == bank->sectors)
+ return ERROR_FAIL;
+
+ bank->base = base;
+ bank->size = size;
+ bank->write_start_alignment = 0;
+ bank->write_end_alignment = 0;
+ bank->num_sectors = num_sectors;
+
+ for (int i = 0; i < num_sectors; i++) {
+ bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
+ bank->sectors[i].size = FLASH_SECTOR_SIZE;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 0;
+ }
+
+ /* We've successfully recorded the stats on this flash bank */
+ cc3220sf_bank->probed = true;
+
+ /* If we fall through to here, then all went well */
+
+ return ERROR_OK;
+}
+
+static int cc3220sf_auto_probe(struct flash_bank *bank)
+{
+ struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
+
+ int retval = ERROR_OK;
+
+ if (0 != bank->bank_number) {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+
+ if (!cc3220sf_bank->probed)
+ retval = cc3220sf_probe(bank);
+
+ return retval;
+}
+
+static int cc3220sf_protect_check(struct flash_bank *bank)
+{
+ return ERROR_OK;
+}
+
+static int cc3220sf_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ int printed;
+
+ printed = snprintf(buf, buf_size, "CC3220SF with 1MB internal flash\n");
+
+ if (printed >= buf_size)
+ return ERROR_BUF_TOO_SMALL;
+
+ return ERROR_OK;
+}
+
+struct flash_driver cc3220sf_flash = {
+ .name = "cc3220sf",
+ .flash_bank_command = cc3220sf_flash_bank_command,
+ .erase = cc3220sf_erase,
+ .protect = cc3220sf_protect,
+ .write = cc3220sf_write,
+ .read = default_flash_read,
+ .probe = cc3220sf_probe,
+ .auto_probe = cc3220sf_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = cc3220sf_protect_check,
+ .info = cc3220sf_info,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/cc3220sf.h b/src/flash/nor/cc3220sf.h
new file mode 100644
index 0000000..36c17be
--- /dev/null
+++ b/src/flash/nor/cc3220sf.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Texas Instruments, Inc. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_FLASH_NOR_CC3220SF_H
+#define OPENOCD_FLASH_NOR_CC3220SF_H
+
+/* CC3220SF device types */
+#define CC3220_NO_TYPE 0 /* Device type not determined yet */
+#define CC3220_OTHER 1 /* CC3220 variant without flash */
+#define CC3220SF 2 /* CC3220SF variant with flash */
+
+/* Flash parameters */
+#define FLASH_BASE_ADDR 0x01000000
+#define FLASH_SECTOR_SIZE 2048
+#define FLASH_NUM_SECTORS 512
+
+/* CC2200SF flash registers */
+#define FMA_REGISTER_ADDR 0x400FD000
+#define FMC_REGISTER_ADDR 0x400FD008
+#define FMC_DEFAULT_VALUE 0xA4420000
+#define FMC_ERASE_BIT 0x00000002
+#define FMC_MERASE_BIT 0x00000004
+#define FMC_ERASE_VALUE (FMC_DEFAULT_VALUE | FMC_ERASE_BIT)
+#define FMC_MERASE_VALUE (FMC_DEFAULT_VALUE | FMC_MERASE_BIT)
+
+/* Flash helper algorithm for CC3220SF */
+const uint8_t cc3220sf_algo[] = {
+#include "../../../contrib/loaders/flash/cc3220sf/cc3220sf.inc"
+};
+
+#endif /* OPENOCD_FLASH_NOR_CC3220SF_H */
diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c
index f05c68b..4941281 100644
--- a/src/flash/nor/core.c
+++ b/src/flash/nor/core.c
@@ -188,9 +188,17 @@ void flash_free_all_banks(void)
else
LOG_WARNING("Flash driver of %s does not support free_driver_priv()", bank->name);
+ /* For 'virtual' flash driver bank->sectors and bank->prot_blocks pointers are copied from
+ * master flash_bank structure. They point to memory locations allocated by master flash driver
+ * so master driver is responsible for releasing them.
+ * Avoid UB caused by double-free memory corruption if flash bank is 'virtual'. */
+
+ if (strcmp(bank->driver->name, "virtual") != 0) {
+ free(bank->sectors);
+ free(bank->prot_blocks);
+ }
+
free(bank->name);
- free(bank->sectors);
- free(bank->prot_blocks);
free(bank);
bank = next;
}
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 3d6dab2..a094c20 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -32,6 +32,7 @@ extern struct flash_driver ath79_flash;
extern struct flash_driver atsamv_flash;
extern struct flash_driver avr_flash;
extern struct flash_driver bluenrgx_flash;
+extern struct flash_driver cc3220sf_flash;
extern struct flash_driver cfi_flash;
extern struct flash_driver dsp5680xx_flash;
extern struct flash_driver efm32_flash;
@@ -56,6 +57,9 @@ extern struct flash_driver numicro_flash;
extern struct flash_driver ocl_flash;
extern struct flash_driver pic32mx_flash;
extern struct flash_driver psoc4_flash;
+extern struct flash_driver psoc5lp_flash;
+extern struct flash_driver psoc5lp_eeprom_flash;
+extern struct flash_driver psoc5lp_nvl_flash;
extern struct flash_driver psoc6_flash;
extern struct flash_driver sim3x_flash;
extern struct flash_driver stellaris_flash;
@@ -91,6 +95,7 @@ static struct flash_driver *flash_drivers[] = {
&atsamv_flash,
&avr_flash,
&bluenrgx_flash,
+ &cc3220sf_flash,
&cfi_flash,
&dsp5680xx_flash,
&efm32_flash,
@@ -115,6 +120,9 @@ static struct flash_driver *flash_drivers[] = {
&ocl_flash,
&pic32mx_flash,
&psoc4_flash,
+ &psoc5lp_flash,
+ &psoc5lp_eeprom_flash,
+ &psoc5lp_nvl_flash,
&psoc6_flash,
&sim3x_flash,
&stellaris_flash,
diff --git a/src/flash/nor/psoc5lp.c b/src/flash/nor/psoc5lp.c
new file mode 100644
index 0000000..ae8e3d3
--- /dev/null
+++ b/src/flash/nor/psoc5lp.c
@@ -0,0 +1,1574 @@
+/*
+ * PSoC 5LP flash driver
+ *
+ * Copyright (c) 2016 Andreas Färber
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/time_support.h>
+#include <target/armv7m.h>
+
+#define PM_ACT_CFG0 0x400043A0
+#define PM_ACT_CFG12 0x400043AC
+#define SPC_CPU_DATA 0x40004720
+#define SPC_SR 0x40004722
+#define PRT1_PC2 0x4000500A
+#define PHUB_CH0_BASIC_CFG 0x40007010
+#define PHUB_CH0_ACTION 0x40007014
+#define PHUB_CH0_BASIC_STATUS 0x40007018
+#define PHUB_CH1_BASIC_CFG 0x40007020
+#define PHUB_CH1_ACTION 0x40007024
+#define PHUB_CH1_BASIC_STATUS 0x40007028
+#define PHUB_CFGMEM0_CFG0 0x40007600
+#define PHUB_CFGMEM0_CFG1 0x40007604
+#define PHUB_CFGMEM1_CFG0 0x40007608
+#define PHUB_CFGMEM1_CFG1 0x4000760C
+#define PHUB_TDMEM0_ORIG_TD0 0x40007800
+#define PHUB_TDMEM0_ORIG_TD1 0x40007804
+#define PHUB_TDMEM1_ORIG_TD0 0x40007808
+#define PHUB_TDMEM1_ORIG_TD1 0x4000780C
+#define PANTHER_DEVICE_ID 0x4008001C
+
+/* NVL is not actually mapped to the Cortex-M address space
+ * As we need a base addess different from other banks in the device
+ * we use the address of NVL programming data in Cypress images */
+#define NVL_META_BASE 0x90000000
+
+#define PM_ACT_CFG12_EN_EE (1 << 4)
+
+#define SPC_KEY1 0xB6
+#define SPC_KEY2 0xD3
+
+#define SPC_LOAD_BYTE 0x00
+#define SPC_LOAD_MULTI_BYTE 0x01
+#define SPC_LOAD_ROW 0x02
+#define SPC_READ_BYTE 0x03
+#define SPC_READ_MULTI_BYTE 0x04
+#define SPC_WRITE_ROW 0x05
+#define SPC_WRITE_USER_NVL 0x06
+#define SPC_PRG_ROW 0x07
+#define SPC_ERASE_SECTOR 0x08
+#define SPC_ERASE_ALL 0x09
+#define SPC_READ_HIDDEN_ROW 0x0A
+#define SPC_PROGRAM_PROTECT_ROW 0x0B
+#define SPC_GET_CHECKSUM 0x0C
+#define SPC_GET_TEMP 0x0E
+#define SPC_READ_VOLATILE_BYTE 0x10
+
+#define SPC_ARRAY_ALL 0x3F
+#define SPC_ARRAY_EEPROM 0x40
+#define SPC_ARRAY_NVL_USER 0x80
+#define SPC_ARRAY_NVL_WO 0xF8
+
+#define SPC_ROW_PROTECTION 0
+
+#define SPC_OPCODE_LEN 3
+
+#define SPC_SR_DATA_READY (1 << 0)
+#define SPC_SR_IDLE (1 << 1)
+
+#define PM_ACT_CFG0_EN_CLK_SPC (1 << 3)
+
+#define PHUB_CHx_BASIC_CFG_EN (1 << 0)
+#define PHUB_CHx_BASIC_CFG_WORK_SEP (1 << 5)
+
+#define PHUB_CHx_ACTION_CPU_REQ (1 << 0)
+
+#define PHUB_CFGMEMx_CFG0 (1 << 7)
+
+#define PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST (0xff << 16)
+#define PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR (1 << 24)
+
+#define NVL_3_ECCEN (1 << 3)
+
+#define ROW_SIZE 256
+#define ROW_ECC_SIZE 32
+#define ROWS_PER_SECTOR 64
+#define SECTOR_SIZE (ROWS_PER_SECTOR * ROW_SIZE)
+#define ROWS_PER_BLOCK 256
+#define BLOCK_SIZE (ROWS_PER_BLOCK * ROW_SIZE)
+#define SECTORS_PER_BLOCK (BLOCK_SIZE / SECTOR_SIZE)
+#define EEPROM_ROW_SIZE 16
+#define EEPROM_SECTOR_SIZE (ROWS_PER_SECTOR * EEPROM_ROW_SIZE)
+#define EEPROM_BLOCK_SIZE (ROWS_PER_BLOCK * EEPROM_ROW_SIZE)
+
+#define PART_NUMBER_LEN (17 + 1)
+
+struct psoc5lp_device {
+ uint32_t id;
+ unsigned fam;
+ unsigned speed_mhz;
+ unsigned flash_kb;
+ unsigned eeprom_kb;
+};
+
+/*
+ * Device information collected from datasheets.
+ * Different temperature ranges (C/I/Q/A) may share IDs, not differing otherwise.
+ */
+static const struct psoc5lp_device psoc5lp_devices[] = {
+ /* CY8C58LP Family Datasheet */
+ { .id = 0x2E11F069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E120069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E123069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E124069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E126069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E127069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E117069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E118069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E119069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E11C069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E114069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E115069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E116069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E160069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E161069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E1D2069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D6069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+ /* CY8C56LP Family Datasheet */
+ { .id = 0x2E10A069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E10D069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E10E069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E106069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E108069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E109069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E101069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E104069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E105069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E128069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ /* '' */
+ { .id = 0x2E122069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E129069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E163069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E156069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D3069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+ /* CY8C54LP Family Datasheet */
+ { .id = 0x2E11A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E16A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E12A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E103069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E16C069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E102069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E148069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E155069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E16B069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E12B069, .fam = 4, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E168069, .fam = 4, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E178069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E15D069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D4069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+ /* CY8C52LP Family Datasheet */
+ { .id = 0x2E11E069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E12F069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E133069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E159069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+ { .id = 0x2E11D069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E121069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E184069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E196069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 },
+ { .id = 0x2E132069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E138069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E13A069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E152069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 },
+ { .id = 0x2E15F069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E15A069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+ { .id = 0x2E1D5069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+};
+
+static void psoc5lp_get_part_number(const struct psoc5lp_device *dev, char *str)
+{
+ strcpy(str, "CY8Cabcdefg-LPxxx");
+
+ str[4] = '5';
+ str[5] = '0' + dev->fam;
+
+ switch (dev->speed_mhz) {
+ case 67:
+ str[6] = '6';
+ break;
+ case 80:
+ str[6] = '8';
+ break;
+ default:
+ str[6] = '?';
+ }
+
+ switch (dev->flash_kb) {
+ case 32:
+ str[7] = '5';
+ break;
+ case 64:
+ str[7] = '6';
+ break;
+ case 128:
+ str[7] = '7';
+ break;
+ case 256:
+ str[7] = '8';
+ break;
+ default:
+ str[7] = '?';
+ }
+
+ /* Package does not matter. */
+ strncpy(str + 8, "xx", 2);
+
+ /* Temperate range cannot uniquely be identified. */
+ str[10] = 'x';
+}
+
+static int psoc5lp_get_device_id(struct target *target, uint32_t *id)
+{
+ int retval;
+
+ retval = target_read_u32(target, PANTHER_DEVICE_ID, id); /* dummy read */
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_read_u32(target, PANTHER_DEVICE_ID, id);
+ return retval;
+}
+
+static int psoc5lp_find_device(struct target *target,
+ const struct psoc5lp_device **device)
+{
+ uint32_t device_id;
+ unsigned i;
+ int retval;
+
+ *device = NULL;
+
+ retval = psoc5lp_get_device_id(target, &device_id);
+ if (retval != ERROR_OK)
+ return retval;
+ LOG_DEBUG("PANTHER_DEVICE_ID = 0x%08" PRIX32, device_id);
+
+ for (i = 0; i < ARRAY_SIZE(psoc5lp_devices); i++) {
+ if (psoc5lp_devices[i].id == device_id) {
+ *device = &psoc5lp_devices[i];
+ return ERROR_OK;
+ }
+ }
+
+ LOG_ERROR("Device 0x%08" PRIX32 " not supported", device_id);
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+}
+
+static int psoc5lp_spc_enable_clock(struct target *target)
+{
+ int retval;
+ uint8_t pm_act_cfg0;
+
+ retval = target_read_u8(target, PM_ACT_CFG0, &pm_act_cfg0);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Cannot read PM_ACT_CFG0");
+ return retval;
+ }
+
+ if (pm_act_cfg0 & PM_ACT_CFG0_EN_CLK_SPC)
+ return ERROR_OK; /* clock already enabled */
+
+ retval = target_write_u8(target, PM_ACT_CFG0, pm_act_cfg0 | PM_ACT_CFG0_EN_CLK_SPC);
+ if (retval != ERROR_OK)
+ LOG_ERROR("Cannot enable SPC clock");
+
+ return retval;
+}
+
+static int psoc5lp_spc_write_opcode(struct target *target, uint8_t opcode)
+{
+ int retval;
+
+ retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY1);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY2 + opcode);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, opcode);
+ return retval;
+}
+
+static void psoc5lp_spc_write_opcode_buffer(struct target *target,
+ uint8_t *buf, uint8_t opcode)
+{
+ buf[0] = SPC_KEY1;
+ buf[1] = SPC_KEY2 + opcode;
+ buf[2] = opcode;
+}
+
+static int psoc5lp_spc_busy_wait_data(struct target *target)
+{
+ int64_t endtime;
+ uint8_t sr;
+ int retval;
+
+ retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */
+ if (retval != ERROR_OK)
+ return retval;
+
+ endtime = timeval_ms() + 1000; /* 1 second timeout */
+ do {
+ alive_sleep(1);
+ retval = target_read_u8(target, SPC_SR, &sr);
+ if (retval != ERROR_OK)
+ return retval;
+ if (sr == SPC_SR_DATA_READY)
+ return ERROR_OK;
+ } while (timeval_ms() < endtime);
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int psoc5lp_spc_busy_wait_idle(struct target *target)
+{
+ int64_t endtime;
+ uint8_t sr;
+ int retval;
+
+ retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */
+ if (retval != ERROR_OK)
+ return retval;
+
+ endtime = timeval_ms() + 1000; /* 1 second timeout */
+ do {
+ alive_sleep(1);
+ retval = target_read_u8(target, SPC_SR, &sr);
+ if (retval != ERROR_OK)
+ return retval;
+ if (sr == SPC_SR_IDLE)
+ return ERROR_OK;
+ } while (timeval_ms() < endtime);
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int psoc5lp_spc_load_byte(struct target *target,
+ uint8_t array_id, uint8_t offset, uint8_t value)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_BYTE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, offset);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, value);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_load_row(struct target *target,
+ uint8_t array_id, const uint8_t *data, unsigned row_size)
+{
+ unsigned i;
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_ROW);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (i = 0; i < row_size; i++) {
+ retval = target_write_u8(target, SPC_CPU_DATA, data[i]);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_byte(struct target *target,
+ uint8_t array_id, uint8_t offset, uint8_t *data)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_READ_BYTE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, offset);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u8(target, SPC_CPU_DATA, data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_write_row(struct target *target,
+ uint8_t array_id, uint16_t row_id, const uint8_t *temp)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_ROW);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id >> 8);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id & 0xff);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, temp[0]);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, temp[1]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_write_user_nvl(struct target *target,
+ uint8_t array_id)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_USER_NVL);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_erase_sector(struct target *target,
+ uint8_t array_id, uint8_t row_id)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_SECTOR);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_erase_all(struct target *target)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_ALL);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_hidden_row(struct target *target,
+ uint8_t array_id, uint8_t row_id, uint8_t *data)
+{
+ int i, retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_READ_HIDDEN_ROW);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, row_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (i = 0; i < ROW_SIZE; i++) {
+ retval = target_read_u8(target, SPC_CPU_DATA, &data[i]);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples,
+ uint8_t *data)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_GET_TEMP);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, samples);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u8(target, SPC_CPU_DATA, &data[0]);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_read_u8(target, SPC_CPU_DATA, &data[1]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_volatile_byte(struct target *target,
+ uint8_t array_id, uint8_t offset, uint8_t *data)
+{
+ int retval;
+
+ retval = psoc5lp_spc_write_opcode(target, SPC_READ_VOLATILE_BYTE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_write_u8(target, SPC_CPU_DATA, offset);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_data(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u8(target, SPC_CPU_DATA, data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return ERROR_OK;
+}
+
+/*
+ * NV Latch
+ */
+
+struct psoc5lp_nvl_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+};
+
+static int psoc5lp_nvl_read(struct flash_bank *bank,
+ uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ int retval;
+
+ retval = psoc5lp_spc_enable_clock(bank->target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ while (count > 0) {
+ retval = psoc5lp_spc_read_byte(bank->target,
+ SPC_ARRAY_NVL_USER, offset, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+ buffer++;
+ offset++;
+ count--;
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_erase(struct flash_bank *bank, int first, int last)
+{
+ LOG_WARNING("There is no erase operation for NV Latches");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+}
+
+static int psoc5lp_nvl_erase_check(struct flash_bank *bank)
+{
+ int i;
+
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased = 0;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_write(struct flash_bank *bank,
+ const uint8_t *buffer, uint32_t offset, uint32_t byte_count)
+{
+ struct target *target = bank->target;
+ uint8_t *current_data, val;
+ bool write_required = false, pullup_needed = false, ecc_changed = false;
+ uint32_t i;
+ int retval;
+
+ if (offset != 0 || byte_count != bank->size) {
+ LOG_ERROR("NVL can only be written in whole");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+ }
+
+ current_data = calloc(1, bank->size);
+ if (!current_data)
+ return ERROR_FAIL;
+ retval = psoc5lp_nvl_read(bank, current_data, offset, byte_count);
+ if (retval != ERROR_OK) {
+ free(current_data);
+ return retval;
+ }
+ for (i = offset; i < byte_count; i++) {
+ if (current_data[i] != buffer[i]) {
+ write_required = true;
+ break;
+ }
+ }
+ if (((buffer[2] & 0x80) == 0x80) && ((current_data[0] & 0x0C) != 0x08))
+ pullup_needed = true;
+ if (((buffer[3] ^ current_data[3]) & 0x08) == 0x08)
+ ecc_changed = true;
+ free(current_data);
+
+ if (!write_required) {
+ LOG_INFO("Unchanged, skipping NVL write");
+ return ERROR_OK;
+ }
+ if (pullup_needed) {
+ retval = target_read_u8(target, PRT1_PC2, &val);
+ if (retval != ERROR_OK)
+ return retval;
+ val &= 0xF0;
+ val |= 0x05;
+ retval = target_write_u8(target, PRT1_PC2, val);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ for (i = offset; i < byte_count; i++) {
+ retval = psoc5lp_spc_load_byte(target,
+ SPC_ARRAY_NVL_USER, i, buffer[i]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_read_volatile_byte(target,
+ SPC_ARRAY_NVL_USER, i, &val);
+ if (retval != ERROR_OK)
+ return retval;
+ if (val != buffer[i]) {
+ LOG_ERROR("Failed to load NVL byte %" PRIu32 ": "
+ "expected 0x%02" PRIx8 ", read 0x%02" PRIx8,
+ i, buffer[i], val);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ retval = psoc5lp_spc_write_user_nvl(target, SPC_ARRAY_NVL_USER);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (ecc_changed) {
+ retval = target_call_reset_callbacks(target, RESET_INIT);
+ if (retval != ERROR_OK)
+ LOG_WARNING("Reset failed after enabling or disabling ECC");
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_protect_check(struct flash_bank *bank)
+{
+ int i;
+
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_protected = -1;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_get_info_command(struct flash_bank *bank,
+ char *buf, int buf_size)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+ char part_number[PART_NUMBER_LEN];
+
+ psoc5lp_get_part_number(psoc_nvl_bank->device, part_number);
+
+ snprintf(buf, buf_size, "%s", part_number);
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+ int retval;
+
+ if (psoc_nvl_bank->probed)
+ return ERROR_OK;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = psoc5lp_find_device(bank->target, &psoc_nvl_bank->device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ bank->base = NVL_META_BASE;
+ bank->size = 4;
+ bank->num_sectors = 1;
+ bank->sectors = calloc(bank->num_sectors,
+ sizeof(struct flash_sector));
+ bank->sectors[0].offset = 0;
+ bank->sectors[0].size = 4;
+ bank->sectors[0].is_erased = -1;
+ bank->sectors[0].is_protected = -1;
+
+ psoc_nvl_bank->probed = true;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_nvl_auto_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+
+ if (psoc_nvl_bank->probed)
+ return ERROR_OK;
+
+ return psoc5lp_nvl_probe(bank);
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_nvl_flash_bank_command)
+{
+ struct psoc5lp_nvl_flash_bank *psoc_nvl_bank;
+
+ psoc_nvl_bank = malloc(sizeof(struct psoc5lp_nvl_flash_bank));
+ if (!psoc_nvl_bank)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ psoc_nvl_bank->probed = false;
+
+ bank->driver_priv = psoc_nvl_bank;
+
+ return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_nvl_exec_command_handlers[] = {
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_nvl_command_handlers[] = {
+ {
+ .name = "psoc5lp_nvl",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 5LP NV Latch command group",
+ .usage = "",
+ .chain = psoc5lp_nvl_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_nvl_flash = {
+ .name = "psoc5lp_nvl",
+ .commands = psoc5lp_nvl_command_handlers,
+ .flash_bank_command = psoc5lp_nvl_flash_bank_command,
+ .info = psoc5lp_nvl_get_info_command,
+ .probe = psoc5lp_nvl_probe,
+ .auto_probe = psoc5lp_nvl_auto_probe,
+ .protect_check = psoc5lp_nvl_protect_check,
+ .read = psoc5lp_nvl_read,
+ .erase = psoc5lp_nvl_erase,
+ .erase_check = psoc5lp_nvl_erase_check,
+ .write = psoc5lp_nvl_write,
+};
+
+/*
+ * EEPROM
+ */
+
+struct psoc5lp_eeprom_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+};
+
+static int psoc5lp_eeprom_erase(struct flash_bank *bank, int first, int last)
+{
+ int i, retval;
+
+ for (i = first; i <= last; i++) {
+ retval = psoc5lp_spc_erase_sector(bank->target,
+ SPC_ARRAY_EEPROM, i);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_write(struct flash_bank *bank,
+ const uint8_t *buffer, uint32_t offset, uint32_t byte_count)
+{
+ struct target *target = bank->target;
+ uint8_t temp[2];
+ unsigned row;
+ int retval;
+
+ if (offset % EEPROM_ROW_SIZE != 0) {
+ LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32,
+ offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ retval = psoc5lp_spc_get_temp(target, 3, temp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to read Die temperature");
+ return retval;
+ }
+ LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8,
+ temp[0], temp[1]);
+
+ for (row = offset / EEPROM_ROW_SIZE; byte_count >= EEPROM_ROW_SIZE; row++) {
+ retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM,
+ buffer, EEPROM_ROW_SIZE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM,
+ row, temp);
+ if (retval != ERROR_OK)
+ return retval;
+
+ buffer += EEPROM_ROW_SIZE;
+ byte_count -= EEPROM_ROW_SIZE;
+ offset += EEPROM_ROW_SIZE;
+ }
+ if (byte_count > 0) {
+ uint8_t buf[EEPROM_ROW_SIZE];
+
+ memcpy(buf, buffer, byte_count);
+ memset(buf + byte_count, bank->default_padded_value,
+ EEPROM_ROW_SIZE - byte_count);
+
+ LOG_DEBUG("Padding %d bytes", EEPROM_ROW_SIZE - byte_count);
+ retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM,
+ buf, EEPROM_ROW_SIZE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM,
+ row, temp);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_protect_check(struct flash_bank *bank)
+{
+ int i;
+
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_protected = -1;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
+ char part_number[PART_NUMBER_LEN];
+
+ psoc5lp_get_part_number(psoc_eeprom_bank->device, part_number);
+
+ snprintf(buf, buf_size, "%s", part_number);
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
+ uint32_t flash_addr = bank->base;
+ uint32_t val;
+ int i, retval;
+
+ if (psoc_eeprom_bank->probed)
+ return ERROR_OK;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = psoc5lp_find_device(bank->target, &psoc_eeprom_bank->device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u32(bank->target, PM_ACT_CFG12, &val);
+ if (retval != ERROR_OK)
+ return retval;
+ if (!(val & PM_ACT_CFG12_EN_EE)) {
+ val |= PM_ACT_CFG12_EN_EE;
+ retval = target_write_u32(bank->target, PM_ACT_CFG12, val);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ bank->size = psoc_eeprom_bank->device->eeprom_kb * 1024;
+ bank->num_sectors = DIV_ROUND_UP(bank->size, EEPROM_SECTOR_SIZE);
+ bank->sectors = calloc(bank->num_sectors,
+ sizeof(struct flash_sector));
+ for (i = 0; i < bank->num_sectors; i++) {
+ bank->sectors[i].size = EEPROM_SECTOR_SIZE;
+ bank->sectors[i].offset = flash_addr - bank->base;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+
+ flash_addr += bank->sectors[i].size;
+ }
+
+ bank->default_padded_value = bank->erased_value = 0x00;
+
+ psoc_eeprom_bank->probed = true;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_eeprom_auto_probe(struct flash_bank *bank)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
+
+ if (psoc_eeprom_bank->probed)
+ return ERROR_OK;
+
+ return psoc5lp_eeprom_probe(bank);
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_eeprom_flash_bank_command)
+{
+ struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank;
+
+ psoc_eeprom_bank = malloc(sizeof(struct psoc5lp_eeprom_flash_bank));
+ if (!psoc_eeprom_bank)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ psoc_eeprom_bank->probed = false;
+ psoc_eeprom_bank->device = NULL;
+
+ bank->driver_priv = psoc_eeprom_bank;
+
+ return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_eeprom_exec_command_handlers[] = {
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_eeprom_command_handlers[] = {
+ {
+ .name = "psoc5lp_eeprom",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 5LP EEPROM command group",
+ .usage = "",
+ .chain = psoc5lp_eeprom_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_eeprom_flash = {
+ .name = "psoc5lp_eeprom",
+ .commands = psoc5lp_eeprom_command_handlers,
+ .flash_bank_command = psoc5lp_eeprom_flash_bank_command,
+ .info = psoc5lp_eeprom_get_info_command,
+ .probe = psoc5lp_eeprom_probe,
+ .auto_probe = psoc5lp_eeprom_auto_probe,
+ .protect_check = psoc5lp_eeprom_protect_check,
+ .read = default_flash_read,
+ .erase = psoc5lp_eeprom_erase,
+ .erase_check = default_flash_blank_check,
+ .write = psoc5lp_eeprom_write,
+};
+
+/*
+ * Program Flash
+ */
+
+struct psoc5lp_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+ bool ecc_enabled;
+};
+
+static int psoc5lp_erase(struct flash_bank *bank, int first, int last)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ int i, retval;
+
+ if (!psoc_bank->ecc_enabled) {
+ /* Silently avoid erasing sectors twice */
+ if (last >= first + bank->num_sectors / 2) {
+ LOG_DEBUG("Skipping duplicate erase of sectors %d to %d",
+ first + bank->num_sectors / 2, last);
+ last = first + (bank->num_sectors / 2) - 1;
+ }
+ /* Check for any remaining ECC sectors */
+ if (last >= bank->num_sectors / 2) {
+ LOG_WARNING("Skipping erase of ECC region sectors %d to %d",
+ bank->num_sectors / 2, last);
+ last = (bank->num_sectors / 2) - 1;
+ }
+ }
+
+ for (i = first; i <= last; i++) {
+ retval = psoc5lp_spc_erase_sector(bank->target,
+ i / SECTORS_PER_BLOCK, i % SECTORS_PER_BLOCK);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+/* Derived from core.c:default_flash_blank_check() */
+static int psoc5lp_erase_check(struct flash_bank *bank)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ struct target *target = bank->target;
+ int i, retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ struct target_memory_check_block *block_array;
+ block_array = malloc(bank->num_sectors * sizeof(struct target_memory_check_block));
+ if (block_array == NULL)
+ return ERROR_FAIL;
+
+ for (i = 0; i < bank->num_sectors; i++) {
+ block_array[i].address = bank->base + bank->sectors[i].offset;
+ block_array[i].size = bank->sectors[i].size;
+ block_array[i].result = UINT32_MAX; /* erase state unknown */
+ }
+
+ bool fast_check = true;
+ for (i = 0; i < bank->num_sectors; ) {
+ retval = armv7m_blank_check_memory(target,
+ block_array + i, bank->num_sectors - i,
+ bank->erased_value);
+ if (retval < 1) {
+ /* Run slow fallback if the first run gives no result
+ * otherwise use possibly incomplete results */
+ if (i == 0)
+ fast_check = false;
+ break;
+ }
+ i += retval; /* add number of blocks done this round */
+ }
+
+ if (fast_check) {
+ if (!psoc_bank->ecc_enabled) {
+ int half_sectors = bank->num_sectors / 2;
+ for (i = 0; i < half_sectors / 2; i++)
+ bank->sectors[i].is_erased =
+ (block_array[i].result != 1)
+ ? block_array[i + half_sectors].result
+ : block_array[i].result;
+ } else {
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased = block_array[i].result;
+ }
+ retval = ERROR_OK;
+ } else {
+ LOG_ERROR("Can't run erase check - add working memory");
+ retval = ERROR_FAIL;
+ }
+ free(block_array);
+
+ return retval;
+}
+
+static int psoc5lp_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t byte_count)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ struct target *target = bank->target;
+ struct working_area *code_area, *even_row_area, *odd_row_area;
+ uint32_t row_size;
+ uint8_t temp[2], buf[12], ecc_bytes[ROW_ECC_SIZE];
+ unsigned array_id, row;
+ int i, retval;
+
+ if (offset + byte_count > bank->size) {
+ LOG_ERROR("Writing to ECC not supported");
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+ }
+
+ if (offset % ROW_SIZE != 0) {
+ LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32,
+ offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ row_size = ROW_SIZE;
+ if (!psoc_bank->ecc_enabled) {
+ row_size += ROW_ECC_SIZE;
+ memset(ecc_bytes, bank->default_padded_value, ROW_ECC_SIZE);
+ }
+
+ retval = psoc5lp_spc_get_temp(target, 3, temp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to read Die temperature");
+ return retval;
+ }
+ LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8,
+ temp[0], temp[1]);
+
+ assert(target_get_working_area_avail(target) == target->working_area_size);
+ retval = target_alloc_working_area(target,
+ target_get_working_area_avail(target) / 2, &code_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not allocate working area for program SRAM");
+ return retval;
+ }
+ assert(code_area->address < 0x20000000);
+
+ retval = target_alloc_working_area(target,
+ SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 6,
+ &even_row_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not allocate working area for even row");
+ goto err_alloc_even;
+ }
+ assert(even_row_area->address >= 0x20000000);
+
+ retval = target_alloc_working_area(target, even_row_area->size,
+ &odd_row_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not allocate working area for odd row");
+ goto err_alloc_odd;
+ }
+ assert(odd_row_area->address >= 0x20000000);
+
+ for (array_id = offset / BLOCK_SIZE; byte_count > 0; array_id++) {
+ for (row = (offset / ROW_SIZE) % ROWS_PER_BLOCK;
+ row < ROWS_PER_BLOCK && byte_count > 0; row++) {
+ bool even_row = (row % 2 == 0);
+ struct working_area *data_area = even_row ? even_row_area : odd_row_area;
+ unsigned len = MIN(ROW_SIZE, byte_count);
+
+ LOG_DEBUG("Writing load command for array %u row %u at 0x%08" TARGET_PRIxADDR,
+ array_id, row, data_area->address);
+
+ psoc5lp_spc_write_opcode_buffer(target, buf, SPC_LOAD_ROW);
+ buf[SPC_OPCODE_LEN] = array_id;
+ retval = target_write_buffer(target, data_area->address, 4, buf);
+ if (retval != ERROR_OK)
+ goto err_write;
+
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1,
+ len, buffer);
+ if (retval != ERROR_OK)
+ goto err_write;
+ buffer += len;
+ byte_count -= len;
+ offset += len;
+
+ if (len < ROW_SIZE) {
+ uint8_t padding[ROW_SIZE];
+
+ memset(padding, bank->default_padded_value, ROW_SIZE);
+
+ LOG_DEBUG("Padding %d bytes", ROW_SIZE - len);
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1 + len,
+ ROW_SIZE - len, padding);
+ if (retval != ERROR_OK)
+ goto err_write;
+ }
+
+ if (!psoc_bank->ecc_enabled) {
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1 + ROW_SIZE,
+ sizeof(ecc_bytes), ecc_bytes);
+ if (retval != ERROR_OK)
+ goto err_write;
+ }
+
+ for (i = 0; i < 3; i++)
+ buf[i] = 0x00; /* 3 NOPs for short delay */
+ psoc5lp_spc_write_opcode_buffer(target, buf + 3, SPC_PRG_ROW);
+ buf[3 + SPC_OPCODE_LEN] = array_id;
+ buf[3 + SPC_OPCODE_LEN + 1] = row >> 8;
+ buf[3 + SPC_OPCODE_LEN + 2] = row & 0xff;
+ memcpy(buf + 3 + SPC_OPCODE_LEN + 3, temp, 2);
+ buf[3 + SPC_OPCODE_LEN + 5] = 0x00; /* padding */
+ retval = target_write_buffer(target,
+ data_area->address + SPC_OPCODE_LEN + 1 + row_size,
+ 12, buf);
+ if (retval != ERROR_OK)
+ goto err_write;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CH0_BASIC_STATUS : PHUB_CH1_BASIC_STATUS,
+ (even_row ? 0 : 1) << 8);
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CH0_BASIC_CFG : PHUB_CH1_BASIC_CFG,
+ PHUB_CHx_BASIC_CFG_WORK_SEP | PHUB_CHx_BASIC_CFG_EN);
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CFGMEM0_CFG0 : PHUB_CFGMEM1_CFG0,
+ PHUB_CFGMEMx_CFG0);
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CFGMEM0_CFG1 : PHUB_CFGMEM1_CFG1,
+ ((SPC_CPU_DATA >> 16) << 16) | (data_area->address >> 16));
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_TDMEM0_ORIG_TD0 : PHUB_TDMEM1_ORIG_TD0,
+ PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR |
+ PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST |
+ ((SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 5) & 0xfff));
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_TDMEM0_ORIG_TD1 : PHUB_TDMEM1_ORIG_TD1,
+ ((SPC_CPU_DATA & 0xffff) << 16) | (data_area->address & 0xffff));
+ if (retval != ERROR_OK)
+ goto err_dma;
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+ if (retval != ERROR_OK)
+ goto err_idle;
+
+ retval = target_write_u32(target,
+ even_row ? PHUB_CH0_ACTION : PHUB_CH1_ACTION,
+ PHUB_CHx_ACTION_CPU_REQ);
+ if (retval != ERROR_OK)
+ goto err_dma_action;
+ }
+ }
+
+ retval = psoc5lp_spc_busy_wait_idle(target);
+
+err_dma_action:
+err_idle:
+err_dma:
+err_write:
+ target_free_working_area(target, odd_row_area);
+err_alloc_odd:
+ target_free_working_area(target, even_row_area);
+err_alloc_even:
+ target_free_working_area(target, code_area);
+
+ return retval;
+}
+
+static int psoc5lp_protect_check(struct flash_bank *bank)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ uint8_t row_data[ROW_SIZE];
+ const unsigned protection_bytes_per_sector = ROWS_PER_SECTOR * 2 / 8;
+ unsigned i, j, k, num_sectors;
+ int retval;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ for (i = 0; i < DIV_ROUND_UP(bank->size, BLOCK_SIZE); i++) {
+ retval = psoc5lp_spc_read_hidden_row(bank->target, i,
+ SPC_ROW_PROTECTION, row_data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Last flash array may have less rows, but in practice full sectors. */
+ if (i == bank->size / BLOCK_SIZE)
+ num_sectors = (bank->size % BLOCK_SIZE) / SECTOR_SIZE;
+ else
+ num_sectors = SECTORS_PER_BLOCK;
+
+ for (j = 0; j < num_sectors; j++) {
+ int sector_nr = i * SECTORS_PER_BLOCK + j;
+ struct flash_sector *sector = &bank->sectors[sector_nr];
+ struct flash_sector *ecc_sector;
+
+ if (psoc_bank->ecc_enabled)
+ ecc_sector = &bank->sectors[bank->num_sectors + sector_nr];
+ else
+ ecc_sector = &bank->sectors[bank->num_sectors / 2 + sector_nr];
+
+ sector->is_protected = ecc_sector->is_protected = 0;
+ for (k = protection_bytes_per_sector * j;
+ k < protection_bytes_per_sector * (j + 1); k++) {
+ assert(k < protection_bytes_per_sector * SECTORS_PER_BLOCK);
+ LOG_DEBUG("row[%u][%02u] = 0x%02" PRIx8, i, k, row_data[k]);
+ if (row_data[k] != 0x00) {
+ sector->is_protected = ecc_sector->is_protected = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ char part_number[PART_NUMBER_LEN];
+ const char *ecc;
+
+ psoc5lp_get_part_number(psoc_bank->device, part_number);
+ ecc = psoc_bank->ecc_enabled ? "ECC enabled" : "ECC disabled";
+
+ snprintf(buf, buf_size, "%s %s", part_number, ecc);
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+ uint32_t flash_addr = bank->base;
+ uint8_t nvl[4], temp[2];
+ int i, retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!psoc_bank->device) {
+ retval = psoc5lp_find_device(target, &psoc_bank->device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ bank->size = psoc_bank->device->flash_kb * 1024;
+ }
+
+ bank->num_sectors = DIV_ROUND_UP(bank->size, SECTOR_SIZE);
+
+ if (!psoc_bank->probed) {
+ retval = psoc5lp_spc_enable_clock(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* First values read are inaccurate, so do it once now. */
+ retval = psoc5lp_spc_get_temp(target, 3, temp);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Unable to read Die temperature");
+ return retval;
+ }
+
+ bank->sectors = calloc(bank->num_sectors * 2,
+ sizeof(struct flash_sector));
+ for (i = 0; i < bank->num_sectors; i++) {
+ bank->sectors[i].size = SECTOR_SIZE;
+ bank->sectors[i].offset = flash_addr - bank->base;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+
+ flash_addr += bank->sectors[i].size;
+ }
+ flash_addr = 0x48000000;
+ for (i = bank->num_sectors; i < bank->num_sectors * 2; i++) {
+ bank->sectors[i].size = ROWS_PER_SECTOR * ROW_ECC_SIZE;
+ bank->sectors[i].offset = flash_addr - bank->base;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+
+ flash_addr += bank->sectors[i].size;
+ }
+
+ bank->default_padded_value = bank->erased_value = 0x00;
+
+ psoc_bank->probed = true;
+ }
+
+ retval = psoc5lp_spc_read_byte(target, SPC_ARRAY_NVL_USER, 3, &nvl[3]);
+ if (retval != ERROR_OK)
+ return retval;
+ LOG_DEBUG("NVL[%d] = 0x%02" PRIx8, 3, nvl[3]);
+ psoc_bank->ecc_enabled = nvl[3] & NVL_3_ECCEN;
+
+ if (!psoc_bank->ecc_enabled)
+ bank->num_sectors *= 2;
+
+ return ERROR_OK;
+}
+
+static int psoc5lp_auto_probe(struct flash_bank *bank)
+{
+ return psoc5lp_probe(bank);
+}
+
+COMMAND_HANDLER(psoc5lp_handle_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 = psoc5lp_spc_erase_all(bank->target);
+ if (retval == ERROR_OK)
+ command_print(CMD_CTX, "PSoC 5LP erase succeeded");
+ else
+ command_print(CMD_CTX, "PSoC 5LP erase failed");
+
+ return retval;
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_flash_bank_command)
+{
+ struct psoc5lp_flash_bank *psoc_bank;
+
+ psoc_bank = malloc(sizeof(struct psoc5lp_flash_bank));
+ if (!psoc_bank)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ psoc_bank->probed = false;
+ psoc_bank->device = NULL;
+
+ bank->driver_priv = psoc_bank;
+
+ return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_exec_command_handlers[] = {
+ {
+ .name = "mass_erase",
+ .handler = psoc5lp_handle_mass_erase_command,
+ .mode = COMMAND_EXEC,
+ .usage = "bank_id",
+ .help = "Erase all flash data and ECC/configuration bytes, "
+ "all flash protection rows, "
+ "and all row latches in all flash arrays on the device.",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_command_handlers[] = {
+ {
+ .name = "psoc5lp",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 5LP flash command group",
+ .usage = "",
+ .chain = psoc5lp_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_flash = {
+ .name = "psoc5lp",
+ .commands = psoc5lp_command_handlers,
+ .flash_bank_command = psoc5lp_flash_bank_command,
+ .info = psoc5lp_get_info_command,
+ .probe = psoc5lp_probe,
+ .auto_probe = psoc5lp_auto_probe,
+ .protect_check = psoc5lp_protect_check,
+ .read = default_flash_read,
+ .erase = psoc5lp_erase,
+ .erase_check = psoc5lp_erase_check,
+ .write = psoc5lp_write,
+};
diff --git a/src/flash/nor/virtual.c b/src/flash/nor/virtual.c
index d5d688b..15c4bff 100644
--- a/src/flash/nor/virtual.c
+++ b/src/flash/nor/virtual.c
@@ -46,8 +46,13 @@ static void virtual_update_bank_info(struct flash_bank *bank)
bank->bus_width = master_bank->bus_width;
bank->erased_value = master_bank->erased_value;
bank->default_padded_value = master_bank->default_padded_value;
+ bank->write_start_alignment = master_bank->write_start_alignment;
+ bank->write_end_alignment = master_bank->write_end_alignment;
+ bank->minimal_write_gap = master_bank->minimal_write_gap;
bank->num_sectors = master_bank->num_sectors;
bank->sectors = master_bank->sectors;
+ bank->num_prot_blocks = master_bank->num_prot_blocks;
+ bank->prot_blocks = master_bank->prot_blocks;
}
FLASH_BANK_COMMAND_HANDLER(virtual_flash_bank_command)
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index b3b4d21..ccef018 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -156,10 +156,12 @@ endif
if IMX_GPIO
DRIVERFILES += %D%/imx_gpio.c
endif
-
if KITPROG
DRIVERFILES += %D%/kitprog.c
endif
+if XDS110
+DRIVERFILES += %D%/xds110.c
+endif
DRIVERHEADERS = \
%D%/bitbang.h \
diff --git a/src/jtag/drivers/sysfsgpio.c b/src/jtag/drivers/sysfsgpio.c
index 5a4651d..5535c71 100644
--- a/src/jtag/drivers/sysfsgpio.c
+++ b/src/jtag/drivers/sysfsgpio.c
@@ -58,11 +58,11 @@
/*
* Helper func to determine if gpio number valid
*
- * Assume here that there will be less than 1000 gpios on a system
+ * Assume here that there will be less than 10000 gpios on a system
*/
static int is_gpio_valid(int gpio)
{
- return gpio >= 0 && gpio < 1000;
+ return gpio >= 0 && gpio < 10000;
}
/*
@@ -89,7 +89,7 @@ static int open_write_close(const char *name, const char *valstr)
*/
static void unexport_sysfs_gpio(int gpio)
{
- char gpiostr[4];
+ char gpiostr[5];
if (!is_gpio_valid(gpio))
return;
@@ -113,7 +113,7 @@ static void unexport_sysfs_gpio(int gpio)
static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
{
char buf[40];
- char gpiostr[4];
+ char gpiostr[5];
int ret;
if (!is_gpio_valid(gpio))
diff --git a/src/jtag/drivers/xds110.c b/src/jtag/drivers/xds110.c
new file mode 100644
index 0000000..b97eef2
--- /dev/null
+++ b/src/jtag/drivers/xds110.c
@@ -0,0 +1,1973 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Texas Instruments, Inc. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <transport/transport.h>
+#include <jtag/swd.h>
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <jtag/tcl.h>
+#include <libusb.h>
+
+/* XDS110 USB serial number length */
+#define XDS110_SERIAL_LEN 8
+
+/* Firmware version that introduced OpenOCD support via block accesses */
+#define OCD_FIRMWARE_VERSION 0x02030011
+#define OCD_FIRMWARE_UPGRADE \
+ "XDS110: upgrade to version 2.3.0.11+ for improved support"
+
+/***************************************************************************
+ * USB Connection Buffer Definitions *
+ ***************************************************************************/
+
+/* Max USB packet size for up to USB 3.0 */
+#define MAX_PACKET 1024
+
+/*
+ * Maximum data payload that can be handled in a single call
+ * Limitation is the size of the buffers in the XDS110 firmware
+ */
+#define MAX_DATA_BLOCK 4096
+
+#ifndef USB_PAYLOAD_SIZE
+/* Largest data block plus parameters */
+#define USB_PAYLOAD_SIZE (MAX_DATA_BLOCK + 60)
+#endif
+#define MAX_RESULT_QUEUE (MAX_DATA_BLOCK / 4)
+
+/***************************************************************************
+ * USB Connection Endpoints *
+ ***************************************************************************/
+
+/* Bulk endpoints used by the XDS110 debug interface */
+#define INTERFACE_DEBUG (2)
+#define ENDPOINT_DEBUG_IN (3 | LIBUSB_ENDPOINT_IN)
+#define ENDPOINT_DEBUG_OUT (2 | LIBUSB_ENDPOINT_OUT)
+
+/***************************************************************************
+ * XDS110 Firmware API Definitions *
+ ***************************************************************************/
+
+/*
+ * Default values controlling how the host communicates commands
+ * with XDS110 firmware (automatic retry count and wait timeout)
+ */
+#define DEFAULT_ATTEMPTS (1)
+#define DEFAULT_TIMEOUT (4000)
+
+/* XDS110 API error codes */
+#define SC_ERR_NONE 0
+#define SC_ERR_XDS110_FAIL -261
+#define SC_ERR_SWD_WAIT -613
+#define SC_ERR_SWD_FAULT -614
+#define SC_ERR_SWD_PROTOCOL -615
+#define SC_ERR_SWD_PARITY -616
+#define SC_ERR_SWD_DEVICE_ID -617
+
+/* TCK frequency limits */
+#define XDS110_MIN_TCK_SPEED 100 /* kHz */
+#define XDS110_MAX_TCK_SPEED 2500 /* kHz */
+#define XDS110_TCK_PULSE_INCREMENT 66.0
+
+/* Scan mode on connect */
+#define MODE_JTAG 1
+
+/* XDS110 API JTAG state definitions */
+#define XDS_JTAG_STATE_RESET 1
+#define XDS_JTAG_STATE_IDLE 2
+#define XDS_JTAG_STATE_SHIFT_DR 3
+#define XDS_JTAG_STATE_SHIFT_IR 4
+#define XDS_JTAG_STATE_PAUSE_DR 5
+#define XDS_JTAG_STATE_PAUSE_IR 6
+#define XDS_JTAG_STATE_EXIT1_DR 8
+#define XDS_JTAG_STATE_EXIT1_IR 9
+#define XDS_JTAG_STATE_EXIT2_DR 10
+#define XDS_JTAG_STATE_EXIT2_IR 11
+#define XDS_JTAG_STATE_SELECT_DR 12
+#define XDS_JTAG_STATE_SELECT_IR 13
+#define XDS_JTAG_STATE_UPDATE_DR 14
+#define XDS_JTAG_STATE_UPDATE_IR 15
+#define XDS_JTAG_STATE_CAPTURE_DR 16
+#define XDS_JTAG_STATE_CAPTURE_IR 17
+
+/* XDS110 API JTAG transit definitions */
+#define XDS_JTAG_TRANSIT_QUICKEST 1
+#define XDS_JTAG_TRANSIT_VIA_CAPTURE 2
+#define XDS_JTAG_TRANSIT_VIA_IDLE 3
+
+/* DAP register definitions as used by XDS110 APIs */
+
+#define DAP_AP 0 /* DAP AP register type */
+#define DAP_DP 1 /* DAP DP register type */
+
+#define DAP_DP_IDCODE 0x0 /* DAP DP IDCODE register (read only) */
+#define DAP_DP_ABORT 0x0 /* DAP DP ABORT register (write only) */
+#define DAP_DP_STAT 0x4 /* DAP DP STAT register (for read only) */
+#define DAP_DP_CTRL 0x4 /* DAP DP CTRL register (for write only) */
+#define DAP_DP_ADDR 0x8 /* DAP DP SELECT register (legacy name) */
+#define DAP_DP_RESEND 0x8 /* DAP DP RESEND register (read only) */
+#define DAP_DP_SELECT 0x8 /* DAP DP SELECT register (write only) */
+#define DAP_DP_RDBUFF 0xc /* DAP DP RDBUFF Read Buffer register */
+
+#define DAP_AP_CSW 0x00 /* DAP AP Control Status Word */
+#define DAP_AP_TAR 0x04 /* DAP AP Transfer Address */
+#define DAP_AP_DRW 0x0C /* DAP AP Data Read/Write */
+#define DAP_AP_BD0 0x10 /* DAP AP Banked Data 0 */
+#define DAP_AP_BD1 0x14 /* DAP AP Banked Data 1 */
+#define DAP_AP_BD2 0x18 /* DAP AP Banked Data 2 */
+#define DAP_AP_BD3 0x1C /* DAP AP Banked Data 3 */
+#define DAP_AP_RTBL 0xF8 /* DAP AP Debug ROM Table */
+#define DAP_AP_IDR 0xFC /* DAP AP Identification Register */
+
+/* Command packet definitions */
+
+#define XDS_OUT_LEN 1 /* command (byte) */
+#define XDS_IN_LEN 4 /* error code (int) */
+
+/* XDS API Commands */
+#define XDS_CONNECT 0x01 /* Connect JTAG connection */
+#define XDS_DISCONNECT 0x02 /* Disconnect JTAG connection */
+#define XDS_VERSION 0x03 /* Get firmware version and hardware ID */
+#define XDS_SET_TCK 0x04 /* Set TCK delay (to set TCK frequency) */
+#define XDS_SET_TRST 0x05 /* Assert or deassert nTRST signal */
+#define XDS_CYCLE_TCK 0x07 /* Toggle TCK for a number of cycles */
+#define XDS_GOTO_STATE 0x09 /* Go to requested JTAG state */
+#define XDS_JTAG_SCAN 0x0c /* Send and receive JTAG scan */
+#define XDS_SET_SRST 0x0e /* Assert or deassert nSRST signal */
+#define CMAPI_CONNECT 0x0f /* CMAPI connect */
+#define CMAPI_DISCONNECT 0x10 /* CMAPI disconnect */
+#define CMAPI_ACQUIRE 0x11 /* CMAPI acquire */
+#define CMAPI_RELEASE 0x12 /* CMAPI release */
+#define CMAPI_REG_READ 0x15 /* CMAPI DAP register read */
+#define CMAPI_REG_WRITE 0x16 /* CMAPI DAP register write */
+#define SWD_CONNECT 0x17 /* Switch from JTAG to SWD connection */
+#define SWD_DISCONNECT 0x18 /* Switch from SWD to JTAG connection */
+#define CJTAG_CONNECT 0x2b /* Switch from JTAG to cJTAG connection */
+#define CJTAG_DISCONNECT 0x2c /* Switch from cJTAG to JTAG connection */
+#define OCD_DAP_REQUEST 0x3a /* Handle block of DAP requests */
+#define OCD_SCAN_REQUEST 0x3b /* Handle block of JTAG scan requests */
+#define OCD_PATHMOVE 0x3c /* Handle PATHMOVE to navigate JTAG states */
+
+#define CMD_IR_SCAN 1
+#define CMD_DR_SCAN 2
+#define CMD_RUNTEST 3
+#define CMD_STABLECLOCKS 4
+
+/* Array to convert from OpenOCD tap_state_t to XDS JTAG state */
+const uint32_t xds_jtag_state[] = {
+ XDS_JTAG_STATE_EXIT2_DR, /* TAP_DREXIT2 = 0x0 */
+ XDS_JTAG_STATE_EXIT1_DR, /* TAP_DREXIT1 = 0x1 */
+ XDS_JTAG_STATE_SHIFT_DR, /* TAP_DRSHIFT = 0x2 */
+ XDS_JTAG_STATE_PAUSE_DR, /* TAP_DRPAUSE = 0x3 */
+ XDS_JTAG_STATE_SELECT_IR, /* TAP_IRSELECT = 0x4 */
+ XDS_JTAG_STATE_UPDATE_DR, /* TAP_DRUPDATE = 0x5 */
+ XDS_JTAG_STATE_CAPTURE_DR, /* TAP_DRCAPTURE = 0x6 */
+ XDS_JTAG_STATE_SELECT_DR, /* TAP_DRSELECT = 0x7 */
+ XDS_JTAG_STATE_EXIT2_IR, /* TAP_IREXIT2 = 0x8 */
+ XDS_JTAG_STATE_EXIT1_IR, /* TAP_IREXIT1 = 0x9 */
+ XDS_JTAG_STATE_SHIFT_IR, /* TAP_IRSHIFT = 0xa */
+ XDS_JTAG_STATE_PAUSE_IR, /* TAP_IRPAUSE = 0xb */
+ XDS_JTAG_STATE_IDLE, /* TAP_IDLE = 0xc */
+ XDS_JTAG_STATE_UPDATE_IR, /* TAP_IRUPDATE = 0xd */
+ XDS_JTAG_STATE_CAPTURE_IR, /* TAP_IRCAPTURE = 0xe */
+ XDS_JTAG_STATE_RESET, /* TAP_RESET = 0xf */
+};
+
+struct scan_result {
+ bool first;
+ uint8_t *buffer;
+ uint32_t num_bits;
+};
+
+struct xds110_info {
+ /* USB connection handles and data buffers */
+ libusb_context *ctx;
+ libusb_device_handle *dev;
+ unsigned char read_payload[USB_PAYLOAD_SIZE];
+ unsigned char write_packet[3];
+ unsigned char write_payload[USB_PAYLOAD_SIZE];
+ /* Status flags */
+ bool is_connected;
+ bool is_cmapi_connected;
+ bool is_cmapi_acquired;
+ bool is_swd_mode;
+ bool is_ap_dirty;
+ /* DAP register caches */
+ uint32_t select;
+ uint32_t rdbuff;
+ bool use_rdbuff;
+ /* TCK speed and delay count*/
+ uint32_t speed;
+ uint32_t delay_count;
+ /* XDS110 serial number */
+ char serial[XDS110_SERIAL_LEN + 1];
+ /* XDS110 firmware and hardware version */
+ uint32_t firmware;
+ uint16_t hardware;
+ /* Transaction queues */
+ unsigned char txn_requests[MAX_DATA_BLOCK];
+ uint32_t *txn_dap_results[MAX_DATA_BLOCK / 4];
+ struct scan_result txn_scan_results[MAX_DATA_BLOCK / 4];
+ uint32_t txn_request_size;
+ uint32_t txn_result_size;
+ uint32_t txn_result_count;
+};
+
+static struct xds110_info xds110 = {
+ .ctx = NULL,
+ .dev = NULL,
+ .is_connected = false,
+ .is_cmapi_connected = false,
+ .is_cmapi_acquired = false,
+ .is_swd_mode = false,
+ .is_ap_dirty = false,
+ .speed = XDS110_MAX_TCK_SPEED,
+ .delay_count = 0,
+ .serial = {0},
+ .firmware = 0,
+ .hardware = 0,
+ .txn_request_size = 0,
+ .txn_result_size = 0,
+ .txn_result_count = 0
+};
+
+static inline void xds110_set_u32(uint8_t *buffer, uint32_t value)
+{
+ buffer[3] = (value >> 24) & 0xff;
+ buffer[2] = (value >> 16) & 0xff;
+ buffer[1] = (value >> 8) & 0xff;
+ buffer[0] = (value >> 0) & 0xff;
+}
+
+static inline void xds110_set_u16(uint8_t *buffer, uint16_t value)
+{
+ buffer[1] = (value >> 8) & 0xff;
+ buffer[0] = (value >> 0) & 0xff;
+}
+
+static inline uint32_t xds110_get_u32(uint8_t *buffer)
+{
+ uint32_t value = (((uint32_t)buffer[3]) << 24) |
+ (((uint32_t)buffer[2]) << 16) |
+ (((uint32_t)buffer[1]) << 8) |
+ (((uint32_t)buffer[0]) << 0);
+ return value;
+}
+
+static inline uint16_t xds110_get_u16(uint8_t *buffer)
+{
+ uint16_t value = (((uint32_t)buffer[1]) << 8) |
+ (((uint32_t)buffer[0]) << 0);
+ return value;
+}
+
+/***************************************************************************
+ * usb connection routines *
+ * *
+ * The following functions handle connecting, reading, and writing to *
+ * the XDS110 over USB using the libusb library. *
+ ***************************************************************************/
+
+static bool usb_connect(void)
+{
+ libusb_context *ctx = NULL;
+ libusb_device **list = NULL;
+ libusb_device_handle *dev = NULL;
+
+ struct libusb_device_descriptor desc;
+
+ uint16_t vid = 0x0451;
+ uint16_t pid = 0xbef3;
+ ssize_t count = 0;
+ ssize_t i = 0;
+ int result = 0;
+ bool found = false;
+
+ /* Initialize libusb context */
+ result = libusb_init(&ctx);
+
+ if (0 == result) {
+ /* Get list of USB devices attached to system */
+ count = libusb_get_device_list(ctx, &list);
+ if (count <= 0) {
+ result = -1;
+ list = NULL;
+ }
+ }
+
+ if (0 == result) {
+ /* Scan through list of devices for any XDS110s */
+ for (i = 0; i < count; i++) {
+ /* Check for device VID/PID match */
+ libusb_get_device_descriptor(list[i], &desc);
+ if (desc.idVendor == vid && desc.idProduct == pid) {
+ result = libusb_open(list[i], &dev);
+ if (0 == result) {
+ const int MAX_DATA = 256;
+ unsigned char data[MAX_DATA + 1];
+ *data = '\0';
+
+ /* May be the requested device if serial number matches */
+ if (0 == xds110.serial[0]) {
+ /* No serial number given; match first XDS110 found */
+ found = true;
+ break;
+ } else {
+ /* Get the device's serial number string */
+ result = libusb_get_string_descriptor_ascii(dev,
+ desc.iSerialNumber, data, MAX_DATA);
+ if (0 < result &&
+ 0 == strcmp((char *)data, (char *)xds110.serial)) {
+ found = true;
+ break;
+ }
+ }
+
+ /* If we fall though to here, we don't want this device */
+ libusb_close(dev);
+ dev = NULL;
+ }
+ }
+ }
+ }
+
+ /*
+ * We can fall through the for() loop with two possible exit conditions:
+ * 1) found the right XDS110, and that device is open
+ * 2) didn't find the XDS110, and no devices are currently open
+ */
+
+ if (NULL != list) {
+ /* Free the device list, we're done with it */
+ libusb_free_device_list(list, 1);
+ }
+
+ if (found) {
+ /* Save the context and device handles */
+ xds110.ctx = ctx;
+ xds110.dev = dev;
+
+ /* Set libusb to auto detach kernel */
+ (void)libusb_set_auto_detach_kernel_driver(dev, 1);
+
+ /* Claim the debug interface on the XDS110 */
+ result = libusb_claim_interface(dev, INTERFACE_DEBUG);
+ } else {
+ /* Couldn't find an XDS110, flag the error */
+ result = -1;
+ }
+
+ /* On an error, clean up what we can */
+ if (0 != result) {
+ if (NULL != dev) {
+ /* Release the debug and data interface on the XDS110 */
+ (void)libusb_release_interface(dev, INTERFACE_DEBUG);
+ libusb_close(dev);
+ }
+ if (NULL != ctx)
+ libusb_exit(ctx);
+ xds110.ctx = NULL;
+ xds110.dev = NULL;
+ }
+
+ /* Log the results */
+ if (0 == result)
+ LOG_INFO("XDS110: connected");
+ else
+ LOG_ERROR("XDS110: failed to connect");
+
+ return (0 == result) ? true : false;
+}
+
+static void usb_disconnect(void)
+{
+ if (NULL != xds110.dev) {
+ /* Release the debug and data interface on the XDS110 */
+ (void)libusb_release_interface(xds110.dev, INTERFACE_DEBUG);
+ libusb_close(xds110.dev);
+ xds110.dev = NULL;
+ }
+ if (NULL != xds110.ctx) {
+ libusb_exit(xds110.ctx);
+ xds110.ctx = NULL;
+ }
+
+ LOG_INFO("XDS110: disconnected");
+}
+
+static bool usb_read(unsigned char *buffer, int size, int *bytes_read,
+ int timeout)
+{
+ int result;
+
+ if (NULL == xds110.dev || NULL == buffer || NULL == bytes_read)
+ return false;
+
+ /* Force a non-zero timeout to prevent blocking */
+ if (0 == timeout)
+ timeout = DEFAULT_TIMEOUT;
+
+ result = libusb_bulk_transfer(xds110.dev, ENDPOINT_DEBUG_IN, buffer, size,
+ bytes_read, timeout);
+
+ return (0 == result) ? true : false;
+}
+
+static bool usb_write(unsigned char *buffer, int size, int *written)
+{
+ int bytes_written = 0;
+ int result = LIBUSB_SUCCESS;
+ int retries = 0;
+
+ if (NULL == xds110.dev || NULL == buffer)
+ return false;
+
+ result = libusb_bulk_transfer(xds110.dev, ENDPOINT_DEBUG_OUT, buffer,
+ size, &bytes_written, 0);
+
+ while (LIBUSB_ERROR_PIPE == result && retries < 3) {
+ /* Try clearing the pipe stall and retry transfer */
+ libusb_clear_halt(xds110.dev, ENDPOINT_DEBUG_OUT);
+ result = libusb_bulk_transfer(xds110.dev, ENDPOINT_DEBUG_OUT, buffer,
+ size, &bytes_written, 0);
+ retries++;
+ }
+
+ if (NULL != written)
+ *written = bytes_written;
+
+ return (0 == result && size == bytes_written) ? true : false;
+}
+
+static bool usb_get_response(uint32_t *total_bytes_read, uint32_t timeout)
+{
+ static unsigned char buffer[MAX_PACKET];
+ int bytes_read;
+ uint16_t size;
+ uint16_t count;
+ bool success;
+
+ size = 0;
+ success = true;
+ while (success) {
+ success = usb_read(buffer, sizeof(buffer), &bytes_read, timeout);
+ if (success) {
+ /*
+ * Validate that this appears to be a good response packet
+ * First check it contains enough data for header and error
+ * code, plus the first character is the start character
+ */
+ if (bytes_read >= 7 && '*' == buffer[0]) {
+ /* Extract the payload size */
+ size = xds110_get_u16(&buffer[1]);
+ /* Sanity test on payload size */
+ if (USB_PAYLOAD_SIZE >= size && 4 <= size) {
+ /* Check we didn't get more data than expected */
+ if ((bytes_read - 3) <= size) {
+ /* Packet appears to be valid, move on */
+ break;
+ }
+ }
+ }
+ }
+ /*
+ * Somehow received an invalid packet, retry till we
+ * time out or a valid response packet is received
+ */
+ }
+
+ /* Abort now if we didn't receive a valid response */
+ if (!success) {
+ if (NULL != total_bytes_read)
+ *total_bytes_read = 0;
+ return false;
+ }
+
+ /* Build the return payload into xds110.read_payload */
+
+ /* Copy over payload data from received buffer (skipping header) */
+ count = 0;
+ bytes_read -= 3;
+ memcpy((void *)&xds110.read_payload[count], (void *)&buffer[3], bytes_read);
+ count += bytes_read;
+ /*
+ * Drop timeout to just 1/2 second. Once the XDS110 starts sending
+ * a response, the remaining packets should arrive in short order
+ */
+ if (timeout > 500)
+ timeout = 500; /* ms */
+
+ /* If there's more data to retrieve, get it now */
+ while ((count < size) && success) {
+ success = usb_read(buffer, sizeof(buffer), &bytes_read, timeout);
+ if (success) {
+ if ((count + bytes_read) > size) {
+ /* Read too much data, not a valid packet, abort */
+ success = false;
+ } else {
+ /* Copy this data over to xds110.read_payload */
+ memcpy((void *)&xds110.read_payload[count], (void *)buffer,
+ bytes_read);
+ count += bytes_read;
+ }
+ }
+ }
+
+ if (!success)
+ count = 0;
+ if (NULL != total_bytes_read)
+ *total_bytes_read = count;
+
+ return success;
+}
+
+static bool usb_send_command(uint16_t size)
+{
+ int written;
+ bool success = true;
+
+ /* Check the packet length */
+ if (size > USB_PAYLOAD_SIZE)
+ return false;
+
+ /* Place the start character into the packet buffer */
+ xds110.write_packet[0] = '*';
+
+ /* Place the payload size into the packet buffer */
+ xds110_set_u16(&xds110.write_packet[1], size);
+
+ /* Adjust size to include header */
+ size += 3;
+
+ /* Send the data via the USB connection */
+ success = usb_write(xds110.write_packet, (int)size, &written);
+
+ /* Check if the correct number of bytes was written */
+ if (written != (int)size)
+ success = false;
+
+ return success;
+}
+
+/***************************************************************************
+ * XDS110 firmware API routines *
+ * *
+ * The following functions handle calling into the XDS110 firmware to *
+ * perform requested debug actions. *
+ ***************************************************************************/
+
+static bool xds_execute(uint32_t out_length, uint32_t in_length,
+ uint32_t attempts, uint32_t timeout)
+{
+ bool done = false;
+ bool success = true;
+ int error = 0;
+ uint32_t bytes_read = 0;
+
+ if (NULL == xds110.dev)
+ return false;
+
+ while (!done && attempts > 0) {
+ attempts--;
+
+ /* Send command to XDS110 */
+ success = usb_send_command(out_length);
+
+ if (success) {
+ /* Get response from XDS110 */
+ success = usb_get_response(&bytes_read, timeout);
+ }
+
+ if (success) {
+ /* Check for valid response from XDS code handling */
+ if (bytes_read != in_length) {
+ /* Unexpected amount of data returned */
+ success = false;
+ } else {
+ /* Extract error code from return packet */
+ error = (int)xds110_get_u32(&xds110.read_payload[0]);
+ done = true;
+ }
+ }
+ }
+
+ if (!success)
+ error = SC_ERR_XDS110_FAIL;
+
+ if (0 != error)
+ success = false;
+
+ return success;
+}
+
+static bool xds_connect(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = XDS_CONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool xds_disconnect(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = XDS_DISCONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool xds_version(uint32_t *firmware_id, uint16_t *hardware_id)
+{
+ uint8_t *fw_id_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */
+ uint8_t *hw_id_pntr = &xds110.read_payload[XDS_IN_LEN + 4]; /* 16-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_VERSION;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN + 6, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ if (success) {
+ if (NULL != firmware_id)
+ *firmware_id = xds110_get_u32(fw_id_pntr);
+ if (NULL != hardware_id)
+ *hardware_id = xds110_get_u16(hw_id_pntr);
+ }
+
+ return success;
+}
+
+static bool xds_set_tck_delay(uint32_t delay)
+{
+ uint8_t *delay_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_SET_TCK;
+
+ xds110_set_u32(delay_pntr, delay);
+
+ success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool xds_set_trst(uint8_t trst)
+{
+ uint8_t *trst_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_SET_TRST;
+
+ *trst_pntr = trst;
+
+ success = xds_execute(XDS_OUT_LEN + 1, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool xds_cycle_tck(uint32_t count)
+{
+ uint8_t *count_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_CYCLE_TCK;
+
+ xds110_set_u32(count_pntr, count);
+
+ success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool xds_goto_state(uint32_t state)
+{
+ uint8_t *state_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */
+ uint8_t *transit_pntr = &xds110.write_payload[XDS_OUT_LEN+4]; /* 32-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_GOTO_STATE;
+
+ xds110_set_u32(state_pntr, state);
+ xds110_set_u32(transit_pntr, XDS_JTAG_TRANSIT_QUICKEST);
+
+ success = xds_execute(XDS_OUT_LEN+8, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool xds_jtag_scan(uint32_t shift_state, uint16_t shift_bits,
+ uint32_t end_state, uint8_t *data_out, uint8_t *data_in)
+{
+ uint8_t *bits_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 16-bits */
+ uint8_t *path_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */
+ uint8_t *trans1_pntr = &xds110.write_payload[XDS_OUT_LEN + 3]; /* 8-bits */
+ uint8_t *end_pntr = &xds110.write_payload[XDS_OUT_LEN + 4]; /* 8-bits */
+ uint8_t *trans2_pntr = &xds110.write_payload[XDS_OUT_LEN + 5]; /* 8-bits */
+ uint8_t *pre_pntr = &xds110.write_payload[XDS_OUT_LEN + 6]; /* 16-bits */
+ uint8_t *pos_pntr = &xds110.write_payload[XDS_OUT_LEN + 8]; /* 16-bits */
+ uint8_t *delay_pntr = &xds110.write_payload[XDS_OUT_LEN + 10]; /* 16-bits */
+ uint8_t *rep_pntr = &xds110.write_payload[XDS_OUT_LEN + 12]; /* 16-bits */
+ uint8_t *out_pntr = &xds110.write_payload[XDS_OUT_LEN + 14]; /* 16-bits */
+ uint8_t *in_pntr = &xds110.write_payload[XDS_OUT_LEN + 16]; /* 16-bits */
+ uint8_t *data_out_pntr = &xds110.write_payload[XDS_OUT_LEN + 18];
+ uint8_t *data_in_pntr = &xds110.read_payload[XDS_IN_LEN+0];
+
+ uint16_t total_bytes = DIV_ROUND_UP(shift_bits, 8);
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_JTAG_SCAN;
+
+ xds110_set_u16(bits_pntr, shift_bits); /* bits to scan */
+ *path_pntr = (uint8_t)(shift_state & 0xff); /* IR vs DR path */
+ *trans1_pntr = (uint8_t)XDS_JTAG_TRANSIT_QUICKEST; /* start state route */
+ *end_pntr = (uint8_t)(end_state & 0xff); /* JTAG state after scan */
+ *trans2_pntr = (uint8_t)XDS_JTAG_TRANSIT_QUICKEST; /* end state route */
+ xds110_set_u16(pre_pntr, 0); /* number of preamble bits */
+ xds110_set_u16(pos_pntr, 0); /* number of postamble bits */
+ xds110_set_u16(delay_pntr, 0); /* number of extra TCKs after scan */
+ xds110_set_u16(rep_pntr, 1); /* number of repetitions */
+ xds110_set_u16(out_pntr, total_bytes); /* out buffer offset (if repeats) */
+ xds110_set_u16(in_pntr, total_bytes); /* in buffer offset (if repeats) */
+
+ memcpy((void *)data_out_pntr, (void *)data_out, total_bytes);
+
+ success = xds_execute(XDS_OUT_LEN + 18 + total_bytes,
+ XDS_IN_LEN + total_bytes, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT);
+
+ if (success)
+ memcpy((void *)data_in, (void *)data_in_pntr, total_bytes);
+
+ return success;
+}
+
+static bool xds_set_srst(uint8_t srst)
+{
+ uint8_t *srst_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = XDS_SET_SRST;
+
+ *srst_pntr = srst;
+
+ success = xds_execute(XDS_OUT_LEN + 1, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool cmapi_connect(uint32_t *idcode)
+{
+ uint8_t *idcode_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = CMAPI_CONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN+4, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ if (success) {
+ if (NULL != idcode)
+ *idcode = xds110_get_u32(idcode_pntr);
+ }
+
+ return success;
+}
+
+static bool cmapi_disconnect(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = CMAPI_DISCONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool cmapi_acquire(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = CMAPI_ACQUIRE;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool cmapi_release(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = CMAPI_RELEASE;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool cmapi_read_dap_reg(uint32_t type, uint32_t ap_num,
+ uint32_t address, uint32_t *value)
+{
+ uint8_t *type_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */
+ uint8_t *ap_num_pntr = &xds110.write_payload[XDS_OUT_LEN + 1]; /* 8-bits */
+ uint8_t *address_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */
+ uint8_t *value_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = CMAPI_REG_READ;
+
+ *type_pntr = (uint8_t)(type & 0xff);
+ *ap_num_pntr = (uint8_t)(ap_num & 0xff);
+ *address_pntr = (uint8_t)(address & 0xff);
+
+ success = xds_execute(XDS_OUT_LEN + 3, XDS_IN_LEN + 4, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ if (success) {
+ if (NULL != value)
+ *value = xds110_get_u32(value_pntr);
+ }
+
+ return success;
+}
+
+static bool cmapi_write_dap_reg(uint32_t type, uint32_t ap_num,
+ uint32_t address, uint32_t *value)
+{
+ uint8_t *type_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */
+ uint8_t *ap_num_pntr = &xds110.write_payload[XDS_OUT_LEN + 1]; /* 8-bits */
+ uint8_t *address_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */
+ uint8_t *value_pntr = &xds110.write_payload[XDS_OUT_LEN + 3]; /* 32-bits */
+
+ bool success;
+
+ if (NULL == value)
+ return false;
+
+ xds110.write_payload[0] = CMAPI_REG_WRITE;
+
+ *type_pntr = (uint8_t)(type & 0xff);
+ *ap_num_pntr = (uint8_t)(ap_num & 0xff);
+ *address_pntr = (uint8_t)(address & 0xff);
+ xds110_set_u32(value_pntr, *value);
+
+ success = xds_execute(XDS_OUT_LEN + 7, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool swd_connect(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = SWD_CONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool swd_disconnect(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = SWD_DISCONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool cjtag_connect(uint32_t format)
+{
+ uint8_t *format_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */
+
+ bool success;
+
+ xds110.write_payload[0] = CJTAG_CONNECT;
+
+ xds110_set_u32(format_pntr, format);
+
+ success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool cjtag_disconnect(void)
+{
+ bool success;
+
+ xds110.write_payload[0] = CJTAG_DISCONNECT;
+
+ success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+static bool ocd_dap_request(uint8_t *dap_requests, uint32_t request_size,
+ uint32_t *dap_results, uint32_t result_count)
+{
+ uint8_t *request_pntr = &xds110.write_payload[XDS_OUT_LEN + 0];
+ uint8_t *result_pntr = &xds110.read_payload[XDS_IN_LEN + 0];
+
+ bool success;
+
+ if (NULL == dap_requests || NULL == dap_results)
+ return false;
+
+ xds110.write_payload[0] = OCD_DAP_REQUEST;
+
+ memcpy((void *)request_pntr, (void *)dap_requests, request_size);
+
+ success = xds_execute(XDS_OUT_LEN + request_size,
+ XDS_IN_LEN + (result_count * 4), DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ if (success && (result_count > 0))
+ memcpy((void *)dap_results, (void *)result_pntr, result_count * 4);
+
+ return success;
+}
+
+static bool ocd_scan_request(uint8_t *scan_requests, uint32_t request_size,
+ uint8_t *scan_results, uint32_t result_size)
+{
+ uint8_t *request_pntr = &xds110.write_payload[XDS_OUT_LEN + 0];
+ uint8_t *result_pntr = &xds110.read_payload[XDS_IN_LEN + 0];
+
+ bool success;
+
+ if (NULL == scan_requests || NULL == scan_results)
+ return false;
+
+ xds110.write_payload[0] = OCD_SCAN_REQUEST;
+
+ memcpy((void *)request_pntr, (void *)scan_requests, request_size);
+
+ success = xds_execute(XDS_OUT_LEN + request_size,
+ XDS_IN_LEN + result_size, DEFAULT_ATTEMPTS,
+ DEFAULT_TIMEOUT);
+
+ if (success && (result_size > 0))
+ memcpy((void *)scan_results, (void *)result_pntr, result_size);
+
+ return success;
+}
+
+static bool ocd_pathmove(uint32_t num_states, uint8_t *path)
+{
+ uint8_t *num_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */
+ uint8_t *path_pntr = &xds110.write_payload[XDS_OUT_LEN + 4];
+
+ bool success;
+
+ if (NULL == path)
+ return false;
+
+ xds110.write_payload[0] = OCD_PATHMOVE;
+
+ xds110_set_u32(num_pntr, num_states);
+
+ memcpy((void *)path_pntr, (void *)path, num_states);
+
+ success = xds_execute(XDS_OUT_LEN + 4 + num_states, XDS_IN_LEN,
+ DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT);
+
+ return success;
+}
+
+/***************************************************************************
+ * swd driver interface *
+ * *
+ * The following functions provide SWD support to OpenOCD. *
+ ***************************************************************************/
+
+static int xds110_swd_init(void)
+{
+ xds110.is_swd_mode = true;
+ return ERROR_OK;
+}
+
+static int xds110_swd_switch_seq(enum swd_special_seq seq)
+{
+ uint32_t idcode;
+ bool success;
+
+ switch (seq) {
+ case LINE_RESET:
+ LOG_ERROR("Sequence SWD line reset (%d) not supported", seq);
+ return ERROR_FAIL;
+ case JTAG_TO_SWD:
+ LOG_DEBUG("JTAG-to-SWD");
+ xds110.is_swd_mode = false;
+ xds110.is_cmapi_connected = false;
+ xds110.is_cmapi_acquired = false;
+ /* Run sequence to put target in SWD mode */
+ success = swd_connect();
+ /* Re-iniitialize CMAPI API for DAP access */
+ if (success) {
+ xds110.is_swd_mode = true;
+ success = cmapi_connect(&idcode);
+ if (success) {
+ xds110.is_cmapi_connected = true;
+ success = cmapi_acquire();
+ }
+ }
+ break;
+ case SWD_TO_JTAG:
+ LOG_DEBUG("SWD-to-JTAG");
+ xds110.is_swd_mode = false;
+ xds110.is_cmapi_connected = false;
+ xds110.is_cmapi_acquired = false;
+ /* Run sequence to put target in JTAG mode */
+ success = swd_disconnect();
+ if (success) {
+ /* Re-initialize JTAG interface */
+ success = cjtag_connect(MODE_JTAG);
+ }
+ break;
+ default:
+ LOG_ERROR("Sequence %d not supported", seq);
+ return ERROR_FAIL;
+ }
+
+ if (success)
+ return ERROR_OK;
+ else
+ return ERROR_FAIL;
+}
+
+static bool xds110_legacy_read_reg(uint8_t cmd, uint32_t *value)
+{
+ /* Make sure this is a read request */
+ bool is_read_request = (0 != (SWD_CMD_RnW & cmd));
+ /* Determine whether this is a DP or AP register access */
+ uint32_t type = (0 != (SWD_CMD_APnDP & cmd)) ? DAP_AP : DAP_DP;
+ /* Determine the AP number from cached SELECT value */
+ uint32_t ap_num = (xds110.select & 0xff000000) >> 24;
+ /* Extract register address from command */
+ uint32_t address = ((cmd & SWD_CMD_A32) >> 1);
+ /* Extract bank address from cached SELECT value */
+ uint32_t bank = (xds110.select & 0x000000f0);
+
+ uint32_t reg_value = 0;
+ uint32_t temp_value = 0;
+
+ bool success;
+
+ if (!is_read_request)
+ return false;
+
+ if (DAP_AP == type) {
+ /* Add bank address to register address for CMAPI call */
+ address |= bank;
+ }
+
+ if (DAP_DP == type && DAP_DP_RDBUFF == address && xds110.use_rdbuff) {
+ /* If RDBUFF is cached and this is a DP RDBUFF read, use the cache */
+ reg_value = xds110.rdbuff;
+ success = true;
+ } else if (DAP_AP == type && DAP_AP_DRW == address && xds110.use_rdbuff) {
+ /* If RDBUFF is cached and this is an AP DRW read, use the cache, */
+ /* but still call into the firmware to get the next read. */
+ reg_value = xds110.rdbuff;
+ success = cmapi_read_dap_reg(type, ap_num, address, &temp_value);
+ } else {
+ success = cmapi_read_dap_reg(type, ap_num, address, &temp_value);
+ if (success)
+ reg_value = temp_value;
+ }
+
+ /* Mark that we have consumed or invalidated the RDBUFF cache */
+ xds110.use_rdbuff = false;
+
+ /* Handle result of read attempt */
+ if (!success)
+ LOG_ERROR("XDS110: failed to read DAP register");
+ else if (NULL != value)
+ *value = reg_value;
+
+ if (success && DAP_AP == type) {
+ /*
+ * On a successful DAP AP read, we actually have the value from RDBUFF,
+ * the firmware will have run the AP request and made the RDBUFF read
+ */
+ xds110.use_rdbuff = true;
+ xds110.rdbuff = temp_value;
+ }
+
+ return success;
+}
+
+static bool xds110_legacy_write_reg(uint8_t cmd, uint32_t value)
+{
+ /* Make sure this isn't a read request */
+ bool is_read_request = (0 != (SWD_CMD_RnW & cmd));
+ /* Determine whether this is a DP or AP register access */
+ uint32_t type = (0 != (SWD_CMD_APnDP & cmd)) ? DAP_AP : DAP_DP;
+ /* Determine the AP number from cached SELECT value */
+ uint32_t ap_num = (xds110.select & 0xff000000) >> 24;
+ /* Extract register address from command */
+ uint32_t address = ((cmd & SWD_CMD_A32) >> 1);
+ /* Extract bank address from cached SELECT value */
+ uint32_t bank = (xds110.select & 0x000000f0);
+
+ bool success;
+
+ if (is_read_request)
+ return false;
+
+ /* Invalidate the RDBUFF cache */
+ xds110.use_rdbuff = false;
+
+ if (DAP_AP == type) {
+ /* Add bank address to register address for CMAPI call */
+ address |= bank;
+ /* Any write to an AP register invalidates the firmware's cache */
+ xds110.is_ap_dirty = true;
+ } else if (DAP_DP_SELECT == address) {
+ /* Any write to the SELECT register invalidates the firmware's cache */
+ xds110.is_ap_dirty = true;
+ }
+
+ success = cmapi_write_dap_reg(type, ap_num, address, &value);
+
+ if (!success) {
+ LOG_ERROR("XDS110: failed to write DAP register");
+ } else {
+ /*
+ * If the debugger wrote to SELECT, cache the value
+ * to use to build the apNum and address values above
+ */
+ if ((DAP_DP == type) && (DAP_DP_SELECT == address))
+ xds110.select = value;
+ }
+
+ return success;
+}
+
+static int xds110_swd_run_queue(void)
+{
+ static uint32_t dap_results[MAX_RESULT_QUEUE];
+ uint8_t cmd;
+ uint32_t request;
+ uint32_t result;
+ uint32_t value;
+ bool success = true;
+
+ if (0 == xds110.txn_request_size)
+ return ERROR_OK;
+
+ /* Terminate request queue */
+ xds110.txn_requests[xds110.txn_request_size++] = 0;
+
+ if (xds110.firmware >= OCD_FIRMWARE_VERSION) {
+ /* XDS110 firmware has the API to directly handle the queue */
+ success = ocd_dap_request(xds110.txn_requests,
+ xds110.txn_request_size, dap_results, xds110.txn_result_count);
+ } else {
+ /* Legacy firmware needs to handle queue via discrete DAP calls */
+ request = 0;
+ result = 0;
+ while (xds110.txn_requests[request] != 0) {
+ cmd = xds110.txn_requests[request++];
+ if (0 == (SWD_CMD_RnW & cmd)) {
+ /* DAP register write command */
+ value = (uint32_t)(xds110.txn_requests[request++]) << 0;
+ value |= (uint32_t)(xds110.txn_requests[request++]) << 8;
+ value |= (uint32_t)(xds110.txn_requests[request++]) << 16;
+ value |= (uint32_t)(xds110.txn_requests[request++]) << 24;
+ if (success)
+ success = xds110_legacy_write_reg(cmd, value);
+ } else {
+ /* DAP register read command */
+ value = 0;
+ if (success)
+ success = xds110_legacy_read_reg(cmd, &value);
+ dap_results[result++] = value;
+ }
+ }
+ }
+
+ /* Transfer results into caller's buffers */
+ for (result = 0; result < xds110.txn_result_count; result++)
+ if (0 != xds110.txn_dap_results[result])
+ *xds110.txn_dap_results[result] = dap_results[result];
+
+ xds110.txn_request_size = 0;
+ xds110.txn_result_size = 0;
+ xds110.txn_result_count = 0;
+
+ return (success) ? ERROR_OK : ERROR_FAIL;
+}
+
+static void xds110_swd_queue_cmd(uint8_t cmd, uint32_t *value)
+{
+ /* Check if this is a read or write request */
+ bool is_read_request = (0 != (SWD_CMD_RnW & cmd));
+ /* Determine whether this is a DP or AP register access */
+ uint32_t type = (0 != (SWD_CMD_APnDP & cmd)) ? DAP_AP : DAP_DP;
+ /* Extract register address from command */
+ uint32_t address = ((cmd & SWD_CMD_A32) >> 1);
+ uint32_t request_size = (is_read_request) ? 1 : 5;
+
+ /* Check if new request would be too large to fit */
+ if (((xds110.txn_request_size + request_size + 1) > MAX_DATA_BLOCK) ||
+ ((xds110.txn_result_count + 1) > MAX_RESULT_QUEUE))
+ xds110_swd_run_queue();
+
+ /* Set the START bit in cmd to ensure cmd is not zero */
+ /* (a value of zero is used to terminate the buffer) */
+ cmd |= SWD_CMD_START;
+
+ /* Add request to queue; queue is built marshalled for XDS110 call */
+ if (is_read_request) {
+ /* Queue read request, save pointer to pass back result */
+ xds110.txn_requests[xds110.txn_request_size++] = cmd;
+ xds110.txn_dap_results[xds110.txn_result_count++] = value;
+ xds110.txn_result_size += 4;
+ } else {
+ /* Check for and prevent sticky overrun detection */
+ if (DAP_DP == type && DAP_DP_CTRL == address &&
+ (*value & CORUNDETECT)) {
+ LOG_DEBUG("XDS110: refusing to enable sticky overrun detection");
+ *value &= ~CORUNDETECT;
+ }
+ /* Queue write request, add value directly to queue buffer */
+ xds110.txn_requests[xds110.txn_request_size++] = cmd;
+ xds110.txn_requests[xds110.txn_request_size++] = (*value >> 0) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (*value >> 8) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (*value >> 16) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (*value >> 24) & 0xff;
+ }
+}
+
+static void xds110_swd_read_reg(uint8_t cmd, uint32_t *value,
+ uint32_t ap_delay_clk)
+{
+ xds110_swd_queue_cmd(cmd, value);
+}
+static void xds110_swd_write_reg(uint8_t cmd, uint32_t value,
+ uint32_t ap_delay_clk)
+{
+ xds110_swd_queue_cmd(cmd, &value);
+}
+
+/***************************************************************************
+ * jtag interface *
+ * *
+ * The following functions provide XDS110 interface to OpenOCD. *
+ ***************************************************************************/
+
+static void xds110_show_info(void)
+{
+ uint32_t firmware = xds110.firmware;
+
+ LOG_INFO("XDS110: firmware version = %d.%d.%d.%d",
+ (((firmware >> 28) & 0xf) * 10) + ((firmware >> 24) & 0xf),
+ (((firmware >> 20) & 0xf) * 10) + ((firmware >> 16) & 0xf),
+ (((firmware >> 12) & 0xf) * 10) + ((firmware >> 8) & 0xf),
+ (((firmware >> 4) & 0xf) * 10) + ((firmware >> 0) & 0xf));
+ LOG_INFO("XDS110: hardware version = 0x%04x", xds110.hardware);
+ if (0 != xds110.serial[0])
+ LOG_INFO("XDS110: serial number = %s)", xds110.serial);
+ if (xds110.is_swd_mode) {
+ LOG_INFO("XDS110: connected to target via SWD");
+ LOG_INFO("XDS110: SWCLK set to %d kHz", xds110.speed);
+ } else {
+ LOG_INFO("XDS110: connected to target via JTAG");
+ LOG_INFO("XDS110: TCK set to %d kHz", xds110.speed);
+ }
+
+ /* Alert user that there's a better firmware to use */
+ if (firmware < OCD_FIRMWARE_VERSION) {
+ LOG_WARNING("XDS110: the firmware is not optimized for OpenOCD");
+ LOG_WARNING(OCD_FIRMWARE_UPGRADE);
+ }
+}
+
+static int xds110_quit(void)
+{
+ if (xds110.is_cmapi_acquired) {
+ (void)cmapi_release();
+ xds110.is_cmapi_acquired = false;
+ }
+ if (xds110.is_cmapi_connected) {
+ (void)cmapi_disconnect();
+ xds110.is_cmapi_connected = false;
+ }
+ if (xds110.is_connected) {
+ if (xds110.is_swd_mode) {
+ /* Switch out of SWD mode */
+ (void)swd_disconnect();
+ } else {
+ /* Switch out of cJTAG mode */
+ (void)cjtag_disconnect();
+ }
+ /* Tell firmware we're disconnecting */
+ (void)xds_disconnect();
+ xds110.is_connected = false;
+ }
+ /* Close down the USB connection to the XDS110 debug probe */
+ usb_disconnect();
+
+ return ERROR_OK;
+}
+
+static int xds110_init(void)
+{
+ bool success;
+
+ /* Establish USB connection to the XDS110 debug probe */
+ success = usb_connect();
+
+ if (success) {
+ /* Send connect message to XDS110 firmware */
+ success = xds_connect();
+ if (success)
+ xds110.is_connected = true;
+ }
+
+ if (success) {
+ uint32_t firmware;
+ uint16_t hardware;
+
+ /* Retrieve version IDs from firmware */
+ /* Version numbers are stored in BCD format */
+ success = xds_version(&firmware, &hardware);
+ if (success) {
+ /* Save the firmware and hardware version */
+ xds110.firmware = firmware;
+ xds110.hardware = hardware;
+ }
+ }
+
+ if (success) {
+ success = xds_set_trst(0);
+ if (success)
+ success = xds_cycle_tck(50);
+ if (success)
+ success = xds_set_trst(1);
+ if (success)
+ success = xds_cycle_tck(50);
+ }
+
+ if (success) {
+ if (xds110.is_swd_mode) {
+ /* Switch to SWD if needed */
+ success = swd_connect();
+ } else {
+ success = cjtag_connect(MODE_JTAG);
+ }
+ }
+
+ if (success && xds110.is_swd_mode) {
+ uint32_t idcode;
+
+ /* Connect to CMAPI interface in XDS110 */
+ success = cmapi_connect(&idcode);
+
+ /* Acquire exclusive access to CMAPI interface */
+ if (success) {
+ xds110.is_cmapi_connected = true;
+ success = cmapi_acquire();
+ if (success)
+ xds110.is_cmapi_acquired = true;
+ }
+ }
+
+ if (!success)
+ xds110_quit();
+
+ if (success)
+ xds110_show_info();
+
+ return (success) ? ERROR_OK : ERROR_FAIL;
+}
+
+static void xds110_legacy_scan(uint32_t shift_state, uint32_t total_bits,
+ uint32_t end_state, uint8_t *data_out, uint8_t *data_in)
+{
+ (void)xds_jtag_scan(shift_state, total_bits, end_state, data_out, data_in);
+}
+
+static void xds110_legacy_runtest(uint32_t clocks, uint32_t end_state)
+{
+ xds_goto_state(XDS_JTAG_STATE_IDLE);
+ xds_cycle_tck(clocks);
+ xds_goto_state(end_state);
+}
+
+static void xds110_legacy_stableclocks(uint32_t clocks)
+{
+ xds_cycle_tck(clocks);
+}
+
+static void xds110_flush(void)
+{
+ uint8_t command;
+ uint32_t clocks;
+ uint32_t shift_state;
+ uint32_t end_state;
+ uint32_t bits;
+ uint32_t bytes;
+ uint32_t request;
+ uint32_t result;
+ uint8_t *data_out;
+ uint8_t data_in[MAX_DATA_BLOCK];
+ uint8_t *data_pntr;
+
+ if (0 == xds110.txn_request_size)
+ return;
+
+ /* Terminate request queue */
+ xds110.txn_requests[xds110.txn_request_size++] = 0;
+
+ if (xds110.firmware >= OCD_FIRMWARE_VERSION) {
+ /* Updated firmware has the API to directly handle the queue */
+ (void)ocd_scan_request(xds110.txn_requests, xds110.txn_request_size,
+ data_in, xds110.txn_result_size);
+ } else {
+ /* Legacy firmware needs to handle queue via discrete JTAG calls */
+ request = 0;
+ result = 0;
+ while (xds110.txn_requests[request] != 0) {
+ command = xds110.txn_requests[request++];
+ switch (command) {
+ case CMD_IR_SCAN:
+ case CMD_DR_SCAN:
+ if (command == CMD_IR_SCAN)
+ shift_state = XDS_JTAG_STATE_SHIFT_IR;
+ else
+ shift_state = XDS_JTAG_STATE_SHIFT_DR;
+ end_state = (uint32_t)(xds110.txn_requests[request++]);
+ bits = (uint32_t)(xds110.txn_requests[request++]) << 0;
+ bits |= (uint32_t)(xds110.txn_requests[request++]) << 8;
+ data_out = &xds110.txn_requests[request];
+ bytes = DIV_ROUND_UP(bits, 8);
+ xds110_legacy_scan(shift_state, bits, end_state, data_out,
+ &data_in[result]);
+ result += bytes;
+ request += bytes;
+ break;
+ case CMD_RUNTEST:
+ clocks = (uint32_t)(xds110.txn_requests[request++]) << 0;
+ clocks |= (uint32_t)(xds110.txn_requests[request++]) << 8;
+ clocks |= (uint32_t)(xds110.txn_requests[request++]) << 16;
+ clocks |= (uint32_t)(xds110.txn_requests[request++]) << 24;
+ end_state = (uint32_t)xds110.txn_requests[request++];
+ xds110_legacy_runtest(clocks, end_state);
+ break;
+ case CMD_STABLECLOCKS:
+ clocks = (uint32_t)(xds110.txn_requests[request++]) << 0;
+ clocks |= (uint32_t)(xds110.txn_requests[request++]) << 8;
+ clocks |= (uint32_t)(xds110.txn_requests[request++]) << 16;
+ clocks |= (uint32_t)(xds110.txn_requests[request++]) << 24;
+ xds110_legacy_stableclocks(clocks);
+ break;
+ default:
+ LOG_ERROR("BUG: unknown JTAG command type 0x%x encountered",
+ command);
+ exit(-1);
+ break;
+ }
+ }
+ }
+
+ /* Transfer results into caller's buffers from data_in buffer */
+ bits = 0; /* Bit offset into current scan result */
+ data_pntr = data_in;
+ for (result = 0; result < xds110.txn_result_count; result++) {
+ if (xds110.txn_scan_results[result].first) {
+ if (bits != 0) {
+ bytes = DIV_ROUND_UP(bits, 8);
+ data_pntr += bytes;
+ }
+ bits = 0;
+ }
+ if (xds110.txn_scan_results[result].buffer != 0)
+ bit_copy(xds110.txn_scan_results[result].buffer, 0, data_pntr,
+ bits, xds110.txn_scan_results[result].num_bits);
+ bits += xds110.txn_scan_results[result].num_bits;
+ }
+
+ xds110.txn_request_size = 0;
+ xds110.txn_result_size = 0;
+ xds110.txn_result_count = 0;
+}
+
+static void xds110_execute_reset(struct jtag_command *cmd)
+{
+ char trst;
+ char srst;
+
+ if (cmd->cmd.reset->trst != -1) {
+ if (cmd->cmd.reset->trst == 0) {
+ /* Deassert nTRST (active low) */
+ trst = 1;
+ } else {
+ /* Assert nTRST (active low) */
+ trst = 0;
+ }
+ (void)xds_set_trst(trst);
+ }
+
+ if (cmd->cmd.reset->srst != -1) {
+ if (cmd->cmd.reset->srst == 0) {
+ /* Deassert nSRST (active low) */
+ srst = 1;
+ } else {
+ /* Assert nSRST (active low) */
+ srst = 0;
+ }
+ (void)xds_set_srst(srst);
+ }
+}
+
+static void xds110_execute_sleep(struct jtag_command *cmd)
+{
+ jtag_sleep(cmd->cmd.sleep->us);
+ return;
+}
+
+static void xds110_execute_tlr_reset(struct jtag_command *cmd)
+{
+ (void)xds_goto_state(XDS_JTAG_STATE_RESET);
+
+ return;
+}
+
+static void xds110_execute_pathmove(struct jtag_command *cmd)
+{
+ uint32_t i;
+ uint32_t num_states;
+ uint8_t *path;
+
+ num_states = (uint32_t)cmd->cmd.pathmove->num_states;
+
+ if (num_states == 0)
+ return;
+
+ path = (uint8_t *)malloc(num_states * sizeof(uint8_t));
+ if (path == 0) {
+ LOG_ERROR("XDS110: unable to allocate memory");
+ return;
+ }
+
+ /* Convert requested path states into XDS API states */
+ for (i = 0; i < num_states; i++)
+ path[i] = (uint8_t)xds_jtag_state[cmd->cmd.pathmove->path[i]];
+
+ if (xds110.firmware >= OCD_FIRMWARE_VERSION) {
+ /* Updated firmware fully supports pathmove */
+ (void)ocd_pathmove(num_states, path);
+ } else {
+ /* Notify user that legacy firmware simply cannot handle pathmove */
+ LOG_ERROR("XDS110: the firmware does not support pathmove command");
+ LOG_ERROR(OCD_FIRMWARE_UPGRADE);
+ /* If pathmove is required, then debug is not possible */
+ exit(-1);
+ }
+
+ free((void *)path);
+
+ return;
+}
+
+static void xds110_queue_scan(struct jtag_command *cmd)
+{
+ int i;
+ uint32_t offset;
+ uint32_t total_fields;
+ uint32_t total_bits;
+ uint32_t total_bytes;
+ uint8_t end_state;
+ uint8_t *buffer;
+
+ /* Calculate the total number of bits to scan */
+ total_bits = 0;
+ total_fields = 0;
+ for (i = 0; i < cmd->cmd.scan->num_fields; i++) {
+ total_fields++;
+ total_bits += (uint32_t)cmd->cmd.scan->fields[i].num_bits;
+ }
+
+ if (total_bits == 0)
+ return;
+
+ total_bytes = DIV_ROUND_UP(total_bits, 8);
+
+ /* Check if new request would be too large to fit */
+ if (((xds110.txn_request_size + 1 + total_bytes + sizeof(end_state) + 1)
+ > MAX_DATA_BLOCK) || ((xds110.txn_result_count + total_fields) >
+ MAX_RESULT_QUEUE))
+ xds110_flush();
+
+ /* Check if this single request is too large to fit */
+ if ((1 + total_bytes + sizeof(end_state) + 1) > MAX_DATA_BLOCK) {
+ LOG_ERROR("BUG: JTAG scan request is too large to handle (%d bits)",
+ total_bits);
+ /* Failing to run this scan mucks up debug on this target */
+ exit(-1);
+ }
+
+ if (cmd->cmd.scan->ir_scan)
+ xds110.txn_requests[xds110.txn_request_size++] = CMD_IR_SCAN;
+ else
+ xds110.txn_requests[xds110.txn_request_size++] = CMD_DR_SCAN;
+
+ end_state = (uint8_t)xds_jtag_state[cmd->cmd.scan->end_state];
+ xds110.txn_requests[xds110.txn_request_size++] = end_state;
+
+ xds110.txn_requests[xds110.txn_request_size++] = (total_bits >> 0) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (total_bits >> 8) & 0xff;
+
+ /* Build request data by flattening fields into single buffer */
+ /* also populate the results array to return the results when run */
+ offset = 0;
+ buffer = &xds110.txn_requests[xds110.txn_request_size];
+ /* Clear data out buffer to default value of all zeros */
+ memset((void *)buffer, 0x00, total_bytes);
+ for (i = 0; i < cmd->cmd.scan->num_fields; i++) {
+ if (cmd->cmd.scan->fields[i].out_value != 0) {
+ /* Copy over data to scan out into request buffer */
+ bit_copy(buffer, offset, cmd->cmd.scan->fields[i].out_value, 0,
+ cmd->cmd.scan->fields[i].num_bits);
+ }
+ offset += cmd->cmd.scan->fields[i].num_bits;
+ xds110.txn_scan_results[xds110.txn_result_count].first = (i == 0);
+ xds110.txn_scan_results[xds110.txn_result_count].num_bits =
+ cmd->cmd.scan->fields[i].num_bits;
+ xds110.txn_scan_results[xds110.txn_result_count++].buffer =
+ cmd->cmd.scan->fields[i].in_value;
+ }
+ xds110.txn_request_size += total_bytes;
+ xds110.txn_result_size += total_bytes;
+
+ return;
+}
+
+static void xds110_queue_runtest(struct jtag_command *cmd)
+{
+ uint32_t clocks = (uint32_t)cmd->cmd.stableclocks->num_cycles;
+ uint8_t end_state = (uint8_t)xds_jtag_state[cmd->cmd.runtest->end_state];
+
+ /* Check if new request would be too large to fit */
+ if ((xds110.txn_request_size + 1 + sizeof(clocks) + sizeof(end_state) + 1)
+ > MAX_DATA_BLOCK)
+ xds110_flush();
+
+ /* Queue request and cycle count directly to queue buffer */
+ xds110.txn_requests[xds110.txn_request_size++] = CMD_RUNTEST;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 0) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 8) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 16) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 24) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = end_state;
+
+ return;
+}
+
+static void xds110_queue_stableclocks(struct jtag_command *cmd)
+{
+ uint32_t clocks = (uint32_t)cmd->cmd.stableclocks->num_cycles;
+
+ /* Check if new request would be too large to fit */
+ if ((xds110.txn_request_size + 1 + sizeof(clocks) + 1) > MAX_DATA_BLOCK)
+ xds110_flush();
+
+ /* Queue request and cycle count directly to queue buffer */
+ xds110.txn_requests[xds110.txn_request_size++] = CMD_STABLECLOCKS;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 0) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 8) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 16) & 0xff;
+ xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 24) & 0xff;
+
+ return;
+}
+
+static void xds110_execute_command(struct jtag_command *cmd)
+{
+ switch (cmd->type) {
+ case JTAG_RESET:
+ xds110_flush();
+ xds110_execute_reset(cmd);
+ break;
+ case JTAG_SLEEP:
+ xds110_flush();
+ xds110_execute_sleep(cmd);
+ break;
+ case JTAG_TLR_RESET:
+ xds110_flush();
+ xds110_execute_tlr_reset(cmd);
+ break;
+ case JTAG_PATHMOVE:
+ xds110_flush();
+ xds110_execute_pathmove(cmd);
+ break;
+ case JTAG_SCAN:
+ xds110_queue_scan(cmd);
+ break;
+ case JTAG_RUNTEST:
+ xds110_queue_runtest(cmd);
+ break;
+ case JTAG_STABLECLOCKS:
+ xds110_queue_stableclocks(cmd);
+ break;
+ case JTAG_TMS:
+ default:
+ LOG_ERROR("BUG: unknown JTAG command type 0x%x encountered",
+ cmd->type);
+ exit(-1);
+ }
+}
+
+static int xds110_execute_queue(void)
+{
+ struct jtag_command *cmd = jtag_command_queue;
+
+ while (cmd != NULL) {
+ xds110_execute_command(cmd);
+ cmd = cmd->next;
+ }
+
+ xds110_flush();
+
+ return ERROR_OK;
+}
+
+static int xds110_speed(int speed)
+{
+ bool success;
+
+ if (speed == 0) {
+ LOG_INFO("XDS110: RTCK not supported");
+ return ERROR_JTAG_NOT_IMPLEMENTED;
+ }
+
+ if (speed > XDS110_MAX_TCK_SPEED) {
+ LOG_INFO("XDS110: reduce speed request: %dkHz to %dkHz maximum",
+ speed, XDS110_MAX_TCK_SPEED);
+ speed = XDS110_MAX_TCK_SPEED;
+ }
+
+ if (speed < XDS110_MIN_TCK_SPEED) {
+ LOG_INFO("XDS110: increase speed request: %dkHz to %dkHz minimum",
+ speed, XDS110_MIN_TCK_SPEED);
+ speed = XDS110_MIN_TCK_SPEED;
+ }
+
+ /* The default is the maximum frequency the XDS110 can support */
+ uint32_t freq_to_use = XDS110_MAX_TCK_SPEED * 1000; /* Hz */
+ uint32_t delay_count = 0;
+
+ if (XDS110_MAX_TCK_SPEED != speed) {
+ freq_to_use = speed * 1000; /* Hz */
+
+ /* Calculate the delay count value */
+ double one_giga = 1000000000;
+ /* Get the pulse duration for the maximum frequency supported in ns */
+ double max_freq_pulse_duration = one_giga /
+ (XDS110_MAX_TCK_SPEED * 1000);
+
+ /* Convert frequency to pulse duration */
+ double freq_to_pulse_width_in_ns = one_giga / freq_to_use;
+
+ /*
+ * Start with the pulse duration for the maximum frequency. Keep
+ * decrementing the time added by each count value till the requested
+ * frequency pulse is less than the calculated value.
+ */
+ double current_value = max_freq_pulse_duration;
+
+ while (current_value < freq_to_pulse_width_in_ns) {
+ current_value += XDS110_TCK_PULSE_INCREMENT;
+ ++delay_count;
+ }
+
+ /*
+ * Determine which delay count yields the best match.
+ * The one obtained above or one less.
+ */
+ if (delay_count) {
+ double diff_freq_1 = freq_to_use -
+ (one_giga / (max_freq_pulse_duration +
+ (XDS110_TCK_PULSE_INCREMENT * delay_count)));
+ double diff_freq_2 = (one_giga / (max_freq_pulse_duration +
+ (XDS110_TCK_PULSE_INCREMENT * (delay_count - 1)))) -
+ freq_to_use;
+
+ /* One less count value yields a better match */
+ if (diff_freq_1 > diff_freq_2)
+ --delay_count;
+ }
+ }
+
+ /* Send the delay count to the XDS110 firmware */
+ success = xds_set_tck_delay(delay_count);
+
+ if (success) {
+ xds110.delay_count = delay_count;
+ xds110.speed = speed;
+ }
+
+ return (success) ? ERROR_OK : ERROR_FAIL;
+}
+
+static int xds110_speed_div(int speed, int *khz)
+{
+ *khz = speed;
+ return ERROR_OK;
+}
+
+static int xds110_khz(int khz, int *jtag_speed)
+{
+ *jtag_speed = khz;
+ return ERROR_OK;
+}
+
+static int_least32_t xds110_swd_frequency(int_least32_t hz)
+{
+ if (hz > 0)
+ xds110_speed(hz / 1000);
+ return hz;
+}
+
+COMMAND_HANDLER(xds110_handle_info_command)
+{
+ xds110_show_info();
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xds110_handle_serial_command)
+{
+ wchar_t serial[XDS110_SERIAL_LEN + 1];
+
+ xds110.serial[0] = 0;
+
+ if (CMD_ARGC == 1) {
+ size_t len = mbstowcs(0, CMD_ARGV[0], 0);
+ if (len > XDS110_SERIAL_LEN) {
+ LOG_ERROR("XDS110: serial number is limited to %d characters",
+ XDS110_SERIAL_LEN);
+ return ERROR_FAIL;
+ }
+ if ((size_t)-1 == mbstowcs(serial, CMD_ARGV[0], len + 1)) {
+ LOG_ERROR("XDS110: unable to convert serial number");
+ return ERROR_FAIL;
+ }
+
+ for (uint32_t i = 0; i < len; i++)
+ xds110.serial[i] = (char)serial[i];
+
+ xds110.serial[len] = 0;
+ } else {
+ LOG_ERROR("XDS110: expected exactly one argument to xds110_serial "
+ "<serial-number>");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static const struct command_registration xds110_subcommand_handlers[] = {
+ {
+ .name = "info",
+ .handler = &xds110_handle_info_command,
+ .mode = COMMAND_EXEC,
+ .usage = "",
+ .help = "show XDS110 info",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration xds110_command_handlers[] = {
+ {
+ .name = "xds110",
+ .mode = COMMAND_ANY,
+ .help = "perform XDS110 management",
+ .usage = "<cmd>",
+ .chain = xds110_subcommand_handlers,
+ },
+ {
+ .name = "xds110_serial",
+ .handler = &xds110_handle_serial_command,
+ .mode = COMMAND_CONFIG,
+ .help = "set the XDS110 probe serial number",
+ .usage = "serial_string",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct swd_driver xds110_swd_driver = {
+ .init = xds110_swd_init,
+ .frequency = xds110_swd_frequency,
+ .switch_seq = xds110_swd_switch_seq,
+ .read_reg = xds110_swd_read_reg,
+ .write_reg = xds110_swd_write_reg,
+ .run = xds110_swd_run_queue,
+};
+
+static const char * const xds110_transport[] = { "swd", "jtag", NULL };
+
+struct jtag_interface xds110_interface = {
+ .name = "xds110",
+ .commands = xds110_command_handlers,
+ .swd = &xds110_swd_driver,
+ .transports = xds110_transport,
+
+ .execute_queue = xds110_execute_queue,
+ .speed = xds110_speed,
+ .speed_div = xds110_speed_div,
+ .khz = xds110_khz,
+ .init = xds110_init,
+ .quit = xds110_quit,
+};
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index ddeadb4..286a73a 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -132,6 +132,9 @@ extern struct jtag_interface kitprog_interface;
#if BUILD_IMX_GPIO == 1
extern struct jtag_interface imx_gpio_interface;
#endif
+#if BUILD_XDS110 == 1
+extern struct jtag_interface xds110_interface;
+#endif
#endif /* standard drivers */
/**
@@ -234,6 +237,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_IMX_GPIO == 1
&imx_gpio_interface,
#endif
+#if BUILD_XDS110 == 1
+ &xds110_interface,
+#endif
#endif /* standard drivers */
NULL,
};
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 81dc32d..8e2d2ed 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -791,64 +791,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio
bool program_exited = false;
if (strcmp(target->fileio_info->identifier, "open") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2,
target->fileio_info->param_3,
target->fileio_info->param_4);
else if (strcmp(target->fileio_info->identifier, "close") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1);
else if (strcmp(target->fileio_info->identifier, "read") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2,
target->fileio_info->param_3);
else if (strcmp(target->fileio_info->identifier, "write") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2,
target->fileio_info->param_3);
else if (strcmp(target->fileio_info->identifier, "lseek") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2,
target->fileio_info->param_3);
else if (strcmp(target->fileio_info->identifier, "rename") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2,
target->fileio_info->param_3,
target->fileio_info->param_4);
else if (strcmp(target->fileio_info->identifier, "unlink") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2);
else if (strcmp(target->fileio_info->identifier, "stat") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2,
target->fileio_info->param_3);
else if (strcmp(target->fileio_info->identifier, "fstat") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2);
else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2);
else if (strcmp(target->fileio_info->identifier, "isatty") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1);
else if (strcmp(target->fileio_info->identifier, "system") == 0)
- sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
+ sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
target->fileio_info->param_1,
target->fileio_info->param_2);
else if (strcmp(target->fileio_info->identifier, "exit") == 0) {
/* If target hits exit syscall, report to GDB the program is terminated.
* In addition, let target run its own exit syscall handler. */
program_exited = true;
- sprintf(fileio_command, "W%02" PRIx32, target->fileio_info->param_1);
+ sprintf(fileio_command, "W%02" PRIx64, target->fileio_info->param_1);
} else {
LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
@@ -2704,6 +2704,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
/* simple case, a continue packet */
if (parse[0] == 'c') {
+ gdb_running_type = 'c';
LOG_DEBUG("target %s continue", target_name(target));
log_add_callback(gdb_log_callback, connection);
retval = target_resume(target, 1, 0, 0, 0);
@@ -2730,6 +2731,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
/* single-step or step-over-breakpoint */
if (parse[0] == 's') {
+ gdb_running_type = 's';
bool fake_step = false;
if (strncmp(parse, "s:", 2) == 0) {
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 030015a..a37b717 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -40,6 +40,7 @@ TARGET_CORE_SRC = \
%D%/target.c \
%D%/target_request.c \
%D%/testee.c \
+ %D%/semihosting_common.c \
%D%/smp.c
ARMV4_5_SRC = \
@@ -218,6 +219,7 @@ RISCV_SRC = \
%D%/nds32_v3.h \
%D%/nds32_v3m.h \
%D%/nds32_aice.h \
+ %D%/semihosting_common.h \
%D%/stm8.h \
%D%/lakemont.h \
%D%/x86_32_common.h \
diff --git a/src/target/aarch64.c b/src/target/aarch64.c
index cd83502..df1e49c 100644
--- a/src/target/aarch64.c
+++ b/src/target/aarch64.c
@@ -2590,6 +2590,143 @@ COMMAND_HANDLER(aarch64_mask_interrupts_command)
return ERROR_OK;
}
+static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ struct command_context *context;
+ struct target *target;
+ struct arm *arm;
+ int retval;
+ bool is_mcr = false;
+ int arg_cnt = 0;
+
+ if (Jim_CompareStringImmediate(interp, argv[0], "mcr")) {
+ is_mcr = true;
+ arg_cnt = 7;
+ } else {
+ arg_cnt = 6;
+ }
+
+ context = current_command_context(interp);
+ assert(context != NULL);
+
+ target = get_current_target(context);
+ if (target == NULL) {
+ LOG_ERROR("%s: no current target", __func__);
+ return JIM_ERR;
+ }
+ if (!target_was_examined(target)) {
+ LOG_ERROR("%s: not yet examined", target_name(target));
+ return JIM_ERR;
+ }
+
+ arm = target_to_arm(target);
+ if (!is_arm(arm)) {
+ LOG_ERROR("%s: not an ARM", target_name(target));
+ return JIM_ERR;
+ }
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ if (arm->core_state == ARM_STATE_AARCH64) {
+ LOG_ERROR("%s: not 32-bit arm target", target_name(target));
+ return JIM_ERR;
+ }
+
+ if (argc != arg_cnt) {
+ LOG_ERROR("%s: wrong number of arguments", __func__);
+ return JIM_ERR;
+ }
+
+ int cpnum;
+ uint32_t op1;
+ uint32_t op2;
+ uint32_t CRn;
+ uint32_t CRm;
+ uint32_t value;
+ long l;
+
+ /* NOTE: parameter sequence matches ARM instruction set usage:
+ * MCR pNUM, op1, rX, CRn, CRm, op2 ; write CP from rX
+ * MRC pNUM, op1, rX, CRn, CRm, op2 ; read CP into rX
+ * The "rX" is necessarily omitted; it uses Tcl mechanisms.
+ */
+ retval = Jim_GetLong(interp, argv[1], &l);
+ if (retval != JIM_OK)
+ return retval;
+ if (l & ~0xf) {
+ LOG_ERROR("%s: %s %d out of range", __func__,
+ "coprocessor", (int) l);
+ return JIM_ERR;
+ }
+ cpnum = l;
+
+ retval = Jim_GetLong(interp, argv[2], &l);
+ if (retval != JIM_OK)
+ return retval;
+ if (l & ~0x7) {
+ LOG_ERROR("%s: %s %d out of range", __func__,
+ "op1", (int) l);
+ return JIM_ERR;
+ }
+ op1 = l;
+
+ retval = Jim_GetLong(interp, argv[3], &l);
+ if (retval != JIM_OK)
+ return retval;
+ if (l & ~0xf) {
+ LOG_ERROR("%s: %s %d out of range", __func__,
+ "CRn", (int) l);
+ return JIM_ERR;
+ }
+ CRn = l;
+
+ retval = Jim_GetLong(interp, argv[4], &l);
+ if (retval != JIM_OK)
+ return retval;
+ if (l & ~0xf) {
+ LOG_ERROR("%s: %s %d out of range", __func__,
+ "CRm", (int) l);
+ return JIM_ERR;
+ }
+ CRm = l;
+
+ retval = Jim_GetLong(interp, argv[5], &l);
+ if (retval != JIM_OK)
+ return retval;
+ if (l & ~0x7) {
+ LOG_ERROR("%s: %s %d out of range", __func__,
+ "op2", (int) l);
+ return JIM_ERR;
+ }
+ op2 = l;
+
+ value = 0;
+
+ if (is_mcr == true) {
+ retval = Jim_GetLong(interp, argv[6], &l);
+ if (retval != JIM_OK)
+ return retval;
+ value = l;
+
+ /* NOTE: parameters reordered! */
+ /* ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2) */
+ retval = arm->mcr(target, cpnum, op1, op2, CRn, CRm, value);
+ if (retval != ERROR_OK)
+ return JIM_ERR;
+ } else {
+ /* NOTE: parameters reordered! */
+ /* ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2) */
+ retval = arm->mrc(target, cpnum, op1, op2, CRn, CRm, &value);
+ if (retval != ERROR_OK)
+ return JIM_ERR;
+
+ Jim_SetResult(interp, Jim_NewIntObj(interp, value));
+ }
+
+ return JIM_OK;
+}
+
static const struct command_registration aarch64_exec_command_handlers[] = {
{
.name = "cache_info",
@@ -2625,9 +2762,25 @@ static const struct command_registration aarch64_exec_command_handlers[] = {
.help = "mask aarch64 interrupts during single-step",
.usage = "['on'|'off']",
},
+ {
+ .name = "mcr",
+ .mode = COMMAND_EXEC,
+ .jim_handler = jim_mcrmrc,
+ .help = "write coprocessor register",
+ .usage = "cpnum op1 CRn CRm op2 value",
+ },
+ {
+ .name = "mrc",
+ .mode = COMMAND_EXEC,
+ .jim_handler = jim_mcrmrc,
+ .help = "read coprocessor register",
+ .usage = "cpnum op1 CRn CRm op2",
+ },
+
COMMAND_REGISTRATION_DONE
};
+
static const struct command_registration aarch64_command_handlers[] = {
{
.chain = armv8_command_handlers,
diff --git a/src/target/arm.h b/src/target/arm.h
index dd25d53..316ff9a 100644
--- a/src/target/arm.h
+++ b/src/target/arm.h
@@ -8,6 +8,9 @@
* Copyright (C) 2009 by Øyvind Harboe
* oyvind.harboe@zylin.com
*
+ * Copyright (C) 2018 by Liviu Ionescu
+ * <ilg@livius.net>
+ *
* 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
@@ -28,7 +31,6 @@
#include <helper/command.h>
#include "target.h"
-
/**
* @file
* Holds the interface to ARM cores.
@@ -181,32 +183,11 @@ struct arm {
/** Flag reporting armv6m based core. */
bool is_armv6m;
- /** Flag reporting whether semihosting is active. */
- bool is_semihosting;
-
- /** Flag reporting whether semihosting fileio is active. */
- bool is_semihosting_fileio;
-
- /** Flag reporting whether semihosting fileio operation is active. */
- bool semihosting_hit_fileio;
-
/** Floating point or VFP version, 0 if disabled. */
int arm_vfp_version;
- /** Current semihosting operation. */
- int semihosting_op;
-
- /** Current semihosting result. */
- int semihosting_result;
-
- /** Value to be returned by semihosting SYS_ERRNO request. */
- int semihosting_errno;
-
int (*setup_semihosting)(struct target *target, int enable);
- /** Semihosting command line. */
- char *semihosting_cmdline;
-
/** Backpointer to the target. */
struct target *target;
diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c
index f31f901..57f3139 100644
--- a/src/target/arm_semihosting.c
+++ b/src/target/arm_semihosting.c
@@ -8,6 +8,9 @@
* Copyright (C) 2016 by Square, Inc. *
* Steven Stallion <stallion@squareup.com> *
* *
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
* 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 *
@@ -52,21 +55,6 @@
#include <helper/log.h>
#include <sys/stat.h>
-static const int open_modeflags[12] = {
- O_RDONLY,
- O_RDONLY | O_BINARY,
- O_RDWR,
- O_RDWR | O_BINARY,
- O_WRONLY | O_CREAT | O_TRUNC,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
- O_RDWR | O_CREAT | O_TRUNC,
- O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
- O_WRONLY | O_CREAT | O_APPEND,
- O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
- O_RDWR | O_CREAT | O_APPEND,
- O_RDWR | O_CREAT | O_APPEND | O_BINARY
-};
-
static int post_result(struct target *target)
{
struct arm *arm = target_to_arm(target);
@@ -79,7 +67,7 @@ static int post_result(struct target *target)
uint32_t spsr;
/* return value in R0 */
- buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
+ buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
arm->core_cache->reg_list[0].dirty = 1;
/* LR --> PC */
@@ -105,523 +93,13 @@ static int post_result(struct target *target)
* bkpt instruction */
/* return result in R0 */
- buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
+ buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
arm->core_cache->reg_list[0].dirty = 1;
}
return ERROR_OK;
}
-static int do_semihosting(struct target *target)
-{
- struct arm *arm = target_to_arm(target);
- struct gdb_fileio_info *fileio_info = target->fileio_info;
- uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
- uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
- uint8_t params[16];
- int retval;
-
- /*
- * TODO: lots of security issues are not considered yet, such as:
- * - no validation on target provided file descriptors
- * - no safety checks on opened/deleted/renamed file paths
- * Beware the target app you use this support with.
- *
- * TODO: unsupported semihosting fileio operations could be
- * implemented if we had a small working area at our disposal.
- */
- switch ((arm->semihosting_op = r0)) {
- case 0x01: /* SYS_OPEN */
- retval = target_read_memory(target, r1, 4, 3, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- uint32_t a = target_buffer_get_u32(target, params+0);
- uint32_t m = target_buffer_get_u32(target, params+4);
- uint32_t l = target_buffer_get_u32(target, params+8);
- uint8_t fn[256];
- retval = target_read_memory(target, a, 1, l, fn);
- if (retval != ERROR_OK)
- return retval;
- fn[l] = 0;
- if (arm->is_semihosting_fileio) {
- if (strcmp((char *)fn, ":tt") == 0)
- arm->semihosting_result = 0;
- else {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "open";
- fileio_info->param_1 = a;
- fileio_info->param_2 = l;
- fileio_info->param_3 = open_modeflags[m];
- fileio_info->param_4 = 0644;
- }
- } else {
- if (l <= 255 && m <= 11) {
- if (strcmp((char *)fn, ":tt") == 0) {
- if (m < 4)
- arm->semihosting_result = dup(STDIN_FILENO);
- else
- arm->semihosting_result = dup(STDOUT_FILENO);
- } else {
- /* cygwin requires the permission setting
- * otherwise it will fail to reopen a previously
- * written file */
- arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644);
- }
- arm->semihosting_errno = errno;
- } else {
- arm->semihosting_result = -1;
- arm->semihosting_errno = EINVAL;
- }
- }
- }
- break;
-
- case 0x02: /* SYS_CLOSE */
- retval = target_read_memory(target, r1, 4, 1, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- int fd = target_buffer_get_u32(target, params+0);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "close";
- fileio_info->param_1 = fd;
- } else {
- arm->semihosting_result = close(fd);
- arm->semihosting_errno = errno;
- }
- }
- break;
-
- case 0x03: /* SYS_WRITEC */
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "write";
- fileio_info->param_1 = 1;
- fileio_info->param_2 = r1;
- fileio_info->param_3 = 1;
- } else {
- unsigned char c;
- retval = target_read_memory(target, r1, 1, 1, &c);
- if (retval != ERROR_OK)
- return retval;
- putchar(c);
- arm->semihosting_result = 0;
- }
- break;
-
- case 0x04: /* SYS_WRITE0 */
- if (arm->is_semihosting_fileio) {
- size_t count = 0;
- for (uint32_t a = r1;; a++) {
- unsigned char c;
- retval = target_read_memory(target, a, 1, 1, &c);
- if (retval != ERROR_OK)
- return retval;
- if (c == '\0')
- break;
- count++;
- }
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "write";
- fileio_info->param_1 = 1;
- fileio_info->param_2 = r1;
- fileio_info->param_3 = count;
- } else {
- do {
- unsigned char c;
- retval = target_read_memory(target, r1++, 1, 1, &c);
- if (retval != ERROR_OK)
- return retval;
- if (!c)
- break;
- putchar(c);
- } while (1);
- arm->semihosting_result = 0;
- }
- break;
-
- case 0x05: /* SYS_WRITE */
- retval = target_read_memory(target, r1, 4, 3, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- int fd = target_buffer_get_u32(target, params+0);
- uint32_t a = target_buffer_get_u32(target, params+4);
- size_t l = target_buffer_get_u32(target, params+8);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "write";
- fileio_info->param_1 = fd;
- fileio_info->param_2 = a;
- fileio_info->param_3 = l;
- } else {
- uint8_t *buf = malloc(l);
- if (!buf) {
- arm->semihosting_result = -1;
- arm->semihosting_errno = ENOMEM;
- } else {
- retval = target_read_buffer(target, a, l, buf);
- if (retval != ERROR_OK) {
- free(buf);
- return retval;
- }
- arm->semihosting_result = write(fd, buf, l);
- arm->semihosting_errno = errno;
- if (arm->semihosting_result >= 0)
- arm->semihosting_result = l - arm->semihosting_result;
- free(buf);
- }
- }
- }
- break;
-
- case 0x06: /* SYS_READ */
- retval = target_read_memory(target, r1, 4, 3, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- int fd = target_buffer_get_u32(target, params+0);
- uint32_t a = target_buffer_get_u32(target, params+4);
- ssize_t l = target_buffer_get_u32(target, params+8);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "read";
- fileio_info->param_1 = fd;
- fileio_info->param_2 = a;
- fileio_info->param_3 = l;
- } else {
- uint8_t *buf = malloc(l);
- if (!buf) {
- arm->semihosting_result = -1;
- arm->semihosting_errno = ENOMEM;
- } else {
- arm->semihosting_result = read(fd, buf, l);
- arm->semihosting_errno = errno;
- if (arm->semihosting_result >= 0) {
- retval = target_write_buffer(target, a, arm->semihosting_result, buf);
- if (retval != ERROR_OK) {
- free(buf);
- return retval;
- }
- arm->semihosting_result = l - arm->semihosting_result;
- }
- free(buf);
- }
- }
- }
- break;
-
- case 0x07: /* SYS_READC */
- if (arm->is_semihosting_fileio) {
- LOG_ERROR("SYS_READC not supported by semihosting fileio");
- return ERROR_FAIL;
- }
- arm->semihosting_result = getchar();
- break;
-
- case 0x08: /* SYS_ISERROR */
- retval = target_read_memory(target, r1, 4, 1, params);
- if (retval != ERROR_OK)
- return retval;
- arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0);
- break;
-
- case 0x09: /* SYS_ISTTY */
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "isatty";
- fileio_info->param_1 = r1;
- } else {
- retval = target_read_memory(target, r1, 4, 1, params);
- if (retval != ERROR_OK)
- return retval;
- arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0));
- }
- break;
-
- case 0x0a: /* SYS_SEEK */
- retval = target_read_memory(target, r1, 4, 2, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- int fd = target_buffer_get_u32(target, params+0);
- off_t pos = target_buffer_get_u32(target, params+4);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "lseek";
- fileio_info->param_1 = fd;
- fileio_info->param_2 = pos;
- fileio_info->param_3 = SEEK_SET;
- } else {
- arm->semihosting_result = lseek(fd, pos, SEEK_SET);
- arm->semihosting_errno = errno;
- if (arm->semihosting_result == pos)
- arm->semihosting_result = 0;
- }
- }
- break;
-
- case 0x0c: /* SYS_FLEN */
- if (arm->is_semihosting_fileio) {
- LOG_ERROR("SYS_FLEN not supported by semihosting fileio");
- return ERROR_FAIL;
- }
- retval = target_read_memory(target, r1, 4, 1, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- int fd = target_buffer_get_u32(target, params+0);
- struct stat buf;
- arm->semihosting_result = fstat(fd, &buf);
- if (arm->semihosting_result == -1) {
- arm->semihosting_errno = errno;
- arm->semihosting_result = -1;
- break;
- }
- arm->semihosting_result = buf.st_size;
- }
- break;
-
- case 0x0e: /* SYS_REMOVE */
- retval = target_read_memory(target, r1, 4, 2, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- uint32_t a = target_buffer_get_u32(target, params+0);
- uint32_t l = target_buffer_get_u32(target, params+4);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "unlink";
- fileio_info->param_1 = a;
- fileio_info->param_2 = l;
- } else {
- if (l <= 255) {
- uint8_t fn[256];
- retval = target_read_memory(target, a, 1, l, fn);
- if (retval != ERROR_OK)
- return retval;
- fn[l] = 0;
- arm->semihosting_result = remove((char *)fn);
- arm->semihosting_errno = errno;
- } else {
- arm->semihosting_result = -1;
- arm->semihosting_errno = EINVAL;
- }
- }
- }
- break;
-
- case 0x0f: /* SYS_RENAME */
- retval = target_read_memory(target, r1, 4, 4, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- uint32_t a1 = target_buffer_get_u32(target, params+0);
- uint32_t l1 = target_buffer_get_u32(target, params+4);
- uint32_t a2 = target_buffer_get_u32(target, params+8);
- uint32_t l2 = target_buffer_get_u32(target, params+12);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "rename";
- fileio_info->param_1 = a1;
- fileio_info->param_2 = l1;
- fileio_info->param_3 = a2;
- fileio_info->param_4 = l2;
- } else {
- if (l1 <= 255 && l2 <= 255) {
- uint8_t fn1[256], fn2[256];
- retval = target_read_memory(target, a1, 1, l1, fn1);
- if (retval != ERROR_OK)
- return retval;
- retval = target_read_memory(target, a2, 1, l2, fn2);
- if (retval != ERROR_OK)
- return retval;
- fn1[l1] = 0;
- fn2[l2] = 0;
- arm->semihosting_result = rename((char *)fn1, (char *)fn2);
- arm->semihosting_errno = errno;
- } else {
- arm->semihosting_result = -1;
- arm->semihosting_errno = EINVAL;
- }
- }
- }
- break;
-
- case 0x11: /* SYS_TIME */
- arm->semihosting_result = time(NULL);
- break;
-
- case 0x13: /* SYS_ERRNO */
- arm->semihosting_result = arm->semihosting_errno;
- break;
-
- case 0x15: /* SYS_GET_CMDLINE */
- retval = target_read_memory(target, r1, 4, 2, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- uint32_t a = target_buffer_get_u32(target, params+0);
- uint32_t l = target_buffer_get_u32(target, params+4);
- char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : "";
- uint32_t s = strlen(arg) + 1;
- if (l < s)
- arm->semihosting_result = -1;
- else {
- retval = target_write_buffer(target, a, s, (uint8_t *)arg);
- if (retval != ERROR_OK)
- return retval;
- arm->semihosting_result = 0;
- }
- }
- break;
-
- case 0x16: /* SYS_HEAPINFO */
- retval = target_read_memory(target, r1, 4, 1, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- uint32_t a = target_buffer_get_u32(target, params+0);
- /* tell the remote we have no idea */
- memset(params, 0, 4*4);
- retval = target_write_memory(target, a, 4, 4, params);
- if (retval != ERROR_OK)
- return retval;
- arm->semihosting_result = 0;
- }
- break;
-
- case 0x18: /* angel_SWIreason_ReportException */
- switch (r1) {
- case 0x20026: /* ADP_Stopped_ApplicationExit */
- fprintf(stderr, "semihosting: *** application exited ***\n");
- break;
- case 0x20000: /* ADP_Stopped_BranchThroughZero */
- case 0x20001: /* ADP_Stopped_UndefinedInstr */
- case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
- case 0x20003: /* ADP_Stopped_PrefetchAbort */
- case 0x20004: /* ADP_Stopped_DataAbort */
- case 0x20005: /* ADP_Stopped_AddressException */
- case 0x20006: /* ADP_Stopped_IRQ */
- case 0x20007: /* ADP_Stopped_FIQ */
- case 0x20020: /* ADP_Stopped_BreakPoint */
- case 0x20021: /* ADP_Stopped_WatchPoint */
- case 0x20022: /* ADP_Stopped_StepComplete */
- case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
- case 0x20024: /* ADP_Stopped_InternalError */
- case 0x20025: /* ADP_Stopped_UserInterruption */
- case 0x20027: /* ADP_Stopped_StackOverflow */
- case 0x20028: /* ADP_Stopped_DivisionByZero */
- case 0x20029: /* ADP_Stopped_OSSpecific */
- default:
- fprintf(stderr, "semihosting: exception %#x\n",
- (unsigned) r1);
- }
- return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
-
- case 0x12: /* SYS_SYSTEM */
- /* Provide SYS_SYSTEM functionality. Uses the
- * libc system command, there may be a reason *NOT*
- * to use this, but as I can't think of one, I
- * implemented it this way.
- */
- retval = target_read_memory(target, r1, 4, 2, params);
- if (retval != ERROR_OK)
- return retval;
- else {
- uint32_t len = target_buffer_get_u32(target, params+4);
- uint32_t c_ptr = target_buffer_get_u32(target, params);
- if (arm->is_semihosting_fileio) {
- arm->semihosting_hit_fileio = true;
- fileio_info->identifier = "system";
- fileio_info->param_1 = c_ptr;
- fileio_info->param_2 = len;
- } else {
- uint8_t cmd[256];
- if (len > 255) {
- arm->semihosting_result = -1;
- arm->semihosting_errno = EINVAL;
- } else {
- memset(cmd, 0x0, 256);
- retval = target_read_memory(target, c_ptr, 1, len, cmd);
- if (retval != ERROR_OK)
- return retval;
- else
- arm->semihosting_result = system((const char *)cmd);
- }
- }
- }
- break;
- case 0x0d: /* SYS_TMPNAM */
- case 0x10: /* SYS_CLOCK */
- case 0x17: /* angel_SWIreason_EnterSVC */
- case 0x30: /* SYS_ELAPSED */
- case 0x31: /* SYS_TICKFREQ */
- default:
- fprintf(stderr, "semihosting: unsupported call %#x\n",
- (unsigned) r0);
- arm->semihosting_result = -1;
- arm->semihosting_errno = ENOTSUP;
- }
-
- return ERROR_OK;
-}
-
-static int get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
-{
- struct arm *arm = target_to_arm(target);
-
- /* To avoid uneccessary duplication, semihosting prepares the
- * fileio_info structure out-of-band when the target halts. See
- * do_semihosting for more detail.
- */
- if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio)
- return ERROR_FAIL;
-
- return ERROR_OK;
-}
-
-static int gdb_fileio_end(struct target *target, int result, int fileio_errno, bool ctrl_c)
-{
- struct arm *arm = target_to_arm(target);
- struct gdb_fileio_info *fileio_info = target->fileio_info;
-
- /* clear pending status */
- arm->semihosting_hit_fileio = false;
-
- arm->semihosting_result = result;
- arm->semihosting_errno = fileio_errno;
-
- /* Some fileio results do not match up with what the semihosting
- * operation expects; for these operations, we munge the results
- * below:
- */
- switch (arm->semihosting_op) {
- case 0x05: /* SYS_WRITE */
- if (result < 0)
- arm->semihosting_result = fileio_info->param_3;
- else
- arm->semihosting_result = 0;
- break;
-
- case 0x06: /* SYS_READ */
- if (result == (int)fileio_info->param_3)
- arm->semihosting_result = 0;
- if (result <= 0)
- arm->semihosting_result = fileio_info->param_3;
- break;
-
- case 0x0a: /* SYS_SEEK */
- if (result > 0)
- arm->semihosting_result = 0;
- break;
- }
-
- return post_result(target);
-}
-
/**
* Initialize ARM semihosting support.
*
@@ -630,14 +108,9 @@ static int gdb_fileio_end(struct target *target, int result, int fileio_errno, b
*/
int arm_semihosting_init(struct target *target)
{
- target->fileio_info = malloc(sizeof(*target->fileio_info));
- if (target->fileio_info == NULL) {
- LOG_ERROR("out of memory");
- return ERROR_FAIL;
- }
-
- target->type->get_gdb_fileio_info = get_gdb_fileio_info;
- target->type->gdb_fileio_end = gdb_fileio_end;
+ struct arm *arm = target_to_arm(target);
+ assert(arm->setup_semihosting);
+ semihosting_common_init(target, arm->setup_semihosting, post_result);
return ERROR_OK;
}
@@ -662,7 +135,11 @@ int arm_semihosting(struct target *target, int *retval)
uint32_t pc, lr, spsr;
struct reg *r;
- if (!arm->is_semihosting)
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting)
+ return 0;
+
+ if (!semihosting->is_active)
return 0;
if (is_arm7_9(target_to_arm7_9(target)) ||
@@ -766,10 +243,24 @@ int arm_semihosting(struct target *target, int *retval)
/* Perform semihosting if we are not waiting on a fileio
* operation to complete.
*/
- if (!arm->semihosting_hit_fileio) {
- *retval = do_semihosting(target);
- if (*retval != ERROR_OK) {
- LOG_ERROR("Failed semihosting operation");
+ if (!semihosting->hit_fileio) {
+ /* TODO: update for 64-bits */
+ uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
+ uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
+
+ semihosting->op = r0;
+ semihosting->param = r1;
+ semihosting->word_size_bytes = 4;
+
+ /* Check for ARM operation numbers. */
+ if (0 <= semihosting->op && semihosting->op <= 0x31) {
+ *retval = semihosting_common(target);
+ if (*retval != ERROR_OK) {
+ LOG_ERROR("Failed semihosting operation");
+ return 0;
+ }
+ } else {
+ /* Unknown operation number, not a semihosting call. */
return 0;
}
}
@@ -777,13 +268,8 @@ int arm_semihosting(struct target *target, int *retval)
/* Post result to target if we are not waiting on a fileio
* operation to complete:
*/
- if (!arm->semihosting_hit_fileio) {
- *retval = post_result(target);
- if (*retval != ERROR_OK) {
- LOG_ERROR("Failed to post semihosting result");
- return 0;
- }
-
+ if (semihosting->is_resumable && !semihosting->hit_fileio) {
+ /* Resume right after the BRK instruction. */
*retval = target_resume(target, 1, 0, 0, 0);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to resume target");
diff --git a/src/target/arm_semihosting.h b/src/target/arm_semihosting.h
index 011f19f..cf1f8de 100644
--- a/src/target/arm_semihosting.h
+++ b/src/target/arm_semihosting.h
@@ -19,6 +19,8 @@
#ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H
#define OPENOCD_TARGET_ARM_SEMIHOSTING_H
+#include "semihosting_common.h"
+
int arm_semihosting_init(struct target *target);
int arm_semihosting(struct target *target, int *retval);
diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c
index 06994ca..5ee8ead 100644
--- a/src/target/armv4_5.c
+++ b/src/target/armv4_5.c
@@ -8,6 +8,9 @@
* Copyright (C) 2008 by Oyvind Harboe *
* oyvind.harboe@zylin.com *
* *
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
* 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 *
@@ -34,6 +37,7 @@
#include <helper/binarybuffer.h>
#include "algorithm.h"
#include "register.h"
+#include "semihosting_common.h"
/* offsets into armv4_5 core register cache */
enum {
@@ -748,7 +752,7 @@ int arm_arch_state(struct target *target)
}
/* avoid filling log waiting for fileio reply */
- if (arm->semihosting_hit_fileio)
+ if (target->semihosting->hit_fileio)
return ERROR_OK;
LOG_USER("target halted in %s state due to %s, current mode: %s\n"
@@ -758,8 +762,8 @@ int arm_arch_state(struct target *target)
arm_mode_name(arm->core_mode),
buf_get_u32(arm->cpsr->value, 0, 32),
buf_get_u32(arm->pc->value, 0, 32),
- arm->is_semihosting ? ", semihosting" : "",
- arm->is_semihosting_fileio ? " fileio" : "");
+ target->semihosting->is_active ? ", semihosting" : "",
+ target->semihosting->is_fileio ? " fileio" : "");
return ERROR_OK;
}
@@ -1094,119 +1098,10 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
return JIM_OK;
}
-COMMAND_HANDLER(handle_arm_semihosting_command)
-{
- struct target *target = get_current_target(CMD_CTX);
-
- if (target == NULL) {
- LOG_ERROR("No target selected");
- return ERROR_FAIL;
- }
-
- struct arm *arm = target_to_arm(target);
-
- if (!is_arm(arm)) {
- command_print(CMD_CTX, "current target isn't an ARM");
- return ERROR_FAIL;
- }
-
- if (!arm->setup_semihosting) {
- command_print(CMD_CTX, "semihosting not supported for current target");
- return ERROR_FAIL;
- }
-
- if (CMD_ARGC > 0) {
- int semihosting;
-
- COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting);
-
- if (!target_was_examined(target)) {
- LOG_ERROR("Target not examined yet");
- return ERROR_FAIL;
- }
-
- if (arm->setup_semihosting(target, semihosting) != ERROR_OK) {
- LOG_ERROR("Failed to Configure semihosting");
- return ERROR_FAIL;
- }
-
- /* FIXME never let that "catch" be dropped! */
- arm->is_semihosting = semihosting;
- }
-
- command_print(CMD_CTX, "semihosting is %s",
- arm->is_semihosting
- ? "enabled" : "disabled");
-
- return ERROR_OK;
-}
-
-COMMAND_HANDLER(handle_arm_semihosting_fileio_command)
-{
- struct target *target = get_current_target(CMD_CTX);
-
- if (target == NULL) {
- LOG_ERROR("No target selected");
- return ERROR_FAIL;
- }
-
- struct arm *arm = target_to_arm(target);
-
- if (!is_arm(arm)) {
- command_print(CMD_CTX, "current target isn't an ARM");
- return ERROR_FAIL;
- }
-
- if (!arm->is_semihosting) {
- command_print(CMD_CTX, "semihosting is not enabled");
- return ERROR_FAIL;
- }
-
- if (CMD_ARGC > 0)
- COMMAND_PARSE_ENABLE(CMD_ARGV[0], arm->is_semihosting_fileio);
-
- command_print(CMD_CTX, "semihosting fileio is %s",
- arm->is_semihosting_fileio
- ? "enabled" : "disabled");
-
- return ERROR_OK;
-}
-
-COMMAND_HANDLER(handle_arm_semihosting_cmdline)
-{
- struct target *target = get_current_target(CMD_CTX);
- unsigned int i;
-
- if (target == NULL) {
- LOG_ERROR("No target selected");
- return ERROR_FAIL;
- }
-
- struct arm *arm = target_to_arm(target);
-
- if (!is_arm(arm)) {
- command_print(CMD_CTX, "current target isn't an ARM");
- return ERROR_FAIL;
- }
-
- if (!arm->setup_semihosting) {
- command_print(CMD_CTX, "semihosting not supported for current target");
- return ERROR_FAIL;
- }
-
- free(arm->semihosting_cmdline);
- arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
-
- for (i = 1; i < CMD_ARGC; i++) {
- char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]);
- if (cmdline == NULL)
- break;
- free(arm->semihosting_cmdline);
- arm->semihosting_cmdline = cmdline;
- }
-
- return ERROR_OK;
-}
+extern __COMMAND_HANDLER(handle_common_semihosting_command);
+extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
+extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
+extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
static const struct command_registration arm_exec_command_handlers[] = {
{
@@ -1245,26 +1140,32 @@ static const struct command_registration arm_exec_command_handlers[] = {
},
{
"semihosting",
- .handler = handle_arm_semihosting_command,
+ .handler = handle_common_semihosting_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting operations",
},
{
"semihosting_cmdline",
- .handler = handle_arm_semihosting_cmdline,
+ .handler = handle_common_semihosting_cmdline,
.mode = COMMAND_EXEC,
.usage = "arguments",
.help = "command line arguments to be passed to program",
},
{
"semihosting_fileio",
- .handler = handle_arm_semihosting_fileio_command,
+ .handler = handle_common_semihosting_fileio_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting fileio operations",
},
-
+ {
+ "semihosting_resexit",
+ .handler = handle_common_semihosting_resumable_exit_command,
+ .mode = COMMAND_EXEC,
+ .usage = "['enable'|'disable']",
+ .help = "activate support for semihosting resumable exit",
+ },
COMMAND_REGISTRATION_DONE
};
const struct command_registration arm_command_handlers[] = {
diff --git a/src/target/armv7m.c b/src/target/armv7m.c
index 7b7893f..1b4e5b1 100644
--- a/src/target/armv7m.c
+++ b/src/target/armv7m.c
@@ -11,6 +11,9 @@
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
* 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 *
@@ -37,6 +40,7 @@
#include "armv7m.h"
#include "algorithm.h"
#include "register.h"
+#include "semihosting_common.h"
#if 0
#define _DEBUG_INSTRUCTION_EXECUTION_
@@ -537,7 +541,7 @@ int armv7m_arch_state(struct target *target)
uint32_t ctrl, sp;
/* avoid filling log waiting for fileio reply */
- if (arm->semihosting_hit_fileio)
+ if (target->semihosting->hit_fileio)
return ERROR_OK;
ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
@@ -552,8 +556,8 @@ int armv7m_arch_state(struct target *target)
buf_get_u32(arm->pc->value, 0, 32),
(ctrl & 0x02) ? 'p' : 'm',
sp,
- arm->is_semihosting ? ", semihosting" : "",
- arm->is_semihosting_fileio ? " fileio" : "");
+ target->semihosting->is_active ? ", semihosting" : "",
+ target->semihosting->is_fileio ? " fileio" : "");
return ERROR_OK;
}
diff --git a/src/target/armv8.c b/src/target/armv8.c
index 82b2b24..20f2b67 100644
--- a/src/target/armv8.c
+++ b/src/target/armv8.c
@@ -1,6 +1,9 @@
/***************************************************************************
* Copyright (C) 2015 by David Ung *
* *
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
* 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 *
@@ -36,6 +39,7 @@
#include "armv8_opcodes.h"
#include "target.h"
#include "target_type.h"
+#include "semihosting_common.h"
static const char * const armv8_state_strings[] = {
"AArch32", "Thumb", "Jazelle", "ThumbEE", "AArch64",
@@ -1046,7 +1050,7 @@ int armv8_aarch64_state(struct target *target)
armv8_mode_name(arm->core_mode),
buf_get_u32(arm->cpsr->value, 0, 32),
buf_get_u64(arm->pc->value, 0, 64),
- arm->is_semihosting ? ", semihosting" : "");
+ target->semihosting->is_active ? ", semihosting" : "");
return ERROR_OK;
}
diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c
index 7cf4a69..58bcc86 100644
--- a/src/target/breakpoints.c
+++ b/src/target/breakpoints.c
@@ -315,11 +315,8 @@ int breakpoint_remove_internal(struct target *target, target_addr_t address)
struct breakpoint *breakpoint = target->breakpoints;
while (breakpoint) {
- if ((breakpoint->address == address) && (breakpoint->asid == 0))
- break;
- else if ((breakpoint->address == 0) && (breakpoint->asid == address))
- break;
- else if ((breakpoint->address == address) && (breakpoint->asid != 0))
+ if ((breakpoint->address == address) ||
+ (breakpoint->address == 0 && breakpoint->asid == address))
break;
breakpoint = breakpoint->next;
}
diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c
index 30439f4..b6043fc 100644
--- a/src/target/cortex_m.c
+++ b/src/target/cortex_m.c
@@ -1806,11 +1806,11 @@ static int cortex_m_dwt_set_reg(struct reg *reg, uint8_t *buf)
struct dwt_reg {
uint32_t addr;
- char *name;
+ const char *name;
unsigned size;
};
-static struct dwt_reg dwt_base_regs[] = {
+static const struct dwt_reg dwt_base_regs[] = {
{ DWT_CTRL, "dwt_ctrl", 32, },
/* NOTE that Erratum 532314 (fixed r2p0) affects CYCCNT: it wrongly
* increments while the core is asleep.
@@ -1819,7 +1819,7 @@ static struct dwt_reg dwt_base_regs[] = {
/* plus some 8 bit counters, useful for profiling with TPIU */
};
-static struct dwt_reg dwt_comp[] = {
+static const struct dwt_reg dwt_comp[] = {
#define DWT_COMPARATOR(i) \
{ DWT_COMP0 + 0x10 * (i), "dwt_" #i "_comp", 32, }, \
{ DWT_MASK0 + 0x10 * (i), "dwt_" #i "_mask", 4, }, \
@@ -1848,7 +1848,7 @@ static const struct reg_arch_type dwt_reg_type = {
.set = cortex_m_dwt_set_reg,
};
-static void cortex_m_dwt_addreg(struct target *t, struct reg *r, struct dwt_reg *d)
+static void cortex_m_dwt_addreg(struct target *t, struct reg *r, const struct dwt_reg *d)
{
struct dwt_reg_state *state;
diff --git a/src/target/nds32.c b/src/target/nds32.c
index e4bb17f..4115ea4 100644
--- a/src/target/nds32.c
+++ b/src/target/nds32.c
@@ -2339,63 +2339,66 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
fileio_info->identifier = NULL;
}
+ uint32_t reg_r0, reg_r1, reg_r2;
+ nds32_get_mapped_reg(nds32, R0, &reg_r0);
+ nds32_get_mapped_reg(nds32, R1, &reg_r1);
+ nds32_get_mapped_reg(nds32, R2, &reg_r2);
+
switch (syscall_id) {
case NDS32_SYSCALL_EXIT:
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "exit");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
break;
case NDS32_SYSCALL_OPEN:
{
uint8_t filename[256];
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "open");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of path */
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
- nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_4));
+ fileio_info->param_3 = reg_r1;
+ fileio_info->param_4 = reg_r2;
- target->type->read_buffer(target, fileio_info->param_1,
- 256, filename);
+ target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
}
break;
case NDS32_SYSCALL_CLOSE:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "close");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
break;
case NDS32_SYSCALL_READ:
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "read");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
- nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
+ fileio_info->param_1 = reg_r0;
+ fileio_info->param_2 = reg_r1;
+ fileio_info->param_3 = reg_r2;
break;
case NDS32_SYSCALL_WRITE:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "write");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
- nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
+ fileio_info->param_1 = reg_r0;
+ fileio_info->param_2 = reg_r1;
+ fileio_info->param_3 = reg_r2;
break;
case NDS32_SYSCALL_LSEEK:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "lseek");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
- nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
+ fileio_info->param_1 = reg_r0;
+ fileio_info->param_2 = reg_r1;
+ fileio_info->param_3 = reg_r2;
break;
case NDS32_SYSCALL_UNLINK:
{
uint8_t filename[256];
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "unlink");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of path */
- target->type->read_buffer(target, fileio_info->param_1,
- 256, filename);
+ target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
}
break;
@@ -2404,61 +2407,57 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
uint8_t filename[256];
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "rename");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of old path */
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
+ fileio_info->param_3 = reg_r1;
/* reserve fileio_info->param_4 for length of new path */
- target->type->read_buffer(target, fileio_info->param_1,
- 256, filename);
+ target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
- target->type->read_buffer(target, fileio_info->param_3,
- 256, filename);
+ target->type->read_buffer(target, reg_r1, 256, filename);
fileio_info->param_4 = strlen((char *)filename) + 1;
}
break;
case NDS32_SYSCALL_FSTAT:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "fstat");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ fileio_info->param_1 = reg_r0;
+ fileio_info->param_2 = reg_r1;
break;
case NDS32_SYSCALL_STAT:
{
uint8_t filename[256];
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "stat");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of old path */
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
+ fileio_info->param_3 = reg_r1;
- target->type->read_buffer(target, fileio_info->param_1,
- 256, filename);
+ target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
}
break;
case NDS32_SYSCALL_GETTIMEOFDAY:
fileio_info->identifier = malloc(13);
sprintf(fileio_info->identifier, "gettimeofday");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
- nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ fileio_info->param_1 = reg_r0;
+ fileio_info->param_2 = reg_r1;
break;
case NDS32_SYSCALL_ISATTY:
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "isatty");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
break;
case NDS32_SYSCALL_SYSTEM:
{
uint8_t command[256];
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "system");
- nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of old path */
- target->type->read_buffer(target, fileio_info->param_1,
- 256, command);
+ target->type->read_buffer(target, reg_r0, 256, command);
fileio_info->param_2 = strlen((char *)command) + 1;
}
break;
diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c
new file mode 100644
index 0000000..beeb474
--- /dev/null
+++ b/src/target/semihosting_common.c
@@ -0,0 +1,1579 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
+ * Copyright (C) 2018 by Marvell Technology Group Ltd. *
+ * Written by Nicolas Pitre <nico@marvell.com> *
+ * *
+ * Copyright (C) 2010 by Spencer Oliver *
+ * spen@spen-soft.co.uk *
+ * *
+ * Copyright (C) 2016 by Square, Inc. *
+ * Steven Stallion <stallion@squareup.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+/**
+ * @file
+ * Common ARM semihosting support.
+ *
+ * Semihosting enables code running on a target to use some of the I/O
+ * facilities on the host computer. The target application must be linked
+ * against a library that forwards operation requests by using an
+ * instruction trapped by the debugger.
+ *
+ * Details can be found in
+ * "Semihosting for AArch32 and AArch64, Release 2.0"
+ * https://static.docs.arm.com/100863/0200/semihosting.pdf
+ * from ARM Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target.h"
+#include "target_type.h"
+#include "semihosting_common.h"
+
+#include <helper/binarybuffer.h>
+#include <helper/log.h>
+#include <sys/stat.h>
+
+static const int open_modeflags[12] = {
+ O_RDONLY,
+ O_RDONLY | O_BINARY,
+ O_RDWR,
+ O_RDWR | O_BINARY,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ O_RDWR | O_CREAT | O_TRUNC,
+ O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
+ O_WRONLY | O_CREAT | O_APPEND,
+ O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
+ O_RDWR | O_CREAT | O_APPEND,
+ O_RDWR | O_CREAT | O_APPEND | O_BINARY
+};
+
+static int semihosting_common_fileio_info(struct target *target,
+ struct gdb_fileio_info *fileio_info);
+static int semihosting_common_fileio_end(struct target *target, int result,
+ int fileio_errno, bool ctrl_c);
+
+static int semihosting_read_fields(struct target *target, size_t number,
+ uint8_t *fields);
+static int semihosting_write_fields(struct target *target, size_t number,
+ uint8_t *fields);
+static uint64_t semihosting_get_field(struct target *target, size_t index,
+ uint8_t *fields);
+static void semihosting_set_field(struct target *target, uint64_t value,
+ size_t index,
+ uint8_t *fields);
+
+/* Attempts to include gdb_server.h failed. */
+extern int gdb_actual_connections;
+
+/**
+ * Initialize common semihosting support.
+ *
+ * @param target Pointer to the target to initialize.
+ * @return An error status if there is a problem during initialization.
+ */
+int semihosting_common_init(struct target *target, void *setup,
+ void *post_result)
+{
+ LOG_DEBUG(" ");
+
+ target->fileio_info = malloc(sizeof(*target->fileio_info));
+ if (target->fileio_info == NULL) {
+ LOG_ERROR("out of memory");
+ return ERROR_FAIL;
+ }
+ memset(target->fileio_info, 0, sizeof(*target->fileio_info));
+
+ struct semihosting *semihosting;
+ semihosting = malloc(sizeof(*target->semihosting));
+ if (semihosting == NULL) {
+ LOG_ERROR("out of memory");
+ return ERROR_FAIL;
+ }
+
+ semihosting->is_active = false;
+ semihosting->is_fileio = false;
+ semihosting->hit_fileio = false;
+ semihosting->is_resumable = false;
+ semihosting->has_resumable_exit = false;
+ semihosting->word_size_bytes = 0;
+ semihosting->op = -1;
+ semihosting->param = 0;
+ semihosting->result = -1;
+ semihosting->sys_errno = -1;
+ semihosting->cmdline = NULL;
+
+ /* If possible, update it in setup(). */
+ semihosting->setup_time = clock();
+
+ semihosting->setup = setup;
+ semihosting->post_result = post_result;
+
+ target->semihosting = semihosting;
+
+ target->type->get_gdb_fileio_info = semihosting_common_fileio_info;
+ target->type->gdb_fileio_end = semihosting_common_fileio_end;
+
+ return ERROR_OK;
+}
+
+/**
+ * Portable implementation of ARM semihosting calls.
+ * Performs the currently pending semihosting operation
+ * encoded in target->semihosting.
+ */
+int semihosting_common(struct target *target)
+{
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ /* Silently ignore if the semhosting field was not set. */
+ return ERROR_OK;
+ }
+
+ struct gdb_fileio_info *fileio_info = target->fileio_info;
+
+ /*
+ * By default return an error.
+ * The actual result must be set by each function
+ */
+ semihosting->result = -1;
+
+ /* Most operations are resumable, except the two exit calls. */
+ semihosting->is_resumable = true;
+
+ int retval;
+
+ /* Enough space to hold 4 long words. */
+ uint8_t fields[4*8];
+
+ LOG_DEBUG("op=0x%x, param=0x%" PRIx64, (int)semihosting->op,
+ semihosting->param);
+
+ switch (semihosting->op) {
+
+ case SEMIHOSTING_SYS_CLOCK: /* 0x10 */
+ /*
+ * Returns the number of centiseconds (hundredths of a second)
+ * since the execution started.
+ *
+ * Values returned can be of limited use for some benchmarking
+ * purposes because of communication overhead or other
+ * agent-specific factors. For example, with a debug hardware
+ * unit the request is passed back to the host for execution.
+ * This can lead to unpredictable delays in transmission and
+ * process scheduling.
+ *
+ * Use this function to calculate time intervals, by calculating
+ * differences between intervals with and without the code
+ * sequence to be timed.
+ *
+ * Entry
+ * The PARAMETER REGISTER must contain 0. There are no other
+ * parameters.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - The number of centiseconds since some arbitrary start
+ * point, if the call is successful.
+ * - –1 if the call is not successful. For example, because
+ * of a communications error.
+ */
+ {
+ clock_t delta = clock() - semihosting->setup_time;
+
+ semihosting->result = delta / (CLOCKS_PER_SEC / 100);
+ }
+ break;
+
+ case SEMIHOSTING_SYS_CLOSE: /* 0x02 */
+ /*
+ * Closes a file on the host system. The handle must reference
+ * a file that was opened with SYS_OPEN.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * one-field argument block:
+ * - field 1 Contains a handle for an open file.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the call is successful
+ * - –1 if the call is not successful.
+ */
+ retval = semihosting_read_fields(target, 1, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int fd = semihosting_get_field(target, 0, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "close";
+ fileio_info->param_1 = fd;
+ } else {
+ semihosting->result = close(fd);
+ semihosting->sys_errno = errno;
+
+ LOG_DEBUG("close(%d)=%d", fd, (int)semihosting->result);
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_ERRNO: /* 0x13 */
+ /*
+ * Returns the value of the C library errno variable that is
+ * associated with the semihosting implementation. The errno
+ * variable can be set by a number of C library semihosted
+ * functions, including:
+ * - SYS_REMOVE
+ * - SYS_OPEN
+ * - SYS_CLOSE
+ * - SYS_READ
+ * - SYS_WRITE
+ * - SYS_SEEK.
+ *
+ * Whether errno is set or not, and to what value, is entirely
+ * host-specific, except where the ISO C standard defines the
+ * behavior.
+ *
+ * Entry
+ * There are no parameters. The PARAMETER REGISTER must be 0.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains the value of the C
+ * library errno variable.
+ */
+ semihosting->result = semihosting->sys_errno;
+ break;
+
+ case SEMIHOSTING_SYS_EXIT: /* 0x18 */
+ /*
+ * Note: SYS_EXIT was called angel_SWIreason_ReportException in
+ * previous versions of the documentation.
+ *
+ * An application calls this operation to report an exception
+ * to the debugger directly. The most common use is to report
+ * that execution has completed, using ADP_Stopped_ApplicationExit.
+ *
+ * Note: This semihosting operation provides no means for 32-bit
+ * callers to indicate an application exit with a specified exit
+ * code. Semihosting callers may prefer to check for the presence
+ * of the SH_EXT_EXTENDED_REPORT_EXCEPTION extension and use
+ * the SYS_REPORT_EXCEPTION_EXTENDED operation instead, if it
+ * is available.
+ *
+ * Entry (32-bit)
+ * On entry, the PARAMETER register is set to a reason code
+ * describing the cause of the trap. Not all semihosting client
+ * implementations will necessarily trap every corresponding
+ * event. Important reason codes are:
+ *
+ * - ADP_Stopped_ApplicationExit 0x20026
+ * - ADP_Stopped_RunTimeErrorUnknown 0x20023
+ *
+ * Entry (64-bit)
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * two-field argument block:
+ * - field 1 The exception type, which is one of the set of
+ * reason codes in the above tables.
+ * - field 2 A subcode, whose meaning depends on the reason
+ * code in field 1.
+ * In particular, if field 1 is ADP_Stopped_ApplicationExit
+ * then field 2 is an exit status code, as passed to the C
+ * standard library exit() function. A simulator receiving
+ * this request must notify a connected debugger, if present,
+ * and then exit with the specified status.
+ *
+ * Return
+ * No return is expected from these calls. However, it is
+ * possible for the debugger to request that the application
+ * continues by performing an RDI_Execute request or equivalent.
+ * In this case, execution continues with the registers as they
+ * were on entry to the operation, or as subsequently modified
+ * by the debugger.
+ */
+ if (semihosting->word_size_bytes == 8) {
+ retval = semihosting_read_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int type = semihosting_get_field(target, 0, fields);
+ int code = semihosting_get_field(target, 1, fields);
+
+ if (type == ADP_STOPPED_APPLICATION_EXIT) {
+ if (!gdb_actual_connections)
+ exit(code);
+ else {
+ fprintf(stderr,
+ "semihosting: *** application exited with %d ***\n",
+ code);
+ }
+ } else {
+ fprintf(stderr,
+ "semihosting: application exception %#x\n",
+ type);
+ }
+ }
+ } else {
+ if (semihosting->param == ADP_STOPPED_APPLICATION_EXIT) {
+ if (!gdb_actual_connections)
+ exit(0);
+ else {
+ fprintf(stderr,
+ "semihosting: *** application exited normally ***\n");
+ }
+ } else if (semihosting->param == ADP_STOPPED_RUN_TIME_ERROR) {
+ /* Chosen more or less arbitrarly to have a nicer message,
+ * otherwise all other return the same exit code 1. */
+ if (!gdb_actual_connections)
+ exit(1);
+ else {
+ fprintf(stderr,
+ "semihosting: *** application exited with error ***\n");
+ }
+ } else {
+ if (!gdb_actual_connections)
+ exit(1);
+ else {
+ fprintf(stderr,
+ "semihosting: application exception %#x\n",
+ (unsigned) semihosting->param);
+ }
+ }
+ }
+ if (!semihosting->has_resumable_exit) {
+ semihosting->is_resumable = false;
+ return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ break;
+
+ case SEMIHOSTING_SYS_EXIT_EXTENDED: /* 0x20 */
+ /*
+ * This operation is only supported if the semihosting extension
+ * SH_EXT_EXIT_EXTENDED is implemented. SH_EXT_EXIT_EXTENDED is
+ * reported using feature byte 0, bit 0. If this extension is
+ * supported, then the implementation provides a means to
+ * report a normal exit with a nonzero exit status in both 32-bit
+ * and 64-bit semihosting APIs.
+ *
+ * The implementation must provide the semihosting call
+ * SYS_EXIT_EXTENDED for both A64 and A32/T32 semihosting APIs.
+ *
+ * SYS_EXIT_EXTENDED is used by an application to report an
+ * exception or exit to the debugger directly. The most common
+ * use is to report that execution has completed, using
+ * ADP_Stopped_ApplicationExit.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * two-field argument block:
+ * - field 1 The exception type, which should be one of the set
+ * of reason codes that are documented for the SYS_EXIT
+ * (0x18) call. For example, ADP_Stopped_ApplicationExit.
+ * - field 2 A subcode, whose meaning depends on the reason
+ * code in field 1. In particular, if field 1 is
+ * ADP_Stopped_ApplicationExit then field 2 is an exit status
+ * code, as passed to the C standard library exit() function.
+ * A simulator receiving this request must notify a connected
+ * debugger, if present, and then exit with the specified status.
+ *
+ * Return
+ * No return is expected from these calls.
+ *
+ * For the A64 API, this call is identical to the behavior of
+ * the mandatory SYS_EXIT (0x18) call. If this extension is
+ * supported, then both calls must be implemented.
+ */
+ retval = semihosting_read_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int type = semihosting_get_field(target, 0, fields);
+ int code = semihosting_get_field(target, 1, fields);
+
+ if (type == ADP_STOPPED_APPLICATION_EXIT) {
+ if (!gdb_actual_connections)
+ exit(code);
+ else {
+ fprintf(stderr,
+ "semihosting: *** application exited with %d ***\n",
+ code);
+ }
+ } else {
+ fprintf(stderr, "semihosting: exception %#x\n",
+ type);
+ }
+ }
+ if (!semihosting->has_resumable_exit) {
+ semihosting->is_resumable = false;
+ return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ break;
+
+ case SEMIHOSTING_SYS_FLEN: /* 0x0C */
+ /*
+ * Returns the length of a specified file.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * one-field argument block:
+ * - field 1 A handle for a previously opened, seekable file
+ * object.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - The current length of the file object, if the call is
+ * successful.
+ * - –1 if an error occurs.
+ */
+ if (semihosting->is_fileio) {
+ LOG_ERROR("SYS_FLEN not supported by semihosting fileio");
+ return ERROR_FAIL;
+ }
+ retval = semihosting_read_fields(target, 1, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int fd = semihosting_get_field(target, 0, fields);
+ struct stat buf;
+ semihosting->result = fstat(fd, &buf);
+ if (semihosting->result == -1) {
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result);
+ break;
+ }
+ LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result);
+ semihosting->result = buf.st_size;
+ }
+ break;
+
+ case SEMIHOSTING_SYS_GET_CMDLINE: /* 0x15 */
+ /*
+ * Returns the command line that is used for the call to the
+ * executable, that is, argc and argv.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER points to a two-field data
+ * block to be used for returning the command string and its length:
+ * - field 1 A pointer to a buffer of at least the size that is
+ * specified in field 2.
+ * - field 2 The length of the buffer in bytes.
+ *
+ * Return
+ * On exit:
+ * If the call is successful, then the RETURN REGISTER contains 0,
+ * the PARAMETER REGISTER is unchanged, and the data block is
+ * updated as follows:
+ * - field 1 A pointer to a null-terminated string of the command
+ * line.
+ * - field 2 The length of the string in bytes.
+ * If the call is not successful, then the RETURN REGISTER
+ * contains -1.
+ *
+ * Note: The semihosting implementation might impose limits on
+ * the maximum length of the string that can be transferred.
+ * However, the implementation must be able to support a
+ * command-line length of at least 80 bytes.
+ */
+ retval = semihosting_read_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ uint64_t addr = semihosting_get_field(target, 0, fields);
+ size_t size = semihosting_get_field(target, 1, fields);
+
+ char *arg = semihosting->cmdline != NULL ?
+ semihosting->cmdline : "";
+ uint32_t len = strlen(arg) + 1;
+ if (len > size)
+ semihosting->result = -1;
+ else {
+ semihosting_set_field(target, len, 1, fields);
+ retval = target_write_buffer(target, addr, len,
+ (uint8_t *)arg);
+ if (retval != ERROR_OK)
+ return retval;
+ semihosting->result = 0;
+
+ retval = semihosting_write_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+ LOG_DEBUG("SYS_GET_CMDLINE=[%s],%d", arg,
+ (int)semihosting->result);
+ }
+ break;
+
+ case SEMIHOSTING_SYS_HEAPINFO: /* 0x16 */
+ /*
+ * Returns the system stack and heap parameters.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains the address of a
+ * pointer to a four-field data block. The contents of the data
+ * block are filled by the function. The following C-like
+ * pseudocode describes the layout of the block:
+ * struct block {
+ * void* heap_base;
+ * void* heap_limit;
+ * void* stack_base;
+ * void* stack_limit;
+ * };
+ *
+ * Return
+ * On exit, the PARAMETER REGISTER is unchanged and the data
+ * block has been updated.
+ */
+ retval = semihosting_read_fields(target, 1, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ uint64_t addr = semihosting_get_field(target, 0, fields);
+ /* tell the remote we have no idea */
+ memset(fields, 0, 4 * semihosting->word_size_bytes);
+ retval = target_write_memory(target, addr, 4,
+ semihosting->word_size_bytes,
+ fields);
+ if (retval != ERROR_OK)
+ return retval;
+ semihosting->result = 0;
+ }
+ break;
+
+ case SEMIHOSTING_SYS_ISERROR: /* 0x08 */
+ /*
+ * Determines whether the return code from another semihosting
+ * call is an error status or not.
+ *
+ * This call is passed a parameter block containing the error
+ * code to examine.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * one-field data block:
+ * - field 1 The required status word to check.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the status field is not an error indication
+ * - A nonzero value if the status field is an error indication.
+ */
+ retval = semihosting_read_fields(target, 1, fields);
+ if (retval != ERROR_OK)
+ return retval;
+
+ uint64_t code = semihosting_get_field(target, 0, fields);
+ semihosting->result = (code != 0);
+ break;
+
+ case SEMIHOSTING_SYS_ISTTY: /* 0x09 */
+ /*
+ * Checks whether a file is connected to an interactive device.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * one-field argument block:
+ * field 1 A handle for a previously opened file object.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 1 if the handle identifies an interactive device.
+ * - 0 if the handle identifies a file.
+ * - A value other than 1 or 0 if an error occurs.
+ */
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "isatty";
+ fileio_info->param_1 = semihosting->param;
+ } else {
+ retval = semihosting_read_fields(target, 1, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ int fd = semihosting_get_field(target, 0, fields);
+ semihosting->result = isatty(fd);
+ LOG_DEBUG("isatty(%d)=%d", fd, (int)semihosting->result);
+ }
+ break;
+
+ case SEMIHOSTING_SYS_OPEN: /* 0x01 */
+ /*
+ * Opens a file on the host system.
+ *
+ * The file path is specified either as relative to the current
+ * directory of the host process, or absolute, using the path
+ * conventions of the host operating system.
+ *
+ * Semihosting implementations must support opening the special
+ * path name :semihosting-features as part of the semihosting
+ * extensions reporting mechanism.
+ *
+ * ARM targets interpret the special path name :tt as meaning
+ * the console input stream, for an open-read or the console
+ * output stream, for an open-write. Opening these streams is
+ * performed as part of the standard startup code for those
+ * applications that reference the C stdio streams. The
+ * semihosting extension SH_EXT_STDOUT_STDERR allows the
+ * semihosting caller to open separate output streams
+ * corresponding to stdout and stderr. This extension is
+ * reported using feature byte 0, bit 1. Use SYS_OPEN with
+ * the special path name :semihosting-features to access the
+ * feature bits.
+ *
+ * If this extension is supported, the implementation must
+ * support the following additional semantics to SYS_OPEN:
+ * - If the special path name :tt is opened with an fopen
+ * mode requesting write access (w, wb, w+, or w+b), then
+ * this is a request to open stdout.
+ * - If the special path name :tt is opened with a mode
+ * requesting append access (a, ab, a+, or a+b), then this is
+ * a request to open stderr.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * three-field argument block:
+ * - field 1 A pointer to a null-terminated string containing
+ * a file or device name.
+ * - field 2 An integer that specifies the file opening mode.
+ * - field 3 An integer that gives the length of the string
+ * pointed to by field 1.
+ *
+ * The length does not include the terminating null character
+ * that must be present.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - A nonzero handle if the call is successful.
+ * - –1 if the call is not successful.
+ */
+ retval = semihosting_read_fields(target, 3, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ uint64_t addr = semihosting_get_field(target, 0, fields);
+ uint32_t mode = semihosting_get_field(target, 1, fields);
+ size_t len = semihosting_get_field(target, 2, fields);
+
+ if (mode > 11) {
+ semihosting->result = -1;
+ semihosting->sys_errno = EINVAL;
+ break;
+ }
+ uint8_t *fn = malloc(len+1);
+ if (!fn) {
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOMEM;
+ } else {
+ retval = target_read_memory(target, addr, 1, len, fn);
+ if (retval != ERROR_OK) {
+ free(fn);
+ return retval;
+ }
+ fn[len] = 0;
+ /* TODO: implement the :semihosting-features special file.
+ * */
+ if (semihosting->is_fileio) {
+ if (strcmp((char *)fn, ":tt") == 0)
+ semihosting->result = 0;
+ else {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "open";
+ fileio_info->param_1 = addr;
+ fileio_info->param_2 = len;
+ fileio_info->param_3 = open_modeflags[mode];
+ fileio_info->param_4 = 0644;
+ }
+ } else {
+ if (strcmp((char *)fn, ":tt") == 0) {
+ /* Mode is:
+ * - 0-3 ("r") for stdin,
+ * - 4-7 ("w") for stdout,
+ * - 8-11 ("a") for stderr */
+ if (mode < 4) {
+ semihosting->result = dup(
+ STDIN_FILENO);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("dup(STDIN)=%d",
+ (int)semihosting->result);
+ } else if (mode < 8) {
+ semihosting->result = dup(
+ STDOUT_FILENO);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("dup(STDOUT)=%d",
+ (int)semihosting->result);
+ } else {
+ semihosting->result = dup(
+ STDERR_FILENO);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("dup(STDERR)=%d",
+ (int)semihosting->result);
+ }
+ } else {
+ /* cygwin requires the permission setting
+ * otherwise it will fail to reopen a previously
+ * written file */
+ semihosting->result = open((char *)fn,
+ open_modeflags[mode],
+ 0644);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("open('%s')=%d", fn,
+ (int)semihosting->result);
+ }
+ }
+ free(fn);
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_READ: /* 0x06 */
+ /*
+ * Reads the contents of a file into a buffer. The file position
+ * is specified either:
+ * - Explicitly by a SYS_SEEK.
+ * - Implicitly one byte beyond the previous SYS_READ or
+ * SYS_WRITE request.
+ *
+ * The file position is at the start of the file when it is
+ * opened, and is lost when the file is closed. Perform the
+ * file operation as a single action whenever possible. For
+ * example, do not split a read of 16KB into four 4KB chunks
+ * unless there is no alternative.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * three-field data block:
+ * - field 1 Contains a handle for a file previously opened
+ * with SYS_OPEN.
+ * - field 2 Points to a buffer.
+ * - field 3 Contains the number of bytes to read to the buffer
+ * from the file.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains the number of bytes not
+ * filled in the buffer (buffer_length - bytes_read) as follows:
+ * - If the RETURN REGISTER is 0, the entire buffer was
+ * successfully filled.
+ * - If the RETURN REGISTER is the same as field 3, no bytes
+ * were read (EOF can be assumed).
+ * - If the RETURN REGISTER contains a value smaller than
+ * field 3, the read succeeded but the buffer was only partly
+ * filled. For interactive devices, this is the most common
+ * return value.
+ */
+ retval = semihosting_read_fields(target, 3, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int fd = semihosting_get_field(target, 0, fields);
+ uint64_t addr = semihosting_get_field(target, 1, fields);
+ size_t len = semihosting_get_field(target, 2, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "read";
+ fileio_info->param_1 = fd;
+ fileio_info->param_2 = addr;
+ fileio_info->param_3 = len;
+ } else {
+ uint8_t *buf = malloc(len);
+ if (!buf) {
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOMEM;
+ } else {
+ semihosting->result = read(fd, buf, len);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("read(%d, 0x%" PRIx64 ", %zu)=%d",
+ fd,
+ addr,
+ len,
+ (int)semihosting->result);
+ if (semihosting->result >= 0) {
+ retval = target_write_buffer(target, addr,
+ semihosting->result,
+ buf);
+ if (retval != ERROR_OK) {
+ free(buf);
+ return retval;
+ }
+ /* the number of bytes NOT filled in */
+ semihosting->result = len -
+ semihosting->result;
+ }
+ free(buf);
+ }
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_READC: /* 0x07 */
+ /*
+ * Reads a byte from the console.
+ *
+ * Entry
+ * The PARAMETER REGISTER must contain 0. There are no other
+ * parameters or values possible.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains the byte read from
+ * the console.
+ */
+ if (semihosting->is_fileio) {
+ LOG_ERROR("SYS_READC not supported by semihosting fileio");
+ return ERROR_FAIL;
+ }
+ semihosting->result = getchar();
+ LOG_DEBUG("getchar()=%d", (int)semihosting->result);
+ break;
+
+ case SEMIHOSTING_SYS_REMOVE: /* 0x0E */
+ /*
+ * Deletes a specified file on the host filing system.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * two-field argument block:
+ * - field 1 Points to a null-terminated string that gives the
+ * path name of the file to be deleted.
+ * - field 2 The length of the string.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the delete is successful
+ * - A nonzero, host-specific error code if the delete fails.
+ */
+ retval = semihosting_read_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ uint64_t addr = semihosting_get_field(target, 0, fields);
+ size_t len = semihosting_get_field(target, 1, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "unlink";
+ fileio_info->param_1 = addr;
+ fileio_info->param_2 = len;
+ } else {
+ uint8_t *fn = malloc(len+1);
+ if (!fn) {
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOMEM;
+ } else {
+ retval =
+ target_read_memory(target, addr, 1, len,
+ fn);
+ if (retval != ERROR_OK) {
+ free(fn);
+ return retval;
+ }
+ fn[len] = 0;
+ semihosting->result = remove((char *)fn);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("remove('%s')=%d", fn,
+ (int)semihosting->result);
+
+ free(fn);
+ }
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_RENAME: /* 0x0F */
+ /*
+ * Renames a specified file.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * four-field data block:
+ * - field 1 A pointer to the name of the old file.
+ * - field 2 The length of the old filename.
+ * - field 3 A pointer to the new filename.
+ * - field 4 The length of the new filename. Both strings are
+ * null-terminated.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the rename is successful.
+ * - A nonzero, host-specific error code if the rename fails.
+ */
+ retval = semihosting_read_fields(target, 4, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ uint64_t addr1 = semihosting_get_field(target, 0, fields);
+ size_t len1 = semihosting_get_field(target, 1, fields);
+ uint64_t addr2 = semihosting_get_field(target, 2, fields);
+ size_t len2 = semihosting_get_field(target, 3, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "rename";
+ fileio_info->param_1 = addr1;
+ fileio_info->param_2 = len1;
+ fileio_info->param_3 = addr2;
+ fileio_info->param_4 = len2;
+ } else {
+ uint8_t *fn1 = malloc(len1+1);
+ uint8_t *fn2 = malloc(len2+1);
+ if (!fn1 || !fn2) {
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOMEM;
+ } else {
+ retval = target_read_memory(target, addr1, 1, len1,
+ fn1);
+ if (retval != ERROR_OK) {
+ free(fn1);
+ free(fn2);
+ return retval;
+ }
+ retval = target_read_memory(target, addr2, 1, len2,
+ fn2);
+ if (retval != ERROR_OK) {
+ free(fn1);
+ free(fn2);
+ return retval;
+ }
+ fn1[len1] = 0;
+ fn2[len2] = 0;
+ semihosting->result = rename((char *)fn1,
+ (char *)fn2);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("rename('%s', '%s')=%d", fn1, fn2,
+ (int)semihosting->result);
+
+ free(fn1);
+ free(fn2);
+ }
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_SEEK: /* 0x0A */
+ /*
+ * Seeks to a specified position in a file using an offset
+ * specified from the start of the file. The file is assumed
+ * to be a byte array and the offset is given in bytes.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * two-field data block:
+ * - field 1 A handle for a seekable file object.
+ * - field 2 The absolute byte position to seek to.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the request is successful.
+ * - A negative value if the request is not successful.
+ * Use SYS_ERRNO to read the value of the host errno variable
+ * describing the error.
+ *
+ * Note: The effect of seeking outside the current extent of
+ * the file object is undefined.
+ */
+ retval = semihosting_read_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int fd = semihosting_get_field(target, 0, fields);
+ off_t pos = semihosting_get_field(target, 1, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "lseek";
+ fileio_info->param_1 = fd;
+ fileio_info->param_2 = pos;
+ fileio_info->param_3 = SEEK_SET;
+ } else {
+ semihosting->result = lseek(fd, pos, SEEK_SET);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("lseek(%d, %d)=%d", fd, (int)pos,
+ (int)semihosting->result);
+ if (semihosting->result == pos)
+ semihosting->result = 0;
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_SYSTEM: /* 0x12 */
+ /*
+ * Passes a command to the host command-line interpreter.
+ * This enables you to execute a system command such as dir,
+ * ls, or pwd. The terminal I/O is on the host, and is not
+ * visible to the target.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * two-field argument block:
+ * - field 1 Points to a string to be passed to the host
+ * command-line interpreter.
+ * - field 2 The length of the string.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains the return status.
+ */
+
+ /* Provide SYS_SYSTEM functionality. Uses the
+ * libc system command, there may be a reason *NOT*
+ * to use this, but as I can't think of one, I
+ * implemented it this way.
+ */
+ retval = semihosting_read_fields(target, 2, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ uint64_t addr = semihosting_get_field(target, 0, fields);
+ size_t len = semihosting_get_field(target, 1, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "system";
+ fileio_info->param_1 = addr;
+ fileio_info->param_2 = len;
+ } else {
+ uint8_t *cmd = malloc(len+1);
+ if (!cmd) {
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOMEM;
+ } else {
+ retval = target_read_memory(target,
+ addr,
+ 1,
+ len,
+ cmd);
+ if (retval != ERROR_OK) {
+ free(cmd);
+ return retval;
+ } else {
+ cmd[len] = 0;
+ semihosting->result = system(
+ (const char *)cmd);
+ LOG_DEBUG("system('%s')=%d",
+ cmd,
+ (int)semihosting->result);
+ }
+
+ free(cmd);
+ }
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_TIME: /* 0x11 */
+ /*
+ * Returns the number of seconds since 00:00 January 1, 1970.
+ * This value is real-world time, regardless of any debug agent
+ * configuration.
+ *
+ * Entry
+ * There are no parameters.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains the number of seconds.
+ */
+ semihosting->result = time(NULL);
+ break;
+
+ case SEMIHOSTING_SYS_WRITE: /* 0x05 */
+ /*
+ * Writes the contents of a buffer to a specified file at the
+ * current file position. The file position is specified either:
+ * - Explicitly, by a SYS_SEEK.
+ * - Implicitly as one byte beyond the previous SYS_READ or
+ * SYS_WRITE request.
+ *
+ * The file position is at the start of the file when the file
+ * is opened, and is lost when the file is closed.
+ *
+ * Perform the file operation as a single action whenever
+ * possible. For example, do not split a write of 16KB into
+ * four 4KB chunks unless there is no alternative.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * three-field data block:
+ * - field 1 Contains a handle for a file previously opened
+ * with SYS_OPEN.
+ * - field 2 Points to the memory containing the data to be written.
+ * - field 3 Contains the number of bytes to be written from
+ * the buffer to the file.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the call is successful.
+ * - The number of bytes that are not written, if there is an error.
+ */
+ retval = semihosting_read_fields(target, 3, fields);
+ if (retval != ERROR_OK)
+ return retval;
+ else {
+ int fd = semihosting_get_field(target, 0, fields);
+ uint64_t addr = semihosting_get_field(target, 1, fields);
+ size_t len = semihosting_get_field(target, 2, fields);
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "write";
+ fileio_info->param_1 = fd;
+ fileio_info->param_2 = addr;
+ fileio_info->param_3 = len;
+ } else {
+ uint8_t *buf = malloc(len);
+ if (!buf) {
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOMEM;
+ } else {
+ retval = target_read_buffer(target, addr, len, buf);
+ if (retval != ERROR_OK) {
+ free(buf);
+ return retval;
+ }
+ semihosting->result = write(fd, buf, len);
+ semihosting->sys_errno = errno;
+ LOG_DEBUG("write(%d, 0x%" PRIx64 ", %zu)=%d",
+ fd,
+ addr,
+ len,
+ (int)semihosting->result);
+ if (semihosting->result >= 0) {
+ /* The number of bytes that are NOT written.
+ * */
+ semihosting->result = len -
+ semihosting->result;
+ }
+
+ free(buf);
+ }
+ }
+ }
+ break;
+
+ case SEMIHOSTING_SYS_WRITEC: /* 0x03 */
+ /*
+ * Writes a character byte, pointed to by the PARAMETER REGISTER,
+ * to the debug channel. When executed under a semihosting
+ * debugger, the character appears on the host debugger console.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to the
+ * character.
+ *
+ * Return
+ * None. The RETURN REGISTER is corrupted.
+ */
+ if (semihosting->is_fileio) {
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "write";
+ fileio_info->param_1 = 1;
+ fileio_info->param_2 = semihosting->param;
+ fileio_info->param_3 = 1;
+ } else {
+ uint64_t addr = semihosting->param;
+ unsigned char c;
+ retval = target_read_memory(target, addr, 1, 1, &c);
+ if (retval != ERROR_OK)
+ return retval;
+ putchar(c);
+ semihosting->result = 0;
+ }
+ break;
+
+ case SEMIHOSTING_SYS_WRITE0: /* 0x04 */
+ /*
+ * Writes a null-terminated string to the debug channel.
+ * When executed under a semihosting debugger, the characters
+ * appear on the host debugger console.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to the
+ * first byte of the string.
+ *
+ * Return
+ * None. The RETURN REGISTER is corrupted.
+ */
+ if (semihosting->is_fileio) {
+ size_t count = 0;
+ uint64_t addr = semihosting->param;
+ for (;; addr++) {
+ unsigned char c;
+ retval = target_read_memory(target, addr, 1, 1, &c);
+ if (retval != ERROR_OK)
+ return retval;
+ if (c == '\0')
+ break;
+ count++;
+ }
+ semihosting->hit_fileio = true;
+ fileio_info->identifier = "write";
+ fileio_info->param_1 = 1;
+ fileio_info->param_2 = semihosting->param;
+ fileio_info->param_3 = count;
+ } else {
+ uint64_t addr = semihosting->param;
+ do {
+ unsigned char c;
+ retval = target_read_memory(target, addr++, 1, 1, &c);
+ if (retval != ERROR_OK)
+ return retval;
+ if (!c)
+ break;
+ putchar(c);
+ } while (1);
+ semihosting->result = 0;
+ }
+ break;
+
+ case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */
+ /*
+ * Returns the number of elapsed target ticks since execution
+ * started.
+ * Use SYS_TICKFREQ to determine the tick frequency.
+ *
+ * Entry (32-bit)
+ * On entry, the PARAMETER REGISTER points to a two-field data
+ * block to be used for returning the number of elapsed ticks:
+ * - field 1 The least significant field and is at the low address.
+ * - field 2 The most significant field and is at the high address.
+ *
+ * Entry (64-bit)
+ * On entry the PARAMETER REGISTER points to a one-field data
+ * block to be used for returning the number of elapsed ticks:
+ * - field 1 The number of elapsed ticks as a 64-bit value.
+ *
+ * Return
+ * On exit:
+ * - On success, the RETURN REGISTER contains 0, the PARAMETER
+ * REGISTER is unchanged, and the data block pointed to by the
+ * PARAMETER REGISTER is filled in with the number of elapsed
+ * ticks.
+ * - On failure, the RETURN REGISTER contains -1, and the
+ * PARAMETER REGISTER contains -1.
+ *
+ * Note: Some semihosting implementations might not support this
+ * semihosting operation, and they always return -1 in the
+ * RETURN REGISTER.
+ */
+
+ case SEMIHOSTING_SYS_TICKFREQ: /* 0x31 */
+ /*
+ * Returns the tick frequency.
+ *
+ * Entry
+ * The PARAMETER REGISTER must contain 0 on entry to this routine.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains either:
+ * - The number of ticks per second.
+ * - –1 if the target does not know the value of one tick.
+ *
+ * Note: Some semihosting implementations might not support
+ * this semihosting operation, and they always return -1 in the
+ * RETURN REGISTER.
+ */
+
+ case SEMIHOSTING_SYS_TMPNAM: /* 0x0D */
+ /*
+ * Returns a temporary name for a file identified by a system
+ * file identifier.
+ *
+ * Entry
+ * On entry, the PARAMETER REGISTER contains a pointer to a
+ * three-word argument block:
+ * - field 1 A pointer to a buffer.
+ * - field 2 A target identifier for this filename. Its value
+ * must be an integer in the range 0-255.
+ * - field 3 Contains the length of the buffer. The length must
+ * be at least the value of L_tmpnam on the host system.
+ *
+ * Return
+ * On exit, the RETURN REGISTER contains:
+ * - 0 if the call is successful.
+ * - –1 if an error occurs.
+ *
+ * The buffer pointed to by the PARAMETER REGISTER contains
+ * the filename, prefixed with a suitable directory name.
+ * If you use the same target identifier again, the same
+ * filename is returned.
+ *
+ * Note: The returned string must be null-terminated.
+ */
+
+ default:
+ fprintf(stderr, "semihosting: unsupported call %#x\n",
+ (unsigned) semihosting->op);
+ semihosting->result = -1;
+ semihosting->sys_errno = ENOTSUP;
+ }
+
+ if (!semihosting->hit_fileio) {
+ retval = semihosting->post_result(target);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to post semihosting result");
+ return retval;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/* -------------------------------------------------------------------------
+ * Local functions. */
+
+static int semihosting_common_fileio_info(struct target *target,
+ struct gdb_fileio_info *fileio_info)
+{
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting)
+ return ERROR_FAIL;
+
+ /*
+ * To avoid unnecessary duplication, semihosting prepares the
+ * fileio_info structure out-of-band when the target halts. See
+ * do_semihosting for more detail.
+ */
+ if (!semihosting->is_fileio || !semihosting->hit_fileio)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
+static int semihosting_common_fileio_end(struct target *target, int result,
+ int fileio_errno, bool ctrl_c)
+{
+ struct gdb_fileio_info *fileio_info = target->fileio_info;
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting)
+ return ERROR_FAIL;
+
+ /* clear pending status */
+ semihosting->hit_fileio = false;
+
+ semihosting->result = result;
+ semihosting->sys_errno = fileio_errno;
+
+ /*
+ * Some fileio results do not match up with what the semihosting
+ * operation expects; for these operations, we munge the results
+ * below:
+ */
+ switch (semihosting->op) {
+ case SEMIHOSTING_SYS_WRITE: /* 0x05 */
+ if (result < 0)
+ semihosting->result = fileio_info->param_3;
+ else
+ semihosting->result = 0;
+ break;
+
+ case SEMIHOSTING_SYS_READ: /* 0x06 */
+ if (result == (int)fileio_info->param_3)
+ semihosting->result = 0;
+ if (result <= 0)
+ semihosting->result = fileio_info->param_3;
+ break;
+
+ case SEMIHOSTING_SYS_SEEK: /* 0x0a */
+ if (result > 0)
+ semihosting->result = 0;
+ break;
+ }
+
+ return semihosting->post_result(target);
+}
+
+/**
+ * Read all fields of a command from target to buffer.
+ */
+static int semihosting_read_fields(struct target *target, size_t number,
+ uint8_t *fields)
+{
+ struct semihosting *semihosting = target->semihosting;
+ return target_read_memory(target, semihosting->param,
+ semihosting->word_size_bytes, number, fields);
+}
+
+/**
+ * Write all fields of a command from buffer to target.
+ */
+static int semihosting_write_fields(struct target *target, size_t number,
+ uint8_t *fields)
+{
+ struct semihosting *semihosting = target->semihosting;
+ return target_write_memory(target, semihosting->param,
+ semihosting->word_size_bytes, number, fields);
+}
+
+/**
+ * Extract a field from the buffer, considering register size and endianness.
+ */
+static uint64_t semihosting_get_field(struct target *target, size_t index,
+ uint8_t *fields)
+{
+ struct semihosting *semihosting = target->semihosting;
+ if (semihosting->word_size_bytes == 8)
+ return target_buffer_get_u64(target, fields + (index * 8));
+ else
+ return target_buffer_get_u32(target, fields + (index * 4));
+}
+
+/**
+ * Store a field in the buffer, considering register size and endianness.
+ */
+static void semihosting_set_field(struct target *target, uint64_t value,
+ size_t index,
+ uint8_t *fields)
+{
+ struct semihosting *semihosting = target->semihosting;
+ if (semihosting->word_size_bytes == 8)
+ target_buffer_set_u64(target, fields + (index * 8), value);
+ else
+ target_buffer_set_u32(target, fields + (index * 4), value);
+}
+
+
+/* -------------------------------------------------------------------------
+ * Common semihosting commands handlers. */
+
+__COMMAND_HANDLER(handle_common_semihosting_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+
+ if (target == NULL) {
+ LOG_ERROR("No target selected");
+ return ERROR_FAIL;
+ }
+
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ command_print(CMD_CTX, "semihosting not supported for current target");
+ return ERROR_FAIL;
+ }
+
+ if (CMD_ARGC > 0) {
+ int is_active;
+
+ COMMAND_PARSE_ENABLE(CMD_ARGV[0], is_active);
+
+ if (!target_was_examined(target)) {
+ LOG_ERROR("Target not examined yet");
+ return ERROR_FAIL;
+ }
+
+ if (semihosting && semihosting->setup(target, is_active) != ERROR_OK) {
+ LOG_ERROR("Failed to Configure semihosting");
+ return ERROR_FAIL;
+ }
+
+ /* FIXME never let that "catch" be dropped! (???) */
+ semihosting->is_active = is_active;
+ }
+
+ command_print(CMD_CTX, "semihosting is %s",
+ semihosting->is_active
+ ? "enabled" : "disabled");
+
+ return ERROR_OK;
+}
+
+
+__COMMAND_HANDLER(handle_common_semihosting_fileio_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+
+ if (target == NULL) {
+ LOG_ERROR("No target selected");
+ return ERROR_FAIL;
+ }
+
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ command_print(CMD_CTX, "semihosting not supported for current target");
+ return ERROR_FAIL;
+ }
+
+ if (!semihosting->is_active) {
+ command_print(CMD_CTX, "semihosting not yet enabled for current target");
+ return ERROR_FAIL;
+ }
+
+ if (CMD_ARGC > 0)
+ COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->is_fileio);
+
+ command_print(CMD_CTX, "semihosting fileio is %s",
+ semihosting->is_fileio
+ ? "enabled" : "disabled");
+
+ return ERROR_OK;
+}
+
+__COMMAND_HANDLER(handle_common_semihosting_cmdline)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ unsigned int i;
+
+ if (target == NULL) {
+ LOG_ERROR("No target selected");
+ return ERROR_FAIL;
+ }
+
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ command_print(CMD_CTX, "semihosting not supported for current target");
+ return ERROR_FAIL;
+ }
+
+ free(semihosting->cmdline);
+ semihosting->cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
+
+ for (i = 1; i < CMD_ARGC; i++) {
+ char *cmdline = alloc_printf("%s %s", semihosting->cmdline, CMD_ARGV[i]);
+ if (cmdline == NULL)
+ break;
+ free(semihosting->cmdline);
+ semihosting->cmdline = cmdline;
+ }
+
+ command_print(CMD_CTX, "semihosting command line is [%s]",
+ semihosting->cmdline);
+
+ return ERROR_OK;
+}
+
+__COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+
+ if (target == NULL) {
+ LOG_ERROR("No target selected");
+ return ERROR_FAIL;
+ }
+
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ command_print(CMD_CTX, "semihosting not supported for current target");
+ return ERROR_FAIL;
+ }
+
+ if (!semihosting->is_active) {
+ command_print(CMD_CTX, "semihosting not yet enabled for current target");
+ return ERROR_FAIL;
+ }
+
+ if (CMD_ARGC > 0)
+ COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->has_resumable_exit);
+
+ command_print(CMD_CTX, "semihosting resumable exit is %s",
+ semihosting->has_resumable_exit
+ ? "enabled" : "disabled");
+
+ return ERROR_OK;
+}
diff --git a/src/target/semihosting_common.h b/src/target/semihosting_common.h
new file mode 100644
index 0000000..8fb5e0c
--- /dev/null
+++ b/src/target/semihosting_common.h
@@ -0,0 +1,163 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
+ * Copyright (C) 2009 by Marvell Technology Group Ltd. *
+ * Written by Nicolas Pitre <nico@marvell.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H
+#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+
+/*
+ * According to:
+ * "Semihosting for AArch32 and AArch64, Release 2.0"
+ * https://static.docs.arm.com/100863/0200/semihosting.pdf
+ * from ARM Ltd.
+ *
+ * The available semihosting operation numbers passed in R0 are allocated
+ * as follows:
+ * - 0x00-0x31 Used by ARM.
+ * - 0x32-0xFF Reserved for future use by ARM.
+ * - 0x100-0x1FF Reserved for user applications. These are not used by ARM.
+ * However, if you are writing your own SVC operations, you are advised
+ * to use a different SVC number rather than using the semihosted
+ * SVC number and these operation type numbers.
+ * - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended
+ * that you do not use these.
+ */
+
+enum semihosting_operation_numbers {
+ /*
+ * ARM semihosting operations, in lexicographic order.
+ */
+ SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */
+
+ SEMIHOSTING_SYS_CLOSE = 0x02,
+ SEMIHOSTING_SYS_CLOCK = 0x10,
+ SEMIHOSTING_SYS_ELAPSED = 0x30,
+ SEMIHOSTING_SYS_ERRNO = 0x13,
+ SEMIHOSTING_SYS_EXIT = 0x18,
+ SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
+ SEMIHOSTING_SYS_FLEN = 0x0C,
+ SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
+ SEMIHOSTING_SYS_HEAPINFO = 0x16,
+ SEMIHOSTING_SYS_ISERROR = 0x08,
+ SEMIHOSTING_SYS_ISTTY = 0x09,
+ SEMIHOSTING_SYS_OPEN = 0x01,
+ SEMIHOSTING_SYS_READ = 0x06,
+ SEMIHOSTING_SYS_READC = 0x07,
+ SEMIHOSTING_SYS_REMOVE = 0x0E,
+ SEMIHOSTING_SYS_RENAME = 0x0F,
+ SEMIHOSTING_SYS_SEEK = 0x0A,
+ SEMIHOSTING_SYS_SYSTEM = 0x12,
+ SEMIHOSTING_SYS_TICKFREQ = 0x31,
+ SEMIHOSTING_SYS_TIME = 0x11,
+ SEMIHOSTING_SYS_TMPNAM = 0x0D,
+ SEMIHOSTING_SYS_WRITE = 0x05,
+ SEMIHOSTING_SYS_WRITEC = 0x03,
+ SEMIHOSTING_SYS_WRITE0 = 0x04,
+};
+
+/*
+ * Codes used by SEMIHOSTING_SYS_EXIT (formerly
+ * SEMIHOSTING_REPORT_EXCEPTION).
+ * On 64-bits, the exit code is passed explicitly.
+ */
+enum semihosting_reported_exceptions {
+ /* On 32 bits, use it for exit(0) */
+ ADP_STOPPED_APPLICATION_EXIT = ((2 << 16) + 38),
+ /* On 32 bits, use it for exit(1) */
+ ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35),
+};
+
+struct target;
+
+/*
+ * A pointer to this structure was added to the target structure.
+ */
+struct semihosting {
+
+ /** A flag reporting whether semihosting is active. */
+ bool is_active;
+
+ /** A flag reporting whether semihosting fileio is active. */
+ bool is_fileio;
+
+ /** A flag reporting whether semihosting fileio operation is active. */
+ bool hit_fileio;
+
+ /** Most are resumable, except the two exit calls. */
+ bool is_resumable;
+
+ /**
+ * When SEMIHOSTING_SYS_EXIT is called outside a debug session,
+ * things are simple, the openocd process calls exit() and passes
+ * the value returned by the target.
+ * When SEMIHOSTING_SYS_EXIT is called during a debug session,
+ * by default execution returns to the debugger, leaving the
+ * debugger in a HALT state, similar to the state entered when
+ * encountering a break.
+ * In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT
+ * return normally, as any semihosting call, and do not break
+ * to the debugger.
+ * The standard allows this to happen, but the condition
+ * to trigger it is a bit obscure ("by performing an RDI_Execute
+ * request or equivalent").
+ *
+ * To make the SEMIHOSTING_SYS_EXIT call return normally, enable
+ * this variable via the dedicated command (default: disabled).
+ */
+ bool has_resumable_exit;
+
+ /** The Target (hart) word size; 8 for 64-bits targets. */
+ size_t word_size_bytes;
+
+ /** The current semihosting operation (R0 on ARM). */
+ int op;
+
+ /** The current semihosting parameter (R1 or ARM). */
+ uint64_t param;
+
+ /**
+ * The current semihosting result to be returned to the application.
+ * Usually 0 for success, -1 for error,
+ * but sometimes a useful value, even a pointer.
+ */
+ int64_t result;
+
+ /** The value to be returned by semihosting SYS_ERRNO request. */
+ int sys_errno;
+
+ /** The semihosting command line to be passed to the target. */
+ char *cmdline;
+
+ /** The current time when 'execution starts' */
+ clock_t setup_time;
+
+ int (*setup)(struct target *target, int enable);
+ int (*post_result)(struct target *target);
+};
+
+int semihosting_common_init(struct target *target, void *setup,
+ void *post_result);
+int semihosting_common(struct target *target);
+
+#endif /* OPENOCD_TARGET_SEMIHOSTING_COMMON_H */
diff --git a/src/target/target.c b/src/target/target.c
index 890f727..dc2cedd 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -1895,6 +1895,9 @@ static void target_destroy(struct target *target)
if (target->type->deinit_target)
target->type->deinit_target(target);
+ if (target->semihosting)
+ free(target->semihosting);
+
jtag_unregister_event_callback(jtag_enable_callback, target);
struct target_event_action *teap = target->event_action;
@@ -5448,21 +5451,19 @@ static const struct command_registration target_instance_command_handlers[] = {
.mode = COMMAND_EXEC,
.jim_handler = jim_target_examine,
.help = "used internally for reset processing",
- .usage = "arp_examine ['allow-defer']",
+ .usage = "['allow-defer']",
},
{
.name = "was_examined",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_was_examined,
.help = "used internally for reset processing",
- .usage = "was_examined",
},
{
.name = "examine_deferred",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_examine_deferred,
.help = "used internally for reset processing",
- .usage = "examine_deferred",
},
{
.name = "arp_halt_gdb",
diff --git a/src/target/target.h b/src/target/target.h
index c5fb55b..51a5b69 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -205,6 +205,9 @@ struct target {
/* file-I/O information for host to do syscall */
struct gdb_fileio_info *fileio_info;
+
+ /* The semihosting information, extracted from the target. */
+ struct semihosting *semihosting;
};
struct target_list {
@@ -214,10 +217,10 @@ struct target_list {
struct gdb_fileio_info {
char *identifier;
- uint32_t param_1;
- uint32_t param_2;
- uint32_t param_3;
- uint32_t param_4;
+ uint64_t param_1;
+ uint64_t param_2;
+ uint64_t param_3;
+ uint64_t param_4;
};
/** Returns the instance-specific name of the specified target. */