diff --git a/HACKING b/HACKING index 0d24957e1..b7ef0705b 100644 --- a/HACKING +++ b/HACKING @@ -201,7 +201,7 @@ Further reading: http://www.coreboot.org/Git The code review is intended to take as long as a week or two to allow maintainers and contributors who work on OpenOCD only in their spare -time oportunity to perform a review and raise objections. +time opportunity to perform a review and raise objections. With Gerrit much of the urgency of getting things committed has been removed as the work in progress is safely stored in Gerrit and diff --git a/README b/README index 985e39a98..30443d37c 100644 --- a/README +++ b/README @@ -42,7 +42,7 @@ e.g.: openocd -f interface/ftdi/jtagkey2.cfg -c "transport select jtag" \ -f target/ti_calypso.cfg - openocd -f interface/stlink-v2-1.cfg -c "transport select hla_swd" \ + openocd -f interface/stlink.cfg -c "transport select hla_swd" \ -f target/stm32l0.cfg After OpenOCD startup, connect GDB with @@ -117,17 +117,18 @@ Debug targets ------------- ARM11, ARM7, ARM9, AVR32, Cortex-A, Cortex-R, Cortex-M, LS102x-SAP, -Feroceon/Dragonite, DSP563xx, DSP5680xx, FA526, MIPS EJTAG, NDS32, -XScale, Intel Quark. +Feroceon/Dragonite, DSP563xx, DSP5680xx, EnSilica eSi-RISC, FA526, MIPS +EJTAG, NDS32, XScale, Intel Quark. Flash drivers ------------- -ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis, -LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI, -Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x, Stellaris, STM32, -STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx, -i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC1xxx, XMC4xxx. +ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, eSi-TSMC, +FM3, FM4, Kinetis, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, +Marvell QSPI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x, +Stellaris, STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of +AT91SAM9, LPC3180, LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood, +S3C24xx, S3C6400, XMC1xxx, XMC4xxx. ================== diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules index af092c18b..692e1b10b 100644 --- a/contrib/60-openocd.rules +++ b/contrib/60-openocd.rules @@ -1,4 +1,6 @@ # Copy this file to /etc/udev/rules.d/ +# If rules fail to reload automatically, you can refresh udev rules +# with the command "udevadm control --reload" ACTION!="add|change", GOTO="openocd_rules_end" SUBSYSTEM!="usb|tty|hidraw", GOTO="openocd_rules_end" diff --git a/contrib/loaders/Makefile b/contrib/loaders/Makefile index a9a27706d..0a637aff5 100644 --- a/contrib/loaders/Makefile +++ b/contrib/loaders/Makefile @@ -12,6 +12,7 @@ ARM_CROSS_COMPILE ?= arm-none-eabi- arm_dirs = \ flash/fm4 \ flash/kinetis_ke \ + flash/max32xxx \ flash/xmc1xxx \ debug/xscale diff --git a/contrib/loaders/flash/max32xxx/Makefile b/contrib/loaders/flash/max32xxx/Makefile new file mode 100644 index 000000000..8f3f9242e --- /dev/null +++ b/contrib/loaders/flash/max32xxx/Makefile @@ -0,0 +1,19 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= arm-none-eabi- +AS = $(CROSS_COMPILE)as +OBJCOPY = $(CROSS_COMPILE)objcopy + +all: max32xxx.inc + +%.elf: %.s + $(AS) $< -o $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.bin *.inc diff --git a/contrib/loaders/flash/max32xxx/max32xxx.inc b/contrib/loaders/flash/max32xxx/max32xxx.inc new file mode 100644 index 000000000..442165d0d --- /dev/null +++ b/contrib/loaders/flash/max32xxx/max32xxx.inc @@ -0,0 +1,6 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0xdf,0xf8,0x44,0x40,0xd0,0xf8,0x00,0x80,0xb8,0xf1,0x00,0x0f,0x1a,0xd0,0x47,0x68, +0x47,0x45,0xf7,0xd0,0x22,0x60,0x02,0xf1,0x04,0x02,0x57,0xf8,0x04,0x8b,0xc4,0xf8, +0x30,0x80,0xa5,0x68,0x45,0xf0,0x01,0x05,0xa5,0x60,0xd4,0xf8,0x08,0x80,0x18,0xf0, +0x01,0x0f,0xfa,0xd1,0x8f,0x42,0x28,0xbf,0x00,0xf1,0x08,0x07,0x47,0x60,0x01,0x3b, +0x03,0xb1,0xdf,0xe7,0x00,0xbe,0x00,0xbf,0x00,0x00,0x00,0x40, diff --git a/contrib/loaders/flash/max32xxx/max32xxx.s b/contrib/loaders/flash/max32xxx/max32xxx.s new file mode 100644 index 000000000..f5306d6c5 --- /dev/null +++ b/contrib/loaders/flash/max32xxx/max32xxx.s @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2016 by Maxim Integrated * + * Kevin Gillespie . * + ***************************************************************************/ + +.text +.syntax unified +.cpu cortex-m3 +.thumb +.thumb_func + +/* + * Params : + * r0 = workarea start + * r1 = workarea end + * r2 = target address + * r3 = count (32bit words) + * r4 = pFLASH_CTRL_BASE + * + * Clobbered: + * r5 = FLASHWRITECMD + * r7 - rp + * r8 - wp, tmp + */ + +write: + +wait_fifo: +ldr r8, [r0, #0] /* read wp */ +cmp r8, #0 /* abort if wp == 0 */ +beq exit +ldr r7, [r0, #4] /* read rp */ +cmp r7, r8 /* wait until rp != wp */ +beq wait_fifo + +mainloop: +str r2, [r4, #0x00] /* FLSH_ADDR - write address */ +add r2, r2, #4 /* increment target address */ +ldr r8, [r7], #4 +str r8, [r4, #0x30] /* FLSH_DATA0 - write data */ +ldr r5, [r4, #0x08] /* FLSH_CN */ +orr r5, r5, #1 +str r5, [r4, #0x08] /* FLSH_CN - enable write */ +busy: +ldr r8, [r4, #0x08] /* FLSH_CN */ +tst r8, #1 +bne busy + +cmp r7, r1 /* wrap rp at end of buffer */ +it cs +addcs r7, r0, #8 /* skip loader args */ +str r7, [r0, #4] /* store rp */ +subs r3, r3, #1 /* decrement word count */ +cbz r3, exit /* loop if not done */ +b wait_fifo +exit: +bkpt diff --git a/contrib/rtos-helpers/uCOS-III-openocd.c b/contrib/rtos-helpers/uCOS-III-openocd.c index 9037334f2..5a37bd4c5 100644 --- a/contrib/rtos-helpers/uCOS-III-openocd.c +++ b/contrib/rtos-helpers/uCOS-III-openocd.c @@ -3,8 +3,8 @@ * impossible to determine the appropriate offsets within the structure * unaided. A priori knowledge of offsets based on os_dbg.c is tied to a * specific release and thusly, brittle. The constants defined below - * provide the neccessary information OpenOCD needs to provide support - * in the most robust manner possible. + * provide the necessary information OpenOCD needs to provide support in + * the most robust manner possible. * * This file should be linked along with the project to enable RTOS * support for uC/OS-III. diff --git a/doc/openocd.texi b/doc/openocd.texi index 3932db3c3..6c5cb502e 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2114,6 +2114,7 @@ In such cases, just specify the relevant port number as "disabled". If you disable all access through TCP/IP, you will need to use the command line @option{-pipe} option. +@anchor{gdb_port} @deffn {Command} gdb_port [number] @cindex GDB server Normally gdb listens to a TCP/IP port, but GDB can also @@ -2139,11 +2140,15 @@ The GDB port for the first target will be the base port, the second target will listen on gdb_port + 1, and so on. When not specified during the configuration stage, the port @var{number} defaults to 3333. +When @var{number} is not a numeric value, incrementing it to compute +the next port number does not work. In this case, specify the proper +@var{number} for each target by using the option @code{-gdb-port} of the +commands @command{target create} or @command{$target_name configure}. +@xref{gdbportoverride,,option -gdb-port}. Note: when using "gdb_port pipe", increasing the default remote timeout in gdb (with 'set remotetimeout') is recommended. An insufficient timeout may cause initialization to fail with "Unknown remote qXfer reply: OK". - @end deffn @deffn {Command} tcl_port [number] @@ -4270,6 +4275,8 @@ compact Thumb2 instruction set. @item @code{dragonite} -- resembles arm966e @item @code{dsp563xx} -- implements Freescale's 24-bit DSP. (Support for this is still incomplete.) +@item @code{esirisc} -- this is an EnSilica eSi-RISC core. +The current implementation supports eSi-32xx cores. @item @code{fa526} -- resembles arm920 (w/o Thumb) @item @code{feroceon} -- resembles arm926 @item @code{mips_m4k} -- a MIPS core @@ -4458,6 +4465,13 @@ to the target. Currently, only the @code{aarch64} target makes use of this optio where it is a mandatory configuration for the target run control. @xref{armcrosstrigger,,ARM Cross-Trigger Interface}, for instruction on how to declare and control a CTI instance. + +@anchor{gdbportoverride} +@item @code{-gdb-port} @var{number} -- see command @command{gdb_port} for the +possible values of the parameter @var{number}, which are not only numeric values. +Use this option to override, for this target only, the global parameter set with +command @command{gdb_port}. +@xref{gdb_port,,command gdb_port}. @end itemize @end deffn @@ -5635,6 +5649,27 @@ Note that in order for this command to take effect, the target needs to be reset supported.} @end deffn +@deffn {Flash Driver} esirisc +Members of the eSi-RISC family may optionally include internal flash programmed +via the eSi-TSMC Flash interface. Additional parameters are required to +configure the driver: @option{cfg_address} is the base address of the +configuration register interface, @option{clock_hz} is the expected clock +frequency, and @option{wait_states} is the number of configured read wait states. + +@example +flash bank $_FLASHNAME esirisc base_address size_bytes 0 0 $_TARGETNAME cfg_address clock_hz wait_states +@end example + +@deffn Command {esirisc_flash mass_erase} (bank_id) +Erases all pages in data memory for the bank identified by @option{bank_id}. +@end deffn + +@deffn Command {esirisc_flash ref_erase} (bank_id) +Erases the reference cell for the bank identified by @option{bank_id}. This is +an uncommon operation. +@end deffn +@end deffn + @deffn {Flash Driver} fm3 All members of the FM3 microcontroller family from Fujitsu include internal flash and use ARM Cortex-M3 cores. @@ -6394,23 +6429,24 @@ flash bank $_FLASHNAME stm32f1x 0x08080000 0 0 0 $_TARGETNAME Some stm32f1x-specific commands are defined: @deffn Command {stm32f1x lock} num -Locks the entire stm32 device. +Locks the entire stm32 device against reading. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn Command {stm32f1x unlock} num -Unlocks the entire stm32 device. +Unlocks the entire stm32 device for reading. This command will cause +a mass erase of the entire stm32 device if previously locked. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn Command {stm32f1x mass_erase} num -Mass erases the entire stm32f1x device. +Mass erases the entire stm32 device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn Command {stm32f1x options_read} num -Read and display the stm32 option bytes written by -the @command{stm32f1x options_write} command. +Reads and displays active stm32 option bytes loaded during POR +or upon executing the @command{stm32f1x options_load} command. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @@ -6418,6 +6454,13 @@ The @var{num} parameter is a value shown by @command{flash banks}. Writes the stm32 option byte with the specified values. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn + +@deffn Command {stm32f1x options_load} num +Generates a special kind of reset to re-load the stm32 option bytes written +by the @command{stm32f1x options_write} or @command{flash protect} commands +without having to power cycle the target. Not applicable to stm32f1x devices. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn @end deffn @deffn {Flash Driver} stm32f2x @@ -6584,6 +6627,42 @@ The @var{num} parameter is a value shown by @command{flash banks}. Mass erases the entire stm32l4x device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn + +@deffn Command {stm32l4x option_read} num reg_offset +Reads an option byte register from the stm32l4x device. +The @var{num} parameter is a value shown by @command{flash banks}, @var{reg_offset} +is the register offset of the Option byte to read. + +For example to read the FLASH_OPTR register: +@example +stm32l4x option_read 0 0x20 +# Option Register: <0x40022020> = 0xffeff8aa +@end example + +The above example will read out the FLASH_OPTR register which contains the RDP +option byte, Watchdog configuration, BOR level etc. +@end deffn + +@deffn Command {stm32l4x option_write} num reg_offset reg_mask +Write an option byte register of the stm32l4x device. +The @var{num} parameter is a value shown by @command{flash banks}, @var{reg_offset} +is the register offset of the Option byte to write, and @var{reg_mask} is the mask +to apply when writing the register (only bits with a '1' will be touched). + +For example to write the WRP1AR option bytes: +@example +stm32l4x option_write 0 0x28 0x00FF0000 0x00FF00FF +@end example + +The above example will write the WRP1AR option register configuring the Write protection +Area A for bank 1. The above example set WRP1AR_END=255, WRP1AR_START=0. +This will effectively write protect all sectors in flash bank 1. +@end deffn + +@deffn Command {stm32l4x option_load} num +Forces a re-load of the option byte registers. Will cause a reset of the device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn @end deffn @deffn {Flash Driver} str7x @@ -8687,6 +8766,12 @@ Selects whether interrupts will be processed when single stepping configure l2x cache @end deffn +@deffn Command {cortex_a mmu dump} [@option{0}|@option{1}|@option{addr} address [@option{num_entries}]] +Dump the MMU translation table from TTB0 or TTB1 register, or from physical +memory location @var{address}. When dumping the table from @var{address}, print at most +@var{num_entries} page table entries. @var{num_entries} is optional, if omitted, the maximum +possible (4096) entries are printed. +@end deffn @subsection ARMv7-R specific commands @cindex Cortex-R @@ -8777,7 +8862,7 @@ baud with our custom divisor to get 12MHz) @item @code{itmdump -f /dev/ttyUSB1 -d1} @item OpenOCD invocation line: @example -openocd -f interface/stlink-v2-1.cfg \ +openocd -f interface/stlink.cfg \ -c "transport select hla_swd" \ -f target/stm32l1.cfg \ -c "tpiu config external uart off 24000000 12000000" @@ -8885,6 +8970,29 @@ Selects whether interrupts will be processed when single stepping. The default c @option{on}. @end deffn +@section EnSilica eSi-RISC Architecture + +eSi-RISC is a highly configurable microprocessor architecture for embedded systems +provided by EnSilica. (See: @url{http://www.ensilica.com/risc-ip/}.) + +@subsection esirisc specific commands +@deffn Command {esirisc cache_arch} (@option{harvard}|@option{von_neumann}) +Configure the caching architecture. Targets with the @code{UNIFIED_ADDRESS_SPACE} +option disabled employ a Harvard architecture. By default, @option{von_neumann} is assumed. +@end deffn + +@deffn Command {esirisc flush_caches} +Flush instruction and data caches. This command requires that the target is halted +when the command is issued and configured with an instruction or data cache. +@end deffn + +@deffn Command {esirisc hwdc} (@option{all}|@option{none}|mask ...) +Configure hardware debug control. The HWDC register controls which exceptions return +control back to the debugger. Possible masks are @option{all}, @option{none}, +@option{reset}, @option{interrupt}, @option{syscall}, @option{error}, and @option{debug}. +By default, @option{reset}, @option{error}, and @option{debug} are enabled. +@end deffn + @section Intel Architecture Intel Quark X10xx is the first product in the Quark family of SoCs. It is an IA-32 diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 9a58239eb..639330549 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -25,6 +25,7 @@ NOR_DRIVERS = \ %D%/dsp5680xx_flash.c \ %D%/efm32.c \ %D%/em357.c \ + %D%/esirisc_flash.c \ %D%/faux.c \ %D%/fespi.c \ %D%/fm3.c \ @@ -36,6 +37,7 @@ NOR_DRIVERS = \ %D%/lpc288x.c \ %D%/lpc2900.c \ %D%/lpcspifi.c \ + %D%/max32xxx.c \ %D%/mdr.c \ %D%/msp432.c \ %D%/mrvlqspi.c \ diff --git a/src/flash/nor/at91sam4.c b/src/flash/nor/at91sam4.c index c5b31e964..2eec15e2a 100644 --- a/src/flash/nor/at91sam4.c +++ b/src/flash/nor/at91sam4.c @@ -2376,6 +2376,11 @@ static int sam4_GetInfo(struct sam4_chip *pChip) { const struct sam4_reg_list *pReg; uint32_t regval; + int r; + + r = sam4_ReadAllRegs(pChip); + if (r != ERROR_OK) + return r; pReg = &(sam4_all_regs[0]); while (pReg->name) { @@ -2545,7 +2550,7 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate) sam4_explain_chipid_cidr(pPrivate->pChip); return ERROR_FAIL; } else { - LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr); + LOG_DEBUG("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr); } /* DANGER: THERE ARE DRAGONS HERE */ @@ -2581,14 +2586,35 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate) return ERROR_OK; } -static int _sam4_probe(struct flash_bank *bank, int noise) +static int sam4_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct sam4_bank_private *pPrivate; + int k = bank->size / 1024; + + pPrivate = get_sam4_bank_private(bank); + if (pPrivate == NULL) { + buf[0] = '\0'; + return ERROR_FAIL; + } + + snprintf(buf, buf_size, + "%s bank %d: %d kB at 0x%08" PRIx32, + pPrivate->pChip->details.name, + pPrivate->bank_number, + k, + bank->base); + + return ERROR_OK; +} + +static int sam4_probe(struct flash_bank *bank) { unsigned x; int r; struct sam4_bank_private *pPrivate; - LOG_DEBUG("Begin: Bank: %d, Noise: %d", bank->bank_number, noise); + LOG_DEBUG("Begin: Bank: %d", bank->bank_number); if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -2616,7 +2642,7 @@ static int _sam4_probe(struct flash_bank *bank, int noise) for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) { if (bank->base == pPrivate->pChip->details.bank[x].base_address) { bank->size = pPrivate->pChip->details.bank[x].size_bytes; - LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x); + LOG_DEBUG("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x); break; } } @@ -2655,14 +2681,15 @@ static int _sam4_probe(struct flash_bank *bank, int noise) return r; } -static int sam4_probe(struct flash_bank *bank) -{ - return _sam4_probe(bank, 1); -} - static int sam4_auto_probe(struct flash_bank *bank) { - return _sam4_probe(bank, 0); + struct sam4_bank_private *pPrivate; + + pPrivate = get_sam4_bank_private(bank); + if (pPrivate && pPrivate->probed) + return ERROR_OK; + + return sam4_probe(bank); } static int sam4_erase(struct flash_bank *bank, int first, int last) @@ -2762,20 +2789,17 @@ static int sam4_page_read(struct sam4_bank_private *pPrivate, unsigned pagenum, return r; } -static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, const uint8_t *buf) +static int sam4_set_wait(struct sam4_bank_private *pPrivate) { - uint32_t adr; - uint32_t status; uint32_t fmr; /* EEFC Flash Mode Register */ int r; - adr = pagenum * pPrivate->page_size; - adr = (adr + pPrivate->base_address); - /* Get flash mode register value */ r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address, &fmr); - if (r != ERROR_OK) - LOG_DEBUG("Error Read failed: read flash mode register"); + if (r != ERROR_OK) { + LOG_ERROR("Error Read failed: read flash mode register"); + return r; + } /* Clear flash wait state field */ fmr &= 0xfffff0ff; @@ -2786,7 +2810,19 @@ static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, LOG_DEBUG("Flash Mode: 0x%08x", ((unsigned int)(fmr))); r = target_write_u32(pPrivate->pBank->target, pPrivate->controller_address, fmr); if (r != ERROR_OK) - LOG_DEBUG("Error Write failed: set flash mode register"); + LOG_ERROR("Error Write failed: set flash mode register"); + + return r; +} + +static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, const uint8_t *buf) +{ + uint32_t adr; + uint32_t status; + int r; + + adr = pagenum * pPrivate->page_size; + adr = (adr + pPrivate->base_address); /* 1st sector 8kBytes - page 0 - 15*/ /* 2nd sector 8kBytes - page 16 - 30*/ @@ -2874,6 +2910,10 @@ static int sam4_write(struct flash_bank *bank, goto done; } + r = sam4_set_wait(pPrivate); + if (r != ERROR_OK) + goto done; + /* what page do we start & end in? */ page_cur = offset / pPrivate->page_size; page_end = (offset + count - 1) / pPrivate->page_size; @@ -3091,7 +3131,8 @@ showall: } if ((who >= 0) && (((unsigned)(who)) < pChip->details.n_gpnvms)) { r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), who, &v); - command_print(CMD_CTX, "sam4-gpnvm%u: %u", who, v); + if (r == ERROR_OK) + command_print(CMD_CTX, "sam4-gpnvm%u: %u", who, v); return r; } else { command_print(CMD_CTX, "sam4-gpnvm invalid GPNVM: %u", who); @@ -3203,5 +3244,6 @@ struct flash_driver at91sam4_flash = { .auto_probe = sam4_auto_probe, .erase_check = default_flash_blank_check, .protect_check = sam4_protect_check, + .info = sam4_info, .free_driver_priv = sam4_free_driver_priv, }; diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index 017d1441e..5f172d118 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -165,12 +165,13 @@ static const struct samd_part samd21_parts[] = { { 0xE, "SAMD21E14A", 16, 2 }, /* SAMR21 parts have integrated SAMD21 with a radio */ + { 0x18, "SAMR21G19A", 256, 32 }, /* with 512k of serial flash */ { 0x19, "SAMR21G18A", 256, 32 }, { 0x1A, "SAMR21G17A", 128, 32 }, - { 0x1B, "SAMR21G16A", 64, 32 }, + { 0x1B, "SAMR21G16A", 64, 16 }, { 0x1C, "SAMR21E18A", 256, 32 }, { 0x1D, "SAMR21E17A", 128, 32 }, - { 0x1E, "SAMR21E16A", 64, 32 }, + { 0x1E, "SAMR21E16A", 64, 16 }, /* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */ { 0x20, "SAMD21J16B", 64, 8 }, diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 2b3146da9..62db3feea 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -38,6 +38,7 @@ extern struct flash_driver cfi_flash; extern struct flash_driver dsp5680xx_flash; extern struct flash_driver efm32_flash; extern struct flash_driver em357_flash; +extern struct flash_driver esirisc_flash; extern struct flash_driver faux_flash; extern struct flash_driver fm3_flash; extern struct flash_driver fm4_flash; @@ -49,6 +50,7 @@ extern struct flash_driver lpc2000_flash; extern struct flash_driver lpc288x_flash; extern struct flash_driver lpc2900_flash; extern struct flash_driver lpcspifi_flash; +extern struct flash_driver max32xxx_flash; extern struct flash_driver mdr_flash; extern struct flash_driver mrvlqspi_flash; extern struct flash_driver msp432_flash; @@ -103,6 +105,7 @@ static struct flash_driver *flash_drivers[] = { &dsp5680xx_flash, &efm32_flash, &em357_flash, + &esirisc_flash, &faux_flash, &fm3_flash, &fm4_flash, @@ -114,6 +117,7 @@ static struct flash_driver *flash_drivers[] = { &lpc288x_flash, &lpc2900_flash, &lpcspifi_flash, + &max32xxx_flash, &mdr_flash, &mrvlqspi_flash, &msp432_flash, diff --git a/src/flash/nor/esirisc_flash.c b/src/flash/nor/esirisc_flash.c new file mode 100644 index 000000000..f3833df1c --- /dev/null +++ b/src/flash/nor/esirisc_flash.c @@ -0,0 +1,621 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +/* eSi-TSMC Flash Registers */ +#define CONTROL 0x00 /* Control Register */ +#define TIMING0 0x04 /* Timing Register 0 */ +#define TIMING1 0x08 /* Timing Register 1 */ +#define TIMING2 0x0c /* Timing Register 2 */ +#define UNLOCK1 0x18 /* Unlock 1 */ +#define UNLOCK2 0x1c /* Unlock 2 */ +#define ADDRESS 0x20 /* Erase/Program Address */ +#define PB_DATA 0x24 /* Program Buffer Data */ +#define PB_INDEX 0x28 /* Program Buffer Index */ +#define STATUS 0x2c /* Status Register */ +#define REDUN_0 0x30 /* Redundant Address 0 */ +#define REDUN_1 0x34 /* Redundant Address 1 */ + +/* Control Fields */ +#define CONTROL_SLM (1<<0) /* Sleep Mode */ +#define CONTROL_WP (1<<1) /* Register Write Protect */ +#define CONTROL_E (1<<3) /* Erase */ +#define CONTROL_EP (1<<4) /* Erase Page */ +#define CONTROL_P (1<<5) /* Program Flash */ +#define CONTROL_ERC (1<<6) /* Erase Reference Cell */ +#define CONTROL_R (1<<7) /* Recall Trim Code */ +#define CONTROL_AP (1<<8) /* Auto-Program */ + +/* Timing Fields */ +#define TIMING0_R(x) (((x) << 0) & 0x3f) /* Read Wait States */ +#define TIMING0_F(x) (((x) << 16) & 0xffff0000) /* Tnvh Clock Cycles */ +#define TIMING1_E(x) (((x) << 0) & 0xffffff) /* Tme/Terase/Tre Clock Cycles */ +#define TIMING2_P(x) (((x) << 0) & 0xffff) /* Tprog Clock Cycles */ +#define TIMING2_H(x) (((x) << 16) & 0xff0000) /* Clock Cycles in 100ns */ +#define TIMING2_T(x) (((x) << 24) & 0xf000000) /* Clock Cycles in 10ns */ + +/* Status Fields */ +#define STATUS_BUSY (1<<0) /* Busy (Erase/Program) */ +#define STATUS_WER (1<<1) /* Write Protect Error */ +#define STATUS_DR (1<<2) /* Disable Redundancy */ +#define STATUS_DIS (1<<3) /* Discharged */ +#define STATUS_BO (1<<4) /* Brown Out */ + +/* Redundant Address Fields */ +#define REDUN_R (1<<0) /* Used */ +#define REDUN_P(x) (((x) << 12) & 0x7f000) /* Redundant Page Address */ + +/* + * The eSi-TSMC Flash manual provides two sets of timings based on the + * underlying flash process. By default, 90nm is assumed. + */ +#if 0 /* 55nm */ +#define TNVH 5000 /* 5us */ +#define TME 80000000 /* 80ms */ +#define TERASE 160000000 /* 160ms */ +#define TRE 100000000 /* 100ms */ +#define TPROG 8000 /* 8us */ +#else /* 90nm */ +#define TNVH 5000 /* 5us */ +#define TME 20000000 /* 20ms */ +#define TERASE 40000000 /* 40ms */ +#define TRE 40000000 /* 40ms */ +#define TPROG 40000 /* 40us */ +#endif + +#define CONTROL_TIMEOUT 5000 /* 5s */ +#define PAGE_SIZE 4096 +#define PB_MAX 32 + +#define NUM_NS_PER_S 1000000000ULL + +struct esirisc_flash_bank { + bool probed; + uint32_t cfg; + uint32_t clock; + uint32_t wait_states; +}; + +FLASH_BANK_COMMAND_HANDLER(esirisc_flash_bank_command) +{ + struct esirisc_flash_bank *esirisc_info; + + if (CMD_ARGC < 9) + return ERROR_COMMAND_SYNTAX_ERROR; + + esirisc_info = calloc(1, sizeof(struct esirisc_flash_bank)); + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], esirisc_info->cfg); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], esirisc_info->clock); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], esirisc_info->wait_states); + + bank->driver_priv = esirisc_info; + + return ERROR_OK; +} + +/* + * Register writes are ignored if the control.WP flag is set; the + * following sequence is required to modify this flag even when + * protection is disabled. + */ +static int esirisc_flash_unlock(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + + target_write_u32(target, esirisc_info->cfg + UNLOCK1, 0x7123); + target_write_u32(target, esirisc_info->cfg + UNLOCK2, 0x812a); + target_write_u32(target, esirisc_info->cfg + UNLOCK1, 0xbee1); + + return ERROR_OK; +} + +static int esirisc_flash_disable_protect(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t control; + + target_read_u32(target, esirisc_info->cfg + CONTROL, &control); + if (!(control & CONTROL_WP)) + return ERROR_OK; + + esirisc_flash_unlock(bank); + + control &= ~CONTROL_WP; + + target_write_u32(target, esirisc_info->cfg + CONTROL, control); + + return ERROR_OK; +} + +static int esirisc_flash_enable_protect(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t control; + + target_read_u32(target, esirisc_info->cfg + CONTROL, &control); + if (control & CONTROL_WP) + return ERROR_OK; + + esirisc_flash_unlock(bank); + + control |= CONTROL_WP; + + target_write_u32(target, esirisc_info->cfg + CONTROL, control); + + return ERROR_OK; +} + +static int esirisc_flash_check_status(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t status; + + target_read_u32(target, esirisc_info->cfg + STATUS, &status); + if (status & STATUS_WER) { + LOG_ERROR("%s: bad status: 0x%" PRIx32, bank->name, status); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int esirisc_flash_clear_status(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + + target_write_u32(target, esirisc_info->cfg + STATUS, STATUS_WER); + + return ERROR_OK; +} + +static int esirisc_flash_wait(struct flash_bank *bank, int ms) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t status; + int64_t t; + + t = timeval_ms(); + for (;;) { + target_read_u32(target, esirisc_info->cfg + STATUS, &status); + if (!(status & STATUS_BUSY)) + return ERROR_OK; + + if ((timeval_ms() - t) > ms) + return ERROR_TARGET_TIMEOUT; + + keep_alive(); + } +} + +static int esirisc_flash_control(struct flash_bank *bank, uint32_t control) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + + esirisc_flash_clear_status(bank); + + target_write_u32(target, esirisc_info->cfg + CONTROL, control); + + int retval = esirisc_flash_wait(bank, CONTROL_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: control timed out: 0x%" PRIx32, bank->name, control); + return retval; + } + + return esirisc_flash_check_status(bank); +} + +static int esirisc_flash_recall(struct flash_bank *bank) +{ + return esirisc_flash_control(bank, CONTROL_R); +} + +static int esirisc_flash_erase(struct flash_bank *bank, int first, int last) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval = ERROR_OK; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + for (int page = first; page < last; ++page) { + uint32_t address = page * PAGE_SIZE; + + target_write_u32(target, esirisc_info->cfg + ADDRESS, address); + + retval = esirisc_flash_control(bank, CONTROL_EP); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to erase address: 0x%" PRIx32, bank->name, address); + break; + } + } + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static int esirisc_flash_mass_erase(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + target_write_u32(target, esirisc_info->cfg + ADDRESS, 0); + + retval = esirisc_flash_control(bank, CONTROL_E); + if (retval != ERROR_OK) + LOG_ERROR("%s: failed to mass erase", bank->name); + + esirisc_flash_enable_protect(bank); + + return retval; +} + +/* + * Per TSMC, the reference cell should be erased once per sample. This + * is typically done during wafer sort, however we include support for + * those that may need to calibrate flash at a later time. + */ +static int esirisc_flash_ref_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + retval = esirisc_flash_control(bank, CONTROL_ERC); + if (retval != ERROR_OK) + LOG_ERROR("%s: failed to erase reference cell", bank->name); + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static int esirisc_flash_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + if (set) + esirisc_flash_enable_protect(bank); + else + esirisc_flash_disable_protect(bank); + + return ERROR_OK; +} + +static int esirisc_flash_fill_pb(struct flash_bank *bank, + const uint8_t *buffer, uint32_t count) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + struct esirisc_common *esirisc = target_to_esirisc(target); + + /* + * The pb_index register is auto-incremented when pb_data is written + * and should be cleared before each operation. + */ + target_write_u32(target, esirisc_info->cfg + PB_INDEX, 0); + + /* + * The width of the pb_data register depends on the underlying + * target; writing one byte at a time incurs a significant + * performance penalty and should be avoided. + */ + while (count > 0) { + uint32_t max_bytes = DIV_ROUND_UP(esirisc->num_bits, 8); + uint32_t num_bytes = MIN(count, max_bytes); + + target_write_buffer(target, esirisc_info->cfg + PB_DATA, num_bytes, buffer); + + buffer += num_bytes; + count -= num_bytes; + } + + return ERROR_OK; +} + +static int esirisc_flash_write(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval = ERROR_OK; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + esirisc_flash_disable_protect(bank); + + /* + * The address register is auto-incremented based on the contents of + * the pb_index register after each operation completes. It can be + * set once provided pb_index is cleared before each operation. + */ + target_write_u32(target, esirisc_info->cfg + ADDRESS, offset); + + /* + * Care must be taken when filling the program buffer; a maximum of + * 32 bytes may be written at a time and may not cross a 32-byte + * boundary based on the current offset. + */ + while (count > 0) { + uint32_t max_bytes = PB_MAX - (offset & 0x1f); + uint32_t num_bytes = MIN(count, max_bytes); + + esirisc_flash_fill_pb(bank, buffer, num_bytes); + + retval = esirisc_flash_control(bank, CONTROL_P); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to program address: 0x%" PRIx32, bank->name, offset); + break; + } + + buffer += num_bytes; + offset += num_bytes; + count -= num_bytes; + } + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static uint32_t esirisc_flash_num_cycles(struct flash_bank *bank, uint64_t ns) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + + /* apply scaling factor to avoid truncation */ + uint64_t hz = (uint64_t)esirisc_info->clock * 1000; + uint64_t num_cycles = ((hz / NUM_NS_PER_S) * ns) / 1000; + + if (hz % NUM_NS_PER_S > 0) + num_cycles++; + + return num_cycles; +} + +static int esirisc_flash_init(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t value; + int retval; + + esirisc_flash_disable_protect(bank); + + /* initialize timing registers */ + value = TIMING0_F(esirisc_flash_num_cycles(bank, TNVH)) | + TIMING0_R(esirisc_info->wait_states); + + LOG_DEBUG("TIMING0: 0x%" PRIx32, value); + target_write_u32(target, esirisc_info->cfg + TIMING0, value); + + value = TIMING1_E(esirisc_flash_num_cycles(bank, TERASE)); + + LOG_DEBUG("TIMING1: 0x%" PRIx32, value); + target_write_u32(target, esirisc_info->cfg + TIMING1, value); + + value = TIMING2_T(esirisc_flash_num_cycles(bank, 10)) | + TIMING2_H(esirisc_flash_num_cycles(bank, 100)) | + TIMING2_P(esirisc_flash_num_cycles(bank, TPROG)); + + LOG_DEBUG("TIMING2: 0x%" PRIx32, value); + target_write_u32(target, esirisc_info->cfg + TIMING2, value); + + /* recall trim code */ + retval = esirisc_flash_recall(bank); + if (retval != ERROR_OK) + LOG_ERROR("%s: failed to recall trim code", bank->name); + + esirisc_flash_enable_protect(bank); + + return retval; +} + +static int esirisc_flash_probe(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + bank->num_sectors = bank->size / PAGE_SIZE; + bank->sectors = alloc_block_array(0, PAGE_SIZE, bank->num_sectors); + + /* + * Register write protection is enforced using a single protection + * block for the entire bank. This is as good as it gets. + */ + bank->num_prot_blocks = 1; + bank->prot_blocks = alloc_block_array(0, bank->size, bank->num_prot_blocks); + + retval = esirisc_flash_init(bank); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to initialize bank", bank->name); + return retval; + } + + esirisc_info->probed = true; + + return ERROR_OK; +} + +static int esirisc_flash_auto_probe(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + + if (esirisc_info->probed) + return ERROR_OK; + + return esirisc_flash_probe(bank); +} + +static int esirisc_flash_protect_check(struct flash_bank *bank) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t control; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + target_read_u32(target, esirisc_info->cfg + CONTROL, &control); + + /* single protection block (also see: esirisc_flash_probe()) */ + bank->prot_blocks[0].is_protected = !!(control & CONTROL_WP); + + return ERROR_OK; +} + +static int esirisc_flash_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct esirisc_flash_bank *esirisc_info = bank->driver_priv; + + snprintf(buf, buf_size, + "%4s cfg at 0x%" PRIx32 ", clock %" PRId32 ", wait_states %" PRId32, + "", /* align with first line */ + esirisc_info->cfg, + esirisc_info->clock, + esirisc_info->wait_states); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_esirisc_flash_mass_erase_command) +{ + struct flash_bank *bank; + int retval; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = esirisc_flash_mass_erase(bank); + + command_print(CMD_CTX, "mass erase %s", + (retval == ERROR_OK) ? "successful" : "failed"); + + return retval; +} + +COMMAND_HANDLER(handle_esirisc_flash_ref_erase_command) +{ + struct flash_bank *bank; + int retval; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = esirisc_flash_ref_erase(bank); + + command_print(CMD_CTX, "erase reference cell %s", + (retval == ERROR_OK) ? "successful" : "failed"); + + return retval; +} + +static const struct command_registration esirisc_flash_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = handle_esirisc_flash_mass_erase_command, + .mode = COMMAND_EXEC, + .help = "erases all pages in data memory", + .usage = "bank_id", + }, + { + .name = "ref_erase", + .handler = handle_esirisc_flash_ref_erase_command, + .mode = COMMAND_EXEC, + .help = "erases reference cell (uncommon)", + .usage = "bank_id", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration esirisc_flash_command_handlers[] = { + { + .name = "esirisc_flash", + .mode = COMMAND_ANY, + .help = "eSi-RISC flash command group", + .usage = "", + .chain = esirisc_flash_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver esirisc_flash = { + .name = "esirisc", + .commands = esirisc_flash_command_handlers, + .usage = "flash bank bank_id 'esirisc' base_address size_bytes 0 0 target " + "cfg_address clock_hz wait_states", + .flash_bank_command = esirisc_flash_bank_command, + .erase = esirisc_flash_erase, + .protect = esirisc_flash_protect, + .write = esirisc_flash_write, + .read = default_flash_read, + .probe = esirisc_flash_probe, + .auto_probe = esirisc_flash_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = esirisc_flash_protect_check, + .info = esirisc_flash_info, +}; diff --git a/src/flash/nor/max32xxx.c b/src/flash/nor/max32xxx.c new file mode 100644 index 000000000..ca037de4d --- /dev/null +++ b/src/flash/nor/max32xxx.c @@ -0,0 +1,997 @@ +/*************************************************************************** + * Copyright (C) 2016 by Maxim Integrated * + * Kevin Gillespie * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include + +/* Register Addresses */ +#define FLSH_ADDR 0x000 +#define FLSH_CLKDIV 0x004 +#define FLSH_CN 0x008 +#define PR1E_ADDR 0x00C +#define PR2S_ADDR 0x010 +#define PR2E_ADDR 0x014 +#define PR3S_ADDR 0x018 +#define PR3E_ADDR 0x01C +#define FLSH_MD 0x020 +#define FLSH_INT 0x024 +#define FLSH_DATA0 0x030 +#define FLSH_DATA1 0x034 +#define FLSH_DATA2 0x038 +#define FLSH_DATA3 0x03C +#define FLSH_BL_CTRL 0x170 +#define FLSH_PROT 0x300 + +#define ARM_PID_REG 0xE00FFFE0 +#define MAX326XX_ID_REG 0x40000838 + +/* Register settings */ +#define FLSH_INT_AF 0x00000002 + +#define FLSH_CN_UNLOCK_MASK 0xF0000000 +#define FLSH_CN_UNLOCK_VALUE 0x20000000 + +#define FLSH_CN_PEND 0x01000000 + +#define FLSH_CN_ERASE_CODE_MASK 0x0000FF00 +#define FLSH_CN_ERASE_CODE_PGE 0x00005500 +#define FLSH_CN_ERASE_CODE_ME 0x0000AA00 + +#define FLSH_CN_PGE 0x00000004 +#define FLSH_CN_ME 0x00000002 +#define FLSH_CN_WR 0x00000001 +#define FLASH_BL_CTRL_23 0x00020000 +#define FLASH_BL_CTRL_IFREN 0x00000001 + +#define ARM_PID_DEFAULT_CM3 0xB4C3 +#define ARM_PID_DEFAULT_CM4 0xB4C4 +#define MAX326XX_ID 0x4D + +static int max32xxx_mass_erase(struct flash_bank *bank); + +struct max32xxx_flash_bank { + int probed; + int max326xx; + unsigned int flash_size; + unsigned int flc_base; + unsigned int sector_size; + unsigned int clkdiv_value; + unsigned int int_state; + unsigned int burst_size_bits; +}; + +/* see contib/loaders/flash/max32xxx/max32xxx.s for src */ +static const uint8_t write_code[] = { +#include "../../contrib/loaders/flash/max32xxx/max32xxx.inc" +}; + +/* Config Command: flash bank name driver base size chip_width bus_width target [driver_option] + flash bank max32xxx 0 0 [burst_bits] + */ +FLASH_BANK_COMMAND_HANDLER(max32xxx_flash_bank_command) +{ + struct max32xxx_flash_bank *info; + + if (CMD_ARGC < 9) { + LOG_WARNING("incomplete flash bank max32xxx configuration: 0 0 [burst_bits]"); + return ERROR_FLASH_BANK_INVALID; + } + + info = calloc(sizeof(struct max32xxx_flash_bank), 1); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], info->flash_size); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->flc_base); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->sector_size); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], info->clkdiv_value); + + if (CMD_ARGC > 9) + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[9], info->burst_size_bits); + else + info->burst_size_bits = 32; + + info->int_state = 0; + bank->driver_priv = info; + return ERROR_OK; +} + +static int get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct max32xxx_flash_bank *info = bank->driver_priv; + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + printed = snprintf(buf, buf_size, "\nMaxim Integrated max32xxx flash driver\n"); + buf += printed; + buf_size -= printed; + return ERROR_OK; +} + +/*************************************************************************** +* flash operations +***************************************************************************/ + +static int max32xxx_flash_op_pre(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct max32xxx_flash_bank *info = bank->driver_priv; + uint32_t flsh_cn; + uint32_t bootloader; + + /* Check if the flash controller is busy */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + if (flsh_cn & (FLSH_CN_PEND | FLSH_CN_ERASE_CODE_MASK | FLSH_CN_PGE | + FLSH_CN_ME | FLSH_CN_WR)) + return ERROR_FLASH_BUSY; + + /* Refresh flash controller timing */ + target_write_u32(target, info->flc_base + FLSH_CLKDIV, info->clkdiv_value); + + /* Clear and disable flash programming interrupts */ + target_read_u32(target, info->flc_base + FLSH_INT, &info->int_state); + target_write_u32(target, info->flc_base + FLSH_INT, 0x00000000); + + /* Clear the lower bit in the bootloader configuration register in case flash page 0 has been replaced */ + if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { + LOG_ERROR("Read failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (bootloader & FLASH_BL_CTRL_23) { + LOG_WARNING("FLSH_BL_CTRL indicates BL mode 2 or mode 3."); + if (bootloader & FLASH_BL_CTRL_IFREN) { + LOG_WARNING("Flash page 0 swapped out, attempting to swap back in for programming"); + bootloader &= ~(FLASH_BL_CTRL_IFREN); + if (target_write_u32(target, info->flc_base + FLSH_BL_CTRL, bootloader) != ERROR_OK) { + LOG_ERROR("Write failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (target_read_u32(target, info->flc_base + FLSH_BL_CTRL, &bootloader) != ERROR_OK) { + LOG_ERROR("Read failure on FLSH_BL_CTRL"); + return ERROR_FAIL; + } + if (bootloader & FLASH_BL_CTRL_IFREN) { + /* Bummer */ + LOG_ERROR("Unable to swap flash page 0 back in. Writes to page 0 will fail."); + } + } + } + + /* Unlock flash */ + flsh_cn &= ~FLSH_CN_UNLOCK_MASK; + flsh_cn |= FLSH_CN_UNLOCK_VALUE; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Confirm flash is unlocked */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + if ((flsh_cn & FLSH_CN_UNLOCK_VALUE) != FLSH_CN_UNLOCK_VALUE) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_flash_op_post(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct max32xxx_flash_bank *info = bank->driver_priv; + uint32_t flsh_cn; + + /* Restore flash programming interrupts */ + target_write_u32(target, info->flc_base + FLSH_INT, info->int_state); + + /* Lock flash */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= ~FLSH_CN_UNLOCK_MASK; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + return ERROR_OK; +} + +static int max32xxx_protect_check(struct flash_bank *bank) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int i; + uint32_t temp_reg; + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (!info->max326xx) { + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = -1; + + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + /* Check the protection */ + for (i = 0; i < bank->num_sectors; i++) { + if (i%32 == 0) + target_read_u32(target, info->flc_base + FLSH_PROT + ((i/32)*4), &temp_reg); + + if (temp_reg & (0x1 << i%32)) + bank->sectors[i].is_protected = 1; + else + bank->sectors[i].is_protected = 0; + } + return ERROR_OK; +} + +static int max32xxx_erase(struct flash_bank *bank, int first, int last) +{ + int banknr; + uint32_t flsh_cn, flsh_int; + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int retval; + int retry; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + if ((first == 0) && (last == (bank->num_sectors - 1))) + return max32xxx_mass_erase(bank); + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + int erased = 0; + for (banknr = first; banknr <= last; banknr++) { + + /* Check the protection */ + if (bank->sectors[banknr].is_protected == 1) { + LOG_WARNING("Flash sector %d is protected", banknr); + continue; + } else + erased = 1; + + /* Address is first word in page */ + target_write_u32(target, info->flc_base + FLSH_ADDR, banknr * info->sector_size); + + /* Write page erase code */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn |= FLSH_CN_ERASE_CODE_PGE; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Issue page erase command */ + flsh_cn |= 0x4; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Wait until erase complete */ + retry = 1000; + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash page erase @ 0x%08x", + banknr * info->sector_size); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Error erasing flash page %i", banknr); + target_write_u32(target, info->flc_base + FLSH_INT, 0); + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + bank->sectors[banknr].is_erased = 1; + } + + if (!erased) { + LOG_ERROR("All pages protected %d to %d", first, last); + max32xxx_flash_op_post(bank); + return ERROR_FAIL; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + int page; + uint32_t temp_reg; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (!info->max326xx) + return ERROR_FLASH_OPER_UNSUPPORTED; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + /* Setup the protection on the pages given */ + for (page = first; page <= last; page++) { + if (set) { + /* Set the write/erase bit for this page */ + target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); + temp_reg |= (0x1 << page%32); + target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); + bank->sectors[page].is_protected = 1; + } else { + /* Clear the write/erase bit for this page */ + target_read_u32(target, info->flc_base + FLSH_PROT + (page/32), &temp_reg); + temp_reg &= ~(0x1 << page%32); + target_write_u32(target, info->flc_base + FLSH_PROT + (page/32), temp_reg); + bank->sectors[page].is_protected = 0; + } + } + + return ERROR_OK; +} + +static int max32xxx_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t wcount) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *source; + struct working_area *write_algorithm; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + /* power of two, and multiple of word size */ + static const unsigned buf_min = 128; + + /* for small buffers it's faster not to download an algorithm */ + if (wcount * 4 < buf_min) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "", + bank, buffer, offset, wcount); + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(write_code), &write_algorithm) != ERROR_OK) { + LOG_DEBUG("no working area for block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* plus a buffer big enough for this data */ + if (wcount * 4 < buffer_size) + buffer_size = wcount * 4; + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + buffer_size /= 2; + + if (buffer_size <= buf_min) { + target_free_working_area(target, write_algorithm); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + LOG_DEBUG("retry target_alloc_working_area(%s, size=%u)", + target_name(target), (unsigned) buffer_size); + } + + target_write_buffer(target, write_algorithm->address, sizeof(write_code), + write_code); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[2].value, 0, 32, address); + buf_set_u32(reg_params[3].value, 0, 32, wcount); + buf_set_u32(reg_params[4].value, 0, 32, info->flc_base); + retval = target_run_flash_async_algorithm(target, buffer, wcount, 4, 0, NULL, + 5, reg_params, source->address, source->size, write_algorithm->address, 0, &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) + LOG_ERROR("error %d executing max32xxx flash write algorithm", retval); + + target_free_working_area(target, write_algorithm); + target_free_working_area(target, source); + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + return retval; +} + +static int max32xxx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t flsh_cn, flsh_int; + uint32_t address = offset; + uint32_t remaining = count; + uint32_t words_remaining; + int retval; + int retry; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_DEBUG("bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", + bank, buffer, offset, count); + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (offset & 0x3) { + LOG_WARNING("offset size must be word aligned"); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + if (remaining >= 4) { + /* write in 32-bit units */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* try using a block write */ + words_remaining = remaining / 4; + retval = max32xxx_write_block(bank, buffer, offset, words_remaining); + + if (retval != ERROR_OK) { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + LOG_DEBUG("writing flash word-at-a-time"); + else { + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + } else { + /* all 32-bit words have been written */ + buffer += words_remaining * 4; + address += words_remaining * 4; + remaining -= words_remaining * 4; + } + } + + if ((remaining >= 4) && ((address & 0x1F) != 0)) { + /* write in 32-bit units until we are 128-bit aligned */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + while ((remaining >= 4) && ((address & 0x1F) != 0)) { + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 4; + address += 4; + remaining -= 4; + } + } + + if ((info->burst_size_bits == 128) && (remaining >= 16)) { + /* write in 128-bit bursts while we can */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + + flsh_cn &= 0xFFFFFFEF; + flsh_cn |= 0x08000000; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + + while (remaining >= 16) { + if ((address & 0xFFF) == 0) + LOG_DEBUG("Writing @ 0x%08x", address); + + target_write_buffer(target, info->flc_base + FLSH_DATA0, 16, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 16; + address += 16; + remaining -= 16; + } + } + + if (remaining >= 4) { + + /* write in 32-bit units while we can */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + while (remaining >= 4) { + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, buffer); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += 4; + address += 4; + remaining -= 4; + } + } + + if (remaining > 0) { + /* write remaining bytes in a 32-bit unit */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn &= 0xF7FFFFFF; + flsh_cn |= 0x00000010; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff}; + int i = 0; + + while (remaining > 0) { + last_word[i++] = *buffer; + buffer++; + remaining--; + } + + target_write_u32(target, info->flc_base + FLSH_ADDR, address); + target_write_buffer(target, info->flc_base + FLSH_DATA0, 4, last_word); + flsh_cn |= 0x00000001; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + /* Wait until flash operation is complete */ + retry = 10; + + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash write @ 0x%08x", address); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Flash Error writing 0x%x bytes at 0x%08x", count, offset); + max32xxx_flash_op_post(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int max32xxx_probe(struct flash_bank *bank) +{ + struct max32xxx_flash_bank *info = bank->driver_priv; + struct target *target = bank->target; + uint32_t arm_id[2]; + uint16_t arm_pid; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + /* provide this for the benefit of the NOR flash framework */ + bank->size = info->flash_size; + bank->num_sectors = info->flash_size / info->sector_size; + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + + for (int i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * info->sector_size; + bank->sectors[i].size = info->sector_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + /* Probe to determine if this part is in the max326xx family */ + info->max326xx = 0; + target_read_u32(target, ARM_PID_REG, &arm_id[0]); + target_read_u32(target, ARM_PID_REG+4, &arm_id[1]); + arm_pid = (arm_id[1] << 8) + arm_id[0]; + LOG_DEBUG("arm_pid = 0x%x", arm_pid); + + if ((arm_pid == ARM_PID_DEFAULT_CM3) || arm_pid == ARM_PID_DEFAULT_CM4) { + uint32_t max326xx_id; + target_read_u32(target, MAX326XX_ID_REG, &max326xx_id); + LOG_DEBUG("max326xx_id = 0x%x", max326xx_id); + max326xx_id = ((max326xx_id & 0xFF000000) >> 24); + if (max326xx_id == MAX326XX_ID) + info->max326xx = 1; + } + LOG_DEBUG("info->max326xx = %d", info->max326xx); + + /* Initialize the protection bits for each flash page */ + if (max32xxx_protect_check(bank) == ERROR_FLASH_OPER_UNSUPPORTED) + LOG_WARNING("Flash protection not supported on this device"); + + info->probed = 1; + return ERROR_OK; +} + +static int max32xxx_mass_erase(struct flash_bank *bank) +{ + struct target *target = NULL; + struct max32xxx_flash_bank *info = NULL; + uint32_t flsh_cn, flsh_int; + int retval; + int retry; + info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + int not_protected = 0; + for (int i = 0; i < bank->num_sectors; i++) { + if (bank->sectors[i].is_protected == 1) + LOG_WARNING("Flash sector %d is protected", i); + else + not_protected = 1; + } + + if (!not_protected) { + LOG_ERROR("All pages protected"); + return ERROR_FAIL; + } + + /* Prepare to issue flash operation */ + retval = max32xxx_flash_op_pre(bank); + + if (retval != ERROR_OK) + return retval; + + /* Write mass erase code */ + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + flsh_cn |= FLSH_CN_ERASE_CODE_ME; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Issue mass erase command */ + flsh_cn |= 0x2; + target_write_u32(target, info->flc_base + FLSH_CN, flsh_cn); + + /* Wait until erase complete */ + retry = 1000; + do { + target_read_u32(target, info->flc_base + FLSH_CN, &flsh_cn); + } while ((--retry > 0) && (flsh_cn & FLSH_CN_PEND)); + + if (retry <= 0) { + LOG_ERROR("Timed out waiting for flash mass erase"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check access violations */ + target_read_u32(target, info->flc_base + FLSH_INT, &flsh_int); + if (flsh_int & FLSH_INT_AF) { + LOG_ERROR("Error mass erasing"); + target_write_u32(target, info->flc_base + FLSH_INT, 0); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (max32xxx_flash_op_post(bank) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_mass_erase_command) +{ + int i; + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "max32xxx mass_erase "); + return ERROR_OK; + } + + if (ERROR_OK != retval) + return retval; + + if (max32xxx_mass_erase(bank) == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD_CTX, "max32xxx mass erase complete"); + } else + command_print(CMD_CTX, "max32xxx mass erase failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_set_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + uint32_t addr, len; + + if (CMD_ARGC != 3) { + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Convert the range to the page numbers */ + if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { + LOG_WARNING("Error parsing address"); + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_FAIL; + } + /* Mask off the top portion on the address */ + addr = (addr & 0x0FFFFFFF); + + if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { + LOG_WARNING("Error parsing length"); + command_print(CMD_CTX, "max32xxx protection_set "); + return ERROR_FAIL; + } + + /* Check the address is in the range of the flash */ + if ((addr+len) >= info->flash_size) + return ERROR_FLASH_SECTOR_INVALID; + + if (len == 0) + return ERROR_OK; + + /* Convert the address and length to the page boundaries */ + addr = addr - (addr % info->sector_size); + if (len % info->sector_size) + len = len + info->sector_size - (len % info->sector_size); + + /* Convert the address and length to page numbers */ + addr = (addr / info->sector_size); + len = addr + (len / info->sector_size) - 1; + + if (max32xxx_protect(bank, 1, addr, len) == ERROR_OK) + command_print(CMD_CTX, "max32xxx protection set complete"); + else + command_print(CMD_CTX, "max32xxx protection set failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_clr_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + uint32_t addr, len; + + if (CMD_ARGC != 3) { + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Convert the range to the page numbers */ + if (1 != sscanf(CMD_ARGV[1], "0x%"SCNx32, &addr)) { + LOG_WARNING("Error parsing address"); + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_FAIL; + } + /* Mask off the top portion on the address */ + addr = (addr & 0x0FFFFFFF); + + if (1 != sscanf(CMD_ARGV[2], "0x%"SCNx32, &len)) { + LOG_WARNING("Error parsing length"); + command_print(CMD_CTX, "max32xxx protection_clr "); + return ERROR_FAIL; + } + + /* Check the address is in the range of the flash */ + if ((addr+len) >= info->flash_size) + return ERROR_FLASH_SECTOR_INVALID; + + if (len == 0) + return ERROR_OK; + + /* Convert the address and length to the page boundaries */ + addr = addr - (addr % info->sector_size); + if (len % info->sector_size) + len = len + info->sector_size - (len % info->sector_size); + + /* Convert the address and length to page numbers */ + addr = (addr / info->sector_size); + len = addr + (len / info->sector_size) - 1; + + if (max32xxx_protect(bank, 0, addr, len) == ERROR_OK) + command_print(CMD_CTX, "max32xxx protection clear complete"); + else + command_print(CMD_CTX, "max32xxx protection clear failed"); + + return ERROR_OK; +} + +COMMAND_HANDLER(max32xxx_handle_protection_check_command) +{ + struct flash_bank *bank; + int retval; + struct max32xxx_flash_bank *info; + int i; + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "max32xxx protection_check "); + return ERROR_OK; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + info = bank->driver_priv; + + /* Update the protection array */ + retval = max32xxx_protect_check(bank); + if (ERROR_OK != retval) { + LOG_WARNING("Error updating the protection array"); + return retval; + } + + LOG_WARNING("s: a:
p:"); + for (i = 0; i < bank->num_sectors; i += 4) { + LOG_WARNING("s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d | s:%03d a:0x%06x p:%d", + (i+0), (i+0)*info->sector_size, bank->sectors[(i+0)].is_protected, + (i+1), (i+1)*info->sector_size, bank->sectors[(i+1)].is_protected, + (i+2), (i+2)*info->sector_size, bank->sectors[(i+2)].is_protected, + (i+3), (i+3)*info->sector_size, bank->sectors[(i+3)].is_protected); + } + + return ERROR_OK; +} + +static const struct command_registration max32xxx_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = max32xxx_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "mass erase flash", + }, + { + .name = "protection_set", + .handler = max32xxx_handle_protection_set_command, + .mode = COMMAND_EXEC, + .usage = "bank_id addr size", + .help = "set flash protection for address range", + }, + { + .name = "protection_clr", + .handler = max32xxx_handle_protection_clr_command, + .mode = COMMAND_EXEC, + .usage = "bank_id addr size", + .help = "clear flash protection for address range", + }, + { + .name = "protection_check", + .handler = max32xxx_handle_protection_check_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "check flash protection", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration max32xxx_command_handlers[] = { + { + .name = "max32xxx", + .mode = COMMAND_EXEC, + .help = "max32xxx flash command group", + .chain = max32xxx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver max32xxx_flash = { + .name = "max32xxx", + .commands = max32xxx_command_handlers, + .flash_bank_command = max32xxx_flash_bank_command, + .erase = max32xxx_erase, + .protect = max32xxx_protect, + .write = max32xxx_write, + .read = default_flash_read, + .probe = max32xxx_probe, + .auto_probe = max32xxx_probe, + .erase_check = default_flash_blank_check, + .protect_check = max32xxx_protect_check, + .info = get_info, +}; diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c index 16459c7f8..de5c23051 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -26,6 +26,7 @@ #include #include #include +#include enum { NRF5_FLASH_BASE = 0x00000000, @@ -203,9 +204,16 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { NRF5_DEVICE_DEF(0x007A, "51422", "CEAA", "C0", 256), NRF5_DEVICE_DEF(0x0088, "51422", "CFAC", "A0", 256), + /* nRF52810 Devices */ + NRF5_DEVICE_DEF(0x0142, "52810", "QFAA", "B0", 192), + NRF5_DEVICE_DEF(0x0143, "52810", "QCAA", "C0", 192), + /* nRF52832 Devices */ NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512), NRF5_DEVICE_DEF(0x0139, "52832", "QFAA", "E0", 512), + + /* nRF52840 Devices */ + NRF5_DEVICE_DEF(0x0150, "52840", "QIAA", "C0", 1024), }; static int nrf5_bank_is_probed(struct flash_bank *bank) @@ -240,7 +248,8 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip) { uint32_t ready; int res; - int timeout = 100; + int timeout_ms = 200; + int64_t ts_start = timeval_ms(); do { res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready); @@ -252,8 +261,9 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip) if (ready == 0x00000001) return ERROR_OK; - alive_sleep(1); - } while (timeout--); + keep_alive(); + + } while ((timeval_ms()-ts_start) < timeout_ms); LOG_DEBUG("Timed out waiting for NVMC_READY"); return ERROR_FLASH_BUSY; diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c index faada9a61..fbd759828 100644 --- a/src/flash/nor/stm32f1x.c +++ b/src/flash/nor/stm32f1x.c @@ -70,14 +70,15 @@ /* FLASH_CR register bits */ -#define FLASH_PG (1 << 0) -#define FLASH_PER (1 << 1) -#define FLASH_MER (1 << 2) -#define FLASH_OPTPG (1 << 4) -#define FLASH_OPTER (1 << 5) -#define FLASH_STRT (1 << 6) -#define FLASH_LOCK (1 << 7) -#define FLASH_OPTWRE (1 << 9) +#define FLASH_PG (1 << 0) +#define FLASH_PER (1 << 1) +#define FLASH_MER (1 << 2) +#define FLASH_OPTPG (1 << 4) +#define FLASH_OPTER (1 << 5) +#define FLASH_STRT (1 << 6) +#define FLASH_LOCK (1 << 7) +#define FLASH_OPTWRE (1 << 9) +#define FLASH_OBL_LAUNCH (1 << 13) /* except stm32f1x series */ /* FLASH_SR register bits */ @@ -106,10 +107,10 @@ #define FLASH_ERASE_TIMEOUT 100 struct stm32x_options { - uint16_t RDP; - uint16_t user_options; - uint16_t user_data; - uint16_t protection[4]; + uint8_t rdp; + uint8_t user; + uint16_t data; + uint32_t protection; }; struct stm32x_flash_bank { @@ -119,8 +120,9 @@ struct stm32x_flash_bank { bool has_dual_banks; /* used to access dual flash bank stm32xl */ + bool can_load_options; uint32_t register_base; - uint16_t default_rdp; + uint8_t default_rdp; int user_data_offset; int option_offset; uint32_t user_bank_size; @@ -145,6 +147,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) bank->driver_priv = stm32x_info; stm32x_info->probed = 0; stm32x_info->has_dual_banks = false; + stm32x_info->can_load_options = false; stm32x_info->register_base = FLASH_REG_BASE_B0; stm32x_info->user_bank_size = bank->size; @@ -221,44 +224,47 @@ static int stm32x_check_operation_supported(struct flash_bank *bank) static int stm32x_read_options(struct flash_bank *bank) { - uint32_t optiondata; - struct stm32x_flash_bank *stm32x_info = NULL; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; struct target *target = bank->target; + uint32_t option_bytes; + int retval; - stm32x_info = bank->driver_priv; - - /* read current option bytes */ - int retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optiondata); + /* read user and read protection option bytes */ + retval = target_read_u32(target, STM32_OB_RDP, &option_bytes); if (retval != ERROR_OK) return retval; - stm32x_info->option_bytes.user_options = (optiondata >> stm32x_info->option_offset >> 2) & 0xffff; - stm32x_info->option_bytes.user_data = (optiondata >> stm32x_info->user_data_offset) & 0xffff; - stm32x_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5; + stm32x_info->option_bytes.rdp = option_bytes & 0xFF; + stm32x_info->option_bytes.user = (option_bytes >> 16) & 0xFF; - if (optiondata & (1 << OPT_READOUT)) - LOG_INFO("Device Security Bit Set"); - - /* each bit refers to a 4bank protection */ - retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &optiondata); + /* read user data option bytes */ + retval = target_read_u32(target, STM32_OB_DATA0, &option_bytes); if (retval != ERROR_OK) return retval; - stm32x_info->option_bytes.protection[0] = (uint16_t)optiondata; - stm32x_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); - stm32x_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); - stm32x_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); + stm32x_info->option_bytes.data = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF); + + /* read write protection option bytes */ + retval = target_read_u32(target, STM32_OB_WRP0, &option_bytes); + if (retval != ERROR_OK) + return retval; + + stm32x_info->option_bytes.protection = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF); + + retval = target_read_u32(target, STM32_OB_WRP2, &option_bytes); + if (retval != ERROR_OK) + return retval; + + stm32x_info->option_bytes.protection |= (((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF)) << 16; return ERROR_OK; } static int stm32x_erase_options(struct flash_bank *bank) { - struct stm32x_flash_bank *stm32x_info = NULL; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; struct target *target = bank->target; - stm32x_info = bank->driver_priv; - /* read current options */ stm32x_read_options(bank); @@ -291,9 +297,9 @@ static int stm32x_erase_options(struct flash_bank *bank) if (retval != ERROR_OK) return retval; - /* clear readout protection and complementary option bytes + /* clear read protection option byte * this will also force a device unlock if set */ - stm32x_info->option_bytes.RDP = stm32x_info->default_rdp; + stm32x_info->option_bytes.rdp = stm32x_info->default_rdp; return ERROR_OK; } @@ -328,14 +334,14 @@ static int stm32x_write_options(struct flash_bank *bank) uint8_t opt_bytes[16]; - target_buffer_set_u16(target, opt_bytes, stm32x_info->option_bytes.RDP); - target_buffer_set_u16(target, opt_bytes + 2, stm32x_info->option_bytes.user_options); - target_buffer_set_u16(target, opt_bytes + 4, stm32x_info->option_bytes.user_data & 0xff); - target_buffer_set_u16(target, opt_bytes + 6, (stm32x_info->option_bytes.user_data >> 8) & 0xff); - target_buffer_set_u16(target, opt_bytes + 8, stm32x_info->option_bytes.protection[0]); - target_buffer_set_u16(target, opt_bytes + 10, stm32x_info->option_bytes.protection[1]); - target_buffer_set_u16(target, opt_bytes + 12, stm32x_info->option_bytes.protection[2]); - target_buffer_set_u16(target, opt_bytes + 14, stm32x_info->option_bytes.protection[3]); + target_buffer_set_u16(target, opt_bytes, stm32x_info->option_bytes.rdp); + target_buffer_set_u16(target, opt_bytes + 2, stm32x_info->option_bytes.user); + target_buffer_set_u16(target, opt_bytes + 4, stm32x_info->option_bytes.data & 0xff); + target_buffer_set_u16(target, opt_bytes + 6, (stm32x_info->option_bytes.data >> 8) & 0xff); + target_buffer_set_u16(target, opt_bytes + 8, stm32x_info->option_bytes.protection & 0xff); + target_buffer_set_u16(target, opt_bytes + 10, (stm32x_info->option_bytes.protection >> 8) & 0xff); + target_buffer_set_u16(target, opt_bytes + 12, (stm32x_info->option_bytes.protection >> 16) & 0xff); + target_buffer_set_u16(target, opt_bytes + 14, (stm32x_info->option_bytes.protection >> 24) & 0xff); uint32_t offset = STM32_OB_RDP - bank->base; retval = stm32x_write_block(bank, opt_bytes, offset, sizeof(opt_bytes) / 2); @@ -355,64 +361,21 @@ static int stm32x_write_options(struct flash_bank *bank) static int stm32x_protect_check(struct flash_bank *bank) { struct target *target = bank->target; - struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - uint32_t protection; - int i, s; - int num_bits; - int set; int retval = stm32x_check_operation_supported(bank); if (ERROR_OK != retval) return retval; - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ + /* medium density - each bit refers to a 4 sector protection block + * high density - each bit refers to a 2 sector protection block + * bit 31 refers to all remaining sectors in a bank */ retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection); if (retval != ERROR_OK) return retval; - /* medium density - each protection bit is for 4 * 1K pages - * high density - each protection bit is for 2 * 2K pages */ - num_bits = (bank->num_sectors / stm32x_info->ppage_size); - - if (stm32x_info->ppage_size == 2) { - /* high density flash/connectivity line protection */ - - set = 1; - - if (protection & (1 << 31)) - set = 0; - - /* bit 31 controls sector 62 - 255 protection for high density - * bit 31 controls sector 62 - 127 protection for connectivity line */ - for (s = 62; s < bank->num_sectors; s++) - bank->sectors[s].is_protected = set; - - if (bank->num_sectors > 61) - num_bits = 31; - - for (i = 0; i < num_bits; i++) { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (s = 0; s < stm32x_info->ppage_size; s++) - bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; - } - } else { - /* low/medium density flash protection */ - for (i = 0; i < num_bits; i++) { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (s = 0; s < stm32x_info->ppage_size; s++) - bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; - } - } + for (int i = 0; i < bank->num_prot_blocks; i++) + bank->prot_blocks[i].is_protected = (protection & (1 << i)) ? 0 : 1; return ERROR_OK; } @@ -467,14 +430,8 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last) static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) { - struct stm32x_flash_bank *stm32x_info = NULL; struct target *target = bank->target; - uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; - int i, reg, bit; - int status; - uint32_t protection; - - stm32x_info = bank->driver_priv; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -482,80 +439,20 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) } int retval = stm32x_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - if ((first % stm32x_info->ppage_size) != 0) { - LOG_WARNING("aligned start protect sector to a %d sector boundary", - stm32x_info->ppage_size); - first = first - (first % stm32x_info->ppage_size); - } - if (((last + 1) % stm32x_info->ppage_size) != 0) { - LOG_WARNING("aligned end protect sector to a %d sector boundary", - stm32x_info->ppage_size); - last++; - last = last - (last % stm32x_info->ppage_size); - last--; - } - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection); if (retval != ERROR_OK) return retval; - prot_reg[0] = (uint16_t)protection; - prot_reg[1] = (uint16_t)(protection >> 8); - prot_reg[2] = (uint16_t)(protection >> 16); - prot_reg[3] = (uint16_t)(protection >> 24); + retval = stm32x_erase_options(bank); + if (retval != ERROR_OK) + return retval; - if (stm32x_info->ppage_size == 2) { - /* high density flash */ - - /* bit 7 controls sector 62 - 255 protection */ - if (last > 61) { - if (set) - prot_reg[3] &= ~(1 << 7); - else - prot_reg[3] |= (1 << 7); - } - - if (first > 61) - first = 62; - if (last > 61) - last = 61; - - for (i = first; i <= last; i++) { - reg = (i / stm32x_info->ppage_size) / 8; - bit = (i / stm32x_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } else { - /* medium density flash */ - for (i = first; i <= last; i++) { - reg = (i / stm32x_info->ppage_size) / 8; - bit = (i / stm32x_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } + for (int i = first; i <= last; i++) { + if (set) + stm32x_info->option_bytes.protection &= ~(1 << i); + else + stm32x_info->option_bytes.protection |= (1 << i); } - status = stm32x_erase_options(bank); - if (status != ERROR_OK) - return status; - - stm32x_info->option_bytes.protection[0] = prot_reg[0]; - stm32x_info->option_bytes.protection[1] = prot_reg[1]; - stm32x_info->option_bytes.protection[2] = prot_reg[2]; - stm32x_info->option_bytes.protection[3] = prot_reg[3]; - return stm32x_write_options(bank); } @@ -808,7 +705,6 @@ static int stm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_i static int stm32x_probe(struct flash_bank *bank) { struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - int i; uint16_t flash_size_in_kb; uint16_t max_flash_size_in_kb; uint32_t device_id; @@ -820,8 +716,8 @@ static int stm32x_probe(struct flash_bank *bank) stm32x_info->user_data_offset = 10; stm32x_info->option_offset = 0; - /* default factory protection level */ - stm32x_info->default_rdp = 0x5AA5; + /* default factory read protection level 0 */ + stm32x_info->default_rdp = 0xA5; /* read stm32 device id register */ int retval = stm32x_get_device_id(bank, &device_id); @@ -863,7 +759,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 256; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x446: /* stm32f303xD/E */ page_size = 2048; @@ -871,7 +768,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 512; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x428: /* value line High density */ page_size = 2048; @@ -890,7 +788,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 256; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x438: /* stm32f33x */ case 0x439: /* stm32f302x6/8 */ @@ -899,7 +798,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 64; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x440: /* stm32f05x */ case 0x444: /* stm32f03x */ @@ -909,7 +809,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 64; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; case 0x448: /* stm32f07x */ case 0x442: /* stm32f09x */ @@ -918,7 +819,8 @@ static int stm32x_probe(struct flash_bank *bank) max_flash_size_in_kb = 256; stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; - stm32x_info->default_rdp = 0x55AA; + stm32x_info->default_rdp = 0xAA; + stm32x_info->can_load_options = true; break; default: LOG_WARNING("Cannot identify target as a STM32 family."); @@ -972,17 +874,31 @@ static int stm32x_probe(struct flash_bank *bank) bank->sectors = NULL; } + if (bank->prot_blocks) { + free(bank->prot_blocks); + bank->prot_blocks = NULL; + } + bank->base = base_address; bank->size = (num_pages * page_size); - bank->num_sectors = num_pages; - bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - for (i = 0; i < num_pages; i++) { - bank->sectors[i].offset = i * page_size; - bank->sectors[i].size = page_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } + bank->num_sectors = num_pages; + bank->sectors = alloc_block_array(0, page_size, num_pages); + if (!bank->sectors) + return ERROR_FAIL; + + /* calculate number of write protection blocks */ + int num_prot_blocks = num_pages / stm32x_info->ppage_size; + if (num_prot_blocks > 32) + num_prot_blocks = 32; + + bank->num_prot_blocks = num_prot_blocks; + bank->prot_blocks = alloc_block_array(0, stm32x_info->ppage_size * page_size, num_prot_blocks); + if (!bank->prot_blocks) + return ERROR_FAIL; + + if (num_prot_blocks == 32) + bank->prot_blocks[31].size = (num_pages - (31 * stm32x_info->ppage_size)) * page_size; stm32x_info->probed = 1; @@ -1275,7 +1191,7 @@ COMMAND_HANDLER(stm32x_handle_lock_command) } /* set readout protection */ - stm32x_info->option_bytes.RDP = 0; + stm32x_info->option_bytes.rdp = 0; if (stm32x_write_options(bank) != ERROR_OK) { command_print(CMD_CTX, "stm32x failed to lock device"); @@ -1329,7 +1245,7 @@ COMMAND_HANDLER(stm32x_handle_unlock_command) COMMAND_HANDLER(stm32x_handle_options_read_command) { - uint32_t optionbyte; + uint32_t optionbyte, protection; struct target *target = NULL; struct stm32x_flash_bank *stm32x_info = NULL; @@ -1357,47 +1273,38 @@ COMMAND_HANDLER(stm32x_handle_options_read_command) retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optionbyte); if (retval != ERROR_OK) return retval; - command_print(CMD_CTX, "Option Byte: 0x%" PRIx32 "", optionbyte); - int user_data = optionbyte; + uint16_t user_data = optionbyte >> stm32x_info->user_data_offset; - if (optionbyte >> OPT_ERROR & 1) - command_print(CMD_CTX, "Option Byte Complement Error"); + retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection); + if (retval != ERROR_OK) + return retval; - if (optionbyte >> OPT_READOUT & 1) - command_print(CMD_CTX, "Readout Protection On"); - else - command_print(CMD_CTX, "Readout Protection Off"); + if (optionbyte & (1 << OPT_ERROR)) + command_print(CMD_CTX, "option byte complement error"); + + command_print(CMD_CTX, "option byte register = 0x%" PRIx32 "", optionbyte); + command_print(CMD_CTX, "write protection register = 0x%" PRIx32 "", protection); + + command_print(CMD_CTX, "read protection: %s", + (optionbyte & (1 << OPT_READOUT)) ? "on" : "off"); /* user option bytes are offset depending on variant */ optionbyte >>= stm32x_info->option_offset; - if (optionbyte >> OPT_RDWDGSW & 1) - command_print(CMD_CTX, "Software Watchdog"); - else - command_print(CMD_CTX, "Hardware Watchdog"); + command_print(CMD_CTX, "watchdog: %sware", + (optionbyte & (1 << OPT_RDWDGSW)) ? "soft" : "hard"); - if (optionbyte >> OPT_RDRSTSTOP & 1) - command_print(CMD_CTX, "Stop: No reset generated"); - else - command_print(CMD_CTX, "Stop: Reset generated"); + command_print(CMD_CTX, "stop mode: %sreset generated upon entry", + (optionbyte & (1 << OPT_RDRSTSTOP)) ? "no " : ""); - if (optionbyte >> OPT_RDRSTSTDBY & 1) - command_print(CMD_CTX, "Standby: No reset generated"); - else - command_print(CMD_CTX, "Standby: Reset generated"); + command_print(CMD_CTX, "standby mode: %sreset generated upon entry", + (optionbyte & (1 << OPT_RDRSTSTDBY)) ? "no " : ""); - if (stm32x_info->has_dual_banks) { - if (optionbyte >> OPT_BFB2 & 1) - command_print(CMD_CTX, "Boot: Bank 0"); - else - command_print(CMD_CTX, "Boot: Bank 1"); - } + if (stm32x_info->has_dual_banks) + command_print(CMD_CTX, "boot: bank %d", (optionbyte & (1 << OPT_BFB2)) ? 0 : 1); - command_print(CMD_CTX, "User Option0: 0x%02" PRIx8, - (uint8_t)((user_data >> stm32x_info->user_data_offset) & 0xff)); - command_print(CMD_CTX, "User Option1: 0x%02" PRIx8, - (uint8_t)((user_data >> (stm32x_info->user_data_offset + 8)) & 0xff)); + command_print(CMD_CTX, "user data = 0x%02" PRIx16 "", user_data); return ERROR_OK; } @@ -1434,7 +1341,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command) return retval; /* start with current options */ - optionbyte = stm32x_info->option_bytes.user_options; + optionbyte = stm32x_info->option_bytes.user; /* skip over flash bank */ CMD_ARGC--; @@ -1471,7 +1378,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command) return ERROR_OK; } - stm32x_info->option_bytes.user_options = optionbyte; + stm32x_info->option_bytes.user = optionbyte; if (stm32x_write_options(bank) != ERROR_OK) { command_print(CMD_CTX, "stm32x failed to write options"); @@ -1479,8 +1386,55 @@ COMMAND_HANDLER(stm32x_handle_options_write_command) } command_print(CMD_CTX, "stm32x write options complete.\n" - "INFO: a reset or power cycle is required " - "for the new settings to take effect."); + "INFO: %spower cycle is required " + "for the new settings to take effect.", + stm32x_info->can_load_options + ? "'stm32f1x options_load' command or " : ""); + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_options_load_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + + if (!stm32x_info->can_load_options) { + LOG_ERROR("Command not applicable to stm32f1x devices - power cycle is " + "required instead."); + return ERROR_FAIL; + } + + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = stm32x_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + /* unlock option flash registers */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + return retval; + + /* force re-load of option bytes - generates software reset */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBL_LAUNCH); + if (retval != ERROR_OK) + return retval; return ERROR_OK; } @@ -1574,7 +1528,7 @@ static const struct command_registration stm32x_exec_command_handlers[] = { .handler = stm32x_handle_options_read_command, .mode = COMMAND_EXEC, .usage = "bank_id", - .help = "Read and display device option byte.", + .help = "Read and display device option bytes.", }, { .name = "options_write", @@ -1583,7 +1537,14 @@ static const struct command_registration stm32x_exec_command_handlers[] = { .usage = "bank_id ('SWWDG'|'HWWDG') " "('RSTSTNDBY'|'NORSTSTNDBY') " "('RSTSTOP'|'NORSTSTOP')", - .help = "Replace bits in device option byte.", + .help = "Replace bits in device option bytes.", + }, + { + .name = "options_load", + .handler = stm32x_handle_options_load_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Force re-load of device option bytes.", }, COMMAND_REGISTRATION_DONE }; diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index 413d04d77..b46fb0786 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -1190,6 +1190,9 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size) case 0x1000: rev_str = "A"; break; + case 0x1001: + rev_str = "Z"; + break; } break; diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 4fb7e039a..ad179216d 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -57,8 +57,8 @@ #define STM32_FLASH_CR 0x40022014 #define STM32_FLASH_OPTR 0x40022020 #define STM32_FLASH_WRP1AR 0x4002202c -#define STM32_FLASH_WRP2AR 0x40022030 -#define STM32_FLASH_WRP1BR 0x4002204c +#define STM32_FLASH_WRP1BR 0x40022030 +#define STM32_FLASH_WRP2AR 0x4002204c #define STM32_FLASH_WRP2BR 0x40022050 /* FLASH_CR register bits */ @@ -70,8 +70,10 @@ #define FLASH_CR_BKER (1 << 11) #define FLASH_MER2 (1 << 15) #define FLASH_STRT (1 << 16) +#define FLASH_OPTSTRT (1 << 17) #define FLASH_EOPIE (1 << 24) #define FLASH_ERRIE (1 << 25) +#define FLASH_OBLLAUNCH (1 << 27) #define FLASH_OPTLOCK (1 << 30) #define FLASH_LOCK (1 << 31) @@ -102,28 +104,17 @@ #define OPTKEY1 0x08192A3B #define OPTKEY2 0x4C5D6E7F +#define RDP_LEVEL_0 0xAA +#define RDP_LEVEL_1 0xBB +#define RDP_LEVEL_2 0xCC + /* other registers */ #define DBGMCU_IDCODE 0xE0042000 #define FLASH_SIZE_REG 0x1FFF75E0 -struct stm32l4_options { - uint8_t RDP; - uint16_t bank_b_start; - uint8_t user_options; - uint8_t wpr1a_start; - uint8_t wpr1a_end; - uint8_t wpr1b_start; - uint8_t wpr1b_end; - uint8_t wpr2a_start; - uint8_t wpr2a_end; - uint8_t wpr2b_start; - uint8_t wpr2b_end; - /* Fixme: Handle PCROP */ -}; - struct stm32l4_flash_bank { - struct stm32l4_options option_bytes; + uint16_t bank2_start; int probed; }; @@ -265,97 +256,80 @@ static int stm32l4_unlock_option_reg(struct target *target) return ERROR_OK; } -static int stm32l4_read_options(struct flash_bank *bank) +static int stm32l4_read_option(struct flash_bank *bank, uint32_t address, uint32_t* value) { - uint32_t optiondata; - struct stm32l4_flash_bank *stm32l4_info = NULL; struct target *target = bank->target; - - stm32l4_info = bank->driver_priv; - - /* read current option bytes */ - int retval = target_read_u32(target, STM32_FLASH_OPTR, &optiondata); - if (retval != ERROR_OK) - return retval; - - stm32l4_info->option_bytes.user_options = (optiondata >> 8) & 0x3ffff; - stm32l4_info->option_bytes.RDP = optiondata & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP1AR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr1a_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr1a_end = (optiondata >> 16) & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP2AR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr2a_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr2a_end = (optiondata >> 16) & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP1BR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr1b_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr1b_end = (optiondata >> 16) & 0xff; - - retval = target_read_u32(target, STM32_FLASH_WRP2BR, &optiondata); - if (retval != ERROR_OK) - return retval; - stm32l4_info->option_bytes.wpr2b_start = optiondata & 0xff; - stm32l4_info->option_bytes.wpr2b_end = (optiondata >> 16) & 0xff; - - if (stm32l4_info->option_bytes.RDP != 0xAA) - LOG_INFO("Device Security Bit Set"); - - return ERROR_OK; + return target_read_u32(target, address, value); } -static int stm32l4_write_options(struct flash_bank *bank) +static int stm32l4_write_option(struct flash_bank *bank, uint32_t address, uint32_t value, uint32_t mask) { - struct stm32l4_flash_bank *stm32l4_info = NULL; struct target *target = bank->target; uint32_t optiondata; - stm32l4_info = bank->driver_priv; - - (void) optiondata; - (void) stm32l4_info; - - int retval = stm32l4_unlock_option_reg(target); + int retval = target_read_u32(target, address, &optiondata); if (retval != ERROR_OK) return retval; - /* FIXME: Implement Option writing!*/ - return ERROR_OK; + + retval = stm32l4_unlock_reg(target); + if (retval != ERROR_OK) + return retval; + + retval = stm32l4_unlock_option_reg(target); + if (retval != ERROR_OK) + return retval; + + optiondata = (optiondata & ~mask) | (value & mask); + + retval = target_write_u32(target, address, optiondata); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OPTSTRT); + if (retval != ERROR_OK) + return retval; + + retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + return retval; } static int stm32l4_protect_check(struct flash_bank *bank) { struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br; + stm32l4_read_option(bank, STM32_FLASH_WRP1AR, &wrp1ar); + stm32l4_read_option(bank, STM32_FLASH_WRP1BR, &wrp1br); + stm32l4_read_option(bank, STM32_FLASH_WRP2AR, &wrp2ar); + stm32l4_read_option(bank, STM32_FLASH_WRP2BR, &wrp2br); - /* read write protection settings */ - int retval = stm32l4_read_options(bank); - if (retval != ERROR_OK) { - LOG_DEBUG("unable to read option bytes"); - return retval; - } + const uint8_t wrp1a_start = wrp1ar & 0xFF; + const uint8_t wrp1a_end = (wrp1ar >> 16) & 0xFF; + const uint8_t wrp1b_start = wrp1br & 0xFF; + const uint8_t wrp1b_end = (wrp1br >> 16) & 0xFF; + const uint8_t wrp2a_start = wrp2ar & 0xFF; + const uint8_t wrp2a_end = (wrp2ar >> 16) & 0xFF; + const uint8_t wrp2b_start = wrp2br & 0xFF; + const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF; for (int i = 0; i < bank->num_sectors; i++) { - if (i < stm32l4_info->option_bytes.bank_b_start) { - if (((i >= stm32l4_info->option_bytes.wpr1a_start) && - (i <= stm32l4_info->option_bytes.wpr1a_end)) || - ((i >= stm32l4_info->option_bytes.wpr2a_start) && - (i <= stm32l4_info->option_bytes.wpr2a_end))) + if (i < stm32l4_info->bank2_start) { + if (((i >= wrp1a_start) && + (i <= wrp1a_end)) || + ((i >= wrp1b_start) && + (i <= wrp1b_end))) bank->sectors[i].is_protected = 1; else bank->sectors[i].is_protected = 0; } else { uint8_t snb; - snb = i - stm32l4_info->option_bytes.bank_b_start + 256; - if (((snb >= stm32l4_info->option_bytes.wpr1b_start) && - (snb <= stm32l4_info->option_bytes.wpr1b_end)) || - ((snb >= stm32l4_info->option_bytes.wpr2b_start) && - (snb <= stm32l4_info->option_bytes.wpr2b_end))) + snb = i - stm32l4_info->bank2_start + 256; + if (((snb >= wrp2a_start) && + (snb <= wrp2a_end)) || + ((snb >= wrp2b_start) && + (snb <= wrp2b_end))) bank->sectors[i].is_protected = 1; else bank->sectors[i].is_protected = 0; @@ -398,9 +372,9 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last) uint32_t erase_flags; erase_flags = FLASH_PER | FLASH_STRT; - if (i >= stm32l4_info->option_bytes.bank_b_start) { + if (i >= stm32l4_info->bank2_start) { uint8_t snb; - snb = (i - stm32l4_info->option_bytes.bank_b_start) + 256; + snb = (i - stm32l4_info->bank2_start) + 256; erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER; } else erase_flags |= i << FLASH_PAGE_SHIFT; @@ -434,20 +408,29 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last return ERROR_TARGET_NOT_HALTED; } - /* read protection settings */ - int retval = stm32l4_read_options(bank); - if (retval != ERROR_OK) { - LOG_DEBUG("unable to read option bytes"); - return retval; + int ret = ERROR_OK; + /* Bank 2 */ + uint32_t reg_value = 0xFF; /* Default to bank un-protected */ + if (last >= stm32l4_info->bank2_start) { + if (set == 1) { + uint8_t begin = first > stm32l4_info->bank2_start ? first : 0x00; + reg_value = ((last & 0xFF) << 16) | begin; + } + + ret = stm32l4_write_option(bank, STM32_FLASH_WRP2AR, reg_value, 0xffffffff); + } + /* Bank 1 */ + reg_value = 0xFF; /* Default to bank un-protected */ + if (first < stm32l4_info->bank2_start) { + if (set == 1) { + uint8_t end = last >= stm32l4_info->bank2_start ? 0xFF : last; + reg_value = (end << 16) | (first & 0xFF); + } + + ret = stm32l4_write_option(bank, STM32_FLASH_WRP1AR, reg_value, 0xffffffff); } - (void)stm32l4_info; - /* FIXME: Write First and last in a valid WRPxx_start/end combo*/ - retval = stm32l4_write_options(bank); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; + return ret; } /* Count is in halfwords */ @@ -650,9 +633,9 @@ static int stm32l4_probe(struct flash_bank *bank) /* only devices with < 1024 kiB may be set to single bank dual banks */ if ((flash_size_in_kb == 1024) || !(options & OPT_DUALBANK)) - stm32l4_info->option_bytes.bank_b_start = 256; + stm32l4_info->bank2_start = 256; else - stm32l4_info->option_bytes.bank_b_start = flash_size_in_kb << 9; + stm32l4_info->bank2_start = flash_size_in_kb << 9; /* did we assign flash size? */ assert((flash_size_in_kb != 0xffff) && flash_size_in_kb); @@ -747,89 +730,6 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size) return ERROR_OK; } -COMMAND_HANDLER(stm32l4_handle_lock_command) -{ - struct target *target = NULL; - struct stm32l4_flash_bank *stm32l4_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32l4_info = bank->driver_priv; - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stm32l4_read_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to read options", - bank->driver->name); - return ERROR_OK; - } - - /* set readout protection */ - stm32l4_info->option_bytes.RDP = 0; - - if (stm32l4_write_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to lock device", bank->driver->name); - return ERROR_OK; - } - - command_print(CMD_CTX, "%s locked", bank->driver->name); - - return ERROR_OK; -} - -COMMAND_HANDLER(stm32l4_handle_unlock_command) -{ - struct target *target = NULL; - struct stm32l4_flash_bank *stm32l4_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32l4_info = bank->driver_priv; - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stm32l4_read_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to read options", bank->driver->name); - return ERROR_OK; - } - - /* clear readout protection and complementary option bytes - * this will also force a device unlock if set */ - stm32l4_info->option_bytes.RDP = 0xAA; - - if (stm32l4_write_options(bank) != ERROR_OK) { - command_print(CMD_CTX, "%s failed to unlock device", - bank->driver->name); - return ERROR_OK; - } - - command_print(CMD_CTX, "%s unlocked.\n" - "INFO: a reset or power cycle is required " - "for the new settings to take effect.", bank->driver->name); - - return ERROR_OK; -} - static int stm32l4_mass_erase(struct flash_bank *bank, uint32_t action) { int retval; @@ -873,7 +773,7 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command) uint32_t action; if (CMD_ARGC < 1) { - command_print(CMD_CTX, "stm32x mass_erase "); + command_print(CMD_CTX, "stm32l4x mass_erase "); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -889,14 +789,151 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command) for (i = 0; i < bank->num_sectors; i++) bank->sectors[i].is_erased = 1; - command_print(CMD_CTX, "stm32x mass erase complete"); + command_print(CMD_CTX, "stm32l4x mass erase complete"); } else { - command_print(CMD_CTX, "stm32x mass erase failed"); + command_print(CMD_CTX, "stm32l4x mass erase failed"); } return retval; } +COMMAND_HANDLER(stm32l4_handle_option_read_command) +{ + if (CMD_ARGC < 2) { + command_print(CMD_CTX, "stm32l4x option_read "); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + uint32_t reg_addr = STM32_FLASH_BASE; + uint32_t value = 0; + + reg_addr += strtoul(CMD_ARGV[1], NULL, 16); + + retval = stm32l4_read_option(bank, reg_addr, &value); + if (ERROR_OK != retval) + return retval; + + command_print(CMD_CTX, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value); + + return retval; +} + +COMMAND_HANDLER(stm32l4_handle_option_write_command) +{ + if (CMD_ARGC < 3) { + command_print(CMD_CTX, "stm32l4x option_write [mask]"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + uint32_t reg_addr = STM32_FLASH_BASE; + uint32_t value = 0; + uint32_t mask = 0xFFFFFFFF; + + reg_addr += strtoul(CMD_ARGV[1], NULL, 16); + value = strtoul(CMD_ARGV[2], NULL, 16); + if (CMD_ARGC > 3) + mask = strtoul(CMD_ARGV[3], NULL, 16); + + command_print(CMD_CTX, "%s Option written.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect.", bank->driver->name); + + retval = stm32l4_write_option(bank, reg_addr, value, mask); + return retval; +} + +COMMAND_HANDLER(stm32l4_handle_option_load_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct target *target = bank->target; + + retval = stm32l4_unlock_reg(target); + if (ERROR_OK != retval) + return retval; + + retval = stm32l4_unlock_option_reg(target); + if (ERROR_OK != retval) + return retval; + + /* Write the OBLLAUNCH bit in CR -> Cause device "POR" and option bytes reload */ + retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBLLAUNCH); + + command_print(CMD_CTX, "stm32l4x option load (POR) completed."); + return retval; +} + +COMMAND_HANDLER(stm32l4_handle_lock_command) +{ + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* set readout protection level 1 by erasing the RDP option byte */ + if (stm32l4_write_option(bank, STM32_FLASH_OPTR, 0, 0x000000FF) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to lock device", bank->driver->name); + return ERROR_OK; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32l4_handle_unlock_command) +{ + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name); + return ERROR_OK; + } + + return ERROR_OK; +} + static const struct command_registration stm32l4_exec_command_handlers[] = { { .name = "lock", @@ -919,6 +956,27 @@ static const struct command_registration stm32l4_exec_command_handlers[] = { .usage = "bank_id", .help = "Erase entire flash device.", }, + { + .name = "option_read", + .handler = stm32l4_handle_option_read_command, + .mode = COMMAND_EXEC, + .usage = "bank_id reg_offset", + .help = "Read & Display device option bytes.", + }, + { + .name = "option_write", + .handler = stm32l4_handle_option_write_command, + .mode = COMMAND_EXEC, + .usage = "bank_id reg_offset value mask", + .help = "Write device option bit fields with provided value.", + }, + { + .name = "option_load", + .handler = stm32l4_handle_option_load_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Force re-load of device options (will cause device reset).", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/jtag/Makefile.am b/src/jtag/Makefile.am index 50ee263d0..a76486399 100644 --- a/src/jtag/Makefile.am +++ b/src/jtag/Makefile.am @@ -1,6 +1,6 @@ noinst_LTLIBRARIES += %D%/libjtag.la -JTAG_SRCS = +JTAG_SRCS = %D%/commands.c %C%_libjtag_la_LIBADD = BUILT_SOURCES += %D%/minidriver_imp.h @@ -13,7 +13,7 @@ JTAG_SRCS += %D%/zy1000/zy1000.c JTAG_MINIDRIVER_DIR = %D%/zy1000 endif if MINIDRIVER_DUMMY -JTAG_SRCS += %D%/minidummy/minidummy.c %D%/commands.c +JTAG_SRCS += %D%/minidummy/minidummy.c JTAG_MINIDRIVER_DIR = %D%/minidummy endif @@ -29,7 +29,6 @@ CLEANFILES += %D%/jtag_minidriver.h else MINIDRIVER_IMP_DIR = %D%/drivers -JTAG_SRCS += %D%/commands.c if HLADAPTER include %D%/hla/Makefile.am diff --git a/src/jtag/commands.c b/src/jtag/commands.c index ed40755b7..e2d22cc94 100644 --- a/src/jtag/commands.c +++ b/src/jtag/commands.c @@ -144,6 +144,18 @@ void jtag_command_queue_reset(void) next_command_pointer = &jtag_command_queue; } +/** + * Copy a struct scan_field for insertion into the queue. + * + * This allocates a new copy of out_value using cmd_queue_alloc. + */ +void jtag_scan_field_clone(struct scan_field *dst, const struct scan_field *src) +{ + dst->num_bits = src->num_bits; + dst->out_value = buf_cpy(src->out_value, cmd_queue_alloc(DIV_ROUND_UP(src->num_bits, 8)), src->num_bits); + dst->in_value = src->in_value; +} + enum scan_type jtag_scan_type(const struct scan_command *cmd) { int i; diff --git a/src/jtag/commands.h b/src/jtag/commands.h index 947c94725..c0375964c 100644 --- a/src/jtag/commands.h +++ b/src/jtag/commands.h @@ -168,6 +168,7 @@ void *cmd_queue_alloc(size_t size); void jtag_queue_command(struct jtag_command *cmd); void jtag_command_queue_reset(void); +void jtag_scan_field_clone(struct scan_field *dst, const struct scan_field *src); enum scan_type jtag_scan_type(const struct scan_command *cmd); int jtag_scan_size(const struct scan_command *cmd); int jtag_read_buffer(uint8_t *buffer, const struct scan_command *cmd); diff --git a/src/jtag/drivers/bitq.c b/src/jtag/drivers/bitq.c index 66285f700..55dfe0aa4 100644 --- a/src/jtag/drivers/bitq.c +++ b/src/jtag/drivers/bitq.c @@ -123,7 +123,7 @@ static void bitq_path_move(struct pathmove_command *cmd) { int i; - for (i = 0; i <= cmd->num_states; i++) { + for (i = 0; i < cmd->num_states; i++) { if (tap_state_transition(tap_get_state(), false) == cmd->path[i]) bitq_io(0, 0, 0); else if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap_usb.c index 4ee483659..035cc446e 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap_usb.c @@ -166,7 +166,7 @@ static const char * const info_caps_str[] = { struct cmsis_dap { hid_device *dev_handle; uint16_t packet_size; - uint16_t packet_count; + int packet_count; uint8_t *packet_buffer; uint8_t caps; uint8_t mode; @@ -178,6 +178,11 @@ struct pending_transfer_result { void *buffer; }; +struct pending_request_block { + struct pending_transfer_result *transfers; + int transfer_count; +}; + struct pending_scan_result { /** Offset in bytes in the CMD_DAP_JTAG_SEQ response buffer. */ unsigned first; @@ -189,8 +194,16 @@ struct pending_scan_result { unsigned buffer_offset; }; -static int pending_transfer_count, pending_queue_len; -static struct pending_transfer_result *pending_transfers; +/* Up to MIN(packet_count, MAX_PENDING_REQUESTS) requests may be issued + * until the first response arrives */ +#define MAX_PENDING_REQUESTS 3 + +/* Pending requests are organized as a FIFO - circular buffer */ +/* Each block in FIFO can contain up to pending_queue_len transfers */ +static int pending_queue_len; +static struct pending_request_block pending_fifo[MAX_PENDING_REQUESTS]; +static int pending_fifo_put_idx, pending_fifo_get_idx; +static int pending_fifo_block_count; /* pointers to buffers that will receive jtag scan results on the next flush */ #define MAX_PENDING_SCAN_RESULTS 256 @@ -346,14 +359,16 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap) cmsis_dap_handle = NULL; free(cmsis_dap_serial); cmsis_dap_serial = NULL; - free(pending_transfers); - pending_transfers = NULL; + + for (int i = 0; i < MAX_PENDING_REQUESTS; i++) { + free(pending_fifo[i].transfers); + pending_fifo[i].transfers = NULL; + } return; } -/* Send a message and receive the reply */ -static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) +static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen) { #ifdef CMSIS_DAP_JTAG_DEBUG LOG_DEBUG("cmsis-dap usb xfer cmd=%02X", dap->packet_buffer[1]); @@ -368,6 +383,26 @@ static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) return ERROR_FAIL; } + return ERROR_OK; +} + +/* Send a message and receive the reply */ +static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) +{ + if (pending_fifo_block_count) { + LOG_ERROR("pending %d blocks, flushing", pending_fifo_block_count); + while (pending_fifo_block_count) { + hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, 10); + pending_fifo_block_count--; + } + pending_fifo_put_idx = 0; + pending_fifo_get_idx = 0; + } + + int retval = cmsis_dap_usb_write(dap, txlen); + if (retval != ERROR_OK) + return retval; + /* get reply */ retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, USB_TIMEOUT); if (retval == -1 || retval == 0) { @@ -594,29 +629,31 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us) } #endif -static int cmsis_dap_swd_run_queue(void) +static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) { - uint8_t *buffer = cmsis_dap_handle->packet_buffer; + uint8_t *buffer = dap->packet_buffer; + struct pending_request_block *block = &pending_fifo[pending_fifo_put_idx]; - LOG_DEBUG_IO("Executing %d queued transactions", pending_transfer_count); + LOG_DEBUG_IO("Executing %d queued transactions from FIFO index %d", block->transfer_count, pending_fifo_put_idx); if (queued_retval != ERROR_OK) { LOG_DEBUG("Skipping due to previous errors: %d", queued_retval); goto skip; } - if (!pending_transfer_count) + if (block->transfer_count == 0) goto skip; size_t idx = 0; buffer[idx++] = 0; /* report number */ buffer[idx++] = CMD_DAP_TFER; buffer[idx++] = 0x00; /* DAP Index */ - buffer[idx++] = pending_transfer_count; + buffer[idx++] = block->transfer_count; - for (int i = 0; i < pending_transfer_count; i++) { - uint8_t cmd = pending_transfers[i].cmd; - uint32_t data = pending_transfers[i].data; + for (int i = 0; i < block->transfer_count; i++) { + struct pending_transfer_result *transfer = &(block->transfers[i]); + uint8_t cmd = transfer->cmd; + uint32_t data = transfer->data; LOG_DEBUG_IO("%s %s reg %x %"PRIx32, cmd & SWD_CMD_APnDP ? "AP" : "DP", @@ -651,26 +688,62 @@ static int cmsis_dap_swd_run_queue(void) } } - queued_retval = cmsis_dap_usb_xfer(cmsis_dap_handle, idx); + queued_retval = cmsis_dap_usb_write(dap, idx); if (queued_retval != ERROR_OK) goto skip; - idx = 2; - uint8_t ack = buffer[idx] & 0x07; - if (ack != SWD_ACK_OK || (buffer[idx] & 0x08)) { - LOG_DEBUG("SWD ack not OK: %d %s", buffer[idx-1], + pending_fifo_put_idx = (pending_fifo_put_idx + 1) % dap->packet_count; + pending_fifo_block_count++; + if (pending_fifo_block_count > dap->packet_count) + LOG_ERROR("too much pending writes %d", pending_fifo_block_count); + + return; + +skip: + block->transfer_count = 0; +} + +static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) +{ + uint8_t *buffer = dap->packet_buffer; + struct pending_request_block *block = &pending_fifo[pending_fifo_get_idx]; + + if (pending_fifo_block_count == 0) + LOG_ERROR("no pending write"); + + /* get reply */ + int retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms); + if (retval == 0 && timeout_ms < USB_TIMEOUT) + return; + + if (retval == -1 || retval == 0) { + LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle)); + queued_retval = ERROR_FAIL; + goto skip; + } + + if (buffer[2] & 0x08) { + LOG_DEBUG("CMSIS-DAP Protocol Error @ %d (wrong parity)", buffer[1]); + queued_retval = ERROR_FAIL; + goto skip; + } + uint8_t ack = buffer[2] & 0x07; + if (ack != SWD_ACK_OK) { + LOG_DEBUG("SWD ack not OK @ %d %s", buffer[1], ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK"); queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL; goto skip; } - idx++; - if (pending_transfer_count != buffer[1]) + if (block->transfer_count != buffer[1]) LOG_ERROR("CMSIS-DAP transfer count mismatch: expected %d, got %d", - pending_transfer_count, buffer[1]); + block->transfer_count, buffer[1]); + LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %d", buffer[1], pending_fifo_get_idx); + size_t idx = 3; for (int i = 0; i < buffer[1]; i++) { - if (pending_transfers[i].cmd & SWD_CMD_RnW) { + struct pending_transfer_result *transfer = &(block->transfers[i]); + if (transfer->cmd & SWD_CMD_RnW) { static uint32_t last_read; uint32_t data = le_to_h_u32(&buffer[idx]); uint32_t tmp = data; @@ -679,19 +752,36 @@ static int cmsis_dap_swd_run_queue(void) LOG_DEBUG_IO("Read result: %"PRIx32, data); /* Imitate posted AP reads */ - if ((pending_transfers[i].cmd & SWD_CMD_APnDP) || - ((pending_transfers[i].cmd & SWD_CMD_A32) >> 1 == DP_RDBUFF)) { + if ((transfer->cmd & SWD_CMD_APnDP) || + ((transfer->cmd & SWD_CMD_A32) >> 1 == DP_RDBUFF)) { tmp = last_read; last_read = data; } - if (pending_transfers[i].buffer) - *(uint32_t *)pending_transfers[i].buffer = tmp; + if (transfer->buffer) + *(uint32_t *)(transfer->buffer) = tmp; } } skip: - pending_transfer_count = 0; + block->transfer_count = 0; + pending_fifo_get_idx = (pending_fifo_get_idx + 1) % dap->packet_count; + pending_fifo_block_count--; +} + +static int cmsis_dap_swd_run_queue(void) +{ + if (pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, 0); + + cmsis_dap_swd_write_from_queue(cmsis_dap_handle); + + while (pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, USB_TIMEOUT); + + pending_fifo_put_idx = 0; + pending_fifo_get_idx = 0; + int retval = queued_retval; queued_retval = ERROR_OK; @@ -700,21 +790,29 @@ skip: static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) { - if (pending_transfer_count == pending_queue_len) { + if (pending_fifo[pending_fifo_put_idx].transfer_count == pending_queue_len) { + if (pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, 0); + /* Not enough room in the queue. Run the queue. */ - queued_retval = cmsis_dap_swd_run_queue(); + cmsis_dap_swd_write_from_queue(cmsis_dap_handle); + + if (pending_fifo_block_count >= cmsis_dap_handle->packet_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, USB_TIMEOUT); } if (queued_retval != ERROR_OK) return; - pending_transfers[pending_transfer_count].data = data; - pending_transfers[pending_transfer_count].cmd = cmd; + struct pending_request_block *block = &pending_fifo[pending_fifo_put_idx]; + struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]); + transfer->data = data; + transfer->cmd = cmd; if (cmd & SWD_CMD_RnW) { /* Queue a read transaction */ - pending_transfers[pending_transfer_count].buffer = dst; + transfer->buffer = dst; } - pending_transfer_count++; + block->transfer_count++; } static void cmsis_dap_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) @@ -895,9 +993,7 @@ static int cmsis_dap_init(void) retval = cmsis_dap_swd_open(); if (retval != ERROR_OK) return retval; - } - - if (cmsis_dap_handle == NULL) { + } else { /* Connect in JTAG mode */ if (!(cmsis_dap_handle->caps & INFO_CAPS_JTAG)) { LOG_ERROR("CMSIS-DAP: JTAG not supported"); @@ -911,6 +1007,11 @@ static int cmsis_dap_init(void) LOG_INFO("CMSIS-DAP: Interface Initialised (JTAG)"); } + /* Be conservative and supress submiting multiple HID requests + * until we get packet count info from the adaptor */ + cmsis_dap_handle->packet_count = 1; + pending_queue_len = 12; + /* INFO_ID_PKT_SZ - short */ retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data); if (retval != ERROR_OK) @@ -923,11 +1024,6 @@ static int cmsis_dap_init(void) * write. For bulk read sequences just 4 bytes are * needed per transfer, so this is suboptimal. */ pending_queue_len = (pkt_sz - 4) / 5; - pending_transfers = malloc(pending_queue_len * sizeof(*pending_transfers)); - if (!pending_transfers) { - LOG_ERROR("Unable to allocate memory for CMSIS-DAP queue"); - return ERROR_FAIL; - } if (cmsis_dap_handle->packet_size != pkt_sz + 1) { /* reallocate buffer */ @@ -949,11 +1045,23 @@ static int cmsis_dap_init(void) return retval; if (data[0] == 1) { /* byte */ - uint16_t pkt_cnt = data[1]; - cmsis_dap_handle->packet_count = pkt_cnt; - LOG_DEBUG("CMSIS-DAP: Packet Count = %" PRId16, pkt_cnt); + int pkt_cnt = data[1]; + if (pkt_cnt > 1) + cmsis_dap_handle->packet_count = MIN(MAX_PENDING_REQUESTS, pkt_cnt); + + LOG_DEBUG("CMSIS-DAP: Packet Count = %d", pkt_cnt); } + LOG_DEBUG("Allocating FIFO for %d pending HID requests", cmsis_dap_handle->packet_count); + for (int i = 0; i < cmsis_dap_handle->packet_count; i++) { + pending_fifo[i].transfers = malloc(pending_queue_len * sizeof(struct pending_transfer_result)); + if (!pending_fifo[i].transfers) { + LOG_ERROR("Unable to allocate memory for CMSIS-DAP queue"); + return ERROR_FAIL; + } + } + + retval = cmsis_dap_get_status(); if (retval != ERROR_OK) return ERROR_FAIL; diff --git a/src/jtag/drivers/driver.c b/src/jtag/drivers/driver.c index daf7cd426..4923f96eb 100644 --- a/src/jtag/drivers/driver.c +++ b/src/jtag/drivers/driver.c @@ -55,18 +55,6 @@ static void jtag_callback_queue_reset(void) jtag_callback_queue_tail = NULL; } -/** - * Copy a struct scan_field for insertion into the queue. - * - * This allocates a new copy of out_value using cmd_queue_alloc. - */ -static void cmd_queue_scan_field_clone(struct scan_field *dst, const struct scan_field *src) -{ - dst->num_bits = src->num_bits; - dst->out_value = buf_cpy(src->out_value, cmd_queue_alloc(DIV_ROUND_UP(src->num_bits, 8)), src->num_bits); - dst->in_value = src->in_value; -} - /** * see jtag_add_ir_scan() * @@ -101,7 +89,7 @@ int interface_jtag_add_ir_scan(struct jtag_tap *active, /* if TAP is listed in input fields, copy the value */ tap->bypass = 0; - cmd_queue_scan_field_clone(field, in_fields); + jtag_scan_field_clone(field, in_fields); } else { /* if a TAP isn't listed in input fields, set it to BYPASS */ @@ -168,7 +156,7 @@ int interface_jtag_add_dr_scan(struct jtag_tap *active, int in_num_fields, #endif /* NDEBUG */ for (int j = 0; j < in_num_fields; j++) { - cmd_queue_scan_field_clone(field, in_fields + j); + jtag_scan_field_clone(field, in_fields + j); field++; } diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 6c1e50492..7deb1c493 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -506,11 +506,11 @@ static void ftdi_execute_scan(struct jtag_command *cmd) while (cmd->cmd.scan->num_fields > 0 && cmd->cmd.scan->fields[cmd->cmd.scan->num_fields - 1].num_bits == 0) { cmd->cmd.scan->num_fields--; - LOG_DEBUG("discarding trailing empty field"); + DEBUG_JTAG_IO("discarding trailing empty field"); } if (cmd->cmd.scan->num_fields == 0) { - LOG_DEBUG("empty scan, doing nothing"); + DEBUG_JTAG_IO("empty scan, doing nothing"); return; } diff --git a/src/jtag/drivers/libusb1_common.c b/src/jtag/drivers/libusb1_common.c index 89f809271..a1db86f4b 100644 --- a/src/jtag/drivers/libusb1_common.c +++ b/src/jtag/drivers/libusb1_common.c @@ -71,11 +71,11 @@ int jtag_libusb_open(const uint16_t vids[], const uint16_t pids[], struct jtag_libusb_device_handle **out) { int cnt, idx, errCode; - int retval = -ENODEV; + int retval = ERROR_FAIL; struct jtag_libusb_device_handle *libusb_handle = NULL; if (libusb_init(&jtag_libusb_context) < 0) - return -ENODEV; + return ERROR_FAIL; cnt = libusb_get_device_list(jtag_libusb_context, &devs); @@ -105,7 +105,7 @@ int jtag_libusb_open(const uint16_t vids[], const uint16_t pids[], /* Success. */ *out = libusb_handle; - retval = 0; + retval = ERROR_OK; break; } if (cnt >= 0) diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c index 6c70be6ad..43e924e03 100644 --- a/src/jtag/drivers/mpsse.c +++ b/src/jtag/drivers/mpsse.c @@ -121,7 +121,7 @@ static bool device_location_equal(libusb_device *device, const char *location) LOG_DEBUG("device path has %i steps", path_len); - ptr = strtok(loc, ":"); + ptr = strtok(loc, "-:"); if (ptr == NULL) { LOG_DEBUG("no ':' in path"); goto done; @@ -133,7 +133,7 @@ static bool device_location_equal(libusb_device *device, const char *location) path_step = 0; while (path_step < 7) { - ptr = strtok(NULL, ","); + ptr = strtok(NULL, ".,"); if (ptr == NULL) { LOG_DEBUG("no more tokens in path at step %i", path_step); break; diff --git a/src/rtos/ChibiOS.c b/src/rtos/ChibiOS.c index 312fc4d99..8839acc9a 100644 --- a/src/rtos/ChibiOS.c +++ b/src/rtos/ChibiOS.c @@ -105,7 +105,8 @@ static struct ChibiOS_params ChibiOS_params_list[] = { static bool ChibiOS_detect_rtos(struct target *target); static int ChibiOS_create(struct target *target); static int ChibiOS_update_threads(struct rtos *rtos); -static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type ChibiOS_rtos = { @@ -464,13 +465,13 @@ static int ChibiOS_update_threads(struct rtos *rtos) return 0; } -static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct ChibiOS_params *param; uint32_t stack_ptr = 0; - *hex_reg_list = NULL; if ((rtos == NULL) || (thread_id == 0) || (rtos->rtos_specific_params == NULL)) return -1; @@ -495,7 +496,7 @@ static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, cha return retval; } - return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs); } static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c index 6027d6739..9d89974cc 100644 --- a/src/rtos/FreeRTOS.c +++ b/src/rtos/FreeRTOS.c @@ -102,7 +102,8 @@ static const struct FreeRTOS_params FreeRTOS_params_list[] = { static bool FreeRTOS_detect_rtos(struct target *target); static int FreeRTOS_create(struct target *target); static int FreeRTOS_update_threads(struct rtos *rtos); -static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); static int FreeRTOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type FreeRTOS_rtos = { @@ -395,13 +396,13 @@ static int FreeRTOS_update_threads(struct rtos *rtos) return 0; } -static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct FreeRTOS_params *param; int64_t stack_ptr = 0; - *hex_reg_list = NULL; if (rtos == NULL) return -1; @@ -460,11 +461,11 @@ static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, ch return retval; } if ((LR_svc & 0x10) == 0) - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs); else - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs); } else - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs); } static int FreeRTOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/ThreadX.c b/src/rtos/ThreadX.c index b7dbe6d11..9605533aa 100644 --- a/src/rtos/ThreadX.c +++ b/src/rtos/ThreadX.c @@ -38,7 +38,7 @@ static int is_thread_id_valid_arm926ejs(const struct rtos *rtos, int64_t thread_ static bool ThreadX_detect_rtos(struct target *target); static int ThreadX_create(struct target *target); static int ThreadX_update_threads(struct rtos *rtos); -static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs); static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); @@ -69,45 +69,45 @@ static const struct ThreadX_thread_state ThreadX_thread_states[] = { #define ARM926EJS_REGISTERS_SIZE_SOLICITED (11 * 4) static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_solicited[] = { - { -1, 32 }, /* r0 */ - { -1, 32 }, /* r1 */ - { -1, 32 }, /* r2 */ - { -1, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0C, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1C, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { -1, 32 }, /* r12 */ - { -2, 32 }, /* sp (r13) */ - { 0x28, 32 }, /* lr (r14) */ - { -1, 32 }, /* pc (r15) */ - /*{ -1, 32 },*/ /* lr (r14) */ - /*{ 0x28, 32 },*/ /* pc (r15) */ - { 0x04, 32 }, /* xPSR */ + { 0, -1, 32 }, /* r0 */ + { 1, -1, 32 }, /* r1 */ + { 2, -1, 32 }, /* r2 */ + { 3, -1, 32 }, /* r3 */ + { 4, 0x08, 32 }, /* r4 */ + { 5, 0x0C, 32 }, /* r5 */ + { 6, 0x10, 32 }, /* r6 */ + { 7, 0x14, 32 }, /* r7 */ + { 8, 0x18, 32 }, /* r8 */ + { 9, 0x1C, 32 }, /* r9 */ + { 10, 0x20, 32 }, /* r10 */ + { 11, 0x24, 32 }, /* r11 */ + { 12, -1, 32 }, /* r12 */ + { 13, -2, 32 }, /* sp (r13) */ + { 14, 0x28, 32 }, /* lr (r14) */ + { 15, -1, 32 }, /* pc (r15) */ + /*{ 16, -1, 32 },*/ /* lr (r14) */ + /*{ 17, 0x28, 32 },*/ /* pc (r15) */ + { 16, 0x04, 32 }, /* xPSR */ }; #define ARM926EJS_REGISTERS_SIZE_INTERRUPT (17 * 4) static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_interrupt[] = { - { 0x08, 32 }, /* r0 */ - { 0x0C, 32 }, /* r1 */ - { 0x10, 32 }, /* r2 */ - { 0x14, 32 }, /* r3 */ - { 0x18, 32 }, /* r4 */ - { 0x1C, 32 }, /* r5 */ - { 0x20, 32 }, /* r6 */ - { 0x24, 32 }, /* r7 */ - { 0x28, 32 }, /* r8 */ - { 0x2C, 32 }, /* r9 */ - { 0x30, 32 }, /* r10 */ - { 0x34, 32 }, /* r11 */ - { 0x38, 32 }, /* r12 */ - { -2, 32 }, /* sp (r13) */ - { 0x3C, 32 }, /* lr (r14) */ - { 0x40, 32 }, /* pc (r15) */ - { 0x04, 32 }, /* xPSR */ + { 0, 0x08, 32 }, /* r0 */ + { 1, 0x0C, 32 }, /* r1 */ + { 2, 0x10, 32 }, /* r2 */ + { 3, 0x14, 32 }, /* r3 */ + { 4, 0x18, 32 }, /* r4 */ + { 5, 0x1C, 32 }, /* r5 */ + { 6, 0x20, 32 }, /* r6 */ + { 7, 0x24, 32 }, /* r7 */ + { 8, 0x28, 32 }, /* r8 */ + { 9, 0x2C, 32 }, /* r9 */ + { 10, 0x30, 32 }, /* r10 */ + { 11, 0x34, 32 }, /* r11 */ + { 12, 0x38, 32 }, /* r12 */ + { 13, -2, 32 }, /* sp (r13) */ + { 14, 0x3C, 32 }, /* lr (r14) */ + { 15, 0x40, 32 }, /* pc (r15) */ + { 16, 0x04, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = { @@ -433,13 +433,12 @@ static int ThreadX_update_threads(struct rtos *rtos) return 0; } -static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct ThreadX_params *param; - *hex_reg_list = NULL; - if (rtos == NULL) return -1; @@ -477,7 +476,7 @@ static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, cha return -6; } - return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, reg_list, num_regs); } static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/eCos.c b/src/rtos/eCos.c index 9e41030ee..e6b70730b 100644 --- a/src/rtos/eCos.c +++ b/src/rtos/eCos.c @@ -30,7 +30,7 @@ static bool eCos_detect_rtos(struct target *target); static int eCos_create(struct target *target); static int eCos_update_threads(struct rtos *rtos); -static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs); static int eCos_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct eCos_thread_state { @@ -285,13 +285,12 @@ static int eCos_update_threads(struct rtos *rtos) return 0; } -static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct eCos_params *param; - *hex_reg_list = NULL; - if (rtos == NULL) return -1; @@ -345,7 +344,8 @@ static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char * return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, - hex_reg_list); + reg_list, + num_regs); } return -1; diff --git a/src/rtos/embKernel.c b/src/rtos/embKernel.c index a40c86c3f..8a307f198 100644 --- a/src/rtos/embKernel.c +++ b/src/rtos/embKernel.c @@ -34,7 +34,8 @@ static bool embKernel_detect_rtos(struct target *target); static int embKernel_create(struct target *target); static int embKernel_update_threads(struct rtos *rtos); -static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); static int embKernel_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type embKernel_rtos = { @@ -300,13 +301,13 @@ static int embKernel_update_threads(struct rtos *rtos) return 0; } -static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { int retval; const struct embKernel_params *param; int64_t stack_ptr = 0; - *hex_reg_list = NULL; if (rtos == NULL) return -1; @@ -326,7 +327,7 @@ static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, c return retval; } - return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list); + return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs); } static int embKernel_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/linux.c b/src/rtos/linux.c index 3b2a2b0c3..74172b70a 100644 --- a/src/rtos/linux.c +++ b/src/rtos/linux.c @@ -144,16 +144,6 @@ static int linux_read_memory(struct target *target, return ERROR_OK; } -static char *reg_converter(char *buffer, void *reg, int size) -{ - int i; - - for (i = 0; i < size; i++) - buffer += sprintf(buffer, "%02x", ((uint8_t *) reg)[i]); - - return buffer; -} - int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer) { @@ -174,15 +164,13 @@ uint32_t get_buffer(struct target *target, const uint8_t *buffer) } static int linux_os_thread_reg_list(struct rtos *rtos, - int64_t thread_id, char **hex_reg_list) + int64_t thread_id, struct rtos_reg **reg_list, int *num_regs) { struct target *target = rtos->target; struct linux_os *linux_os = (struct linux_os *) target->rtos->rtos_specific_params; - int i = 0; struct current_thread *tmp = linux_os->current_threads; struct current_thread *next; - char *hex_string; int found = 0; int retval; /* check if a current thread is requested */ @@ -195,117 +183,52 @@ static int linux_os_thread_reg_list(struct rtos *rtos, next = next->next; } while ((found == 0) && (next != tmp) && (next != NULL)); - if (found == 1) { - /* search target to perfom the access */ - struct reg **reg_list; - int reg_list_size, reg_packet_size = 0; - struct target_list *head; - head = target->head; - found = 0; - do { - if (head->target->coreid == next->core_id) { - - target = head->target; - found = 1; - } else - head = head->next; - - } while ((head != (struct target_list *)NULL) && (found == 0)); - - if (found == 0) { - LOG_ERROR - ( - "current thread %" PRIx64 ": no target to perform access of core id %" PRIx32, - thread_id, - next->core_id); - return ERROR_FAIL; - } - - /*LOG_INFO("thread %lx current on core %x",thread_id, - * target->coreid);*/ - retval = - target_get_gdb_reg_list(target, ®_list, ®_list_size, - REG_CLASS_GENERAL); - - if (retval != ERROR_OK) - return retval; - - for (i = 0; i < reg_list_size; i++) - reg_packet_size += reg_list[i]->size; - - assert(reg_packet_size > 0); - - *hex_reg_list = malloc(DIV_ROUND_UP(reg_packet_size, 8) * 2); - - hex_string = *hex_reg_list; - - for (i = 0; i < reg_list_size; i++) { - if (!reg_list[i]->valid) - reg_list[i]->type->get(reg_list[i]); - - hex_string = reg_converter(hex_string, - reg_list[i]->value, - (reg_list[i]->size) / 8); - } - - free(reg_list); - - } else { - struct threads *temp = linux_os->thread_list; - *hex_reg_list = calloc(1, 500 * sizeof(char)); - hex_string = *hex_reg_list; - - for (i = 0; i < 16; i++) - hex_string += sprintf(hex_string, "%02x", 0); - - while ((temp != NULL) && - (temp->threadid != target->rtos->current_threadid)) - temp = temp->next; - - if (temp != NULL) { - if (temp->context == NULL) - temp->context = cpu_context_read(target, - temp-> - base_addr, - &temp-> - thread_info_addr); - - hex_string = - reg_converter(hex_string, &temp->context->R4, 4); - hex_string = - reg_converter(hex_string, &temp->context->R5, 4); - hex_string = - reg_converter(hex_string, &temp->context->R6, 4); - hex_string = - reg_converter(hex_string, &temp->context->R7, 4); - hex_string = - reg_converter(hex_string, &temp->context->R8, 4); - hex_string = - reg_converter(hex_string, &temp->context->R9, 4); - - for (i = 0; i < 4; i++) /*R10 = 0x0 */ - hex_string += sprintf(hex_string, "%02x", 0); - - hex_string = - reg_converter(hex_string, &temp->context->FP, 4); - hex_string = - reg_converter(hex_string, &temp->context->IP, 4); - hex_string = - reg_converter(hex_string, &temp->context->SP, 4); - - for (i = 0; i < 4; i++) - hex_string += sprintf(hex_string, "%02x", 0); - - hex_string = - reg_converter(hex_string, &temp->context->PC, 4); - - for (i = 0; i < 100; i++) /*100 */ - hex_string += sprintf(hex_string, "%02x", 0); - - uint32_t cpsr = 0x00000000; - reg_converter(hex_string, &cpsr, 4); - } + if (found == 0) { + LOG_ERROR("could not find thread: %" PRIx64, thread_id); + return ERROR_FAIL; } + + /* search target to perfom the access */ + struct reg **gdb_reg_list; + struct target_list *head; + head = target->head; + found = 0; + do { + if (head->target->coreid == next->core_id) { + + target = head->target; + found = 1; + } else + head = head->next; + + } while ((head != (struct target_list *)NULL) && (found == 0)); + + if (found == 0) { + LOG_ERROR + ( + "current thread %" PRIx64 ": no target to perform access of core id %" PRIx32, + thread_id, + next->core_id); + return ERROR_FAIL; + } + + /*LOG_INFO("thread %lx current on core %x",thread_id, target->coreid);*/ + retval = target_get_gdb_reg_list(target, &gdb_reg_list, num_regs, REG_CLASS_GENERAL); + if (retval != ERROR_OK) + return retval; + + *reg_list = calloc(*num_regs, sizeof(struct rtos_reg)); + + for (int i = 0; i < *num_regs; ++i) { + if (!gdb_reg_list[i]->valid) + gdb_reg_list[i]->type->get(gdb_reg_list[i]); + + (*reg_list)[i].number = gdb_reg_list[i]->number; + (*reg_list)[i].size = gdb_reg_list[i]->size; + + buf_cpy(gdb_reg_list[i]->value, (*reg_list)[i].value, (*reg_list)[i].size); + } + return ERROR_OK; } @@ -1134,7 +1057,7 @@ int linux_gdb_thread_packet(struct target *target, if (retval != ERROR_OK) return ERROR_TARGET_FAILURE; - char *out_str = calloc(1, 350 * sizeof(int64_t)); + char *out_str = calloc(MAX_THREADS * 17 + 10, 1); char *tmp_str = out_str; tmp_str += sprintf(tmp_str, "m"); struct threads *temp = linux_os->thread_list; @@ -1171,7 +1094,7 @@ int linux_gdb_thread_update(struct target *target, if (found == 1) { /*LOG_INFO("INTO GDB THREAD UPDATE FOUNDING START TASK");*/ - char *out_strr = calloc(1, 350 * sizeof(int64_t)); + char *out_strr = calloc(MAX_THREADS * 17 + 10, 1); char *tmp_strr = out_strr; tmp_strr += sprintf(tmp_strr, "m"); /*LOG_INFO("CHAR MALLOC & M DONE");*/ diff --git a/src/rtos/mqx.c b/src/rtos/mqx.c index 531b03b57..6646ad4de 100644 --- a/src/rtos/mqx.c +++ b/src/rtos/mqx.c @@ -456,7 +456,8 @@ static int mqx_update_threads( static int mqx_get_thread_reg_list( struct rtos *rtos, int64_t thread_id, - char **hex_reg_list + struct rtos_reg **reg_list, + int *num_regs ) { int64_t stack_ptr = 0; @@ -465,7 +466,6 @@ static int mqx_get_thread_reg_list( uint32_t task_queue_size = 0; uint32_t kernel_data_addr = 0; - *hex_reg_list = NULL; if (thread_id == 0) { LOG_ERROR("MQX RTOS - invalid threadid: 0x%X", (int)thread_id); return ERROR_FAIL; @@ -535,7 +535,7 @@ static int mqx_get_thread_reg_list( return ERROR_FAIL; } return rtos_generic_stack_read( - rtos->target, ((struct mqx_params *)rtos->rtos_specific_params)->stacking_info, stack_ptr, hex_reg_list + rtos->target, ((struct mqx_params *)rtos->rtos_specific_params)->stacking_info, stack_ptr, reg_list, num_regs ); } diff --git a/src/rtos/nuttx.c b/src/rtos/nuttx.c index 284b968cc..61fd9aa6c 100644 --- a/src/rtos/nuttx.c +++ b/src/rtos/nuttx.c @@ -98,23 +98,23 @@ static char *task_state_str[] = { /* see arch/arm/include/armv7-m/irq_cmnvector.h */ static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = { - { 0x28, 32 }, /* r0 */ - { 0x2c, 32 }, /* r1 */ - { 0x30, 32 }, /* r2 */ - { 0x34, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0c, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1c, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { 0x38, 32 }, /* r12 */ - { 0, 32 }, /* sp */ - { 0x3c, 32 }, /* lr */ - { 0x40, 32 }, /* pc */ - { 0x44, 32 }, /* xPSR */ + { ARMV7M_R0, 0x28, 32 }, /* r0 */ + { ARMV7M_R1, 0x2c, 32 }, /* r1 */ + { ARMV7M_R2, 0x30, 32 }, /* r2 */ + { ARMV7M_R3, 0x34, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0c, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1c, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x38, 32 }, /* r12 */ + { ARMV7M_R13, 0, 32 }, /* sp */ + { ARMV7M_R14, 0x3c, 32 }, /* lr */ + { ARMV7M_PC, 0x40, 32 }, /* pc */ + { ARMV7M_xPSR, 0x44, 32 }, /* xPSR */ }; @@ -127,23 +127,23 @@ static const struct rtos_register_stacking nuttx_stacking_cortex_m = { }; static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = { - { 0x6c, 32 }, /* r0 */ - { 0x70, 32 }, /* r1 */ - { 0x74, 32 }, /* r2 */ - { 0x78, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0c, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1c, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { 0x7c, 32 }, /* r12 */ - { 0, 32 }, /* sp */ - { 0x80, 32 }, /* lr */ - { 0x84, 32 }, /* pc */ - { 0x88, 32 }, /* xPSR */ + { ARMV7M_R0, 0x6c, 32 }, /* r0 */ + { ARMV7M_R1, 0x70, 32 }, /* r1 */ + { ARMV7M_R2, 0x74, 32 }, /* r2 */ + { ARMV7M_R3, 0x78, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0c, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1c, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x7c, 32 }, /* r12 */ + { ARMV7M_R13, 0, 32 }, /* sp */ + { ARMV7M_R14, 0x80, 32 }, /* lr */ + { ARMV7M_PC, 0x84, 32 }, /* pc */ + { ARMV7M_xPSR, 0x88, 32 }, /* xPSR */ }; static const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = { @@ -344,11 +344,10 @@ static int nuttx_update_threads(struct rtos *rtos) * thread_id = tcb address; */ static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, - char **hex_reg_list) { + struct rtos_reg **reg_list, int *num_regs) +{ int retval; - *hex_reg_list = NULL; - /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ bool cm4_fpu_enabled = false; struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); @@ -378,7 +377,7 @@ static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, stacking = &nuttx_stacking_cortex_m; return rtos_generic_stack_read(rtos->target, stacking, - (uint32_t)thread_id + xcpreg_offset, hex_reg_list); + (uint32_t)thread_id + xcpreg_offset, reg_list, num_regs); } static int nuttx_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c index 36500218e..d5cd4a1bf 100644 --- a/src/rtos/riscv_debug.c +++ b/src/rtos/riscv_debug.c @@ -270,39 +270,35 @@ static int riscv_gdb_v_packet(struct connection *connection, const char *packet, return GDB_THREAD_PACKET_NOT_CONSUMED; } -static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1)); - size_t n_regs = 32; - size_t xlen = 64; - size_t reg_chars = xlen / 8 * 2; + /* We return just the GPRs here. */ - ssize_t hex_reg_list_length = n_regs * reg_chars + 2; - *hex_reg_list = malloc(hex_reg_list_length); - *hex_reg_list[0] = '\0'; - char *p = hex_reg_list[0]; - for (size_t i = 0; i < n_regs; ++i) { - assert(p - hex_reg_list[0] > 3); - if (riscv_has_register(rtos->target, thread_id, i)) { - uint64_t reg_value; - int result = riscv_get_register_on_hart(rtos->target, ®_value, - thread_id - 1, i); - if (result != ERROR_OK) - return JIM_ERR; + *num_regs = 32; + int xlen = riscv_xlen_of_hart(rtos->target, thread_id - 1); - for (size_t byte = 0; byte < xlen / 8; ++byte) { - uint8_t reg_byte = reg_value >> (byte * 8); - p += snprintf(p, 3, "%02x", reg_byte); - } - } else { - for (size_t byte = 0; byte < xlen / 8; ++byte) { - strcpy(p, "xx"); - p += 2; - } - } + *reg_list = calloc(*num_regs, sizeof(struct rtos_reg)); + *reg_list = 0; + for (int i = 0; i < *num_regs; ++i) { + uint64_t reg_value; + if (riscv_get_register_on_hart(rtos->target, ®_value, thread_id - 1, + i) != ERROR_OK) + return JIM_ERR; + + (*reg_list)[i].number = i; + (*reg_list)[i].size = xlen; + (*reg_list)[i].value[0] = reg_value & 0xff; + (*reg_list)[i].value[1] = (reg_value >> 8) & 0xff; + (*reg_list)[i].value[2] = (reg_value >> 16) & 0xff; + (*reg_list)[i].value[3] = (reg_value >> 24) & 0xff; + (*reg_list)[i].value[4] = (reg_value >> 32) & 0xff; + (*reg_list)[i].value[5] = (reg_value >> 40) & 0xff; + (*reg_list)[i].value[6] = (reg_value >> 48) & 0xff; + (*reg_list)[i].value[7] = (reg_value >> 56) & 0xff; } - LOG_DEBUG("%s", *hex_reg_list); return JIM_OK; } diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index bd9da0d2a..9b05ab5c4 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -438,6 +438,68 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa return GDB_THREAD_PACKET_NOT_CONSUMED; } +static int rtos_put_gdb_reg_list(struct connection *connection, + struct rtos_reg *reg_list, int num_regs) +{ + size_t num_bytes = 1; /* NUL */ + for (int i = 0; i < num_regs; ++i) + num_bytes += DIV_ROUND_UP(reg_list[i].size, 8) * 2; + + char *hex = malloc(num_bytes); + char *hex_p = hex; + + for (int i = 0; i < num_regs; ++i) { + size_t count = DIV_ROUND_UP(reg_list[i].size, 8); + size_t n = hexify(hex_p, reg_list[i].value, count, num_bytes); + hex_p += n; + num_bytes -= n; + } + + gdb_put_packet(connection, hex, strlen(hex)); + free(hex); + + return ERROR_OK; +} + +int rtos_get_gdb_reg(struct connection *connection, int reg_num) +{ + struct target *target = get_target_from_connection(connection); + int64_t current_threadid = target->rtos->current_threadid; + if ((target->rtos != NULL) && (current_threadid != -1) && + (current_threadid != 0) && + ((current_threadid != target->rtos->current_thread) || + (target->smp))) { /* in smp several current thread are possible */ + struct rtos_reg *reg_list; + int num_regs; + + LOG_DEBUG("RTOS: getting register %d for thread 0x%" PRIx64 + ", target->rtos->current_thread=0x%" PRIx64 "\r\n", + reg_num, + current_threadid, + target->rtos->current_thread); + + int retval = target->rtos->type->get_thread_reg_list(target->rtos, + current_threadid, + ®_list, + &num_regs); + if (retval != ERROR_OK) { + LOG_ERROR("RTOS: failed to get register list"); + return retval; + } + + for (int i = 0; i < num_regs; ++i) { + if (reg_list[i].number == (uint32_t)reg_num) { + rtos_put_gdb_reg_list(connection, reg_list + i, 1); + free(reg_list); + return ERROR_OK; + } + } + + free(reg_list); + } + return ERROR_FAIL; +} + int rtos_get_gdb_reg_list(struct connection *connection) { struct target *target = get_target_from_connection(connection); @@ -446,7 +508,8 @@ int rtos_get_gdb_reg_list(struct connection *connection) (current_threadid != 0) && ((current_threadid != target->rtos->current_thread) || (target->smp))) { /* in smp several current thread are possible */ - char *hex_reg_list; + struct rtos_reg *reg_list; + int num_regs; LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64 ", target->rtos->current_thread=0x%" PRIx64 "\r\n", @@ -455,17 +518,17 @@ int rtos_get_gdb_reg_list(struct connection *connection) int retval = target->rtos->type->get_thread_reg_list(target->rtos, current_threadid, - &hex_reg_list); + ®_list, + &num_regs); if (retval != ERROR_OK) { LOG_ERROR("RTOS: failed to get register list"); return retval; } - if (hex_reg_list != NULL) { - gdb_put_packet(connection, hex_reg_list, strlen(hex_reg_list)); - free(hex_reg_list); - return ERROR_OK; - } + rtos_put_gdb_reg_list(connection, reg_list, num_regs); + free(reg_list); + + return ERROR_OK; } return ERROR_FAIL; } @@ -473,12 +536,9 @@ int rtos_get_gdb_reg_list(struct connection *connection) int rtos_generic_stack_read(struct target *target, const struct rtos_register_stacking *stacking, int64_t stack_ptr, - char **hex_reg_list) + struct rtos_reg **reg_list, + int *num_regs) { - int list_size = 0; - char *tmp_str_ptr; - int64_t new_stack_ptr; - int i; int retval; if (stack_ptr == 0) { @@ -505,10 +565,8 @@ int rtos_generic_stack_read(struct target *target, LOG_OUTPUT("%02X", stack_data[i]); LOG_OUTPUT("\r\n"); #endif - for (i = 0; i < stacking->num_output_registers; i++) - list_size += stacking->register_offsets[i].width_bits/8; - *hex_reg_list = malloc(list_size*2 + 1); - tmp_str_ptr = *hex_reg_list; + + int64_t new_stack_ptr; if (stacking->calculate_process_stack != NULL) { new_stack_ptr = stacking->calculate_process_stack(target, stack_data, stacking, stack_ptr); @@ -516,19 +574,21 @@ int rtos_generic_stack_read(struct target *target, new_stack_ptr = stack_ptr - stacking->stack_growth_direction * stacking->stack_registers_size; } - for (i = 0; i < stacking->num_output_registers; i++) { - int j; - for (j = 0; j < stacking->register_offsets[i].width_bits/8; j++) { - if (stacking->register_offsets[i].offset == -1) - tmp_str_ptr += sprintf(tmp_str_ptr, "%02x", 0); - else if (stacking->register_offsets[i].offset == -2) - tmp_str_ptr += sprintf(tmp_str_ptr, "%02x", - ((uint8_t *)&new_stack_ptr)[j]); - else - tmp_str_ptr += sprintf(tmp_str_ptr, "%02x", - stack_data[stacking->register_offsets[i].offset + j]); - } + + *reg_list = calloc(stacking->num_output_registers, sizeof(struct rtos_reg)); + *num_regs = stacking->num_output_registers; + + for (int i = 0; i < stacking->num_output_registers; ++i) { + (*reg_list)[i].number = stacking->register_offsets[i].number; + (*reg_list)[i].size = stacking->register_offsets[i].width_bits; + + int offset = stacking->register_offsets[i].offset; + if (offset == -2) + buf_cpy(&new_stack_ptr, (*reg_list)[i].value, (*reg_list)[i].size); + else if (offset != -1) + buf_cpy(stack_data + offset, (*reg_list)[i].value, (*reg_list)[i].size); } + free(stack_data); /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */ return ERROR_OK; diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h index c9ac1e555..22de33fe2 100644 --- a/src/rtos/rtos.h +++ b/src/rtos/rtos.h @@ -59,19 +59,27 @@ struct rtos { void *rtos_specific_params; }; +struct rtos_reg { + uint32_t number; + uint32_t size; + uint8_t value[8]; +}; + struct rtos_type { const char *name; bool (*detect_rtos)(struct target *target); int (*create)(struct target *target); int (*smp_init)(struct target *target); int (*update_threads)(struct rtos *rtos); - int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); + int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); int (*get_symbol_list_to_lookup)(symbol_table_elem_t *symbol_list[]); int (*clean)(struct target *target); char * (*ps_command)(struct target *target); }; struct stack_register_offset { + unsigned short number; /* register number */ signed short offset; /* offset in bytes from stack head, or -1 to indicate * register is not stacked, or -2 to indicate this is the * stack pointer register */ @@ -100,9 +108,11 @@ int rtos_create(Jim_GetOptInfo *goi, struct target *target); int rtos_generic_stack_read(struct target *target, const struct rtos_register_stacking *stacking, int64_t stack_ptr, - char **hex_reg_list); + struct rtos_reg **reg_list, + int *num_regs); int rtos_try_next(struct target *target); int gdb_thread_packet(struct connection *connection, char const *packet, int packet_size); +int rtos_get_gdb_reg(struct connection *connection, int reg_num); int rtos_get_gdb_reg_list(struct connection *connection); int rtos_update_threads(struct target *target); void rtos_free_threadlist(struct rtos *rtos); diff --git a/src/rtos/rtos_chibios_stackings.c b/src/rtos/rtos_chibios_stackings.c index 3651c49a9..2887930bd 100644 --- a/src/rtos/rtos_chibios_stackings.c +++ b/src/rtos/rtos_chibios_stackings.c @@ -27,23 +27,23 @@ #include "target/armv7m.h" static const struct stack_register_offset rtos_chibios_arm_v7m_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { -1, 32 }, /* r0 */ - { -1, 32 }, /* r1 */ - { -1, 32 }, /* r2 */ - { -1, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { -1, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { -1, 32 }, /* lr */ - { 0x20, 32 }, /* pc */ - { -1, 32 }, /* xPSR */ + { ARMV7M_R0, -1, 32 }, /* r0 */ + { ARMV7M_R1, -1, 32 }, /* r1 */ + { ARMV7M_R2, -1, 32 }, /* r2 */ + { ARMV7M_R3, -1, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, -1, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, -1, 32 }, /* lr */ + { ARMV7M_PC, 0x20, 32 }, /* pc */ + { ARMV7M_xPSR, -1, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking = { @@ -55,23 +55,23 @@ const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking = { }; static const struct stack_register_offset rtos_chibios_arm_v7m_stack_offsets_w_fpu[ARMV7M_NUM_CORE_REGS] = { - { -1, 32 }, /* r0 */ - { -1, 32 }, /* r1 */ - { -1, 32 }, /* r2 */ - { -1, 32 }, /* r3 */ - { 0x40, 32 }, /* r4 */ - { 0x44, 32 }, /* r5 */ - { 0x48, 32 }, /* r6 */ - { 0x4c, 32 }, /* r7 */ - { 0x50, 32 }, /* r8 */ - { 0x54, 32 }, /* r9 */ - { 0x58, 32 }, /* r10 */ - { 0x5c, 32 }, /* r11 */ - { -1, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { -1, 32 }, /* lr */ - { 0x60, 32 }, /* pc */ - { -1, 32 }, /* xPSR */ + { ARMV7M_R0, -1, 32 }, /* r0 */ + { ARMV7M_R1, -1, 32 }, /* r1 */ + { ARMV7M_R2, -1, 32 }, /* r2 */ + { ARMV7M_R3, -1, 32 }, /* r3 */ + { ARMV7M_R4, 0x40, 32 }, /* r4 */ + { ARMV7M_R5, 0x44, 32 }, /* r5 */ + { ARMV7M_R6, 0x48, 32 }, /* r6 */ + { ARMV7M_R7, 0x4c, 32 }, /* r7 */ + { ARMV7M_R8, 0x50, 32 }, /* r8 */ + { ARMV7M_R9, 0x54, 32 }, /* r9 */ + { ARMV7M_R10, 0x58, 32 }, /* r10 */ + { ARMV7M_R11, 0x5c, 32 }, /* r11 */ + { ARMV7M_R12, -1, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, -1, 32 }, /* lr */ + { ARMV7M_PC, 0x60, 32 }, /* pc */ + { ARMV7M_xPSR, -1, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking_w_fpu = { diff --git a/src/rtos/rtos_ecos_stackings.c b/src/rtos/rtos_ecos_stackings.c index 43d97a605..ca98d9417 100644 --- a/src/rtos/rtos_ecos_stackings.c +++ b/src/rtos/rtos_ecos_stackings.c @@ -23,23 +23,23 @@ #include "target/armv7m.h" static const struct stack_register_offset rtos_eCos_Cortex_M3_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x0c, 32 }, /* r0 */ - { 0x10, 32 }, /* r1 */ - { 0x14, 32 }, /* r2 */ - { 0x18, 32 }, /* r3 */ - { 0x1c, 32 }, /* r4 */ - { 0x20, 32 }, /* r5 */ - { 0x24, 32 }, /* r6 */ - { 0x28, 32 }, /* r7 */ - { 0x2c, 32 }, /* r8 */ - { 0x30, 32 }, /* r9 */ - { 0x34, 32 }, /* r10 */ - { 0x38, 32 }, /* r11 */ - { 0x3c, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { -1, 32 }, /* lr */ - { 0x40, 32 }, /* pc */ - { -1, 32 }, /* xPSR */ + { ARMV7M_R0, 0x0c, 32 }, /* r0 */ + { ARMV7M_R1, 0x10, 32 }, /* r1 */ + { ARMV7M_R2, 0x14, 32 }, /* r2 */ + { ARMV7M_R3, 0x18, 32 }, /* r3 */ + { ARMV7M_R4, 0x1c, 32 }, /* r4 */ + { ARMV7M_R5, 0x20, 32 }, /* r5 */ + { ARMV7M_R6, 0x24, 32 }, /* r6 */ + { ARMV7M_R7, 0x28, 32 }, /* r7 */ + { ARMV7M_R8, 0x2c, 32 }, /* r8 */ + { ARMV7M_R9, 0x30, 32 }, /* r9 */ + { ARMV7M_R10, 0x34, 32 }, /* r10 */ + { ARMV7M_R11, 0x38, 32 }, /* r11 */ + { ARMV7M_R12, 0x3c, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, -1, 32 }, /* lr */ + { ARMV7M_PC, 0x40, 32 }, /* pc */ + { ARMV7M_xPSR, -1, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_eCos_Cortex_M3_stacking = { diff --git a/src/rtos/rtos_embkernel_stackings.c b/src/rtos/rtos_embkernel_stackings.c index 2a3062955..4ee79af93 100644 --- a/src/rtos/rtos_embkernel_stackings.c +++ b/src/rtos/rtos_embkernel_stackings.c @@ -25,23 +25,23 @@ #include "rtos_standard_stackings.h" static const struct stack_register_offset rtos_embkernel_Cortex_M_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x24, 32 }, /* r0 */ - { 0x28, 32 }, /* r1 */ - { 0x2c, 32 }, /* r2 */ - { 0x30, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x34, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x38, 32 }, /* lr */ - { 0x3c, 32 }, /* pc */ - { 0x40, 32 }, /* xPSR */ + { ARMV7M_R0, 0x24, 32 }, /* r0 */ + { ARMV7M_R1, 0x28, 32 }, /* r1 */ + { ARMV7M_R2, 0x2c, 32 }, /* r2 */ + { ARMV7M_R3, 0x30, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x34, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x38, 32 }, /* lr */ + { ARMV7M_PC, 0x3c, 32 }, /* pc */ + { ARMV7M_xPSR, 0x40, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_embkernel_Cortex_M_stacking = { diff --git a/src/rtos/rtos_mqx_stackings.c b/src/rtos/rtos_mqx_stackings.c index 5db2f8ec3..1a8bdfcc7 100644 --- a/src/rtos/rtos_mqx_stackings.c +++ b/src/rtos/rtos_mqx_stackings.c @@ -51,23 +51,23 @@ */ static const struct stack_register_offset rtos_mqx_arm_v7m_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x2C, 32 }, /* r0 */ - { 0x30, 32 }, /* r1 */ - { 0x34, 32 }, /* r2 */ - { 0x38, 32 }, /* r3 */ - { 0x08, 32 }, /* r4 */ - { 0x0C, 32 }, /* r5 */ - { 0x10, 32 }, /* r6 */ - { 0x14, 32 }, /* r7 */ - { 0x18, 32 }, /* r8 */ - { 0x1C, 32 }, /* r9 */ - { 0x20, 32 }, /* r10 */ - { 0x24, 32 }, /* r11 */ - { 0x3C, 32 }, /* r12 */ - { -2 , 32 }, /* sp */ - { 0x28, 32 }, /* lr */ - { 0x44, 32 }, /* pc */ - { 0x48, 32 }, /* xPSR */ + { ARMV7M_R0, 0x2C, 32 }, /* r0 */ + { ARMV7M_R1, 0x30, 32 }, /* r1 */ + { ARMV7M_R2, 0x34, 32 }, /* r2 */ + { ARMV7M_R3, 0x38, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0C, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1C, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x3C, 32 }, /* r12 */ + { ARMV7M_R13, -2 , 32 }, /* sp */ + { ARMV7M_R14, 0x28, 32 }, /* lr */ + { ARMV7M_PC, 0x44, 32 }, /* pc */ + { ARMV7M_xPSR, 0x48, 32 }, /* xPSR */ }; const struct rtos_register_stacking rtos_mqx_arm_v7m_stacking = { diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c index 931cfc7ed..7b054cbbc 100644 --- a/src/rtos/rtos_standard_stackings.c +++ b/src/rtos/rtos_standard_stackings.c @@ -24,132 +24,132 @@ #include "target/armv7m.h" static const struct stack_register_offset rtos_standard_Cortex_M3_stack_offsets[ARMV7M_NUM_CORE_REGS] = { - { 0x20, 32 }, /* r0 */ - { 0x24, 32 }, /* r1 */ - { 0x28, 32 }, /* r2 */ - { 0x2c, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x30, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x34, 32 }, /* lr */ - { 0x38, 32 }, /* pc */ - { 0x3c, 32 }, /* xPSR */ + { ARMV7M_R0, 0x20, 32 }, /* r0 */ + { ARMV7M_R1, 0x24, 32 }, /* r1 */ + { ARMV7M_R2, 0x28, 32 }, /* r2 */ + { ARMV7M_R3, 0x2c, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x30, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x34, 32 }, /* lr */ + { ARMV7M_PC, 0x38, 32 }, /* pc */ + { ARMV7M_xPSR, 0x3c, 32 }, /* xPSR */ }; static const struct stack_register_offset rtos_standard_Cortex_M4F_stack_offsets[] = { - { 0x24, 32 }, /* r0 */ - { 0x28, 32 }, /* r1 */ - { 0x2c, 32 }, /* r2 */ - { 0x30, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x34, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x38, 32 }, /* lr */ - { 0x3c, 32 }, /* pc */ - { 0x40, 32 }, /* xPSR */ + { ARMV7M_R0, 0x24, 32 }, /* r0 */ + { ARMV7M_R1, 0x28, 32 }, /* r1 */ + { ARMV7M_R2, 0x2c, 32 }, /* r2 */ + { ARMV7M_R3, 0x30, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x34, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x38, 32 }, /* lr */ + { ARMV7M_PC, 0x3c, 32 }, /* pc */ + { ARMV7M_xPSR, 0x40, 32 }, /* xPSR */ }; static const struct stack_register_offset rtos_standard_Cortex_M4F_FPU_stack_offsets[] = { - { 0x64, 32 }, /* r0 */ - { 0x68, 32 }, /* r1 */ - { 0x6c, 32 }, /* r2 */ - { 0x70, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x74, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x78, 32 }, /* lr */ - { 0x7c, 32 }, /* pc */ - { 0x80, 32 }, /* xPSR */ + { ARMV7M_R0, 0x64, 32 }, /* r0 */ + { ARMV7M_R1, 0x68, 32 }, /* r1 */ + { ARMV7M_R2, 0x6c, 32 }, /* r2 */ + { ARMV7M_R3, 0x70, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x74, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x78, 32 }, /* lr */ + { ARMV7M_PC, 0x7c, 32 }, /* pc */ + { ARMV7M_xPSR, 0x80, 32 }, /* xPSR */ }; static const struct stack_register_offset rtos_standard_Cortex_R4_stack_offsets[] = { - { 0x08, 32 }, /* r0 (a1) */ - { 0x0c, 32 }, /* r1 (a2) */ - { 0x10, 32 }, /* r2 (a3) */ - { 0x14, 32 }, /* r3 (a4) */ - { 0x18, 32 }, /* r4 (v1) */ - { 0x1c, 32 }, /* r5 (v2) */ - { 0x20, 32 }, /* r6 (v3) */ - { 0x24, 32 }, /* r7 (v4) */ - { 0x28, 32 }, /* r8 (a1) */ - { 0x2c, 32 }, /* r9 (sb) */ - { 0x30, 32 }, /* r10 (sl) */ - { 0x34, 32 }, /* r11 (fp) */ - { 0x38, 32 }, /* r12 (ip) */ - { -2, 32 }, /* sp */ - { 0x3c, 32 }, /* lr */ - { 0x40, 32 }, /* pc */ - { -1, 96 }, /* FPA1 */ - { -1, 96 }, /* FPA2 */ - { -1, 96 }, /* FPA3 */ - { -1, 96 }, /* FPA4 */ - { -1, 96 }, /* FPA5 */ - { -1, 96 }, /* FPA6 */ - { -1, 96 }, /* FPA7 */ - { -1, 96 }, /* FPA8 */ - { -1, 32 }, /* FPS */ - { 0x04, 32 }, /* CSPR */ + { 0, 0x08, 32 }, /* r0 (a1) */ + { 1, 0x0c, 32 }, /* r1 (a2) */ + { 2, 0x10, 32 }, /* r2 (a3) */ + { 3, 0x14, 32 }, /* r3 (a4) */ + { 4, 0x18, 32 }, /* r4 (v1) */ + { 5, 0x1c, 32 }, /* r5 (v2) */ + { 6, 0x20, 32 }, /* r6 (v3) */ + { 7, 0x24, 32 }, /* r7 (v4) */ + { 8, 0x28, 32 }, /* r8 (a1) */ + { 10, 0x2c, 32 }, /* r9 (sb) */ + { 11, 0x30, 32 }, /* r10 (sl) */ + { 12, 0x34, 32 }, /* r11 (fp) */ + { 13, 0x38, 32 }, /* r12 (ip) */ + { 14, -2, 32 }, /* sp */ + { 15, 0x3c, 32 }, /* lr */ + { 16, 0x40, 32 }, /* pc */ + { 17, -1, 96 }, /* FPA1 */ + { 18, -1, 96 }, /* FPA2 */ + { 19, -1, 96 }, /* FPA3 */ + { 20, -1, 96 }, /* FPA4 */ + { 21, -1, 96 }, /* FPA5 */ + { 22, -1, 96 }, /* FPA6 */ + { 23, -1, 96 }, /* FPA7 */ + { 24, -1, 96 }, /* FPA8 */ + { 25, -1, 32 }, /* FPS */ + { 26, 0x04, 32 }, /* CSPR */ }; static const struct stack_register_offset rtos_standard_NDS32_N1068_stack_offsets[] = { - { 0x88, 32 }, /* R0 */ - { 0x8C, 32 }, /* R1 */ - { 0x14, 32 }, /* R2 */ - { 0x18, 32 }, /* R3 */ - { 0x1C, 32 }, /* R4 */ - { 0x20, 32 }, /* R5 */ - { 0x24, 32 }, /* R6 */ - { 0x28, 32 }, /* R7 */ - { 0x2C, 32 }, /* R8 */ - { 0x30, 32 }, /* R9 */ - { 0x34, 32 }, /* R10 */ - { 0x38, 32 }, /* R11 */ - { 0x3C, 32 }, /* R12 */ - { 0x40, 32 }, /* R13 */ - { 0x44, 32 }, /* R14 */ - { 0x48, 32 }, /* R15 */ - { 0x4C, 32 }, /* R16 */ - { 0x50, 32 }, /* R17 */ - { 0x54, 32 }, /* R18 */ - { 0x58, 32 }, /* R19 */ - { 0x5C, 32 }, /* R20 */ - { 0x60, 32 }, /* R21 */ - { 0x64, 32 }, /* R22 */ - { 0x68, 32 }, /* R23 */ - { 0x6C, 32 }, /* R24 */ - { 0x70, 32 }, /* R25 */ - { 0x74, 32 }, /* R26 */ - { 0x78, 32 }, /* R27 */ - { 0x7C, 32 }, /* R28 */ - { 0x80, 32 }, /* R29 */ - { 0x84, 32 }, /* R30 (LP) */ - { 0x00, 32 }, /* R31 (SP) */ - { 0x04, 32 }, /* PSW */ - { 0x08, 32 }, /* IPC */ - { 0x0C, 32 }, /* IPSW */ - { 0x10, 32 }, /* IFC_LP */ + { 0, 0x88, 32 }, /* R0 */ + { 1, 0x8C, 32 }, /* R1 */ + { 2, 0x14, 32 }, /* R2 */ + { 3, 0x18, 32 }, /* R3 */ + { 4, 0x1C, 32 }, /* R4 */ + { 5, 0x20, 32 }, /* R5 */ + { 6, 0x24, 32 }, /* R6 */ + { 7, 0x28, 32 }, /* R7 */ + { 8, 0x2C, 32 }, /* R8 */ + { 9, 0x30, 32 }, /* R9 */ + { 10, 0x34, 32 }, /* R10 */ + { 11, 0x38, 32 }, /* R11 */ + { 12, 0x3C, 32 }, /* R12 */ + { 13, 0x40, 32 }, /* R13 */ + { 14, 0x44, 32 }, /* R14 */ + { 15, 0x48, 32 }, /* R15 */ + { 16, 0x4C, 32 }, /* R16 */ + { 17, 0x50, 32 }, /* R17 */ + { 18, 0x54, 32 }, /* R18 */ + { 19, 0x58, 32 }, /* R19 */ + { 20, 0x5C, 32 }, /* R20 */ + { 21, 0x60, 32 }, /* R21 */ + { 22, 0x64, 32 }, /* R22 */ + { 23, 0x68, 32 }, /* R23 */ + { 24, 0x6C, 32 }, /* R24 */ + { 25, 0x70, 32 }, /* R25 */ + { 26, 0x74, 32 }, /* R26 */ + { 27, 0x78, 32 }, /* R27 */ + { 28, 0x7C, 32 }, /* R28 */ + { 29, 0x80, 32 }, /* R29 */ + { 30, 0x84, 32 }, /* R30 (LP) */ + { 31, 0x00, 32 }, /* R31 (SP) */ + { 32, 0x04, 32 }, /* PSW */ + { 33, 0x08, 32 }, /* IPC */ + { 34, 0x0C, 32 }, /* IPSW */ + { 35, 0x10, 32 }, /* IFC_LP */ }; static int64_t rtos_generic_stack_align(struct target *target, diff --git a/src/rtos/rtos_ucos_iii_stackings.c b/src/rtos/rtos_ucos_iii_stackings.c index c260b7f80..d093563ba 100644 --- a/src/rtos/rtos_ucos_iii_stackings.c +++ b/src/rtos/rtos_ucos_iii_stackings.c @@ -24,25 +24,47 @@ #include #include #include +#include static const struct stack_register_offset rtos_uCOS_III_Cortex_M_stack_offsets[] = { - { 0x20, 32 }, /* r0 */ - { 0x24, 32 }, /* r1 */ - { 0x28, 32 }, /* r2 */ - { 0x2c, 32 }, /* r3 */ - { 0x00, 32 }, /* r4 */ - { 0x04, 32 }, /* r5 */ - { 0x08, 32 }, /* r6 */ - { 0x0c, 32 }, /* r7 */ - { 0x10, 32 }, /* r8 */ - { 0x14, 32 }, /* r9 */ - { 0x18, 32 }, /* r10 */ - { 0x1c, 32 }, /* r11 */ - { 0x30, 32 }, /* r12 */ - { -2, 32 }, /* sp */ - { 0x34, 32 }, /* lr */ - { 0x38, 32 }, /* pc */ - { 0x3c, 32 }, /* xPSR */ + { ARMV7M_R0, 0x20, 32 }, /* r0 */ + { ARMV7M_R1, 0x24, 32 }, /* r1 */ + { ARMV7M_R2, 0x28, 32 }, /* r2 */ + { ARMV7M_R3, 0x2c, 32 }, /* r3 */ + { ARMV7M_R4, 0x00, 32 }, /* r4 */ + { ARMV7M_R5, 0x04, 32 }, /* r5 */ + { ARMV7M_R6, 0x08, 32 }, /* r6 */ + { ARMV7M_R7, 0x0c, 32 }, /* r7 */ + { ARMV7M_R8, 0x10, 32 }, /* r8 */ + { ARMV7M_R9, 0x14, 32 }, /* r9 */ + { ARMV7M_R10, 0x18, 32 }, /* r10 */ + { ARMV7M_R11, 0x1c, 32 }, /* r11 */ + { ARMV7M_R12, 0x30, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x34, 32 }, /* lr */ + { ARMV7M_PC, 0x38, 32 }, /* pc */ + { ARMV7M_xPSR, 0x3c, 32 }, /* xPSR */ +}; + +static const struct stack_register_offset rtos_uCOS_III_eSi_RISC_stack_offsets[] = { + { ESIRISC_SP, -2, 32 }, /* sp */ + { ESIRISC_RA, 0x48, 32 }, /* ra */ + { ESIRISC_R2, 0x44, 32 }, /* r2 */ + { ESIRISC_R3, 0x40, 32 }, /* r3 */ + { ESIRISC_R4, 0x3c, 32 }, /* r4 */ + { ESIRISC_R5, 0x38, 32 }, /* r5 */ + { ESIRISC_R6, 0x34, 32 }, /* r6 */ + { ESIRISC_R7, 0x30, 32 }, /* r7 */ + { ESIRISC_R8, 0x2c, 32 }, /* r8 */ + { ESIRISC_R9, 0x28, 32 }, /* r9 */ + { ESIRISC_R10, 0x24, 32 }, /* r10 */ + { ESIRISC_R11, 0x20, 32 }, /* r11 */ + { ESIRISC_R12, 0x1c, 32 }, /* r12 */ + { ESIRISC_R13, 0x18, 32 }, /* r13 */ + { ESIRISC_R14, 0x14, 32 }, /* r14 */ + { ESIRISC_R15, 0x10, 32 }, /* r15 */ + { ESIRISC_PC, 0x04, 32 }, /* PC */ + { ESIRISC_CAS, 0x08, 32 }, /* CAS */ }; const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking = { @@ -52,3 +74,11 @@ const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking = { rtos_generic_stack_align8, /* stack_alignment */ rtos_uCOS_III_Cortex_M_stack_offsets /* register_offsets */ }; + +const struct rtos_register_stacking rtos_uCOS_III_eSi_RISC_stacking = { + 0x4c, /* stack_registers_size */ + -1, /* stack_growth_direction */ + ARRAY_SIZE(rtos_uCOS_III_eSi_RISC_stack_offsets), /* num_output_registers */ + NULL, /* stack_alignment */ + rtos_uCOS_III_eSi_RISC_stack_offsets /* register_offsets */ +}; diff --git a/src/rtos/rtos_ucos_iii_stackings.h b/src/rtos/rtos_ucos_iii_stackings.h index f4703da37..a9398138b 100644 --- a/src/rtos/rtos_ucos_iii_stackings.h +++ b/src/rtos/rtos_ucos_iii_stackings.h @@ -26,5 +26,6 @@ #include extern const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking; +extern const struct rtos_register_stacking rtos_uCOS_III_eSi_RISC_stacking; #endif /* OPENOCD_RTOS_RTOS_UCOS_III_STACKINGS_H */ diff --git a/src/rtos/uCOS-III.c b/src/rtos/uCOS-III.c index 8e63ea4e6..304d07c59 100644 --- a/src/rtos/uCOS-III.c +++ b/src/rtos/uCOS-III.c @@ -68,6 +68,20 @@ static const struct uCOS_III_params uCOS_III_params_list[] = { &rtos_uCOS_III_Cortex_M_stacking, /* stacking_info */ 0, /* num_threads */ }, + { + "esirisc", /* target_name */ + sizeof(uint32_t), /* pointer_width */ + 0, /* thread_stack_offset */ + 0, /* thread_name_offset */ + 0, /* thread_state_offset */ + 0, /* thread_priority_offset */ + 0, /* thread_prev_offset */ + 0, /* thread_next_offset */ + false, /* thread_offsets_updated */ + 1, /* threadid_start */ + &rtos_uCOS_III_eSi_RISC_stacking, /* stacking_info */ + 0, /* num_threads */ + }, }; static const char * const uCOS_III_symbol_list[] = { @@ -286,6 +300,11 @@ static int uCOS_III_update_threads(struct rtos *rtos) struct uCOS_III_params *params = rtos->rtos_specific_params; int retval; + if (rtos->symbols == NULL) { + LOG_ERROR("uCOS-III: symbol list not loaded"); + return ERROR_FAIL; + } + /* free previous thread details */ rtos_free_threadlist(rtos); @@ -454,7 +473,8 @@ static int uCOS_III_update_threads(struct rtos *rtos) return ERROR_OK; } -static int uCOS_III_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, char **hex_reg_list) +static int uCOS_III_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, + struct rtos_reg **reg_list, int *num_regs) { struct uCOS_III_params *params = rtos->rtos_specific_params; int retval; @@ -484,7 +504,8 @@ static int uCOS_III_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, return rtos_generic_stack_read(rtos->target, params->stacking_info, stack_address, - hex_reg_list); + reg_list, + num_regs); } static int uCOS_III_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 3ea324260..3548f166c 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -1179,8 +1179,11 @@ static int gdb_get_registers_packet(struct connection *connection, if (retval != ERROR_OK) return gdb_error(connection, retval); - for (i = 0; i < reg_list_size; i++) + for (i = 0; i < reg_list_size; i++) { + if (reg_list[i] == NULL || reg_list[i]->exist == false) + continue; reg_packet_size += DIV_ROUND_UP(reg_list[i]->size, 8) * 2; + } assert(reg_packet_size > 0); @@ -1191,6 +1194,8 @@ static int gdb_get_registers_packet(struct connection *connection, reg_packet_p = reg_packet; for (i = 0; i < reg_list_size; i++) { + if (reg_list[i] == NULL || reg_list[i]->exist == false) + continue; if (!reg_list[i]->valid) { retval = reg_list[i]->type->get(reg_list[i]); if (retval != ERROR_OK && gdb_report_register_access_error) { @@ -1296,6 +1301,9 @@ static int gdb_get_register_packet(struct connection *connection, LOG_DEBUG("-"); #endif + if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num))) + return ERROR_OK; + retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) @@ -2188,6 +2196,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o int retval = ERROR_OK; struct reg **reg_list = NULL; int reg_list_size; + char const *architecture; char const **features = NULL; char const **arch_defined_types = NULL; int feature_list_size = 0; @@ -2229,6 +2238,12 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o "\n" "\n"); + /* generate architecture element if supported by target */ + architecture = target_get_gdb_arch(target); + if (architecture != NULL) + xml_printf(&retval, &tdesc, &pos, &size, + "%s\n", architecture); + /* generate target description according to register list */ if (features != NULL) { while (features[current_feature]) { @@ -2378,6 +2393,8 @@ static int gdb_target_description_supported(struct target *target, int *supporte char const **features = NULL; int feature_list_size = 0; + char const *architecture = target_get_gdb_arch(target); + retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) { @@ -2399,7 +2416,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte } if (supported) { - if (feature_list_size) + if (architecture || feature_list_size) *supported = 1; else *supported = 0; @@ -3377,6 +3394,8 @@ static int gdb_target_start(struct target *target, const char *port) if (NULL == gdb_service) return -ENOMEM; + LOG_DEBUG("starting gdb server for %s on %s", target_name(target), port); + gdb_service->target = target; gdb_service->core[0] = -1; gdb_service->core[1] = -1; @@ -3402,16 +3421,36 @@ static int gdb_target_start(struct target *target, const char *port) static int gdb_target_add_one(struct target *target) { + /* one gdb instance per smp list */ + if ((target->smp) && (target->gdb_service)) + return ERROR_OK; + + /* skip targets that cannot handle a gdb connections (e.g. mem_ap) */ + if (!target_supports_gdb_connection(target)) { + LOG_DEBUG("skip gdb server for target %s", target_name(target)); + return ERROR_OK; + } + + if (target->gdb_port_override) { + if (strcmp(target->gdb_port_override, "disabled") == 0) { + LOG_INFO("gdb port disabled"); + return ERROR_OK; + } + return gdb_target_start(target, target->gdb_port_override); + } + if (strcmp(gdb_port, "disabled") == 0) { LOG_INFO("gdb port disabled"); return ERROR_OK; } - /* one gdb instance per smp list */ - if ((target->smp) && (target->gdb_service)) - return ERROR_OK; int retval = gdb_target_start(target, gdb_port_next); if (retval == ERROR_OK) { + /* save the port number so can be queried with + * $target_name cget -gdb-port + */ + target->gdb_port_override = strdup(gdb_port_next); + long portnumber; /* If we can parse the port number * then we increment the port number for the next target. @@ -3436,11 +3475,6 @@ static int gdb_target_add_one(struct target *target) int gdb_target_add_all(struct target *target) { - if (strcmp(gdb_port, "disabled") == 0) { - LOG_INFO("gdb server disabled"); - return ERROR_OK; - } - if (NULL == target) { LOG_WARNING("gdb services need one or more targets defined"); return ERROR_OK; diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index 3cb63a275..0676c883b 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -157,7 +157,7 @@ static int tcl_new_connection(struct connection *connection) connection->priv = tclc; - struct target *target = get_current_target(connection->cmd_ctx); + struct target *target = get_current_target_or_null(connection->cmd_ctx); if (target != NULL) tclc->tc_laststate = target->state; diff --git a/src/target/Makefile.am b/src/target/Makefile.am index b1119e7df..8e9fcb27e 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -23,6 +23,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(NDS32_SRC) \ $(STM8_SRC) \ $(INTEL_IA32_SRC) \ + $(ESIRISC_SRC) \ %D%/avrt.c \ %D%/dsp563xx.c \ %D%/dsp563xx_once.c \ @@ -74,8 +75,10 @@ ARMV7_SRC = \ %D%/armv7m_trace.c \ %D%/cortex_m.c \ %D%/armv7a.c \ + %D%/armv7a_mmu.c \ %D%/cortex_a.c \ - %D%/ls1_sap.c + %D%/ls1_sap.c \ + %D%/mem_ap.c ARMV8_SRC = \ %D%/armv8_dpm.c \ @@ -138,6 +141,10 @@ INTEL_IA32_SRC = \ %D%/lakemont.c \ %D%/x86_32_common.c +ESIRISC_SRC = \ + %D%/esirisc.c \ + %D%/esirisc_jtag.c + %C%_libtarget_la_SOURCES += \ %D%/algorithm.h \ %D%/arm.h \ @@ -146,6 +153,7 @@ INTEL_IA32_SRC = \ %D%/arm_adi_v5.h \ %D%/armv7a_cache.h \ %D%/armv7a_cache_l2x.h \ + %D%/armv7a_mmu.h \ %D%/arm_disassembler.h \ %D%/arm_opcodes.h \ %D%/arm_simulator.h \ @@ -217,7 +225,10 @@ INTEL_IA32_SRC = \ %D%/stm8.h \ %D%/lakemont.h \ %D%/x86_32_common.h \ - %D%/arm_cti.h + %D%/arm_cti.h \ + %D%/esirisc.h \ + %D%/esirisc_jtag.h \ + %D%/esirisc_regs.h include %D%/openrisc/Makefile.am include %D%/riscv/Makefile.am diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 302ea7891..03e642bfe 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -652,6 +652,15 @@ int dap_dp_init(struct adiv5_dap *dap) dap_invalidate_cache(dap); + /* + * Early initialize dap->dp_ctrl_stat. + * In jtag mode only, if the following atomic reads fail and set the + * sticky error, it will trigger the clearing of the sticky. Without this + * initialization system and debug power would be disabled while clearing + * the sticky error bit. + */ + dap->dp_ctrl_stat = CDBGPWRUPREQ | CSYSPWRUPREQ; + for (size_t i = 0; i < 30; i++) { /* DP initialization */ @@ -660,7 +669,18 @@ int dap_dp_init(struct adiv5_dap *dap) break; } - retval = dap_queue_dp_write(dap, DP_CTRL_STAT, SSTICKYERR); + /* + * This write operation clears the sticky error bit in jtag mode only and + * is ignored in swd mode. It also powers-up system and debug domains in + * both jtag and swd modes, if not done before. + * Actually we do not need to clear the sticky error here because it has + * been already cleared (if it was set) in the previous atomic read. This + * write could be removed, but this initial part of dap_dp_init() is the + * result of years of fine tuning and there are strong concerns about any + * unnecessary code change. It doesn't harm, so let's keep it here and + * preserve the historical sequence of read/write operations! + */ + retval = dap_queue_dp_write(dap, DP_CTRL_STAT, dap->dp_ctrl_stat | SSTICKYERR); if (retval != ERROR_OK) return retval; @@ -668,7 +688,6 @@ int dap_dp_init(struct adiv5_dap *dap) if (retval != ERROR_OK) return retval; - dap->dp_ctrl_stat = CDBGPWRUPREQ | CSYSPWRUPREQ; retval = dap_queue_dp_write(dap, DP_CTRL_STAT, dap->dp_ctrl_stat); if (retval != ERROR_OK) return retval; @@ -793,7 +812,7 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a int ap_num; /* Maximum AP number is 255 since the SELECT register is 8 bits */ - for (ap_num = 0; ap_num <= 255; ap_num++) { + for (ap_num = 0; ap_num <= DP_APSEL_MAX; ap_num++) { /* read the IDR register of the Access Port */ uint32_t id_val = 0; @@ -1429,7 +1448,7 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) pc = (struct adiv5_private_config *)target->private_config; if (pc == NULL) { pc = calloc(1, sizeof(struct adiv5_private_config)); - pc->ap_num = -1; + pc->ap_num = DP_APSEL_INVALID; target->private_config = pc; } @@ -1498,6 +1517,10 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) e = Jim_GetOpt_Wide(goi, &ap_num); if (e != JIM_OK) return e; + if (ap_num < 0 || ap_num > DP_APSEL_MAX) { + Jim_SetResultString(goi->interp, "Invalid AP number!", -1); + return JIM_ERR; + } pc->ap_num = ap_num; } else { if (goi->argc != 0) { @@ -1507,11 +1530,11 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) return JIM_ERR; } - if (pc->ap_num < 0) { + if (pc->ap_num == DP_APSEL_INVALID) { Jim_SetResultString(goi->interp, "AP number not configured", -1); return JIM_ERR; } - Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, (int)pc->ap_num)); + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, pc->ap_num)); } break; } @@ -1543,7 +1566,7 @@ COMMAND_HANDLER(handle_dap_info_command) break; case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1566,7 +1589,7 @@ COMMAND_HANDLER(dap_baseaddr_command) case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1625,7 +1648,7 @@ COMMAND_HANDLER(dap_apsel_command) case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1691,7 +1714,7 @@ COMMAND_HANDLER(dap_apid_command) case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: @@ -1722,7 +1745,7 @@ COMMAND_HANDLER(dap_apreg_command) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; ap = dap_ap(dap, apsel); @@ -1734,8 +1757,10 @@ COMMAND_HANDLER(dap_apreg_command) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); switch (reg) { case MEM_AP_REG_CSW: - ap->csw_default = 0; /* invalid, force write */ - retval = mem_ap_setup_csw(ap, value); + ap->csw_value = 0; /* invalid, in case write fails */ + retval = dap_queue_ap_write(ap, reg, value); + if (retval == ERROR_OK) + ap->csw_value = value; break; case MEM_AP_REG_TAR: ap->tar_valid = false; /* invalid, force write */ diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index 883ac8b5d..a340b76f0 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -136,6 +136,9 @@ #define DP_SELECT_DPBANK 0x0000000F #define DP_SELECT_INVALID 0x00FFFF00 /* Reserved bits one */ +#define DP_APSEL_MAX (255) +#define DP_APSEL_INVALID (-1) + /** * This represents an ARM Debug Interface (v5) Access Port (AP). * Most common is a MEM-AP, for memory access. diff --git a/src/target/arm_cti.c b/src/target/arm_cti.c index 0d117e76d..dcaf21e50 100644 --- a/src/target/arm_cti.c +++ b/src/target/arm_cti.c @@ -431,6 +431,10 @@ static int cti_configure(Jim_GetOptInfo *goi, struct arm_cti_object *cti) e = Jim_GetOpt_Wide(goi, &w); if (e != JIM_OK) return e; + if (w < 0 || w > DP_APSEL_MAX) { + Jim_SetResultString(goi->interp, "-ap-num is invalid", -1); + return JIM_ERR; + } cti->ap_num = (uint32_t)w; } } diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c index 3be4d7199..3adb4ed26 100644 --- a/src/target/arm_dap.c +++ b/src/target/arm_dap.c @@ -48,7 +48,7 @@ static void dap_instance_init(struct adiv5_dap *dap) { int i; /* Set up with safe defaults */ - for (i = 0; i <= 255; i++) { + for (i = 0; i <= DP_APSEL_MAX; i++) { dap->ap[i].dap = dap; dap->ap[i].ap_num = i; /* memaccess_tck max is 255 */ @@ -319,7 +319,7 @@ COMMAND_HANDLER(handle_dap_info_command) break; case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); - if (apsel >= 256) + if (apsel > DP_APSEL_MAX) return ERROR_COMMAND_SYNTAX_ERROR; break; default: diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c index f9b30c187..8b9957033 100644 --- a/src/target/arm_dpm.c +++ b/src/target/arm_dpm.c @@ -108,7 +108,7 @@ static int dpm_mcr(struct target *target, int cpnum, /* Toggles between recorded core mode (USR, SVC, etc) and a temporary one. * Routines *must* restore the original mode before returning!! */ -int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) +int arm_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) { int retval; uint32_t cpsr; @@ -168,7 +168,7 @@ static int dpm_read_reg_u64(struct arm_dpm *dpm, struct reg *r, unsigned regnum) } /* just read the register -- rely on the core mode being right */ -static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) +int arm_dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) { uint32_t value; int retval; @@ -352,7 +352,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) for (unsigned i = 0; i < 2; i++) { r = arm->core_cache->reg_list + i; if (!r->valid) { - retval = dpm_read_reg(dpm, r, i); + retval = arm_dpm_read_reg(dpm, r, i); if (retval != ERROR_OK) goto fail; } @@ -372,7 +372,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) if (r->valid) continue; - retval = dpm_read_reg(dpm, r, i); + retval = arm_dpm_read_reg(dpm, r, i); if (retval != ERROR_OK) goto fail; } @@ -543,7 +543,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) /* REVISIT error checks */ if (tmode != ARM_MODE_ANY) { - retval = dpm_modeswitch(dpm, tmode); + retval = arm_dpm_modeswitch(dpm, tmode); if (retval != ERROR_OK) goto done; } @@ -564,7 +564,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) * or it's dirty. Must write PC to ensure the return address is * defined, and must not write it before CPSR. */ - retval = dpm_modeswitch(dpm, ARM_MODE_ANY); + retval = arm_dpm_modeswitch(dpm, ARM_MODE_ANY); if (retval != ERROR_OK) goto done; arm->cpsr->dirty = false; @@ -671,18 +671,18 @@ static int arm_dpm_read_core_reg(struct target *target, struct reg *r, return retval; if (mode != ARM_MODE_ANY) { - retval = dpm_modeswitch(dpm, mode); + retval = arm_dpm_modeswitch(dpm, mode); if (retval != ERROR_OK) goto fail; } - retval = dpm_read_reg(dpm, r, regnum); + retval = arm_dpm_read_reg(dpm, r, regnum); if (retval != ERROR_OK) goto fail; /* always clean up, regardless of error */ if (mode != ARM_MODE_ANY) - /* (void) */ dpm_modeswitch(dpm, ARM_MODE_ANY); + /* (void) */ arm_dpm_modeswitch(dpm, ARM_MODE_ANY); fail: /* (void) */ dpm->finish(dpm); @@ -715,7 +715,7 @@ static int arm_dpm_write_core_reg(struct target *target, struct reg *r, return retval; if (mode != ARM_MODE_ANY) { - retval = dpm_modeswitch(dpm, mode); + retval = arm_dpm_modeswitch(dpm, mode); if (retval != ERROR_OK) goto fail; } @@ -724,7 +724,7 @@ static int arm_dpm_write_core_reg(struct target *target, struct reg *r, /* always clean up, regardless of error */ if (mode != ARM_MODE_ANY) - /* (void) */ dpm_modeswitch(dpm, ARM_MODE_ANY); + /* (void) */ arm_dpm_modeswitch(dpm, ARM_MODE_ANY); fail: /* (void) */ dpm->finish(dpm); @@ -773,9 +773,9 @@ static int arm_dpm_full_context(struct target *target) * in FIQ mode we need to patch mode. */ if (mode != ARM_MODE_ANY) - retval = dpm_modeswitch(dpm, mode); + retval = arm_dpm_modeswitch(dpm, mode); else - retval = dpm_modeswitch(dpm, ARM_MODE_USR); + retval = arm_dpm_modeswitch(dpm, ARM_MODE_USR); if (retval != ERROR_OK) goto done; @@ -784,7 +784,7 @@ static int arm_dpm_full_context(struct target *target) continue; /* CPSR was read, so "R16" must mean SPSR */ - retval = dpm_read_reg(dpm, + retval = arm_dpm_read_reg(dpm, &cache->reg_list[i], (r->num == 16) ? 17 : r->num); if (retval != ERROR_OK) @@ -793,7 +793,7 @@ static int arm_dpm_full_context(struct target *target) } while (did_read); - retval = dpm_modeswitch(dpm, ARM_MODE_ANY); + retval = arm_dpm_modeswitch(dpm, ARM_MODE_ANY); /* (void) */ dpm->finish(dpm); done: return retval; diff --git a/src/target/arm_dpm.h b/src/target/arm_dpm.h index f8d124813..d05c66c43 100644 --- a/src/target/arm_dpm.h +++ b/src/target/arm_dpm.h @@ -152,8 +152,9 @@ struct arm_dpm { int arm_dpm_setup(struct arm_dpm *dpm); int arm_dpm_initialize(struct arm_dpm *dpm); +int arm_dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum); int arm_dpm_read_current_registers(struct arm_dpm *); -int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode); +int arm_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode); int arm_dpm_write_dirty_registers(struct arm_dpm *, bool bpwp); diff --git a/src/target/armv7a.c b/src/target/armv7a.c index eecfa7097..f9594c9c3 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -24,6 +24,7 @@ #include #include "armv7a.h" +#include "armv7a_mmu.h" #include "arm_disassembler.h" #include "register.h" @@ -188,174 +189,6 @@ done: return retval; } -/* method adapted to Cortex-A : reused ARM v4 v5 method */ -int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) -{ - uint32_t first_lvl_descriptor = 0x0; - uint32_t second_lvl_descriptor = 0x0; - int retval; - struct armv7a_common *armv7a = target_to_armv7a(target); - uint32_t ttbidx = 0; /* default to ttbr0 */ - uint32_t ttb_mask; - uint32_t va_mask; - uint32_t ttb; - - if (target->state != TARGET_HALTED) - LOG_INFO("target not halted, using cached values for translation table!"); - - /* if va is above the range handled by ttbr0, select ttbr1 */ - if (va > armv7a->armv7a_mmu.ttbr_range[0]) { - /* select ttb 1 */ - ttbidx = 1; - } - - ttb = armv7a->armv7a_mmu.ttbr[ttbidx]; - ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx]; - va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx]; - - LOG_DEBUG("ttb_mask %" PRIx32 " va_mask %" PRIx32 " ttbidx %i", - ttb_mask, va_mask, ttbidx); - retval = armv7a->armv7a_mmu.read_physical_memory(target, - (ttb & ttb_mask) | ((va & va_mask) >> 18), - 4, 1, (uint8_t *)&first_lvl_descriptor); - if (retval != ERROR_OK) - return retval; - first_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) - &first_lvl_descriptor); - /* reuse armv4_5 piece of code, specific armv7a changes may come later */ - LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor); - - if ((first_lvl_descriptor & 0x3) == 0) { - LOG_ERROR("Address translation failure"); - return ERROR_TARGET_TRANSLATION_FAULT; - } - - - if ((first_lvl_descriptor & 0x40002) == 2) { - /* section descriptor */ - *val = (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); - return ERROR_OK; - } else if ((first_lvl_descriptor & 0x40002) == 0x40002) { - /* supersection descriptor */ - if (first_lvl_descriptor & 0x00f001e0) { - LOG_ERROR("Physical address does not fit into 32 bits"); - return ERROR_TARGET_TRANSLATION_FAULT; - } - *val = (first_lvl_descriptor & 0xff000000) | (va & 0x00ffffff); - return ERROR_OK; - } - - /* page table */ - retval = armv7a->armv7a_mmu.read_physical_memory(target, - (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), - 4, 1, (uint8_t *)&second_lvl_descriptor); - if (retval != ERROR_OK) - return retval; - - second_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) - &second_lvl_descriptor); - - LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor); - - if ((second_lvl_descriptor & 0x3) == 0) { - LOG_ERROR("Address translation failure"); - return ERROR_TARGET_TRANSLATION_FAULT; - } - - if ((second_lvl_descriptor & 0x3) == 1) { - /* large page descriptor */ - *val = (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); - } else { - /* small page descriptor */ - *val = (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); - } - - return ERROR_OK; -} - -/* V7 method VA TO PA */ -int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, - uint32_t *val, int meminfo) -{ - int retval = ERROR_FAIL; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct arm_dpm *dpm = armv7a->arm.dpm; - uint32_t virt = va & ~0xfff; - uint32_t NOS, NS, INNER, OUTER; - *val = 0xdeadbeef; - retval = dpm->prepare(dpm); - if (retval != ERROR_OK) - goto done; - /* mmu must be enable in order to get a correct translation - * use VA to PA CP15 register for conversion */ - retval = dpm->instr_write_data_r0(dpm, - ARMV4_5_MCR(15, 0, 0, 7, 8, 0), - virt); - if (retval != ERROR_OK) - goto done; - retval = dpm->instr_read_data_r0(dpm, - ARMV4_5_MRC(15, 0, 0, 7, 4, 0), - val); - /* decode memory attribute */ - NOS = (*val >> 10) & 1; /* Not Outer shareable */ - NS = (*val >> 9) & 1; /* Non secure */ - INNER = (*val >> 4) & 0x7; - OUTER = (*val >> 2) & 0x3; - - if (retval != ERROR_OK) - goto done; - *val = (*val & ~0xfff) + (va & 0xfff); - if (*val == va) - LOG_WARNING("virt = phys : MMU disable !!"); - if (meminfo) { - LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured", - va, *val, - NOS == 1 ? "not" : " ", - NS == 1 ? "not" : ""); - switch (OUTER) { - case 0: - LOG_INFO("outer: Non-Cacheable"); - break; - case 1: - LOG_INFO("outer: Write-Back, Write-Allocate"); - break; - case 2: - LOG_INFO("outer: Write-Through, No Write-Allocate"); - break; - case 3: - LOG_INFO("outer: Write-Back, no Write-Allocate"); - break; - } - switch (INNER) { - case 0: - LOG_INFO("inner: Non-Cacheable"); - break; - case 1: - LOG_INFO("inner: Strongly-ordered"); - break; - case 3: - LOG_INFO("inner: Device"); - break; - case 5: - LOG_INFO("inner: Write-Back, Write-Allocate"); - break; - case 6: - LOG_INFO("inner: Write-Through"); - break; - case 7: - LOG_INFO("inner: Write-Back, no Write-Allocate"); - break; - default: - LOG_INFO("inner: %" PRIx32 " ???", INNER); - } - } - -done: - dpm->finish(dpm); - - return retval; -} - /* FIXME: remove it */ static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way) { diff --git a/src/target/armv7a.h b/src/target/armv7a.h index 57779c61a..1e88c98cf 100644 --- a/src/target/armv7a.h +++ b/src/target/armv7a.h @@ -106,8 +106,6 @@ struct armv7a_common { struct arm_dpm dpm; uint32_t debug_base; struct adiv5_ap *debug_ap; - struct adiv5_ap *memory_ap; - bool memory_ap_available; /* mdir */ uint8_t multi_processor_system; uint8_t cluster_id; @@ -188,9 +186,6 @@ static inline bool is_armv7a(struct armv7a_common *armv7a) int armv7a_arch_state(struct target *target); int armv7a_identify_cache(struct target *target); int armv7a_init_arch_info(struct target *target, struct armv7a_common *armv7a); -int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, - uint32_t *val, int meminfo); -int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val); int armv7a_handle_cache_info_command(struct command_context *cmd_ctx, struct armv7a_cache_common *armv7a_cache); diff --git a/src/target/armv7a_mmu.c b/src/target/armv7a_mmu.c new file mode 100644 index 000000000..eed73ee58 --- /dev/null +++ b/src/target/armv7a_mmu.c @@ -0,0 +1,456 @@ +/*************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * matthias.welwarsky@sysgo.com * + * * + * Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "jtag/interface.h" +#include "arm.h" +#include "armv7a.h" +#include "armv7a_mmu.h" +#include "arm_opcodes.h" +#include "cortex_a.h" + +#define SCTLR_BIT_AFE (1 << 29) + +/* method adapted to Cortex-A : reused ARM v4 v5 method */ +int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) +{ + uint32_t first_lvl_descriptor = 0x0; + uint32_t second_lvl_descriptor = 0x0; + int retval; + struct armv7a_common *armv7a = target_to_armv7a(target); + uint32_t ttbidx = 0; /* default to ttbr0 */ + uint32_t ttb_mask; + uint32_t va_mask; + uint32_t ttb; + + if (target->state != TARGET_HALTED) + LOG_INFO("target not halted, using cached values for translation table!"); + + /* if va is above the range handled by ttbr0, select ttbr1 */ + if (va > armv7a->armv7a_mmu.ttbr_range[0]) { + /* select ttb 1 */ + ttbidx = 1; + } + + ttb = armv7a->armv7a_mmu.ttbr[ttbidx]; + ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx]; + va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx]; + + LOG_DEBUG("ttb_mask %" PRIx32 " va_mask %" PRIx32 " ttbidx %i", + ttb_mask, va_mask, ttbidx); + retval = armv7a->armv7a_mmu.read_physical_memory(target, + (ttb & ttb_mask) | ((va & va_mask) >> 18), + 4, 1, (uint8_t *)&first_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + first_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) + &first_lvl_descriptor); + /* reuse armv4_5 piece of code, specific armv7a changes may come later */ + LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor); + + if ((first_lvl_descriptor & 0x3) == 0) { + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if ((first_lvl_descriptor & 0x40002) == 2) { + /* section descriptor */ + *val = (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); + return ERROR_OK; + } else if ((first_lvl_descriptor & 0x40002) == 0x40002) { + /* supersection descriptor */ + if (first_lvl_descriptor & 0x00f001e0) { + LOG_ERROR("Physical address does not fit into 32 bits"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + *val = (first_lvl_descriptor & 0xff000000) | (va & 0x00ffffff); + return ERROR_OK; + } + + /* page table */ + retval = armv7a->armv7a_mmu.read_physical_memory(target, + (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), + 4, 1, (uint8_t *)&second_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + + second_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) + &second_lvl_descriptor); + + LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor); + + if ((second_lvl_descriptor & 0x3) == 0) { + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if ((second_lvl_descriptor & 0x3) == 1) { + /* large page descriptor */ + *val = (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); + } else { + /* small page descriptor */ + *val = (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); + } + + return ERROR_OK; +} + +/* V7 method VA TO PA */ +int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, + uint32_t *val, int meminfo) +{ + int retval = ERROR_FAIL; + struct armv7a_common *armv7a = target_to_armv7a(target); + struct arm_dpm *dpm = armv7a->arm.dpm; + uint32_t virt = va & ~0xfff; + uint32_t NOS, NS, INNER, OUTER; + *val = 0xdeadbeef; + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + /* mmu must be enable in order to get a correct translation + * use VA to PA CP15 register for conversion */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MCR(15, 0, 0, 7, 8, 0), + virt); + if (retval != ERROR_OK) + goto done; + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 7, 4, 0), + val); + /* decode memory attribute */ + NOS = (*val >> 10) & 1; /* Not Outer shareable */ + NS = (*val >> 9) & 1; /* Non secure */ + INNER = (*val >> 4) & 0x7; + OUTER = (*val >> 2) & 0x3; + + if (retval != ERROR_OK) + goto done; + *val = (*val & ~0xfff) + (va & 0xfff); + if (*val == va) + LOG_WARNING("virt = phys : MMU disable !!"); + if (meminfo) { + LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured", + va, *val, + NOS == 1 ? "not" : " ", + NS == 1 ? "not" : ""); + switch (OUTER) { + case 0: + LOG_INFO("outer: Non-Cacheable"); + break; + case 1: + LOG_INFO("outer: Write-Back, Write-Allocate"); + break; + case 2: + LOG_INFO("outer: Write-Through, No Write-Allocate"); + break; + case 3: + LOG_INFO("outer: Write-Back, no Write-Allocate"); + break; + } + switch (INNER) { + case 0: + LOG_INFO("inner: Non-Cacheable"); + break; + case 1: + LOG_INFO("inner: Strongly-ordered"); + break; + case 3: + LOG_INFO("inner: Device"); + break; + case 5: + LOG_INFO("inner: Write-Back, Write-Allocate"); + break; + case 6: + LOG_INFO("inner: Write-Through"); + break; + case 7: + LOG_INFO("inner: Write-Back, no Write-Allocate"); + break; + default: + LOG_INFO("inner: %" PRIx32 " ???", INNER); + } + } + +done: + dpm->finish(dpm); + + return retval; +} + +static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe) +{ + static char bits_string[64]; + unsigned int len; + + if (afe) { + bool acc_r = true; + bool acc_w = !ap2; + bool priv = !(ap10 & 2); + len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access%s: %s%s", + s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", + priv ? "(priv)" : "", acc_r ? "R" : "N", acc_w ? "W " : "O "); + } else { + bool priv_acc_w = !ap2; + bool priv_acc_r = true; + bool unpriv_acc_w = priv_acc_w; + bool unpriv_acc_r = priv_acc_r; + + switch (ap10) { + case 0: + priv_acc_r = priv_acc_w = false; + unpriv_acc_r = unpriv_acc_w = false; + break; + case 1: + unpriv_acc_r = unpriv_acc_w = false; + break; + case 2: + unpriv_acc_w = false; + break; + default: + break; + } + + len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access(priv): %s%s access(unpriv): %s%s", + s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", priv_acc_r ? "R" : "N", priv_acc_w ? "W" : "O", + unpriv_acc_r ? "R" : "N", unpriv_acc_w ? "W" : "O"); + } + + if (len >= sizeof(bits_string)) + bits_string[63] = 0; + + return bits_string; +} + +static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe) +{ + bool c_bit = !!(l2_desc & (1 << 3)); + bool b_bit = !!(l2_desc & (1 << 2)); + bool s_bit = !!(l2_desc & (1 << 10)); + bool ap2 = !!(l2_desc & (1 << 9)); + int ap10 = (l2_desc >> 4) & 3; + + return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe); +} + +static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe) +{ + bool c_bit = !!(l1_desc & (1 << 3)); + bool b_bit = !!(l1_desc & (1 << 2)); + bool s_bit = !!(l1_desc & (1 << 16)); + bool ap2 = !!(l1_desc & (1 << 15)); + int ap10 = (l1_desc >> 10) & 3; + + return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe); +} + +COMMAND_HANDLER(armv7a_mmu_dump_table) +{ + struct target *target = get_current_target(CMD_CTX); + struct cortex_a_common *cortex_a = target_to_cortex_a(target); + struct armv7a_common *armv7a = target_to_armv7a(target); + struct armv7a_mmu_common *mmu = &armv7a->armv7a_mmu; + struct armv7a_cache_common *cache = &mmu->armv7a_cache; + uint32_t *first_lvl_ptbl; + target_addr_t ttb; + int ttbidx = 0; + int retval; + int pt_idx; + int max_pt_idx = 4095; + bool afe; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!strcmp(CMD_ARGV[0], "addr")) { + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb); + + if (CMD_ARGC > 2) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx); + + if (max_pt_idx < 1 || max_pt_idx > 4096) + return ERROR_COMMAND_ARGUMENT_INVALID; + max_pt_idx -= 1; + } + } else { + if (mmu->cached != 1) { + LOG_ERROR("TTB not cached!"); + return ERROR_FAIL; + } + + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx); + if (ttbidx < 0 || ttbidx > 1) + return ERROR_COMMAND_ARGUMENT_INVALID; + + ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx]; + + if (ttbidx == 0) { + int ttbcr_n = mmu->ttbcr & 0x7; + max_pt_idx = 0x0fff >> ttbcr_n; + } + } + + LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb); + + first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1)); + if (first_lvl_ptbl == NULL) + return ERROR_FAIL; + + /* + * this may or may not be necessary depending on whether + * the table walker is configured to use the cache or not. + */ + cache->flush_all_data_cache(target); + + retval = mmu->read_physical_memory(target, ttb, 4, max_pt_idx+1, (uint8_t *)first_lvl_ptbl); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read first-level page table!"); + return retval; + } + + afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE); + + for (pt_idx = 0; pt_idx <= max_pt_idx;) { + uint32_t first_lvl_descriptor = target_buffer_get_u32(target, + (uint8_t *)&first_lvl_ptbl[pt_idx]); + + LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor); + + /* skip empty entries in the first level table */ + if ((first_lvl_descriptor & 3) == 0) { + pt_idx++; + } else + if ((first_lvl_descriptor & 0x40002) == 2) { + /* section descriptor */ + uint32_t va_range = 1024*1024-1; /* 1MB range */ + uint32_t va_start = pt_idx << 20; + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (first_lvl_descriptor & 0xfff00000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("SECT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe)); + pt_idx++; + } else + if ((first_lvl_descriptor & 0x40002) == 0x40002) { + /* supersection descriptor */ + uint32_t va_range = 16*1024*1024-1; /* 16MB range */ + uint32_t va_start = pt_idx << 20; + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (first_lvl_descriptor & 0xff000000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("SSCT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe)); + + /* skip next 15 entries, they're duplicating the first entry */ + pt_idx += 16; + } else { + target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00; + uint32_t second_lvl_descriptor; + uint32_t *pt2; + int pt2_idx; + + /* page table, always 1KB long */ + pt2 = malloc(1024); + retval = mmu->read_physical_memory(target, second_lvl_ptbl, + 4, 256, (uint8_t *)pt2); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read second-level page table!"); + return ERROR_FAIL; + } + + for (pt2_idx = 0; pt2_idx < 256; ) { + second_lvl_descriptor = target_buffer_get_u32(target, + (uint8_t *)&pt2[pt2_idx]); + + if ((second_lvl_descriptor & 3) == 0) { + /* skip entry */ + pt2_idx++; + } else + if ((second_lvl_descriptor & 3) == 1) { + /* large page */ + uint32_t va_range = 64*1024-1; /* 64KB range */ + uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12); + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (second_lvl_descriptor & 0xffff0000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("LPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe)); + + pt2_idx += 16; + } else { + /* small page */ + uint32_t va_range = 4*1024-1; /* 4KB range */ + uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12); + uint32_t va_end = va_start + va_range; + + uint32_t pa_start = (second_lvl_descriptor & 0xfffff000); + uint32_t pa_end = pa_start + va_range; + + LOG_USER("SPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s", + va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe)); + + pt2_idx++; + } + } + free(pt2); + pt_idx++; + } + } + + free(first_lvl_ptbl); + return ERROR_OK; +} + +static const struct command_registration armv7a_mmu_group_handlers[] = { + { + .name = "dump", + .handler = armv7a_mmu_dump_table, + .mode = COMMAND_ANY, + .help = "dump translation table 0, 1 or from
", + .usage = "(0|1|addr
[num_entries])", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct command_registration armv7a_mmu_command_handlers[] = { + { + .name = "mmu", + .mode = COMMAND_ANY, + .help = "mmu command group", + .usage = "", + .chain = armv7a_mmu_group_handlers, + }, + COMMAND_REGISTRATION_DONE +}; diff --git a/src/target/armv7a_mmu.h b/src/target/armv7a_mmu.h new file mode 100644 index 000000000..4372aa8eb --- /dev/null +++ b/src/target/armv7a_mmu.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * matthias.welwarsky@sysgo.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ARMV7A_MMU_H +#define OPENOCD_TARGET_ARMV7A_MMU_H + +extern int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val); +extern int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, + uint32_t *val, int meminfo); + +extern const struct command_registration armv7a_mmu_command_handlers[]; + +#endif /* OPENOCD_TARGET_ARMV7A_MMU_H */ diff --git a/src/target/armv8.c b/src/target/armv8.c index dfa2c67a5..75ada896d 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -936,6 +936,11 @@ int armv8_mmu_translate_va_pa(struct target *target, target_addr_t va, "Secure", "Not Secure" }; + if (target->state != TARGET_HALTED) { + LOG_WARNING("target %s not halted", target_name(target)); + return ERROR_TARGET_NOT_HALTED; + } + retval = dpm->prepare(dpm); if (retval != ERROR_OK) return retval; diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index 4aae5e473..92ba54789 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -50,6 +50,7 @@ #include "breakpoints.h" #include "cortex_a.h" #include "register.h" +#include "armv7a_mmu.h" #include "target_request.h" #include "target_type.h" #include "arm_opcodes.h" @@ -57,6 +58,9 @@ #include "transport/transport.h" #include +#define foreach_smp_target(pos, head) \ + for (pos = head; (pos != NULL); pos = pos->next) + static int cortex_a_poll(struct target *target); static int cortex_a_debug_entry(struct target *target); static int cortex_a_restore_context(struct target *target, bool bpwp); @@ -68,10 +72,6 @@ static int cortex_a_set_hybrid_breakpoint(struct target *target, struct breakpoint *breakpoint); static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *breakpoint); -static int cortex_a_dap_read_coreregister_u32(struct target *target, - uint32_t *value, int regnum); -static int cortex_a_dap_write_coreregister_u32(struct target *target, - uint32_t value, int regnum); static int cortex_a_mmu(struct target *target, int *enabled); static int cortex_a_mmu_modify(struct target *target, int enable); static int cortex_a_virt2phys(struct target *target, @@ -110,7 +110,7 @@ static int cortex_a_prep_memaccess(struct target *target, int phys_access) int mmu_enabled = 0; if (phys_access == 0) { - dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); cortex_a_mmu(target, &mmu_enabled); if (mmu_enabled) cortex_a_mmu_modify(target, 1); @@ -145,7 +145,7 @@ static int cortex_a_post_memaccess(struct target *target, int phys_access) 0, 0, 3, 0, cortex_a->cp15_dacr_reg); } - dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); } else { int mmu_enabled = 0; cortex_a_mmu(target, &mmu_enabled); @@ -304,172 +304,6 @@ static int cortex_a_exec_opcode(struct target *target, return retval; } -/************************************************************************** -Read core register with very few exec_opcode, fast but needs work_area. -This can cause problems with MMU active. -**************************************************************************/ -static int cortex_a_read_regs_through_mem(struct target *target, uint32_t address, - uint32_t *regfile) -{ - int retval = ERROR_OK; - struct armv7a_common *armv7a = target_to_armv7a(target); - - retval = cortex_a_dap_read_coreregister_u32(target, regfile, 0); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_dap_write_coreregister_u32(target, address, 0); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, ARMV4_5_STMIA(0, 0xFFFE, 0, 0), NULL); - if (retval != ERROR_OK) - return retval; - - retval = mem_ap_read_buf(armv7a->memory_ap, - (uint8_t *)(®file[1]), 4, 15, address); - - return retval; -} - -static int cortex_a_dap_read_coreregister_u32(struct target *target, - uint32_t *value, int regnum) -{ - int retval = ERROR_OK; - uint8_t reg = regnum&0xFF; - uint32_t dscr = 0; - struct armv7a_common *armv7a = target_to_armv7a(target); - - if (reg > 17) - return retval; - - if (reg < 15) { - /* Rn to DCCTX, "MCR p14, 0, Rn, c0, c5, 0" 0xEE00nE15 */ - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(14, 0, reg, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } else if (reg == 15) { - /* "MOV r0, r15"; then move r0 to DCCTX */ - retval = cortex_a_exec_opcode(target, 0xE1A0000F, &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } else { - /* "MRS r0, CPSR" or "MRS r0, SPSR" - * then move r0 to DCCTX - */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRS(0, reg & 1), &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } - - /* Wait for DTRRXfull then read DTRRTX */ - int64_t then = timeval_ms(); - while ((dscr & DSCR_DTR_TX_FULL) == 0) { - retval = mem_ap_read_atomic_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DSCR, &dscr); - if (retval != ERROR_OK) - return retval; - if (timeval_ms() > then + 1000) { - LOG_ERROR("Timeout waiting for cortex_a_exec_opcode"); - return ERROR_FAIL; - } - } - - retval = mem_ap_read_atomic_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DTRTX, value); - LOG_DEBUG("read DCC 0x%08" PRIx32, *value); - - return retval; -} - -static int cortex_a_dap_write_coreregister_u32(struct target *target, - uint32_t value, int regnum) -{ - int retval = ERROR_OK; - uint8_t Rd = regnum&0xFF; - uint32_t dscr; - struct armv7a_common *armv7a = target_to_armv7a(target); - - LOG_DEBUG("register %i, value 0x%08" PRIx32, regnum, value); - - /* Check that DCCRX is not full */ - retval = mem_ap_read_atomic_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DSCR, &dscr); - if (retval != ERROR_OK) - return retval; - if (dscr & DSCR_DTR_RX_FULL) { - LOG_ERROR("DSCR_DTR_RX_FULL, dscr 0x%08" PRIx32, dscr); - /* Clear DCCRX with MRC(p14, 0, Rd, c0, c5, 0), opcode 0xEE100E15 */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - } - - if (Rd > 17) - return retval; - - /* Write DTRRX ... sets DSCR.DTRRXfull but exec_opcode() won't care */ - LOG_DEBUG("write DCC 0x%08" PRIx32, value); - retval = mem_ap_write_u32(armv7a->debug_ap, - armv7a->debug_base + CPUDBG_DTRRX, value); - if (retval != ERROR_OK) - return retval; - - if (Rd < 15) { - /* DCCRX to Rn, "MRC p14, 0, Rn, c0, c5, 0", 0xEE10nE15 */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, Rd, 0, 5, 0), - &dscr); - - if (retval != ERROR_OK) - return retval; - } else if (Rd == 15) { - /* DCCRX to R0, "MRC p14, 0, R0, c0, c5, 0", 0xEE100E15 - * then "mov r15, r0" - */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, 0xE1A0F000, &dscr); - if (retval != ERROR_OK) - return retval; - } else { - /* DCCRX to R0, "MRC p14, 0, R0, c0, c5, 0", 0xEE100E15 - * then "MSR CPSR_cxsf, r0" or "MSR SPSR_cxsf, r0" (all fields) - */ - retval = cortex_a_exec_opcode(target, ARMV4_5_MRC(14, 0, 0, 0, 5, 0), - &dscr); - if (retval != ERROR_OK) - return retval; - retval = cortex_a_exec_opcode(target, ARMV4_5_MSR_GP(0, 0xF, Rd & 1), - &dscr); - if (retval != ERROR_OK) - return retval; - - /* "Prefetch flush" after modifying execution status in CPSR */ - if (Rd == 16) { - retval = cortex_a_exec_opcode(target, - ARMV4_5_MCR(15, 0, 0, 7, 5, 4), - &dscr); - if (retval != ERROR_OK) - return retval; - } - } - - return retval; -} - /* Write to memory mapped registers directly with no cache or mmu handling */ static int cortex_a_dap_write_memap_register_u32(struct target *target, uint32_t address, @@ -806,12 +640,43 @@ static int cortex_a_halt_smp(struct target *target) static int update_halt_gdb(struct target *target) { + struct target *gdb_target = NULL; + struct target_list *head; + struct target *curr; int retval = 0; + if (target->gdb_service && target->gdb_service->core[0] == -1) { target->gdb_service->target = target; target->gdb_service->core[0] = target->coreid; retval += cortex_a_halt_smp(target); } + + if (target->gdb_service) + gdb_target = target->gdb_service->target; + + foreach_smp_target(head, target->head) { + curr = head->target; + /* skip calling context */ + if (curr == target) + continue; + if (!target_was_examined(curr)) + continue; + /* skip targets that were already halted */ + if (curr->state == TARGET_HALTED) + continue; + /* Skip gdb_target; it alerts GDB so has to be polled as last one */ + if (curr == gdb_target) + continue; + + /* avoid recursion in cortex_a_poll() */ + curr->smp = 0; + cortex_a_poll(curr); + curr->smp = 1; + } + + /* after all targets were updated, poll the gdb serving target */ + if (gdb_target != NULL && gdb_target != target) + cortex_a_poll(gdb_target); return retval; } @@ -1002,7 +867,7 @@ static int cortex_a_internal_restore(struct target *target, int current, arm->pc->valid = 1; /* restore dpm_mode at system halt */ - dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); /* called it now before restoring context because it uses cpu * register r0 for restoring cp15 control register */ retval = cortex_a_restore_cp15_control_reg(target); @@ -1149,14 +1014,11 @@ static int cortex_a_resume(struct target *target, int current, static int cortex_a_debug_entry(struct target *target) { - int i; - uint32_t regfile[16], cpsr, spsr, dscr; + uint32_t dscr; int retval = ERROR_OK; - struct working_area *regfile_working_area = NULL; struct cortex_a_common *cortex_a = target_to_cortex_a(target); struct armv7a_common *armv7a = target_to_armv7a(target); struct arm *arm = &armv7a->arm; - struct reg *reg; LOG_DEBUG("dscr = 0x%08" PRIx32, cortex_a->cpudbg_dscr); @@ -1193,68 +1055,16 @@ static int cortex_a_debug_entry(struct target *target) arm_dpm_report_wfar(&armv7a->dpm, wfar); } - /* REVISIT fast_reg_read is never set ... */ - - /* Examine target state and mode */ - if (cortex_a->fast_reg_read) - target_alloc_working_area(target, 64, ®file_working_area); - - - /* First load register acessible through core debug port*/ - if (!regfile_working_area) - retval = arm_dpm_read_current_registers(&armv7a->dpm); - else { - retval = cortex_a_read_regs_through_mem(target, - regfile_working_area->address, regfile); - - target_free_working_area(target, regfile_working_area); - if (retval != ERROR_OK) - return retval; - - /* read Current PSR */ - retval = cortex_a_dap_read_coreregister_u32(target, &cpsr, 16); - /* store current cpsr */ - if (retval != ERROR_OK) - return retval; - - LOG_DEBUG("cpsr: %8.8" PRIx32, cpsr); - - arm_set_cpsr(arm, cpsr); - - /* update cache */ - for (i = 0; i <= ARM_PC; i++) { - reg = arm_reg_current(arm, i); - - buf_set_u32(reg->value, 0, 32, regfile[i]); - reg->valid = 1; - reg->dirty = 0; - } - - /* Fixup PC Resume Address */ - if (cpsr & (1 << 5)) { - /* T bit set for Thumb or ThumbEE state */ - regfile[ARM_PC] -= 4; - } else { - /* ARM state */ - regfile[ARM_PC] -= 8; - } - - reg = arm->pc; - buf_set_u32(reg->value, 0, 32, regfile[ARM_PC]); - reg->dirty = reg->valid; - } + /* First load register accessible through core debug port */ + retval = arm_dpm_read_current_registers(&armv7a->dpm); + if (retval != ERROR_OK) + return retval; if (arm->spsr) { - /* read Saved PSR */ - retval = cortex_a_dap_read_coreregister_u32(target, &spsr, 17); - /* store current spsr */ + /* read SPSR */ + retval = arm_dpm_read_reg(&armv7a->dpm, arm->spsr, 17); if (retval != ERROR_OK) return retval; - - reg = arm->spsr; - buf_set_u32(reg->value, 0, 32, spsr); - reg->valid = 1; - reg->dirty = 0; } #if 0 @@ -1316,7 +1126,7 @@ static int cortex_a_post_debug_entry(struct target *target) cortex_a->curr_mode = armv7a->arm.core_mode; /* switch to SVC mode to read DACR */ - dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); armv7a->arm.mrc(target, 15, 0, 0, 3, 0, &cortex_a->cp15_dacr_reg); @@ -1324,7 +1134,7 @@ static int cortex_a_post_debug_entry(struct target *target) LOG_DEBUG("cp15_dacr_reg: %8.8" PRIx32, cortex_a->cp15_dacr_reg); - dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); + arm_dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); return ERROR_OK; } @@ -1386,6 +1196,7 @@ static int cortex_a_step(struct target *target, int current, target_addr_t addre /* Setup single step breakpoint */ stepbreakpoint.address = address; + stepbreakpoint.asid = 0; stepbreakpoint.length = (arm->core_state == ARM_STATE_THUMB) ? 2 : 4; stepbreakpoint.type = BKPT_HARD; @@ -2660,9 +2471,6 @@ static int cortex_a_read_phys_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; int retval; if (!count || !buffer) @@ -2671,9 +2479,6 @@ static int cortex_a_read_phys_memory(struct target *target, LOG_DEBUG("Reading memory at real address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, address, size, count); - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) - return mem_ap_read_buf(armv7a->memory_ap, buffer, size, count, address); - /* read memory through the CPU */ cortex_a_prep_memaccess(target, 1); retval = cortex_a_read_cpu_memory(target, address, size, count, buffer); @@ -2698,57 +2503,10 @@ static int cortex_a_read_memory(struct target *target, target_addr_t address, return retval; } -static int cortex_a_read_memory_ahb(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer) -{ - int mmu_enabled = 0; - target_addr_t virt, phys; - int retval; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; - - if (!armv7a->memory_ap_available || (apsel != armv7a->memory_ap->ap_num)) - return target_read_memory(target, address, size, count, buffer); - - /* cortex_a handles unaligned memory access */ - LOG_DEBUG("Reading memory at address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, - address, size, count); - - /* determine if MMU was enabled on target stop */ - if (!armv7a->is_armv7r) { - retval = cortex_a_mmu(target, &mmu_enabled); - if (retval != ERROR_OK) - return retval; - } - - if (mmu_enabled) { - virt = address; - retval = cortex_a_virt2phys(target, virt, &phys); - if (retval != ERROR_OK) - return retval; - - LOG_DEBUG("Reading at virtual address. " - "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT, - virt, phys); - address = phys; - } - - if (!count || !buffer) - return ERROR_COMMAND_SYNTAX_ERROR; - - retval = mem_ap_read_buf(armv7a->memory_ap, buffer, size, count, address); - - return retval; -} - static int cortex_a_write_phys_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; int retval; if (!count || !buffer) @@ -2757,9 +2515,6 @@ static int cortex_a_write_phys_memory(struct target *target, LOG_DEBUG("Writing memory to real address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, address, size, count); - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) - return mem_ap_write_buf(armv7a->memory_ap, buffer, size, count, address); - /* write memory through the CPU */ cortex_a_prep_memaccess(target, 1); retval = cortex_a_write_cpu_memory(target, address, size, count, buffer); @@ -2786,51 +2541,6 @@ static int cortex_a_write_memory(struct target *target, target_addr_t address, return retval; } -static int cortex_a_write_memory_ahb(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) -{ - int mmu_enabled = 0; - target_addr_t virt, phys; - int retval; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; - - if (!armv7a->memory_ap_available || (apsel != armv7a->memory_ap->ap_num)) - return target_write_memory(target, address, size, count, buffer); - - /* cortex_a handles unaligned memory access */ - LOG_DEBUG("Writing memory at address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32, - address, size, count); - - /* determine if MMU was enabled on target stop */ - if (!armv7a->is_armv7r) { - retval = cortex_a_mmu(target, &mmu_enabled); - if (retval != ERROR_OK) - return retval; - } - - if (mmu_enabled) { - virt = address; - retval = cortex_a_virt2phys(target, virt, &phys); - if (retval != ERROR_OK) - return retval; - - LOG_DEBUG("Writing to virtual address. " - "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT, - virt, - phys); - address = phys; - } - - if (!count || !buffer) - return ERROR_COMMAND_SYNTAX_ERROR; - - retval = mem_ap_write_buf(armv7a->memory_ap, buffer, size, count, address); - - return retval; -} - static int cortex_a_read_buffer(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer) { @@ -2840,7 +2550,7 @@ static int cortex_a_read_buffer(struct target *target, target_addr_t address, * will have something to do with the size we leave to it. */ for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) { if (address & size) { - int retval = cortex_a_read_memory_ahb(target, address, size, 1, buffer); + int retval = target_read_memory(target, address, size, 1, buffer); if (retval != ERROR_OK) return retval; address += size; @@ -2853,7 +2563,7 @@ static int cortex_a_read_buffer(struct target *target, target_addr_t address, for (; size > 0; size /= 2) { uint32_t aligned = count - count % size; if (aligned > 0) { - int retval = cortex_a_read_memory_ahb(target, address, size, aligned / size, buffer); + int retval = target_read_memory(target, address, size, aligned / size, buffer); if (retval != ERROR_OK) return retval; address += aligned; @@ -2874,7 +2584,7 @@ static int cortex_a_write_buffer(struct target *target, target_addr_t address, * will have something to do with the size we leave to it. */ for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) { if (address & size) { - int retval = cortex_a_write_memory_ahb(target, address, size, 1, buffer); + int retval = target_write_memory(target, address, size, 1, buffer); if (retval != ERROR_OK) return retval; address += size; @@ -2887,7 +2597,7 @@ static int cortex_a_write_buffer(struct target *target, target_addr_t address, for (; size > 0; size /= 2) { uint32_t aligned = count - count % size; if (aligned > 0) { - int retval = cortex_a_write_memory_ahb(target, address, size, aligned / size, buffer); + int retval = target_write_memory(target, address, size, aligned / size, buffer); if (retval != ERROR_OK) return retval; address += aligned; @@ -2965,21 +2675,6 @@ static int cortex_a_examine_first(struct target *target) armv7a->debug_ap->memaccess_tck = 80; - /* Search for the AHB-AB. - * REVISIT: We should search for AXI-AP as well and make sure the AP's MEMTYPE says it - * can access system memory. */ - armv7a->memory_ap_available = false; - retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7a->memory_ap); - if (retval == ERROR_OK) { - retval = mem_ap_init(armv7a->memory_ap); - if (retval == ERROR_OK) - armv7a->memory_ap_available = true; - } - if (retval != ERROR_OK) { - /* AHB-AP not found or unavailable - use the CPU */ - LOG_DEBUG("No AHB-AP available for memory access"); - } - if (!target->dbgbase_set) { uint32_t dbgbase; /* Get ROM Table base */ @@ -3139,8 +2834,6 @@ static int cortex_a_init_arch_info(struct target *target, cortex_a->common_magic = CORTEX_A_COMMON_MAGIC; armv7a->arm.dap = dap; - cortex_a->fast_reg_read = 0; - /* register arch-specific functions */ armv7a->examine_debug_reason = NULL; @@ -3225,10 +2918,7 @@ static int cortex_a_mmu(struct target *target, int *enabled) static int cortex_a_virt2phys(struct target *target, target_addr_t virt, target_addr_t *phys) { - int retval = ERROR_FAIL; - struct armv7a_common *armv7a = target_to_armv7a(target); - struct adiv5_dap *swjdp = armv7a->arm.dap; - uint8_t apsel = swjdp->apsel; + int retval; int mmu_enabled = 0; /* @@ -3243,23 +2933,12 @@ static int cortex_a_virt2phys(struct target *target, return ERROR_OK; } - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) { - uint32_t ret; - retval = armv7a_mmu_translate_va(target, - virt, &ret); - if (retval != ERROR_OK) - goto done; - *phys = ret; - } else {/* use this method if armv7a->memory_ap not selected - * mmu must be enable in order to get a correct translation */ - retval = cortex_a_mmu_modify(target, 1); - if (retval != ERROR_OK) - goto done; - retval = armv7a_mmu_translate_va_pa(target, (uint32_t)virt, + /* mmu must be enable in order to get a correct translation */ + retval = cortex_a_mmu_modify(target, 1); + if (retval != ERROR_OK) + return retval; + return armv7a_mmu_translate_va_pa(target, (uint32_t)virt, (uint32_t *)phys, 1); - } -done: - return retval; } COMMAND_HANDLER(cortex_a_handle_cache_info_command) @@ -3443,6 +3122,9 @@ static const struct command_registration cortex_a_exec_command_handlers[] = { "on memory access", .usage = "['on'|'off']", }, + { + .chain = armv7a_mmu_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/cortex_a.h b/src/target/cortex_a.h index ff0343208..197a5992d 100644 --- a/src/target/cortex_a.h +++ b/src/target/cortex_a.h @@ -93,9 +93,6 @@ struct cortex_a_common { int brp_num_available; struct cortex_a_brp *brp_list; - /* Use cortex_a_read_regs_through_mem for fast register reads */ - int fast_reg_read; - uint32_t cpuid; uint32_t didr; diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index ca3dbec78..07fea5137 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -42,7 +42,7 @@ /* NOTE: most of this should work fine for the Cortex-M1 and * Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M. - * Some differences: M0/M1 doesn't have FBP remapping or the + * Some differences: M0/M1 doesn't have FPB remapping or the * DWT tracing/profiling support. (So the cycle counter will * not be usable; the other stuff isn't currently used here.) * @@ -255,7 +255,7 @@ static int cortex_m_endreset_event(struct target *target) return retval; /* Paranoia: evidently some (early?) chips don't preserve all the - * debug state (including FBP, DWT, etc) across reset... + * debug state (including FPB, DWT, etc) across reset... */ /* Enable FPB */ @@ -1277,7 +1277,7 @@ int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpo { struct cortex_m_common *cortex_m = target_to_cm(target); - /* REVISIT why check? FBP can be updated with core running ... */ + /* REVISIT why check? FPB can be updated with core running ... */ if (target->state != TARGET_HALTED) { LOG_WARNING("target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -1986,7 +1986,7 @@ int cortex_m_examine(struct target *target) /* stlink shares the examine handler but does not support * all its calls */ if (!armv7m->stlink) { - if (cortex_m->apsel < 0) { + if (cortex_m->apsel == DP_APSEL_INVALID) { /* Search for the MEM-AP */ retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7m->debug_ap); if (retval != ERROR_OK) { diff --git a/src/target/esirisc.c b/src/target/esirisc.c new file mode 100644 index 000000000..38100e834 --- /dev/null +++ b/src/target/esirisc.c @@ -0,0 +1,1787 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esirisc.h" + +#define RESET_TIMEOUT 5000 /* 5s */ +#define STEP_TIMEOUT 1000 /* 1s */ + +/* + * eSi-RISC targets support a configurable number of interrupts; + * up to 32 interrupts are supported. + */ +static const char * const esirisc_exceptions[] = { + "Reset", "HardwareFailure", "NMI", "InstBreakpoint", "DataBreakpoint", + "Unsupported", "PrivilegeViolation", "InstBusError", "DataBusError", + "AlignmentError", "ArithmeticError", "SystemCall", "MemoryManagement", + "Unrecoverable", "Reserved", + + "Interrupt0", "Interrupt1", "Interrupt2", "Interrupt3", + "Interrupt4", "Interrupt5", "Interrupt6", "Interrupt7", + "Interrupt8", "Interrupt9", "Interrupt10", "Interrupt11", + "Interrupt12", "Interrupt13", "Interrupt14", "Interrupt15", + "Interrupt16", "Interrupt17", "Interrupt18", "Interrupt19", + "Interrupt20", "Interrupt21", "Interrupt22", "Interrupt23", + "Interrupt24", "Interrupt25", "Interrupt26", "Interrupt27", + "Interrupt28", "Interrupt29", "Interrupt30", "Interrupt31", +}; + +/* + * eSi-RISC targets support a configurable number of general purpose + * registers; 8, 16, and 32 registers are supported. + */ +static const struct { + enum esirisc_reg_num number; + const char *name; + enum reg_type type; + const char *group; +} esirisc_regs[] = { + { ESIRISC_SP, "sp", REG_TYPE_DATA_PTR, "general" }, + { ESIRISC_RA, "ra", REG_TYPE_INT, "general" }, + { ESIRISC_R2, "r2", REG_TYPE_INT, "general" }, + { ESIRISC_R3, "r3", REG_TYPE_INT, "general" }, + { ESIRISC_R4, "r4", REG_TYPE_INT, "general" }, + { ESIRISC_R5, "r5", REG_TYPE_INT, "general" }, + { ESIRISC_R6, "r6", REG_TYPE_INT, "general" }, + { ESIRISC_R7, "r7", REG_TYPE_INT, "general" }, + { ESIRISC_R8, "r8", REG_TYPE_INT, "general" }, + { ESIRISC_R9, "r9", REG_TYPE_INT, "general" }, + { ESIRISC_R10, "r10", REG_TYPE_INT, "general" }, + { ESIRISC_R11, "r11", REG_TYPE_INT, "general" }, + { ESIRISC_R12, "r12", REG_TYPE_INT, "general" }, + { ESIRISC_R13, "r13", REG_TYPE_INT, "general" }, + { ESIRISC_R14, "r14", REG_TYPE_INT, "general" }, + { ESIRISC_R15, "r15", REG_TYPE_INT, "general" }, + { ESIRISC_R16, "r16", REG_TYPE_INT, "general" }, + { ESIRISC_R17, "r17", REG_TYPE_INT, "general" }, + { ESIRISC_R18, "r18", REG_TYPE_INT, "general" }, + { ESIRISC_R19, "r19", REG_TYPE_INT, "general" }, + { ESIRISC_R20, "r20", REG_TYPE_INT, "general" }, + { ESIRISC_R21, "r21", REG_TYPE_INT, "general" }, + { ESIRISC_R22, "r22", REG_TYPE_INT, "general" }, + { ESIRISC_R23, "r23", REG_TYPE_INT, "general" }, + { ESIRISC_R24, "r24", REG_TYPE_INT, "general" }, + { ESIRISC_R25, "r25", REG_TYPE_INT, "general" }, + { ESIRISC_R26, "r26", REG_TYPE_INT, "general" }, + { ESIRISC_R27, "r27", REG_TYPE_INT, "general" }, + { ESIRISC_R28, "r28", REG_TYPE_INT, "general" }, + { ESIRISC_R29, "r29", REG_TYPE_INT, "general" }, + { ESIRISC_R30, "r30", REG_TYPE_INT, "general" }, + { ESIRISC_R31, "r31", REG_TYPE_INT, "general" }, +}; + +/* + * Control and Status Registers (CSRs) are largely defined as belonging + * to the system register group. The exception to this rule are the PC + * and CAS registers, which belong to the general group. While debug is + * active, EPC, ECAS, and ETC must be used to read and write the PC, + * CAS, and TC CSRs, respectively. + */ +static const struct { + enum esirisc_reg_num number; + uint8_t bank; + uint8_t csr; + const char *name; + enum reg_type type; + const char *group; +} esirisc_csrs[] = { + { ESIRISC_PC, CSR_THREAD, CSR_THREAD_EPC, "PC", REG_TYPE_CODE_PTR, "general" }, /* PC -> EPC */ + { ESIRISC_CAS, CSR_THREAD, CSR_THREAD_ECAS, "CAS", REG_TYPE_INT, "general" }, /* CAS -> ECAS */ + { ESIRISC_TC, CSR_THREAD, CSR_THREAD_ETC, "TC", REG_TYPE_INT, "system" }, /* TC -> ETC */ + { ESIRISC_ETA, CSR_THREAD, CSR_THREAD_ETA, "ETA", REG_TYPE_INT, "system" }, + { ESIRISC_ETC, CSR_THREAD, CSR_THREAD_ETC, "ETC", REG_TYPE_INT, "system" }, + { ESIRISC_EPC, CSR_THREAD, CSR_THREAD_EPC, "EPC", REG_TYPE_CODE_PTR, "system" }, + { ESIRISC_ECAS, CSR_THREAD, CSR_THREAD_ECAS, "ECAS", REG_TYPE_INT, "system" }, + { ESIRISC_EID, CSR_THREAD, CSR_THREAD_EID, "EID", REG_TYPE_INT, "system" }, + { ESIRISC_ED, CSR_THREAD, CSR_THREAD_ED, "ED", REG_TYPE_INT, "system" }, + { ESIRISC_IP, CSR_INTERRUPT, CSR_INTERRUPT_IP, "IP", REG_TYPE_INT, "system"}, + { ESIRISC_IM, CSR_INTERRUPT, CSR_INTERRUPT_IM, "IM", REG_TYPE_INT, "system"}, + { ESIRISC_IS, CSR_INTERRUPT, CSR_INTERRUPT_IS, "IS", REG_TYPE_INT, "system"}, + { ESIRISC_IT, CSR_INTERRUPT, CSR_INTERRUPT_IT, "IT", REG_TYPE_INT, "system"}, +}; + +static int esirisc_disable_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t etc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETC", target_name(target)); + return retval; + } + + etc &= ~(1<<0); /* TC.I */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +#if 0 +static int esirisc_enable_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t etc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETC", target_name(target)); + return retval; + } + + etc |= (1<<0); /* TC.I */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} +#endif + +static int esirisc_save_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, + &esirisc->etc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_restore_interrupts(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, + esirisc->etc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: ETC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +#if 0 +static int esirisc_save_hwdc(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC, + &esirisc->hwdc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: HWDC", target_name(target)); + return retval; + } + + return ERROR_OK; +} +#endif + +static int esirisc_restore_hwdc(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC, + esirisc->hwdc_save); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: HWDC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_save_context(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + for (unsigned i = 0; i < esirisc->reg_cache->num_regs; ++i) { + struct reg *reg = esirisc->reg_cache->reg_list + i; + struct esirisc_reg *reg_info = reg->arch_info; + + if (reg->exist && !reg->valid) + reg_info->read(reg); + } + + return ERROR_OK; +} + +static int esirisc_restore_context(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + for (unsigned i = 0; i < esirisc->reg_cache->num_regs; ++i) { + struct reg *reg = esirisc->reg_cache->reg_list + i; + struct esirisc_reg *reg_info = reg->arch_info; + + if (reg->exist && reg->dirty) + reg_info->write(reg); + } + + return ERROR_OK; +} + +static int esirisc_flush_caches(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + int retval = esirisc_jtag_flush_caches(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to flush caches", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_wait_debug_active(struct esirisc_common *esirisc, int ms) +{ + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int64_t t; + + LOG_DEBUG("-"); + + t = timeval_ms(); + for (;;) { + int retval = esirisc_jtag_enable_debug(jtag_info); + if (retval == ERROR_OK && esirisc_jtag_is_debug_active(jtag_info)) + return retval; + + if ((timeval_ms() - t) > ms) + return ERROR_TARGET_TIMEOUT; + + alive_sleep(100); + } +} + +static int esirisc_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + int num_bits = 8 * size; + for (uint32_t i = 0; i < count; ++i) { + union esirisc_memory value; + void *value_p; + + switch (size) { + case sizeof(value.word): + value_p = &value.word; + retval = esirisc_jtag_read_word(jtag_info, address, value_p); + break; + + case sizeof(value.hword): + value_p = &value.hword; + retval = esirisc_jtag_read_hword(jtag_info, address, value_p); + break; + + case sizeof(value.byte): + value_p = &value.byte; + retval = esirisc_jtag_read_byte(jtag_info, address, value_p); + break; + + default: + LOG_ERROR("%s: unsupported size: %" PRIu32, target_name(target), size); + return ERROR_FAIL; + } + + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read address: 0x%" TARGET_PRIxADDR, target_name(target), + address); + return retval; + } + + buf_cpy(value_p, buffer, num_bits); + address += size; + buffer += size; + } + + return ERROR_OK; +} + +static int esirisc_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + int num_bits = 8 * size; + for (uint32_t i = 0; i < count; ++i) { + union esirisc_memory value; + + switch (size) { + case sizeof(value.word): + value.word = buf_get_u32(buffer, 0, num_bits); + retval = esirisc_jtag_write_word(jtag_info, address, value.word); + break; + + case sizeof(value.hword): + value.hword = buf_get_u32(buffer, 0, num_bits); + retval = esirisc_jtag_write_hword(jtag_info, address, value.hword); + break; + + case sizeof(value.byte): + value.byte = buf_get_u32(buffer, 0, num_bits); + retval = esirisc_jtag_write_byte(jtag_info, address, value.byte); + break; + + default: + LOG_ERROR("%s: unsupported size: %" PRIu32, target_name(target), size); + return ERROR_FAIL; + } + + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write address: 0x%" TARGET_PRIxADDR, target_name(target), + address); + return retval; + } + + address += size; + buffer += size; + } + + return ERROR_OK; +} + +static int esirisc_checksum_memory(struct target *target, target_addr_t address, + uint32_t count, uint32_t *checksum) +{ + return ERROR_FAIL; /* not supported */ +} + +static int esirisc_next_breakpoint(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct breakpoint **breakpoints_p = esirisc->breakpoints_p; + struct breakpoint **breakpoints_e = breakpoints_p + esirisc->num_breakpoints; + + LOG_DEBUG("-"); + + for (int bp_index = 0; breakpoints_p < breakpoints_e; ++breakpoints_p, ++bp_index) + if (*breakpoints_p == NULL) + return bp_index; + + return -1; +} + +static int esirisc_add_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int bp_index; + uint32_t ibc; + int retval; + + LOG_DEBUG("-"); + + /* + * The default linker scripts provided by the eSi-RISC toolchain do + * not specify attributes on memory regions, which results in + * incorrect application of software breakpoints by GDB. Targets + * must be configured with `gdb_breakpoint_override hard` as + * software breakpoints are not supported. + */ + if (breakpoint->type != BKPT_HARD) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + bp_index = esirisc_next_breakpoint(target); + if (bp_index < 0) { + LOG_ERROR("%s: out of hardware breakpoints", target_name(target)); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + breakpoint->set = bp_index + 1; + esirisc->breakpoints_p[bp_index] = breakpoint; + + /* specify instruction breakpoint address */ + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBAn + bp_index, + breakpoint->address); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBA", target_name(target)); + return retval; + } + + /* enable instruction breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: IBC", target_name(target)); + return retval; + } + + ibc |= (1 << bp_index); /* IBC.In */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_add_breakpoints(struct target *target) +{ + struct breakpoint *breakpoint = target->breakpoints; + + LOG_DEBUG("-"); + + while (breakpoint != NULL) { + if (breakpoint->set == 0) + esirisc_add_breakpoint(target, breakpoint); + + breakpoint = breakpoint->next; + } + + return ERROR_OK; +} + +static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int bp_index = breakpoint->set - 1; + uint32_t ibc; + int retval; + + LOG_DEBUG("-"); + + /* disable instruction breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: IBC", target_name(target)); + return retval; + } + + ibc &= ~(1 << bp_index); /* IBC.In */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBC", target_name(target)); + return retval; + } + + esirisc->breakpoints_p[bp_index] = NULL; + breakpoint->set = 0; + + return ERROR_OK; +} + +static int esirisc_remove_breakpoints(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + /* clear instruction breakpoints */ + int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, 0); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: IBC", target_name(target)); + return retval; + } + + memset(esirisc->breakpoints_p, 0, sizeof(esirisc->breakpoints_p)); + + return ERROR_OK; +} + +static int esirisc_next_watchpoint(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct watchpoint **watchpoints_p = esirisc->watchpoints_p; + struct watchpoint **watchpoints_e = watchpoints_p + esirisc->num_watchpoints; + + LOG_DEBUG("-"); + + for (int wp_index = 0; watchpoints_p < watchpoints_e; ++watchpoints_p, ++wp_index) + if (*watchpoints_p == NULL) + return wp_index; + + return -1; +} + +static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watchpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int wp_index; + uint32_t dbs, dbc; + int retval; + + LOG_DEBUG("-"); + + wp_index = esirisc_next_watchpoint(target); + if (wp_index < 0) { + LOG_ERROR("%s: out of hardware watchpoints", target_name(target)); + return ERROR_FAIL; + } + + watchpoint->set = wp_index + 1; + esirisc->watchpoints_p[wp_index] = watchpoint; + + /* specify data breakpoint address */ + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBAn + wp_index, + watchpoint->address); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBA", target_name(target)); + return retval; + } + + /* specify data breakpoint size */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, &dbs); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBS", target_name(target)); + return retval; + } + + uint32_t sn; + switch (watchpoint->length) { + case sizeof(uint64_t): + sn = 0x3; + break; + case sizeof(uint32_t): + sn = 0x2; + break; + + case sizeof(uint16_t): + sn = 0x1; + break; + + case sizeof(uint8_t): + sn = 0x0; + break; + + default: + LOG_ERROR("%s: unsupported length: %" PRIu32, target_name(target), + watchpoint->length); + return ERROR_FAIL; + } + + dbs |= (sn << (2 * wp_index)); /* DBS.Sn */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, dbs); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBS", target_name(target)); + return retval; + } + + /* enable data breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBC", target_name(target)); + return retval; + } + + uint32_t dn; + switch (watchpoint->rw) { + case WPT_READ: + dn = 0x1; + break; + + case WPT_WRITE: + dn = 0x2; + break; + + case WPT_ACCESS: + dn = 0x3; + break; + + default: + LOG_ERROR("%s: unsupported rw: %" PRId32, target_name(target), + watchpoint->rw); + return ERROR_FAIL; + } + + dbc |= (dn << (2 * wp_index)); /* DBC.Dn */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_add_watchpoints(struct target *target) +{ + struct watchpoint *watchpoint = target->watchpoints; + + LOG_DEBUG("-"); + + while (watchpoint != NULL) { + if (watchpoint->set == 0) + esirisc_add_watchpoint(target, watchpoint); + + watchpoint = watchpoint->next; + } + + return ERROR_OK; +} + +static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *watchpoint) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int wp_index = watchpoint->set - 1; + uint32_t dbc; + int retval; + + LOG_DEBUG("-"); + + /* disable data breakpoint */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBC", target_name(target)); + return retval; + } + + dbc &= ~(0x3 << (2 * wp_index)); /* DBC.Dn */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBC", target_name(target)); + return retval; + } + + esirisc->watchpoints_p[wp_index] = NULL; + watchpoint->set = 0; + + return ERROR_OK; +} + +static int esirisc_remove_watchpoints(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + /* clear data breakpoints */ + int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, 0); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DBC", target_name(target)); + return retval; + } + + memset(esirisc->watchpoints_p, 0, sizeof(esirisc->watchpoints_p)); + + return ERROR_OK; +} + +static int esirisc_halt(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + + LOG_DEBUG("-"); + + if (target->state == TARGET_HALTED) + return ERROR_OK; + + int retval = esirisc_jtag_break(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to halt target", target_name(target)); + return retval; + } + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +static int esirisc_disable_step(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t dc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DC", target_name(target)); + return retval; + } + + dc &= ~(1<<0); /* DC.S */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_enable_step(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t dc; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DC", target_name(target)); + return retval; + } + + dc |= (1<<0); /* DC.S */ + + retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: DC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_resume_or_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution, bool step) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct breakpoint *breakpoint = NULL; + int retval; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + if (!debug_execution) { + target_free_all_working_areas(target); + esirisc_add_breakpoints(target); + esirisc_add_watchpoints(target); + } + + if (current) + address = buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size); + else { + buf_set_u32(esirisc->epc->value, 0, esirisc->epc->size, address); + esirisc->epc->dirty = true; + esirisc->epc->valid = true; + } + + esirisc_restore_context(target); + + if (esirisc_has_cache(esirisc)) + esirisc_flush_caches(target); + + if (handle_breakpoints) { + breakpoint = breakpoint_find(target, address); + if (breakpoint != NULL) + esirisc_remove_breakpoint(target, breakpoint); + } + + if (step) { + esirisc_disable_interrupts(target); + esirisc_enable_step(target); + target->debug_reason = DBG_REASON_SINGLESTEP; + } else { + esirisc_disable_step(target); + esirisc_restore_interrupts(target); + target->debug_reason = DBG_REASON_NOTHALTED; + } + + esirisc_restore_hwdc(target); + + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + + register_cache_invalidate(esirisc->reg_cache); + + if (!debug_execution) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } else { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + } + + return ERROR_OK; +} + +static int esirisc_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + LOG_DEBUG("-"); + + return esirisc_resume_or_step(target, current, address, + handle_breakpoints, debug_execution, false); +} + +static int esirisc_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints) +{ + LOG_DEBUG("-"); + + return esirisc_resume_or_step(target, current, address, + handle_breakpoints, 0, true); +} + +static int esirisc_debug_step(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + esirisc_disable_interrupts(target); + esirisc_enable_step(target); + + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + + retval = esirisc_wait_debug_active(esirisc, STEP_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: step timed out", target_name(target)); + return retval; + } + + esirisc_disable_step(target); + esirisc_restore_interrupts(target); + + return ERROR_OK; +} + +static int esirisc_debug_reset(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_assert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to assert reset", target_name(target)); + return retval; + } + + retval = esirisc_jtag_deassert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to deassert reset", target_name(target)); + return retval; + } + + retval = esirisc_wait_debug_active(esirisc, RESET_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: reset timed out", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_debug_enable(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_enable_debug(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to enable debug mode", target_name(target)); + return retval; + } + + /* + * The debug clock is inactive until the first command is sent. + * If the target is stopped, we must first issue a reset before + * attempting further communication. This also handles unpowered + * targets, which will respond with all ones and appear active. + */ + if (esirisc_jtag_is_stopped(jtag_info)) { + LOG_INFO("%s: debug clock inactive; attempting debug reset", target_name(target)); + retval = esirisc_debug_reset(target); + if (retval != ERROR_OK) + return retval; + + if (esirisc_jtag_is_stopped(jtag_info)) { + LOG_ERROR("%s: target unresponsive; giving up", target_name(target)); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int esirisc_debug_entry(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct breakpoint *breakpoint; + + LOG_DEBUG("-"); + + esirisc_save_context(target); + + if (esirisc_has_cache(esirisc)) + esirisc_flush_caches(target); + + if (target->debug_reason != DBG_REASON_SINGLESTEP) { + esirisc_save_interrupts(target); + + uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size); + switch (eid) { + /* + * InstBreakpoint exceptions are also raised when a core is + * halted for debugging. The following is required to + * determine if a breakpoint was encountered. + */ + case EID_INST_BREAKPOINT: + breakpoint = breakpoint_find(target, + buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size)); + target->debug_reason = (breakpoint != NULL) ? + DBG_REASON_BREAKPOINT : DBG_REASON_DBGRQ; + break; + + /* + * eSi-RISC treats watchpoints similarly to breakpoints, + * however GDB will not request to step over the current + * instruction when a watchpoint fires. The following is + * required to resume the target. + */ + case EID_DATA_BREAKPOINT: + esirisc_remove_watchpoints(target); + esirisc_debug_step(target); + esirisc_add_watchpoints(target); + target->debug_reason = DBG_REASON_WATCHPOINT; + break; + + default: + target->debug_reason = DBG_REASON_DBGRQ; + } + } + + return ERROR_OK; +} + +static int esirisc_poll(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + retval = esirisc_jtag_enable_debug(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to poll target", target_name(target)); + return retval; + } + + if (esirisc_jtag_is_stopped(jtag_info)) { + LOG_ERROR("%s: target has stopped; reset required", target_name(target)); + target->state = TARGET_UNKNOWN; + return ERROR_TARGET_FAILURE; + } + + if (esirisc_jtag_is_debug_active(jtag_info)) { + if (target->state == TARGET_RUNNING || target->state == TARGET_RESET) { + target->state = TARGET_HALTED; + + retval = esirisc_debug_entry(target); + if (retval != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + + } else if (target->state == TARGET_HALTED || target->state == TARGET_RESET) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } + + return ERROR_OK; +} + +static int esirisc_assert_reset(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + if (jtag_get_reset_config() & RESET_HAS_SRST) { + jtag_add_reset(1, 1); + if ((jtag_get_reset_config() & RESET_SRST_PULLS_TRST) == 0) + jtag_add_reset(0, 1); + } else { + esirisc_remove_breakpoints(target); + esirisc_remove_watchpoints(target); + + retval = esirisc_jtag_assert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to assert reset", target_name(target)); + return retval; + } + } + + target->state = TARGET_RESET; + + register_cache_invalidate(esirisc->reg_cache); + + return ERROR_OK; +} + +static int esirisc_reset_entry(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t eta, epc; + int retval; + + LOG_DEBUG("-"); + + /* read exception table address */ + retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETA, &eta); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ETA", target_name(target)); + return retval; + } + + /* read reset entry point */ + retval = esirisc_jtag_read_word(jtag_info, eta + ENTRY_RESET, &epc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read address: 0x%" TARGET_PRIxADDR, target_name(target), + (target_addr_t)epc); + return retval; + } + + /* write reset entry point */ + retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_EPC, epc); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: EPC", target_name(target)); + return retval; + } + + return ERROR_OK; +} + +static int esirisc_deassert_reset(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + if (jtag_get_reset_config() & RESET_HAS_SRST) { + jtag_add_reset(0, 0); + + retval = esirisc_debug_enable(target); + if (retval != ERROR_OK) + return retval; + + retval = esirisc_debug_reset(target); + if (retval != ERROR_OK) + return retval; + + } else { + retval = esirisc_jtag_deassert_reset(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to deassert reset", target_name(target)); + return retval; + } + } + + retval = esirisc_wait_debug_active(esirisc, RESET_TIMEOUT); + if (retval != ERROR_OK) { + LOG_ERROR("%s: reset timed out", target_name(target)); + return retval; + } + + retval = esirisc_reset_entry(target); + if (retval != ERROR_OK) + return retval; + + esirisc_add_breakpoints(target); + esirisc_add_watchpoints(target); + + esirisc_restore_hwdc(target); + + if (!target->reset_halt) { + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + } + + return ERROR_OK; +} + +static int esirisc_arch_state(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + uint32_t epc = buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size); + uint32_t ecas = buf_get_u32(esirisc->ecas->value, 0, esirisc->ecas->size); + uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size); + uint32_t ed = buf_get_u32(esirisc->ed->value, 0, esirisc->ed->size); + + LOG_DEBUG("-"); + + const char *exception = "Unknown"; + if (eid < ARRAY_SIZE(esirisc_exceptions)) + exception = esirisc_exceptions[eid]; + + LOG_USER("target halted due to %s, exception: %s\n" + "EPC: 0x%" PRIx32 " ECAS: 0x%" PRIx32 " EID: 0x%" PRIx32 " ED: 0x%" PRIx32, + debug_reason_name(target), exception, epc, ecas, eid, ed); + + return ERROR_OK; +} + +static const char *esirisc_get_gdb_arch(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + /* + * Targets with the UNIFIED_ADDRESS_SPACE option disabled employ a + * Harvard architecture. This option is not exposed in a CSR, which + * requires additional configuration to properly interact with these + * targets in GDB (also see: `esirisc cache_arch`). + */ + if (esirisc->gdb_arch == NULL && target_was_examined(target)) + esirisc->gdb_arch = alloc_printf("esirisc:%d_bit_%d_reg_%s", + esirisc->num_bits, esirisc->num_regs, esirisc_cache_arch(esirisc)); + + return esirisc->gdb_arch; +} + +static int esirisc_get_gdb_reg_list(struct target *target, struct reg **reg_list[], + int *reg_list_size, enum target_register_class reg_class) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + LOG_DEBUG("-"); + + *reg_list_size = ESIRISC_NUM_REGS; + + *reg_list = calloc(*reg_list_size, sizeof(struct reg *)); + if (*reg_list == NULL) + return ERROR_FAIL; + + if (reg_class == REG_CLASS_ALL) + for (int i = 0; i < *reg_list_size; ++i) + (*reg_list)[i] = esirisc->reg_cache->reg_list + i; + else { + for (int i = 0; i < esirisc->num_regs; ++i) + (*reg_list)[i] = esirisc->reg_cache->reg_list + i; + + (*reg_list)[ESIRISC_PC] = esirisc->reg_cache->reg_list + ESIRISC_PC; + (*reg_list)[ESIRISC_CAS] = esirisc->reg_cache->reg_list + ESIRISC_CAS; + } + + return ERROR_OK; +} + +static int esirisc_read_reg(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_reg(jtag_info, reg->number, &data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read register: %s", target_name(target), reg->name); + return retval; + } + + buf_set_u32(reg->value, 0, reg->size, data); + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_write_reg(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data = buf_get_u32(reg->value, 0, reg->size); + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_reg(jtag_info, reg->number, data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write register: %s", target_name(target), reg->name); + return retval; + } + + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_read_csr(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data; + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_read_csr(jtag_info, reg_info->bank, reg_info->csr, &data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: %s", target_name(target), reg->name); + return retval; + } + + buf_set_u32(reg->value, 0, reg->size, data); + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_write_csr(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + struct target *target = esirisc->target; + uint32_t data = buf_get_u32(reg->value, 0, reg->size); + + LOG_DEBUG("-"); + + int retval = esirisc_jtag_write_csr(jtag_info, reg_info->bank, reg_info->csr, data); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to write CSR: %s", target_name(target), reg->name); + return retval; + } + + reg->dirty = false; + reg->valid = true; + + return ERROR_OK; +} + +static int esirisc_get_reg(struct reg *reg) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct target *target = esirisc->target; + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + return reg_info->read(reg); +} + +static int esirisc_set_reg(struct reg *reg, uint8_t *buf) +{ + struct esirisc_reg *reg_info = reg->arch_info; + struct esirisc_common *esirisc = reg_info->esirisc; + struct target *target = esirisc->target; + uint32_t value = buf_get_u32(buf, 0, reg->size); + + LOG_DEBUG("-"); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + buf_set_u32(reg->value, 0, reg->size, value); + reg->dirty = true; + reg->valid = true; + + return ERROR_OK; +} + +static const struct reg_arch_type esirisc_reg_type = { + .get = esirisc_get_reg, + .set = esirisc_set_reg, +}; + +static struct reg_cache *esirisc_build_reg_cache(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache); + struct reg_cache *cache = malloc(sizeof(struct reg_cache)); + struct reg *reg_list = calloc(ESIRISC_NUM_REGS, sizeof(struct reg)); + + LOG_DEBUG("-"); + + cache->name = "eSi-RISC registers"; + cache->next = NULL; + cache->reg_list = reg_list; + cache->num_regs = ESIRISC_NUM_REGS; + (*cache_p) = cache; + + esirisc->reg_cache = cache; + esirisc->epc = reg_list + ESIRISC_EPC; + esirisc->ecas = reg_list + ESIRISC_ECAS; + esirisc->eid = reg_list + ESIRISC_EID; + esirisc->ed = reg_list + ESIRISC_ED; + + for (int i = 0; i < esirisc->num_regs; ++i) { + struct reg *reg = reg_list + esirisc_regs[i].number; + struct esirisc_reg *reg_info = calloc(1, sizeof(struct esirisc_reg)); + + reg->name = esirisc_regs[i].name; + reg->number = esirisc_regs[i].number; + reg->value = calloc(1, DIV_ROUND_UP(esirisc->num_bits, 8)); + reg->size = esirisc->num_bits; + reg->reg_data_type = calloc(1, sizeof(struct reg_data_type)); + reg->reg_data_type->type = esirisc_regs[i].type; + reg->group = esirisc_regs[i].group; + reg_info->esirisc = esirisc; + reg_info->read = esirisc_read_reg; + reg_info->write = esirisc_write_reg; + reg->arch_info = reg_info; + reg->type = &esirisc_reg_type; + reg->exist = true; + } + + for (size_t i = 0; i < ARRAY_SIZE(esirisc_csrs); ++i) { + struct reg *reg = reg_list + esirisc_csrs[i].number; + struct esirisc_reg *reg_info = calloc(1, sizeof(struct esirisc_reg)); + + reg->name = esirisc_csrs[i].name; + reg->number = esirisc_csrs[i].number; + reg->value = calloc(1, DIV_ROUND_UP(esirisc->num_bits, 8)); + reg->size = esirisc->num_bits; + reg->reg_data_type = calloc(1, sizeof(struct reg_data_type)); + reg->reg_data_type->type = esirisc_csrs[i].type; + reg->group = esirisc_csrs[i].group; + reg_info->esirisc = esirisc; + reg_info->bank = esirisc_csrs[i].bank; + reg_info->csr = esirisc_csrs[i].csr; + reg_info->read = esirisc_read_csr; + reg_info->write = esirisc_write_csr; + reg->arch_info = reg_info; + reg->type = &esirisc_reg_type; + reg->exist = true; + } + + return cache; +} + +static int esirisc_identify(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + uint32_t csr; + int retval; + + LOG_DEBUG("-"); + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_ARCH0, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: ARCH0", target_name(target)); + return retval; + } + + esirisc->num_bits = (csr >> 0) & 0x3f; /* ARCH0.B */ + esirisc->num_regs = (csr >> 10) & 0x3f; /* ARCH0.R */ + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_MEM, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: MEM", target_name(target)); + return retval; + } + + target->endianness = (csr & 1<<0) ? /* MEM.E */ + TARGET_BIG_ENDIAN : TARGET_LITTLE_ENDIAN; + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_IC, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: IC", target_name(target)); + return retval; + } + + esirisc->has_icache = !!(csr & 1<<0); /* IC.E */ + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DC, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DC", target_name(target)); + return retval; + } + + esirisc->has_dcache = !!(csr & 1<<0); /* DC.E */ + + retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DBG, &csr); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to read CSR: DBG", target_name(target)); + return retval; + } + + esirisc->num_breakpoints = (csr >> 7) & 0xf; /* DBG.BP */ + esirisc->num_watchpoints = (csr >> 12) & 0xf; /* DBG.WP */ + + return ERROR_OK; +} + +static int esirisc_target_create(struct target *target, Jim_Interp *interp) +{ + struct jtag_tap *tap = target->tap; + struct esirisc_common *esirisc; + + if (tap == NULL) + return ERROR_FAIL; + + if (tap->ir_length != INSTR_LENGTH) { + LOG_ERROR("%s: invalid IR length; expected %d", target_name(target), + INSTR_LENGTH); + return ERROR_FAIL; + } + + esirisc = calloc(1, sizeof(struct esirisc_common)); + if (esirisc == NULL) + return ERROR_FAIL; + + esirisc->target = target; + esirisc->jtag_info.tap = tap; + target->arch_info = esirisc; + + return ERROR_OK; +} + +static int esirisc_init_target(struct command_context *cmd_ctx, struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + + /* trap reset, error, and debug exceptions */ + esirisc->hwdc_save = HWDC_R | HWDC_E | HWDC_D; + + return ERROR_OK; +} + +static int esirisc_examine(struct target *target) +{ + struct esirisc_common *esirisc = target_to_esirisc(target); + struct esirisc_jtag *jtag_info = &esirisc->jtag_info; + int retval; + + LOG_DEBUG("-"); + + if (!target_was_examined(target)) { + retval = esirisc_debug_enable(target); + if (retval != ERROR_OK) + return retval; + + /* + * In order to identify the target we must first halt the core. + * We quietly resume once identification has completed for those + * targets that were running when target_examine was called. + */ + if (esirisc_jtag_is_debug_active(jtag_info)) { + if (target->state == TARGET_UNKNOWN) + target->debug_reason = DBG_REASON_DBGRQ; + + target->state = TARGET_HALTED; + } else { + retval = esirisc_jtag_break(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to halt target", target_name(target)); + return retval; + } + + target->state = TARGET_RUNNING; + } + + retval = esirisc_identify(target); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to identify target", target_name(target)); + return retval; + } + + esirisc_build_reg_cache(target); + + esirisc_remove_breakpoints(target); + esirisc_remove_watchpoints(target); + + esirisc_disable_step(target); + esirisc_restore_hwdc(target); + + if (target->state == TARGET_HALTED) + esirisc_save_interrupts(target); + else { + retval = esirisc_jtag_continue(jtag_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: failed to resume target", target_name(target)); + return retval; + } + } + + target_set_examined(target); + + LOG_INFO("%s: %d bit, %d registers, %s%s%s", target_name(target), + esirisc->num_bits, esirisc->num_regs, + target_endianness(target), + esirisc->has_icache ? ", icache" : "", + esirisc->has_dcache ? ", dcache" : ""); + + LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints", target_name(target), + esirisc->num_breakpoints, esirisc->num_watchpoints); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_esirisc_cache_arch_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct esirisc_common *esirisc = target_to_esirisc(target); + + if (CMD_ARGC > 0) { + if (strcmp(*CMD_ARGV, "harvard") == 0) + esirisc->cache_arch = ESIRISC_CACHE_HARVARD; + else if (strcmp(*CMD_ARGV, "von_neumann") == 0) + esirisc->cache_arch = ESIRISC_CACHE_VON_NEUMANN; + else { + LOG_ERROR("invalid cache_arch: %s", *CMD_ARGV); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + command_print(CMD_CTX, "esirisc cache_arch %s", esirisc_cache_arch(esirisc)); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_esirisc_flush_caches_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct esirisc_common *esirisc = target_to_esirisc(target); + int retval; + + if (!esirisc_has_cache(esirisc)) { + LOG_ERROR("target does not support caching"); + return ERROR_FAIL; + } + + retval = esirisc_flush_caches(target); + + command_print(CMD_CTX, "cache flush %s", + (retval == ERROR_OK) ? "successful" : "failed"); + + return retval; +} + +static const struct { + const char *name; + int mask; +} esirisc_hwdc_masks[] = { + { "reset", HWDC_R }, + { "interrupt", HWDC_I }, + { "syscall", HWDC_S }, + { "error", HWDC_E }, + { "debug", HWDC_D }, +}; + +static int esirisc_find_hwdc_mask(const char *name) +{ + for (size_t i = 0; i < ARRAY_SIZE(esirisc_hwdc_masks); ++i) + if (strcmp(esirisc_hwdc_masks[i].name, name) == 0) + return esirisc_hwdc_masks[i].mask; + + return -1; +} + +COMMAND_HANDLER(handle_esirisc_hwdc_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct esirisc_common *esirisc = target_to_esirisc(target); + + if (CMD_ARGC > 0) { + if (strcmp(CMD_ARGV[0], "all") == 0) + esirisc->hwdc_save = HWDC_R | HWDC_I | HWDC_S | HWDC_E | HWDC_D; + else { + esirisc->hwdc_save = 0; + if (strcmp(CMD_ARGV[0], "none") != 0) { + while (CMD_ARGC-- > 0) { + int mask = esirisc_find_hwdc_mask(CMD_ARGV[CMD_ARGC]); + if (mask < 0) { + LOG_ERROR("invalid mask: %s", CMD_ARGV[CMD_ARGC]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + esirisc->hwdc_save |= mask; + } + } + } + } + + for (size_t i = 0; i < ARRAY_SIZE(esirisc_hwdc_masks); ++i) + command_print(CMD_CTX, "%9s: %s", esirisc_hwdc_masks[i].name, + (esirisc->hwdc_save & esirisc_hwdc_masks[i].mask) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +static const struct command_registration esirisc_exec_command_handlers[] = { + { + .name = "cache_arch", + .handler = handle_esirisc_cache_arch_command, + .mode = COMMAND_ANY, + .help = "configure cache architecture", + .usage = "['harvard'|'von_neumann']", + }, + { + .name = "flush_caches", + .handler = handle_esirisc_flush_caches_command, + .mode = COMMAND_EXEC, + .help = "flush instruction and data caches", + .usage = "", + }, + { + .name = "hwdc", + .handler = handle_esirisc_hwdc_command, + .mode = COMMAND_ANY, + .help = "configure hardware debug control", + .usage = "['all'|'none'|mask ...]", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration esirisc_command_handlers[] = { + { + .name = "esirisc", + .mode = COMMAND_ANY, + .help = "eSi-RISC command group", + .usage = "", + .chain = esirisc_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct target_type esirisc_target = { + .name = "esirisc", + + .poll = esirisc_poll, + .arch_state = esirisc_arch_state, + + .halt = esirisc_halt, + .resume = esirisc_resume, + .step = esirisc_step, + + .assert_reset = esirisc_assert_reset, + .deassert_reset = esirisc_deassert_reset, + + .get_gdb_arch = esirisc_get_gdb_arch, + .get_gdb_reg_list = esirisc_get_gdb_reg_list, + + .read_memory = esirisc_read_memory, + .write_memory = esirisc_write_memory, + .checksum_memory = esirisc_checksum_memory, + + .add_breakpoint = esirisc_add_breakpoint, + .remove_breakpoint = esirisc_remove_breakpoint, + .add_watchpoint = esirisc_add_watchpoint, + .remove_watchpoint = esirisc_remove_watchpoint, + + .commands = esirisc_command_handlers, + + .target_create = esirisc_target_create, + .init_target = esirisc_init_target, + .examine = esirisc_examine, +}; diff --git a/src/target/esirisc.h b/src/target/esirisc.h new file mode 100644 index 000000000..bb50652aa --- /dev/null +++ b/src/target/esirisc.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESIRISC_H +#define OPENOCD_TARGET_ESIRISC_H + +#include +#include +#include + +#include "esirisc_jtag.h" +#include "esirisc_regs.h" + +#define MAX_BREAKPOINTS 8 +#define MAX_WATCHPOINTS 8 + +/* Exception IDs */ +#define EID_RESET 0x00 +#define EID_HARDWARE_FAILURE 0x01 +#define EID_NMI 0x02 +#define EID_INST_BREAKPOINT 0x03 +#define EID_DATA_BREAKPOINT 0x04 +#define EID_UNSUPPORTED 0x05 +#define EID_PRIVILEGE_VIOLATION 0x06 +#define EID_INST_BUS_ERROR 0x07 +#define EID_DATA_BUS_ERROR 0x08 +#define EID_ALIGNMENT_ERROR 0x09 +#define EID_ARITHMETIC_ERROR 0x0a +#define EID_SYSTEM_CALL 0x0b +#define EID_MEMORY_MANAGEMENT 0x0c +#define EID_UNRECOVERABLE 0x0d +#define EID_INTERRUPTn 0x20 + +/* Exception Entry Points */ +#define ENTRY_RESET 0x00 +#define ENTRY_UNRECOVERABLE 0x01 +#define ENTRY_HARDWARE_FAILURE 0x02 +#define ENTRY_RUNTIME 0x03 +#define ENTRY_MEMORY 0x04 +#define ENTRY_SYSCALL 0x05 +#define ENTRY_DEBUG 0x06 +#define ENTRY_NMI 0x07 +#define ENTRY_INTERRUPTn 0x08 + +/* Hardware Debug Control */ +#define HWDC_R (1<<4) /* Reset & Hardware Failure */ +#define HWDC_I (1<<3) /* Interrupts */ +#define HWDC_S (1<<2) /* System Calls */ +#define HWDC_E (1<<1) /* Program Errors */ +#define HWDC_D (1<<0) /* Debug Exceptions */ + +enum esirisc_cache { + ESIRISC_CACHE_VON_NEUMANN, + ESIRISC_CACHE_HARVARD, +}; + +struct esirisc_common { + struct target *target; + struct esirisc_jtag jtag_info; + enum esirisc_cache cache_arch; + char *gdb_arch; + + struct reg_cache *reg_cache; + struct reg *epc; + struct reg *ecas; + struct reg *eid; + struct reg *ed; + uint32_t etc_save; + uint32_t hwdc_save; + + int num_bits; + int num_regs; + bool has_icache; + bool has_dcache; + int num_breakpoints; + int num_watchpoints; + + struct breakpoint *breakpoints_p[MAX_BREAKPOINTS]; + struct watchpoint *watchpoints_p[MAX_WATCHPOINTS]; +}; + +union esirisc_memory { + uint32_t word; + uint16_t hword; + uint8_t byte; +}; + +struct esirisc_reg { + struct esirisc_common *esirisc; + + uint8_t bank; + uint8_t csr; + + int (*read)(struct reg *reg); + int (*write)(struct reg *reg); +}; + +static inline struct esirisc_common *target_to_esirisc(struct target *target) +{ + return (struct esirisc_common *)target->arch_info; +} + +static inline char *esirisc_cache_arch(struct esirisc_common *esirisc) +{ + return esirisc->cache_arch == ESIRISC_CACHE_HARVARD ? "harvard" : "von_neumann"; +} + +static inline bool esirisc_has_cache(struct esirisc_common *esirisc) +{ + return esirisc->has_icache || esirisc->has_dcache; +} + +#endif /* OPENOCD_TARGET_ESIRISC_H */ diff --git a/src/target/esirisc_jtag.c b/src/target/esirisc_jtag.c new file mode 100644 index 000000000..8ab47fa8f --- /dev/null +++ b/src/target/esirisc_jtag.c @@ -0,0 +1,514 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "esirisc_jtag.h" + +static void esirisc_jtag_set_instr(struct esirisc_jtag *jtag_info, uint32_t new_instr) +{ + struct jtag_tap *tap = jtag_info->tap; + + if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) { + struct scan_field field; + uint8_t t[4]; + + field.num_bits = tap->ir_length; + field.out_value = t; + buf_set_u32(t, 0, field.num_bits, new_instr); + field.in_value = NULL; + + jtag_add_ir_scan(tap, &field, TAP_IDLE); + } +} + +/* + * The data register is latched every 8 bits while in the Shift-DR state + * (Update-DR is not supported). This necessitates prepending padding + * bits to ensure data is aligned when multiple TAPs are present. + */ +static int esirisc_jtag_get_padding(void) +{ + int padding = 0; + int bypass_devices = 0; + + for (struct jtag_tap *tap = jtag_tap_next_enabled(NULL); tap != NULL; + tap = jtag_tap_next_enabled(tap)) + if (tap->bypass) + bypass_devices++; + + int num_bits = bypass_devices % 8; + if (num_bits > 0) + padding = 8 - num_bits; + + return padding; +} + +static int esirisc_jtag_count_bits(int num_fields, struct scan_field *fields) +{ + int bit_count = 0; + + for (int i = 0; i < num_fields; ++i) + bit_count += fields[i].num_bits; + + return bit_count; +} + +/* + * Data received from the target will be byte-stuffed if it contains + * either the pad byte (0xAA) or stuffing marker (0x55). Buffers should + * be sized twice the expected length to account for stuffing overhead. + */ +static void esirisc_jtag_unstuff(uint8_t *data, size_t len) +{ + uint8_t *r, *w; + uint8_t *end; + + r = w = data; + end = data + len; + while (r < end) { + if (*r == STUFF_MARKER) { + r++; /* skip stuffing marker */ + assert(r < end); + *w++ = *r++ ^ STUFF_MARKER; + } else + *w++ = *r++; + } +} + +/* + * The eSi-Debug protocol defines a byte-oriented command/response + * channel that operates over serial or JTAG. While not strictly + * required, separate DR scans are used for sending and receiving data. + * This allows the TAP to recover gracefully if the byte stream is + * corrupted at the expense of sending additional padding bits. + */ + +static int esirisc_jtag_send(struct esirisc_jtag *jtag_info, uint8_t command, + int num_out_fields, struct scan_field *out_fields) +{ + int num_fields = 2 + num_out_fields; + struct scan_field *fields = cmd_queue_alloc(num_fields * sizeof(struct scan_field)); + + esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG); + + fields[0].num_bits = esirisc_jtag_get_padding(); + fields[0].out_value = NULL; + fields[0].in_value = NULL; + + fields[1].num_bits = 8; + fields[1].out_value = &command; + fields[1].in_value = NULL; + + /* append command data */ + for (int i = 0; i < num_out_fields; ++i) + jtag_scan_field_clone(&fields[2+i], &out_fields[i]); + + jtag_add_dr_scan(jtag_info->tap, num_fields, fields, TAP_IDLE); + + return jtag_execute_queue(); +} + +static int esirisc_jtag_recv(struct esirisc_jtag *jtag_info, + int num_in_fields, struct scan_field *in_fields) +{ + int num_in_bits = esirisc_jtag_count_bits(num_in_fields, in_fields); + int num_in_bytes = DIV_ROUND_UP(num_in_bits, 8); + + struct scan_field fields[3]; + uint8_t r[num_in_bytes * 2]; + + esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG); + + fields[0].num_bits = esirisc_jtag_get_padding() + 1; + fields[0].out_value = NULL; + fields[0].in_value = NULL; + + fields[1].num_bits = 8; + fields[1].out_value = NULL; + fields[1].in_value = &jtag_info->status; + + fields[2].num_bits = num_in_bits * 2; + fields[2].out_value = NULL; + fields[2].in_value = r; + + jtag_add_dr_scan(jtag_info->tap, ARRAY_SIZE(fields), fields, TAP_IDLE); + + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + /* unstuff response data and write back to caller */ + if (num_in_fields > 0) { + esirisc_jtag_unstuff(r, ARRAY_SIZE(r)); + + int bit_count = 0; + for (int i = 0; i < num_in_fields; ++i) { + buf_set_buf(r, bit_count, in_fields[i].in_value, 0, in_fields[i].num_bits); + bit_count += in_fields[i].num_bits; + } + } + + return ERROR_OK; +} + +static int esirisc_jtag_check_status(struct esirisc_jtag *jtag_info) +{ + uint8_t eid = esirisc_jtag_get_eid(jtag_info); + if (eid != EID_NONE) { + LOG_ERROR("esirisc_jtag: bad status: 0x%02" PRIx32 " (DA: %" PRId32 ", " + "S: %" PRId32 ", EID: 0x%02" PRIx32 ")", + jtag_info->status, esirisc_jtag_is_debug_active(jtag_info), + esirisc_jtag_is_stopped(jtag_info), eid); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int esirisc_jtag_send_and_recv(struct esirisc_jtag *jtag_info, uint8_t command, + int num_out_fields, struct scan_field *out_fields, + int num_in_fields, struct scan_field *in_fields) +{ + int retval; + + jtag_info->status = 0; /* clear status */ + + retval = esirisc_jtag_send(jtag_info, command, num_out_fields, out_fields); + if (retval != ERROR_OK) { + LOG_ERROR("esirisc_jtag: send failed (command: 0x%02" PRIx32 ")", command); + return ERROR_FAIL; + } + + retval = esirisc_jtag_recv(jtag_info, num_in_fields, in_fields); + if (retval != ERROR_OK) { + LOG_ERROR("esirisc_jtag: recv failed (command: 0x%02" PRIx32 ")", command); + return ERROR_FAIL; + } + + return esirisc_jtag_check_status(jtag_info); +} + +/* + * Status is automatically updated after each command completes; + * these functions make each field available to the caller. + */ + +bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info) +{ + return !!(jtag_info->status & 1<<7); /* DA */ +} + +bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info) +{ + return !!(jtag_info->status & 1<<6); /* S */ +} + +uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info) +{ + return jtag_info->status & 0x3f; /* EID */ +} + +/* + * Most commands manipulate target data (eg. memory and registers); each + * command returns a status byte that indicates success. Commands must + * transmit multibyte values in big-endian order, however response + * values are in little-endian order. Target endianness does not have an + * effect on this ordering. + */ + +int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t *data) +{ + struct scan_field out_fields[1]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[1]; + + in_fields[0].num_bits = 8; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_BYTE, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = *d; + + return ERROR_OK; +} + +int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t *data) +{ + struct scan_field out_fields[1]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[2]; + + in_fields[0].num_bits = 16; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_HWORD, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u16(d); + + return ERROR_OK; +} + +int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t *data) +{ + struct scan_field out_fields[1]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[4]; + + in_fields[0].num_bits = 32; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_WORD, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u32(d); + + return ERROR_OK; +} + +int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t data) +{ + struct scan_field out_fields[2]; + uint8_t a[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 8; + out_fields[1].out_value = &data; + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_BYTE, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t data) +{ + struct scan_field out_fields[2]; + uint8_t a[4], d[2]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 16; + out_fields[1].out_value = d; + h_u16_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_HWORD, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t data) +{ + struct scan_field out_fields[2]; + uint8_t a[4], d[4]; + + out_fields[0].num_bits = 32; + out_fields[0].out_value = a; + h_u32_to_be(a, address); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 32; + out_fields[1].out_value = d; + h_u32_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_WORD, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t *data) +{ + struct scan_field out_fields[1]; + + out_fields[0].num_bits = 8; + out_fields[0].out_value = ® + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[4]; + + in_fields[0].num_bits = 32; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_REG, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u32(d); + + return ERROR_OK; +} + +int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t data) +{ + struct scan_field out_fields[2]; + uint8_t d[4]; + + out_fields[0].num_bits = 8; + out_fields[0].out_value = ® + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 32; + out_fields[1].out_value = d; + h_u32_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_REG, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t *data) +{ + struct scan_field out_fields[1]; + uint8_t c[2]; + + out_fields[0].num_bits = 16; + out_fields[0].out_value = c; + h_u16_to_be(c, (csr << 5) | bank); + out_fields[0].in_value = NULL; + + struct scan_field in_fields[1]; + uint8_t d[4]; + + in_fields[0].num_bits = 32; + in_fields[0].out_value = NULL; + in_fields[0].in_value = d; + + int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_CSR, + ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields); + if (retval != ERROR_OK) + return retval; + + *data = le_to_h_u32(d); + + return ERROR_OK; +} + +int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t data) +{ + struct scan_field out_fields[2]; + uint8_t c[2], d[4]; + + out_fields[0].num_bits = 16; + out_fields[0].out_value = c; + h_u16_to_be(c, (csr << 5) | bank); + out_fields[0].in_value = NULL; + + out_fields[1].num_bits = 32; + out_fields[1].out_value = d; + h_u32_to_be(d, data); + out_fields[1].in_value = NULL; + + return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_CSR, + ARRAY_SIZE(out_fields), out_fields, 0, NULL); +} + +/* + * Control commands affect CPU operation; these commands send no data + * and return a status byte. + */ + +static inline int esirisc_jtag_send_ctrl(struct esirisc_jtag *jtag_info, uint8_t command) +{ + return esirisc_jtag_send_and_recv(jtag_info, command, 0, NULL, 0, NULL); +} + +int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ENABLE_DEBUG); +} + +int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DISABLE_DEBUG); +} + +int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ASSERT_RESET); +} + +int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DEASSERT_RESET); +} + +int esirisc_jtag_break(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_BREAK); +} + +int esirisc_jtag_continue(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_CONTINUE); +} + +int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info) +{ + return esirisc_jtag_send_ctrl(jtag_info, DEBUG_FLUSH_CACHES); +} diff --git a/src/target/esirisc_jtag.h b/src/target/esirisc_jtag.h new file mode 100644 index 000000000..8189ddc6c --- /dev/null +++ b/src/target/esirisc_jtag.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESIRISC_JTAG_H +#define OPENOCD_TARGET_ESIRISC_JTAG_H + +#include + +/* TAP Instructions */ +#define INSTR_IDCODE 0x8 +#define INSTR_DEBUG 0x9 +#define INSTR_BYPASS 0xf +#define INSTR_LENGTH 4 + +/* eSi-Debug Commands */ +#define DEBUG_NOP 0x00 +#define DEBUG_READ_BYTE 0x10 +#define DEBUG_READ_HWORD 0x20 +#define DEBUG_READ_WORD 0x30 +#define DEBUG_WRITE_BYTE 0x60 +#define DEBUG_WRITE_HWORD 0x70 +#define DEBUG_WRITE_WORD 0x80 +#define DEBUG_READ_REG 0xb0 +#define DEBUG_WRITE_REG 0xc0 +#define DEBUG_READ_CSR 0xd0 +#define DEBUG_WRITE_CSR 0xe0 +#define DEBUG_ENABLE_DEBUG 0xf0 +#define DEBUG_DISABLE_DEBUG 0xf2 +#define DEBUG_ASSERT_RESET 0xf4 +#define DEBUG_DEASSERT_RESET 0xf6 +#define DEBUG_BREAK 0xf8 +#define DEBUG_CONTINUE 0xfa +#define DEBUG_FLUSH_CACHES 0xfc + +/* Exception IDs */ +#define EID_OVERFLOW 0x3d +#define EID_CANT_DEBUG 0x3e +#define EID_NONE 0x3f + +/* Byte Stuffing */ +#define STUFF_MARKER 0x55 +#define PAD_BYTE 0xaa + +struct esirisc_jtag { + struct jtag_tap *tap; + uint8_t status; +}; + +bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info); +bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info); +uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, + uint32_t address, uint8_t *data); +int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, + uint32_t address, uint16_t *data); +int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, + uint32_t address, uint32_t *data); + +int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, + uint32_t address, uint8_t data); +int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, + uint32_t address, uint16_t data); +int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, + uint32_t address, uint32_t data); + +int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, + uint8_t reg, uint32_t *data); +int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, + uint8_t reg, uint32_t data); + +int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, + uint8_t bank, uint8_t csr, uint32_t *data); +int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, + uint8_t bank, uint8_t csr, uint32_t data); + +int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info); +int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info); +int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_break(struct esirisc_jtag *jtag_info); +int esirisc_jtag_continue(struct esirisc_jtag *jtag_info); + +int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info); + +#endif /* OPENOCD_TARGET_ESIRISC_JTAG_H */ diff --git a/src/target/esirisc_regs.h b/src/target/esirisc_regs.h new file mode 100644 index 000000000..ad3385819 --- /dev/null +++ b/src/target/esirisc_regs.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2018 by Square, Inc. * + * Steven Stallion * + * James Zhao * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESIRISC_REGS_H +#define OPENOCD_TARGET_ESIRISC_REGS_H + +enum esirisc_reg_num { + ESIRISC_SP, + ESIRISC_RA, + ESIRISC_R2, + ESIRISC_R3, + ESIRISC_R4, + ESIRISC_R5, + ESIRISC_R6, + ESIRISC_R7, + ESIRISC_R8, + ESIRISC_R9, + ESIRISC_R10, + ESIRISC_R11, + ESIRISC_R12, + ESIRISC_R13, + ESIRISC_R14, + ESIRISC_R15, + ESIRISC_R16, + ESIRISC_R17, + ESIRISC_R18, + ESIRISC_R19, + ESIRISC_R20, + ESIRISC_R21, + ESIRISC_R22, + ESIRISC_R23, + ESIRISC_R24, + ESIRISC_R25, + ESIRISC_R26, + ESIRISC_R27, + ESIRISC_R28, + ESIRISC_R29, + ESIRISC_R30, + ESIRISC_R31, + + ESIRISC_V0, + ESIRISC_V1, + ESIRISC_V2, + ESIRISC_V3, + ESIRISC_V4, + ESIRISC_V5, + ESIRISC_V6, + ESIRISC_V7, + ESIRISC_V8, + ESIRISC_V9, + ESIRISC_V10, + ESIRISC_V11, + ESIRISC_V12, + ESIRISC_V13, + ESIRISC_V14, + ESIRISC_V15, + ESIRISC_V16, + ESIRISC_V17, + ESIRISC_V18, + ESIRISC_V19, + ESIRISC_V20, + ESIRISC_V21, + ESIRISC_V22, + ESIRISC_V23, + ESIRISC_V24, + ESIRISC_V25, + ESIRISC_V26, + ESIRISC_V27, + ESIRISC_V28, + ESIRISC_V29, + ESIRISC_V30, + ESIRISC_V31, + + ESIRISC_A0, + ESIRISC_A1, + ESIRISC_A2, + ESIRISC_A3, + ESIRISC_A4, + ESIRISC_A5, + ESIRISC_A6, + ESIRISC_A7, + + ESIRISC_PC, + ESIRISC_CAS, + ESIRISC_TC, + ESIRISC_ETA, + ESIRISC_ETC, + ESIRISC_EPC, + ESIRISC_ECAS, + ESIRISC_EID, + ESIRISC_ED, + ESIRISC_IP, + ESIRISC_IM, + ESIRISC_IS, + ESIRISC_IT, + + ESIRISC_NUM_REGS, +}; + +/* CSR Banks */ +#define CSR_THREAD 0x00 +#define CSR_INTERRUPT 0x01 +#define CSR_DEBUG 0x04 +#define CSR_CONFIG 0x05 +#define CSR_TRACE 0x09 + +/* Thread CSRs */ +#define CSR_THREAD_TC 0x00 /* Thread Control */ +#define CSR_THREAD_PC 0x01 /* Program Counter */ +#define CSR_THREAD_CAS 0x02 /* Comparison & Arithmetic Status */ +#define CSR_THREAD_AC 0x03 /* Arithmetic Control */ +#define CSR_THREAD_LF 0x04 /* Locked Flag */ +#define CSR_THREAD_LA 0x05 /* Locked Address */ +#define CSR_THREAD_ETA 0x07 /* Exception Table Address */ +#define CSR_THREAD_ETC 0x08 /* Exception TC */ +#define CSR_THREAD_EPC 0x09 /* Exception PC */ +#define CSR_THREAD_ECAS 0x0a /* Exception CAS */ +#define CSR_THREAD_EID 0x0b /* Exception ID */ +#define CSR_THREAD_ED 0x0c /* Exception Data */ + +/* Interrupt CSRs */ +#define CSR_INTERRUPT_IP 0x00 /* Interrupt Pending */ +#define CSR_INTERRUPT_IA 0x01 /* Interrupt Acknowledge */ +#define CSR_INTERRUPT_IM 0x02 /* Interrupt Mask */ +#define CSR_INTERRUPT_IS 0x03 /* Interrupt Sense */ +#define CSR_INTERRUPT_IT 0x04 /* Interrupt Trigger */ + +/* Debug CSRs */ +#define CSR_DEBUG_DC 0x00 /* Debug Control */ +#define CSR_DEBUG_IBC 0x01 /* Instruction Breakpoint Control */ +#define CSR_DEBUG_DBC 0x02 /* Data Breakpoint Control */ +#define CSR_DEBUG_HWDC 0x03 /* Hardware Debug Control */ +#define CSR_DEBUG_DBS 0x04 /* Data Breakpoint Size */ +#define CSR_DEBUG_DBR 0x05 /* Data Breakpoint Range */ +#define CSR_DEBUG_IBAn 0x08 /* Instruction Breakpoint Address [0..7] */ +#define CSR_DEBUG_DBAn 0x10 /* Data Breakpoint Address [0..7] */ + +/* Configuration CSRs */ +#define CSR_CONFIG_ARCH0 0x00 /* Architectural Configuration 0 */ +#define CSR_CONFIG_ARCH1 0x01 /* Architectural Configuration 1 */ +#define CSR_CONFIG_ARCH2 0x02 /* Architectural Configuration 2 */ +#define CSR_CONFIG_ARCH3 0x03 /* Architectural Configuration 3 */ +#define CSR_CONFIG_MEM 0x04 /* Memory Configuration */ +#define CSR_CONFIG_IC 0x05 /* Instruction Cache Configuration */ +#define CSR_CONFIG_DC 0x06 /* Data Cache Configuration */ +#define CSR_CONFIG_INT 0x07 /* Interrupt Configuration */ +#define CSR_CONFIG_ISAn 0x08 /* Instruction Set Configuration [0..6] */ +#define CSR_CONFIG_DBG 0x0f /* Debug Configuration */ +#define CSR_CONFIG_MID 0x10 /* Manufacturer ID */ +#define CSR_CONFIG_REV 0x11 /* Revision Number */ +#define CSR_CONFIG_MPID 0x12 /* Mulitprocessor ID */ +#define CSR_CONFIG_FREQn 0x13 /* Frequency [0..2] */ +#define CSR_CONFIG_TRACE 0x16 /* Trace Configuration */ + +/* Trace CSRs */ +#define CSR_TRACE_CONTROL 0x00 +#define CSR_TRACE_STATUS 0x01 +#define CSR_TRACE_BUFFER_START 0x02 +#define CSR_TRACE_BUFFER_END 0x03 +#define CSR_TRACE_BUFFER_CUR 0x04 +#define CSR_TRACE_TRIGGER 0x05 +#define CSR_TRACE_START_DATA 0x06 +#define CSR_TRACE_START_MASK 0x07 +#define CSR_TRACE_STOP_DATA 0x08 +#define CSR_TRACE_STOP_MASK 0x09 +#define CSR_TRACE_DELAY 0x0a + +#endif /* OPENOCD_TARGET_ESIRISC_REGS_H */ diff --git a/src/target/mem_ap.c b/src/target/mem_ap.c new file mode 100644 index 000000000..3a2d4b7c5 --- /dev/null +++ b/src/target/mem_ap.c @@ -0,0 +1,181 @@ +/***************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "target.h" +#include "target_type.h" +#include "arm.h" +#include "arm_adi_v5.h" + +#include + +struct mem_ap { + struct arm arm; + struct adiv5_ap *ap; + int ap_num; +}; + +static int mem_ap_target_create(struct target *target, Jim_Interp *interp) +{ + struct mem_ap *mem_ap = calloc(1, sizeof(struct mem_ap)); + struct adiv5_private_config *pc; + + pc = (struct adiv5_private_config *)target->private_config; + if (pc == NULL) + return ERROR_FAIL; + + if (pc->ap_num == DP_APSEL_INVALID) { + LOG_ERROR("AP number not specified"); + return ERROR_FAIL; + } + + mem_ap->ap_num = pc->ap_num; + mem_ap->arm.common_magic = ARM_COMMON_MAGIC; + mem_ap->arm.dap = pc->dap; + + target->arch_info = mem_ap; + + return ERROR_OK; +} + +static int mem_ap_init_target(struct command_context *cmd_ctx, struct target *target) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_UNKNOWN; + return ERROR_OK; +} + +static int mem_ap_arch_state(struct target *target) +{ + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int mem_ap_poll(struct target *target) +{ + if (target->state == TARGET_UNKNOWN) + target->state = TARGET_RUNNING; + + return ERROR_OK; +} + +static int mem_ap_halt(struct target *target) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_HALTED; + return ERROR_OK; +} + +static int mem_ap_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_RUNNING; + return ERROR_OK; +} + +static int mem_ap_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints) +{ + LOG_DEBUG("%s", __func__); + target->state = TARGET_HALTED; + return ERROR_OK; +} + +static int mem_ap_assert_reset(struct target *target) +{ + target->state = TARGET_RESET; + + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int mem_ap_examine(struct target *target) +{ + struct mem_ap *mem_ap = target->arch_info; + + if (!target_was_examined(target)) { + mem_ap->ap = dap_ap(mem_ap->arm.dap, mem_ap->ap_num); + target_set_examined(target); + target->state = TARGET_UNKNOWN; + return mem_ap_init(mem_ap->ap); + } + + return ERROR_OK; +} + +static int mem_ap_deassert_reset(struct target *target) +{ + if (target->reset_halt) + target->state = TARGET_HALTED; + else + target->state = TARGET_RUNNING; + + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int mem_ap_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + struct mem_ap *mem_ap = target->arch_info; + + LOG_DEBUG("Reading memory at physical address 0x" TARGET_ADDR_FMT + "; size %" PRId32 "; count %" PRId32, address, size, count); + + if (count == 0 || buffer == NULL) + return ERROR_COMMAND_SYNTAX_ERROR; + + return mem_ap_read_buf(mem_ap->ap, buffer, size, count, address); +} + +static int mem_ap_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, + const uint8_t *buffer) +{ + struct mem_ap *mem_ap = target->arch_info; + + LOG_DEBUG("Writing memory at physical address 0x" TARGET_ADDR_FMT + "; size %" PRId32 "; count %" PRId32, address, size, count); + + if (count == 0 || buffer == NULL) + return ERROR_COMMAND_SYNTAX_ERROR; + + return mem_ap_write_buf(mem_ap->ap, buffer, size, count, address); +} + +struct target_type mem_ap_target = { + .name = "mem_ap", + + .target_create = mem_ap_target_create, + .init_target = mem_ap_init_target, + .examine = mem_ap_examine, + .target_jim_configure = adiv5_jim_configure, + + .poll = mem_ap_poll, + .arch_state = mem_ap_arch_state, + + .halt = mem_ap_halt, + .resume = mem_ap_resume, + .step = mem_ap_step, + + .assert_reset = mem_ap_assert_reset, + .deassert_reset = mem_ap_deassert_reset, + + .read_memory = mem_ap_read_memory, + .write_memory = mem_ap_write_memory, +}; diff --git a/src/target/register.c b/src/target/register.c index 1d63e12f7..850641448 100644 --- a/src/target/register.c +++ b/src/target/register.c @@ -44,6 +44,8 @@ struct reg *register_get_by_name(struct reg_cache *first, while (cache) { for (i = 0; i < cache->num_regs; i++) { + if (cache->reg_list[i].exist == false) + continue; if (strcmp(cache->reg_list[i].name, name) == 0) return &(cache->reg_list[i]); } @@ -84,6 +86,8 @@ void register_cache_invalidate(struct reg_cache *cache) struct reg *reg = cache->reg_list; for (unsigned n = cache->num_regs; n != 0; n--, reg++) { + if (reg->exist == false) + continue; reg->valid = 0; reg->dirty = 0; } diff --git a/src/target/target.c b/src/target/target.c index 8240e6544..7de3e7615 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -108,6 +108,8 @@ extern struct target_type quark_x10xx_target; extern struct target_type quark_d20xx_target; extern struct target_type stm8_target; extern struct target_type riscv_target; +extern struct target_type mem_ap_target; +extern struct target_type esirisc_target; static struct target_type *target_types[] = { &arm7tdmi_target, @@ -141,6 +143,8 @@ static struct target_type *target_types[] = { &quark_d20xx_target, &stm8_target, &riscv_target, + &mem_ap_target, + &esirisc_target, #if BUILD_TARGET64 &aarch64_target, #endif @@ -513,9 +517,7 @@ struct target *get_target_by_num(int num) struct target *get_current_target(struct command_context *cmd_ctx) { - struct target *target = cmd_ctx->current_target_override - ? cmd_ctx->current_target_override - : cmd_ctx->current_target; + struct target *target = get_current_target_or_null(cmd_ctx); if (target == NULL) { LOG_ERROR("BUG: current_target out of bounds"); @@ -525,6 +527,13 @@ struct target *get_current_target(struct command_context *cmd_ctx) return target; } +struct target *get_current_target_or_null(struct command_context *cmd_ctx) +{ + return cmd_ctx->current_target_override + ? cmd_ctx->current_target_override + : cmd_ctx->current_target; +} + int target_poll(struct target *target) { int retval; @@ -1043,6 +1052,9 @@ int target_run_flash_async_algorithm(struct target *target, retval = target_write_u32(target, wp_addr, wp); if (retval != ERROR_OK) break; + + /* Avoid GDB timeouts */ + keep_alive(); } if (retval != ERROR_OK) { @@ -1197,12 +1209,29 @@ int target_hit_watchpoint(struct target *target, return target->type->hit_watchpoint(target, hit_watchpoint); } +const char *target_get_gdb_arch(struct target *target) +{ + if (target->type->get_gdb_arch == NULL) + return NULL; + return target->type->get_gdb_arch(target); +} + int target_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class) { return target->type->get_gdb_reg_list(target, reg_list, reg_list_size, reg_class); } + +bool target_supports_gdb_connection(struct target *target) +{ + /* + * based on current code, we can simply exclude all the targets that + * don't provide get_gdb_reg_list; this could change with new targets. + */ + return !!target->type->get_gdb_reg_list; +} + int target_step(struct target *target, int current, target_addr_t address, int handle_breakpoints) { @@ -1927,6 +1956,7 @@ static void target_destroy(struct target *target) target->smp = 0; } + free(target->gdb_port_override); free(target->type); free(target->trace_info); free(target->fileio_info); @@ -2792,6 +2822,8 @@ COMMAND_HANDLER(handle_reg_command) for (i = 0, reg = cache->reg_list; i < cache->num_regs; i++, reg++, count++) { + if (reg->exist == false) + continue; /* only print cached values if they are valid */ if (reg->exist) { if (reg->valid) { @@ -2847,14 +2879,15 @@ COMMAND_HANDLER(handle_reg_command) /* access a single register by its name */ reg = register_get_by_name(target->reg_cache, CMD_ARGV[0], 1); - if (!reg) { - command_print(CMD_CTX, "register %s not found in current target", CMD_ARGV[0]); - return ERROR_OK; - } + if (!reg) + goto not_found; } assert(reg != NULL); /* give clang a hint that we *know* reg is != NULL here */ + if (!reg->exist) + goto not_found; + /* display a register */ if ((CMD_ARGC == 1) || ((CMD_ARGC == 2) && !((CMD_ARGV[1][0] >= '0') && (CMD_ARGV[1][0] <= '9')))) { @@ -2898,6 +2931,10 @@ COMMAND_HANDLER(handle_reg_command) } return ERROR_COMMAND_SYNTAX_ERROR; + +not_found: + command_print(CMD_CTX, "register %s not found in current target", CMD_ARGV[0]); + return ERROR_OK; } COMMAND_HANDLER(handle_poll_command) @@ -4550,6 +4587,7 @@ enum target_cfg_param { TCFG_DBGBASE, TCFG_RTOS, TCFG_DEFER_EXAMINE, + TCFG_GDB_PORT, }; static Jim_Nvp nvp_config_opts[] = { @@ -4565,6 +4603,7 @@ static Jim_Nvp nvp_config_opts[] = { { .name = "-dbgbase", .value = TCFG_DBGBASE }, { .name = "-rtos", .value = TCFG_RTOS }, { .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE }, + { .name = "-gdb-port", .value = TCFG_GDB_PORT }, { .name = NULL, .value = -1 } }; @@ -4852,6 +4891,20 @@ no_params: /* loop for more */ break; + case TCFG_GDB_PORT: + if (goi->isconfigure) { + const char *s; + e = Jim_GetOpt_String(goi, &s, NULL); + if (e != JIM_OK) + return e; + target->gdb_port_override = strdup(s); + } else { + if (goi->argc != 0) + goto no_params; + } + Jim_SetResultString(goi->interp, target->gdb_port_override ? : "undefined", -1); + /* loop for more */ + break; } } /* while (goi->argc) */ @@ -5626,6 +5679,8 @@ static int target_create(Jim_GetOptInfo *goi) target->rtos = NULL; target->rtos_auto_detect = false; + target->gdb_port_override = NULL; + /* Do the rest as "configure" options */ goi->isconfigure = 1; e = target_configure(goi, target); @@ -5648,6 +5703,7 @@ static int target_create(Jim_GetOptInfo *goi) } if (e != JIM_OK) { + free(target->gdb_port_override); free(target->type); free(target); return e; @@ -5665,6 +5721,7 @@ static int target_create(Jim_GetOptInfo *goi) e = (*(target->type->target_create))(target, goi->interp); if (e != ERROR_OK) { LOG_DEBUG("target_create failed"); + free(target->gdb_port_override); free(target->type); free(target->cmd_name); free(target); diff --git a/src/target/target.h b/src/target/target.h index 51a5b6935..fb9d71465 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -206,6 +206,8 @@ struct target { /* file-I/O information for host to do syscall */ struct gdb_fileio_info *fileio_info; + char *gdb_port_override; /* target-specific override for gdb_port */ + /* The semihosting information, extracted from the target. */ struct semihosting *semihosting; }; @@ -223,6 +225,13 @@ struct gdb_fileio_info { uint64_t param_4; }; +/** Returns a description of the endianness for the specified target. */ +static inline const char *target_endianness(struct target *target) +{ + return (target->endianness == TARGET_ENDIAN_UNKNOWN) ? "unknown" : + (target->endianness == TARGET_BIG_ENDIAN) ? "big endian" : "little endian"; +} + /** Returns the instance-specific name of the specified target. */ static inline const char *target_name(struct target *target) { @@ -387,6 +396,7 @@ int target_call_timer_callbacks_now(void); struct target *get_target_by_num(int num); struct target *get_current_target(struct command_context *cmd_ctx); +struct target *get_current_target_or_null(struct command_context *cmd_ctx); struct target *get_target(const char *id); /** @@ -470,6 +480,13 @@ int target_remove_watchpoint(struct target *target, int target_hit_watchpoint(struct target *target, struct watchpoint **watchpoint); +/** + * Obtain the architecture for GDB. + * + * This routine is a wrapper for target->type->get_gdb_arch. + */ +const char *target_get_gdb_arch(struct target *target); + /** * Obtain the registers for GDB. * @@ -479,6 +496,13 @@ int target_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class); +/** + * Check if @a target allows GDB connections. + * + * Some target do not implement the necessary code required by GDB. + */ +bool target_supports_gdb_connection(struct target *target); + /** * Step the target. * diff --git a/src/target/target_type.h b/src/target/target_type.h index fbbd57d98..a8928911f 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -88,6 +88,15 @@ struct target_type { int (*deassert_reset)(struct target *target); int (*soft_reset_halt)(struct target *target); + /** + * Target architecture for GDB. + * + * The string returned by this function will not be automatically freed; + * if dynamic allocation is used for this value, it must be managed by + * the target, ideally by caching the result for subsequent calls. + */ + const char *(*get_gdb_arch)(struct target *target); + /** * Target register access for GDB. Do @b not call this function * directly, use target_get_gdb_reg_list() instead. diff --git a/tcl/board/arty_s7.cfg b/tcl/board/arty_s7.cfg new file mode 100644 index 000000000..ca7d3f1c4 --- /dev/null +++ b/tcl/board/arty_s7.cfg @@ -0,0 +1,35 @@ +# +# Arty S7: Spartan7 25/50 FPGA Board for Makers and Hobbyists +# +# https://www.xilinx.com/products/boards-and-kits/1-pnziih.html +# https://store.digilentinc.com/arty-s7-spartan-7-fpga-board-for-makers-and-hobbyists/ + +source [find interface/ftdi/digilent-hs1.cfg] + +# Xilinx Spartan7-25/50 FPGA (XC7S{25,50}-CSGA324) +source [find cpld/xilinx-xc7.cfg] +source [find cpld/jtagspi.cfg] + +adapter_khz 25000 + +# Usage: +# +# Load Bitstream into FPGA: +# openocd -f board/arty_s7.cfg -c "init;\ +# pld load 0 bitstream.bit;\ +# shutdown" +# +# Write Bitstream to Flash: +# openocd -f board/arty_s7.cfg -c "init;\ +# jtagspi_init 0 bscan_spi_xc7s??.bit;\ +# jtagspi_program bitstream.bin 0;\ +# xc7s_program xc7s.tap;\ +# shutdown" +# +# jtagspi flash proxies can be found at: +# https://github.com/quartiq/bscan_spi_bitstreams +# +# For the Spartan 50 variant, use +# - https://github.com/quartiq/bscan_spi_bitstreams/raw/master/bscan_spi_xc7s50.bit +# For the Spartan 25 variant, use +# - https://github.com/quartiq/bscan_spi_bitstreams/raw/master/bscan_spi_xc7s25.bit diff --git a/tcl/board/emcraft_imx8m-som-bsb.cfg b/tcl/board/emcraft_imx8m-som-bsb.cfg new file mode 100644 index 000000000..5571d0ecb --- /dev/null +++ b/tcl/board/emcraft_imx8m-som-bsb.cfg @@ -0,0 +1,22 @@ +# +# configuration file for Emcraft IMX8M-SOM-BSB +# + +# only JTAG supported +transport select jtag + +# set a safe JTAG clock speed, can be overridden +adapter_khz 1000 + +# SRST and TRST are wired up +reset_config trst_and_srst + +# delay after SRST goes inactive +adapter_nsrst_delay 70 + +# board has an i.MX8MQ with 4 Cortex-A53 cores +set CHIPNAME imx8mq +set CHIPCORES 4 + +# source SoC configuration +source [find target/imx8m.cfg] diff --git a/tcl/board/numato_mimas_a7.cfg b/tcl/board/numato_mimas_a7.cfg new file mode 100644 index 000000000..1261feacc --- /dev/null +++ b/tcl/board/numato_mimas_a7.cfg @@ -0,0 +1,36 @@ +# +# Numato Mimas A7 - Artix 7 FPGA Board +# +# https://numato.com/product/mimas-a7-artix-7-fpga-development-board-with-ddr-sdram-and-gigabit-ethernet +# +# Note: Connect external DC power supply if programming a heavy design onto FPGA. +# Programming while powering via USB may lead to programming failure. +# Therefore, prefer external power supply. + +interface ftdi +ftdi_device_desc "Mimas Artix 7 FPGA Module" +ftdi_vid_pid 0x2a19 0x1009 + +# channel 0 is for custom purpose by users (like uart, fifo etc) +# channel 1 is reserved for JTAG (by-default) or SPI (possible via changing solder jumpers) +ftdi_channel 1 +ftdi_tdo_sample_edge falling + + +# FTDI Pin Layout +# +# +--------+-------+-------+-------+-------+-------+-------+-------+ +# | DBUS7 | DBUS6 | DBUS5 | DBUS4 | DBUS3 | DBUS2 | DBUS1 | DBUS0 | +# +--------+-------+-------+-------+-------+-------+-------+-------+ +# | PROG_B | OE_N | NC | NC | TMS | TDO | TDI | TCK | +# +--------+-------+-------+-------+-------+-------+-------+-------+ +# +# OE_N is JTAG buffer output enable signal (active-low) +# PROG_B is not used, so left as input to FTDI. +# +ftdi_layout_init 0x0008 0x004b +reset_config none +adapter_khz 30000 + +source [find cpld/xilinx-xc7.cfg] +source [find cpld/jtagspi.cfg] diff --git a/tcl/board/renesas_salvator-xs.cfg b/tcl/board/renesas_salvator-xs.cfg new file mode 100644 index 000000000..1558a5274 --- /dev/null +++ b/tcl/board/renesas_salvator-xs.cfg @@ -0,0 +1,23 @@ +# Renesas R-Car Gen3 Salvator-X(S) Board Config + +# The Salvator-X(S) boards come with either an H3, M3W, or M3N SOC. + +echo "\nSalvator-X(S):" +if { ![info exists SOC] } { + set SOC H3 +} +source [find target/renesas_rcar_gen3.cfg] + +reset_config trst_and_srst srst_nogate + +proc init_reset {mode} { + # Assert both resets: equivalent to a power-on reset + jtag_reset 1 1 + + # Deassert TRST to begin TAP communication + jtag_reset 0 1 + + # TAP should now be responsive, validate the scan-chain + jtag arp_init +} + diff --git a/tcl/cpld/xilinx-xc7.cfg b/tcl/cpld/xilinx-xc7.cfg index d5824f8a1..4c0502c5d 100644 --- a/tcl/cpld/xilinx-xc7.cfg +++ b/tcl/cpld/xilinx-xc7.cfg @@ -9,7 +9,15 @@ if { [info exists CHIPNAME] } { # the 4 top bits (28:31) are the die stepping/revisions. ignore it. jtag newtap $_CHIPNAME tap -irlen 6 -ignore-version \ + -expected-id 0x03622093 \ + -expected-id 0x03620093 \ + -expected-id 0x037C4093 \ + -expected-id 0x0362F093 \ + -expected-id 0x037C8093 \ + -expected-id 0x037C7093 \ + -expected-id 0x037C3093 \ -expected-id 0x0362E093 \ + -expected-id 0x037C2093 \ -expected-id 0x0362D093 \ -expected-id 0x0362C093 \ -expected-id 0x03632093 \ diff --git a/tcl/interface/ftdi/c232hm.cfg b/tcl/interface/ftdi/c232hm.cfg new file mode 100644 index 000000000..387abbb05 --- /dev/null +++ b/tcl/interface/ftdi/c232hm.cfg @@ -0,0 +1,15 @@ +# +# FTDI USB Hi-Speed to MPSSE Cable +# +# http://www.ftdichip.com/Products/Cables/USBMPSSE.htm +# +# C232HM-DDHSL-0 and C232HM-EDSL-0 provide 3.3V and 5V on pin 1 (Red), +# respectively. +# + +interface ftdi +#ftdi_device_desc "C232HM-DDHSL-0" +#ftdi_device_desc "C232HM-EDHSL-0" +ftdi_vid_pid 0x0403 0x6014 + +ftdi_layout_init 0x0008 0x000b diff --git a/tcl/target/atsamv.cfg b/tcl/target/atsamv.cfg index d1f8454d5..43962de31 100644 --- a/tcl/target/atsamv.cfg +++ b/tcl/target/atsamv.cfg @@ -45,6 +45,14 @@ if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to # perform a soft reset cortex_m reset_config sysresetreq + + # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal + # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3 + # makes the data access cacheable. This allows reading and writing data in the + # CPU cache from the debugger, which is far more useful than going straight to + # RAM when operating on typical variables, and is generally no worse when + # operating on special memory locations. + $_CHIPNAME.dap apcsw 0x08000000 0x08000000 } set _FLASHNAME $_CHIPNAME.flash diff --git a/tcl/target/esi32xx.cfg b/tcl/target/esi32xx.cfg new file mode 100644 index 000000000..d32af39bd --- /dev/null +++ b/tcl/target/esi32xx.cfg @@ -0,0 +1,36 @@ +# +# EnSilica eSi-32xx SoC (eSi-RISC Family) +# http://www.ensilica.com/risc-ip/ +# + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME esi32xx +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x11234001 +} + +jtag newtap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME esirisc -chain-position $_CHIPNAME.cpu + +# Targets with the UNIFIED_ADDRESS_SPACE option disabled should set +# CACHEARCH to 'harvard'. By default, 'von_neumann' is assumed. +if { [info exists CACHEARCH] } { + $_TARGETNAME esirisc cache_arch $CACHEARCH +} + +adapter_khz 2000 + +reset_config none + +# The default linker scripts provided by the eSi-RISC toolchain do not +# specify attributes on memory regions, which results in incorrect +# application of software breakpoints by GDB. +gdb_breakpoint_override hard diff --git a/tcl/target/max32620.cfg b/tcl/target/max32620.cfg new file mode 100644 index 000000000..80cb25a47 --- /dev/null +++ b/tcl/target/max32620.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX32620 OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max32620 cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -ignore-version +} else { + swd newdap max32620 cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max32620.cpu cortex_m -chain-position max32620.cpu +max32620.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max32620 flash base address 0x00000000 +# max32620 flash size 0x200000 (2MB) +# max32620 FLC base address 0x40002000 +# max32620 sector (page) size 0x2000 (8kB) +# max32620 clock speed 96 (MHz) +flash bank max32620.flash max32xxx 0x00000000 0x200000 0 0 max32620.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/max32625.cfg b/tcl/target/max32625.cfg new file mode 100644 index 000000000..7182b235f --- /dev/null +++ b/tcl/target/max32625.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX32625 OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max32625 cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -expected-id 0x07f71197 -ignore-version +} else { + swd newdap max32625 cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max32625.cpu cortex_m -chain-position max32625.cpu +max32625.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max32625 flash base address 0x00000000 +# max32625 flash size 0x80000 (512k) +# max32625 FLC base address 0x40002000 +# max32625 sector (page) size 0x2000 (8kB) +# max32625 clock speed 96 (MHz) +flash bank max32625.flash max32xxx 0x00000000 0x80000 0 0 max32625.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/max3263x.cfg b/tcl/target/max3263x.cfg new file mode 100644 index 000000000..f23b0b64d --- /dev/null +++ b/tcl/target/max3263x.cfg @@ -0,0 +1,28 @@ +# Maxim Integrated MAX3263X OpenOCD target configuration file +# www.maximintegrated.com + +# adapter speed +adapter_khz 4000 + +# reset pin configuration +reset_config srst_only + +if {[using_jtag]} { + jtag newtap max3263x cpu -irlen 4 -irmask 0xf -expected-id 0x4ba00477 -ignore-version + jtag newtap maxtest tap -irlen 4 -irmask 0xf -ircapture 0x1 -expected-id 0x07f76197 -ignore-version +} else { + swd newdap max3263x cpu -irlen 4 -irmask 0xf -expected-id 0x2ba01477 -ignore-version +} + +# target configuration +target create max3263x.cpu cortex_m -chain-position max3263x.cpu +max3263x.cpu configure -work-area-phys 0x20005000 -work-area-size 0x2000 + +# Config Command: flash bank name driver base size chip_width bus_width target [driver_options] +# flash bank max32xxx 0 0 +# max3263x flash base address 0x00000000 +# max3263x flash size 0x200000 (2MB) +# max3263x FLC base address 0x40002000 +# max3263x sector (page) size 0x2000 (8kB) +# max3263x clock speed 96 (MHz) +flash bank max3263x.flash max32xxx 0x00000000 0x200000 0 0 max3263x.cpu 0x40002000 0x2000 96 diff --git a/tcl/target/renesas_rcar_gen3.cfg b/tcl/target/renesas_rcar_gen3.cfg new file mode 100644 index 000000000..a6eef674b --- /dev/null +++ b/tcl/target/renesas_rcar_gen3.cfg @@ -0,0 +1,169 @@ +# Renesas R-Car Generation 3 SOCs +# - There are a combination of Cortex-A57s, Cortex-A53s, and Cortex-R7 for each Gen3 SOC +# - Each SOC can boot through any of the, up to 3, core types that it has +# e.g. H3 can boot through Cortex-A57, Cortex-A53, or Cortex-R7 + +# Supported Gen3 SOCs and their cores: +# H3: Cortex-A57 x 4, Cortex-A53 x 4, Cortex-R7 x 2 (Lock-Step) +# M3W: Cortex-A57 x 2, Cortex-A53 x 4, Cortex-R7 x 2 (Lock-Step) +# M3N: Cortex-A57 x 2, Cortex-R7 x 2 (Lock-Step) +# V3H: Cortex-A53 x 4, Cortex-R7 x 2 (Lock-Step) +# V3M: Cortex-A53 x 2, Cortex-R7 x 2 (Lock-Step) +# E3: Cortex-A53 x 1, Cortex-R7 x 2 (Lock-Step) +# D3: Cortex-A53 x 1 + +# Usage: +# There are 2 configuration options: +# SOC: Selects the supported SOC. (Default 'H3') +# BOOT_CORE: Selects the booting core. 'CA57', 'CA53', or 'CR7' +# Defaults to 'CA57' if the SOC has one, else defaults to 'CA53' + +if { [info exists SOC] } { + set _soc $SOC +} else { + set _soc H3 +} + +# Set configuration for each SOC and the default 'BOOT_CORE' +switch $_soc { + H3 { + set _CHIPNAME r8a77950 + set _num_ca57 4 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA57 + } + M3W { + set _CHIPNAME r8a77960 + set _num_ca57 2 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA57 + } + M3N { + set _CHIPNAME r8a77965 + set _num_ca57 2 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA57 + } + V3H { + set _CHIPNAME r8a77970 + set _num_ca57 0 + set _num_ca53 4 + set _num_cr7 1 + set _boot_core CA53 + } + V3M { + set _CHIPNAME r8a77980 + set _num_ca57 0 + set _num_ca53 2 + set _num_cr7 1 + set _boot_core CA53 + } + E3 { + set _CHIPNAME r8a77990 + set _num_ca57 0 + set _num_ca53 1 + set _num_cr7 1 + set _boot_core CA53 + } + D3 { + set _CHIPNAME r8a77995 + set _num_ca57 0 + set _num_ca53 1 + set _num_cr7 0 + set _boot_core CA53 + } + default { + echo "'$_soc' is invalid!" + } +} + +# If configured, override the default 'CHIPNAME' +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} + +# If configured, override the default 'BOOT_CORE' +if { [info exists BOOT_CORE] } { + set _boot_core $BOOT_CORE +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x5ba00477 +} + +echo "\t$_soc - $_num_ca57 CA57(s), $_num_ca53 CA53(s), $_num_cr7 CR7(s)" +echo "\tBoot Core - $_boot_core\n" + +set _DAPNAME $_CHIPNAME.dap + +# TAP and DAP +jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x01 -irmask 0x0f -expected-id $_DAP_TAPID +dap create $_DAPNAME -chain-position $_CHIPNAME.cpu + +set CA57_DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000} +set CA57_CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000} +set CA53_DBGBASE {0x80C10000 0x80D10000 0x80E10000 0x80F10000} +set CA53_CTIBASE {0x80C20000 0x80D20000 0x80E20000 0x80F20000} +set CR7_DBGBASE 0x80910000 +set CR7_CTIBASE 0x80918000 + +set smp_targets "" + +proc setup_a5x {core_name dbgbase ctibase num boot} { + global _CHIPNAME + global _DAPNAME + global smp_targets + for { set _core 0 } { $_core < $num } { incr _core } { + set _TARGETNAME $_CHIPNAME.$core_name.$_core + set _CTINAME $_TARGETNAME.cti + cti create $_CTINAME -dap $_DAPNAME -ap-num 1 \ + -ctibase [lindex $ctibase $_core] + set _command "target create $_TARGETNAME aarch64 -dap $_DAPNAME \ + -ap-num 1 -dbgbase [lindex $dbgbase $_core] -cti $_CTINAME" + if { $_core == 0 && $boot == 1 } { + set _targets "$_TARGETNAME" + } else { + set _command "$_command -defer-examine" + } + set smp_targets "$smp_targets $_TARGETNAME" + eval $_command + } +} + +proc setup_cr7 {dbgbase ctibase boot} { + global _CHIPNAME + global _DAPNAME + set _TARGETNAME $_CHIPNAME.r7 + set _CTINAME $_TARGETNAME.cti + cti create $_CTINAME -dap $_DAPNAME -ap-num 1 -ctibase $ctibase + set _command "target create $_TARGETNAME cortex_r4 -dap $_DAPNAME \ + -ap-num 1 -dbgbase $dbgbase" + if { $boot == 1 } { + set _targets "$_TARGETNAME" + } else { + set _command "$_command -defer-examine" + } + eval $_command +} + +# Organize target list based on the boot core +if { [string equal $_boot_core CA57] } { + setup_a5x a57 $CA57_DBGBASE $CA57_CTIBASE $_num_ca57 1 + setup_a5x a53 $CA53_DBGBASE $CA53_CTIBASE $_num_ca53 0 + setup_cr7 $CR7_DBGBASE $CR7_CTIBASE 0 +} elseif { [string equal $_boot_core CA53] } { + setup_a5x a53 $CA53_DBGBASE $CA53_CTIBASE $_num_ca53 1 + setup_a5x a57 $CA57_DBGBASE $CA57_CTIBASE $_num_ca57 0 + setup_cr7 $CR7_DBGBASE $CR7_CTIBASE 0 +} else { + setup_cr7 $CR7_DBGBASE $CR7_CTIBASE 1 + setup_a5x a57 $CA57_DBGBASE $CA57_CTIBASE $_num_ca57 0 + setup_a5x a53 $CA53_DBGBASE $CA53_CTIBASE $_num_ca53 0 +} + +eval "target smp $smp_targets" diff --git a/tcl/target/stm32f7x.cfg b/tcl/target/stm32f7x.cfg index 562de30f6..b0468e21e 100755 --- a/tcl/target/stm32f7x.cfg +++ b/tcl/target/stm32f7x.cfg @@ -65,6 +65,14 @@ if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to # perform a soft reset cortex_m reset_config sysresetreq + + # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal + # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3 + # makes the data access cacheable. This allows reading and writing data in the + # CPU cache from the debugger, which is far more useful than going straight to + # RAM when operating on typical variables, and is generally no worse when + # operating on special memory locations. + $_CHIPNAME.dap apcsw 0x08000000 0x08000000 } $_TARGETNAME configure -event examine-end { @@ -145,3 +153,4 @@ $_TARGETNAME configure -event reset-start { # Reduce speed since CPU speed will slow down to 16MHz with the reset adapter_khz 2000 } + diff --git a/tcl/target/stm32h7x.cfg b/tcl/target/stm32h7x.cfg index 10477a5a7..0bfc43dfd 100644 --- a/tcl/target/stm32h7x.cfg +++ b/tcl/target/stm32h7x.cfg @@ -56,13 +56,31 @@ if {[using_jtag]} { jtag_ntrst_delay 100 } -# use hardware reset, connect under reset +# use hardware reset +# +# The STM32H7 does not support connect_assert_srst mode because the AXI is +# unavailable while SRST is asserted, and that is used to access the DBGMCU +# component at 0x5C001000 in the examine-end event handler. +# +# It is possible to access the DBGMCU component at 0xE00E1000 via AP2 instead +# of the default AP0, and that works with SRST asserted; however, nonzero AP +# usage does not work with HLA, so is not done by default. That change could be +# made in a local configuration file if connect_assert_srst mode is needed for +# a specific application and a non-HLA adapter is in use. reset_config srst_only srst_nogate if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to # perform a soft reset cortex_m reset_config sysresetreq + + # Set CSW[27], which according to ARM ADI v5 appendix E1.4 maps to AHB signal + # HPROT[3], which according to AMBA AHB/ASB/APB specification chapter 3.7.3 + # makes the data access cacheable. This allows reading and writing data in the + # CPU cache from the debugger, which is far more useful than going straight to + # RAM when operating on typical variables, and is generally no worse when + # operating on special memory locations. + $_CHIPNAME.dap apcsw 0x08000000 0x08000000 } $_TARGETNAME configure -event examine-end { @@ -92,3 +110,4 @@ $_TARGETNAME configure -event reset-init { # Clock after reset is HSI at 64 MHz, no need of PLL adapter_khz 4000 } + diff --git a/tcl/target/zynq_7000.cfg b/tcl/target/zynq_7000.cfg index 07a6c8352..1562768c5 100644 --- a/tcl/target/zynq_7000.cfg +++ b/tcl/target/zynq_7000.cfg @@ -27,3 +27,22 @@ adapter_khz 1000 ${_TARGETNAME}0 configure -event reset-assert-post "cortex_a dbginit" ${_TARGETNAME}1 configure -event reset-assert-post "cortex_a dbginit" + +pld device virtex2 zynq_pl.bs 1 + +set XC7_JSHUTDOWN 0x0d +set XC7_JPROGRAM 0x0b +set XC7_JSTART 0x0c +set XC7_BYPASS 0x3f + +proc zynqpl_program {tap} { + global XC7_JSHUTDOWN XC7_JPROGRAM XC7_JSTART XC7_BYPASS + irscan $tap $XC7_JSHUTDOWN + irscan $tap $XC7_JPROGRAM + runtest 60000 + #JSTART prevents this from working... + #irscan $tap $XC7_JSTART + runtest 2000 + irscan $tap $XC7_BYPASS + runtest 2000 +}