aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2018-08-29 15:55:30 -0700
committerGitHub <noreply@github.com>2018-08-29 15:55:30 -0700
commit164415cfbe1647b19661f9e4046372dd5df2c204 (patch)
tree299273e5df105d0b16ad42dd1e9554074f4b6676 /src
parent0ed96e80d3f9b8828e6c3ca33ae91b0da6543c33 (diff)
parentb4b2ec7d2d143146226e7b2f06e1399ee560148d (diff)
downloadriscv-openocd-164415cfbe1647b19661f9e4046372dd5df2c204.zip
riscv-openocd-164415cfbe1647b19661f9e4046372dd5df2c204.tar.gz
riscv-openocd-164415cfbe1647b19661f9e4046372dd5df2c204.tar.bz2
Merge branch 'riscv' into sba_tests
Diffstat (limited to 'src')
-rw-r--r--src/flash/nor/Makefile.am11
-rw-r--r--src/flash/nor/ambiqmicro.c1
-rw-r--r--src/flash/nor/at91sam3.c17
-rw-r--r--src/flash/nor/at91sam4.c98
-rw-r--r--src/flash/nor/at91sam4l.c41
-rw-r--r--src/flash/nor/at91sam7.c48
-rw-r--r--src/flash/nor/at91samd.c401
-rw-r--r--src/flash/nor/ath79.c1
-rw-r--r--src/flash/nor/atsamv.c1
-rw-r--r--src/flash/nor/avrf.c1
-rw-r--r--src/flash/nor/bluenrg-x.c554
-rw-r--r--src/flash/nor/cc26xx.c567
-rw-r--r--src/flash/nor/cc26xx.h101
-rw-r--r--src/flash/nor/cc3220sf.c529
-rw-r--r--src/flash/nor/cc3220sf.h45
-rw-r--r--src/flash/nor/cfi.c1
-rw-r--r--src/flash/nor/core.c288
-rw-r--r--src/flash/nor/core.h44
-rw-r--r--src/flash/nor/driver.h8
-rw-r--r--src/flash/nor/drivers.c16
-rw-r--r--src/flash/nor/efm32.c273
-rw-r--r--src/flash/nor/em357.c1
-rw-r--r--src/flash/nor/faux.c3
-rw-r--r--src/flash/nor/fespi.c79
-rw-r--r--src/flash/nor/fm3.c1
-rw-r--r--src/flash/nor/fm4.c1
-rw-r--r--src/flash/nor/jtagspi.c3
-rw-r--r--src/flash/nor/kinetis.c29
-rw-r--r--src/flash/nor/kinetis_ke.c4
-rw-r--r--src/flash/nor/lpc2000.c1
-rw-r--r--src/flash/nor/lpc288x.c1
-rw-r--r--src/flash/nor/lpc2900.c1
-rw-r--r--src/flash/nor/lpcspifi.c1
-rw-r--r--src/flash/nor/mdr.c1
-rw-r--r--src/flash/nor/mrvlqspi.c1
-rw-r--r--src/flash/nor/msp432.c1103
-rw-r--r--src/flash/nor/msp432.h127
-rw-r--r--src/flash/nor/niietcm4.c1
-rw-r--r--src/flash/nor/nrf5.c86
-rw-r--r--src/flash/nor/numicro.c1
-rw-r--r--src/flash/nor/ocl.c1
-rw-r--r--src/flash/nor/pic32mx.c1
-rw-r--r--src/flash/nor/psoc4.c655
-rw-r--r--src/flash/nor/psoc5lp.c1586
-rw-r--r--src/flash/nor/psoc6.c1065
-rw-r--r--src/flash/nor/sim3x.c3
-rw-r--r--src/flash/nor/spi.c1
-rw-r--r--src/flash/nor/stellaris.c1
-rw-r--r--src/flash/nor/stm32f1x.c44
-rw-r--r--src/flash/nor/stm32f2x.c46
-rw-r--r--src/flash/nor/stm32h7x.c56
-rw-r--r--src/flash/nor/stm32l4x.c20
-rw-r--r--src/flash/nor/stm32lx.c7
-rw-r--r--src/flash/nor/stmsmi.c1
-rw-r--r--src/flash/nor/str7x.c1
-rw-r--r--src/flash/nor/str9x.c1
-rw-r--r--src/flash/nor/str9xpec.c1
-rw-r--r--src/flash/nor/tcl.c262
-rw-r--r--src/flash/nor/tms470.c37
-rw-r--r--src/flash/nor/virtual.c6
-rw-r--r--src/flash/nor/xcf.c4
-rw-r--r--src/flash/nor/xmc1xxx.c1
-rw-r--r--src/flash/nor/xmc4xxx.c1
-rw-r--r--src/helper/command.c24
-rw-r--r--src/helper/command.h19
-rw-r--r--src/helper/configuration.c15
-rw-r--r--src/helper/configuration.h2
-rw-r--r--src/helper/fileio.c5
-rw-r--r--src/helper/fileio.h1
-rw-r--r--src/helper/log.h2
-rw-r--r--src/helper/replacements.h2
-rw-r--r--src/helper/types.h17
-rw-r--r--src/jtag/adapter.c16
-rw-r--r--src/jtag/aice/aice_interface.c24
-rw-r--r--src/jtag/aice/aice_interface.h1
-rw-r--r--src/jtag/aice/aice_transport.c60
-rw-r--r--src/jtag/aice/aice_usb.c52
-rw-r--r--src/jtag/aice/aice_usb.h1
-rw-r--r--src/jtag/core.c94
-rw-r--r--src/jtag/drivers/Makefile.am7
-rw-r--r--src/jtag/drivers/buspirate.c509
-rw-r--r--src/jtag/drivers/cmsis_dap_usb.c67
-rw-r--r--src/jtag/drivers/ft232r.c669
-rw-r--r--src/jtag/drivers/ftdi.c12
-rw-r--r--src/jtag/drivers/jlink.c4
-rw-r--r--src/jtag/drivers/jtag_vpi.c15
-rw-r--r--src/jtag/drivers/kitprog.c20
-rw-r--r--src/jtag/drivers/mpsse.c8
-rw-r--r--src/jtag/drivers/stlink_usb.c9
-rw-r--r--src/jtag/drivers/sysfsgpio.c8
-rw-r--r--src/jtag/drivers/usb_blaster/usb_blaster.c41
-rw-r--r--src/jtag/drivers/xds110.c1973
-rw-r--r--src/jtag/hla/hla_interface.c7
-rw-r--r--src/jtag/hla/hla_layout.h2
-rw-r--r--src/jtag/hla/hla_tcl.c15
-rw-r--r--src/jtag/hla/hla_transport.c8
-rw-r--r--src/jtag/interface.h4
-rw-r--r--src/jtag/interfaces.c12
-rw-r--r--src/jtag/jtag.h4
-rw-r--r--src/jtag/swd.h2
-rw-r--r--src/jtag/tcl.c1
-rw-r--r--src/jtag/zy1000/zy1000.c2
-rw-r--r--src/openocd.c33
-rw-r--r--src/rtos/ChibiOS.c9
-rw-r--r--src/rtos/Makefile.am2
-rw-r--r--src/rtos/nuttx.c405
-rw-r--r--src/rtos/nuttx_header.h71
-rw-r--r--src/rtos/riscv_debug.c18
-rw-r--r--src/rtos/rtos.c18
-rw-r--r--src/rtos/rtos.h1
-rw-r--r--src/rtos/rtos_standard_stackings.c23
-rw-r--r--src/server/gdb_server.c438
-rw-r--r--src/server/gdb_server.h2
-rw-r--r--src/server/server.c123
-rw-r--r--src/server/server.h6
-rw-r--r--src/server/tcl_server.c7
-rw-r--r--src/server/tcl_server.h1
-rw-r--r--src/server/telnet_server.c5
-rw-r--r--src/server/telnet_server.h1
-rw-r--r--src/svf/svf.c5
-rw-r--r--src/target/Makefile.am16
-rw-r--r--src/target/aarch64.c326
-rw-r--r--src/target/adi_v5_jtag.c36
-rw-r--r--src/target/adi_v5_swd.c71
-rw-r--r--src/target/arm.h75
-rw-r--r--src/target/arm_adi_v5.c364
-rw-r--r--src/target/arm_adi_v5.h35
-rw-r--r--src/target/arm_cti.c447
-rw-r--r--src/target/arm_cti.h8
-rw-r--r--src/target/arm_dap.c378
-rw-r--r--src/target/arm_disassembler.c82
-rw-r--r--src/target/arm_dpm.c127
-rw-r--r--src/target/arm_opcodes.h24
-rw-r--r--src/target/arm_semihosting.c647
-rw-r--r--src/target/arm_semihosting.h2
-rw-r--r--src/target/armv4_5.c247
-rw-r--r--src/target/armv7a.c53
-rw-r--r--src/target/armv7a.h2
-rw-r--r--src/target/armv7a_cache.c34
-rw-r--r--src/target/armv7m.c144
-rw-r--r--src/target/armv7m.h2
-rw-r--r--src/target/armv7m_trace.c20
-rw-r--r--src/target/armv7m_trace.h16
-rw-r--r--src/target/armv8.c643
-rw-r--r--src/target/armv8.h79
-rw-r--r--src/target/armv8_cache.c9
-rw-r--r--src/target/armv8_dpm.c89
-rw-r--r--src/target/armv8_dpm.h2
-rw-r--r--src/target/armv8_opcodes.h8
-rw-r--r--src/target/breakpoints.c7
-rw-r--r--src/target/cortex_a.c94
-rw-r--r--src/target/cortex_m.c127
-rw-r--r--src/target/cortex_m.h1
-rw-r--r--src/target/hla_target.c35
-rw-r--r--src/target/image.c596
-rw-r--r--src/target/mips32.c17
-rw-r--r--src/target/mips32.h2
-rw-r--r--src/target/mips_m4k.c2
-rw-r--r--src/target/nds32.c73
-rw-r--r--src/target/openrisc/jsp_server.c4
-rw-r--r--src/target/openrisc/jsp_server.h1
-rw-r--r--src/target/register.h3
-rw-r--r--src/target/riscv/Makefile.am16
-rw-r--r--src/target/riscv/batch.c5
-rw-r--r--src/target/riscv/debug_defines.h368
-rw-r--r--src/target/riscv/encoding.h24
-rw-r--r--src/target/riscv/riscv-011.c35
-rw-r--r--src/target/riscv/riscv-013.c551
-rw-r--r--src/target/riscv/riscv.c595
-rw-r--r--src/target/riscv/riscv.h32
-rw-r--r--src/target/riscv/riscv_semihosting.c194
-rw-r--r--src/target/semihosting_common.c1595
-rw-r--r--src/target/semihosting_common.h163
-rw-r--r--src/target/startup.tcl1
-rw-r--r--src/target/stm8.c16
-rw-r--r--src/target/target.c184
-rw-r--r--src/target/target.h39
-rw-r--r--src/target/target_type.h5
-rw-r--r--src/transport/transport.h16
179 files changed, 18287 insertions, 3694 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index cc72088..9a58239 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -18,6 +18,9 @@ NOR_DRIVERS = \
%D%/ath79.c \
%D%/atsamv.c \
%D%/avrf.c \
+ %D%/bluenrg-x.c \
+ %D%/cc3220sf.c \
+ %D%/cc26xx.c \
%D%/cfi.c \
%D%/dsp5680xx_flash.c \
%D%/efm32.c \
@@ -34,6 +37,7 @@ NOR_DRIVERS = \
%D%/lpc2900.c \
%D%/lpcspifi.c \
%D%/mdr.c \
+ %D%/msp432.c \
%D%/mrvlqspi.c \
%D%/niietcm4.c \
%D%/non_cfi.c \
@@ -42,6 +46,8 @@ NOR_DRIVERS = \
%D%/ocl.c \
%D%/pic32mx.c \
%D%/psoc4.c \
+ %D%/psoc5lp.c \
+ %D%/psoc6.c \
%D%/sim3x.c \
%D%/spi.c \
%D%/stmsmi.c \
@@ -62,9 +68,12 @@ NOR_DRIVERS = \
NORHEADERS = \
%D%/core.h \
+ %D%/cc3220sf.h \
+ %D%/cc26xx.h \
%D%/cfi.h \
%D%/driver.h \
%D%/imp.h \
%D%/non_cfi.h \
%D%/ocl.h \
- %D%/spi.h
+ %D%/spi.h \
+ %D%/msp432.h
diff --git a/src/flash/nor/ambiqmicro.c b/src/flash/nor/ambiqmicro.c
index b2c30e6..13b2b26 100644
--- a/src/flash/nor/ambiqmicro.c
+++ b/src/flash/nor/ambiqmicro.c
@@ -901,4 +901,5 @@ struct flash_driver ambiqmicro_flash = {
.erase_check = default_flash_blank_check,
.protect_check = ambiqmicro_protect_check,
.info = get_ambiqmicro_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/at91sam3.c b/src/flash/nor/at91sam3.c
index 1536378..d80b6fe 100644
--- a/src/flash/nor/at91sam3.c
+++ b/src/flash/nor/at91sam3.c
@@ -3117,6 +3117,22 @@ FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command)
return ERROR_OK;
}
+/**
+ * Remove all chips from the internal list without distingushing which one
+ * is owned by this bank. This simplification works only for one shot
+ * deallocation like current flash_free_all_banks()
+ */
+void sam3_free_driver_priv(struct flash_bank *bank)
+{
+ struct sam3_chip *chip = all_sam3_chips;
+ while (chip) {
+ struct sam3_chip *next = chip->next;
+ free(chip);
+ chip = next;
+ }
+ all_sam3_chips = NULL;
+}
+
static int sam3_GetDetails(struct sam3_bank_private *pPrivate)
{
const struct sam3_chip_details *pDetails;
@@ -3771,4 +3787,5 @@ struct flash_driver at91sam3_flash = {
.auto_probe = sam3_auto_probe,
.erase_check = sam3_erase_check,
.protect_check = sam3_protect_check,
+ .free_driver_priv = sam3_free_driver_priv,
};
diff --git a/src/flash/nor/at91sam4.c b/src/flash/nor/at91sam4.c
index d101c9b..c5b31e9 100644
--- a/src/flash/nor/at91sam4.c
+++ b/src/flash/nor/at91sam4.c
@@ -682,6 +682,40 @@ static const struct sam4_chip_details all_sam4_details[] = {
},
},
},
+ /*at91sam4sa16c - TFBGA100/VFBGA100/LQFP100*/
+ {
+ .chipid_cidr = 0x28a70ce0,
+ .name = "at91sam4sa16c",
+ .total_flash_size = 1024 * 1024,
+ .total_sram_size = 160 * 1024,
+ .n_gpnvms = 2,
+ .n_banks = 1,
+
+/* .bank[0] = { */
+ {
+ {
+ .probed = 0,
+ .pChip = NULL,
+ .pBank = NULL,
+ .bank_number = 0,
+ .base_address = FLASH_BANK_BASE_S,
+ .controller_address = 0x400e0a00,
+ .flash_wait_states = 5,
+ .present = 1,
+ .size_bytes = 1024 * 1024,
+ .nsectors = 128,
+ .sector_size = 8192,
+ .page_size = 512,
+ },
+/* .bank[1] = {*/
+ {
+ .present = 0,
+ .probed = 0,
+ .bank_number = 1,
+
+ },
+ },
+ },
/*atsam4s16b - LQFP64/QFN64/WLCSP64*/
{
.chipid_cidr = 0x289C0CE0,
@@ -1261,50 +1295,6 @@ static const struct sam4_chip_details all_sam4_details[] = {
},
},
- /*at91sam4sa16c*/
- {
- .chipid_cidr = 0x28a70ce0,
- .name = "at91sam4sa16c",
- .total_flash_size = 1024 * 1024,
- .total_sram_size = 160 * 1024,
- .n_gpnvms = 3,
- .n_banks = 2,
-
-/* .bank[0] = { */
- {
- {
- .probed = 0,
- .pChip = NULL,
- .pBank = NULL,
- .bank_number = 0,
- .base_address = FLASH_BANK0_BASE_SD,
- .controller_address = 0x400e0a00,
- .flash_wait_states = 5,
- .present = 1,
- .size_bytes = 512 * 1024,
- .nsectors = 64,
- .sector_size = 8192,
- .page_size = 512,
- },
-
-/* .bank[1] = { */
- {
- .probed = 0,
- .pChip = NULL,
- .pBank = NULL,
- .bank_number = 1,
- .base_address = FLASH_BANK1_BASE_1024K_SD,
- .controller_address = 0x400e0c00,
- .flash_wait_states = 5,
- .present = 1,
- .size_bytes = 512 * 1024,
- .nsectors = 64,
- .sector_size = 8192,
- .page_size = 512,
- },
- },
- },
-
/* atsamg53n19 */
{
.chipid_cidr = 0x247e0ae0,
@@ -2514,6 +2504,22 @@ FLASH_BANK_COMMAND_HANDLER(sam4_flash_bank_command)
return ERROR_OK;
}
+/**
+ * Remove all chips from the internal list without distingushing which one
+ * is owned by this bank. This simplification works only for one shot
+ * deallocation like current flash_free_all_banks()
+ */
+static void sam4_free_driver_priv(struct flash_bank *bank)
+{
+ struct sam4_chip *chip = all_sam4_chips;
+ while (chip) {
+ struct sam4_chip *next = chip->next;
+ free(chip);
+ chip = next;
+ }
+ all_sam4_chips = NULL;
+}
+
static int sam4_GetDetails(struct sam4_bank_private *pPrivate)
{
const struct sam4_chip_details *pDetails;
@@ -2538,6 +2544,8 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate)
pPrivate->pChip->cfg.CHIPID_CIDR);
sam4_explain_chipid_cidr(pPrivate->pChip);
return ERROR_FAIL;
+ } else {
+ LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr);
}
/* DANGER: THERE ARE DRAGONS HERE */
@@ -2608,6 +2616,7 @@ static int _sam4_probe(struct flash_bank *bank, int noise)
for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) {
if (bank->base == pPrivate->pChip->details.bank[x].base_address) {
bank->size = pPrivate->pChip->details.bank[x].size_bytes;
+ LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x);
break;
}
}
@@ -3194,4 +3203,5 @@ struct flash_driver at91sam4_flash = {
.auto_probe = sam4_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = sam4_protect_check,
+ .free_driver_priv = sam4_free_driver_priv,
};
diff --git a/src/flash/nor/at91sam4l.c b/src/flash/nor/at91sam4l.c
index 0a605d5..794ccbb 100644
--- a/src/flash/nor/at91sam4l.c
+++ b/src/flash/nor/at91sam4l.c
@@ -129,10 +129,8 @@ struct sam4l_info {
bool probed;
struct target *target;
- struct sam4l_info *next;
};
-static struct sam4l_info *sam4l_chips;
static int sam4l_flash_wait_until_ready(struct target *target)
{
@@ -204,30 +202,6 @@ static int sam4l_flash_command(struct target *target, uint8_t cmd, int page)
FLASH_BANK_COMMAND_HANDLER(sam4l_flash_bank_command)
{
- struct sam4l_info *chip = sam4l_chips;
-
- while (chip) {
- if (chip->target == bank->target)
- break;
- chip = chip->next;
- }
-
- if (!chip) {
- /* Create a new chip */
- chip = calloc(1, sizeof(*chip));
- if (!chip)
- return ERROR_FAIL;
-
- chip->target = bank->target;
- chip->probed = false;
-
- bank->driver_priv = chip;
-
- /* Insert it into the chips list (at head) */
- chip->next = sam4l_chips;
- sam4l_chips = chip;
- }
-
if (bank->base != SAM4L_FLASH) {
LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32
"[at91sam4l series] )",
@@ -235,6 +209,18 @@ FLASH_BANK_COMMAND_HANDLER(sam4l_flash_bank_command)
return ERROR_FAIL;
}
+ struct sam4l_info *chip;
+ chip = calloc(1, sizeof(*chip));
+ if (!chip) {
+ LOG_ERROR("No memory for flash bank chip info");
+ return ERROR_FAIL;
+ }
+
+ chip->target = bank->target;
+ chip->probed = false;
+
+ bank->driver_priv = chip;
+
return ERROR_OK;
}
@@ -396,7 +382,7 @@ static int sam4l_protect_check(struct flash_bank *bank)
static int sam4l_protect(struct flash_bank *bank, int set, int first, int last)
{
- struct sam4l_info *chip = sam4l_chips;
+ struct sam4l_info *chip = (struct sam4l_info *)bank->driver_priv;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
@@ -709,4 +695,5 @@ struct flash_driver at91sam4l_flash = {
.auto_probe = sam4l_probe,
.erase_check = default_flash_blank_check,
.protect_check = sam4l_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/at91sam7.c b/src/flash/nor/at91sam7.c
index 03f771c..9de8293 100644
--- a/src/flash/nor/at91sam7.c
+++ b/src/flash/nor/at91sam7.c
@@ -639,14 +639,6 @@ static int at91sam7_read_part_info(struct flash_bank *bank)
static int at91sam7_erase_check(struct flash_bank *bank)
{
- struct target *target = bank->target;
- uint16_t retval;
- uint32_t blank;
- uint16_t fast_check;
- uint8_t *buffer;
- uint16_t nSector;
- uint16_t nByte;
-
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
@@ -656,45 +648,7 @@ static int at91sam7_erase_check(struct flash_bank *bank)
at91sam7_read_clock_info(bank);
at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH);
- fast_check = 1;
- for (nSector = 0; nSector < bank->num_sectors; nSector++) {
- retval = target_blank_check_memory(target,
- bank->base + bank->sectors[nSector].offset,
- bank->sectors[nSector].size,
- &blank, bank->erased_value);
- if (retval != ERROR_OK) {
- fast_check = 0;
- break;
- }
- if (blank == 0xFF)
- bank->sectors[nSector].is_erased = 1;
- else
- bank->sectors[nSector].is_erased = 0;
- }
-
- if (fast_check)
- return ERROR_OK;
-
- LOG_USER("Running slow fallback erase check - add working memory");
-
- buffer = malloc(bank->sectors[0].size);
- for (nSector = 0; nSector < bank->num_sectors; nSector++) {
- bank->sectors[nSector].is_erased = 1;
- retval = target_read_memory(target, bank->base + bank->sectors[nSector].offset, 4,
- bank->sectors[nSector].size/4, buffer);
- if (retval != ERROR_OK)
- return retval;
-
- for (nByte = 0; nByte < bank->sectors[nSector].size; nByte++) {
- if (buffer[nByte] != 0xFF) {
- bank->sectors[nSector].is_erased = 0;
- break;
- }
- }
- }
- free(buffer);
-
- return ERROR_OK;
+ return default_flash_blank_check(bank);
}
static int at91sam7_protect_check(struct flash_bank *bank)
diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c
index ad88c51..017d144 100644
--- a/src/flash/nor/at91samd.c
+++ b/src/flash/nor/at91samd.c
@@ -83,6 +83,9 @@
#define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F))
#define SAMD_GET_DEVSEL(id) (id & 0xFF)
+/* Bits to mask out lockbits in user row */
+#define NVMUSERROW_LOCKBIT_MASK ((uint64_t)0x0000FFFFFFFFFFFF)
+
struct samd_part {
uint8_t id;
const char *name;
@@ -112,15 +115,16 @@ static const struct samd_part samd10_parts[] = {
/* Known SAMD11 parts */
static const struct samd_part samd11_parts[] = {
- { 0x0, "SAMD11D14AMU", 16, 4 },
+ { 0x0, "SAMD11D14AM", 16, 4 },
{ 0x1, "SAMD11D13AMU", 8, 4 },
{ 0x2, "SAMD11D12AMU", 4, 4 },
- { 0x3, "SAMD11D14ASU", 16, 4 },
+ { 0x3, "SAMD11D14ASS", 16, 4 },
{ 0x4, "SAMD11D13ASU", 8, 4 },
{ 0x5, "SAMD11D12ASU", 4, 4 },
{ 0x6, "SAMD11C14A", 16, 4 },
{ 0x7, "SAMD11C13A", 8, 4 },
{ 0x8, "SAMD11C12A", 4, 4 },
+ { 0x9, "SAMD11D14AU", 16, 4 },
};
/* Known SAMD20 parts. See Table 12-8 in 42129F–SAM–10/2013 */
@@ -159,23 +163,22 @@ static const struct samd_part samd21_parts[] = {
{ 0xC, "SAMD21E16A", 64, 8 },
{ 0xD, "SAMD21E15A", 32, 4 },
{ 0xE, "SAMD21E14A", 16, 2 },
- /* Below are B Variants (Table 3-7 from rev I of datasheet) */
- { 0x20, "SAMD21J16B", 64, 8 },
- { 0x21, "SAMD21J15B", 32, 4 },
- { 0x23, "SAMD21G16B", 64, 8 },
- { 0x24, "SAMD21G15B", 32, 4 },
- { 0x26, "SAMD21E16B", 64, 8 },
- { 0x27, "SAMD21E15B", 32, 4 },
-};
-/* Known SAMR21 parts. */
-static const struct samd_part samr21_parts[] = {
+ /* SAMR21 parts have integrated SAMD21 with a radio */
{ 0x19, "SAMR21G18A", 256, 32 },
{ 0x1A, "SAMR21G17A", 128, 32 },
{ 0x1B, "SAMR21G16A", 64, 32 },
{ 0x1C, "SAMR21E18A", 256, 32 },
{ 0x1D, "SAMR21E17A", 128, 32 },
{ 0x1E, "SAMR21E16A", 64, 32 },
+
+ /* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */
+ { 0x20, "SAMD21J16B", 64, 8 },
+ { 0x21, "SAMD21J15B", 32, 4 },
+ { 0x23, "SAMD21G16B", 64, 8 },
+ { 0x24, "SAMD21G15B", 32, 4 },
+ { 0x26, "SAMD21E16B", 64, 8 },
+ { 0x27, "SAMD21E15B", 32, 4 },
};
/* Known SAML21 parts. */
@@ -200,6 +203,10 @@ static const struct samd_part saml21_parts[] = {
{ 0x1A, "SAML21E17B", 128, 16 },
{ 0x1B, "SAML21E16B", 64, 8 },
{ 0x1C, "SAML21E15B", 32, 4 },
+
+ /* SAMR30 parts have integrated SAML21 with a radio */
+ { 0x1E, "SAMR30G18A", 256, 32 },
+ { 0x1F, "SAMR30E18A", 256, 32 },
};
/* Known SAML22 parts. */
@@ -256,30 +263,38 @@ struct samd_family {
uint8_t series;
const struct samd_part *parts;
size_t num_parts;
+ uint64_t nvm_userrow_res_mask; /* protect bits which are reserved, 0 -> protect */
};
/* Known SAMD families */
static const struct samd_family samd_families[] = {
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20,
- samd20_parts, ARRAY_SIZE(samd20_parts) },
- { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21,
- samd21_parts, ARRAY_SIZE(samd21_parts) },
+ samd20_parts, ARRAY_SIZE(samd20_parts),
+ (uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21,
- samr21_parts, ARRAY_SIZE(samr21_parts) },
+ samd21_parts, ARRAY_SIZE(samd21_parts),
+ (uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09,
- samd09_parts, ARRAY_SIZE(samd09_parts) },
+ samd09_parts, ARRAY_SIZE(samd09_parts),
+ (uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10,
- samd10_parts, ARRAY_SIZE(samd10_parts) },
+ samd10_parts, ARRAY_SIZE(samd10_parts),
+ (uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11,
- samd11_parts, ARRAY_SIZE(samd11_parts) },
+ samd11_parts, ARRAY_SIZE(samd11_parts),
+ (uint64_t)0xFFFF01FFFE01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_21,
- saml21_parts, ARRAY_SIZE(saml21_parts) },
+ saml21_parts, ARRAY_SIZE(saml21_parts),
+ (uint64_t)0xFFFF03FFFC01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_22,
- saml22_parts, ARRAY_SIZE(saml22_parts) },
+ saml22_parts, ARRAY_SIZE(saml22_parts),
+ (uint64_t)0xFFFF03FFFC01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_20,
- samc20_parts, ARRAY_SIZE(samc20_parts) },
+ samc20_parts, ARRAY_SIZE(samc20_parts),
+ (uint64_t)0xFFFF03FFFC01FF77 },
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_21,
- samc21_parts, ARRAY_SIZE(samc21_parts) },
+ samc21_parts, ARRAY_SIZE(samc21_parts),
+ (uint64_t)0xFFFF03FFFC01FF77 },
};
struct samd_info {
@@ -290,29 +305,45 @@ struct samd_info {
bool probed;
struct target *target;
- struct samd_info *next;
};
-static struct samd_info *samd_chips;
-
-
-static const struct samd_part *samd_find_part(uint32_t id)
+/**
+ * Gives the family structure to specific device id.
+ * @param id The id of the device.
+ * @return On failure NULL, otherwise a pointer to the structure.
+ */
+static const struct samd_family *samd_find_family(uint32_t id)
{
uint8_t processor = SAMD_GET_PROCESSOR(id);
uint8_t family = SAMD_GET_FAMILY(id);
uint8_t series = SAMD_GET_SERIES(id);
- uint8_t devsel = SAMD_GET_DEVSEL(id);
for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) {
if (samd_families[i].processor == processor &&
samd_families[i].series == series &&
- samd_families[i].family == family) {
- for (unsigned j = 0; j < samd_families[i].num_parts; j++) {
- if (samd_families[i].parts[j].id == devsel)
- return &samd_families[i].parts[j];
- }
- }
+ samd_families[i].family == family)
+ return &samd_families[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * Gives the part structure to specific device id.
+ * @param id The id of the device.
+ * @return On failure NULL, otherwise a pointer to the structure.
+ */
+static const struct samd_part *samd_find_part(uint32_t id)
+{
+ uint8_t devsel = SAMD_GET_DEVSEL(id);
+ const struct samd_family *family = samd_find_family(id);
+ if (family == NULL)
+ return NULL;
+
+ for (unsigned i = 0; i < family->num_parts; i++) {
+ if (family->parts[i].id == devsel)
+ return &family->parts[i];
}
return NULL;
@@ -483,6 +514,12 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
return samd_check_error(target);
}
+/**
+ * Erases a flash-row at the given address.
+ * @param target Pointer to the target structure.
+ * @param address The address of the row.
+ * @return On success ERROR_OK, on failure an errorcode.
+ */
static int samd_erase_row(struct target *target, uint32_t address)
{
int res;
@@ -504,49 +541,62 @@ static int samd_erase_row(struct target *target, uint32_t address)
return ERROR_OK;
}
-static bool is_user_row_reserved_bit(uint8_t bit)
+/**
+ * Returns the bitmask of reserved bits in register.
+ * @param target Pointer to the target structure.
+ * @param mask Bitmask, 0 -> value stays untouched.
+ * @return On success ERROR_OK, on failure an errorcode.
+ */
+static int samd_get_reservedmask(struct target *target, uint64_t *mask)
{
- /* See Table 9-3 in the SAMD20 datasheet for more information. */
- switch (bit) {
- /* Reserved bits */
- case 3:
- case 7:
- /* Voltage regulator internal configuration with default value of 0x70,
- * may not be changed. */
- case 17 ... 24:
- /* 41 is voltage regulator internal configuration and must not be
- * changed. 42 through 47 are reserved. */
- case 41 ... 47:
- return true;
- default:
- break;
+ int res;
+ /* Get the devicetype */
+ uint32_t id;
+ res = target_read_u32(target, SAMD_DSU + SAMD_DSU_DID, &id);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't read Device ID register");
+ return res;
}
+ const struct samd_family *family;
+ family = samd_find_family(id);
+ if (family == NULL) {
+ LOG_ERROR("Couldn't determine device family");
+ return ERROR_FAIL;
+ }
+ *mask = family->nvm_userrow_res_mask;
+ return ERROR_OK;
+}
- return false;
+static int read_userrow(struct target *target, uint64_t *userrow)
+{
+ int res;
+ uint8_t buffer[8];
+
+ res = target_read_memory(target, SAMD_USER_ROW, 4, 2, buffer);
+ if (res != ERROR_OK)
+ return res;
+
+ *userrow = target_buffer_get_u64(target, buffer);
+ return ERROR_OK;
}
-/* Modify the contents of the User Row in Flash. These are described in Table
- * 9-3 of the SAMD20 datasheet. The User Row itself has a size of one page
- * and contains a combination of "fuses" and calibration data in bits 24:17.
- * We therefore try not to erase the row's contents unless we absolutely have
- * to and we don't permit modifying reserved bits. */
-static int samd_modify_user_row(struct target *target, uint32_t value,
- uint8_t startb, uint8_t endb)
+/**
+ * Modify the contents of the User Row in Flash. The User Row itself
+ * has a size of one page and contains a combination of "fuses" and
+ * calibration data. Bits which have a value of zero in the mask will
+ * not be changed. Up to now devices only use the first 64 bits.
+ * @param target Pointer to the target structure.
+ * @param value_input The value to write.
+ * @param value_mask Bitmask, 0 -> value stays untouched.
+ * @return On success ERROR_OK, on failure an errorcode.
+ */
+static int samd_modify_user_row_masked(struct target *target,
+ uint64_t value_input, uint64_t value_mask)
{
int res;
uint32_t nvm_ctrlb;
bool manual_wp = true;
- if (is_user_row_reserved_bit(startb) || is_user_row_reserved_bit(endb)) {
- LOG_ERROR("Can't modify bits in the requested range");
- return ERROR_FAIL;
- }
-
- /* Check if we need to do manual page write commands */
- res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
- if (res == ERROR_OK)
- manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0;
-
/* Retrieve the MCU's page size, in bytes. This is also the size of the
* entire User Row. */
uint32_t page_size;
@@ -556,44 +606,49 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
return res;
}
- /* Make sure the size is sane before we allocate. */
- assert(page_size > 0 && page_size <= SAMD_PAGE_SIZE_MAX);
-
- /* Make sure we're within the single page that comprises the User Row. */
- if (startb >= (page_size * 8) || endb >= (page_size * 8)) {
- LOG_ERROR("Can't modify bits outside the User Row page range");
- return ERROR_FAIL;
- }
-
- uint8_t *buf = malloc(page_size);
- if (!buf)
- return ERROR_FAIL;
+ /* Make sure the size is sane. */
+ assert(page_size <= SAMD_PAGE_SIZE_MAX &&
+ page_size >= sizeof(value_input));
+ uint8_t buf[SAMD_PAGE_SIZE_MAX];
/* Read the user row (comprising one page) by words. */
res = target_read_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
if (res != ERROR_OK)
- goto out_user_row;
+ return res;
+
+ uint64_t value_device;
+ res = read_userrow(target, &value_device);
+ if (res != ERROR_OK)
+ return res;
+ uint64_t value_new = (value_input & value_mask) | (value_device & ~value_mask);
/* We will need to erase before writing if the new value needs a '1' in any
* position for which the current value had a '0'. Otherwise we can avoid
* erasing. */
- uint32_t cur = buf_get_u32(buf, startb, endb - startb + 1);
- if ((~cur) & value) {
+ if ((~value_device) & value_new) {
res = samd_erase_row(target, SAMD_USER_ROW);
if (res != ERROR_OK) {
LOG_ERROR("Couldn't erase user row");
- goto out_user_row;
+ return res;
}
}
/* Modify */
- buf_set_u32(buf, startb, endb - startb + 1, value);
+ target_buffer_set_u64(target, buf, value_new);
/* Write the page buffer back out to the target. */
res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
if (res != ERROR_OK)
- goto out_user_row;
+ return res;
+ /* Check if we need to do manual page write commands */
+ res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
+ if (res == ERROR_OK)
+ manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0;
+ else {
+ LOG_ERROR("Read of NVM register CTRKB failed.");
+ return ERROR_FAIL;
+ }
if (manual_wp) {
/* Trigger flash write */
res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_WAP);
@@ -601,12 +656,28 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
res = samd_check_error(target);
}
-out_user_row:
- free(buf);
-
return res;
}
+/**
+ * Modifies the user row register to the given value.
+ * @param target Pointer to the target structure.
+ * @param value The value to write.
+ * @param startb The bit-offset by which the given value is shifted.
+ * @param endb The bit-offset of the last bit in value to write.
+ * @return On success ERROR_OK, on failure an errorcode.
+ */
+static int samd_modify_user_row(struct target *target, uint64_t value,
+ uint8_t startb, uint8_t endb)
+{
+ uint64_t mask = 0;
+ int i;
+ for (i = startb ; i <= endb ; i++)
+ mask |= ((uint64_t)1) << i;
+
+ return samd_modify_user_row_masked(target, value << startb, mask);
+}
+
static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl)
{
int res = ERROR_OK;
@@ -643,7 +714,8 @@ static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int
* corresponding to Sector 15. A '1' means unlocked and a '0' means
* locked. See Table 9-3 in the SAMD20 datasheet for more details. */
- res = samd_modify_user_row(bank->target, set ? 0x0000 : 0xFFFF,
+ res = samd_modify_user_row(bank->target,
+ set ? (uint64_t)0 : (uint64_t)UINT64_MAX,
48 + first_prot_bl, 48 + last_prot_bl);
if (res != ERROR_OK)
LOG_WARNING("SAMD: protect settings were not made persistent!");
@@ -803,30 +875,6 @@ free_pb:
FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command)
{
- struct samd_info *chip = samd_chips;
-
- while (chip) {
- if (chip->target == bank->target)
- break;
- chip = chip->next;
- }
-
- if (!chip) {
- /* Create a new chip */
- chip = calloc(1, sizeof(*chip));
- if (!chip)
- return ERROR_FAIL;
-
- chip->target = bank->target;
- chip->probed = false;
-
- bank->driver_priv = chip;
-
- /* Insert it into the chips list (at head) */
- chip->next = samd_chips;
- samd_chips = chip;
- }
-
if (bank->base != SAMD_FLASH) {
LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32
"[at91samd series] )",
@@ -834,6 +882,18 @@ FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command)
return ERROR_FAIL;
}
+ struct samd_info *chip;
+ chip = calloc(1, sizeof(*chip));
+ if (!chip) {
+ LOG_ERROR("No memory for flash bank chip info");
+ return ERROR_FAIL;
+ }
+
+ chip->target = bank->target;
+ chip->probed = false;
+
+ bank->driver_priv = chip;
+
return ERROR_OK;
}
@@ -944,6 +1004,83 @@ COMMAND_HANDLER(samd_handle_eeprom_command)
return res;
}
+static COMMAND_HELPER(get_u64_from_hexarg, unsigned int num, uint64_t *value)
+{
+ if (num >= CMD_ARGC) {
+ command_print(CMD_CTX, "Too few Arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (strlen(CMD_ARGV[num]) >= 3 &&
+ CMD_ARGV[num][0] == '0' &&
+ CMD_ARGV[num][1] == 'x') {
+ char *check = NULL;
+ *value = strtoull(&(CMD_ARGV[num][2]), &check, 16);
+ if ((value == 0 && errno == ERANGE) ||
+ check == NULL || *check != 0) {
+ command_print(CMD_CTX, "Invalid 64-bit hex value in argument %d.",
+ num + 1);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ } else {
+ command_print(CMD_CTX, "Argument %d needs to be a hex value.", num + 1);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(samd_handle_nvmuserrow_command)
+{
+ int res = ERROR_OK;
+ struct target *target = get_current_target(CMD_CTX);
+
+ if (target) {
+ if (CMD_ARGC > 2) {
+ command_print(CMD_CTX, "Too much Arguments given.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (CMD_ARGC > 0) {
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted.");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ uint64_t mask;
+ res = samd_get_reservedmask(target, &mask);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't determine the mask for reserved bits.");
+ return ERROR_FAIL;
+ }
+ mask &= NVMUSERROW_LOCKBIT_MASK;
+
+ uint64_t value;
+ res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 0, &value);
+ if (res != ERROR_OK)
+ return res;
+ if (CMD_ARGC == 2) {
+ uint64_t mask_temp;
+ res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 1, &mask_temp);
+ if (res != ERROR_OK)
+ return res;
+ mask &= mask_temp;
+ }
+ res = samd_modify_user_row_masked(target, value, mask);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ /* read register */
+ uint64_t value;
+ res = read_userrow(target, &value);
+ if (res == ERROR_OK)
+ command_print(CMD_CTX, "NVMUSERROW: 0x%016"PRIX64, value);
+ else
+ LOG_ERROR("NVMUSERROW could not be read.");
+ }
+ return res;
+}
+
COMMAND_HANDLER(samd_handle_bootloader_command)
{
int res = ERROR_OK;
@@ -1049,29 +1186,29 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.name = "dsu_reset_deassert",
.handler = samd_handle_reset_deassert,
.mode = COMMAND_EXEC,
- .help = "deasert internal reset held by DSU"
+ .help = "Deasert internal reset held by DSU."
},
{
.name = "info",
.handler = samd_handle_info_command,
.mode = COMMAND_EXEC,
- .help = "Print information about the current at91samd chip"
+ .help = "Print information about the current at91samd chip "
"and its flash configuration.",
},
{
.name = "chip-erase",
.handler = samd_handle_chip_erase_command,
.mode = COMMAND_EXEC,
- .help = "Erase the entire Flash by using the Chip"
+ .help = "Erase the entire Flash by using the Chip-"
"Erase feature in the Device Service Unit (DSU).",
},
{
.name = "set-security",
.handler = samd_handle_set_security_command,
.mode = COMMAND_EXEC,
- .help = "Secure the chip's Flash by setting the Security Bit."
- "This makes it impossible to read the Flash contents."
- "The only way to undo this is to issue the chip-erase"
+ .help = "Secure the chip's Flash by setting the Security Bit. "
+ "This makes it impossible to read the Flash contents. "
+ "The only way to undo this is to issue the chip-erase "
"command.",
},
{
@@ -1079,9 +1216,9 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.usage = "[size_in_bytes]",
.handler = samd_handle_eeprom_command,
.mode = COMMAND_EXEC,
- .help = "Show or set the EEPROM size setting, stored in the User Row."
- "Please see Table 20-3 of the SAMD20 datasheet for allowed values."
- "Changes are stored immediately but take affect after the MCU is"
+ .help = "Show or set the EEPROM size setting, stored in the User Row. "
+ "Please see Table 20-3 of the SAMD20 datasheet for allowed values. "
+ "Changes are stored immediately but take affect after the MCU is "
"reset.",
},
{
@@ -1089,11 +1226,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.usage = "[size_in_bytes]",
.handler = samd_handle_bootloader_command,
.mode = COMMAND_EXEC,
- .help = "Show or set the bootloader size, stored in the User Row."
- "Please see Table 20-2 of the SAMD20 datasheet for allowed values."
- "Changes are stored immediately but take affect after the MCU is"
+ .help = "Show or set the bootloader size, stored in the User Row. "
+ "Please see Table 20-2 of the SAMD20 datasheet for allowed values. "
+ "Changes are stored immediately but take affect after the MCU is "
"reset.",
},
+ {
+ .name = "nvmuserrow",
+ .usage = "[value] [mask]",
+ .handler = samd_handle_nvmuserrow_command,
+ .mode = COMMAND_EXEC,
+ .help = "Show or set the nvmuserrow register. It is 64 bit wide "
+ "and located at address 0x804000. Use the optional mask argument "
+ "to prevent changes at positions where the bitvalue is zero. "
+ "For security reasons the lock- and reserved-bits are masked out "
+ "in background and therefore cannot be changed.",
+ },
COMMAND_REGISTRATION_DONE
};
@@ -1120,4 +1268,5 @@ struct flash_driver at91samd_flash = {
.auto_probe = samd_probe,
.erase_check = default_flash_blank_check,
.protect_check = samd_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/ath79.c b/src/flash/nor/ath79.c
index 451e843..c5f9eed 100644
--- a/src/flash/nor/ath79.c
+++ b/src/flash/nor/ath79.c
@@ -898,4 +898,5 @@ struct flash_driver ath79_flash = {
.erase_check = ath79_flash_blank_check,
.protect_check = ath79_protect_check,
.info = get_ath79_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/atsamv.c b/src/flash/nor/atsamv.c
index 73f0238..9c07bdf 100644
--- a/src/flash/nor/atsamv.c
+++ b/src/flash/nor/atsamv.c
@@ -739,4 +739,5 @@ struct flash_driver atsamv_flash = {
.erase_check = default_flash_blank_check,
.protect_check = samv_protect_check,
.info = samv_get_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/avrf.c b/src/flash/nor/avrf.c
index 11cc3b2..65ac601 100644
--- a/src/flash/nor/avrf.c
+++ b/src/flash/nor/avrf.c
@@ -487,4 +487,5 @@ struct flash_driver avr_flash = {
.erase_check = default_flash_blank_check,
.protect_check = avrf_protect_check,
.info = avrf_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/bluenrg-x.c b/src/flash/nor/bluenrg-x.c
new file mode 100644
index 0000000..2b56859
--- /dev/null
+++ b/src/flash/nor/bluenrg-x.c
@@ -0,0 +1,554 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Michele Sardo *
+ * msmttchr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+#include <target/cortex_m.h>
+#include "imp.h"
+
+#define FLASH_SIZE_REG (0x40100014)
+#define DIE_ID_REG (0x4090001C)
+#define JTAG_IDCODE_REG (0x40900028)
+#define BLUENRG2_IDCODE (0x0200A041)
+#define FLASH_BASE (0x10040000)
+#define FLASH_PAGE_SIZE (2048)
+#define FLASH_REG_COMMAND (0x40100000)
+#define FLASH_REG_IRQRAW (0x40100010)
+#define FLASH_REG_ADDRESS (0x40100018)
+#define FLASH_REG_DATA (0x40100040)
+#define FLASH_CMD_ERASE_PAGE 0x11
+#define FLASH_CMD_MASSERASE 0x22
+#define FLASH_CMD_WRITE 0x33
+#define FLASH_CMD_BURSTWRITE 0xCC
+#define FLASH_INT_CMDDONE 0x01
+#define FLASH_WORD_LEN 4
+
+struct bluenrgx_flash_bank {
+ int probed;
+ uint32_t idcode;
+ uint32_t die_id;
+};
+
+static int bluenrgx_protect_check(struct flash_bank *bank)
+{
+ /* Nothing to do. Protection is only handled in SW. */
+ return ERROR_OK;
+}
+
+/* flash_bank bluenrg-x 0 0 0 0 <target#> */
+FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command)
+{
+ struct bluenrgx_flash_bank *bluenrgx_info;
+ /* Create the bank structure */
+ bluenrgx_info = calloc(1, sizeof(*bluenrgx_info));
+
+ /* Check allocation */
+ if (bluenrgx_info == NULL) {
+ LOG_ERROR("failed to allocate bank structure");
+ return ERROR_FAIL;
+ }
+
+ bank->driver_priv = bluenrgx_info;
+
+ bluenrgx_info->probed = 0;
+
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ return ERROR_OK;
+}
+
+static int bluenrgx_erase(struct flash_bank *bank, int first, int last)
+{
+ int retval = ERROR_OK;
+ struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
+ int num_sectors = (last - first + 1);
+ int mass_erase = (num_sectors == bank->num_sectors);
+ struct target *target = bank->target;
+ uint32_t address, command;
+
+ /* check preconditions */
+ if (bluenrgx_info->probed == 0)
+ return ERROR_FLASH_BANK_NOT_PROBED;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ /* Disable blue module */
+ if (target_write_u32(target, 0x200000c0, 0) != ERROR_OK) {
+ LOG_ERROR("Blue disable failed");
+ return ERROR_FAIL;
+ }
+
+ if (mass_erase) {
+ command = FLASH_CMD_MASSERASE;
+ address = bank->base;
+ if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+
+ if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+
+ if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+
+ for (int i = 0; i < 100; i++) {
+ uint32_t value;
+ if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+ if (value & FLASH_INT_CMDDONE)
+ break;
+ if (i == 99) {
+ LOG_ERROR("Mass erase command failed (timeout)");
+ retval = ERROR_FAIL;
+ }
+ }
+
+ } else {
+ command = FLASH_CMD_ERASE_PAGE;
+ for (int i = first; i <= last; i++) {
+ address = bank->base+i*FLASH_PAGE_SIZE;
+
+ if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+
+ if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+
+ if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) {
+ LOG_ERROR("Failed");
+ return ERROR_FAIL;
+ }
+
+ for (int j = 0; j < 100; j++) {
+ uint32_t value;
+ if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) {
+ LOG_ERROR("Register write failed");
+ return ERROR_FAIL;
+ }
+ if (value & FLASH_INT_CMDDONE)
+ break;
+ if (j == 99) {
+ LOG_ERROR("Erase command failed (timeout)");
+ retval = ERROR_FAIL;
+ }
+ }
+ }
+ }
+
+ return retval;
+
+}
+
+static int bluenrgx_protect(struct flash_bank *bank, int set, int first, int last)
+{
+ /* Protection is only handled in software: no hardware write protection
+ available in BlueNRG-x devices */
+ int sector;
+
+ for (sector = first; sector <= last; sector++)
+ bank->sectors[sector].is_protected = set;
+ return ERROR_OK;
+}
+static int bluenrgx_write_word(struct target *target, uint32_t address_base, uint8_t *values, uint32_t count)
+{
+ int retval = ERROR_OK;
+
+ retval = target_write_u32(target, FLASH_REG_IRQRAW, 0x3f);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Register write failed, error code: %d", retval);
+ return retval;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ uint32_t address = address_base + i * FLASH_WORD_LEN;
+
+ retval = target_write_u32(target, FLASH_REG_ADDRESS, address >> 2);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Register write failed, error code: %d", retval);
+ return retval;
+ }
+
+ retval = target_write_buffer(target, FLASH_REG_DATA, FLASH_WORD_LEN, values + i * FLASH_WORD_LEN);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Register write failed, error code: %d", retval);
+ return retval;
+ }
+
+ retval = target_write_u32(target, FLASH_REG_COMMAND, FLASH_CMD_WRITE);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Register write failed, error code: %d", retval);
+ return retval;
+ }
+
+ for (int j = 0; j < 100; j++) {
+ uint32_t reg_value;
+ retval = target_read_u32(target, FLASH_REG_IRQRAW, &reg_value);
+
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Register read failed, error code: %d", retval);
+ return retval;
+ }
+
+ if (reg_value & FLASH_INT_CMDDONE)
+ break;
+
+ if (j == 99) {
+ LOG_ERROR("Write command failed (timeout)");
+ return ERROR_FAIL;
+ }
+ }
+ }
+ return retval;
+}
+
+static int bluenrgx_write_bytes(struct target *target, uint32_t address_base, uint8_t *buffer, uint32_t count)
+{
+ int retval = ERROR_OK;
+ uint8_t *new_buffer = NULL;
+ uint32_t pre_bytes = 0, post_bytes = 0, pre_word, post_word, pre_address, post_address;
+
+ if (count == 0) {
+ /* Just return if there are no bytes to write */
+ return retval;
+ }
+
+ if (address_base & 3) {
+ pre_bytes = address_base & 3;
+ pre_address = address_base - pre_bytes;
+ }
+
+ if ((count + pre_bytes) & 3) {
+ post_bytes = ((count + pre_bytes + 3) & ~3) - (count + pre_bytes);
+ post_address = (address_base + count) & ~3;
+ }
+
+ if (pre_bytes || post_bytes) {
+ uint32_t old_count = count;
+
+ count = old_count + pre_bytes + post_bytes;
+
+ new_buffer = malloc(count);
+
+ if (new_buffer == NULL) {
+ LOG_ERROR("odd number of bytes to write and no memory "
+ "for padding buffer");
+ return ERROR_FAIL;
+ }
+
+ LOG_INFO("Requested number of bytes to write and/or address not word aligned (%" PRIu32 "), extending to %"
+ PRIu32 " ", old_count, count);
+
+ if (pre_bytes) {
+ if (target_read_u32(target, pre_address, &pre_word)) {
+ LOG_ERROR("Memory read failed");
+ free(new_buffer);
+ return ERROR_FAIL;
+ }
+
+ }
+
+ if (post_bytes) {
+ if (target_read_u32(target, post_address, &post_word)) {
+ LOG_ERROR("Memory read failed");
+ free(new_buffer);
+ return ERROR_FAIL;
+ }
+
+ }
+
+ memcpy(new_buffer, &pre_word, pre_bytes);
+ memcpy((new_buffer+((pre_bytes+old_count) & ~3)), &post_word, 4);
+ memcpy(new_buffer+pre_bytes, buffer, old_count);
+ buffer = new_buffer;
+ }
+
+ retval = bluenrgx_write_word(target, address_base - pre_bytes, buffer, count/4);
+
+ if (new_buffer)
+ free(new_buffer);
+
+ return retval;
+}
+
+static int bluenrgx_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ uint32_t buffer_size = 16384 + 8;
+ struct working_area *write_algorithm;
+ struct working_area *write_algorithm_sp;
+ struct working_area *source;
+ uint32_t address = bank->base + offset;
+ struct reg_param reg_params[5];
+ struct armv7m_algorithm armv7m_info;
+ int retval = ERROR_OK;
+ uint32_t pre_size = 0, fast_size = 0, post_size = 0;
+ uint32_t pre_offset = 0, fast_offset = 0, post_offset = 0;
+
+ /* See contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c for source and
+ * hints how to generate the data!
+ */
+ static const uint8_t bluenrgx_flash_write_code[] = {
+#include "../../../contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc"
+ };
+
+ if ((offset + count) > bank->size) {
+ LOG_ERROR("Requested write past beyond of flash size: (offset+count) = %d, size=%d",
+ (offset + count),
+ bank->size);
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+ }
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* We are good here and we need to compute pre_size, fast_size, post_size */
+ pre_size = MIN(count, ((offset+0xF) & ~0xF) - offset);
+ pre_offset = offset;
+ fast_size = 16*((count - pre_size) / 16);
+ fast_offset = offset + pre_size;
+ post_size = (count-pre_size-fast_size) % 16;
+ post_offset = fast_offset + fast_size;
+
+ LOG_DEBUG("pre_size = %08x, pre_offset=%08x", pre_size, pre_offset);
+ LOG_DEBUG("fast_size = %08x, fast_offset=%08x", fast_size, fast_offset);
+ LOG_DEBUG("post_size = %08x, post_offset=%08x", post_size, post_offset);
+
+ /* Program initial chunk not 16 bytes aligned */
+ retval = bluenrgx_write_bytes(target, bank->base+pre_offset, (uint8_t *) buffer, pre_size);
+ if (retval) {
+ LOG_ERROR("bluenrgx_write_bytes failed %d", retval);
+ return ERROR_FAIL;
+ }
+
+ /* Program chunk 16 bytes aligned in fast mode */
+ if (fast_size) {
+
+ if (target_alloc_working_area(target, sizeof(bluenrgx_flash_write_code),
+ &write_algorithm) != ERROR_OK) {
+ LOG_WARNING("no working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ retval = target_write_buffer(target, write_algorithm->address,
+ sizeof(bluenrgx_flash_write_code),
+ bluenrgx_flash_write_code);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* memory buffer */
+ if (target_alloc_working_area(target, buffer_size, &source)) {
+ LOG_WARNING("no large enough working area available");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ /* Stack pointer area */
+ if (target_alloc_working_area(target, 64,
+ &write_algorithm_sp) != ERROR_OK) {
+ LOG_DEBUG("no working area for write code stack pointer");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ armv7m_info.core_mode = ARM_MODE_THREAD;
+
+ init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT);
+ init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+ init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
+ init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
+ init_reg_param(&reg_params[4], "sp", 32, PARAM_OUT);
+
+ /* FIFO start address (first two words used for write and read pointers) */
+ buf_set_u32(reg_params[0].value, 0, 32, source->address);
+ /* FIFO end address (first two words used for write and read pointers) */
+ buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
+ /* Flash memory address */
+ buf_set_u32(reg_params[2].value, 0, 32, address+pre_size);
+ /* Number of bytes */
+ buf_set_u32(reg_params[3].value, 0, 32, fast_size);
+ /* Stack pointer for program working area */
+ buf_set_u32(reg_params[4].value, 0, 32, write_algorithm_sp->address);
+
+ LOG_DEBUG("source->address = %08" TARGET_PRIxADDR, source->address);
+ LOG_DEBUG("source->address+ source->size = %08" TARGET_PRIxADDR, source->address+source->size);
+ LOG_DEBUG("write_algorithm_sp->address = %08" TARGET_PRIxADDR, write_algorithm_sp->address);
+ LOG_DEBUG("address = %08x", address+pre_size);
+ LOG_DEBUG("count = %08x", count);
+
+ retval = target_run_flash_async_algorithm(target,
+ buffer+pre_size,
+ fast_size/16,
+ 16, /* Block size: we write in block of 16 bytes to enjoy burstwrite speed */
+ 0,
+ NULL,
+ 5,
+ reg_params,
+ source->address,
+ source->size,
+ write_algorithm->address,
+ 0,
+ &armv7m_info);
+
+ if (retval == ERROR_FLASH_OPERATION_FAILED) {
+ LOG_ERROR("error executing bluenrg-x flash write algorithm");
+
+ uint32_t error = buf_get_u32(reg_params[0].value, 0, 32);
+
+ if (error != 0)
+ LOG_ERROR("flash write failed = %08" PRIx32, error);
+ }
+ if (retval == ERROR_OK) {
+ uint32_t rp;
+ /* Read back rp and check that is valid */
+ retval = target_read_u32(target, source->address+4, &rp);
+ if (retval == ERROR_OK) {
+ if ((rp < source->address+8) || (rp > (source->address + source->size))) {
+ LOG_ERROR("flash write failed = %08" PRIx32, rp);
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ }
+ target_free_working_area(target, source);
+ target_free_working_area(target, write_algorithm);
+ target_free_working_area(target, write_algorithm_sp);
+
+ destroy_reg_param(&reg_params[0]);
+ destroy_reg_param(&reg_params[1]);
+ destroy_reg_param(&reg_params[2]);
+ destroy_reg_param(&reg_params[3]);
+ destroy_reg_param(&reg_params[4]);
+ if (retval != ERROR_OK)
+ return retval;
+
+ }
+
+ /* Program chunk at end, not addressable by fast burst write algorithm */
+ retval = bluenrgx_write_bytes(target, bank->base+post_offset, (uint8_t *) (buffer+pre_size+fast_size), post_size);
+ if (retval) {
+ LOG_ERROR("bluenrgx_write_bytes failed %d", retval);
+ return ERROR_FAIL;
+ }
+ return retval;
+}
+
+static int bluenrgx_probe(struct flash_bank *bank)
+{
+ struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
+ uint32_t idcode, size_info, die_id;
+ int i;
+ int retval = target_read_u32(bank->target, JTAG_IDCODE_REG, &idcode);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = target_read_u32(bank->target, FLASH_SIZE_REG, &size_info);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u32(bank->target, DIE_ID_REG, &die_id);
+ if (retval != ERROR_OK)
+ return retval;
+
+ bank->size = (size_info + 1) * 4;
+ bank->base = FLASH_BASE;
+ bank->num_sectors = bank->size/FLASH_PAGE_SIZE;
+ bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector) * bank->num_sectors);
+
+ for (i = 0; i < bank->num_sectors; i++) {
+ bank->sectors[i].offset = i * FLASH_PAGE_SIZE;
+ bank->sectors[i].size = FLASH_PAGE_SIZE;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 0;
+ }
+
+ bluenrgx_info->probed = 1;
+ bluenrgx_info->die_id = die_id;
+ bluenrgx_info->idcode = idcode;
+ return ERROR_OK;
+}
+
+static int bluenrgx_auto_probe(struct flash_bank *bank)
+{
+ struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
+
+ if (bluenrgx_info->probed)
+ return ERROR_OK;
+
+ return bluenrgx_probe(bank);
+}
+
+/* This method must return a string displaying information about the bank */
+static int bluenrgx_get_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
+ int mask_number, cut_number;
+ char *part_name;
+
+ if (!bluenrgx_info->probed) {
+ int retval = bluenrgx_probe(bank);
+ if (retval != ERROR_OK) {
+ snprintf(buf, buf_size,
+ "Unable to find bank information.");
+ return retval;
+ }
+ }
+
+ if (bluenrgx_info->idcode == BLUENRG2_IDCODE)
+ part_name = "BLUENRG-2";
+ else
+ part_name = "BLUENRG-1";
+
+ mask_number = (bluenrgx_info->die_id >> 4) & 0xF;
+ cut_number = bluenrgx_info->die_id & 0xF;
+
+ snprintf(buf, buf_size,
+ "%s - Rev: %d.%d", part_name, mask_number, cut_number);
+ return ERROR_OK;
+}
+
+struct flash_driver bluenrgx_flash = {
+ .name = "bluenrg-x",
+ .flash_bank_command = bluenrgx_flash_bank_command,
+ .erase = bluenrgx_erase,
+ .protect = bluenrgx_protect,
+ .write = bluenrgx_write,
+ .read = default_flash_read,
+ .probe = bluenrgx_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = bluenrgx_protect_check,
+ .auto_probe = bluenrgx_auto_probe,
+ .info = bluenrgx_get_info,
+};
diff --git a/src/flash/nor/cc26xx.c b/src/flash/nor/cc26xx.c
new file mode 100644
index 0000000..e6e9e59
--- /dev/null
+++ b/src/flash/nor/cc26xx.c
@@ -0,0 +1,567 @@
+/***************************************************************************
+ * 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 "cc26xx.h"
+#include <helper/binarybuffer.h>
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+#include <target/image.h>
+
+#define FLASH_TIMEOUT 8000
+
+struct cc26xx_bank {
+ const char *family_name;
+ uint32_t icepick_id;
+ uint32_t user_id;
+ uint32_t device_type;
+ uint32_t sector_length;
+ bool probed;
+ struct working_area *working_area;
+ struct armv7m_algorithm armv7m_info;
+ const uint8_t *algo_code;
+ uint32_t algo_size;
+ uint32_t algo_working_size;
+ uint32_t buffer_addr[2];
+ uint32_t params_addr[2];
+};
+
+static int cc26xx_auto_probe(struct flash_bank *bank);
+
+static uint32_t cc26xx_device_type(uint32_t icepick_id, uint32_t user_id)
+{
+ uint32_t device_type = 0;
+
+ switch (icepick_id & ICEPICK_ID_MASK) {
+ case CC26X0_ICEPICK_ID:
+ device_type = CC26X0_TYPE;
+ break;
+ case CC26X1_ICEPICK_ID:
+ device_type = CC26X1_TYPE;
+ break;
+ case CC13X0_ICEPICK_ID:
+ device_type = CC13X0_TYPE;
+ break;
+ case CC13X2_CC26X2_ICEPICK_ID:
+ default:
+ if ((user_id & USER_ID_CC13_MASK) != 0)
+ device_type = CC13X2_TYPE;
+ else
+ device_type = CC26X2_TYPE;
+ break;
+ }
+
+ return device_type;
+}
+
+static uint32_t cc26xx_sector_length(uint32_t icepick_id)
+{
+ uint32_t sector_length;
+
+ switch (icepick_id & ICEPICK_ID_MASK) {
+ case CC26X0_ICEPICK_ID:
+ case CC26X1_ICEPICK_ID:
+ case CC13X0_ICEPICK_ID:
+ /* Chameleon family device */
+ sector_length = CC26X0_SECTOR_LENGTH;
+ break;
+ case CC13X2_CC26X2_ICEPICK_ID:
+ default:
+ /* Agama family device */
+ sector_length = CC26X2_SECTOR_LENGTH;
+ break;
+ }
+
+ return sector_length;
+}
+
+static int cc26xx_wait_algo_done(struct flash_bank *bank, uint32_t params_addr)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+
+ uint32_t status_addr = params_addr + CC26XX_STATUS_OFFSET;
+ uint32_t status = CC26XX_BUFFER_FULL;
+ long long start_ms;
+ long long elapsed_ms;
+
+ int retval = ERROR_OK;
+
+ start_ms = timeval_ms();
+ while (CC26XX_BUFFER_FULL == status) {
+ retval = target_read_u32(target, status_addr, &status);
+ if (ERROR_OK != retval)
+ return retval;
+
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ if (elapsed_ms > FLASH_TIMEOUT)
+ break;
+ };
+
+ if (CC26XX_BUFFER_EMPTY != status) {
+ LOG_ERROR("%s: Flash operation failed", cc26xx_bank->family_name);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int cc26xx_init(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+
+ int retval;
+
+ /* Make sure we've probed the flash to get the device and size */
+ retval = cc26xx_auto_probe(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Check for working area to use for flash helper algorithm */
+ if (NULL != cc26xx_bank->working_area)
+ target_free_working_area(target, cc26xx_bank->working_area);
+ retval = target_alloc_working_area(target, cc26xx_bank->algo_working_size,
+ &cc26xx_bank->working_area);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Confirm the defined working address is the area we need to use */
+ if (CC26XX_ALGO_BASE_ADDRESS != cc26xx_bank->working_area->address)
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+ /* Write flash helper algorithm into target memory */
+ retval = target_write_buffer(target, CC26XX_ALGO_BASE_ADDRESS,
+ cc26xx_bank->algo_size, cc26xx_bank->algo_code);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("%s: Failed to load flash helper algorithm",
+ cc26xx_bank->family_name);
+ target_free_working_area(target, cc26xx_bank->working_area);
+ return retval;
+ }
+
+ /* Initialize the ARMv7 specific info to run the algorithm */
+ cc26xx_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ cc26xx_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
+
+ /* Begin executing the flash helper algorithm */
+ retval = target_start_algorithm(target, 0, NULL, 0, NULL,
+ CC26XX_ALGO_BASE_ADDRESS, 0, &cc26xx_bank->armv7m_info);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("%s: Failed to start flash helper algorithm",
+ cc26xx_bank->family_name);
+ target_free_working_area(target, cc26xx_bank->working_area);
+ return retval;
+ }
+
+ /*
+ * At this point, the algorithm is running on the target and
+ * ready to receive commands and data to flash the target
+ */
+
+ return retval;
+}
+
+static int cc26xx_quit(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+
+ int retval;
+
+ /* Regardless of the algo's status, attempt to halt the target */
+ (void)target_halt(target);
+
+ /* Now confirm target halted and clean up from flash helper algorithm */
+ retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT,
+ &cc26xx_bank->armv7m_info);
+
+ target_free_working_area(target, cc26xx_bank->working_area);
+ cc26xx_bank->working_area = NULL;
+
+ return retval;
+}
+
+static int cc26xx_mass_erase(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+ struct cc26xx_algo_params algo_params;
+
+ int retval;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = cc26xx_init(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize algorithm parameters */
+ buf_set_u32(algo_params.address, 0, 32, 0);
+ buf_set_u32(algo_params.length, 0, 32, 4);
+ buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_ALL);
+ buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL);
+
+ /* Issue flash helper algorithm parameters for mass erase */
+ retval = target_write_buffer(target, cc26xx_bank->params_addr[0],
+ sizeof(algo_params), (uint8_t *)&algo_params);
+
+ /* Wait for command to complete */
+ if (ERROR_OK == retval)
+ retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]);
+
+ /* Regardless of errors, try to close down algo */
+ (void)cc26xx_quit(bank);
+
+ return retval;
+}
+
+FLASH_BANK_COMMAND_HANDLER(cc26xx_flash_bank_command)
+{
+ struct cc26xx_bank *cc26xx_bank;
+
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ cc26xx_bank = malloc(sizeof(struct cc26xx_bank));
+ if (NULL == cc26xx_bank)
+ return ERROR_FAIL;
+
+ /* Initialize private flash information */
+ memset((void *)cc26xx_bank, 0x00, sizeof(struct cc26xx_bank));
+ cc26xx_bank->family_name = "cc26xx";
+ cc26xx_bank->device_type = CC26XX_NO_TYPE;
+ cc26xx_bank->sector_length = 0x1000;
+
+ /* Finish initialization of bank */
+ bank->driver_priv = cc26xx_bank;
+ bank->next = NULL;
+
+ return ERROR_OK;
+}
+
+static int cc26xx_erase(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+ struct cc26xx_algo_params algo_params;
+
+ uint32_t address;
+ uint32_t length;
+ int retval;
+
+ 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 cc26xx_mass_erase(bank);
+ }
+
+ address = first * cc26xx_bank->sector_length;
+ length = (last - first + 1) * cc26xx_bank->sector_length;
+
+ retval = cc26xx_init(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Set up algorithm parameters for erase command */
+ buf_set_u32(algo_params.address, 0, 32, address);
+ buf_set_u32(algo_params.length, 0, 32, length);
+ buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_SECTORS);
+ buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL);
+
+ /* Issue flash helper algorithm parameters for erase */
+ retval = target_write_buffer(target, cc26xx_bank->params_addr[0],
+ sizeof(algo_params), (uint8_t *)&algo_params);
+
+ /* If no error, wait for erase to finish */
+ if (ERROR_OK == retval)
+ retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]);
+
+ /* Regardless of errors, try to close down algo */
+ (void)cc26xx_quit(bank);
+
+ return retval;
+}
+
+static int cc26xx_protect(struct flash_bank *bank, int set, int first,
+ int last)
+{
+ return ERROR_OK;
+}
+
+static int cc26xx_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+ struct cc26xx_algo_params algo_params[2];
+ uint32_t size = 0;
+ long long start_ms;
+ long long elapsed_ms;
+ uint32_t address;
+
+ uint32_t index;
+ int retval;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = cc26xx_init(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize algorithm parameters to default values */
+ buf_set_u32(algo_params[0].command, 0, 32, CC26XX_CMD_PROGRAM);
+ buf_set_u32(algo_params[1].command, 0, 32, CC26XX_CMD_PROGRAM);
+
+ /* Write requested data, ping-ponging between two buffers */
+ index = 0;
+ start_ms = timeval_ms();
+ address = bank->base + offset;
+ while (count > 0) {
+
+ if (count > cc26xx_bank->sector_length)
+ size = cc26xx_bank->sector_length;
+ else
+ size = count;
+
+ /* Put next block of data to flash into buffer */
+ retval = target_write_buffer(target, cc26xx_bank->buffer_addr[index],
+ size, buffer);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("Unable to write data to target memory");
+ break;
+ }
+
+ /* Update algo parameters for next block */
+ buf_set_u32(algo_params[index].address, 0, 32, address);
+ buf_set_u32(algo_params[index].length, 0, 32, size);
+ buf_set_u32(algo_params[index].status, 0, 32, CC26XX_BUFFER_FULL);
+
+ /* Issue flash helper algorithm parameters for block write */
+ retval = target_write_buffer(target, cc26xx_bank->params_addr[index],
+ sizeof(algo_params[index]), (uint8_t *)&algo_params[index]);
+ if (ERROR_OK != retval)
+ break;
+
+ /* Wait for next ping pong buffer to be ready */
+ index ^= 1;
+ retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]);
+ if (ERROR_OK != retval)
+ break;
+
+ count -= size;
+ buffer += size;
+ address += size;
+
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ }
+
+ /* If no error yet, wait for last buffer to finish */
+ if (ERROR_OK == retval) {
+ index ^= 1;
+ retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]);
+ }
+
+ /* Regardless of errors, try to close down algo */
+ (void)cc26xx_quit(bank);
+
+ return retval;
+}
+
+static int cc26xx_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+
+ uint32_t sector_length;
+ uint32_t value;
+ int num_sectors;
+ int max_sectors;
+
+ int retval;
+
+ retval = target_read_u32(target, FCFG1_ICEPICK_ID, &value);
+ if (ERROR_OK != retval)
+ return retval;
+ cc26xx_bank->icepick_id = value;
+
+ retval = target_read_u32(target, FCFG1_USER_ID, &value);
+ if (ERROR_OK != retval)
+ return retval;
+ cc26xx_bank->user_id = value;
+
+ cc26xx_bank->device_type = cc26xx_device_type(cc26xx_bank->icepick_id,
+ cc26xx_bank->user_id);
+
+ sector_length = cc26xx_sector_length(cc26xx_bank->icepick_id);
+
+ /* Set up appropriate flash helper algorithm */
+ switch (cc26xx_bank->icepick_id & ICEPICK_ID_MASK) {
+ case CC26X0_ICEPICK_ID:
+ case CC26X1_ICEPICK_ID:
+ case CC13X0_ICEPICK_ID:
+ /* Chameleon family device */
+ cc26xx_bank->algo_code = cc26x0_algo;
+ cc26xx_bank->algo_size = sizeof(cc26x0_algo);
+ cc26xx_bank->algo_working_size = CC26X0_WORKING_SIZE;
+ cc26xx_bank->buffer_addr[0] = CC26X0_ALGO_BUFFER_0;
+ cc26xx_bank->buffer_addr[1] = CC26X0_ALGO_BUFFER_1;
+ cc26xx_bank->params_addr[0] = CC26X0_ALGO_PARAMS_0;
+ cc26xx_bank->params_addr[1] = CC26X0_ALGO_PARAMS_1;
+ max_sectors = CC26X0_MAX_SECTORS;
+ break;
+ case CC13X2_CC26X2_ICEPICK_ID:
+ default:
+ /* Agama family device */
+ cc26xx_bank->algo_code = cc26x2_algo;
+ cc26xx_bank->algo_size = sizeof(cc26x2_algo);
+ cc26xx_bank->algo_working_size = CC26X2_WORKING_SIZE;
+ cc26xx_bank->buffer_addr[0] = CC26X2_ALGO_BUFFER_0;
+ cc26xx_bank->buffer_addr[1] = CC26X2_ALGO_BUFFER_1;
+ cc26xx_bank->params_addr[0] = CC26X2_ALGO_PARAMS_0;
+ cc26xx_bank->params_addr[1] = CC26X2_ALGO_PARAMS_1;
+ max_sectors = CC26X2_MAX_SECTORS;
+ break;
+ }
+
+ retval = target_read_u32(target, CC26XX_FLASH_SIZE_INFO, &value);
+ if (ERROR_OK != retval)
+ return retval;
+ num_sectors = value & 0xff;
+ if (num_sectors > max_sectors)
+ num_sectors = max_sectors;
+
+ bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+ if (NULL == bank->sectors)
+ return ERROR_FAIL;
+
+ bank->base = CC26XX_FLASH_BASE_ADDR;
+ bank->num_sectors = num_sectors;
+ bank->size = num_sectors * sector_length;
+ bank->write_start_alignment = 0;
+ bank->write_end_alignment = 0;
+ cc26xx_bank->sector_length = sector_length;
+
+ for (int i = 0; i < num_sectors; i++) {
+ bank->sectors[i].offset = i * sector_length;
+ bank->sectors[i].size = sector_length;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 0;
+ }
+
+ /* We've successfully determined the stats on the flash bank */
+ cc26xx_bank->probed = true;
+
+ /* If we fall through to here, then all went well */
+
+ return ERROR_OK;
+}
+
+static int cc26xx_auto_probe(struct flash_bank *bank)
+{
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+
+ int retval = ERROR_OK;
+
+ if (bank->bank_number != 0) {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+
+ if (!cc26xx_bank->probed)
+ retval = cc26xx_probe(bank);
+
+ return retval;
+}
+
+static int cc26xx_protect_check(struct flash_bank *bank)
+{
+ return ERROR_OK;
+}
+
+static int cc26xx_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
+ int printed = 0;
+ const char *device;
+
+ switch (cc26xx_bank->device_type) {
+ case CC26X0_TYPE:
+ device = "CC26x0";
+ break;
+ case CC26X1_TYPE:
+ device = "CC26x1";
+ break;
+ case CC13X0_TYPE:
+ device = "CC13x0";
+ break;
+ case CC13X2_TYPE:
+ device = "CC13x2";
+ break;
+ case CC26X2_TYPE:
+ device = "CC26x2";
+ break;
+ case CC26XX_NO_TYPE:
+ default:
+ device = "Unrecognized";
+ break;
+ }
+
+ printed = snprintf(buf, buf_size,
+ "%s device: ICEPick ID 0x%08x, USER ID 0x%08x\n",
+ device, cc26xx_bank->icepick_id, cc26xx_bank->user_id);
+
+ if (printed >= buf_size)
+ return ERROR_BUF_TOO_SMALL;
+
+ return ERROR_OK;
+}
+
+struct flash_driver cc26xx_flash = {
+ .name = "cc26xx",
+ .flash_bank_command = cc26xx_flash_bank_command,
+ .erase = cc26xx_erase,
+ .protect = cc26xx_protect,
+ .write = cc26xx_write,
+ .read = default_flash_read,
+ .probe = cc26xx_probe,
+ .auto_probe = cc26xx_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = cc26xx_protect_check,
+ .info = cc26xx_info,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/cc26xx.h b/src/flash/nor/cc26xx.h
new file mode 100644
index 0000000..51a09f1
--- /dev/null
+++ b/src/flash/nor/cc26xx.h
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * 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_CC26XX_H
+#define OPENOCD_FLASH_NOR_CC26XX_H
+
+/* Addresses of FCFG1 registers to access ICEPick Device ID and User ID */
+#define FCFG1_ICEPICK_ID 0x50001318
+#define FCFG1_USER_ID 0x50001294
+
+/* ICEPick device ID mask and values */
+#define ICEPICK_ID_MASK 0x0fffffff
+#define ICEPICK_REV_MASK 0xf0000000
+#define CC26X0_ICEPICK_ID 0x0b99a02f
+#define CC26X1_ICEPICK_ID 0x0b9bd02f
+#define CC13X0_ICEPICK_ID 0x0b9be02f
+#define CC13X2_CC26X2_ICEPICK_ID 0x0bb4102f
+
+/* User ID mask for Agama CC13x2 vs CC26x2 */
+#define USER_ID_CC13_MASK 0x00800000
+
+/* Common CC26xx/CC13xx flash and memory parameters */
+#define CC26XX_FLASH_BASE_ADDR 0x00000000
+#define CC26XX_FLASH_SIZE_INFO 0x4003002c
+#define CC26XX_SRAM_SIZE_INFO 0x40082250
+#define CC26XX_ALGO_BASE_ADDRESS 0x20000000
+
+/* Chameleon CC26x0/CC13x0 specific parameters */
+#define CC26X0_MAX_SECTORS 32
+#define CC26X0_SECTOR_LENGTH 0x1000
+#define CC26X0_ALGO_BUFFER_0 0x20001c00
+#define CC26X0_ALGO_BUFFER_1 0x20002c00
+#define CC26X0_ALGO_PARAMS_0 0x20001bd8
+#define CC26X0_ALGO_PARAMS_1 0x20001bec
+#define CC26X0_WORKING_SIZE (CC26X0_ALGO_BUFFER_1 + CC26X0_SECTOR_LENGTH - \
+ CC26XX_ALGO_BASE_ADDRESS)
+
+/* Agama CC26x2/CC13x2 specific parameters */
+#define CC26X2_MAX_SECTORS 128
+#define CC26X2_SECTOR_LENGTH 0x2000
+#define CC26X2_ALGO_BUFFER_0 0x20002000
+#define CC26X2_ALGO_BUFFER_1 0x20004000
+#define CC26X2_ALGO_PARAMS_0 0x20001fd8
+#define CC26X2_ALGO_PARAMS_1 0x20001fec
+#define CC26X2_WORKING_SIZE (CC26X2_ALGO_BUFFER_1 + CC26X2_SECTOR_LENGTH - \
+ CC26XX_ALGO_BASE_ADDRESS)
+
+/* CC26xx flash helper algorithm buffer flags */
+#define CC26XX_BUFFER_EMPTY 0x00000000
+#define CC26XX_BUFFER_FULL 0xffffffff
+
+/* CC26XX flash helper algorithm commands */
+#define CC26XX_CMD_NO_ACTION 0
+#define CC26XX_CMD_ERASE_ALL 1
+#define CC26XX_CMD_PROGRAM 2
+#define CC26XX_CMD_ERASE_AND_PROGRAM 3
+#define CC26XX_CMD_ERASE_AND_PROGRAM_WITH_RETAIN 4
+#define CC26XX_CMD_ERASE_SECTORS 5
+
+/* CC26xx and CC13xx device types */
+#define CC26XX_NO_TYPE 0 /* Device type not determined yet */
+#define CC26X0_TYPE 1 /* CC26x0 Chameleon device */
+#define CC26X1_TYPE 2 /* CC26x1 Chameleon device */
+#define CC26X2_TYPE 3 /* CC26x2 Agama device */
+#define CC13X0_TYPE 4 /* CC13x0 Chameleon device */
+#define CC13X2_TYPE 5 /* CC13x2 Agama device */
+
+/* Flash helper algorithm parameter block struct */
+#define CC26XX_STATUS_OFFSET 0x0c
+struct cc26xx_algo_params {
+ uint8_t address[4];
+ uint8_t length[4];
+ uint8_t command[4];
+ uint8_t status[4];
+};
+
+/* Flash helper algorithm for CC26x0 Chameleon targets */
+const uint8_t cc26x0_algo[] = {
+#include "../../../contrib/loaders/flash/cc26xx/cc26x0_algo.inc"
+};
+
+/* Flash helper algorithm for CC26x2 Agama targets */
+const uint8_t cc26x2_algo[] = {
+#include "../../../contrib/loaders/flash/cc26xx/cc26x2_algo.inc"
+};
+
+#endif /* OPENOCD_FLASH_NOR_CC26XX_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/cfi.c b/src/flash/nor/cfi.c
index ac0db82..0ae72d4 100644
--- a/src/flash/nor/cfi.c
+++ b/src/flash/nor/cfi.c
@@ -3128,4 +3128,5 @@ struct flash_driver cfi_flash = {
.erase_check = default_flash_blank_check,
.protect_check = cfi_protect_check,
.info = get_cfi_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c
index ab69a32..4941281 100644
--- a/src/flash/nor/core.c
+++ b/src/flash/nor/core.c
@@ -4,6 +4,7 @@
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
+ * Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.cz> *
* *
* 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 *
@@ -171,6 +172,39 @@ int flash_get_bank_count(void)
return i;
}
+void default_flash_free_driver_priv(struct flash_bank *bank)
+{
+ free(bank->driver_priv);
+ bank->driver_priv = NULL;
+}
+
+void flash_free_all_banks(void)
+{
+ struct flash_bank *bank = flash_banks;
+ while (bank) {
+ struct flash_bank *next = bank->next;
+ if (bank->driver->free_driver_priv)
+ bank->driver->free_driver_priv(bank);
+ 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);
+ bank = next;
+ }
+ flash_banks = NULL;
+}
+
struct flash_bank *get_flash_bank_by_name_noprobe(const char *name)
{
unsigned requested = get_flash_name_index(name);
@@ -314,36 +348,49 @@ int default_flash_blank_check(struct flash_bank *bank)
struct target *target = bank->target;
int i;
int retval;
- int fast_check = 0;
- uint32_t blank;
if (bank->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 default_flash_mem_blank_check(bank);
+
for (i = 0; i < bank->num_sectors; i++) {
- uint32_t address = bank->base + bank->sectors[i].offset;
- uint32_t size = bank->sectors[i].size;
+ 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 */
+ }
- retval = target_blank_check_memory(target, address, size, &blank, bank->erased_value);
- if (retval != ERROR_OK) {
- fast_check = 0;
+ bool fast_check = true;
+ for (i = 0; i < bank->num_sectors; ) {
+ retval = target_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;
}
- if (blank == bank->erased_value)
- bank->sectors[i].is_erased = 1;
- else
- bank->sectors[i].is_erased = 0;
- fast_check = 1;
+ i += retval; /* add number of blocks done this round */
}
- if (!fast_check) {
+ if (fast_check) {
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased = block_array[i].result;
+ retval = ERROR_OK;
+ } else {
LOG_USER("Running slow fallback erase check - add working memory");
- return default_flash_mem_blank_check(bank);
+ retval = default_flash_mem_blank_check(bank);
}
+ free(block_array);
- return ERROR_OK;
+ return retval;
}
/* Manipulate given flash region, selecting the bank according to target
@@ -399,18 +446,21 @@ static int flash_iterate_address_range_inner(struct target *target,
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
- addr -= c->base;
- last_addr -= c->base;
+ if (c->prot_blocks == NULL || c->num_prot_blocks == 0) {
+ /* flash driver does not define protect blocks, use sectors instead */
+ iterate_protect_blocks = false;
+ }
- if (iterate_protect_blocks && c->prot_blocks && c->num_prot_blocks) {
+ if (iterate_protect_blocks) {
block_array = c->prot_blocks;
num_blocks = c->num_prot_blocks;
} else {
block_array = c->sectors;
num_blocks = c->num_sectors;
- iterate_protect_blocks = false;
}
+ addr -= c->base;
+ last_addr -= c->base;
for (i = 0; i < num_blocks; i++) {
struct flash_sector *f = &block_array[i];
@@ -559,6 +609,87 @@ static int compare_section(const void *a, const void *b)
return -1;
}
+/**
+ * Get aligned start address of a flash write region
+ */
+target_addr_t flash_write_align_start(struct flash_bank *bank, target_addr_t addr)
+{
+ if (addr < bank->base || addr >= bank->base + bank->size
+ || bank->write_start_alignment <= 1)
+ return addr;
+
+ if (bank->write_start_alignment == FLASH_WRITE_ALIGN_SECTOR) {
+ uint32_t offset = addr - bank->base;
+ uint32_t aligned = 0;
+ int sect;
+ for (sect = 0; sect < bank->num_sectors; sect++) {
+ if (bank->sectors[sect].offset > offset)
+ break;
+
+ aligned = bank->sectors[sect].offset;
+ }
+ return bank->base + aligned;
+ }
+
+ return addr & ~(bank->write_start_alignment - 1);
+}
+
+/**
+ * Get aligned end address of a flash write region
+ */
+target_addr_t flash_write_align_end(struct flash_bank *bank, target_addr_t addr)
+{
+ if (addr < bank->base || addr >= bank->base + bank->size
+ || bank->write_end_alignment <= 1)
+ return addr;
+
+ if (bank->write_end_alignment == FLASH_WRITE_ALIGN_SECTOR) {
+ uint32_t offset = addr - bank->base;
+ uint32_t aligned = 0;
+ int sect;
+ for (sect = 0; sect < bank->num_sectors; sect++) {
+ aligned = bank->sectors[sect].offset + bank->sectors[sect].size - 1;
+ if (aligned >= offset)
+ break;
+ }
+ return bank->base + aligned;
+ }
+
+ return addr | (bank->write_end_alignment - 1);
+}
+
+/**
+ * Check if gap between sections is bigger than minimum required to discontinue flash write
+ */
+static bool flash_write_check_gap(struct flash_bank *bank,
+ target_addr_t addr1, target_addr_t addr2)
+{
+ if (bank->minimal_write_gap == FLASH_WRITE_CONTINUOUS
+ || addr1 < bank->base || addr1 >= bank->base + bank->size
+ || addr2 < bank->base || addr2 >= bank->base + bank->size)
+ return false;
+
+ if (bank->minimal_write_gap == FLASH_WRITE_GAP_SECTOR) {
+ int sect;
+ uint32_t offset1 = addr1 - bank->base;
+ /* find the sector following the one containing addr1 */
+ for (sect = 0; sect < bank->num_sectors; sect++) {
+ if (bank->sectors[sect].offset > offset1)
+ break;
+ }
+ if (sect >= bank->num_sectors)
+ return false;
+
+ uint32_t offset2 = addr2 - bank->base;
+ return bank->sectors[sect].offset + bank->sectors[sect].size <= offset2;
+ }
+
+ target_addr_t aligned1 = flash_write_align_end(bank, addr1);
+ target_addr_t aligned2 = flash_write_align_start(bank, addr2);
+ return aligned1 + bank->minimal_write_gap < aligned2;
+}
+
+
int flash_write_unlock(struct target *target, struct image *image,
uint32_t *written, int erase, bool unlock)
{
@@ -598,10 +729,10 @@ int flash_write_unlock(struct target *target, struct image *image,
/* loop until we reach end of the image */
while (section < image->num_sections) {
- uint32_t buffer_size;
+ uint32_t buffer_idx;
uint8_t *buffer;
int section_last;
- uint32_t run_address = sections[section]->base_address + section_offset;
+ target_addr_t run_address = sections[section]->base_address + section_offset;
uint32_t run_size = sections[section]->size - section_offset;
int pad_bytes = 0;
@@ -617,7 +748,7 @@ int flash_write_unlock(struct target *target, struct image *image,
if (retval != ERROR_OK)
goto done;
if (c == NULL) {
- LOG_WARNING("no flash bank found for address %" PRIx32, run_address);
+ LOG_WARNING("no flash bank found for address " TARGET_ADDR_FMT, run_address);
section++; /* and skip it */
section_offset = 0;
continue;
@@ -635,32 +766,37 @@ int flash_write_unlock(struct target *target, struct image *image,
break;
}
- /* FIXME This needlessly touches sectors BETWEEN the
- * sections it's writing. Without auto erase, it just
- * writes ones. That WILL INVALIDATE data in cases
- * like Stellaris Tempest chips, corrupting internal
- * ECC codes; and at least FreeScale suggests issues
- * with that approach (in HC11 documentation).
- *
- * With auto erase enabled, data in those sectors will
- * be needlessly destroyed; and some of the limited
- * number of flash erase cycles will be wasted...
- *
- * In both cases, the extra writes slow things down.
- */
-
/* if we have multiple sections within our image,
* flash programming could fail due to alignment issues
* attempt to rebuild a consecutive buffer for the flash loader */
- pad_bytes = (sections[section_last + 1]->base_address) - (run_address + run_size);
- padding[section_last] = pad_bytes;
- run_size += sections[++section_last]->size;
- run_size += pad_bytes;
+ target_addr_t run_next_addr = run_address + run_size;
+ target_addr_t next_section_base = sections[section_last + 1]->base_address;
+ if (next_section_base < run_next_addr) {
+ LOG_ERROR("Section at " TARGET_ADDR_FMT
+ " overlaps section ending at " TARGET_ADDR_FMT,
+ next_section_base, run_next_addr);
+ LOG_ERROR("Flash write aborted.");
+ retval = ERROR_FAIL;
+ goto done;
+ }
+ pad_bytes = next_section_base - run_next_addr;
+ if (pad_bytes) {
+ if (flash_write_check_gap(c, run_next_addr - 1, next_section_base)) {
+ LOG_INFO("Flash write discontinued at " TARGET_ADDR_FMT
+ ", next section at " TARGET_ADDR_FMT,
+ run_next_addr, next_section_base);
+ break;
+ }
+ }
if (pad_bytes > 0)
- LOG_INFO("Padding image section %d with %d bytes",
- section_last-1,
- pad_bytes);
+ LOG_INFO("Padding image section %d at " TARGET_ADDR_FMT
+ " with %d bytes",
+ section_last, run_next_addr, pad_bytes);
+
+ padding[section_last] = pad_bytes;
+ run_size += pad_bytes;
+ run_size += sections[++section_last]->size;
}
if (run_address + run_size - 1 > c->base + c->size - 1) {
@@ -673,10 +809,38 @@ int flash_write_unlock(struct target *target, struct image *image,
assert(run_size > 0);
}
- /* If we're applying any sector automagic, then pad this
- * (maybe-combined) segment to the end of its last sector.
- */
- if (unlock || erase) {
+ uint32_t padding_at_start = 0;
+ if (c->write_start_alignment || c->write_end_alignment) {
+ /* align write region according to bank requirements */
+ target_addr_t aligned_start = flash_write_align_start(c, run_address);
+ padding_at_start = run_address - aligned_start;
+ if (padding_at_start > 0) {
+ LOG_WARNING("Section start address " TARGET_ADDR_FMT
+ " breaks the required alignment of flash bank %s",
+ run_address, c->name);
+ LOG_WARNING("Padding %d bytes from " TARGET_ADDR_FMT,
+ padding_at_start, aligned_start);
+
+ run_address -= padding_at_start;
+ run_size += padding_at_start;
+ }
+
+ target_addr_t run_end = run_address + run_size - 1;
+ target_addr_t aligned_end = flash_write_align_end(c, run_end);
+ pad_bytes = aligned_end - run_end;
+ if (pad_bytes > 0) {
+ LOG_INFO("Padding image section %d at " TARGET_ADDR_FMT
+ " with %d bytes (bank write end alignment)",
+ section_last, run_end + 1, pad_bytes);
+
+ padding[section_last] += pad_bytes;
+ run_size += pad_bytes;
+ }
+
+ } else if (unlock || erase) {
+ /* If we're applying any sector automagic, then pad this
+ * (maybe-combined) segment to the end of its last sector.
+ */
int sector;
uint32_t offset_start = run_address - c->base;
uint32_t offset_end = offset_start + run_size;
@@ -701,13 +865,17 @@ int flash_write_unlock(struct target *target, struct image *image,
retval = ERROR_FAIL;
goto done;
}
- buffer_size = 0;
+
+ if (padding_at_start)
+ memset(buffer, c->default_padded_value, padding_at_start);
+
+ buffer_idx = padding_at_start;
/* read sections to the buffer */
- while (buffer_size < run_size) {
+ while (buffer_idx < run_size) {
size_t size_read;
- size_read = run_size - buffer_size;
+ size_read = run_size - buffer_idx;
if (size_read > sections[section]->size - section_offset)
size_read = sections[section]->size - section_offset;
@@ -720,23 +888,25 @@ int flash_write_unlock(struct target *target, struct image *image,
int t_section_num = diff / sizeof(struct imagesection);
LOG_DEBUG("image_read_section: section = %d, t_section_num = %d, "
- "section_offset = %d, buffer_size = %d, size_read = %d",
- (int)section, (int)t_section_num, (int)section_offset,
- (int)buffer_size, (int)size_read);
+ "section_offset = %"PRIu32", buffer_idx = %"PRIu32", size_read = %zu",
+ section, t_section_num, section_offset,
+ buffer_idx, size_read);
retval = image_read_section(image, t_section_num, section_offset,
- size_read, buffer + buffer_size, &size_read);
+ size_read, buffer + buffer_idx, &size_read);
if (retval != ERROR_OK || size_read == 0) {
free(buffer);
goto done;
}
- /* see if we need to pad the section */
- while (padding[section]--)
- (buffer + buffer_size)[size_read++] = c->default_padded_value;
-
- buffer_size += size_read;
+ buffer_idx += size_read;
section_offset += size_read;
+ /* see if we need to pad the section */
+ if (padding[section]) {
+ memset(buffer + buffer_idx, c->default_padded_value, padding[section]);
+ buffer_idx += padding[section];
+ }
+
if (section_offset >= sections[section]->size) {
section++;
section_offset = 0;
diff --git a/src/flash/nor/core.h b/src/flash/nor/core.h
index 338363e..67de94e 100644
--- a/src/flash/nor/core.h
+++ b/src/flash/nor/core.h
@@ -65,6 +65,13 @@ struct flash_sector {
int is_protected;
};
+/** Special value for write_start_alignment and write_end_alignment field */
+#define FLASH_WRITE_ALIGN_SECTOR UINT32_MAX
+
+/** Special values for minimal_write_gap field */
+#define FLASH_WRITE_CONTINUOUS 0
+#define FLASH_WRITE_GAP_SECTOR UINT32_MAX
+
/**
* Provides details of a flash bank, available either on-chip or through
* a major interface.
@@ -76,7 +83,7 @@ struct flash_sector {
* per-bank basis, if required.
*/
struct flash_bank {
- const char *name;
+ char *name;
struct target *target; /**< Target to which this bank belongs. */
@@ -97,6 +104,18 @@ struct flash_bank {
* erased value. Defaults to 0xFF. */
uint8_t default_padded_value;
+ /** Required alignment of flash write start address.
+ * Default 0, no alignment. Can be any power of two or FLASH_WRITE_ALIGN_SECTOR */
+ uint32_t write_start_alignment;
+ /** Required alignment of flash write end address.
+ * Default 0, no alignment. Can be any power of two or FLASH_WRITE_ALIGN_SECTOR */
+ uint32_t write_end_alignment;
+ /** Minimal gap between sections to discontinue flash write
+ * Default FLASH_WRITE_GAP_SECTOR splits the write if one or more untouched
+ * sectors in between.
+ * Can be size in bytes or FLASH_WRITE_CONTINUOUS */
+ uint32_t minimal_write_gap;
+
/**
* The number of sectors on this chip. This value will
* be set intially to 0, and the flash driver must set this to
@@ -136,6 +155,22 @@ int flash_unlock_address_range(struct target *target, uint32_t addr,
uint32_t length);
/**
+ * Align start address of a flash write region according to bank requirements.
+ * @param bank Pointer to bank descriptor structure
+ * @param addr Address to align
+ * @returns Aligned address
+*/
+target_addr_t flash_write_align_start(struct flash_bank *bank, target_addr_t addr);
+/**
+ * Align end address of a flash write region according to bank requirements.
+ * Note: Use address of the last byte to write, not the next after the region.
+ * @param bank Pointer to bank descriptor structure
+ * @param addr Address to align (address of the last byte to write)
+ * @returns Aligned address (address of the last byte of padded region)
+*/
+target_addr_t flash_write_align_end(struct flash_bank *bank, target_addr_t addr);
+
+/**
* Writes @a image into the @a target flash. The @a written parameter
* will contain the
* @param target The target with the flash to be programmed.
@@ -153,8 +188,15 @@ int flash_write(struct target *target,
* This routine must be called when the system may modify the status.
*/
void flash_set_dirty(void);
+
/** @returns The number of flash banks currently defined. */
int flash_get_bank_count(void);
+
+/** Deallocates bank->driver_priv */
+void default_flash_free_driver_priv(struct flash_bank *bank);
+
+/** Deallocates all flash banks */
+void flash_free_all_banks(void);
/**
* Provides default read implementation for flash memory.
* @param bank The bank to read.
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index 0ae4d8e..e7b3234 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -209,6 +209,14 @@ struct flash_driver {
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*auto_probe)(struct flash_bank *bank);
+
+ /**
+ * Deallocates private driver structures.
+ * Use default_flash_free_driver_priv() to simply free(bank->driver_priv)
+ *
+ * @param bank - the bank being destroyed
+ */
+ void (*free_driver_priv)(struct flash_bank *bank);
};
#define FLASH_BANK_COMMAND_HANDLER(name) \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 8168011..2b3146d 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -31,6 +31,9 @@ extern struct flash_driver at91samd_flash;
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 cc26xx_flash;
extern struct flash_driver cfi_flash;
extern struct flash_driver dsp5680xx_flash;
extern struct flash_driver efm32_flash;
@@ -48,6 +51,7 @@ extern struct flash_driver lpc2900_flash;
extern struct flash_driver lpcspifi_flash;
extern struct flash_driver mdr_flash;
extern struct flash_driver mrvlqspi_flash;
+extern struct flash_driver msp432_flash;
extern struct flash_driver niietcm4_flash;
extern struct flash_driver nrf5_flash;
extern struct flash_driver nrf51_flash;
@@ -55,6 +59,10 @@ 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;
extern struct flash_driver stm32f1x_flash;
@@ -88,6 +96,9 @@ static struct flash_driver *flash_drivers[] = {
&ath79_flash,
&atsamv_flash,
&avr_flash,
+ &bluenrgx_flash,
+ &cc3220sf_flash,
+ &cc26xx_flash,
&cfi_flash,
&dsp5680xx_flash,
&efm32_flash,
@@ -105,6 +116,7 @@ static struct flash_driver *flash_drivers[] = {
&lpcspifi_flash,
&mdr_flash,
&mrvlqspi_flash,
+ &msp432_flash,
&niietcm4_flash,
&nrf5_flash,
&nrf51_flash,
@@ -112,6 +124,10 @@ 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,
&stm32f1x_flash,
diff --git a/src/flash/nor/efm32.c b/src/flash/nor/efm32.c
index b8453e1..1d70bd5 100644
--- a/src/flash/nor/efm32.c
+++ b/src/flash/nor/efm32.c
@@ -38,19 +38,8 @@
#include <target/armv7m.h>
#include <target/cortex_m.h>
-/* keep family IDs in decimal */
-#define EFM_FAMILY_ID_GECKO 71
#define EFM_FAMILY_ID_GIANT_GECKO 72
-#define EFM_FAMILY_ID_TINY_GECKO 73
#define EFM_FAMILY_ID_LEOPARD_GECKO 74
-#define EFM_FAMILY_ID_WONDER_GECKO 75
-#define EFM_FAMILY_ID_ZERO_GECKO 76
-#define EFM_FAMILY_ID_HAPPY_GECKO 77
-#define EZR_FAMILY_ID_WONDER_GECKO 120
-#define EZR_FAMILY_ID_LEOPARD_GECKO 121
-#define EZR_FAMILY_ID_HAPPY_GECKO 122
-#define EFR_FAMILY_ID_MIGHTY_GECKO 16
-#define EFR_FAMILY_ID_BLUE_GECKO 20
#define EFM32_FLASH_ERASE_TMO 100
#define EFM32_FLASH_WDATAREADY_TMO 100
@@ -65,7 +54,7 @@
#define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000)
#define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000)
-/* PAGE_SIZE is only present in Leopard, Giant and Wonder Gecko MCUs */
+/* PAGE_SIZE is not present in Zero, Happy and the original Gecko MCU */
#define EFM32_MSC_DI_PAGE_SIZE (EFM32_MSC_DEV_INFO+0x1e7)
#define EFM32_MSC_DI_FLASH_SZ (EFM32_MSC_DEV_INFO+0x1f8)
#define EFM32_MSC_DI_RAM_SZ (EFM32_MSC_DEV_INFO+0x1fa)
@@ -74,7 +63,7 @@
#define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff)
#define EFM32_MSC_REGBASE 0x400c0000
-#define EFR32_MSC_REGBASE 0x400e0000
+#define EFM32_MSC_REGBASE_SERIES1 0x400e0000
#define EFM32_MSC_REG_WRITECTRL 0x008
#define EFM32_MSC_WRITECTRL_WREN_MASK 0x1
#define EFM32_MSC_REG_WRITECMD 0x00c
@@ -91,9 +80,24 @@
#define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10
#define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20
#define EFM32_MSC_REG_LOCK 0x03c
-#define EFR32_MSC_REG_LOCK 0x040
+#define EFM32_MSC_REG_LOCK_SERIES1 0x040
#define EFM32_MSC_LOCK_LOCKKEY 0x1b71
+struct efm32_family_data {
+ int family_id;
+ const char *name;
+
+ /* EFM32 series (EFM32LG995F is the "old" series 0, while EFR32MG12P132
+ is the "new" series 1). Determines location of MSC registers. */
+ int series;
+
+ /* Page size in bytes, or 0 to read from EFM32_MSC_DI_PAGE_SIZE */
+ int page_size;
+
+ /* MSC register base address, or 0 to use default */
+ uint32_t msc_regbase;
+};
+
struct efm32x_flash_bank {
int probed;
uint32_t lb_page[LOCKBITS_PAGE_SZ/4];
@@ -102,6 +106,7 @@ struct efm32x_flash_bank {
};
struct efm32_info {
+ const struct efm32_family_data *family_data;
uint16_t flash_sz_kib;
uint16_t ram_sz_kib;
uint16_t part_num;
@@ -110,6 +115,64 @@ struct efm32_info {
uint16_t page_size;
};
+static const struct efm32_family_data efm32_families[] = {
+ { 16, "EFR32MG1P Mighty", .series = 1 },
+ { 17, "EFR32MG1B Mighty", .series = 1 },
+ { 18, "EFR32MG1V Mighty", .series = 1 },
+ { 19, "EFR32MG1P Blue", .series = 1 },
+ { 20, "EFR32MG1B Blue", .series = 1 },
+ { 21, "EFR32MG1V Blue", .series = 1 },
+ { 25, "EFR32FG1P Flex", .series = 1 },
+ { 26, "EFR32FG1B Flex", .series = 1 },
+ { 27, "EFR32FG1V Flex", .series = 1 },
+ { 28, "EFR32MG2P Mighty", .series = 1 },
+ { 29, "EFR32MG2B Mighty", .series = 1 },
+ { 30, "EFR32MG2V Mighty", .series = 1 },
+ { 31, "EFR32BG12P Blue", .series = 1 },
+ { 32, "EFR32BG12B Blue", .series = 1 },
+ { 33, "EFR32BG12V Blue", .series = 1 },
+ { 37, "EFR32FG12P Flex", .series = 1 },
+ { 38, "EFR32FG12B Flex", .series = 1 },
+ { 39, "EFR32FG12V Flex", .series = 1 },
+ { 40, "EFR32MG13P Mighty", .series = 1 },
+ { 41, "EFR32MG13B Mighty", .series = 1 },
+ { 42, "EFR32MG13V Mighty", .series = 1 },
+ { 43, "EFR32BG13P Blue", .series = 1 },
+ { 44, "EFR32BG13B Blue", .series = 1 },
+ { 45, "EFR32BG13V Blue", .series = 1 },
+ { 49, "EFR32FG13P Flex", .series = 1 },
+ { 50, "EFR32FG13B Flex", .series = 1 },
+ { 51, "EFR32FG13V Flex", .series = 1 },
+ { 52, "EFR32MG14P Mighty", .series = 1 },
+ { 53, "EFR32MG14B Mighty", .series = 1 },
+ { 54, "EFR32MG14V Mighty", .series = 1 },
+ { 55, "EFR32BG14P Blue", .series = 1 },
+ { 56, "EFR32BG14B Blue", .series = 1 },
+ { 57, "EFR32BG14V Blue", .series = 1 },
+ { 61, "EFR32FG14P Flex", .series = 1 },
+ { 62, "EFR32FG14B Flex", .series = 1 },
+ { 63, "EFR32FG14V Flex", .series = 1 },
+ { 71, "EFM32G", .series = 0, .page_size = 512 },
+ { 72, "EFM32GG Giant", .series = 0 },
+ { 73, "EFM32TG Tiny", .series = 0, .page_size = 512 },
+ { 74, "EFM32LG Leopard", .series = 0 },
+ { 75, "EFM32WG Wonder", .series = 0 },
+ { 76, "EFM32ZG Zero", .series = 0, .page_size = 1024 },
+ { 77, "EFM32HG Happy", .series = 0, .page_size = 1024 },
+ { 81, "EFM32PG1B Pearl", .series = 1 },
+ { 83, "EFM32JG1B Jade", .series = 1 },
+ { 85, "EFM32PG12B Pearl", .series = 1 },
+ { 87, "EFM32JG12B Jade", .series = 1 },
+ { 89, "EFM32PG13B Pearl", .series = 1 },
+ { 91, "EFM32JG13B Jade", .series = 1 },
+ { 100, "EFM32GG11B Giant", .series = 1, .msc_regbase = 0x40000000 },
+ { 103, "EFM32TG11B Tiny", .series = 1 },
+ { 120, "EZR32WG Wonder", .series = 0 },
+ { 121, "EZR32LG Leopard", .series = 0 },
+ { 122, "EZR32HG Happy", .series = 0, .page_size = 1024 },
+};
+
+
static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count);
@@ -200,51 +263,33 @@ static int efm32x_read_info(struct flash_bank *bank,
if (ERROR_OK != ret)
return ret;
- if (EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family ||
- EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) {
- efm32x_info->reg_base = EFR32_MSC_REGBASE;
- efm32x_info->reg_lock = EFR32_MSC_REG_LOCK;
- } else {
- efm32x_info->reg_base = EFM32_MSC_REGBASE;
- efm32x_info->reg_lock = EFM32_MSC_REG_LOCK;
+ for (size_t i = 0; i < ARRAY_SIZE(efm32_families); i++) {
+ if (efm32_families[i].family_id == efm32_info->part_family)
+ efm32_info->family_data = &efm32_families[i];
}
- if (EFM_FAMILY_ID_GECKO == efm32_info->part_family ||
- EFM_FAMILY_ID_TINY_GECKO == efm32_info->part_family)
- efm32_info->page_size = 512;
- else if (EFM_FAMILY_ID_ZERO_GECKO == efm32_info->part_family ||
- EFM_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family ||
- EZR_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family)
- efm32_info->page_size = 1024;
- else if (EFM_FAMILY_ID_GIANT_GECKO == efm32_info->part_family ||
- EFM_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family) {
- if (efm32_info->prod_rev >= 18) {
- uint8_t pg_size = 0;
- ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
- &pg_size);
- if (ERROR_OK != ret)
- return ret;
-
- efm32_info->page_size = (1 << ((pg_size+10) & 0xff));
- } else {
- /* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid
- for MCUs with PROD_REV < 18 */
- if (efm32_info->flash_sz_kib < 512)
- efm32_info->page_size = 2048;
- else
- efm32_info->page_size = 4096;
- }
+ if (efm32_info->family_data == NULL) {
+ LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
+ return ERROR_FAIL;
+ }
- if ((2048 != efm32_info->page_size) &&
- (4096 != efm32_info->page_size)) {
- LOG_ERROR("Invalid page size %u", efm32_info->page_size);
- return ERROR_FAIL;
- }
- } else if (EFM_FAMILY_ID_WONDER_GECKO == efm32_info->part_family ||
- EZR_FAMILY_ID_WONDER_GECKO == efm32_info->part_family ||
- EZR_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family ||
- EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family ||
- EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) {
+ switch (efm32_info->family_data->series) {
+ case 0:
+ efm32x_info->reg_base = EFM32_MSC_REGBASE;
+ efm32x_info->reg_lock = EFM32_MSC_REG_LOCK;
+ break;
+ case 1:
+ efm32x_info->reg_base = EFM32_MSC_REGBASE_SERIES1;
+ efm32x_info->reg_lock = EFM32_MSC_REG_LOCK_SERIES1;
+ break;
+ }
+
+ if (efm32_info->family_data->msc_regbase != 0)
+ efm32x_info->reg_base = efm32_info->family_data->msc_regbase;
+
+ if (efm32_info->family_data->page_size != 0) {
+ efm32_info->page_size = efm32_info->family_data->page_size;
+ } else {
uint8_t pg_size = 0;
ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
&pg_size);
@@ -252,13 +297,25 @@ static int efm32x_read_info(struct flash_bank *bank,
return ret;
efm32_info->page_size = (1 << ((pg_size+10) & 0xff));
- if (2048 != efm32_info->page_size) {
+
+ if (efm32_info->part_family == EFM_FAMILY_ID_GIANT_GECKO ||
+ efm32_info->part_family == EFM_FAMILY_ID_LEOPARD_GECKO) {
+ /* Giant or Leopard Gecko */
+ if (efm32_info->prod_rev < 18) {
+ /* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid
+ for MCUs with PROD_REV < 18 */
+ if (efm32_info->flash_sz_kib < 512)
+ efm32_info->page_size = 2048;
+ else
+ efm32_info->page_size = 4096;
+ }
+ }
+
+ if ((efm32_info->page_size != 2048) &&
+ (efm32_info->page_size != 4096)) {
LOG_ERROR("Invalid page size %u", efm32_info->page_size);
return ERROR_FAIL;
}
- } else {
- LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
- return ERROR_FAIL;
}
return ERROR_OK;
@@ -270,71 +327,10 @@ static int efm32x_read_info(struct flash_bank *bank,
static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size)
{
int printed = 0;
+ printed = snprintf(buf, buf_size, "%s Gecko, rev %d",
+ info->family_data->name, info->prod_rev);
- switch (info->part_family) {
- case EZR_FAMILY_ID_WONDER_GECKO:
- case EZR_FAMILY_ID_LEOPARD_GECKO:
- case EZR_FAMILY_ID_HAPPY_GECKO:
- printed = snprintf(buf, buf_size, "EZR32 ");
- break;
- case EFR_FAMILY_ID_MIGHTY_GECKO:
- case EFR_FAMILY_ID_BLUE_GECKO:
- printed = snprintf(buf, buf_size, "EFR32 ");
- break;
- default:
- printed = snprintf(buf, buf_size, "EFM32 ");
- }
-
- buf += printed;
- buf_size -= printed;
-
- if (0 >= buf_size)
- return ERROR_BUF_TOO_SMALL;
-
- switch (info->part_family) {
- case EFM_FAMILY_ID_GECKO:
- printed = snprintf(buf, buf_size, "Gecko");
- break;
- case EFM_FAMILY_ID_GIANT_GECKO:
- printed = snprintf(buf, buf_size, "Giant Gecko");
- break;
- case EFM_FAMILY_ID_TINY_GECKO:
- printed = snprintf(buf, buf_size, "Tiny Gecko");
- break;
- case EFM_FAMILY_ID_LEOPARD_GECKO:
- case EZR_FAMILY_ID_LEOPARD_GECKO:
- printed = snprintf(buf, buf_size, "Leopard Gecko");
- break;
- case EFM_FAMILY_ID_WONDER_GECKO:
- case EZR_FAMILY_ID_WONDER_GECKO:
- printed = snprintf(buf, buf_size, "Wonder Gecko");
- break;
- case EFM_FAMILY_ID_ZERO_GECKO:
- printed = snprintf(buf, buf_size, "Zero Gecko");
- break;
- case EFM_FAMILY_ID_HAPPY_GECKO:
- case EZR_FAMILY_ID_HAPPY_GECKO:
- printed = snprintf(buf, buf_size, "Happy Gecko");
- break;
- case EFR_FAMILY_ID_BLUE_GECKO:
- printed = snprintf(buf, buf_size, "Blue Gecko");
- break;
- case EFR_FAMILY_ID_MIGHTY_GECKO:
- printed = snprintf(buf, buf_size, "Mighty Gecko");
- break;
- }
-
- buf += printed;
- buf_size -= printed;
-
- if (0 >= buf_size)
- return ERROR_BUF_TOO_SMALL;
-
- printed = snprintf(buf, buf_size, " - Rev: %d", info->prod_rev);
- buf += printed;
- buf_size -= printed;
-
- if (0 >= buf_size)
+ if (printed >= buf_size)
return ERROR_BUF_TOO_SMALL;
return ERROR_OK;
@@ -522,7 +518,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
}
}
- /* also, read ULW, DLW and MLW */
+ /* also, read ULW, DLW, MLW, ALW and CLW words */
/* ULW, word 126 */
ptr = efm32x_info->lb_page + 126;
@@ -540,7 +536,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
return ret;
}
- /* MLW, word 125, present in GG and LG */
+ /* MLW, word 125, present in GG, LG, PG, JG, EFR32 */
ptr = efm32x_info->lb_page + 125;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+125*4, ptr);
if (ERROR_OK != ret) {
@@ -548,6 +544,30 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
return ret;
}
+ /* ALW, word 124, present in GG, LG, PG, JG, EFR32 */
+ ptr = efm32x_info->lb_page + 124;
+ ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+124*4, ptr);
+ if (ERROR_OK != ret) {
+ LOG_ERROR("Failed to read ALW");
+ return ret;
+ }
+
+ /* CLW1, word 123, present in EFR32 */
+ ptr = efm32x_info->lb_page + 123;
+ ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+123*4, ptr);
+ if (ERROR_OK != ret) {
+ LOG_ERROR("Failed to read CLW1");
+ return ret;
+ }
+
+ /* CLW0, word 122, present in GG, LG, PG, JG, EFR32 */
+ ptr = efm32x_info->lb_page + 122;
+ ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+122*4, ptr);
+ if (ERROR_OK != ret) {
+ LOG_ERROR("Failed to read CLW0");
+ return ret;
+ }
+
return ERROR_OK;
}
@@ -1113,4 +1133,5 @@ struct flash_driver efm32_flash = {
.erase_check = default_flash_blank_check,
.protect_check = efm32x_protect_check,
.info = get_efm32x_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/em357.c b/src/flash/nor/em357.c
index a11743b..b14e032 100644
--- a/src/flash/nor/em357.c
+++ b/src/flash/nor/em357.c
@@ -941,4 +941,5 @@ struct flash_driver em357_flash = {
.auto_probe = em357_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = em357_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/faux.c b/src/flash/nor/faux.c
index 203eb6f..46eda72 100644
--- a/src/flash/nor/faux.c
+++ b/src/flash/nor/faux.c
@@ -136,5 +136,6 @@ struct flash_driver faux_flash = {
.auto_probe = faux_probe,
.erase_check = default_flash_blank_check,
.protect_check = faux_protect_check,
- .info = faux_info
+ .info = faux_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c
index cedcb3b..6667d36 100644
--- a/src/flash/nor/fespi.c
+++ b/src/flash/nor/fespi.c
@@ -436,6 +436,12 @@ static int slow_fespi_write_buffer(struct flash_bank *bank,
uint32_t ctrl_base = fespi_info->ctrl_base;
uint32_t ii;
+ if (offset & 0xFF000000) {
+ LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x",
+ offset);
+ return ERROR_FAIL;
+ }
+
/* TODO!!! assert that len < page size */
fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
@@ -607,11 +613,11 @@ struct algorithm_steps {
uint8_t **steps;
};
-static struct algorithm_steps *as_new(unsigned size)
+static struct algorithm_steps *as_new(void)
{
struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps));
- as->size = size;
- as->steps = calloc(size, sizeof(as->steps[0]));
+ as->size = 8;
+ as->steps = malloc(as->size * sizeof(as->steps[0]));
return as;
}
@@ -701,17 +707,27 @@ static unsigned as_compile(struct algorithm_steps *as, uint8_t *target,
return offset;
}
+static void as_add_step(struct algorithm_steps *as, uint8_t *step)
+{
+ if (as->used == as->size) {
+ as->size *= 2;
+ as->steps = realloc(as->steps, sizeof(as->steps[0]) * as->size);
+ LOG_DEBUG("Increased size to 0x%x", as->size);
+ }
+ as->steps[as->used] = step;
+ as->used++;
+}
+
static void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data)
{
LOG_DEBUG("count=%d", count);
while (count > 0) {
unsigned step_count = MIN(count, 255);
- assert(as->used < as->size);
- as->steps[as->used] = malloc(step_count + 2);
- as->steps[as->used][0] = STEP_TX;
- as->steps[as->used][1] = step_count;
- memcpy(as->steps[as->used] + 2, data, step_count);
- as->used++;
+ uint8_t *step = malloc(step_count + 2);
+ step[0] = STEP_TX;
+ step[1] = step_count;
+ memcpy(step + 2, data, step_count);
+ as_add_step(as, step);
data += step_count;
count -= step_count;
}
@@ -726,43 +742,45 @@ static void as_add_tx1(struct algorithm_steps *as, uint8_t byte)
static void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data)
{
- assert(as->used < as->size);
- as->steps[as->used] = malloc(3);
- as->steps[as->used][0] = STEP_WRITE_REG;
- as->steps[as->used][1] = offset;
- as->steps[as->used][2] = data;
- as->used++;
+ uint8_t *step = malloc(3);
+ step[0] = STEP_WRITE_REG;
+ step[1] = offset;
+ step[2] = data;
+ as_add_step(as, step);
}
static void as_add_txwm_wait(struct algorithm_steps *as)
{
- assert(as->used < as->size);
- as->steps[as->used] = malloc(1);
- as->steps[as->used][0] = STEP_TXWM_WAIT;
- as->used++;
+ uint8_t *step = malloc(1);
+ step[0] = STEP_TXWM_WAIT;
+ as_add_step(as, step);
}
static void as_add_wip_wait(struct algorithm_steps *as)
{
- assert(as->used < as->size);
- as->steps[as->used] = malloc(1);
- as->steps[as->used][0] = STEP_WIP_WAIT;
- as->used++;
+ uint8_t *step = malloc(1);
+ step[0] = STEP_WIP_WAIT;
+ as_add_step(as, step);
}
static void as_add_set_dir(struct algorithm_steps *as, bool dir)
{
- assert(as->used < as->size);
- as->steps[as->used] = malloc(2);
- as->steps[as->used][0] = STEP_SET_DIR;
- as->steps[as->used][1] = FESPI_FMT_DIR(dir);
- as->used++;
+ uint8_t *step = malloc(2);
+ step[0] = STEP_SET_DIR;
+ step[1] = FESPI_FMT_DIR(dir);
+ as_add_step(as, step);
}
/* This should write something less than or equal to a page.*/
static int steps_add_buffer_write(struct algorithm_steps *as,
const uint8_t *buffer, uint32_t chip_offset, uint32_t len)
{
+ if (chip_offset & 0xFF000000) {
+ LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x",
+ chip_offset);
+ return ERROR_FAIL;
+ }
+
as_add_tx1(as, SPIFLASH_WRITE_ENABLE);
as_add_txwm_wait(as);
as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
@@ -910,7 +928,7 @@ static int fespi_write(struct flash_bank *bank, const uint8_t *buffer,
if (retval != ERROR_OK)
return retval;
- struct algorithm_steps *as = as_new(count / 4);
+ struct algorithm_steps *as = as_new();
/* unaligned buffer head */
if (count > 0 && (offset & 3) != 0) {
@@ -1161,5 +1179,6 @@ struct flash_driver fespi_flash = {
.auto_probe = fespi_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = fespi_protect_check,
- .info = get_fespi_info
+ .info = get_fespi_info,
+ .free_driver_priv = default_flash_free_driver_priv
};
diff --git a/src/flash/nor/fm3.c b/src/flash/nor/fm3.c
index 6269a65..6c61977 100644
--- a/src/flash/nor/fm3.c
+++ b/src/flash/nor/fm3.c
@@ -997,4 +997,5 @@ struct flash_driver fm3_flash = {
.probe = fm3_probe,
.auto_probe = fm3_auto_probe,
.erase_check = default_flash_blank_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/fm4.c b/src/flash/nor/fm4.c
index c8fe8b6..f5eab9c 100644
--- a/src/flash/nor/fm4.c
+++ b/src/flash/nor/fm4.c
@@ -719,4 +719,5 @@ struct flash_driver fm4_flash = {
.erase = fm4_flash_erase,
.erase_check = default_flash_blank_check,
.write = fm4_flash_write,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c
index a73812d..c28ad22 100644
--- a/src/flash/nor/jtagspi.c
+++ b/src/flash/nor/jtagspi.c
@@ -432,5 +432,6 @@ struct flash_driver jtagspi_flash = {
.auto_probe = jtagspi_probe,
.erase_check = default_flash_blank_check,
.protect_check = jtagspi_protect_check,
- .info = jtagspi_info
+ .info = jtagspi_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/kinetis.c b/src/flash/nor/kinetis.c
index 48a5de4..86fad72 100644
--- a/src/flash/nor/kinetis.c
+++ b/src/flash/nor/kinetis.c
@@ -915,13 +915,29 @@ FLASH_BANK_COMMAND_HANDLER(kinetis_flash_bank_command)
}
+static void kinetis_free_driver_priv(struct flash_bank *bank)
+{
+ struct kinetis_flash_bank *k_bank = bank->driver_priv;
+ if (k_bank == NULL)
+ return;
+
+ struct kinetis_chip *k_chip = k_bank->k_chip;
+ if (k_chip == NULL)
+ return;
+
+ k_chip->num_banks--;
+ if (k_chip->num_banks == 0)
+ free(k_chip);
+}
+
+
static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
{
unsigned bank_idx;
unsigned num_blocks;
struct kinetis_flash_bank *k_bank;
struct flash_bank *bank;
- char base_name[80], name[80], num[4];
+ char base_name[69], name[80], num[4];
char *class, *p;
num_blocks = k_chip->num_pflash_blocks + k_chip->num_nvm_blocks;
@@ -932,19 +948,21 @@ static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
bank = k_chip->banks[0].bank;
if (bank && bank->name) {
- strncpy(base_name, bank->name, sizeof(base_name));
+ strncpy(base_name, bank->name, sizeof(base_name) - 1);
+ base_name[sizeof(base_name) - 1] = '\0';
p = strstr(base_name, ".pflash");
if (p) {
*p = '\0';
if (k_chip->num_pflash_blocks > 1) {
/* rename first bank if numbering is needed */
snprintf(name, sizeof(name), "%s.pflash0", base_name);
- free((void *)bank->name);
+ free(bank->name);
bank->name = strdup(name);
}
}
} else {
- strncpy(base_name, target_name(k_chip->target), sizeof(base_name));
+ strncpy(base_name, target_name(k_chip->target), sizeof(base_name) - 1);
+ base_name[sizeof(base_name) - 1] = '\0';
p = strstr(base_name, ".cpu");
if (p)
*p = '\0';
@@ -1996,7 +2014,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
unsigned cpu_mhz = 120;
unsigned idx;
bool use_nvm_marking = false;
- char flash_marking[11], nvm_marking[2];
+ char flash_marking[12], nvm_marking[2];
char name[40];
k_chip->probed = false;
@@ -3132,4 +3150,5 @@ struct flash_driver kinetis_flash = {
.erase_check = kinetis_blank_check,
.protect_check = kinetis_protect_check,
.info = kinetis_info,
+ .free_driver_priv = kinetis_free_driver_priv,
};
diff --git a/src/flash/nor/kinetis_ke.c b/src/flash/nor/kinetis_ke.c
index 636fcc2..8103b63 100644
--- a/src/flash/nor/kinetis_ke.c
+++ b/src/flash/nor/kinetis_ke.c
@@ -478,14 +478,13 @@ int kinetis_ke_stop_watchdog(struct target *target)
watchdog_algorithm->address, 0, 100000, &armv7m_info);
if (retval != ERROR_OK) {
LOG_ERROR("Error executing Kinetis KE watchdog algorithm");
- retval = ERROR_FAIL;
} else {
LOG_INFO("Watchdog stopped");
}
target_free_working_area(target, watchdog_algorithm);
- return ERROR_OK;
+ return retval;
}
COMMAND_HANDLER(kinetis_ke_disable_wdog_handler)
@@ -1311,4 +1310,5 @@ struct flash_driver kinetis_ke_flash = {
.erase_check = kinetis_ke_blank_check,
.protect_check = kinetis_ke_protect_check,
.info = kinetis_ke_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/lpc2000.c b/src/flash/nor/lpc2000.c
index 9da5da2..8e15c31 100644
--- a/src/flash/nor/lpc2000.c
+++ b/src/flash/nor/lpc2000.c
@@ -1579,4 +1579,5 @@ struct flash_driver lpc2000_flash = {
.erase_check = lpc2000_erase_check,
.protect_check = lpc2000_protect_check,
.info = get_lpc2000_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/lpc288x.c b/src/flash/nor/lpc288x.c
index a4d88de..2472913 100644
--- a/src/flash/nor/lpc288x.c
+++ b/src/flash/nor/lpc288x.c
@@ -433,4 +433,5 @@ struct flash_driver lpc288x_flash = {
.auto_probe = lpc288x_probe,
.erase_check = lpc288x_erase_check,
.protect_check = lpc288x_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/lpc2900.c b/src/flash/nor/lpc2900.c
index 515a3f7..1c65933 100644
--- a/src/flash/nor/lpc2900.c
+++ b/src/flash/nor/lpc2900.c
@@ -1598,4 +1598,5 @@ struct flash_driver lpc2900_flash = {
.auto_probe = lpc2900_probe,
.erase_check = lpc2900_erase_check,
.protect_check = lpc2900_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/lpcspifi.c b/src/flash/nor/lpcspifi.c
index 943c151..828c60c 100644
--- a/src/flash/nor/lpcspifi.c
+++ b/src/flash/nor/lpcspifi.c
@@ -942,4 +942,5 @@ struct flash_driver lpcspifi_flash = {
.erase_check = default_flash_blank_check,
.protect_check = lpcspifi_protect_check,
.info = get_lpcspifi_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/mdr.c b/src/flash/nor/mdr.c
index 8ceb1bf..f3916de 100644
--- a/src/flash/nor/mdr.c
+++ b/src/flash/nor/mdr.c
@@ -633,4 +633,5 @@ struct flash_driver mdr_flash = {
.erase_check = default_flash_blank_check,
.protect_check = mdr_protect_check,
.info = get_mdr_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/mrvlqspi.c b/src/flash/nor/mrvlqspi.c
index d799170..eda6cc1 100644
--- a/src/flash/nor/mrvlqspi.c
+++ b/src/flash/nor/mrvlqspi.c
@@ -955,4 +955,5 @@ struct flash_driver mrvlqspi_flash = {
.erase_check = mrvlqspi_flash_erase_check,
.protect_check = mrvlqspi_protect_check,
.info = mrvlqspi_get_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/msp432.c b/src/flash/nor/msp432.c
new file mode 100644
index 0000000..5caa052
--- /dev/null
+++ b/src/flash/nor/msp432.c
@@ -0,0 +1,1103 @@
+/***************************************************************************
+ * Copyright (C) 2018 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 "msp432.h"
+#include <helper/binarybuffer.h>
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+#include <target/image.h>
+
+/* MSP432P4 hardware registers */
+#define P4_FLASH_MAIN_SIZE_REG 0xE0043020
+#define P4_FLASH_INFO_SIZE_REG 0xE0043024
+#define P4_DEVICE_ID_REG 0x0020100C
+#define P4_HARDWARE_REV_REG 0x00201010
+
+/* MSP432E4 hardware registers */
+#define E4_DID0_REG 0x400FE000
+#define E4_DID1_REG 0x400FE004
+
+#define FLASH_TIMEOUT 8000
+
+#define SUPPORT_MESSAGE \
+ "Your pre-production MSP432P401x silicon is not fully supported\n" \
+ "You can find more information at www.ti.com/product/MSP432P401R"
+
+struct msp432_bank {
+ uint32_t device_id;
+ uint32_t hardware_rev;
+ int family_type;
+ int device_type;
+ uint32_t sector_length;
+ bool probed[2];
+ bool unlock_bsl;
+ struct working_area *working_area;
+ struct armv7m_algorithm armv7m_info;
+};
+
+static int msp432_auto_probe(struct flash_bank *bank);
+
+static int msp432_device_type(uint32_t family_type, uint32_t device_id,
+ uint32_t hardware_rev)
+{
+ int device_type = MSP432_NO_TYPE;
+
+ if (MSP432E4 == family_type) {
+ /* MSP432E4 device family */
+
+ if (device_id == 0x180C0002) {
+ if (hardware_rev == 0x102DC06E) {
+ /* The 01Y variant */
+ device_type = MSP432E401Y;
+ } else if (hardware_rev == 0x1032E076) {
+ /* The 11Y variant */
+ device_type = MSP432E411Y;
+ } else {
+ /* Reasonable guess that this is a new variant */
+ device_type = MSP432E4X_GUESS;
+ }
+ } else {
+ /* Wild guess that this is an MSP432E4 */
+ device_type = MSP432E4X_GUESS;
+ }
+ } else {
+ /* MSP432P4 device family */
+
+ /* Examine the device ID and hardware revision to get the device type */
+ switch (device_id) {
+ case 0xA000:
+ case 0xA001:
+ case 0xA002:
+ case 0xA003:
+ case 0xA004:
+ case 0xA005:
+ /* Device is definitely MSP432P401x, check hardware revision */
+ if (hardware_rev == 0x41 || hardware_rev == 0x42) {
+ /* Rev A or B of the silicon has been deprecated */
+ device_type = MSP432P401X_DEPR;
+ } else if (hardware_rev >= 0x43 && hardware_rev <= 0x49) {
+ /* Current and future revisions of the MSP432P401x device */
+ device_type = MSP432P401X;
+ } else {
+ /* Unknown or unanticipated hardware revision */
+ device_type = MSP432P401X_GUESS;
+ }
+ break;
+ case 0xA010:
+ case 0xA012:
+ case 0xA016:
+ case 0xA019:
+ case 0xA01F:
+ case 0xA020:
+ case 0xA022:
+ case 0xA026:
+ case 0xA029:
+ case 0xA02F:
+ /* Device is definitely MSP432P411x, check hardware revision */
+ if (hardware_rev >= 0x41 && hardware_rev <= 0x49) {
+ /* Current and future revisions of the MSP432P411x device */
+ device_type = MSP432P411X;
+ } else {
+ /* Unknown or unanticipated hardware revision */
+ device_type = MSP432P411X_GUESS;
+ }
+ break;
+ case 0xFFFF:
+ /* Device is very early silicon that has been deprecated */
+ device_type = MSP432P401X_DEPR;
+ break;
+ default:
+ if (device_id < 0xA010) {
+ /* Wild guess that this is an MSP432P401x */
+ device_type = MSP432P401X_GUESS;
+ } else {
+ /* Reasonable guess that this is a new variant */
+ device_type = MSP432P411X_GUESS;
+ }
+ break;
+ }
+ }
+
+ return device_type;
+}
+
+static const char *msp432_return_text(uint32_t return_code)
+{
+ switch (return_code) {
+ case FLASH_BUSY:
+ return "FLASH_BUSY";
+ case FLASH_SUCCESS:
+ return "FLASH_SUCCESS";
+ case FLASH_ERROR:
+ return "FLASH_ERROR";
+ case FLASH_TIMEOUT_ERROR:
+ return "FLASH_TIMEOUT_ERROR";
+ case FLASH_VERIFY_ERROR:
+ return "FLASH_VERIFY_WRONG";
+ case FLASH_WRONG_COMMAND:
+ return "FLASH_WRONG_COMMAND";
+ case FLASH_POWER_ERROR:
+ return "FLASH_POWER_ERROR";
+ default:
+ return "UNDEFINED_RETURN_CODE";
+ }
+}
+
+static void msp432_init_params(struct msp432_algo_params *algo_params)
+{
+ buf_set_u32(algo_params->flash_command, 0, 32, FLASH_NO_COMMAND);
+ buf_set_u32(algo_params->return_code, 0, 32, 0);
+ buf_set_u32(algo_params->_reserved0, 0, 32, 0);
+ buf_set_u32(algo_params->address, 0, 32, 0);
+ buf_set_u32(algo_params->length, 0, 32, 0);
+ buf_set_u32(algo_params->buffer1_status, 0, 32, BUFFER_INACTIVE);
+ buf_set_u32(algo_params->buffer2_status, 0, 32, BUFFER_INACTIVE);
+ buf_set_u32(algo_params->erase_param, 0, 32, FLASH_ERASE_MAIN);
+ buf_set_u32(algo_params->unlock_bsl, 0, 32, FLASH_LOCK_BSL);
+}
+
+static int msp432_exec_cmd(struct target *target, struct msp432_algo_params
+ *algo_params, uint32_t command)
+{
+ int retval;
+
+ /* Make sure the given params do not include the command */
+ buf_set_u32(algo_params->flash_command, 0, 32, FLASH_NO_COMMAND);
+ buf_set_u32(algo_params->return_code, 0, 32, 0);
+ buf_set_u32(algo_params->buffer1_status, 0, 32, BUFFER_INACTIVE);
+ buf_set_u32(algo_params->buffer2_status, 0, 32, BUFFER_INACTIVE);
+
+ /* Write out parameters to target memory */
+ retval = target_write_buffer(target, ALGO_PARAMS_BASE_ADDR,
+ sizeof(struct msp432_algo_params), (uint8_t *)algo_params);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Write out command to target memory */
+ retval = target_write_buffer(target, ALGO_FLASH_COMMAND_ADDR,
+ sizeof(command), (uint8_t *)&command);
+
+ return retval;
+}
+
+static int msp432_wait_return_code(struct target *target)
+{
+ uint32_t return_code = 0;
+ long long start_ms;
+ long long elapsed_ms;
+
+ int retval = ERROR_OK;
+
+ start_ms = timeval_ms();
+ while ((0 == return_code) || (FLASH_BUSY == return_code)) {
+ retval = target_read_buffer(target, ALGO_RETURN_CODE_ADDR,
+ sizeof(return_code), (uint8_t *)&return_code);
+ if (ERROR_OK != retval)
+ return retval;
+
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ if (elapsed_ms > FLASH_TIMEOUT)
+ break;
+ };
+
+ if (FLASH_SUCCESS != return_code) {
+ LOG_ERROR("msp432: Flash operation failed: %s",
+ msp432_return_text(return_code));
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int msp432_wait_inactive(struct target *target, uint32_t buffer)
+{
+ uint32_t status_code = BUFFER_ACTIVE;
+ uint32_t status_addr;
+ long long start_ms;
+ long long elapsed_ms;
+
+ int retval;
+
+ switch (buffer) {
+ case 1: /* Buffer 1 */
+ status_addr = ALGO_BUFFER1_STATUS_ADDR;
+ break;
+ case 2: /* Buffer 2 */
+ status_addr = ALGO_BUFFER2_STATUS_ADDR;
+ break;
+ default:
+ return ERROR_FAIL;
+ }
+
+ start_ms = timeval_ms();
+ while (BUFFER_INACTIVE != status_code) {
+ retval = target_read_buffer(target, status_addr, sizeof(status_code),
+ (uint8_t *)&status_code);
+ if (ERROR_OK != retval)
+ return retval;
+
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ if (elapsed_ms > FLASH_TIMEOUT)
+ break;
+ };
+
+ if (BUFFER_INACTIVE != status_code) {
+ LOG_ERROR(
+ "msp432: Flash operation failed: buffer not written to flash");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int msp432_init(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+ struct msp432_algo_params algo_params;
+ struct reg_param reg_params[1];
+
+ const uint8_t *loader_code;
+ uint32_t loader_size;
+ uint32_t algo_entry_addr;
+ int retval;
+
+ /* Make sure we've probed the flash to get the device and size */
+ retval = msp432_auto_probe(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Choose appropriate flash helper algorithm */
+ switch (msp432_bank->device_type) {
+ case MSP432P401X:
+ case MSP432P401X_DEPR:
+ case MSP432P401X_GUESS:
+ default:
+ loader_code = msp432p401x_algo;
+ loader_size = sizeof(msp432p401x_algo);
+ algo_entry_addr = P4_ALGO_ENTRY_ADDR;
+ break;
+ case MSP432P411X:
+ case MSP432P411X_GUESS:
+ loader_code = msp432p411x_algo;
+ loader_size = sizeof(msp432p411x_algo);
+ algo_entry_addr = P4_ALGO_ENTRY_ADDR;
+ break;
+ case MSP432E401Y:
+ case MSP432E411Y:
+ case MSP432E4X_GUESS:
+ loader_code = msp432e4x_algo;
+ loader_size = sizeof(msp432e4x_algo);
+ algo_entry_addr = E4_ALGO_ENTRY_ADDR;
+ break;
+ }
+
+ /* Issue warnings if this is a device we may not be able to flash */
+ if (MSP432P401X_GUESS == msp432_bank->device_type ||
+ MSP432P411X_GUESS == msp432_bank->device_type) {
+ /* Explicit device type check failed. Report this. */
+ LOG_WARNING(
+ "msp432: Unrecognized MSP432P4 Device ID and Hardware "
+ "Rev (%04X, %02X)", msp432_bank->device_id,
+ msp432_bank->hardware_rev);
+ } else if (MSP432P401X_DEPR == msp432_bank->device_type) {
+ LOG_WARNING(
+ "msp432: MSP432P401x pre-production device (deprecated "
+ "silicon)\n" SUPPORT_MESSAGE);
+ } else if (MSP432E4X_GUESS == msp432_bank->device_type) {
+ /* Explicit device type check failed. Report this. */
+ LOG_WARNING(
+ "msp432: Unrecognized MSP432E4 DID0 and DID1 values "
+ "(%08X, %08X)", msp432_bank->device_id,
+ msp432_bank->hardware_rev);
+ }
+
+ /* Check for working area to use for flash helper algorithm */
+ if (NULL != msp432_bank->working_area)
+ target_free_working_area(target, msp432_bank->working_area);
+ retval = target_alloc_working_area(target, ALGO_WORKING_SIZE,
+ &msp432_bank->working_area);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Confirm the defined working address is the area we need to use */
+ if (ALGO_BASE_ADDR != msp432_bank->working_area->address)
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+ /* Write flash helper algorithm into target memory */
+ retval = target_write_buffer(target, ALGO_BASE_ADDR, loader_size,
+ loader_code);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize the ARMv7 specific info to run the algorithm */
+ msp432_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ msp432_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
+
+ /* Initialize algorithm parameters to default values */
+ msp432_init_params(&algo_params);
+
+ /* Write out parameters to target memory */
+ retval = target_write_buffer(target, ALGO_PARAMS_BASE_ADDR,
+ sizeof(algo_params), (uint8_t *)&algo_params);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize stack pointer for flash helper algorithm */
+ init_reg_param(&reg_params[0], "sp", 32, PARAM_OUT);
+ buf_set_u32(reg_params[0].value, 0, 32, ALGO_STACK_POINTER_ADDR);
+
+ /* Begin executing the flash helper algorithm */
+ retval = target_start_algorithm(target, 0, 0, 1, reg_params,
+ algo_entry_addr, 0, &msp432_bank->armv7m_info);
+ destroy_reg_param(&reg_params[0]);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("msp432: Failed to start flash helper algorithm");
+ return retval;
+ }
+
+ /*
+ * At this point, the algorithm is running on the target and
+ * ready to receive commands and data to flash the target
+ */
+
+ /* Issue the init command to the flash helper algorithm */
+ retval = msp432_exec_cmd(target, &algo_params, FLASH_INIT);
+ if (ERROR_OK != retval)
+ return retval;
+
+ retval = msp432_wait_return_code(target);
+
+ return retval;
+}
+
+static int msp432_quit(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+ struct msp432_algo_params algo_params;
+
+ int retval;
+
+ /* Initialize algorithm parameters to default values */
+ msp432_init_params(&algo_params);
+
+ /* Issue the exit command to the flash helper algorithm */
+ retval = msp432_exec_cmd(target, &algo_params, FLASH_EXIT);
+ if (ERROR_OK != retval)
+ return retval;
+
+ (void)msp432_wait_return_code(target);
+
+ /* Regardless of the return code, attempt to halt the target */
+ (void)target_halt(target);
+
+ /* Now confirm target halted and clean up from flash helper algorithm */
+ retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT,
+ &msp432_bank->armv7m_info);
+
+ target_free_working_area(target, msp432_bank->working_area);
+ msp432_bank->working_area = NULL;
+
+ return retval;
+}
+
+static int msp432_mass_erase(struct flash_bank *bank, bool all)
+{
+ struct target *target = bank->target;
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+ struct msp432_algo_params algo_params;
+
+ int retval;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = msp432_init(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize algorithm parameters to default values */
+ msp432_init_params(&algo_params);
+ if (all) {
+ buf_set_u32(algo_params.erase_param, 0, 32,
+ FLASH_ERASE_MAIN | FLASH_ERASE_INFO);
+ if (msp432_bank->unlock_bsl)
+ buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL);
+ }
+
+ /* Issue the mass erase command to the flash helper algorithm */
+ retval = msp432_exec_cmd(target, &algo_params, FLASH_MASS_ERASE);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+
+ retval = msp432_wait_return_code(target);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+
+ retval = msp432_quit(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ return retval;
+}
+
+COMMAND_HANDLER(msp432_mass_erase_command)
+{
+ struct flash_bank *bank;
+ struct msp432_bank *msp432_bank;
+ bool all;
+ int retval;
+
+ if (0 == CMD_ARGC) {
+ all = false;
+ } else if (1 == CMD_ARGC) {
+ /* Check argument for how much to erase */
+ if (0 == strcmp(CMD_ARGV[0], "main"))
+ all = false;
+ else if (0 == strcmp(CMD_ARGV[0], "all"))
+ all = true;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ } else {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ retval = get_flash_bank_by_num(0, &bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ msp432_bank = bank->driver_priv;
+
+ if (MSP432E4 == msp432_bank->family_type) {
+ /* MSP432E4 does not have main vs info regions, ignore "all" */
+ all = false;
+ }
+
+ retval = msp432_mass_erase(bank, all);
+ if (ERROR_OK != retval)
+ return retval;
+
+ if (MSP432E4 == msp432_bank->family_type) {
+ /* MSP432E4 does not have main vs info regions */
+ LOG_INFO("msp432: Mass erase of flash is complete");
+ } else {
+ LOG_INFO("msp432: Mass erase of %s is complete",
+ all ? "main + info flash" : "main flash");
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(msp432_bsl_command)
+{
+ struct flash_bank *bank;
+ struct msp432_bank *msp432_bank;
+ int retval;
+
+ if (1 < CMD_ARGC)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ retval = get_flash_bank_by_num(0, &bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ msp432_bank = bank->driver_priv;
+
+ if (MSP432E4 == msp432_bank->family_type) {
+ LOG_WARNING("msp432: MSP432E4 does not have a BSL region");
+ return ERROR_OK;
+ }
+
+ if (1 == CMD_ARGC) {
+ if (0 == strcmp(CMD_ARGV[0], "lock"))
+ msp432_bank->unlock_bsl = false;
+ else if (0 == strcmp(CMD_ARGV[0], "unlock"))
+ msp432_bank->unlock_bsl = true;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ LOG_INFO("msp432: BSL flash region is currently %slocked",
+ msp432_bank->unlock_bsl ? "un" : "");
+
+ return ERROR_OK;
+}
+
+FLASH_BANK_COMMAND_HANDLER(msp432_flash_bank_command)
+{
+ struct msp432_bank *msp432_bank;
+
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ msp432_bank = malloc(sizeof(struct msp432_bank));
+ if (NULL == msp432_bank)
+ return ERROR_FAIL;
+
+ /* Initialize private flash information */
+ msp432_bank->device_id = 0;
+ msp432_bank->hardware_rev = 0;
+ msp432_bank->family_type = MSP432_NO_FAMILY;
+ msp432_bank->device_type = MSP432_NO_TYPE;
+ msp432_bank->sector_length = 0x1000;
+ msp432_bank->probed[0] = false;
+ msp432_bank->probed[1] = false;
+ msp432_bank->unlock_bsl = false;
+ msp432_bank->working_area = NULL;
+
+ /* Finish initialization of bank 0 (main flash) */
+ bank->driver_priv = msp432_bank;
+ bank->next = NULL;
+
+ return ERROR_OK;
+}
+
+static int msp432_erase(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+ struct msp432_algo_params algo_params;
+
+ int retval;
+
+ 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 main flash */
+ if ((0 == bank->bank_number) && (first == 0) &&
+ (last == (bank->num_sectors - 1))) {
+ /* Request mass erase of main flash */
+ return msp432_mass_erase(bank, false);
+ }
+
+ retval = msp432_init(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize algorithm parameters to default values */
+ msp432_init_params(&algo_params);
+
+ /* Adjust params if this is the info bank */
+ if (1 == bank->bank_number) {
+ buf_set_u32(algo_params.erase_param, 0, 32, FLASH_ERASE_INFO);
+ /* And flag if BSL is unlocked */
+ if (msp432_bank->unlock_bsl)
+ buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL);
+ }
+
+ /* Erase requested sectors one by one */
+ for (int i = first; i <= last; i++) {
+
+ /* Skip TVL (read-only) sector of the info bank */
+ if (1 == bank->bank_number && 1 == i)
+ continue;
+
+ /* Skip BSL sectors of info bank if locked */
+ if (1 == bank->bank_number && (2 == i || 3 == i) &&
+ !msp432_bank->unlock_bsl)
+ continue;
+
+ /* Convert sector number to starting address of sector */
+ buf_set_u32(algo_params.address, 0, 32, bank->base +
+ (i * msp432_bank->sector_length));
+
+ /* Issue the sector erase command to the flash helper algorithm */
+ retval = msp432_exec_cmd(target, &algo_params, FLASH_SECTOR_ERASE);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+
+ retval = msp432_wait_return_code(target);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+ }
+
+ retval = msp432_quit(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ return retval;
+}
+
+static int msp432_protect(struct flash_bank *bank, int set, int first,
+ int last)
+{
+ return ERROR_OK;
+}
+
+static int msp432_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+ struct msp432_algo_params algo_params;
+ uint32_t size;
+ uint32_t data_ready = BUFFER_DATA_READY;
+ long long start_ms;
+ long long elapsed_ms;
+
+ int retval;
+
+ if (TARGET_HALTED != target->state) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /*
+ * Block attempts to write to read-only sectors of flash
+ * The TVL region in sector 1 of the info flash is always read-only
+ * The BSL region in sectors 2 and 3 of the info flash may be unlocked
+ * The helper algorithm will hang on attempts to write to TVL
+ */
+ if (1 == bank->bank_number) {
+ /* Set read-only start to TVL sector */
+ uint32_t start = 0x1000;
+ /* Set read-only end after BSL region if locked */
+ uint32_t end = (msp432_bank->unlock_bsl) ? 0x2000 : 0x4000;
+ /* Check if request includes anything in read-only sectors */
+ if ((offset + count - 1) < start || offset >= end) {
+ /* The request includes no bytes in read-only sectors */
+ /* Fall out and process the request normally */
+ } else {
+ /* Send a request for anything before read-only sectors */
+ if (offset < start) {
+ uint32_t start_count = MIN(start - offset, count);
+ retval = msp432_write(bank, buffer, offset, start_count);
+ if (ERROR_OK != retval)
+ return retval;
+ }
+ /* Send a request for anything after read-only sectors */
+ if ((offset + count - 1) >= end) {
+ uint32_t skip = end - offset;
+ count -= skip;
+ offset += skip;
+ buffer += skip;
+ return msp432_write(bank, buffer, offset, count);
+ } else {
+ /* Request is entirely in read-only sectors */
+ return ERROR_OK;
+ }
+ }
+ }
+
+ retval = msp432_init(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ /* Initialize algorithm parameters to default values */
+ msp432_init_params(&algo_params);
+
+ /* Set up parameters for requested flash write operation */
+ buf_set_u32(algo_params.address, 0, 32, bank->base + offset);
+ buf_set_u32(algo_params.length, 0, 32, count);
+
+ /* Check if this is the info bank */
+ if (1 == bank->bank_number) {
+ /* And flag if BSL is unlocked */
+ if (msp432_bank->unlock_bsl)
+ buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL);
+ }
+
+ /* Set up flash helper algorithm to continuous flash mode */
+ retval = msp432_exec_cmd(target, &algo_params, FLASH_CONTINUOUS);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+
+ /* Write requested data, one buffer at a time */
+ start_ms = timeval_ms();
+ while (count > 0) {
+
+ if (count > ALGO_BUFFER_SIZE)
+ size = ALGO_BUFFER_SIZE;
+ else
+ size = count;
+
+ /* Put next block of data to flash into buffer */
+ retval = target_write_buffer(target, ALGO_BUFFER1_ADDR, size, buffer);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("Unable to write data to target memory");
+ (void)msp432_quit(bank);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* Signal the flash helper algorithm that data is ready to flash */
+ retval = target_write_buffer(target, ALGO_BUFFER1_STATUS_ADDR,
+ sizeof(data_ready), (uint8_t *)&data_ready);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ retval = msp432_wait_inactive(target, 1);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+
+ count -= size;
+ buffer += size;
+
+ elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ }
+
+ /* Confirm that the flash helper algorithm is finished */
+ retval = msp432_wait_return_code(target);
+ if (ERROR_OK != retval) {
+ (void)msp432_quit(bank);
+ return retval;
+ }
+
+ retval = msp432_quit(bank);
+ if (ERROR_OK != retval)
+ return retval;
+
+ return retval;
+}
+
+static int msp432_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+
+ char *name;
+
+ uint32_t device_id;
+ uint32_t hardware_rev;
+
+ uint32_t base;
+ uint32_t sector_length;
+ uint32_t size;
+ int num_sectors;
+ int bank_id;
+
+ int retval;
+
+ bank_id = bank->bank_number;
+
+ /* Read the flash size register to determine this is a P4 or not */
+ /* MSP432P4s will return the size of flash. MSP432E4s will return zero */
+ retval = target_read_u32(target, P4_FLASH_MAIN_SIZE_REG, &size);
+ if (ERROR_OK != retval)
+ return retval;
+
+ if (0 == size) {
+ /* This is likely an MSP432E4 */
+ msp432_bank->family_type = MSP432E4;
+
+ retval = target_read_u32(target, E4_DID0_REG, &device_id);
+ if (ERROR_OK != retval)
+ return retval;
+
+ msp432_bank->device_id = device_id;
+
+ retval = target_read_u32(target, E4_DID1_REG, &hardware_rev);
+ if (ERROR_OK != retval)
+ return retval;
+
+ msp432_bank->hardware_rev = hardware_rev;
+ } else {
+ /* This is likely an MSP432P4 */
+ msp432_bank->family_type = MSP432P4;
+
+ retval = target_read_u32(target, P4_DEVICE_ID_REG, &device_id);
+ if (ERROR_OK != retval)
+ return retval;
+
+ msp432_bank->device_id = device_id & 0xFFFF;
+
+ retval = target_read_u32(target, P4_HARDWARE_REV_REG, &hardware_rev);
+ if (ERROR_OK != retval)
+ return retval;
+
+ msp432_bank->hardware_rev = hardware_rev & 0xFF;
+ }
+
+ msp432_bank->device_type = msp432_device_type(msp432_bank->family_type,
+ msp432_bank->device_id, msp432_bank->hardware_rev);
+
+ /* If not already allocated, create the info bank for MSP432P4 */
+ /* We could not determine it was needed until device was probed */
+ if (MSP432P4 == msp432_bank->family_type) {
+ /* If we've been given bank 1, then this was already done */
+ if (0 == bank_id) {
+ /* And only allocate it if it doesn't exist yet */
+ if (NULL == bank->next) {
+ struct flash_bank *info_bank;
+ info_bank = malloc(sizeof(struct flash_bank));
+ if (NULL == info_bank)
+ return ERROR_FAIL;
+
+ name = malloc(strlen(bank->name)+1);
+ if (NULL == name) {
+ free(info_bank);
+ return ERROR_FAIL;
+ }
+ strcpy(name, bank->name);
+
+ /* Initialize bank 1 (info region) */
+ info_bank->name = name;
+ info_bank->target = bank->target;
+ info_bank->driver = bank->driver;
+ info_bank->driver_priv = bank->driver_priv;
+ info_bank->bank_number = 1;
+ info_bank->base = 0x00200000;
+ info_bank->size = 0;
+ info_bank->chip_width = 0;
+ info_bank->bus_width = 0;
+ info_bank->erased_value = 0xff;
+ info_bank->default_padded_value = 0xff;
+ info_bank->write_start_alignment = 0;
+ info_bank->write_end_alignment = 0;
+ info_bank->minimal_write_gap = FLASH_WRITE_GAP_SECTOR;
+ info_bank->num_sectors = 0;
+ info_bank->sectors = NULL;
+ info_bank->num_prot_blocks = 0;
+ info_bank->prot_blocks = NULL;
+ info_bank->next = NULL;
+
+ /* Enable the new bank */
+ bank->next = info_bank;
+ }
+ }
+ }
+
+ if (MSP432P4 == msp432_bank->family_type) {
+ /* Set up MSP432P4 specific flash parameters */
+ if (0 == bank_id) {
+ retval = target_read_u32(target, P4_FLASH_MAIN_SIZE_REG, &size);
+ if (ERROR_OK != retval)
+ return retval;
+
+ base = P4_FLASH_MAIN_BASE;
+ sector_length = P4_SECTOR_LENGTH;
+ num_sectors = size / sector_length;
+ } else if (1 == bank_id) {
+ if (msp432_bank->device_type == MSP432P411X ||
+ msp432_bank->device_type == MSP432P411X_GUESS) {
+ /* MSP432P411x has an info size register, use that for size */
+ retval = target_read_u32(target, P4_FLASH_INFO_SIZE_REG, &size);
+ if (ERROR_OK != retval)
+ return retval;
+ } else {
+ /* All other MSP432P401x devices have fixed info region size */
+ size = 0x4000; /* 16 KB info region */
+ }
+ base = P4_FLASH_INFO_BASE;
+ sector_length = P4_SECTOR_LENGTH;
+ num_sectors = size / sector_length;
+ } else {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+ } else {
+ /* Set up MSP432E4 specific flash parameters */
+ base = E4_FLASH_BASE;
+ size = E4_FLASH_SIZE;
+ sector_length = E4_SECTOR_LENGTH;
+ num_sectors = size / sector_length;
+ }
+
+ 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;
+ msp432_bank->sector_length = sector_length;
+
+ for (int i = 0; i < num_sectors; i++) {
+ bank->sectors[i].offset = i * sector_length;
+ bank->sectors[i].size = sector_length;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 0;
+ }
+
+ /* We've successfully determined the stats on this flash bank */
+ msp432_bank->probed[bank_id] = true;
+
+ /* If we fall through to here, then all went well */
+
+ return ERROR_OK;
+}
+
+static int msp432_auto_probe(struct flash_bank *bank)
+{
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+
+ int retval = ERROR_OK;
+
+ if (bank->bank_number < 0 || bank->bank_number > 1) {
+ /* Invalid bank number somehow */
+ return ERROR_FAIL;
+ }
+
+ if (!msp432_bank->probed[bank->bank_number])
+ retval = msp432_probe(bank);
+
+ return retval;
+}
+
+static int msp432_protect_check(struct flash_bank *bank)
+{
+ return ERROR_OK;
+}
+
+static int msp432_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct msp432_bank *msp432_bank = bank->driver_priv;
+ int printed = 0;
+
+ switch (msp432_bank->device_type) {
+ case MSP432P401X_DEPR:
+ if (0xFFFF == msp432_bank->device_id) {
+ /* Very early pre-production silicon currently deprecated */
+ printed = snprintf(buf, buf_size,
+ "MSP432P401x pre-production device (deprecated silicon)\n"
+ SUPPORT_MESSAGE);
+ } else {
+ /* Revision A or B silicon, also deprecated */
+ printed = snprintf(buf, buf_size,
+ "MSP432P401x Device Rev %c (deprecated silicon)\n"
+ SUPPORT_MESSAGE, (char)msp432_bank->hardware_rev);
+ }
+ break;
+ case MSP432P401X:
+ printed = snprintf(buf, buf_size,
+ "MSP432P401x Device Rev %c\n",
+ (char)msp432_bank->hardware_rev);
+ break;
+ case MSP432P411X:
+ printed = snprintf(buf, buf_size,
+ "MSP432P411x Device Rev %c\n",
+ (char)msp432_bank->hardware_rev);
+ break;
+ case MSP432E401Y:
+ printed = snprintf(buf, buf_size, "MSP432E401Y Device\n");
+ break;
+ case MSP432E411Y:
+ printed = snprintf(buf, buf_size, "MSP432E411Y Device\n");
+ break;
+ case MSP432E4X_GUESS:
+ printed = snprintf(buf, buf_size,
+ "Unrecognized MSP432E4 DID0 and DID1 IDs (%08X, %08X)",
+ msp432_bank->device_id, msp432_bank->hardware_rev);
+ break;
+ case MSP432P401X_GUESS:
+ case MSP432P411X_GUESS:
+ default:
+ printed = snprintf(buf, buf_size,
+ "Unrecognized MSP432P4 Device ID and Hardware Rev (%04X, %02X)",
+ msp432_bank->device_id, msp432_bank->hardware_rev);
+ break;
+ }
+
+ buf_size -= printed;
+
+ if (0 > buf_size)
+ return ERROR_BUF_TOO_SMALL;
+
+ return ERROR_OK;
+}
+
+static void msp432_flash_free_driver_priv(struct flash_bank *bank)
+{
+ /* A single private struct is shared between main and info banks */
+ /* Only free it on the call for main bank (#0) */
+ if ((0 == bank->bank_number) && (NULL != bank->driver_priv))
+ free(bank->driver_priv);
+ /* Forget about the private struct on both main and info banks */
+ bank->driver_priv = NULL;
+}
+
+static const struct command_registration msp432_exec_command_handlers[] = {
+ {
+ .name = "mass_erase",
+ .handler = msp432_mass_erase_command,
+ .mode = COMMAND_EXEC,
+ .help = "Erase entire flash memory on device.",
+ .usage = "['main' | 'all']",
+ },
+ {
+ .name = "bsl",
+ .handler = msp432_bsl_command,
+ .mode = COMMAND_EXEC,
+ .help = "Allow BSL to be erased or written by flash commands.",
+ .usage = "['unlock' | 'lock']",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration msp432_command_handlers[] = {
+ {
+ .name = "msp432",
+ .mode = COMMAND_EXEC,
+ .help = "MSP432 flash command group",
+ .usage = "",
+ .chain = msp432_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver msp432_flash = {
+ .name = "msp432",
+ .commands = msp432_command_handlers,
+ .flash_bank_command = msp432_flash_bank_command,
+ .erase = msp432_erase,
+ .protect = msp432_protect,
+ .write = msp432_write,
+ .read = default_flash_read,
+ .probe = msp432_probe,
+ .auto_probe = msp432_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = msp432_protect_check,
+ .info = msp432_info,
+ .free_driver_priv = msp432_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/msp432.h b/src/flash/nor/msp432.h
new file mode 100644
index 0000000..ffefa8f
--- /dev/null
+++ b/src/flash/nor/msp432.h
@@ -0,0 +1,127 @@
+/***************************************************************************
+ * Copyright (C) 2018 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_MSP432_H
+#define OPENOCD_FLASH_NOR_MSP432_H
+
+/* MSP432 family types */
+#define MSP432_NO_FAMILY 0 /* Family type not determined yet */
+#define MSP432E4 1 /* MSP432E4 family of devices */
+#define MSP432P4 2 /* MSP432P4 family of devices */
+
+/* MSP432 device types */
+#define MSP432_NO_TYPE 0 /* Device type not determined yet */
+#define MSP432P401X_DEPR 1 /* Early MSP432P401x offerings, now deprecated */
+#define MSP432P401X 2 /* MSP432P401x device, revision C or higher */
+#define MSP432P411X 3 /* MSP432P411x device, revision A or higher */
+#define MSP432P401X_GUESS 4 /* Assuming it's an MSP432P401x device */
+#define MSP432P411X_GUESS 5 /* Assuming it's an MSP432P411x device */
+#define MSP432E401Y 6 /* MSP432E401Y device */
+#define MSP432E411Y 7 /* MSP432E401Y device */
+#define MSP432E4X_GUESS 8 /* Assuming it's an MSP432E4x device */
+
+/* MSP432P4 flash parameters */
+#define P4_FLASH_MAIN_BASE 0x00000000
+#define P4_FLASH_INFO_BASE 0x00200000
+#define P4_SECTOR_LENGTH 0x1000
+#define P4_ALGO_ENTRY_ADDR 0x01000110
+
+/* MSP432E4 flash paramters */
+#define E4_FLASH_BASE 0x00000000
+#define E4_FLASH_SIZE 0x100000
+#define E4_SECTOR_LENGTH 0x4000
+#define E4_ALGO_ENTRY_ADDR 0x20000110
+
+/* Flash helper algorithm key addresses */
+#define ALGO_BASE_ADDR 0x20000000
+#define ALGO_BUFFER1_ADDR 0x20002000
+#define ALGO_BUFFER2_ADDR 0x20003000
+#define ALGO_PARAMS_BASE_ADDR 0x20000150
+#define ALGO_FLASH_COMMAND_ADDR 0x20000150
+#define ALGO_RETURN_CODE_ADDR 0x20000154
+#define ALGO_FLASH_DEST_ADDR 0x2000015c
+#define ALGO_FLASH_LENGTH_ADDR 0x20000160
+#define ALGO_BUFFER1_STATUS_ADDR 0x20000164
+#define ALGO_BUFFER2_STATUS_ADDR 0x20000168
+#define ALGO_ERASE_PARAM_ADDR 0x2000016c
+#define ALGO_UNLOCK_BSL_ADDR 0x20000170
+#define ALGO_STACK_POINTER_ADDR 0x20002000
+
+/* Flash helper algorithm key sizes */
+#define ALGO_BUFFER_SIZE 0x1000
+#define ALGO_WORKING_SIZE (ALGO_BUFFER2_ADDR + 0x1000 - ALGO_BASE_ADDR)
+
+/* Flash helper algorithm flash commands */
+#define FLASH_NO_COMMAND 0
+#define FLASH_MASS_ERASE 1
+#define FLASH_SECTOR_ERASE 2
+#define FLASH_PROGRAM 4
+#define FLASH_INIT 8
+#define FLASH_EXIT 16
+#define FLASH_CONTINUOUS 32
+
+/* Flash helper algorithm return codes */
+#define FLASH_BUSY 0x00000001
+#define FLASH_SUCCESS 0x00000ACE
+#define FLASH_ERROR 0x0000DEAD
+#define FLASH_TIMEOUT_ERROR 0xDEAD0000
+#define FLASH_VERIFY_ERROR 0xDEADDEAD
+#define FLASH_WRONG_COMMAND 0x00000BAD
+#define FLASH_POWER_ERROR 0x00DEAD00
+
+/* Flash helper algorithm buffer status values */
+#define BUFFER_INACTIVE 0x00
+#define BUFFER_ACTIVE 0x01
+#define BUFFER_DATA_READY 0x10
+
+/* Flash helper algorithm erase parameters */
+#define FLASH_ERASE_MAIN 0x01
+#define FLASH_ERASE_INFO 0x02
+
+/* Flash helper algorithm lock/unlock BSL options */
+#define FLASH_LOCK_BSL 0x00
+#define FLASH_UNLOCK_BSL 0x0b
+
+/* Flash helper algorithm parameter block struct */
+struct msp432_algo_params {
+ uint8_t flash_command[4];
+ uint8_t return_code[4];
+ uint8_t _reserved0[4];
+ uint8_t address[4];
+ uint8_t length[4];
+ uint8_t buffer1_status[4];
+ uint8_t buffer2_status[4];
+ uint8_t erase_param[4];
+ uint8_t unlock_bsl[4];
+};
+
+/* Flash helper algorithm for MSP432P401x targets */
+const uint8_t msp432p401x_algo[] = {
+#include "../../../contrib/loaders/flash/msp432/msp432p401x_algo.inc"
+};
+
+/* Flash helper algorithm for MSP432P411x targets */
+const uint8_t msp432p411x_algo[] = {
+#include "../../../contrib/loaders/flash/msp432/msp432p411x_algo.inc"
+};
+
+/* Flash helper algorithm for MSP432E4x targets */
+const uint8_t msp432e4x_algo[] = {
+#include "../../../contrib/loaders/flash/msp432/msp432e4x_algo.inc"
+};
+
+#endif /* OPENOCD_FLASH_NOR_MSP432_H */
diff --git a/src/flash/nor/niietcm4.c b/src/flash/nor/niietcm4.c
index 4a849fd..fd7d519 100644
--- a/src/flash/nor/niietcm4.c
+++ b/src/flash/nor/niietcm4.c
@@ -1741,4 +1741,5 @@ struct flash_driver niietcm4_flash = {
.erase_check = default_flash_blank_check,
.protect_check = niietcm4_protect_check,
.info = get_niietcm4_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c
index 4fa62e3..16459c7 100644
--- a/src/flash/nor/nrf5.c
+++ b/src/flash/nor/nrf5.c
@@ -108,6 +108,7 @@ enum nrf5_nvmc_config_bits {
struct nrf5_info {
uint32_t code_page_size;
+ uint32_t refcount;
struct {
bool probed;
@@ -155,6 +156,11 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
NRF5_DEVICE_DEF(0x0020, "51822", "CEAA", "BA", 256),
NRF5_DEVICE_DEF(0x002F, "51822", "CEAA", "B0", 256),
+ /* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards
+ with built-in jlink seem to use engineering samples not listed
+ in the nRF51 Series Compatibility Matrix V1.0. */
+ NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256),
+
/* nRF51822 Devices (IC rev 2). */
NRF5_DEVICE_DEF(0x002A, "51822", "QFAA", "FA0", 256),
NRF5_DEVICE_DEF(0x0044, "51822", "QFAA", "GC0", 256),
@@ -175,6 +181,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
NRF5_DEVICE_DEF(0x007D, "51822", "CDAB", "A0", 128),
NRF5_DEVICE_DEF(0x0079, "51822", "CEAA", "E0", 256),
NRF5_DEVICE_DEF(0x0087, "51822", "CFAC", "A0", 256),
+ NRF5_DEVICE_DEF(0x008F, "51822", "QFAA", "H1", 256),
/* nRF51422 Devices (IC rev 1). */
NRF5_DEVICE_DEF(0x001E, "51422", "QFAA", "CA", 256),
@@ -198,11 +205,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
/* nRF52832 Devices */
NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512),
-
- /* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards
- with built-in jlink seem to use engineering samples not listed
- in the nRF51 Series Compatibility Matrix V1.0. */
- NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256),
+ NRF5_DEVICE_DEF(0x0139, "52832", "QFAA", "E0", 512),
};
static int nrf5_bank_is_probed(struct flash_bank *bank)
@@ -530,7 +533,6 @@ static int nrf5_probe(struct flash_bank *bank)
bank->sectors[0].size = bank->size;
bank->sectors[0].offset = 0;
- /* mark as unknown */
bank->sectors[0].is_erased = 0;
bank->sectors[0].is_protected = 0;
@@ -552,17 +554,6 @@ static int nrf5_auto_probe(struct flash_bank *bank)
return nrf5_probe(bank);
}
-static struct flash_sector *nrf5_find_sector_by_address(struct flash_bank *bank, uint32_t address)
-{
- struct nrf5_info *chip = bank->driver_priv;
-
- for (int i = 0; i < bank->num_sectors; i++)
- if (bank->sectors[i].offset <= address &&
- address < (bank->sectors[i].offset + chip->code_page_size))
- return &bank->sectors[i];
- return NULL;
-}
-
static int nrf5_erase_all(struct nrf5_info *chip)
{
LOG_DEBUG("Erasing all non-volatile memory");
@@ -614,9 +605,6 @@ static int nrf5_erase_page(struct flash_bank *bank,
sector->offset);
}
- if (res == ERROR_OK)
- sector->is_erased = 1;
-
return res;
}
@@ -742,48 +730,22 @@ static int nrf5_write_pages(struct flash_bank *bank, uint32_t start, uint32_t en
{
int res = ERROR_FAIL;
struct nrf5_info *chip = bank->driver_priv;
- struct flash_sector *sector;
- uint32_t offset;
assert(start % chip->code_page_size == 0);
assert(end % chip->code_page_size == 0);
- /* Erase all sectors */
- for (offset = start; offset < end; offset += chip->code_page_size) {
- sector = nrf5_find_sector_by_address(bank, offset);
- if (!sector) {
- LOG_ERROR("Invalid sector @ 0x%08"PRIx32, offset);
- return ERROR_FLASH_SECTOR_INVALID;
- }
-
- if (sector->is_protected) {
- LOG_ERROR("Can't erase protected sector @ 0x%08"PRIx32, offset);
- goto error;
- }
-
- if (sector->is_erased != 1) { /* 1 = erased, 0= not erased, -1 = unknown */
- res = nrf5_erase_page(bank, chip, sector);
- if (res != ERROR_OK) {
- LOG_ERROR("Failed to erase sector @ 0x%08"PRIx32, sector->offset);
- goto error;
- }
- }
- sector->is_erased = 0;
- }
-
res = nrf5_nvmc_write_enable(chip);
if (res != ERROR_OK)
goto error;
res = nrf5_ll_flash_write(chip, start, buffer, (end - start));
if (res != ERROR_OK)
- goto set_read_only;
+ goto error;
return nrf5_nvmc_read_only(chip);
-set_read_only:
- nrf5_nvmc_read_only(chip);
error:
+ nrf5_nvmc_read_only(chip);
LOG_ERROR("Failed to write to nrf5 flash");
return res;
}
@@ -875,11 +837,9 @@ static int nrf5_uicr_flash_write(struct flash_bank *bank,
if (res != ERROR_OK)
return res;
- if (sector->is_erased != 1) {
- res = nrf5_erase_page(bank, chip, sector);
- if (res != ERROR_OK)
- return res;
- }
+ res = nrf5_erase_page(bank, chip, sector);
+ if (res != ERROR_OK)
+ return res;
res = nrf5_nvmc_write_enable(chip);
if (res != ERROR_OK)
@@ -910,6 +870,18 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer,
return chip->bank[bank->bank_number].write(bank, chip, buffer, offset, count);
}
+static void nrf5_free_driver_priv(struct flash_bank *bank)
+{
+ struct nrf5_info *chip = bank->driver_priv;
+ if (chip == NULL)
+ return;
+
+ chip->refcount--;
+ if (chip->refcount == 0) {
+ free(chip);
+ bank->driver_priv = NULL;
+ }
+}
FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
{
@@ -945,6 +917,7 @@ FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
break;
}
+ chip->refcount++;
chip->bank[bank->bank_number].probed = false;
bank->driver_priv = chip;
@@ -991,9 +964,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
return res;
}
- for (int i = 0; i < bank->num_sectors; i++)
- bank->sectors[i].is_erased = 1;
-
res = nrf5_protect_check(bank);
if (res != ERROR_OK) {
LOG_ERROR("Failed to check chip's write protection");
@@ -1004,8 +974,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
if (res != ERROR_OK)
return res;
- bank->sectors[0].is_erased = 1;
-
return ERROR_OK;
}
@@ -1174,6 +1142,7 @@ struct flash_driver nrf5_flash = {
.auto_probe = nrf5_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = nrf5_protect_check,
+ .free_driver_priv = nrf5_free_driver_priv,
};
/* We need to retain the flash-driver name as well as the commands
@@ -1191,4 +1160,5 @@ struct flash_driver nrf51_flash = {
.auto_probe = nrf5_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = nrf5_protect_check,
+ .free_driver_priv = nrf5_free_driver_priv,
};
diff --git a/src/flash/nor/numicro.c b/src/flash/nor/numicro.c
index 992baa5..4d951f0 100644
--- a/src/flash/nor/numicro.c
+++ b/src/flash/nor/numicro.c
@@ -1880,4 +1880,5 @@ struct flash_driver numicro_flash = {
.auto_probe = numicro_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = numicro_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/ocl.c b/src/flash/nor/ocl.c
index 4ae5652..895c4af 100644
--- a/src/flash/nor/ocl.c
+++ b/src/flash/nor/ocl.c
@@ -340,4 +340,5 @@ struct flash_driver ocl_flash = {
.erase_check = ocl_erase_check,
.protect_check = ocl_protect_check,
.auto_probe = ocl_auto_probe,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/pic32mx.c b/src/flash/nor/pic32mx.c
index 1f148fd..e3b8028 100644
--- a/src/flash/nor/pic32mx.c
+++ b/src/flash/nor/pic32mx.c
@@ -980,4 +980,5 @@ struct flash_driver pic32mx_flash = {
.erase_check = default_flash_blank_check,
.protect_check = pic32mx_protect_check,
.info = pic32mx_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/psoc4.c b/src/flash/nor/psoc4.c
index c7c8573..47d60de 100644
--- a/src/flash/nor/psoc4.c
+++ b/src/flash/nor/psoc4.c
@@ -41,24 +41,67 @@
Document Number: 001-87197 Rev. *B Revised August 29, 2013
PSoC 4100/4200 Family PSoC(R) 4 Architecture TRM
- Document No. 001-85634 Rev. *C March 25, 2014
+ Document No. 001-85634 Rev. *E June 28, 2016
PSoC(R) 4 Registers TRM Spec.
Document No. 001-85847 Rev. *A June 25, 2013
+ PSoC 4000 Family PSoC(R) 4 Technical Reference Manual
+ Document No. 001-89309 Rev. *B May 9, 2016
+
+ PSoC 41XX_BLE/42XX_BLE Family PSoC 4 BLE Architecture TRM
+ Document No. 001-92738 Rev. *C February 12, 2016
+
+ PSoC 4200L Family PSoC 4 Architecture TRM
+ Document No. 001-97952 Rev. *A December 15, 2015
+
+ PSoC 4200L Family PSoC 4 Registers TRM
+ Document No. 001-98126 Rev. *A December 16, 2015
+
+ PSoC 4100M/4200M Family PSoC 4 Architecture TRM
+ Document No. 001-95223 Rev. *B July 29, 2015
+
+ PSoC 4100S Family PSoC 4 Architecture TRM
+ Document No. 002-10621 Rev. *A July 29, 2016
+
+ PSoC 4100S Family PSoC 4 Registers TRM
+ Document No. 002-10523 Rev. *A July 20, 2016
+
+ PSoC Analog Coprocessor Architecture TRM
+ Document No. 002-10404 Rev. ** December 18, 2015
+
+ CY8C4Axx PSoC Analog Coprocessor Registers TRM
+ Document No. 002-10405 Rev. ** December 18, 2015
+
CY8C41xx, CY8C42xx Programming Specifications
Document No. 001-81799 Rev. *C March 4, 2014
+
+ CYBL10x6x, CY8C4127_BL, CY8C4247_BL Programming Specifications
+ Document No. 001-91508 Rev. *B September 22, 2014
+
+ http://dmitry.gr/index.php?r=05.Projects&proj=24.%20PSoC4%20confidential
*/
/* register locations */
-#define PSOC4_CPUSS_SYSREQ 0x40000004
-#define PSOC4_CPUSS_SYSARG 0x40000008
-#define PSOC4_TEST_MODE 0x40030014
-#define PSOC4_SPCIF_GEOMETRY 0x400E0000
+#define PSOC4_SFLASH_MACRO0 0x0FFFF000
+
+#define PSOC4_CPUSS_SYSREQ_LEGACY 0x40000004
+#define PSOC4_CPUSS_SYSARG_LEGACY 0x40000008
+#define PSOC4_SPCIF_GEOMETRY_LEGACY 0x400E0000
+
+#define PSOC4_CPUSS_SYSREQ_NEW 0x40100004
+#define PSOC4_CPUSS_SYSARG_NEW 0x40100008
+#define PSOC4_SPCIF_GEOMETRY_NEW 0x40110000
+
+#define PSOC4_TEST_MODE 0x40030014
+
+#define PSOC4_ROMTABLE_PID0 0xF0000FE0
-#define PSOC4_SFLASH_MACRO 0x0ffff000
/* constants */
+#define PSOC4_SFLASH_MACRO_SIZE 0x800
+#define PSOC4_ROWS_PER_MACRO 512
+
#define PSOC4_SROM_KEY1 0xb6
#define PSOC4_SROM_KEY2 0xd3
#define PSOC4_SROM_SYSREQ_BIT (1<<31)
@@ -66,6 +109,10 @@
#define PSOC4_SROM_PRIVILEGED_BIT (1<<28)
#define PSOC4_SROM_STATUS_SUCCEEDED 0xa0000000
#define PSOC4_SROM_STATUS_FAILED 0xf0000000
+#define PSOC4_SROM_STATUS_MASK 0xf0000000
+
+/* not documented in any TRM */
+#define PSOC4_SROM_ERR_IMO_NOT_IMPLEM 0xf0000013
#define PSOC4_CMD_GET_SILICON_ID 0
#define PSOC4_CMD_LOAD_LATCH 4
@@ -74,76 +121,60 @@
#define PSOC4_CMD_ERASE_ALL 0xa
#define PSOC4_CMD_CHECKSUM 0xb
#define PSOC4_CMD_WRITE_PROTECTION 0xd
+#define PSOC4_CMD_SET_IMO48 0x15
+#define PSOC4_CMD_WRITE_SFLASH_ROW 0x18
#define PSOC4_CHIP_PROT_VIRGIN 0x0
#define PSOC4_CHIP_PROT_OPEN 0x1
#define PSOC4_CHIP_PROT_PROTECTED 0x2
#define PSOC4_CHIP_PROT_KILL 0x4
+#define PSOC4_ROMTABLE_DESIGNER_CHECK 0xb4
+
+#define PSOC4_FAMILY_FLAG_LEGACY 1
-struct psoc4_chip_details {
+struct psoc4_chip_family {
uint16_t id;
- const char *type;
- const char *package;
- uint32_t flash_size_in_kb;
+ const char *name;
+ uint32_t flags;
};
-/* list of PSoC 4 chips
- * flash_size_in_kb is not necessary as it can be decoded from SPCIF_GEOMETRY
- */
-const struct psoc4_chip_details psoc4_devices[] = {
- /* 4200 series */
- { 0x04A6, "CY8C4245PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
- { 0x04B6, "CY8C4245LQI-483", "QFN-40", .flash_size_in_kb = 32 },
- { 0x04C8, "CY8C4245AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
- { 0x04FB, "CY8C4245AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
- { 0x04F0, "CY8C4244PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
- { 0x04F1, "CY8C4244PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
- { 0x04F6, "CY8C4244LQI-443", "QFN-40", .flash_size_in_kb = 16 },
- { 0x04FA, "CY8C4244AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
-
- /* 4100 series */
- { 0x0410, "CY8C4124PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
- { 0x0411, "CY8C4124PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
- { 0x041C, "CY8C4124LQI-443", "QFN-40", .flash_size_in_kb = 16 },
- { 0x041A, "CY8C4124AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
- { 0x041B, "CY8C4125AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
- { 0x0412, "CY8C4125PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
- { 0x0417, "CY8C4125LQI-483", "QFN-40", .flash_size_in_kb = 32 },
- { 0x0416, "CY8C4125AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
-
- /* CCG1 series */
- { 0x0490, "CYPD1103-35FNXI", "CSP-35", .flash_size_in_kb = 32 },
- { 0x0489, "CYPD1121-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
- { 0x048A, "CYPD1122-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
- { 0x0491, "CYPD1131-35FNXI", "CSP-35", .flash_size_in_kb = 32 },
- { 0x0498, "CYPD1132-16SXI", "SOIC-16", .flash_size_in_kb = 32 },
- { 0x0481, "CYPD1134-28PVXI", "SSOP-28", .flash_size_in_kb = 32 },
- { 0x048B, "CYPD1134-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
+const struct psoc4_chip_family psoc4_families[] = {
+ { 0x93, "PSoC4100/4200", .flags = PSOC4_FAMILY_FLAG_LEGACY },
+ { 0x9A, "PSoC4000", .flags = 0 },
+ { 0x9E, "PSoC/PRoC BLE (119E)", .flags = 0 },
+ { 0xA0, "PSoC4200L", .flags = 0 },
+ { 0xA1, "PSoC4100M/4200M", .flags = 0 },
+ { 0xA3, "PSoC/PRoC BLE (11A3)", .flags = 0 },
+ { 0xA9, "PSoC4000S", .flags = 0 },
+ { 0xAA, "PSoC/PRoC BLE (11AA)", .flags = 0 },
+ { 0xAB, "PSoC4100S", .flags = 0 },
+ { 0xAC, "PSoC Analog Coprocessor", .flags = 0 },
+ { 0, "Unknown", .flags = 0 }
};
struct psoc4_flash_bank {
uint32_t row_size;
uint32_t user_bank_size;
- int probed;
- uint32_t silicon_id;
- uint8_t chip_protection;
+ int num_macros;
+ bool probed;
uint8_t cmd_program_row;
+ uint16_t family_id;
+ bool legacy_family;
+ uint32_t cpuss_sysreq_addr;
+ uint32_t cpuss_sysarg_addr;
+ uint32_t spcif_geometry_addr;
};
-static const struct psoc4_chip_details *psoc4_details_by_id(uint32_t silicon_id)
+static const struct psoc4_chip_family *psoc4_family_by_id(uint16_t family_id)
{
- const struct psoc4_chip_details *p = psoc4_devices;
- unsigned int i;
- uint16_t id = silicon_id >> 16; /* ignore die revision */
- for (i = 0; i < sizeof(psoc4_devices)/sizeof(psoc4_devices[0]); i++, p++) {
- if (p->id == id)
- return p;
- }
- LOG_DEBUG("Unknown PSoC 4 device silicon id 0x%08" PRIx32 ".", silicon_id);
- return NULL;
+ const struct psoc4_chip_family *p = psoc4_families;
+ while (p->id && p->id != family_id)
+ p++;
+
+ return p;
}
static const char *psoc4_decode_chip_protection(uint8_t protection)
@@ -176,7 +207,9 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
psoc4_info = calloc(1, sizeof(struct psoc4_flash_bank));
bank->driver_priv = psoc4_info;
+ bank->default_padded_value = bank->erased_value = 0x00;
psoc4_info->user_bank_size = bank->size;
+ psoc4_info->cmd_program_row = PSOC4_CMD_WRITE_ROW;
return ERROR_OK;
}
@@ -189,9 +222,14 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
* Otherwise address of memory parameter block is set in CPUSS_SYSARG
* and the first parameter is written to the first word of parameter block
*/
-static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
- uint32_t *sysreq_params, uint32_t sysreq_params_size)
+static int psoc4_sysreq(struct flash_bank *bank, uint8_t cmd,
+ uint16_t cmd_param,
+ uint32_t *sysreq_params, uint32_t sysreq_params_size,
+ uint32_t *sysarg_out)
{
+ struct target *target = bank->target;
+ struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+
struct working_area *sysreq_wait_algorithm;
struct working_area *sysreq_mem;
@@ -212,8 +250,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
const int code_words = (sizeof(psoc4_sysreq_wait_code) + 3) / 4;
/* stack must be aligned */
- const int stack_size = 196;
- /* tested stack sizes on PSoC 4:
+ const int stack_size = 256;
+ /* tested stack sizes on PSoC4200:
ERASE_ALL 144
PROGRAM_ROW 112
other sysreq 68
@@ -238,6 +276,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
}
if (sysreq_params_size) {
+ LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32 " size %" PRIu32,
+ cmd, cmd_param, param1, sysreq_params_size);
/* Allocate memory for sysreq_params */
retval = target_alloc_working_area(target, sysreq_params_size, &sysreq_mem);
if (retval != ERROR_OK) {
@@ -250,21 +290,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
}
/* Write sysreq_params */
- sysreq_params[0] = param1;
+ target_buffer_set_u32(target, (uint8_t *)sysreq_params, param1);
retval = target_write_buffer(target, sysreq_mem->address,
sysreq_params_size, (uint8_t *)sysreq_params);
if (retval != ERROR_OK)
goto cleanup_mem;
/* Set address of sysreq parameters block */
- retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, sysreq_mem->address);
+ retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, sysreq_mem->address);
if (retval != ERROR_OK)
goto cleanup_mem;
} else {
/* Sysreq without memory block of parameters */
+ LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32,
+ cmd, cmd_param, param1);
/* Set register parameter */
- retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, param1);
+ retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, param1);
if (retval != ERROR_OK)
goto cleanup_mem;
}
@@ -279,14 +321,14 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
struct armv7m_common *armv7m = target_to_armv7m(target);
if (armv7m == NULL) {
-
/* something is very wrong if armv7m is NULL */
LOG_ERROR("unable to get armv7m target");
+ retval = ERROR_FAIL;
goto cleanup;
}
/* Set SROM request */
- retval = target_write_u32(target, PSOC4_CPUSS_SYSREQ,
+ retval = target_write_u32(target, psoc4_info->cpuss_sysreq_addr,
PSOC4_SROM_SYSREQ_BIT | PSOC4_SROM_HMASTER_BIT | cmd);
if (retval != ERROR_OK)
goto cleanup;
@@ -295,9 +337,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
retval = target_run_algorithm(target, 0, NULL,
sizeof(reg_params) / sizeof(*reg_params), reg_params,
sysreq_wait_algorithm->address, 0, 1000, &armv7m_info);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
LOG_ERROR("sysreq wait code execution failed");
+ goto cleanup;
+ }
+ uint32_t sysarg_out_tmp;
+ retval = target_read_u32(target, psoc4_info->cpuss_sysarg_addr, &sysarg_out_tmp);
+ if (retval != ERROR_OK)
+ goto cleanup;
+
+ if (sysarg_out) {
+ *sysarg_out = sysarg_out_tmp;
+ /* If result is an error, do not show now, let caller to decide */
+ } else if ((sysarg_out_tmp & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
+ LOG_ERROR("sysreq error 0x%" PRIx32, sysarg_out_tmp);
+ retval = ERROR_FAIL;
+ }
cleanup:
destroy_reg_param(&reg_params[0]);
@@ -313,92 +369,162 @@ cleanup_algo:
/* helper routine to get silicon ID from a PSoC 4 chip */
-static int psoc4_get_silicon_id(struct target *target, uint32_t *silicon_id, uint8_t *protection)
+static int psoc4_get_silicon_id(struct flash_bank *bank, uint32_t *silicon_id, uint16_t *family_id, uint8_t *protection)
{
- uint32_t params = PSOC4_SROM_KEY1
- | ((PSOC4_SROM_KEY2 + PSOC4_CMD_GET_SILICON_ID) << 8);
- uint32_t part0, part1;
+ struct target *target = bank->target;
+ struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
- int retval = psoc4_sysreq(target, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0);
- if (retval != ERROR_OK)
- return retval;
+ uint32_t part0, part1;
- retval = target_read_u32(target, PSOC4_CPUSS_SYSARG, &part0);
+ int retval = psoc4_sysreq(bank, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0, &part0);
if (retval != ERROR_OK)
return retval;
- if (part0 == params) {
- LOG_ERROR("sysreq silicon id request not served");
+ if ((part0 & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
+ LOG_ERROR("sysreq error 0x%" PRIx32, part0);
return ERROR_FAIL;
}
- retval = target_read_u32(target, PSOC4_CPUSS_SYSREQ, &part1);
+ retval = target_read_u32(target, psoc4_info->cpuss_sysreq_addr, &part1);
if (retval != ERROR_OK)
return retval;
- uint32_t silicon = ((part0 & 0xffff) << 16)
- | (((part0 >> 16) & 0xff) << 8)
- | (part1 & 0xff);
- uint8_t prot = (part1 >> 12) & 0xff;
-
+ /* build ID as Cypress sw does:
+ * bit 31..16 silicon ID
+ * bit 15..8 revision ID (so far 0x11 for all devices)
+ * bit 7..0 family ID (lowes 8 bits)
+ */
if (silicon_id)
- *silicon_id = silicon;
+ *silicon_id = ((part0 & 0x0000ffff) << 16)
+ | ((part0 & 0x00ff0000) >> 8)
+ | (part1 & 0x000000ff);
+
+ if (family_id)
+ *family_id = part1 & 0x0fff;
+
if (protection)
- *protection = prot;
+ *protection = (part1 >> 12) & 0x0f;
- LOG_DEBUG("silicon id: 0x%08" PRIx32 "", silicon);
- LOG_DEBUG("protection: 0x%02" PRIx8 "", prot);
- return retval;
+ return ERROR_OK;
}
-static int psoc4_protect_check(struct flash_bank *bank)
+static int psoc4_get_family(struct target *target, uint16_t *family_id)
+{
+ int retval, i;
+ uint32_t pidbf[3];
+ uint8_t pid[3];
+
+ retval = target_read_memory(target, PSOC4_ROMTABLE_PID0, 4, 3, (uint8_t *)pidbf);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (i = 0; i < 3; i++) {
+ uint32_t tmp = target_buffer_get_u32(target, (uint8_t *)(pidbf + i));
+ if (tmp & 0xffffff00) {
+ LOG_ERROR("Unexpected data in ROMTABLE");
+ return ERROR_FAIL;
+ }
+ pid[i] = tmp & 0xff;
+ }
+
+ uint16_t family = pid[0] | ((pid[1] & 0xf) << 8);
+ uint32_t designer = ((pid[1] & 0xf0) >> 4) | ((pid[2] & 0xf) << 4);
+
+ if (designer != PSOC4_ROMTABLE_DESIGNER_CHECK) {
+ LOG_ERROR("ROMTABLE designer is not Cypress");
+ return ERROR_FAIL;
+ }
+
+ *family_id = family;
+ return ERROR_OK;
+}
+
+
+static int psoc4_flash_prepare(struct flash_bank *bank)
{
struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
- uint32_t prot_addr = PSOC4_SFLASH_MACRO;
- uint32_t protection;
- int i, s;
- int num_bits;
- int retval = ERROR_OK;
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
- num_bits = bank->num_sectors;
+ uint16_t family_id;
+ int retval;
+
+ /* get family ID from SROM call */
+ retval = psoc4_get_silicon_id(bank, NULL, &family_id, NULL);
+ if (retval != ERROR_OK)
+ return retval;
- for (i = 0; i < num_bits; i += 32) {
- retval = target_read_u32(target, prot_addr, &protection);
+ /* and check with family ID from ROMTABLE */
+ if (family_id != psoc4_info->family_id) {
+ LOG_ERROR("Family mismatch");
+ return ERROR_FAIL;
+ }
+
+ if (!psoc4_info->legacy_family) {
+ uint32_t sysreq_status;
+ retval = psoc4_sysreq(bank, PSOC4_CMD_SET_IMO48, 0, NULL, 0, &sysreq_status);
if (retval != ERROR_OK)
return retval;
- prot_addr += 4;
-
- for (s = 0; s < 32; s++) {
- if (i + s >= num_bits)
- break;
- bank->sectors[i + s].is_protected = (protection & (1 << s)) ? 1 : 0;
+ if ((sysreq_status & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
+ /* This undocumented error code is returned probably when
+ * PSOC4_CMD_SET_IMO48 command is not implemented.
+ * Can be safely ignored, programming works.
+ */
+ if (sysreq_status == PSOC4_SROM_ERR_IMO_NOT_IMPLEM)
+ LOG_INFO("PSOC4_CMD_SET_IMO48 is not implemented on this device.");
+ else {
+ LOG_ERROR("sysreq error 0x%" PRIx32, sysreq_status);
+ return ERROR_FAIL;
+ }
}
}
- retval = psoc4_get_silicon_id(target, NULL, &(psoc4_info->chip_protection));
- return retval;
+ return ERROR_OK;
}
-static int psoc4_mass_erase(struct flash_bank *bank)
+static int psoc4_protect_check(struct flash_bank *bank)
{
struct target *target = bank->target;
- int i;
+ struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
- if (bank->target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
+ uint32_t prot_addr = PSOC4_SFLASH_MACRO0;
+ int retval;
+ int s = 0;
+ int m, i;
+ uint8_t bf[PSOC4_ROWS_PER_MACRO/8];
+
+ for (m = 0; m < psoc4_info->num_macros; m++, prot_addr += PSOC4_SFLASH_MACRO_SIZE) {
+ retval = target_read_memory(target, prot_addr, 4, PSOC4_ROWS_PER_MACRO/32, bf);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (i = 0; i < PSOC4_ROWS_PER_MACRO && s < bank->num_sectors; i++, s++)
+ bank->sectors[s].is_protected = bf[i/8] & (1 << (i%8)) ? 1 : 0;
}
+ return ERROR_OK;
+}
+
+
+static int psoc4_mass_erase(struct flash_bank *bank)
+{
+ int i;
+ int retval = psoc4_flash_prepare(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
/* Call "Erase All" system ROM API */
- uint32_t param;
- int retval = psoc4_sysreq(target, PSOC4_CMD_ERASE_ALL,
+ uint32_t param = 0;
+ retval = psoc4_sysreq(bank, PSOC4_CMD_ERASE_ALL,
0,
- &param, sizeof(param));
+ &param, sizeof(param), NULL);
if (retval == ERROR_OK)
/* set all sectors as erased */
@@ -420,7 +546,7 @@ static int psoc4_erase(struct flash_bank *bank, int first, int last)
if ((first == 0) && (last == (bank->num_sectors - 1)))
return psoc4_mass_erase(bank);
- LOG_ERROR("Only mass erase available");
+ LOG_ERROR("Only mass erase available! Consider using 'psoc4 flash_autoerase 0 on'");
return ERROR_FAIL;
}
@@ -431,56 +557,63 @@ static int psoc4_protect(struct flash_bank *bank, int set, int first, int last)
struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
- if (psoc4_info->probed == 0)
+ if (!psoc4_info->probed)
return ERROR_FAIL;
- if (target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
- }
+ int retval = psoc4_flash_prepare(bank);
+ if (retval != ERROR_OK)
+ return retval;
uint32_t *sysrq_buffer = NULL;
- int retval;
- int num_bits = bank->num_sectors;
const int param_sz = 8;
- int prot_sz = num_bits / 8;
int chip_prot = PSOC4_CHIP_PROT_OPEN;
- int flash_macro = 0; /* PSoC 42xx has only macro 0 */
- int i;
+ int i, m, sect;
+ int num_bits = bank->num_sectors;
+
+ if (num_bits > PSOC4_ROWS_PER_MACRO)
+ num_bits = PSOC4_ROWS_PER_MACRO;
+
+ int prot_sz = num_bits / 8;
- sysrq_buffer = calloc(1, param_sz + prot_sz);
+ sysrq_buffer = malloc(param_sz + prot_sz);
if (sysrq_buffer == NULL) {
LOG_ERROR("no memory for row buffer");
return ERROR_FAIL;
}
- for (i = first; i < num_bits && i <= last; i++)
+ for (i = first; i <= last && i < bank->num_sectors; i++)
bank->sectors[i].is_protected = set;
- uint32_t *p = sysrq_buffer + 2;
- for (i = 0; i < num_bits; i++) {
- if (bank->sectors[i].is_protected)
- p[i / 32] |= 1 << (i % 32);
- }
+ for (m = 0, sect = 0; m < psoc4_info->num_macros; m++) {
+ uint8_t *p = (uint8_t *)(sysrq_buffer + 2);
+ memset(p, 0, prot_sz);
+ for (i = 0; i < num_bits && sect < bank->num_sectors; i++, sect++) {
+ if (bank->sectors[sect].is_protected)
+ p[i/8] |= 1 << (i%8);
+ }
- /* Call "Load Latch" system ROM API */
- sysrq_buffer[1] = prot_sz - 1;
- retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
- 0, /* Byte number in latch from what to write */
- sysrq_buffer, param_sz + psoc4_info->row_size);
- if (retval != ERROR_OK)
- goto cleanup;
+ /* Call "Load Latch" system ROM API */
+ target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1),
+ prot_sz - 1);
+ retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH,
+ 0 /* Byte number in latch from what to write */
+ | (m << 8), /* flash macro index */
+ sysrq_buffer, param_sz + prot_sz,
+ NULL);
+ if (retval != ERROR_OK)
+ break;
- /* Call "Write Protection" system ROM API */
- retval = psoc4_sysreq(target, PSOC4_CMD_WRITE_PROTECTION,
- chip_prot | (flash_macro << 8), NULL, 0);
-cleanup:
- if (retval != ERROR_OK)
- psoc4_protect_check(bank);
+ /* Call "Write Protection" system ROM API */
+ retval = psoc4_sysreq(bank, PSOC4_CMD_WRITE_PROTECTION,
+ chip_prot | (m << 8), NULL, 0, NULL);
+ if (retval != ERROR_OK)
+ break;
+ }
if (sysrq_buffer)
free(sysrq_buffer);
+ psoc4_protect_check(bank);
return retval;
}
@@ -516,21 +649,14 @@ COMMAND_HANDLER(psoc4_handle_flash_autoerase_command)
static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
- struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
struct target *target = bank->target;
+ struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
uint32_t *sysrq_buffer = NULL;
- int retval = ERROR_OK;
const int param_sz = 8;
- if (bank->target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
- }
-
- if (offset & 0x1) {
- LOG_ERROR("offset 0x%08" PRIx32 " breaks required 2-byte alignment", offset);
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
- }
+ int retval = psoc4_flash_prepare(bank);
+ if (retval != ERROR_OK)
+ return retval;
sysrq_buffer = malloc(param_sz + psoc4_info->row_size);
if (sysrq_buffer == NULL) {
@@ -542,7 +668,7 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t row_num = offset / psoc4_info->row_size;
uint32_t row_offset = offset - row_num * psoc4_info->row_size;
if (row_offset)
- memset(row_buffer, 0, row_offset);
+ memset(row_buffer, bank->default_padded_value, row_offset);
bool save_poll = jtag_poll_get_enabled();
jtag_poll_set_enabled(false);
@@ -551,25 +677,31 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t chunk_size = psoc4_info->row_size - row_offset;
if (chunk_size > count) {
chunk_size = count;
- memset(row_buffer + chunk_size, 0, psoc4_info->row_size - chunk_size);
+ memset(row_buffer + chunk_size, bank->default_padded_value, psoc4_info->row_size - chunk_size);
}
memcpy(row_buffer + row_offset, buffer, chunk_size);
LOG_DEBUG("offset / row: 0x%08" PRIx32 " / %" PRIu32 ", size %" PRIu32 "",
offset, row_offset, chunk_size);
+ uint32_t macro_idx = row_num / PSOC4_ROWS_PER_MACRO;
+
/* Call "Load Latch" system ROM API */
- sysrq_buffer[1] = psoc4_info->row_size - 1;
- retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
- 0, /* Byte number in latch from what to write */
- sysrq_buffer, param_sz + psoc4_info->row_size);
+ target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1),
+ psoc4_info->row_size - 1);
+ retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH,
+ 0 /* Byte number in latch from what to write */
+ | (macro_idx << 8),
+ sysrq_buffer, param_sz + psoc4_info->row_size,
+ NULL);
if (retval != ERROR_OK)
goto cleanup;
/* Call "Program Row" or "Write Row" system ROM API */
uint32_t sysrq_param;
- retval = psoc4_sysreq(target, psoc4_info->cmd_program_row,
+ retval = psoc4_sysreq(bank, psoc4_info->cmd_program_row,
row_num & 0xffff,
- &sysrq_param, sizeof(sysrq_param));
+ &sysrq_param, sizeof(sysrq_param),
+ NULL);
if (retval != ERROR_OK)
goto cleanup;
@@ -589,84 +721,82 @@ cleanup:
}
+/* Due to Cypress's method of market segmentation some devices
+ * have accessible only 1/2, 1/4 or 1/8 of SPCIF described flash */
+static int psoc4_test_flash_wounding(struct target *target, uint32_t flash_size)
+{
+ int retval, i;
+ for (i = 3; i >= 1; i--) {
+ uint32_t addr = flash_size >> i;
+ uint32_t dummy;
+ retval = target_read_u32(target, addr, &dummy);
+ if (retval != ERROR_OK)
+ return i;
+ }
+ return 0;
+}
+
+
static int psoc4_probe(struct flash_bank *bank)
{
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
struct target *target = bank->target;
- uint32_t flash_size_in_kb = 0;
- uint32_t max_flash_size_in_kb;
- uint32_t cpu_id;
- uint32_t silicon_id;
- uint32_t row_size;
- uint32_t base_address = 0x00000000;
- uint8_t protection;
- if (target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
- }
+ int retval;
+ uint16_t family_id;
- psoc4_info->probed = 0;
- psoc4_info->cmd_program_row = PSOC4_CMD_PROGRAM_ROW;
+ psoc4_info->probed = false;
- /* Get the CPUID from the ARM Core
- * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/DDI0432C_cortex_m0_r0p0_trm.pdf 4.2.1 */
- int retval = target_read_u32(target, 0xE000ED00, &cpu_id);
+ retval = psoc4_get_family(target, &family_id);
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("cpu id = 0x%08" PRIx32 "", cpu_id);
+ const struct psoc4_chip_family *family = psoc4_family_by_id(family_id);
- /* set page size, protection granularity and max flash size depending on family */
- switch ((cpu_id >> 4) & 0xFFF) {
- case 0xc20: /* M0 -> PSoC4 */
- row_size = 128;
- max_flash_size_in_kb = 32;
- break;
- default:
- LOG_WARNING("Cannot identify target as a PSoC 4 family.");
+ if (family->id == 0) {
+ LOG_ERROR("Cannot identify PSoC 4 family.");
return ERROR_FAIL;
}
- uint32_t spcif_geometry;
- retval = target_read_u32(target, PSOC4_SPCIF_GEOMETRY, &spcif_geometry);
- if (retval == ERROR_OK) {
- row_size = 128 * ((spcif_geometry >> 22) & 3);
- flash_size_in_kb = (spcif_geometry & 0xffff) * 256 / 1024;
- LOG_INFO("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.",
- flash_size_in_kb, row_size);
+ if (family->flags & PSOC4_FAMILY_FLAG_LEGACY) {
+ LOG_INFO("%s legacy family detected.", family->name);
+ psoc4_info->legacy_family = true;
+ psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_LEGACY;
+ psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_LEGACY;
+ psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_LEGACY;
+ } else {
+ LOG_INFO("%s family detected.", family->name);
+ psoc4_info->legacy_family = false;
+ psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_NEW;
+ psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_NEW;
+ psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_NEW;
}
- /* Early revisions of ST-Link v2 have some problem reading PSOC4_SPCIF_GEOMETRY
- and an error is reported late. Dummy read gets this error. */
- uint32_t dummy;
- target_read_u32(target, PSOC4_CPUSS_SYSREQ, &dummy);
-
- /* get silicon ID from target. */
- retval = psoc4_get_silicon_id(target, &silicon_id, &protection);
+ uint32_t spcif_geometry;
+ retval = target_read_u32(target, psoc4_info->spcif_geometry_addr, &spcif_geometry);
if (retval != ERROR_OK)
return retval;
- const struct psoc4_chip_details *details = psoc4_details_by_id(silicon_id);
- if (details) {
- LOG_INFO("%s device detected.", details->type);
- if (flash_size_in_kb == 0)
- flash_size_in_kb = details->flash_size_in_kb;
- else if (flash_size_in_kb != details->flash_size_in_kb)
- LOG_ERROR("Flash size mismatch");
+ uint32_t flash_size_in_kb = spcif_geometry & 0x3fff;
+ /* TRM of legacy, M and L version describes FLASH field as 16-bit.
+ * S-series and PSoC Analog Coprocessor changes spec to 14-bit only.
+ * Impose PSoC Analog Coprocessor limit to all devices as it
+ * does not make any harm: flash size is safely below 4 MByte limit
+ */
+ uint32_t row_size = (spcif_geometry >> 22) & 3;
+ uint32_t num_macros = (spcif_geometry >> 20) & 3;
+
+ if (psoc4_info->legacy_family) {
+ flash_size_in_kb = flash_size_in_kb * 256 / 1024;
+ row_size *= 128;
+ } else {
+ flash_size_in_kb = (flash_size_in_kb + 1) * 256 / 1024;
+ row_size = 64 * (row_size + 1);
+ num_macros++;
}
- psoc4_info->row_size = row_size;
- psoc4_info->silicon_id = silicon_id;
- psoc4_info->chip_protection = protection;
-
- /* failed reading flash size or flash size invalid (early silicon),
- * default to max target family */
- if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
- LOG_WARNING("PSoC 4 flash size failed, probe inaccurate - assuming %" PRIu32 " k flash",
- max_flash_size_in_kb);
- flash_size_in_kb = max_flash_size_in_kb;
- }
+ LOG_DEBUG("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.",
+ flash_size_in_kb, row_size);
/* if the user sets the size manually then ignore the probed value
* this allows us to work around devices that have a invalid flash size register value */
@@ -675,37 +805,44 @@ static int psoc4_probe(struct flash_bank *bank)
flash_size_in_kb = psoc4_info->user_bank_size / 1024;
}
- LOG_INFO("flash size = %" PRIu32 " kbytes", flash_size_in_kb);
+ char macros_txt[20] = "";
+ if (num_macros > 1)
+ snprintf(macros_txt, sizeof(macros_txt), " in %" PRIu32 " macros", num_macros);
- /* did we assign flash size? */
- assert(flash_size_in_kb != 0xffff);
+ LOG_INFO("flash size = %" PRIu32 " kbytes%s", flash_size_in_kb, macros_txt);
- /* calculate numbers of pages */
+ /* calculate number of pages */
uint32_t num_rows = flash_size_in_kb * 1024 / row_size;
- /* check that calculation result makes sense */
- assert(num_rows > 0);
+ /* check number of flash macros */
+ if (num_macros != (num_rows + PSOC4_ROWS_PER_MACRO - 1) / PSOC4_ROWS_PER_MACRO)
+ LOG_WARNING("Number of macros does not correspond with flash size!");
+
+ if (!psoc4_info->legacy_family) {
+ int wounding = psoc4_test_flash_wounding(target, num_rows * row_size);
+ if (wounding > 0) {
+ flash_size_in_kb = flash_size_in_kb >> wounding;
+ num_rows = num_rows >> wounding;
+ LOG_INFO("WOUNDING detected: accessible flash size %" PRIu32 " kbytes", flash_size_in_kb);
+ }
+ }
if (bank->sectors) {
free(bank->sectors);
- bank->sectors = NULL;
}
- bank->base = base_address;
+ psoc4_info->family_id = family_id;
+ psoc4_info->num_macros = num_macros;
+ psoc4_info->row_size = row_size;
+ bank->base = 0x00000000;
bank->size = num_rows * row_size;
bank->num_sectors = num_rows;
- bank->sectors = malloc(sizeof(struct flash_sector) * num_rows);
-
- uint32_t i;
- for (i = 0; i < num_rows; i++) {
- bank->sectors[i].offset = i * row_size;
- bank->sectors[i].size = row_size;
- bank->sectors[i].is_erased = -1;
- bank->sectors[i].is_protected = 1;
- }
+ bank->sectors = alloc_block_array(0, row_size, num_rows);
+ if (bank->sectors == NULL)
+ return ERROR_FAIL;
- LOG_INFO("flash bank set %" PRIu32 " rows", num_rows);
- psoc4_info->probed = 1;
+ LOG_DEBUG("flash bank set %" PRIu32 " rows", num_rows);
+ psoc4_info->probed = true;
return ERROR_OK;
}
@@ -721,28 +858,45 @@ static int psoc4_auto_probe(struct flash_bank *bank)
static int get_psoc4_info(struct flash_bank *bank, char *buf, int buf_size)
{
+ struct target *target = bank->target;
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
- int printed = 0;
- if (psoc4_info->probed == 0)
+ if (!psoc4_info->probed)
return ERROR_FAIL;
- const struct psoc4_chip_details *details = psoc4_details_by_id(psoc4_info->silicon_id);
+ const struct psoc4_chip_family *family = psoc4_family_by_id(psoc4_info->family_id);
+ uint32_t size_in_kb = bank->size / 1024;
+
+ if (target->state != TARGET_HALTED) {
+ snprintf(buf, buf_size, "%s, flash %" PRIu32 " kb"
+ " (halt target to see details)", family->name, size_in_kb);
+ return ERROR_OK;
+ }
+
+ int retval;
+ int printed = 0;
+ uint32_t silicon_id;
+ uint16_t family_id;
+ uint8_t protection;
- if (details) {
- uint32_t chip_revision = psoc4_info->silicon_id & 0xffff;
- printed = snprintf(buf, buf_size, "PSoC 4 %s rev 0x%04" PRIx32 " package %s",
- details->type, chip_revision, details->package);
- } else
- printed = snprintf(buf, buf_size, "PSoC 4 silicon id 0x%08" PRIx32 "",
- psoc4_info->silicon_id);
+ retval = psoc4_get_silicon_id(bank, &silicon_id, &family_id, &protection);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (family_id != psoc4_info->family_id)
+ printed = snprintf(buf, buf_size, "Family id mismatch 0x%02" PRIx16
+ "/0x%02" PRIx16 ", silicon id 0x%08" PRIx32,
+ psoc4_info->family_id, family_id, silicon_id);
+ else {
+ printed = snprintf(buf, buf_size, "%s silicon id 0x%08" PRIx32 "",
+ family->name, silicon_id);
+ }
buf += printed;
buf_size -= printed;
- const char *prot_txt = psoc4_decode_chip_protection(psoc4_info->chip_protection);
- uint32_t size_in_kb = bank->size / 1024;
- snprintf(buf, buf_size, " flash %" PRIu32 " kb %s", size_in_kb, prot_txt);
+ const char *prot_txt = psoc4_decode_chip_protection(protection);
+ snprintf(buf, buf_size, ", flash %" PRIu32 " kb %s", size_in_kb, prot_txt);
return ERROR_OK;
}
@@ -809,4 +963,5 @@ struct flash_driver psoc4_flash = {
.erase_check = default_flash_blank_check,
.protect_check = psoc4_protect_check,
.info = get_psoc4_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/psoc5lp.c b/src/flash/nor/psoc5lp.c
new file mode 100644
index 0000000..b88abbb
--- /dev/null
+++ b/src/flash/nor/psoc5lp.c
@@ -0,0 +1,1586 @@
+/*
+ * 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. */
+ str[8] = 'x';
+ str[9] = 'x';
+
+ /* 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,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
+
+/*
+ * 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,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
+
+/*
+ * Program Flash
+ */
+
+struct psoc5lp_flash_bank {
+ bool probed;
+ const struct psoc5lp_device *device;
+ bool ecc_enabled;
+ /* If ecc is disabled, num_sectors counts both std and ecc sectors.
+ * If ecc is enabled, num_sectors indicates just the number of std sectors.
+ * However ecc sector descriptors bank->sector[num_sectors..2*num_sectors-1]
+ * are used for driver private flash operations */
+};
+
+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;
+ }
+
+ int num_sectors = bank->num_sectors;
+ if (psoc_bank->ecc_enabled)
+ num_sectors *= 2; /* count both std and ecc sector always */
+
+ struct target_memory_check_block *block_array;
+ block_array = malloc(num_sectors * sizeof(struct target_memory_check_block));
+ if (block_array == NULL)
+ return ERROR_FAIL;
+
+ for (i = 0; i < 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 < num_sectors; ) {
+ retval = armv7m_blank_check_memory(target,
+ block_array + i, 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) {
+ for (i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_erased =
+ (block_array[i].result != 1)
+ ? block_array[i].result
+ : block_array[i + bank->num_sectors].result;
+ /* if std sector is erased, use status of ecc sector */
+ } else {
+ for (i = 0; i < 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,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/psoc6.c b/src/flash/nor/psoc6.c
new file mode 100644
index 0000000..9352ad4
--- /dev/null
+++ b/src/flash/nor/psoc6.c
@@ -0,0 +1,1065 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2018 by Bohdan Tymkiv *
+ * bohdan.tymkiv@cypress.com bohdan200@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <time.h>
+
+#include "imp.h"
+#include "target/target.h"
+#include "target/cortex_m.h"
+#include "target/breakpoints.h"
+#include "target/target_type.h"
+#include "time_support.h"
+#include "target/algorithm.h"
+
+/**************************************************************************************************
+ * PSoC6 device definitions
+ *************************************************************************************************/
+#define MFLASH_SECTOR_SIZE (256u * 1024u)
+#define WFLASH_SECTOR_SIZE (32u * 1024u)
+
+#define MEM_BASE_MFLASH 0x10000000u
+#define MEM_BASE_WFLASH 0x14000000u
+#define MEM_WFLASH_SIZE 32768u
+#define MEM_BASE_SFLASH 0x16000000u
+#define RAM_STACK_WA_SIZE 2048u
+#define PSOC6_SPCIF_GEOMETRY 0x4025F00Cu
+
+#define PROTECTION_UNKNOWN 0x00u
+#define PROTECTION_VIRGIN 0x01u
+#define PROTECTION_NORMAL 0x02u
+#define PROTECTION_SECURE 0x03u
+#define PROTECTION_DEAD 0x04u
+
+#define MEM_BASE_IPC 0x40230000u
+#define IPC_STRUCT_SIZE 0x20u
+#define MEM_IPC(n) (MEM_BASE_IPC + (n) * IPC_STRUCT_SIZE)
+#define MEM_IPC_ACQUIRE(n) (MEM_IPC(n) + 0x00u)
+#define MEM_IPC_NOTIFY(n) (MEM_IPC(n) + 0x08u)
+#define MEM_IPC_DATA(n) (MEM_IPC(n) + 0x0Cu)
+#define MEM_IPC_LOCK_STATUS(n) (MEM_IPC(n) + 0x10u)
+
+#define MEM_BASE_IPC_INTR 0x40231000u
+#define IPC_INTR_STRUCT_SIZE 0x20u
+#define MEM_IPC_INTR(n) (MEM_BASE_IPC_INTR + (n) * IPC_INTR_STRUCT_SIZE)
+#define MEM_IPC_INTR_MASK(n) (MEM_IPC_INTR(n) + 0x08u)
+#define IPC_ACQUIRE_SUCCESS_MSK 0x80000000u
+#define IPC_LOCK_ACQUIRED_MSK 0x80000000u
+
+#define IPC_ID 2u
+#define IPC_INTR_ID 0u
+#define IPC_TIMEOUT_MS 1000
+
+#define SROMAPI_SIID_REQ 0x00000001u
+#define SROMAPI_SIID_REQ_FAMILY_REVISION (SROMAPI_SIID_REQ | 0x000u)
+#define SROMAPI_SIID_REQ_SIID_PROTECTION (SROMAPI_SIID_REQ | 0x100u)
+#define SROMAPI_WRITEROW_REQ 0x05000100u
+#define SROMAPI_PROGRAMROW_REQ 0x06000100u
+#define SROMAPI_ERASESECTOR_REQ 0x14000100u
+#define SROMAPI_ERASEALL_REQ 0x0A000100u
+#define SROMAPI_ERASEROW_REQ 0x1C000100u
+
+#define SROMAPI_STATUS_MSK 0xF0000000u
+#define SROMAPI_STAT_SUCCESS 0xA0000000u
+#define SROMAPI_DATA_LOCATION_MSK 0x00000001u
+#define SROMAPI_CALL_TIMEOUT_MS 1500
+
+struct psoc6_target_info {
+ uint32_t silicon_id;
+ uint8_t protection;
+ uint32_t main_flash_sz;
+ uint32_t row_sz;
+ bool is_probed;
+};
+
+struct timeout {
+ int64_t start_time;
+ long timeout_ms;
+};
+
+struct row_region {
+ uint32_t addr;
+ size_t size;
+};
+
+static const struct row_region safe_sflash_regions[] = {
+ {0x16000800, 0x800}, /* SFLASH: User Data */
+ {0x16001A00, 0x200}, /* SFLASH: NAR */
+ {0x16005A00, 0xC00}, /* SFLASH: Public Key */
+ {0x16007C00, 0x400}, /* SFLASH: TOC2 */
+};
+
+#define SFLASH_NUM_REGIONS (sizeof(safe_sflash_regions) / sizeof(safe_sflash_regions[0]))
+
+static struct working_area *g_stack_area;
+static struct armv7m_algorithm g_armv7m_info;
+
+/** ***********************************************************************************************
+ * @brief Initializes `struct timeout` structure with given timeout value
+ * @param to pointer to `struct timeout` structure
+ * @param timeout_ms timeout, in milliseconds
+ *************************************************************************************************/
+static void timeout_init(struct timeout *to, long timeout_ms)
+{
+ to->start_time = timeval_ms();
+ to->timeout_ms = timeout_ms;
+}
+
+/** ***********************************************************************************************
+ * @brief Returns true if given `struct timeout` structure has expired
+ * @param to pointer to `struct timeout` structure
+ * @return true if timeout expired
+ *************************************************************************************************/
+static bool timeout_expired(struct timeout *to)
+{
+ return (timeval_ms() - to->start_time) > to->timeout_ms;
+}
+
+/** ***********************************************************************************************
+ * @brief Starts pseudo flash algorithm and leaves it running. Function allocates working area for
+ * algorithm code and CPU stack, adjusts stack pointer, uploads and starts the algorithm.
+ * Algorithm (a basic infinite loop) runs asynchronously while driver performs Flash operations.
+ *
+ * @param target target for the algorithm
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int sromalgo_prepare(struct target *target)
+{
+ int hr;
+
+ /* Initialize Vector Table Offset register (in case FW modified it) */
+ hr = target_write_u32(target, 0xE000ED08, 0x00000000);
+ if (hr != ERROR_OK)
+ return hr;
+
+ /* Restore THUMB bit in xPSR register */
+ const struct armv7m_common *cm = target_to_armv7m(target);
+ hr = cm->store_core_reg_u32(target, ARMV7M_xPSR, 0x01000000);
+ if (hr != ERROR_OK)
+ return hr;
+
+ /* Allocate Working Area for Stack and Flash algorithm */
+ hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &g_stack_area);
+ if (hr != ERROR_OK)
+ return hr;
+
+ g_armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ g_armv7m_info.core_mode = ARM_MODE_THREAD;
+
+ struct reg_param reg_params;
+ init_reg_param(&reg_params, "sp", 32, PARAM_OUT);
+ buf_set_u32(reg_params.value, 0, 32, g_stack_area->address + g_stack_area->size);
+
+ /* Write basic infinite loop algorithm to target RAM */
+ hr = target_write_u32(target, g_stack_area->address, 0xFEE7FEE7);
+ if (hr != ERROR_OK)
+ goto destroy_rp_free_wa;
+
+ hr = target_start_algorithm(target, 0, NULL, 1, &reg_params, g_stack_area->address,
+ 0, &g_armv7m_info);
+ if (hr != ERROR_OK)
+ goto destroy_rp_free_wa;
+
+ destroy_reg_param(&reg_params);
+
+ return hr;
+
+destroy_rp_free_wa:
+ /* Something went wrong, do some cleanup */
+ destroy_reg_param(&reg_params);
+
+ if (g_stack_area) {
+ target_free_working_area(target, g_stack_area);
+ g_stack_area = NULL;
+ }
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Stops running flash algorithm and releases associated resources.
+ * This function is also used for cleanup in case of errors so g_stack_area may be NULL.
+ * These cases have to be handled gracefully.
+ *
+ * @param target current target
+ *************************************************************************************************/
+static void sromalgo_release(struct target *target)
+{
+ int hr = ERROR_OK;
+
+ if (g_stack_area) {
+ /* Stop flash algorithm if it is running */
+ if (target->running_alg) {
+ hr = target_halt(target);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ hr = target_wait_algorithm(target, 0, NULL, 0, NULL, 0,
+ IPC_TIMEOUT_MS, &g_armv7m_info);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+ }
+
+exit_free_wa:
+ /* Free Stack/Flash algorithm working area */
+ target_free_working_area(target, g_stack_area);
+ g_stack_area = NULL;
+ }
+}
+
+/** ***********************************************************************************************
+ * @brief Waits for expected IPC lock status. PSoC6 uses IPC structures for inter-core
+ * communication. Same IPCs are used to invoke SROM API. IPC structure must be locked prior to
+ * invoking any SROM API. This ensures nothing else in the system will use same IPC thus corrupting
+ * our data. Locking is performed by ipc_acquire(), this function ensures that IPC is actually
+ * in expected state
+ *
+ * @param target current target
+ * @param ipc_id IPC index to poll. IPC #2 is dedicated for DAP access
+ * @param lock_expected expected lock status
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_expected)
+{
+ int hr;
+ uint32_t reg_val;
+
+ struct timeout to;
+ timeout_init(&to, IPC_TIMEOUT_MS);
+
+ while (!timeout_expired(&to)) {
+ /* Process any server requests */
+ keep_alive();
+
+ /* Read IPC Lock status */
+ hr = target_read_u32(target, MEM_IPC_LOCK_STATUS(ipc_id), &reg_val);
+ if (hr != ERROR_OK) {
+ LOG_ERROR("Unable to read IPC Lock Status register");
+ return hr;
+ }
+
+ bool is_locked = (reg_val & IPC_LOCK_ACQUIRED_MSK) != 0;
+
+ if (lock_expected == is_locked)
+ return ERROR_OK;
+ }
+
+ if (target->coreid) {
+ LOG_WARNING("SROM API calls via CM4 target are supported on single-core PSoC6 devices only. "
+ "Please perform all Flash-related operations via CM0+ target on dual-core devices.");
+ }
+
+ LOG_ERROR("Timeout polling IPC Lock Status");
+ return ERROR_TARGET_TIMEOUT;
+}
+
+/** ***********************************************************************************************
+ * @brief Acquires IPC structure. PSoC6 uses IPC structures for inter-core communication.
+ * Same IPCs are used to invoke SROM API. IPC structure must be locked prior to invoking any SROM API.
+ * This ensures nothing else in the system will use same IPC thus corrupting our data.
+ * This function locks the IPC.
+ *
+ * @param target current target
+ * @param ipc_id ipc_id IPC index to acquire. IPC #2 is dedicated for DAP access
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int ipc_acquire(struct target *target, char ipc_id)
+{
+ int hr = ERROR_OK;
+ bool is_acquired = false;
+ uint32_t reg_val;
+
+ struct timeout to;
+ timeout_init(&to, IPC_TIMEOUT_MS);
+
+ while (!timeout_expired(&to)) {
+ keep_alive();
+
+ hr = target_write_u32(target, MEM_IPC_ACQUIRE(ipc_id), IPC_ACQUIRE_SUCCESS_MSK);
+ if (hr != ERROR_OK) {
+ LOG_ERROR("Unable to write to IPC Acquire register");
+ return hr;
+ }
+
+ /* Check if data is written on first step */
+ hr = target_read_u32(target, MEM_IPC_ACQUIRE(ipc_id), &reg_val);
+ if (hr != ERROR_OK) {
+ LOG_ERROR("Unable to read IPC Acquire register");
+ return hr;
+ }
+
+ is_acquired = (reg_val & IPC_ACQUIRE_SUCCESS_MSK) != 0;
+ if (is_acquired) {
+ /* If IPC structure is acquired, the lock status should be set */
+ hr = ipc_poll_lock_stat(target, ipc_id, true);
+ break;
+ }
+ }
+
+ if (!is_acquired)
+ LOG_ERROR("Timeout acquiring IPC structure");
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Invokes SROM API functions which are responsible for Flash operations
+ *
+ * @param target current target
+ * @param req_and_params requect id of the function to invoke
+ * @param working_area address of memory buffer in target's memory space for SROM API parameters
+ * @param data_out pointer to variable which will be populated with execution status
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int call_sromapi(struct target *target,
+ uint32_t req_and_params,
+ uint32_t working_area,
+ uint32_t *data_out)
+{
+ int hr;
+
+ bool is_data_in_ram = (req_and_params & SROMAPI_DATA_LOCATION_MSK) == 0;
+
+ hr = ipc_acquire(target, IPC_ID);
+ if (hr != ERROR_OK)
+ return hr;
+
+ if (is_data_in_ram)
+ hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), working_area);
+ else
+ hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), req_and_params);
+
+ if (hr != ERROR_OK)
+ return hr;
+
+ /* Enable notification interrupt of IPC_INTR_STRUCT0(CM0+) for IPC_STRUCT2 */
+ hr = target_write_u32(target, MEM_IPC_INTR_MASK(IPC_INTR_ID), 1u << (16 + IPC_ID));
+ if (hr != ERROR_OK)
+ return hr;
+
+ hr = target_write_u32(target, MEM_IPC_NOTIFY(IPC_ID), 1);
+ if (hr != ERROR_OK)
+ return hr;
+
+ /* Poll lock status */
+ hr = ipc_poll_lock_stat(target, IPC_ID, false);
+ if (hr != ERROR_OK)
+ return hr;
+
+ /* Poll Data byte */
+ if (is_data_in_ram)
+ hr = target_read_u32(target, working_area, data_out);
+ else
+ hr = target_read_u32(target, MEM_IPC_DATA(IPC_ID), data_out);
+
+ if (hr != ERROR_OK) {
+ LOG_ERROR("Error reading SROM API Status location");
+ return hr;
+ }
+
+ bool is_success = (*data_out & SROMAPI_STATUS_MSK) == SROMAPI_STAT_SUCCESS;
+ if (!is_success) {
+ LOG_ERROR("SROM API execution failed. Status: 0x%08X", (uint32_t)*data_out);
+ return ERROR_TARGET_FAILURE;
+ }
+
+ return ERROR_OK;
+}
+
+/** ***********************************************************************************************
+ * @brief Retrieves SiliconID and Protection status of the target device
+ * @param target current target
+ * @param si_id pointer to variable, will be populated with SiliconID
+ * @param protection pointer to variable, will be populated with protection status
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int get_silicon_id(struct target *target, uint32_t *si_id, uint8_t *protection)
+{
+ int hr;
+ uint32_t family_rev, siid_prot;
+
+ hr = sromalgo_prepare(target);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ /* Read FamilyID and Revision */
+ hr = call_sromapi(target, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ /* Read SiliconID and Protection */
+ hr = call_sromapi(target, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ *si_id = (siid_prot & 0x0000FFFF) << 16;
+ *si_id |= (family_rev & 0x00FF0000) >> 8;
+ *si_id |= (family_rev & 0x000000FF) >> 0;
+
+ *protection = (siid_prot & 0x000F0000) >> 0x10;
+
+exit:
+ sromalgo_release(target);
+ return ERROR_OK;
+}
+
+/** ***********************************************************************************************
+ * @brief Translates Protection status to openocd-friendly boolean value
+ * @param bank current flash bank
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_protect_check(struct flash_bank *bank)
+{
+ int is_protected;
+
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+ int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection);
+ if (hr != ERROR_OK)
+ return hr;
+
+ switch (psoc6_info->protection) {
+ case PROTECTION_VIRGIN:
+ case PROTECTION_NORMAL:
+ is_protected = 0;
+ break;
+
+ case PROTECTION_UNKNOWN:
+ case PROTECTION_SECURE:
+ case PROTECTION_DEAD:
+ default:
+ is_protected = 1;
+ break;
+ }
+
+ for (int i = 0; i < bank->num_sectors; i++)
+ bank->sectors[i].is_protected = is_protected;
+
+ return ERROR_OK;
+}
+
+/** ***********************************************************************************************
+ * @brief Dummy function, Life Cycle transition is not currently supported
+ * @return ERROR_OK always
+ *************************************************************************************************/
+static int psoc6_protect(struct flash_bank *bank, int set, int first, int last)
+{
+ (void)bank;
+ (void)set;
+ (void)first;
+ (void)last;
+
+ LOG_WARNING("Life Cycle transition for PSoC6 is not supported");
+ return ERROR_OK;
+}
+
+/** ***********************************************************************************************
+ * @brief Translates Protection status to string
+ * @param protection protection value
+ * @return pointer to const string describintg protection status
+ *************************************************************************************************/
+static const char *protection_to_str(uint8_t protection)
+{
+ switch (protection) {
+ case PROTECTION_VIRGIN:
+ return "VIRGIN";
+ break;
+ case PROTECTION_NORMAL:
+ return "NORMAL";
+ break;
+ case PROTECTION_SECURE:
+ return "SECURE";
+ break;
+ case PROTECTION_DEAD:
+ return "DEAD";
+ break;
+ case PROTECTION_UNKNOWN:
+ default:
+ return "UNKNOWN";
+ break;
+ }
+}
+
+/** ***********************************************************************************************
+ * @brief psoc6_get_info Displays human-readable information about acquired device
+ * @param bank current flash bank
+ * @param buf pointer to buffer for human-readable text
+ * @param buf_size size of the buffer
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_get_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+
+ if (psoc6_info->is_probed == false)
+ return ERROR_FAIL;
+
+ int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection);
+ if (hr != ERROR_OK)
+ return hr;
+
+ snprintf(buf, buf_size,
+ "PSoC6 Silicon ID: 0x%08X\n"
+ "Protection: %s\n"
+ "Main Flash size: %d kB\n"
+ "Work Flash size: 32 kB\n",
+ psoc6_info->silicon_id,
+ protection_to_str(psoc6_info->protection),
+ psoc6_info->main_flash_sz / 1024);
+
+ return ERROR_OK;
+}
+
+/** ***********************************************************************************************
+ * @brief Checks if given flash bank belongs to Supervisory Flash
+ * @param bank current flash bank
+ * @return true if flash bank belongs to Supervisory Flash
+ *************************************************************************************************/
+static bool is_sflash_bank(struct flash_bank *bank)
+{
+ for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) {
+ if (bank->base == safe_sflash_regions[i].addr)
+ return true;
+ }
+
+ return false;
+}
+
+/** ***********************************************************************************************
+ * @brief Checks if given flash bank belongs to Work Flash
+ * @param bank current flash bank
+ * @return true if flash bank belongs to Work Flash
+ *************************************************************************************************/
+static inline bool is_wflash_bank(struct flash_bank *bank)
+{
+ return (bank->base == MEM_BASE_WFLASH);
+}
+
+/** ***********************************************************************************************
+ * @brief Checks if given flash bank belongs to Main Flash
+ * @param bank current flash bank
+ * @return true if flash bank belongs to Main Flash
+ *************************************************************************************************/
+static inline bool is_mflash_bank(struct flash_bank *bank)
+{
+ return (bank->base == MEM_BASE_MFLASH);
+}
+
+/** ***********************************************************************************************
+ * @brief Probes the device and populates related data structures with target flash geometry data.
+ * This is done in non-intrusive way, no SROM API calls are involved so GDB can safely attach to a
+ * running target. Function assumes that size of Work Flash is 32kB (true for all current part numbers)
+ *
+ * @param bank current flash bank
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+
+ int hr = ERROR_OK;
+
+ /* Retrieve data from SPCIF_GEOMATRY */
+ uint32_t geom;
+ target_read_u32(target, PSOC6_SPCIF_GEOMETRY, &geom);
+ uint32_t row_sz_lg2 = (geom & 0xF0) >> 4;
+ uint32_t row_sz = (0x01 << row_sz_lg2);
+ uint32_t row_cnt = 1 + ((geom & 0x00FFFF00) >> 8);
+ uint32_t bank_cnt = 1 + ((geom & 0xFF000000) >> 24);
+
+ /* Calculate size of Main Flash*/
+ uint32_t flash_sz_bytes = bank_cnt * row_cnt * row_sz;
+
+ if (bank->sectors) {
+ free(bank->sectors);
+ bank->sectors = NULL;
+ }
+
+ size_t bank_size = 0;
+
+ if (is_mflash_bank(bank))
+ bank_size = flash_sz_bytes;
+ else if (is_wflash_bank(bank))
+ bank_size = MEM_WFLASH_SIZE;
+ else if (is_sflash_bank(bank)) {
+ for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) {
+ if (safe_sflash_regions[i].addr == bank->base) {
+ bank_size = safe_sflash_regions[i].size;
+ break;
+ }
+ }
+ }
+
+ if (bank_size == 0) {
+ LOG_ERROR("Invalid Flash Bank base address in config file");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ size_t num_sectors = bank_size / row_sz;
+ bank->size = bank_size;
+ bank->chip_width = 4;
+ bank->bus_width = 4;
+ bank->erased_value = 0;
+ bank->default_padded_value = 0;
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = calloc(num_sectors, sizeof(struct flash_sector));
+ for (size_t i = 0; i < num_sectors; i++) {
+ bank->sectors[i].size = row_sz;
+ bank->sectors[i].offset = i * row_sz;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = -1;
+ }
+
+ psoc6_info->is_probed = true;
+ psoc6_info->main_flash_sz = flash_sz_bytes;
+ psoc6_info->row_sz = row_sz;
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Probes target device only if it hasn't been probed yet
+ * @param bank current flash bank
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_auto_probe(struct flash_bank *bank)
+{
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+ int hr;
+
+ if (psoc6_info->is_probed)
+ hr = ERROR_OK;
+ else
+ hr = psoc6_probe(bank);
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Erases single sector (256k) on target device
+ * @param bank current flash bank
+ * @param wa working area for SROM API parameters
+ * @param addr starting address of the sector
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_erase_sector(struct flash_bank *bank, struct working_area *wa, uint32_t addr)
+{
+ struct target *target = bank->target;
+
+ LOG_DEBUG("Erasing SECTOR @%08X", addr);
+
+ int hr = target_write_u32(target, wa->address, SROMAPI_ERASESECTOR_REQ);
+ if (hr != ERROR_OK)
+ return hr;
+
+ hr = target_write_u32(target, wa->address + 0x04, addr);
+ if (hr != ERROR_OK)
+ return hr;
+
+ uint32_t data_out;
+ hr = call_sromapi(target, SROMAPI_ERASESECTOR_REQ, wa->address, &data_out);
+ if (hr != ERROR_OK)
+ LOG_ERROR("SECTOR @%08X not erased!", addr);
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Erases single row (512b) on target device
+ * @param bank current flash bank
+ * @param wa working area for SROM API parameters
+ * @param addr starting address of the flash row
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_erase_row(struct flash_bank *bank, struct working_area *wa, uint32_t addr)
+{
+ struct target *target = bank->target;
+
+ LOG_DEBUG("Erasing ROW @%08X", addr);
+
+ int hr = target_write_u32(target, wa->address, SROMAPI_ERASEROW_REQ);
+ if (hr != ERROR_OK)
+ return hr;
+
+ hr = target_write_u32(target, wa->address + 0x04, addr);
+ if (hr != ERROR_OK)
+ return hr;
+
+ uint32_t data_out;
+ hr = call_sromapi(target, SROMAPI_ERASEROW_REQ, wa->address, &data_out);
+ if (hr != ERROR_OK)
+ LOG_ERROR("ROW @%08X not erased!", addr);
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Performs Erase operation. Function will try to use biggest erase block possible to
+ * speedup the operation.
+ *
+ * @param bank current flash bank
+ * @param first first sector to erase
+ * @param last last sector to erase
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_erase(struct flash_bank *bank, int first, int last)
+{
+ struct target *target = bank->target;
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+ const uint32_t sector_size = is_wflash_bank(bank) ? WFLASH_SECTOR_SIZE : MFLASH_SECTOR_SIZE;
+
+ int hr;
+ struct working_area *wa;
+
+ if (is_sflash_bank(bank)) {
+ LOG_INFO("Erase operation on Supervisory Flash is not required, skipping");
+ return ERROR_OK;
+ }
+
+ hr = sromalgo_prepare(target);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ /* Number of rows in single sector */
+ const int rows_in_sector = sector_size / psoc6_info->row_sz;
+
+ while (last >= first) {
+ /* Erase Sector if we are on sector boundary and erase size covers whole sector */
+ if ((first % rows_in_sector) == 0 &&
+ (last - first + 1) >= rows_in_sector) {
+ hr = psoc6_erase_sector(bank, wa, bank->base + first * psoc6_info->row_sz);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ for (int i = first; i < first + rows_in_sector; i++)
+ bank->sectors[i].is_erased = 1;
+
+ first += rows_in_sector;
+ } else {
+ /* Perform Row Erase otherwise */
+ hr = psoc6_erase_row(bank, wa, bank->base + first * psoc6_info->row_sz);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ bank->sectors[first].is_erased = 1;
+ first += 1;
+ }
+ }
+
+exit_free_wa:
+ target_free_working_area(target, wa);
+exit:
+ sromalgo_release(target);
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Programs single Flash Row
+ * @param bank current flash bank
+ * @param addr address of the flash row
+ * @param buffer pointer to the buffer with data
+ * @param is_sflash true if current flash bank belongs to Supervisory Flash
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_program_row(struct flash_bank *bank,
+ uint32_t addr,
+ const uint8_t *buffer,
+ bool is_sflash)
+{
+ struct target *target = bank->target;
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+ struct working_area *wa;
+ const uint32_t sromapi_req = is_sflash ? SROMAPI_WRITEROW_REQ : SROMAPI_PROGRAMROW_REQ;
+ uint32_t data_out;
+ int hr = ERROR_OK;
+
+ LOG_DEBUG("Programming ROW @%08X", addr);
+
+ hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ hr = target_write_u32(target, wa->address, sromapi_req);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ hr = target_write_u32(target,
+ wa->address + 0x04,
+ 0x106);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ hr = target_write_u32(target, wa->address + 0x08, addr);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ hr = target_write_u32(target, wa->address + 0x0C, wa->address + 0x10);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ hr = target_write_buffer(target, wa->address + 0x10, psoc6_info->row_sz, buffer);
+ if (hr != ERROR_OK)
+ goto exit_free_wa;
+
+ hr = call_sromapi(target, sromapi_req, wa->address, &data_out);
+
+exit_free_wa:
+ target_free_working_area(target, wa);
+
+exit:
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Performs Program operation
+ * @param bank current flash bank
+ * @param buffer pointer to the buffer with data
+ * @param offset starting offset in falsh bank
+ * @param count number of bytes in buffer
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+static int psoc6_program(struct flash_bank *bank,
+ const uint8_t *buffer,
+ uint32_t offset,
+ uint32_t count)
+{
+ struct target *target = bank->target;
+ struct psoc6_target_info *psoc6_info = bank->driver_priv;
+ const bool is_sflash = is_sflash_bank(bank);
+ int hr;
+
+ uint8_t page_buf[psoc6_info->row_sz];
+
+ hr = sromalgo_prepare(target);
+ if (hr != ERROR_OK)
+ goto exit;
+
+ while (count) {
+ uint32_t row_offset = offset % psoc6_info->row_sz;
+ uint32_t aligned_addr = bank->base + offset - row_offset;
+ uint32_t row_bytes = MIN(psoc6_info->row_sz - row_offset, count);
+
+ memset(page_buf, 0, sizeof(page_buf));
+ memcpy(&page_buf[row_offset], buffer, row_bytes);
+
+ hr = psoc6_program_row(bank, aligned_addr, page_buf, is_sflash);
+ if (hr != ERROR_OK) {
+ LOG_ERROR("Failed to program Flash at address 0x%08X", aligned_addr);
+ goto exit;
+ }
+
+ buffer += row_bytes;
+ offset += row_bytes;
+ count -= row_bytes;
+ }
+
+exit:
+ sromalgo_release(target);
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Performs Mass Erase operation
+ * @param bank flash bank index to erase
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+COMMAND_HANDLER(psoc6_handle_mass_erase_command)
+{
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ struct flash_bank *bank;
+ int hr = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+ if (hr != ERROR_OK)
+ return hr;
+
+ hr = psoc6_erase(bank, 0, bank->num_sectors - 1);
+
+ return hr;
+}
+
+/** ***********************************************************************************************
+ * @brief Simulates broken Vector Catch
+ * Function will try to determine entry point of user application. If it succeeds it will set HW
+ * breakpoint at that address, issue SW Reset and remove the breakpoint afterwards.
+ * In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will
+ * reset CM4 anyway, so using SYSRESETREQ is safe here.
+ * In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core.
+ *
+ * @param target current target
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+int handle_reset_halt(struct target *target)
+{
+ int hr;
+ uint32_t reset_addr;
+ bool is_cm0 = (target->coreid == 0);
+
+ /* Halt target device */
+ if (target->state != TARGET_HALTED) {
+ hr = target_halt(target);
+ if (hr != ERROR_OK)
+ return hr;
+
+ target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS);
+ if (hr != ERROR_OK)
+ return hr;
+ }
+
+ /* Read Vector Offset register */
+ uint32_t vt_base;
+ const uint32_t vt_offset_reg = is_cm0 ? 0x402102B0 : 0x402102C0;
+ hr = target_read_u32(target, vt_offset_reg, &vt_base);
+ if (hr != ERROR_OK)
+ return ERROR_OK;
+
+ /* Invalid value means flash is empty */
+ vt_base &= 0xFFFFFF00;
+ if ((vt_base == 0) || (vt_base == 0xFFFFFF00))
+ return ERROR_OK;
+
+ /* Read Reset Vector value*/
+ hr = target_read_u32(target, vt_base + 4, &reset_addr);
+ if (hr != ERROR_OK)
+ return hr;
+
+ /* Invalid value means flash is empty */
+ if ((reset_addr == 0) || (reset_addr == 0xFFFFFF00))
+ return ERROR_OK;
+
+
+ /* Set breakpoint at User Application entry point */
+ hr = breakpoint_add(target, reset_addr, 2, BKPT_HARD);
+ if (hr != ERROR_OK)
+ return hr;
+
+ const struct armv7m_common *cm = target_to_armv7m(target);
+
+ /* PSoC6 reboots immediatelly after issuing SYSRESETREQ / VECTRESET
+ * this disables SWD/JTAG pins momentarily and may break communication
+ * Ignoring return value of mem_ap_write_atomic_u32 seems to be ok here */
+ if (is_cm0) {
+ /* Reset the CM0 by asserting SYSRESETREQ. This will also reset CM4 */
+ LOG_INFO("psoc6.cm0: bkpt @0x%08X, issuing SYSRESETREQ", reset_addr);
+ mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR,
+ AIRCR_VECTKEY | AIRCR_SYSRESETREQ);
+ } else {
+ LOG_INFO("psoc6.cm4: bkpt @0x%08X, issuing VECTRESET", reset_addr);
+ mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR,
+ AIRCR_VECTKEY | AIRCR_VECTRESET);
+ }
+
+ /* Wait 100ms for bootcode and reinitialize DAP */
+ usleep(100000);
+ dap_dp_init(cm->debug_ap->dap);
+
+ target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS);
+
+ /* Remove the break point */
+ breakpoint_remove(target, reset_addr);
+
+ return ERROR_OK;
+}
+
+/** ***********************************************************************************************
+ * @brief Simulates broken Vector Catch
+ * Function will try to determine entry point of user application. If it succeeds it will set HW
+ * breakpoint at that address, issue SW Reset and remove the breakpoint afterwards.
+ * In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will
+ * reset CM4 anyway, so using SYSRESETREQ is safe here.
+ * In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core.
+ *
+ * @return ERROR_OK in case of success, ERROR_XXX code otherwise
+ *************************************************************************************************/
+COMMAND_HANDLER(psoc6_handle_reset_halt)
+{
+ if (CMD_ARGC)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ struct target *target = get_current_target(CMD_CTX);
+ return handle_reset_halt(target);
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc6_flash_bank_command)
+{
+ struct psoc6_target_info *psoc6_info;
+ int hr = ERROR_OK;
+
+ if (CMD_ARGC < 6)
+ hr = ERROR_COMMAND_SYNTAX_ERROR;
+ else {
+ psoc6_info = calloc(1, sizeof(struct psoc6_target_info));
+ psoc6_info->is_probed = false;
+ bank->driver_priv = psoc6_info;
+ }
+ return hr;
+}
+
+static const struct command_registration psoc6_exec_command_handlers[] = {
+ {
+ .name = "mass_erase",
+ .handler = psoc6_handle_mass_erase_command,
+ .mode = COMMAND_EXEC,
+ .usage = "bank",
+ .help = "Erases entire Main Flash",
+ },
+ {
+ .name = "reset_halt",
+ .handler = psoc6_handle_reset_halt,
+ .mode = COMMAND_EXEC,
+ .usage = NULL,
+ .help = "Tries to simulate broken Vector Catch",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc6_command_handlers[] = {
+ {
+ .name = "psoc6",
+ .mode = COMMAND_ANY,
+ .help = "PSoC 6 flash command group",
+ .usage = "",
+ .chain = psoc6_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc6_flash = {
+ .name = "psoc6",
+ .commands = psoc6_command_handlers,
+ .flash_bank_command = psoc6_flash_bank_command,
+ .erase = psoc6_erase,
+ .protect = psoc6_protect,
+ .write = psoc6_program,
+ .read = default_flash_read,
+ .probe = psoc6_probe,
+ .auto_probe = psoc6_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = psoc6_protect_check,
+ .info = psoc6_get_info,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/sim3x.c b/src/flash/nor/sim3x.c
index ce9a21e..f282ba0 100644
--- a/src/flash/nor/sim3x.c
+++ b/src/flash/nor/sim3x.c
@@ -1122,5 +1122,6 @@ struct flash_driver sim3x_flash = {
.auto_probe = sim3x_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = sim3x_flash_protect_check,
- .info = sim3x_flash_info
+ .info = sim3x_flash_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/spi.c b/src/flash/nor/spi.c
index b255edb..6ac253d 100644
--- a/src/flash/nor/spi.c
+++ b/src/flash/nor/spi.c
@@ -54,6 +54,7 @@ const struct flash_device flash_devices[] = {
FLASH_ID("sp s25fl164k", 0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 0x800000),
FLASH_ID("sp s25fl128", 0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 0x1000000),
FLASH_ID("sp s25fl256", 0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 0x2000000),
+ FLASH_ID("cy s25fl256", 0xd8, 0xc7, 0x00196001, 0x100, 0x10000, 0x2000000),
FLASH_ID("atmel 25f512", 0x52, 0xc7, 0x0065001f, 0x80, 0x8000, 0x10000),
FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000),
FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000),
diff --git a/src/flash/nor/stellaris.c b/src/flash/nor/stellaris.c
index d28ceee..79aaf3b 100644
--- a/src/flash/nor/stellaris.c
+++ b/src/flash/nor/stellaris.c
@@ -1452,4 +1452,5 @@ struct flash_driver stellaris_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stellaris_protect_check,
.info = get_stellaris_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c
index d446707..faada9a 100644
--- a/src/flash/nor/stm32f1x.c
+++ b/src/flash/nor/stm32f1x.c
@@ -572,45 +572,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
- /* see contrib/loaders/flash/stm32f1x.S for src */
-
static const uint8_t stm32x_flash_write_code[] = {
- /* #define STM32_FLASH_SR_OFFSET 0x0C */
- /* wait_fifo: */
- 0x16, 0x68, /* ldr r6, [r2, #0] */
- 0x00, 0x2e, /* cmp r6, #0 */
- 0x18, 0xd0, /* beq exit */
- 0x55, 0x68, /* ldr r5, [r2, #4] */
- 0xb5, 0x42, /* cmp r5, r6 */
- 0xf9, 0xd0, /* beq wait_fifo */
- 0x2e, 0x88, /* ldrh r6, [r5, #0] */
- 0x26, 0x80, /* strh r6, [r4, #0] */
- 0x02, 0x35, /* adds r5, #2 */
- 0x02, 0x34, /* adds r4, #2 */
- /* busy: */
- 0xc6, 0x68, /* ldr r6, [r0, #STM32_FLASH_SR_OFFSET] */
- 0x01, 0x27, /* movs r7, #1 */
- 0x3e, 0x42, /* tst r6, r7 */
- 0xfb, 0xd1, /* bne busy */
- 0x14, 0x27, /* movs r7, #0x14 */
- 0x3e, 0x42, /* tst r6, r7 */
- 0x08, 0xd1, /* bne error */
- 0x9d, 0x42, /* cmp r5, r3 */
- 0x01, 0xd3, /* bcc no_wrap */
- 0x15, 0x46, /* mov r5, r2 */
- 0x08, 0x35, /* adds r5, #8 */
- /* no_wrap: */
- 0x55, 0x60, /* str r5, [r2, #4] */
- 0x01, 0x39, /* subs r1, r1, #1 */
- 0x00, 0x29, /* cmp r1, #0 */
- 0x02, 0xd0, /* beq exit */
- 0xe5, 0xe7, /* b wait_fifo */
- /* error: */
- 0x00, 0x20, /* movs r0, #0 */
- 0x50, 0x60, /* str r0, [r2, #4] */
- /* exit: */
- 0x30, 0x46, /* mov r0, r6 */
- 0x00, 0xbe, /* bkpt #0 */
+#include "../../../contrib/loaders/flash/stm32/stm32f1x.inc"
};
/* flash write code */
@@ -622,8 +585,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
retval = target_write_buffer(target, write_algorithm->address,
sizeof(stm32x_flash_write_code), stm32x_flash_write_code);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ target_free_working_area(target, write_algorithm);
return retval;
+ }
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
@@ -1647,4 +1612,5 @@ struct flash_driver stm32f1x_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check,
.info = get_stm32x_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c
index b0992b4..413d04d 100644
--- a/src/flash/nor/stm32f2x.c
+++ b/src/flash/nor/stm32f2x.c
@@ -252,6 +252,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
/* Clear but report errors */
if (status & FLASH_ERROR) {
+ if (retval == ERROR_OK)
+ retval = ERROR_FAIL;
/* If this operation fails, we ignore it and report the original
* retval
*/
@@ -584,45 +586,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
- /* see contrib/loaders/flash/stm32f2x.S for src */
-
static const uint8_t stm32x_flash_write_code[] = {
- /* wait_fifo: */
- 0xD0, 0xF8, 0x00, 0x80, /* ldr r8, [r0, #0] */
- 0xB8, 0xF1, 0x00, 0x0F, /* cmp r8, #0 */
- 0x1A, 0xD0, /* beq exit */
- 0x47, 0x68, /* ldr r7, [r0, #4] */
- 0x47, 0x45, /* cmp r7, r8 */
- 0xF7, 0xD0, /* beq wait_fifo */
-
- 0xDF, 0xF8, 0x34, 0x60, /* ldr r6, STM32_PROG16 */
- 0x26, 0x61, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */
- 0x37, 0xF8, 0x02, 0x6B, /* ldrh r6, [r7], #0x02 */
- 0x22, 0xF8, 0x02, 0x6B, /* strh r6, [r2], #0x02 */
- 0xBF, 0xF3, 0x4F, 0x8F, /* dsb sy */
- /* busy: */
- 0xE6, 0x68, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */
- 0x16, 0xF4, 0x80, 0x3F, /* tst r6, #0x10000 */
- 0xFB, 0xD1, /* bne busy */
- 0x16, 0xF0, 0xF0, 0x0F, /* tst r6, #0xf0 */
- 0x07, 0xD1, /* bne error */
-
- 0x8F, 0x42, /* cmp r7, r1 */
- 0x28, 0xBF, /* it cs */
- 0x00, 0xF1, 0x08, 0x07, /* addcs r7, r0, #8 */
- 0x47, 0x60, /* str r7, [r0, #4] */
- 0x01, 0x3B, /* subs r3, r3, #1 */
- 0x13, 0xB1, /* cbz r3, exit */
- 0xDF, 0xE7, /* b wait_fifo */
- /* error: */
- 0x00, 0x21, /* movs r1, #0 */
- 0x41, 0x60, /* str r1, [r0, #4] */
- /* exit: */
- 0x30, 0x46, /* mov r0, r6 */
- 0x00, 0xBE, /* bkpt #0x00 */
-
- /* <STM32_PROG16>: */
- 0x01, 0x01, 0x00, 0x00, /* .word 0x00000101 */
+#include "../../../contrib/loaders/flash/stm32/stm32f2x.inc"
};
if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
@@ -634,8 +599,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
retval = target_write_buffer(target, write_algorithm->address,
sizeof(stm32x_flash_write_code),
stm32x_flash_write_code);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ target_free_working_area(target, write_algorithm);
return retval;
+ }
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
@@ -1634,4 +1601,5 @@ struct flash_driver stm32f2x_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check,
.info = get_stm32x_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/stm32h7x.c b/src/flash/nor/stm32h7x.c
index 01e6f06..fcfcf91 100644
--- a/src/flash/nor/stm32h7x.c
+++ b/src/flash/nor/stm32h7x.c
@@ -64,7 +64,7 @@
#define FLASH_WRPERR (1 << 17) /* Write protection error */
#define FLASH_PGSERR (1 << 18) /* Programming sequence error */
#define FLASH_STRBERR (1 << 19) /* Strobe error */
-#define FLASH_INCERR (1 << 21) /* Increment error */
+#define FLASH_INCERR (1 << 21) /* Inconsistency error */
#define FLASH_OPERR (1 << 22) /* Operation error */
#define FLASH_RDPERR (1 << 23) /* Read Protection error */
#define FLASH_RDSERR (1 << 24) /* Secure Protection error */
@@ -220,6 +220,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
/* Clear error + EOP flags but report errors */
if (status & FLASH_ERROR) {
+ if (retval == ERROR_OK)
+ retval = ERROR_FAIL;
/* If this operation fails, we ignore it and report the original retval */
target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), status);
}
@@ -495,7 +497,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
if (retval != ERROR_OK) {
- LOG_ERROR("erase time-out error sector %d", i);
+ LOG_ERROR("erase time-out or operation error sector %d", i);
return retval;
}
bank->sectors[i].is_erased = 1;
@@ -568,51 +570,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
int retval = ERROR_OK;
- /* see contrib/loaders/flash/smt32h7x.S for src */
static const uint8_t stm32x_flash_write_code[] = {
- /* <code>: */
- 0x45, 0x68, /* ldr r5, [r0, #4] */
- /* <wait_fifo>: */
- 0x06, 0x68, /* ldr r6, [r0, #0] */
- 0x26, 0xb3, /* cbz r6, <exit> */
- 0x76, 0x1b, /* subs r6, r6, r5 */
- 0x42, 0xbf, /* ittt mi */
- 0x76, 0x18, /* addmi r6, r6, r1 */
- 0x36, 0x1a, /* submi r6, r6, r0 */
- 0x08, 0x3e, /* submi r6, #8 */
- 0x20, 0x2e, /* cmp r6, #32 */
- 0xf6, 0xd3, /* bcc.n <wait_fifo> */
- 0x4f, 0xf0, 0x32, 0x06, /* mov.w r6, #STM32_PROG */
- 0xe6, 0x60, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */
- 0x4f, 0xf0, 0x08, 0x07, /* mov.w r7, #8 */
- /* <write_flash>: */
- 0x55, 0xf8, 0x04, 0x6b, /* ldr.w r6, [r5], #4 */
- 0x42, 0xf8, 0x04, 0x6b, /* str.w r6, [r2], #4 */
- 0xbf, 0xf3, 0x4f, 0x8f, /* dsb sy */
- 0x8d, 0x42, /* cmp r5, r1 */
- 0x28, 0xbf, /* it cs */
- 0x00, 0xf1, 0x08, 0x05, /* addcs.w r5, r0, #8 */
- 0x01, 0x3f, /* subs r7, #1 */
- 0xf3, 0xd1, /* bne.n <write_flash> */
- /* <busy>: */
- 0x26, 0x69, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */
- 0x16, 0xf0, 0x01, 0x0f, /* tst.w r6, #STM32_SR_BUSY_MASK */
- 0xfb, 0xd1, /* bne.n <busy> */
- 0x05, 0x4f, /* ldr r7, [pc, #20] ; (<stm32_sr_error_mask>) */
- 0x3e, 0x42, /* tst r6, r7 */
- 0x03, 0xd1, /* bne.n <error> */
- 0x45, 0x60, /* str r5, [r0, #4] */
- 0x01, 0x3b, /* subs r3, #1 */
- 0xdb, 0xd1, /* bne.n <wait_fifo> */
- 0x01, 0xe0, /* b.n <exit> */
- /* <error>: */
- 0x00, 0x27, /* movs r7, #0 */
- 0x47, 0x60, /* str r7, [r0, #4] */
- /* <exit>: */
- 0x30, 0x46, /* mov r0, r6 */
- 0x00, 0xbe, /* bkpt 0x0000 */
- /* <stm32_sr_error_mask>: */
- 0x00, 0x00, 0xee, 0x03 /* .word 0x03ee0000 ; (STM32_SR_ERROR_MASK) */
+#include "../../../contrib/loaders/flash/stm32/stm32h7x.inc"
};
if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
@@ -624,8 +583,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
retval = target_write_buffer(target, write_algorithm->address,
sizeof(stm32x_flash_write_code),
stm32x_flash_write_code);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ target_free_working_area(target, write_algorithm);
return retval;
+ }
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
@@ -1180,4 +1141,5 @@ struct flash_driver stm32h7x_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stm32x_protect_check,
.info = stm32x_get_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c
index 6a1fa07..4fb7e03 100644
--- a/src/flash/nor/stm32l4x.c
+++ b/src/flash/nor/stm32l4x.c
@@ -187,6 +187,8 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
/* Clear but report errors */
if (status & FLASH_ERROR) {
+ if (retval == ERROR_OK)
+ retval = ERROR_FAIL;
/* If this operation fails, we ignore it and report the original
* retval
*/
@@ -461,19 +463,8 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
- /* See contrib/loaders/flash/stm32l4x.S for source and
- * hints how to generate the data!
- */
-
static const uint8_t stm32l4_flash_write_code[] = {
- 0xd0, 0xf8, 0x00, 0x80, 0xb8, 0xf1, 0x00, 0x0f, 0x21, 0xd0, 0x45, 0x68,
- 0xb8, 0xeb, 0x05, 0x06, 0x44, 0xbf, 0x76, 0x18, 0x36, 0x1a, 0x08, 0x2e,
- 0xf2, 0xd3, 0xdf, 0xf8, 0x36, 0x60, 0x66, 0x61, 0xf5, 0xe8, 0x02, 0x67,
- 0xe2, 0xe8, 0x02, 0x67, 0xbf, 0xf3, 0x4f, 0x8f, 0x26, 0x69, 0x16, 0xf4,
- 0x80, 0x3f, 0xfb, 0xd1, 0x16, 0xf0, 0xfa, 0x0f, 0x07, 0xd1, 0x8d, 0x42,
- 0x28, 0xbf, 0x00, 0xf1, 0x08, 0x05, 0x45, 0x60, 0x01, 0x3b, 0x13, 0xb1,
- 0xda, 0xe7, 0x00, 0x21, 0x41, 0x60, 0x30, 0x46, 0x00, 0xbe, 0x01, 0x00,
- 0x00, 0x00
+#include "../../../contrib/loaders/flash/stm32/stm32l4x.inc"
};
if (target_alloc_working_area(target, sizeof(stm32l4_flash_write_code),
@@ -485,8 +476,10 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
retval = target_write_buffer(target, write_algorithm->address,
sizeof(stm32l4_flash_write_code),
stm32l4_flash_write_code);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ target_free_working_area(target, write_algorithm);
return retval;
+ }
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) !=
@@ -953,4 +946,5 @@ struct flash_driver stm32l4x_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stm32l4_protect_check,
.info = get_stm32l4_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c
index fdfaad4..3251df3 100644
--- a/src/flash/nor/stm32lx.c
+++ b/src/flash/nor/stm32lx.c
@@ -146,7 +146,7 @@ static const struct stm32lx_rev stm32_425_revs[] = {
{ 0x1000, "A" }, { 0x2000, "B" }, { 0x2008, "Y" },
};
static const struct stm32lx_rev stm32_427_revs[] = {
- { 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" },
+ { 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" }, { 0x10f8, "V" },
};
static const struct stm32lx_rev stm32_429_revs[] = {
{ 0x1000, "A" }, { 0x1018, "Z" },
@@ -448,10 +448,8 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
int retval = ERROR_OK;
- /* see contib/loaders/flash/stm32lx.S for src */
-
static const uint8_t stm32lx_flash_write_code[] = {
- 0x92, 0x00, 0x8A, 0x18, 0x01, 0xE0, 0x08, 0xC9, 0x08, 0xC0, 0x91, 0x42, 0xFB, 0xD1, 0x00, 0xBE
+#include "../../../contrib/loaders/flash/stm32/stm32lx.inc"
};
/* Make sure we're performing a half-page aligned write. */
@@ -965,6 +963,7 @@ struct flash_driver stm32lx_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stm32lx_protect_check,
.info = stm32lx_get_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
/* Static methods implementation */
diff --git a/src/flash/nor/stmsmi.c b/src/flash/nor/stmsmi.c
index 781ea3b..c839bf7 100644
--- a/src/flash/nor/stmsmi.c
+++ b/src/flash/nor/stmsmi.c
@@ -654,4 +654,5 @@ struct flash_driver stmsmi_flash = {
.erase_check = default_flash_blank_check,
.protect_check = stmsmi_protect_check,
.info = get_stmsmi_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/str7x.c b/src/flash/nor/str7x.c
index 11179f5..015202a 100644
--- a/src/flash/nor/str7x.c
+++ b/src/flash/nor/str7x.c
@@ -812,4 +812,5 @@ struct flash_driver str7x_flash = {
.erase_check = default_flash_blank_check,
.protect_check = str7x_protect_check,
.info = get_str7x_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/str9x.c b/src/flash/nor/str9x.c
index 3b7ca2a..37700ce 100644
--- a/src/flash/nor/str9x.c
+++ b/src/flash/nor/str9x.c
@@ -679,4 +679,5 @@ struct flash_driver str9x_flash = {
.auto_probe = str9x_probe,
.erase_check = default_flash_blank_check,
.protect_check = str9x_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/str9xpec.c b/src/flash/nor/str9xpec.c
index eb391e8..29e0977 100644
--- a/src/flash/nor/str9xpec.c
+++ b/src/flash/nor/str9xpec.c
@@ -1207,4 +1207,5 @@ struct flash_driver str9xpec_flash = {
.auto_probe = str9xpec_probe,
.erase_check = str9xpec_erase_check,
.protect_check = str9xpec_protect_check,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/tcl.c b/src/flash/nor/tcl.c
index e5e2801..95ca819 100644
--- a/src/flash/nor/tcl.c
+++ b/src/flash/nor/tcl.c
@@ -3,6 +3,7 @@
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
+ * Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.cz> *
* *
* 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 *
@@ -287,24 +288,6 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
return retval;
}
-static int flash_check_sector_parameters(struct command_context *cmd_ctx,
- uint32_t first, uint32_t last, uint32_t num_sectors)
-{
- if (!(first <= last)) {
- command_print(cmd_ctx, "ERROR: "
- "first sector must be <= last sector");
- return ERROR_FAIL;
- }
-
- if (!(last <= (num_sectors - 1))) {
- command_print(cmd_ctx, "ERROR: last sector must be <= %" PRIu32,
- num_sectors - 1);
- return ERROR_FAIL;
- }
-
- return ERROR_OK;
-}
-
COMMAND_HANDLER(handle_flash_erase_command)
{
if (CMD_ARGC != 3)
@@ -326,9 +309,18 @@ COMMAND_HANDLER(handle_flash_erase_command)
else
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
- retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors);
- if (retval != ERROR_OK)
- return retval;
+ if (!(first <= last)) {
+ command_print(CMD_CTX, "ERROR: "
+ "first sector must be <= last");
+ return ERROR_FAIL;
+ }
+
+ if (!(last <= (uint32_t)(p->num_sectors - 1))) {
+ command_print(CMD_CTX, "ERROR: "
+ "last sector must be <= %" PRIu32,
+ p->num_sectors - 1);
+ return ERROR_FAIL;
+ }
struct duration bench;
duration_start(&bench);
@@ -374,15 +366,28 @@ COMMAND_HANDLER(handle_flash_protect_command)
bool set;
COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set);
- retval = flash_check_sector_parameters(CMD_CTX, first, last, num_blocks);
- if (retval != ERROR_OK)
- return retval;
+ if (!(first <= last)) {
+ command_print(CMD_CTX, "ERROR: "
+ "first %s must be <= last",
+ (p->num_prot_blocks) ? "block" : "sector");
+ return ERROR_FAIL;
+ }
+
+ if (!(last <= (uint32_t)(num_blocks - 1))) {
+ command_print(CMD_CTX, "ERROR: "
+ "last %s must be <= %" PRIu32,
+ (p->num_prot_blocks) ? "block" : "sector",
+ num_blocks - 1);
+ return ERROR_FAIL;
+ }
retval = flash_driver_protect(p, set, first, last);
if (retval == ERROR_OK) {
- command_print(CMD_CTX, "%s protection for sectors %" PRIu32
+ command_print(CMD_CTX, "%s protection for %s %" PRIu32
" through %" PRIu32 " on flash bank %d",
- (set) ? "set" : "cleared", first, last, p->bank_number);
+ (set) ? "set" : "cleared",
+ (p->num_prot_blocks) ? "blocks" : "sectors",
+ first, last, p->bank_number);
}
return retval;
@@ -460,42 +465,29 @@ COMMAND_HANDLER(handle_flash_write_image_command)
COMMAND_HANDLER(handle_flash_fill_command)
{
- int err = ERROR_OK;
- uint32_t address;
+ target_addr_t address;
uint32_t pattern;
uint32_t count;
- uint32_t wrote = 0;
- uint32_t cur_size = 0;
- uint32_t chunk_count;
struct target *target = get_current_target(CMD_CTX);
unsigned i;
uint32_t wordsize;
- int retval = ERROR_OK;
-
- static size_t const chunksize = 1024;
- uint8_t *chunk = NULL, *readback = NULL;
+ int retval;
- if (CMD_ARGC != 3) {
- retval = ERROR_COMMAND_SYNTAX_ERROR;
- goto done;
- }
+ if (CMD_ARGC != 3)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+#if BUILD_TARGET64
+ COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], address);
+#else
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+#endif
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], pattern);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], count);
- chunk = malloc(chunksize);
- if (chunk == NULL)
- return ERROR_FAIL;
-
- readback = malloc(chunksize);
- if (readback == NULL) {
- free(chunk);
- return ERROR_FAIL;
- }
-
- if (count == 0)
- goto done;
+ struct flash_bank *bank;
+ retval = get_flash_bank_by_addr(target, address, true, &bank);
+ if (retval != ERROR_OK)
+ return retval;
switch (CMD_NAME[4]) {
case 'w':
@@ -508,73 +500,109 @@ COMMAND_HANDLER(handle_flash_fill_command)
wordsize = 1;
break;
default:
- retval = ERROR_COMMAND_SYNTAX_ERROR;
- goto done;
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (count == 0)
+ return ERROR_OK;
+
+ if (address + count * wordsize > bank->base + bank->size) {
+ LOG_ERROR("Cannot cross flash bank borders");
+ return ERROR_FAIL;
}
- chunk_count = MIN(count, (chunksize / wordsize));
+ uint32_t size_bytes = count * wordsize;
+ target_addr_t aligned_start = flash_write_align_start(bank, address);
+ target_addr_t end_addr = address + size_bytes - 1;
+ target_addr_t aligned_end = flash_write_align_end(bank, end_addr);
+ uint32_t aligned_size = aligned_end + 1 - aligned_start;
+ uint32_t padding_at_start = address - aligned_start;
+ uint32_t padding_at_end = aligned_end - end_addr;
+
+ uint8_t *buffer = malloc(aligned_size);
+ if (buffer == NULL)
+ return ERROR_FAIL;
+
+ if (padding_at_start) {
+ memset(buffer, bank->default_padded_value, padding_at_start);
+ LOG_WARNING("Start address " TARGET_ADDR_FMT
+ " breaks the required alignment of flash bank %s",
+ address, bank->name);
+ LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT,
+ padding_at_start, aligned_start);
+ }
+
+ uint8_t *ptr = buffer + padding_at_start;
+
switch (wordsize) {
case 4:
- for (i = 0; i < chunk_count; i++)
- target_buffer_set_u32(target, chunk + i * wordsize, pattern);
+ for (i = 0; i < count; i++, ptr += wordsize)
+ target_buffer_set_u32(target, ptr, pattern);
break;
case 2:
- for (i = 0; i < chunk_count; i++)
- target_buffer_set_u16(target, chunk + i * wordsize, pattern);
+ for (i = 0; i < count; i++, ptr += wordsize)
+ target_buffer_set_u16(target, ptr, pattern);
break;
case 1:
- memset(chunk, pattern, chunk_count);
+ memset(ptr, pattern, count);
+ ptr += count;
break;
default:
LOG_ERROR("BUG: can't happen");
exit(-1);
}
+ if (padding_at_end) {
+ memset(ptr, bank->default_padded_value, padding_at_end);
+ LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32
+ " bytes (bank write end alignment)",
+ end_addr + 1, padding_at_end);
+ }
+
struct duration bench;
duration_start(&bench);
- for (wrote = 0; wrote < (count*wordsize); wrote += cur_size) {
- struct flash_bank *bank;
+ retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size);
+ if (retval != ERROR_OK)
+ goto done;
- retval = get_flash_bank_by_addr(target, address, true, &bank);
- if (retval != ERROR_OK)
- goto done;
+ retval = flash_driver_read(bank, buffer, address - bank->base, size_bytes);
+ if (retval != ERROR_OK)
+ goto done;
- cur_size = MIN((count * wordsize - wrote), chunksize);
- err = flash_driver_write(bank, chunk, address - bank->base + wrote, cur_size);
- if (err != ERROR_OK) {
- retval = err;
- goto done;
- }
+ for (i = 0, ptr = buffer; i < count; i++) {
+ uint32_t readback = 0;
- err = flash_driver_read(bank, readback, address - bank->base + wrote, cur_size);
- if (err != ERROR_OK) {
- retval = err;
- goto done;
+ switch (wordsize) {
+ case 4:
+ readback = target_buffer_get_u32(target, ptr);
+ break;
+ case 2:
+ readback = target_buffer_get_u16(target, ptr);
+ break;
+ case 1:
+ readback = *ptr;
+ break;
}
-
- for (i = 0; i < cur_size; i++) {
- if (readback[i] != chunk[i]) {
- LOG_ERROR(
- "Verification error address 0x%08" PRIx32 ", read back 0x%02x, expected 0x%02x",
- address + wrote + i,
- readback[i],
- chunk[i]);
- retval = ERROR_FAIL;
- goto done;
- }
+ if (readback != pattern) {
+ LOG_ERROR(
+ "Verification error address " TARGET_ADDR_FMT
+ ", read back 0x%02" PRIx32 ", expected 0x%02" PRIx32,
+ address + i * wordsize, readback, pattern);
+ retval = ERROR_FAIL;
+ goto done;
}
+ ptr += wordsize;
}
if ((retval == ERROR_OK) && (duration_measure(&bench) == ERROR_OK)) {
- command_print(CMD_CTX, "wrote %" PRIu32 " bytes to 0x%8.8" PRIx32
- " in %fs (%0.3f KiB/s)", wrote, address,
- duration_elapsed(&bench), duration_kbps(&bench, wrote));
+ command_print(CMD_CTX, "wrote %" PRIu32 " bytes to " TARGET_ADDR_FMT
+ " in %fs (%0.3f KiB/s)", size_bytes, address,
+ duration_elapsed(&bench), duration_kbps(&bench, size_bytes));
}
done:
- free(readback);
- free(chunk);
+ free(buffer);
return retval;
}
@@ -592,8 +620,8 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
struct duration bench;
duration_start(&bench);
- struct flash_bank *p;
- int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
+ struct flash_bank *bank;
+ int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
@@ -602,7 +630,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
if (CMD_ARGC > 2)
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset);
- if (offset > p->size) {
+ if (offset > bank->size) {
LOG_ERROR("Offset 0x%8.8" PRIx32 " is out of range of the flash bank",
offset);
return ERROR_COMMAND_ARGUMENT_INVALID;
@@ -618,7 +646,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
return retval;
}
- length = MIN(filesize, p->size - offset);
+ length = MIN(filesize, bank->size - offset);
if (!length) {
LOG_INFO("Nothing to write to flash bank");
@@ -630,14 +658,33 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
LOG_INFO("File content exceeds flash bank size. Only writing the "
"first %zu bytes of the file", length);
- buffer = malloc(length);
+ target_addr_t start_addr = bank->base + offset;
+ target_addr_t aligned_start = flash_write_align_start(bank, start_addr);
+ target_addr_t end_addr = start_addr + length - 1;
+ target_addr_t aligned_end = flash_write_align_end(bank, end_addr);
+ uint32_t aligned_size = aligned_end + 1 - aligned_start;
+ uint32_t padding_at_start = start_addr - aligned_start;
+ uint32_t padding_at_end = aligned_end - end_addr;
+
+ buffer = malloc(aligned_size);
if (buffer == NULL) {
fileio_close(fileio);
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
+
+ if (padding_at_start) {
+ memset(buffer, bank->default_padded_value, padding_at_start);
+ LOG_WARNING("Start offset 0x%08" PRIx32
+ " breaks the required alignment of flash bank %s",
+ offset, bank->name);
+ LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT,
+ padding_at_start, aligned_start);
+ }
+
+ uint8_t *ptr = buffer + padding_at_start;
size_t buf_cnt;
- if (fileio_read(fileio, length, buffer, &buf_cnt) != ERROR_OK) {
+ if (fileio_read(fileio, length, ptr, &buf_cnt) != ERROR_OK) {
free(buffer);
fileio_close(fileio);
return ERROR_FAIL;
@@ -649,15 +696,23 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
return ERROR_FAIL;
}
- retval = flash_driver_write(p, buffer, offset, length);
+ ptr += length;
+
+ if (padding_at_end) {
+ memset(ptr, bank->default_padded_value, padding_at_end);
+ LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32
+ " bytes (bank write end alignment)",
+ end_addr + 1, padding_at_end);
+ }
+
+ retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size);
free(buffer);
- buffer = NULL;
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "wrote %zu bytes from file %s to flash bank %u"
" at offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)",
- length, CMD_ARGV[1], p->bank_number, offset,
+ length, CMD_ARGV[1], bank->bank_number, offset,
duration_elapsed(&bench), duration_kbps(&bench, length));
}
@@ -1071,21 +1126,16 @@ COMMAND_HANDLER(handle_flash_bank_command)
}
}
- struct flash_bank *c = malloc(sizeof(*c));
+ struct flash_bank *c = calloc(1, sizeof(*c));
c->name = strdup(bank_name);
c->target = target;
c->driver = driver;
- c->driver_priv = NULL;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], c->base);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], c->size);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], c->chip_width);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], c->bus_width);
c->default_padded_value = c->erased_value = 0xff;
- c->num_sectors = 0;
- c->sectors = NULL;
- c->num_prot_blocks = 0;
- c->prot_blocks = NULL;
- c->next = NULL;
+ c->minimal_write_gap = FLASH_WRITE_GAP_SECTOR;
int retval;
retval = CALL_COMMAND_HANDLER(driver->flash_bank_command, c);
diff --git a/src/flash/nor/tms470.c b/src/flash/nor/tms470.c
index a70891e..2435e79 100644
--- a/src/flash/nor/tms470.c
+++ b/src/flash/nor/tms470.c
@@ -151,6 +151,7 @@ static int tms470_read_part_info(struct flash_bank *bank)
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
+ bank->num_sectors = 0;
}
/*
@@ -754,9 +755,6 @@ static int tms470_erase_sector(struct flash_bank *bank, int sector)
target_write_u32(target, 0xFFFFFFDC, glbctrl);
LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl);
- if (result == ERROR_OK)
- bank->sectors[sector].is_erased = 1;
-
return result;
}
@@ -1044,27 +1042,17 @@ static int tms470_erase_check(struct flash_bank *bank)
* an attempt to reduce the JTAG overhead.
*/
for (sector = 0; sector < bank->num_sectors; sector++) {
- if (bank->sectors[sector].is_erased != 1) {
- uint32_t i, addr = bank->base + bank->sectors[sector].offset;
-
- LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector);
-
- target_read_buffer(target, addr, bank->sectors[sector].size, buffer);
-
- bank->sectors[sector].is_erased = 1;
- for (i = 0; i < bank->sectors[sector].size; i++) {
- if (buffer[i] != 0xff) {
- LOG_WARNING("tms470 bank %d, sector %d, not erased.",
- tms470_info->ordinal,
- sector);
- LOG_WARNING(
- "at location 0x%08" PRIx32 ": flash data is 0x%02x.",
- addr + i,
- buffer[i]);
-
- bank->sectors[sector].is_erased = 0;
- break;
- }
+ uint32_t i, addr = bank->base + bank->sectors[sector].offset;
+
+ LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector);
+
+ target_read_buffer(target, addr, bank->sectors[sector].size, buffer);
+
+ bank->sectors[sector].is_erased = 1;
+ for (i = 0; i < bank->sectors[sector].size; i++) {
+ if (buffer[i] != 0xff) {
+ bank->sectors[sector].is_erased = 0;
+ break;
}
}
if (bank->sectors[sector].is_erased != 1) {
@@ -1186,4 +1174,5 @@ struct flash_driver tms470_flash = {
.erase_check = tms470_erase_check,
.protect_check = tms470_protect_check,
.info = get_tms470_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/virtual.c b/src/flash/nor/virtual.c
index 06981f4..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)
@@ -231,4 +236,5 @@ struct flash_driver virtual_flash = {
.erase_check = virtual_blank_check,
.protect_check = virtual_protect_check,
.info = virtual_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/xcf.c b/src/flash/nor/xcf.c
index 035791e..bc4b1be 100644
--- a/src/flash/nor/xcf.c
+++ b/src/flash/nor/xcf.c
@@ -636,6 +636,7 @@ static int xcf_probe(struct flash_bank *bank)
fill_sector_table(bank);
priv->probed = true;
+ /* REVISIT: Why is unchanged bank->driver_priv rewritten by same value? */
bank->driver_priv = priv;
LOG_INFO("product name: %s", product_name(bank));
@@ -893,5 +894,6 @@ struct flash_driver xcf_flash = {
.auto_probe = xcf_auto_probe,
.erase_check = xcf_erase_check,
.protect_check = xcf_protect_check,
- .info = xcf_info
+ .info = xcf_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/xmc1xxx.c b/src/flash/nor/xmc1xxx.c
index 0a76b21..4b25398 100644
--- a/src/flash/nor/xmc1xxx.c
+++ b/src/flash/nor/xmc1xxx.c
@@ -546,4 +546,5 @@ struct flash_driver xmc1xxx_flash = {
.erase = xmc1xxx_erase,
.erase_check = xmc1xxx_erase_check,
.write = xmc1xxx_write,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/flash/nor/xmc4xxx.c b/src/flash/nor/xmc4xxx.c
index 5677ef0..0b6d48c 100644
--- a/src/flash/nor/xmc4xxx.c
+++ b/src/flash/nor/xmc4xxx.c
@@ -1356,4 +1356,5 @@ struct flash_driver xmc4xxx_flash = {
.info = xmc4xxx_get_info_command,
.protect_check = xmc4xxx_protect_check,
.protect = xmc4xxx_protect,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/helper/command.c b/src/helper/command.c
index cbd52fb..f8b731e 100644
--- a/src/helper/command.c
+++ b/src/helper/command.c
@@ -608,7 +608,23 @@ static int run_command(struct command_context *context,
.argc = num_words - 1,
.argv = words + 1,
};
+ /* Black magic of overridden current target:
+ * If the command we are going to handle has a target prefix,
+ * override the current target temporarily for the time
+ * of processing the command.
+ * current_target_override is used also for event handlers
+ * therefore we prevent touching it if command has no prefix.
+ * Previous override is saved and restored back to ensure
+ * correct work when run_command() is re-entered. */
+ struct target *saved_target_override = context->current_target_override;
+ if (c->jim_handler_data)
+ context->current_target_override = c->jim_handler_data;
+
int retval = c->handler(&cmd);
+
+ if (c->jim_handler_data)
+ context->current_target_override = saved_target_override;
+
if (retval == ERROR_COMMAND_SYNTAX_ERROR) {
/* Print help for command */
char *full_name = command_name(c, ' ');
@@ -643,6 +659,8 @@ int command_run_line(struct command_context *context, char *line)
* happen when the Jim Tcl interpreter is provided by eCos for
* instance.
*/
+ context->current_target_override = NULL;
+
Jim_Interp *interp = context->interp;
Jim_DeleteAssocData(interp, "context");
retcode = Jim_SetAssocData(interp, "context", NULL, context);
@@ -1269,14 +1287,10 @@ static const struct command_registration command_builtin_handlers[] = {
struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp)
{
- struct command_context *context = malloc(sizeof(struct command_context));
+ struct command_context *context = calloc(1, sizeof(struct command_context));
const char *HostOs;
context->mode = COMMAND_EXEC;
- context->commands = NULL;
- context->current_target = 0;
- context->output_handler = NULL;
- context->output_handler_priv = NULL;
/* Create a jim interpreter if we were not handed one */
if (interp == NULL) {
diff --git a/src/helper/command.h b/src/helper/command.h
index f696ab8..f9c02e5 100644
--- a/src/helper/command.h
+++ b/src/helper/command.h
@@ -22,8 +22,12 @@
#ifndef OPENOCD_HELPER_COMMAND_H
#define OPENOCD_HELPER_COMMAND_H
+#include <stdint.h>
+#include <stdbool.h>
#include <jim-nvp.h>
+#include <helper/types.h>
+
/* To achieve C99 printf compatibility in MinGW, gnu_printf should be
* used for __attribute__((format( ... ))), with GCC v4.4 or later
*/
@@ -49,7 +53,15 @@ struct command_context {
Jim_Interp *interp;
enum command_mode mode;
struct command *commands;
- int current_target;
+ struct target *current_target;
+ /* The target set by 'targets xx' command or the latest created */
+ struct target *current_target_override;
+ /* If set overrides current_target
+ * It happens during processing of
+ * 1) a target prefixed command
+ * 2) an event handler
+ * Pay attention to reentrancy when setting override.
+ */
command_output_handler_t output_handler;
void *output_handler_priv;
};
@@ -168,6 +180,11 @@ struct command {
command_handler_t handler;
Jim_CmdProc *jim_handler;
void *jim_handler_data;
+ /* Currently used only for target of target-prefixed cmd.
+ * Native OpenOCD commands use jim_handler_data exclusively
+ * as a target override.
+ * Jim handlers outside of target cmd tree can use
+ * jim_handler_data for any handler specific data */
enum command_mode mode;
struct command *next;
};
diff --git a/src/helper/configuration.c b/src/helper/configuration.c
index 2a27883..114ad2c 100644
--- a/src/helper/configuration.c
+++ b/src/helper/configuration.c
@@ -51,6 +51,21 @@ void add_config_command(const char *cfg)
config_file_names[num_config_files] = NULL;
}
+void free_config(void)
+{
+ while (num_config_files)
+ free(config_file_names[--num_config_files]);
+
+ free(config_file_names);
+ config_file_names = NULL;
+
+ while (num_script_dirs)
+ free(script_search_dirs[--num_script_dirs]);
+
+ free(script_search_dirs);
+ script_search_dirs = NULL;
+}
+
/* return full path or NULL according to search rules */
char *find_file(const char *file)
{
diff --git a/src/helper/configuration.h b/src/helper/configuration.h
index 3cbcd41..cc28efc 100644
--- a/src/helper/configuration.h
+++ b/src/helper/configuration.h
@@ -32,6 +32,8 @@ void add_config_command(const char *cfg);
void add_script_search_dir(const char *dir);
+void free_config(void);
+
int configuration_output_handler(struct command_context *cmd_ctx,
const char *line);
diff --git a/src/helper/fileio.c b/src/helper/fileio.c
index 47494df..9dd57e7 100644
--- a/src/helper/fileio.c
+++ b/src/helper/fileio.c
@@ -153,6 +153,11 @@ int fileio_close(struct fileio *fileio)
return retval;
}
+int fileio_feof(struct fileio *fileio)
+{
+ return feof(fileio->file);
+}
+
int fileio_seek(struct fileio *fileio, size_t position)
{
int retval;
diff --git a/src/helper/fileio.h b/src/helper/fileio.h
index ae4a3ec..02592e2 100644
--- a/src/helper/fileio.h
+++ b/src/helper/fileio.h
@@ -46,6 +46,7 @@ struct fileio;
int fileio_open(struct fileio **fileio, const char *url,
enum fileio_access access_type, enum fileio_type type);
int fileio_close(struct fileio *fileio);
+int fileio_feof(struct fileio *fileio);
int fileio_seek(struct fileio *fileio, size_t position);
int fileio_fgets(struct fileio *fileio, size_t size, void *buffer);
diff --git a/src/helper/log.h b/src/helper/log.h
index 512bcc5..d60587f 100644
--- a/src/helper/log.h
+++ b/src/helper/log.h
@@ -149,6 +149,8 @@ extern int debug_level;
*/
#define ERROR_FAIL (-4)
#define ERROR_WAIT (-5)
+/* ERROR_TIMEOUT is already taken by winerror.h. */
+#define ERROR_TIMEOUT_REACHED (-6)
#endif /* OPENOCD_HELPER_LOG_H */
diff --git a/src/helper/replacements.h b/src/helper/replacements.h
index f43b7e0..ac50095 100644
--- a/src/helper/replacements.h
+++ b/src/helper/replacements.h
@@ -25,6 +25,8 @@
#ifndef OPENOCD_HELPER_REPLACEMENTS_H
#define OPENOCD_HELPER_REPLACEMENTS_H
+#include <stdint.h>
+
/* MIN,MAX macros */
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
diff --git a/src/helper/types.h b/src/helper/types.h
index 58c9e72..5e35c13 100644
--- a/src/helper/types.h
+++ b/src/helper/types.h
@@ -22,7 +22,12 @@
#ifndef OPENOCD_HELPER_TYPES_H
#define OPENOCD_HELPER_TYPES_H
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <stddef.h>
+#include <assert.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
@@ -123,17 +128,17 @@ static inline uint64_t le_to_h_u64(const uint8_t *buf)
static inline uint32_t le_to_h_u32(const uint8_t* buf)
{
- return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
+ return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24);
}
static inline uint32_t le_to_h_u24(const uint8_t* buf)
{
- return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16);
+ return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16);
}
static inline uint16_t le_to_h_u16(const uint8_t* buf)
{
- return (uint16_t)(buf[0] | buf[1] << 8);
+ return (uint16_t)((uint16_t)buf[0] | (uint16_t)buf[1] << 8);
}
static inline uint64_t be_to_h_u64(const uint8_t *buf)
@@ -150,17 +155,17 @@ static inline uint64_t be_to_h_u64(const uint8_t *buf)
static inline uint32_t be_to_h_u32(const uint8_t* buf)
{
- return (uint32_t)(buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24);
+ return (uint32_t)((uint32_t)buf[3] | (uint32_t)buf[2] << 8 | (uint32_t)buf[1] << 16 | (uint32_t)buf[0] << 24);
}
static inline uint32_t be_to_h_u24(const uint8_t* buf)
{
- return (uint32_t)(buf[2] | buf[1] << 8 | buf[0] << 16);
+ return (uint32_t)((uint32_t)buf[2] | (uint32_t)buf[1] << 8 | (uint32_t)buf[0] << 16);
}
static inline uint16_t be_to_h_u16(const uint8_t* buf)
{
- return (uint16_t)(buf[1] | buf[0] << 8);
+ return (uint16_t)((uint16_t)buf[1] | (uint16_t)buf[0] << 8);
}
static inline void h_u64_to_le(uint8_t *buf, int64_t val)
diff --git a/src/jtag/adapter.c b/src/jtag/adapter.c
index 5953de7..2035788 100644
--- a/src/jtag/adapter.c
+++ b/src/jtag/adapter.c
@@ -149,14 +149,14 @@ COMMAND_HANDLER(handle_interface_command)
jtag_interface = jtag_interfaces[i];
- /* LEGACY SUPPORT ... adapter drivers must declare what
- * transports they allow. Until they all do so, assume
- * the legacy drivers are JTAG-only
- */
- if (!jtag_interface->transports)
- LOG_WARNING("Adapter driver '%s' did not declare "
- "which transports it allows; assuming "
- "legacy JTAG-only", jtag_interface->name);
+ /* LEGACY SUPPORT ... adapter drivers must declare what
+ * transports they allow. Until they all do so, assume
+ * the legacy drivers are JTAG-only
+ */
+ if (!jtag_interface->transports)
+ LOG_WARNING("Adapter driver '%s' did not declare "
+ "which transports it allows; assuming "
+ "legacy JTAG-only", jtag_interface->name);
retval = allow_transports(CMD_CTX, jtag_interface->transports
? jtag_interface->transports : jtag_only);
if (ERROR_OK != retval)
diff --git a/src/jtag/aice/aice_interface.c b/src/jtag/aice/aice_interface.c
index 20f1f07..c758bb4 100644
--- a/src/jtag/aice/aice_interface.c
+++ b/src/jtag/aice/aice_interface.c
@@ -239,6 +239,30 @@ static int aice_khz(int khz, int *jtag_speed)
return ERROR_OK;
}
+int aice_scan_jtag_chain(void)
+{
+ LOG_DEBUG("=== %s ===", __func__);
+ uint8_t num_of_idcode = 0;
+ struct target *target;
+
+ int res = aice_port->api->idcode(aice_target_id_codes, &num_of_idcode);
+ if (res != ERROR_OK) {
+ LOG_ERROR("<-- TARGET ERROR! Failed to identify AndesCore "
+ "JTAG Manufacture ID in the JTAG scan chain. "
+ "Failed to access EDM registers. -->");
+ return res;
+ }
+
+ for (uint32_t i = 0; i < num_of_idcode; i++)
+ LOG_DEBUG("id_codes[%d] = 0x%x", i, aice_target_id_codes[i]);
+
+ /* Update tap idcode */
+ for (target = all_targets; target; target = target->next)
+ target->tap->idcode = aice_target_id_codes[target->tap->abs_chain_position];
+
+ return ERROR_OK;
+}
+
/***************************************************************************/
/* Command handlers */
COMMAND_HANDLER(aice_handle_aice_info_command)
diff --git a/src/jtag/aice/aice_interface.h b/src/jtag/aice/aice_interface.h
index 0e3f108..220b0b0 100644
--- a/src/jtag/aice/aice_interface.h
+++ b/src/jtag/aice/aice_interface.h
@@ -31,5 +31,6 @@ struct aice_interface_param_s {
};
int aice_init_targets(void);
+int aice_scan_jtag_chain(void);
#endif /* OPENOCD_JTAG_AICE_AICE_INTERFACE_H */
diff --git a/src/jtag/aice/aice_transport.c b/src/jtag/aice/aice_transport.c
index 9f07946..57c93f2 100644
--- a/src/jtag/aice/aice_transport.c
+++ b/src/jtag/aice/aice_transport.c
@@ -158,6 +158,59 @@ COMMAND_HANDLER(handle_aice_init_command)
return jtag_init(CMD_CTX);
}
+COMMAND_HANDLER(handle_scan_chain_command)
+{
+ struct jtag_tap *tap;
+ char expected_id[12];
+
+ aice_scan_jtag_chain();
+ tap = jtag_all_taps();
+ command_print(CMD_CTX,
+ " TapName Enabled IdCode Expected IrLen IrCap IrMask");
+ command_print(CMD_CTX,
+ "-- ------------------- -------- ---------- ---------- ----- ----- ------");
+
+ while (tap) {
+ uint32_t expected, expected_mask, ii;
+
+ snprintf(expected_id, sizeof expected_id, "0x%08x",
+ (unsigned)((tap->expected_ids_cnt > 0)
+ ? tap->expected_ids[0]
+ : 0));
+ if (tap->ignore_version)
+ expected_id[2] = '*';
+
+ expected = buf_get_u32(tap->expected, 0, tap->ir_length);
+ expected_mask = buf_get_u32(tap->expected_mask, 0, tap->ir_length);
+
+ command_print(CMD_CTX,
+ "%2d %-18s %c 0x%08x %s %5d 0x%02x 0x%02x",
+ tap->abs_chain_position,
+ tap->dotted_name,
+ tap->enabled ? 'Y' : 'n',
+ (unsigned int)(tap->idcode),
+ expected_id,
+ (unsigned int)(tap->ir_length),
+ (unsigned int)(expected),
+ (unsigned int)(expected_mask));
+
+ for (ii = 1; ii < tap->expected_ids_cnt; ii++) {
+ snprintf(expected_id, sizeof expected_id, "0x%08x",
+ (unsigned) tap->expected_ids[ii]);
+ if (tap->ignore_version)
+ expected_id[2] = '*';
+
+ command_print(CMD_CTX,
+ " %s",
+ expected_id);
+ }
+
+ tap = tap->next_tap;
+ }
+
+ return ERROR_OK;
+}
+
static int jim_aice_arp_init(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
LOG_DEBUG("No implement: jim_aice_arp_init");
@@ -307,6 +360,13 @@ aice_transport_jtag_subcommand_handlers[] = {
.jim_handler = jim_aice_names,
.help = "Returns list of all JTAG tap names.",
},
+ {
+ .name = "scan_chain",
+ .handler = handle_scan_chain_command,
+ .mode = COMMAND_ANY,
+ .help = "print current scan chain configuration",
+ .usage = ""
+ },
COMMAND_REGISTRATION_DONE
};
diff --git a/src/jtag/aice/aice_usb.c b/src/jtag/aice/aice_usb.c
index 50468f3..d77b26b 100644
--- a/src/jtag/aice/aice_usb.c
+++ b/src/jtag/aice/aice_usb.c
@@ -2289,37 +2289,39 @@ get_delay:
static int aice_usb_set_clock(int set_clock)
{
- if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL,
- AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK)
- return ERROR_FAIL;
+ if (set_clock & AICE_TCK_CONTROL_TCK_SCAN) {
+ if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL,
+ AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK)
+ return ERROR_FAIL;
- /* Read out TCK_SCAN clock value */
- uint32_t scan_clock;
- if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK)
- return ERROR_FAIL;
+ /* Read out TCK_SCAN clock value */
+ uint32_t scan_clock;
+ if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK)
+ return ERROR_FAIL;
- scan_clock &= 0x0F;
+ scan_clock &= 0x0F;
- uint32_t scan_base_freq;
- if (scan_clock & 0x8)
- scan_base_freq = 48000; /* 48 MHz */
- else
- scan_base_freq = 30000; /* 30 MHz */
+ uint32_t scan_base_freq;
+ if (scan_clock & 0x8)
+ scan_base_freq = 48000; /* 48 MHz */
+ else
+ scan_base_freq = 30000; /* 30 MHz */
- uint32_t set_base_freq;
- if (set_clock & 0x8)
- set_base_freq = 48000;
- else
- set_base_freq = 30000;
+ uint32_t set_base_freq;
+ if (set_clock & 0x8)
+ set_base_freq = 48000;
+ else
+ set_base_freq = 30000;
- uint32_t set_freq;
- uint32_t scan_freq;
- set_freq = set_base_freq >> (set_clock & 0x7);
- scan_freq = scan_base_freq >> (scan_clock & 0x7);
+ uint32_t set_freq;
+ uint32_t scan_freq;
+ set_freq = set_base_freq >> (set_clock & 0x7);
+ scan_freq = scan_base_freq >> (scan_clock & 0x7);
- if (scan_freq < set_freq) {
- LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock");
- return ERROR_FAIL;
+ if (scan_freq < set_freq) {
+ LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock");
+ return ERROR_FAIL;
+ }
}
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL, set_clock) != ERROR_OK)
diff --git a/src/jtag/aice/aice_usb.h b/src/jtag/aice/aice_usb.h
index 2911ae5..15cc1f6 100644
--- a/src/jtag/aice/aice_usb.h
+++ b/src/jtag/aice/aice_usb.h
@@ -71,6 +71,7 @@
/* Constants for AICE command WRITE_CTRL:TCK_CONTROL */
#define AICE_TCK_CONTROL_TCK3048 0x08
+#define AICE_TCK_CONTROL_TCK_SCAN 0x10
/* Constants for AICE command WRITE_CTRL:JTAG_PIN_CONTROL */
#define AICE_JTAG_PIN_CONTROL_SRST 0x01
diff --git a/src/jtag/core.c b/src/jtag/core.c
index 71a731d..f90ae99 100644
--- a/src/jtag/core.c
+++ b/src/jtag/core.c
@@ -836,68 +836,7 @@ int default_interface_jtag_execute_queue(void)
return ERROR_FAIL;
}
- int result = jtag->execute_queue();
-
- struct jtag_command *cmd = jtag_command_queue;
- while (debug_level >= LOG_LVL_DEBUG && cmd) {
- switch (cmd->type) {
- case JTAG_SCAN:
- DEBUG_JTAG_IO("JTAG %s SCAN to %s",
- cmd->cmd.scan->ir_scan ? "IR" : "DR",
- tap_state_name(cmd->cmd.scan->end_state));
- for (int i = 0; i < cmd->cmd.scan->num_fields; i++) {
- struct scan_field *field = cmd->cmd.scan->fields + i;
- if (field->out_value) {
- char *str = buf_to_str(field->out_value, field->num_bits, 16);
- DEBUG_JTAG_IO(" %db out: %s", field->num_bits, str);
- free(str);
- }
- if (field->in_value) {
- char *str = buf_to_str(field->in_value, field->num_bits, 16);
- DEBUG_JTAG_IO(" %db in: %s", field->num_bits, str);
- free(str);
- }
- }
- break;
- case JTAG_TLR_RESET:
- DEBUG_JTAG_IO("JTAG TLR RESET to %s",
- tap_state_name(cmd->cmd.statemove->end_state));
- break;
- case JTAG_RUNTEST:
- DEBUG_JTAG_IO("JTAG RUNTEST %d cycles to %s",
- cmd->cmd.runtest->num_cycles,
- tap_state_name(cmd->cmd.runtest->end_state));
- break;
- case JTAG_RESET:
- {
- const char *reset_str[3] = {
- "leave", "deassert", "assert"
- };
- DEBUG_JTAG_IO("JTAG RESET %s TRST, %s SRST",
- reset_str[cmd->cmd.reset->trst + 1],
- reset_str[cmd->cmd.reset->srst + 1]);
- }
- break;
- case JTAG_PATHMOVE:
- DEBUG_JTAG_IO("JTAG PATHMOVE (TODO)");
- break;
- case JTAG_SLEEP:
- DEBUG_JTAG_IO("JTAG SLEEP (TODO)");
- break;
- case JTAG_STABLECLOCKS:
- DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)");
- break;
- case JTAG_TMS:
- DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)");
- break;
- default:
- LOG_ERROR("Unknown JTAG command: %d", cmd->type);
- break;
- }
- cmd = cmd->next;
- }
-
- return result;
+ return jtag->execute_queue();
}
void jtag_execute_queue_noclear(void)
@@ -1168,8 +1107,7 @@ static int jtag_examine_chain(void)
if ((idcode & 1) == 0) {
/* Zero for LSB indicates a device in bypass */
- LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%x)",
- tap->dotted_name, idcode);
+ LOG_INFO("TAP %s does not have IDCODE", tap->dotted_name);
tap->hasidcode = false;
tap->idcode = 0;
@@ -1370,6 +1308,14 @@ void jtag_tap_free(struct jtag_tap *tap)
{
jtag_unregister_event_callback(&jtag_reset_callback, tap);
+ struct jtag_tap_event_action *jteap = tap->event_action;
+ while (jteap) {
+ struct jtag_tap_event_action *next = jteap->next;
+ Jim_DecrRefCount(jteap->interp, jteap->body);
+ free(jteap);
+ jteap = next;
+ }
+
free(tap->expected);
free(tap->expected_mask);
free(tap->expected_ids);
@@ -1534,13 +1480,19 @@ int jtag_init_inner(struct command_context *cmd_ctx)
int adapter_quit(void)
{
- if (!jtag || !jtag->quit)
- return ERROR_OK;
+ if (jtag && jtag->quit) {
+ /* close the JTAG interface */
+ int result = jtag->quit();
+ if (ERROR_OK != result)
+ LOG_ERROR("failed: %d", result);
+ }
- /* close the JTAG interface */
- int result = jtag->quit();
- if (ERROR_OK != result)
- LOG_ERROR("failed: %d", result);
+ struct jtag_tap *t = jtag_all_taps();
+ while (t) {
+ struct jtag_tap *n = t->next_tap;
+ jtag_tap_free(t);
+ t = n;
+ }
return ERROR_OK;
}
@@ -1891,7 +1843,7 @@ void adapter_deassert_reset(void)
LOG_ERROR("transport is not selected");
}
-int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
+int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq)
{
if (jtag->config_trace)
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 3e5974d..ccef018 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -80,6 +80,9 @@ if USB_BLASTER_DRIVER
%C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la
include %D%/usb_blaster/Makefile.am
endif
+if FT232R
+DRIVERFILES += %D%/ft232r.c
+endif
if AMTJTAGACCEL
DRIVERFILES += %D%/amt_jtagaccel.c
endif
@@ -153,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/buspirate.c b/src/jtag/drivers/buspirate.c
index aa0d8cc..35649c2 100644
--- a/src/jtag/drivers/buspirate.c
+++ b/src/jtag/drivers/buspirate.c
@@ -22,6 +22,7 @@
#endif
#include <jtag/interface.h>
+#include <jtag/swd.h>
#include <jtag/commands.h>
#include <termios.h>
@@ -48,9 +49,21 @@ static void buspirate_stableclocks(int num_cycles);
#define CMD_READ_ADCS 0x03
/*#define CMD_TAP_SHIFT 0x04 // old protocol */
#define CMD_TAP_SHIFT 0x05
+#define CMD_ENTER_RWIRE 0x05
#define CMD_ENTER_OOCD 0x06
#define CMD_UART_SPEED 0x07
#define CMD_JTAG_SPEED 0x08
+#define CMD_RAW_PERIPH 0x40
+#define CMD_RAW_SPEED 0x60
+#define CMD_RAW_MODE 0x80
+
+/* raw-wire mode configuration */
+#define CMD_RAW_CONFIG_HIZ 0x00
+#define CMD_RAW_CONFIG_3V3 0x08
+#define CMD_RAW_CONFIG_2W 0x00
+#define CMD_RAW_CONFIG_3W 0x04
+#define CMD_RAW_CONFIG_MSB 0x00
+#define CMD_RAW_CONFIG_LSB 0x02
/* Not all OSes have this speed defined */
#if !defined(B1000000)
@@ -81,6 +94,18 @@ enum {
SERIAL_FAST = 1
};
+enum {
+ SPEED_RAW_5_KHZ = 0x0,
+ SPEED_RAW_50_KHZ = 0x1,
+ SPEED_RAW_100_KHZ = 0x2,
+ SPEED_RAW_400_KHZ = 0x3
+};
+
+/* SWD mode specific */
+static bool swd_mode;
+static int queued_retval;
+static char swd_features;
+
static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */
static const cc_t NORMAL_TIMEOUT = 10;
@@ -93,6 +118,12 @@ static char *buspirate_port;
static enum tap_state last_tap_state = TAP_RESET;
+/* SWD interface */
+static int buspirate_swd_init(void);
+static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk);
+static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk);
+static int buspirate_swd_switch_seq(enum swd_special_seq seq);
+static int buspirate_swd_run_queue(void);
/* TAP interface */
static void buspirate_tap_init(void);
@@ -103,23 +134,31 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer,
static void buspirate_tap_make_space(int scan, int bits);
static void buspirate_reset(int trst, int srst);
+static void buspirate_set_feature(int, char, char);
+static void buspirate_set_mode(int, char);
+static void buspirate_set_speed(int, char);
/* low level interface */
+static void buspirate_bbio_enable(int);
static void buspirate_jtag_reset(int);
-static void buspirate_jtag_enable(int);
-static unsigned char buspirate_jtag_command(int, char *, int);
+static unsigned char buspirate_jtag_command(int, uint8_t *, int);
static void buspirate_jtag_set_speed(int, char);
static void buspirate_jtag_set_mode(int, char);
static void buspirate_jtag_set_feature(int, char, char);
static void buspirate_jtag_get_adcs(int);
+/* low level two-wire interface */
+static void buspirate_swd_set_speed(int, char);
+static void buspirate_swd_set_feature(int, char, char);
+static void buspirate_swd_set_mode(int, char);
+
/* low level HW communication interface */
static int buspirate_serial_open(char *port);
static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout);
-static int buspirate_serial_write(int fd, char *buf, int size);
-static int buspirate_serial_read(int fd, char *buf, int size);
+static int buspirate_serial_write(int fd, uint8_t *buf, int size);
+static int buspirate_serial_read(int fd, uint8_t *buf, int size);
static void buspirate_serial_close(int fd);
-static void buspirate_print_buffer(char *buf, int size);
+static void buspirate_print_buffer(uint8_t *buf, int size);
static int buspirate_execute_queue(void)
{
@@ -216,7 +255,7 @@ static bool read_and_discard_all_data(const int fd)
bool was_msg_already_printed = false;
for ( ; ; ) {
- char buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */
+ uint8_t buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */
const ssize_t read_count = read(fd, buffer, sizeof(buffer));
@@ -295,18 +334,20 @@ static int buspirate_init(void)
return ERROR_JTAG_INIT_FAILED;
}
- buspirate_jtag_enable(buspirate_fd);
+ buspirate_bbio_enable(buspirate_fd);
- if (buspirate_baudrate != SERIAL_NORMAL)
- buspirate_jtag_set_speed(buspirate_fd, SERIAL_FAST);
+ if (swd_mode || buspirate_baudrate != SERIAL_NORMAL)
+ buspirate_set_speed(buspirate_fd, SERIAL_FAST);
- LOG_INFO("Buspirate Interface ready!");
+ LOG_INFO("Buspirate %s Interface ready!", swd_mode ? "SWD" : "JTAG");
- buspirate_tap_init();
- buspirate_jtag_set_mode(buspirate_fd, buspirate_pinmode);
- buspirate_jtag_set_feature(buspirate_fd, FEATURE_VREG,
+ if (!swd_mode)
+ buspirate_tap_init();
+
+ buspirate_set_mode(buspirate_fd, buspirate_pinmode);
+ buspirate_set_feature(buspirate_fd, FEATURE_VREG,
(buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE);
- buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP,
+ buspirate_set_feature(buspirate_fd, FEATURE_PULLUP,
(buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE);
buspirate_reset(0, 0);
@@ -316,9 +357,9 @@ static int buspirate_init(void)
static int buspirate_quit(void)
{
LOG_INFO("Shutting down buspirate.");
- buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ);
+ buspirate_set_mode(buspirate_fd, MODE_HIZ);
+ buspirate_set_speed(buspirate_fd, SERIAL_NORMAL);
- buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL);
buspirate_jtag_reset(buspirate_fd);
buspirate_serial_close(buspirate_fd);
@@ -336,6 +377,10 @@ COMMAND_HANDLER(buspirate_handle_adc_command)
if (buspirate_fd == -1)
return ERROR_OK;
+ /* unavailable in SWD mode */
+ if (swd_mode)
+ return ERROR_OK;
+
/* send the command */
buspirate_jtag_get_adcs(buspirate_fd);
@@ -382,11 +427,11 @@ COMMAND_HANDLER(buspirate_handle_led_command)
if (atoi(CMD_ARGV[0]) == 1) {
/* enable led */
- buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED,
+ buspirate_set_feature(buspirate_fd, FEATURE_LED,
ACTION_ENABLE);
} else if (atoi(CMD_ARGV[0]) == 0) {
/* disable led */
- buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED,
+ buspirate_set_feature(buspirate_fd, FEATURE_LED,
ACTION_DISABLE);
} else {
LOG_ERROR("usage: buspirate_led <1|0>");
@@ -492,10 +537,22 @@ static const struct command_registration buspirate_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
+static const struct swd_driver buspirate_swd = {
+ .init = buspirate_swd_init,
+ .switch_seq = buspirate_swd_switch_seq,
+ .read_reg = buspirate_swd_read_reg,
+ .write_reg = buspirate_swd_write_reg,
+ .run = buspirate_swd_run_queue,
+};
+
+static const char * const buspirate_transports[] = { "jtag", "swd", NULL };
+
struct jtag_interface buspirate_interface = {
.name = "buspirate",
.execute_queue = buspirate_execute_queue,
.commands = buspirate_command_handlers,
+ .transports = buspirate_transports,
+ .swd = &buspirate_swd,
.init = buspirate_init,
.quit = buspirate_quit
};
@@ -633,8 +690,8 @@ static void buspirate_stableclocks(int num_cycles)
make it incompatible with the Bus Pirate firmware. */
#define BUSPIRATE_MAX_PENDING_SCANS 128
-static char tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
-static char tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
+static uint8_t tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
+static uint8_t tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
static int tap_chain_index;
struct pending_scan_result /* this was stolen from arm-jtag-ew */
@@ -659,7 +716,7 @@ static int buspirate_tap_execute(void)
{
static const int CMD_TAP_SHIFT_HEADER_LEN = 3;
- char tmp[4096];
+ uint8_t tmp[4096];
uint8_t *in_buf;
int i;
int fill_index = 0;
@@ -675,8 +732,8 @@ static int buspirate_tap_execute(void)
bytes_to_send = DIV_ROUND_UP(tap_chain_index, 8);
tmp[0] = CMD_TAP_SHIFT; /* this command expects number of bits */
- tmp[1] = (char)(tap_chain_index >> 8); /* high */
- tmp[2] = (char)(tap_chain_index); /* low */
+ tmp[1] = tap_chain_index >> 8; /* high */
+ tmp[2] = tap_chain_index; /* low */
fill_index = CMD_TAP_SHIFT_HEADER_LEN;
for (i = 0; i < bytes_to_send; i++) {
@@ -799,7 +856,7 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer,
tap_pending_scans_num++;
}
-/*************** jtag wrapper functions *********************/
+/*************** wrapper functions *********************/
/* (1) assert or (0) deassert reset lines */
static void buspirate_reset(int trst, int srst)
@@ -807,33 +864,148 @@ static void buspirate_reset(int trst, int srst)
LOG_DEBUG("trst: %i, srst: %i", trst, srst);
if (trst)
- buspirate_jtag_set_feature(buspirate_fd,
- FEATURE_TRST, ACTION_DISABLE);
+ buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_DISABLE);
else
- buspirate_jtag_set_feature(buspirate_fd,
- FEATURE_TRST, ACTION_ENABLE);
+ buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_ENABLE);
if (srst)
- buspirate_jtag_set_feature(buspirate_fd,
- FEATURE_SRST, ACTION_DISABLE);
+ buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_DISABLE);
+ else
+ buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_ENABLE);
+}
+
+static void buspirate_set_feature(int fd, char feat, char action)
+{
+ if (swd_mode)
+ buspirate_swd_set_feature(fd, feat, action);
else
- buspirate_jtag_set_feature(buspirate_fd,
- FEATURE_SRST, ACTION_ENABLE);
+ buspirate_jtag_set_feature(fd, feat, action);
+}
+
+static void buspirate_set_mode(int fd, char mode)
+{
+ if (swd_mode)
+ buspirate_swd_set_mode(fd, mode);
+ else
+ buspirate_jtag_set_mode(fd, mode);
+}
+
+static void buspirate_set_speed(int fd, char speed)
+{
+ if (swd_mode)
+ buspirate_swd_set_speed(fd, speed);
+ else
+ buspirate_jtag_set_speed(fd, speed);
+}
+
+
+/*************** swd lowlevel functions ********************/
+
+static void buspirate_swd_set_speed(int fd, char speed)
+{
+ int ret;
+ uint8_t tmp[1];
+
+ LOG_DEBUG("Buspirate speed setting in SWD mode defaults to 400 kHz");
+
+ /* speed settings */
+ tmp[0] = CMD_RAW_SPEED | SPEED_RAW_400_KHZ;
+ buspirate_serial_write(fd, tmp, 1);
+ ret = buspirate_serial_read(fd, tmp, 1);
+ if (ret != 1) {
+ LOG_ERROR("Buspirate did not answer correctly");
+ exit(-1);
+ }
+ if (tmp[0] != 1) {
+ LOG_ERROR("Buspirate did not reply as expected to the speed change command");
+ exit(-1);
+ }
+}
+
+static void buspirate_swd_set_mode(int fd, char mode)
+{
+ int ret;
+ uint8_t tmp[1];
+
+ /* raw-wire mode configuration */
+ if (mode == MODE_HIZ)
+ tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB;
+ else
+ tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB | CMD_RAW_CONFIG_3V3;
+
+ buspirate_serial_write(fd, tmp, 1);
+ ret = buspirate_serial_read(fd, tmp, 1);
+ if (ret != 1) {
+ LOG_ERROR("Buspirate did not answer correctly");
+ exit(-1);
+ }
+ if (tmp[0] != 1) {
+ LOG_ERROR("Buspirate did not reply as expected to the configure command");
+ exit(-1);
+ }
+}
+
+static void buspirate_swd_set_feature(int fd, char feat, char action)
+{
+ int ret;
+ uint8_t tmp[1];
+
+ switch (feat) {
+ case FEATURE_TRST:
+ LOG_DEBUG("Buspirate TRST feature not available in SWD mode");
+ return;
+ case FEATURE_LED:
+ LOG_ERROR("Buspirate LED feature not available in SWD mode");
+ return;
+ case FEATURE_SRST:
+ swd_features = (action == ACTION_ENABLE) ? swd_features | 0x02 : swd_features & 0x0D;
+ break;
+ case FEATURE_PULLUP:
+ swd_features = (action == ACTION_ENABLE) ? swd_features | 0x04 : swd_features & 0x0B;
+ break;
+ case FEATURE_VREG:
+ swd_features = (action == ACTION_ENABLE) ? swd_features | 0x08 : swd_features & 0x07;
+ break;
+ default:
+ LOG_DEBUG("Buspirate unknown feature %d", feat);
+ return;
+ }
+
+ tmp[0] = CMD_RAW_PERIPH | swd_features;
+ buspirate_serial_write(fd, tmp, 1);
+ ret = buspirate_serial_read(fd, tmp, 1);
+ if (ret != 1) {
+ LOG_DEBUG("Buspirate feature %d not supported in SWD mode", feat);
+ } else if (tmp[0] != 1) {
+ LOG_ERROR("Buspirate did not reply as expected to the configure command");
+ exit(-1);
+ }
}
/*************** jtag lowlevel functions ********************/
-static void buspirate_jtag_enable(int fd)
+static void buspirate_bbio_enable(int fd)
{
int ret;
- char tmp[21] = { [0 ... 20] = 0x00 };
+ char command;
+ const char *mode_answers[2] = { "OCD1", "RAW1" };
+ const char *correct_ans = NULL;
+ uint8_t tmp[21] = { [0 ... 20] = 0x00 };
int done = 0;
int cmd_sent = 0;
- LOG_DEBUG("Entering binary mode");
+ if (swd_mode) {
+ command = CMD_ENTER_RWIRE;
+ correct_ans = mode_answers[1];
+ } else {
+ command = CMD_ENTER_OOCD;
+ correct_ans = mode_answers[0];
+ }
+
+ LOG_DEBUG("Entering binary mode, that is %s", correct_ans);
buspirate_serial_write(fd, tmp, 20);
usleep(10000);
- /* reads 1 to n "BBIO1"s and one "OCD1" */
+ /* reads 1 to n "BBIO1"s and one "OCD1" or "RAW1" */
while (!done) {
ret = buspirate_serial_read(fd, tmp, 4);
if (ret != 4) {
@@ -841,7 +1013,7 @@ static void buspirate_jtag_enable(int fd)
"/OpenOCD support enabled?");
exit(-1);
}
- if (strncmp(tmp, "BBIO", 4) == 0) {
+ if (strncmp((char *)tmp, "BBIO", 4) == 0) {
ret = buspirate_serial_read(fd, tmp, 1);
if (ret != 1) {
LOG_ERROR("Buspirate did not answer correctly! "
@@ -854,14 +1026,14 @@ static void buspirate_jtag_enable(int fd)
}
if (cmd_sent == 0) {
cmd_sent = 1;
- tmp[0] = CMD_ENTER_OOCD;
+ tmp[0] = command;
ret = buspirate_serial_write(fd, tmp, 1);
if (ret != 1) {
LOG_ERROR("error reading");
exit(-1);
}
}
- } else if (strncmp(tmp, "OCD1", 4) == 0)
+ } else if (strncmp((char *)tmp, correct_ans, 4) == 0)
done = 1;
else {
LOG_ERROR("Buspirate did not answer correctly! "
@@ -874,14 +1046,14 @@ static void buspirate_jtag_enable(int fd)
static void buspirate_jtag_reset(int fd)
{
- char tmp[5];
+ uint8_t tmp[5];
tmp[0] = 0x00; /* exit OCD1 mode */
buspirate_serial_write(fd, tmp, 1);
usleep(10000);
/* We ignore the return value here purposly, nothing we can do */
buspirate_serial_read(fd, tmp, 5);
- if (strncmp(tmp, "BBIO1", 5) == 0) {
+ if (strncmp((char *)tmp, "BBIO1", 5) == 0) {
tmp[0] = 0x0F; /* reset BP */
buspirate_serial_write(fd, tmp, 1);
} else
@@ -891,8 +1063,8 @@ static void buspirate_jtag_reset(int fd)
static void buspirate_jtag_set_speed(int fd, char speed)
{
int ret;
- char tmp[2];
- char ack[2];
+ uint8_t tmp[2];
+ uint8_t ack[2];
ack[0] = 0xAA;
ack[1] = 0x55;
@@ -924,7 +1096,7 @@ static void buspirate_jtag_set_speed(int fd, char speed)
static void buspirate_jtag_set_mode(int fd, char mode)
{
- char tmp[2];
+ uint8_t tmp[2];
tmp[0] = CMD_PORT_MODE;
tmp[1] = mode;
buspirate_jtag_command(fd, tmp, 2);
@@ -932,7 +1104,7 @@ static void buspirate_jtag_set_mode(int fd, char mode)
static void buspirate_jtag_set_feature(int fd, char feat, char action)
{
- char tmp[3];
+ uint8_t tmp[3];
tmp[0] = CMD_FEATURE;
tmp[1] = feat; /* what */
tmp[2] = action; /* action */
@@ -944,7 +1116,7 @@ static void buspirate_jtag_get_adcs(int fd)
uint8_t tmp[10];
uint16_t a, b, c, d;
tmp[0] = CMD_READ_ADCS;
- buspirate_jtag_command(fd, (char *)tmp, 1);
+ buspirate_jtag_command(fd, tmp, 1);
a = tmp[2] << 8 | tmp[3];
b = tmp[4] << 8 | tmp[5];
c = tmp[6] << 8 | tmp[7];
@@ -957,7 +1129,7 @@ static void buspirate_jtag_get_adcs(int fd)
}
static unsigned char buspirate_jtag_command(int fd,
- char *cmd, int cmdlen)
+ uint8_t *cmd, int cmdlen)
{
int res;
int len = 0;
@@ -1048,7 +1220,7 @@ static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout)
return 0;
}
-static int buspirate_serial_write(int fd, char *buf, int size)
+static int buspirate_serial_write(int fd, uint8_t *buf, int size)
{
int ret = 0;
@@ -1063,7 +1235,7 @@ static int buspirate_serial_write(int fd, char *buf, int size)
return ret;
}
-static int buspirate_serial_read(int fd, char *buf, int size)
+static int buspirate_serial_read(int fd, uint8_t *buf, int size)
{
int len = 0;
int ret = 0;
@@ -1102,7 +1274,7 @@ static void buspirate_serial_close(int fd)
#define LINE_SIZE 81
#define BYTES_PER_LINE 16
-static void buspirate_print_buffer(char *buf, int size)
+static void buspirate_print_buffer(uint8_t *buf, int size)
{
char line[LINE_SIZE];
char tmp[10];
@@ -1124,3 +1296,240 @@ static void buspirate_print_buffer(char *buf, int size)
if (line[0] != 0)
LOG_DEBUG("%s", line);
}
+
+/************************* SWD related stuff **********/
+
+static int buspirate_swd_init(void)
+{
+ LOG_INFO("Buspirate SWD mode enabled");
+ swd_mode = true;
+
+ return ERROR_OK;
+}
+
+static int buspirate_swd_switch_seq(enum swd_special_seq seq)
+{
+ const uint8_t *sequence;
+ int sequence_len;
+ uint8_t tmp[64];
+
+ switch (seq) {
+ case LINE_RESET:
+ LOG_DEBUG("SWD line reset");
+ sequence = swd_seq_line_reset;
+ sequence_len = DIV_ROUND_UP(swd_seq_line_reset_len, 8);
+ break;
+ case JTAG_TO_SWD:
+ LOG_DEBUG("JTAG-to-SWD");
+ sequence = swd_seq_jtag_to_swd;
+ sequence_len = DIV_ROUND_UP(swd_seq_jtag_to_swd_len, 8);
+ break;
+ case SWD_TO_JTAG:
+ LOG_DEBUG("SWD-to-JTAG");
+ sequence = swd_seq_swd_to_jtag;
+ sequence_len = DIV_ROUND_UP(swd_seq_swd_to_jtag_len, 8);
+ break;
+ default:
+ LOG_ERROR("Sequence %d not supported", seq);
+ return ERROR_FAIL;
+ }
+
+ /* FIXME: all above sequences fit into one pirate command for now
+ * but it may cause trouble later
+ */
+
+ tmp[0] = 0x10 + ((sequence_len - 1) & 0x0F);
+ memcpy(tmp + 1, sequence, sequence_len);
+
+ buspirate_serial_write(buspirate_fd, tmp, sequence_len + 1);
+ buspirate_serial_read(buspirate_fd, tmp, sequence_len + 1);
+
+ return ERROR_OK;
+}
+
+static uint8_t buspirate_swd_write_header(uint8_t cmd)
+{
+ uint8_t tmp[8];
+ int to_send;
+
+ tmp[0] = 0x10; /* bus pirate: send 1 byte */
+ tmp[1] = cmd; /* swd cmd */
+ tmp[2] = 0x07; /* ack __x */
+ tmp[3] = 0x07; /* ack _x_ */
+ tmp[4] = 0x07; /* ack x__ */
+ tmp[5] = 0x07; /* write mode trn_1 */
+ tmp[6] = 0x07; /* write mode trn_2 */
+
+ to_send = ((cmd & SWD_CMD_RnW) == 0) ? 7 : 5;
+ buspirate_serial_write(buspirate_fd, tmp, to_send);
+
+ /* read ack */
+ buspirate_serial_read(buspirate_fd, tmp, 2); /* drop pirate command ret vals */
+ buspirate_serial_read(buspirate_fd, tmp, to_send - 2); /* ack bits */
+
+ return tmp[2] << 2 | tmp[1] << 1 | tmp[0];
+}
+
+static void buspirate_swd_idle_clocks(uint32_t no_bits)
+{
+ uint32_t no_bytes;
+ uint8_t tmp[20];
+
+ no_bytes = (no_bits + 7) / 8;
+ memset(tmp + 1, 0x00, sizeof(tmp) - 1);
+
+ /* unfortunately bus pirate misbehaves when clocks are sent in parts
+ * so we need to limit at 128 clock cycles
+ */
+ if (no_bytes > 16)
+ no_bytes = 16;
+
+ while (no_bytes) {
+ uint8_t to_send = no_bytes > 16 ? 16 : no_bytes;
+ tmp[0] = 0x10 + ((to_send - 1) & 0x0F);
+
+ buspirate_serial_write(buspirate_fd, tmp, to_send + 1);
+ buspirate_serial_read(buspirate_fd, tmp, to_send + 1);
+
+ no_bytes -= to_send;
+ }
+}
+
+static void buspirate_swd_clear_sticky_errors(void)
+{
+ buspirate_swd_write_reg(swd_cmd(false, false, DP_ABORT),
+ STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
+}
+
+static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
+{
+ uint8_t tmp[16];
+
+ LOG_DEBUG("buspirate_swd_read_reg");
+ assert(cmd & SWD_CMD_RnW);
+
+ if (queued_retval != ERROR_OK) {
+ LOG_DEBUG("Skip buspirate_swd_read_reg because queued_retval=%d", queued_retval);
+ return;
+ }
+
+ cmd |= SWD_CMD_START | SWD_CMD_PARK;
+ uint8_t ack = buspirate_swd_write_header(cmd);
+
+ /* do a read transaction */
+ tmp[0] = 0x06; /* 4 data bytes */
+ tmp[1] = 0x06;
+ tmp[2] = 0x06;
+ tmp[3] = 0x06;
+ tmp[4] = 0x07; /* parity bit */
+ tmp[5] = 0x21; /* 2 turnaround clocks */
+
+ buspirate_serial_write(buspirate_fd, tmp, 6);
+ buspirate_serial_read(buspirate_fd, tmp, 6);
+
+ /* store the data and parity */
+ uint32_t data = (uint8_t) tmp[0];
+ data |= (uint8_t) tmp[1] << 8;
+ data |= (uint8_t) tmp[2] << 16;
+ data |= (uint8_t) tmp[3] << 24;
+ int parity = tmp[4] ? 0x01 : 0x00;
+
+ LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+ ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+ cmd & SWD_CMD_APnDP ? "AP" : "DP",
+ cmd & SWD_CMD_RnW ? "read" : "write",
+ (cmd & SWD_CMD_A32) >> 1,
+ data);
+
+ switch (ack) {
+ case SWD_ACK_OK:
+ if (parity != parity_u32(data)) {
+ LOG_DEBUG("Read data parity mismatch %x %x", parity, parity_u32(data));
+ queued_retval = ERROR_FAIL;
+ return;
+ }
+ if (value)
+ *value = data;
+ if (cmd & SWD_CMD_APnDP)
+ buspirate_swd_idle_clocks(ap_delay_clk);
+ return;
+ case SWD_ACK_WAIT:
+ LOG_DEBUG("SWD_ACK_WAIT");
+ buspirate_swd_clear_sticky_errors();
+ return;
+ case SWD_ACK_FAULT:
+ LOG_DEBUG("SWD_ACK_FAULT");
+ queued_retval = ack;
+ return;
+ default:
+ LOG_DEBUG("No valid acknowledge: ack=%d", ack);
+ queued_retval = ack;
+ return;
+ }
+}
+
+static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
+{
+ uint8_t tmp[16];
+
+ LOG_DEBUG("buspirate_swd_write_reg");
+ assert(!(cmd & SWD_CMD_RnW));
+
+ if (queued_retval != ERROR_OK) {
+ LOG_DEBUG("Skip buspirate_swd_write_reg because queued_retval=%d", queued_retval);
+ return;
+ }
+
+ cmd |= SWD_CMD_START | SWD_CMD_PARK;
+ uint8_t ack = buspirate_swd_write_header(cmd);
+
+ /* do a write transaction */
+ tmp[0] = 0x10 + ((4 + 1 - 1) & 0xF); /* bus pirate: send 4+1 bytes */
+ buf_set_u32((uint8_t *) tmp + 1, 0, 32, value);
+ /* write sequence ends with parity bit and 7 idle ticks */
+ tmp[5] = parity_u32(value) ? 0x01 : 0x00;
+
+ buspirate_serial_write(buspirate_fd, tmp, 6);
+ buspirate_serial_read(buspirate_fd, tmp, 6);
+
+ LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+ ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+ cmd & SWD_CMD_APnDP ? "AP" : "DP",
+ cmd & SWD_CMD_RnW ? "read" : "write",
+ (cmd & SWD_CMD_A32) >> 1,
+ value);
+
+ switch (ack) {
+ case SWD_ACK_OK:
+ if (cmd & SWD_CMD_APnDP)
+ buspirate_swd_idle_clocks(ap_delay_clk);
+ return;
+ case SWD_ACK_WAIT:
+ LOG_DEBUG("SWD_ACK_WAIT");
+ buspirate_swd_clear_sticky_errors();
+ return;
+ case SWD_ACK_FAULT:
+ LOG_DEBUG("SWD_ACK_FAULT");
+ queued_retval = ack;
+ return;
+ default:
+ LOG_DEBUG("No valid acknowledge: ack=%d", ack);
+ queued_retval = ack;
+ return;
+ }
+}
+
+static int buspirate_swd_run_queue(void)
+{
+ LOG_DEBUG("buspirate_swd_run_queue");
+ /* A transaction must be followed by another transaction or at least 8 idle cycles to
+ * ensure that data is clocked through the AP. */
+ buspirate_swd_idle_clocks(8);
+
+ int retval = queued_retval;
+ queued_retval = ERROR_OK;
+ LOG_DEBUG("SWD queue return value: %02x", retval);
+ return retval;
+}
+
+
diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap_usb.c
index b8181d6..4ee4836 100644
--- a/src/jtag/drivers/cmsis_dap_usb.c
+++ b/src/jtag/drivers/cmsis_dap_usb.c
@@ -729,6 +729,20 @@ static void cmsis_dap_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_del
cmsis_dap_swd_queue_cmd(cmd, value, 0);
}
+static int cmsis_dap_get_serial_info(void)
+{
+ uint8_t *data;
+
+ int retval = cmsis_dap_cmd_DAP_Info(INFO_ID_SERNUM, &data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (data[0]) /* strlen */
+ LOG_INFO("CMSIS-DAP: Serial# = %s", &data[1]);
+
+ return ERROR_OK;
+}
+
static int cmsis_dap_get_version_info(void)
{
uint8_t *data;
@@ -802,8 +816,7 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
/* When we are reconnecting, DAP_Connect needs to be rerun, at
* least on Keil ULINK-ME */
- retval = cmsis_dap_cmd_DAP_Connect(seq == LINE_RESET || seq == JTAG_TO_SWD ?
- CONNECT_SWD : CONNECT_JTAG);
+ retval = cmsis_dap_cmd_DAP_Connect(CONNECT_SWD);
if (retval != ERROR_OK)
return retval;
}
@@ -842,17 +855,6 @@ static int cmsis_dap_swd_open(void)
{
int retval;
- if (cmsis_dap_handle == NULL) {
- /* SWD init */
- retval = cmsis_dap_usb_open();
- if (retval != ERROR_OK)
- return retval;
-
- retval = cmsis_dap_get_caps_info();
- if (retval != ERROR_OK)
- return retval;
- }
-
if (!(cmsis_dap_handle->caps & INFO_CAPS_SWD)) {
LOG_ERROR("CMSIS-DAP: SWD not supported");
return ERROR_JTAG_DEVICE_ERROR;
@@ -873,6 +875,22 @@ static int cmsis_dap_init(void)
int retval;
uint8_t *data;
+ retval = cmsis_dap_usb_open();
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = cmsis_dap_get_caps_info();
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = cmsis_dap_get_version_info();
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = cmsis_dap_get_serial_info();
+ if (retval != ERROR_OK)
+ return retval;
+
if (swd_mode) {
retval = cmsis_dap_swd_open();
if (retval != ERROR_OK)
@@ -880,16 +898,6 @@ static int cmsis_dap_init(void)
}
if (cmsis_dap_handle == NULL) {
-
- /* JTAG init */
- retval = cmsis_dap_usb_open();
- if (retval != ERROR_OK)
- return retval;
-
- retval = cmsis_dap_get_caps_info();
- if (retval != ERROR_OK)
- return retval;
-
/* Connect in JTAG mode */
if (!(cmsis_dap_handle->caps & INFO_CAPS_JTAG)) {
LOG_ERROR("CMSIS-DAP: JTAG not supported");
@@ -903,10 +911,6 @@ static int cmsis_dap_init(void)
LOG_INFO("CMSIS-DAP: Interface Initialised (JTAG)");
}
- retval = cmsis_dap_get_version_info();
- if (retval != ERROR_OK)
- return retval;
-
/* INFO_ID_PKT_SZ - short */
retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data);
if (retval != ERROR_OK)
@@ -1458,6 +1462,12 @@ static void cmsis_dap_execute_stableclocks(struct jtag_command *cmd)
cmsis_dap_stableclocks(cmd->cmd.runtest->num_cycles);
}
+static void cmsis_dap_execute_tms(struct jtag_command *cmd)
+{
+ DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
+ cmsis_dap_cmd_DAP_SWJ_Sequence(cmd->cmd.tms->num_bits, cmd->cmd.tms->bits);
+}
+
/* TODO: Is there need to call cmsis_dap_flush() for the JTAG_PATHMOVE,
* JTAG_RUNTEST, JTAG_STABLECLOCKS? */
static void cmsis_dap_execute_command(struct jtag_command *cmd)
@@ -1488,6 +1498,8 @@ static void cmsis_dap_execute_command(struct jtag_command *cmd)
cmsis_dap_execute_stableclocks(cmd);
break;
case JTAG_TMS:
+ cmsis_dap_execute_tms(cmd);
+ break;
default:
LOG_ERROR("BUG: unknown JTAG command type 0x%X encountered", cmd->type);
exit(-1);
@@ -1650,6 +1662,7 @@ static const char * const cmsis_dap_transport[] = { "swd", "jtag", NULL };
struct jtag_interface cmsis_dap_interface = {
.name = "cmsis-dap",
+ .supported = DEBUG_CAP_TMS_SEQ,
.commands = cmsis_dap_command_handlers,
.swd = &cmsis_dap_swd_driver,
.transports = cmsis_dap_transport,
diff --git a/src/jtag/drivers/ft232r.c b/src/jtag/drivers/ft232r.c
new file mode 100644
index 0000000..fc3d75f
--- /dev/null
+++ b/src/jtag/drivers/ft232r.c
@@ -0,0 +1,669 @@
+/***************************************************************************
+ * Copyright (C) 2010 Serge Vakulenko *
+ * serge@vak.ru *
+ * *
+ * 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
+
+#if IS_CYGWIN == 1
+#include "windows.h"
+#undef LOG_ERROR
+#endif
+
+/* project specific includes */
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <helper/time_support.h>
+#include "libusb1_common.h"
+
+/* system includes */
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+
+/*
+ * Bit 7 (0x80, pin 6, RI ): unused.
+ * Bit 6 (0x40, pin 10,DCD): /SYSRST output.
+ * Bit 5 (0x20, pin 9, DSR): unused.
+ * Bit 4 (0x10, pin 2, DTR): /TRST output.
+ * Bit 3 (0x08, pin 11,CTS): TMS output.
+ * Bit 2 (0x04, pin 3, RTS): TDO input.
+ * Bit 1 (0x02, pin 5, RXD): TDI output.
+ * Bit 0 (0x01, pin 1, TXD): TCK output.
+ *
+ * Sync bit bang mode is implemented as described in FTDI Application
+ * Note AN232R-01: "Bit Bang Modes for the FT232R and FT245R".
+ */
+#define TCK (1 << 0)
+#define TDI (1 << 1)
+#define READ_TDO (1 << 2)
+#define TMS (1 << 3)
+#define NTRST (1 << 4)
+#define NSYSRST (1 << 6)
+
+/*
+ * USB endpoints.
+ */
+#define IN_EP 0x02
+#define OUT_EP 0x81
+
+/* Requests */
+#define SIO_RESET 0 /* Reset the port */
+#define SIO_MODEM_CTRL 1 /* Set the modem control register */
+#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */
+#define SIO_SET_BAUD_RATE 3 /* Set baud rate */
+#define SIO_SET_DATA 4 /* Set the data characteristics of the port */
+#define SIO_POLL_MODEM_STATUS 5
+#define SIO_SET_EVENT_CHAR 6
+#define SIO_SET_ERROR_CHAR 7
+#define SIO_SET_LATENCY_TIMER 9
+#define SIO_GET_LATENCY_TIMER 10
+#define SIO_SET_BITMODE 11
+#define SIO_READ_PINS 12
+#define SIO_READ_EEPROM 0x90
+#define SIO_WRITE_EEPROM 0x91
+#define SIO_ERASE_EEPROM 0x92
+
+#define FT232R_BUF_SIZE 4000
+
+static char *ft232r_serial_desc;
+static uint16_t ft232r_vid = 0x0403; /* FTDI */
+static uint16_t ft232r_pid = 0x6001; /* FT232R */
+static jtag_libusb_device_handle *adapter;
+
+static uint8_t *ft232r_output;
+static size_t ft232r_output_len;
+
+/**
+ * Perform sync bitbang output/input transaction.
+ * Before call, an array ft232r_output[] should be filled with data to send.
+ * Counter ft232r_output_len contains the number of bytes to send.
+ * On return, received data is put back to array ft232r_output[].
+ */
+static int ft232r_send_recv(void)
+{
+ /* FIFO TX buffer has 128 bytes.
+ * FIFO RX buffer has 256 bytes.
+ * First two bytes of received packet contain contain modem
+ * and line status and are ignored.
+ * Unfortunately, transfer sizes bigger than 64 bytes
+ * frequently cause hang ups. */
+ assert(ft232r_output_len > 0);
+
+ size_t total_written = 0;
+ size_t total_read = 0;
+ int rxfifo_free = 128;
+
+ while (total_read < ft232r_output_len) {
+ /* Write */
+ int bytes_to_write = ft232r_output_len - total_written;
+ if (bytes_to_write > 64)
+ bytes_to_write = 64;
+ if (bytes_to_write > rxfifo_free)
+ bytes_to_write = rxfifo_free;
+
+ if (bytes_to_write) {
+ int n = jtag_libusb_bulk_write(adapter, IN_EP,
+ (char *) ft232r_output + total_written,
+ bytes_to_write, 1000);
+
+ if (n == 0) {
+ LOG_ERROR("usb bulk write failed");
+ return ERROR_JTAG_DEVICE_ERROR;
+ }
+
+ total_written += n;
+ rxfifo_free -= n;
+ }
+
+ /* Read */
+ uint8_t reply[64];
+
+ int n = jtag_libusb_bulk_read(adapter, OUT_EP,
+ (char *) reply,
+ sizeof(reply), 1000);
+
+ if (n == 0) {
+ LOG_ERROR("usb bulk read failed");
+ return ERROR_JTAG_DEVICE_ERROR;
+ }
+ if (n > 2) {
+ /* Copy data, ignoring first 2 bytes. */
+ memcpy(ft232r_output + total_read, reply + 2, n - 2);
+ int bytes_read = n - 2;
+ total_read += bytes_read;
+ rxfifo_free += bytes_read;
+ if (total_read > total_written) {
+ LOG_ERROR("read more bytes than wrote");
+ return ERROR_JTAG_DEVICE_ERROR;
+ }
+ }
+ }
+ ft232r_output_len = 0;
+ return ERROR_OK;
+}
+
+/**
+ * Add one TCK/TMS/TDI sample to send buffer.
+ */
+static void ft232r_write(int tck, int tms, int tdi)
+{
+ unsigned out_value = NTRST | NSYSRST;
+ if (tck)
+ out_value |= TCK;
+ if (tms)
+ out_value |= TMS;
+ if (tdi)
+ out_value |= TDI;
+
+ if (ft232r_output_len >= FT232R_BUF_SIZE) {
+ /* FIXME: should we just execute queue here? */
+ LOG_ERROR("ft232r_write: buffer overflow");
+ return;
+ }
+ ft232r_output[ft232r_output_len++] = out_value;
+}
+
+/**
+ * Control /TRST and /SYSRST pins.
+ * Perform immediate bitbang transaction.
+ */
+static void ft232r_reset(int trst, int srst)
+{
+ unsigned out_value = NTRST | NSYSRST;
+ LOG_DEBUG("ft232r_reset(%d,%d)", trst, srst);
+
+ if (trst == 1)
+ out_value &= ~NTRST; /* switch /TRST low */
+ else if (trst == 0)
+ out_value |= NTRST; /* switch /TRST high */
+
+ if (srst == 1)
+ out_value &= ~NSYSRST; /* switch /SYSRST low */
+ else if (srst == 0)
+ out_value |= NSYSRST; /* switch /SYSRST high */
+
+ if (ft232r_output_len >= FT232R_BUF_SIZE) {
+ /* FIXME: should we just execute queue here? */
+ LOG_ERROR("ft232r_write: buffer overflow");
+ return;
+ }
+
+ ft232r_output[ft232r_output_len++] = out_value;
+ ft232r_send_recv();
+}
+
+static int ft232r_speed(int divisor)
+{
+ int baud = (divisor == 0) ? 3000000 :
+ (divisor == 1) ? 2000000 :
+ 3000000 / divisor;
+ LOG_DEBUG("ft232r_speed(%d) rate %d bits/sec", divisor, baud);
+
+ if (jtag_libusb_control_transfer(adapter,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ SIO_SET_BAUD_RATE, divisor, 0, 0, 0, 1000) != 0) {
+ LOG_ERROR("cannot set baud rate");
+ return ERROR_JTAG_DEVICE_ERROR;
+ }
+ return ERROR_OK;
+}
+
+static int ft232r_init(void)
+{
+ uint16_t avids[] = {ft232r_vid, 0};
+ uint16_t apids[] = {ft232r_pid, 0};
+ if (jtag_libusb_open(avids, apids, ft232r_serial_desc, &adapter)) {
+ LOG_ERROR("ft232r not found: vid=%04x, pid=%04x, serial=%s\n",
+ ft232r_vid, ft232r_pid, (ft232r_serial_desc == NULL) ? "[any]" : ft232r_serial_desc);
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ libusb_detach_kernel_driver(adapter, 0);
+
+ if (jtag_libusb_claim_interface(adapter, 0)) {
+ LOG_ERROR("unable to claim interface");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ /* Reset the device. */
+ if (jtag_libusb_control_transfer(adapter,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ SIO_RESET, 0, 0, 0, 0, 1000) != 0) {
+ LOG_ERROR("unable to reset device");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ /* Sync bit bang mode. */
+ if (jtag_libusb_control_transfer(adapter,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ SIO_SET_BITMODE, TCK | TDI | TMS | NTRST | NSYSRST | 0x400,
+ 0, 0, 0, 1000) != 0) {
+ LOG_ERROR("cannot set sync bitbang mode");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ /* Exactly 500 nsec between updates. */
+ unsigned divisor = 1;
+ unsigned char latency_timer = 1;
+
+ /* Frequency divisor is 14-bit non-zero value. */
+ if (jtag_libusb_control_transfer(adapter,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ SIO_SET_BAUD_RATE, divisor,
+ 0, 0, 0, 1000) != 0) {
+ LOG_ERROR("cannot set baud rate");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+ if (jtag_libusb_control_transfer(adapter,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ SIO_SET_LATENCY_TIMER, latency_timer, 0, 0, 0, 1000) != 0) {
+ LOG_ERROR("unable to set latency timer");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ ft232r_output = malloc(FT232R_BUF_SIZE);
+ if (ft232r_output == NULL) {
+ LOG_ERROR("Unable to allocate memory for the buffer");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+static int ft232r_quit(void)
+{
+ if (jtag_libusb_release_interface(adapter, 0) != 0)
+ LOG_ERROR("usb release interface failed");
+
+ jtag_libusb_close(adapter);
+ free(ft232r_output);
+
+ return ERROR_OK;
+}
+
+static int ft232r_speed_div(int divisor, int *khz)
+{
+ /* Maximum 3 Mbaud for bit bang mode. */
+ if (divisor == 0)
+ *khz = 3000;
+ else if (divisor == 1)
+ *khz = 2000;
+ else
+ *khz = 3000 / divisor;
+ return ERROR_OK;
+}
+
+static int ft232r_khz(int khz, int *divisor)
+{
+ if (khz == 0) {
+ LOG_DEBUG("RCLK not supported");
+ return ERROR_FAIL;
+ }
+
+ /* Calculate frequency divisor. */
+ if (khz > 2500)
+ *divisor = 0; /* Special case: 3 MHz */
+ else if (khz > 1700)
+ *divisor = 1; /* Special case: 2 MHz */
+ else {
+ *divisor = (2*3000 / khz + 1) / 2;
+ if (*divisor > 0x3FFF)
+ *divisor = 0x3FFF;
+ }
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(ft232r_handle_serial_desc_command)
+{
+ if (CMD_ARGC == 1)
+ ft232r_serial_desc = strdup(CMD_ARGV[0]);
+ else
+ LOG_ERROR("require exactly one argument to "
+ "ft232r_serial_desc <serial>");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(ft232r_handle_vid_pid_command)
+{
+ if (CMD_ARGC > 2) {
+ LOG_WARNING("ignoring extra IDs in ft232r_vid_pid "
+ "(maximum is 1 pair)");
+ CMD_ARGC = 2;
+ }
+ if (CMD_ARGC == 2) {
+ COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], ft232r_vid);
+ COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], ft232r_pid);
+ } else
+ LOG_WARNING("incomplete ft232r_vid_pid configuration");
+
+ return ERROR_OK;
+}
+
+static const struct command_registration ft232r_command_handlers[] = {
+ {
+ .name = "ft232r_serial_desc",
+ .handler = ft232r_handle_serial_desc_command,
+ .mode = COMMAND_CONFIG,
+ .help = "USB serial descriptor of the adapter",
+ .usage = "serial string",
+ },
+ {
+ .name = "ft232r_vid_pid",
+ .handler = ft232r_handle_vid_pid_command,
+ .mode = COMMAND_CONFIG,
+ .help = "USB VID and PID of the adapter",
+ .usage = "vid pid",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+/*
+ * Synchronous bitbang protocol implementation.
+ */
+
+static void syncbb_end_state(tap_state_t state)
+{
+ if (tap_is_state_stable(state))
+ tap_set_end_state(state);
+ else {
+ LOG_ERROR("BUG: %i is not a valid end state", state);
+ exit(-1);
+ }
+}
+
+static void syncbb_state_move(int skip)
+{
+ int i = 0, tms = 0;
+ uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state());
+ int tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state());
+
+ for (i = skip; i < tms_count; i++) {
+ tms = (tms_scan >> i) & 1;
+ ft232r_write(0, tms, 0);
+ ft232r_write(1, tms, 0);
+ }
+ ft232r_write(0, tms, 0);
+
+ tap_set_state(tap_get_end_state());
+}
+
+/**
+ * Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG
+ * (or SWD) state machine.
+ */
+static int syncbb_execute_tms(struct jtag_command *cmd)
+{
+ unsigned num_bits = cmd->cmd.tms->num_bits;
+ const uint8_t *bits = cmd->cmd.tms->bits;
+
+ DEBUG_JTAG_IO("TMS: %d bits", num_bits);
+
+ int tms = 0;
+ for (unsigned i = 0; i < num_bits; i++) {
+ tms = ((bits[i/8] >> (i % 8)) & 1);
+ ft232r_write(0, tms, 0);
+ ft232r_write(1, tms, 0);
+ }
+ ft232r_write(0, tms, 0);
+
+ return ERROR_OK;
+}
+
+static void syncbb_path_move(struct pathmove_command *cmd)
+{
+ int num_states = cmd->num_states;
+ int state_count;
+ int tms = 0;
+
+ state_count = 0;
+ while (num_states) {
+ if (tap_state_transition(tap_get_state(), false) == cmd->path[state_count]) {
+ tms = 0;
+ } else if (tap_state_transition(tap_get_state(), true) == cmd->path[state_count]) {
+ tms = 1;
+ } else {
+ LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition",
+ tap_state_name(tap_get_state()),
+ tap_state_name(cmd->path[state_count]));
+ exit(-1);
+ }
+
+ ft232r_write(0, tms, 0);
+ ft232r_write(1, tms, 0);
+
+ tap_set_state(cmd->path[state_count]);
+ state_count++;
+ num_states--;
+ }
+
+ ft232r_write(0, tms, 0);
+
+ tap_set_end_state(tap_get_state());
+}
+
+static void syncbb_runtest(int num_cycles)
+{
+ int i;
+
+ tap_state_t saved_end_state = tap_get_end_state();
+
+ /* only do a state_move when we're not already in IDLE */
+ if (tap_get_state() != TAP_IDLE) {
+ syncbb_end_state(TAP_IDLE);
+ syncbb_state_move(0);
+ }
+
+ /* execute num_cycles */
+ for (i = 0; i < num_cycles; i++) {
+ ft232r_write(0, 0, 0);
+ ft232r_write(1, 0, 0);
+ }
+ ft232r_write(0, 0, 0);
+
+ /* finish in end_state */
+ syncbb_end_state(saved_end_state);
+ if (tap_get_state() != tap_get_end_state())
+ syncbb_state_move(0);
+}
+
+/**
+ * Function syncbb_stableclocks
+ * issues a number of clock cycles while staying in a stable state.
+ * Because the TMS value required to stay in the RESET state is a 1, whereas
+ * the TMS value required to stay in any of the other stable states is a 0,
+ * this function checks the current stable state to decide on the value of TMS
+ * to use.
+ */
+static void syncbb_stableclocks(int num_cycles)
+{
+ int tms = (tap_get_state() == TAP_RESET ? 1 : 0);
+ int i;
+
+ /* send num_cycles clocks onto the cable */
+ for (i = 0; i < num_cycles; i++) {
+ ft232r_write(1, tms, 0);
+ ft232r_write(0, tms, 0);
+ }
+}
+
+static void syncbb_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size)
+{
+ tap_state_t saved_end_state = tap_get_end_state();
+ int bit_cnt, bit0_index;
+
+ if (!((!ir_scan && (tap_get_state() == TAP_DRSHIFT)) || (ir_scan && (tap_get_state() == TAP_IRSHIFT)))) {
+ if (ir_scan)
+ syncbb_end_state(TAP_IRSHIFT);
+ else
+ syncbb_end_state(TAP_DRSHIFT);
+
+ syncbb_state_move(0);
+ syncbb_end_state(saved_end_state);
+ }
+
+ bit0_index = ft232r_output_len;
+ for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) {
+ int tms = (bit_cnt == scan_size-1) ? 1 : 0;
+ int tdi;
+ int bytec = bit_cnt/8;
+ int bcval = 1 << (bit_cnt % 8);
+
+ /* if we're just reading the scan, but don't care about the output
+ * default to outputting 'low', this also makes valgrind traces more readable,
+ * as it removes the dependency on an uninitialised value
+ */
+ tdi = 0;
+ if ((type != SCAN_IN) && (buffer[bytec] & bcval))
+ tdi = 1;
+
+ ft232r_write(0, tms, tdi);
+ ft232r_write(1, tms, tdi);
+ }
+
+ if (tap_get_state() != tap_get_end_state()) {
+ /* we *KNOW* the above loop transitioned out of
+ * the shift state, so we skip the first state
+ * and move directly to the end state.
+ */
+ syncbb_state_move(1);
+ }
+ ft232r_send_recv();
+
+ if (type != SCAN_OUT)
+ for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) {
+ int bytec = bit_cnt/8;
+ int bcval = 1 << (bit_cnt % 8);
+ int val = ft232r_output[bit0_index + bit_cnt*2 + 1];
+
+ if (val & READ_TDO)
+ buffer[bytec] |= bcval;
+ else
+ buffer[bytec] &= ~bcval;
+ }
+}
+
+static int syncbb_execute_queue(void)
+{
+ struct jtag_command *cmd = jtag_command_queue; /* currently processed command */
+ int scan_size;
+ enum scan_type type;
+ uint8_t *buffer;
+ int retval;
+
+ /* return ERROR_OK, unless a jtag_read_buffer returns a failed check
+ * that wasn't handled by a caller-provided error handler
+ */
+ retval = ERROR_OK;
+
+/* ft232r_blink(1);*/
+
+ while (cmd) {
+ switch (cmd->type) {
+ case JTAG_RESET:
+ LOG_DEBUG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst);
+
+ if ((cmd->cmd.reset->trst == 1) ||
+ (cmd->cmd.reset->srst &&
+ (jtag_get_reset_config() & RESET_SRST_PULLS_TRST))) {
+ tap_set_state(TAP_RESET);
+ }
+ ft232r_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst);
+ break;
+
+ case JTAG_RUNTEST:
+ LOG_DEBUG_IO("runtest %i cycles, end in %s", cmd->cmd.runtest->num_cycles,
+ tap_state_name(cmd->cmd.runtest->end_state));
+
+ syncbb_end_state(cmd->cmd.runtest->end_state);
+ syncbb_runtest(cmd->cmd.runtest->num_cycles);
+ break;
+
+ case JTAG_STABLECLOCKS:
+ /* this is only allowed while in a stable state. A check for a stable
+ * state was done in jtag_add_clocks()
+ */
+ syncbb_stableclocks(cmd->cmd.stableclocks->num_cycles);
+ break;
+
+ case JTAG_TLR_RESET: /* renamed from JTAG_STATEMOVE */
+ LOG_DEBUG_IO("statemove end in %s", tap_state_name(cmd->cmd.statemove->end_state));
+
+ syncbb_end_state(cmd->cmd.statemove->end_state);
+ syncbb_state_move(0);
+ break;
+
+ case JTAG_PATHMOVE:
+ LOG_DEBUG_IO("pathmove: %i states, end in %s", cmd->cmd.pathmove->num_states,
+ tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]));
+
+ syncbb_path_move(cmd->cmd.pathmove);
+ break;
+
+ case JTAG_SCAN:
+ LOG_DEBUG_IO("%s scan end in %s", (cmd->cmd.scan->ir_scan) ? "IR" : "DR",
+ tap_state_name(cmd->cmd.scan->end_state));
+
+ syncbb_end_state(cmd->cmd.scan->end_state);
+ scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer);
+ type = jtag_scan_type(cmd->cmd.scan);
+ syncbb_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size);
+ if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK)
+ retval = ERROR_JTAG_QUEUE_FAILED;
+ if (buffer)
+ free(buffer);
+ break;
+
+ case JTAG_SLEEP:
+ LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us);
+
+ jtag_sleep(cmd->cmd.sleep->us);
+ break;
+
+ case JTAG_TMS:
+ retval = syncbb_execute_tms(cmd);
+ break;
+ default:
+ LOG_ERROR("BUG: unknown JTAG command type encountered");
+ exit(-1);
+ }
+ if (ft232r_output_len > 0)
+ ft232r_send_recv();
+ cmd = cmd->next;
+ }
+/* ft232r_blink(0);*/
+
+ return retval;
+}
+
+struct jtag_interface ft232r_interface = {
+ .name = "ft232r",
+ .commands = ft232r_command_handlers,
+ .transports = jtag_only,
+ .supported = DEBUG_CAP_TMS_SEQ,
+
+ .execute_queue = syncbb_execute_queue,
+
+ .speed = ft232r_speed,
+ .init = ft232r_init,
+ .quit = ft232r_quit,
+ .speed_div = ft232r_speed_div,
+ .khz = ft232r_khz,
+};
diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c
index e69707e..7c6709c 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -694,6 +694,18 @@ static int ftdi_quit(void)
{
mpsse_close(mpsse_ctx);
+ struct signal *sig = signals;
+ while (sig) {
+ struct signal *next = sig->next;
+ free((void *)sig->name);
+ free(sig);
+ sig = next;
+ }
+
+ free(ftdi_device_desc);
+ free(ftdi_serial);
+ free(ftdi_location);
+
free(swd_cmd_queue);
return ERROR_OK;
diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c
index 132ef06..e74965e 100644
--- a/src/jtag/drivers/jlink.c
+++ b/src/jtag/drivers/jlink.c
@@ -1263,7 +1263,7 @@ static bool check_trace_freq(struct jaylink_swo_speed speed,
return false;
}
-static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
+static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq)
{
int ret;
@@ -1275,7 +1275,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
return ERROR_FAIL;
}
- if (pin_protocol != ASYNC_UART) {
+ if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) {
LOG_ERROR("Selected pin protocol is not supported.");
return ERROR_FAIL;
}
diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c
index 1b0fcba..1a42b3a 100644
--- a/src/jtag/drivers/jtag_vpi.c
+++ b/src/jtag/drivers/jtag_vpi.c
@@ -204,23 +204,20 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift)
static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift)
{
int nb_xfer = DIV_ROUND_UP(nb_bits, XFERT_MAX_SIZE * 8);
- uint8_t *xmit_buffer = bits;
- int xmit_nb_bits = nb_bits;
- int i = 0;
int retval;
while (nb_xfer) {
-
if (nb_xfer == 1) {
- retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], xmit_nb_bits, tap_shift);
+ retval = jtag_vpi_queue_tdi_xfer(bits, nb_bits, tap_shift);
if (retval != ERROR_OK)
return retval;
} else {
- retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
+ retval = jtag_vpi_queue_tdi_xfer(bits, XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
if (retval != ERROR_OK)
return retval;
- xmit_nb_bits -= XFERT_MAX_SIZE * 8;
- i += XFERT_MAX_SIZE;
+ nb_bits -= XFERT_MAX_SIZE * 8;
+ if (bits)
+ bits += XFERT_MAX_SIZE;
}
nb_xfer--;
@@ -318,7 +315,7 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state)
if (retval != ERROR_OK)
return retval;
- retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT);
+ retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT);
if (retval != ERROR_OK)
return retval;
diff --git a/src/jtag/drivers/kitprog.c b/src/jtag/drivers/kitprog.c
index db5b62e..e3ad84d 100644
--- a/src/jtag/drivers/kitprog.c
+++ b/src/jtag/drivers/kitprog.c
@@ -741,12 +741,22 @@ static int kitprog_swd_run_queue(void)
break;
}
- /* We use the maximum buffer size here because the KitProg sometimes
- * doesn't like bulk reads of fewer than 62 bytes. (?!?!)
+ /* KitProg firmware does not send a zero length packet
+ * after the bulk-in transmission of a length divisible by bulk packet
+ * size (64 bytes) as required by the USB specification.
+ * Therefore libusb would wait for continuation of transmission.
+ * Workaround: Limit bulk read size to expected number of bytes
+ * for problematic tranfer sizes. Otherwise use the maximum buffer
+ * size here because the KitProg sometimes doesn't like bulk reads
+ * of fewer than 62 bytes. (?!?!)
*/
+ size_t read_count_workaround = SWD_MAX_BUFFER_LENGTH;
+ if (read_count % 64 == 0)
+ read_count_workaround = read_count;
+
ret = jtag_libusb_bulk_read(kitprog_handle->usb_handle,
BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)buffer,
- SWD_MAX_BUFFER_LENGTH, 0);
+ read_count_workaround, 1000);
if (ret > 0) {
/* Handle garbage data by offsetting the initial read index */
if ((unsigned int)ret > read_count)
@@ -878,13 +888,11 @@ COMMAND_HANDLER(kitprog_handle_acquire_psoc_command)
COMMAND_HANDLER(kitprog_handle_serial_command)
{
if (CMD_ARGC == 1) {
- size_t len = strlen(CMD_ARGV[0]);
- kitprog_serial = calloc(len + 1, sizeof(char));
+ kitprog_serial = strdup(CMD_ARGV[0]);
if (kitprog_serial == NULL) {
LOG_ERROR("Failed to allocate memory for the serial number");
return ERROR_FAIL;
}
- strncpy(kitprog_serial, CMD_ARGV[0], len + 1);
} else {
LOG_ERROR("expected exactly one argument to kitprog_serial <serial-number>");
return ERROR_FAIL;
diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c
index 924c974..06d008b 100644
--- a/src/jtag/drivers/mpsse.c
+++ b/src/jtag/drivers/mpsse.c
@@ -335,7 +335,13 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
ctx->write_size = 16384;
ctx->read_chunk = malloc(ctx->read_chunk_size);
ctx->read_buffer = malloc(ctx->read_size);
- ctx->write_buffer = malloc(ctx->write_size);
+
+ /* Use calloc to make valgrind happy: buffer_write() sets payload
+ * on bit basis, so some bits can be left uninitialized in write_buffer.
+ * Although this is perfectly ok with MPSSE, valgrind reports
+ * Syscall param ioctl(USBDEVFS_SUBMITURB).buffer points to uninitialised byte(s) */
+ ctx->write_buffer = calloc(1, ctx->write_size);
+
if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer)
goto error;
diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c
index 6f720b8..d9ca53e 100644
--- a/src/jtag/drivers/stlink_usb.c
+++ b/src/jtag/drivers/stlink_usb.c
@@ -457,8 +457,8 @@ static int stlink_usb_error_check(void *handle)
LOG_DEBUG("Write error");
return ERROR_FAIL;
case STLINK_JTAG_WRITE_VERIF_ERROR:
- LOG_DEBUG("Verify error");
- return ERROR_FAIL;
+ LOG_DEBUG("Write verify error, ignoring");
+ return ERROR_OK;
case STLINK_SWD_AP_FAULT:
/* git://git.ac6.fr/openocd commit 657e3e885b9ee10
* returns ERROR_OK with the comment:
@@ -2194,12 +2194,13 @@ error_open:
return ERROR_FAIL;
}
-int stlink_config_trace(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
+int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq)
{
struct stlink_usb_handle_s *h = handle;
- if (enabled && (h->jtag_api < 2 || pin_protocol != ASYNC_UART)) {
+ if (enabled && (h->jtag_api < 2 ||
+ pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) {
LOG_ERROR("The attached ST-LINK version doesn't support this trace mode");
return ERROR_FAIL;
}
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/usb_blaster/usb_blaster.c b/src/jtag/drivers/usb_blaster/usb_blaster.c
index df9f2a1..8ccf871 100644
--- a/src/jtag/drivers/usb_blaster/usb_blaster.c
+++ b/src/jtag/drivers/usb_blaster/usb_blaster.c
@@ -445,6 +445,7 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
* ublast_tms_seq - write a TMS sequence transition to JTAG
* @bits: TMS bits to be written (bit0, bit1 .. bitN)
* @nb_bits: number of TMS bits (between 1 and 8)
+ * @skip: number of TMS bits to skip at the beginning of the series
*
* Write a serie of TMS transitions, where each transition consists in :
* - writing out TCK=0, TMS=<new_state>, TDI=<???>
@@ -452,12 +453,12 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
* The function ensures that at the end of the sequence, the clock (TCK) is put
* low.
*/
-static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
+static void ublast_tms_seq(const uint8_t *bits, int nb_bits, int skip)
{
int i;
DEBUG_JTAG_IO("(bits=%02x..., nb_bits=%d)", bits[0], nb_bits);
- for (i = 0; i < nb_bits; i++)
+ for (i = skip; i < nb_bits; i++)
ublast_clock_tms((bits[i / 8] >> (i % 8)) & 0x01);
ublast_idle_clock();
}
@@ -469,7 +470,7 @@ static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
static void ublast_tms(struct tms_command *cmd)
{
DEBUG_JTAG_IO("(num_bits=%d)", cmd->num_bits);
- ublast_tms_seq(cmd->bits, cmd->num_bits);
+ ublast_tms_seq(cmd->bits, cmd->num_bits, 0);
}
/**
@@ -501,11 +502,12 @@ static void ublast_path_move(struct pathmove_command *cmd)
/**
* ublast_state_move - move JTAG state to the target state
* @state: the target state
+ * @skip: number of bits to skip at the beginning of the path
*
* Input the correct TMS sequence to the JTAG TAP so that we end up in the
* target state. This assumes the current state (tap_get_state()) is correct.
*/
-static void ublast_state_move(tap_state_t state)
+static void ublast_state_move(tap_state_t state, int skip)
{
uint8_t tms_scan;
int tms_len;
@@ -516,7 +518,7 @@ static void ublast_state_move(tap_state_t state)
return;
tms_scan = tap_get_tms_path(tap_get_state(), state);
tms_len = tap_get_tms_path_len(tap_get_state(), state);
- ublast_tms_seq(&tms_scan, tms_len);
+ ublast_tms_seq(&tms_scan, tms_len, skip);
tap_set_state(state);
}
@@ -688,9 +690,9 @@ static void ublast_runtest(int cycles, tap_state_t state)
{
DEBUG_JTAG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state);
- ublast_state_move(TAP_IDLE);
+ ublast_state_move(TAP_IDLE, 0);
ublast_queue_tdi(NULL, cycles, SCAN_OUT);
- ublast_state_move(state);
+ ublast_state_move(state, 0);
}
static void ublast_stableclocks(int cycles)
@@ -720,9 +722,9 @@ static int ublast_scan(struct scan_command *cmd)
scan_bits = jtag_build_buffer(cmd, &buf);
if (cmd->ir_scan)
- ublast_state_move(TAP_IRSHIFT);
+ ublast_state_move(TAP_IRSHIFT, 0);
else
- ublast_state_move(TAP_DRSHIFT);
+ ublast_state_move(TAP_DRSHIFT, 0);
log_buf = hexdump(buf, DIV_ROUND_UP(scan_bits, 8));
DEBUG_JTAG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", __func__,
@@ -733,20 +735,15 @@ static int ublast_scan(struct scan_command *cmd)
ublast_queue_tdi(buf, scan_bits, type);
- /*
- * As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it
- * forward to a stable IRPAUSE or DRPAUSE.
- */
- ublast_clock_tms(0);
- if (cmd->ir_scan)
- tap_set_state(TAP_IRPAUSE);
- else
- tap_set_state(TAP_DRPAUSE);
-
ret = jtag_read_buffer(buf, cmd);
if (buf)
free(buf);
- ublast_state_move(cmd->end_state);
+ /*
+ * ublast_queue_tdi sends the last bit with TMS=1. We are therefore
+ * already in Exit1-DR/IR and have to skip the first step on our way
+ * to end_state.
+ */
+ ublast_state_move(cmd->end_state, 1);
return ret;
}
@@ -776,7 +773,7 @@ static void ublast_initial_wipeout(void)
/*
* Put JTAG in RESET state (five 1 on TMS)
*/
- ublast_tms_seq(&tms_reset, 5);
+ ublast_tms_seq(&tms_reset, 5, 0);
tap_set_state(TAP_RESET);
}
@@ -805,7 +802,7 @@ static int ublast_execute_queue(void)
ublast_stableclocks(cmd->cmd.stableclocks->num_cycles);
break;
case JTAG_TLR_RESET:
- ublast_state_move(cmd->cmd.statemove->end_state);
+ ublast_state_move(cmd->cmd.statemove->end_state, 0);
break;
case JTAG_PATHMOVE:
ublast_path_move(cmd->cmd.pathmove);
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/hla/hla_interface.c b/src/jtag/hla/hla_interface.c
index 62a8f59..2abed21 100644
--- a/src/jtag/hla/hla_interface.c
+++ b/src/jtag/hla/hla_interface.c
@@ -119,6 +119,11 @@ static int hl_interface_quit(void)
if (hl_if.layout->api->close)
hl_if.layout->api->close(hl_if.handle);
+ jtag_command_queue_reset();
+
+ free((void *)hl_if.param.device_desc);
+ free((void *)hl_if.param.serial);
+
return ERROR_OK;
}
@@ -186,7 +191,7 @@ int hl_interface_override_target(const char **targetname)
return ERROR_FAIL;
}
-int hl_interface_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
+int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq)
{
if (hl_if.layout->api->config_trace)
diff --git a/src/jtag/hla/hla_layout.h b/src/jtag/hla/hla_layout.h
index 40c1321..9f41b59 100644
--- a/src/jtag/hla/hla_layout.h
+++ b/src/jtag/hla/hla_layout.h
@@ -91,7 +91,7 @@ struct hl_layout_api_s {
* its maximum supported rate there
* @returns ERROR_OK on success, an error code on failure.
*/
- int (*config_trace)(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
+ int (*config_trace)(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq);
/**
* Poll for new trace data
diff --git a/src/jtag/hla/hla_tcl.c b/src/jtag/hla/hla_tcl.c
index 9378427..73f78fc 100644
--- a/src/jtag/hla/hla_tcl.c
+++ b/src/jtag/hla/hla_tcl.c
@@ -39,20 +39,15 @@ static int jim_newtap_expected_id(Jim_Nvp *n, Jim_GetOptInfo *goi,
return e;
}
- unsigned expected_len = sizeof(uint32_t) * pTap->expected_ids_cnt;
- uint32_t *new_expected_ids = malloc(expected_len + sizeof(uint32_t));
- if (new_expected_ids == NULL) {
+ uint32_t *p = realloc(pTap->expected_ids,
+ (pTap->expected_ids_cnt + 1) * sizeof(uint32_t));
+ if (!p) {
Jim_SetResultFormatted(goi->interp, "no memory");
return JIM_ERR;
}
- memcpy(new_expected_ids, pTap->expected_ids, expected_len);
-
- new_expected_ids[pTap->expected_ids_cnt] = w;
-
- free(pTap->expected_ids);
- pTap->expected_ids = new_expected_ids;
- pTap->expected_ids_cnt++;
+ pTap->expected_ids = p;
+ pTap->expected_ids[pTap->expected_ids_cnt++] = w;
return JIM_OK;
}
diff --git a/src/jtag/hla/hla_transport.c b/src/jtag/hla/hla_transport.c
index 5a5671d..ddacea3 100644
--- a/src/jtag/hla/hla_transport.c
+++ b/src/jtag/hla/hla_transport.c
@@ -233,3 +233,11 @@ static void hl_constructor(void)
transport_register(&hl_jtag_transport);
transport_register(&stlink_swim_transport);
}
+
+bool transport_is_hla(void)
+{
+ struct transport *t;
+ t = get_current_transport();
+ return t == &hl_swd_transport || t == &hl_jtag_transport
+ || t == &stlink_swim_transport;
+}
diff --git a/src/jtag/interface.h b/src/jtag/interface.h
index cdfc676..e6fa0ca 100644
--- a/src/jtag/interface.h
+++ b/src/jtag/interface.h
@@ -309,7 +309,7 @@ struct jtag_interface {
* its maximum supported rate there
* @returns ERROR_OK on success, an error code on failure.
*/
- int (*config_trace)(bool enabled, enum tpio_pin_protocol pin_protocol,
+ int (*config_trace)(bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq);
/**
@@ -328,7 +328,7 @@ extern const char * const jtag_only[];
void adapter_assert_reset(void);
void adapter_deassert_reset(void);
-int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
+int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
uint32_t port_size, unsigned int *trace_freq);
int adapter_poll_trace(uint8_t *buf, size_t *size);
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index 174c63a..286a73a 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -60,6 +60,9 @@ extern struct jtag_interface usb_blaster_interface;
#if BUILD_JTAG_VPI == 1
extern struct jtag_interface jtag_vpi_interface;
#endif
+#if BUILD_FT232R == 1
+extern struct jtag_interface ft232r_interface;
+#endif
#if BUILD_AMTJTAGACCEL == 1
extern struct jtag_interface amt_jtagaccel_interface;
#endif
@@ -129,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 */
/**
@@ -159,6 +165,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_JTAG_VPI == 1
&jtag_vpi_interface,
#endif
+#if BUILD_FT232R == 1
+ &ft232r_interface,
+#endif
#if BUILD_AMTJTAGACCEL == 1
&amt_jtagaccel_interface,
#endif
@@ -228,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/jtag/jtag.h b/src/jtag/jtag.h
index 7702d6c..a6891c0 100644
--- a/src/jtag/jtag.h
+++ b/src/jtag/jtag.h
@@ -153,8 +153,6 @@ struct jtag_tap {
struct jtag_tap_event_action *event_action;
struct jtag_tap *next_tap;
- /* dap instance if some null if no instance , initialized to 0 by calloc*/
- struct adiv5_dap *dap;
/* private pointer to support none-jtag specific functions */
void *priv;
};
@@ -642,8 +640,6 @@ void jtag_poll_set_enabled(bool value);
* level APIs that are used in inner loops. */
#include <jtag/minidriver.h>
-bool transport_is_jtag(void);
-
int jim_jtag_newtap(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
#endif /* OPENOCD_JTAG_JTAG_H */
diff --git a/src/jtag/swd.h b/src/jtag/swd.h
index c888cc0..52f41d5 100644
--- a/src/jtag/swd.h
+++ b/src/jtag/swd.h
@@ -211,6 +211,4 @@ struct swd_driver {
int swd_init_reset(struct command_context *cmd_ctx);
void swd_add_reset(int req_srst);
-bool transport_is_swd(void);
-
#endif /* OPENOCD_JTAG_SWD_H */
diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c
index bc6bbf2..e32f0ca 100644
--- a/src/jtag/tcl.c
+++ b/src/jtag/tcl.c
@@ -42,6 +42,7 @@
#endif
#include <helper/time_support.h>
+#include "transport/transport.h"
/**
* @file
diff --git a/src/jtag/zy1000/zy1000.c b/src/jtag/zy1000/zy1000.c
index 67d9907..4e53dd1 100644
--- a/src/jtag/zy1000/zy1000.c
+++ b/src/jtag/zy1000/zy1000.c
@@ -265,8 +265,8 @@ COMMAND_HANDLER(handle_power_command)
bool enable;
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
setPower(enable);
- /* fall through */
}
+ /* fall through */
case 0:
LOG_INFO("Target power %s", savePower ? "on" : "off");
break;
diff --git a/src/openocd.c b/src/openocd.c
index 831bd17..f084dd4 100644
--- a/src/openocd.c
+++ b/src/openocd.c
@@ -37,6 +37,8 @@
#include <flash/nand/core.h>
#include <pld/pld.h>
#include <flash/mflash.h>
+#include <target/arm_cti.h>
+#include <target/arm_adi_v5.h>
#include <server/server.h>
#include <server/gdb_server.h>
@@ -85,13 +87,13 @@ static int log_target_callback_event_handler(struct target *target,
{
switch (event) {
case TARGET_EVENT_GDB_START:
- target->display = 0;
+ target->verbose_halt_msg = false;
break;
case TARGET_EVENT_GDB_END:
- target->display = 1;
+ target->verbose_halt_msg = true;
break;
case TARGET_EVENT_HALTED:
- if (target->display) {
+ if (target->verbose_halt_msg) {
/* do not display information when debugger caused the halt */
target_arch_state(target);
}
@@ -150,6 +152,10 @@ COMMAND_HANDLER(handle_init_command)
if (ERROR_OK != retval)
return ERROR_FAIL;
+ retval = command_run_line(CMD_CTX, "dap init");
+ if (ERROR_OK != retval)
+ return ERROR_FAIL;
+
LOG_DEBUG("Examining targets...");
if (target_examine() != ERROR_OK)
LOG_DEBUG("target examination failed");
@@ -252,6 +258,8 @@ struct command_context *setup_command_handler(Jim_Interp *interp)
&nand_register_commands,
&pld_register_commands,
&mflash_register_commands,
+ &cti_register_commands,
+ &dap_register_commands,
NULL
};
for (unsigned i = 0; NULL != command_registrants[i]; i++) {
@@ -286,10 +294,13 @@ static int openocd_thread(int argc, char *argv[], struct command_context *cmd_ct
return ERROR_FAIL;
ret = parse_config_file(cmd_ctx);
- if (ret == ERROR_COMMAND_CLOSE_CONNECTION)
+ if (ret == ERROR_COMMAND_CLOSE_CONNECTION) {
+ server_quit(); /* gdb server may be initialized by -c init */
return ERROR_OK;
- else if (ret != ERROR_OK)
+ } else if (ret != ERROR_OK) {
+ server_quit(); /* gdb server may be initialized by -c init */
return ERROR_FAIL;
+ }
ret = server_init(cmd_ctx);
if (ERROR_OK != ret)
@@ -342,12 +353,22 @@ int openocd_main(int argc, char *argv[])
/* Start the executable meat that can evolve into thread in future. */
ret = openocd_thread(argc, argv, cmd_ctx);
+ flash_free_all_banks();
+ gdb_service_free();
+ server_free();
+
unregister_all_commands(cmd_ctx, NULL);
+ /* free all DAP and CTI objects */
+ dap_cleanup_all();
+ arm_cti_cleanup_all();
+
+ adapter_quit();
+
/* Shutdown commandline interface */
command_exit(cmd_ctx);
- adapter_quit();
+ free_config();
if (ERROR_FAIL == ret)
return EXIT_FAILURE;
diff --git a/src/rtos/ChibiOS.c b/src/rtos/ChibiOS.c
index ef0bb16..312fc4d 100644
--- a/src/rtos/ChibiOS.c
+++ b/src/rtos/ChibiOS.c
@@ -69,10 +69,9 @@ struct ChibiOS_chdebug {
/**
* @brief ChibiOS thread states.
*/
-static const char * const ChibiOS_thread_states[] = {
- "READY", "CURRENT", "SUSPENDED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING",
- "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "WTQUEUE",
- "FINAL"
+static const char * const ChibiOS_thread_states[] = { "READY", "CURRENT",
+"WTSTART", "SUSPENDED", "QUEUED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING",
+"WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "FINAL"
};
#define CHIBIOS_NUM_STATES (sizeof(ChibiOS_thread_states)/sizeof(char *))
@@ -247,7 +246,7 @@ static int ChibiOS_update_stacking(struct rtos *rtos)
/* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4 */
struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
if (is_armv7m(armv7m_target)) {
- if (armv7m_target->fp_feature == FPv4_SP) {
+ if (armv7m_target->fp_feature != FP_NONE) {
/* Found ARM v7m target which includes a FPU */
uint32_t cpacr;
diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am
index 22f7da5..6f14b42 100644
--- a/src/rtos/Makefile.am
+++ b/src/rtos/Makefile.am
@@ -16,6 +16,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/mqx.c \
%D%/riscv_debug.c \
%D%/uCOS-III.c \
+ %D%/nuttx.c \
%D%/rtos.h \
%D%/rtos_standard_stackings.h \
%D%/rtos_ecos_stackings.h \
@@ -24,6 +25,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/rtos_embkernel_stackings.h \
%D%/rtos_mqx_stackings.h \
%D%/rtos_ucos_iii_stackings.h \
+ %D%/nuttx_header.h \
%D%/riscv_debug.h
%C%_librtos_la_CFLAGS = $(AM_CFLAGS)
diff --git a/src/rtos/nuttx.c b/src/rtos/nuttx.c
new file mode 100644
index 0000000..284b968
--- /dev/null
+++ b/src/rtos/nuttx.c
@@ -0,0 +1,405 @@
+/***************************************************************************
+ * Copyright 2016,2017 Sony Video & Sound Products Inc. *
+ * Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com *
+ * Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <jtag/jtag.h>
+#include "target/target.h"
+#include "target/target_type.h"
+#include "target/armv7m.h"
+#include "target/cortex_m.h"
+#include "rtos.h"
+#include "helper/log.h"
+#include "helper/types.h"
+#include "server/gdb_server.h"
+
+#include "nuttx_header.h"
+
+
+int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
+
+#ifdef CONFIG_DISABLE_SIGNALS
+#define SIG_QUEUE_NUM 0
+#else
+#define SIG_QUEUE_NUM 1
+#endif /* CONFIG_DISABLE_SIGNALS */
+
+#ifdef CONFIG_DISABLE_MQUEUE
+#define M_QUEUE_NUM 0
+#else
+#define M_QUEUE_NUM 2
+#endif /* CONFIG_DISABLE_MQUEUE */
+
+#ifdef CONFIG_PAGING
+#define PAGING_QUEUE_NUM 1
+#else
+#define PAGING_QUEUE_NUM 0
+#endif /* CONFIG_PAGING */
+
+
+#define TASK_QUEUE_NUM (6 + SIG_QUEUE_NUM + M_QUEUE_NUM + PAGING_QUEUE_NUM)
+
+
+/* see nuttx/sched/os_start.c */
+static char *nuttx_symbol_list[] = {
+ "g_readytorun", /* 0: must be top of this array */
+ "g_tasklisttable",
+ NULL
+};
+
+/* see nuttx/include/nuttx/sched.h */
+struct tcb {
+ uint32_t flink;
+ uint32_t blink;
+ uint8_t dat[512];
+};
+
+struct {
+ uint32_t addr;
+ uint32_t prio;
+} g_tasklist[TASK_QUEUE_NUM];
+
+static char *task_state_str[] = {
+ "INVALID",
+ "PENDING",
+ "READYTORUN",
+ "RUNNING",
+ "INACTIVE",
+ "WAIT_SEM",
+#ifndef CONFIG_DISABLE_SIGNALS
+ "WAIT_SIG",
+#endif /* CONFIG_DISABLE_SIGNALS */
+#ifndef CONFIG_DISABLE_MQUEUE
+ "WAIT_MQNOTEMPTY",
+ "WAIT_MQNOTFULL",
+#endif /* CONFIG_DISABLE_MQUEUE */
+#ifdef CONFIG_PAGING
+ "WAIT_PAGEFILL",
+#endif /* CONFIG_PAGING */
+};
+
+/* see arch/arm/include/armv7-m/irq_cmnvector.h */
+static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = {
+ { 0x28, 32 }, /* r0 */
+ { 0x2c, 32 }, /* r1 */
+ { 0x30, 32 }, /* r2 */
+ { 0x34, 32 }, /* r3 */
+ { 0x08, 32 }, /* r4 */
+ { 0x0c, 32 }, /* r5 */
+ { 0x10, 32 }, /* r6 */
+ { 0x14, 32 }, /* r7 */
+ { 0x18, 32 }, /* r8 */
+ { 0x1c, 32 }, /* r9 */
+ { 0x20, 32 }, /* r10 */
+ { 0x24, 32 }, /* r11 */
+ { 0x38, 32 }, /* r12 */
+ { 0, 32 }, /* sp */
+ { 0x3c, 32 }, /* lr */
+ { 0x40, 32 }, /* pc */
+ { 0x44, 32 }, /* xPSR */
+};
+
+
+static const struct rtos_register_stacking nuttx_stacking_cortex_m = {
+ 0x48, /* stack_registers_size */
+ -1, /* stack_growth_direction */
+ 17, /* num_output_registers */
+ 0, /* stack_alignment */
+ nuttx_stack_offsets_cortex_m /* register_offsets */
+};
+
+static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = {
+ { 0x6c, 32 }, /* r0 */
+ { 0x70, 32 }, /* r1 */
+ { 0x74, 32 }, /* r2 */
+ { 0x78, 32 }, /* r3 */
+ { 0x08, 32 }, /* r4 */
+ { 0x0c, 32 }, /* r5 */
+ { 0x10, 32 }, /* r6 */
+ { 0x14, 32 }, /* r7 */
+ { 0x18, 32 }, /* r8 */
+ { 0x1c, 32 }, /* r9 */
+ { 0x20, 32 }, /* r10 */
+ { 0x24, 32 }, /* r11 */
+ { 0x7c, 32 }, /* r12 */
+ { 0, 32 }, /* sp */
+ { 0x80, 32 }, /* lr */
+ { 0x84, 32 }, /* pc */
+ { 0x88, 32 }, /* xPSR */
+};
+
+static const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = {
+ 0x8c, /* stack_registers_size */
+ -1, /* stack_growth_direction */
+ 17, /* num_output_registers */
+ 0, /* stack_alignment */
+ nuttx_stack_offsets_cortex_m_fpu /* register_offsets */
+};
+
+static int pid_offset = PID;
+static int state_offset = STATE;
+static int name_offset = NAME;
+static int xcpreg_offset = XCPREG;
+static int name_size = NAME_SIZE;
+
+static int rcmd_offset(const char *cmd, const char *name)
+{
+ if (strncmp(cmd, name, strlen(name)))
+ return -1;
+
+ if (strlen(cmd) <= strlen(name) + 1)
+ return -1;
+
+ return atoi(cmd + strlen(name));
+}
+
+static int nuttx_thread_packet(struct connection *connection,
+ char const *packet, int packet_size)
+{
+ char cmd[GDB_BUFFER_SIZE / 2] = "";
+
+ if (!strncmp(packet, "qRcmd", 5)) {
+ size_t len = unhexify((uint8_t *)cmd, packet + 6, sizeof(cmd));
+ int offset;
+
+ if (len <= 0)
+ goto pass;
+
+ offset = rcmd_offset(cmd, "nuttx.pid_offset");
+
+ if (offset >= 0) {
+ LOG_INFO("pid_offset: %d", offset);
+ pid_offset = offset;
+ goto retok;
+ }
+
+ offset = rcmd_offset(cmd, "nuttx.state_offset");
+
+ if (offset >= 0) {
+ LOG_INFO("state_offset: %d", offset);
+ state_offset = offset;
+ goto retok;
+ }
+
+ offset = rcmd_offset(cmd, "nuttx.name_offset");
+
+ if (offset >= 0) {
+ LOG_INFO("name_offset: %d", offset);
+ name_offset = offset;
+ goto retok;
+ }
+
+ offset = rcmd_offset(cmd, "nuttx.xcpreg_offset");
+
+ if (offset >= 0) {
+ LOG_INFO("xcpreg_offset: %d", offset);
+ xcpreg_offset = offset;
+ goto retok;
+ }
+
+ offset = rcmd_offset(cmd, "nuttx.name_size");
+
+ if (offset >= 0) {
+ LOG_INFO("name_size: %d", offset);
+ name_size = offset;
+ goto retok;
+ }
+ }
+pass:
+ return rtos_thread_packet(connection, packet, packet_size);
+retok:
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+}
+
+
+static bool nuttx_detect_rtos(struct target *target)
+{
+ if ((target->rtos->symbols != NULL) &&
+ (target->rtos->symbols[0].address != 0) &&
+ (target->rtos->symbols[1].address != 0)) {
+ return true;
+ }
+ return false;
+}
+
+static int nuttx_create(struct target *target)
+{
+
+ target->rtos->gdb_thread_packet = nuttx_thread_packet;
+ LOG_INFO("target type name = %s", target->type->name);
+ return 0;
+}
+
+static int nuttx_update_threads(struct rtos *rtos)
+{
+ uint32_t thread_count;
+ struct tcb tcb;
+ int ret;
+ uint32_t head;
+ uint32_t tcb_addr;
+ uint32_t i;
+ uint8_t state;
+
+ if (rtos->symbols == NULL) {
+ LOG_ERROR("No symbols for NuttX");
+ return -3;
+ }
+
+ /* free previous thread details */
+ rtos_free_threadlist(rtos);
+
+ ret = target_read_buffer(rtos->target, rtos->symbols[1].address,
+ sizeof(g_tasklist), (uint8_t *)&g_tasklist);
+ if (ret) {
+ LOG_ERROR("target_read_buffer : ret = %d\n", ret);
+ return ERROR_FAIL;
+ }
+
+ thread_count = 0;
+
+ for (i = 0; i < TASK_QUEUE_NUM; i++) {
+
+ if (g_tasklist[i].addr == 0)
+ continue;
+
+ ret = target_read_u32(rtos->target, g_tasklist[i].addr,
+ &head);
+
+ if (ret) {
+ LOG_ERROR("target_read_u32 : ret = %d\n", ret);
+ return ERROR_FAIL;
+ }
+
+ /* readytorun head is current thread */
+ if (g_tasklist[i].addr == rtos->symbols[0].address)
+ rtos->current_thread = head;
+
+
+ tcb_addr = head;
+ while (tcb_addr) {
+ struct thread_detail *thread;
+ ret = target_read_buffer(rtos->target, tcb_addr,
+ sizeof(tcb), (uint8_t *)&tcb);
+ if (ret) {
+ LOG_ERROR("target_read_buffer : ret = %d\n",
+ ret);
+ return ERROR_FAIL;
+ }
+ thread_count++;
+
+ rtos->thread_details = realloc(rtos->thread_details,
+ sizeof(struct thread_detail) * thread_count);
+ thread = &rtos->thread_details[thread_count - 1];
+ thread->threadid = tcb_addr;
+ thread->exists = true;
+
+ state = tcb.dat[state_offset - 8];
+ thread->extra_info_str = NULL;
+ if (state < sizeof(task_state_str)/sizeof(char *)) {
+ thread->extra_info_str = malloc(256);
+ snprintf(thread->extra_info_str, 256, "pid:%d, %s",
+ tcb.dat[pid_offset - 8] |
+ tcb.dat[pid_offset - 8 + 1] << 8,
+ task_state_str[state]);
+ }
+
+ if (name_offset) {
+ thread->thread_name_str = malloc(name_size + 1);
+ snprintf(thread->thread_name_str, name_size,
+ "%s", (char *)&tcb.dat[name_offset - 8]);
+ } else {
+ thread->thread_name_str = malloc(sizeof("None"));
+ strcpy(thread->thread_name_str, "None");
+ }
+
+ tcb_addr = tcb.flink;
+ }
+ }
+ rtos->thread_count = thread_count;
+
+ return 0;
+}
+
+
+/*
+ * thread_id = tcb address;
+ */
+static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+ char **hex_reg_list) {
+ int retval;
+
+ *hex_reg_list = NULL;
+
+ /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
+ bool cm4_fpu_enabled = false;
+ struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
+ if (is_armv7m(armv7m_target)) {
+ if (armv7m_target->fp_feature == FPv4_SP) {
+ /* Found ARM v7m target which includes a FPU */
+ uint32_t cpacr;
+
+ retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not read CPACR register to check FPU state");
+ return -1;
+ }
+
+ /* Check if CP10 and CP11 are set to full access. */
+ if (cpacr & 0x00F00000) {
+ /* Found target with enabled FPU */
+ cm4_fpu_enabled = 1;
+ }
+ }
+ }
+
+ const struct rtos_register_stacking *stacking;
+ if (cm4_fpu_enabled)
+ stacking = &nuttx_stacking_cortex_m_fpu;
+ else
+ stacking = &nuttx_stacking_cortex_m;
+
+ return rtos_generic_stack_read(rtos->target, stacking,
+ (uint32_t)thread_id + xcpreg_offset, hex_reg_list);
+}
+
+static int nuttx_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
+{
+ unsigned int i;
+
+ *symbol_list = (symbol_table_elem_t *) calloc(1,
+ sizeof(symbol_table_elem_t) * ARRAY_SIZE(nuttx_symbol_list));
+
+ for (i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++)
+ (*symbol_list)[i].symbol_name = nuttx_symbol_list[i];
+
+ return 0;
+}
+
+struct rtos_type nuttx_rtos = {
+ .name = "nuttx",
+ .detect_rtos = nuttx_detect_rtos,
+ .create = nuttx_create,
+ .update_threads = nuttx_update_threads,
+ .get_thread_reg_list = nuttx_get_thread_reg_list,
+ .get_symbol_list_to_lookup = nuttx_get_symbol_list_to_lookup,
+};
+
diff --git a/src/rtos/nuttx_header.h b/src/rtos/nuttx_header.h
new file mode 100644
index 0000000..00b0484
--- /dev/null
+++ b/src/rtos/nuttx_header.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright 2016,2017 Sony Video & Sound Products Inc. *
+ * Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com *
+ * Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.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_RTOS_NUTTX_HEADER_H
+#define OPENOCD_RTOS_NUTTX_HEADER_H
+
+/* gdb script to update the header file
+ according to kernel version and build option
+ before executing function awareness
+ kernel symbol must be loaded : symbol nuttx
+
+define awareness
+ set logging off
+ set logging file nuttx_header.h
+ set logging on
+
+ printf "#define PID %p\n",&((struct tcb_s *)(0))->pid
+ printf "#define XCPREG %p\n",&((struct tcb_s *)(0))->xcp.regs
+ printf "#define STATE %p\n",&((struct tcb_s *)(0))->task_state
+ printf "#define NAME %p\n",&((struct tcb_s *)(0))->name
+ printf "#define NAME_SIZE %d\n",sizeof(((struct tcb_s *)(0))->name)
+ end
+
+
+ OR ~/.gdbinit
+
+
+define hookpost-file
+
+ if &g_readytorun != 0
+ eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid
+ eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs
+ eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state
+ eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name
+ eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name)
+ end
+
+end
+
+*/
+
+/* default offset */
+#define PID 0xc
+#define XCPREG 0x70
+#define STATE 0x19
+#define NAME 0xb8
+#define NAME_SIZE 32
+
+/* defconfig of nuttx */
+/* #define CONFIG_DISABLE_SIGNALS */
+#define CONFIG_DISABLE_MQUEUE
+/* #define CONFIG_PAGING */
+
+
+#endif /* OPENOCD_RTOS_NUTTX_HEADER_H */
diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c
index 90caf40..6703008 100644
--- a/src/rtos/riscv_debug.c
+++ b/src/rtos/riscv_debug.c
@@ -22,11 +22,6 @@ static int riscv_create_rtos(struct target *target)
struct riscv_rtos *r = calloc(1, sizeof(*r));
target->rtos->rtos_specific_params = r;
-#if 0
- r->target_hartid = 0;
- r->target_any_hart = true;
- r->target_every_hart = true;
-#endif
target->rtos->current_threadid = 1;
target->rtos->current_thread = 1;
@@ -129,6 +124,13 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *pa
return ERROR_OK;
}
+ if (strcmp(packet, "qC") == 0) {
+ char rep_str[32];
+ snprintf(rep_str, 32, "QC%" PRIx64, rtos->current_threadid);
+ gdb_put_packet(connection, rep_str, strlen(rep_str));
+ return ERROR_OK;
+ }
+
return GDB_THREAD_PACKET_NOT_CONSUMED;
case 'Q':
@@ -266,12 +268,6 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char
{
LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1));
-#if 0
- LOG_ERROR(" Not actually updating");
- *hex_reg_list = 0;
- return JIM_OK;
-#endif
-
size_t n_regs = 32;
size_t xlen = 64;
size_t reg_chars = xlen / 8 * 2;
diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c
index a37f44a..bd9da0d 100644
--- a/src/rtos/rtos.c
+++ b/src/rtos/rtos.c
@@ -35,6 +35,7 @@ extern struct rtos_type ChibiOS_rtos;
extern struct rtos_type embKernel_rtos;
extern struct rtos_type mqx_rtos;
extern struct rtos_type uCOS_III_rtos;
+extern struct rtos_type nuttx_rtos;
extern struct rtos_type riscv_rtos;
static struct rtos_type *rtos_types[] = {
@@ -46,6 +47,7 @@ static struct rtos_type *rtos_types[] = {
&embKernel_rtos,
&mqx_rtos,
&uCOS_III_rtos,
+ &nuttx_rtos,
&riscv_rtos,
NULL
};
@@ -59,6 +61,15 @@ int rtos_smp_init(struct target *target)
return ERROR_TARGET_INIT_FAILED;
}
+static int rtos_target_for_threadid(struct connection *connection, int64_t threadid, struct target **t)
+{
+ struct target *curr = get_target_from_connection(connection);
+ if (t)
+ *t = curr;
+
+ return ERROR_OK;
+}
+
static int os_alloc(struct target *target, struct rtos_type *ostype)
{
struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos));
@@ -75,6 +86,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet;
os->gdb_v_packet = NULL;
+ os->gdb_target_for_threadid = rtos_target_for_threadid;
return JIM_OK;
}
@@ -339,8 +351,10 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa
return ERROR_OK;
} else if (strncmp(packet, "qSymbol", 7) == 0) {
if (rtos_qsymbol(connection, packet, packet_size) == 1) {
- target->rtos_auto_detect = false;
- target->rtos->type->create(target);
+ if (target->rtos_auto_detect == true) {
+ target->rtos_auto_detect = false;
+ target->rtos->type->create(target);
+ }
target->rtos->type->update_threads(target->rtos);
}
return ERROR_OK;
diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h
index 9da035a..c9ac1e5 100644
--- a/src/rtos/rtos.h
+++ b/src/rtos/rtos.h
@@ -55,6 +55,7 @@ struct rtos {
int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size);
+ int (*gdb_target_for_threadid)(struct connection *connection, int64_t thread_id, struct target **p_target);
void *rtos_specific_params;
};
diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c
index 0176c01..931cfc7 100644
--- a/src/rtos/rtos_standard_stackings.c
+++ b/src/rtos/rtos_standard_stackings.c
@@ -229,6 +229,25 @@ static int64_t rtos_standard_Cortex_M3_stack_align(struct target *target,
stack_ptr, XPSR_OFFSET);
}
+static int64_t rtos_standard_Cortex_M4F_stack_align(struct target *target,
+ const uint8_t *stack_data, const struct rtos_register_stacking *stacking,
+ int64_t stack_ptr)
+{
+ const int XPSR_OFFSET = 0x40;
+ return rtos_Cortex_M_stack_align(target, stack_data, stacking,
+ stack_ptr, XPSR_OFFSET);
+}
+
+static int64_t rtos_standard_Cortex_M4F_FPU_stack_align(struct target *target,
+ const uint8_t *stack_data, const struct rtos_register_stacking *stacking,
+ int64_t stack_ptr)
+{
+ const int XPSR_OFFSET = 0x80;
+ return rtos_Cortex_M_stack_align(target, stack_data, stacking,
+ stack_ptr, XPSR_OFFSET);
+}
+
+
const struct rtos_register_stacking rtos_standard_Cortex_M3_stacking = {
0x40, /* stack_registers_size */
-1, /* stack_growth_direction */
@@ -241,7 +260,7 @@ const struct rtos_register_stacking rtos_standard_Cortex_M4F_stacking = {
0x44, /* stack_registers_size 4 more for LR*/
-1, /* stack_growth_direction */
ARMV7M_NUM_CORE_REGS, /* num_output_registers */
- rtos_standard_Cortex_M3_stack_align, /* stack_alignment */
+ rtos_standard_Cortex_M4F_stack_align, /* stack_alignment */
rtos_standard_Cortex_M4F_stack_offsets /* register_offsets */
};
@@ -249,7 +268,7 @@ const struct rtos_register_stacking rtos_standard_Cortex_M4F_FPU_stacking = {
0xcc, /* stack_registers_size 4 more for LR + 48 more for FPU S0-S15 register*/
-1, /* stack_growth_direction */
ARMV7M_NUM_CORE_REGS, /* num_output_registers */
- rtos_standard_Cortex_M3_stack_align, /* stack_alignment */
+ rtos_standard_Cortex_M4F_FPU_stack_align, /* stack_alignment */
rtos_standard_Cortex_M4F_FPU_stack_offsets /* register_offsets */
};
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 836e783..58df51a 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -41,6 +41,8 @@
#include <target/breakpoints.h>
#include <target/target_request.h>
#include <target/register.h>
+#include <target/target.h>
+#include <target/target_type.h>
#include "server.h"
#include <flash/nor/core.h>
#include "gdb_server.h"
@@ -110,6 +112,8 @@ static char *gdb_port_next;
static void gdb_log_callback(void *priv, const char *file, unsigned line,
const char *function, const char *string);
+static void gdb_sig_halted(struct connection *connection);
+
/* number of gdb connections, mainly to suppress gdb related debugging spam
* in helper/log.c when no gdb connections are actually active */
int gdb_actual_connections;
@@ -126,6 +130,9 @@ static int gdb_flash_program = 1;
* Disabled by default.
*/
static int gdb_report_data_abort;
+/* If set, errors when accessing registers are reported to gdb. Disabled by
+ * default. */
+static int gdb_report_register_access_error;
/* set if we are sending target descriptions to gdb
* via qXfer:features:read packet */
@@ -715,7 +722,7 @@ static int gdb_output(struct command_context *context, const char *line)
static void gdb_signal_reply(struct target *target, struct connection *connection)
{
struct gdb_connection *gdb_connection = connection->priv;
- char sig_reply[45];
+ char sig_reply[65];
char stop_reason[20];
char current_thread[25];
int sig_reply_len;
@@ -728,7 +735,6 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
} else {
if (gdb_connection->ctrl_c) {
signal_var = 0x2;
- gdb_connection->ctrl_c = 0;
} else
signal_var = gdb_last_signal(target);
@@ -760,12 +766,19 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
current_thread[0] = '\0';
if (target->rtos != NULL) {
- snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread);
+ struct target *ct;
+ snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
+ target->rtos->current_thread);
target->rtos->current_threadid = target->rtos->current_thread;
+ target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
+ if (!gdb_connection->ctrl_c)
+ signal_var = gdb_last_signal(ct);
}
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread);
+
+ gdb_connection->ctrl_c = 0;
}
gdb_put_packet(connection, sig_reply, sig_reply_len);
@@ -780,64 +793,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);
@@ -923,6 +936,7 @@ static int gdb_new_connection(struct connection *connection)
target = get_target_from_connection(connection);
connection->priv = gdb_connection;
+ connection->cmd_ctx->current_target = target;
/* initialize gdb connection information */
gdb_connection->buf_p = gdb_connection->buffer;
@@ -953,9 +967,14 @@ static int gdb_new_connection(struct connection *connection)
breakpoint_clear_target(target);
watchpoint_clear_target(target);
- /* clean previous rtos session if supported*/
- if ((target->rtos) && (target->rtos->type->clean))
- target->rtos->type->clean(target);
+ if (target->rtos) {
+ /* clean previous rtos session if supported*/
+ if (target->rtos->type->clean)
+ target->rtos->type->clean(target);
+
+ /* update threads */
+ rtos_update_threads(target);
+ }
/* remove the initial ACK from the incoming buffer */
retval = gdb_get_char(connection, &initial_ack);
@@ -1174,7 +1193,7 @@ static int gdb_get_registers_packet(struct connection *connection,
for (i = 0; i < reg_list_size; i++) {
if (!reg_list[i]->valid) {
retval = reg_list[i]->type->get(reg_list[i]);
- if (retval != ERROR_OK && target->propagate_register_errors) {
+ if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name);
free(reg_packet);
free(reg_list);
@@ -1242,7 +1261,7 @@ static int gdb_set_registers_packet(struct connection *connection,
gdb_target_to_reg(target, packet_p, chars, bin_buf);
retval = reg_list[i]->type->set(reg_list[i], bin_buf);
- if (retval != ERROR_OK && target->propagate_register_errors) {
+ if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name);
free(reg_list);
free(bin_buf);
@@ -1289,7 +1308,7 @@ static int gdb_get_register_packet(struct connection *connection,
if (!reg_list[reg_num]->valid) {
retval = reg_list[reg_num]->type->get(reg_list[reg_num]);
- if (retval != ERROR_OK && target->propagate_register_errors) {
+ if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name);
free(reg_list);
return gdb_error(connection, retval);
@@ -1341,7 +1360,8 @@ static int gdb_set_register_packet(struct connection *connection,
int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
if ((unsigned int)chars != strlen(separator + 1)) {
- LOG_ERROR("gdb sent a packet with wrong register size");
+ LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)",
+ strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name);
free(bin_buf);
return ERROR_SERVER_REMOTE_CLOSED;
}
@@ -1349,7 +1369,7 @@ static int gdb_set_register_packet(struct connection *connection,
gdb_target_to_reg(target, separator + 1, chars, bin_buf);
retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
- if (retval != ERROR_OK && target->propagate_register_errors) {
+ if (retval != ERROR_OK && gdb_report_register_access_error) {
LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name);
free(bin_buf);
free(reg_list);
@@ -1790,7 +1810,7 @@ static int gdb_memory_map(struct connection *connection,
int offset;
int length;
char *separator;
- uint32_t ram_start = 0;
+ target_addr_t ram_start = 0;
int i;
int target_flash_banks = 0;
@@ -1805,9 +1825,6 @@ static int gdb_memory_map(struct connection *connection,
/* Sort banks in ascending order. We need to report non-flash
* memory as ram (or rather read/write) by default for GDB, since
* it has no concept of non-cacheable read/write memory (i/o etc).
- *
- * FIXME Most non-flash addresses are *NOT* RAM! Don't lie.
- * Current versions of GDB assume unlisted addresses are RAM...
*/
banks = malloc(sizeof(struct flash_bank *)*flash_get_bank_count());
@@ -1830,14 +1847,13 @@ static int gdb_memory_map(struct connection *connection,
for (i = 0; i < target_flash_banks; i++) {
int j;
unsigned sector_size = 0;
- uint32_t start;
+ unsigned group_len = 0;
p = banks[i];
- start = p->base;
if (ram_start < p->base)
xml_printf(&retval, &xml, &pos, &size,
- "<memory type=\"ram\" start=\"0x%x\" "
+ "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" "
"length=\"0x%x\"/>\n",
ram_start, p->base - ram_start);
@@ -1848,27 +1864,35 @@ static int gdb_memory_map(struct connection *connection,
* regions with 8KB, 32KB, and 64KB sectors; etc.
*/
for (j = 0; j < p->num_sectors; j++) {
- unsigned group_len;
/* Maybe start a new group of sectors. */
if (sector_size == 0) {
+ if (p->sectors[j].offset + p->sectors[j].size > p->size) {
+ LOG_WARNING("The flash sector at offset 0x%08" PRIx32
+ " overflows the end of %s bank.",
+ p->sectors[j].offset, p->name);
+ LOG_WARNING("The rest of bank will not show in gdb memory map.");
+ break;
+ }
+ target_addr_t start;
start = p->base + p->sectors[j].offset;
xml_printf(&retval, &xml, &pos, &size,
"<memory type=\"flash\" "
- "start=\"0x%x\" ",
+ "start=\"" TARGET_ADDR_FMT "\" ",
start);
sector_size = p->sectors[j].size;
+ group_len = sector_size;
+ } else {
+ group_len += sector_size; /* equal to p->sectors[j].size */
}
/* Does this finish a group of sectors?
* If not, continue an already-started group.
*/
- if (j == p->num_sectors - 1)
- group_len = (p->base + p->size) - start;
- else if (p->sectors[j + 1].size != sector_size)
- group_len = p->base + p->sectors[j + 1].offset
- - start;
- else
+ if (j < p->num_sectors - 1
+ && p->sectors[j + 1].size == sector_size
+ && p->sectors[j + 1].offset == p->sectors[j].offset + sector_size
+ && p->sectors[j + 1].offset + p->sectors[j + 1].size <= p->size)
continue;
xml_printf(&retval, &xml, &pos, &size,
@@ -1886,7 +1910,7 @@ static int gdb_memory_map(struct connection *connection,
if (ram_start != 0)
xml_printf(&retval, &xml, &pos, &size,
- "<memory type=\"ram\" start=\"0x%x\" "
+ "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" "
"length=\"0x%x\"/>\n",
ram_start, 0-ram_start);
/* ELSE a flash chip could be at the very end of the 32 bit address
@@ -1894,11 +1918,11 @@ static int gdb_memory_map(struct connection *connection,
*/
free(banks);
- banks = NULL;
xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
if (retval != ERROR_OK) {
+ free(xml);
gdb_error(connection, retval);
return retval;
}
@@ -1919,6 +1943,8 @@ static int gdb_memory_map(struct connection *connection,
static const char *gdb_get_reg_type_name(enum reg_type type)
{
switch (type) {
+ case REG_TYPE_BOOL:
+ return "bool";
case REG_TYPE_INT:
return "int";
case REG_TYPE_INT8:
@@ -1931,6 +1957,8 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
return "int64";
case REG_TYPE_INT128:
return "int128";
+ case REG_TYPE_UINT:
+ return "uint";
case REG_TYPE_UINT8:
return "uint8";
case REG_TYPE_UINT16:
@@ -1958,12 +1986,45 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
return "int"; /* "int" as default value */
}
+static int lookup_add_arch_defined_types(char const **arch_defined_types_list[], const char *type_id,
+ int *num_arch_defined_types)
+{
+ int tbl_sz = *num_arch_defined_types;
+
+ if (type_id != NULL && (strcmp(type_id, ""))) {
+ for (int j = 0; j < (tbl_sz + 1); j++) {
+ if (!((*arch_defined_types_list)[j])) {
+ (*arch_defined_types_list)[tbl_sz++] = type_id;
+ *arch_defined_types_list = realloc(*arch_defined_types_list,
+ sizeof(char *) * (tbl_sz + 1));
+ (*arch_defined_types_list)[tbl_sz] = NULL;
+ *num_arch_defined_types = tbl_sz;
+ return 1;
+ } else {
+ if (!strcmp((*arch_defined_types_list)[j], type_id))
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
static int gdb_generate_reg_type_description(struct target *target,
- char **tdesc, int *pos, int *size, struct reg_data_type *type)
+ char **tdesc, int *pos, int *size, struct reg_data_type *type,
+ char const **arch_defined_types_list[], int * num_arch_defined_types)
{
int retval = ERROR_OK;
if (type->type_class == REG_TYPE_CLASS_VECTOR) {
+ struct reg_data_type *data_type = type->reg_type_vector->type;
+ if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+ if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+ num_arch_defined_types))
+ gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+ arch_defined_types_list,
+ num_arch_defined_types);
+ }
/* <vector id="id" type="type" count="count"/> */
xml_printf(&retval, tdesc, pos, size,
"<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n",
@@ -1971,6 +2032,20 @@ static int gdb_generate_reg_type_description(struct target *target,
type->reg_type_vector->count);
} else if (type->type_class == REG_TYPE_CLASS_UNION) {
+ struct reg_data_type_union_field *field;
+ field = type->reg_type_union->fields;
+ while (field != NULL) {
+ struct reg_data_type *data_type = field->type;
+ if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+ if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+ num_arch_defined_types))
+ gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+ arch_defined_types_list,
+ num_arch_defined_types);
+ }
+
+ field = field->next;
+ }
/* <union id="id">
* <field name="name" type="type"/> ...
* </union> */
@@ -1978,7 +2053,6 @@ static int gdb_generate_reg_type_description(struct target *target,
"<union id=\"%s\">\n",
type->id);
- struct reg_data_type_union_field *field;
field = type->reg_type_union->fields;
while (field != NULL) {
xml_printf(&retval, tdesc, pos, size,
@@ -2004,13 +2078,24 @@ static int gdb_generate_reg_type_description(struct target *target,
type->id, type->reg_type_struct->size);
while (field != NULL) {
xml_printf(&retval, tdesc, pos, size,
- "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
- field->name, field->bitfield->start,
- field->bitfield->end);
+ "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
+ field->name, field->bitfield->start, field->bitfield->end,
+ gdb_get_reg_type_name(field->bitfield->type));
field = field->next;
}
} else {
+ while (field != NULL) {
+ struct reg_data_type *data_type = field->type;
+ if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+ if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+ num_arch_defined_types))
+ gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+ arch_defined_types_list,
+ num_arch_defined_types);
+ }
+ }
+
/* <struct id="id">
* <field name="name" type="type"/> ...
* </struct> */
@@ -2041,8 +2126,9 @@ static int gdb_generate_reg_type_description(struct target *target,
field = type->reg_type_flags->fields;
while (field != NULL) {
xml_printf(&retval, tdesc, pos, size,
- "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
- field->name, field->bitfield->start, field->bitfield->end);
+ "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
+ field->name, field->bitfield->start, field->bitfield->end,
+ gdb_get_reg_type_name(field->bitfield->type));
field = field->next;
}
@@ -2103,11 +2189,15 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
struct reg **reg_list = NULL;
int reg_list_size;
char const **features = NULL;
+ char const **arch_defined_types = NULL;
int feature_list_size = 0;
+ int num_arch_defined_types = 0;
char *tdesc = NULL;
int pos = 0;
int size = 0;
+ arch_defined_types = calloc(1, sizeof(char *));
+
retval = target_get_gdb_reg_list(target, &reg_list,
&reg_list_size, REG_CLASS_ALL);
@@ -2160,8 +2250,13 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
if (reg_list[i]->reg_data_type != NULL) {
if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
/* generate <type... first, if there are architecture-defined types. */
- gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
- reg_list[i]->reg_data_type);
+ if (lookup_add_arch_defined_types(&arch_defined_types,
+ reg_list[i]->reg_data_type->id,
+ &num_arch_defined_types))
+ gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
+ reg_list[i]->reg_data_type,
+ &arch_defined_types,
+ &num_arch_defined_types);
type_str = reg_list[i]->reg_data_type->id;
} else {
@@ -2211,6 +2306,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
error:
free(features);
free(reg_list);
+ free(arch_defined_types);
if (retval == ERROR_OK)
*tdesc_out = tdesc;
@@ -2387,7 +2483,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
else
transfer_type = 'l';
- *chunk = malloc(length + 2);
+ *chunk = malloc(length + 2 + 3);
+ /* Allocating extra 3 bytes prevents false positive valgrind report
+ * of strlen(chunk) word access:
+ * Invalid read of size 4
+ * Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */
if (*chunk == NULL) {
LOG_ERROR("Unable to allocate memory");
return ERROR_FAIL;
@@ -2495,7 +2595,7 @@ static int gdb_query_packet(struct connection *connection,
&buffer,
&pos,
&size,
- "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+",
+ "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
(GDB_BUFFER_SIZE - 1),
((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
(gdb_target_desc_supported == 1) ? '+' : '-');
@@ -2584,6 +2684,187 @@ static int gdb_query_packet(struct connection *connection,
return ERROR_OK;
}
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ struct gdb_connection *gdb_connection = connection->priv;
+ struct target *target = get_target_from_connection(connection);
+ const char *parse = packet;
+ int retval;
+
+ /* query for vCont supported */
+ if (parse[0] == '?') {
+ if (target->type->step != NULL) {
+ /* gdb doesn't accept c without C and s without S */
+ gdb_put_packet(connection, "vCont;c;C;s;S", 13);
+ return true;
+ }
+ return false;
+ }
+
+ if (parse[0] == ';') {
+ ++parse;
+ --packet_size;
+ }
+
+ /* 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);
+ if (retval == ERROR_TARGET_NOT_HALTED)
+ LOG_INFO("target %s was not halted when resume was requested", target_name(target));
+
+ /* poll target in an attempt to make its internal state consistent */
+ if (retval != ERROR_OK) {
+ retval = target_poll(target);
+ if (retval != ERROR_OK)
+ LOG_DEBUG("error polling target %s after failed resume", target_name(target));
+ }
+
+ /*
+ * We don't report errors to gdb here, move frontend_state to
+ * TARGET_RUNNING to stay in sync with gdb's expectation of the
+ * target state
+ */
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+
+ return true;
+ }
+
+ /* single-step or step-over-breakpoint */
+ if (parse[0] == 's') {
+ gdb_running_type = 's';
+ bool fake_step = false;
+
+ if (strncmp(parse, "s:", 2) == 0) {
+ struct target *ct = target;
+ int current_pc = 1;
+ int64_t thread_id;
+ char *endp;
+
+ parse += 2;
+ packet_size -= 2;
+
+ thread_id = strtoll(parse, &endp, 16);
+ if (endp != NULL) {
+ packet_size -= endp - parse;
+ parse = endp;
+ }
+
+ if (target->rtos != NULL) {
+ /* FIXME: why is this necessary? rtos state should be up-to-date here already! */
+ rtos_update_threads(target);
+
+ target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
+
+ /*
+ * check if the thread to be stepped is the current rtos thread
+ * if not, we must fake the step
+ */
+ if (target->rtos->current_thread != thread_id)
+ fake_step = true;
+ }
+
+ if (parse[0] == ';') {
+ ++parse;
+ --packet_size;
+
+ if (parse[0] == 'c') {
+ parse += 1;
+ packet_size -= 1;
+
+ /* check if thread-id follows */
+ if (parse[0] == ':') {
+ int64_t tid;
+ parse += 1;
+ packet_size -= 1;
+
+ tid = strtoll(parse, &endp, 16);
+ if (tid == thread_id) {
+ /*
+ * Special case: only step a single thread (core),
+ * keep the other threads halted. Currently, only
+ * aarch64 target understands it. Other target types don't
+ * care (nobody checks the actual value of 'current')
+ * and it doesn't really matter. This deserves
+ * a symbolic constant and a formal interface documentation
+ * at a later time.
+ */
+ LOG_DEBUG("request to step current core only");
+ /* uncomment after checking that indeed other targets are safe */
+ /*current_pc = 2;*/
+ }
+ }
+ }
+ }
+
+ LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id);
+ log_add_callback(gdb_log_callback, connection);
+ target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
+
+ /*
+ * work around an annoying gdb behaviour: when the current thread
+ * is changed in gdb, it assumes that the target can follow and also
+ * make the thread current. This is an assumption that cannot hold
+ * for a real target running a multi-threading OS. We just fake
+ * the step to not trigger an internal error in gdb. See
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
+ */
+ if (fake_step) {
+ int sig_reply_len;
+ char sig_reply[128];
+
+ LOG_DEBUG("fake step thread %"PRIx64, thread_id);
+
+ sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
+ "T05thread:%016"PRIx64";", thread_id);
+
+ gdb_put_packet(connection, sig_reply, sig_reply_len);
+ log_remove_callback(gdb_log_callback, connection);
+
+ return true;
+ }
+
+ /* support for gdb_sync command */
+ if (gdb_connection->sync) {
+ gdb_connection->sync = false;
+ if (ct->state == TARGET_HALTED) {
+ LOG_WARNING("stepi ignored. GDB will now fetch the register state " \
+ "from the target.");
+ gdb_sig_halted(connection);
+ log_remove_callback(gdb_log_callback, connection);
+ } else
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ return true;
+ }
+
+ retval = target_step(ct, current_pc, 0, 0);
+ if (retval == ERROR_TARGET_NOT_HALTED)
+ LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+
+ /* if step was successful send a reply back to gdb */
+ if (retval == ERROR_OK) {
+ retval = target_poll(ct);
+ if (retval != ERROR_OK)
+ LOG_DEBUG("error polling target %s after successful step", target_name(ct));
+ /* send back signal information */
+ gdb_signal_reply(ct, connection);
+ /* stop forwarding log packets! */
+ log_remove_callback(gdb_log_callback, connection);
+ } else
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ } else {
+ LOG_ERROR("Unknown vCont packet");
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
static int gdb_v_packet(struct connection *connection,
char const *packet, int packet_size)
{
@@ -2597,6 +2878,19 @@ static int gdb_v_packet(struct connection *connection,
return out;
}
+ if (strncmp(packet, "vCont", 5) == 0) {
+ bool handled;
+
+ packet += 5;
+ packet_size -= 5;
+
+ handled = gdb_handle_vcont_packet(connection, packet, packet_size);
+ if (!handled)
+ gdb_put_packet(connection, "", 0);
+
+ return ERROR_OK;
+ }
+
/* if flash programming disabled - send a empty reply */
if (gdb_flash_program == 0) {
@@ -2728,9 +3022,12 @@ static int gdb_v_packet(struct connection *connection,
static int gdb_detach(struct connection *connection)
{
- target_call_event_callbacks(get_target_from_connection(connection),
- TARGET_EVENT_GDB_DETACH);
-
+ /*
+ * Only reply "OK" to GDB
+ * it will close the connection and this will trigger a call to
+ * gdb_connection_closed() that will in turn trigger the event
+ * TARGET_EVENT_GDB_DETACH
+ */
return gdb_put_packet(connection, "OK", 2);
}
@@ -2796,7 +3093,7 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
gdb_output_con(connection, string);
}
-void gdb_sig_halted(struct connection *connection)
+static void gdb_sig_halted(struct connection *connection)
{
char sig_reply[4];
snprintf(sig_reply, 4, "T%2.2x", 2);
@@ -3036,7 +3333,12 @@ static int gdb_input_inner(struct connection *connection)
if (gdb_con->ctrl_c) {
if (target->state == TARGET_RUNNING) {
- retval = target_halt(target);
+ struct target *t = target;
+ if (target->rtos)
+ target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
+ retval = target_halt(t);
+ if (retval == ERROR_OK)
+ retval = target_poll(t);
if (retval != ERROR_OK)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
gdb_con->ctrl_c = 0;
@@ -3209,6 +3511,15 @@ COMMAND_HANDLER(handle_gdb_report_data_abort_command)
return ERROR_OK;
}
+COMMAND_HANDLER(handle_gdb_report_register_access_error)
+{
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_ENABLE(CMD_ARGV[0], gdb_report_register_access_error);
+ return ERROR_OK;
+}
+
/* gdb_breakpoint_override */
COMMAND_HANDLER(handle_gdb_breakpoint_override_command)
{
@@ -3331,6 +3642,13 @@ static const struct command_registration gdb_command_handlers[] = {
.usage = "('enable'|'disable')"
},
{
+ .name = "gdb_report_register_access_error",
+ .handler = handle_gdb_report_register_access_error,
+ .mode = COMMAND_CONFIG,
+ .help = "enable or disable reporting register access errors",
+ .usage = "('enable'|'disable')"
+ },
+ {
.name = "gdb_breakpoint_override",
.handler = handle_gdb_breakpoint_override_command,
.mode = COMMAND_ANY,
@@ -3366,3 +3684,9 @@ void gdb_set_frontend_state_running(struct connection *connection)
struct gdb_connection *gdb_con = connection->priv;
gdb_con->frontend_state = TARGET_RUNNING;
}
+
+void gdb_service_free(void)
+{
+ free(gdb_port);
+ free(gdb_port_next);
+}
diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h
index 1dc30e0..0c50836 100644
--- a/src/server/gdb_server.h
+++ b/src/server/gdb_server.h
@@ -36,6 +36,7 @@ struct reg;
int gdb_target_add_all(struct target *target);
int gdb_register_commands(struct command_context *command_context);
+void gdb_service_free(void);
int gdb_put_packet(struct connection *connection, char *buffer, int len);
@@ -46,7 +47,6 @@ static inline struct target *get_target_from_connection(struct connection *conne
}
void gdb_set_frontend_state_running(struct connection *connection);
-void gdb_sig_halted(struct connection *connection);
#define ERROR_GDB_BUFFER_TOO_SMALL (-800)
#define ERROR_GDB_TIMEOUT (-801)
diff --git a/src/server/server.c b/src/server/server.c
index 46c860f..f8273d4 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -46,9 +46,13 @@
static struct service *services;
-/* shutdown_openocd == 1: exit the main event loop, and quit the
- * debugger; 2: quit with non-zero return code */
-static int shutdown_openocd;
+enum shutdown_reason {
+ CONTINUE_MAIN_LOOP, /* stay in main event loop */
+ SHUTDOWN_REQUESTED, /* set by shutdown command; exit the event loop and quit the debugger */
+ SHUTDOWN_WITH_ERROR_CODE, /* set by shutdown command; quit with non-zero return code */
+ SHUTDOWN_WITH_SIGNAL_CODE /* set by sig_handler; exec shutdown then exit with signal as return code */
+};
+static enum shutdown_reason shutdown_openocd = CONTINUE_MAIN_LOOP;
/* store received signal to exit application by killing ourselves */
static int last_signal;
@@ -259,7 +263,7 @@ int add_service(char *name,
c->sin.sin_family = AF_INET;
if (bindto_name == NULL)
- c->sin.sin_addr.s_addr = INADDR_ANY;
+ c->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else {
hp = gethostbyname(bindto_name);
if (hp == NULL) {
@@ -345,6 +349,50 @@ int add_service(char *name,
return ERROR_OK;
}
+static void remove_connections(struct service *service)
+{
+ struct connection *connection;
+
+ connection = service->connections;
+
+ while (connection) {
+ struct connection *tmp;
+
+ tmp = connection->next;
+ remove_connection(service, connection);
+ connection = tmp;
+ }
+}
+
+int remove_service(const char *name, const char *port)
+{
+ struct service *tmp;
+ struct service *prev;
+
+ prev = services;
+
+ for (tmp = services; tmp; prev = tmp, tmp = tmp->next) {
+ if (!strcmp(tmp->name, name) && !strcmp(tmp->port, port)) {
+ remove_connections(tmp);
+
+ if (tmp == services)
+ services = tmp->next;
+ else
+ prev->next = tmp->next;
+
+ if (tmp->type != CONNECTION_STDINOUT)
+ close_socket(tmp->fd);
+
+ free(tmp->priv);
+ free_service(tmp);
+
+ return ERROR_OK;
+ }
+ }
+
+ return ERROR_OK;
+}
+
static int remove_services(void)
{
struct service *c = services;
@@ -353,6 +401,8 @@ static int remove_services(void)
while (c) {
struct service *next = c->next;
+ remove_connections(c);
+
if (c->name)
free(c->name);
@@ -396,7 +446,7 @@ int server_loop(struct command_context *command_context)
LOG_ERROR("couldn't set SIGPIPE to SIG_IGN");
#endif
- while (!shutdown_openocd) {
+ while (shutdown_openocd == CONTINUE_MAIN_LOOP) {
/* monitor sockets for activity */
fd_max = 0;
FD_ZERO(&read_fds);
@@ -488,7 +538,7 @@ int server_loop(struct command_context *command_context)
for (service = services; service; service = service->next) {
/* handle new connections on listeners */
if ((service->fd != -1)
- && (FD_ISSET(service->fd, &read_fds))) {
+ && (FD_ISSET(service->fd, &read_fds))) {
if (service->max_connections != 0)
add_connection(service, command_context);
else {
@@ -520,7 +570,7 @@ int server_loop(struct command_context *command_context)
service->type == CONNECTION_STDINOUT) {
/* if connection uses a pipe then
* shutdown openocd on error */
- shutdown_openocd = 1;
+ shutdown_openocd = SHUTDOWN_REQUESTED;
}
remove_connection(service, c);
LOG_INFO("dropped '%s' connection",
@@ -538,29 +588,48 @@ int server_loop(struct command_context *command_context)
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
- shutdown_openocd = 1;
+ shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
}
#endif
}
- return shutdown_openocd != 2 ? ERROR_OK : ERROR_FAIL;
+ /* when quit for signal or CTRL-C, run (eventually user implemented) "shutdown" */
+ if (shutdown_openocd == SHUTDOWN_WITH_SIGNAL_CODE)
+ command_run_line(command_context, "shutdown");
+
+ return shutdown_openocd == SHUTDOWN_WITH_ERROR_CODE ? ERROR_FAIL : ERROR_OK;
}
+void sig_handler(int sig)
+{
+ /* store only first signal that hits us */
+ if (shutdown_openocd == CONTINUE_MAIN_LOOP) {
+ shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
+ last_signal = sig;
+ LOG_DEBUG("Terminating on Signal %d", sig);
+ } else
+ LOG_DEBUG("Ignored extra Signal %d", sig);
+}
+
+
#ifdef _WIN32
BOOL WINAPI ControlHandler(DWORD dwCtrlType)
{
- shutdown_openocd = 1;
+ shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
return TRUE;
}
-#endif
-
-void sig_handler(int sig)
+#else
+static void sigkey_handler(int sig)
{
- /* store only first signal that hits us */
- if (!last_signal)
- last_signal = sig;
- shutdown_openocd = 1;
+ /* ignore keystroke generated signals if not in foreground process group */
+
+ if (tcgetpgrp(STDIN_FILENO) > 0)
+ sig_handler(sig);
+ else
+ LOG_DEBUG("Ignored Signal %d", sig);
}
+#endif
+
int server_preinit(void)
{
@@ -583,8 +652,13 @@ int server_preinit(void)
SetConsoleCtrlHandler(ControlHandler, TRUE);
signal(SIGBREAK, sig_handler);
-#endif
signal(SIGINT, sig_handler);
+#else
+ signal(SIGHUP, sig_handler);
+ signal(SIGPIPE, sig_handler);
+ signal(SIGQUIT, sigkey_handler);
+ signal(SIGINT, sigkey_handler);
+#endif
signal(SIGTERM, sig_handler);
signal(SIGABRT, sig_handler);
@@ -624,6 +698,13 @@ int server_quit(void)
return last_signal;
}
+void server_free(void)
+{
+ tcl_service_free();
+ telnet_service_free();
+ jsp_service_free();
+}
+
void exit_on_signal(int sig)
{
#ifndef _WIN32
@@ -658,11 +739,11 @@ COMMAND_HANDLER(handle_shutdown_command)
{
LOG_USER("shutdown command invoked");
- shutdown_openocd = 1;
+ shutdown_openocd = SHUTDOWN_REQUESTED;
if (CMD_ARGC == 1) {
if (!strcmp(CMD_ARGV[0], "error")) {
- shutdown_openocd = 2;
+ shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
return ERROR_FAIL;
}
}
@@ -719,7 +800,7 @@ static const struct command_registration server_command_handlers[] = {
.mode = COMMAND_ANY,
.usage = "[name]",
.help = "Specify address by name on which to listen for "
- "incoming TCP/IP connections",
+ "incoming TCP/IP connections",
},
COMMAND_REGISTRATION_DONE
};
diff --git a/src/server/server.h b/src/server/server.h
index 68ad16d..96e0b48 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -25,6 +25,10 @@
#ifndef OPENOCD_SERVER_SERVER_H
#define OPENOCD_SERVER_SERVER_H
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <helper/log.h>
#ifdef HAVE_NETINET_IN_H
@@ -74,10 +78,12 @@ int add_service(char *name, const char *port,
int max_connections, new_connection_handler_t new_connection_handler,
input_handler_t in_handler, connection_closed_handler_t close_handler,
void *priv);
+int remove_service(const char *name, const char *port);
int server_preinit(void);
int server_init(struct command_context *cmd_ctx);
int server_quit(void);
+void server_free(void);
void exit_on_signal(int);
int server_loop(struct command_context *command_context);
diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c
index 0077339..3cb63a2 100644
--- a/src/server/tcl_server.c
+++ b/src/server/tcl_server.c
@@ -157,7 +157,7 @@ static int tcl_new_connection(struct connection *connection)
connection->priv = tclc;
- struct target *target = get_target_by_num(connection->cmd_ctx->current_target);
+ struct target *target = get_current_target(connection->cmd_ctx);
if (target != NULL)
tclc->tc_laststate = target->state;
@@ -359,3 +359,8 @@ int tcl_register_commands(struct command_context *cmd_ctx)
tcl_port = strdup("6666");
return register_commands(cmd_ctx, NULL, tcl_command_handlers);
}
+
+void tcl_service_free(void)
+{
+ free(tcl_port);
+}
diff --git a/src/server/tcl_server.h b/src/server/tcl_server.h
index 422c794..6ce3ab9 100644
--- a/src/server/tcl_server.h
+++ b/src/server/tcl_server.h
@@ -22,5 +22,6 @@
int tcl_init(void);
int tcl_register_commands(struct command_context *cmd_ctx);
+void tcl_service_free(void);
#endif /* OPENOCD_SERVER_TCL_SERVER_H */
diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c
index 9077b6c..a864f5f 100644
--- a/src/server/telnet_server.c
+++ b/src/server/telnet_server.c
@@ -719,3 +719,8 @@ int telnet_register_commands(struct command_context *cmd_ctx)
telnet_port = strdup("4444");
return register_commands(cmd_ctx, NULL, telnet_command_handlers);
}
+
+void telnet_service_free(void)
+{
+ free(telnet_port);
+}
diff --git a/src/server/telnet_server.h b/src/server/telnet_server.h
index 5e238f4..27148d7 100644
--- a/src/server/telnet_server.h
+++ b/src/server/telnet_server.h
@@ -64,5 +64,6 @@ struct telnet_service {
int telnet_init(char *banner);
int telnet_register_commands(struct command_context *command_context);
+void telnet_service_free(void);
#endif /* OPENOCD_SERVER_TELNET_SERVER_H */
diff --git a/src/svf/svf.c b/src/svf/svf.c
index 1d686ba..223af7e 100644
--- a/src/svf/svf.c
+++ b/src/svf/svf.c
@@ -741,6 +741,9 @@ parse_char:
pos++;
}
+ if (num == 0)
+ return ERROR_FAIL;
+
*num_of_argu = num;
return ERROR_OK;
@@ -1313,7 +1316,7 @@ XXR_common:
* SEC]] [ENDSTATE end_state] */
/* RUNTEST [run_state] min_time SEC [MAXIMUM max_time SEC] [ENDSTATE
* end_state] */
- if ((num_of_argu < 3) && (num_of_argu > 11)) {
+ if ((num_of_argu < 3) || (num_of_argu > 11)) {
LOG_ERROR("invalid parameter of %s", argus[0]);
return ERROR_FAIL;
}
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 9576b23..b1119e7 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -4,7 +4,9 @@ else
OOCD_TRACE_FILES =
endif
-%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la
+%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
+ %D%/riscv/libriscv.la
+
STARTUP_TCL_SRCS += %D%/startup.tcl
@@ -21,7 +23,6 @@ noinst_LTLIBRARIES += %D%/libtarget.la
$(NDS32_SRC) \
$(STM8_SRC) \
$(INTEL_IA32_SRC) \
- $(RISCV_SRC) \
%D%/avrt.c \
%D%/dsp563xx.c \
%D%/dsp563xx_once.c \
@@ -40,6 +41,7 @@ TARGET_CORE_SRC = \
%D%/target.c \
%D%/target_request.c \
%D%/testee.c \
+ %D%/semihosting_common.c \
%D%/smp.c
ARMV4_5_SRC = \
@@ -89,6 +91,7 @@ ARM_DEBUG_SRC = \
%D%/arm_simulator.c \
%D%/arm_semihosting.c \
%D%/arm_adi_v5.c \
+ %D%/arm_dap.c \
%D%/armv7a_cache.c \
%D%/armv7a_cache_l2x.c \
%D%/adi_v5_jtag.c \
@@ -135,13 +138,6 @@ INTEL_IA32_SRC = \
%D%/lakemont.c \
%D%/x86_32_common.c
-RISCV_SRC = \
- %D%/riscv/riscv-011.c \
- %D%/riscv/riscv-013.c \
- %D%/riscv/riscv.c \
- %D%/riscv/program.c \
- %D%/riscv/batch.c
-
%C%_libtarget_la_SOURCES += \
%D%/algorithm.h \
%D%/arm.h \
@@ -217,9 +213,11 @@ 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 \
%D%/arm_cti.h
include %D%/openrisc/Makefile.am
+include %D%/riscv/Makefile.am
diff --git a/src/target/aarch64.c b/src/target/aarch64.c
index 14a2da6..454de9e 100644
--- a/src/target/aarch64.c
+++ b/src/target/aarch64.c
@@ -28,6 +28,7 @@
#include "target_type.h"
#include "armv8_opcodes.h"
#include "armv8_cache.h"
+#include "arm_semihosting.h"
#include <helper/time_support.h>
enum restart_mode {
@@ -40,6 +41,11 @@ enum halt_mode {
HALT_SYNC,
};
+struct aarch64_private_config {
+ struct adiv5_private_config adiv5_config;
+ struct arm_cti *cti;
+};
+
static int aarch64_poll(struct target *target);
static int aarch64_debug_entry(struct target *target);
static int aarch64_restore_context(struct target *target, bool bpwp);
@@ -452,7 +458,7 @@ static int update_halt_gdb(struct target *target, enum target_debug_reason debug
struct target *curr;
if (debug_reason == DBG_REASON_NOTHALTED) {
- LOG_INFO("Halting remaining targets in SMP group");
+ LOG_DEBUG("Halting remaining targets in SMP group");
aarch64_halt_smp(target, true);
}
@@ -517,6 +523,9 @@ static int aarch64_poll(struct target *target)
if (target->smp)
update_halt_gdb(target, debug_reason);
+ if (arm_semihosting(target, &retval) != 0)
+ return retval;
+
switch (prev_target_state) {
case TARGET_RUNNING:
case TARGET_UNKNOWN:
@@ -538,6 +547,9 @@ static int aarch64_poll(struct target *target)
static int aarch64_halt(struct target *target)
{
+ struct armv8_common *armv8 = target_to_armv8(target);
+ armv8->last_run_control_op = ARMV8_RUNCONTROL_HALT;
+
if (target->smp)
return aarch64_halt_smp(target, false);
@@ -826,6 +838,9 @@ static int aarch64_resume(struct target *target, int current,
int retval = 0;
uint64_t addr = address;
+ struct armv8_common *armv8 = target_to_armv8(target);
+ armv8->last_run_control_op = ARMV8_RUNCONTROL_RESUME;
+
if (target->state != TARGET_HALTED)
return ERROR_TARGET_NOT_HALTED;
@@ -1064,6 +1079,8 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres
int retval;
uint32_t edecr;
+ armv8->last_run_control_op = ARMV8_RUNCONTROL_STEP;
+
if (target->state != TARGET_HALTED) {
LOG_WARNING("target not halted");
return ERROR_TARGET_NOT_HALTED;
@@ -1086,7 +1103,7 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres
if (retval != ERROR_OK)
return retval;
- if (target->smp && !handle_breakpoints) {
+ if (target->smp && (current == 1)) {
/*
* isolate current target so that it doesn't get resumed
* together with the others
@@ -1677,17 +1694,19 @@ static int aarch64_deassert_reset(struct target *target)
if (retval != ERROR_OK)
return retval;
+ retval = aarch64_init_debug_access(target);
+ if (retval != ERROR_OK)
+ return retval;
+
if (target->reset_halt) {
if (target->state != TARGET_HALTED) {
LOG_WARNING("%s: ran after reset and before halt ...",
target_name(target));
retval = target_halt(target);
- if (retval != ERROR_OK)
- return retval;
}
}
- return aarch64_init_debug_access(target);
+ return retval;
}
static int aarch64_write_cpu_memory_slow(struct target *target,
@@ -1861,7 +1880,7 @@ static int aarch64_write_cpu_memory(struct target *target,
if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) {
/* Abort occurred - clear it and exit */
LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr);
- armv8_dpm_handle_exception(dpm);
+ armv8_dpm_handle_exception(dpm, true);
return ERROR_FAIL;
}
@@ -2080,7 +2099,7 @@ static int aarch64_read_cpu_memory(struct target *target,
if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) {
/* Abort occurred - clear it and exit */
LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr);
- armv8_dpm_handle_exception(dpm);
+ armv8_dpm_handle_exception(dpm, true);
return ERROR_FAIL;
}
@@ -2198,7 +2217,7 @@ static int aarch64_examine_first(struct target *target)
struct aarch64_common *aarch64 = target_to_aarch64(target);
struct armv8_common *armv8 = &aarch64->armv8_common;
struct adiv5_dap *swjdp = armv8->arm.dap;
- uint32_t cti_base;
+ struct aarch64_private_config *pc;
int i;
int retval = ERROR_OK;
uint64_t debug, ttypr;
@@ -2206,10 +2225,6 @@ static int aarch64_examine_first(struct target *target)
uint32_t tmp0, tmp1, tmp2, tmp3;
debug = ttypr = cpuid = 0;
- retval = dap_dp_init(swjdp);
- if (retval != ERROR_OK)
- return retval;
-
/* Search for the APB-AB - it is needed for access to debug registers */
retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv8->debug_ap);
if (retval != ERROR_OK) {
@@ -2289,17 +2304,15 @@ static int aarch64_examine_first(struct target *target)
LOG_DEBUG("ttypr = 0x%08" PRIx64, ttypr);
LOG_DEBUG("debug = 0x%08" PRIx64, debug);
- if (target->ctibase == 0) {
- /* assume a v8 rom table layout */
- cti_base = armv8->debug_base + 0x10000;
- LOG_INFO("Target ctibase is not set, assuming 0x%0" PRIx32, cti_base);
- } else
- cti_base = target->ctibase;
+ if (target->private_config == NULL)
+ return ERROR_FAIL;
- armv8->cti = arm_cti_create(armv8->debug_ap, cti_base);
- if (armv8->cti == NULL)
+ pc = (struct aarch64_private_config *)target->private_config;
+ if (pc->cti == NULL)
return ERROR_FAIL;
+ armv8->cti = pc->cti;
+
retval = aarch64_dpm_setup(aarch64, debug);
if (retval != ERROR_OK)
return retval;
@@ -2352,22 +2365,18 @@ static int aarch64_init_target(struct command_context *cmd_ctx,
struct target *target)
{
/* examine_first() does a bunch of this */
+ arm_semihosting_init(target);
return ERROR_OK;
}
static int aarch64_init_arch_info(struct target *target,
- struct aarch64_common *aarch64, struct jtag_tap *tap)
+ struct aarch64_common *aarch64, struct adiv5_dap *dap)
{
struct armv8_common *armv8 = &aarch64->armv8_common;
/* Setup struct aarch64_common */
aarch64->common_magic = AARCH64_COMMON_MAGIC;
- /* tap has no dap initialized */
- if (!tap->dap) {
- tap->dap = dap_init();
- tap->dap->tap = tap;
- }
- armv8->arm.dap = tap->dap;
+ armv8->arm.dap = dap;
/* register arch-specific functions */
armv8->examine_debug_reason = NULL;
@@ -2383,9 +2392,27 @@ static int aarch64_init_arch_info(struct target *target,
static int aarch64_target_create(struct target *target, Jim_Interp *interp)
{
+ struct aarch64_private_config *pc = target->private_config;
struct aarch64_common *aarch64 = calloc(1, sizeof(struct aarch64_common));
- return aarch64_init_arch_info(target, aarch64, target->tap);
+ if (adiv5_verify_config(&pc->adiv5_config) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return aarch64_init_arch_info(target, aarch64, pc->adiv5_config.dap);
+}
+
+static void aarch64_deinit_target(struct target *target)
+{
+ struct aarch64_common *aarch64 = target_to_aarch64(target);
+ struct armv8_common *armv8 = &aarch64->armv8_common;
+ struct arm_dpm *dpm = &armv8->dpm;
+
+ armv8_free_reg_cache(target);
+ free(aarch64->brp_list);
+ free(dpm->dbp);
+ free(dpm->dwp);
+ free(target->private_config);
+ free(aarch64);
}
static int aarch64_mmu(struct target *target, int *enabled)
@@ -2405,6 +2432,94 @@ static int aarch64_virt2phys(struct target *target, target_addr_t virt,
return armv8_mmu_translate_va_pa(target, virt, phys, 1);
}
+/*
+ * private target configuration items
+ */
+enum aarch64_cfg_param {
+ CFG_CTI,
+};
+
+static const Jim_Nvp nvp_config_opts[] = {
+ { .name = "-cti", .value = CFG_CTI },
+ { .name = NULL, .value = -1 }
+};
+
+static int aarch64_jim_configure(struct target *target, Jim_GetOptInfo *goi)
+{
+ struct aarch64_private_config *pc;
+ Jim_Nvp *n;
+ int e;
+
+ pc = (struct aarch64_private_config *)target->private_config;
+ if (pc == NULL) {
+ pc = calloc(1, sizeof(struct aarch64_private_config));
+ target->private_config = pc;
+ }
+
+ /*
+ * Call adiv5_jim_configure() to parse the common DAP options
+ * It will return JIM_CONTINUE if it didn't find any known
+ * options, JIM_OK if it correctly parsed the topmost option
+ * and JIM_ERR if an error occured during parameter evaluation.
+ * For JIM_CONTINUE, we check our own params.
+ */
+ e = adiv5_jim_configure(target, goi);
+ if (e != JIM_CONTINUE)
+ return e;
+
+ /* parse config or cget options ... */
+ if (goi->argc > 0) {
+ Jim_SetEmptyResult(goi->interp);
+
+ /* check first if topmost item is for us */
+ e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts,
+ goi->argv[0], &n);
+ if (e != JIM_OK)
+ return JIM_CONTINUE;
+
+ e = Jim_GetOpt_Obj(goi, NULL);
+ if (e != JIM_OK)
+ return e;
+
+ switch (n->value) {
+ case CFG_CTI: {
+ if (goi->isconfigure) {
+ Jim_Obj *o_cti;
+ struct arm_cti *cti;
+ e = Jim_GetOpt_Obj(goi, &o_cti);
+ if (e != JIM_OK)
+ return e;
+ cti = cti_instance_by_jim_obj(goi->interp, o_cti);
+ if (cti == NULL) {
+ Jim_SetResultString(goi->interp, "CTI name invalid!", -1);
+ return JIM_ERR;
+ }
+ pc->cti = cti;
+ } else {
+ if (goi->argc != 0) {
+ Jim_WrongNumArgs(goi->interp,
+ goi->argc, goi->argv,
+ "NO PARAMS");
+ return JIM_ERR;
+ }
+
+ if (pc == NULL || pc->cti == NULL) {
+ Jim_SetResultString(goi->interp, "CTI not configured", -1);
+ return JIM_ERR;
+ }
+ Jim_SetResultString(goi->interp, arm_cti_name(pc->cti), -1);
+ }
+ break;
+ }
+
+ default:
+ return JIM_CONTINUE;
+ }
+ }
+
+ return JIM_OK;
+}
+
COMMAND_HANDLER(aarch64_handle_cache_info_command)
{
struct target *target = get_current_target(CMD_CTX);
@@ -2490,6 +2605,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",
@@ -2525,9 +2777,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,
@@ -2570,7 +2838,9 @@ struct target_type aarch64_target = {
.commands = aarch64_command_handlers,
.target_create = aarch64_target_create,
+ .target_jim_configure = aarch64_jim_configure,
.init_target = aarch64_init_target,
+ .deinit_target = aarch64_deinit_target,
.examine = aarch64_examine,
.read_phys_memory = aarch64_read_phys_memory,
diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c
index c7dc4f7..8c20611 100644
--- a/src/target/adi_v5_jtag.c
+++ b/src/target/adi_v5_jtag.c
@@ -553,7 +553,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap)
static int jtagdp_transaction_endcheck(struct adiv5_dap *dap)
{
int retval;
- uint32_t ctrlstat;
+ uint32_t ctrlstat, pwrmask;
/* too expensive to call keep_alive() here */
@@ -571,9 +571,12 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap)
if (ctrlstat & SSTICKYERR) {
LOG_DEBUG("jtag-dp: CTRL/STAT 0x%" PRIx32, ctrlstat);
/* Check power to debug regions */
- if ((ctrlstat & (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) !=
- (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) {
+ pwrmask = CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ;
+ if (!dap->ignore_syspwrupack)
+ pwrmask |= CSYSPWRUPACK;
+ if ((ctrlstat & pwrmask) != pwrmask) {
LOG_ERROR("Debug regions are unpowered, an unexpected reset might have happened");
+ dap->do_reconnect = true;
}
if (ctrlstat & SSTICKYERR)
@@ -598,6 +601,20 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap)
/*--------------------------------------------------------------------------*/
+static int jtag_connect(struct adiv5_dap *dap)
+{
+ dap->do_reconnect = false;
+ return dap_dp_init(dap);
+}
+
+static int jtag_check_reconnect(struct adiv5_dap *dap)
+{
+ if (dap->do_reconnect)
+ return jtag_connect(dap);
+
+ return ERROR_OK;
+}
+
static int jtag_dp_q_read(struct adiv5_dap *dap, unsigned reg,
uint32_t *data)
{
@@ -633,7 +650,11 @@ static int jtag_ap_q_bankselect(struct adiv5_ap *ap, unsigned reg)
static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg,
uint32_t *data)
{
- int retval = jtag_ap_q_bankselect(ap, reg);
+ int retval = jtag_check_reconnect(ap->dap);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = jtag_ap_q_bankselect(ap, reg);
if (retval != ERROR_OK)
return retval;
@@ -647,7 +668,11 @@ static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg,
static int jtag_ap_q_write(struct adiv5_ap *ap, unsigned reg,
uint32_t data)
{
- int retval = jtag_ap_q_bankselect(ap, reg);
+ int retval = jtag_check_reconnect(ap->dap);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = jtag_ap_q_bankselect(ap, reg);
if (retval != ERROR_OK)
return retval;
@@ -692,6 +717,7 @@ static int jtag_dp_sync(struct adiv5_dap *dap)
* part of DAP setup
*/
const struct dap_ops jtag_dp_ops = {
+ .connect = jtag_connect,
.queue_dp_read = jtag_dp_q_read,
.queue_dp_write = jtag_dp_q_write,
.queue_ap_read = jtag_ap_q_read,
diff --git a/src/target/adi_v5_swd.c b/src/target/adi_v5_swd.c
index c503f09..b520223 100644
--- a/src/target/adi_v5_swd.c
+++ b/src/target/adi_v5_swd.c
@@ -53,13 +53,11 @@
#include <jtag/swd.h>
-/* YUK! - but this is currently a global.... */
-extern struct jtag_interface *jtag_interface;
static bool do_sync;
static void swd_finish_read(struct adiv5_dap *dap)
{
- const struct swd_driver *swd = jtag_interface->swd;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
if (dap->last_read != NULL) {
swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0);
dap->last_read = NULL;
@@ -73,7 +71,7 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
static void swd_clear_sticky_errors(struct adiv5_dap *dap)
{
- const struct swd_driver *swd = jtag_interface->swd;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd);
swd->write_reg(swd_cmd(false, false, DP_ABORT),
@@ -82,7 +80,7 @@ static void swd_clear_sticky_errors(struct adiv5_dap *dap)
static int swd_run_inner(struct adiv5_dap *dap)
{
- const struct swd_driver *swd = jtag_interface->swd;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
int retval;
retval = swd->run();
@@ -97,6 +95,7 @@ static int swd_run_inner(struct adiv5_dap *dap)
static int swd_connect(struct adiv5_dap *dap)
{
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
uint32_t dpidr;
int status;
@@ -120,7 +119,7 @@ static int swd_connect(struct adiv5_dap *dap)
}
/* Note, debugport_init() does setup too */
- jtag_interface->swd->switch_seq(JTAG_TO_SWD);
+ swd->switch_seq(JTAG_TO_SWD);
/* Clear link state, including the SELECT cache. */
dap->do_reconnect = false;
@@ -136,6 +135,7 @@ static int swd_connect(struct adiv5_dap *dap)
if (status == ERROR_OK) {
LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr);
dap->do_reconnect = false;
+ status = dap_dp_init(dap);
} else
dap->do_reconnect = true;
@@ -157,7 +157,7 @@ static int swd_check_reconnect(struct adiv5_dap *dap)
static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
{
- const struct swd_driver *swd = jtag_interface->swd;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd);
swd->write_reg(swd_cmd(false, false, DP_ABORT),
@@ -187,7 +187,7 @@ static void swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned reg)
static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
uint32_t *data)
{
- const struct swd_driver *swd = jtag_interface->swd;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd);
int retval = swd_check_reconnect(dap);
@@ -203,7 +203,7 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
uint32_t data)
{
- const struct swd_driver *swd = jtag_interface->swd;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
assert(swd);
int retval = swd_check_reconnect(dap);
@@ -236,10 +236,9 @@ static void swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg)
static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
uint32_t *data)
{
- const struct swd_driver *swd = jtag_interface->swd;
- assert(swd);
-
struct adiv5_dap *dap = ap->dap;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
+ assert(swd);
int retval = swd_check_reconnect(dap);
if (retval != ERROR_OK)
@@ -255,10 +254,9 @@ static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
uint32_t data)
{
- const struct swd_driver *swd = jtag_interface->swd;
- assert(swd);
-
struct adiv5_dap *dap = ap->dap;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
+ assert(swd);
int retval = swd_check_reconnect(dap);
if (retval != ERROR_OK)
@@ -278,13 +276,25 @@ static int swd_run(struct adiv5_dap *dap)
return swd_run_inner(dap);
}
+/** Put the SWJ-DP back to JTAG mode */
+static void swd_quit(struct adiv5_dap *dap)
+{
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
+
+ swd->switch_seq(SWD_TO_JTAG);
+ /* flush the queue before exit */
+ swd->run();
+}
+
const struct dap_ops swd_dap_ops = {
+ .connect = swd_connect,
.queue_dp_read = swd_queue_dp_read,
.queue_dp_write = swd_queue_dp_write,
.queue_ap_read = swd_queue_ap_read,
.queue_ap_write = swd_queue_ap_write,
.queue_ap_abort = swd_queue_ap_abort,
.run = swd_run,
+ .quit = swd_quit,
};
/*
@@ -381,15 +391,15 @@ static const struct command_registration swd_handlers[] = {
static int swd_select(struct command_context *ctx)
{
+ /* FIXME: only place where global 'jtag_interface' is still needed */
+ extern struct jtag_interface *jtag_interface;
+ const struct swd_driver *swd = jtag_interface->swd;
int retval;
retval = register_commands(ctx, NULL, swd_handlers);
-
if (retval != ERROR_OK)
return retval;
- const struct swd_driver *swd = jtag_interface->swd;
-
/* be sure driver is in SWD mode; start
* with hardware default TRN (1), it can be changed later
*/
@@ -404,33 +414,14 @@ static int swd_select(struct command_context *ctx)
return retval;
}
- /* force DAP into SWD mode (not JTAG) */
- /*retval = dap_to_swd(target);*/
-
- if (ctx->current_target) {
- /* force DAP into SWD mode (not JTAG) */
- struct target *target = get_current_target(ctx);
- retval = dap_to_swd(target);
- }
-
return retval;
}
static int swd_init(struct command_context *ctx)
{
- struct target *target = get_current_target(ctx);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
- /* Force the DAP's ops vector for SWD mode.
- * messy - is there a better way? */
- arm->dap->ops = &swd_dap_ops;
- /* First connect after init is not reconnecting. */
- dap->do_reconnect = false;
-
- int retval = swd_connect(dap);
- if (retval != ERROR_OK)
- LOG_ERROR("SWD connect failed");
- return retval;
+ /* nothing done here, SWD is initialized
+ * together with the DAP */
+ return ERROR_OK;
}
static struct transport swd_transport = {
diff --git a/src/target/arm.h b/src/target/arm.h
index eb4a51f..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.
@@ -77,6 +79,43 @@ enum arm_mode {
ARM_MODE_ANY = -1
};
+/* VFPv3 internal register numbers mapping to d0:31 */
+enum {
+ ARM_VFP_V3_D0 = 51,
+ ARM_VFP_V3_D1,
+ ARM_VFP_V3_D2,
+ ARM_VFP_V3_D3,
+ ARM_VFP_V3_D4,
+ ARM_VFP_V3_D5,
+ ARM_VFP_V3_D6,
+ ARM_VFP_V3_D7,
+ ARM_VFP_V3_D8,
+ ARM_VFP_V3_D9,
+ ARM_VFP_V3_D10,
+ ARM_VFP_V3_D11,
+ ARM_VFP_V3_D12,
+ ARM_VFP_V3_D13,
+ ARM_VFP_V3_D14,
+ ARM_VFP_V3_D15,
+ ARM_VFP_V3_D16,
+ ARM_VFP_V3_D17,
+ ARM_VFP_V3_D18,
+ ARM_VFP_V3_D19,
+ ARM_VFP_V3_D20,
+ ARM_VFP_V3_D21,
+ ARM_VFP_V3_D22,
+ ARM_VFP_V3_D23,
+ ARM_VFP_V3_D24,
+ ARM_VFP_V3_D25,
+ ARM_VFP_V3_D26,
+ ARM_VFP_V3_D27,
+ ARM_VFP_V3_D28,
+ ARM_VFP_V3_D29,
+ ARM_VFP_V3_D30,
+ ARM_VFP_V3_D31,
+ ARM_VFP_V3_FPSCR,
+};
+
const char *arm_mode_name(unsigned psr_mode);
bool is_arm_mode(unsigned psr_mode);
@@ -89,6 +128,14 @@ enum arm_state {
ARM_STATE_AARCH64,
};
+/** ARM vector floating point enabled, if yes which version. */
+enum arm_vfp_version {
+ ARM_VFP_DISABLED,
+ ARM_VFP_V1,
+ ARM_VFP_V2,
+ ARM_VFP_V3,
+};
+
#define ARM_COMMON_MAGIC 0x0A450A45
/**
@@ -136,29 +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;
-
- /** Current semihosting operation. */
- int semihosting_op;
-
- /** Current semihosting result. */
- int semihosting_result;
-
- /** Value to be returned by semihosting SYS_ERRNO request. */
- int semihosting_errno;
+ /** Floating point or VFP version, 0 if disabled. */
+ int arm_vfp_version;
int (*setup_semihosting)(struct target *target, int enable);
- /** Semihosting command line. */
- char *semihosting_cmdline;
-
/** Backpointer to the target. */
struct target *target;
@@ -225,7 +254,7 @@ struct arm_reg {
enum arm_mode mode;
struct target *target;
struct arm *arm;
- uint8_t value[8];
+ uint8_t value[16];
};
struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm);
@@ -260,7 +289,7 @@ int armv4_5_run_algorithm_inner(struct target *target,
int arm_checksum_memory(struct target *target,
target_addr_t address, uint32_t count, uint32_t *checksum);
int arm_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value);
+ struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value);
void arm_set_cpsr(struct arm *arm, uint32_t cpsr);
struct reg *arm_reg_current(struct arm *arm, unsigned regnum);
diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c
index dfbc5ad..302ea78 100644
--- a/src/target/arm_adi_v5.c
+++ b/src/target/arm_adi_v5.c
@@ -76,6 +76,7 @@
#include <helper/jep106.h>
#include <helper/time_support.h>
#include <helper/list.h>
+#include <helper/jim-nvp.h>
/* ARM ADI Specification requires at least 10 bits used for TAR autoincrement */
@@ -96,14 +97,15 @@ static uint32_t max_tar_block_size(uint32_t tar_autoincr_block, uint32_t address
static int mem_ap_setup_csw(struct adiv5_ap *ap, uint32_t csw)
{
- csw = csw | CSW_DBGSWENABLE | CSW_MASTER_DEBUG | CSW_HPROT |
- ap->csw_default;
+ csw |= ap->csw_default;
if (csw != ap->csw_value) {
/* LOG_DEBUG("DAP: Set CSW %x",csw); */
int retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ ap->csw_value = 0;
return retval;
+ }
ap->csw_value = csw;
}
return ERROR_OK;
@@ -114,8 +116,10 @@ static int mem_ap_setup_tar(struct adiv5_ap *ap, uint32_t tar)
if (!ap->tar_valid || tar != ap->tar_value) {
/* LOG_DEBUG("DAP: Set TAR %x",tar); */
int retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR, tar);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ ap->tar_valid = false;
return retval;
+ }
ap->tar_value = tar;
ap->tar_valid = true;
}
@@ -152,6 +156,8 @@ static uint32_t mem_ap_get_tar_increment(struct adiv5_ap *ap)
return 2;
case CSW_32BIT:
return 4;
+ default:
+ return 0;
}
case CSW_ADDRINC_PACKED:
return 4;
@@ -614,34 +620,9 @@ int mem_ap_write_buf_noincr(struct adiv5_ap *ap,
#define DAP_POWER_DOMAIN_TIMEOUT (10)
-/* FIXME don't import ... just initialize as
- * part of DAP transport setup
-*/
-extern const struct dap_ops jtag_dp_ops;
-
/*--------------------------------------------------------------------------*/
/**
- * Create a new DAP
- */
-struct adiv5_dap *dap_init(void)
-{
- struct adiv5_dap *dap = calloc(1, sizeof(struct adiv5_dap));
- int i;
- /* Set up with safe defaults */
- for (i = 0; i <= 255; i++) {
- dap->ap[i].dap = dap;
- dap->ap[i].ap_num = i;
- /* memaccess_tck max is 255 */
- dap->ap[i].memaccess_tck = 255;
- /* Number of bits for tar autoincrement, impl. dep. at least 10 */
- dap->ap[i].tar_autoincr_block = (1<<10);
- }
- INIT_LIST_HEAD(&dap->cmd_journal);
- return dap;
-}
-
-/**
* Invalidate cached DP select and cached TAR and CSW of all APs
*/
void dap_invalidate_cache(struct adiv5_dap *dap)
@@ -667,14 +648,7 @@ int dap_dp_init(struct adiv5_dap *dap)
{
int retval;
- LOG_DEBUG(" ");
- /* JTAG-DP or SWJ-DP, in JTAG mode
- * ... for SWD mode this is patched as part
- * of link switchover
- * FIXME: This should already be setup by the respective transport specific DAP creation.
- */
- if (!dap->ops)
- dap->ops = &jtag_dp_ops;
+ LOG_DEBUG("%s", adiv5_dap_name(dap));
dap_invalidate_cache(dap);
@@ -707,12 +681,14 @@ int dap_dp_init(struct adiv5_dap *dap)
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("DAP: wait CSYSPWRUPACK");
- retval = dap_dp_poll_register(dap, DP_CTRL_STAT,
- CSYSPWRUPACK, CSYSPWRUPACK,
- DAP_POWER_DOMAIN_TIMEOUT);
- if (retval != ERROR_OK)
- return retval;
+ if (!dap->ignore_syspwrupack) {
+ LOG_DEBUG("DAP: wait CSYSPWRUPACK");
+ retval = dap_dp_poll_register(dap, DP_CTRL_STAT,
+ CSYSPWRUPACK, CSYSPWRUPACK,
+ DAP_POWER_DOMAIN_TIMEOUT);
+ if (retval != ERROR_OK)
+ return retval;
+ }
retval = dap_queue_dp_read(dap, DP_CTRL_STAT, NULL);
if (retval != ERROR_OK)
@@ -1376,7 +1352,7 @@ static int dap_rom_display(struct command_context *cmd_ctx,
return ERROR_OK;
}
-static int dap_info_command(struct command_context *cmd_ctx,
+int dap_info_command(struct command_context *cmd_ctx,
struct adiv5_ap *ap)
{
int retval;
@@ -1434,46 +1410,131 @@ static int dap_info_command(struct command_context *cmd_ctx,
return ERROR_OK;
}
+enum adiv5_cfg_param {
+ CFG_DAP,
+ CFG_AP_NUM
+};
+
+static const Jim_Nvp nvp_config_opts[] = {
+ { .name = "-dap", .value = CFG_DAP },
+ { .name = "-ap-num", .value = CFG_AP_NUM },
+ { .name = NULL, .value = -1 }
+};
+
int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi)
{
struct adiv5_private_config *pc;
- const char *arg;
- jim_wide ap_num;
int e;
- /* check if argv[0] is for us */
- arg = Jim_GetString(goi->argv[0], NULL);
- if (strcmp(arg, "-ap-num"))
- return JIM_CONTINUE;
+ pc = (struct adiv5_private_config *)target->private_config;
+ if (pc == NULL) {
+ pc = calloc(1, sizeof(struct adiv5_private_config));
+ pc->ap_num = -1;
+ target->private_config = pc;
+ }
- e = Jim_GetOpt_String(goi, &arg, NULL);
- if (e != JIM_OK)
- return e;
+ target->has_dap = true;
+
+ if (goi->argc > 0) {
+ Jim_Nvp *n;
+
+ Jim_SetEmptyResult(goi->interp);
+
+ /* check first if topmost item is for us */
+ e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts,
+ goi->argv[0], &n);
+ if (e != JIM_OK)
+ return JIM_CONTINUE;
+
+ e = Jim_GetOpt_Obj(goi, NULL);
+ if (e != JIM_OK)
+ return e;
+
+ switch (n->value) {
+ case CFG_DAP:
+ if (goi->isconfigure) {
+ Jim_Obj *o_t;
+ struct adiv5_dap *dap;
+ e = Jim_GetOpt_Obj(goi, &o_t);
+ if (e != JIM_OK)
+ return e;
+ dap = dap_instance_by_jim_obj(goi->interp, o_t);
+ if (dap == NULL) {
+ Jim_SetResultString(goi->interp, "DAP name invalid!", -1);
+ return JIM_ERR;
+ }
+ if (pc->dap != NULL && pc->dap != dap) {
+ Jim_SetResultString(goi->interp,
+ "DAP assignment cannot be changed after target was created!", -1);
+ return JIM_ERR;
+ }
+ if (target->tap_configured) {
+ Jim_SetResultString(goi->interp,
+ "-chain-position and -dap configparams are mutually exclusive!", -1);
+ return JIM_ERR;
+ }
+ pc->dap = dap;
+ target->tap = dap->tap;
+ target->dap_configured = true;
+ } else {
+ if (goi->argc != 0) {
+ Jim_WrongNumArgs(goi->interp,
+ goi->argc, goi->argv,
+ "NO PARAMS");
+ return JIM_ERR;
+ }
+
+ if (pc->dap == NULL) {
+ Jim_SetResultString(goi->interp, "DAP not configured", -1);
+ return JIM_ERR;
+ }
+ Jim_SetResultString(goi->interp, adiv5_dap_name(pc->dap), -1);
+ }
+ break;
- if (goi->argc == 0) {
- Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-ap-num ?ap-number? ...");
- return JIM_ERR;
+ case CFG_AP_NUM:
+ if (goi->isconfigure) {
+ jim_wide ap_num;
+ e = Jim_GetOpt_Wide(goi, &ap_num);
+ if (e != JIM_OK)
+ return e;
+ pc->ap_num = ap_num;
+ } else {
+ if (goi->argc != 0) {
+ Jim_WrongNumArgs(goi->interp,
+ goi->argc, goi->argv,
+ "NO PARAMS");
+ return JIM_ERR;
+ }
+
+ if (pc->ap_num < 0) {
+ Jim_SetResultString(goi->interp, "AP number not configured", -1);
+ return JIM_ERR;
+ }
+ Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, (int)pc->ap_num));
+ }
+ break;
+ }
}
- e = Jim_GetOpt_Wide(goi, &ap_num);
- if (e != JIM_OK)
- return e;
+ return JIM_OK;
+}
- if (target->private_config == NULL) {
- pc = calloc(1, sizeof(struct adiv5_private_config));
- target->private_config = pc;
- pc->ap_num = ap_num;
- }
+int adiv5_verify_config(struct adiv5_private_config *pc)
+{
+ if (pc == NULL)
+ return ERROR_FAIL;
+ if (pc->dap == NULL)
+ return ERROR_FAIL;
- return JIM_OK;
+ return ERROR_OK;
}
+
COMMAND_HANDLER(handle_dap_info_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t apsel;
switch (CMD_ARGC) {
@@ -1494,10 +1555,7 @@ COMMAND_HANDLER(handle_dap_info_command)
COMMAND_HANDLER(dap_baseaddr_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
-
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t apsel, baseaddr;
int retval;
@@ -1534,10 +1592,7 @@ COMMAND_HANDLER(dap_baseaddr_command)
COMMAND_HANDLER(dap_memaccess_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
-
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t memaccess_tck;
switch (CMD_ARGC) {
@@ -1560,17 +1615,13 @@ COMMAND_HANDLER(dap_memaccess_command)
COMMAND_HANDLER(dap_apsel_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
-
- uint32_t apsel, apid;
- int retval;
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
+ uint32_t apsel;
switch (CMD_ARGC) {
case 0:
- apsel = dap->apsel;
- break;
+ command_print(CMD_CTX, "%" PRIi32, dap->apsel);
+ return ERROR_OK;
case 1:
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
/* AP address is in bits 31:24 of DP_SELECT */
@@ -1582,42 +1633,40 @@ COMMAND_HANDLER(dap_apsel_command)
}
dap->apsel = apsel;
-
- retval = dap_queue_ap_read(dap_ap(dap, apsel), AP_REG_IDR, &apid);
- if (retval != ERROR_OK)
- return retval;
- retval = dap_run(dap);
- if (retval != ERROR_OK)
- return retval;
-
- command_print(CMD_CTX, "ap %" PRIi32 " selected, identification register 0x%8.8" PRIx32,
- apsel, apid);
-
- return retval;
+ return ERROR_OK;
}
COMMAND_HANDLER(dap_apcsw_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
-
- uint32_t apcsw = dap->ap[dap->apsel].csw_default, sprot = 0;
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
+ uint32_t apcsw = dap->ap[dap->apsel].csw_default;
+ uint32_t csw_val, csw_mask;
switch (CMD_ARGC) {
case 0:
- command_print(CMD_CTX, "apsel %" PRIi32 " selected, csw 0x%8.8" PRIx32,
- (dap->apsel), apcsw);
- break;
+ command_print(CMD_CTX, "ap %" PRIi32 " selected, csw 0x%8.8" PRIx32,
+ dap->apsel, apcsw);
+ return ERROR_OK;
case 1:
- COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], sprot);
- /* AP address is in bits 31:24 of DP_SELECT */
- if (sprot > 1)
- return ERROR_COMMAND_SYNTAX_ERROR;
- if (sprot)
- apcsw |= CSW_SPROT;
+ if (strcmp(CMD_ARGV[0], "default") == 0)
+ csw_val = CSW_DEFAULT;
else
- apcsw &= ~CSW_SPROT;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], csw_val);
+
+ if (csw_val & (CSW_SIZE_MASK | CSW_ADDRINC_MASK)) {
+ LOG_ERROR("CSW value cannot include 'Size' and 'AddrInc' bit-fields");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ apcsw = csw_val;
+ break;
+ case 2:
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], csw_val);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], csw_mask);
+ if (csw_mask & (CSW_SIZE_MASK | CSW_ADDRINC_MASK)) {
+ LOG_ERROR("CSW mask cannot include 'Size' and 'AddrInc' bit-fields");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ apcsw = (apcsw & ~csw_mask) | (csw_val & csw_mask);
break;
default:
return ERROR_COMMAND_SYNTAX_ERROR;
@@ -1631,10 +1680,7 @@ COMMAND_HANDLER(dap_apcsw_command)
COMMAND_HANDLER(dap_apid_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
-
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t apsel, apid;
int retval;
@@ -1666,11 +1712,9 @@ COMMAND_HANDLER(dap_apid_command)
COMMAND_HANDLER(dap_apreg_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
-
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t apsel, reg, value;
+ struct adiv5_ap *ap;
int retval;
if (CMD_ARGC < 2 || CMD_ARGC > 3)
@@ -1680,6 +1724,7 @@ COMMAND_HANDLER(dap_apreg_command)
/* AP address is in bits 31:24 of DP_SELECT */
if (apsel >= 256)
return ERROR_COMMAND_SYNTAX_ERROR;
+ ap = dap_ap(dap, apsel);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg);
if (reg >= 256 || (reg & 3))
@@ -1687,9 +1732,21 @@ COMMAND_HANDLER(dap_apreg_command)
if (CMD_ARGC == 3) {
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
- retval = dap_queue_ap_write(dap_ap(dap, apsel), reg, value);
+ switch (reg) {
+ case MEM_AP_REG_CSW:
+ ap->csw_default = 0; /* invalid, force write */
+ retval = mem_ap_setup_csw(ap, value);
+ break;
+ case MEM_AP_REG_TAR:
+ ap->tar_valid = false; /* invalid, force write */
+ retval = mem_ap_setup_tar(ap, value);
+ break;
+ default:
+ retval = dap_queue_ap_write(ap, reg, value);
+ break;
+ }
} else {
- retval = dap_queue_ap_read(dap_ap(dap, apsel), reg, &value);
+ retval = dap_queue_ap_read(ap, reg, &value);
}
if (retval == ERROR_OK)
retval = dap_run(dap);
@@ -1703,12 +1760,40 @@ COMMAND_HANDLER(dap_apreg_command)
return retval;
}
-COMMAND_HANDLER(dap_ti_be_32_quirks_command)
+COMMAND_HANDLER(dap_dpreg_command)
{
- struct target *target = get_current_target(CMD_CTX);
- struct arm *arm = target_to_arm(target);
- struct adiv5_dap *dap = arm->dap;
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
+ uint32_t reg, value;
+ int retval;
+ if (CMD_ARGC < 1 || CMD_ARGC > 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], reg);
+ if (reg >= 256 || (reg & 3))
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 2) {
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+ retval = dap_queue_dp_write(dap, reg, value);
+ } else {
+ retval = dap_queue_dp_read(dap, reg, &value);
+ }
+ if (retval == ERROR_OK)
+ retval = dap_run(dap);
+
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (CMD_ARGC == 1)
+ command_print(CMD_CTX, "0x%08" PRIx32, value);
+
+ return retval;
+}
+
+COMMAND_HANDLER(dap_ti_be_32_quirks_command)
+{
+ struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t enable = dap->ti_be_32_quirks;
switch (CMD_ARGC) {
@@ -1729,7 +1814,7 @@ COMMAND_HANDLER(dap_ti_be_32_quirks_command)
return 0;
}
-static const struct command_registration dap_commands[] = {
+const struct command_registration dap_instance_commands[] = {
{
.name = "info",
.handler = handle_dap_info_command,
@@ -1741,7 +1826,7 @@ static const struct command_registration dap_commands[] = {
{
.name = "apsel",
.handler = dap_apsel_command,
- .mode = COMMAND_EXEC,
+ .mode = COMMAND_ANY,
.help = "Set the currently selected AP (default 0) "
"and display the result",
.usage = "[ap_num]",
@@ -1749,9 +1834,9 @@ static const struct command_registration dap_commands[] = {
{
.name = "apcsw",
.handler = dap_apcsw_command,
- .mode = COMMAND_EXEC,
- .help = "Set csw access bit ",
- .usage = "[sprot]",
+ .mode = COMMAND_ANY,
+ .help = "Set CSW default bits",
+ .usage = "[value [mask]]",
},
{
@@ -1771,6 +1856,14 @@ static const struct command_registration dap_commands[] = {
.usage = "ap_num reg [value]",
},
{
+ .name = "dpreg",
+ .handler = dap_dpreg_command,
+ .mode = COMMAND_EXEC,
+ .help = "read/write a register from DP "
+ "(reg is byte address (bank << 4 | reg) of a word register, like 0 4 8...)",
+ .usage = "reg [value]",
+ },
+ {
.name = "baseaddr",
.handler = dap_baseaddr_command,
.mode = COMMAND_EXEC,
@@ -1795,14 +1888,3 @@ static const struct command_registration dap_commands[] = {
},
COMMAND_REGISTRATION_DONE
};
-
-const struct command_registration dap_command_handlers[] = {
- {
- .name = "dap",
- .mode = COMMAND_EXEC,
- .help = "DAP command group",
- .usage = "",
- .chain = dap_commands,
- },
- COMMAND_REGISTRATION_DONE
-};
diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h
index 657427b..883ac8b 100644
--- a/src/target/arm_adi_v5.h
+++ b/src/target/arm_adi_v5.h
@@ -112,13 +112,16 @@
#define CSW_ADDRINC_PACKED (2UL << 4)
#define CSW_DEVICE_EN (1UL << 6)
#define CSW_TRIN_PROG (1UL << 7)
+/* all fields in bits 12 and above are implementation-defined! */
#define CSW_SPIDEN (1UL << 23)
-/* 30:24 - implementation-defined! */
-#define CSW_HPROT (1UL << 25) /* ? */
-#define CSW_MASTER_DEBUG (1UL << 29) /* ? */
+#define CSW_HPROT1 (1UL << 25) /* AHB: Privileged */
+#define CSW_MASTER_DEBUG (1UL << 29) /* AHB: set HMASTER signals to AHB-AP ID */
#define CSW_SPROT (1UL << 30)
#define CSW_DBGSWENABLE (1UL << 31)
+/* initial value of csw_default used for MEM-AP transfers */
+#define CSW_DEFAULT (CSW_HPROT1 | CSW_MASTER_DEBUG | CSW_DBGSWENABLE)
+
/* Fields of the MEM-AP's IDR register */
#define IDR_REV (0xFUL << 28)
#define IDR_JEP106 (0x7FFUL << 17)
@@ -244,6 +247,10 @@ struct adiv5_dap {
* should be performed before the next access.
*/
bool do_reconnect;
+
+ /** Flag saying whether to ignore the syspwrupack flag in DAP. Some devices
+ * do not set this bit until later in the bringup sequence */
+ bool ignore_syspwrupack;
};
/**
@@ -254,6 +261,8 @@ struct adiv5_dap {
* available until run().
*/
struct dap_ops {
+ /** connect operation for SWD */
+ int (*connect)(struct adiv5_dap *dap);
/** DP register read. */
int (*queue_dp_read)(struct adiv5_dap *dap, unsigned reg,
uint32_t *data);
@@ -277,6 +286,9 @@ struct dap_ops {
/** Executes all queued DAP operations but doesn't check
* sticky error conditions */
int (*sync)(struct adiv5_dap *dap);
+
+ /** Optional; called at OpenOCD exit */
+ void (*quit)(struct adiv5_dap *dap);
};
/*
@@ -473,9 +485,6 @@ int mem_ap_read_buf_noincr(struct adiv5_ap *ap,
int mem_ap_write_buf_noincr(struct adiv5_ap *ap,
const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address);
-/* Create DAP struct */
-struct adiv5_dap *dap_init(void);
-
/* Initialisation of the debug system, power domains and registers */
int dap_dp_init(struct adiv5_dap *dap);
int mem_ap_init(struct adiv5_ap *ap);
@@ -509,12 +518,24 @@ int dap_to_swd(struct target *target);
/* Put debug link into JTAG mode */
int dap_to_jtag(struct target *target);
-extern const struct command_registration dap_command_handlers[];
+extern const struct command_registration dap_instance_commands[];
+
+struct arm_dap_object;
+extern struct adiv5_dap *dap_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o);
+extern struct adiv5_dap *adiv5_get_dap(struct arm_dap_object *obj);
+extern int dap_info_command(struct command_context *cmd_ctx,
+ struct adiv5_ap *ap);
+extern int dap_register_commands(struct command_context *cmd_ctx);
+extern const char *adiv5_dap_name(struct adiv5_dap *self);
+extern const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self);
+extern int dap_cleanup_all(void);
struct adiv5_private_config {
int ap_num;
+ struct adiv5_dap *dap;
};
+extern int adiv5_verify_config(struct adiv5_private_config *pc);
extern int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi);
#endif /* OPENOCD_TARGET_ARM_ADI_V5_H */
diff --git a/src/target/arm_cti.c b/src/target/arm_cti.c
index 75169b2..0d117e7 100644
--- a/src/target/arm_cti.c
+++ b/src/target/arm_cti.c
@@ -27,21 +27,47 @@
#include "target/arm_cti.h"
#include "target/target.h"
#include "helper/time_support.h"
+#include "helper/list.h"
+#include "helper/command.h"
struct arm_cti {
- uint32_t base;
+ target_addr_t base;
struct adiv5_ap *ap;
};
-struct arm_cti *arm_cti_create(struct adiv5_ap *ap, uint32_t base)
+struct arm_cti_object {
+ struct list_head lh;
+ struct arm_cti cti;
+ int ap_num;
+ char *name;
+};
+
+static LIST_HEAD(all_cti);
+
+const char *arm_cti_name(struct arm_cti *self)
+{
+ struct arm_cti_object *obj = container_of(self, struct arm_cti_object, cti);
+ return obj->name;
+}
+
+struct arm_cti *cti_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o)
{
- struct arm_cti *self = calloc(1, sizeof(struct arm_cti));
- if (!self)
- return NULL;
+ struct arm_cti_object *obj = NULL;
+ const char *name;
+ bool found = false;
- self->base = base;
- self->ap = ap;
- return self;
+ name = Jim_GetString(o, NULL);
+
+ list_for_each_entry(obj, &all_cti, lh) {
+ if (!strcmp(name, obj->name)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ return &obj->cti;
+ return NULL;
}
static int arm_cti_mod_reg_bits(struct arm_cti *self, unsigned int reg, uint32_t mask, uint32_t value)
@@ -146,3 +172,408 @@ int arm_cti_clear_channel(struct arm_cti *self, uint32_t channel)
return arm_cti_write_reg(self, CTI_APPCLEAR, CTI_CHNL(channel));
}
+
+static uint32_t cti_regs[26];
+
+static const struct {
+ uint32_t offset;
+ const char *label;
+ uint32_t *p_val;
+} cti_names[] = {
+ { CTI_CTR, "CTR", &cti_regs[0] },
+ { CTI_GATE, "GATE", &cti_regs[1] },
+ { CTI_INEN0, "INEN0", &cti_regs[2] },
+ { CTI_INEN1, "INEN1", &cti_regs[3] },
+ { CTI_INEN2, "INEN2", &cti_regs[4] },
+ { CTI_INEN3, "INEN3", &cti_regs[5] },
+ { CTI_INEN4, "INEN4", &cti_regs[6] },
+ { CTI_INEN5, "INEN5", &cti_regs[7] },
+ { CTI_INEN6, "INEN6", &cti_regs[8] },
+ { CTI_INEN7, "INEN7", &cti_regs[9] },
+ { CTI_INEN8, "INEN8", &cti_regs[10] },
+ { CTI_OUTEN0, "OUTEN0", &cti_regs[11] },
+ { CTI_OUTEN1, "OUTEN1", &cti_regs[12] },
+ { CTI_OUTEN2, "OUTEN2", &cti_regs[13] },
+ { CTI_OUTEN3, "OUTEN3", &cti_regs[14] },
+ { CTI_OUTEN4, "OUTEN4", &cti_regs[15] },
+ { CTI_OUTEN5, "OUTEN5", &cti_regs[16] },
+ { CTI_OUTEN6, "OUTEN6", &cti_regs[17] },
+ { CTI_OUTEN7, "OUTEN7", &cti_regs[18] },
+ { CTI_OUTEN8, "OUTEN8", &cti_regs[19] },
+ { CTI_TRIN_STATUS, "TRIN", &cti_regs[20] },
+ { CTI_TROUT_STATUS, "TROUT", &cti_regs[21] },
+ { CTI_CHIN_STATUS, "CHIN", &cti_regs[22] },
+ { CTI_CHOU_STATUS, "CHOUT", &cti_regs[23] },
+ { CTI_APPSET, "APPSET", &cti_regs[24] },
+ { CTI_APPCLEAR, "APPCLR", &cti_regs[25] },
+};
+
+static int cti_find_reg_offset(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cti_names); i++) {
+ if (!strcmp(name, cti_names[i].label))
+ return cti_names[i].offset;
+ }
+ return -1;
+}
+
+int arm_cti_cleanup_all(void)
+{
+ struct arm_cti_object *obj, *tmp;
+
+ list_for_each_entry_safe(obj, tmp, &all_cti, lh) {
+ free(obj->name);
+ free(obj);
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_cti_dump)
+{
+ struct arm_cti_object *obj = CMD_DATA;
+ struct arm_cti *cti = &obj->cti;
+ int retval = ERROR_OK;
+
+ for (int i = 0; (retval == ERROR_OK) && (i < (int)ARRAY_SIZE(cti_names)); i++)
+ retval = mem_ap_read_u32(cti->ap,
+ cti->base + cti_names[i].offset, cti_names[i].p_val);
+
+ if (retval == ERROR_OK)
+ retval = dap_run(cti->ap->dap);
+
+ if (retval != ERROR_OK)
+ return JIM_ERR;
+
+ for (int i = 0; i < (int)ARRAY_SIZE(cti_names); i++)
+ command_print(CMD_CTX, "%8.8s (0x%04"PRIx32") 0x%08"PRIx32,
+ cti_names[i].label, cti_names[i].offset, *cti_names[i].p_val);
+
+ return JIM_OK;
+}
+
+COMMAND_HANDLER(handle_cti_enable)
+{
+ struct arm_cti_object *obj = CMD_DATA;
+ Jim_Interp *interp = CMD_CTX->interp;
+ struct arm_cti *cti = &obj->cti;
+ bool on_off;
+
+ if (CMD_ARGC != 1) {
+ Jim_SetResultString(interp, "wrong number of args", -1);
+ return ERROR_FAIL;
+ }
+
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], on_off);
+
+ return arm_cti_enable(cti, on_off);
+}
+
+COMMAND_HANDLER(handle_cti_testmode)
+{
+ struct arm_cti_object *obj = CMD_DATA;
+ Jim_Interp *interp = CMD_CTX->interp;
+ struct arm_cti *cti = &obj->cti;
+ bool on_off;
+
+ if (CMD_ARGC != 1) {
+ Jim_SetResultString(interp, "wrong number of args", -1);
+ return ERROR_FAIL;
+ }
+
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], on_off);
+
+ return arm_cti_write_reg(cti, 0xf00, on_off ? 0x1 : 0x0);
+}
+
+COMMAND_HANDLER(handle_cti_write)
+{
+ struct arm_cti_object *obj = CMD_DATA;
+ Jim_Interp *interp = CMD_CTX->interp;
+ struct arm_cti *cti = &obj->cti;
+ int offset;
+ uint32_t value;
+
+ if (CMD_ARGC != 2) {
+ Jim_SetResultString(interp, "Wrong numer of args", -1);
+ return ERROR_FAIL;
+ }
+
+ offset = cti_find_reg_offset(CMD_ARGV[0]);
+ if (offset < 0)
+ return ERROR_FAIL;
+
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+
+ return arm_cti_write_reg(cti, offset, value);
+}
+
+COMMAND_HANDLER(handle_cti_read)
+{
+ struct arm_cti_object *obj = CMD_DATA;
+ Jim_Interp *interp = CMD_CTX->interp;
+ struct arm_cti *cti = &obj->cti;
+ int offset;
+ int retval;
+ uint32_t value;
+
+ if (CMD_ARGC != 1) {
+ Jim_SetResultString(interp, "Wrong numer of args", -1);
+ return ERROR_FAIL;
+ }
+
+ offset = cti_find_reg_offset(CMD_ARGV[0]);
+ if (offset < 0)
+ return ERROR_FAIL;
+
+ retval = arm_cti_read_reg(cti, offset, &value);
+ if (retval != ERROR_OK)
+ return retval;
+
+ command_print(CMD_CTX, "0x%08"PRIx32, value);
+
+ return ERROR_OK;
+}
+
+static const struct command_registration cti_instance_command_handlers[] = {
+ {
+ .name = "dump",
+ .mode = COMMAND_EXEC,
+ .handler = handle_cti_dump,
+ .help = "dump CTI registers",
+ .usage = "",
+ },
+ {
+ .name = "enable",
+ .mode = COMMAND_EXEC,
+ .handler = handle_cti_enable,
+ .help = "enable or disable the CTI",
+ .usage = "'on'|'off'",
+ },
+ {
+ .name = "testmode",
+ .mode = COMMAND_EXEC,
+ .handler = handle_cti_testmode,
+ .help = "enable or disable integration test mode",
+ .usage = "'on'|'off'",
+ },
+ {
+ .name = "write",
+ .mode = COMMAND_EXEC,
+ .handler = handle_cti_write,
+ .help = "write to a CTI register",
+ .usage = "register_name value",
+ },
+ {
+ .name = "read",
+ .mode = COMMAND_EXEC,
+ .handler = handle_cti_read,
+ .help = "read a CTI register",
+ .usage = "register_name",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+enum cti_cfg_param {
+ CFG_DAP,
+ CFG_AP_NUM,
+ CFG_CTIBASE
+};
+
+static const Jim_Nvp nvp_config_opts[] = {
+ { .name = "-dap", .value = CFG_DAP },
+ { .name = "-ctibase", .value = CFG_CTIBASE },
+ { .name = "-ap-num", .value = CFG_AP_NUM },
+ { .name = NULL, .value = -1 }
+};
+
+static int cti_configure(Jim_GetOptInfo *goi, struct arm_cti_object *cti)
+{
+ struct adiv5_dap *dap = NULL;
+ Jim_Nvp *n;
+ jim_wide w;
+ int e;
+
+ /* parse config or cget options ... */
+ while (goi->argc > 0) {
+ Jim_SetEmptyResult(goi->interp);
+
+ e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0);
+ return e;
+ }
+ switch (n->value) {
+ case CFG_DAP: {
+ Jim_Obj *o_t;
+ e = Jim_GetOpt_Obj(goi, &o_t);
+ if (e != JIM_OK)
+ return e;
+ dap = dap_instance_by_jim_obj(goi->interp, o_t);
+ if (dap == NULL) {
+ Jim_SetResultString(goi->interp, "-dap is invalid", -1);
+ return JIM_ERR;
+ }
+ /* loop for more */
+ break;
+ }
+ case CFG_CTIBASE:
+ e = Jim_GetOpt_Wide(goi, &w);
+ if (e != JIM_OK)
+ return e;
+ cti->cti.base = (uint32_t)w;
+ /* loop for more */
+ break;
+
+ case CFG_AP_NUM:
+ e = Jim_GetOpt_Wide(goi, &w);
+ if (e != JIM_OK)
+ return e;
+ cti->ap_num = (uint32_t)w;
+ }
+ }
+
+ if (dap == NULL) {
+ Jim_SetResultString(goi->interp, "-dap required when creating CTI", -1);
+ return JIM_ERR;
+ }
+
+ cti->cti.ap = dap_ap(dap, cti->ap_num);
+
+ return JIM_OK;
+}
+
+static int cti_create(Jim_GetOptInfo *goi)
+{
+ struct command_context *cmd_ctx;
+ static struct arm_cti_object *cti;
+ Jim_Obj *new_cmd;
+ Jim_Cmd *cmd;
+ const char *cp;
+ int e;
+
+ cmd_ctx = current_command_context(goi->interp);
+ assert(cmd_ctx != NULL);
+
+ if (goi->argc < 3) {
+ Jim_WrongNumArgs(goi->interp, 1, goi->argv, "?name? ..options...");
+ return JIM_ERR;
+ }
+ /* COMMAND */
+ Jim_GetOpt_Obj(goi, &new_cmd);
+ /* does this command exist? */
+ cmd = Jim_GetCommand(goi->interp, new_cmd, JIM_ERRMSG);
+ if (cmd) {
+ cp = Jim_GetString(new_cmd, NULL);
+ Jim_SetResultFormatted(goi->interp, "Command: %s Exists", cp);
+ return JIM_ERR;
+ }
+
+ /* Create it */
+ cti = calloc(1, sizeof(struct arm_cti_object));
+ if (cti == NULL)
+ return JIM_ERR;
+
+ e = cti_configure(goi, cti);
+ if (e != JIM_OK) {
+ free(cti);
+ return e;
+ }
+
+ cp = Jim_GetString(new_cmd, NULL);
+ cti->name = strdup(cp);
+
+ /* now - create the new cti name command */
+ const struct command_registration cti_subcommands[] = {
+ {
+ .chain = cti_instance_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+ };
+ const struct command_registration cti_commands[] = {
+ {
+ .name = cp,
+ .mode = COMMAND_ANY,
+ .help = "cti instance command group",
+ .usage = "",
+ .chain = cti_subcommands,
+ },
+ COMMAND_REGISTRATION_DONE
+ };
+ e = register_commands(cmd_ctx, NULL, cti_commands);
+ if (ERROR_OK != e)
+ return JIM_ERR;
+
+ struct command *c = command_find_in_context(cmd_ctx, cp);
+ assert(c);
+ command_set_handler_data(c, cti);
+
+ list_add_tail(&cti->lh, &all_cti);
+
+ return (ERROR_OK == e) ? JIM_OK : JIM_ERR;
+}
+
+static int jim_cti_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ Jim_GetOptInfo goi;
+ Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+ if (goi.argc < 2) {
+ Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
+ "<name> [<cti_options> ...]");
+ return JIM_ERR;
+ }
+ return cti_create(&goi);
+}
+
+static int jim_cti_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ struct arm_cti_object *obj;
+
+ if (argc != 1) {
+ Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
+ return JIM_ERR;
+ }
+ Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
+ list_for_each_entry(obj, &all_cti, lh) {
+ Jim_ListAppendElement(interp, Jim_GetResult(interp),
+ Jim_NewStringObj(interp, obj->name, -1));
+ }
+ return JIM_OK;
+}
+
+
+static const struct command_registration cti_subcommand_handlers[] = {
+ {
+ .name = "create",
+ .mode = COMMAND_ANY,
+ .jim_handler = jim_cti_create,
+ .usage = "name '-chain-position' name [options ...]",
+ .help = "Creates a new CTI object",
+ },
+ {
+ .name = "names",
+ .mode = COMMAND_ANY,
+ .jim_handler = jim_cti_names,
+ .usage = "",
+ .help = "Lists all registered CTI objects by name",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration cti_command_handlers[] = {
+ {
+ .name = "cti",
+ .mode = COMMAND_CONFIG,
+ .help = "CTI commands",
+ .chain = cti_subcommand_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+int cti_register_commands(struct command_context *cmd_ctx)
+{
+ return register_commands(cmd_ctx, NULL, cti_command_handlers);
+}
+
diff --git a/src/target/arm_cti.h b/src/target/arm_cti.h
index 99724c4..7c4f7eb 100644
--- a/src/target/arm_cti.h
+++ b/src/target/arm_cti.h
@@ -34,6 +34,7 @@
#define CTI_INEN5 0x34
#define CTI_INEN6 0x38
#define CTI_INEN7 0x3C
+#define CTI_INEN8 0x40
#define CTI_INEN(n) (0x20 + 4 * n)
#define CTI_OUTEN0 0xA0
#define CTI_OUTEN1 0xA4
@@ -43,6 +44,7 @@
#define CTI_OUTEN5 0xB4
#define CTI_OUTEN6 0xB8
#define CTI_OUTEN7 0xBC
+#define CTI_OUTEN8 0xC0
#define CTI_OUTEN(n) (0xA0 + 4 * n)
#define CTI_TRIN_STATUS 0x130
#define CTI_TROUT_STATUS 0x134
@@ -58,8 +60,10 @@
/* forward-declare arm_cti struct */
struct arm_cti;
+struct adiv5_ap;
-extern struct arm_cti *arm_cti_create(struct adiv5_ap *ap, uint32_t base);
+extern const char *arm_cti_name(struct arm_cti *self);
+extern struct arm_cti *cti_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o);
extern int arm_cti_enable(struct arm_cti *self, bool enable);
extern int arm_cti_ack_events(struct arm_cti *self, uint32_t event);
extern int arm_cti_gate_channel(struct arm_cti *self, uint32_t channel);
@@ -69,5 +73,7 @@ extern int arm_cti_read_reg(struct arm_cti *self, unsigned int reg, uint32_t *va
extern int arm_cti_pulse_channel(struct arm_cti *self, uint32_t channel);
extern int arm_cti_set_channel(struct arm_cti *self, uint32_t channel);
extern int arm_cti_clear_channel(struct arm_cti *self, uint32_t channel);
+extern int arm_cti_cleanup_all(void);
+extern int cti_register_commands(struct command_context *cmd_ctx);
#endif /* OPENOCD_TARGET_ARM_CTI_H */
diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c
new file mode 100644
index 0000000..3be4d71
--- /dev/null
+++ b/src/target/arm_dap.c
@@ -0,0 +1,378 @@
+/***************************************************************************
+ * Copyright (C) 2016 by Matthias Welwarsky *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "target/arm_adi_v5.h"
+#include "target/arm.h"
+#include "helper/list.h"
+#include "helper/command.h"
+#include "transport/transport.h"
+#include "jtag/interface.h"
+
+static LIST_HEAD(all_dap);
+
+extern const struct dap_ops swd_dap_ops;
+extern const struct dap_ops jtag_dp_ops;
+extern struct jtag_interface *jtag_interface;
+
+/* DAP command support */
+struct arm_dap_object {
+ struct list_head lh;
+ struct adiv5_dap dap;
+ char *name;
+ const struct swd_driver *swd;
+};
+
+static void dap_instance_init(struct adiv5_dap *dap)
+{
+ int i;
+ /* Set up with safe defaults */
+ for (i = 0; i <= 255; i++) {
+ dap->ap[i].dap = dap;
+ dap->ap[i].ap_num = i;
+ /* memaccess_tck max is 255 */
+ dap->ap[i].memaccess_tck = 255;
+ /* Number of bits for tar autoincrement, impl. dep. at least 10 */
+ dap->ap[i].tar_autoincr_block = (1<<10);
+ /* default CSW value */
+ dap->ap[i].csw_default = CSW_DEFAULT;
+ }
+ INIT_LIST_HEAD(&dap->cmd_journal);
+}
+
+const char *adiv5_dap_name(struct adiv5_dap *self)
+{
+ struct arm_dap_object *obj = container_of(self, struct arm_dap_object, dap);
+ return obj->name;
+}
+
+const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self)
+{
+ struct arm_dap_object *obj = container_of(self, struct arm_dap_object, dap);
+ return obj->swd;
+}
+
+struct adiv5_dap *adiv5_get_dap(struct arm_dap_object *obj)
+{
+ return &obj->dap;
+}
+struct adiv5_dap *dap_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o)
+{
+ struct arm_dap_object *obj = NULL;
+ const char *name;
+ bool found = false;
+
+ name = Jim_GetString(o, NULL);
+
+ list_for_each_entry(obj, &all_dap, lh) {
+ if (!strcmp(name, obj->name)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ return &obj->dap;
+ return NULL;
+}
+
+static int dap_init_all(void)
+{
+ struct arm_dap_object *obj;
+ int retval;
+
+ LOG_DEBUG("Initializing all DAPs ...");
+
+ list_for_each_entry(obj, &all_dap, lh) {
+ struct adiv5_dap *dap = &obj->dap;
+
+ /* with hla, dap is just a dummy */
+ if (transport_is_hla())
+ continue;
+
+ /* skip taps that are disabled */
+ if (!dap->tap->enabled)
+ continue;
+
+ if (transport_is_swd()) {
+ dap->ops = &swd_dap_ops;
+ obj->swd = jtag_interface->swd;
+ } else
+ dap->ops = &jtag_dp_ops;
+
+ retval = dap->ops->connect(dap);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+int dap_cleanup_all(void)
+{
+ struct arm_dap_object *obj, *tmp;
+ struct adiv5_dap *dap;
+
+ list_for_each_entry_safe(obj, tmp, &all_dap, lh) {
+ dap = &obj->dap;
+ if (dap->ops && dap->ops->quit)
+ dap->ops->quit(dap);
+
+ free(obj->name);
+ free(obj);
+ }
+
+ return ERROR_OK;
+}
+
+enum dap_cfg_param {
+ CFG_CHAIN_POSITION,
+ CFG_IGNORE_SYSPWRUPACK,
+};
+
+static const Jim_Nvp nvp_config_opts[] = {
+ { .name = "-chain-position", .value = CFG_CHAIN_POSITION },
+ { .name = "-ignore-syspwrupack", .value = CFG_IGNORE_SYSPWRUPACK },
+ { .name = NULL, .value = -1 }
+};
+
+static int dap_configure(Jim_GetOptInfo *goi, struct arm_dap_object *dap)
+{
+ struct jtag_tap *tap = NULL;
+ Jim_Nvp *n;
+ int e;
+
+ /* parse config or cget options ... */
+ while (goi->argc > 0) {
+ Jim_SetEmptyResult(goi->interp);
+
+ e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0);
+ return e;
+ }
+ switch (n->value) {
+ case CFG_CHAIN_POSITION: {
+ Jim_Obj *o_t;
+ e = Jim_GetOpt_Obj(goi, &o_t);
+ if (e != JIM_OK)
+ return e;
+ tap = jtag_tap_by_jim_obj(goi->interp, o_t);
+ if (tap == NULL) {
+ Jim_SetResultString(goi->interp, "-chain-position is invalid", -1);
+ return JIM_ERR;
+ }
+ /* loop for more */
+ break;
+ }
+ case CFG_IGNORE_SYSPWRUPACK:
+ dap->dap.ignore_syspwrupack = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (tap == NULL) {
+ Jim_SetResultString(goi->interp, "-chain-position required when creating DAP", -1);
+ return JIM_ERR;
+ }
+
+ dap_instance_init(&dap->dap);
+ dap->dap.tap = tap;
+
+ return JIM_OK;
+}
+
+static int dap_create(Jim_GetOptInfo *goi)
+{
+ struct command_context *cmd_ctx;
+ static struct arm_dap_object *dap;
+ Jim_Obj *new_cmd;
+ Jim_Cmd *cmd;
+ const char *cp;
+ int e;
+
+ cmd_ctx = current_command_context(goi->interp);
+ assert(cmd_ctx != NULL);
+
+ if (goi->argc < 3) {
+ Jim_WrongNumArgs(goi->interp, 1, goi->argv, "?name? ..options...");
+ return JIM_ERR;
+ }
+ /* COMMAND */
+ Jim_GetOpt_Obj(goi, &new_cmd);
+ /* does this command exist? */
+ cmd = Jim_GetCommand(goi->interp, new_cmd, JIM_ERRMSG);
+ if (cmd) {
+ cp = Jim_GetString(new_cmd, NULL);
+ Jim_SetResultFormatted(goi->interp, "Command: %s Exists", cp);
+ return JIM_ERR;
+ }
+
+ /* Create it */
+ dap = calloc(1, sizeof(struct arm_dap_object));
+ if (dap == NULL)
+ return JIM_ERR;
+
+ e = dap_configure(goi, dap);
+ if (e != JIM_OK) {
+ free(dap);
+ return e;
+ }
+
+ cp = Jim_GetString(new_cmd, NULL);
+ dap->name = strdup(cp);
+
+ struct command_registration dap_commands[] = {
+ {
+ .name = cp,
+ .mode = COMMAND_ANY,
+ .help = "dap instance command group",
+ .usage = "",
+ .chain = dap_instance_commands,
+ },
+ COMMAND_REGISTRATION_DONE
+ };
+
+ /* don't expose the instance commands when using hla */
+ if (transport_is_hla())
+ dap_commands[0].chain = NULL;
+
+ e = register_commands(cmd_ctx, NULL, dap_commands);
+ if (ERROR_OK != e)
+ return JIM_ERR;
+
+ struct command *c = command_find_in_context(cmd_ctx, cp);
+ assert(c);
+ command_set_handler_data(c, dap);
+
+ list_add_tail(&dap->lh, &all_dap);
+
+ return (ERROR_OK == e) ? JIM_OK : JIM_ERR;
+}
+
+static int jim_dap_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ Jim_GetOptInfo goi;
+ Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+ if (goi.argc < 2) {
+ Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
+ "<name> [<dap_options> ...]");
+ return JIM_ERR;
+ }
+ return dap_create(&goi);
+}
+
+static int jim_dap_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ struct arm_dap_object *obj;
+
+ if (argc != 1) {
+ Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
+ return JIM_ERR;
+ }
+ Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
+ list_for_each_entry(obj, &all_dap, lh) {
+ Jim_ListAppendElement(interp, Jim_GetResult(interp),
+ Jim_NewStringObj(interp, obj->name, -1));
+ }
+ return JIM_OK;
+}
+
+COMMAND_HANDLER(handle_dap_init)
+{
+ return dap_init_all();
+}
+
+COMMAND_HANDLER(handle_dap_info_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct arm *arm = target_to_arm(target);
+ struct adiv5_dap *dap = arm->dap;
+ uint32_t apsel;
+
+ switch (CMD_ARGC) {
+ case 0:
+ apsel = dap->apsel;
+ break;
+ case 1:
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
+ if (apsel >= 256)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ break;
+ default:
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return dap_info_command(CMD_CTX, &dap->ap[apsel]);
+}
+
+static const struct command_registration dap_subcommand_handlers[] = {
+ {
+ .name = "create",
+ .mode = COMMAND_ANY,
+ .jim_handler = jim_dap_create,
+ .usage = "name '-chain-position' name",
+ .help = "Creates a new DAP instance",
+ },
+ {
+ .name = "names",
+ .mode = COMMAND_ANY,
+ .jim_handler = jim_dap_names,
+ .usage = "",
+ .help = "Lists all registered DAP instances by name",
+ },
+ {
+ .name = "init",
+ .mode = COMMAND_ANY,
+ .handler = handle_dap_init,
+ .usage = "",
+ .help = "Initialize all registered DAP instances"
+ },
+ {
+ .name = "info",
+ .handler = handle_dap_info_command,
+ .mode = COMMAND_EXEC,
+ .help = "display ROM table for MEM-AP of current target "
+ "(default currently selected AP)",
+ .usage = "[ap_num]",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration dap_commands[] = {
+ {
+ .name = "dap",
+ .mode = COMMAND_CONFIG,
+ .help = "DAP commands",
+ .chain = dap_subcommand_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+int dap_register_commands(struct command_context *cmd_ctx)
+{
+ return register_commands(cmd_ctx, NULL, dap_commands);
+}
diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c
index ef69a20..8eb8194 100644
--- a/src/target/arm_disassembler.c
+++ b/src/target/arm_disassembler.c
@@ -118,15 +118,78 @@ static int evaluate_pld(uint32_t opcode,
uint32_t address, struct arm_instruction *instruction)
{
/* PLD */
- if ((opcode & 0x0d70f000) == 0x0550f000) {
+ if ((opcode & 0x0d30f000) == 0x0510f000) {
+ uint8_t Rn;
+ uint8_t U;
+ unsigned offset;
+
instruction->type = ARM_PLD;
+ Rn = (opcode & 0xf0000) >> 16;
+ U = (opcode & 0x00800000) >> 23;
+ if (Rn == 0xf) {
+ /* literal */
+ offset = opcode & 0x0fff;
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD %s%d",
+ address, opcode, U ? "" : "-", offset);
+ } else {
+ uint8_t I, R;
- snprintf(instruction->text,
- 128,
- "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD ...TODO...",
- address,
- opcode);
+ I = (opcode & 0x02000000) >> 25;
+ R = (opcode & 0x00400000) >> 22;
+
+ if (I) {
+ /* register PLD{W} [<Rn>,+/-<Rm>{, <shift>}] */
+ offset = (opcode & 0x0F80) >> 7;
+ uint8_t Rm;
+ Rm = opcode & 0xf;
+
+ if (offset == 0) {
+ /* No shift */
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d]",
+ address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm);
+
+ } else {
+ uint8_t shift;
+ shift = (opcode & 0x60) >> 5;
+ if (shift == 0x0) {
+ /* LSL */
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSL #0x%x)",
+ address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
+ } else if (shift == 0x1) {
+ /* LSR */
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSR #0x%x)",
+ address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
+ } else if (shift == 0x2) {
+ /* ASR */
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ASR #0x%x)",
+ address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
+ } else if (shift == 0x3) {
+ /* ROR */
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ROR #0x%x)",
+ address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
+ }
+ }
+ } else {
+ /* immediate PLD{W} [<Rn>, #+/-<imm12>] */
+ offset = opcode & 0x0fff;
+ if (offset == 0) {
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d]",
+ address, opcode, R ? "" : "W", Rn);
+ } else {
+ snprintf(instruction->text, 128,
+ "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, #%s%d]",
+ address, opcode, R ? "" : "W", Rn, U ? "" : "-", offset);
+ }
+ }
+ }
return ERROR_OK;
}
/* DSB */
@@ -1549,7 +1612,7 @@ static int evaluate_misc_instr(uint32_t opcode,
}
/* SMLAW < y> */
- if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) {
+ if (((opcode & 0x00600000) == 0x00200000) && (x == 0)) {
uint8_t Rd, Rm, Rs, Rn;
instruction->type = ARM_SMLAWy;
Rd = (opcode & 0xf0000) >> 16;
@@ -1571,7 +1634,7 @@ static int evaluate_misc_instr(uint32_t opcode,
}
/* SMUL < x><y> */
- if ((opcode & 0x00600000) == 0x00300000) {
+ if ((opcode & 0x00600000) == 0x00600000) {
uint8_t Rd, Rm, Rs;
instruction->type = ARM_SMULxy;
Rd = (opcode & 0xf0000) >> 16;
@@ -1592,7 +1655,7 @@ static int evaluate_misc_instr(uint32_t opcode,
}
/* SMULW < y> */
- if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) {
+ if (((opcode & 0x00600000) == 0x00200000) && (x == 1)) {
uint8_t Rd, Rm, Rs;
instruction->type = ARM_SMULWy;
Rd = (opcode & 0xf0000) >> 16;
@@ -2978,6 +3041,7 @@ static int t2ev_b_bl(uint32_t opcode, uint32_t address,
case 0x4:
inst = "BLX";
instruction->type = ARM_BLX;
+ address &= 0xfffffffc;
break;
case 0x5:
inst = "BL";
diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c
index 3e8180c..f9b30c1 100644
--- a/src/target/arm_dpm.c
+++ b/src/target/arm_dpm.c
@@ -131,6 +131,42 @@ int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
return retval;
}
+/* Read 64bit VFP registers */
+static int dpm_read_reg_u64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+ int retval = ERROR_FAIL;
+ uint32_t value_r0, value_r1;
+
+ switch (regnum) {
+ case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31:
+ /* move from double word register to r0:r1: "vmov r0, r1, vm"
+ * then read r0 via dcc
+ */
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV4_5_VMOV(1, 1, 0, ((regnum - ARM_VFP_V3_D0) >> 4),
+ ((regnum - ARM_VFP_V3_D0) & 0xf)), &value_r0);
+ /* read r1 via dcc */
+ retval = dpm->instr_read_data_dcc(dpm,
+ ARMV4_5_MCR(14, 0, 1, 0, 5, 0),
+ &value_r1);
+ break;
+ default:
+
+ break;
+ }
+
+ if (retval == ERROR_OK) {
+ buf_set_u32(r->value, 0, 32, value_r0);
+ buf_set_u32(r->value + 4, 0, 32, value_r1);
+ r->valid = true;
+ r->dirty = false;
+ LOG_DEBUG("READ: %s, %8.8x, %8.8x", r->name,
+ (unsigned) value_r0, (unsigned) value_r1);
+ }
+
+ return retval;
+}
+
/* just read the register -- rely on the core mode being right */
static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
{
@@ -171,6 +207,14 @@ static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
break;
}
break;
+ case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31:
+ return dpm_read_reg_u64(dpm, r, regnum);
+ break;
+ case ARM_VFP_V3_FPSCR:
+ /* "VMRS r0, FPSCR"; then return via DCC */
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV4_5_VMRS(0), &value);
+ break;
default:
/* 16: "MRS r0, CPSR"; then return via DCC
* 17: "MRS r0, SPSR"; then return via DCC
@@ -191,6 +235,40 @@ static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
return retval;
}
+/* Write 64bit VFP registers */
+static int dpm_write_reg_u64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+ int retval = ERROR_FAIL;
+ uint32_t value_r0 = buf_get_u32(r->value, 0, 32);
+ uint32_t value_r1 = buf_get_u32(r->value + 4, 0, 32);
+
+ switch (regnum) {
+ case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31:
+ /* write value_r1 to r1 via dcc */
+ retval = dpm->instr_write_data_dcc(dpm,
+ ARMV4_5_MRC(14, 0, 1, 0, 5, 0),
+ value_r1);
+ /* write value_r0 to r0 via dcc then,
+ * move to double word register from r0:r1: "vmov vm, r0, r1"
+ */
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV4_5_VMOV(0, 1, 0, ((regnum - ARM_VFP_V3_D0) >> 4),
+ ((regnum - ARM_VFP_V3_D0) & 0xf)), value_r0);
+ break;
+ default:
+
+ break;
+ }
+
+ if (retval == ERROR_OK) {
+ r->dirty = false;
+ LOG_DEBUG("WRITE: %s, %8.8x, %8.8x", r->name,
+ (unsigned) value_r0, (unsigned) value_r1);
+ }
+
+ return retval;
+}
+
/* just write the register -- rely on the core mode being right */
static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
{
@@ -208,6 +286,14 @@ static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
* read r0 from DCC; then "MOV pc, r0" */
retval = dpm->instr_write_data_r0(dpm, 0xe1a0f000, value);
break;
+ case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31:
+ return dpm_write_reg_u64(dpm, r, regnum);
+ break;
+ case ARM_VFP_V3_FPSCR:
+ /* move to r0 from DCC, then "VMSR FPSCR, r0" */
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV4_5_VMSR(0), value);
+ break;
default:
/* 16: read r0 from DCC, then "MSR r0, CPSR_cxsf"
* 17: read r0 from DCC, then "MSR r0, SPSR_cxsf"
@@ -262,14 +348,16 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm)
if (retval != ERROR_OK)
return retval;
- /* read R0 first (it's used for scratch), then CPSR */
- r = arm->core_cache->reg_list + 0;
- if (!r->valid) {
- retval = dpm_read_reg(dpm, r, 0);
- if (retval != ERROR_OK)
- goto fail;
+ /* read R0 and R1 first (it's used for scratch), then CPSR */
+ for (unsigned i = 0; i < 2; i++) {
+ r = arm->core_cache->reg_list + i;
+ if (!r->valid) {
+ retval = dpm_read_reg(dpm, r, i);
+ if (retval != ERROR_OK)
+ goto fail;
+ }
+ r->dirty = true;
}
- r->dirty = true;
retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRS(0, 0), &cpsr);
if (retval != ERROR_OK)
@@ -279,7 +367,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm)
arm_set_cpsr(arm, cpsr);
/* REVISIT we can probably avoid reading R1..R14, saving time... */
- for (unsigned i = 1; i < 16; i++) {
+ for (unsigned i = 2; i < 16; i++) {
r = arm_reg_current(arm, i);
if (r->valid)
continue;
@@ -412,8 +500,8 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
did_write = false;
- /* check everything except our scratch register R0 */
- for (unsigned i = 1; i < cache->num_regs; i++) {
+ /* check everything except our scratch registers R0 and R1 */
+ for (unsigned i = 2; i < cache->num_regs; i++) {
struct arm_reg *r;
unsigned regnum;
@@ -499,11 +587,13 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
goto done;
arm->pc->dirty = false;
- /* flush R0 -- it's *very* dirty by now */
- retval = dpm_write_reg(dpm, &cache->reg_list[0], 0);
- if (retval != ERROR_OK)
- goto done;
- cache->reg_list[0].dirty = false;
+ /* flush R0 and R1 (our scratch registers) */
+ for (unsigned i = 0; i < 2; i++) {
+ retval = dpm_write_reg(dpm, &cache->reg_list[i], i);
+ if (retval != ERROR_OK)
+ goto done;
+ cache->reg_list[i].dirty = false;
+ }
/* (void) */ dpm->finish(dpm);
done:
@@ -540,6 +630,7 @@ static enum arm_mode dpm_mapmode(struct arm *arm,
/* r13/sp, and r14/lr are always shadowed */
case 13:
case 14:
+ case ARM_VFP_V3_D0 ... ARM_VFP_V3_FPSCR:
return mode;
default:
LOG_WARNING("invalid register #%u", num);
@@ -561,7 +652,8 @@ static int arm_dpm_read_core_reg(struct target *target, struct reg *r,
struct arm_dpm *dpm = target_to_arm(target)->dpm;
int retval;
- if (regnum < 0 || regnum > 16)
+ if (regnum < 0 || (regnum > 16 && regnum < ARM_VFP_V3_D0) ||
+ (regnum > ARM_VFP_V3_FPSCR))
return ERROR_COMMAND_SYNTAX_ERROR;
if (regnum == 16) {
@@ -604,7 +696,8 @@ static int arm_dpm_write_core_reg(struct target *target, struct reg *r,
int retval;
- if (regnum < 0 || regnum > 16)
+ if (regnum < 0 || (regnum > 16 && regnum < ARM_VFP_V3_D0) ||
+ (regnum > ARM_VFP_V3_FPSCR))
return ERROR_COMMAND_SYNTAX_ERROR;
if (regnum == 16) {
diff --git a/src/target/arm_opcodes.h b/src/target/arm_opcodes.h
index a53fee7..482abe6 100644
--- a/src/target/arm_opcodes.h
+++ b/src/target/arm_opcodes.h
@@ -132,6 +132,30 @@
*/
#define ARMV4_5_BX(Rm) (0xe12fff10 | (Rm))
+/* Copies two words from two ARM core registers
+ * into a doubleword extension register, or
+ * from a doubleword extension register to two ARM core registers.
+ * See Armv7-A arch reference manual section A8.8.345
+ * Rt: Arm core register 1
+ * Rt2: Arm core register 2
+ * Vm: The doubleword extension register
+ * M: m = UInt(M:Vm);
+ * op: to_arm_registers = (op == ‘1’);
+ */
+#define ARMV4_5_VMOV(op, Rt2, Rt, M, Vm) \
+ (0xec400b10 | ((op) << 20) | ((Rt2) << 16) | \
+ ((Rt) << 12) | ((M) << 5) | (Vm))
+
+/* Moves the value of the FPSCR to an ARM core register
+ * Rt: Arm core register
+ */
+#define ARMV4_5_VMRS(Rt) (0xeef10a10 | ((Rt) << 12))
+
+/* Moves the value of an ARM core register to the FPSCR.
+ * Rt: Arm core register
+ */
+#define ARMV4_5_VMSR(Rt) (0xeee10a10 | ((Rt) << 12))
+
/* Store data from coprocessor to consecutive memory
* See Armv7-A arch doc section A8.6.187
* P: 1=index mode (offset from Rn)
diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c
index f31f901..9117a74 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 *
@@ -43,6 +46,7 @@
#include "arm7_9_common.h"
#include "armv7m.h"
#include "armv7a.h"
+#include "armv8.h"
#include "cortex_m.h"
#include "register.h"
#include "arm_opcodes.h"
@@ -52,25 +56,35 @@
#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 arm_semihosting_resume(struct target *target, int *retval)
+{
+ if (is_armv8(target_to_armv8(target))) {
+ struct armv8_common *armv8 = target_to_armv8(target);
+ if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) {
+ *retval = target_resume(target, 1, 0, 0, 0);
+ if (*retval != ERROR_OK) {
+ LOG_ERROR("Failed to resume target");
+ return 0;
+ }
+ } else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP)
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ } else {
+ *retval = target_resume(target, 1, 0, 0, 0);
+ if (*retval != ERROR_OK) {
+ LOG_ERROR("Failed to resume target");
+ return 0;
+ }
+ }
+ return 1;
+}
static int post_result(struct target *target)
{
struct arm *arm = target_to_arm(target);
+ if (!target->semihosting)
+ return ERROR_FAIL;
+
/* REVISIT this looks wrong ... ARM11 and Cortex-A8
* should work this way at least sometimes.
*/
@@ -79,7 +93,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 */
@@ -100,528 +114,28 @@ static int post_result(struct target *target)
if (spsr & 0x20)
arm->core_state = ARM_STATE_THUMB;
+ } else if (is_armv8(target_to_armv8(target))) {
+ if (arm->core_state == ARM_STATE_AARCH64) {
+ /* return value in R0 */
+ buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result);
+ arm->core_cache->reg_list[0].dirty = 1;
+
+ uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64);
+ buf_set_u64(arm->pc->value, 0, 64, pc + 4);
+ arm->pc->dirty = 1;
+ }
} else {
/* resume execution, this will be pc+2 to skip over the
* 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 +144,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 +171,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)) ||
@@ -758,6 +271,24 @@ int arm_semihosting(struct target *target, int *retval)
/* bkpt 0xAB */
if (insn != 0xBEAB)
return 0;
+ } else if (is_armv8(target_to_armv8(target))) {
+ if (target->debug_reason != DBG_REASON_BREAKPOINT)
+ return 0;
+
+ if (arm->core_state == ARM_STATE_AARCH64) {
+ uint32_t insn = 0;
+ r = arm->pc;
+ uint64_t pc64 = buf_get_u64(r->value, 0, 64);
+ *retval = target_read_u32(target, pc64, &insn);
+
+ if (*retval != ERROR_OK)
+ return 1;
+
+ /* bkpt 0xAB */
+ if (insn != 0xD45E0000)
+ return 0;
+ } else
+ return 1;
} else {
LOG_ERROR("Unsupported semi-hosting Target");
return 0;
@@ -766,32 +297,38 @@ 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) {
+ if (is_armv8(target_to_armv8(target)) &&
+ arm->core_state == ARM_STATE_AARCH64) {
+ /* Read op and param from register x0 and x1 respectively. */
+ semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
+ semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
+ semihosting->word_size_bytes = 8;
+ } else {
+ /* Read op and param from register r0 and r1 respectively. */
+ semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
+ semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
+ 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;
}
}
- /* Post result to target if we are not waiting on a fileio
+ /* Resume if target it is resumable and 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;
- }
-
- *retval = target_resume(target, 1, 0, 0, 0);
- if (*retval != ERROR_OK) {
- LOG_ERROR("Failed to resume target");
- return 0;
- }
-
- return 1;
- }
+ if (semihosting->is_resumable && !semihosting->hit_fileio)
+ return arm_semihosting_resume(target, retval);
return 0;
}
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 48050b0..96a63e4 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 {
@@ -340,6 +344,50 @@ static const struct {
};
+static const struct {
+ unsigned int id;
+ const char *name;
+ uint32_t bits;
+ enum arm_mode mode;
+ enum reg_type type;
+ const char *group;
+ const char *feature;
+} arm_vfp_v3_regs[] = {
+ { ARM_VFP_V3_D0, "d0", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D1, "d1", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D2, "d2", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D3, "d3", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D4, "d4", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D5, "d5", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D6, "d6", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D7, "d7", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D8, "d8", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D9, "d9", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D10, "d10", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D11, "d11", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D12, "d12", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D13, "d13", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D14, "d14", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D15, "d15", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D16, "d16", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D17, "d17", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D18, "d18", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D19, "d19", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D20, "d20", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D21, "d21", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D22, "d22", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D23, "d23", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D24, "d24", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D25, "d25", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D26, "d26", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D27, "d27", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D28, "d28", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D29, "d29", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D30, "d30", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_D31, "d31", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARM_VFP_V3_FPSCR, "fpscr", 32, ARM_MODE_ANY, REG_TYPE_INT, "float", "org.gnu.gdb.arm.vfp"},
+};
+
/* map core mode (USR, FIQ, ...) and register number to
* indices into the register cache
*/
@@ -567,6 +615,10 @@ static int armv4_5_set_core_reg(struct reg *reg, uint8_t *buf)
}
} else {
buf_set_u32(reg->value, 0, 32, value);
+ if (reg->size == 64) {
+ value = buf_get_u32(buf + 4, 0, 32);
+ buf_set_u32(reg->value + 4, 0, 32, value);
+ }
reg->valid = 1;
}
reg->dirty = 1;
@@ -582,6 +634,10 @@ static const struct reg_arch_type arm_reg_type = {
struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm)
{
int num_regs = ARRAY_SIZE(arm_core_regs);
+ int num_core_regs = num_regs;
+ if (arm->arm_vfp_version == ARM_VFP_V3)
+ num_regs += ARRAY_SIZE(arm_vfp_v3_regs);
+
struct reg_cache *cache = malloc(sizeof(struct reg_cache));
struct reg *reg_list = calloc(num_regs, sizeof(struct reg));
struct arm_reg *reg_arch_info = calloc(num_regs, sizeof(struct arm_reg));
@@ -599,7 +655,7 @@ struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm)
cache->reg_list = reg_list;
cache->num_regs = 0;
- for (i = 0; i < num_regs; i++) {
+ for (i = 0; i < num_core_regs; i++) {
/* Skip registers this core doesn't expose */
if (arm_core_regs[i].mode == ARM_MODE_MON
&& arm->core_type != ARM_MODE_MON)
@@ -651,9 +707,38 @@ struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm)
cache->num_regs++;
}
+ int j;
+ for (i = num_core_regs, j = 0; i < num_regs; i++, j++) {
+ reg_arch_info[i].num = arm_vfp_v3_regs[j].id;
+ reg_arch_info[i].mode = arm_vfp_v3_regs[j].mode;
+ reg_arch_info[i].target = target;
+ reg_arch_info[i].arm = arm;
+
+ reg_list[i].name = arm_vfp_v3_regs[j].name;
+ reg_list[i].number = arm_vfp_v3_regs[j].id;
+ reg_list[i].size = arm_vfp_v3_regs[j].bits;
+ reg_list[i].value = reg_arch_info[i].value;
+ reg_list[i].type = &arm_reg_type;
+ reg_list[i].arch_info = &reg_arch_info[i];
+ reg_list[i].exist = true;
+
+ reg_list[i].caller_save = false;
+
+ reg_list[i].reg_data_type = malloc(sizeof(struct reg_data_type));
+ reg_list[i].reg_data_type->type = arm_vfp_v3_regs[j].type;
+
+ reg_list[i].feature = malloc(sizeof(struct reg_feature));
+ reg_list[i].feature->name = arm_vfp_v3_regs[j].feature;
+
+ reg_list[i].group = arm_vfp_v3_regs[j].group;
+
+ cache->num_regs++;
+ }
+
arm->pc = reg_list + 15;
arm->cpsr = reg_list + ARMV4_5_CPSR;
arm->core_cache = cache;
+
return cache;
}
@@ -667,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 && target->semihosting->hit_fileio)
return ERROR_OK;
LOG_USER("target halted in %s state due to %s, current mode: %s\n"
@@ -677,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 && target->semihosting->is_active) ? ", semihosting" : "",
+ (target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
return ERROR_OK;
}
@@ -1013,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[] = {
{
@@ -1164,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[] = {
@@ -1229,6 +1211,10 @@ int arm_get_gdb_reg_list(struct target *target,
case REG_CLASS_ALL:
*reg_list_size = (arm->core_type != ARM_MODE_MON ? 48 : 51);
+ unsigned int list_size_core = *reg_list_size;
+ if (arm->arm_vfp_version == ARM_VFP_V3)
+ *reg_list_size += 33;
+
*reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));
for (i = 0; i < 16; i++)
@@ -1249,6 +1235,12 @@ int arm_get_gdb_reg_list(struct target *target,
(*reg_list)[24] = &arm_gdb_dummy_fps_reg;
(*reg_list)[24]->size = 0;
+ if (arm->arm_vfp_version == ARM_VFP_V3) {
+ unsigned int num_core_regs = ARRAY_SIZE(arm_core_regs);
+ for (i = 0; i < 33; i++)
+ (*reg_list)[list_size_core + i] = &(arm->core_cache->reg_list[num_core_regs + i]);
+ }
+
return ERROR_OK;
break;
@@ -1572,7 +1564,7 @@ cleanup:
*
*/
int arm_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value)
+ struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
{
struct working_area *check_algorithm;
struct reg_param reg_params[3];
@@ -1615,10 +1607,10 @@ int arm_blank_check_memory(struct target *target,
arm_algo.core_state = ARM_STATE_ARM;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
- buf_set_u32(reg_params[0].value, 0, 32, address);
+ buf_set_u32(reg_params[0].value, 0, 32, blocks[0].address);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
- buf_set_u32(reg_params[1].value, 0, 32, count);
+ buf_set_u32(reg_params[1].value, 0, 32, blocks[0].size);
init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT);
buf_set_u32(reg_params[2].value, 0, 32, erased_value);
@@ -1633,7 +1625,7 @@ int arm_blank_check_memory(struct target *target,
10000, &arm_algo);
if (retval == ERROR_OK)
- *blank = buf_get_u32(reg_params[2].value, 0, 32);
+ blocks[0].result = buf_get_u32(reg_params[2].value, 0, 32);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
@@ -1642,7 +1634,10 @@ int arm_blank_check_memory(struct target *target,
cleanup:
target_free_working_area(target, check_algorithm);
- return retval;
+ if (retval != ERROR_OK)
+ return retval;
+
+ return 1; /* only one block has been checked */
}
static int arm_full_context(struct target *target)
diff --git a/src/target/armv7a.c b/src/target/armv7a.c
index db72afd..eecfa70 100644
--- a/src/target/armv7a.c
+++ b/src/target/armv7a.c
@@ -124,14 +124,18 @@ done:
return retval;
}
-static int armv7a_read_ttbcr(struct target *target)
+int armv7a_read_ttbcr(struct target *target)
{
struct armv7a_common *armv7a = target_to_armv7a(target);
struct arm_dpm *dpm = armv7a->arm.dpm;
uint32_t ttbcr, ttbcr_n;
- int retval = dpm->prepare(dpm);
+ int ttbidx;
+ int retval;
+
+ retval = dpm->prepare(dpm);
if (retval != ERROR_OK)
goto done;
+
/* MRC p15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/
retval = dpm->instr_read_data_r0(dpm,
ARMV4_5_MRC(15, 0, 0, 2, 0, 2),
@@ -145,6 +149,15 @@ static int armv7a_read_ttbcr(struct target *target)
armv7a->armv7a_mmu.ttbcr = ttbcr;
armv7a->armv7a_mmu.cached = 1;
+ for (ttbidx = 0; ttbidx < 2; ttbidx++) {
+ /* MRC p15,0,<Rt>,c2,c0,ttbidx */
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV4_5_MRC(15, 0, 0, 2, 0, ttbidx),
+ &armv7a->armv7a_mmu.ttbr[ttbidx]);
+ if (retval != ERROR_OK)
+ goto done;
+ }
+
/*
* ARM Architecture Reference Manual (ARMv7-A and ARMv7-Redition),
* document # ARM DDI 0406C
@@ -182,42 +195,21 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val)
uint32_t second_lvl_descriptor = 0x0;
int retval;
struct armv7a_common *armv7a = target_to_armv7a(target);
- struct arm_dpm *dpm = armv7a->arm.dpm;
uint32_t ttbidx = 0; /* default to ttbr0 */
uint32_t ttb_mask;
uint32_t va_mask;
- uint32_t ttbcr;
uint32_t ttb;
- retval = dpm->prepare(dpm);
- if (retval != ERROR_OK)
- goto done;
-
- /* MRC p15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/
- retval = dpm->instr_read_data_r0(dpm,
- ARMV4_5_MRC(15, 0, 0, 2, 0, 2),
- &ttbcr);
- if (retval != ERROR_OK)
- goto done;
-
- /* if ttbcr has changed or was not read before, re-read the information */
- if ((armv7a->armv7a_mmu.cached == 0) ||
- (armv7a->armv7a_mmu.ttbcr != ttbcr)) {
- armv7a_read_ttbcr(target);
- }
+ if (target->state != TARGET_HALTED)
+ LOG_INFO("target not halted, using cached values for translation table!");
/* if va is above the range handled by ttbr0, select ttbr1 */
if (va > armv7a->armv7a_mmu.ttbr_range[0]) {
/* select ttb 1 */
ttbidx = 1;
}
- /* MRC p15,0,<Rt>,c2,c0,ttbidx */
- retval = dpm->instr_read_data_r0(dpm,
- ARMV4_5_MRC(15, 0, 0, 2, 0, ttbidx),
- &ttb);
- if (retval != ERROR_OK)
- return retval;
+ ttb = armv7a->armv7a_mmu.ttbr[ttbidx];
ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx];
va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx];
@@ -279,9 +271,6 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val)
}
return ERROR_OK;
-
-done:
- return retval;
}
/* V7 method VA TO PA */
@@ -565,9 +554,6 @@ int armv7a_identify_cache(struct target *target)
struct armv7a_cache_common *cache =
&(armv7a->armv7a_mmu.armv7a_cache);
- if (!armv7a->is_armv7r)
- armv7a_read_ttbcr(target);
-
retval = dpm->prepare(dpm);
if (retval != ERROR_OK)
goto done;
@@ -786,9 +772,6 @@ const struct command_registration l2x_cache_command_handlers[] = {
const struct command_registration armv7a_command_handlers[] = {
{
- .chain = dap_command_handlers,
- },
- {
.chain = l2x_cache_command_handlers,
},
{
diff --git a/src/target/armv7a.h b/src/target/armv7a.h
index 14112e4..57779c6 100644
--- a/src/target/armv7a.h
+++ b/src/target/armv7a.h
@@ -87,6 +87,7 @@ struct armv7a_mmu_common {
/* following field mmu working way */
int32_t cached; /* 0: not initialized, 1: initialized */
uint32_t ttbcr; /* cache for ttbcr register */
+ uint32_t ttbr[2];
uint32_t ttbr_mask[2];
uint32_t ttbr_range[2];
@@ -193,6 +194,7 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val);
int armv7a_handle_cache_info_command(struct command_context *cmd_ctx,
struct armv7a_cache_common *armv7a_cache);
+int armv7a_read_ttbcr(struct target *target);
extern const struct command_registration armv7a_command_handlers[];
diff --git a/src/target/armv7a_cache.c b/src/target/armv7a_cache.c
index 7af3e6d..7435aab 100644
--- a/src/target/armv7a_cache.c
+++ b/src/target/armv7a_cache.c
@@ -70,6 +70,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach
LOG_DEBUG("cl %" PRId32, cl);
do {
+ keep_alive();
c_way = size->way;
do {
uint32_t value = (c_index << size->index_shift)
@@ -89,6 +90,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach
} while (c_index >= 0);
done:
+ keep_alive();
return retval;
}
@@ -148,10 +150,11 @@ int armv7a_cache_auto_flush_all_data(struct target *target)
} else
retval = armv7a_l1_d_cache_clean_inval_all(target);
- /* do outer cache flushing after inner caches have been flushed */
- retval = arm7a_l2x_flush_all_data(target);
+ if (retval != ERROR_OK)
+ return retval;
- return retval;
+ /* do outer cache flushing after inner caches have been flushed */
+ return arm7a_l2x_flush_all_data(target);
}
@@ -163,7 +166,7 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->dminline;
uint32_t va_line, va_end;
- int retval;
+ int retval, i = 0;
retval = armv7a_l1_d_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -197,6 +200,8 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
}
while (va_line < va_end) {
+ if ((i++ & 0x3f) == 0)
+ keep_alive();
/* DCIMVAC - Invalidate data cache line by VA to PoC. */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
@@ -205,11 +210,13 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
va_line += linelen;
}
+ keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("d-cache invalidate failed");
+ keep_alive();
dpm->finish(dpm);
return retval;
@@ -223,7 +230,7 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->dminline;
uint32_t va_line, va_end;
- int retval;
+ int retval, i = 0;
retval = armv7a_l1_d_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -237,6 +244,8 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
va_end = virt + size;
while (va_line < va_end) {
+ if ((i++ & 0x3f) == 0)
+ keep_alive();
/* DCCMVAC - Data Cache Clean by MVA to PoC */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line);
@@ -245,11 +254,13 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
va_line += linelen;
}
+ keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("d-cache invalidate failed");
+ keep_alive();
dpm->finish(dpm);
return retval;
@@ -263,7 +274,7 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->dminline;
uint32_t va_line, va_end;
- int retval;
+ int retval, i = 0;
retval = armv7a_l1_d_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -277,6 +288,8 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
va_end = virt + size;
while (va_line < va_end) {
+ if ((i++ & 0x3f) == 0)
+ keep_alive();
/* DCCIMVAC */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
@@ -285,11 +298,13 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
va_line += linelen;
}
+ keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("d-cache invalidate failed");
+ keep_alive();
dpm->finish(dpm);
return retval;
@@ -341,7 +356,7 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
&armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->iminline;
uint32_t va_line, va_end;
- int retval;
+ int retval, i = 0;
retval = armv7a_l1_i_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -355,6 +370,8 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
va_end = virt + size;
while (va_line < va_end) {
+ if ((i++ & 0x3f) == 0)
+ keep_alive();
/* ICIMVAU - Invalidate instruction cache by VA to PoU. */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
@@ -367,10 +384,13 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
goto done;
va_line += linelen;
}
+ keep_alive();
+ dpm->finish(dpm);
return retval;
done:
LOG_ERROR("i-cache invalidate failed");
+ keep_alive();
dpm->finish(dpm);
return retval;
diff --git a/src/target/armv7m.c b/src/target/armv7m.c
index e0911c3..7d3bd73 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 && 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 && target->semihosting->is_active) ? ", semihosting" : "",
+ (target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
return ERROR_OK;
}
@@ -731,34 +735,23 @@ cleanup:
return retval;
}
-/** Checks whether a memory region is erased. */
+/** Checks an array of memory regions whether they are erased. */
int armv7m_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value)
+ struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
{
struct working_area *erase_check_algorithm;
- struct reg_param reg_params[3];
+ struct working_area *erase_check_params;
+ struct reg_param reg_params[2];
struct armv7m_algorithm armv7m_info;
- const uint8_t *code;
- uint32_t code_size;
int retval;
+ static bool timed_out;
+
static const uint8_t erase_check_code[] = {
#include "../../contrib/loaders/erase_check/armv7m_erase_check.inc"
};
- static const uint8_t zero_erase_check_code[] = {
-#include "../../contrib/loaders/erase_check/armv7m_0_erase_check.inc"
- };
- switch (erased_value) {
- case 0x00:
- code = zero_erase_check_code;
- code_size = sizeof(zero_erase_check_code);
- break;
- case 0xff:
- default:
- code = erase_check_code;
- code_size = sizeof(erase_check_code);
- }
+ const uint32_t code_size = sizeof(erase_check_code);
/* make sure we have a working area */
if (target_alloc_working_area(target, code_size,
@@ -766,40 +759,110 @@ int armv7m_blank_check_memory(struct target *target,
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
retval = target_write_buffer(target, erase_check_algorithm->address,
- code_size, code);
+ code_size, erase_check_code);
if (retval != ERROR_OK)
- goto cleanup;
+ goto cleanup1;
+
+ /* prepare blocks array for algo */
+ struct algo_block {
+ union {
+ uint32_t size;
+ uint32_t result;
+ };
+ uint32_t address;
+ };
+
+ uint32_t avail = target_get_working_area_avail(target);
+ int blocks_to_check = avail / sizeof(struct algo_block) - 1;
+ if (num_blocks < blocks_to_check)
+ blocks_to_check = num_blocks;
+
+ struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block));
+ if (params == NULL) {
+ retval = ERROR_FAIL;
+ goto cleanup1;
+ }
+
+ int i;
+ uint32_t total_size = 0;
+ for (i = 0; i < blocks_to_check; i++) {
+ total_size += blocks[i].size;
+ target_buffer_set_u32(target, (uint8_t *)&(params[i].size),
+ blocks[i].size / sizeof(uint32_t));
+ target_buffer_set_u32(target, (uint8_t *)&(params[i].address),
+ blocks[i].address);
+ }
+ target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0);
+
+ uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block);
+ if (target_alloc_working_area(target, param_size,
+ &erase_check_params) != ERROR_OK) {
+ retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ goto cleanup2;
+ }
+
+ retval = target_write_buffer(target, erase_check_params->address,
+ param_size, (uint8_t *)params);
+ if (retval != ERROR_OK)
+ goto cleanup3;
+
+ uint32_t erased_word = erased_value | (erased_value << 8)
+ | (erased_value << 16) | (erased_value << 24);
+
+ LOG_DEBUG("Starting erase check of %d blocks, parameters@"
+ TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address);
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
- buf_set_u32(reg_params[0].value, 0, 32, address);
+ buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
- buf_set_u32(reg_params[1].value, 0, 32, count);
+ buf_set_u32(reg_params[1].value, 0, 32, erased_word);
- init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT);
- buf_set_u32(reg_params[2].value, 0, 32, erased_value);
+ /* assume CPU clk at least 1 MHz */
+ int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000;
retval = target_run_algorithm(target,
- 0,
- NULL,
- 3,
- reg_params,
- erase_check_algorithm->address,
- erase_check_algorithm->address + (code_size - 2),
- 10000,
- &armv7m_info);
+ 0, NULL,
+ ARRAY_SIZE(reg_params), reg_params,
+ erase_check_algorithm->address,
+ erase_check_algorithm->address + (code_size - 2),
+ timeout,
+ &armv7m_info);
+
+ timed_out = retval == ERROR_TARGET_TIMEOUT;
+ if (retval != ERROR_OK && !timed_out)
+ goto cleanup4;
+
+ retval = target_read_buffer(target, erase_check_params->address,
+ param_size, (uint8_t *)params);
+ if (retval != ERROR_OK)
+ goto cleanup4;
- if (retval == ERROR_OK)
- *blank = buf_get_u32(reg_params[2].value, 0, 32);
+ for (i = 0; i < blocks_to_check; i++) {
+ uint32_t result = target_buffer_get_u32(target,
+ (uint8_t *)&(params[i].result));
+ if (result != 0 && result != 1)
+ break;
+
+ blocks[i].result = result;
+ }
+ if (i && timed_out)
+ LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i);
+
+ retval = i; /* return number of blocks really checked */
+cleanup4:
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
- destroy_reg_param(&reg_params[2]);
-cleanup:
+cleanup3:
+ target_free_working_area(target, erase_check_params);
+cleanup2:
+ free(params);
+cleanup1:
target_free_working_area(target, erase_check_algorithm);
return retval;
@@ -843,8 +906,5 @@ const struct command_registration armv7m_command_handlers[] = {
{
.chain = arm_command_handlers,
},
- {
- .chain = dap_command_handlers,
- },
COMMAND_REGISTRATION_DONE
};
diff --git a/src/target/armv7m.h b/src/target/armv7m.h
index 6f5d6f9..01bf19e 100644
--- a/src/target/armv7m.h
+++ b/src/target/armv7m.h
@@ -225,7 +225,7 @@ int armv7m_restore_context(struct target *target);
int armv7m_checksum_memory(struct target *target,
target_addr_t address, uint32_t count, uint32_t *checksum);
int armv7m_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value);
+ struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value);
int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found);
diff --git a/src/target/armv7m_trace.c b/src/target/armv7m_trace.c
index c1e4f5b..62f0f8e 100644
--- a/src/target/armv7m_trace.c
+++ b/src/target/armv7m_trace.c
@@ -62,7 +62,7 @@ int armv7m_trace_tpiu_config(struct target *target)
target_unregister_timer_callback(armv7m_poll_trace, target);
- retval = adapter_config_trace(trace_config->config_type == INTERNAL,
+ retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
trace_config->pin_protocol,
trace_config->port_size,
&trace_config->trace_freq);
@@ -83,7 +83,7 @@ int armv7m_trace_tpiu_config(struct target *target)
trace_config->trace_freq, trace_config->traceclkin_freq,
trace_freq);
trace_config->trace_freq = trace_freq;
- retval = adapter_config_trace(trace_config->config_type == INTERNAL,
+ retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
trace_config->pin_protocol,
trace_config->port_size,
&trace_config->trace_freq);
@@ -115,7 +115,7 @@ int armv7m_trace_tpiu_config(struct target *target)
if (retval != ERROR_OK)
return retval;
- if (trace_config->config_type == INTERNAL)
+ if (trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL)
target_register_timer_callback(armv7m_poll_trace, 1, 1, target);
target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
@@ -173,7 +173,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
if (CMD_ARGC == cmd_idx + 1) {
close_trace_file(armv7m);
- armv7m->trace_config.config_type = DISABLED;
+ armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
if (CMD_CTX->mode == COMMAND_EXEC)
return armv7m_trace_tpiu_config(target);
else
@@ -183,13 +183,13 @@ COMMAND_HANDLER(handle_tpiu_config_command)
!strcmp(CMD_ARGV[cmd_idx], "internal")) {
close_trace_file(armv7m);
- armv7m->trace_config.config_type = EXTERNAL;
+ armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
cmd_idx++;
if (CMD_ARGC == cmd_idx)
return ERROR_COMMAND_SYNTAX_ERROR;
- armv7m->trace_config.config_type = INTERNAL;
+ armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL;
if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
@@ -204,7 +204,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
return ERROR_COMMAND_SYNTAX_ERROR;
if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
- armv7m->trace_config.pin_protocol = SYNC;
+ armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_SYNC;
cmd_idx++;
if (CMD_ARGC == cmd_idx)
@@ -213,9 +213,9 @@ COMMAND_HANDLER(handle_tpiu_config_command)
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
} else {
if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
- armv7m->trace_config.pin_protocol = ASYNC_MANCHESTER;
+ armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER;
else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
- armv7m->trace_config.pin_protocol = ASYNC_UART;
+ armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_UART;
else
return ERROR_COMMAND_SYNTAX_ERROR;
@@ -237,7 +237,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
cmd_idx++;
} else {
- if (armv7m->trace_config.config_type != INTERNAL) {
+ if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_INTERNAL) {
LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
return ERROR_COMMAND_SYNTAX_ERROR;
}
diff --git a/src/target/armv7m_trace.h b/src/target/armv7m_trace.h
index 4f99394..c63f36d 100644
--- a/src/target/armv7m_trace.h
+++ b/src/target/armv7m_trace.h
@@ -27,15 +27,15 @@
*/
enum trace_config_type {
- DISABLED, /**< tracing is disabled */
- EXTERNAL, /**< trace output is captured externally */
- INTERNAL /**< trace output is handled by OpenOCD adapter driver */
+ TRACE_CONFIG_TYPE_DISABLED, /**< tracing is disabled */
+ TRACE_CONFIG_TYPE_EXTERNAL, /**< trace output is captured externally */
+ TRACE_CONFIG_TYPE_INTERNAL /**< trace output is handled by OpenOCD adapter driver */
};
-enum tpio_pin_protocol {
- SYNC, /**< synchronous trace output */
- ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
- ASYNC_UART /**< asynchronous output with NRZ coding */
+enum tpiu_pin_protocol {
+ TPIU_PIN_PROTOCOL_SYNC, /**< synchronous trace output */
+ TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
+ TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */
};
enum itm_ts_prescaler {
@@ -50,7 +50,7 @@ struct armv7m_trace_config {
enum trace_config_type config_type;
/** Currently active trace output mode */
- enum tpio_pin_protocol pin_protocol;
+ enum tpiu_pin_protocol pin_protocol;
/** TPIU formatter enable/disable (in async mode) */
bool formatter;
/** Synchronous output port width */
diff --git a/src/target/armv8.c b/src/target/armv8.c
index 1b8e450..dfa2c67 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",
@@ -135,6 +139,16 @@ static int armv8_read_reg(struct armv8_common *armv8, int regnum, uint64_t *regv
ARMV8_MRS_DSPSR(0), &value);
value_64 = value;
break;
+ case ARMV8_FPSR:
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV8_MRS_FPSR(0), &value);
+ value_64 = value;
+ break;
+ case ARMV8_FPCR:
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV8_MRS_FPCR(0), &value);
+ value_64 = value;
+ break;
case ARMV8_ELR_EL1:
retval = dpm->instr_read_data_r0_64(dpm,
ARMV8_MRS(SYSTEM_ELR_EL1, 0), &value_64);
@@ -184,6 +198,31 @@ static int armv8_read_reg(struct armv8_common *armv8, int regnum, uint64_t *regv
if (retval == ERROR_OK && regval != NULL)
*regval = value_64;
+ else
+ retval = ERROR_FAIL;
+
+ return retval;
+}
+
+static int armv8_read_reg_simdfp_aarch64(struct armv8_common *armv8, int regnum, uint64_t *lvalue, uint64_t *hvalue)
+{
+ int retval = ERROR_FAIL;
+ struct arm_dpm *dpm = &armv8->dpm;
+
+ switch (regnum) {
+ case ARMV8_V0 ... ARMV8_V31:
+ retval = dpm->instr_read_data_r0_64(dpm,
+ ARMV8_MOV_GPR_VFP(0, (regnum - ARMV8_V0), 1), hvalue);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = dpm->instr_read_data_r0_64(dpm,
+ ARMV8_MOV_GPR_VFP(0, (regnum - ARMV8_V0), 0), lvalue);
+ break;
+
+ default:
+ retval = ERROR_FAIL;
+ break;
+ }
return retval;
}
@@ -216,6 +255,18 @@ static int armv8_write_reg(struct armv8_common *armv8, int regnum, uint64_t valu
ARMV8_MSR_DSPSR(0),
value);
break;
+ case ARMV8_FPSR:
+ value = value_64;
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV8_MSR_FPSR(0),
+ value);
+ break;
+ case ARMV8_FPCR:
+ value = value_64;
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV8_MSR_FPCR(0),
+ value);
+ break;
/* registers clobbered by taking exception in debug state */
case ARMV8_ELR_EL1:
retval = dpm->instr_write_data_r0_64(dpm,
@@ -267,6 +318,29 @@ static int armv8_write_reg(struct armv8_common *armv8, int regnum, uint64_t valu
return retval;
}
+static int armv8_write_reg_simdfp_aarch64(struct armv8_common *armv8, int regnum, uint64_t lvalue, uint64_t hvalue)
+{
+ int retval = ERROR_FAIL;
+ struct arm_dpm *dpm = &armv8->dpm;
+
+ switch (regnum) {
+ case ARMV8_V0 ... ARMV8_V31:
+ retval = dpm->instr_write_data_r0_64(dpm,
+ ARMV8_MOV_VFP_GPR((regnum - ARMV8_V0), 0, 1), hvalue);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = dpm->instr_write_data_r0_64(dpm,
+ ARMV8_MOV_VFP_GPR((regnum - ARMV8_V0), 0, 0), lvalue);
+ break;
+
+ default:
+ retval = ERROR_FAIL;
+ break;
+ }
+
+ return retval;
+}
+
static int armv8_read_reg32(struct armv8_common *armv8, int regnum, uint64_t *regval)
{
struct arm_dpm *dpm = &armv8->dpm;
@@ -338,6 +412,11 @@ static int armv8_read_reg32(struct armv8_common *armv8, int regnum, uint64_t *re
ARMV8_MRS_xPSR_T1(1, 0),
&value);
break;
+ case ARMV8_FPSR:
+ /* "VMRS r0, FPSCR"; then return via DCC */
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV4_5_VMRS(0), &value);
+ break;
default:
retval = ERROR_FAIL;
break;
@@ -349,6 +428,56 @@ static int armv8_read_reg32(struct armv8_common *armv8, int regnum, uint64_t *re
return retval;
}
+static int armv8_read_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum, uint64_t *lvalue, uint64_t *hvalue)
+{
+ int retval = ERROR_FAIL;
+ struct arm_dpm *dpm = &armv8->dpm;
+ struct reg *reg_r1 = dpm->arm->core_cache->reg_list + ARMV8_R1;
+ uint32_t value_r0 = 0, value_r1 = 0;
+ unsigned num = (regnum - ARMV8_V0) << 1;
+
+ switch (regnum) {
+ case ARMV8_V0 ... ARMV8_V15:
+ /* we are going to write R1, mark it dirty */
+ reg_r1->dirty = true;
+ /* move from double word register to r0:r1: "vmov r0, r1, vm"
+ * then read r0 via dcc
+ */
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV4_5_VMOV(1, 1, 0, (num >> 4), (num & 0xf)),
+ &value_r0);
+ /* read r1 via dcc */
+ retval = dpm->instr_read_data_dcc(dpm,
+ ARMV4_5_MCR(14, 0, 1, 0, 5, 0),
+ &value_r1);
+ if (retval == ERROR_OK) {
+ *lvalue = value_r1;
+ *lvalue = ((*lvalue) << 32) | value_r0;
+ } else
+ return retval;
+
+ num++;
+ /* repeat above steps for high 64 bits of V register */
+ retval = dpm->instr_read_data_r0(dpm,
+ ARMV4_5_VMOV(1, 1, 0, (num >> 4), (num & 0xf)),
+ &value_r0);
+ retval = dpm->instr_read_data_dcc(dpm,
+ ARMV4_5_MCR(14, 0, 1, 0, 5, 0),
+ &value_r1);
+ if (retval == ERROR_OK) {
+ *hvalue = value_r1;
+ *hvalue = ((*hvalue) << 32) | value_r0;
+ } else
+ return retval;
+ break;
+ default:
+ retval = ERROR_FAIL;
+ break;
+ }
+
+ return retval;
+}
+
static int armv8_write_reg32(struct armv8_common *armv8, int regnum, uint64_t value)
{
struct arm_dpm *dpm = &armv8->dpm;
@@ -417,6 +546,11 @@ static int armv8_write_reg32(struct armv8_common *armv8, int regnum, uint64_t va
ARMV8_MSR_GP_xPSR_T1(1, 0, 15),
value);
break;
+ case ARMV8_FPSR:
+ /* move to r0 from DCC, then "VMSR FPSCR, r0" */
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV4_5_VMSR(0), value);
+ break;
default:
retval = ERROR_FAIL;
break;
@@ -426,14 +560,63 @@ static int armv8_write_reg32(struct armv8_common *armv8, int regnum, uint64_t va
}
+static int armv8_write_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum, uint64_t lvalue, uint64_t hvalue)
+{
+ int retval = ERROR_FAIL;
+ struct arm_dpm *dpm = &armv8->dpm;
+ struct reg *reg_r1 = dpm->arm->core_cache->reg_list + ARMV8_R1;
+ uint32_t value_r0 = 0, value_r1 = 0;
+ unsigned num = (regnum - ARMV8_V0) << 1;
+
+ switch (regnum) {
+ case ARMV8_V0 ... ARMV8_V15:
+ /* we are going to write R1, mark it dirty */
+ reg_r1->dirty = true;
+ value_r1 = lvalue >> 32;
+ value_r0 = lvalue & 0xFFFFFFFF;
+ /* write value_r1 to r1 via dcc */
+ retval = dpm->instr_write_data_dcc(dpm,
+ ARMV4_5_MRC(14, 0, 1, 0, 5, 0),
+ value_r1);
+ /* write value_r0 to r0 via dcc then,
+ * move to double word register from r0:r1: "vmov vm, r0, r1"
+ */
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV4_5_VMOV(0, 1, 0, (num >> 4), (num & 0xf)),
+ value_r0);
+
+ num++;
+ /* repeat above steps for high 64 bits of V register */
+ value_r1 = hvalue >> 32;
+ value_r0 = hvalue & 0xFFFFFFFF;
+ retval = dpm->instr_write_data_dcc(dpm,
+ ARMV4_5_MRC(14, 0, 1, 0, 5, 0),
+ value_r1);
+ retval = dpm->instr_write_data_r0(dpm,
+ ARMV4_5_VMOV(0, 1, 0, (num >> 4), (num & 0xf)),
+ value_r0);
+ break;
+ default:
+ retval = ERROR_FAIL;
+ break;
+ }
+
+ return retval;
+}
+
void armv8_select_reg_access(struct armv8_common *armv8, bool is_aarch64)
{
if (is_aarch64) {
armv8->read_reg_u64 = armv8_read_reg;
armv8->write_reg_u64 = armv8_write_reg;
+ armv8->read_reg_u128 = armv8_read_reg_simdfp_aarch64;
+ armv8->write_reg_u128 = armv8_write_reg_simdfp_aarch64;
+
} else {
armv8->read_reg_u64 = armv8_read_reg32;
armv8->write_reg_u64 = armv8_write_reg32;
+ armv8->read_reg_u128 = armv8_read_reg_simdfp_aarch32;
+ armv8->write_reg_u128 = armv8_write_reg_simdfp_aarch32;
}
}
@@ -441,6 +624,7 @@ void armv8_select_reg_access(struct armv8_common *armv8, bool is_aarch64)
int armv8_read_mpidr(struct armv8_common *armv8)
{
int retval = ERROR_FAIL;
+ struct arm *arm = &armv8->arm;
struct arm_dpm *dpm = armv8->arm.dpm;
uint32_t mpidr;
@@ -448,6 +632,13 @@ int armv8_read_mpidr(struct armv8_common *armv8)
if (retval != ERROR_OK)
goto done;
+ /* check if we're in an unprivileged mode */
+ if (armv8_curel_from_core_mode(arm->core_mode) < SYSTEM_CUREL_EL1) {
+ retval = armv8_dpm_modeswitch(dpm, ARMV8_64_EL1H);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
retval = dpm->instr_read_data_r0(dpm, armv8_opcode(armv8, READ_REG_MPIDR), &mpidr);
if (retval != ERROR_OK)
goto done;
@@ -463,6 +654,7 @@ int armv8_read_mpidr(struct armv8_common *armv8)
LOG_ERROR("mpidr not in multiprocessor format");
done:
+ armv8_dpm_modeswitch(dpm, ARM_MODE_ANY);
dpm->finish(dpm);
return retval;
}
@@ -825,11 +1017,24 @@ int armv8_handle_cache_info_command(struct command_context *cmd_ctx,
return ERROR_OK;
}
+static int armv8_setup_semihosting(struct target *target, int enable)
+{
+ struct arm *arm = target_to_arm(target);
+
+ if (arm->core_state != ARM_STATE_AARCH64) {
+ LOG_ERROR("semihosting only supported in AArch64 state\n");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
int armv8_init_arch_info(struct target *target, struct armv8_common *armv8)
{
struct arm *arm = &armv8->arm;
arm->arch_info = armv8;
target->arch_info = &armv8->arm;
+ arm->setup_semihosting = armv8_setup_semihosting;
/* target is useful in all function arm v4 5 compatible */
armv8->arm.target = target;
armv8->arm.common_magic = ARM_COMMON_MAGIC;
@@ -858,7 +1063,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 && target->semihosting->is_active) ? ", semihosting" : "");
return ERROR_OK;
}
@@ -897,6 +1102,152 @@ int armv8_arch_state(struct target *target)
return ERROR_OK;
}
+static struct reg_data_type aarch64_vector_base_types[] = {
+ {REG_TYPE_IEEE_DOUBLE, "ieee_double", 0, {NULL} },
+ {REG_TYPE_UINT64, "uint64", 0, {NULL} },
+ {REG_TYPE_INT64, "int64", 0, {NULL} },
+ {REG_TYPE_IEEE_SINGLE, "ieee_single", 0, {NULL} },
+ {REG_TYPE_UINT32, "uint32", 0, {NULL} },
+ {REG_TYPE_INT32, "int32", 0, {NULL} },
+ {REG_TYPE_UINT16, "uint16", 0, {NULL} },
+ {REG_TYPE_INT16, "int16", 0, {NULL} },
+ {REG_TYPE_UINT8, "uint8", 0, {NULL} },
+ {REG_TYPE_INT8, "int8", 0, {NULL} },
+ {REG_TYPE_UINT128, "uint128", 0, {NULL} },
+ {REG_TYPE_INT128, "int128", 0, {NULL} }
+};
+
+static struct reg_data_type_vector aarch64_vector_types[] = {
+ {aarch64_vector_base_types + 0, 2},
+ {aarch64_vector_base_types + 1, 2},
+ {aarch64_vector_base_types + 2, 2},
+ {aarch64_vector_base_types + 3, 4},
+ {aarch64_vector_base_types + 4, 4},
+ {aarch64_vector_base_types + 5, 4},
+ {aarch64_vector_base_types + 6, 8},
+ {aarch64_vector_base_types + 7, 8},
+ {aarch64_vector_base_types + 8, 16},
+ {aarch64_vector_base_types + 9, 16},
+ {aarch64_vector_base_types + 10, 01},
+ {aarch64_vector_base_types + 11, 01},
+};
+
+static struct reg_data_type aarch64_fpu_vector[] = {
+ {REG_TYPE_ARCH_DEFINED, "v2d", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 0} },
+ {REG_TYPE_ARCH_DEFINED, "v2u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 1} },
+ {REG_TYPE_ARCH_DEFINED, "v2i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 2} },
+ {REG_TYPE_ARCH_DEFINED, "v4f", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 3} },
+ {REG_TYPE_ARCH_DEFINED, "v4u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 4} },
+ {REG_TYPE_ARCH_DEFINED, "v4i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 5} },
+ {REG_TYPE_ARCH_DEFINED, "v8u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 6} },
+ {REG_TYPE_ARCH_DEFINED, "v8i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 7} },
+ {REG_TYPE_ARCH_DEFINED, "v16u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 8} },
+ {REG_TYPE_ARCH_DEFINED, "v16i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 9} },
+ {REG_TYPE_ARCH_DEFINED, "v1u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 10} },
+ {REG_TYPE_ARCH_DEFINED, "v1i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 11} },
+};
+
+static struct reg_data_type_union_field aarch64_union_fields_vnd[] = {
+ {"f", aarch64_fpu_vector + 0, aarch64_union_fields_vnd + 1},
+ {"u", aarch64_fpu_vector + 1, aarch64_union_fields_vnd + 2},
+ {"s", aarch64_fpu_vector + 2, NULL},
+};
+
+static struct reg_data_type_union_field aarch64_union_fields_vns[] = {
+ {"f", aarch64_fpu_vector + 3, aarch64_union_fields_vns + 1},
+ {"u", aarch64_fpu_vector + 4, aarch64_union_fields_vns + 2},
+ {"s", aarch64_fpu_vector + 5, NULL},
+};
+
+static struct reg_data_type_union_field aarch64_union_fields_vnh[] = {
+ {"u", aarch64_fpu_vector + 6, aarch64_union_fields_vnh + 1},
+ {"s", aarch64_fpu_vector + 7, NULL},
+};
+
+static struct reg_data_type_union_field aarch64_union_fields_vnb[] = {
+ {"u", aarch64_fpu_vector + 8, aarch64_union_fields_vnb + 1},
+ {"s", aarch64_fpu_vector + 9, NULL},
+};
+
+static struct reg_data_type_union_field aarch64_union_fields_vnq[] = {
+ {"u", aarch64_fpu_vector + 10, aarch64_union_fields_vnq + 1},
+ {"s", aarch64_fpu_vector + 11, NULL},
+};
+
+static struct reg_data_type_union aarch64_union_types[] = {
+ {aarch64_union_fields_vnd},
+ {aarch64_union_fields_vns},
+ {aarch64_union_fields_vnh},
+ {aarch64_union_fields_vnb},
+ {aarch64_union_fields_vnq},
+};
+
+static struct reg_data_type aarch64_fpu_union[] = {
+ {REG_TYPE_ARCH_DEFINED, "vnd", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 0} },
+ {REG_TYPE_ARCH_DEFINED, "vns", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 1} },
+ {REG_TYPE_ARCH_DEFINED, "vnh", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 2} },
+ {REG_TYPE_ARCH_DEFINED, "vnb", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 3} },
+ {REG_TYPE_ARCH_DEFINED, "vnq", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 4} },
+};
+
+static struct reg_data_type_union_field aarch64v_union_fields[] = {
+ {"d", aarch64_fpu_union + 0, aarch64v_union_fields + 1},
+ {"s", aarch64_fpu_union + 1, aarch64v_union_fields + 2},
+ {"h", aarch64_fpu_union + 2, aarch64v_union_fields + 3},
+ {"b", aarch64_fpu_union + 3, aarch64v_union_fields + 4},
+ {"q", aarch64_fpu_union + 4, NULL},
+};
+
+static struct reg_data_type_union aarch64v_union[] = {
+ {aarch64v_union_fields}
+};
+
+static struct reg_data_type aarch64v[] = {
+ {REG_TYPE_ARCH_DEFINED, "aarch64v", REG_TYPE_CLASS_UNION,
+ {.reg_type_union = aarch64v_union} },
+};
+
+static struct reg_data_type_bitfield aarch64_cpsr_bits[] = {
+ { 0, 0 , REG_TYPE_UINT8 },
+ { 2, 3, REG_TYPE_UINT8 },
+ { 4, 4 , REG_TYPE_UINT8 },
+ { 6, 6 , REG_TYPE_BOOL },
+ { 7, 7 , REG_TYPE_BOOL },
+ { 8, 8 , REG_TYPE_BOOL },
+ { 9, 9 , REG_TYPE_BOOL },
+ { 20, 20, REG_TYPE_BOOL },
+ { 21, 21, REG_TYPE_BOOL },
+ { 28, 28, REG_TYPE_BOOL },
+ { 29, 29, REG_TYPE_BOOL },
+ { 30, 30, REG_TYPE_BOOL },
+ { 31, 31, REG_TYPE_BOOL },
+};
+
+static struct reg_data_type_flags_field aarch64_cpsr_fields[] = {
+ { "SP", aarch64_cpsr_bits + 0, aarch64_cpsr_fields + 1 },
+ { "EL", aarch64_cpsr_bits + 1, aarch64_cpsr_fields + 2 },
+ { "nRW", aarch64_cpsr_bits + 2, aarch64_cpsr_fields + 3 },
+ { "F" , aarch64_cpsr_bits + 3, aarch64_cpsr_fields + 4 },
+ { "I" , aarch64_cpsr_bits + 4, aarch64_cpsr_fields + 5 },
+ { "A" , aarch64_cpsr_bits + 5, aarch64_cpsr_fields + 6 },
+ { "D" , aarch64_cpsr_bits + 6, aarch64_cpsr_fields + 7 },
+ { "IL" , aarch64_cpsr_bits + 7, aarch64_cpsr_fields + 8 },
+ { "SS" , aarch64_cpsr_bits + 8, aarch64_cpsr_fields + 9 },
+ { "V" , aarch64_cpsr_bits + 9, aarch64_cpsr_fields + 10 },
+ { "C" , aarch64_cpsr_bits + 10, aarch64_cpsr_fields + 11 },
+ { "Z" , aarch64_cpsr_bits + 11, aarch64_cpsr_fields + 12 },
+ { "N" , aarch64_cpsr_bits + 12, NULL }
+};
+
+static struct reg_data_type_flags aarch64_cpsr_flags[] = {
+ { 4, aarch64_cpsr_fields }
+};
+
+static struct reg_data_type aarch64_flags_cpsr[] = {
+ {REG_TYPE_ARCH_DEFINED, "cpsr_flags", REG_TYPE_CLASS_FLAGS,
+ {.reg_type_flags = aarch64_cpsr_flags} },
+};
+
static const struct {
unsigned id;
const char *name;
@@ -905,59 +1256,104 @@ static const struct {
enum reg_type type;
const char *group;
const char *feature;
+ struct reg_data_type *data_type;
} armv8_regs[] = {
- { ARMV8_R0, "x0", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R1, "x1", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R2, "x2", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R3, "x3", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R4, "x4", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R5, "x5", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R6, "x6", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R7, "x7", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R8, "x8", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R9, "x9", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R10, "x10", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R11, "x11", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R12, "x12", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R13, "x13", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R14, "x14", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R15, "x15", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R16, "x16", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R17, "x17", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R18, "x18", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R19, "x19", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R20, "x20", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R21, "x21", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R22, "x22", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R23, "x23", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R24, "x24", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R25, "x25", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R26, "x26", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R27, "x27", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R28, "x28", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R29, "x29", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_R30, "x30", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" },
-
- { ARMV8_SP, "sp", 64, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.aarch64.core" },
- { ARMV8_PC, "pc", 64, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.aarch64.core" },
-
- { ARMV8_xPSR, "CPSR", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.aarch64.core" },
-
- { ARMV8_ELR_EL1, "ELR_EL1", 64, ARMV8_64_EL1H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked" },
- { ARMV8_ESR_EL1, "ESR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" },
- { ARMV8_SPSR_EL1, "SPSR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" },
-
- { ARMV8_ELR_EL2, "ELR_EL2", 64, ARMV8_64_EL2H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked" },
- { ARMV8_ESR_EL2, "ESR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" },
- { ARMV8_SPSR_EL2, "SPSR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" },
-
- { ARMV8_ELR_EL3, "ELR_EL3", 64, ARMV8_64_EL3H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked" },
- { ARMV8_ESR_EL3, "ESR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" },
- { ARMV8_SPSR_EL3, "SPSR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" },
+ { ARMV8_R0, "x0", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R1, "x1", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R2, "x2", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R3, "x3", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R4, "x4", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R5, "x5", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R6, "x6", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R7, "x7", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R8, "x8", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R9, "x9", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R10, "x10", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R11, "x11", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R12, "x12", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R13, "x13", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R14, "x14", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R15, "x15", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R16, "x16", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R17, "x17", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R18, "x18", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R19, "x19", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R20, "x20", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R21, "x21", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R22, "x22", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R23, "x23", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R24, "x24", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R25, "x25", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R26, "x26", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R27, "x27", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R28, "x28", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R29, "x29", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_R30, "x30", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL},
+
+ { ARMV8_SP, "sp", 64, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_PC, "pc", 64, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.aarch64.core", NULL},
+ { ARMV8_xPSR, "cpsr", 32, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED,
+ "general", "org.gnu.gdb.aarch64.core", aarch64_flags_cpsr},
+ { ARMV8_V0, "v0", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V1, "v1", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V2, "v2", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V3, "v3", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V4, "v4", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V5, "v5", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V6, "v6", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V7, "v7", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V8, "v8", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V9, "v9", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V10, "v10", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V11, "v11", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V12, "v12", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V13, "v13", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V14, "v14", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V15, "v15", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V16, "v16", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V17, "v17", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V18, "v18", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V19, "v19", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V20, "v20", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V21, "v21", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V22, "v22", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V23, "v23", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V24, "v24", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V25, "v25", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V26, "v26", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V27, "v27", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V28, "v28", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V29, "v29", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V30, "v30", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_V31, "v31", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v},
+ { ARMV8_FPSR, "fpsr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "simdfp", "org.gnu.gdb.aarch64.fpu", NULL},
+ { ARMV8_FPCR, "fpcr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "simdfp", "org.gnu.gdb.aarch64.fpu", NULL},
+
+ { ARMV8_ELR_EL1, "ELR_EL1", 64, ARMV8_64_EL1H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+ { ARMV8_ESR_EL1, "ESR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+ { ARMV8_SPSR_EL1, "SPSR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+
+ { ARMV8_ELR_EL2, "ELR_EL2", 64, ARMV8_64_EL2H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+ { ARMV8_ESR_EL2, "ESR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+ { ARMV8_SPSR_EL2, "SPSR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+
+ { ARMV8_ELR_EL3, "ELR_EL3", 64, ARMV8_64_EL3H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+ { ARMV8_ESR_EL3, "ESR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked",
+ NULL},
+ { ARMV8_SPSR_EL3, "SPSR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked",
+ NULL},
};
static const struct {
unsigned id;
+ unsigned mapping;
const char *name;
unsigned bits;
enum arm_mode mode;
@@ -965,23 +1361,56 @@ static const struct {
const char *group;
const char *feature;
} armv8_regs32[] = {
- { ARMV8_R0, "r0", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R1, "r1", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R2, "r2", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R3, "r3", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R4, "r4", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R5, "r5", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R6, "r6", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R7, "r7", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R8, "r8", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R9, "r9", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R10, "r10", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R11, "r11", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R12, "r12", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R13, "sp", 32, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_R14, "lr", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_PC, "pc", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" },
- { ARMV8_xPSR, "cpsr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R0, 0, "r0", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R1, 0, "r1", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R2, 0, "r2", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R3, 0, "r3", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R4, 0, "r4", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R5, 0, "r5", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R6, 0, "r6", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R7, 0, "r7", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R8, 0, "r8", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R9, 0, "r9", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R10, 0, "r10", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R11, 0, "r11", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R12, 0, "r12", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R13, 0, "sp", 32, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_R14, 0, "lr", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_PC, 0, "pc", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_xPSR, 0, "cpsr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" },
+ { ARMV8_V0, 0, "d0", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V0, 8, "d1", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V1, 0, "d2", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V1, 8, "d3", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V2, 0, "d4", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V2, 8, "d5", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V3, 0, "d6", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V3, 8, "d7", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V4, 0, "d8", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V4, 8, "d9", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V5, 0, "d10", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V5, 8, "d11", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V6, 0, "d12", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V6, 8, "d13", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V7, 0, "d14", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V7, 8, "d15", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V8, 0, "d16", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V8, 8, "d17", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V9, 0, "d18", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V9, 8, "d19", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V10, 0, "d20", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V10, 8, "d21", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V11, 0, "d22", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V11, 8, "d23", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V12, 0, "d24", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V12, 8, "d25", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V13, 0, "d26", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V13, 8, "d27", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V14, 0, "d28", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V14, 8, "d29", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V15, 0, "d30", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_V15, 8, "d31", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"},
+ { ARMV8_FPSR, 0, "fpscr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "float", "org.gnu.gdb.arm.vfp"},
};
#define ARMV8_NUM_REGS ARRAY_SIZE(armv8_regs)
@@ -1004,15 +1433,23 @@ static int armv8_set_core_reg(struct reg *reg, uint8_t *buf)
struct arm_reg *armv8_reg = reg->arch_info;
struct target *target = armv8_reg->target;
struct arm *arm = target_to_arm(target);
- uint64_t value = buf_get_u64(buf, 0, 64);
+ uint64_t value = buf_get_u64(buf, 0, reg->size);
if (target->state != TARGET_HALTED)
return ERROR_TARGET_NOT_HALTED;
- if (reg == arm->cpsr) {
- armv8_set_cpsr(arm, (uint32_t)value);
- } else {
+ if (reg->size <= 64) {
+ if (reg == arm->cpsr)
+ armv8_set_cpsr(arm, (uint32_t)value);
+ else {
+ buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = 1;
+ }
+ } else if (reg->size <= 128) {
+ uint64_t hvalue = buf_get_u64(buf + 8, 0, reg->size - 64);
+
buf_set_u64(reg->value, 0, 64, value);
+ buf_set_u64(reg->value + 8, 0, reg->size - 64, hvalue);
reg->valid = 1;
}
@@ -1061,7 +1498,12 @@ static int armv8_set_core_reg32(struct reg *reg, uint8_t *buf)
if (reg64 == arm->cpsr) {
armv8_set_cpsr(arm, value);
} else {
- buf_set_u32(reg->value, 0, 32, value);
+ if (reg->size <= 32)
+ buf_set_u32(reg->value, 0, 32, value);
+ else if (reg->size <= 64) {
+ uint64_t value64 = buf_get_u64(buf, 0, 64);
+ buf_set_u64(reg->value, 0, 64, value64);
+ }
reg->valid = 1;
reg64->valid = 1;
}
@@ -1123,9 +1565,12 @@ struct reg_cache *armv8_build_reg_cache(struct target *target)
LOG_ERROR("unable to allocate feature list");
reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type));
- if (reg_list[i].reg_data_type)
- reg_list[i].reg_data_type->type = armv8_regs[i].type;
- else
+ if (reg_list[i].reg_data_type) {
+ if (armv8_regs[i].data_type == NULL)
+ reg_list[i].reg_data_type->type = armv8_regs[i].type;
+ else
+ *reg_list[i].reg_data_type = *armv8_regs[i].data_type;
+ } else
LOG_ERROR("unable to allocate reg type list");
}
@@ -1142,7 +1587,7 @@ struct reg_cache *armv8_build_reg_cache(struct target *target)
for (i = 0; i < num_regs32; i++) {
reg_list32[i].name = armv8_regs32[i].name;
reg_list32[i].size = armv8_regs32[i].bits;
- reg_list32[i].value = &arch_info[armv8_regs32[i].id].value[0];
+ reg_list32[i].value = &arch_info[armv8_regs32[i].id].value[armv8_regs32[i].mapping];
reg_list32[i].type = &armv8_reg32_type;
reg_list32[i].arch_info = &arch_info[armv8_regs32[i].id];
reg_list32[i].group = armv8_regs32[i].group;
@@ -1179,14 +1624,45 @@ struct reg *armv8_reg_current(struct arm *arm, unsigned regnum)
return r;
}
+static void armv8_free_cache(struct reg_cache *cache, bool regs32)
+{
+ struct reg *reg;
+ unsigned int i;
+
+ if (!cache)
+ return;
+
+ for (i = 0; i < cache->num_regs; i++) {
+ reg = &cache->reg_list[i];
+
+ free(reg->feature);
+ free(reg->reg_data_type);
+ }
+
+ if (!regs32)
+ free(cache->reg_list[0].arch_info);
+ free(cache->reg_list);
+ free(cache);
+}
+
+void armv8_free_reg_cache(struct target *target)
+{
+ struct armv8_common *armv8 = target_to_armv8(target);
+ struct arm *arm = &armv8->arm;
+ struct reg_cache *cache = NULL, *cache32 = NULL;
+
+ cache = arm->core_cache;
+ if (cache != NULL)
+ cache32 = cache->next;
+ armv8_free_cache(cache32, true);
+ armv8_free_cache(cache, false);
+ arm->core_cache = NULL;
+}
+
const struct command_registration armv8_command_handlers[] = {
- {
- .chain = dap_command_handlers,
- },
COMMAND_REGISTRATION_DONE
};
-
int armv8_get_gdb_reg_list(struct target *target,
struct reg **reg_list[], int *reg_list_size,
enum target_register_class reg_class)
@@ -1200,7 +1676,7 @@ int armv8_get_gdb_reg_list(struct target *target,
switch (reg_class) {
case REG_CLASS_GENERAL:
- *reg_list_size = ARMV8_ELR_EL1;
+ *reg_list_size = ARMV8_V0;
*reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));
for (i = 0; i < *reg_list_size; i++)
@@ -1227,6 +1703,13 @@ int armv8_get_gdb_reg_list(struct target *target,
switch (reg_class) {
case REG_CLASS_GENERAL:
+ *reg_list_size = ARMV8_R14 + 3;
+ *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));
+
+ for (i = 0; i < *reg_list_size; i++)
+ (*reg_list)[i] = cache32->reg_list + i;
+
+ return ERROR_OK;
case REG_CLASS_ALL:
*reg_list_size = cache32->num_regs;
*reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));
diff --git a/src/target/armv8.h b/src/target/armv8.h
index 0f3e66f..dfd54ed 100644
--- a/src/target/armv8.h
+++ b/src/target/armv8.h
@@ -63,21 +63,62 @@ enum {
ARMV8_PC = 32,
ARMV8_xPSR = 33,
- ARMV8_ELR_EL1 = 34,
- ARMV8_ESR_EL1 = 35,
- ARMV8_SPSR_EL1 = 36,
-
- ARMV8_ELR_EL2 = 37,
- ARMV8_ESR_EL2 = 38,
- ARMV8_SPSR_EL2 = 39,
-
- ARMV8_ELR_EL3 = 40,
- ARMV8_ESR_EL3 = 41,
- ARMV8_SPSR_EL3 = 42,
+ ARMV8_V0 = 34,
+ ARMV8_V1,
+ ARMV8_V2,
+ ARMV8_V3,
+ ARMV8_V4,
+ ARMV8_V5,
+ ARMV8_V6,
+ ARMV8_V7,
+ ARMV8_V8,
+ ARMV8_V9,
+ ARMV8_V10,
+ ARMV8_V11,
+ ARMV8_V12,
+ ARMV8_V13,
+ ARMV8_V14,
+ ARMV8_V15,
+ ARMV8_V16,
+ ARMV8_V17,
+ ARMV8_V18,
+ ARMV8_V19,
+ ARMV8_V20,
+ ARMV8_V21,
+ ARMV8_V22,
+ ARMV8_V23,
+ ARMV8_V24,
+ ARMV8_V25,
+ ARMV8_V26,
+ ARMV8_V27,
+ ARMV8_V28,
+ ARMV8_V29,
+ ARMV8_V30,
+ ARMV8_V31,
+ ARMV8_FPSR,
+ ARMV8_FPCR,
+
+ ARMV8_ELR_EL1 = 68,
+ ARMV8_ESR_EL1 = 69,
+ ARMV8_SPSR_EL1 = 70,
+
+ ARMV8_ELR_EL2 = 71,
+ ARMV8_ESR_EL2 = 72,
+ ARMV8_SPSR_EL2 = 73,
+
+ ARMV8_ELR_EL3 = 74,
+ ARMV8_ESR_EL3 = 75,
+ ARMV8_SPSR_EL3 = 76,
ARMV8_LAST_REG,
};
+enum run_control_op {
+ ARMV8_RUNCONTROL_UNKNOWN = 0,
+ ARMV8_RUNCONTROL_RESUME = 1,
+ ARMV8_RUNCONTROL_HALT = 2,
+ ARMV8_RUNCONTROL_STEP = 3,
+};
#define ARMV8_COMMON_MAGIC 0x0A450AAA
@@ -175,10 +216,19 @@ struct armv8_common {
struct arm_cti *cti;
+ /* last run-control command issued to this target (resume, halt, step) */
+ enum run_control_op last_run_control_op;
+
/* Direct processor core register read and writes */
int (*read_reg_u64)(struct armv8_common *armv8, int num, uint64_t *value);
int (*write_reg_u64)(struct armv8_common *armv8, int num, uint64_t value);
+ /* SIMD/FPU registers read/write interface */
+ int (*read_reg_u128)(struct armv8_common *armv8, int num,
+ uint64_t *lvalue, uint64_t *hvalue);
+ int (*write_reg_u128)(struct armv8_common *armv8, int num,
+ uint64_t lvalue, uint64_t hvalue);
+
int (*examine_debug_reason)(struct target *target);
int (*post_debug_entry)(struct target *target);
@@ -191,6 +241,11 @@ target_to_armv8(struct target *target)
return container_of(target->arch_info, struct armv8_common, arm);
}
+static inline bool is_armv8(struct armv8_common *armv8)
+{
+ return armv8->common_magic == ARMV8_COMMON_MAGIC;
+}
+
/* register offsets from armv8.debug_base */
#define CPUV8_DBG_MAINID0 0xD00
#define CPUV8_DBG_CPUFEATURE0 0xD20
@@ -277,6 +332,8 @@ static inline unsigned int armv8_curel_from_core_mode(enum arm_mode core_mode)
void armv8_select_reg_access(struct armv8_common *armv8, bool is_aarch64);
int armv8_set_dbgreg_bits(struct armv8_common *armv8, unsigned int reg, unsigned long mask, unsigned long value);
+extern void armv8_free_reg_cache(struct target *target);
+
extern const struct command_registration armv8_command_handlers[];
#endif /* OPENOCD_TARGET_ARMV8_H */
diff --git a/src/target/armv8_cache.c b/src/target/armv8_cache.c
index 7f610c9..40965eb 100644
--- a/src/target/armv8_cache.c
+++ b/src/target/armv8_cache.c
@@ -310,6 +310,7 @@ int armv8_identify_cache(struct armv8_common *armv8)
{
/* read cache descriptor */
int retval = ERROR_FAIL;
+ struct arm *arm = &armv8->arm;
struct arm_dpm *dpm = armv8->arm.dpm;
uint32_t csselr, clidr, ctr;
uint32_t cache_reg;
@@ -320,6 +321,13 @@ int armv8_identify_cache(struct armv8_common *armv8)
if (retval != ERROR_OK)
goto done;
+ /* check if we're in an unprivileged mode */
+ if (armv8_curel_from_core_mode(arm->core_mode) < SYSTEM_CUREL_EL1) {
+ retval = armv8_dpm_modeswitch(dpm, ARMV8_64_EL1H);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
/* retrieve CTR */
retval = dpm->instr_read_data_r0(dpm,
armv8_opcode(armv8, READ_REG_CTR), &ctr);
@@ -417,6 +425,7 @@ int armv8_identify_cache(struct armv8_common *armv8)
}
done:
+ armv8_dpm_modeswitch(dpm, ARM_MODE_ANY);
dpm->finish(dpm);
return retval;
diff --git a/src/target/armv8_dpm.c b/src/target/armv8_dpm.c
index c79b1a0..3c941fa 100644
--- a/src/target/armv8_dpm.c
+++ b/src/target/armv8_dpm.c
@@ -258,7 +258,7 @@ static int dpmv8_exec_opcode(struct arm_dpm *dpm,
if (dscr & DSCR_ERR) {
LOG_ERROR("Opcode 0x%08"PRIx32", DSCR.ERR=1, DSCR.EL=%i", opcode, dpm->last_el);
- armv8_dpm_handle_exception(dpm);
+ armv8_dpm_handle_exception(dpm, true);
retval = ERROR_FAIL;
}
@@ -600,7 +600,7 @@ int armv8_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
armv8_opcode(armv8, ARMV8_OPC_DCPS) | target_el);
/* DCPS clobbers registers just like an exception taken */
- armv8_dpm_handle_exception(dpm);
+ armv8_dpm_handle_exception(dpm, false);
} else {
core_state = armv8_dpm_get_core_state(dpm);
if (core_state != ARM_STATE_AARCH64) {
@@ -650,21 +650,37 @@ int armv8_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
static int dpmv8_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
- uint64_t value_64;
- int retval;
+ int retval = ERROR_FAIL;
+
+ if (r->size <= 64) {
+ uint64_t value_64;
+ retval = armv8->read_reg_u64(armv8, regnum, &value_64);
+
+ if (retval == ERROR_OK) {
+ r->valid = true;
+ r->dirty = false;
+ buf_set_u64(r->value, 0, r->size, value_64);
+ if (r->size == 64)
+ LOG_DEBUG("READ: %s, %16.8llx", r->name, (unsigned long long) value_64);
+ else
+ LOG_DEBUG("READ: %s, %8.8x", r->name, (unsigned int) value_64);
+ }
+ } else if (r->size <= 128) {
+ uint64_t lvalue = 0, hvalue = 0;
+ retval = armv8->read_reg_u128(armv8, regnum, &lvalue, &hvalue);
+
+ if (retval == ERROR_OK) {
+ r->valid = true;
+ r->dirty = false;
- retval = armv8->read_reg_u64(armv8, regnum, &value_64);
+ buf_set_u64(r->value, 0, 64, lvalue);
+ buf_set_u64(r->value + 8, 0, r->size - 64, hvalue);
- if (retval == ERROR_OK) {
- r->valid = true;
- r->dirty = false;
- buf_set_u64(r->value, 0, r->size, value_64);
- if (r->size == 64)
- LOG_DEBUG("READ: %s, %16.8llx", r->name, (unsigned long long) value_64);
- else
- LOG_DEBUG("READ: %s, %8.8x", r->name, (unsigned int) value_64);
+ LOG_DEBUG("READ: %s, lvalue=%16.8llx", r->name, (unsigned long long) lvalue);
+ LOG_DEBUG("READ: %s, hvalue=%16.8llx", r->name, (unsigned long long) hvalue);
+ }
}
- return ERROR_OK;
+ return retval;
}
/*
@@ -674,20 +690,36 @@ static int dpmv8_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
int retval = ERROR_FAIL;
- uint64_t value_64;
- value_64 = buf_get_u64(r->value, 0, r->size);
+ if (r->size <= 64) {
+ uint64_t value_64;
+
+ value_64 = buf_get_u64(r->value, 0, r->size);
+ retval = armv8->write_reg_u64(armv8, regnum, value_64);
- retval = armv8->write_reg_u64(armv8, regnum, value_64);
- if (retval == ERROR_OK) {
- r->dirty = false;
- if (r->size == 64)
- LOG_DEBUG("WRITE: %s, %16.8llx", r->name, (unsigned long long)value_64);
- else
- LOG_DEBUG("WRITE: %s, %8.8x", r->name, (unsigned int)value_64);
+ if (retval == ERROR_OK) {
+ r->dirty = false;
+ if (r->size == 64)
+ LOG_DEBUG("WRITE: %s, %16.8llx", r->name, (unsigned long long)value_64);
+ else
+ LOG_DEBUG("WRITE: %s, %8.8x", r->name, (unsigned int)value_64);
+ }
+ } else if (r->size <= 128) {
+ uint64_t lvalue, hvalue;
+
+ lvalue = buf_get_u64(r->value, 0, 64);
+ hvalue = buf_get_u64(r->value + 8, 0, r->size - 64);
+ retval = armv8->write_reg_u128(armv8, regnum, lvalue, hvalue);
+
+ if (retval == ERROR_OK) {
+ r->dirty = false;
+
+ LOG_DEBUG("WRITE: %s, lvalue=%16.8llx", r->name, (unsigned long long) lvalue);
+ LOG_DEBUG("WRITE: %s, hvalue=%16.8llx", r->name, (unsigned long long) hvalue);
+ }
}
- return ERROR_OK;
+ return retval;
}
/**
@@ -745,6 +777,10 @@ int armv8_dpm_read_current_registers(struct arm_dpm *dpm)
if (r->valid)
continue;
+ /* Skip reading FP-SIMD registers */
+ if (r->number >= ARMV8_V0 && r->number <= ARMV8_FPCR)
+ continue;
+
/*
* Only read registers that are available from the
* current EL (or core mode).
@@ -1262,7 +1298,7 @@ void armv8_dpm_report_wfar(struct arm_dpm *dpm, uint64_t addr)
* This function must not perform any actions that trigger another exception
* or a recursion will happen.
*/
-void armv8_dpm_handle_exception(struct arm_dpm *dpm)
+void armv8_dpm_handle_exception(struct arm_dpm *dpm, bool do_restore)
{
struct armv8_common *armv8 = dpm->arm->arch_info;
struct reg_cache *cache = dpm->arm->core_cache;
@@ -1308,7 +1344,8 @@ void armv8_dpm_handle_exception(struct arm_dpm *dpm)
armv8_select_opcodes(armv8, core_state == ARM_STATE_AARCH64);
armv8_select_reg_access(armv8, core_state == ARM_STATE_AARCH64);
- armv8_dpm_modeswitch(dpm, ARM_MODE_ANY);
+ if (do_restore)
+ armv8_dpm_modeswitch(dpm, ARM_MODE_ANY);
}
/*----------------------------------------------------------------------*/
diff --git a/src/target/armv8_dpm.h b/src/target/armv8_dpm.h
index c039359..f404403 100644
--- a/src/target/armv8_dpm.h
+++ b/src/target/armv8_dpm.h
@@ -116,7 +116,7 @@ void armv8_dpm_report_wfar(struct arm_dpm *, uint64_t wfar);
#define PRCR_COREPURQ (1 << 3)
void armv8_dpm_report_dscr(struct arm_dpm *dpm, uint32_t dcsr);
-void armv8_dpm_handle_exception(struct arm_dpm *dpm);
+void armv8_dpm_handle_exception(struct arm_dpm *dpm, bool do_restore);
enum arm_state armv8_dpm_get_core_state(struct arm_dpm *dpm);
#endif /* OPENOCD_TARGET_ARM_DPM_H */
diff --git a/src/target/armv8_opcodes.h b/src/target/armv8_opcodes.h
index 987198a..3fda296 100644
--- a/src/target/armv8_opcodes.h
+++ b/src/target/armv8_opcodes.h
@@ -167,6 +167,14 @@
#define ARMV8_STRH_IP(Rd, Rn) (0x78002400 | (Rn << 5) | Rd)
#define ARMV8_STRW_IP(Rd, Rn) (0xb8004400 | (Rn << 5) | Rd)
+#define ARMV8_MOV_GPR_VFP(Rd, Rn, Index) (0x4e083c00 | (Index << 20) | (Rn << 5) | Rd)
+#define ARMV8_MOV_VFP_GPR(Rd, Rn, Index) (0x4e081c00 | (Index << 20) | (Rn << 5) | Rd)
+
+#define ARMV8_MRS_FPCR(Rt) (0xd53b4400 | (Rt))
+#define ARMV8_MRS_FPSR(Rt) (0xd53b4420 | (Rt))
+#define ARMV8_MSR_FPCR(Rt) (0xd51b4400 | (Rt))
+#define ARMV8_MSR_FPSR(Rt) (0xd51b4420 | (Rt))
+
#define ARMV8_SYS(System, Rt) (0xD5080000 | ((System) << 5) | Rt)
enum armv8_opcode {
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_a.c b/src/target/cortex_a.c
index 5d90e34..4aae5e4 100644
--- a/src/target/cortex_a.c
+++ b/src/target/cortex_a.c
@@ -54,7 +54,7 @@
#include "target_type.h"
#include "arm_opcodes.h"
#include "arm_semihosting.h"
-#include "jtag/swd.h"
+#include "transport/transport.h"
#include <helper/time_support.h>
static int cortex_a_poll(struct target *target);
@@ -206,23 +206,27 @@ static int cortex_a_init_debug_access(struct target *target)
/* lock memory-mapped access to debug registers to prevent
* software interference */
- retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ retval = mem_ap_write_u32(armv7a->debug_ap,
armv7a->debug_base + CPUDBG_LOCKACCESS, 0);
if (retval != ERROR_OK)
return retval;
/* Disable cacheline fills and force cache write-through in debug state */
- retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ retval = mem_ap_write_u32(armv7a->debug_ap,
armv7a->debug_base + CPUDBG_DSCCR, 0);
if (retval != ERROR_OK)
return retval;
/* Disable TLB lookup and refill/eviction in debug state */
- retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ retval = mem_ap_write_u32(armv7a->debug_ap,
armv7a->debug_base + CPUDBG_DSMCR, 0);
if (retval != ERROR_OK)
return retval;
+ retval = dap_run(armv7a->debug_ap->dap);
+ if (retval != ERROR_OK)
+ return retval;
+
/* Enabling of instruction execution in debug mode is done in debug_entry code */
/* Resync breakpoint registers */
@@ -1293,6 +1297,9 @@ static int cortex_a_post_debug_entry(struct target *target)
LOG_DEBUG("cp15_control_reg: %8.8" PRIx32, cortex_a->cp15_control_reg);
cortex_a->cp15_control_reg_curr = cortex_a->cp15_control_reg;
+ if (!armv7a->is_armv7r)
+ armv7a_read_ttbcr(target);
+
if (armv7a->armv7a_mmu.armv7a_cache.info == -1)
armv7a_identify_cache(target);
@@ -1496,10 +1503,22 @@ static int cortex_a_set_breakpoint(struct target *target,
brp_list[brp_i].value);
} else if (breakpoint->type == BKPT_SOFT) {
uint8_t code[4];
+ /* length == 2: Thumb breakpoint */
if (breakpoint->length == 2)
buf_set_u32(code, 0, 32, ARMV5_T_BKPT(0x11));
else
+ /* length == 3: Thumb-2 breakpoint, actual encoding is
+ * a regular Thumb BKPT instruction but we replace a
+ * 32bit Thumb-2 instruction, so fix-up the breakpoint
+ * length
+ */
+ if (breakpoint->length == 3) {
+ buf_set_u32(code, 0, 32, ARMV5_T_BKPT(0x11));
+ breakpoint->length = 4;
+ } else
+ /* length == 4, normal ARM breakpoint */
buf_set_u32(code, 0, 32, ARMV5_BKPT(0x11));
+
retval = target_read_memory(target,
breakpoint->address & 0xFFFFFFFE,
breakpoint->length, 1,
@@ -2931,12 +2950,6 @@ static int cortex_a_examine_first(struct target *target)
int retval = ERROR_OK;
uint32_t didr, cpuid, dbg_osreg;
- retval = dap_dp_init(swjdp);
- if (retval != ERROR_OK) {
- LOG_ERROR("Could not initialize the debug port");
- return retval;
- }
-
/* Search for the APB-AP - it is needed for access to debug registers */
retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv7a->debug_ap);
if (retval != ERROR_OK) {
@@ -3118,22 +3131,13 @@ static int cortex_a_init_target(struct command_context *cmd_ctx,
}
static int cortex_a_init_arch_info(struct target *target,
- struct cortex_a_common *cortex_a, struct jtag_tap *tap)
+ struct cortex_a_common *cortex_a, struct adiv5_dap *dap)
{
struct armv7a_common *armv7a = &cortex_a->armv7a_common;
/* Setup struct cortex_a_common */
cortex_a->common_magic = CORTEX_A_COMMON_MAGIC;
-
- /* tap has no dap initialized */
- if (!tap->dap) {
- tap->dap = dap_init();
-
- /* Leave (only) generic DAP stuff for debugport_init() */
- tap->dap->tap = tap;
- }
-
- armv7a->arm.dap = tap->dap;
+ armv7a->arm.dap = dap;
cortex_a->fast_reg_read = 0;
@@ -3159,19 +3163,34 @@ static int cortex_a_init_arch_info(struct target *target,
static int cortex_a_target_create(struct target *target, Jim_Interp *interp)
{
struct cortex_a_common *cortex_a = calloc(1, sizeof(struct cortex_a_common));
+ cortex_a->common_magic = CORTEX_A_COMMON_MAGIC;
+ struct adiv5_private_config *pc;
+
+ if (target->private_config == NULL)
+ return ERROR_FAIL;
+
+ pc = (struct adiv5_private_config *)target->private_config;
cortex_a->armv7a_common.is_armv7r = false;
- return cortex_a_init_arch_info(target, cortex_a, target->tap);
+ cortex_a->armv7a_common.arm.arm_vfp_version = ARM_VFP_V3;
+
+ return cortex_a_init_arch_info(target, cortex_a, pc->dap);
}
static int cortex_r4_target_create(struct target *target, Jim_Interp *interp)
{
struct cortex_a_common *cortex_a = calloc(1, sizeof(struct cortex_a_common));
+ cortex_a->common_magic = CORTEX_A_COMMON_MAGIC;
+ struct adiv5_private_config *pc;
+
+ pc = (struct adiv5_private_config *)target->private_config;
+ if (adiv5_verify_config(pc) != ERROR_OK)
+ return ERROR_FAIL;
cortex_a->armv7a_common.is_armv7r = true;
- return cortex_a_init_arch_info(target, cortex_a, target->tap);
+ return cortex_a_init_arch_info(target, cortex_a, pc->dap);
}
static void cortex_a_deinit_target(struct target *target)
@@ -3182,6 +3201,7 @@ static void cortex_a_deinit_target(struct target *target)
free(cortex_a->brp_list);
free(dpm->dbp);
free(dpm->dwp);
+ free(target->private_config);
free(cortex_a);
}
@@ -3209,6 +3229,20 @@ static int cortex_a_virt2phys(struct target *target,
struct armv7a_common *armv7a = target_to_armv7a(target);
struct adiv5_dap *swjdp = armv7a->arm.dap;
uint8_t apsel = swjdp->apsel;
+ int mmu_enabled = 0;
+
+ /*
+ * If the MMU was not enabled at debug entry, there is no
+ * way of knowing if there was ever a valid configuration
+ * for it and thus it's not safe to enable it. In this case,
+ * just return the virtual address as physical.
+ */
+ cortex_a_mmu(target, &mmu_enabled);
+ if (!mmu_enabled) {
+ *phys = virt;
+ return ERROR_OK;
+ }
+
if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) {
uint32_t ret;
retval = armv7a_mmu_translate_va(target,
@@ -3404,7 +3438,7 @@ static const struct command_registration cortex_a_exec_command_handlers[] = {
{
.name = "dacrfixup",
.handler = handle_cortex_a_dacrfixup_command,
- .mode = COMMAND_EXEC,
+ .mode = COMMAND_ANY,
.help = "set domain access control (DACR) to all-manager "
"on memory access",
.usage = "['on'|'off']",
@@ -3466,6 +3500,7 @@ struct target_type cortexa_target = {
.commands = cortex_a_command_handlers,
.target_create = cortex_a_target_create,
+ .target_jim_configure = adiv5_jim_configure,
.init_target = cortex_a_init_target,
.examine = cortex_a_examine,
.deinit_target = cortex_a_deinit_target,
@@ -3478,13 +3513,6 @@ struct target_type cortexa_target = {
static const struct command_registration cortex_r4_exec_command_handlers[] = {
{
- .name = "cache_info",
- .handler = cortex_a_handle_cache_info_command,
- .mode = COMMAND_EXEC,
- .help = "display information about target caches",
- .usage = "",
- },
- {
.name = "dbginit",
.handler = cortex_a_handle_dbginit_command,
.mode = COMMAND_EXEC,
@@ -3506,9 +3534,6 @@ static const struct command_registration cortex_r4_command_handlers[] = {
.chain = arm_command_handlers,
},
{
- .chain = armv7a_command_handlers,
- },
- {
.name = "cortex_r4",
.mode = COMMAND_ANY,
.help = "Cortex-R4 command group",
@@ -3551,6 +3576,7 @@ struct target_type cortexr4_target = {
.commands = cortex_r4_command_handlers,
.target_create = cortex_r4_target_create,
+ .target_jim_configure = adiv5_jim_configure,
.init_target = cortex_a_init_target,
.examine = cortex_a_examine,
.deinit_target = cortex_a_deinit_target,
diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c
index 79af632..ca3dbec 100644
--- a/src/target/cortex_m.c
+++ b/src/target/cortex_m.c
@@ -51,11 +51,6 @@
* any longer.
*/
-/**
- * Returns the type of a break point required by address location
- */
-#define BKPT_TYPE_BY_ADDR(addr) ((addr) < 0x20000000 ? BKPT_HARD : BKPT_SOFT)
-
/* forward declarations */
static int cortex_m_store_core_reg_u32(struct target *target,
uint32_t num, uint32_t value);
@@ -170,7 +165,7 @@ static int cortex_m_single_step_core(struct target *target)
struct armv7m_common *armv7m = &cortex_m->armv7m;
int retval;
- /* Mask interrupts before clearing halt, if done already. This avoids
+ /* Mask interrupts before clearing halt, if not done already. This avoids
* Erratum 377497 (fixed in r1p0) where setting MASKINTS while clearing
* HALT can put the core into an unknown state.
*/
@@ -242,8 +237,11 @@ static int cortex_m_endreset_event(struct target *target)
return retval;
}
- /* clear any interrupt masking */
- cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
+ /* Restore proper interrupt masking setting. */
+ if (cortex_m->isrmasking_mode == CORTEX_M_ISRMASK_ON)
+ cortex_m_write_debug_halt_mask(target, C_MASKINTS, 0);
+ else
+ cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
/* Enable features controlled by ITM and DWT blocks, and catch only
* the vectors we were told to pay attention to.
@@ -868,7 +866,7 @@ static int cortex_m_step(struct target *target, int current,
if (breakpoint)
retval = cortex_m_set_breakpoint(target, breakpoint);
else
- retval = breakpoint_add(target, pc_value, 2, BKPT_TYPE_BY_ADDR(pc_value));
+ retval = breakpoint_add(target, pc_value, 2, BKPT_HARD);
bool tmp_bp_set = (retval == ERROR_OK);
/* No more breakpoints left, just do a step */
@@ -1131,9 +1129,6 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint
return ERROR_OK;
}
- if (cortex_m->auto_bp_type)
- breakpoint->type = BKPT_TYPE_BY_ADDR(breakpoint->address);
-
if (breakpoint->type == BKPT_HARD) {
uint32_t fpcr_value;
while (comparator_list[fp_num].used && (fp_num < cortex_m->fp_num_code))
@@ -1145,6 +1140,10 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint
breakpoint->set = fp_num + 1;
fpcr_value = breakpoint->address | 1;
if (cortex_m->fp_rev == 0) {
+ if (breakpoint->address > 0x1FFFFFFF) {
+ LOG_ERROR("Cortex-M Flash Patch Breakpoint rev.1 cannot handle HW breakpoint above address 0x1FFFFFFE");
+ return ERROR_FAIL;
+ }
uint32_t hilo;
hilo = (breakpoint->address & 0x2) ? FPCR_REPLACE_BKPT_HIGH : FPCR_REPLACE_BKPT_LOW;
fpcr_value = (fpcr_value & 0x1FFFFFFC) | hilo | 1;
@@ -1253,21 +1252,6 @@ int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint
{
struct cortex_m_common *cortex_m = target_to_cm(target);
- if (cortex_m->auto_bp_type)
- breakpoint->type = BKPT_TYPE_BY_ADDR(breakpoint->address);
-
- if (breakpoint->type != BKPT_TYPE_BY_ADDR(breakpoint->address)) {
- if (breakpoint->type == BKPT_HARD) {
- LOG_INFO("flash patch comparator requested outside code memory region");
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
-
- if (breakpoint->type == BKPT_SOFT) {
- LOG_INFO("soft breakpoint requested in code (flash) memory region");
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
- }
-
if ((breakpoint->type == BKPT_HARD) && (cortex_m->fp_code_available < 1)) {
LOG_INFO("no flash patch comparator unit available for hardware breakpoint");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
@@ -1299,9 +1283,6 @@ int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpo
return ERROR_TARGET_NOT_HALTED;
}
- if (cortex_m->auto_bp_type)
- breakpoint->type = BKPT_TYPE_BY_ADDR(breakpoint->address);
-
if (breakpoint->set)
cortex_m_unset_breakpoint(target, breakpoint);
@@ -1832,11 +1813,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.
@@ -1845,7 +1826,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, }, \
@@ -1854,6 +1835,18 @@ static struct dwt_reg dwt_comp[] = {
DWT_COMPARATOR(1),
DWT_COMPARATOR(2),
DWT_COMPARATOR(3),
+ DWT_COMPARATOR(4),
+ DWT_COMPARATOR(5),
+ DWT_COMPARATOR(6),
+ DWT_COMPARATOR(7),
+ DWT_COMPARATOR(8),
+ DWT_COMPARATOR(9),
+ DWT_COMPARATOR(10),
+ DWT_COMPARATOR(11),
+ DWT_COMPARATOR(12),
+ DWT_COMPARATOR(13),
+ DWT_COMPARATOR(14),
+ DWT_COMPARATOR(15),
#undef DWT_COMPARATOR
};
@@ -1862,7 +1855,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;
@@ -1887,6 +1880,7 @@ void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target)
int reg, i;
target_read_u32(target, DWT_CTRL, &dwtcr);
+ LOG_DEBUG("DWT_CTRL: 0x%" PRIx32, dwtcr);
if (!dwtcr) {
LOG_DEBUG("no DWT");
return;
@@ -1992,12 +1986,6 @@ int cortex_m_examine(struct target *target)
/* stlink shares the examine handler but does not support
* all its calls */
if (!armv7m->stlink) {
- retval = dap_dp_init(swjdp);
- if (retval != ERROR_OK) {
- LOG_ERROR("Could not initialize the debug port");
- return retval;
- }
-
if (cortex_m->apsel < 0) {
/* Search for the MEM-AP */
retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7m->debug_ap);
@@ -2095,7 +2083,7 @@ int cortex_m_examine(struct target *target)
if (retval != ERROR_OK)
return retval;
- if (armv7m->trace_config.config_type != DISABLED) {
+ if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_DISABLED) {
armv7m_trace_tpiu_config(target);
armv7m_trace_itm_config(target);
}
@@ -2104,7 +2092,6 @@ int cortex_m_examine(struct target *target)
/* Setup FPB */
target_read_u32(target, FP_CTRL, &fpcr);
- cortex_m->auto_bp_type = 1;
/* bits [14:12] and [7:4] */
cortex_m->fp_num_code = ((fpcr >> 8) & 0x70) | ((fpcr >> 4) & 0xF);
cortex_m->fp_num_lit = (fpcr >> 8) & 0xF;
@@ -2228,25 +2215,17 @@ static int cortex_m_handle_target_request(void *priv)
}
static int cortex_m_init_arch_info(struct target *target,
- struct cortex_m_common *cortex_m, struct jtag_tap *tap)
+ struct cortex_m_common *cortex_m, struct adiv5_dap *dap)
{
struct armv7m_common *armv7m = &cortex_m->armv7m;
armv7m_init_arch_info(target, armv7m);
- /* tap has no dap initialized */
- if (!tap->dap) {
- tap->dap = dap_init();
-
- /* Leave (only) generic DAP stuff for debugport_init() */
- tap->dap->tap = tap;
- }
-
/* default reset mode is to use srst if fitted
* if not it will use CORTEX_M3_RESET_VECTRESET */
cortex_m->soft_reset_config = CORTEX_M_RESET_VECTRESET;
- armv7m->arm.dap = tap->dap;
+ armv7m->arm.dap = dap;
/* register arch-specific functions */
armv7m->examine_debug_reason = cortex_m_examine_debug_reason;
@@ -2266,16 +2245,16 @@ static int cortex_m_init_arch_info(struct target *target,
static int cortex_m_target_create(struct target *target, Jim_Interp *interp)
{
struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common));
-
cortex_m->common_magic = CORTEX_M_COMMON_MAGIC;
- cortex_m_init_arch_info(target, cortex_m, target->tap);
+ struct adiv5_private_config *pc;
+
+ pc = (struct adiv5_private_config *)target->private_config;
+ if (adiv5_verify_config(pc) != ERROR_OK)
+ return ERROR_FAIL;
- if (target->private_config != NULL) {
- struct adiv5_private_config *pc =
- (struct adiv5_private_config *)target->private_config;
- cortex_m->apsel = pc->ap_num;
- } else
- cortex_m->apsel = -1;
+ cortex_m->apsel = pc->ap_num;
+
+ cortex_m_init_arch_info(target, cortex_m, pc->dap);
return ERROR_OK;
}
@@ -2298,20 +2277,6 @@ static int cortex_m_verify_pointer(struct command_context *cmd_ctx,
* cortexm3_target structure, which is only used with CM3 targets.
*/
-static const struct {
- char name[10];
- unsigned mask;
-} vec_ids[] = {
- { "hard_err", VC_HARDERR, },
- { "int_err", VC_INTERR, },
- { "bus_err", VC_BUSERR, },
- { "state_err", VC_STATERR, },
- { "chk_err", VC_CHKERR, },
- { "nocp_err", VC_NOCPERR, },
- { "mm_err", VC_MMERR, },
- { "reset", VC_CORERESET, },
-};
-
COMMAND_HANDLER(handle_cortex_m_vector_catch_command)
{
struct target *target = get_current_target(CMD_CTX);
@@ -2320,6 +2285,20 @@ COMMAND_HANDLER(handle_cortex_m_vector_catch_command)
uint32_t demcr = 0;
int retval;
+ static const struct {
+ char name[10];
+ unsigned mask;
+ } vec_ids[] = {
+ { "hard_err", VC_HARDERR, },
+ { "int_err", VC_INTERR, },
+ { "bus_err", VC_BUSERR, },
+ { "state_err", VC_STATERR, },
+ { "chk_err", VC_CHKERR, },
+ { "nocp_err", VC_NOCPERR, },
+ { "mm_err", VC_MMERR, },
+ { "reset", VC_CORERESET, },
+ };
+
retval = cortex_m_verify_pointer(CMD_CTX, cortex_m);
if (retval != ERROR_OK)
return retval;
diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h
index 9500acc..2daf4cb 100644
--- a/src/target/cortex_m.h
+++ b/src/target/cortex_m.h
@@ -175,7 +175,6 @@ struct cortex_m_common {
int fp_code_available;
int fp_rev;
int fpb_enabled;
- int auto_bp_type;
struct cortex_m_fp_comparator *fp_comparator_list;
/* Data Watchpoint and Trace (DWT) */
diff --git a/src/target/hla_target.c b/src/target/hla_target.c
index a3e6835..9ebf241 100644
--- a/src/target/hla_target.c
+++ b/src/target/hla_target.c
@@ -271,7 +271,10 @@ static int hl_target_request_data(struct target *target,
uint32_t i;
for (i = 0; i < (size * 4); i++) {
- hl_dcc_read(hl_if, &data, &ctrl);
+ int err = hl_dcc_read(hl_if, &data, &ctrl);
+ if (err != ERROR_OK)
+ return err;
+
buffer[i] = data;
}
@@ -281,6 +284,8 @@ static int hl_target_request_data(struct target *target,
static int hl_handle_target_request(void *priv)
{
struct target *target = priv;
+ int err;
+
if (!target_was_examined(target))
return ERROR_OK;
struct hl_interface_s *hl_if = target_to_adapter(target);
@@ -292,7 +297,9 @@ static int hl_handle_target_request(void *priv)
uint8_t data;
uint8_t ctrl;
- hl_dcc_read(hl_if, &data, &ctrl);
+ err = hl_dcc_read(hl_if, &data, &ctrl);
+ if (err != ERROR_OK)
+ return err;
/* check if we have data */
if (ctrl & (1 << 0)) {
@@ -300,11 +307,20 @@ static int hl_handle_target_request(void *priv)
/* we assume target is quick enough */
request = data;
- hl_dcc_read(hl_if, &data, &ctrl);
+ err = hl_dcc_read(hl_if, &data, &ctrl);
+ if (err != ERROR_OK)
+ return err;
+
request |= (data << 8);
- hl_dcc_read(hl_if, &data, &ctrl);
+ err = hl_dcc_read(hl_if, &data, &ctrl);
+ if (err != ERROR_OK)
+ return err;
+
request |= (data << 16);
- hl_dcc_read(hl_if, &data, &ctrl);
+ err = hl_dcc_read(hl_if, &data, &ctrl);
+ if (err != ERROR_OK)
+ return err;
+
request |= (data << 24);
target_request(target, request);
}
@@ -349,12 +365,16 @@ static int adapter_target_create(struct target *target,
Jim_Interp *interp)
{
LOG_DEBUG("%s", __func__);
-
+ struct adiv5_private_config *pc = target->private_config;
struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common));
-
if (!cortex_m)
return ERROR_COMMAND_SYNTAX_ERROR;
+ if (pc != NULL && pc->ap_num > 0) {
+ LOG_ERROR("hla_target: invalid parameter -ap-num (> 0)");
+ return ERROR_FAIL;
+ }
+
adapter_init_arch_info(target, cortex_m, target->tap);
return ERROR_OK;
@@ -785,6 +805,7 @@ struct target_type hla_target = {
.init_target = adapter_init_target,
.deinit_target = cortex_m_deinit_target,
.target_create = adapter_target_create,
+ .target_jim_configure = adiv5_jim_configure,
.examine = cortex_m_examine,
.commands = adapter_command_handlers,
diff --git a/src/target/image.c b/src/target/image.c
index f97d904..0d98c57 100644
--- a/src/target/image.c
+++ b/src/target/image.c
@@ -121,8 +121,9 @@ static int image_ihex_buffer_complete_inner(struct image *image,
{
struct image_ihex *ihex = image->type_private;
struct fileio *fileio = ihex->fileio;
- uint32_t full_address = 0x0;
+ uint32_t full_address;
uint32_t cooked_bytes;
+ bool end_rec = false;
int i;
/* we can't determine the number of sections that we'll have to create ahead of time,
@@ -137,175 +138,190 @@ static int image_ihex_buffer_complete_inner(struct image *image,
ihex->buffer = malloc(filesize >> 1);
cooked_bytes = 0x0;
image->num_sections = 0;
- section[image->num_sections].private = &ihex->buffer[cooked_bytes];
- section[image->num_sections].base_address = 0x0;
- section[image->num_sections].size = 0x0;
- section[image->num_sections].flags = 0;
-
- while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) {
- uint32_t count;
- uint32_t address;
- uint32_t record_type;
- uint32_t checksum;
- uint8_t cal_checksum = 0;
- size_t bytes_read = 0;
-
- if (lpszLine[0] == '#')
- continue;
-
- if (sscanf(&lpszLine[bytes_read], ":%2" SCNx32 "%4" SCNx32 "%2" SCNx32, &count,
- &address, &record_type) != 3)
- return ERROR_IMAGE_FORMAT_ERROR;
- bytes_read += 9;
-
- cal_checksum += (uint8_t)count;
- cal_checksum += (uint8_t)(address >> 8);
- cal_checksum += (uint8_t)address;
- cal_checksum += (uint8_t)record_type;
-
- if (record_type == 0) { /* Data Record */
- if ((full_address & 0xffff) != address) {
- /* we encountered a nonconsecutive location, create a new section,
- * unless the current section has zero size, in which case this specifies
- * the current section's base address
- */
- if (section[image->num_sections].size != 0) {
- image->num_sections++;
- if (image->num_sections >= IMAGE_MAX_SECTIONS) {
- /* too many sections */
- LOG_ERROR("Too many sections found in IHEX file");
- return ERROR_IMAGE_FORMAT_ERROR;
+
+ while (!fileio_feof(fileio)) {
+ full_address = 0x0;
+ section[image->num_sections].private = &ihex->buffer[cooked_bytes];
+ section[image->num_sections].base_address = 0x0;
+ section[image->num_sections].size = 0x0;
+ section[image->num_sections].flags = 0;
+
+ while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) {
+ uint32_t count;
+ uint32_t address;
+ uint32_t record_type;
+ uint32_t checksum;
+ uint8_t cal_checksum = 0;
+ size_t bytes_read = 0;
+
+ /* skip comments and blank lines */
+ if ((lpszLine[0] == '#') || (strlen(lpszLine + strspn(lpszLine, "\n\t\r ")) == 0))
+ continue;
+
+ if (sscanf(&lpszLine[bytes_read], ":%2" SCNx32 "%4" SCNx32 "%2" SCNx32, &count,
+ &address, &record_type) != 3)
+ return ERROR_IMAGE_FORMAT_ERROR;
+ bytes_read += 9;
+
+ cal_checksum += (uint8_t)count;
+ cal_checksum += (uint8_t)(address >> 8);
+ cal_checksum += (uint8_t)address;
+ cal_checksum += (uint8_t)record_type;
+
+ if (record_type == 0) { /* Data Record */
+ if ((full_address & 0xffff) != address) {
+ /* we encountered a nonconsecutive location, create a new section,
+ * unless the current section has zero size, in which case this specifies
+ * the current section's base address
+ */
+ if (section[image->num_sections].size != 0) {
+ image->num_sections++;
+ if (image->num_sections >= IMAGE_MAX_SECTIONS) {
+ /* too many sections */
+ LOG_ERROR("Too many sections found in IHEX file");
+ return ERROR_IMAGE_FORMAT_ERROR;
+ }
+ section[image->num_sections].size = 0x0;
+ section[image->num_sections].flags = 0;
+ section[image->num_sections].private =
+ &ihex->buffer[cooked_bytes];
}
- section[image->num_sections].size = 0x0;
- section[image->num_sections].flags = 0;
- section[image->num_sections].private =
- &ihex->buffer[cooked_bytes];
+ section[image->num_sections].base_address =
+ (full_address & 0xffff0000) | address;
+ full_address = (full_address & 0xffff0000) | address;
}
- section[image->num_sections].base_address =
- (full_address & 0xffff0000) | address;
- full_address = (full_address & 0xffff0000) | address;
- }
-
- while (count-- > 0) {
- unsigned value;
- sscanf(&lpszLine[bytes_read], "%2x", &value);
- ihex->buffer[cooked_bytes] = (uint8_t)value;
- cal_checksum += (uint8_t)ihex->buffer[cooked_bytes];
- bytes_read += 2;
- cooked_bytes += 1;
- section[image->num_sections].size += 1;
- full_address++;
- }
- } else if (record_type == 1) { /* End of File Record */
- /* finish the current section */
- image->num_sections++;
-
- /* copy section information */
- image->sections = malloc(sizeof(struct imagesection) * image->num_sections);
- for (i = 0; i < image->num_sections; i++) {
- image->sections[i].private = section[i].private;
- image->sections[i].base_address = section[i].base_address;
- image->sections[i].size = section[i].size;
- image->sections[i].flags = section[i].flags;
- }
- return ERROR_OK;
- } else if (record_type == 2) { /* Linear Address Record */
- uint16_t upper_address;
-
- sscanf(&lpszLine[bytes_read], "%4hx", &upper_address);
- cal_checksum += (uint8_t)(upper_address >> 8);
- cal_checksum += (uint8_t)upper_address;
- bytes_read += 4;
+ while (count-- > 0) {
+ unsigned value;
+ sscanf(&lpszLine[bytes_read], "%2x", &value);
+ ihex->buffer[cooked_bytes] = (uint8_t)value;
+ cal_checksum += (uint8_t)ihex->buffer[cooked_bytes];
+ bytes_read += 2;
+ cooked_bytes += 1;
+ section[image->num_sections].size += 1;
+ full_address++;
+ }
+ } else if (record_type == 1) { /* End of File Record */
+ /* finish the current section */
+ image->num_sections++;
+
+ /* copy section information */
+ image->sections = malloc(sizeof(struct imagesection) * image->num_sections);
+ for (i = 0; i < image->num_sections; i++) {
+ image->sections[i].private = section[i].private;
+ image->sections[i].base_address = section[i].base_address;
+ image->sections[i].size = section[i].size;
+ image->sections[i].flags = section[i].flags;
+ }
- if ((full_address >> 4) != upper_address) {
- /* we encountered a nonconsecutive location, create a new section,
- * unless the current section has zero size, in which case this specifies
- * the current section's base address
- */
- if (section[image->num_sections].size != 0) {
- image->num_sections++;
- if (image->num_sections >= IMAGE_MAX_SECTIONS) {
- /* too many sections */
- LOG_ERROR("Too many sections found in IHEX file");
- return ERROR_IMAGE_FORMAT_ERROR;
+ end_rec = true;
+ break;
+ } else if (record_type == 2) { /* Linear Address Record */
+ uint16_t upper_address;
+
+ sscanf(&lpszLine[bytes_read], "%4hx", &upper_address);
+ cal_checksum += (uint8_t)(upper_address >> 8);
+ cal_checksum += (uint8_t)upper_address;
+ bytes_read += 4;
+
+ if ((full_address >> 4) != upper_address) {
+ /* we encountered a nonconsecutive location, create a new section,
+ * unless the current section has zero size, in which case this specifies
+ * the current section's base address
+ */
+ if (section[image->num_sections].size != 0) {
+ image->num_sections++;
+ if (image->num_sections >= IMAGE_MAX_SECTIONS) {
+ /* too many sections */
+ LOG_ERROR("Too many sections found in IHEX file");
+ return ERROR_IMAGE_FORMAT_ERROR;
+ }
+ section[image->num_sections].size = 0x0;
+ section[image->num_sections].flags = 0;
+ section[image->num_sections].private =
+ &ihex->buffer[cooked_bytes];
}
- section[image->num_sections].size = 0x0;
- section[image->num_sections].flags = 0;
- section[image->num_sections].private =
- &ihex->buffer[cooked_bytes];
+ section[image->num_sections].base_address =
+ (full_address & 0xffff) | (upper_address << 4);
+ full_address = (full_address & 0xffff) | (upper_address << 4);
}
- section[image->num_sections].base_address =
- (full_address & 0xffff) | (upper_address << 4);
- full_address = (full_address & 0xffff) | (upper_address << 4);
- }
- } else if (record_type == 3) { /* Start Segment Address Record */
- uint32_t dummy;
-
- /* "Start Segment Address Record" will not be supported
- * but we must consume it, and do not create an error. */
- while (count-- > 0) {
- sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy);
- cal_checksum += (uint8_t)dummy;
- bytes_read += 2;
- }
- } else if (record_type == 4) { /* Extended Linear Address Record */
- uint16_t upper_address;
-
- sscanf(&lpszLine[bytes_read], "%4hx", &upper_address);
- cal_checksum += (uint8_t)(upper_address >> 8);
- cal_checksum += (uint8_t)upper_address;
- bytes_read += 4;
-
- if ((full_address >> 16) != upper_address) {
- /* we encountered a nonconsecutive location, create a new section,
- * unless the current section has zero size, in which case this specifies
- * the current section's base address
- */
- if (section[image->num_sections].size != 0) {
- image->num_sections++;
- if (image->num_sections >= IMAGE_MAX_SECTIONS) {
- /* too many sections */
- LOG_ERROR("Too many sections found in IHEX file");
- return ERROR_IMAGE_FORMAT_ERROR;
+ } else if (record_type == 3) { /* Start Segment Address Record */
+ uint32_t dummy;
+
+ /* "Start Segment Address Record" will not be supported
+ * but we must consume it, and do not create an error. */
+ while (count-- > 0) {
+ sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy);
+ cal_checksum += (uint8_t)dummy;
+ bytes_read += 2;
+ }
+ } else if (record_type == 4) { /* Extended Linear Address Record */
+ uint16_t upper_address;
+
+ sscanf(&lpszLine[bytes_read], "%4hx", &upper_address);
+ cal_checksum += (uint8_t)(upper_address >> 8);
+ cal_checksum += (uint8_t)upper_address;
+ bytes_read += 4;
+
+ if ((full_address >> 16) != upper_address) {
+ /* we encountered a nonconsecutive location, create a new section,
+ * unless the current section has zero size, in which case this specifies
+ * the current section's base address
+ */
+ if (section[image->num_sections].size != 0) {
+ image->num_sections++;
+ if (image->num_sections >= IMAGE_MAX_SECTIONS) {
+ /* too many sections */
+ LOG_ERROR("Too many sections found in IHEX file");
+ return ERROR_IMAGE_FORMAT_ERROR;
+ }
+ section[image->num_sections].size = 0x0;
+ section[image->num_sections].flags = 0;
+ section[image->num_sections].private =
+ &ihex->buffer[cooked_bytes];
}
- section[image->num_sections].size = 0x0;
- section[image->num_sections].flags = 0;
- section[image->num_sections].private =
- &ihex->buffer[cooked_bytes];
+ section[image->num_sections].base_address =
+ (full_address & 0xffff) | (upper_address << 16);
+ full_address = (full_address & 0xffff) | (upper_address << 16);
}
- section[image->num_sections].base_address =
- (full_address & 0xffff) | (upper_address << 16);
- full_address = (full_address & 0xffff) | (upper_address << 16);
+ } else if (record_type == 5) { /* Start Linear Address Record */
+ uint32_t start_address;
+
+ sscanf(&lpszLine[bytes_read], "%8" SCNx32, &start_address);
+ cal_checksum += (uint8_t)(start_address >> 24);
+ cal_checksum += (uint8_t)(start_address >> 16);
+ cal_checksum += (uint8_t)(start_address >> 8);
+ cal_checksum += (uint8_t)start_address;
+ bytes_read += 8;
+
+ image->start_address_set = 1;
+ image->start_address = be_to_h_u32((uint8_t *)&start_address);
+ } else {
+ LOG_ERROR("unhandled IHEX record type: %i", (int)record_type);
+ return ERROR_IMAGE_FORMAT_ERROR;
}
- } else if (record_type == 5) { /* Start Linear Address Record */
- uint32_t start_address;
-
- sscanf(&lpszLine[bytes_read], "%8" SCNx32, &start_address);
- cal_checksum += (uint8_t)(start_address >> 24);
- cal_checksum += (uint8_t)(start_address >> 16);
- cal_checksum += (uint8_t)(start_address >> 8);
- cal_checksum += (uint8_t)start_address;
- bytes_read += 8;
-
- image->start_address_set = 1;
- image->start_address = be_to_h_u32((uint8_t *)&start_address);
- } else {
- LOG_ERROR("unhandled IHEX record type: %i", (int)record_type);
- return ERROR_IMAGE_FORMAT_ERROR;
- }
- sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum);
+ sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum);
- if ((uint8_t)checksum != (uint8_t)(~cal_checksum + 1)) {
- /* checksum failed */
- LOG_ERROR("incorrect record checksum found in IHEX file");
- return ERROR_IMAGE_CHECKSUM;
+ if ((uint8_t)checksum != (uint8_t)(~cal_checksum + 1)) {
+ /* checksum failed */
+ LOG_ERROR("incorrect record checksum found in IHEX file");
+ return ERROR_IMAGE_CHECKSUM;
+ }
+
+ if (end_rec) {
+ end_rec = false;
+ LOG_WARNING("continuing after end-of-file record: %.40s", lpszLine);
+ }
}
}
- LOG_ERROR("premature end of IHEX file, no end-of-file record found");
- return ERROR_IMAGE_FORMAT_ERROR;
+ if (end_rec)
+ return ERROR_OK;
+ else {
+ LOG_ERROR("premature end of IHEX file, no matching end-of-file record found");
+ return ERROR_IMAGE_FORMAT_ERROR;
+ }
}
/**
@@ -510,8 +526,9 @@ static int image_mot_buffer_complete_inner(struct image *image,
{
struct image_mot *mot = image->type_private;
struct fileio *fileio = mot->fileio;
- uint32_t full_address = 0x0;
+ uint32_t full_address;
uint32_t cooked_bytes;
+ bool end_rec = false;
int i;
/* we can't determine the number of sections that we'll have to create ahead of time,
@@ -526,140 +543,158 @@ static int image_mot_buffer_complete_inner(struct image *image,
mot->buffer = malloc(filesize >> 1);
cooked_bytes = 0x0;
image->num_sections = 0;
- section[image->num_sections].private = &mot->buffer[cooked_bytes];
- section[image->num_sections].base_address = 0x0;
- section[image->num_sections].size = 0x0;
- section[image->num_sections].flags = 0;
-
- while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) {
- uint32_t count;
- uint32_t address;
- uint32_t record_type;
- uint32_t checksum;
- uint8_t cal_checksum = 0;
- uint32_t bytes_read = 0;
-
- /* get record type and record length */
- if (sscanf(&lpszLine[bytes_read], "S%1" SCNx32 "%2" SCNx32, &record_type,
- &count) != 2)
- return ERROR_IMAGE_FORMAT_ERROR;
-
- bytes_read += 4;
- cal_checksum += (uint8_t)count;
-
- /* skip checksum byte */
- count -= 1;
-
- if (record_type == 0) {
- /* S0 - starting record (optional) */
- int iValue;
-
- while (count-- > 0) {
- sscanf(&lpszLine[bytes_read], "%2x", &iValue);
- cal_checksum += (uint8_t)iValue;
- bytes_read += 2;
- }
- } else if (record_type >= 1 && record_type <= 3) {
- switch (record_type) {
- case 1:
- /* S1 - 16 bit address data record */
- sscanf(&lpszLine[bytes_read], "%4" SCNx32, &address);
- cal_checksum += (uint8_t)(address >> 8);
- cal_checksum += (uint8_t)address;
- bytes_read += 4;
- count -= 2;
- break;
-
- case 2:
- /* S2 - 24 bit address data record */
- sscanf(&lpszLine[bytes_read], "%6" SCNx32, &address);
- cal_checksum += (uint8_t)(address >> 16);
- cal_checksum += (uint8_t)(address >> 8);
- cal_checksum += (uint8_t)address;
- bytes_read += 6;
- count -= 3;
- break;
-
- case 3:
- /* S3 - 32 bit address data record */
- sscanf(&lpszLine[bytes_read], "%8" SCNx32, &address);
- cal_checksum += (uint8_t)(address >> 24);
- cal_checksum += (uint8_t)(address >> 16);
- cal_checksum += (uint8_t)(address >> 8);
- cal_checksum += (uint8_t)address;
- bytes_read += 8;
- count -= 4;
- break;
- }
+ while (!fileio_feof(fileio)) {
+ full_address = 0x0;
+ section[image->num_sections].private = &mot->buffer[cooked_bytes];
+ section[image->num_sections].base_address = 0x0;
+ section[image->num_sections].size = 0x0;
+ section[image->num_sections].flags = 0;
+
+ while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) {
+ uint32_t count;
+ uint32_t address;
+ uint32_t record_type;
+ uint32_t checksum;
+ uint8_t cal_checksum = 0;
+ uint32_t bytes_read = 0;
+
+ /* skip comments and blank lines */
+ if ((lpszLine[0] == '#') || (strlen(lpszLine + strspn(lpszLine, "\n\t\r ")) == 0))
+ continue;
+
+ /* get record type and record length */
+ if (sscanf(&lpszLine[bytes_read], "S%1" SCNx32 "%2" SCNx32, &record_type,
+ &count) != 2)
+ return ERROR_IMAGE_FORMAT_ERROR;
+
+ bytes_read += 4;
+ cal_checksum += (uint8_t)count;
- if (full_address != address) {
- /* we encountered a nonconsecutive location, create a new section,
- * unless the current section has zero size, in which case this specifies
- * the current section's base address
- */
- if (section[image->num_sections].size != 0) {
- image->num_sections++;
- section[image->num_sections].size = 0x0;
- section[image->num_sections].flags = 0;
- section[image->num_sections].private =
- &mot->buffer[cooked_bytes];
+ /* skip checksum byte */
+ count -= 1;
+
+ if (record_type == 0) {
+ /* S0 - starting record (optional) */
+ int iValue;
+
+ while (count-- > 0) {
+ sscanf(&lpszLine[bytes_read], "%2x", &iValue);
+ cal_checksum += (uint8_t)iValue;
+ bytes_read += 2;
}
- section[image->num_sections].base_address = address;
- full_address = address;
- }
+ } else if (record_type >= 1 && record_type <= 3) {
+ switch (record_type) {
+ case 1:
+ /* S1 - 16 bit address data record */
+ sscanf(&lpszLine[bytes_read], "%4" SCNx32, &address);
+ cal_checksum += (uint8_t)(address >> 8);
+ cal_checksum += (uint8_t)address;
+ bytes_read += 4;
+ count -= 2;
+ break;
+
+ case 2:
+ /* S2 - 24 bit address data record */
+ sscanf(&lpszLine[bytes_read], "%6" SCNx32, &address);
+ cal_checksum += (uint8_t)(address >> 16);
+ cal_checksum += (uint8_t)(address >> 8);
+ cal_checksum += (uint8_t)address;
+ bytes_read += 6;
+ count -= 3;
+ break;
+
+ case 3:
+ /* S3 - 32 bit address data record */
+ sscanf(&lpszLine[bytes_read], "%8" SCNx32, &address);
+ cal_checksum += (uint8_t)(address >> 24);
+ cal_checksum += (uint8_t)(address >> 16);
+ cal_checksum += (uint8_t)(address >> 8);
+ cal_checksum += (uint8_t)address;
+ bytes_read += 8;
+ count -= 4;
+ break;
- while (count-- > 0) {
- unsigned value;
- sscanf(&lpszLine[bytes_read], "%2x", &value);
- mot->buffer[cooked_bytes] = (uint8_t)value;
- cal_checksum += (uint8_t)mot->buffer[cooked_bytes];
- bytes_read += 2;
- cooked_bytes += 1;
- section[image->num_sections].size += 1;
- full_address++;
- }
- } else if (record_type == 5) {
- /* S5 is the data count record, we ignore it */
- uint32_t dummy;
-
- while (count-- > 0) {
- sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy);
- cal_checksum += (uint8_t)dummy;
- bytes_read += 2;
- }
- } else if (record_type >= 7 && record_type <= 9) {
- /* S7, S8, S9 - ending records for 32, 24 and 16bit */
- image->num_sections++;
+ }
- /* copy section information */
- image->sections = malloc(sizeof(struct imagesection) * image->num_sections);
- for (i = 0; i < image->num_sections; i++) {
- image->sections[i].private = section[i].private;
- image->sections[i].base_address = section[i].base_address;
- image->sections[i].size = section[i].size;
- image->sections[i].flags = section[i].flags;
+ if (full_address != address) {
+ /* we encountered a nonconsecutive location, create a new section,
+ * unless the current section has zero size, in which case this specifies
+ * the current section's base address
+ */
+ if (section[image->num_sections].size != 0) {
+ image->num_sections++;
+ section[image->num_sections].size = 0x0;
+ section[image->num_sections].flags = 0;
+ section[image->num_sections].private =
+ &mot->buffer[cooked_bytes];
+ }
+ section[image->num_sections].base_address = address;
+ full_address = address;
+ }
+
+ while (count-- > 0) {
+ unsigned value;
+ sscanf(&lpszLine[bytes_read], "%2x", &value);
+ mot->buffer[cooked_bytes] = (uint8_t)value;
+ cal_checksum += (uint8_t)mot->buffer[cooked_bytes];
+ bytes_read += 2;
+ cooked_bytes += 1;
+ section[image->num_sections].size += 1;
+ full_address++;
+ }
+ } else if (record_type == 5) {
+ /* S5 is the data count record, we ignore it */
+ uint32_t dummy;
+
+ while (count-- > 0) {
+ sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy);
+ cal_checksum += (uint8_t)dummy;
+ bytes_read += 2;
+ }
+ } else if (record_type >= 7 && record_type <= 9) {
+ /* S7, S8, S9 - ending records for 32, 24 and 16bit */
+ image->num_sections++;
+
+ /* copy section information */
+ image->sections = malloc(sizeof(struct imagesection) * image->num_sections);
+ for (i = 0; i < image->num_sections; i++) {
+ image->sections[i].private = section[i].private;
+ image->sections[i].base_address = section[i].base_address;
+ image->sections[i].size = section[i].size;
+ image->sections[i].flags = section[i].flags;
+ }
+
+ end_rec = true;
+ break;
+ } else {
+ LOG_ERROR("unhandled S19 record type: %i", (int)(record_type));
+ return ERROR_IMAGE_FORMAT_ERROR;
}
- return ERROR_OK;
- } else {
- LOG_ERROR("unhandled S19 record type: %i", (int)(record_type));
- return ERROR_IMAGE_FORMAT_ERROR;
- }
+ /* account for checksum, will always be 0xFF */
+ sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum);
+ cal_checksum += (uint8_t)checksum;
- /* account for checksum, will always be 0xFF */
- sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum);
- cal_checksum += (uint8_t)checksum;
+ if (cal_checksum != 0xFF) {
+ /* checksum failed */
+ LOG_ERROR("incorrect record checksum found in S19 file");
+ return ERROR_IMAGE_CHECKSUM;
+ }
- if (cal_checksum != 0xFF) {
- /* checksum failed */
- LOG_ERROR("incorrect record checksum found in S19 file");
- return ERROR_IMAGE_CHECKSUM;
+ if (end_rec) {
+ end_rec = false;
+ LOG_WARNING("continuing after end-of-file record: %.40s", lpszLine);
+ }
}
}
- LOG_ERROR("premature end of S19 file, no end-of-file record found");
- return ERROR_IMAGE_FORMAT_ERROR;
+ if (end_rec)
+ return ERROR_OK;
+ else {
+ LOG_ERROR("premature end of S19 file, no matching end-of-file record found");
+ return ERROR_IMAGE_FORMAT_ERROR;
+ }
}
/**
@@ -1013,8 +1048,7 @@ int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, uint32_t *checksu
static bool first_init;
if (!first_init) {
/* Initialize the CRC table and the decoding table. */
- int i, j;
- unsigned int c;
+ unsigned int i, j, c;
for (i = 0; i < 256; i++) {
/* as per gdb */
for (c = i << 24, j = 8; j > 0; --j)
diff --git a/src/target/mips32.c b/src/target/mips32.c
index 93fb4e6..b5dbea3 100644
--- a/src/target/mips32.c
+++ b/src/target/mips32.c
@@ -827,7 +827,8 @@ int mips32_checksum_memory(struct target *target, target_addr_t address,
/** Checks whether a memory region is erased. */
int mips32_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value)
+ struct target_memory_check_block *blocks, int num_blocks,
+ uint8_t erased_value)
{
struct working_area *erase_check_algorithm;
struct reg_param reg_params[3];
@@ -866,16 +867,16 @@ int mips32_blank_check_memory(struct target *target,
int retval = target_write_buffer(target, erase_check_algorithm->address,
sizeof(erase_check_code), erase_check_code_8);
if (retval != ERROR_OK)
- return retval;
+ goto cleanup;
mips32_info.common_magic = MIPS32_COMMON_MAGIC;
mips32_info.isa_mode = isa ? MIPS32_ISA_MMIPS32 : MIPS32_ISA_MIPS32;
init_reg_param(&reg_params[0], "r4", 32, PARAM_OUT);
- buf_set_u32(reg_params[0].value, 0, 32, address);
+ buf_set_u32(reg_params[0].value, 0, 32, blocks[0].address);
init_reg_param(&reg_params[1], "r5", 32, PARAM_OUT);
- buf_set_u32(reg_params[1].value, 0, 32, count);
+ buf_set_u32(reg_params[1].value, 0, 32, blocks[0].size);
init_reg_param(&reg_params[2], "r6", 32, PARAM_IN_OUT);
buf_set_u32(reg_params[2].value, 0, 32, erased_value);
@@ -884,15 +885,19 @@ int mips32_blank_check_memory(struct target *target,
erase_check_algorithm->address + (sizeof(erase_check_code) - 4), 10000, &mips32_info);
if (retval == ERROR_OK)
- *blank = buf_get_u32(reg_params[2].value, 0, 32);
+ blocks[0].result = buf_get_u32(reg_params[2].value, 0, 32);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
+cleanup:
target_free_working_area(target, erase_check_algorithm);
- return retval;
+ if (retval != ERROR_OK)
+ return retval;
+
+ return 1; /* only one block has been checked */
}
static int mips32_verify_pointer(struct command_context *cmd_ctx,
diff --git a/src/target/mips32.h b/src/target/mips32.h
index 928598f..4dc164e 100644
--- a/src/target/mips32.h
+++ b/src/target/mips32.h
@@ -428,6 +428,6 @@ int mips32_get_gdb_reg_list(struct target *target,
int mips32_checksum_memory(struct target *target, target_addr_t address,
uint32_t count, uint32_t *checksum);
int mips32_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value);
+ struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value);
#endif /* OPENOCD_TARGET_MIPS32_H */
diff --git a/src/target/mips_m4k.c b/src/target/mips_m4k.c
index 78718ca..20c707b 100644
--- a/src/target/mips_m4k.c
+++ b/src/target/mips_m4k.c
@@ -344,6 +344,8 @@ static int mips_m4k_assert_reset(struct target *target)
jtag_add_reset(1, 1);
else if (!srst_asserted)
jtag_add_reset(0, 1);
+ } else if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
+ target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
} else {
if (mips_m4k->is_pic32mx) {
LOG_DEBUG("Using MTAP reset to reset processor...");
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/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c
index 2d90114..6cd53f4 100644
--- a/src/target/openrisc/jsp_server.c
+++ b/src/target/openrisc/jsp_server.c
@@ -242,3 +242,7 @@ int jsp_register_commands(struct command_context *cmd_ctx)
return register_commands(cmd_ctx, NULL, jsp_command_handlers);
}
+void jsp_service_free(void)
+{
+ free(jsp_port);
+}
diff --git a/src/target/openrisc/jsp_server.h b/src/target/openrisc/jsp_server.h
index f8e7121..e5cfaa8 100644
--- a/src/target/openrisc/jsp_server.h
+++ b/src/target/openrisc/jsp_server.h
@@ -13,5 +13,6 @@ struct jsp_service {
int jsp_init(struct or1k_jtag *jtag_info, char *banner);
int jsp_register_commands(struct command_context *cmd_ctx);
+void jsp_service_free(void);
#endif /* OPENOCD_TARGET_OPENRISC_JSP_SERVER_H */
diff --git a/src/target/register.h b/src/target/register.h
index dc18e9a..32c1f39 100644
--- a/src/target/register.h
+++ b/src/target/register.h
@@ -25,12 +25,14 @@
struct target;
enum reg_type {
+ REG_TYPE_BOOL,
REG_TYPE_INT,
REG_TYPE_INT8,
REG_TYPE_INT16,
REG_TYPE_INT32,
REG_TYPE_INT64,
REG_TYPE_INT128,
+ REG_TYPE_UINT,
REG_TYPE_UINT8,
REG_TYPE_UINT16,
REG_TYPE_UINT32,
@@ -66,6 +68,7 @@ struct reg_data_type_union {
struct reg_data_type_bitfield {
uint32_t start;
uint32_t end;
+ enum reg_type type;
};
struct reg_data_type_struct_field {
diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am
new file mode 100644
index 0000000..83f1a8c
--- /dev/null
+++ b/src/target/riscv/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES += %D%/libriscv.la
+%C%_libriscv_la_SOURCES = \
+ %D%/asm.h \
+ %D%/batch.h \
+ %D%/debug_defines.h \
+ %D%/encoding.h \
+ %D%/gdb_regs.h \
+ %D%/opcodes.h \
+ %D%/program.h \
+ %D%/riscv.h \
+ %D%/batch.c \
+ %D%/program.c \
+ %D%/riscv-011.c \
+ %D%/riscv-013.c \
+ %D%/riscv.c \
+ %D%/riscv_semihosting.c
diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c
index 117119d..9327cb3 100644
--- a/src/target/riscv/batch.c
+++ b/src/target/riscv/batch.c
@@ -160,11 +160,10 @@ void dump_field(const struct scan_field *field)
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__,
- "%db %s %08x @%02x -> %s %08x @%02x [0x%p -> 0x%p]",
+ "%db %s %08x @%02x -> %s %08x @%02x",
field->num_bits,
op_string[out_op], out_data, out_address,
- status_string[in_op], in_data, in_address,
- field->out_value, field->in_value);
+ status_string[in_op], in_data, in_address);
} else {
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?",
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
index 7308bb9..0cabf15 100644
--- a/src/target/riscv/debug_defines.h
+++ b/src/target/riscv/debug_defines.h
@@ -229,7 +229,8 @@
* Explains why Debug Mode was entered.
*
* When there are multiple reasons to enter Debug Mode in a single
-* cycle, the cause with the highest priority is the one written.
+* cycle, hardware should set \Fcause to the cause with the highest
+* priority.
*
* 1: An {\tt ebreak} instruction was executed. (priority 3)
*
@@ -245,6 +246,15 @@
#define CSR_DCSR_CAUSE_LENGTH 3
#define CSR_DCSR_CAUSE (0x7U << CSR_DCSR_CAUSE_OFFSET)
/*
+* When 1, \Fmprv in \Rmstatus takes effect during debug mode.
+* When 0, it is ignored during debug mode.
+* Implementing this bit is optional.
+* If not implemented it should be tied to 0.
+ */
+#define CSR_DCSR_MPRVEN_OFFSET 4
+#define CSR_DCSR_MPRVEN_LENGTH 1
+#define CSR_DCSR_MPRVEN (0x1U << CSR_DCSR_MPRVEN_OFFSET)
+/*
* When set, there is a Non-Maskable-Interrupt (NMI) pending for the hart.
*
* Since an NMI can indicate a hardware error condition,
@@ -279,14 +289,14 @@
#define CSR_DCSR_PRV (0x3U << CSR_DCSR_PRV_OFFSET)
#define CSR_DPC 0x7b1
#define CSR_DPC_DPC_OFFSET 0
-#define CSR_DPC_DPC_LENGTH XLEN
-#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET)
+#define CSR_DPC_DPC_LENGTH MXLEN
+#define CSR_DPC_DPC (((1L<<MXLEN)-1) << CSR_DPC_DPC_OFFSET)
#define CSR_DSCRATCH0 0x7b2
#define CSR_DSCRATCH1 0x7b3
#define CSR_TSELECT 0x7a0
#define CSR_TSELECT_INDEX_OFFSET 0
-#define CSR_TSELECT_INDEX_LENGTH XLEN
-#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
+#define CSR_TSELECT_INDEX_LENGTH MXLEN
+#define CSR_TSELECT_INDEX (((1L<<MXLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
#define CSR_TDATA1 0x7a1
/*
* 0: There is no trigger at this \Rtselect.
@@ -300,12 +310,22 @@
* 3: The trigger is an instruction count trigger. The remaining bits
* in this register act as described in \Ricount.
*
+* 4: The trigger is an interrupt trigger. The remaining bits
+* in this register act as described in \Ritrigger.
+*
+* 5: The trigger is an exception trigger. The remaining bits
+* in this register act as described in \Retrigger.
+*
* 15: This trigger exists (so enumeration shouldn't terminate), but
* is not currently available.
*
* Other values are reserved for future use.
+*
+* When this field is written to an unsupported value, it takes on its
+* reset value instead. The reset value is any one of the types
+* supported by the trigger selected by \Rtselect.
*/
-#define CSR_TDATA1_TYPE_OFFSET (XLEN-4)
+#define CSR_TDATA1_TYPE_OFFSET (MXLEN-4)
#define CSR_TDATA1_TYPE_LENGTH 4
#define CSR_TDATA1_TYPE (0xfULL << CSR_TDATA1_TYPE_OFFSET)
/*
@@ -317,39 +337,57 @@
*
* This bit is only writable from Debug Mode.
*/
-#define CSR_TDATA1_DMODE_OFFSET (XLEN-5)
+#define CSR_TDATA1_DMODE_OFFSET (MXLEN-5)
#define CSR_TDATA1_DMODE_LENGTH 1
#define CSR_TDATA1_DMODE (0x1ULL << CSR_TDATA1_DMODE_OFFSET)
/*
* Trigger-specific data.
*/
#define CSR_TDATA1_DATA_OFFSET 0
-#define CSR_TDATA1_DATA_LENGTH (XLEN - 5)
-#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
+#define CSR_TDATA1_DATA_LENGTH (MXLEN - 5)
+#define CSR_TDATA1_DATA (((1L<<MXLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
#define CSR_TDATA2 0x7a2
#define CSR_TDATA2_DATA_OFFSET 0
-#define CSR_TDATA2_DATA_LENGTH XLEN
-#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET)
+#define CSR_TDATA2_DATA_LENGTH MXLEN
+#define CSR_TDATA2_DATA (((1L<<MXLEN)-1) << CSR_TDATA2_DATA_OFFSET)
#define CSR_TDATA3 0x7a3
#define CSR_TDATA3_DATA_OFFSET 0
-#define CSR_TDATA3_DATA_LENGTH XLEN
-#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET)
+#define CSR_TDATA3_DATA_LENGTH MXLEN
+#define CSR_TDATA3_DATA (((1L<<MXLEN)-1) << CSR_TDATA3_DATA_OFFSET)
+#define CSR_TINFO 0x7a4
+/*
+* One bit for each possible \Ftype enumerated in \Rtdataone. Bit N
+* corresponds to type N. If the bit is set, then that type is
+* supported by the currently selected trigger.
+*
+* If the currently selected trigger doesn't exist, this field
+* contains 1.
+*
+* If \Ftype is not writable, this register may be unimplemented, in
+* which case reading it causes an illegal instruction exception. In
+* this case the debugger can read the only supported type from
+* \Rtdataone.
+ */
+#define CSR_TINFO_INFO_OFFSET 0
+#define CSR_TINFO_INFO_LENGTH 16
+#define CSR_TINFO_INFO (0xffffULL << CSR_TINFO_INFO_OFFSET)
#define CSR_MCONTROL 0x7a1
-#define CSR_MCONTROL_TYPE_OFFSET (XLEN-4)
+#define CSR_MCONTROL_TYPE_OFFSET (MXLEN-4)
#define CSR_MCONTROL_TYPE_LENGTH 4
#define CSR_MCONTROL_TYPE (0xfULL << CSR_MCONTROL_TYPE_OFFSET)
-#define CSR_MCONTROL_DMODE_OFFSET (XLEN-5)
+#define CSR_MCONTROL_DMODE_OFFSET (MXLEN-5)
#define CSR_MCONTROL_DMODE_LENGTH 1
#define CSR_MCONTROL_DMODE (0x1ULL << CSR_MCONTROL_DMODE_OFFSET)
/*
* Specifies the largest naturally aligned powers-of-two (NAPOT) range
-* supported by the hardware. The value is the logarithm base 2 of the
+* supported by the hardware when \Fmatch is 1. The value is the
+* logarithm base 2 of the
* number of bytes in that range. A value of 0 indicates that only
* exact value matches are supported (one byte range). A value of 63
* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
* size.
*/
-#define CSR_MCONTROL_MASKMAX_OFFSET (XLEN-11)
+#define CSR_MCONTROL_MASKMAX_OFFSET (MXLEN-11)
#define CSR_MCONTROL_MASKMAX_LENGTH 6
#define CSR_MCONTROL_MASKMAX (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET)
/*
@@ -400,22 +438,8 @@
#define CSR_MCONTROL_TIMING_LENGTH 1
#define CSR_MCONTROL_TIMING (0x1ULL << CSR_MCONTROL_TIMING_OFFSET)
/*
-* Determines what happens when this trigger matches.
-*
-* 0: Raise a breakpoint exception. (Used when software wants to use
-* the trigger module without an external debugger attached.)
-*
-* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.)
-*
-* 2: Start tracing.
-*
-* 3: Stop tracing.
-*
-* 4: Emit trace data for this match. If it is a data access match,
-* emit appropriate Load/Store Address/Data. If it is an instruction
-* execution, emit its PC.
-*
-* Other values are reserved for future use.
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
*/
#define CSR_MCONTROL_ACTION_OFFSET 12
#define CSR_MCONTROL_ACTION_LENGTH 6
@@ -425,6 +449,18 @@
*
* 1: While this trigger does not match, it prevents the trigger with
* the next index from matching.
+*
+* Because \Fchain affects the next trigger, hardware must zero it in
+* writes to \Rmcontrol that set \Fdmode to 0 if the next trigger has
+* \Fdmode of 1.
+* In addition hardware should ignore writes to \Rmcontrol that set
+* \Fdmode to 1 if the previous trigger has both \Fdmode of 0 and
+* \Fchain of 1. Debuggers must avoid the latter case by checking
+* \Fchain on the previous trigger if they're writing \Rmcontrol.
+*
+* Implementations that wish to limit the maximum length of a trigger
+* chain (eg. to meet timing requirements) may do so by zeroing
+* \Fchain in writes to \Rmcontrol that would make the chain too long.
*/
#define CSR_MCONTROL_CHAIN_OFFSET 11
#define CSR_MCONTROL_CHAIN_LENGTH 1
@@ -433,7 +469,7 @@
* 0: Matches when the value equals \Rtdatatwo.
*
* 1: Matches when the top M bits of the value match the top M bits of
-* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant
+* \Rtdatatwo. M is MXLEN-1 minus the index of the least-significant
* bit containing 0 in \Rtdatatwo.
*
* 2: Matches when the value is greater than (unsigned) or equal to
@@ -492,10 +528,10 @@
#define CSR_MCONTROL_LOAD_LENGTH 1
#define CSR_MCONTROL_LOAD (0x1ULL << CSR_MCONTROL_LOAD_OFFSET)
#define CSR_ICOUNT 0x7a1
-#define CSR_ICOUNT_TYPE_OFFSET (XLEN-4)
+#define CSR_ICOUNT_TYPE_OFFSET (MXLEN-4)
#define CSR_ICOUNT_TYPE_LENGTH 4
#define CSR_ICOUNT_TYPE (0xfULL << CSR_ICOUNT_TYPE_OFFSET)
-#define CSR_ICOUNT_DMODE_OFFSET (XLEN-5)
+#define CSR_ICOUNT_DMODE_OFFSET (MXLEN-5)
#define CSR_ICOUNT_DMODE_LENGTH 1
#define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
/*
@@ -539,26 +575,102 @@
#define CSR_ICOUNT_U_LENGTH 1
#define CSR_ICOUNT_U (0x1ULL << CSR_ICOUNT_U_OFFSET)
/*
-* Determines what happens when this trigger matches.
-*
-* 0: Raise a breakpoint exception. (Used when software wants to use the
-* trigger module without an external debugger attached.)
-*
-* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.)
-*
-* 2: Start tracing.
-*
-* 3: Stop tracing.
-*
-* 4: Emit trace data for this match. If it is a data access match,
-* emit appropriate Load/Store Address/Data. If it is an instruction
-* execution, emit its PC.
-*
-* Other values are reserved for future use.
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
*/
#define CSR_ICOUNT_ACTION_OFFSET 0
#define CSR_ICOUNT_ACTION_LENGTH 6
#define CSR_ICOUNT_ACTION (0x3fULL << CSR_ICOUNT_ACTION_OFFSET)
+#define CSR_ITRIGGER 0x7a1
+#define CSR_ITRIGGER_TYPE_OFFSET (MXLEN-4)
+#define CSR_ITRIGGER_TYPE_LENGTH 4
+#define CSR_ITRIGGER_TYPE (0xfULL << CSR_ITRIGGER_TYPE_OFFSET)
+#define CSR_ITRIGGER_DMODE_OFFSET (MXLEN-5)
+#define CSR_ITRIGGER_DMODE_LENGTH 1
+#define CSR_ITRIGGER_DMODE (0x1ULL << CSR_ITRIGGER_DMODE_OFFSET)
+/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched. If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_ITRIGGER_HIT_OFFSET (MXLEN-6)
+#define CSR_ITRIGGER_HIT_LENGTH 1
+#define CSR_ITRIGGER_HIT (0x1ULL << CSR_ITRIGGER_HIT_OFFSET)
+/*
+* When set, enable this trigger for interrupts that are taken from M
+* mode.
+ */
+#define CSR_ITRIGGER_M_OFFSET 9
+#define CSR_ITRIGGER_M_LENGTH 1
+#define CSR_ITRIGGER_M (0x1ULL << CSR_ITRIGGER_M_OFFSET)
+/*
+* When set, enable this trigger for interrupts that are taken from S
+* mode.
+ */
+#define CSR_ITRIGGER_S_OFFSET 7
+#define CSR_ITRIGGER_S_LENGTH 1
+#define CSR_ITRIGGER_S (0x1ULL << CSR_ITRIGGER_S_OFFSET)
+/*
+* When set, enable this trigger for interrupts that are taken from U
+* mode.
+ */
+#define CSR_ITRIGGER_U_OFFSET 6
+#define CSR_ITRIGGER_U_LENGTH 1
+#define CSR_ITRIGGER_U (0x1ULL << CSR_ITRIGGER_U_OFFSET)
+/*
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
+ */
+#define CSR_ITRIGGER_ACTION_OFFSET 0
+#define CSR_ITRIGGER_ACTION_LENGTH 6
+#define CSR_ITRIGGER_ACTION (0x3fULL << CSR_ITRIGGER_ACTION_OFFSET)
+#define CSR_ETRIGGER 0x7a1
+#define CSR_ETRIGGER_TYPE_OFFSET (MXLEN-4)
+#define CSR_ETRIGGER_TYPE_LENGTH 4
+#define CSR_ETRIGGER_TYPE (0xfULL << CSR_ETRIGGER_TYPE_OFFSET)
+#define CSR_ETRIGGER_DMODE_OFFSET (MXLEN-5)
+#define CSR_ETRIGGER_DMODE_LENGTH 1
+#define CSR_ETRIGGER_DMODE (0x1ULL << CSR_ETRIGGER_DMODE_OFFSET)
+/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched. If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_ETRIGGER_HIT_OFFSET (MXLEN-6)
+#define CSR_ETRIGGER_HIT_LENGTH 1
+#define CSR_ETRIGGER_HIT (0x1ULL << CSR_ETRIGGER_HIT_OFFSET)
+/*
+* When set, enable this trigger for exceptions that are taken from M
+* mode.
+ */
+#define CSR_ETRIGGER_M_OFFSET 9
+#define CSR_ETRIGGER_M_LENGTH 1
+#define CSR_ETRIGGER_M (0x1ULL << CSR_ETRIGGER_M_OFFSET)
+/*
+* When set, enable this trigger for exceptions that are taken from S
+* mode.
+ */
+#define CSR_ETRIGGER_S_OFFSET 7
+#define CSR_ETRIGGER_S_LENGTH 1
+#define CSR_ETRIGGER_S (0x1ULL << CSR_ETRIGGER_S_OFFSET)
+/*
+* When set, enable this trigger for exceptions that are taken from U
+* mode.
+ */
+#define CSR_ETRIGGER_U_OFFSET 6
+#define CSR_ETRIGGER_U_LENGTH 1
+#define CSR_ETRIGGER_U (0x1ULL << CSR_ETRIGGER_U_OFFSET)
+/*
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
+ */
+#define CSR_ETRIGGER_ACTION_OFFSET 0
+#define CSR_ETRIGGER_ACTION_LENGTH 6
+#define CSR_ETRIGGER_ACTION (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET)
#define DMI_DMSTATUS 0x11
/*
* If 1, then there is an implicit {\tt ebreak} instruction at the
@@ -667,6 +779,14 @@
#define DMI_DMSTATUS_AUTHBUSY_LENGTH 1
#define DMI_DMSTATUS_AUTHBUSY (0x1U << DMI_DMSTATUS_AUTHBUSY_OFFSET)
/*
+* 1 if this Debug Module supports halt-on-reset functionality
+* controllable by the \Fsetresethaltreq and \Fclrresethaltreq bits.
+* 0 otherwise.
+ */
+#define DMI_DMSTATUS_HASRESETHALTREQ_OFFSET 5
+#define DMI_DMSTATUS_HASRESETHALTREQ_LENGTH 1
+#define DMI_DMSTATUS_HASRESETHALTREQ (0x1U << DMI_DMSTATUS_HASRESETHALTREQ_OFFSET)
+/*
* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
* is not relevant to the Device Tree.
*
@@ -772,6 +892,29 @@
#define DMI_DMCONTROL_HARTSELHI_LENGTH 10
#define DMI_DMCONTROL_HARTSELHI (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET)
/*
+* This optional field writes the halt-on-reset request bit for all
+* currently selected harts.
+* When set to 1, each selected hart will halt upon the next deassertion
+* of its reset. The halt-on-reset request bit is not automatically
+* cleared. The debugger must write to \Fclrresethaltreq to clear it.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+*
+* If \Fhasresethaltreq is 0, this field is not implemented.
+ */
+#define DMI_DMCONTROL_SETRESETHALTREQ_OFFSET 3
+#define DMI_DMCONTROL_SETRESETHALTREQ_LENGTH 1
+#define DMI_DMCONTROL_SETRESETHALTREQ (0x1U << DMI_DMCONTROL_SETRESETHALTREQ_OFFSET)
+/*
+* This optional field clears the halt-on-reset request bit for all
+* currently selected harts.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET 2
+#define DMI_DMCONTROL_CLRRESETHALTREQ_LENGTH 1
+#define DMI_DMCONTROL_CLRRESETHALTREQ (0x1U << DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET)
+/*
* This bit controls the reset signal from the DM to the rest of the
* system. The signal should reset every part of the system, including
* every hart, except for the DM and any logic required to access the
@@ -796,7 +939,7 @@
* Debug Module after power up, including the platform's system reset
* or Debug Transport reset signals.
*
-* A debugger may pulse this bit low to get the debug module into a
+* A debugger may pulse this bit low to get the Debug Module into a
* known state.
*
* Implementations may use this bit to aid debugging, for example by
@@ -818,7 +961,7 @@
#define DMI_HARTINFO_NSCRATCH (0xfU << DMI_HARTINFO_NSCRATCH_OFFSET)
/*
* 0: The {\tt data} registers are shadowed in the hart by CSR
-* registers. Each CSR register is XLEN bits in size, and corresponds
+* registers. Each CSR register is MXLEN bits in size, and corresponds
* to a single argument, per Table~\ref{tab:datareg}.
*
* 1: The {\tt data} registers are shadowed in the hart's memory map.
@@ -1009,7 +1152,7 @@
* it's explicitly cleared by the debugger.
*
* While this field is non-zero, no more system bus accesses can be
-* initiated by the debug module.
+* initiated by the Debug Module.
*/
#define DMI_SBCS_SBBUSYERROR_OFFSET 22
#define DMI_SBCS_SBBUSYERROR_LENGTH 1
@@ -1020,8 +1163,9 @@
* bit goes high immediately when a read or write is requested for any
* reason, and does not go low until the access is fully completed.
*
-* To avoid race conditions, debuggers must not try to clear \Fsberror
-* until they read \Fsbbusy as 0.
+* Writes to \Rsbcs while \Fsbbusy is high result in undefined
+* behavior. A debugger must not write to \Rsbcs until it reads
+* \Fsbbusy as 0.
*/
#define DMI_SBCS_SBBUSY_OFFSET 21
#define DMI_SBCS_SBBUSY_LENGTH 1
@@ -1067,11 +1211,13 @@
#define DMI_SBCS_SBREADONDATA_LENGTH 1
#define DMI_SBCS_SBREADONDATA (0x1U << DMI_SBCS_SBREADONDATA_OFFSET)
/*
-* When the debug module's system bus
+* When the Debug Module's system bus
* master causes a bus error, this field gets set. The bits in this
* field remain set until they are cleared by writing 1 to them.
* While this field is non-zero, no more system bus accesses can be
-* initiated by the debug module.
+* initiated by the Debug Module.
+*
+* An implementation may report "Other" (7) for any error condition.
*
* An implementation may report "Other" (7) for any error condition.
*
@@ -1268,107 +1414,3 @@
#define VIRT_PRIV_PRV_OFFSET 0
#define VIRT_PRIV_PRV_LENGTH 2
#define VIRT_PRIV_PRV (0x3U << VIRT_PRIV_PRV_OFFSET)
-#define DMI_SERCS 0x34
-/*
-* Number of supported serial ports.
- */
-#define DMI_SERCS_SERIALCOUNT_OFFSET 28
-#define DMI_SERCS_SERIALCOUNT_LENGTH 4
-#define DMI_SERCS_SERIALCOUNT (0xfU << DMI_SERCS_SERIALCOUNT_OFFSET)
-/*
-* Select which serial port is accessed by \Rserrx and \Rsertx.
- */
-#define DMI_SERCS_SERIAL_OFFSET 24
-#define DMI_SERCS_SERIAL_LENGTH 3
-#define DMI_SERCS_SERIAL (0x7U << DMI_SERCS_SERIAL_OFFSET)
-#define DMI_SERCS_ERROR7_OFFSET 23
-#define DMI_SERCS_ERROR7_LENGTH 1
-#define DMI_SERCS_ERROR7 (0x1U << DMI_SERCS_ERROR7_OFFSET)
-#define DMI_SERCS_VALID7_OFFSET 22
-#define DMI_SERCS_VALID7_LENGTH 1
-#define DMI_SERCS_VALID7 (0x1U << DMI_SERCS_VALID7_OFFSET)
-#define DMI_SERCS_FULL7_OFFSET 21
-#define DMI_SERCS_FULL7_LENGTH 1
-#define DMI_SERCS_FULL7 (0x1U << DMI_SERCS_FULL7_OFFSET)
-#define DMI_SERCS_ERROR6_OFFSET 20
-#define DMI_SERCS_ERROR6_LENGTH 1
-#define DMI_SERCS_ERROR6 (0x1U << DMI_SERCS_ERROR6_OFFSET)
-#define DMI_SERCS_VALID6_OFFSET 19
-#define DMI_SERCS_VALID6_LENGTH 1
-#define DMI_SERCS_VALID6 (0x1U << DMI_SERCS_VALID6_OFFSET)
-#define DMI_SERCS_FULL6_OFFSET 18
-#define DMI_SERCS_FULL6_LENGTH 1
-#define DMI_SERCS_FULL6 (0x1U << DMI_SERCS_FULL6_OFFSET)
-#define DMI_SERCS_ERROR5_OFFSET 17
-#define DMI_SERCS_ERROR5_LENGTH 1
-#define DMI_SERCS_ERROR5 (0x1U << DMI_SERCS_ERROR5_OFFSET)
-#define DMI_SERCS_VALID5_OFFSET 16
-#define DMI_SERCS_VALID5_LENGTH 1
-#define DMI_SERCS_VALID5 (0x1U << DMI_SERCS_VALID5_OFFSET)
-#define DMI_SERCS_FULL5_OFFSET 15
-#define DMI_SERCS_FULL5_LENGTH 1
-#define DMI_SERCS_FULL5 (0x1U << DMI_SERCS_FULL5_OFFSET)
-#define DMI_SERCS_ERROR4_OFFSET 14
-#define DMI_SERCS_ERROR4_LENGTH 1
-#define DMI_SERCS_ERROR4 (0x1U << DMI_SERCS_ERROR4_OFFSET)
-#define DMI_SERCS_VALID4_OFFSET 13
-#define DMI_SERCS_VALID4_LENGTH 1
-#define DMI_SERCS_VALID4 (0x1U << DMI_SERCS_VALID4_OFFSET)
-#define DMI_SERCS_FULL4_OFFSET 12
-#define DMI_SERCS_FULL4_LENGTH 1
-#define DMI_SERCS_FULL4 (0x1U << DMI_SERCS_FULL4_OFFSET)
-#define DMI_SERCS_ERROR3_OFFSET 11
-#define DMI_SERCS_ERROR3_LENGTH 1
-#define DMI_SERCS_ERROR3 (0x1U << DMI_SERCS_ERROR3_OFFSET)
-#define DMI_SERCS_VALID3_OFFSET 10
-#define DMI_SERCS_VALID3_LENGTH 1
-#define DMI_SERCS_VALID3 (0x1U << DMI_SERCS_VALID3_OFFSET)
-#define DMI_SERCS_FULL3_OFFSET 9
-#define DMI_SERCS_FULL3_LENGTH 1
-#define DMI_SERCS_FULL3 (0x1U << DMI_SERCS_FULL3_OFFSET)
-#define DMI_SERCS_ERROR2_OFFSET 8
-#define DMI_SERCS_ERROR2_LENGTH 1
-#define DMI_SERCS_ERROR2 (0x1U << DMI_SERCS_ERROR2_OFFSET)
-#define DMI_SERCS_VALID2_OFFSET 7
-#define DMI_SERCS_VALID2_LENGTH 1
-#define DMI_SERCS_VALID2 (0x1U << DMI_SERCS_VALID2_OFFSET)
-#define DMI_SERCS_FULL2_OFFSET 6
-#define DMI_SERCS_FULL2_LENGTH 1
-#define DMI_SERCS_FULL2 (0x1U << DMI_SERCS_FULL2_OFFSET)
-#define DMI_SERCS_ERROR1_OFFSET 5
-#define DMI_SERCS_ERROR1_LENGTH 1
-#define DMI_SERCS_ERROR1 (0x1U << DMI_SERCS_ERROR1_OFFSET)
-#define DMI_SERCS_VALID1_OFFSET 4
-#define DMI_SERCS_VALID1_LENGTH 1
-#define DMI_SERCS_VALID1 (0x1U << DMI_SERCS_VALID1_OFFSET)
-#define DMI_SERCS_FULL1_OFFSET 3
-#define DMI_SERCS_FULL1_LENGTH 1
-#define DMI_SERCS_FULL1 (0x1U << DMI_SERCS_FULL1_OFFSET)
-/*
-* 1 when the debugger-to-core queue for serial port 0 has
-* over or underflowed. This bit will remain set until it is reset by
-* writing 1 to this bit.
- */
-#define DMI_SERCS_ERROR0_OFFSET 2
-#define DMI_SERCS_ERROR0_LENGTH 1
-#define DMI_SERCS_ERROR0 (0x1U << DMI_SERCS_ERROR0_OFFSET)
-/*
-* 1 when the core-to-debugger queue for serial port 0 is not empty.
- */
-#define DMI_SERCS_VALID0_OFFSET 1
-#define DMI_SERCS_VALID0_LENGTH 1
-#define DMI_SERCS_VALID0 (0x1U << DMI_SERCS_VALID0_OFFSET)
-/*
-* 1 when the debugger-to-core queue for serial port 0 is full.
- */
-#define DMI_SERCS_FULL0_OFFSET 0
-#define DMI_SERCS_FULL0_LENGTH 1
-#define DMI_SERCS_FULL0 (0x1U << DMI_SERCS_FULL0_OFFSET)
-#define DMI_SERTX 0x35
-#define DMI_SERTX_DATA_OFFSET 0
-#define DMI_SERTX_DATA_LENGTH 32
-#define DMI_SERTX_DATA (0xffffffffU << DMI_SERTX_DATA_OFFSET)
-#define DMI_SERRX 0x36
-#define DMI_SERRX_DATA_OFFSET 0
-#define DMI_SERRX_DATA_LENGTH 32
-#define DMI_SERRX_DATA (0xffffffffU << DMI_SERRX_DATA_OFFSET)
diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h
index c109ce1..e214c0c 100644
--- a/src/target/riscv/encoding.h
+++ b/src/target/riscv/encoding.h
@@ -1,4 +1,4 @@
-// See LICENSE for license details.
+/* See LICENSE for license details. */
#ifndef RISCV_CSR_ENCODING_H
#define RISCV_CSR_ENCODING_H
@@ -156,16 +156,16 @@
#define EXT_IO_BASE 0x40000000
#define DRAM_BASE 0x80000000
-// page table entry (PTE) fields
-#define PTE_V 0x001 // Valid
-#define PTE_R 0x002 // Read
-#define PTE_W 0x004 // Write
-#define PTE_X 0x008 // Execute
-#define PTE_U 0x010 // User
-#define PTE_G 0x020 // Global
-#define PTE_A 0x040 // Accessed
-#define PTE_D 0x080 // Dirty
-#define PTE_SOFT 0x300 // Reserved for Software
+/* page table entry (PTE) fields */
+#define PTE_V 0x001 /* Valid */
+#define PTE_R 0x002 /* Read */
+#define PTE_W 0x004 /* Write */
+#define PTE_X 0x008 /* Execute */
+#define PTE_U 0x010 /* User */
+#define PTE_G 0x020 /* Global */
+#define PTE_A 0x040 /* Accessed */
+#define PTE_D 0x080 /* Dirty */
+#define PTE_SOFT 0x300 /* Reserved for Software */
#define PTE_PPN_SHIFT 10
@@ -191,6 +191,7 @@
#ifdef __GNUC__
+/*
#define read_csr(reg) ({ unsigned long __tmp; \
asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
__tmp; })
@@ -209,6 +210,7 @@
#define clear_csr(reg, bit) ({ unsigned long __tmp; \
asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \
__tmp; })
+ */
#define rdtime() read_csr(time)
#define rdcycle() read_csr(cycle)
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index 632567f..a77b007 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -240,6 +240,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot)
case SLOT1: return 5;
case SLOT_LAST: return info->dramsize-1;
}
+ break;
case 64:
switch (slot) {
case SLOT0: return 4;
@@ -455,12 +456,12 @@ static uint64_t dbus_read(struct target *target, uint16_t address)
uint64_t value;
dbus_status_t status;
uint16_t address_in;
-
+
/* If the previous read/write was to the same address, we will get the read data
* from the previous access.
* While somewhat nonintuitive, this is an efficient way to get the data.
*/
-
+
unsigned i = 0;
do {
status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, address, 0);
@@ -680,7 +681,7 @@ static bits_t read_bits(struct target *target)
}
increase_dbus_busy_delay(target);
} else if (status == DBUS_STATUS_FAILED) {
- // TODO: return an actual error
+ /* TODO: return an actual error */
return err_result;
}
} while (status == DBUS_STATUS_BUSY && i++ < 256);
@@ -1407,12 +1408,6 @@ static int strict_step(struct target *target, bool announce)
LOG_DEBUG("enter");
- struct breakpoint *breakpoint = target->breakpoints;
- while (breakpoint) {
- riscv_remove_breakpoint(target, breakpoint);
- breakpoint = breakpoint->next;
- }
-
struct watchpoint *watchpoint = target->watchpoints;
while (watchpoint) {
riscv_remove_watchpoint(target, watchpoint);
@@ -1423,12 +1418,6 @@ static int strict_step(struct target *target, bool announce)
if (result != ERROR_OK)
return result;
- breakpoint = target->breakpoints;
- while (breakpoint) {
- riscv_add_breakpoint(target, breakpoint);
- breakpoint = breakpoint->next;
- }
-
watchpoint = target->watchpoints;
while (watchpoint) {
riscv_add_watchpoint(target, watchpoint);
@@ -1572,11 +1561,11 @@ static int examine(struct target *target)
}
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
- if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
+ if (read_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) {
const unsigned old_csr_misa = 0xf10;
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
old_csr_misa);
- if (read_csr(target, &r->misa, old_csr_misa) != ERROR_OK) {
+ if (read_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) {
/* Maybe this is an old core that still has $misa at the old
* address. */
LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa);
@@ -1598,7 +1587,7 @@ static int examine(struct target *target)
for (size_t i = 0; i < 32; ++i)
reg_cache_set(target, i, -1);
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64,
- riscv_xlen(target), r->misa);
+ riscv_xlen(target), r->misa[0]);
return ERROR_OK;
}
@@ -1787,6 +1776,8 @@ static riscv_error_t handle_halt_routine(struct target *target)
break;
default:
assert(0);
+ LOG_ERROR("Got invalid register result %d", result);
+ goto error;
}
if (riscv_xlen(target) == 32) {
reg_cache_set(target, reg, data & 0xffffffff);
@@ -1847,7 +1838,7 @@ static int handle_halt(struct target *target, bool announce)
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case DCSR_CAUSE_HWBP:
- target->debug_reason = DBG_REASON_WPTANDBKPT;
+ target->debug_reason = DBG_REASON_WATCHPOINT;
/* If we halted because of a data trigger, gdb doesn't know to do
* the disable-breakpoints-step-enable-breakpoints dance. */
info->need_strict_step = true;
@@ -1873,6 +1864,12 @@ static int handle_halt(struct target *target, bool announce)
riscv_enumerate_triggers(target);
}
+ if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+ int retval;
+ if (riscv_semihosting(target, &retval) != 0)
+ return retval;
+ }
+
if (announce)
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 1cfda35..3ab8587 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -39,7 +39,7 @@ static void riscv013_clear_abstract_error(struct target *target);
static int riscv013_get_register(struct target *target,
riscv_reg_t *value, int hid, int rid);
static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
-static void riscv013_select_current_hart(struct target *target);
+static int riscv013_select_current_hart(struct target *target);
static int riscv013_halt_current_hart(struct target *target);
static int riscv013_resume_current_hart(struct target *target);
static int riscv013_step_current_hart(struct target *target);
@@ -57,6 +57,7 @@ static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a,
static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a);
static int riscv013_dmi_write_u64_bits(struct target *target);
static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf);
+static int register_read(struct target *target, uint64_t *value, uint32_t number);
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
static int register_write_direct(struct target *target, unsigned number,
uint64_t value);
@@ -149,6 +150,8 @@ typedef struct {
bool was_reset;
/* Targets that are connected to this DM. */
struct list_head target_list;
+ /* The currently selected hartid on this DM. */
+ int current_hartid;
} dm013_info_t;
typedef struct {
@@ -191,8 +194,6 @@ typedef struct {
* go low. */
unsigned int ac_busy_delay;
- bool need_strict_step;
-
bool abstract_read_csr_supported;
bool abstract_write_csr_supported;
bool abstract_read_fpr_supported;
@@ -248,6 +249,7 @@ static dm013_info_t *get_dm(struct target *target)
if (!dm) {
dm = calloc(1, sizeof(dm013_info_t));
dm->abs_chain_position = abs_chain_position;
+ dm->current_hartid = -1;
INIT_LIST_HEAD(&dm->target_list);
list_add(&dm->list, &dm_list);
}
@@ -255,9 +257,8 @@ static dm013_info_t *get_dm(struct target *target)
info->dm = dm;
target_list_t *target_entry;
list_for_each_entry(target_entry, &dm->target_list, list) {
- if (target_entry->target == target) {
+ if (target_entry->target == target)
return dm;
- }
}
target_entry = calloc(1, sizeof(*target_entry));
target_entry->target = target;
@@ -266,10 +267,18 @@ static dm013_info_t *get_dm(struct target *target)
return dm;
}
-static uint32_t hartsel_mask(const struct target *target)
+static uint32_t set_hartsel(uint32_t initial, uint32_t index)
{
- RISCV013_INFO(info);
- return ((1L<<info->hartsellen)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
+ initial &= ~DMI_DMCONTROL_HARTSELLO;
+ initial &= ~DMI_DMCONTROL_HARTSELHI;
+
+ uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1);
+ initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET;
+ uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH;
+ assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH);
+ initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET;
+
+ return initial;
}
static void decode_dmi(char *text, unsigned address, unsigned data)
@@ -283,11 +292,15 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
{ DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" },
{ DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" },
{ DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" },
{ DMI_DMSTATUS, DMI_DMSTATUS_IMPEBREAK, "impebreak" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLHAVERESET, "allhavereset" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYHAVERESET, "anyhavereset" },
{ DMI_DMSTATUS, DMI_DMSTATUS_ALLRESUMEACK, "allresumeack" },
{ DMI_DMSTATUS, DMI_DMSTATUS_ANYRESUMEACK, "anyresumeack" },
{ DMI_DMSTATUS, DMI_DMSTATUS_ALLNONEXISTENT, "allnonexistent" },
@@ -485,58 +498,79 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
-static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
+static int dmi_op_timeout(struct target *target, uint32_t *data_in, int dmi_op,
+ uint32_t address, uint32_t data_out, int timeout_sec)
{
select_dmi(target);
dmi_status_t status;
uint32_t address_in;
- unsigned i = 0;
+ const char *op_name;
+ switch (dmi_op) {
+ case DMI_OP_NOP:
+ op_name = "nop";
+ break;
+ case DMI_OP_READ:
+ op_name = "read";
+ break;
+ case DMI_OP_WRITE:
+ op_name = "write";
+ break;
+ default:
+ LOG_ERROR("Invalid DMI operation: %d", dmi_op);
+ return ERROR_FAIL;
+ }
- /* This first loop ensures that the read request was actually sent
- * to the target. Note that if for some reason this stays busy,
- * it is actually due to the previous dmi_read or dmi_write. */
- for (i = 0; i < 256; i++) {
- status = dmi_scan(target, NULL, NULL, DMI_OP_READ, address, 0,
+ time_t start = time(NULL);
+ /* This first loop performs the request. Note that if for some reason this
+ * stays busy, it is actually due to the previous access. */
+ while (1) {
+ status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out,
false);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
} else if (status == DMI_STATUS_SUCCESS) {
break;
} else {
- LOG_ERROR("failed read from 0x%x, status=%d", address, status);
+ LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status);
return ERROR_FAIL;
}
+ if (time(NULL) - start > timeout_sec)
+ return ERROR_TIMEOUT_REACHED;
}
if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("Failed read from 0x%x; status=%d", address, status);
+ LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status);
return ERROR_FAIL;
}
- /* This second loop ensures that we got the read
- * data back. Note that NOP can result in a 'busy' result as well, but
- * that would be noticed on the next DMI access we do. */
- for (i = 0; i < 256; i++) {
- status = dmi_scan(target, &address_in, value, DMI_OP_NOP, address, 0,
+ /* This second loop ensures the request succeeded, and gets back data.
+ * Note that NOP can result in a 'busy' result as well, but that would be
+ * noticed on the next DMI access we do. */
+ while (1) {
+ status = dmi_scan(target, &address_in, data_in, DMI_OP_NOP, address, 0,
false);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
} else if (status == DMI_STATUS_SUCCESS) {
break;
} else {
- LOG_ERROR("failed read (NOP) at 0x%x, status=%d", address, status);
+ LOG_ERROR("failed %s (NOP) at 0x%x, status=%d", op_name, address,
+ status);
return ERROR_FAIL;
}
+ if (time(NULL) - start > timeout_sec)
+ return ERROR_TIMEOUT_REACHED;
}
if (status != DMI_STATUS_SUCCESS) {
- if (status == DMI_STATUS_FAILED) {
- LOG_ERROR("Failed read (NOP) from 0x%x; status=%d", address, status);
+ if (status == DMI_STATUS_FAILED || !data_in) {
+ LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address,
+ status);
} else {
- LOG_ERROR("Failed read (NOP) from 0x%x; value=0x%x, status=%d",
- address, *value, status);
+ LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d",
+ op_name, address, *data_in, status);
}
return ERROR_FAIL;
}
@@ -544,69 +578,54 @@ static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
return ERROR_OK;
}
-static int dmi_write(struct target *target, uint32_t address, uint32_t value)
+static int dmi_op(struct target *target, uint32_t *data_in, int dmi_op,
+ uint32_t address, uint32_t data_out)
{
- select_dmi(target);
- dmi_status_t status = DMI_STATUS_BUSY;
- unsigned i = 0;
-
- /* The first loop ensures that we successfully sent the write request. */
- for (i = 0; i < 256; i++) {
- status = dmi_scan(target, NULL, NULL, DMI_OP_WRITE, address, value,
- address == DMI_COMMAND);
- if (status == DMI_STATUS_BUSY) {
- increase_dmi_busy_delay(target);
- } else if (status == DMI_STATUS_SUCCESS) {
- break;
- } else {
- LOG_ERROR("failed write to 0x%x, status=%d", address, status);
- break;
- }
- }
-
- if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("Failed write to 0x%x;, status=%d",
- address, status);
+ int result = dmi_op_timeout(target, data_in, dmi_op, address, data_out,
+ riscv_command_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED) {
+ LOG_ERROR("DMI operation didn't complete in %d seconds. The target is "
+ "either really slow or broken. You could increase the "
+ "timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec);
return ERROR_FAIL;
}
+ return result;
+}
- /* The second loop isn't strictly necessary, but would ensure that the
- * write is complete/ has no non-busy errors before returning from this
- * function. */
- for (i = 0; i < 256; i++) {
- status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, address, 0,
- false);
- if (status == DMI_STATUS_BUSY) {
- increase_dmi_busy_delay(target);
- } else if (status == DMI_STATUS_SUCCESS) {
- break;
- } else {
- LOG_ERROR("failed write (NOP) at 0x%x, status=%d", address, status);
- break;
- }
- }
- if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("failed to write (NOP) 0x%x to 0x%x; status=%d", value, address, status);
- return ERROR_FAIL;
- }
+static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
+{
+ return dmi_op(target, value, DMI_OP_READ, address, 0);
+}
- return ERROR_OK;
+static int dmi_write(struct target *target, uint32_t address, uint32_t value)
+{
+ return dmi_op(target, NULL, DMI_OP_WRITE, address, value);
}
-int dmstatus_read(struct target *target, uint32_t *dmstatus,
- bool authenticated)
+int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
+ bool authenticated, unsigned timeout_sec)
{
- if (dmi_read(target, dmstatus, DMI_DMSTATUS) != ERROR_OK)
- return ERROR_FAIL;
+ int result = dmi_op_timeout(target, dmstatus, DMI_OP_READ, DMI_DMSTATUS, 0,
+ timeout_sec);
+ if (result != ERROR_OK)
+ return result;
if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
LOG_ERROR("Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
- "`riscv_authdata_write` commands to authenticate.", *dmstatus);
+ "`riscv authdata_write` commands to authenticate.", *dmstatus);
return ERROR_FAIL;
}
return ERROR_OK;
}
+int dmstatus_read(struct target *target, uint32_t *dmstatus,
+ bool authenticated)
+{
+ return dmstatus_read_timeout(target, dmstatus, authenticated,
+ riscv_command_timeout_sec);
+}
+
static void increase_ac_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
@@ -676,17 +695,12 @@ static int execute_abstract_command(struct target *target, uint32_t command)
LOG_DEBUG("command=0x%x", command);
dmi_write(target, DMI_COMMAND, command);
- {
- uint32_t abstractcs = 0;
- wait_for_idle(target, &abstractcs);
- }
+ uint32_t abstractcs = 0;
+ wait_for_idle(target, &abstractcs);
- uint32_t cs;
- if (dmi_read(target, &cs, DMI_ABSTRACTCS) != ERROR_OK)
- return ERROR_FAIL;
- info->cmderr = get_field(cs, DMI_ABSTRACTCS_CMDERR);
+ info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
if (info->cmderr != 0) {
- LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, cs);
+ LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs);
/* Clear the error. */
dmi_write(target, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR,
info->cmderr));
@@ -737,8 +751,8 @@ static int write_abstract_arg(struct target *target, unsigned index,
/**
* @size in bits
*/
-static uint32_t access_register_command(uint32_t number, unsigned size,
- uint32_t flags)
+static uint32_t access_register_command(struct target *target, uint32_t number,
+ unsigned size, uint32_t flags)
{
uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
switch (size) {
@@ -761,8 +775,13 @@ static uint32_t access_register_command(uint32_t number, unsigned size,
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
number - GDB_REGNO_CSR0);
- } else {
- assert(0);
+ } else if (number >= GDB_REGNO_COUNT) {
+ /* Custom register. */
+ assert(target->reg_cache->reg_list[number].arch_info);
+ riscv_reg_info_t *reg_info = target->reg_cache->reg_list[number].arch_info;
+ assert(reg_info);
+ command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+ 0xc000 + reg_info->custom_number);
}
command |= flags;
@@ -782,7 +801,7 @@ static int register_read_abstract(struct target *target, uint64_t *value,
!info->abstract_read_csr_supported)
return ERROR_FAIL;
- uint32_t command = access_register_command(number, size,
+ uint32_t command = access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER);
int result = execute_abstract_command(target, command);
@@ -817,7 +836,7 @@ static int register_write_abstract(struct target *target, uint32_t number,
!info->abstract_write_csr_supported)
return ERROR_FAIL;
- uint32_t command = access_register_command(number, size,
+ uint32_t command = access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
@@ -857,7 +876,7 @@ static int examine_progbuf(struct target *target)
}
uint64_t s0;
- if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -913,22 +932,25 @@ typedef struct {
riscv_addr_t hart_address;
/* Memory address to access the scratch memory from the debugger. */
riscv_addr_t debug_address;
+ struct working_area *area;
} scratch_mem_t;
/**
* Find some scratch memory to be used with the given program.
*/
-static int scratch_find(struct target *target,
+static int scratch_reserve(struct target *target,
scratch_mem_t *scratch,
struct riscv_program *program,
unsigned size_bytes)
{
- riscv013_info_t *info = get_info(target);
-
riscv_addr_t alignment = 1;
while (alignment < size_bytes)
alignment *= 2;
+ scratch->area = NULL;
+
+ riscv013_info_t *info = get_info(target);
+
if (info->dataaccess == 1) {
/* Sign extend dataaddr. */
scratch->hart_address = info->dataaddr;
@@ -959,8 +981,9 @@ static int scratch_find(struct target *target,
return ERROR_OK;
}
- if (riscv_use_scratch_ram) {
- scratch->hart_address = (riscv_scratch_ram_address + alignment - 1) &
+ if (target_alloc_working_area(target, size_bytes + alignment - 1,
+ &scratch->area) == ERROR_OK) {
+ scratch->hart_address = (scratch->area->address + alignment - 1) &
~(alignment - 1);
scratch->memory_space = SPACE_DMI_RAM;
scratch->debug_address = scratch->hart_address;
@@ -968,10 +991,19 @@ static int scratch_find(struct target *target,
}
LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure "
- "an address with 'riscv set_scratch_ram'.", size_bytes);
+ "a work area with 'configure -work-area-phys'.", size_bytes);
return ERROR_FAIL;
}
+static int scratch_release(struct target *target,
+ scratch_mem_t *scratch)
+{
+ if (scratch->area)
+ return target_free_working_area(target, scratch->area);
+
+ return ERROR_OK;
+}
+
static int scratch_read64(struct target *target, scratch_mem_t *scratch,
uint64_t *value)
{
@@ -1055,6 +1087,10 @@ static unsigned register_size(struct target *target, unsigned number)
return riscv_xlen(target);
}
+/**
+ * Immediately write the new value to the requested register. This mechanism
+ * bypasses any caches.
+ */
static int register_write_direct(struct target *target, unsigned number,
uint64_t value)
{
@@ -1066,6 +1102,11 @@ static int register_write_direct(struct target *target, unsigned number,
int result = register_write_abstract(target, number, value,
register_size(target, number));
+ if (result == ERROR_OK && target->reg_cache) {
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = true;
+ }
if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 ||
!riscv_is_halted(target))
return result;
@@ -1074,33 +1115,39 @@ static int register_write_direct(struct target *target, unsigned number,
riscv_program_init(&program, target);
uint64_t s0;
- if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
+ scratch_mem_t scratch;
+ bool use_scratch = false;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
- riscv_supports_extension(target, 'D') &&
+ riscv_supports_extension(target, riscv_current_hartid(target), 'D') &&
riscv_xlen(target) < 64) {
/* There are no instructions to move all the bits from a register, so
* we need to use some scratch RAM. */
+ use_scratch = true;
riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
- scratch_mem_t scratch;
- if (scratch_find(target, &scratch, &program, 8) != ERROR_OK)
+ if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
return ERROR_FAIL;
if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
- != ERROR_OK)
+ != ERROR_OK) {
+ scratch_release(target, &scratch);
return ERROR_FAIL;
+ }
- if (scratch_write64(target, &scratch, value) != ERROR_OK)
+ if (scratch_write64(target, &scratch, value) != ERROR_OK) {
+ scratch_release(target, &scratch);
return ERROR_FAIL;
+ }
} else {
if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
return ERROR_FAIL;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- if (riscv_supports_extension(target, 'D'))
+ if (riscv_supports_extension(target, riscv_current_hartid(target), 'D'))
riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
else
riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
@@ -1114,6 +1161,14 @@ static int register_write_direct(struct target *target, unsigned number,
int exec_out = riscv_program_exec(&program, target);
/* Don't message on error. Probably the register doesn't exist. */
+ if (exec_out == ERROR_OK && target->reg_cache) {
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = true;
+ }
+
+ if (use_scratch)
+ scratch_release(target, &scratch);
/* Restore S0. */
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
@@ -1122,6 +1177,35 @@ static int register_write_direct(struct target *target, unsigned number,
return exec_out;
}
+/** Return the cached value, or read from the target if necessary. */
+static int register_read(struct target *target, uint64_t *value, uint32_t number)
+{
+ if (number == GDB_REGNO_ZERO) {
+ *value = 0;
+ return ERROR_OK;
+ }
+ if (target->reg_cache &&
+ (number <= GDB_REGNO_XPR31 ||
+ (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31))) {
+ /* Only check the cache for registers that we know won't spontaneously
+ * change. */
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ if (reg && reg->valid) {
+ *value = buf_get_u64(reg->value, 0, reg->size);
+ return ERROR_OK;
+ }
+ }
+ int result = register_read_direct(target, value, number);
+ if (result != ERROR_OK)
+ return ERROR_FAIL;
+ if (target->reg_cache) {
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ buf_set_u64(reg->value, 0, reg->size, *value);
+ reg->valid = true;
+ }
+ return ERROR_OK;
+}
+
/** Actually read registers from the target right now. */
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
{
@@ -1131,10 +1215,9 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
int result = register_read_abstract(target, value, number,
register_size(target, number));
- if (result != ERROR_OK && info->progbufsize + r->impebreak >= 2 &&
- riscv_is_halted(target)) {
- assert(number != GDB_REGNO_S0);
-
+ if (result != ERROR_OK &&
+ info->progbufsize + r->impebreak >= 2 &&
+ number > GDB_REGNO_XPR31) {
struct riscv_program program;
riscv_program_init(&program, target);
@@ -1142,34 +1225,38 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
bool use_scratch = false;
uint64_t s0;
- if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
/* Write program to move data into s0. */
uint64_t mstatus;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- if (register_read_direct(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
return ERROR_FAIL;
if ((mstatus & MSTATUS_FS) == 0)
if (register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) {
+ if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
+ && riscv_xlen(target) < 64) {
/* There are no instructions to move all the bits from a
* register, so we need to use some scratch RAM. */
riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
0));
- if (scratch_find(target, &scratch, &program, 8) != ERROR_OK)
+ if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
return ERROR_FAIL;
use_scratch = true;
if (register_write_direct(target, GDB_REGNO_S0,
- scratch.hart_address) != ERROR_OK)
+ scratch.hart_address) != ERROR_OK) {
+ scratch_release(target, &scratch);
return ERROR_FAIL;
- } else if (riscv_supports_extension(target, 'D')) {
+ }
+ } else if (riscv_supports_extension(target,
+ riscv_current_hartid(target), 'D')) {
riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
} else {
riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
@@ -1186,8 +1273,10 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
/* Don't message on error. Probably the register doesn't exist. */
if (use_scratch) {
- if (scratch_read64(target, &scratch, value) != ERROR_OK)
- return ERROR_FAIL;
+ result = scratch_read64(target, &scratch, value);
+ scratch_release(target, &scratch);
+ if (result != ERROR_OK)
+ return result;
} else {
/* Read S0 */
if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
@@ -1242,6 +1331,7 @@ static void deinit_target(struct target *target)
LOG_DEBUG("riscv_deinit_target()");
riscv_info_t *info = (riscv_info_t *) target->arch_info;
free(info->version_specific);
+ /* TODO: free register arch_info */
info->version_specific = NULL;
}
@@ -1288,8 +1378,8 @@ static int examine(struct target *target)
dm->was_reset = true;
}
- uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
- dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE);
+ dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO |
+ DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE);
uint32_t dmcontrol;
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
@@ -1300,7 +1390,10 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
- uint32_t hartsel = get_field(dmcontrol, max_hartsel_mask);
+ uint32_t hartsel =
+ (get_field(dmcontrol, DMI_DMCONTROL_HARTSELHI) <<
+ DMI_DMCONTROL_HARTSELLO_LENGTH) |
+ get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO);
info->hartsellen = 0;
while (hartsel & 1) {
info->hartsellen++;
@@ -1319,7 +1412,7 @@ static int examine(struct target *target)
if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
LOG_ERROR("Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
- "`riscv_authdata_write` commands to authenticate.", dmstatus);
+ "`riscv authdata_write` commands to authenticate.", dmstatus);
/* If we return ERROR_FAIL here, then in a multicore setup the next
* core won't be examined, which means we won't set up the
* authentication commands for them, which means the config script
@@ -1358,7 +1451,8 @@ static int examine(struct target *target)
continue;
r->current_hartid = i;
- riscv013_select_current_hart(target);
+ if (riscv013_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
uint32_t s;
if (dmstatus_read(target, &s, true) != ERROR_OK)
@@ -1367,6 +1461,10 @@ static int examine(struct target *target)
break;
r->hart_count = i + 1;
+ if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
+
if (!riscv_is_halted(target)) {
if (riscv013_halt_current_hart(target) != ERROR_OK) {
LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i);
@@ -1384,7 +1482,7 @@ static int examine(struct target *target)
else
r->xlen[i] = 32;
- if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) {
+ if (register_read(target, &r->misa[i], GDB_REGNO_MISA)) {
LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i);
return ERROR_FAIL;
}
@@ -1396,7 +1494,7 @@ static int examine(struct target *target)
/* Display this as early as possible to help people who are using
* really slow simulators. */
LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
- r->misa);
+ r->misa[i]);
}
LOG_DEBUG("Enumerated %d harts", r->hart_count);
@@ -1406,9 +1504,6 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
- /* Then we check the number of triggers availiable to each hart. */
- riscv_enumerate_triggers(target);
-
/* Resumes all the harts, so the debugger can later pause them. */
/* TODO: Only do this if the harts were halted to start with. */
riscv_resume_all_harts(target);
@@ -1426,8 +1521,8 @@ static int examine(struct target *target)
riscv_count_harts(target));
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (riscv_hart_enabled(target, i)) {
- LOG_INFO(" hart %d: XLEN=%d, %d triggers", i, r->xlen[i],
- r->trigger_count[i]);
+ LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
+ r->misa[i]);
} else {
LOG_INFO(" hart %d: currently disabled", i);
}
@@ -1538,13 +1633,13 @@ static int assert_reset(struct target *target)
/* TODO: Try to use hasel in dmcontrol */
- /* Set haltreq/resumereq for each hart. */
+ /* Set haltreq for each hart. */
uint32_t control = control_base;
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
- control = set_field(control_base, hartsel_mask(target), i);
+ control = set_hartsel(control_base, i);
control = set_field(control, DMI_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
dmi_write(target, DMI_DMCONTROL, control);
@@ -1555,24 +1650,11 @@ static int assert_reset(struct target *target)
} else {
/* Reset just this hart. */
- uint32_t control = set_field(control_base, hartsel_mask(target),
- r->current_hartid);
+ uint32_t control = set_hartsel(control_base, r->current_hartid);
control = set_field(control, DMI_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
- control = set_field(control, DMI_DMCONTROL_HARTRESET, 1);
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
dmi_write(target, DMI_DMCONTROL, control);
-
- /* Read back to check if hartreset is supported. */
- uint32_t rb;
- if (dmi_read(target, &rb, DMI_DMCONTROL) != ERROR_OK)
- return ERROR_FAIL;
- if (!get_field(rb, DMI_DMCONTROL_HARTRESET)) {
- /* Use ndmreset instead. That will reset the entire device, but
- * that's probably what OpenOCD wants anyway. */
- control = set_field(control, DMI_DMCONTROL_HARTRESET, 0);
- control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
- dmi_write(target, DMI_DMCONTROL, control);
- }
}
target->state = TARGET_RESET;
@@ -1586,57 +1668,69 @@ static int deassert_reset(struct target *target)
RISCV013_INFO(info);
select_dmi(target);
- LOG_DEBUG("%d", r->current_hartid);
-
/* Clear the reset, but make sure haltreq is still set */
uint32_t control = 0;
control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
- control = set_field(control, hartsel_mask(target), r->current_hartid);
control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
- dmi_write(target, DMI_DMCONTROL, control);
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(control, r->current_hartid));
uint32_t dmstatus;
int dmi_busy_delay = info->dmi_busy_delay;
time_t start = time(NULL);
- if (target->reset_halt) {
- LOG_DEBUG("Waiting for hart to be halted.");
- do {
- if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
- return ERROR_FAIL;
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ int index = i;
+ if (target->rtos) {
+ if (!riscv_hart_enabled(target, index))
+ continue;
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(control, index));
+ } else {
+ index = r->current_hartid;
+ }
+
+ char *operation;
+ uint32_t expected_field;
+ if (target->reset_halt) {
+ operation = "halt";
+ expected_field = DMI_DMSTATUS_ALLHALTED;
+ } else {
+ operation = "run";
+ expected_field = DMI_DMSTATUS_ALLRUNNING;
+ }
+ LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation);
+ while (1) {
+ int result = dmstatus_read_timeout(target, &dmstatus, true,
+ riscv_reset_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED)
+ LOG_ERROR("Hart %d didn't complete a DMI read coming out of "
+ "reset in %ds; Increase the timeout with riscv "
+ "set_reset_timeout_sec.",
+ index, riscv_reset_timeout_sec);
+ if (result != ERROR_OK)
+ return result;
+ if (get_field(dmstatus, expected_field))
+ break;
if (time(NULL) - start > riscv_reset_timeout_sec) {
- LOG_ERROR("Hart didn't halt coming out of reset in %ds; "
+ LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; "
"dmstatus=0x%x; "
"Increase the timeout with riscv set_reset_timeout_sec.",
- riscv_reset_timeout_sec, dmstatus);
+ index, operation, riscv_reset_timeout_sec, dmstatus);
return ERROR_FAIL;
}
- target->state = TARGET_HALTED;
- } while (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0);
+ }
+ target->state = TARGET_HALTED;
- control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
- dmi_write(target, DMI_DMCONTROL, control);
+ if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) {
+ /* Ack reset. */
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(control, index) |
+ DMI_DMCONTROL_ACKHAVERESET);
+ }
- } else {
- LOG_DEBUG("Waiting for hart to be running.");
- do {
- if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
- return ERROR_FAIL;
- if (get_field(dmstatus, DMI_DMSTATUS_ANYHALTED) ||
- get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
- LOG_ERROR("Unexpected hart status during reset. dmstatus=0x%x",
- dmstatus);
- return ERROR_FAIL;
- }
- if (time(NULL) - start > riscv_reset_timeout_sec) {
- LOG_ERROR("Hart didn't run coming out of reset in %ds; "
- "dmstatus=0x%x; "
- "Increase the timeout with riscv set_reset_timeout_sec.",
- riscv_reset_timeout_sec, dmstatus);
- return ERROR_FAIL;
- }
- } while (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING) == 0);
- target->state = TARGET_RUNNING;
+ if (!target->rtos)
+ break;
}
info->dmi_busy_delay = dmi_busy_delay;
return ERROR_OK;
@@ -1765,19 +1859,16 @@ static int sb_write_address(struct target *target, target_addr_t address)
RISCV013_INFO(info);
unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
/* There currently is no support for >64-bit addresses in OpenOCD. */
- if (sbasize > 96) {
+ if (sbasize > 96)
dmi_write(target, DMI_SBADDRESS3, 0);
- }
- if (sbasize > 64) {
+ if (sbasize > 64)
dmi_write(target, DMI_SBADDRESS2, 0);
- }
- if (sbasize > 32) {
+ if (sbasize > 32)
#if BUILD_TARGET64
dmi_write(target, DMI_SBADDRESS1, address >> 32);
#else
dmi_write(target, DMI_SBADDRESS1, 0);
#endif
- }
return dmi_write(target, DMI_SBADDRESS0, address);
}
@@ -1793,8 +1884,8 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec, *sbcs);
+ return ERROR_FAIL;
}
- return ERROR_FAIL;
}
}
@@ -1922,6 +2013,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR);
next_address = sb_read_address(target);
info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
+ continue;
}
unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
@@ -1958,9 +2050,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
* s1 holds the next data value to write
*/
uint64_t s0, s1;
- if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+ if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
if (execute_fence(target) != ERROR_OK)
@@ -1993,9 +2085,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
result = register_write_direct(target, GDB_REGNO_S0, address);
if (result != ERROR_OK)
goto error;
- uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target),
- AC_ACCESS_REGISTER_TRANSFER |
- AC_ACCESS_REGISTER_POSTEXEC);
+ uint32_t command = access_register_command(target, GDB_REGNO_S1,
+ riscv_xlen(target),
+ AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
result = execute_abstract_command(target, command);
if (result != ERROR_OK)
goto error;
@@ -2360,6 +2452,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR);
next_address = sb_read_address(target);
info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
+ continue;
}
unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
@@ -2391,9 +2484,9 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
int result = ERROR_OK;
uint64_t s0, s1;
- if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+ if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
/* Write the program (store, increment) */
@@ -2480,7 +2573,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
/* Write and execute command that moves value into S1 and
* executes program buffer. */
- uint32_t command = access_register_command(GDB_REGNO_S1, 32,
+ uint32_t command = access_register_command(target,
+ GDB_REGNO_S1, 32,
AC_ACCESS_REGISTER_POSTEXEC |
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
@@ -2617,14 +2711,14 @@ static int riscv013_get_register(struct target *target,
int result = ERROR_OK;
if (rid == GDB_REGNO_PC) {
- result = register_read_direct(target, value, GDB_REGNO_DPC);
+ result = register_read(target, value, GDB_REGNO_DPC);
LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, *value);
} else if (rid == GDB_REGNO_PRIV) {
uint64_t dcsr;
- result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR);
+ result = register_read(target, &dcsr, GDB_REGNO_DCSR);
*value = get_field(dcsr, CSR_DCSR_PRV);
} else {
- result = register_read_direct(target, value, rid);
+ result = register_read(target, value, rid);
if (result != ERROR_OK)
*value = -1;
}
@@ -2654,7 +2748,7 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64
}
} else if (rid == GDB_REGNO_PRIV) {
uint64_t dcsr;
- register_read_direct(target, &dcsr, GDB_REGNO_DCSR);
+ register_read(target, &dcsr, GDB_REGNO_DCSR);
dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
} else {
@@ -2664,14 +2758,22 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64
return ERROR_OK;
}
-static void riscv013_select_current_hart(struct target *target)
+static int riscv013_select_current_hart(struct target *target)
{
RISCV_INFO(r);
+ dm013_info_t *dm = get_dm(target);
+ if (r->current_hartid == dm->current_hartid)
+ return ERROR_OK;
+
uint32_t dmcontrol;
- dmi_read(target, &dmcontrol, DMI_DMCONTROL);
- dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid);
- dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
+ if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+ return ERROR_FAIL;
+ dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+ int result = dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dm->current_hartid = r->current_hartid;
+ return result;
}
static int riscv013_halt_current_hart(struct target *target)
@@ -2741,16 +2843,32 @@ static bool riscv013_is_halted(struct target *target)
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return false;
if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
- LOG_ERROR("hart %d is unavailiable", riscv_current_hartid(target));
+ LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target));
if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
- LOG_ERROR("hart %d doesn't exist", riscv_current_hartid(target));
+ LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target));
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYHAVERESET)) {
+ int hartid = riscv_current_hartid(target);
+ LOG_INFO("Hart %d unexpectedly reset!", hartid);
+ /* TODO: Can we make this more obvious to eg. a gdb user? */
+ uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE |
+ DMI_DMCONTROL_ACKHAVERESET;
+ dmcontrol = set_hartsel(dmcontrol, hartid);
+ /* If we had been halted when we reset, request another halt. If we
+ * ended up running out of reset, then the user will (hopefully) get a
+ * message that a reset happened, that the target is running, and then
+ * that it is halted again once the request goes through.
+ */
+ if (target->state == TARGET_HALTED)
+ dmcontrol |= DMI_DMCONTROL_HALTREQ;
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ }
return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED);
}
static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
{
riscv_reg_t dcsr;
- int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR);
+ int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
if (result != ERROR_OK)
return RISCV_HALT_UNKNOWN;
@@ -2758,6 +2876,10 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
case CSR_DCSR_CAUSE_SWBP:
return RISCV_HALT_BREAKPOINT;
case CSR_DCSR_CAUSE_TRIGGER:
+ /* We could get here before triggers are enumerated if a trigger was
+ * already set when we connected. Force enumeration now, which has the
+ * side effect of clearing any triggers we did not set. */
+ riscv_enumerate_triggers(target);
return RISCV_HALT_TRIGGER;
case CSR_DCSR_CAUSE_STEP:
return RISCV_HALT_SINGLESTEP;
@@ -3175,7 +3297,7 @@ static int riscv013_on_step_or_resume(struct target *target, bool step)
/* We want to twiddle some bits in the debug CSR so debugging works. */
riscv_reg_t dcsr;
- int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR);
+ int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
if (result != ERROR_OK)
return result;
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
@@ -3198,11 +3320,9 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
return ERROR_FAIL;
/* Issue the resume command, and then wait for the current hart to resume. */
- uint32_t dmcontrol;
- if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
- return ERROR_FAIL;
- dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1);
- dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE;
+ dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ);
uint32_t dmstatus;
for (size_t i = 0; i < 256; ++i) {
@@ -3214,17 +3334,16 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
continue;
- dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
return ERROR_OK;
}
- if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
- return ERROR_FAIL;
+ LOG_ERROR("unable to resume hart %d", r->current_hartid);
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
- LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return ERROR_FAIL;
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
if (step) {
@@ -3249,7 +3368,7 @@ void riscv013_clear_abstract_error(struct target *target)
LOG_ERROR("abstractcs.busy is not going low after %d seconds "
"(abstractcs=0x%x). The target is either really slow or "
"broken. You could increase the timeout with riscv "
- "set_reset_timeout_sec.",
+ "set_command_timeout_sec.",
riscv_command_timeout_sec, abstractcs);
break;
}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 0f8443f..dbfaf59 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -185,18 +185,19 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
-bool riscv_use_scratch_ram;
-uint64_t riscv_scratch_ram_address;
-
bool riscv_prefer_sba;
+typedef struct {
+ uint16_t low, high;
+} range_t;
+
/* In addition to the ones in the standard spec, we'll also expose additional
* CSRs in this list.
* The list is either NULL, or a series of ranges (inclusive), terminated with
* 1,0. */
-struct {
- uint16_t low, high;
-} *expose_csr;
+range_t *expose_csr;
+/* Same, but for custom registers. */
+range_t *expose_custom;
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
@@ -232,6 +233,11 @@ static struct target_type *get_target_type(struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ if (!info) {
+ LOG_ERROR("Target has not been initialized");
+ return NULL;
+ }
+
switch (info->dtm_version) {
case 0:
return &riscv011_target;
@@ -247,7 +253,6 @@ static int riscv_init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("riscv_init_target()");
- target->propagate_register_errors = true;
target->arch_info = calloc(1, sizeof(riscv_info_t));
if (!target->arch_info)
return ERROR_FAIL;
@@ -259,6 +264,8 @@ static int riscv_init_target(struct command_context *cmd_ctx,
select_dbus.num_bits = target->tap->ir_length;
select_idcode.num_bits = target->tap->ir_length;
+ riscv_semihosting_init(target);
+
return ERROR_OK;
}
@@ -266,9 +273,19 @@ static void riscv_deinit_target(struct target *target)
{
LOG_DEBUG("riscv_deinit_target()");
struct target_type *tt = get_target_type(target);
- tt->deinit_target(target);
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
- free(info);
+ if (tt) {
+ tt->deinit_target(target);
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ free(info->reg_names);
+ free(info);
+ }
+ /* Free the shared structure use for most registers. */
+ free(target->reg_cache->reg_list[0].arch_info);
+ /* Free the ones we allocated separately. */
+ for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
+ free(target->reg_cache->reg_list[i].arch_info);
+ free(target->reg_cache->reg_list);
+ free(target->reg_cache);
target->arch_info = NULL;
}
@@ -314,9 +331,12 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
tdata1 = set_field(tdata1, bpcontrol_r, trigger->read);
tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
- tdata1 = set_field(tdata1, bpcontrol_u, !!(r->misa & (1 << ('U' - 'A'))));
- tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & (1 << ('S' - 'A'))));
- tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & (1 << ('H' - 'A'))));
+ tdata1 = set_field(tdata1, bpcontrol_u,
+ !!(r->misa[hartid] & (1 << ('U' - 'A'))));
+ tdata1 = set_field(tdata1, bpcontrol_s,
+ !!(r->misa[hartid] & (1 << ('S' - 'A'))));
+ tdata1 = set_field(tdata1, bpcontrol_h,
+ !!(r->misa[hartid] & (1 << ('H' - 'A'))));
tdata1 |= bpcontrol_m;
tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
@@ -359,11 +379,11 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
MCONTROL_ACTION_DEBUG_MODE);
tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
tdata1 |= MCONTROL_M;
- if (r->misa & (1 << ('H' - 'A')))
+ if (r->misa[hartid] & (1 << ('H' - 'A')))
tdata1 |= MCONTROL_H;
- if (r->misa & (1 << ('S' - 'A')))
+ if (r->misa[hartid] & (1 << ('S' - 'A')))
tdata1 |= MCONTROL_S;
- if (r->misa & (1 << ('U' - 'A')))
+ if (r->misa[hartid] & (1 << ('U' - 'A')))
tdata1 |= MCONTROL_U;
if (trigger->execute)
@@ -398,6 +418,9 @@ static int add_trigger(struct target *target, struct trigger *trigger)
{
RISCV_INFO(r);
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
/* In RTOS mode, we need to set the same trigger in the same slot on every
* hart, to keep up the illusion that each hart is a thread running on the
* same core. */
@@ -522,6 +545,9 @@ static int remove_trigger(struct target *target, struct trigger *trigger)
{
RISCV_INFO(r);
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
int first_hart = -1;
for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
@@ -629,6 +655,89 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK;
}
+/* Sets *hit_watchpoint to the first watchpoint identified as causing the
+ * current halt.
+ *
+ * The GDB server uses this information to tell GDB what data address has
+ * been hit, which enables GDB to print the hit variable along with its old
+ * and new value. */
+int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
+{
+ struct watchpoint *wp = target->watchpoints;
+
+ LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
+
+ /*TODO instead of disassembling the instruction that we think caused the
+ * trigger, check the hit bit of each watchpoint first. The hit bit is
+ * simpler and more reliable to check but as it is optional and relatively
+ * new, not all hardware will implement it */
+ riscv_reg_t dpc;
+ riscv_get_register(target, &dpc, GDB_REGNO_DPC);
+ const uint8_t length = 4;
+ LOG_DEBUG("dpc is 0x%" PRIx64, dpc);
+
+ /* fetch the instruction at dpc */
+ uint8_t buffer[length];
+ if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) {
+ LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc);
+ return ERROR_FAIL;
+ }
+
+ uint32_t instruction = 0;
+
+ for (int i = 0; i < length; i++) {
+ LOG_DEBUG("Next byte is %x", buffer[i]);
+ instruction += (buffer[i] << 8 * i);
+ }
+ LOG_DEBUG("Full instruction is %x", instruction);
+
+ /* find out which memory address is accessed by the instruction at dpc */
+ /* opcode is first 7 bits of the instruction */
+ uint8_t opcode = instruction & 0x7F;
+ uint32_t rs1;
+ int16_t imm;
+ riscv_reg_t mem_addr;
+
+ if (opcode == MATCH_LB || opcode == MATCH_SB) {
+ rs1 = (instruction & 0xf8000) >> 15;
+ riscv_get_register(target, &mem_addr, rs1);
+
+ if (opcode == MATCH_SB) {
+ LOG_DEBUG("%x is store instruction", instruction);
+ imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20);
+ } else {
+ LOG_DEBUG("%x is load instruction", instruction);
+ imm = (instruction & 0xfff00000) >> 20;
+ }
+ /* sign extend 12-bit imm to 16-bits */
+ if (imm & (1 << 11))
+ imm |= 0xf000;
+ mem_addr += imm;
+ LOG_DEBUG("memory address=0x%" PRIx64, mem_addr);
+ } else {
+ LOG_DEBUG("%x is not a RV32I load or store", instruction);
+ return ERROR_FAIL;
+ }
+
+ while (wp) {
+ /*TODO support length/mask */
+ if (wp->address == mem_addr) {
+ *hit_watchpoint = wp;
+ LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address);
+ return ERROR_OK;
+ }
+ wp = wp->next;
+ }
+
+ /* No match found - either we hit a watchpoint caused by an instruction that
+ * this function does not yet disassemble, or we hit a breakpoint.
+ *
+ * OpenOCD will behave as if this function had never been implemented i.e.
+ * report the halt to GDB with no address information. */
+ return ERROR_FAIL;
+}
+
+
static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints)
{
@@ -739,19 +848,20 @@ static int old_or_new_riscv_resume(
return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution);
}
-static void riscv_select_current_hart(struct target *target)
+static int riscv_select_current_hart(struct target *target)
{
RISCV_INFO(r);
if (r->rtos_hartid != -1 && riscv_rtos_enabled(target))
- riscv_set_current_hartid(target, r->rtos_hartid);
+ return riscv_set_current_hartid(target, r->rtos_hartid);
else
- riscv_set_current_hartid(target, target->coreid);
+ return riscv_set_current_hartid(target, target->coreid);
}
static int riscv_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
- riscv_select_current_hart(target);
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
struct target_type *tt = get_target_type(target);
return tt->read_memory(target, address, size, count, buffer);
}
@@ -759,7 +869,8 @@ static int riscv_read_memory(struct target *target, target_addr_t address,
static int riscv_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
- riscv_select_current_hart(target);
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
struct target_type *tt = get_target_type(target);
return tt->write_memory(target, address, size, count, buffer);
}
@@ -777,14 +888,15 @@ static int riscv_get_gdb_reg_list(struct target *target,
return ERROR_FAIL;
}
- riscv_select_current_hart(target);
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
switch (reg_class) {
case REG_CLASS_GENERAL:
*reg_list_size = 32;
break;
case REG_CLASS_ALL:
- *reg_list_size = GDB_REGNO_COUNT;
+ *reg_list_size = target->reg_cache->num_regs;
break;
default:
LOG_ERROR("Unsupported reg_class: %d", reg_class);
@@ -948,68 +1060,63 @@ static int riscv_checksum_memory(struct target *target,
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
-/* Should run code on the target to check whether a memory
-block holds all-ones (because this is generally called on
-NOR flash which is 1 when "blank")
-Not yet implemented.
-*/
-int riscv_blank_check_memory(struct target *target,
- target_addr_t address,
- uint32_t count,
- uint32_t *blank,
- uint8_t erased_value)
-{
- *blank = 0;
-
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-}
-
/*** OpenOCD Helper Functions ***/
-/* 0 means nothing happened, 1 means the hart's state changed (and thus the
- * poll should terminate), and -1 means there was an error. */
-static int riscv_poll_hart(struct target *target, int hartid)
+enum riscv_poll_hart {
+ RPH_NO_CHANGE,
+ RPH_DISCOVERED_HALTED,
+ RPH_DISCOVERED_RUNNING,
+ RPH_ERROR
+};
+static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
- riscv_set_current_hartid(target, hartid);
+ if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ return RPH_ERROR;
- LOG_DEBUG("polling hart %d, target->state=%d (TARGET_HALTED=%d)", hartid, target->state, TARGET_HALTED);
+ LOG_DEBUG("polling hart %d, target->state=%d", hartid, target->state);
- /* If OpenOCD this we're running but this hart is halted then it's time
+ /* If OpenOCD thinks we're running but this hart is halted then it's time
* to raise an event. */
- if (target->state != TARGET_HALTED && riscv_is_halted(target)) {
+ bool halted = riscv_is_halted(target);
+ if (target->state != TARGET_HALTED && halted) {
LOG_DEBUG(" triggered a halt");
r->on_halt(target);
- return 1;
+ return RPH_DISCOVERED_HALTED;
+ } else if (target->state != TARGET_RUNNING && !halted) {
+ LOG_DEBUG(" triggered running");
+ target->state = TARGET_RUNNING;
+ return RPH_DISCOVERED_RUNNING;
}
- return 0;
+ return RPH_NO_CHANGE;
}
/*** OpenOCD Interface ***/
int riscv_openocd_poll(struct target *target)
{
LOG_DEBUG("polling all harts");
- int triggered_hart = -1;
+ int halted_hart = -1;
if (riscv_rtos_enabled(target)) {
/* Check every hart for an event. */
for (int i = 0; i < riscv_count_harts(target); ++i) {
- int out = riscv_poll_hart(target, i);
+ enum riscv_poll_hart out = riscv_poll_hart(target, i);
switch (out) {
- case 0:
+ case RPH_NO_CHANGE:
+ case RPH_DISCOVERED_RUNNING:
continue;
- case 1:
- triggered_hart = i;
+ case RPH_DISCOVERED_HALTED:
+ halted_hart = i;
break;
- case -1:
+ case RPH_ERROR:
return ERROR_FAIL;
}
}
- if (triggered_hart == -1) {
+ if (halted_hart == -1) {
LOG_DEBUG(" no harts just halted, target->state=%d", target->state);
return ERROR_OK;
}
- LOG_DEBUG(" hart %d halted", triggered_hart);
+ LOG_DEBUG(" hart %d halted", halted_hart);
/* If we're here then at least one hart triggered. That means
* we want to go and halt _every_ hart in the system, as that's
@@ -1020,15 +1127,19 @@ int riscv_openocd_poll(struct target *target)
for (int i = 0; i < riscv_count_harts(target); ++i)
riscv_halt_one_hart(target, i);
} else {
- if (riscv_poll_hart(target, riscv_current_hartid(target)) == 0)
+ enum riscv_poll_hart out = riscv_poll_hart(target,
+ riscv_current_hartid(target));
+ if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
return ERROR_OK;
+ else if (out == RPH_ERROR)
+ return ERROR_FAIL;
- triggered_hart = riscv_current_hartid(target);
- LOG_DEBUG(" hart %d halted", triggered_hart);
+ halted_hart = riscv_current_hartid(target);
+ LOG_DEBUG(" hart %d halted", halted_hart);
}
target->state = TARGET_HALTED;
- switch (riscv_halt_reason(target, triggered_hart)) {
+ switch (riscv_halt_reason(target, halted_hart)) {
case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
@@ -1044,14 +1155,24 @@ int riscv_openocd_poll(struct target *target)
case RISCV_HALT_UNKNOWN:
target->debug_reason = DBG_REASON_UNDEFINED;
break;
+ case RISCV_HALT_ERROR:
+ return ERROR_FAIL;
}
if (riscv_rtos_enabled(target)) {
- target->rtos->current_threadid = triggered_hart + 1;
- target->rtos->current_thread = triggered_hart + 1;
+ target->rtos->current_threadid = halted_hart + 1;
+ target->rtos->current_thread = halted_hart + 1;
+ riscv_set_rtos_hartid(target, halted_hart);
}
target->state = TARGET_HALTED;
+
+ if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+ int retval;
+ if (riscv_semihosting(target, &retval) != 0)
+ return retval;
+ }
+
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
}
@@ -1103,16 +1224,14 @@ int riscv_openocd_resume(
while (watchpoint && result == ERROR_OK) {
LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
trigger_temporarily_cleared[i] = watchpoint->set;
- if (watchpoint->set) {
+ if (watchpoint->set)
result = riscv_remove_watchpoint(target, watchpoint);
- }
watchpoint = watchpoint->next;
i++;
}
- if (result == ERROR_OK) {
+ if (result == ERROR_OK)
result = riscv_step_rtos_hart(target);
- }
watchpoint = target->watchpoints;
i = 0;
@@ -1204,31 +1323,6 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
return ERROR_OK;
}
-COMMAND_HANDLER(riscv_set_scratch_ram)
-{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
- return ERROR_COMMAND_SYNTAX_ERROR;
- }
- if (!strcmp(CMD_ARGV[0], "none")) {
- riscv_use_scratch_ram = false;
- return ERROR_OK;
- }
-
- // TODO: use COMMAND_PARSE_NUMBER
- long long unsigned int address;
- int result = sscanf(CMD_ARGV[0], "%llx", &address);
- if (result != (int) strlen(CMD_ARGV[0])) {
- LOG_ERROR("%s is not a valid address for command.", CMD_ARGV[0]);
- riscv_use_scratch_ram = false;
- return ERROR_FAIL;
- }
-
- riscv_scratch_ram_address = address;
- riscv_use_scratch_ram = true;
- return ERROR_OK;
-}
-
COMMAND_HANDLER(riscv_set_prefer_sba)
{
if (CMD_ARGC != 1) {
@@ -1252,20 +1346,15 @@ void parse_error(const char *string, char c, unsigned position)
LOG_ERROR("%s", buf);
}
-COMMAND_HANDLER(riscv_set_expose_csrs)
+int parse_ranges(range_t **ranges, const char **argv)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
- return ERROR_COMMAND_SYNTAX_ERROR;
- }
-
for (unsigned pass = 0; pass < 2; pass++) {
unsigned range = 0;
unsigned low = 0;
bool parse_low = true;
unsigned high = 0;
- for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) {
- char c = CMD_ARGV[0][i];
+ for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
+ char c = argv[0][i];
if (isspace(c)) {
/* Ignore whitespace. */
continue;
@@ -1279,13 +1368,13 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
parse_low = false;
} else if (c == ',' || c == 0) {
if (pass == 1) {
- expose_csr[range].low = low;
- expose_csr[range].high = low;
+ (*ranges)[range].low = low;
+ (*ranges)[range].high = low;
}
low = 0;
range++;
} else {
- parse_error(CMD_ARGV[0], c, i);
+ parse_error(argv[0], c, i);
return ERROR_COMMAND_SYNTAX_ERROR;
}
@@ -1296,31 +1385,52 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
} else if (c == ',' || c == 0) {
parse_low = true;
if (pass == 1) {
- expose_csr[range].low = low;
- expose_csr[range].high = high;
+ (*ranges)[range].low = low;
+ (*ranges)[range].high = high;
}
low = 0;
high = 0;
range++;
} else {
- parse_error(CMD_ARGV[0], c, i);
+ parse_error(argv[0], c, i);
return ERROR_COMMAND_SYNTAX_ERROR;
}
}
}
if (pass == 0) {
- if (expose_csr)
- free(expose_csr);
- expose_csr = calloc(range + 2, sizeof(*expose_csr));
+ if (*ranges)
+ free(*ranges);
+ *ranges = calloc(range + 2, sizeof(range_t));
} else {
- expose_csr[range].low = 1;
- expose_csr[range].high = 0;
+ (*ranges)[range].low = 1;
+ (*ranges)[range].high = 0;
}
}
+
return ERROR_OK;
}
+COMMAND_HANDLER(riscv_set_expose_csrs)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return parse_ranges(&expose_csr, CMD_ARGV);
+}
+
+COMMAND_HANDLER(riscv_set_expose_custom)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return parse_ranges(&expose_custom, CMD_ARGV);
+}
+
COMMAND_HANDLER(riscv_authdata_read)
{
if (CMD_ARGC != 0) {
@@ -1473,13 +1583,6 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.help = "Set the wall-clock timeout (in seconds) after reset is deasserted"
},
{
- .name = "set_scratch_ram",
- .handler = riscv_set_scratch_ram,
- .mode = COMMAND_ANY,
- .usage = "riscv set_scratch_ram none|[address]",
- .help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'."
- },
- {
.name = "set_prefer_sba",
.handler = riscv_set_prefer_sba,
.mode = COMMAND_ANY,
@@ -1497,6 +1600,15 @@ static const struct command_registration riscv_exec_command_handlers[] = {
"`init`."
},
{
+ .name = "expose_custom",
+ .handler = riscv_set_expose_custom,
+ .mode = COMMAND_ANY,
+ .usage = "riscv expose_custom n0[-m0][,n1[-m1]]...",
+ .help = "Configure a list of inclusive ranges for custom registers to "
+ "expose. custom0 is accessed as abstract register number 0xc000, "
+ "etc. This must be executed before `init`."
+ },
+ {
.name = "authdata_read",
.handler = riscv_authdata_read,
.mode = COMMAND_ANY,
@@ -1540,6 +1652,56 @@ static const struct command_registration riscv_exec_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
+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);
+
+/*
+ * To be noted that RISC-V targets use the same semihosting commands as
+ * ARM targets.
+ *
+ * The main reason is compatibility with existing tools. For example the
+ * Eclipse OpenOCD/SEGGER J-Link/QEMU plug-ins have several widgets to
+ * configure semihosting, which generate commands like `arm semihosting
+ * enable`.
+ * A secondary reason is the fact that the protocol used is exactly the
+ * one specified by ARM. If RISC-V will ever define its own semihosting
+ * protocol, then a command like `riscv semihosting enable` will make
+ * sense, but for now all semihosting commands are prefixed with `arm`.
+ */
+static const struct command_registration arm_exec_command_handlers[] = {
+ {
+ "semihosting",
+ .handler = handle_common_semihosting_command,
+ .mode = COMMAND_EXEC,
+ .usage = "['enable'|'disable']",
+ .help = "activate support for semihosting operations",
+ },
+ {
+ "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_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 riscv_command_handlers[] = {
{
.name = "riscv",
@@ -1548,6 +1710,13 @@ const struct command_registration riscv_command_handlers[] = {
.usage = "",
.chain = riscv_exec_command_handlers
},
+ {
+ .name = "arm",
+ .mode = COMMAND_ANY,
+ .help = "ARM Command Group",
+ .usage = "",
+ .chain = arm_exec_command_handlers
+ },
COMMAND_REGISTRATION_DONE
};
@@ -1571,7 +1740,6 @@ struct target_type riscv_target = {
.read_memory = riscv_read_memory,
.write_memory = riscv_write_memory,
- .blank_check_memory = riscv_blank_check_memory,
.checksum_memory = riscv_checksum_memory,
.get_gdb_reg_list = riscv_get_gdb_reg_list,
@@ -1581,6 +1749,7 @@ struct target_type riscv_target = {
.add_watchpoint = riscv_add_watchpoint,
.remove_watchpoint = riscv_remove_watchpoint,
+ .hit_watchpoint = riscv_hit_watchpoint,
.arch_state = riscv_arch_state,
@@ -1624,7 +1793,8 @@ int riscv_halt_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("halting hart %d", hartid);
- riscv_set_current_hartid(target, hartid);
+ if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ return ERROR_FAIL;
if (riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested halt, but was already halted", hartid);
return ERROR_OK;
@@ -1650,7 +1820,8 @@ int riscv_resume_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d", hartid);
- riscv_set_current_hartid(target, hartid);
+ if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ return ERROR_FAIL;
if (!riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid);
return ERROR_OK;
@@ -1671,7 +1842,8 @@ int riscv_step_rtos_hart(struct target *target)
hartid = 0;
}
}
- riscv_set_current_hartid(target, hartid);
+ if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ return ERROR_FAIL;
LOG_DEBUG("stepping hart %d", hartid);
if (!riscv_is_halted(target)) {
@@ -1691,7 +1863,7 @@ int riscv_step_rtos_hart(struct target *target)
return ERROR_OK;
}
-bool riscv_supports_extension(struct target *target, char letter)
+bool riscv_supports_extension(struct target *target, int hartid, char letter)
{
RISCV_INFO(r);
unsigned num;
@@ -1701,7 +1873,7 @@ bool riscv_supports_extension(struct target *target, char letter)
num = letter - 'A';
else
return false;
- return r->misa & (1 << num);
+ return r->misa[hartid] & (1 << num);
}
int riscv_xlen(const struct target *target)
@@ -1721,33 +1893,35 @@ bool riscv_rtos_enabled(const struct target *target)
return target->rtos != NULL;
}
-void riscv_set_current_hartid(struct target *target, int hartid)
+int riscv_set_current_hartid(struct target *target, int hartid)
{
RISCV_INFO(r);
if (!r->select_current_hart)
- return;
+ return ERROR_OK;
int previous_hartid = riscv_current_hartid(target);
r->current_hartid = hartid;
assert(riscv_hart_enabled(target, hartid));
LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
- r->select_current_hart(target);
+ if (r->select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
/* This might get called during init, in which case we shouldn't be
* setting up the register cache. */
if (!target_was_examined(target))
- return;
+ return ERROR_OK;
/* Avoid invalidating the register cache all the time. */
if (r->registers_initialized
&& (!riscv_rtos_enabled(target) || (previous_hartid == hartid))
&& target->reg_cache->reg_list[GDB_REGNO_ZERO].size == (unsigned)riscv_xlen(target)
&& (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) {
- return;
+ return ERROR_OK;
} else
LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target));
riscv_invalidate_register_cache(target);
+ return ERROR_OK;
}
void riscv_invalidate_register_cache(struct target *target)
@@ -1755,7 +1929,7 @@ void riscv_invalidate_register_cache(struct target *target)
RISCV_INFO(r);
register_cache_invalidate(target->reg_cache);
- for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
+ for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i];
reg->valid = false;
}
@@ -1797,6 +1971,10 @@ bool riscv_has_register(struct target *target, int hartid, int regid)
return 1;
}
+/**
+ * This function is called when the debug user wants to change the value of a
+ * register. The new value may be cached, and may not be written until the hart
+ * is resumed. */
int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
{
return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
@@ -1837,7 +2015,8 @@ bool riscv_is_halted(struct target *target)
enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
{
RISCV_INFO(r);
- riscv_set_current_hartid(target, hartid);
+ if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ return RISCV_HALT_ERROR;
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart is not halted!");
return RISCV_HALT_UNKNOWN;
@@ -1914,6 +2093,11 @@ int riscv_enumerate_triggers(struct target *target)
{
RISCV_INFO(r);
+ if (r->triggers_enumerated)
+ return ERROR_OK;
+
+ r->triggers_enumerated = true; /* At the very least we tried. */
+
for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
if (!riscv_hart_enabled(target, hartid))
continue;
@@ -2018,7 +2202,8 @@ const char *gdb_regno_name(enum gdb_regno regno)
static int register_get(struct reg *reg)
{
- struct target *target = (struct target *) reg->arch_info;
+ riscv_reg_info_t *reg_info = reg->arch_info;
+ struct target *target = reg_info->target;
uint64_t value;
int result = riscv_get_register(target, &value, reg->number);
if (result != ERROR_OK)
@@ -2029,7 +2214,8 @@ static int register_get(struct reg *reg)
static int register_set(struct reg *reg, uint8_t *buf)
{
- struct target *target = (struct target *) reg->arch_info;
+ riscv_reg_info_t *reg_info = reg->arch_info;
+ struct target *target = reg_info->target;
uint64_t value = buf_get_u64(buf, 0, reg->size);
@@ -2071,12 +2257,26 @@ int riscv_init_registers(struct target *target)
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
- target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
+ if (expose_custom) {
+ for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
+ for (unsigned number = expose_custom[i].low;
+ number <= expose_custom[i].high;
+ number++)
+ target->reg_cache->num_regs++;
+ }
+ }
+
+ LOG_DEBUG("create register cache for %d registers",
+ target->reg_cache->num_regs);
+
+ target->reg_cache->reg_list =
+ calloc(target->reg_cache->num_regs, sizeof(struct reg));
const unsigned int max_reg_name_len = 12;
if (info->reg_names)
free(info->reg_names);
- info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
+ info->reg_names =
+ calloc(target->reg_cache->num_regs, max_reg_name_len);
char *reg_name = info->reg_names;
static struct reg_feature feature_cpu = {
@@ -2091,6 +2291,9 @@ int riscv_init_registers(struct target *target)
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
+ static struct reg_feature feature_custom = {
+ .name = "org.gnu.gdb.riscv.custom"
+ };
static struct reg_data_type type_ieee_single = {
.type = REG_TYPE_IEEE_SINGLE,
@@ -2109,18 +2312,24 @@ int riscv_init_registers(struct target *target)
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
unsigned csr_info_index = 0;
- /* When gdb request register N, gdb_get_register_packet() assumes that this
+ unsigned custom_range_index = 0;
+ int custom_within_range = 0;
+
+ riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
+ shared_reg_info->target = target;
+
+ /* When gdb requests register N, gdb_get_register_packet() assumes that this
* is register at index N in reg_list. So if there are certain registers
* that don't exist, we need to leave holes in the list (or renumber, but
* it would be nice not to have yet another set of numbers to translate
* between). */
- for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
+ for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) {
struct reg *r = &target->reg_cache->reg_list[number];
r->dirty = false;
r->valid = false;
r->exist = true;
r->type = &riscv_reg_arch_type;
- r->arch_info = target;
+ r->arch_info = shared_reg_info;
r->number = number;
r->size = riscv_xlen(target);
/* r->size is set in riscv_invalidate_register_cache, maybe because the
@@ -2235,10 +2444,12 @@ int riscv_init_registers(struct target *target)
r->feature = &feature_cpu;
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
r->caller_save = true;
- if (riscv_supports_extension(target, 'D')) {
+ if (riscv_supports_extension(target, riscv_current_hartid(target),
+ 'D')) {
r->reg_data_type = &type_ieee_double;
r->size = 64;
- } else if (riscv_supports_extension(target, 'F')) {
+ } else if (riscv_supports_extension(target,
+ riscv_current_hartid(target), 'F')) {
r->reg_data_type = &type_ieee_single;
r->size = 32;
} else {
@@ -2369,7 +2580,8 @@ int riscv_init_registers(struct target *target)
case CSR_FFLAGS:
case CSR_FRM:
case CSR_FCSR:
- r->exist = riscv_supports_extension(target, 'F');
+ r->exist = riscv_supports_extension(target,
+ riscv_current_hartid(target), 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
@@ -2383,7 +2595,82 @@ int riscv_init_registers(struct target *target)
case CSR_SCAUSE:
case CSR_STVAL:
case CSR_SATP:
- r->exist = riscv_supports_extension(target, 'S');
+ r->exist = riscv_supports_extension(target,
+ riscv_current_hartid(target), 'S');
+ break;
+ case CSR_MEDELEG:
+ case CSR_MIDELEG:
+ /* "In systems with only M-mode, or with both M-mode and
+ * U-mode but without U-mode trap support, the medeleg and
+ * mideleg registers should not exist." */
+ r->exist = riscv_supports_extension(target, riscv_current_hartid(target), 'S') ||
+ riscv_supports_extension(target, riscv_current_hartid(target), 'N');
+ break;
+
+ case CSR_CYCLEH:
+ case CSR_TIMEH:
+ case CSR_INSTRETH:
+ case CSR_HPMCOUNTER3H:
+ case CSR_HPMCOUNTER4H:
+ case CSR_HPMCOUNTER5H:
+ case CSR_HPMCOUNTER6H:
+ case CSR_HPMCOUNTER7H:
+ case CSR_HPMCOUNTER8H:
+ case CSR_HPMCOUNTER9H:
+ case CSR_HPMCOUNTER10H:
+ case CSR_HPMCOUNTER11H:
+ case CSR_HPMCOUNTER12H:
+ case CSR_HPMCOUNTER13H:
+ case CSR_HPMCOUNTER14H:
+ case CSR_HPMCOUNTER15H:
+ case CSR_HPMCOUNTER16H:
+ case CSR_HPMCOUNTER17H:
+ case CSR_HPMCOUNTER18H:
+ case CSR_HPMCOUNTER19H:
+ case CSR_HPMCOUNTER20H:
+ case CSR_HPMCOUNTER21H:
+ case CSR_HPMCOUNTER22H:
+ case CSR_HPMCOUNTER23H:
+ case CSR_HPMCOUNTER24H:
+ case CSR_HPMCOUNTER25H:
+ case CSR_HPMCOUNTER26H:
+ case CSR_HPMCOUNTER27H:
+ case CSR_HPMCOUNTER28H:
+ case CSR_HPMCOUNTER29H:
+ case CSR_HPMCOUNTER30H:
+ case CSR_HPMCOUNTER31H:
+ case CSR_MCYCLEH:
+ case CSR_MINSTRETH:
+ case CSR_MHPMCOUNTER3H:
+ case CSR_MHPMCOUNTER4H:
+ case CSR_MHPMCOUNTER5H:
+ case CSR_MHPMCOUNTER6H:
+ case CSR_MHPMCOUNTER7H:
+ case CSR_MHPMCOUNTER8H:
+ case CSR_MHPMCOUNTER9H:
+ case CSR_MHPMCOUNTER10H:
+ case CSR_MHPMCOUNTER11H:
+ case CSR_MHPMCOUNTER12H:
+ case CSR_MHPMCOUNTER13H:
+ case CSR_MHPMCOUNTER14H:
+ case CSR_MHPMCOUNTER15H:
+ case CSR_MHPMCOUNTER16H:
+ case CSR_MHPMCOUNTER17H:
+ case CSR_MHPMCOUNTER18H:
+ case CSR_MHPMCOUNTER19H:
+ case CSR_MHPMCOUNTER20H:
+ case CSR_MHPMCOUNTER21H:
+ case CSR_MHPMCOUNTER22H:
+ case CSR_MHPMCOUNTER23H:
+ case CSR_MHPMCOUNTER24H:
+ case CSR_MHPMCOUNTER25H:
+ case CSR_MHPMCOUNTER26H:
+ case CSR_MHPMCOUNTER27H:
+ case CSR_MHPMCOUNTER28H:
+ case CSR_MHPMCOUNTER29H:
+ case CSR_MHPMCOUNTER30H:
+ case CSR_MHPMCOUNTER31H:
+ r->exist = riscv_xlen(target) == 32;
break;
}
@@ -2402,11 +2689,35 @@ int riscv_init_registers(struct target *target)
r->group = "general";
r->feature = &feature_virtual;
r->size = 8;
+
+ } else {
+ /* Custom registers. */
+ assert(expose_custom);
+
+ range_t *range = &expose_custom[custom_range_index];
+ assert(range->low <= range->high);
+ unsigned custom_number = range->low + custom_within_range;
+
+ r->group = "custom";
+ r->feature = &feature_custom;
+ r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
+ assert(r->arch_info);
+ ((riscv_reg_info_t *) r->arch_info)->target = target;
+ ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
+ sprintf(reg_name, "custom%d", custom_number);
+
+ custom_within_range++;
+ if (custom_within_range > range->high - range->low) {
+ custom_within_range = 0;
+ custom_range_index++;
+ }
}
+
if (reg_name[0])
r->name = reg_name;
reg_name += strlen(reg_name) + 1;
- assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
+ assert(reg_name < info->reg_names + target->reg_cache->num_regs *
+ max_reg_name_len);
r->value = &info->reg_cache_values[number];
}
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index d724538..3123db2 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -7,7 +7,7 @@ struct riscv_program;
#include "opcodes.h"
#include "gdb_regs.h"
-/* The register cache is staticly allocated. */
+/* The register cache is statically allocated. */
#define RISCV_MAX_HARTS 32
#define RISCV_MAX_REGISTERS 5000
#define RISCV_MAX_TRIGGERS 32
@@ -31,13 +31,17 @@ enum riscv_halt_reason {
RISCV_HALT_BREAKPOINT,
RISCV_HALT_SINGLESTEP,
RISCV_HALT_TRIGGER,
- RISCV_HALT_UNKNOWN
+ RISCV_HALT_UNKNOWN,
+ RISCV_HALT_ERROR
};
typedef struct {
- unsigned dtm_version;
+ struct target *target;
+ unsigned custom_number;
+} riscv_reg_info_t;
- riscv_reg_t misa;
+typedef struct {
+ unsigned dtm_version;
struct command_context *cmd_ctx;
void *version_specific;
@@ -59,7 +63,9 @@ typedef struct {
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
- /* The register cache points into here. */
+ /* OpenOCD's register cache points into here. This is not per-hart because
+ * we just invalidate the entire cache when we change which hart is
+ * selected. */
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
/* Single buffer that contains all register names, instead of calling
@@ -68,6 +74,7 @@ typedef struct {
/* It's possible that each core has a different supported ISA set. */
int xlen[RISCV_MAX_HARTS];
+ riscv_reg_t misa[RISCV_MAX_HARTS];
/* The number of triggers per hart. */
unsigned trigger_count[RISCV_MAX_HARTS];
@@ -87,13 +94,15 @@ typedef struct {
/* This hart contains an implicit ebreak at the end of the program buffer. */
bool impebreak;
+ bool triggers_enumerated;
+
/* Helper functions that target the various RISC-V debug spec
* implementations. */
int (*get_register)(struct target *target,
riscv_reg_t *value, int hid, int rid);
int (*set_register)(struct target *, int hartid, int regid,
uint64_t value);
- void (*select_current_hart)(struct target *);
+ int (*select_current_hart)(struct target *);
bool (*is_halted)(struct target *target);
int (*halt_current_hart)(struct target *);
int (*resume_current_hart)(struct target *target);
@@ -128,9 +137,6 @@ extern int riscv_command_timeout_sec;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
extern int riscv_reset_timeout_sec;
-extern bool riscv_use_scratch_ram;
-extern uint64_t riscv_scratch_ram_address;
-
extern bool riscv_prefer_sba;
/* Everything needs the RISC-V specific info structure, so here's a nice macro
@@ -187,7 +193,7 @@ int riscv_resume_one_hart(struct target *target, int hartid);
* then the only hart. */
int riscv_step_rtos_hart(struct target *target);
-bool riscv_supports_extension(struct target *target, char letter);
+bool riscv_supports_extension(struct target *target, int hartid, char letter);
/* Returns XLEN for the given (or current) hart. */
int riscv_xlen(const struct target *target);
@@ -197,7 +203,7 @@ bool riscv_rtos_enabled(const struct target *target);
/* Sets the current hart, which is the hart that will actually be used when
* issuing debug commands. */
-void riscv_set_current_hartid(struct target *target, int hartid);
+int riscv_set_current_hartid(struct target *target, int hartid);
int riscv_current_hartid(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
@@ -256,7 +262,11 @@ int riscv_remove_breakpoint(struct target *target,
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint);
+int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address);
int riscv_init_registers(struct target *target);
+void riscv_semihosting_init(struct target *target);
+int riscv_semihosting(struct target *target, int *retval);
+
#endif
diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c
new file mode 100644
index 0000000..c4b6653
--- /dev/null
+++ b/src/target/riscv/riscv_semihosting.c
@@ -0,0 +1,194 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * ilg@livius.net *
+ * *
+ * Copyright (C) 2009 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
+ * Hold RISC-V semihosting support.
+ *
+ * The RISC-V code is inspired from ARM semihosting.
+ *
+ * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
+ * from ARM Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "log.h"
+
+#include "target/target.h"
+#include "target/semihosting_common.h"
+#include "riscv.h"
+
+static int riscv_semihosting_setup(struct target *target, int enable);
+static int riscv_semihosting_post_result(struct target *target);
+
+/**
+ * Initialize RISC-V semihosting. Use common ARM code.
+ */
+void riscv_semihosting_init(struct target *target)
+{
+ semihosting_common_init(target, riscv_semihosting_setup,
+ riscv_semihosting_post_result);
+}
+
+/**
+ * Check for and process a semihosting request using the ARM protocol). This
+ * is meant to be called when the target is stopped due to a debug mode entry.
+ * If the value 0 is returned then there was nothing to process. A non-zero
+ * return value signifies that a request was processed and the target resumed,
+ * or an error was encountered, in which case the caller must return
+ * immediately.
+ *
+ * @param target Pointer to the target to process.
+ * @param retval Pointer to a location where the return code will be stored
+ * @return non-zero value if a request was processed or an error encountered
+ */
+int riscv_semihosting(struct target *target, int *retval)
+{
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting)
+ return 0;
+
+ if (!semihosting->is_active)
+ return 0;
+
+ riscv_reg_t dpc;
+ int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
+ if (result != ERROR_OK)
+ return 0;
+
+ uint8_t tmp[12];
+
+ /* Read the current instruction, including the bracketing */
+ *retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
+ if (*retval != ERROR_OK)
+ return 0;
+
+ /*
+ * The instructions that trigger a semihosting call,
+ * always uncompressed, should look like:
+ *
+ * 01f01013 slli zero,zero,0x1f
+ * 00100073 ebreak
+ * 40705013 srai zero,zero,0x7
+ */
+ uint32_t pre = target_buffer_get_u32(target, tmp);
+ uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
+ uint32_t post = target_buffer_get_u32(target, tmp + 8);
+ LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
+
+ if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
+
+ /* Not the magic sequence defining semihosting. */
+ return 0;
+ }
+
+ /*
+ * Perform semihosting call if we are not waiting on a fileio
+ * operation to complete.
+ */
+ if (!semihosting->hit_fileio) {
+
+ /* RISC-V uses A0 and A1 to pass function arguments */
+ riscv_reg_t r0;
+ riscv_reg_t r1;
+
+ result = riscv_get_register(target, &r0, GDB_REGNO_A0);
+ if (result != ERROR_OK)
+ return 0;
+
+ result = riscv_get_register(target, &r1, GDB_REGNO_A1);
+ if (result != ERROR_OK)
+ return 0;
+
+ semihosting->op = r0;
+ semihosting->param = r1;
+ semihosting->word_size_bytes = riscv_xlen(target) / 8;
+
+ /* 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;
+ }
+ }
+
+ /*
+ * Resume target if we are not waiting on a fileio
+ * operation to complete.
+ */
+ if (semihosting->is_resumable && !semihosting->hit_fileio) {
+ /* Resume right after the EBREAK 4 bytes instruction. */
+ *retval = target_resume(target, 0, dpc+4, 0, 0);
+ if (*retval != ERROR_OK) {
+ LOG_ERROR("Failed to resume target");
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------
+ * Local functions. */
+
+/**
+ * Called via semihosting->setup() later, after the target is known,
+ * usually on the first semihosting command.
+ */
+static int riscv_semihosting_setup(struct target *target, int enable)
+{
+ LOG_DEBUG("enable=%d", enable);
+
+ struct semihosting *semihosting = target->semihosting;
+ if (semihosting)
+ semihosting->setup_time = clock();
+
+ return ERROR_OK;
+}
+
+static int riscv_semihosting_post_result(struct target *target)
+{
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ /* If not enabled, silently ignored. */
+ return 0;
+ }
+
+ LOG_DEBUG("0x%" PRIx64, semihosting->result);
+ riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
+ return 0;
+}
diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c
new file mode 100644
index 0000000..5920789
--- /dev/null
+++ b/src/target/semihosting_common.c
@@ -0,0 +1,1595 @@
+/***************************************************************************
+ * 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) {
+ if (fd == 0 || fd == 1 || fd == 2) {
+ semihosting->result = 0;
+ break;
+ }
+ 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) {
+ semihosting->result = -1;
+ semihosting->sys_errno = EINVAL;
+ }
+ 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, ":semihosting-features") == 0) {
+ semihosting->result = -1;
+ semihosting->sys_errno = EINVAL;
+ } else if (strcmp((char *)fn, ":tt") == 0) {
+ if (mode == 0)
+ semihosting->result = 0;
+ else if (mode == 4)
+ semihosting->result = 1;
+ else if (mode == 8)
+ semihosting->result = 2;
+ else
+ semihosting->result = -1;
+ } 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;
+ /* Use 4-byte multiples to trigger fast memory access. */
+ return target_read_memory(target, semihosting->param, 4,
+ number * (semihosting->word_size_bytes / 4), 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;
+ /* Use 4-byte multiples to trigger fast memory access. */
+ return target_write_memory(target, semihosting->param, 4,
+ number * (semihosting->word_size_bytes / 4), 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/startup.tcl b/src/target/startup.tcl
index 9bbc6e3..cf844e1 100644
--- a/src/target/startup.tcl
+++ b/src/target/startup.tcl
@@ -203,6 +203,7 @@ proc init_target_events {} {
foreach t $targets {
set_default_target_event $t gdb-flash-erase-start "reset init"
set_default_target_event $t gdb-flash-write-end "reset halt"
+ set_default_target_event $t gdb-attach "halt"
}
}
diff --git a/src/target/stm8.c b/src/target/stm8.c
index 262497b..5a3438a 100644
--- a/src/target/stm8.c
+++ b/src/target/stm8.c
@@ -477,7 +477,8 @@ static int stm8_examine_debug_reason(struct target *target)
uint8_t csr1, csr2;
retval = stm8_read_dm_csrx(target, &csr1, &csr2);
- LOG_DEBUG("csr1 = 0x%02X csr2 = 0x%02X", csr1, csr2);
+ if (retval == ERROR_OK)
+ LOG_DEBUG("csr1 = 0x%02X csr2 = 0x%02X", csr1, csr2);
if ((target->debug_reason != DBG_REASON_DBGRQ)
&& (target->debug_reason != DBG_REASON_SINGLESTEP)) {
@@ -1749,7 +1750,7 @@ static int stm8_examine(struct target *target)
/** Checks whether a memory region is erased. */
static int stm8_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value)
+ struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
{
struct working_area *erase_check_algorithm;
struct reg_param reg_params[2];
@@ -1777,10 +1778,10 @@ static int stm8_blank_check_memory(struct target *target,
stm8_info.common_magic = STM8_COMMON_MAGIC;
init_mem_param(&mem_params[0], 0x0, 3, PARAM_OUT);
- buf_set_u32(mem_params[0].value, 0, 24, address);
+ buf_set_u32(mem_params[0].value, 0, 24, blocks[0].address);
init_mem_param(&mem_params[1], 0x3, 3, PARAM_OUT);
- buf_set_u32(mem_params[1].value, 0, 24, count);
+ buf_set_u32(mem_params[1].value, 0, 24, blocks[0].size);
init_reg_param(&reg_params[0], "a", 32, PARAM_IN_OUT);
buf_set_u32(reg_params[0].value, 0, 32, erased_value);
@@ -1794,7 +1795,7 @@ static int stm8_blank_check_memory(struct target *target,
10000, &stm8_info);
if (retval == ERROR_OK)
- *blank = (*(reg_params[0].value) == 0xff);
+ blocks[0].result = (*(reg_params[0].value) == 0xff);
destroy_mem_param(&mem_params[0]);
destroy_mem_param(&mem_params[1]);
@@ -1802,7 +1803,10 @@ static int stm8_blank_check_memory(struct target *target,
target_free_working_area(target, erase_check_algorithm);
- return retval;
+ if (retval != ERROR_OK)
+ return retval;
+
+ return 1; /* only one block has been checked */
}
static int stm8_checksum_memory(struct target *target, target_addr_t address,
diff --git a/src/target/target.c b/src/target/target.c
index ce7782e..8240e65 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -54,6 +54,7 @@
#include "image.h"
#include "rtos/rtos.h"
#include "transport/transport.h"
+#include "arm_cti.h"
/* default halt wait timeout (ms) */
#define DEFAULT_HALT_TIMEOUT 5000
@@ -512,7 +513,9 @@ struct target *get_target_by_num(int num)
struct target *get_current_target(struct command_context *cmd_ctx)
{
- struct target *target = get_target_by_num(cmd_ctx->current_target);
+ struct target *target = cmd_ctx->current_target_override
+ ? cmd_ctx->current_target_override
+ : cmd_ctx->current_target;
if (target == NULL) {
LOG_ERROR("BUG: current_target out of bounds");
@@ -810,8 +813,7 @@ done:
}
/**
- * Downloads a target-specific native code algorithm to the target,
- * executes and leaves it running.
+ * Executes a target-specific native code algorithm and leaves it running.
*
* @param target used to run the algorithm
* @param arch_info target-specific description of the algorithm.
@@ -884,12 +886,45 @@ done:
}
/**
- * Executes a target-specific native code algorithm in the target.
- * It differs from target_run_algorithm in that the algorithm is asynchronous.
- * Because of this it requires an compliant algorithm:
- * see contrib/loaders/flash/stm32f1x.S for example.
+ * Streams data to a circular buffer on target intended for consumption by code
+ * running asynchronously on target.
+ *
+ * This is intended for applications where target-specific native code runs
+ * on the target, receives data from the circular buffer, does something with
+ * it (most likely writing it to a flash memory), and advances the circular
+ * buffer pointer.
+ *
+ * This assumes that the helper algorithm has already been loaded to the target,
+ * but has not been started yet. Given memory and register parameters are passed
+ * to the algorithm.
+ *
+ * The buffer is defined by (buffer_start, buffer_size) arguments and has the
+ * following format:
+ *
+ * [buffer_start + 0, buffer_start + 4):
+ * Write Pointer address (aka head). Written and updated by this
+ * routine when new data is written to the circular buffer.
+ * [buffer_start + 4, buffer_start + 8):
+ * Read Pointer address (aka tail). Updated by code running on the
+ * target after it consumes data.
+ * [buffer_start + 8, buffer_start + buffer_size):
+ * Circular buffer contents.
+ *
+ * See contrib/loaders/flash/stm32f1x.S for an example.
*
* @param target used to run the algorithm
+ * @param buffer address on the host where data to be sent is located
+ * @param count number of blocks to send
+ * @param block_size size in bytes of each block
+ * @param num_mem_params count of memory-based params to pass to algorithm
+ * @param mem_params memory-based params to pass to algorithm
+ * @param num_reg_params count of register-based params to pass to algorithm
+ * @param reg_params memory-based params to pass to algorithm
+ * @param buffer_start address on the target of the circular buffer structure
+ * @param buffer_size size of the circular buffer structure
+ * @param entry_point address on the target to execute to start the algorithm
+ * @param exit_point address at which to set a breakpoint to catch the
+ * end of the algorithm; can be 0 if target triggers a breakpoint itself
*/
int target_run_flash_async_algorithm(struct target *target,
@@ -1860,8 +1895,41 @@ static void target_destroy(struct target *target)
if (target->type->deinit_target)
target->type->deinit_target(target);
+ if (target->semihosting)
+ free(target->semihosting);
+
+ jtag_unregister_event_callback(jtag_enable_callback, target);
+
+ struct target_event_action *teap = target->event_action;
+ while (teap) {
+ struct target_event_action *next = teap->next;
+ Jim_DecrRefCount(teap->interp, teap->body);
+ free(teap);
+ teap = next;
+ }
+
+ target_free_all_working_areas(target);
+ /* Now we have none or only one working area marked as free */
+ if (target->working_areas) {
+ free(target->working_areas->backup);
+ free(target->working_areas);
+ }
+
+ /* release the targets SMP list */
+ if (target->smp) {
+ struct target_list *head = target->head;
+ while (head != NULL) {
+ struct target_list *pos = head->next;
+ head->target->smp = 0;
+ free(head);
+ head = pos;
+ }
+ target->smp = 0;
+ }
+
free(target->type);
free(target->trace_info);
+ free(target->fileio_info);
free(target->cmd_name);
free(target);
}
@@ -2188,21 +2256,19 @@ int target_checksum_memory(struct target *target, target_addr_t address, uint32_
return retval;
}
-int target_blank_check_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t* blank,
+int target_blank_check_memory(struct target *target,
+ struct target_memory_check_block *blocks, int num_blocks,
uint8_t erased_value)
{
- int retval;
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
- if (target->type->blank_check_memory == 0)
+ if (target->type->blank_check_memory == NULL)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- retval = target->type->blank_check_memory(target, address, size, blank, erased_value);
-
- return retval;
+ return target->type->blank_check_memory(target, blocks, num_blocks, erased_value);
}
int target_read_u64(struct target *target, target_addr_t address, uint64_t *value)
@@ -2475,7 +2541,10 @@ static int find_target(struct command_context *cmd_ctx, const char *name)
return ERROR_FAIL;
}
- cmd_ctx->current_target = target->target_number;
+ cmd_ctx->current_target = target;
+ if (cmd_ctx->current_target_override)
+ cmd_ctx->current_target_override = target;
+
return ERROR_OK;
}
@@ -2503,7 +2572,7 @@ COMMAND_HANDLER(handle_targets_command)
else
state = "tap-disabled";
- if (CMD_CTX->current_target == target->target_number)
+ if (CMD_CTX->current_target == target)
marker = '*';
/* keep columns lined up to match the headers above */
@@ -2920,6 +2989,9 @@ COMMAND_HANDLER(handle_halt_command)
LOG_DEBUG("-");
struct target *target = get_current_target(CMD_CTX);
+
+ target->verbose_halt_msg = true;
+
int retval = target_halt(target);
if (ERROR_OK != retval)
return retval;
@@ -4074,8 +4146,9 @@ static int target_mem2array(Jim_Interp *interp, struct target *target, int argc,
* argv[3] = memory address
* argv[4] = count of times to read
*/
+
if (argc < 4 || argc > 5) {
- Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems [phys]");
+ Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems [phys]");
return JIM_ERR;
}
varname = Jim_GetString(argv[0], &len);
@@ -4424,17 +4497,28 @@ void target_handle_event(struct target *target, enum target_event e)
for (teap = target->event_action; teap != NULL; teap = teap->next) {
if (teap->event == e) {
- LOG_DEBUG("target: (%d) %s (%s) event: %d (%s) action: %s",
+ LOG_DEBUG("target(%d): %s (%s) event: %d (%s) action: %s",
target->target_number,
target_name(target),
target_type_name(target),
e,
Jim_Nvp_value2name_simple(nvp_target_event, e)->name,
Jim_GetString(teap->body, NULL));
+
+ /* Override current target by the target an event
+ * is issued from (lot of scripts need it).
+ * Return back to previous override as soon
+ * as the handler processing is done */
+ struct command_context *cmd_ctx = current_command_context(teap->interp);
+ struct target *saved_target_override = cmd_ctx->current_target_override;
+ cmd_ctx->current_target_override = target;
+
if (Jim_EvalObj(teap->interp, teap->body) != JIM_OK) {
Jim_MakeErrorMessage(teap->interp);
command_print(NULL, "%s\n", Jim_GetString(Jim_GetResult(teap->interp), NULL));
}
+
+ cmd_ctx->current_target_override = saved_target_override;
}
}
}
@@ -4464,7 +4548,6 @@ enum target_cfg_param {
TCFG_COREID,
TCFG_CHAIN_POSITION,
TCFG_DBGBASE,
- TCFG_CTIBASE,
TCFG_RTOS,
TCFG_DEFER_EXAMINE,
};
@@ -4480,7 +4563,6 @@ static Jim_Nvp nvp_config_opts[] = {
{ .name = "-coreid", .value = TCFG_COREID },
{ .name = "-chain-position", .value = TCFG_CHAIN_POSITION },
{ .name = "-dbgbase", .value = TCFG_DBGBASE },
- { .name = "-ctibase", .value = TCFG_CTIBASE },
{ .name = "-rtos", .value = TCFG_RTOS },
{ .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE },
{ .name = NULL, .value = -1 }
@@ -4717,6 +4799,13 @@ no_params:
if (goi->isconfigure) {
Jim_Obj *o_t;
struct jtag_tap *tap;
+
+ if (target->has_dap) {
+ Jim_SetResultString(goi->interp,
+ "target requires -dap parameter instead of -chain-position!", -1);
+ return JIM_ERR;
+ }
+
target_free_all_working_areas(target);
e = Jim_GetOpt_Obj(goi, &o_t);
if (e != JIM_OK)
@@ -4724,8 +4813,8 @@ no_params:
tap = jtag_tap_by_jim_obj(goi->interp, o_t);
if (tap == NULL)
return JIM_ERR;
- /* make this exactly 1 or 0 */
target->tap = tap;
+ target->tap_configured = true;
} else {
if (goi->argc != 0)
goto no_params;
@@ -4747,20 +4836,6 @@ no_params:
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->dbgbase));
/* loop for more */
break;
- case TCFG_CTIBASE:
- if (goi->isconfigure) {
- e = Jim_GetOpt_Wide(goi, &w);
- if (e != JIM_OK)
- return e;
- target->ctibase = (uint32_t)w;
- target->ctibase_set = true;
- } else {
- if (goi->argc != 0)
- goto no_params;
- }
- Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->ctibase));
- /* loop for more */
- break;
case TCFG_RTOS:
/* RTOS */
{
@@ -5377,21 +5452,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",
@@ -5512,7 +5585,7 @@ static int target_create(Jim_GetOptInfo *goi)
target = calloc(1, sizeof(struct target));
/* set target number */
target->target_number = new_target_number();
- cmd_ctx->current_target = target->target_number;
+ cmd_ctx->current_target = target;
/* allocate memory for each unique target type */
target->type = calloc(1, sizeof(struct target_type));
@@ -5538,7 +5611,7 @@ static int target_create(Jim_GetOptInfo *goi)
target->next = NULL;
target->arch_info = NULL;
- target->display = 1;
+ target->verbose_halt_msg = true;
target->halt_issued = false;
@@ -5557,9 +5630,21 @@ static int target_create(Jim_GetOptInfo *goi)
goi->isconfigure = 1;
e = target_configure(goi, target);
- if (target->tap == NULL) {
- Jim_SetResultString(goi->interp, "-chain-position required when creating target", -1);
- e = JIM_ERR;
+ if (e == JIM_OK) {
+ if (target->has_dap) {
+ if (!target->dap_configured) {
+ Jim_SetResultString(goi->interp, "-dap ?name? required when creating target", -1);
+ e = JIM_ERR;
+ }
+ } else {
+ if (!target->tap_configured) {
+ Jim_SetResultString(goi->interp, "-chain-position ?name? required when creating target", -1);
+ e = JIM_ERR;
+ }
+ }
+ /* tap must be set after target was configured */
+ if (target->tap == NULL)
+ e = JIM_ERR;
}
if (e != JIM_OK) {
@@ -5576,14 +5661,23 @@ static int target_create(Jim_GetOptInfo *goi)
cp = Jim_GetString(new_cmd, NULL);
target->cmd_name = strdup(cp);
+ if (target->type->target_create) {
+ e = (*(target->type->target_create))(target, goi->interp);
+ if (e != ERROR_OK) {
+ LOG_DEBUG("target_create failed");
+ free(target->type);
+ free(target->cmd_name);
+ free(target);
+ return JIM_ERR;
+ }
+ }
+
/* create the target specific commands */
if (target->type->commands) {
e = register_commands(cmd_ctx, NULL, target->type->commands);
if (ERROR_OK != e)
LOG_ERROR("unable to register '%s' commands", cp);
}
- if (target->type->target_create)
- (*(target->type->target_create))(target, goi->interp);
/* append to end of list */
{
@@ -6337,7 +6431,7 @@ static const struct command_registration target_exec_command_handlers[] = {
.handler = handle_bp_command,
.mode = COMMAND_EXEC,
.help = "list or set hardware or software breakpoint",
- .usage = "<address> [<asid>]<length> ['hw'|'hw_ctx']",
+ .usage = "<address> [<asid>] <length> ['hw'|'hw_ctx']",
},
{
.name = "rbp",
diff --git a/src/target/target.h b/src/target/target.h
index c11a626..51a5b69 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -153,7 +153,7 @@ struct target {
struct target_event_action *event_action;
int reset_halt; /* attempt resetting the CPU into the halted mode? */
- uint32_t working_area; /* working area (initialised RAM). Evaluated
+ target_addr_t working_area; /* working area (initialised RAM). Evaluated
* upon first allocation from virtual/physical address. */
bool working_area_virt_spec; /* virtual address specified? */
target_addr_t working_area_virt; /* virtual address */
@@ -176,20 +176,21 @@ struct target {
void *private_config; /* pointer to target specific config data (for jim_configure hook) */
struct target *next; /* next target in list */
- int display; /* display async info in telnet session. Do not display
+ bool verbose_halt_msg; /* display async info in telnet session. Do not display
* lots of halted/resumed info when stepping in debugger. */
bool halt_issued; /* did we transition to halted state? */
int64_t halt_issued_time; /* Note time when halt was issued */
+ /* ARM v7/v8 targets with ADIv5 interface */
bool dbgbase_set; /* By default the debug base is not set */
uint32_t dbgbase; /* Really a Cortex-A specific option, but there is no
* system in place to support target specific options
* currently. */
+ bool has_dap; /* set to true if target has ADIv5 support */
+ bool dap_configured; /* set to true if ADIv5 DAP is configured */
+ bool tap_configured; /* set to true if JTAG tap has been configured
+ * through -chain-position */
- bool ctibase_set; /* By default the debug base is not set */
- uint32_t ctibase; /* Really a Cortex-A specific option, but there is no
- * system in place to support target specific options
- * currently. */
struct rtos *rtos; /* Instance of Real Time Operating System support */
bool rtos_auto_detect; /* A flag that indicates that the RTOS has been specified as "auto"
* and must be detected when symbols are offered */
@@ -205,13 +206,8 @@ struct target {
/* file-I/O information for host to do syscall */
struct gdb_fileio_info *fileio_info;
- /**
- * When true, send gdb an error result when reading/writing a register
- * fails. This must be false for some ARM targets (Cortex-M3), where a 'g'
- * packet results in an attempt to read 'r0', which fails, which causes gdb
- * to close the connection.
- */
- bool propagate_register_errors;
+ /* The semihosting information, extracted from the target. */
+ struct semihosting *semihosting;
};
struct target_list {
@@ -221,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. */
@@ -319,6 +315,12 @@ struct target_timer_callback {
struct target_timer_callback *next;
};
+struct target_memory_check_block {
+ target_addr_t address;
+ uint32_t size;
+ uint32_t result;
+};
+
int target_register_commands(struct command_context *cmd_ctx);
int target_examine(void);
@@ -592,7 +594,8 @@ int target_read_buffer(struct target *target,
int target_checksum_memory(struct target *target,
target_addr_t address, uint32_t size, uint32_t *crc);
int target_blank_check_memory(struct target *target,
- target_addr_t address, uint32_t size, uint32_t *blank, uint8_t erased_value);
+ struct target_memory_check_block *blocks, int num_blocks,
+ uint8_t erased_value);
int target_wait_state(struct target *target, enum target_state state, int ms);
/**
diff --git a/src/target/target_type.h b/src/target/target_type.h
index 0ab22bd..fbbd57d 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -130,8 +130,9 @@ struct target_type {
int (*checksum_memory)(struct target *target, target_addr_t address,
uint32_t count, uint32_t *checksum);
- int (*blank_check_memory)(struct target *target, target_addr_t address,
- uint32_t count, uint32_t *blank, uint8_t erased_value);
+ int (*blank_check_memory)(struct target *target,
+ struct target_memory_check_block *blocks, int num_blocks,
+ uint8_t erased_value);
/*
* target break-/watchpoint control
diff --git a/src/transport/transport.h b/src/transport/transport.h
index 6c57067..140ef50 100644
--- a/src/transport/transport.h
+++ b/src/transport/transport.h
@@ -19,6 +19,10 @@
#ifndef OPENOCD_TRANSPORT_TRANSPORT_H
#define OPENOCD_TRANSPORT_TRANSPORT_H
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include "helper/command.h"
/**
@@ -90,4 +94,16 @@ int allow_transports(struct command_context *ctx, const char * const *vector);
bool transports_are_declared(void);
+bool transport_is_jtag(void);
+bool transport_is_swd(void);
+
+#if BUILD_HLADAPTER
+bool transport_is_hla(void);
+#else
+static inline bool transport_is_hla(void)
+{
+ return false;
+}
+#endif
+
#endif /* OPENOCD_TRANSPORT_TRANSPORT_H */