diff options
author | Tim Newsome <tim@sifive.com> | 2020-12-31 13:40:49 -0800 |
---|---|---|
committer | Tim Newsome <tim@sifive.com> | 2020-12-31 13:40:49 -0800 |
commit | 11b8110443bbd158f73c7bf00a52bd6863d6b42f (patch) | |
tree | 23cb0e565256d2c94b0774acbf8156f19e409ef2 /src | |
parent | b8620764c09cbb05d8179fd5f520110fee114417 (diff) | |
parent | c69b4deae36a7bcbab5df80ec2a5dbfd652e25ac (diff) | |
download | riscv-openocd-11b8110443bbd158f73c7bf00a52bd6863d6b42f.zip riscv-openocd-11b8110443bbd158f73c7bf00a52bd6863d6b42f.tar.gz riscv-openocd-11b8110443bbd158f73c7bf00a52bd6863d6b42f.tar.bz2 |
Merge branch 'master' into from_upstream
Conflicts:
.github/workflows/snapshot.yml
.gitmodules
src/flash/nor/drivers.c
src/helper/jep106.inc
src/rtos/hwthread.c
src/target/riscv/riscv.c
src/target/target.c
Change-Id: I62f65e10d15dcda4c405d4042cce1d96f8e1680a
Diffstat (limited to 'src')
133 files changed, 7633 insertions, 1697 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 07981aa..781c1e7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,7 +53,8 @@ endif %D%/target/libtarget.la \ %D%/server/libserver.la \ %D%/rtos/librtos.la \ - %D%/helper/libhelper.la + %D%/helper/libhelper.la \ + %D%/rtt/librtt.la BIN2C = $(srcdir)/%D%/helper/bin2char.sh @@ -83,3 +84,4 @@ include %D%/rtos/Makefile.am include %D%/server/Makefile.am include %D%/flash/Makefile.am include %D%/pld/Makefile.am +include %D%/rtt/Makefile.am diff --git a/src/flash/nand/mxc.c b/src/flash/nand/mxc.c index 2c5de03..9002707 100644 --- a/src/flash/nand/mxc.c +++ b/src/flash/nand/mxc.c @@ -860,7 +860,7 @@ static int validate_target_state(struct nand_device *nand) return ERROR_OK; } -int ecc_status_v1(struct nand_device *nand) +static int ecc_status_v1(struct nand_device *nand) { struct mxc_nf_controller *mxc_nf_info = nand->controller_priv; struct target *target = nand->target; @@ -886,7 +886,7 @@ int ecc_status_v1(struct nand_device *nand) return ERROR_OK; } -int ecc_status_v2(struct nand_device *nand) +static int ecc_status_v2(struct nand_device *nand) { struct mxc_nf_controller *mxc_nf_info = nand->controller_priv; struct target *target = nand->target; diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 16e43a0..771f21f 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -52,10 +52,12 @@ NOR_DRIVERS = \ %D%/psoc5lp.c \ %D%/psoc6.c \ %D%/renesas_rpchf.c \ + %D%/sfdp.c \ %D%/sh_qspi.c \ %D%/sim3x.c \ %D%/spi.c \ %D%/stmsmi.c \ + %D%/stmqspi.c \ %D%/stellaris.c \ %D%/stm32f1x.c \ %D%/stm32f2x.c \ @@ -84,6 +86,8 @@ NORHEADERS = \ %D%/imp.h \ %D%/non_cfi.h \ %D%/ocl.h \ + %D%/sfdp.h \ %D%/spi.h \ %D%/stm32l4x.h \ + %D%/stmqspi.h \ %D%/msp432.h diff --git a/src/flash/nor/at91sam3.c b/src/flash/nor/at91sam3.c index c9baffc..9c4afd4 100644 --- a/src/flash/nor/at91sam3.c +++ b/src/flash/nor/at91sam3.c @@ -3099,7 +3099,7 @@ FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command) * is owned by this bank. This simplification works only for one shot * deallocation like current flash_free_all_banks() */ -void sam3_free_driver_priv(struct flash_bank *bank) +static void sam3_free_driver_priv(struct flash_bank *bank) { struct sam3_chip *chip = all_sam3_chips; while (chip) { diff --git a/src/flash/nor/atsamv.c b/src/flash/nor/atsamv.c index af48398..8f1450b 100644 --- a/src/flash/nor/atsamv.c +++ b/src/flash/nor/atsamv.c @@ -676,6 +676,9 @@ showall: } if ((who >= 0) && (((unsigned)who) < SAMV_NUM_GPNVM_BITS)) { r = samv_get_gpnvm(target, who, &v); + if (r != ERROR_OK) + return r; + command_print(CMD, "samv-gpnvm%u: %u", who, v); return r; } else { diff --git a/src/flash/nor/bluenrg-x.c b/src/flash/nor/bluenrg-x.c index 57ea739..57aebc5 100644 --- a/src/flash/nor/bluenrg-x.c +++ b/src/flash/nor/bluenrg-x.c @@ -43,7 +43,7 @@ struct flash_ctrl_priv_data { char *part_name; }; -const struct flash_ctrl_priv_data flash_priv_data_1 = { +static const struct flash_ctrl_priv_data flash_priv_data_1 = { .die_id_reg = 0x4090001C, .jtag_idcode_reg = 0x40900028, .flash_base = 0x10040000, @@ -53,7 +53,7 @@ const struct flash_ctrl_priv_data flash_priv_data_1 = { .part_name = "BLUENRG-1", }; -const struct flash_ctrl_priv_data flash_priv_data_2 = { +static const struct flash_ctrl_priv_data flash_priv_data_2 = { .die_id_reg = 0x4090001C, .jtag_idcode_reg = 0x40900028, .flash_base = 0x10040000, @@ -63,7 +63,7 @@ const struct flash_ctrl_priv_data flash_priv_data_2 = { .part_name = "BLUENRG-2", }; -const struct flash_ctrl_priv_data flash_priv_data_lp = { +static const struct flash_ctrl_priv_data flash_priv_data_lp = { .die_id_reg = 0x40000000, .jtag_idcode_reg = 0x40000004, .flash_base = 0x10040000, @@ -79,7 +79,11 @@ struct bluenrgx_flash_bank { const struct flash_ctrl_priv_data *flash_ptr; }; -const struct flash_ctrl_priv_data *flash_ctrl[] = {&flash_priv_data_1, &flash_priv_data_2, &flash_priv_data_lp}; +static const struct flash_ctrl_priv_data *flash_ctrl[] = { + &flash_priv_data_1, + &flash_priv_data_2, + &flash_priv_data_lp +}; /* flash_bank bluenrg-x 0 0 0 0 <target#> */ FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command) diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c index d563013..c162c70 100644 --- a/src/flash/nor/core.c +++ b/src/flash/nor/core.c @@ -94,7 +94,7 @@ int flash_driver_protect(struct flash_bank *bank, int set, unsigned int first, } int flash_driver_write(struct flash_bank *bank, - uint8_t *buffer, uint32_t offset, uint32_t count) + const uint8_t *buffer, uint32_t offset, uint32_t count) { int retval; @@ -135,6 +135,43 @@ int default_flash_read(struct flash_bank *bank, return target_read_buffer(bank->target, offset + bank->base, count, buffer); } +int flash_driver_verify(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + int retval; + + retval = bank->driver->verify ? bank->driver->verify(bank, buffer, offset, count) : + default_flash_verify(bank, buffer, offset, count); + if (retval != ERROR_OK) { + LOG_ERROR("verify failed in bank at " TARGET_ADDR_FMT " starting at 0x%8.8" PRIx32, + bank->base, offset); + } + + return retval; +} + +int default_flash_verify(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + uint32_t target_crc, image_crc; + int retval; + + retval = image_calculate_checksum(buffer, count, &image_crc); + if (retval != ERROR_OK) + return retval; + + retval = target_checksum_memory(bank->target, offset + bank->base, count, &target_crc); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("addr " TARGET_ADDR_FMT ", len 0x%08" PRIx32 ", crc 0x%08" PRIx32 " 0x%08" PRIx32, + offset + bank->base, count, ~image_crc, ~target_crc); + if (target_crc == image_crc) + return ERROR_OK; + else + return ERROR_FAIL; +} + void flash_bank_add(struct flash_bank *bank) { /* put flash bank in linked list */ @@ -697,12 +734,12 @@ static bool flash_write_check_gap(struct flash_bank *bank, } -int flash_write_unlock(struct target *target, struct image *image, - uint32_t *written, bool erase, bool unlock) +int flash_write_unlock_verify(struct target *target, struct image *image, + uint32_t *written, bool erase, bool unlock, bool write, bool verify) { int retval = ERROR_OK; - int section; + unsigned int section; uint32_t section_offset; struct flash_bank *c; int *padding; @@ -727,8 +764,8 @@ int flash_write_unlock(struct target *target, struct image *image, * whereas an image can have sections out of order. */ struct imagesection **sections = malloc(sizeof(struct imagesection *) * image->num_sections); - int i; - for (i = 0; i < image->num_sections; i++) + + for (unsigned int i = 0; i < image->num_sections; i++) sections[i] = &image->sections[i]; qsort(sections, image->num_sections, sizeof(struct imagesection *), @@ -738,7 +775,7 @@ int flash_write_unlock(struct target *target, struct image *image, while (section < image->num_sections) { uint32_t buffer_idx; uint8_t *buffer; - int section_last; + unsigned int section_last; target_addr_t run_address = sections[section]->base_address + section_offset; uint32_t run_size = sections[section]->size - section_offset; int pad_bytes = 0; @@ -932,8 +969,17 @@ int flash_write_unlock(struct target *target, struct image *image, } if (retval == ERROR_OK) { - /* write flash sectors */ - retval = flash_driver_write(c, buffer, run_address - c->base, run_size); + if (write) { + /* write flash sectors */ + retval = flash_driver_write(c, buffer, run_address - c->base, run_size); + } + } + + if (retval == ERROR_OK) { + if (verify) { + /* verify flash sectors */ + retval = flash_driver_verify(c, buffer, run_address - c->base, run_size); + } } free(buffer); @@ -957,7 +1003,7 @@ done: int flash_write(struct target *target, struct image *image, uint32_t *written, bool erase) { - return flash_write_unlock(target, image, written, erase, false); + return flash_write_unlock_verify(target, image, written, erase, false, true, false); } struct flash_sector *alloc_block_array(uint32_t offset, uint32_t size, diff --git a/src/flash/nor/core.h b/src/flash/nor/core.h index 163e578..107a1c5 100644 --- a/src/flash/nor/core.h +++ b/src/flash/nor/core.h @@ -200,6 +200,7 @@ void default_flash_free_driver_priv(struct flash_bank *bank); /** Deallocates all flash banks */ void flash_free_all_banks(void); + /** * Provides default read implementation for flash memory. * @param bank The bank to read. @@ -210,6 +211,18 @@ void flash_free_all_banks(void); */ int default_flash_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count); + +/** + * Provides default verify implementation for flash memory. + * @param bank The bank to verify. + * @param buffer The data bytes to verify. + * @param offset The offset into the chip to verify. + * @param count The number of bytes to verify. + * @returns ERROR_OK if successful; otherwise, an error code. + */ +int default_flash_verify(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count); + /** * Provides default erased-bank check handling. Checks to see if * the flash driver knows they are erased; if things look uncertain, @@ -217,7 +230,6 @@ int default_flash_read(struct flash_bank *bank, * @returns ERROR_OK if successful; otherwise, an error code. */ int default_flash_blank_check(struct flash_bank *bank); - /** * Returns the flash bank specified by @a name, which matches the * driver name and a suffix (option) specify the driver-specific diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 7f66047..e29d4f5 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -156,6 +156,20 @@ struct flash_driver { uint8_t *buffer, uint32_t offset, uint32_t count); /** + * Verify data in flash. Note CPU address will be + * "bank->base + offset", while the physical address is + * dependent upon current target MMU mappings. + * + * @param bank The bank to verify + * @param buffer The data bytes to verify against. + * @param offset The offset into the chip to verify. + * @param count The number of bytes to verify. + * @returns ERROR_OK if successful; otherwise, an error code. + */ + int (*verify)(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count); + + /** * Probe to determine what kind of flash is present. * This is invoked by the "probe" script command. * diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 8c8f13e..047b5f7 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -44,6 +44,7 @@ extern const struct flash_driver faux_flash; extern const struct flash_driver fm3_flash; extern const struct flash_driver fm4_flash; extern const struct flash_driver fespi_flash; +extern const struct flash_driver gd32vf103_flash; extern const struct flash_driver jtagspi_flash; extern const struct flash_driver kinetis_flash; extern const struct flash_driver kinetis_ke_flash; @@ -75,7 +76,7 @@ extern const struct flash_driver stm32f2x_flash; extern const struct flash_driver stm32lx_flash; extern const struct flash_driver stm32l4x_flash; extern const struct flash_driver stm32h7x_flash; -extern const struct flash_driver gd32vf103_flash; +extern const struct flash_driver stmqspi_flash; extern const struct flash_driver stmsmi_flash; extern const struct flash_driver str7x_flash; extern const struct flash_driver str9x_flash; @@ -150,6 +151,7 @@ static const struct flash_driver * const flash_drivers[] = { &stm32h7x_flash, &gd32vf103_flash, &stmsmi_flash, + &stmqspi_flash, &str7x_flash, &str9x_flash, &str9xpec_flash, diff --git a/src/flash/nor/imp.h b/src/flash/nor/imp.h index 06fb2a2..f66cf03 100644 --- a/src/flash/nor/imp.h +++ b/src/flash/nor/imp.h @@ -42,12 +42,14 @@ int flash_driver_erase(struct flash_bank *bank, unsigned int first, int flash_driver_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last); int flash_driver_write(struct flash_bank *bank, - uint8_t *buffer, uint32_t offset, uint32_t count); + const uint8_t *buffer, uint32_t offset, uint32_t count); int flash_driver_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count); +int flash_driver_verify(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t count); /* write (optional verify) an image to flash memory of the given target */ -int flash_write_unlock(struct target *target, struct image *image, - uint32_t *written, bool erase, bool unlock); +int flash_write_unlock_verify(struct target *target, struct image *image, + uint32_t *written, bool erase, bool unlock, bool write, bool verify); #endif /* OPENOCD_FLASH_NOR_IMP_H */ diff --git a/src/flash/nor/kinetis_ke.c b/src/flash/nor/kinetis_ke.c index 349b256..5aba7fc 100644 --- a/src/flash/nor/kinetis_ke.c +++ b/src/flash/nor/kinetis_ke.c @@ -434,7 +434,7 @@ static int kinetis_ke_prepare_flash(struct flash_bank *bank) return ERROR_OK; } -int kinetis_ke_stop_watchdog(struct target *target) +static int kinetis_ke_stop_watchdog(struct target *target) { struct working_area *watchdog_algorithm; struct armv7m_algorithm armv7m_info; diff --git a/src/flash/nor/lpc2000.c b/src/flash/nor/lpc2000.c index 942ef55..3ad62d6 100644 --- a/src/flash/nor/lpc2000.c +++ b/src/flash/nor/lpc2000.c @@ -1103,9 +1103,9 @@ static int lpc2000_write(struct flash_bank *bank, const uint8_t *buffer, uint32_ uint32_t original_value = buf_get_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32); if (original_value != checksum) { - LOG_WARNING("Verification will fail since checksum in image (0x%8.8" PRIx32 ") to be written to flash is " + LOG_WARNING("Boot verification checksum in image (0x%8.8" PRIx32 ") to be written to flash is " "different from calculated vector checksum (0x%8.8" PRIx32 ").", original_value, checksum); - LOG_WARNING("To remove this warning modify build tools on developer PC to inject correct LPC vector " + LOG_WARNING("OpenOCD will write the correct checksum. To remove this warning modify build tools on developer PC to inject correct LPC vector " "checksum."); } diff --git a/src/flash/nor/lpc2900.c b/src/flash/nor/lpc2900.c index c8e885a..6596cde 100644 --- a/src/flash/nor/lpc2900.c +++ b/src/flash/nor/lpc2900.c @@ -635,9 +635,9 @@ COMMAND_HANDLER(lpc2900_handle_write_custom_command) /* The image will always start at offset 0 */ struct image image; - image.base_address_set = 1; + image.base_address_set = true; image.base_address = 0; - image.start_address_set = 0; + image.start_address_set = false; const char *filename = CMD_ARGV[1]; const char *type = (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL; diff --git a/src/flash/nor/mrvlqspi.c b/src/flash/nor/mrvlqspi.c index 3293e61..3f5ce2c 100644 --- a/src/flash/nor/mrvlqspi.c +++ b/src/flash/nor/mrvlqspi.c @@ -761,7 +761,7 @@ static int mrvlqspi_flash_write(struct flash_bank *bank, const uint8_t *buffer, return retval; } -int mrvlqspi_flash_read(struct flash_bank *bank, uint8_t *buffer, +static int mrvlqspi_flash_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) { struct target *target = bank->target; @@ -914,7 +914,7 @@ static int mrvlqspi_flash_erase_check(struct flash_bank *bank) return ERROR_OK; } -int mrvlqspi_get_info(struct flash_bank *bank, char *buf, int buf_size) +static int mrvlqspi_get_info(struct flash_bank *bank, char *buf, int buf_size) { struct mrvlqspi_flash_bank *mrvlqspi_info = bank->driver_priv; diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c index 0b65367..45d3d09 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -158,7 +158,7 @@ struct nrf5_info { bool ficr_info_valid; struct nrf52_ficr_info ficr_info; const struct nrf5_device_spec *spec; - uint32_t hwid; + uint16_t hwid; enum nrf5_features features; unsigned int flash_size_kb; unsigned int ram_size_kb; @@ -328,7 +328,7 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip) do { res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready); if (res != ERROR_OK) { - LOG_ERROR("Couldn't read NVMC_READY register"); + LOG_ERROR("Error waiting NVMC_READY: generic flash write/erase error (check protection etc...)"); return res; } @@ -440,6 +440,38 @@ error: return ERROR_FAIL; } +static int nrf5_protect_check_clenr0(struct flash_bank *bank) +{ + int res; + uint32_t clenr0; + struct nrf5_bank *nbank = bank->driver_priv; + struct nrf5_info *chip = nbank->chip; + + assert(chip != NULL); + + res = target_read_u32(chip->target, NRF51_FICR_CLENR0, + &clenr0); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't read code region 0 size[FICR]"); + return res; + } + + if (clenr0 == 0xFFFFFFFF) { + res = target_read_u32(chip->target, NRF51_UICR_CLENR0, + &clenr0); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't read code region 0 size[UICR]"); + return res; + } + } + + for (unsigned int i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = + clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0; + + return ERROR_OK; +} + static int nrf5_protect_check_bprot(struct flash_bank *bank) { struct nrf5_bank *nbank = bank->driver_priv; @@ -469,9 +501,6 @@ static int nrf5_protect_check_bprot(struct flash_bank *bank) static int nrf5_protect_check(struct flash_bank *bank) { - int res; - uint32_t clenr0; - /* UICR cannot be write protected so just return early */ if (bank->base == NRF5_UICR_BASE) return ERROR_OK; @@ -484,53 +513,20 @@ static int nrf5_protect_check(struct flash_bank *bank) if (chip->features & NRF5_FEATURE_BPROT) return nrf5_protect_check_bprot(bank); - if (!(chip->features & NRF5_FEATURE_SERIES_51)) { - LOG_WARNING("Flash protection of this nRF device is not supported"); - return ERROR_FLASH_OPER_UNSUPPORTED; - } - - res = target_read_u32(chip->target, NRF51_FICR_CLENR0, - &clenr0); - if (res != ERROR_OK) { - LOG_ERROR("Couldn't read code region 0 size[FICR]"); - return res; - } + if (chip->features & NRF5_FEATURE_SERIES_51) + return nrf5_protect_check_clenr0(bank); - if (clenr0 == 0xFFFFFFFF) { - res = target_read_u32(chip->target, NRF51_UICR_CLENR0, - &clenr0); - if (res != ERROR_OK) { - LOG_ERROR("Couldn't read code region 0 size[UICR]"); - return res; - } - } - - for (unsigned int i = 0; i < bank->num_sectors; i++) - bank->sectors[i].is_protected = - clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0; - - return ERROR_OK; + LOG_WARNING("Flash protection of this nRF device is not supported"); + return ERROR_FLASH_OPER_UNSUPPORTED; } -static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first, +static int nrf5_protect_clenr0(struct flash_bank *bank, int set, unsigned int first, unsigned int last) { int res; uint32_t clenr0, ppfc; - struct nrf5_info *chip; - - /* UICR cannot be write protected so just bail out early */ - if (bank->base == NRF5_UICR_BASE) - return ERROR_FAIL; - - res = nrf5_get_probed_chip_if_halted(bank, &chip); - if (res != ERROR_OK) - return res; - - if (!(chip->features & NRF5_FEATURE_SERIES_51)) { - LOG_ERROR("Flash protection setting of this nRF device is not supported"); - return ERROR_FLASH_OPER_UNSUPPORTED; - } + struct nrf5_bank *nbank = bank->driver_priv; + struct nrf5_info *chip = nbank->chip; if (first != 0) { LOG_ERROR("Code region 0 must start at the beginning of the bank"); @@ -552,25 +548,59 @@ static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first, res = target_read_u32(chip->target, NRF51_UICR_CLENR0, &clenr0); if (res != ERROR_OK) { - LOG_ERROR("Couldn't read code region 0 size[UICR]"); + LOG_ERROR("Couldn't read code region 0 size from UICR"); return res; } - if (clenr0 == 0xFFFFFFFF) { - res = target_write_u32(chip->target, NRF51_UICR_CLENR0, - clenr0); - if (res != ERROR_OK) { - LOG_ERROR("Couldn't write code region 0 size[UICR]"); - return res; - } - - } else { + if (!set || clenr0 != 0xFFFFFFFF) { LOG_ERROR("You need to perform chip erase before changing the protection settings"); + return ERROR_FAIL; } - nrf5_protect_check(bank); + res = nrf5_nvmc_write_enable(chip); + if (res != ERROR_OK) + goto error; - return ERROR_OK; + clenr0 = bank->sectors[last].offset + bank->sectors[last].size; + res = target_write_u32(chip->target, NRF51_UICR_CLENR0, clenr0); + + int res2 = nrf5_wait_for_nvmc(chip); + + if (res == ERROR_OK) + res = res2; + + if (res == ERROR_OK) + LOG_INFO("A reset or power cycle is required for the new protection settings to take effect."); + else + LOG_ERROR("Couldn't write code region 0 size to UICR"); + +error: + nrf5_nvmc_read_only(chip); + + return res; +} + +static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first, + unsigned int last) +{ + int res; + struct nrf5_info *chip; + + /* UICR cannot be write protected so just bail out early */ + if (bank->base == NRF5_UICR_BASE) { + LOG_ERROR("UICR page does not support protection"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + res = nrf5_get_probed_chip_if_halted(bank, &chip); + if (res != ERROR_OK) + return res; + + if (chip->features & NRF5_FEATURE_SERIES_51) + return nrf5_protect_clenr0(bank, set, first, last); + + LOG_ERROR("Flash protection setting is not supported on this nRF5 device"); + return ERROR_FLASH_OPER_UNSUPPORTED; } static bool nrf5_info_variant_to_str(uint32_t variant, char *bf) @@ -618,7 +648,7 @@ static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size) variant, &variant[2]); } else { - res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%08" PRIx32 ")", + res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%04" PRIx16 ")", chip->hwid); } if (res <= 0) @@ -735,14 +765,15 @@ static int nrf5_probe(struct flash_bank *bank) struct nrf5_info *chip = nbank->chip; struct target *target = chip->target; - res = target_read_u32(target, NRF5_FICR_CONFIGID, &chip->hwid); + uint32_t configid; + res = target_read_u32(target, NRF5_FICR_CONFIGID, &configid); if (res != ERROR_OK) { LOG_ERROR("Couldn't read CONFIGID register"); return res; } - chip->hwid &= 0xFFFF; /* HWID is stored in the lower two - * bytes of the CONFIGID register */ + /* HWID is stored in the lower two bytes of the CONFIGID register */ + chip->hwid = configid & 0xFFFF; /* guess a nRF51 series if the device has no FICR INFO and we don't know HWID */ chip->features = NRF5_FEATURE_SERIES_51; @@ -820,8 +851,6 @@ static int nrf5_probe(struct flash_bank *bank) if (!bank->sectors) return ERROR_FAIL; - nrf5_protect_check(bank); - chip->bank[0].probed = true; } else { @@ -983,7 +1012,7 @@ static int nrf5_ll_flash_write(struct nrf5_info *chip, uint32_t address, const u 0, NULL, ARRAY_SIZE(reg_params), reg_params, source->address, source->size, - write_algorithm->address, 0, + write_algorithm->address, write_algorithm->address + sizeof(nrf5_flash_write_code) - 2, &armv7m_info); target_free_working_area(target, source); @@ -1011,6 +1040,34 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer, assert(offset % 4 == 0); assert(count % 4 == 0); + /* UICR CLENR0 based protection used on nRF51 is somewhat clumsy: + * RM reads: Code running from code region 1 will not be able to write + * to code region 0. + * Unfortunately the flash loader running from RAM can write to both + * code regions whithout any hint the protection is violated. + * + * Update protection state and check if any flash sector to be written + * is protected. */ + if (chip->features & NRF5_FEATURE_SERIES_51) { + + res = nrf5_protect_check_clenr0(bank); + if (res != ERROR_OK) + return res; + + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + struct flash_sector *bs = &bank->sectors[sector]; + + /* Start offset in or before this sector? */ + /* End offset in or behind this sector? */ + if ((offset < (bs->offset + bs->size)) + && ((offset + count - 1) >= bs->offset) + && bs->is_protected == 1) { + LOG_ERROR("Write refused, sector %d is protected", sector); + return ERROR_FLASH_PROTECTED; + } + } + } + res = nrf5_nvmc_write_enable(chip); if (res != ERROR_OK) goto error; @@ -1037,11 +1094,36 @@ static int nrf5_erase(struct flash_bank *bank, unsigned int first, if (res != ERROR_OK) return res; + /* UICR CLENR0 based protection used on nRF51 prevents erase + * absolutely silently. NVMC has no flag to indicate the protection + * was violated. + * + * Update protection state and check if any flash sector to be erased + * is protected. */ + if (chip->features & NRF5_FEATURE_SERIES_51) { + + res = nrf5_protect_check_clenr0(bank); + if (res != ERROR_OK) + return res; + } + /* For each sector to be erased */ - for (unsigned int s = first; s <= last && res == ERROR_OK; s++) + for (unsigned int s = first; s <= last && res == ERROR_OK; s++) { + + if (chip->features & NRF5_FEATURE_SERIES_51 + && bank->sectors[s].is_protected == 1) { + LOG_ERROR("Flash sector %d is protected", s); + return ERROR_FLASH_PROTECTED; + } + res = nrf5_erase_page(bank, chip, &bank->sectors[s]); + if (res != ERROR_OK) { + LOG_ERROR("Error erasing sector %d", s); + return res; + } + } - return res; + return ERROR_OK; } static void nrf5_free_driver_priv(struct flash_bank *bank) @@ -1158,23 +1240,16 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command) } res = nrf5_erase_all(chip); - if (res != ERROR_OK) { - LOG_ERROR("Failed to erase the chip"); - nrf5_protect_check(bank); - return res; - } + if (res == ERROR_OK) { + LOG_INFO("Mass erase completed."); + if (chip->features & NRF5_FEATURE_SERIES_51) + LOG_INFO("A reset or power cycle is required if the flash was protected before."); - res = nrf5_protect_check(bank); - if (res != ERROR_OK) { - LOG_ERROR("Failed to check chip's write protection"); - return res; + } else { + LOG_ERROR("Failed to erase the chip"); } - res = get_flash_bank_by_addr(target, NRF5_UICR_BASE, true, &bank); - if (res != ERROR_OK) - return res; - - return ERROR_OK; + return res; } COMMAND_HANDLER(nrf5_handle_info_command) diff --git a/src/flash/nor/psoc4.c b/src/flash/nor/psoc4.c index 9c2fdf7..b606e18 100644 --- a/src/flash/nor/psoc4.c +++ b/src/flash/nor/psoc4.c @@ -139,7 +139,7 @@ struct psoc4_chip_family { uint32_t flags; }; -const struct psoc4_chip_family psoc4_families[] = { +static const struct psoc4_chip_family psoc4_families[] = { { 0x93, "PSoC4100/4200", .flags = PSOC4_FAMILY_FLAG_LEGACY }, { 0x9A, "PSoC4000", .flags = 0 }, { 0x9E, "PSoC/PRoC BLE (119E)", .flags = 0 }, diff --git a/src/flash/nor/psoc6.c b/src/flash/nor/psoc6.c index df151c1..931404e 100644 --- a/src/flash/nor/psoc6.c +++ b/src/flash/nor/psoc6.c @@ -151,12 +151,6 @@ static int sromalgo_prepare(struct target *target) if (hr != ERROR_OK) return hr; - /* Restore THUMB bit in xPSR register */ - const struct armv7m_common *cm = target_to_armv7m(target); - hr = cm->store_core_reg_u32(target, ARMV7M_xPSR, 0x01000000); - if (hr != ERROR_OK) - return hr; - /* Allocate Working Area for Stack and Flash algorithm */ hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &g_stack_area); if (hr != ERROR_OK) @@ -908,7 +902,7 @@ COMMAND_HANDLER(psoc6_handle_mass_erase_command) * @param target current target * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -int handle_reset_halt(struct target *target) +static int handle_reset_halt(struct target *target) { int hr; uint32_t reset_addr; diff --git a/src/flash/nor/sfdp.c b/src/flash/nor/sfdp.c new file mode 100644 index 0000000..2183ac1 --- /dev/null +++ b/src/flash/nor/sfdp.c @@ -0,0 +1,263 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch <andreas.bolsch@mni.thm.de * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "spi.h" +#include "sfdp.h" + +#define SFDP_MAGIC 0x50444653 +#define SFDP_ACCESS_PROT 0xFF +#define SFDP_BASIC_FLASH 0xFF00 +#define SFDP_4BYTE_ADDR 0xFF84 + +static const char *sfdp_name = "sfdp"; + +struct sfdp_hdr { + uint32_t signature; + uint32_t revision; +}; + +struct sfdp_phdr { + uint32_t revision; + uint32_t ptr; +}; + +struct sfdp_basic_flash_param { + uint32_t fast_addr; /* 01: fast read and 3/4 address bytes */ + uint32_t density; /* 02: memory density */ + uint32_t fast_1x4; /* 03: 1-1-4 and 1-4-4 fast read */ + uint32_t fast_1x2; /* 04: 1-2-2 and 1-1-2 fast read */ + uint32_t fast_444; /* 05: 4-4-4 and 2-2-2 fast read */ + uint32_t read_222; /* 06: 2-2-2 fast read instr and dummy */ + uint32_t read_444; /* 07: 4-4-4 fast read instr and dummy */ + uint32_t erase_t12; /* 08: erase types 1, 2 */ + uint32_t erase_t34; /* 09: erase types 3, 4 */ + uint32_t erase_time; /* 10: erase times for types 1 - 4 */ + uint32_t chip_byte; /* 11: chip erase time, byte prog time, page prog */ + uint32_t susp_time; /* 12: suspend and resume times */ + uint32_t susp_instr; /* 13: suspend and resume instr */ + uint32_t pwrd_instr; /* 14: powerdown instr */ + uint32_t quad_req; /* 15: quad enable requirements */ + uint32_t addr_reset; /* 16: 3-/4-byte addressing and reset */ + uint32_t read_1x8; /* 17: 1-1-8 and 1-8-8 fast read instr and dummy */ + uint32_t dtr_drive; /* 18: dtr modes and drive strength */ + uint32_t octal_req; /* 19: octal enable requirements */ + uint32_t speed_888; /* 20: speed in 8-8-8 modes */ +}; + +struct sfdp_4byte_addr_param { + uint32_t flags; /* 01: various flags */ + uint32_t erase_t1234; /* 02: erase commands */ +}; + +/* Try to get parameters from flash via SFDP */ +int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, + read_sfdp_block_t read_sfdp_block) +{ + struct sfdp_hdr header; + struct sfdp_phdr *pheaders = NULL; + uint32_t *ptable = NULL; + unsigned int j, k, nph; + int retval, erase_type = 0; + + memset(dev, 0, sizeof(struct flash_device)); + + /* retrieve SFDP header */ + memset(&header, 0, sizeof(header)); + retval = read_sfdp_block(bank, 0x0, sizeof(header) >> 2, (uint32_t *)&header); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("header 0x%08" PRIx32 " 0x%08" PRIx32, header.signature, header.revision); + if (header.signature != SFDP_MAGIC) { + LOG_INFO("no SDFP found"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + if (((header.revision >> 24) & 0xFF) != SFDP_ACCESS_PROT) { + LOG_ERROR("access protocol 0x%02x not implemented", + (header.revision >> 24) & 0xFFU); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + /* retrieve table of parameter headers */ + nph = ((header.revision >> 16) & 0xFF) + 1; + LOG_DEBUG("parameter headers: %d", nph); + pheaders = malloc(sizeof(struct sfdp_phdr) * nph); + if (pheaders == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + memset(pheaders, 0, sizeof(struct sfdp_phdr) * nph); + retval = read_sfdp_block(bank, sizeof(header), + (sizeof(struct sfdp_phdr) >> 2) * nph, (uint32_t *)pheaders); + if (retval != ERROR_OK) + goto err; + + for (k = 0; k < nph; k++) { + uint8_t words = (pheaders[k].revision >> 24) & 0xFF; + uint16_t id = (((pheaders[k].ptr) >> 16) & 0xFF00) | (pheaders[k].revision & 0xFF); + uint32_t ptr = pheaders[k].ptr & 0xFFFFFF; + + LOG_DEBUG("pheader %d len=0x%02" PRIx8 " id=0x%04" PRIx16 + " ptr=0x%06" PRIx32, k, words, id, ptr); + + /* retrieve parameter table */ + ptable = malloc(words << 2); + if (ptable == NULL) { + LOG_ERROR("not enough memory"); + retval = ERROR_FAIL; + goto err; + } + retval = read_sfdp_block(bank, ptr, words, ptable); + if (retval != ERROR_OK) + goto err; + + for (j = 0; j < words; j++) + LOG_DEBUG("word %02d 0x%08X", j + 1, ptable[j]); + + if (id == SFDP_BASIC_FLASH) { + struct sfdp_basic_flash_param *table = (struct sfdp_basic_flash_param *)ptable; + uint16_t erase; + + if (words < 9) { + LOG_ERROR("id=0x%04" PRIx16 " invalid length %d", id, words); + retval = ERROR_FLASH_BANK_NOT_PROBED; + goto err; + } + + LOG_DEBUG("basic flash parameter table"); + /* dummy device name */ + dev->name = sfdp_name; + + /* default instructions */ + dev->read_cmd = SPIFLASH_READ; + dev->pprog_cmd = SPIFLASH_PAGE_PROGRAM; + dev->chip_erase_cmd = SPIFLASH_MASS_ERASE; + + /* get device size */ + if (table->density & (1UL << 31)) + dev->size_in_bytes = 1UL << ((table->density & ~(1UL << 31)) - 3); + else + dev->size_in_bytes = (table->density + 1) >> 3; + + /* 2-2-2 read instruction, not used */ + if (table->fast_444 & (1UL << 0)) + dev->qread_cmd = (table->read_222 >> 24) & 0xFF; + + /* 4-4-4 read instruction */ + if (table->fast_444 & (1UL << 4)) + dev->qread_cmd = (table->read_444 >> 24) & 0xFF; + + /* find the largest erase block size and instruction */ + erase = (table->erase_t12 >> 0) & 0xFFFF; + erase_type = 1; + if (((table->erase_t12 >> 16) & 0xFF) > (erase & 0xFF)) { + erase = (table->erase_t12 >> 16) & 0xFFFF; + erase_type = 2; + } + if (((table->erase_t34 >> 0) & 0xFF) > (erase & 0xFF)) { + erase = (table->erase_t34 >> 0) & 0xFFFF; + erase_type = 3; + } + if (((table->erase_t34 >> 16) & 0xFF) > (erase & 0xFF)) { + erase = (table->erase_t34 >> 16) & 0xFFFF; + erase_type = 4; + } + dev->erase_cmd = (erase >> 8) & 0xFF; + dev->sectorsize = 1UL << (erase & 0xFF); + + if ((offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) < words) { + /* get Program Page Size, if chip_byte present, that's optional */ + dev->pagesize = 1UL << ((table->chip_byte >> 4) & 0x0F); + } else { + /* no explicit page size specified ... */ + if (table->fast_addr & (1UL << 2)) { + /* Write Granularity = 1, use 64 bytes */ + dev->pagesize = 1UL << 6; + } else { + /* Write Granularity = 0, use 16 bytes */ + dev->pagesize = 1UL << 4; + } + } + + if (dev->size_in_bytes > (1UL << 24)) { + if (((table->fast_addr >> 17) & 0x3) == 0x0) + LOG_ERROR("device needs paging - not implemented"); + + /* 4-byte addresses needed if more than 16 MBytes */ + if (((offsetof(struct sfdp_basic_flash_param, addr_reset) >> 2) < words) && + (table->addr_reset & (1UL << 29))) { + /* dedicated 4-byte-address instructions, hopefully these ... + * this entry is unfortunately optional as well + * a subsequent 4-byte address table may overwrite this */ + dev->read_cmd = 0x13; + dev->pprog_cmd = 0x12; + dev->erase_cmd = 0xDC; + if (dev->qread_cmd != 0) + dev->qread_cmd = 0xEC; + } else if (((table->fast_addr >> 17) & 0x3) == 0x1) + LOG_INFO("device has to be switched to 4-byte addresses"); + } + } else if (id == SFDP_4BYTE_ADDR) { + struct sfdp_4byte_addr_param *table = (struct sfdp_4byte_addr_param *)ptable; + + if (words >= (offsetof(struct sfdp_4byte_addr_param, erase_t1234) + + sizeof(table->erase_t1234)) >> 2) { + LOG_INFO("4-byte address parameter table"); + + /* read and page program instructions */ + if (table->flags & (1UL << 0)) + dev->read_cmd = 0x13; + if (table->flags & (1UL << 5)) + dev->qread_cmd = 0xEC; + if (table->flags & (1UL << 6)) + dev->pprog_cmd = 0x12; + + /* erase instructions */ + if ((erase_type == 1) && (table->flags & (1UL << 9))) + dev->erase_cmd = (table->erase_t1234 >> 0) & 0xFF; + else if ((erase_type == 2) && (table->flags & (1UL << 10))) + dev->erase_cmd = (table->erase_t1234 >> 8) & 0xFF; + else if ((erase_type == 3) && (table->flags & (1UL << 11))) + dev->erase_cmd = (table->erase_t1234 >> 16) & 0xFF; + else if ((erase_type == 4) && (table->flags & (1UL << 12))) + dev->erase_cmd = (table->erase_t1234 >> 24) & 0xFF; + } else + LOG_ERROR("parameter table id=0x%04" PRIx16 " invalid length %d", id, words); + } else + LOG_DEBUG("unimplemented parameter table id=0x%04" PRIx16, id); + + free(ptable); + ptable = NULL; + } + + if (erase_type != 0) { + LOG_INFO("valid SFDP detected"); + retval = ERROR_OK; + } else { + LOG_ERROR("incomplete/invalid SFDP"); + retval = ERROR_FLASH_BANK_NOT_PROBED; + } + +err: + free(pheaders); + free(ptable); + + return retval; +} diff --git a/src/flash/nor/sfdp.h b/src/flash/nor/sfdp.h new file mode 100644 index 0000000..f924a4e --- /dev/null +++ b/src/flash/nor/sfdp.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch <andreas.bolsch@mni.thm.de * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_FLASH_NOR_SFDP_H +#define OPENOCD_FLASH_NOR_SFDP_H + +/* per JESD216D 'addr' is *byte* based but must be word aligned, + * 'buffer' is word based, word aligned and always little-endian encoded, + * in the flash, 'addr_len' is 3 or 4, 'dummy' ***usually*** 8 + * + * the actual number of dummy clocks should be worked out by this function + * dynamically, i.e. by scanning the first few bytes for the SFDP signature + * + * buffer contents is supposed to be returned in ***host*** endianness */ +typedef int (*read_sfdp_block_t)(struct flash_bank *bank, uint32_t addr, + uint32_t words, uint32_t *buffer); + +extern int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, + read_sfdp_block_t read_sfdp_block); + +#endif /* OPENOCD_FLASH_NOR_SFDP_H */ diff --git a/src/flash/nor/spi.h b/src/flash/nor/spi.h index 11c381f..f8a0a65 100644 --- a/src/flash/nor/spi.h +++ b/src/flash/nor/spi.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2018 by Andreas Bolsch * + * Copyright (C) 2018-2019 by Andreas Bolsch * * andreas.bolsch@mni.thm.de * * * * Copyright (C) 2012 by George Harris * @@ -29,7 +29,7 @@ /* data structure to maintain flash ids from different vendors */ struct flash_device { - char *name; + const char *name; uint8_t read_cmd; uint8_t qread_cmd; uint8_t pprog_cmd; @@ -87,6 +87,8 @@ extern const struct flash_device flash_devices[]; #define SPIFLASH_PAGE_PROGRAM 0x02 /* Page Program */ #define SPIFLASH_FAST_READ 0x0B /* Fast Read */ #define SPIFLASH_READ 0x03 /* Normal Read */ +#define SPIFLASH_MASS_ERASE 0xC7 /* Mass Erase */ +#define SPIFLASH_READ_SFDP 0x5A /* Read Serial Flash Discoverable Parameters */ #define SPIFLASH_DEF_PAGESIZE 256 /* default for non-page-oriented devices (FRAMs) */ diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c index 78efc8b..9cd282d 100644 --- a/src/flash/nor/stm32f1x.c +++ b/src/flash/nor/stm32f1x.c @@ -215,7 +215,7 @@ static int stm32x_check_operation_supported(struct flash_bank *bank) /* if we have a dual flash bank device then * we need to perform option byte stuff on bank0 only */ if (stm32x_info->register_base != FLASH_REG_BASE_B0) { - LOG_ERROR("Option Byte Operation's must use bank0"); + LOG_ERROR("Option byte operations must use bank 0"); return ERROR_FLASH_OPERATION_FAILED; } diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 379f1b4..55a8d8f 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -81,8 +81,7 @@ * http://www.st.com/resource/en/reference_manual/dm00530369.pdf */ -/* - * STM32G0xxx series for reference. +/* STM32G0xxx series for reference. * * RM0444 (STM32G0x1) * http://www.st.com/resource/en/reference_manual/dm00371828.pdf @@ -91,10 +90,9 @@ * http://www.st.com/resource/en/reference_manual/dm00463896.pdf */ -/* - * STM32G4xxx series for reference. +/* STM32G4xxx series for reference. * - * RM0440 (STM32G43x/44x/47x/48x) + * RM0440 (STM32G43x/44x/47x/48x/49x/4Ax) * http://www.st.com/resource/en/reference_manual/dm00355726.pdf * * Cat. 2 devices have single bank only, page size is 2kByte. @@ -104,12 +102,60 @@ * * Bank mode is controlled by bit 22 (DBANK) in option bytes register. * Both banks are treated as a single OpenOCD bank. + * + * Cat. 4 devices have single bank only, page size is 2kByte. + */ + +/* STM32L5xxx series for reference. + * + * RM0428 (STM32L552xx/STM32L562xx) + * http://www.st.com/resource/en/reference_manual/dm00346336.pdf */ /* Erase time can be as high as 25ms, 10x this and assume it's toast... */ #define FLASH_ERASE_TIMEOUT 250 +enum stm32l4_flash_reg_index { + STM32_FLASH_ACR_INDEX, + STM32_FLASH_KEYR_INDEX, + STM32_FLASH_OPTKEYR_INDEX, + STM32_FLASH_SR_INDEX, + STM32_FLASH_CR_INDEX, + STM32_FLASH_OPTR_INDEX, + STM32_FLASH_WRP1AR_INDEX, + STM32_FLASH_WRP1BR_INDEX, + STM32_FLASH_WRP2AR_INDEX, + STM32_FLASH_WRP2BR_INDEX, + STM32_FLASH_REG_INDEX_NUM, +}; + +static const uint32_t stm32l4_flash_regs[STM32_FLASH_REG_INDEX_NUM] = { + [STM32_FLASH_ACR_INDEX] = 0x000, + [STM32_FLASH_KEYR_INDEX] = 0x008, + [STM32_FLASH_OPTKEYR_INDEX] = 0x00C, + [STM32_FLASH_SR_INDEX] = 0x010, + [STM32_FLASH_CR_INDEX] = 0x014, + [STM32_FLASH_OPTR_INDEX] = 0x020, + [STM32_FLASH_WRP1AR_INDEX] = 0x02C, + [STM32_FLASH_WRP1BR_INDEX] = 0x030, + [STM32_FLASH_WRP2AR_INDEX] = 0x04C, + [STM32_FLASH_WRP2BR_INDEX] = 0x050, +}; + +static const uint32_t stm32l5_ns_flash_regs[STM32_FLASH_REG_INDEX_NUM] = { + [STM32_FLASH_ACR_INDEX] = 0x000, + [STM32_FLASH_KEYR_INDEX] = 0x008, + [STM32_FLASH_OPTKEYR_INDEX] = 0x010, + [STM32_FLASH_SR_INDEX] = 0x020, + [STM32_FLASH_CR_INDEX] = 0x028, + [STM32_FLASH_OPTR_INDEX] = 0x040, + [STM32_FLASH_WRP1AR_INDEX] = 0x058, + [STM32_FLASH_WRP1BR_INDEX] = 0x05C, + [STM32_FLASH_WRP2AR_INDEX] = 0x068, + [STM32_FLASH_WRP2BR_INDEX] = 0x06C, +}; + struct stm32l4_rev { const uint16_t rev; const char *str; @@ -123,6 +169,7 @@ struct stm32l4_part_info { const uint16_t max_flash_size_kb; const bool has_dual_bank; const uint32_t flash_regs_base; + const uint32_t *default_flash_regs; const uint32_t fsize_addr; }; @@ -135,10 +182,11 @@ struct stm32l4_flash_bank { uint32_t user_bank_size; uint32_t wrpxxr_mask; const struct stm32l4_part_info *part_info; + const uint32_t *flash_regs; }; -/* human readable list of families this drivers supports */ -static const char *device_families = "STM32L4/L4+/WB/WL/G4/G0"; +/* human readable list of families this drivers supports (sorted alphabetically) */ +static const char *device_families = "STM32G0/G4/L4/L4+/L5/WB/WL"; static const struct stm32l4_rev stm32_415_revs[] = { { 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" } @@ -184,6 +232,14 @@ static const struct stm32l4_rev stm32_471_revs[] = { { 0x1001, "Z" }, }; +static const struct stm32l4_rev stm32_472_revs[] = { + { 0x1000, "A" }, { 0x2000, "B" }, +}; + +static const struct stm32l4_rev stm32_479_revs[] = { + { 0x1000, "A" }, +}; + static const struct stm32l4_rev stm32_495_revs[] = { { 0x2001, "2.1" }, }; @@ -205,6 +261,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 1024, .has_dual_bank = true, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -215,6 +272,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 256, .has_dual_bank = false, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -225,6 +283,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 128, .has_dual_bank = false, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -235,6 +294,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 1024, .has_dual_bank = true, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -245,6 +305,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 512, .has_dual_bank = false, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -255,6 +316,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 128, .has_dual_bank = false, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -265,6 +327,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 64, .has_dual_bank = false, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -275,6 +338,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 128, .has_dual_bank = false, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -285,6 +349,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 512, .has_dual_bank = true, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -295,6 +360,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 2048, .has_dual_bank = true, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -305,6 +371,29 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 1024, .has_dual_bank = true, .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, + .fsize_addr = 0x1FFF75E0, + }, + { + .id = 0x472, + .revs = stm32_472_revs, + .num_revs = ARRAY_SIZE(stm32_472_revs), + .device_str = "STM32L55/L56xx", + .max_flash_size_kb = 512, + .has_dual_bank = true, + .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l5_ns_flash_regs, + .fsize_addr = 0x0BFA05E0, + }, + { + .id = 0x479, + .revs = stm32_479_revs, + .num_revs = ARRAY_SIZE(stm32_479_revs), + .device_str = "STM32G49/G4Axx", + .max_flash_size_kb = 512, + .has_dual_bank = false, + .flash_regs_base = 0x40022000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -315,6 +404,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 1024, .has_dual_bank = false, .flash_regs_base = 0x58004000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -325,6 +415,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 512, .has_dual_bank = false, .flash_regs_base = 0x58004000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, { @@ -335,6 +426,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .max_flash_size_kb = 256, .has_dual_bank = false, .flash_regs_base = 0x58004000, + .default_flash_regs = stm32l4_flash_regs, .fsize_addr = 0x1FFF75E0, }, }; @@ -368,16 +460,37 @@ static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t r return stm32l4_info->part_info->flash_regs_base + reg_offset; } +static inline uint32_t stm32l4_get_flash_reg_by_index(struct flash_bank *bank, + enum stm32l4_flash_reg_index reg_index) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + return stm32l4_get_flash_reg(bank, stm32l4_info->flash_regs[reg_index]); +} + static inline int stm32l4_read_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t *value) { return target_read_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value); } +static inline int stm32l4_read_flash_reg_by_index(struct flash_bank *bank, + enum stm32l4_flash_reg_index reg_index, uint32_t *value) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + return stm32l4_read_flash_reg(bank, stm32l4_info->flash_regs[reg_index], value); +} + static inline int stm32l4_write_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t value) { return target_write_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value); } +static inline int stm32l4_write_flash_reg_by_index(struct flash_bank *bank, + enum stm32l4_flash_reg_index reg_index, uint32_t value) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + return stm32l4_write_flash_reg(bank, stm32l4_info->flash_regs[reg_index], value); +} + static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout) { uint32_t status; @@ -385,7 +498,7 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout) /* wait for busy to clear */ for (;;) { - retval = stm32l4_read_flash_reg(bank, STM32_FLASH_SR, &status); + retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, &status); if (retval != ERROR_OK) return retval; LOG_DEBUG("status: 0x%" PRIx32 "", status); @@ -398,7 +511,6 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout) alive_sleep(1); } - if (status & FLASH_WRPERR) { LOG_ERROR("stm32x device protected"); retval = ERROR_FAIL; @@ -411,7 +523,7 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout) /* If this operation fails, we ignore it and report the original * retval */ - stm32l4_write_flash_reg(bank, STM32_FLASH_SR, status & FLASH_ERROR); + stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, status & FLASH_ERROR); } return retval; @@ -424,7 +536,7 @@ static int stm32l4_unlock_reg(struct flash_bank *bank) /* first check if not already unlocked * otherwise writing on STM32_FLASH_KEYR will fail */ - int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl); + int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl); if (retval != ERROR_OK) return retval; @@ -432,15 +544,15 @@ static int stm32l4_unlock_reg(struct flash_bank *bank) return ERROR_OK; /* unlock flash registers */ - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY1); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_KEYR_INDEX, KEY1); if (retval != ERROR_OK) return retval; - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY2); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_KEYR_INDEX, KEY2); if (retval != ERROR_OK) return retval; - retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl); + retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl); if (retval != ERROR_OK) return retval; @@ -456,7 +568,7 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank) { uint32_t ctrl; - int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl); + int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl); if (retval != ERROR_OK) return retval; @@ -464,15 +576,15 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank) return ERROR_OK; /* unlock option registers */ - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY1); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_OPTKEYR_INDEX, OPTKEY1); if (retval != ERROR_OK) return retval; - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY2); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_OPTKEYR_INDEX, OPTKEY2); if (retval != ERROR_OK) return retval; - retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl); + retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl); if (retval != ERROR_OK) return retval; @@ -508,14 +620,14 @@ static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, if (retval != ERROR_OK) goto err_lock; - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OPTSTRT); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OPTSTRT); if (retval != ERROR_OK) goto err_lock; retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); err_lock: - retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK | FLASH_OPTLOCK); + retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK | FLASH_OPTLOCK); if (retval != ERROR_OK) return retval; @@ -528,11 +640,11 @@ 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_flash_reg(bank, STM32_FLASH_WRP1AR, &wrp1ar); - stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1BR, &wrp1br); + stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP1AR_INDEX, &wrp1ar); + stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP1BR_INDEX, &wrp1br); if (stm32l4_info->part_info->has_dual_bank) { - stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2AR, &wrp2ar); - stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2BR, &wrp2br); + stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP2AR_INDEX, &wrp2ar); + stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_WRP2BR_INDEX, &wrp2br); } else { /* prevent uninitialized errors */ wrp2ar = 0; @@ -611,7 +723,7 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first, erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER; } else erase_flags |= i << FLASH_PAGE_SHIFT; - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, erase_flags); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, erase_flags); if (retval != ERROR_OK) break; @@ -623,7 +735,7 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first, } err_lock: - retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK); + retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK); if (retval != ERROR_OK) return retval; @@ -651,7 +763,7 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, reg_value = ((last & 0xFF) << 16) | begin; } - ret = stm32l4_write_option(bank, STM32_FLASH_WRP2AR, reg_value, 0xffffffff); + ret = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_WRP2AR_INDEX], reg_value, 0xffffffff); } /* Bank 1 */ reg_value = 0xFF; /* Default to bank un-protected */ @@ -661,7 +773,7 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, reg_value = (end << 16) | (first & 0xFF); } - ret = stm32l4_write_option(bank, STM32_FLASH_WRP1AR, reg_value, 0xffffffff); + ret = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_WRP1AR_INDEX], reg_value, 0xffffffff); } return ret; @@ -727,8 +839,8 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, 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, count); - buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg(bank, STM32_FLASH_SR)); - buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg(bank, STM32_FLASH_CR)); + buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX)); + buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX)); retval = target_run_flash_async_algorithm(target, buffer, count, 8, 0, NULL, @@ -748,7 +860,7 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, if (error != 0) { LOG_ERROR("flash write failed = %08" PRIx32, error); /* Clear but report errors */ - stm32l4_write_flash_reg(bank, STM32_FLASH_SR, error); + stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, error); retval = ERROR_FAIL; } } @@ -825,7 +937,7 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, retval = stm32l4_write_block(bank, buffer, offset, count / 8); err_lock: - retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK); + retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK); if (retval != ERROR_OK) { LOG_ERROR("block write failed"); @@ -838,17 +950,17 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) { int retval; - /* try stm32l4/l4+/wb/g4 id register first, then stm32g0 id register */ - retval = target_read_u32(bank->target, DBGMCU_IDCODE_L4_G4, id); - if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) { - retval = target_read_u32(bank->target, DBGMCU_IDCODE_G0, id); - if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) { - LOG_ERROR("can't get device id"); - return (retval == ERROR_OK) ? ERROR_FAIL : retval; - } + /* try reading possible IDCODE registers, in the following order */ + uint32_t DBGMCU_IDCODE[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5}; + + for (unsigned int i = 0; i < ARRAY_SIZE(DBGMCU_IDCODE); i++) { + retval = target_read_u32(bank->target, DBGMCU_IDCODE[i], id); + if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff)) + return ERROR_OK; } - return retval; + LOG_ERROR("can't get the device id"); + return (retval == ERROR_OK) ? ERROR_FAIL : retval; } static int stm32l4_probe(struct flash_bank *bank) @@ -880,6 +992,7 @@ static int stm32l4_probe(struct flash_bank *bank) } part_info = stm32l4_info->part_info; + stm32l4_info->flash_regs = stm32l4_info->part_info->default_flash_regs; char device_info[1024]; retval = bank->driver->info(bank, device_info, sizeof(device_info)); @@ -913,7 +1026,7 @@ static int stm32l4_probe(struct flash_bank *bank) assert((flash_size_kb != 0xffff) && flash_size_kb); /* read flash option register */ - retval = stm32l4_read_flash_reg(bank, STM32_FLASH_OPTR, &options); + retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &options); if (retval != ERROR_OK) return retval; @@ -924,6 +1037,7 @@ static int stm32l4_probe(struct flash_bank *bank) int page_size_kb = 0; stm32l4_info->dual_bank_mode = false; + bool use_dbank_bit = false; switch (device_id) { case 0x415: /* STM32L47/L48xx */ @@ -952,6 +1066,7 @@ static int stm32l4_probe(struct flash_bank *bank) case 0x464: /* STM32L41/L42xx */ case 0x466: /* STM32G03/G04xx */ case 0x468: /* STM32G43/G44xx */ + case 0x479: /* STM32G49/G4Axx */ case 0x497: /* STM32WLEx */ /* single bank flash */ page_size_kb = 2; @@ -989,7 +1104,7 @@ static int stm32l4_probe(struct flash_bank *bank) page_size_kb = 8; num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; - const bool use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb; + use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb; if ((use_dbank_bit && (options & BIT(22))) || (!use_dbank_bit && (options & BIT(21)))) { stm32l4_info->dual_bank_mode = true; @@ -998,6 +1113,23 @@ static int stm32l4_probe(struct flash_bank *bank) stm32l4_info->bank1_sectors = num_pages / 2; } break; + case 0x472: /* STM32L55/L56xx */ + /* STM32L55/L56xx can be single/dual bank: + * if size = 512K check DBANK bit(22) + * if size = 256K check DB256K bit(21) + */ + page_size_kb = 4; + num_pages = flash_size_kb / page_size_kb; + stm32l4_info->bank1_sectors = num_pages; + use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb; + if ((use_dbank_bit && (options & BIT(22))) || + (!use_dbank_bit && (options & BIT(21)))) { + stm32l4_info->dual_bank_mode = true; + page_size_kb = 2; + num_pages = flash_size_kb / page_size_kb; + stm32l4_info->bank1_sectors = num_pages / 2; + } + break; case 0x495: /* STM32WB5x */ case 0x496: /* STM32WB3x */ /* single bank flash */ @@ -1086,19 +1218,17 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size) for (unsigned int i = 0; i < part_info->num_revs; i++) { if (rev_id == part_info->revs[i].rev) { rev_str = part_info->revs[i].str; - - if (rev_str != NULL) { - snprintf(buf, buf_size, "%s - Rev: %s%s", - part_info->device_str, rev_str, stm32l4_info->probed ? - (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : ""); - return ERROR_OK; - } + break; } } - snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)%s", - part_info->device_str, rev_id, stm32l4_info->probed ? - (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : ""); + int buf_len = snprintf(buf, buf_size, "%s - Rev %s : 0x%04x", + part_info->device_str, rev_str ? rev_str : "'unknown'", rev_id); + + if (stm32l4_info->probed) + snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank", + stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single"); + return ERROR_OK; } else { snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families); @@ -1133,18 +1263,18 @@ static int stm32l4_mass_erase(struct flash_bank *bank) if (retval != ERROR_OK) goto err_lock; - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, action); if (retval != ERROR_OK) goto err_lock; - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action | FLASH_STRT); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, action | FLASH_STRT); if (retval != ERROR_OK) goto err_lock; retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); err_lock: - retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK); + retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK); if (retval != ERROR_OK) return retval; @@ -1257,7 +1387,7 @@ COMMAND_HANDLER(stm32l4_handle_option_load_command) * "Note: If the read protection is set while the debugger is still * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset." */ - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBL_LAUNCH); + retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH); command_print(CMD, "stm32l4x option load completed. Power-on reset might be required"); @@ -1288,7 +1418,8 @@ COMMAND_HANDLER(stm32l4_handle_lock_command) } /* set readout protection level 1 by erasing the RDP option byte */ - if (stm32l4_write_option(bank, STM32_FLASH_OPTR, 0, 0x000000FF) != ERROR_OK) { + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], 0, 0x000000FF) != ERROR_OK) { command_print(CMD, "%s failed to lock device", bank->driver->name); return ERROR_OK; } @@ -1315,7 +1446,9 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command) return ERROR_TARGET_NOT_HALTED; } - if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != ERROR_OK) { + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], + RDP_LEVEL_0, 0x000000FF) != ERROR_OK) { command_print(CMD, "%s failed to unlock device", bank->driver->name); return ERROR_OK; } diff --git a/src/flash/nor/stm32l4x.h b/src/flash/nor/stm32l4x.h index abd8010..3e810a0 100644 --- a/src/flash/nor/stm32l4x.h +++ b/src/flash/nor/stm32l4x.h @@ -19,18 +19,6 @@ #ifndef OPENOCD_FLASH_NOR_STM32L4X #define OPENOCD_FLASH_NOR_STM32L4X -/* Flash registers offsets */ -#define STM32_FLASH_ACR 0x00 -#define STM32_FLASH_KEYR 0x08 -#define STM32_FLASH_OPTKEYR 0x0c -#define STM32_FLASH_SR 0x10 -#define STM32_FLASH_CR 0x14 -#define STM32_FLASH_OPTR 0x20 -#define STM32_FLASH_WRP1AR 0x2c -#define STM32_FLASH_WRP1BR 0x30 -#define STM32_FLASH_WRP2AR 0x4c -#define STM32_FLASH_WRP2BR 0x50 - /* FLASH_CR register bits */ #define FLASH_PG (1 << 0) #define FLASH_PER (1 << 1) diff --git a/src/flash/nor/stmqspi.c b/src/flash/nor/stmqspi.c new file mode 100644 index 0000000..f54e497 --- /dev/null +++ b/src/flash/nor/stmqspi.c @@ -0,0 +1,2449 @@ +/*************************************************************************** + * Copyright (C) 2016 - 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * Copyright (C) 2010 by Antonio Borneo * + * borneo.antonio@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +/* STM QuadSPI (QSPI) and OctoSPI (OCTOSPI) controller are SPI bus controllers + * specifically designed for SPI memories. + * Two working modes are available: + * - indirect mode: the SPI is controlled by SW. Any custom commands can be sent + * on the bus. + * - memory mapped mode: the SPI is under QSPI/OCTOSPI control. Memory content + * is directly accessible in CPU memory space. CPU can read and execute from + * memory (but not write to) */ + +/* ATTENTION: + * To have flash mapped in CPU memory space, the QSPI/OCTOSPI controller + * has to be in "memory mapped mode". This requires following constraints: + * 1) The command "reset init" has to initialize QSPI/OCTOSPI controller and put + * it in memory mapped mode; + * 2) every command in this file has to return to prompt in memory mapped mode. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/bits.h> +#include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/armv7m.h> +#include <target/image.h> +#include "stmqspi.h" +#include "sfdp.h" + +/* deprecated */ +#undef SPIFLASH_READ +#undef SPIFLASH_PAGE_PROGRAM + +#define READ_REG(a) \ +({ \ + uint32_t _result; \ + \ + retval = target_read_u32(target, io_base + (a), &_result); \ + (retval == ERROR_OK) ? _result : 0x0; \ +}) + +/* saved mode settings */ +#define QSPI_MODE (stmqspi_info->saved_ccr & \ + (0xF0000000U | QSPI_DCYC_MASK | QSPI_4LINE_MODE | QSPI_ALTB_MODE | QSPI_ADDR4)) + +/* saved read mode settings but indirect read instead of memory mapped + * in particular, use the dummy cycle setting from this saved setting */ +#define QSPI_CCR_READ (QSPI_READ_MODE | (stmqspi_info->saved_ccr & \ + (0xF0000000U | QSPI_DCYC_MASK | QSPI_4LINE_MODE | QSPI_ALTB_MODE | QSPI_ADDR4 | 0xFF))) + +/* QSPI_CCR for various other commands, these never use dummy cycles nor alternate bytes */ +#define QSPI_CCR_READ_STATUS \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB) | \ + (QSPI_READ_MODE | SPIFLASH_READ_STATUS)) + +#define QSPI_CCR_READ_ID \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB) | \ + (QSPI_READ_MODE | SPIFLASH_READ_ID)) + +#define QSPI_CCR_READ_MID \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB) | \ + (QSPI_READ_MODE | SPIFLASH_READ_MID)) + +/* always use 3-byte addresses for read SFDP */ +#define QSPI_CCR_READ_SFDP \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & ~QSPI_ADDR4 & QSPI_NO_ALTB) | \ + (QSPI_READ_MODE | QSPI_ADDR3 | SPIFLASH_READ_SFDP)) + +#define QSPI_CCR_WRITE_ENABLE \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB & QSPI_NO_DATA) | \ + (QSPI_WRITE_MODE | SPIFLASH_WRITE_ENABLE)) + +#define QSPI_CCR_SECTOR_ERASE \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & QSPI_NO_DATA) | \ + (QSPI_WRITE_MODE | stmqspi_info->dev.erase_cmd)) + +#define QSPI_CCR_MASS_ERASE \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB & QSPI_NO_DATA) | \ + (QSPI_WRITE_MODE | stmqspi_info->dev.chip_erase_cmd)) + +#define QSPI_CCR_PAGE_PROG \ + ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB) | \ + (QSPI_WRITE_MODE | stmqspi_info->dev.pprog_cmd)) + +/* saved mode settings */ +#define OCTOSPI_MODE (stmqspi_info->saved_cr & 0xCFFFFFFF) + +#define OPI_MODE ((stmqspi_info->saved_ccr & OCTOSPI_ISIZE_MASK) != 0) + +#define OCTOSPI_MODE_CCR (stmqspi_info->saved_ccr & \ + (0xF0000000U | OCTOSPI_8LINE_MODE | OCTOSPI_ALTB_MODE | OCTOSPI_ADDR4)) + +/* use saved ccr for read */ +#define OCTOSPI_CCR_READ OCTOSPI_MODE_CCR + +/* OCTOSPI_CCR for various other commands, these never use alternate bytes * + * for READ_STATUS and READ_ID, 4-byte address 0 * + * 4 dummy cycles must sent in OPI mode when DQS is disabled. However, when * + * DQS is enabled, some STM32 devices need at least 6 dummy cycles for * + * proper operation, but otherwise the actual number has no effect! * + * E.g. RM0432 Rev. 7 is incorrect regarding this: L4R9 works well with 4 * + * dummy clocks whereas L4P5 not at all. * + */ +#define OPI_DUMMY \ + ((stmqspi_info->saved_ccr & OCTOSPI_DQSEN) ? 6U : 4U) + +#define OCTOSPI_CCR_READ_STATUS \ + ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & \ + (OPI_MODE ? ~0U : OCTOSPI_NO_ADDR) & OCTOSPI_NO_ALTB)) + +#define OCTOSPI_CCR_READ_ID \ + ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & \ + (OPI_MODE ? ~0U : OCTOSPI_NO_ADDR) & OCTOSPI_NO_ALTB)) + +#define OCTOSPI_CCR_READ_MID OCTOSPI_CCR_READ_ID + +/* 4-byte address in octo mode, else 3-byte address for read SFDP */ +#define OCTOSPI_CCR_READ_SFDP(len) \ + ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & ~OCTOSPI_ADDR4 & OCTOSPI_NO_ALTB) | \ + (((len) < 4) ? OCTOSPI_ADDR3 : OCTOSPI_ADDR4)) + +#define OCTOSPI_CCR_WRITE_ENABLE \ + ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ADDR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA)) + +#define OCTOSPI_CCR_SECTOR_ERASE \ + ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA)) + +#define OCTOSPI_CCR_MASS_ERASE \ + ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ADDR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA)) + +#define OCTOSPI_CCR_PAGE_PROG \ + ((OCTOSPI_MODE_CCR & QSPI_NO_ALTB)) + +#define SPI_ADSIZE (((stmqspi_info->saved_ccr >> SPI_ADSIZE_POS) & 0x3) + 1) + +#define OPI_CMD(cmd) ((OPI_MODE ? ((((uint16_t)(cmd)) << 8) | (~(cmd) & 0xFFU)) : (cmd))) + +#define OCTOSPI_CMD(mode, ccr, ir) \ +({ \ + retval = target_write_u32(target, io_base + OCTOSPI_CR, \ + OCTOSPI_MODE | (mode)); \ + if (retval == ERROR_OK) \ + retval = target_write_u32(target, io_base + OCTOSPI_TCR, \ + (stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK) | \ + ((OPI_MODE && ((mode) == OCTOSPI_READ_MODE)) ? \ + (OPI_DUMMY << OCTOSPI_DCYC_POS) : 0)); \ + if (retval == ERROR_OK) \ + retval = target_write_u32(target, io_base + OCTOSPI_CCR, ccr); \ + if (retval == ERROR_OK) \ + retval = target_write_u32(target, io_base + OCTOSPI_IR, \ + OPI_CMD(ir)); \ + retval; \ +}) + +/* convert uint32_t into 4 uint8_t in little endian byte order */ +static inline uint32_t h_to_le_32(uint32_t val) +{ + uint32_t result; + + h_u32_to_le((uint8_t *)&result, val); + return result; +} + +/* Timeout in ms */ +#define SPI_CMD_TIMEOUT (100) +#define SPI_PROBE_TIMEOUT (100) +#define SPI_MAX_TIMEOUT (2000) +#define SPI_MASS_ERASE_TIMEOUT (400000) + +struct sector_info { + uint32_t offset; + uint32_t size; + uint32_t result; +}; + +struct stmqspi_flash_bank { + bool probed; + char devname[32]; + bool octo; + struct flash_device dev; + uint32_t io_base; + uint32_t saved_cr; /* in particalar FSEL, DFM bit mask in QUADSPI_CR *AND* OCTOSPI_CR */ + uint32_t saved_ccr; /* different meaning for QUADSPI and OCTOSPI */ + uint32_t saved_tcr; /* only for OCTOSPI */ + uint32_t saved_ir; /* only for OCTOSPI */ + unsigned int sfdp_dummy1; /* number of dummy bytes for SFDP read for flash1 and octo */ + unsigned int sfdp_dummy2; /* number of dummy bytes for SFDP read for flash2 */ +}; + +FLASH_BANK_COMMAND_HANDLER(stmqspi_flash_bank_command) +{ + struct stmqspi_flash_bank *stmqspi_info; + uint32_t io_base; + + LOG_DEBUG("%s", __func__); + + if (CMD_ARGC < 7) + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], io_base); + + stmqspi_info = malloc(sizeof(struct stmqspi_flash_bank)); + if (stmqspi_info == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = stmqspi_info; + stmqspi_info->sfdp_dummy1 = 0; + stmqspi_info->sfdp_dummy2 = 0; + stmqspi_info->probed = false; + stmqspi_info->io_base = io_base; + + return ERROR_OK; +} + +/* Poll busy flag */ +/* timeout in ms */ +static int poll_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + uint32_t spi_sr; + int retval; + long long endtime; + + endtime = timeval_ms() + timeout; + do { + spi_sr = READ_REG(SPI_SR); + if ((spi_sr & BIT(SPI_BUSY)) == 0) { + if (retval == ERROR_OK) { + /* Clear transmit finished flag */ + retval = target_write_u32(target, io_base + SPI_FCR, BIT(SPI_TCF)); + } + return retval; + } else + LOG_DEBUG("busy: 0x%08X", spi_sr); + alive_sleep(1); + } while (timeval_ms() < endtime); + + LOG_ERROR("Timeout while polling BUSY"); + return ERROR_FLASH_OPERATION_FAILED; +} + +/* Set to memory-mapped mode, e.g. after an error */ +static int set_mm_mode(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + int retval; + + /* Reset Address register bits 0 and 1, see various errata sheets */ + retval = target_write_u32(target, io_base + SPI_AR, 0x0); + if (retval != ERROR_OK) + return retval; + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* Finally switch to memory mapped mode */ + if (IS_OCTOSPI) { + retval = target_write_u32(target, io_base + OCTOSPI_CR, + OCTOSPI_MODE | OCTOSPI_MM_MODE); + if (retval == ERROR_OK) + retval = target_write_u32(target, io_base + OCTOSPI_CCR, + stmqspi_info->saved_ccr); + if (retval == ERROR_OK) + retval = target_write_u32(target, io_base + OCTOSPI_TCR, + stmqspi_info->saved_tcr); + if (retval == ERROR_OK) + retval = target_write_u32(target, io_base + OCTOSPI_IR, + stmqspi_info->saved_ir); + } else { + retval = target_write_u32(target, io_base + QSPI_CR, + stmqspi_info->saved_cr); + if (retval == ERROR_OK) + retval = target_write_u32(target, io_base + QSPI_CCR, + stmqspi_info->saved_ccr); + } + return retval; +} + +/* Read the status register of the external SPI flash chip(s). */ +static int read_status_reg(struct flash_bank *bank, uint16_t *status) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + uint8_t data; + int count, retval; + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Read always two (for DTR mode) bytes per chip */ + count = 2; + retval = target_write_u32(target, io_base + SPI_DLR, + ((stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 2 * count : count) - 1); + if (retval != ERROR_OK) + goto err; + + /* Read status */ + if (IS_OCTOSPI) { + retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_STATUS, SPIFLASH_READ_STATUS); + if (OPI_MODE) { + /* Dummy address 0, only required for 8-line mode */ + retval = target_write_u32(target, io_base + SPI_AR, 0); + if (retval != ERROR_OK) + goto err; + } + } else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_STATUS); + if (retval != ERROR_OK) + goto err; + + *status = 0; + + /* for debugging only */ + (void)READ_REG(SPI_SR); + + for ( ; count > 0; --count) { + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) + != BIT(SPI_FSEL_FLASH)) { + /* get status of flash 1 in dual mode or flash 1 only mode */ + retval = target_read_u8(target, io_base + SPI_DR, &data); + if (retval != ERROR_OK) + goto err; + *status |= data; + } + + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) != 0) { + /* get status of flash 2 in dual mode or flash 2 only mode */ + retval = target_read_u8(target, io_base + SPI_DR, &data); + if (retval != ERROR_OK) + goto err; + *status |= ((uint16_t)data) << 8; + } + } + + LOG_DEBUG("flash status regs: 0x%04" PRIx16, *status); + +err: + return retval; +} + +/* check for WIP (write in progress) bit(s) in status register(s) */ +/* timeout in ms */ +static int wait_till_ready(struct flash_bank *bank, int timeout) +{ + uint16_t status; + int retval; + long long endtime; + + endtime = timeval_ms() + timeout; + do { + /* Read flash status register(s) */ + retval = read_status_reg(bank, &status); + if (retval != ERROR_OK) + return retval; + + if ((status & ((SPIFLASH_BSY_BIT << 8) | SPIFLASH_BSY_BIT)) == 0) + return retval; + alive_sleep(25); + } while (timeval_ms() < endtime); + + LOG_ERROR("timeout"); + return ERROR_FLASH_OPERATION_FAILED; +} + +/* Send "write enable" command to SPI flash chip(s). */ +static int qspi_write_enable(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + uint16_t status; + int retval; + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Send write enable command */ + if (IS_OCTOSPI) { + retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, OCTOSPI_CCR_WRITE_ENABLE, SPIFLASH_WRITE_ENABLE); + if (OPI_MODE) { + /* Dummy address 0, only required for 8-line mode */ + retval = target_write_u32(target, io_base + SPI_AR, 0); + if (retval != ERROR_OK) + goto err; + } + } else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_WRITE_ENABLE); + if (retval != ERROR_OK) + goto err; + + + /* Wait for transmit of command completed */ + poll_busy(bank, SPI_CMD_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Read flash status register */ + retval = read_status_reg(bank, &status); + if (retval != ERROR_OK) + goto err; + + /* Check write enabled for flash 1 */ + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) + != BIT(SPI_FSEL_FLASH)) + if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != SPIFLASH_WE_BIT) { + LOG_ERROR("Cannot write enable flash1. Status=0x%02x", + status & 0xFFU); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Check write enabled for flash 2 */ + status >>= 8; + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) != 0) + if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != SPIFLASH_WE_BIT) { + LOG_ERROR("Cannot write enable flash2. Status=0x%02x", + status & 0xFFU); + return ERROR_FLASH_OPERATION_FAILED; + } + +err: + return retval; +} + +COMMAND_HANDLER(stmqspi_handle_mass_erase_command) +{ + struct target *target = NULL; + struct flash_bank *bank; + struct stmqspi_flash_bank *stmqspi_info; + struct duration bench; + uint32_t io_base; + uint16_t status; + unsigned int sector; + int retval; + + LOG_DEBUG("%s", __func__); + + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stmqspi_info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(stmqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (stmqspi_info->dev.chip_erase_cmd == 0x00) { + LOG_ERROR("Mass erase not available for this device"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + for (sector = 0; sector < bank->num_sectors; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FLASH_PROTECTED; + } + } + + io_base = stmqspi_info->io_base; + duration_start(&bench); + + retval = qspi_write_enable(bank); + if (retval != ERROR_OK) + goto err; + + /* Send Mass Erase command */ + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, OCTOSPI_CCR_MASS_ERASE, + stmqspi_info->dev.chip_erase_cmd); + else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_MASS_ERASE); + if (retval != ERROR_OK) + goto err; + + /* Wait for transmit of command completed */ + poll_busy(bank, SPI_CMD_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Read flash status register(s) */ + retval = read_status_reg(bank, &status); + if (retval != ERROR_OK) + goto err; + + /* Check for command in progress for flash 1 */ + if (((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) + != BIT(SPI_FSEL_FLASH)) && ((status & SPIFLASH_BSY_BIT) == 0) && + ((status & SPIFLASH_WE_BIT) != 0)) { + LOG_ERROR("Mass erase command not accepted by flash1. Status=0x%02x", + status & 0xFFU); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Check for command in progress for flash 2 */ + status >>= 8; + if (((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) != 0) && + ((status & SPIFLASH_BSY_BIT) == 0) && + ((status & SPIFLASH_WE_BIT) != 0)) { + LOG_ERROR("Mass erase command not accepted by flash2. Status=0x%02x", + status & 0xFFU); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Poll WIP for end of self timed Sector Erase cycle */ + retval = wait_till_ready(bank, SPI_MASS_ERASE_TIMEOUT); + + duration_measure(&bench); + if (retval == ERROR_OK) { + /* set all sectors as erased */ + for (sector = 0; sector < bank->num_sectors; sector++) + bank->sectors[sector].is_erased = 1; + + command_print(CMD, "stmqspi mass erase completed in %fs (%0.3f KiB/s)", + duration_elapsed(&bench), + duration_kbps(&bench, bank->size)); + } else { + command_print(CMD, "stmqspi mass erase not completed even after %fs", + duration_elapsed(&bench)); + } + +err: + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +static int log2u(uint32_t word) +{ + int result; + + for (result = 0; (unsigned int) result < sizeof(uint32_t) * CHAR_BIT; result++) + if (word == BIT(result)) + return result; + + return -1; +} + +COMMAND_HANDLER(stmqspi_handle_set) +{ + struct flash_bank *bank = NULL; + struct target *target = NULL; + struct stmqspi_flash_bank *stmqspi_info = NULL; + struct flash_sector *sectors = NULL; + uint32_t io_base; + unsigned int index = 0, dual, fsize; + int retval; + + LOG_DEBUG("%s", __func__); + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + + /* chip_erase_cmd, sectorsize and erase_cmd are optional */ + if ((CMD_ARGC < 7) || (CMD_ARGC > 10)) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, index++, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + stmqspi_info = bank->driver_priv; + + /* invalidate all old info */ + if (stmqspi_info->probed) + free(bank->sectors); + bank->size = 0; + bank->num_sectors = 0; + bank->sectors = NULL; + stmqspi_info->sfdp_dummy1 = 0; + stmqspi_info->sfdp_dummy2 = 0; + stmqspi_info->probed = false; + memset(&stmqspi_info->dev, 0, sizeof(stmqspi_info->dev)); + stmqspi_info->dev.name = "unknown"; + + strncpy(stmqspi_info->devname, CMD_ARGV[index++], sizeof(stmqspi_info->devname) - 1); + stmqspi_info->devname[sizeof(stmqspi_info->devname) - 1] = '\0'; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.size_in_bytes); + if (log2u(stmqspi_info->dev.size_in_bytes) < 8) { + command_print(CMD, "stmqspi: device size must be 2^n with n >= 8"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.pagesize); + if (stmqspi_info->dev.pagesize > stmqspi_info->dev.size_in_bytes || + (log2u(stmqspi_info->dev.pagesize) < 0)) { + command_print(CMD, "stmqspi: page size must be 2^n and <= device size"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.read_cmd); + if ((stmqspi_info->dev.read_cmd != 0x03) && + (stmqspi_info->dev.read_cmd != 0x13)) { + command_print(CMD, "stmqspi: only 0x03/0x13 READ cmd allowed"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.qread_cmd); + if ((stmqspi_info->dev.qread_cmd != 0x00) && + (stmqspi_info->dev.qread_cmd != 0x0B) && + (stmqspi_info->dev.qread_cmd != 0x0C) && + (stmqspi_info->dev.qread_cmd != 0x3B) && + (stmqspi_info->dev.qread_cmd != 0x3C) && + (stmqspi_info->dev.qread_cmd != 0x6B) && + (stmqspi_info->dev.qread_cmd != 0x6C) && + (stmqspi_info->dev.qread_cmd != 0xBB) && + (stmqspi_info->dev.qread_cmd != 0xBC) && + (stmqspi_info->dev.qread_cmd != 0xEB) && + (stmqspi_info->dev.qread_cmd != 0xEC) && + (stmqspi_info->dev.qread_cmd != 0xEE)) { + command_print(CMD, "stmqspi: only 0x0B/0x0C/0x3B/0x3C/" + "0x6B/0x6C/0xBB/0xBC/0xEB/0xEC/0xEE QREAD allowed"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.pprog_cmd); + if ((stmqspi_info->dev.pprog_cmd != 0x02) && + (stmqspi_info->dev.pprog_cmd != 0x12) && + (stmqspi_info->dev.pprog_cmd != 0x32)) { + command_print(CMD, "stmqspi: only 0x02/0x12/0x32 PPRG cmd allowed"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (index < CMD_ARGC) + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.chip_erase_cmd); + else + stmqspi_info->dev.chip_erase_cmd = 0x00; + + if (index < CMD_ARGC) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.sectorsize); + if ((stmqspi_info->dev.sectorsize > stmqspi_info->dev.size_in_bytes) || + (stmqspi_info->dev.sectorsize < stmqspi_info->dev.pagesize) || + (log2u(stmqspi_info->dev.sectorsize) < 0)) { + command_print(CMD, "stmqspi: sector size must be 2^n and <= device size"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (index < CMD_ARGC) + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.erase_cmd); + else + return ERROR_COMMAND_SYNTAX_ERROR; + } else { + /* no sector size / sector erase cmd given, treat whole bank as a single sector */ + stmqspi_info->dev.erase_cmd = 0x00; + stmqspi_info->dev.sectorsize = stmqspi_info->dev.size_in_bytes; + } + + /* set correct size value */ + bank->size = stmqspi_info->dev.size_in_bytes << dual; + + io_base = stmqspi_info->io_base; + fsize = (READ_REG(SPI_DCR) >> SPI_FSIZE_POS) & (BIT(SPI_FSIZE_LEN) - 1); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("FSIZE = 0x%04x", fsize); + if (bank->size == BIT(fsize + 1)) + LOG_DEBUG("FSIZE in DCR(1) matches actual capacity. Beware of silicon bug in H7, L4+, MP1."); + else if (bank->size == BIT(fsize + 0)) + LOG_DEBUG("FSIZE in DCR(1) is off by one regarding actual capacity. Fix for silicon bug?"); + else + LOG_ERROR("FSIZE in DCR(1) doesn't match actual capacity."); + + /* create and fill sectors array */ + bank->num_sectors = + stmqspi_info->dev.size_in_bytes / stmqspi_info->dev.sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (sectors == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * (stmqspi_info->dev.sectorsize << dual); + sectors[sector].size = (stmqspi_info->dev.sectorsize << dual); + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + stmqspi_info->dev.name = stmqspi_info->devname; + if (stmqspi_info->dev.size_in_bytes / 4096) + LOG_INFO("flash \'%s\' id = unknown\nchip size = %" PRIu32 "kbytes," + " bank size = %" PRIu32 "kbytes", stmqspi_info->dev.name, + stmqspi_info->dev.size_in_bytes / 1024, + (stmqspi_info->dev.size_in_bytes / 1024) << dual); + else + LOG_INFO("flash \'%s\' id = unknown\nchip size = %" PRIu32 "bytes," + " bank size = %" PRIu32 "bytes", stmqspi_info->dev.name, + stmqspi_info->dev.size_in_bytes, + stmqspi_info->dev.size_in_bytes << dual); + + stmqspi_info->probed = true; + + return ERROR_OK; +} + +COMMAND_HANDLER(stmqspi_handle_cmd) +{ + struct target *target = NULL; + struct flash_bank *bank; + struct stmqspi_flash_bank *stmqspi_info = NULL; + uint32_t io_base, addr; + uint8_t num_write, num_read, cmd_byte, data; + unsigned int count; + const int max = 21; + char temp[4], output[(2 + max + 256) * 3 + 8]; + int retval; + + LOG_DEBUG("%s", __func__); + + if (CMD_ARGC < 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + num_write = CMD_ARGC - 2; + if (num_write > max) { + LOG_ERROR("at most %d bytes may be sent", max); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + stmqspi_info = bank->driver_priv; + io_base = stmqspi_info->io_base; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[1], num_read); + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[2], cmd_byte); + + if (num_read == 0) { + /* nothing to read, then one command byte and for dual flash + * an *even* number of data bytes to follow */ + if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) { + if ((num_write & 1) == 0) { + LOG_ERROR("number of data bytes to write must be even in dual mode"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + } else { + /* read mode, one command byte and up to four following address bytes */ + if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) { + if ((num_read & 1) != 0) { + LOG_ERROR("number of bytes to read must be even in dual mode"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + if ((num_write < 1) || (num_write > 5)) { + LOG_ERROR("one cmd and up to four addr bytes must be send when reading"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* send command byte */ + snprintf(output, sizeof(output), "spi: %02x ", cmd_byte); + if (num_read == 0) { + /* write, send cmd byte */ + retval = target_write_u32(target, io_base + SPI_DLR, ((uint32_t)num_write) - 2); + if (retval != ERROR_OK) + goto err; + + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, + (OCTOSPI_MODE_CCR & OCTOSPI_NO_ALTB & OCTOSPI_NO_ADDR & + ((num_write == 1) ? OCTOSPI_NO_DATA : ~0U)), cmd_byte); + else + retval = target_write_u32(target, io_base + QSPI_CCR, + (QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & QSPI_NO_ADDR & + ((num_write == 1) ? QSPI_NO_DATA : ~0U)) | + (QSPI_WRITE_MODE | cmd_byte)); + if (retval != ERROR_OK) + goto err; + + /* send additional data bytes */ + for (count = 3; count < CMD_ARGC; count++) { + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[count], data); + snprintf(temp, sizeof(temp), "%02" PRIx8 " ", data); + retval = target_write_u8(target, io_base + SPI_DR, data); + if (retval != ERROR_OK) + goto err; + strncat(output, temp, sizeof(output) - strlen(output) - 1); + } + strncat(output, "-> ", sizeof(output) - strlen(output) - 1); + } else { + /* read, pack additional bytes into address */ + addr = 0; + for (count = 3; count < CMD_ARGC; count++) { + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[count], data); + snprintf(temp, sizeof(temp), "%02" PRIx8 " ", data); + addr = (addr << 8) | data; + strncat(output, temp, sizeof(output) - strlen(output) - 1); + } + strncat(output, "-> ", sizeof(output) - strlen(output) - 1); + + /* send cmd byte, if ADMODE indicates no address, this already triggers command */ + retval = target_write_u32(target, io_base + SPI_DLR, ((uint32_t)num_read) - 1); + if (retval != ERROR_OK) + goto err; + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, + (OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & OCTOSPI_NO_ALTB & ~OCTOSPI_ADDR4 & + ((num_write == 1) ? OCTOSPI_NO_ADDR : ~0U)) | + (((num_write - 2) & 0x3U) << SPI_ADSIZE_POS), cmd_byte); + else + retval = target_write_u32(target, io_base + QSPI_CCR, + (QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & ~QSPI_ADDR4 & + ((num_write == 1) ? QSPI_NO_ADDR : ~0U)) | + ((QSPI_READ_MODE | (((num_write - 2) & 0x3U) << SPI_ADSIZE_POS) | cmd_byte))); + if (retval != ERROR_OK) + goto err; + + if (num_write > 1) { + /* if ADMODE indicates address required, only the write to AR triggers command */ + retval = target_write_u32(target, io_base + SPI_AR, addr); + if (retval != ERROR_OK) + goto err; + } + + /* read response bytes */ + for ( ; num_read > 0; num_read--) { + retval = target_read_u8(target, io_base + SPI_DR, &data); + if (retval != ERROR_OK) + goto err; + snprintf(temp, sizeof(temp), "%02" PRIx8 " ", data); + strncat(output, temp, sizeof(output) - strlen(output) - 1); + } + } + command_print(CMD, "%s", output); + +err: + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +static int qspi_erase_sector(struct flash_bank *bank, unsigned int sector) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + uint16_t status; + int retval; + + retval = qspi_write_enable(bank); + if (retval != ERROR_OK) + goto err; + + /* Send Sector Erase command */ + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, OCTOSPI_CCR_SECTOR_ERASE, + stmqspi_info->dev.erase_cmd); + else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_SECTOR_ERASE); + if (retval != ERROR_OK) + goto err; + + /* Address is sector offset, this write initiates command transmission */ + retval = target_write_u32(target, io_base + SPI_AR, bank->sectors[sector].offset); + if (retval != ERROR_OK) + goto err; + + /* Wait for transmit of command completed */ + poll_busy(bank, SPI_CMD_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Read flash status register(s) */ + retval = read_status_reg(bank, &status); + if (retval != ERROR_OK) + goto err; + + LOG_DEBUG("erase status regs: 0x%04" PRIx16, status); + + /* Check for command in progress for flash 1 */ + /* If BSY and WE are already cleared the erase did probably complete already */ + if (((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) + != BIT(SPI_FSEL_FLASH)) && ((status & SPIFLASH_BSY_BIT) == 0) && + ((status & SPIFLASH_WE_BIT) != 0)) { + LOG_ERROR("Sector erase command not accepted by flash1. Status=0x%02x", + status & 0xFFU); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Check for command in progress for flash 2 */ + /* If BSY and WE are already cleared the erase did probably complete already */ + status >>= 8; + if (((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) != 0) && + ((status & SPIFLASH_BSY_BIT) == 0) && + ((status & SPIFLASH_WE_BIT) != 0)) { + LOG_ERROR("Sector erase command not accepted by flash2. Status=0x%02x", + status & 0xFFU); + retval = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Erase takes a long time, so some sort of progress message is a good idea */ + LOG_DEBUG("erasing sector %4u", sector); + + /* Poll WIP for end of self timed Sector Erase cycle */ + retval = wait_till_ready(bank, SPI_MAX_TIMEOUT); + +err: + return retval; +} + +static int stmqspi_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + unsigned int sector; + int retval = ERROR_OK; + + LOG_DEBUG("%s: from sector %u to sector %u", __func__, first, last); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(stmqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (stmqspi_info->dev.erase_cmd == 0x00) { + LOG_ERROR("Sector erase not available for this device"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + if ((last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + for (sector = first; sector <= last; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FLASH_PROTECTED; + } + } + + for (sector = first; sector <= last; sector++) { + retval = qspi_erase_sector(bank, sector); + if (retval != ERROR_OK) + break; + alive_sleep(10); + keep_alive(); + } + + if (retval != ERROR_OK) + LOG_ERROR("Flash sector_erase failed on sector %u", sector); + + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +static int stmqspi_protect(struct flash_bank *bank, int set, + unsigned int first, unsigned int last) +{ + unsigned int sector; + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + + if (set) + LOG_WARNING("setting soft protection only, not related to flash's hardware write protection"); + + return ERROR_OK; +} + +/* Check whether flash is blank */ +static int stmqspi_blank_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + struct duration bench; + struct reg_param reg_params[2]; + struct armv7m_algorithm armv7m_info; + struct working_area *algorithm; + const uint8_t *code; + struct sector_info erase_check_info; + uint32_t codesize, maxsize, result, exit_point; + unsigned int count, index, num_sectors, sector; + int retval; + const uint32_t erased = 0x00FF; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(stmqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* see contrib/loaders/flash/stmqspi/stmqspi_erase_check.S for src */ + static const uint8_t stmqspi_erase_check_code[] = { + #include "../../../contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc" + }; + + /* see contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S for src */ + static const uint8_t stmoctospi_erase_check_code[] = { + #include "../../../contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc" + }; + + if (IS_OCTOSPI) { + code = stmoctospi_erase_check_code; + codesize = sizeof(stmoctospi_erase_check_code); + } else { + code = stmqspi_erase_check_code; + codesize = sizeof(stmqspi_erase_check_code); + } + + /* This will overlay the last 4 words of stmqspi/stmoctospi_erase_check_code in target */ + /* for read use the saved settings (memory mapped mode) but indirect read mode */ + uint32_t ccr_buffer[][4] = { + /* cr (not used for QSPI) * + * ccr (for both QSPI and OCTOSPI) * + * tcr (not used for QSPI) * + * ir (not used for QSPI) */ + { + h_to_le_32(OCTOSPI_MODE | OCTOSPI_READ_MODE), + h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_READ : QSPI_CCR_READ), + h_to_le_32(stmqspi_info->saved_tcr), + h_to_le_32(stmqspi_info->saved_ir), + }, + }; + + maxsize = target_get_working_area_avail(target); + if (maxsize < codesize + sizeof(erase_check_info)) { + LOG_ERROR("Not enough working area, can't do QSPI blank check"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + num_sectors = (maxsize - codesize) / sizeof(erase_check_info); + num_sectors = (bank->num_sectors < num_sectors) ? bank->num_sectors : num_sectors; + + if (target_alloc_working_area_try(target, + codesize + num_sectors * sizeof(erase_check_info), &algorithm) != ERROR_OK) { + LOG_ERROR("allocating working area failed"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + /* prepare blank check code, excluding ccr_buffer */ + retval = target_write_buffer(target, algorithm->address, + codesize - sizeof(ccr_buffer), code); + if (retval != ERROR_OK) + goto err; + + /* prepare QSPI/OCTOSPI_CCR register values */ + retval = target_write_buffer(target, algorithm->address + + codesize - sizeof(ccr_buffer), + sizeof(ccr_buffer), (uint8_t *)ccr_buffer); + if (retval != ERROR_OK) + goto err; + + duration_start(&bench); + + /* after breakpoint instruction (halfword), one nop (halfword) and + * port_buffer till end of code */ + exit_point = algorithm->address + codesize - sizeof(uint32_t) - sizeof(ccr_buffer); + + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); /* sector count */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* QSPI/OCTOSPI io_base */ + + sector = 0; + while (sector < bank->num_sectors) { + /* at most num_sectors sectors to handle in one run */ + count = bank->num_sectors - sector; + if (count > num_sectors) + count = num_sectors; + + for (index = 0; index < count; index++) { + erase_check_info.offset = h_to_le_32(bank->sectors[sector + index].offset); + erase_check_info.size = h_to_le_32(bank->sectors[sector + index].size); + erase_check_info.result = h_to_le_32(erased); + + retval = target_write_buffer(target, algorithm->address + + codesize + index * sizeof(erase_check_info), + sizeof(erase_check_info), (uint8_t *)&erase_check_info); + if (retval != ERROR_OK) + goto err; + } + + buf_set_u32(reg_params[0].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, stmqspi_info->io_base); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + LOG_DEBUG("checking sectors %u to %u", sector, sector + count - 1); + /* check a block of sectors */ + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm->address, exit_point, + count * ((bank->sectors[sector].size >> 6) + 1) + 1000, + &armv7m_info); + if (retval != ERROR_OK) + break; + + for (index = 0; index < count; index++) { + retval = target_read_buffer(target, algorithm->address + + codesize + index * sizeof(erase_check_info), + sizeof(erase_check_info), (uint8_t *)&erase_check_info); + if (retval != ERROR_OK) + goto err; + + if ((erase_check_info.offset != h_to_le_32(bank->sectors[sector + index].offset)) || + (erase_check_info.size != 0)) { + LOG_ERROR("corrupted blank check info"); + goto err; + } + + /* we need le_32_to_h, but that's the same as h_to_le_32 */ + result = h_to_le_32(erase_check_info.result); + bank->sectors[sector + index].is_erased = ((result & 0xFF) == 0xFF); + LOG_DEBUG("Flash sector %u checked: 0x%04x", sector + index, result & 0xFFFFU); + } + keep_alive(); + sector += count; + } + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + + duration_measure(&bench); + LOG_INFO("stmqspi blank checked in %fs (%0.3f KiB/s)", duration_elapsed(&bench), + duration_kbps(&bench, bank->size)); + +err: + target_free_working_area(target, algorithm); + + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +/* Verify checksum */ +static int qspi_verify(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + struct reg_param reg_params[4]; + struct armv7m_algorithm armv7m_info; + struct working_area *algorithm; + const uint8_t *code; + uint32_t pagesize, codesize, crc32, result, exit_point; + int retval; + + /* see contrib/loaders/flash/stmqspi/stmqspi_crc32.S for src */ + static const uint8_t stmqspi_crc32_code[] = { + #include "../../../contrib/loaders/flash/stmqspi/stmqspi_crc32.inc" + }; + + /* see contrib/loaders/flash/stmqspi/stmoctospi_crc32.S for src */ + static const uint8_t stmoctospi_crc32_code[] = { + #include "../../../contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc" + }; + + if (IS_OCTOSPI) { + code = stmoctospi_crc32_code; + codesize = sizeof(stmoctospi_crc32_code); + } else { + code = stmqspi_crc32_code; + codesize = sizeof(stmqspi_crc32_code); + } + + /* block size doesn't matter that much here */ + pagesize = stmqspi_info->dev.sectorsize; + if (pagesize == 0) + pagesize = stmqspi_info->dev.pagesize; + if (pagesize == 0) + pagesize = SPIFLASH_DEF_PAGESIZE; + + /* adjust size according to dual flash mode */ + pagesize = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? pagesize << 1 : pagesize; + + /* This will overlay the last 4 words of stmqspi/stmoctospi_crc32_code in target */ + /* for read use the saved settings (memory mapped mode) but indirect read mode */ + uint32_t ccr_buffer[][4] = { + /* cr (not used for QSPI) * + * ccr (for both QSPI and OCTOSPI) * + * tcr (not used for QSPI) * + * ir (not used for QSPI) */ + { + h_to_le_32(OCTOSPI_MODE | OCTOSPI_READ_MODE), + h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_READ : QSPI_CCR_READ), + h_to_le_32(stmqspi_info->saved_tcr), + h_to_le_32(stmqspi_info->saved_ir), + }, + }; + + if (target_alloc_working_area_try(target, codesize, &algorithm) != ERROR_OK) { + LOG_ERROR("Not enough working area, can't do QSPI verify"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + /* prepare verify code, excluding ccr_buffer */ + retval = target_write_buffer(target, algorithm->address, + codesize - sizeof(ccr_buffer), code); + if (retval != ERROR_OK) + goto err; + + /* prepare QSPI/OCTOSPI_CCR register values */ + retval = target_write_buffer(target, algorithm->address + + codesize - sizeof(ccr_buffer), + sizeof(ccr_buffer), (uint8_t *)ccr_buffer); + if (retval != ERROR_OK) + goto err; + + /* after breakpoint instruction (halfword), one nop (halfword) and + * port_buffer till end of code */ + exit_point = algorithm->address + codesize - sizeof(uint32_t) - sizeof(ccr_buffer); + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* count (in), crc32 (out) */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* pagesize */ + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* offset into flash address */ + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* QSPI/OCTOSPI io_base */ + + buf_set_u32(reg_params[0].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, pagesize); + buf_set_u32(reg_params[2].value, 0, 32, offset); + buf_set_u32(reg_params[3].value, 0, 32, stmqspi_info->io_base); + + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm->address, exit_point, + (count >> 5) + 1000, + &armv7m_info); + keep_alive(); + + image_calculate_checksum(buffer, count, &crc32); + + if (retval == ERROR_OK) { + result = buf_get_u32(reg_params[0].value, 0, 32); + LOG_DEBUG("addr " TARGET_ADDR_FMT ", len 0x%08" PRIx32 ", crc 0x%08" PRIx32 " 0x%08" PRIx32, + offset + bank->base, count, ~crc32, result); + if (~crc32 != result) + retval = ERROR_FAIL; + } + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + +err: + target_free_working_area(target, algorithm); + + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +static int qspi_read_write_block(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count, bool write) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + struct reg_param reg_params[6]; + struct armv7m_algorithm armv7m_info; + struct working_area *algorithm; + uint32_t pagesize, fifo_start, fifosize, remaining; + uint32_t maxsize, codesize, exit_point; + const uint8_t *code = NULL; + unsigned int dual; + int retval; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32, + __func__, offset, count); + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + + /* see contrib/loaders/flash/stmqspi/stmqspi_read.S for src */ + static const uint8_t stmqspi_read_code[] = { +#include "../../../contrib/loaders/flash/stmqspi/stmqspi_read.inc" + }; + + /* see contrib/loaders/flash/stmqspi/stmoctospi_read.S for src */ + static const uint8_t stmoctospi_read_code[] = { +#include "../../../contrib/loaders/flash/stmqspi/stmoctospi_read.inc" + }; + + /* see contrib/loaders/flash/stmqspi/stmqspi_write.S for src */ + static const uint8_t stmqspi_write_code[] = { +#include "../../../contrib/loaders/flash/stmqspi/stmqspi_write.inc" + }; + + /* see contrib/loaders/flash/stmqspi/stmoctospi_write.S for src */ + static const uint8_t stmoctospi_write_code[] = { +#include "../../../contrib/loaders/flash/stmqspi/stmoctospi_write.inc" + }; + + /* This will overlay the last 12 words of stmqspi/stmoctospi_read/write_code in target */ + /* for read use the saved settings (memory mapped mode) but indirect read mode */ + uint32_t ccr_buffer[][4] = { + /* cr (not used for QSPI) * + * ccr (for both QSPI and OCTOSPI) * + * tcr (not used for QSPI) * + * ir (not used for QSPI) */ + { + h_to_le_32(OCTOSPI_MODE | OCTOSPI_READ_MODE), + h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_READ_STATUS : QSPI_CCR_READ_STATUS), + h_to_le_32((stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK) | + (OPI_MODE ? (OPI_DUMMY << OCTOSPI_DCYC_POS) : 0)), + h_to_le_32(OPI_CMD(SPIFLASH_READ_STATUS)), + }, + { + h_to_le_32(OCTOSPI_MODE | OCTOSPI_WRITE_MODE), + h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_WRITE_ENABLE : QSPI_CCR_WRITE_ENABLE), + h_to_le_32(stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK), + h_to_le_32(OPI_CMD(SPIFLASH_WRITE_ENABLE)), + }, + { + h_to_le_32(OCTOSPI_MODE | (write ? OCTOSPI_WRITE_MODE : OCTOSPI_READ_MODE)), + h_to_le_32(write ? (IS_OCTOSPI ? OCTOSPI_CCR_PAGE_PROG : QSPI_CCR_PAGE_PROG) : + (IS_OCTOSPI ? OCTOSPI_CCR_READ : QSPI_CCR_READ)), + h_to_le_32(write ? (stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK) : + stmqspi_info->saved_tcr), + h_to_le_32(write ? OPI_CMD(stmqspi_info->dev.pprog_cmd) : stmqspi_info->saved_ir), + }, + }; + + /* force reasonable defaults */ + fifosize = stmqspi_info->dev.sectorsize ? + stmqspi_info->dev.sectorsize : stmqspi_info->dev.size_in_bytes; + + if (write) { + if (IS_OCTOSPI) { + code = stmoctospi_write_code; + codesize = sizeof(stmoctospi_write_code); + } else { + code = stmqspi_write_code; + codesize = sizeof(stmqspi_write_code); + } + } else { + if (IS_OCTOSPI) { + code = stmoctospi_read_code; + codesize = sizeof(stmoctospi_read_code); + } else { + code = stmqspi_read_code; + codesize = sizeof(stmqspi_read_code); + } + } + + /* for write, pagesize must be taken into account */ + /* for read, the page size doesn't matter that much */ + pagesize = stmqspi_info->dev.pagesize; + if (pagesize == 0) + pagesize = (fifosize <= SPIFLASH_DEF_PAGESIZE) ? + fifosize : SPIFLASH_DEF_PAGESIZE; + + /* adjust sizes according to dual flash mode */ + pagesize <<= dual; + fifosize <<= dual; + + /* memory buffer, we assume sectorsize to be a power of 2 times pagesize */ + maxsize = target_get_working_area_avail(target); + if (maxsize < codesize + 2 * sizeof(uint32_t) + pagesize) { + LOG_ERROR("not enough working area, can't do QSPI page reads/writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* fifo size at most sector size, and multiple of page size */ + maxsize -= (codesize + 2 * sizeof(uint32_t)); + fifosize = ((maxsize < fifosize) ? maxsize : fifosize) & ~(pagesize - 1); + + if (target_alloc_working_area_try(target, + codesize + 2 * sizeof(uint32_t) + fifosize, &algorithm) != ERROR_OK) { + LOG_ERROR("allocating working area failed"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + /* prepare flash write code, excluding ccr_buffer */ + retval = target_write_buffer(target, algorithm->address, + codesize - sizeof(ccr_buffer), code); + if (retval != ERROR_OK) + goto err; + + /* prepare QSPI/OCTOSPI_CCR register values */ + retval = target_write_buffer(target, algorithm->address + + codesize - sizeof(ccr_buffer), + sizeof(ccr_buffer), (uint8_t *)ccr_buffer); + if (retval != ERROR_OK) + goto err; + + /* target buffer starts right after flash_write_code, i.e. + * wp and rp are implicitly included in buffer!!! */ + fifo_start = algorithm->address + codesize + 2 * sizeof(uint32_t); + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* count (in), status (out) */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* pagesize */ + init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT); /* offset into flash address */ + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* QSPI/OCTOSPI io_base */ + init_reg_param(®_params[4], "r8", 32, PARAM_OUT); /* fifo start */ + init_reg_param(®_params[5], "r9", 32, PARAM_OUT); /* fifo end + 1 */ + + buf_set_u32(reg_params[0].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, pagesize); + buf_set_u32(reg_params[2].value, 0, 32, offset); + buf_set_u32(reg_params[3].value, 0, 32, io_base); + buf_set_u32(reg_params[4].value, 0, 32, fifo_start); + buf_set_u32(reg_params[5].value, 0, 32, fifo_start + fifosize); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + /* after breakpoint instruction (halfword), one nop (halfword) and + * ccr_buffer follow till end of code */ + exit_point = algorithm->address + codesize + - (sizeof(ccr_buffer) + sizeof(uint32_t)); + + if (write) { + retval = target_run_flash_async_algorithm(target, buffer, count, 1, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm->address + codesize, + fifosize + 2 * sizeof(uint32_t), + algorithm->address, exit_point, + &armv7m_info); + } else { + retval = target_run_read_async_algorithm(target, buffer, count, 1, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm->address + codesize, + fifosize + 2 * sizeof(uint32_t), + algorithm->address, exit_point, + &armv7m_info); + } + + remaining = buf_get_u32(reg_params[0].value, 0, 32); + if ((retval == ERROR_OK) && remaining) + retval = ERROR_FLASH_OPERATION_FAILED; + + if (retval != ERROR_OK) { + offset = buf_get_u32(reg_params[2].value, 0, 32); + LOG_ERROR("flash %s failed at address 0x%" PRIx32 ", remaining 0x%" PRIx32, + write ? "write" : "read", offset, remaining); + } + + 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]); + destroy_reg_param(®_params[5]); + +err: + target_free_working_area(target, algorithm); + + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +static int stmqspi_read(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + int retval; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, + __func__, offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(stmqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (offset + count > bank->size) { + LOG_WARNING("Read beyond end of flash. Extra data to be ignored."); + count = bank->size - offset; + } + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + return qspi_read_write_block(bank, buffer, offset, count, false); +} + +static int stmqspi_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + unsigned int dual, sector; + bool octal_dtr; + int retval; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, + __func__, offset, count); + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + octal_dtr = IS_OCTOSPI && (stmqspi_info->saved_ccr & BIT(OCTOSPI_DDTR)); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(stmqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (offset + count > bank->size) { + LOG_WARNING("Write beyond end of flash. Extra data discarded."); + count = bank->size - offset; + } + + /* Check sector protection */ + for (sector = 0; sector < bank->num_sectors; sector++) { + /* Start offset in or before this sector? */ + /* End offset in or behind this sector? */ + if ((offset < (bank->sectors[sector].offset + bank->sectors[sector].size)) && + ((offset + count - 1) >= bank->sectors[sector].offset) && + bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FLASH_PROTECTED; + } + } + + if ((dual || octal_dtr) && ((offset & 1) != 0 || (count & 1) != 0)) { + LOG_ERROR("In dual-QSPI and octal-DTR modes writes must be two byte aligned: " + "%s: address=0x%08" PRIx32 " len=0x%08" PRIx32, __func__, offset, count); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + return qspi_read_write_block(bank, (uint8_t *)buffer, offset, count, true); +} + +static int stmqspi_verify(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + unsigned int dual; + bool octal_dtr; + int retval; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, + __func__, offset, count); + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + octal_dtr = IS_OCTOSPI && (stmqspi_info->saved_ccr & BIT(OCTOSPI_DDTR)); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(stmqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (offset + count > bank->size) { + LOG_WARNING("Verify beyond end of flash. Extra data ignored."); + count = bank->size - offset; + } + + if ((dual || octal_dtr) && ((offset & 1) != 0 || (count & 1) != 0)) { + LOG_ERROR("In dual-QSPI and octal-DTR modes reads must be two byte aligned: " + "%s: address=0x%08" PRIx32 " len=0x%08" PRIx32, __func__, offset, count); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + return qspi_verify(bank, (uint8_t *)buffer, offset, count); +} + +/* Find appropriate dummy setting, in particular octo mode */ +static int find_sfdp_dummy(struct flash_bank *bank, int len) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + uint8_t data; + unsigned int dual, count; + bool flash1 = !(stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)); + int retval; + const unsigned int max_bytes = 64; + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + + LOG_DEBUG("%s: len=%d, dual=%u, flash1=%d", + __func__, len, dual, flash1); + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + stmqspi_info->saved_cr | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + goto err; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Switch to saved_cr (had to be set accordingly before this call) */ + retval = target_write_u32(target, io_base + SPI_CR, stmqspi_info->saved_cr); + if (retval != ERROR_OK) + goto err; + + /* Read at most that many bytes */ + retval = target_write_u32(target, io_base + SPI_DLR, (max_bytes << dual) - 1); + if (retval != ERROR_OK) + return retval; + + /* Read SFDP block */ + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_SFDP(len), + SPIFLASH_READ_SFDP); + else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_SFDP); + if (retval != ERROR_OK) + goto err; + + /* Read from start of sfdp block */ + retval = target_write_u32(target, io_base + SPI_AR, 0); + if (retval != ERROR_OK) + goto err; + + for (count = 0 ; count < max_bytes; count++) { + if ((dual != 0) && !flash1) { + /* discard even byte in dual flash-mode if flash2 */ + retval = target_read_u8(target, io_base + SPI_DR, &data); + if (retval != ERROR_OK) + goto err; + } + + retval = target_read_u8(target, io_base + SPI_DR, &data); + if (retval != ERROR_OK) + goto err; + + if (data == 0x53) { + LOG_DEBUG("start of SFDP header for flash%c after %u dummy bytes", + flash1 ? '1' : '2', count); + if (flash1) + stmqspi_info->sfdp_dummy1 = count; + else + stmqspi_info->sfdp_dummy2 = count; + return ERROR_OK; + } + + if ((dual != 0) && flash1) { + /* discard odd byte in dual flash-mode if flash1 */ + retval = target_read_u8(target, io_base + SPI_DR, &data); + if (retval != ERROR_OK) + goto err; + } + } + + retval = ERROR_FAIL; + LOG_DEBUG("no start of SFDP header even after %u dummy bytes", count); + +err: + /* Abort operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + + return retval; +} + +/* Read SFDP parameter block */ +static int read_sfdp_block(struct flash_bank *bank, uint32_t addr, + uint32_t words, uint32_t *buffer) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + bool flash1 = !(stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)); + unsigned int dual, count, len, *dummy; + int retval; + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + + if (IS_OCTOSPI && (((stmqspi_info->saved_ccr >> SPI_DMODE_POS) & 0x7) > 3)) { + /* in OCTO mode 4-byte address and (yet) unknown number of dummy clocks */ + len = 4; + + /* in octo mode, use sfdp_dummy1 only */ + dummy = &stmqspi_info->sfdp_dummy1; + if (*dummy == 0) { + retval = find_sfdp_dummy(bank, len); + if (retval != ERROR_OK) + return retval; + } + } else { + /* in all other modes 3-byte-address and 8(?) dummy clocks */ + len = 3; + + /* use sfdp_dummy1/2 according to currently selected flash */ + dummy = (stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)) ? + &stmqspi_info->sfdp_dummy2 : &stmqspi_info->sfdp_dummy1; + + /* according to SFDP standard, there should always be 8 dummy *CLOCKS* + * giving 1, 2 or 4 dummy *BYTES*, however, this is apparently not + * always implemented correctly, so determine the number of dummy bytes + * dynamically */ + if (*dummy == 0) { + retval = find_sfdp_dummy(bank, len); + if (retval != ERROR_OK) + return retval; + } + } + + LOG_DEBUG("%s: addr=0x%08" PRIx32 " words=0x%08" PRIx32 " dummy=%u", + __func__, addr, words, *dummy); + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + stmqspi_info->saved_cr | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + goto err; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Switch to one flash only */ + retval = target_write_u32(target, io_base + SPI_CR, stmqspi_info->saved_cr); + if (retval != ERROR_OK) + goto err; + + /* Read that many words plus dummy bytes */ + retval = target_write_u32(target, io_base + SPI_DLR, + ((*dummy + words * sizeof(uint32_t)) << dual) - 1); + if (retval != ERROR_OK) + goto err; + + /* Read SFDP block */ + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_SFDP(len), + SPIFLASH_READ_SFDP); + else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_SFDP); + if (retval != ERROR_OK) + goto err; + + retval = target_write_u32(target, io_base + SPI_AR, addr << dual); + if (retval != ERROR_OK) + goto err; + + /* dummy clocks */ + for (count = *dummy << dual; count > 0; --count) { + retval = target_read_u8(target, io_base + SPI_DR, (uint8_t *)buffer); + if (retval != ERROR_OK) + goto err; + } + + for ( ; words > 0; words--) { + if (dual != 0) { + uint32_t word1, word2; + + retval = target_read_u32(target, io_base + SPI_DR, &word1); + if (retval != ERROR_OK) + goto err; + retval = target_read_u32(target, io_base + SPI_DR, &word2); + if (retval != ERROR_OK) + goto err; + + if (!flash1) { + /* shift odd numbered bytes into even numbered ones */ + word1 >>= 8; + word2 >>= 8; + } + + /* pack even numbered bytes into one word */ + *buffer = (word1 & 0xFFU) | ((word1 & 0xFF0000U) >> 8) | + ((word2 & 0xFFU) << 16) | ((word2 & 0xFF0000U) << 8); + + + } else { + retval = target_read_u32(target, io_base + SPI_DR, buffer); + if (retval != ERROR_OK) + goto err; + } + LOG_DEBUG("raw SFDP data 0x%08" PRIx32, *buffer); + + /* endian correction, sfdp data is always le uint32_t based */ + *buffer = le_to_h_u32((uint8_t *)buffer); + buffer++; + } + +err: + return retval; +} + +/* Return ID of flash device(s) */ +/* On exit, indirect mode is kept */ +static int read_flash_id(struct flash_bank *bank, uint32_t *id1, uint32_t *id2) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + uint32_t io_base = stmqspi_info->io_base; + uint8_t byte; + unsigned int type, count, len1, len2; + int retval; + + /* invalidate both ids */ + *id1 = 0; + *id2 = 0; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* SPIFLASH_READ_MID causes device in octal mode to go berserk, so don't use in this case */ + for (type = (IS_OCTOSPI && OPI_MODE) ? 1 : 0; type < 2 ; type++) { + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + goto err; + + /* Poll WIP */ + retval = wait_till_ready(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + goto err; + + /* Read at most 16 bytes per chip */ + count = 16; + retval = target_write_u32(target, io_base + SPI_DLR, + (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH) ? count * 2 : count) - 1); + if (retval != ERROR_OK) + goto err; + + /* Read id: one particular flash chip (N25Q128) switches back to SPI mode when receiving + * SPI_FLASH_READ_ID in QPI mode, hence try SPIFLASH_READ_MID first */ + switch (type) { + case 0: + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_MID, SPIFLASH_READ_MID); + else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_MID); + break; + + case 1: + if (IS_OCTOSPI) + retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_ID, SPIFLASH_READ_ID); + else + retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_ID); + break; + + default: + return ERROR_FAIL; + } + + if (retval != ERROR_OK) + goto err; + + /* Dummy address 0, only required for 8-line mode */ + if (IS_OCTOSPI && OPI_MODE) { + retval = target_write_u32(target, io_base + SPI_AR, 0); + if (retval != ERROR_OK) + goto err; + } + + /* for debugging only */ + (void)READ_REG(SPI_SR); + + /* Read ID from Data Register */ + for (len1 = 0, len2 = 0; count > 0; --count) { + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | + BIT(SPI_FSEL_FLASH))) != BIT(SPI_FSEL_FLASH)) { + retval = target_read_u8(target, io_base + SPI_DR, &byte); + if (retval != ERROR_OK) + goto err; + /* collect 3 bytes without continuation codes */ + if ((byte != 0x7F) && (len1 < 3)) { + *id1 = (*id1 >> 8) | ((uint32_t)byte) << 16; + len1++; + } + } + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | + BIT(SPI_FSEL_FLASH))) != 0) { + retval = target_read_u8(target, io_base + SPI_DR, &byte); + if (retval != ERROR_OK) + goto err; + /* collect 3 bytes without continuation codes */ + if ((byte != 0x7F) && (len2 < 3)) { + *id2 = (*id2 >> 8) | ((uint32_t)byte) << 16; + len2++; + } + } + } + + if (((*id1 != 0x000000) && (*id1 != 0xFFFFFF)) || + ((*id2 != 0x000000) && (*id2 != 0xFFFFFF))) + break; + } + + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | + BIT(SPI_FSEL_FLASH))) != BIT(SPI_FSEL_FLASH)) { + if ((*id1 == 0x000000) || (*id1 == 0xFFFFFF)) { + /* no id retrieved, so id must be set manually */ + LOG_INFO("No id from flash1"); + retval = ERROR_FLASH_BANK_NOT_PROBED; + } + } + + if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | BIT(SPI_FSEL_FLASH))) != 0) { + if ((*id2 == 0x000000) || (*id2 == 0xFFFFFF)) { + /* no id retrieved, so id must be set manually */ + LOG_INFO("No id from flash2"); + retval = ERROR_FLASH_BANK_NOT_PROBED; + } + } + +err: + return retval; +} + +static int stmqspi_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + struct flash_sector *sectors = NULL; + uint32_t io_base = stmqspi_info->io_base; + uint32_t id1 = 0, id2 = 0, data = 0; + const struct flash_device *p; + const uint32_t magic = 0xAEF1510E; + unsigned int dual, fsize; + bool octal_dtr; + int retval; + + if (stmqspi_info->probed) { + bank->size = 0; + bank->num_sectors = 0; + free(bank->sectors); + bank->sectors = NULL; + memset(&stmqspi_info->dev, 0, sizeof(stmqspi_info->dev)); + stmqspi_info->sfdp_dummy1 = 0; + stmqspi_info->sfdp_dummy2 = 0; + stmqspi_info->probed = false; + } + + /* Abort any previous operation */ + retval = target_write_u32(target, io_base + SPI_CR, + READ_REG(SPI_CR) | BIT(SPI_ABORT)); + if (retval != ERROR_OK) + return retval; + + /* Wait for busy to be cleared */ + retval = poll_busy(bank, SPI_PROBE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* check whether QSPI_ABR is writeable and readback returns the value written */ + retval = target_write_u32(target, io_base + QSPI_ABR, magic); + if (retval == ERROR_OK) { + retval = target_read_u32(target, io_base + QSPI_ABR, &data); + retval = target_write_u32(target, io_base + QSPI_ABR, 0); + } + + if (data == magic) { + LOG_DEBUG("QSPI_ABR register present"); + stmqspi_info->octo = false; + } else if (READ_REG(OCTOSPI_MAGIC) == OCTO_MAGIC_ID) { + LOG_DEBUG("OCTOSPI_MAGIC present"); + stmqspi_info->octo = true; + } else { + LOG_ERROR("No QSPI, no OCTOSPI at 0x%08" PRIx32, io_base); + stmqspi_info->probed = false; + stmqspi_info->dev.name = "none"; + return ERROR_FAIL; + } + + /* save current FSEL and DFM bits in QSPI/OCTOSPI_CR, current QSPI/OCTOSPI_CCR value */ + stmqspi_info->saved_cr = READ_REG(SPI_CR); + if (retval == ERROR_OK) + stmqspi_info->saved_ccr = READ_REG(SPI_CCR); + + if (IS_OCTOSPI) { + uint32_t mtyp; + + mtyp = ((READ_REG(OCTOSPI_DCR1) & OCTOSPI_MTYP_MASK)) >> OCTOSPI_MTYP_POS; + if (retval == ERROR_OK) + stmqspi_info->saved_tcr = READ_REG(OCTOSPI_TCR); + if (retval == ERROR_OK) + stmqspi_info->saved_ir = READ_REG(OCTOSPI_IR); + if ((mtyp != 0x0) && (mtyp != 0x1)) { + retval = ERROR_FAIL; + LOG_ERROR("Only regular SPI protocol supported in OCTOSPI"); + } + if (retval == ERROR_OK) { + LOG_DEBUG("OCTOSPI at 0x%08" PRIx64 ", io_base at 0x%08" PRIx32 ", OCTOSPI_CR 0x%08" + PRIx32 ", OCTOSPI_CCR 0x%08" PRIx32 ", %d-byte addr", bank->base, io_base, + stmqspi_info->saved_cr, stmqspi_info->saved_ccr, SPI_ADSIZE); + } else { + LOG_ERROR("No OCTOSPI at io_base 0x%08" PRIx32, io_base); + stmqspi_info->probed = false; + stmqspi_info->dev.name = "none"; + return ERROR_FAIL; + } + } else { + if (retval == ERROR_OK) { + LOG_DEBUG("QSPI at 0x%08" PRIx64 ", io_base at 0x%08" PRIx32 ", QSPI_CR 0x%08" + PRIx32 ", QSPI_CCR 0x%08" PRIx32 ", %d-byte addr", bank->base, io_base, + stmqspi_info->saved_cr, stmqspi_info->saved_ccr, SPI_ADSIZE); + if (stmqspi_info->saved_ccr & (1U << QSPI_DDRM)) + LOG_WARNING("DDR mode is untested and suffers from some silicon bugs"); + } else { + LOG_ERROR("No QSPI at io_base 0x%08" PRIx32, io_base); + stmqspi_info->probed = false; + stmqspi_info->dev.name = "none"; + return ERROR_FAIL; + } + } + + dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0; + octal_dtr = IS_OCTOSPI && (stmqspi_info->saved_ccr & BIT(OCTOSPI_DDTR)); + if (dual || octal_dtr) + bank->write_start_alignment = bank->write_end_alignment = 2; + else + bank->write_start_alignment = bank->write_end_alignment = 1; + + /* read and decode flash ID; returns in indirect mode */ + retval = read_flash_id(bank, &id1, &id2); + LOG_DEBUG("id1 0x%06" PRIx32 ", id2 0x%06" PRIx32, id1, id2); + if (retval == ERROR_FLASH_BANK_NOT_PROBED) { + /* no id retrieved, so id must be set manually */ + LOG_INFO("No id - set flash parameters manually"); + retval = ERROR_OK; + goto err; + } + + if (retval != ERROR_OK) + goto err; + + /* identify flash1 */ + for (p = flash_devices; id1 && p->name ; p++) { + if (p->device_id == id1) { + memcpy(&stmqspi_info->dev, p, sizeof(stmqspi_info->dev)); + if (p->size_in_bytes / 4096) + LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + "kbytes", p->name, id1, p->size_in_bytes / 1024); + else + LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + "bytes", p->name, id1, p->size_in_bytes); + break; + } + } + + if (id1 && !p->name) { + /* chip not been identified by id, then try SFDP */ + struct flash_device temp; + uint32_t saved_cr = stmqspi_info->saved_cr; + + /* select flash1 */ + stmqspi_info->saved_cr = stmqspi_info->saved_cr & ~BIT(SPI_FSEL_FLASH); + retval = spi_sfdp(bank, &temp, &read_sfdp_block); + + /* restore saved_cr */ + stmqspi_info->saved_cr = saved_cr; + + if (retval == ERROR_OK) { + LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + "kbytes", temp.name, id1, temp.size_in_bytes / 1024); + /* save info and retrieved *good* id as spi_sfdp clears all info */ + memcpy(&stmqspi_info->dev, &temp, sizeof(stmqspi_info->dev)); + stmqspi_info->dev.device_id = id1; + } else { + /* even not identified by SFDP, then give up */ + LOG_WARNING("Unknown flash1 device id = 0x%06" PRIx32 + " - set flash parameters manually", id1); + retval = ERROR_OK; + goto err; + } + } + + /* identify flash2 */ + for (p = flash_devices; id2 && p->name ; p++) { + if (p->device_id == id2) { + if (p->size_in_bytes / 4096) + LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + "kbytes", p->name, id2, p->size_in_bytes / 1024); + else + LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + "bytes", p->name, id2, p->size_in_bytes); + + if (!id1) + memcpy(&stmqspi_info->dev, p, sizeof(stmqspi_info->dev)); + else { + if ((stmqspi_info->dev.read_cmd != p->read_cmd) || + (stmqspi_info->dev.qread_cmd != p->qread_cmd) || + (stmqspi_info->dev.pprog_cmd != p->pprog_cmd) || + (stmqspi_info->dev.erase_cmd != p->erase_cmd) || + (stmqspi_info->dev.chip_erase_cmd != p->chip_erase_cmd) || + (stmqspi_info->dev.sectorsize != p->sectorsize) || + (stmqspi_info->dev.size_in_bytes != p->size_in_bytes)) { + LOG_ERROR("Incompatible flash1/flash2 devices"); + goto err; + } + /* page size is optional in SFDP, so accept smallest value */ + if (p->pagesize < stmqspi_info->dev.pagesize) + stmqspi_info->dev.pagesize = p->pagesize; + } + break; + } + } + + if (id2 && !p->name) { + /* chip not been identified by id, then try SFDP */ + struct flash_device temp; + uint32_t saved_cr = stmqspi_info->saved_cr; + + /* select flash2 */ + stmqspi_info->saved_cr = stmqspi_info->saved_cr | BIT(SPI_FSEL_FLASH); + retval = spi_sfdp(bank, &temp, &read_sfdp_block); + + /* restore saved_cr */ + stmqspi_info->saved_cr = saved_cr; + + if (retval == ERROR_OK) + LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + "kbytes", temp.name, id2, temp.size_in_bytes / 1024); + else { + /* even not identified by SFDP, then give up */ + LOG_WARNING("Unknown flash2 device id = 0x%06" PRIx32 + " - set flash parameters manually", id2); + retval = ERROR_OK; + goto err; + } + + if (!id1) + memcpy(&stmqspi_info->dev, &temp, sizeof(stmqspi_info->dev)); + else { + if ((stmqspi_info->dev.read_cmd != temp.read_cmd) || + (stmqspi_info->dev.qread_cmd != temp.qread_cmd) || + (stmqspi_info->dev.pprog_cmd != temp.pprog_cmd) || + (stmqspi_info->dev.erase_cmd != temp.erase_cmd) || + (stmqspi_info->dev.chip_erase_cmd != temp.chip_erase_cmd) || + (stmqspi_info->dev.sectorsize != temp.sectorsize) || + (stmqspi_info->dev.size_in_bytes != temp.size_in_bytes)) { + LOG_ERROR("Incompatible flash1/flash2 devices"); + goto err; + } + /* page size is optional in SFDP, so accept smallest value */ + if (temp.pagesize < stmqspi_info->dev.pagesize) + stmqspi_info->dev.pagesize = temp.pagesize; + } + } + + /* Set correct size value */ + bank->size = stmqspi_info->dev.size_in_bytes << dual; + + fsize = ((READ_REG(SPI_DCR) >> SPI_FSIZE_POS) & (BIT(SPI_FSIZE_LEN) - 1)); + if (retval != ERROR_OK) + goto err; + + LOG_DEBUG("FSIZE = 0x%04x", fsize); + if (bank->size == BIT((fsize + 1))) + LOG_DEBUG("FSIZE in DCR(1) matches actual capacity. Beware of silicon bug in H7, L4+, MP1."); + else if (bank->size == BIT((fsize + 0))) + LOG_DEBUG("FSIZE in DCR(1) is off by one regarding actual capacity. Fix for silicon bug?"); + else + LOG_ERROR("FSIZE in DCR(1) doesn't match actual capacity."); + + /* if no sectors, then treat whole flash as single sector */ + if (stmqspi_info->dev.sectorsize == 0) + stmqspi_info->dev.sectorsize = stmqspi_info->dev.size_in_bytes; + /* if no page_size, then use sectorsize as page_size */ + if (stmqspi_info->dev.pagesize == 0) + stmqspi_info->dev.pagesize = stmqspi_info->dev.sectorsize; + + /* create and fill sectors array */ + bank->num_sectors = stmqspi_info->dev.size_in_bytes / stmqspi_info->dev.sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (sectors == NULL) { + LOG_ERROR("not enough memory"); + retval = ERROR_FAIL; + goto err; + } + + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * (stmqspi_info->dev.sectorsize << dual); + sectors[sector].size = (stmqspi_info->dev.sectorsize << dual); + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + stmqspi_info->probed = true; + +err: + /* Switch to memory mapped mode before return to prompt */ + set_mm_mode(bank); + + return retval; +} + +static int stmqspi_auto_probe(struct flash_bank *bank) +{ + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + + if (stmqspi_info->probed) + return ERROR_OK; + stmqspi_probe(bank); + return ERROR_OK; +} + +static int stmqspi_protect_check(struct flash_bank *bank) +{ + /* Nothing to do. Protection is only handled in SW. */ + return ERROR_OK; +} + +static int get_stmqspi_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv; + + if (!(stmqspi_info->probed)) { + snprintf(buf, buf_size, + "\nQSPI flash bank not probed yet\n"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + snprintf(buf, buf_size, "flash%s%s \'%s\', device id = 0x%06" PRIx32 + ", flash size = %" PRIu32 "%sbytes\n(page size = %" PRIu32 + ", read = 0x%02" PRIx8 ", qread = 0x%02" PRIx8 + ", pprog = 0x%02" PRIx8 ", mass_erase = 0x%02" PRIx8 + ", sector size = %" PRIu32 "%sbytes, sector_erase = 0x%02" PRIx8 ")", + ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | + BIT(SPI_FSEL_FLASH))) != BIT(SPI_FSEL_FLASH)) ? "1" : "", + ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | + BIT(SPI_FSEL_FLASH))) != 0) ? "2" : "", + stmqspi_info->dev.name, stmqspi_info->dev.device_id, + bank->size / 4096 ? bank->size / 1024 : bank->size, + bank->size / 4096 ? "k" : "", stmqspi_info->dev.pagesize, + stmqspi_info->dev.read_cmd, stmqspi_info->dev.qread_cmd, + stmqspi_info->dev.pprog_cmd, stmqspi_info->dev.chip_erase_cmd, + stmqspi_info->dev.sectorsize / 4096 ? + stmqspi_info->dev.sectorsize / 1024 : stmqspi_info->dev.sectorsize, + stmqspi_info->dev.sectorsize / 4096 ? "k" : "", + stmqspi_info->dev.erase_cmd); + + return ERROR_OK; +} + +static const struct command_registration stmqspi_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = stmqspi_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Mass erase entire flash device.", + }, + { + .name = "set", + .handler = stmqspi_handle_set, + .mode = COMMAND_EXEC, + .usage = "bank_id name chip_size page_size read_cmd qread_cmd pprg_cmd " + "[ mass_erase_cmd ] [ sector_size sector_erase_cmd ]", + .help = "Set params of single flash chip", + }, + { + .name = "cmd", + .handler = stmqspi_handle_cmd, + .mode = COMMAND_EXEC, + .usage = "bank_id num_resp cmd_byte ...", + .help = "Send low-level command cmd_byte and following bytes or read num_resp.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration stmqspi_command_handlers[] = { + { + .name = "stmqspi", + .mode = COMMAND_ANY, + .help = "stmqspi flash command group", + .usage = "", + .chain = stmqspi_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver stmqspi_flash = { + .name = "stmqspi", + .commands = stmqspi_command_handlers, + .flash_bank_command = stmqspi_flash_bank_command, + .erase = stmqspi_erase, + .protect = stmqspi_protect, + .write = stmqspi_write, + .read = stmqspi_read, + .verify = stmqspi_verify, + .probe = stmqspi_probe, + .auto_probe = stmqspi_auto_probe, + .erase_check = stmqspi_blank_check, + .protect_check = stmqspi_protect_check, + .info = get_stmqspi_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/stmqspi.h b/src/flash/nor/stmqspi.h new file mode 100644 index 0000000..85da25f --- /dev/null +++ b/src/flash/nor/stmqspi.h @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2016 - 2018 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_FLASH_NOR_STMQSPI_H +#define OPENOCD_FLASH_NOR_STMQSPI_H + +#include "spi.h" + +/* QSPI register offsets */ +#define QSPI_CR (0x00) /* Control register */ +#define QSPI_DCR (0x04) /* Device configuration register */ +#define QSPI_SR (0x08) /* Status register */ +#define QSPI_FCR (0x0C) /* Flag clear register */ +#define QSPI_DLR (0x10) /* Data length register */ +#define QSPI_CCR (0x14) /* Communication configuration register */ +#define QSPI_AR (0x18) /* Address register */ +#define QSPI_ABR (0x1C) /* Alternate bytes register */ +#define QSPI_DR (0x20) /* Data register */ + +/* common bits in QSPI_CR and OCTOSPI_CR */ +#define SPI_FSEL_FLASH 7 /* Select flash 2 */ +#define SPI_DUAL_FLASH 6 /* Dual flash mode */ +#define SPI_ABORT 1 /* Abort bit */ + +/* common bits in QSPI_DCR and OCTOSPI_DCR1 */ +#define SPI_FSIZE_POS 16 /* bit position of FSIZE */ +#define SPI_FSIZE_LEN 5 /* width of FSIZE field */ + +/* common bits in QSPI_SR/FCR and OCTOSPI_SR/FCR */ +#define SPI_BUSY 5 /* Busy flag */ +#define SPI_FTF 2 /* FIFO threshold flag */ +#define SPI_TCF 1 /* Transfer complete flag */ + +/* fields in QSPI_CCR */ +#define QSPI_DDRM 31 /* position of DDRM bit */ +#define SPI_DMODE_POS 24 /* bit position of DMODE */ +#define QSPI_DCYC_POS 18 /* bit position of DCYC */ +#define QSPI_DCYC_LEN 5 /* width of DCYC field */ +#define QSPI_DCYC_MASK ((BIT(QSPI_DCYC_LEN) - 1) << QSPI_DCYC_POS) +#define SPI_ADSIZE_POS 12 /* bit position of ADSIZE */ + +#define QSPI_WRITE_MODE 0x00000000U /* indirect write mode */ +#define QSPI_READ_MODE 0x04000000U /* indirect read mode */ +#define QSPI_MM_MODE 0x0C000000U /* memory mapped mode */ +#define QSPI_ALTB_MODE 0x0003C000U /* alternate byte mode */ +#define QSPI_4LINE_MODE 0x03000F00U /* 4 lines for data, addr, instr */ +#define QSPI_NO_DATA (~0x03000000U) /* no data */ +#define QSPI_NO_ALTB (~QSPI_ALTB_MODE) /* no alternate */ +#define QSPI_NO_ADDR (~0x00000C00U) /* no address */ +#define QSPI_ADDR3 (0x2U << SPI_ADSIZE_POS) /* 3 byte address */ +#define QSPI_ADDR4 (0x3U << SPI_ADSIZE_POS) /* 4 byte address */ + +/* OCTOSPI register offsets */ +#define OCTOSPI_CR (0x000) /* Control register */ +#define OCTOSPI_DCR1 (0x008) /* Device configuration register 1 */ +#define OCTOSPI_DCR2 (0x00C) /* Device configuration register 2 */ +#define OCTOSPI_DCR3 (0x010) /* Device configuration register 3 */ +#define OCTOSPI_SR (0x020) /* Status register */ +#define OCTOSPI_FCR (0x024) /* Flag clear register */ +#define OCTOSPI_DLR (0x040) /* Data length register */ +#define OCTOSPI_AR (0x048) /* Address register */ +#define OCTOSPI_DR (0x050) /* Data register */ +#define OCTOSPI_CCR (0x100) /* Communication configuration register */ +#define OCTOSPI_TCR (0x108) /* Timing configuration register */ +#define OCTOSPI_IR (0x110) /* Instruction register */ +#define OCTOSPI_WCCR (0x180) /* Write communication configuration register */ +#define OCTOSPI_WIR (0x190) /* Write instruction register */ +#define OCTOSPI_MAGIC (0x3FC) /* Magic ID register, deleted from RM, why? */ + +#define OCTO_MAGIC_ID 0xA3C5DD01 /* Magic ID, deleted from RM, why? */ + +/* additional bits in OCTOSPI_CR */ +#define OCTOSPI_WRITE_MODE 0x00000000U /* indirect write mode */ +#define OCTOSPI_READ_MODE 0x10000000U /* indirect read mode */ +#define OCTOSPI_MM_MODE 0x30000000U /* memory mapped mode */ + +/* additional fields in OCTOSPI_DCR1 */ +#define OCTOSPI_MTYP_POS (24) /* bit position of MTYP */ +#define OCTOSPI_MTYP_LEN (3) /* width of MTYP field */ +#define OCTOSPI_MTYP_MASK ((BIT(OCTOSPI_MTYP_LEN) - 1) << OCTOSPI_MTYP_POS) + +/* fields in OCTOSPI_CCR */ +#define OCTOSPI_ALTB_MODE 0x001F0000U /* alternate byte mode */ +#define OCTOSPI_8LINE_MODE 0x0F003F3FU /* 8 lines DTR for data, addr, instr */ +#define OCTOSPI_NO_DATA (~0x0F000000U) /* no data */ +#define OCTOSPI_NO_ALTB (~OCTOSPI_ALTB_MODE) /* no alternate */ +#define OCTOSPI_NO_ADDR (~0x00000F00U) /* no address */ +#define OCTOSPI_ADDR3 (0x2U << SPI_ADSIZE_POS) /* 3 byte address */ +#define OCTOSPI_ADDR4 (0x3U << SPI_ADSIZE_POS) /* 4 byte address */ +#define OCTOSPI_DQSEN 29 /* DQS enable */ +#define OCTOSPI_DDTR 27 /* DTR for data */ +#define OCTOSPI_NO_DDTR (~BIT(OCTOSPI_DDTR)) /* no DTR for data, but maybe still DQS */ +#define OCTOSPI_ISIZE_MASK (0x30) /* ISIZE field */ + +/* fields in OCTOSPI_TCR */ +#define OCTOSPI_DCYC_POS 0 /* bit position of DCYC */ +#define OCTOSPI_DCYC_LEN 5 /* width of DCYC field */ +#define OCTOSPI_DCYC_MASK ((BIT(OCTOSPI_DCYC_LEN) - 1) << OCTOSPI_DCYC_POS) + +#define IS_OCTOSPI (stmqspi_info->octo) +#define SPI_CR (IS_OCTOSPI ? OCTOSPI_CR : QSPI_CR) +#define SPI_DCR (IS_OCTOSPI ? OCTOSPI_DCR1 : QSPI_DCR) +#define SPI_SR (IS_OCTOSPI ? OCTOSPI_SR : QSPI_SR) +#define SPI_FCR (IS_OCTOSPI ? OCTOSPI_FCR : QSPI_FCR) +#define SPI_DLR (IS_OCTOSPI ? OCTOSPI_DLR : QSPI_DLR) +#define SPI_AR (IS_OCTOSPI ? OCTOSPI_AR : QSPI_AR) +#define SPI_DR (IS_OCTOSPI ? OCTOSPI_DR : QSPI_DR) +#define SPI_CCR (IS_OCTOSPI ? OCTOSPI_CCR : QSPI_CCR) + +#endif /* OPENOCD_FLASH_NOR_STMQSPI_H */ diff --git a/src/flash/nor/stmsmi.c b/src/flash/nor/stmsmi.c index 278c73e..f633e36 100644 --- a/src/flash/nor/stmsmi.c +++ b/src/flash/nor/stmsmi.c @@ -41,34 +41,33 @@ #include <jtag/jtag.h> #include <helper/time_support.h> -#define SMI_READ_REG(a) (_SMI_READ_REG(a)) -#define _SMI_READ_REG(a) \ -{ \ - int __a; \ - uint32_t __v; \ +#define SMI_READ_REG(a) \ +({ \ + int _ret; \ + uint32_t _value; \ \ - __a = target_read_u32(target, io_base + (a), &__v); \ - if (__a != ERROR_OK) \ - return __a; \ - __v; \ -} + _ret = target_read_u32(target, io_base + (a), &_value); \ + if (_ret != ERROR_OK) \ + return _ret; \ + _value; \ +}) #define SMI_WRITE_REG(a, v) \ { \ - int __r; \ + int _retval; \ \ - __r = target_write_u32(target, io_base + (a), (v)); \ - if (__r != ERROR_OK) \ - return __r; \ + _retval = target_write_u32(target, io_base + (a), (v)); \ + if (_retval != ERROR_OK) \ + return _retval; \ } #define SMI_POLL_TFF(timeout) \ { \ - int __r; \ + int _retval; \ \ - __r = poll_tff(target, io_base, timeout); \ - if (__r != ERROR_OK) \ - return __r; \ + _retval = poll_tff(target, io_base, timeout); \ + if (_retval != ERROR_OK) \ + return _retval; \ } #define SMI_SET_SW_MODE() SMI_WRITE_REG(SMI_CR1, \ diff --git a/src/flash/nor/tcl.c b/src/flash/nor/tcl.c index fbb602b..0e0a924 100644 --- a/src/flash/nor/tcl.c +++ b/src/flash/nor/tcl.c @@ -30,7 +30,7 @@ * Implements Tcl commands used to access NOR flash facilities. */ -COMMAND_HELPER(flash_command_get_bank_maybe_probe, unsigned name_index, +static COMMAND_HELPER(flash_command_get_bank_maybe_probe, unsigned name_index, struct flash_bank **bank, bool do_probe) { const char *name = CMD_ARGV[name_index]; @@ -441,20 +441,21 @@ COMMAND_HANDLER(handle_flash_write_image_command) duration_start(&bench); if (CMD_ARGC >= 2) { - image.base_address_set = 1; + image.base_address_set = true; COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], image.base_address); } else { - image.base_address_set = 0; + image.base_address_set = false; image.base_address = 0x0; } - image.start_address_set = 0; + image.start_address_set = false; retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL); if (retval != ERROR_OK) return retval; - retval = flash_write_unlock(target, &image, &written, auto_erase, auto_unlock); + retval = flash_write_unlock_verify(target, &image, &written, auto_erase, + auto_unlock, true, false); if (retval != ERROR_OK) { image_close(&image); return retval; @@ -471,6 +472,58 @@ COMMAND_HANDLER(handle_flash_write_image_command) return retval; } +COMMAND_HANDLER(handle_flash_verify_image_command) +{ + struct target *target = get_current_target(CMD_CTX); + + struct image image; + uint32_t verified; + + int retval; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!target) { + LOG_ERROR("no target selected"); + return ERROR_FAIL; + } + + struct duration bench; + duration_start(&bench); + + if (CMD_ARGC >= 2) { + image.base_address_set = 1; + COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], image.base_address); + } else { + image.base_address_set = 0; + image.base_address = 0x0; + } + + image.start_address_set = 0; + + retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL); + if (retval != ERROR_OK) + return retval; + + retval = flash_write_unlock_verify(target, &image, &verified, false, + false, false, true); + if (retval != ERROR_OK) { + image_close(&image); + return retval; + } + + if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) { + command_print(CMD, "verified %" PRIu32 " bytes from file %s " + "in %fs (%0.3f KiB/s)", verified, CMD_ARGV[0], + duration_elapsed(&bench), duration_kbps(&bench, verified)); + } + + image_close(&image); + + return retval; +} + COMMAND_HANDLER(handle_flash_fill_command) { target_addr_t address; @@ -1143,7 +1196,15 @@ static const struct command_registration flash_exec_command_handlers[] = { .mode = COMMAND_EXEC, .usage = "[erase] [unlock] filename [offset [file_type]]", .help = "Write an image to flash. Optionally first unprotect " - "and/or erase the region to be used. Allow optional " + "and/or erase the region to be used. Allow optional " + "offset from beginning of bank (defaults to zero)", + }, + { + .name = "verify_image", + .handler = handle_flash_verify_image_command, + .mode = COMMAND_EXEC, + .usage = "filename [offset [file_type]]", + .help = "Verify an image against flash. Allow optional " "offset from beginning of bank (defaults to zero)", }, { diff --git a/src/flash/nor/xcf.c b/src/flash/nor/xcf.c index 29eef2d..0cef43b 100644 --- a/src/flash/nor/xcf.c +++ b/src/flash/nor/xcf.c @@ -45,7 +45,7 @@ #define ID_XCF32P 0x05059093 #define ID_MEANINGFUL_MASK 0x0FFFFFFF -const char *xcf_name_list[] = { +static const char * const xcf_name_list[] = { "XCF08P", "XCF16P", "XCF32P", @@ -399,7 +399,7 @@ static void flip_u8(uint8_t *out, const uint8_t *in, int len) * Function presumes need of bit reversing if it can not exactly detects * the opposite. */ -bool need_bit_reverse(const uint8_t *buffer) +static bool need_bit_reverse(const uint8_t *buffer) { const size_t L = 20; uint8_t reference[L]; diff --git a/src/flash/startup.tcl b/src/flash/startup.tcl index aafb939..280a059 100644 --- a/src/flash/startup.tcl +++ b/src/flash/startup.tcl @@ -116,6 +116,7 @@ proc stm32l1x args { eval stm32lx $args } # stm32[g0|g4|wb|wl] uses the same flash driver as the stm32l4x proc stm32g0x args { eval stm32l4x $args } proc stm32g4x args { eval stm32l4x $args } +proc stm32l5x args { eval stm32l4x $args } proc stm32wbx args { eval stm32l4x $args } proc stm32wlx args { eval stm32l4x $args } diff --git a/src/helper/command.c b/src/helper/command.c index cfaa1f7..96b1244 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -349,7 +349,9 @@ static int register_command_handler(struct command_context *cmd_ctx, { Jim_Interp *interp = cmd_ctx->interp; +#if 0 LOG_DEBUG("registering '%s'...", c->name); +#endif Jim_CmdProc *func = c->handler ? &script_command : &command_unknown; int retval = Jim_CreateCommand(interp, c->name, func, c, NULL); @@ -920,39 +922,29 @@ COMMAND_HANDLER(handle_help_command) bool full = strcmp(CMD_NAME, "help") == 0; int retval; struct command *c = CMD_CTX->commands; - char *cmd_match = NULL; - - if (CMD_ARGC == 0) - cmd_match = ""; - else if (CMD_ARGC >= 1) { - unsigned i; - - for (i = 0; i < CMD_ARGC; ++i) { - if (NULL != cmd_match) { - char *prev = cmd_match; - - cmd_match = alloc_printf("%s %s", cmd_match, CMD_ARGV[i]); - free(prev); - if (NULL == cmd_match) { - LOG_ERROR("unable to build search string"); - return -ENOMEM; - } - } else { - cmd_match = alloc_printf("%s", CMD_ARGV[i]); - if (NULL == cmd_match) { - LOG_ERROR("unable to build search string"); - return -ENOMEM; - } - } + char *cmd_match; + + if (CMD_ARGC <= 0) + cmd_match = strdup(""); + + else { + cmd_match = strdup(CMD_ARGV[0]); + + for (unsigned int i = 1; i < CMD_ARGC && cmd_match; ++i) { + char *prev = cmd_match; + cmd_match = alloc_printf("%s %s", prev, CMD_ARGV[i]); + free(prev); } - } else - return ERROR_COMMAND_SYNTAX_ERROR; + } + if (cmd_match == NULL) { + LOG_ERROR("unable to build search string"); + return -ENOMEM; + } retval = CALL_COMMAND_HANDLER(command_help_show_list, c, 0, full, cmd_match); - if (CMD_ARGC >= 1) - free(cmd_match); + free(cmd_match); return retval; } diff --git a/src/helper/jep106.inc b/src/helper/jep106.inc index b0c0468..df93a5c 100644 --- a/src/helper/jep106.inc +++ b/src/helper/jep106.inc @@ -1,4 +1,9 @@ -/* Autogenerated with update_jep106.pl*/ +/* + * Should be autogenerated with update_jep106.pl but latest + * file from JEDEC is only available in PDF form. The PDF + * also breaks the pdftotext flow due to embedded watermarks. + * Created with a mix of scripts and manual editing. + */ [0][0x01 - 1] = "AMD", [0][0x02 - 1] = "AMI", [0][0x03 - 1] = "Fairchild", @@ -194,7 +199,7 @@ [1][0x43 - 1] = "Suwa Electronics", [1][0x44 - 1] = "Transmeta", [1][0x45 - 1] = "Micron CMS", -[1][0x46 - 1] = "American Computer & Digital", +[1][0x46 - 1] = "American Computer & Digital Components Inc", [1][0x47 - 1] = "Enhance 3000 Inc", [1][0x48 - 1] = "Tower Semiconductor", [1][0x49 - 1] = "CPU Design", @@ -438,7 +443,7 @@ [3][0x3b - 1] = "Concept Computer", [3][0x3c - 1] = "SILCOM", [3][0x3d - 1] = "3Dlabs", -[3][0x3e - 1] = "c’t Magazine", +[3][0x3e - 1] = "c't Magazine", [3][0x3f - 1] = "Sanera Systems", [3][0x40 - 1] = "Silicon Packets", [3][0x41 - 1] = "Viasystems Group", @@ -532,7 +537,7 @@ [4][0x1b - 1] = "Phonex Broadband", [4][0x1c - 1] = "Skyworks Solutions", [4][0x1d - 1] = "Entropic Communications", -[4][0x1e - 1] = "I’M Intelligent Memory Ltd", +[4][0x1e - 1] = "I'M Intelligent Memory Ltd", [4][0x1f - 1] = "Zensys A/S", [4][0x20 - 1] = "Legend Silicon Corp", [4][0x21 - 1] = "Sci-worx GmbH", @@ -610,7 +615,7 @@ [4][0x69 - 1] = "Century Micro Inc", [4][0x6a - 1] = "Icera Semiconductor", [4][0x6b - 1] = "Mediaworks Integrated Systems", -[4][0x6c - 1] = "O’Neil Product Development", +[4][0x6c - 1] = "O'Neil Product Development", [4][0x6d - 1] = "Supreme Top Technology Ltd", [4][0x6e - 1] = "MicroDisplay Corporation", [4][0x6f - 1] = "Team Group Inc", @@ -837,7 +842,7 @@ [6][0x50 - 1] = "Silego Technology Inc", [6][0x51 - 1] = "Kinglife", [6][0x52 - 1] = "Ability Industries Ltd", -[6][0x53 - 1] = "Silicon Power Computer &", +[6][0x53 - 1] = "Silicon Power Computer & Communications", [6][0x54 - 1] = "Augusta Technology Inc", [6][0x55 - 1] = "Nantronics Semiconductors", [6][0x56 - 1] = "Hilscher Gesellschaft", @@ -961,7 +966,7 @@ [7][0x4e - 1] = "Mustang", [7][0x4f - 1] = "Orca Systems", [7][0x50 - 1] = "Passif Semiconductor", -[7][0x51 - 1] = "GigaDevice Semiconductor (Beijing)", +[7][0x51 - 1] = "GigaDevice Semiconductor (Beijing) Inc", [7][0x52 - 1] = "Memphis Electronic", [7][0x53 - 1] = "Beckhoff Automation GmbH", [7][0x54 - 1] = "Harmony Semiconductor Corp", @@ -985,7 +990,7 @@ [7][0x66 - 1] = "Avalanche Technology", [7][0x67 - 1] = "R&D Center ELVEES OJSC", [7][0x68 - 1] = "KingboMars Technology Co Ltd", -[7][0x69 - 1] = "High Bridge Solutions Industria", +[7][0x69 - 1] = "High Bridge Solutions Industria Eletronica", [7][0x6a - 1] = "Transcend Technology Co Ltd", [7][0x6b - 1] = "Everspin Technologies", [7][0x6c - 1] = "Hon-Hai Precision", @@ -1031,7 +1036,7 @@ [8][0x17 - 1] = "Axell Corporation", [8][0x18 - 1] = "Essencore Limited", [8][0x19 - 1] = "Phytium", -[8][0x1a - 1] = "Xi’an UniIC Semiconductors Co Ltd", +[8][0x1a - 1] = "Xi'an UniIC Semiconductors Co Ltd", [8][0x1b - 1] = "Ambiq Micro", [8][0x1c - 1] = "eveRAM Technology Inc", [8][0x1d - 1] = "Infomax", @@ -1083,7 +1088,7 @@ [8][0x4b - 1] = "in2H2 inc", [8][0x4c - 1] = "Shenzhen Pango Microsystems Co Ltd", [8][0x4d - 1] = "Vasekey", -[8][0x4e - 1] = "Cal-Comp Industria de", +[8][0x4e - 1] = "Cal-Comp Industria de Semicondutores", [8][0x4f - 1] = "Eyenix Co Ltd", [8][0x50 - 1] = "Heoriady", [8][0x51 - 1] = "Accelerated Memory Production Inc", @@ -1114,7 +1119,7 @@ [8][0x6a - 1] = "Axign", [8][0x6b - 1] = "Kingred Electronic Technology Ltd", [8][0x6c - 1] = "Chao Yue Zhuo Computer Business Dept.", -[8][0x6d - 1] = "Guangzhou Si Nuo Electronic", +[8][0x6d - 1] = "Guangzhou Si Nuo Electronic Technology.", [8][0x6e - 1] = "Crocus Technology Inc", [8][0x6f - 1] = "Creative Chips GmbH", [8][0x70 - 1] = "GE Aviation Systems LLC.", @@ -1130,6 +1135,8 @@ [8][0x7a - 1] = "AltoBeam", [8][0x7b - 1] = "Wave Computing", [8][0x7c - 1] = "Beijing TrustNet Technology Co Ltd", +[8][0x7d - 1] = "Innovium Inc", +[8][0x7e - 1] = "Starsway Technology Limited", [9][0x01 - 1] = "Weltronics Co LTD", [9][0x02 - 1] = "VMware Inc", [9][0x03 - 1] = "Hewlett Packard Enterprise", @@ -1160,6 +1167,7 @@ [9][0x1c - 1] = "MemxPro Inc", [9][0x1d - 1] = "Tammuz Co Ltd", [9][0x1e - 1] = "Allwinner Technology", +[9][0x1f - 1] = "Shenzhen City Futian District Qing Xuan Tong Computer Trading Firm", [9][0x20 - 1] = "XMC", [9][0x21 - 1] = "Teclast", [9][0x22 - 1] = "Maxsun", @@ -1272,7 +1280,7 @@ [10][0x0f - 1] = "UNIC Memory Technology Co Ltd", [10][0x10 - 1] = "Shenzhen Huafeng Science Technology", [10][0x11 - 1] = "ChangXin Memory Technologies Inc", -[10][0x12 - 1] = "Guangzhou Xinyi Heng Computer", +[10][0x12 - 1] = "Guangzhou Xinyi Heng Computer Trading Firm", [10][0x13 - 1] = "SambaNova Systems", [10][0x14 - 1] = "V-GEN", [10][0x15 - 1] = "Jump Trading", @@ -1291,7 +1299,7 @@ [10][0x22 - 1] = "Shenzhen Chuangshifeida Technology", [10][0x23 - 1] = "Guangzhou MiaoYuanJi Technology", [10][0x24 - 1] = "ADVAN Inc", -[10][0x25 - 1] = "Shenzhen Qianhai Weishengda", +[10][0x25 - 1] = "Shenzhen Qianhai Weishengda Electronic Commerce Company Ltd", [10][0x26 - 1] = "Guangzhou Guang Xie Cheng Trading", [10][0x27 - 1] = "StarRam International Co Ltd", [10][0x28 - 1] = "Shen Zhen XinShenHua Tech Co Ltd", @@ -1321,7 +1329,7 @@ [10][0x40 - 1] = "Shenzhen Zhongguang Yunhe Trading", [10][0x41 - 1] = "Smart Shine(QingDao) Microelectronics", [10][0x42 - 1] = "Thermaltake Technology Co Ltd", -[10][0x43 - 1] = "Shenzhen O’Yang Maile Technology Ltd", +[10][0x43 - 1] = "Shenzhen O'Yang Maile Technology Ltd", [10][0x44 - 1] = "UPMEM", [10][0x45 - 1] = "Chun Well Technology Holding Limited", [10][0x46 - 1] = "Astera Labs Inc", @@ -1397,7 +1405,7 @@ [11][0x0e - 1] = "MLOONG", [11][0x0f - 1] = "Quadratica LLC", [11][0x10 - 1] = "Anpec Electronics", -[11][0x11 - 1] = "Xi’an Morebeck Semiconductor Tech Co", +[11][0x11 - 1] = "Xi'an Morebeck Semiconductor Tech Co", [11][0x12 - 1] = "Kingbank Technology Co Ltd", [11][0x13 - 1] = "ITRenew Inc", [11][0x14 - 1] = "Shenzhen Eaget Innovation Tech Ltd", diff --git a/src/jtag/aice/aice_pipe.c b/src/jtag/aice/aice_pipe.c index bdc8c09..c0e532c 100644 --- a/src/jtag/aice/aice_pipe.c +++ b/src/jtag/aice/aice_pipe.c @@ -35,8 +35,8 @@ #ifdef _WIN32 PROCESS_INFORMATION proc_info; -HANDLE aice_pipe_output[2]; -HANDLE aice_pipe_input[2]; +static HANDLE aice_pipe_output[2]; +static HANDLE aice_pipe_input[2]; static int aice_pipe_write(const void *buffer, int count) { @@ -158,8 +158,8 @@ static int aice_pipe_open(struct aice_port_param_s *param) #else -int aice_pipe_output[2]; -int aice_pipe_input[2]; +static int aice_pipe_output[2]; +static int aice_pipe_input[2]; static int aice_pipe_write(const void *buffer, int count) { diff --git a/src/jtag/aice/aice_usb.c b/src/jtag/aice/aice_usb.c index 7144632..7688aea 100644 --- a/src/jtag/aice/aice_usb.c +++ b/src/jtag/aice/aice_usb.c @@ -683,7 +683,7 @@ int aice_write_ctrl(uint32_t address, uint32_t data) return ERROR_OK; } -int aice_read_dtr(uint8_t target_id, uint32_t *data) +static int aice_read_dtr(uint8_t target_id, uint32_t *data) { int retry_times = 0; @@ -733,7 +733,7 @@ int aice_read_dtr(uint8_t target_id, uint32_t *data) return ERROR_OK; } -int aice_read_dtr_to_buffer(uint8_t target_id, uint32_t buffer_idx) +static int aice_read_dtr_to_buffer(uint8_t target_id, uint32_t buffer_idx) { int retry_times = 0; @@ -784,7 +784,7 @@ int aice_read_dtr_to_buffer(uint8_t target_id, uint32_t buffer_idx) return ERROR_OK; } -int aice_write_dtr(uint8_t target_id, uint32_t data) +static int aice_write_dtr(uint8_t target_id, uint32_t data) { int retry_times = 0; @@ -836,7 +836,7 @@ int aice_write_dtr(uint8_t target_id, uint32_t data) return ERROR_OK; } -int aice_write_dtr_from_buffer(uint8_t target_id, uint32_t buffer_idx) +static int aice_write_dtr_from_buffer(uint8_t target_id, uint32_t buffer_idx) { int retry_times = 0; @@ -887,7 +887,7 @@ int aice_write_dtr_from_buffer(uint8_t target_id, uint32_t buffer_idx) return ERROR_OK; } -int aice_read_misc(uint8_t target_id, uint32_t address, uint32_t *data) +static int aice_read_misc(uint8_t target_id, uint32_t address, uint32_t *data) { int retry_times = 0; @@ -936,7 +936,7 @@ int aice_read_misc(uint8_t target_id, uint32_t address, uint32_t *data) return ERROR_OK; } -int aice_write_misc(uint8_t target_id, uint32_t address, uint32_t data) +static int aice_write_misc(uint8_t target_id, uint32_t address, uint32_t data) { int retry_times = 0; @@ -992,7 +992,7 @@ int aice_write_misc(uint8_t target_id, uint32_t address, uint32_t data) return ERROR_OK; } -int aice_read_edmsr(uint8_t target_id, uint32_t address, uint32_t *data) +static int aice_read_edmsr(uint8_t target_id, uint32_t address, uint32_t *data) { int retry_times = 0; @@ -1042,7 +1042,7 @@ int aice_read_edmsr(uint8_t target_id, uint32_t address, uint32_t *data) return ERROR_OK; } -int aice_write_edmsr(uint8_t target_id, uint32_t address, uint32_t data) +static int aice_write_edmsr(uint8_t target_id, uint32_t address, uint32_t data) { int retry_times = 0; @@ -1236,7 +1236,7 @@ static int aice_do_execute(uint8_t target_id) return ERROR_OK; } -int aice_write_mem_b(uint8_t target_id, uint32_t address, uint32_t data) +static int aice_write_mem_b(uint8_t target_id, uint32_t address, uint32_t data) { int retry_times = 0; @@ -1290,7 +1290,7 @@ int aice_write_mem_b(uint8_t target_id, uint32_t address, uint32_t data) return ERROR_OK; } -int aice_write_mem_h(uint8_t target_id, uint32_t address, uint32_t data) +static int aice_write_mem_h(uint8_t target_id, uint32_t address, uint32_t data) { int retry_times = 0; @@ -1345,7 +1345,7 @@ int aice_write_mem_h(uint8_t target_id, uint32_t address, uint32_t data) return ERROR_OK; } -int aice_write_mem(uint8_t target_id, uint32_t address, uint32_t data) +static int aice_write_mem(uint8_t target_id, uint32_t address, uint32_t data) { int retry_times = 0; @@ -1400,7 +1400,7 @@ int aice_write_mem(uint8_t target_id, uint32_t address, uint32_t data) return ERROR_OK; } -int aice_fastread_mem(uint8_t target_id, uint8_t *word, uint32_t num_of_words) +static int aice_fastread_mem(uint8_t target_id, uint8_t *word, uint32_t num_of_words) { int retry_times = 0; @@ -1450,7 +1450,7 @@ int aice_fastread_mem(uint8_t target_id, uint8_t *word, uint32_t num_of_words) return ERROR_OK; } -int aice_fastwrite_mem(uint8_t target_id, const uint8_t *word, uint32_t num_of_words) +static int aice_fastwrite_mem(uint8_t target_id, const uint8_t *word, uint32_t num_of_words) { int retry_times = 0; @@ -1506,7 +1506,7 @@ int aice_fastwrite_mem(uint8_t target_id, const uint8_t *word, uint32_t num_of_w return ERROR_OK; } -int aice_read_mem_b(uint8_t target_id, uint32_t address, uint32_t *data) +static int aice_read_mem_b(uint8_t target_id, uint32_t address, uint32_t *data) { int retry_times = 0; @@ -1556,7 +1556,7 @@ int aice_read_mem_b(uint8_t target_id, uint32_t address, uint32_t *data) return ERROR_OK; } -int aice_read_mem_h(uint8_t target_id, uint32_t address, uint32_t *data) +static int aice_read_mem_h(uint8_t target_id, uint32_t address, uint32_t *data) { int retry_times = 0; @@ -1606,7 +1606,7 @@ int aice_read_mem_h(uint8_t target_id, uint32_t address, uint32_t *data) return ERROR_OK; } -int aice_read_mem(uint8_t target_id, uint32_t address, uint32_t *data) +static int aice_read_mem(uint8_t target_id, uint32_t address, uint32_t *data) { int retry_times = 0; @@ -1657,7 +1657,7 @@ int aice_read_mem(uint8_t target_id, uint32_t address, uint32_t *data) return ERROR_OK; } -int aice_batch_buffer_read(uint8_t buf_index, uint32_t *word, uint32_t num_of_words) +static int aice_batch_buffer_read(uint8_t buf_index, uint32_t *word, uint32_t num_of_words) { int retry_times = 0; @@ -1760,7 +1760,7 @@ int aice_batch_buffer_write(uint8_t buf_index, const uint8_t *word, uint32_t num typedef int (*read_mem_func_t)(uint32_t coreid, uint32_t address, uint32_t *data); typedef int (*write_mem_func_t)(uint32_t coreid, uint32_t address, uint32_t data); -struct aice_nds32_info core_info[AICE_MAX_NUM_CORE]; +static struct aice_nds32_info core_info[AICE_MAX_NUM_CORE]; static uint8_t total_num_of_core; static char *custom_srst_script; diff --git a/src/jtag/core.c b/src/jtag/core.c index 03a26be..5abf832 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -209,7 +209,7 @@ unsigned jtag_tap_count_enabled(void) } /** Append a new TAP to the chain of all taps. */ -void jtag_tap_add(struct jtag_tap *t) +static void jtag_tap_add(struct jtag_tap *t) { unsigned jtag_num_taps = 0; @@ -953,12 +953,12 @@ int default_interface_jtag_execute_queue(void) int result = jtag->jtag_ops->execute_queue(); -#if !BUILD_ZY1000 +#if !HAVE_JTAG_MINIDRIVER_H /* Only build this if we use a regular driver with a command queue. * Otherwise jtag_command_queue won't be found at compile/link time. Its * definition is in jtag/commands.c, which is only built/linked by * jtag/Makefile.am if MINIDRIVER_DUMMY || !MINIDRIVER, but those variables - * aren't accessible here. */ + * aren't accessible here. Use HAVE_JTAG_MINIDRIVER_H */ struct jtag_command *cmd = jtag_command_queue; while (debug_level >= LOG_LVL_DEBUG_IO && cmd) { switch (cmd->type) { diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index e8d20cc..f7a54b0 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -170,8 +170,15 @@ endif if OPENJTAG DRIVERFILES += %D%/openjtag.c endif -if CMSIS_DAP -DRIVERFILES += %D%/cmsis_dap_usb.c +if CMSIS_DAP_HID +DRIVERFILES += %D%/cmsis_dap_usb_hid.c +DRIVERFILES += %D%/cmsis_dap.c +endif +if CMSIS_DAP_USB +DRIVERFILES += %D%/cmsis_dap_usb_bulk.c +if !CMSIS_DAP_HID +DRIVERFILES += %D%/cmsis_dap.c +endif endif if IMX_GPIO DRIVERFILES += %D%/imx_gpio.c @@ -187,7 +194,9 @@ DRIVERHEADERS = \ %D%/bitbang.h \ %D%/bitq.h \ %D%/jtag_usb_common.h \ + %D%/libftdi_helper.h \ %D%/libusb_helper.h \ + %D%/cmsis_dap.h \ %D%/minidriver_imp.h \ %D%/mpsse.h \ %D%/rlink.h \ diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap.c index 6d55392..16480ae 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap.c @@ -1,4 +1,7 @@ /*************************************************************************** + * Copyright (C) 2018 by Mickaël Thomas * + * mickael9@gmail.com * + * * * Copyright (C) 2016 by Maksym Hilliaka * * oter@frozen-team.com * * * @@ -38,12 +41,17 @@ #include <jtag/commands.h> #include <jtag/tcl.h> -#include <hidapi.h> +#include "cmsis_dap.h" -/* - * See CMSIS-DAP documentation: - * Version 0.01 - Beta. - */ +static const struct cmsis_dap_backend *const cmsis_dap_backends[] = { +#if BUILD_CMSIS_DAP_USB == 1 + &cmsis_dap_usb_backend, +#endif + +#if BUILD_CMSIS_DAP_HID == 1 + &cmsis_dap_hid_backend, +#endif +}; /* USB Config */ @@ -52,6 +60,7 @@ * PID 0xf001: LPC-Link-II CMSIS_DAP * PID 0xf002: OPEN-SDA CMSIS_DAP (Freedom Board) * PID 0x2722: Keil ULINK2 CMSIS-DAP + * PID 0x2750: Keil ULINKplus CMSIS-DAP * * VID 0x0d28: mbed Software * PID 0x0204: MBED CMSIS-DAP @@ -61,10 +70,10 @@ /* vid = pid = 0 marks the end of the list */ static uint16_t cmsis_dap_vid[MAX_USB_IDS + 1] = { 0 }; static uint16_t cmsis_dap_pid[MAX_USB_IDS + 1] = { 0 }; -static wchar_t *cmsis_dap_serial; +static char *cmsis_dap_serial; +static int cmsis_dap_backend = -1; static bool swd_mode; -#define PACKET_SIZE (64 + 1) /* 64 bytes plus report id */ #define USB_TIMEOUT 1000 /* CMSIS-DAP General Commands */ @@ -163,14 +172,6 @@ static const char * const info_caps_str[] = { /* max clock speed (kHz) */ #define DAP_MAX_CLOCK 5000 -struct cmsis_dap { - hid_device *dev_handle; - uint16_t packet_size; - int packet_count; - uint8_t *packet_buffer; - uint8_t caps; - uint8_t mode; -}; struct pending_transfer_result { uint8_t cmd; @@ -223,134 +224,66 @@ static uint8_t output_pins = SWJ_PIN_SRST | SWJ_PIN_TRST; static struct cmsis_dap *cmsis_dap_handle; -static int cmsis_dap_usb_open(void) -{ - hid_device *dev = NULL; - int i; - struct hid_device_info *devs, *cur_dev; - unsigned short target_vid, target_pid; - bool found = false; - target_vid = 0; - target_pid = 0; +static int cmsis_dap_open(void) +{ + const struct cmsis_dap_backend *backend = NULL; - if (hid_init() != 0) { - LOG_ERROR("unable to open HIDAPI"); + struct cmsis_dap *dap = malloc(sizeof(struct cmsis_dap)); + if (dap == NULL) { + LOG_ERROR("unable to allocate memory"); return ERROR_FAIL; } - /* - * The CMSIS-DAP specification stipulates: - * "The Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the - * debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer." - */ - devs = hid_enumerate(0x0, 0x0); - cur_dev = devs; - while (NULL != cur_dev) { - if (0 == cmsis_dap_vid[0]) { - if (NULL == cur_dev->product_string) { - LOG_DEBUG("Cannot read product string of device 0x%x:0x%x", - cur_dev->vendor_id, cur_dev->product_id); - } else { - if (wcsstr(cur_dev->product_string, L"CMSIS-DAP")) { - /* if the user hasn't specified VID:PID *and* - * product string contains "CMSIS-DAP", pick it - */ - found = true; - } - } - } else { - /* otherwise, exhaustively compare against all VID:PID in list */ - for (i = 0; cmsis_dap_vid[i] || cmsis_dap_pid[i]; i++) { - if ((cmsis_dap_vid[i] == cur_dev->vendor_id) && (cmsis_dap_pid[i] == cur_dev->product_id)) - found = true; - } - - if (cmsis_dap_vid[i] || cmsis_dap_pid[i]) - found = true; - } - - /* LPC-LINK2 has cmsis-dap on interface 0 and other HID functions on other interfaces */ - if (cur_dev->vendor_id == 0x1fc9 && cur_dev->product_id == 0x0090 && cur_dev->interface_number != 0) - found = false; + dap->caps = 0; + dap->mode = 0; + dap->packet_size = 0; /* initialized by backend */ - if (found) { - /* we have found an adapter, so exit further checks */ - /* check serial number matches if given */ - if (cmsis_dap_serial != NULL) { - if ((cur_dev->serial_number != NULL) && wcscmp(cmsis_dap_serial, cur_dev->serial_number) == 0) { - break; - } - } else + if (cmsis_dap_backend >= 0) { + /* Use forced backend */ + backend = cmsis_dap_backends[cmsis_dap_backend]; + if (backend->open(dap, cmsis_dap_vid, cmsis_dap_pid, cmsis_dap_serial) != ERROR_OK) + backend = NULL; + } else { + /* Try all backends */ + for (unsigned int i = 0; i < ARRAY_SIZE(cmsis_dap_backends); i++) { + backend = cmsis_dap_backends[i]; + if (backend->open(dap, cmsis_dap_vid, cmsis_dap_pid, cmsis_dap_serial) == ERROR_OK) break; - - found = false; + else + backend = NULL; } - - cur_dev = cur_dev->next; - } - - if (NULL != cur_dev) { - target_vid = cur_dev->vendor_id; - target_pid = cur_dev->product_id; } - if (target_vid == 0 && target_pid == 0) { - LOG_ERROR("unable to find CMSIS-DAP device"); - hid_free_enumeration(devs); + if (backend == NULL) { + LOG_ERROR("unable to find a matching CMSIS-DAP device"); + free(dap); return ERROR_FAIL; } - dev = hid_open_path(cur_dev->path); - hid_free_enumeration(devs); + assert(dap->packet_size > 0); - if (dev == NULL) { - LOG_ERROR("unable to open CMSIS-DAP device 0x%x:0x%x", target_vid, target_pid); - return ERROR_FAIL; - } + dap->backend = backend; + dap->packet_buffer = malloc(dap->packet_size); - struct cmsis_dap *dap = malloc(sizeof(struct cmsis_dap)); - if (dap == NULL) { + if (dap->packet_buffer == NULL) { LOG_ERROR("unable to allocate memory"); + dap->backend->close(dap); + free(dap); return ERROR_FAIL; } - dap->dev_handle = dev; - dap->caps = 0; - dap->mode = 0; - cmsis_dap_handle = dap; - /* allocate default packet buffer, may be changed later. - * currently with HIDAPI we have no way of getting the output report length - * without this info we cannot communicate with the adapter. - * For the moment we ahve to hard code the packet size */ - - int packet_size = PACKET_SIZE; - - /* atmel cmsis-dap uses 512 byte reports */ - /* except when it doesn't e.g. with mEDBG on SAMD10 Xplained - * board */ - /* TODO: HID report descriptor should be parsed instead of - * hardcoding a match by VID */ - if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175) - packet_size = 512 + 1; - - cmsis_dap_handle->packet_buffer = malloc(packet_size); - cmsis_dap_handle->packet_size = packet_size; - - if (cmsis_dap_handle->packet_buffer == NULL) { - LOG_ERROR("unable to allocate memory"); - return ERROR_FAIL; - } - return ERROR_OK; } -static void cmsis_dap_usb_close(struct cmsis_dap *dap) +static void cmsis_dap_close(struct cmsis_dap *dap) { - hid_close(dap->dev_handle); - hid_exit(); + if (dap->backend) { + dap->backend->close(dap); + dap->backend = NULL; + } free(cmsis_dap_handle->packet_buffer); free(cmsis_dap_handle); @@ -364,47 +297,27 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap) } } -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]); -#endif - /* Pad the rest of the TX buffer with 0's */ - memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen); - - /* write data to device */ - int retval = hid_write(dap->dev_handle, dap->packet_buffer, dap->packet_size); - if (retval == -1) { - LOG_ERROR("error writing data: %ls", hid_error(dap->dev_handle)); - 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) +static int cmsis_dap_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); + dap->backend->read(dap, 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) + int retval = dap->backend->write(dap, txlen, USB_TIMEOUT); + if (retval < 0) return retval; /* get reply */ - retval = hid_read_timeout(dap->dev_handle, dap->packet_buffer, dap->packet_size, USB_TIMEOUT); - if (retval == -1 || retval == 0) { - LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle)); - return ERROR_FAIL; - } + retval = dap->backend->read(dap, USB_TIMEOUT); + if (retval < 0) + return retval; return ERROR_OK; } @@ -422,7 +335,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Pins(uint8_t pins, uint8_t mask, uint32_t delay buffer[5] = (delay >> 8) & 0xff; buffer[6] = (delay >> 16) & 0xff; buffer[7] = (delay >> 24) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 8); + retval = cmsis_dap_xfer(cmsis_dap_handle, 8); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command CMD_DAP_SWJ_PINS failed."); @@ -448,7 +361,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Clock(uint32_t swj_clock) buffer[3] = (swj_clock >> 8) & 0xff; buffer[4] = (swj_clock >> 16) & 0xff; buffer[5] = (swj_clock >> 24) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 6); + retval = cmsis_dap_xfer(cmsis_dap_handle, 6); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_DAP_SWJ_CLOCK failed."); @@ -477,7 +390,7 @@ static int cmsis_dap_cmd_DAP_SWJ_Sequence(uint8_t s_len, const uint8_t *sequence buffer[2] = s_len; bit_copy(&buffer[3], 0, sequence, 0, s_len); - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) return ERROR_FAIL; @@ -493,7 +406,7 @@ static int cmsis_dap_cmd_DAP_Info(uint8_t info, uint8_t **data) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_INFO; buffer[2] = info; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, 3); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command CMD_INFO failed."); @@ -514,7 +427,7 @@ static int cmsis_dap_cmd_DAP_LED(uint8_t led, uint8_t state) buffer[1] = CMD_DAP_LED; buffer[2] = led; buffer[3] = state; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 4); + retval = cmsis_dap_xfer(cmsis_dap_handle, 4); if (retval != ERROR_OK || buffer[1] != 0x00) { LOG_ERROR("CMSIS-DAP command CMD_LED failed."); @@ -532,7 +445,7 @@ static int cmsis_dap_cmd_DAP_Connect(uint8_t mode) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_CONNECT; buffer[2] = mode; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, 3); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command CMD_CONNECT failed."); @@ -554,7 +467,7 @@ static int cmsis_dap_cmd_DAP_Disconnect(void) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_DISCONNECT; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 2); + retval = cmsis_dap_xfer(cmsis_dap_handle, 2); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_DISCONNECT failed."); @@ -576,7 +489,7 @@ static int cmsis_dap_cmd_DAP_TFER_Configure(uint8_t idle, uint16_t retry_count, buffer[4] = (retry_count >> 8) & 0xff; buffer[5] = match_retry & 0xff; buffer[6] = (match_retry >> 8) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 7); + retval = cmsis_dap_xfer(cmsis_dap_handle, 7); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_TFER_Configure failed."); @@ -594,7 +507,7 @@ static int cmsis_dap_cmd_DAP_SWD_Configure(uint8_t cfg) buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_SWD_CONFIGURE; buffer[2] = cfg; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 3); + retval = cmsis_dap_xfer(cmsis_dap_handle, 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_SWD_Configure failed."); @@ -614,7 +527,7 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us) buffer[1] = CMD_DAP_DELAY; buffer[2] = delay_us & 0xff; buffer[3] = (delay_us >> 8) & 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 4); + retval = cmsis_dap_xfer(cmsis_dap_handle, 4); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_Delay failed."); @@ -684,9 +597,14 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) } } - queued_retval = cmsis_dap_usb_write(dap, idx); - if (queued_retval != ERROR_OK) + int retval = dap->backend->write(dap, idx, USB_TIMEOUT); + + if (retval < 0) { + queued_retval = retval; goto skip; + } else { + queued_retval = ERROR_OK; + } pending_fifo_put_idx = (pending_fifo_put_idx + 1) % dap->packet_count; pending_fifo_block_count++; @@ -708,12 +626,12 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) 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) + int retval = dap->backend->read(dap, timeout_ms); + if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < USB_TIMEOUT) return; - if (retval == -1 || retval == 0) { - LOG_DEBUG("error reading data: %ls", hid_error(dap->dev_handle)); + if (retval <= 0) { + LOG_DEBUG("error reading data"); queued_retval = ERROR_FAIL; goto skip; } @@ -969,7 +887,7 @@ static int cmsis_dap_init(void) int retval; uint8_t *data; - retval = cmsis_dap_usb_open(); + retval = cmsis_dap_open(); if (retval != ERROR_OK) return retval; @@ -1048,7 +966,7 @@ static int cmsis_dap_init(void) LOG_DEBUG("CMSIS-DAP: Packet Count = %d", pkt_cnt); } - LOG_DEBUG("Allocating FIFO for %d pending HID requests", cmsis_dap_handle->packet_count); + LOG_DEBUG("Allocating FIFO for %d pending packets", 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) { @@ -1120,7 +1038,7 @@ static int cmsis_dap_quit(void) cmsis_dap_cmd_DAP_LED(LED_ID_RUN, LED_OFF); cmsis_dap_cmd_DAP_LED(LED_ID_CONNECT, LED_OFF); - cmsis_dap_usb_close(cmsis_dap_handle); + cmsis_dap_close(cmsis_dap_handle); return ERROR_OK; } @@ -1261,7 +1179,7 @@ static void cmsis_dap_flush(void) #endif /* send command to USB device */ - int retval = cmsis_dap_usb_xfer(cmsis_dap_handle, queued_seq_buf_end + 3); + int retval = cmsis_dap_xfer(cmsis_dap_handle, queued_seq_buf_end + 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) { LOG_ERROR("CMSIS-DAP command CMD_DAP_JTAG_SEQ failed."); exit(-1); @@ -1668,7 +1586,7 @@ COMMAND_HANDLER(cmsis_dap_handle_cmd_command) for (i = 0; i < CMD_ARGC; i++) buffer[i + 1] = strtoul(CMD_ARGV[i], NULL, 16); - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, CMD_ARGC + 1); + retval = cmsis_dap_xfer(cmsis_dap_handle, CMD_ARGC + 1); if (retval != ERROR_OK) { LOG_ERROR("CMSIS-DAP command failed."); @@ -1713,20 +1631,31 @@ COMMAND_HANDLER(cmsis_dap_handle_vid_pid_command) COMMAND_HANDLER(cmsis_dap_handle_serial_command) { + if (CMD_ARGC == 1) + cmsis_dap_serial = strdup(CMD_ARGV[0]); + else + LOG_ERROR("expected exactly one argument to cmsis_dap_serial <serial-number>"); + + return ERROR_OK; +} + +COMMAND_HANDLER(cmsis_dap_handle_backend_command) +{ if (CMD_ARGC == 1) { - size_t len = mbstowcs(NULL, CMD_ARGV[0], 0); - cmsis_dap_serial = calloc(len + 1, sizeof(wchar_t)); - if (cmsis_dap_serial == NULL) { - LOG_ERROR("unable to allocate memory"); - return ERROR_OK; - } - if (mbstowcs(cmsis_dap_serial, CMD_ARGV[0], len + 1) == (size_t)-1) { - free(cmsis_dap_serial); - cmsis_dap_serial = NULL; - LOG_ERROR("unable to convert serial"); + if (strcmp(CMD_ARGV[0], "auto") == 0) { + cmsis_dap_backend = -1; /* autoselect */ + } else { + for (unsigned int i = 0; i < ARRAY_SIZE(cmsis_dap_backends); i++) { + if (strcasecmp(cmsis_dap_backends[i]->name, CMD_ARGV[0]) == 0) { + cmsis_dap_backend = i; + return ERROR_OK; + } + } + + LOG_ERROR("invalid backend argument to cmsis_dap_backend <backend>"); } } else { - LOG_ERROR("expected exactly one argument to cmsis_dap_serial <serial-number>"); + LOG_ERROR("expected exactly one argument to cmsis_dap_backend <backend>"); } return ERROR_OK; @@ -1750,6 +1679,7 @@ static const struct command_registration cmsis_dap_subcommand_handlers[] = { COMMAND_REGISTRATION_DONE }; + static const struct command_registration cmsis_dap_command_handlers[] = { { .name = "cmsis-dap", @@ -1772,6 +1702,22 @@ static const struct command_registration cmsis_dap_command_handlers[] = { .help = "set the serial number of the adapter", .usage = "serial_string", }, + { + .name = "cmsis_dap_backend", + .handler = &cmsis_dap_handle_backend_command, + .mode = COMMAND_CONFIG, + .help = "set the communication backend to use (USB bulk or HID).", + .usage = "(auto | usb_bulk | hid)", + }, +#if BUILD_CMSIS_DAP_USB + { + .name = "cmsis_dap_usb", + .chain = cmsis_dap_usb_subcommand_handlers, + .mode = COMMAND_ANY, + .help = "USB bulk backend-specific commands", + .usage = "<cmd>", + }, +#endif COMMAND_REGISTRATION_DONE }; diff --git a/src/jtag/drivers/cmsis_dap.h b/src/jtag/drivers/cmsis_dap.h new file mode 100644 index 0000000..054621c --- /dev/null +++ b/src/jtag/drivers/cmsis_dap.h @@ -0,0 +1,32 @@ +#ifndef OPENOCD_JTAG_DRIVERS_CMSIS_DAP_H +#define OPENOCD_JTAG_DRIVERS_CMSIS_DAP_H + +#include <stdint.h> + +struct cmsis_dap_backend; +struct cmsis_dap_backend_data; +struct command_registration; + +struct cmsis_dap { + struct cmsis_dap_backend_data *bdata; + const struct cmsis_dap_backend *backend; + uint16_t packet_size; + int packet_count; + uint8_t *packet_buffer; + uint8_t caps; + uint8_t mode; +}; + +struct cmsis_dap_backend { + const char *name; + int (*open)(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial); + void (*close)(struct cmsis_dap *dap); + int (*read)(struct cmsis_dap *dap, int timeout_ms); + int (*write)(struct cmsis_dap *dap, int len, int timeout_ms); +}; + +extern const struct cmsis_dap_backend cmsis_dap_hid_backend; +extern const struct cmsis_dap_backend cmsis_dap_usb_backend; +extern const struct command_registration cmsis_dap_usb_subcommand_handlers[]; + +#endif diff --git a/src/jtag/drivers/cmsis_dap_usb_bulk.c b/src/jtag/drivers/cmsis_dap_usb_bulk.c new file mode 100644 index 0000000..0134c03 --- /dev/null +++ b/src/jtag/drivers/cmsis_dap_usb_bulk.c @@ -0,0 +1,454 @@ +/*************************************************************************** + * Copyright (C) 2018 by Mickaël Thomas * + * mickael9@gmail.com * + * * + * Copyright (C) 2016 by Maksym Hilliaka * + * oter@frozen-team.com * + * * + * Copyright (C) 2016 by Phillip Pearson * + * pp@myelin.co.nz * + * * + * Copyright (C) 2014 by Paul Fertser * + * fercerpav@gmail.com * + * * + * Copyright (C) 2013 by mike brown * + * mike@theshedworks.org.uk * + * * + * Copyright (C) 2013 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <libusb.h> +#include <helper/log.h> + +#include "cmsis_dap.h" + +struct cmsis_dap_backend_data { + libusb_context *usb_ctx; + libusb_device_handle *dev_handle; + unsigned int ep_out; + unsigned int ep_in; + int interface; +}; + +static int cmsis_dap_usb_interface = -1; + +static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial) +{ + int err; + libusb_context *ctx; + libusb_device **device_list; + + err = libusb_init(&ctx); + if (err) { + LOG_ERROR("libusb initialization failed: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + + int num_devices = libusb_get_device_list(ctx, &device_list); + if (num_devices < 0) { + LOG_ERROR("could not enumerate USB devices: %s", libusb_strerror(num_devices)); + libusb_exit(ctx); + return ERROR_FAIL; + } + + for (int i = 0; i < num_devices; i++) { + libusb_device *dev = device_list[i]; + struct libusb_device_descriptor dev_desc; + + err = libusb_get_device_descriptor(dev, &dev_desc); + if (err) { + LOG_ERROR("could not get device descriptor for device %d: %s", i, libusb_strerror(err)); + continue; + } + + /* Match VID/PID */ + + bool id_match = false; + bool id_filter = vids[0] || pids[0]; + for (int id = 0; vids[id] || pids[id]; id++) { + id_match = !vids[id] || dev_desc.idVendor == vids[id]; + id_match &= !pids[id] || dev_desc.idProduct == pids[id]; + + if (id_match) + break; + } + + if (id_filter && !id_match) + continue; + + /* Don't continue if we asked for a serial number and the device doesn't have one */ + if (dev_desc.iSerialNumber == 0 && serial && serial[0]) + continue; + + libusb_device_handle *dev_handle = NULL; + err = libusb_open(dev, &dev_handle); + if (err) { + /* It's to be expected that most USB devices can't be opened + * so only report an error if it was explicitly selected + */ + if (id_filter) { + LOG_ERROR("could not open device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } else { + LOG_DEBUG("could not open device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } + continue; + } + + /* Match serial number */ + + bool serial_match = false; + char dev_serial[256] = {0}; + if (dev_desc.iSerialNumber > 0) { + err = libusb_get_string_descriptor_ascii( + dev_handle, dev_desc.iSerialNumber, + (uint8_t *)dev_serial, sizeof(dev_serial)); + + if (err < 0) { + const char *msg = "could not read serial number for device 0x%04x:0x%04x: %s"; + if (serial) + LOG_WARNING(msg, dev_desc.idVendor, dev_desc.idProduct, + libusb_strerror(err)); + else + LOG_DEBUG(msg, dev_desc.idVendor, dev_desc.idProduct, + libusb_strerror(err)); + } else if (serial && strncmp(dev_serial, serial, sizeof(dev_serial)) == 0) { + serial_match = true; + } + } + + if (serial && !serial_match) { + libusb_close(dev_handle); + continue; + } + + /* Find the CMSIS-DAP string in product string */ + + bool cmsis_dap_in_product_str = false; + char product_string[256] = {0}; + if (dev_desc.iProduct > 0) { + err = libusb_get_string_descriptor_ascii( + dev_handle, dev_desc.iProduct, + (uint8_t *)product_string, sizeof(product_string)); + if (err < 0) { + LOG_WARNING("could not read product string for device 0x%04x:0x%04x: %s", + dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + } else if (strstr(product_string, "CMSIS-DAP")) { + LOG_DEBUG("found product string of 0x%04x:0x%04x '%s'", + dev_desc.idVendor, dev_desc.idProduct, product_string); + cmsis_dap_in_product_str = true; + } + } + + bool device_identified_reliably = cmsis_dap_in_product_str + || serial_match || id_match; + + /* Find the CMSIS-DAP interface */ + + for (int config = 0; config < dev_desc.bNumConfigurations; config++) { + struct libusb_config_descriptor *config_desc; + err = libusb_get_config_descriptor(dev, config, &config_desc); + if (err) { + LOG_ERROR("could not get configuration descriptor %d for device 0x%04x:0x%04x: %s", + config, dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err)); + continue; + } + + LOG_DEBUG("enumerating interfaces of 0x%04x:0x%04x", + dev_desc.idVendor, dev_desc.idProduct); + int config_num = config_desc->bConfigurationValue; + const struct libusb_interface_descriptor *intf_desc_candidate = NULL; + const struct libusb_interface_descriptor *intf_desc_found = NULL; + + for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) { + const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0]; + int interface_num = intf_desc->bInterfaceNumber; + + /* Skip this interface if another one was requested explicitly */ + if (cmsis_dap_usb_interface != -1 && cmsis_dap_usb_interface != interface_num) + continue; + + /* CMSIS-DAP v2 spec says: + * + * CMSIS-DAP with default V2 configuration uses WinUSB and is therefore faster. + * Optionally support for streaming SWO trace is provided via an additional USB endpoint. + * + * The WinUSB configuration requires custom class support with the interface setting + * Class Code: 0xFF (Vendor specific) + * Subclass: 0x00 + * Protocol code: 0x00 + * + * Depending on the configuration it uses the following USB endpoints which should be configured + * in the interface descriptor in this order: + * - Endpoint 1: Bulk Out – used for commands received from host PC. + * - Endpoint 2: Bulk In – used for responses send to host PC. + * - Endpoint 3: Bulk In (optional) – used for streaming SWO trace (if enabled with SWO_STREAM). + */ + + /* Search for "CMSIS-DAP" in the interface string */ + bool cmsis_dap_in_interface_str = false; + if (intf_desc->iInterface != 0) { + + char interface_str[256] = {0}; + + err = libusb_get_string_descriptor_ascii( + dev_handle, intf_desc->iInterface, + (uint8_t *)interface_str, sizeof(interface_str)); + if (err < 0) { + LOG_DEBUG("could not read interface string %d for device 0x%04x:0x%04x: %s", + intf_desc->iInterface, + dev_desc.idVendor, dev_desc.idProduct, + libusb_strerror(err)); + } else if (strstr(interface_str, "CMSIS-DAP")) { + cmsis_dap_in_interface_str = true; + LOG_DEBUG("found interface %d string '%s'", + interface_num, interface_str); + } + } + + /* Bypass the following check if this interface was explicitly requested. */ + if (cmsis_dap_usb_interface == -1) { + if (!cmsis_dap_in_product_str && !cmsis_dap_in_interface_str) + continue; + } + + /* check endpoints */ + if (intf_desc->bNumEndpoints < 2) { + LOG_DEBUG("skipping interface %d, has only %d endpoints", + interface_num, intf_desc->bNumEndpoints); + continue; + } + + if ((intf_desc->endpoint[0].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK || + (intf_desc->endpoint[0].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_OUT) { + LOG_DEBUG("skipping interface %d, endpoint[0] is not bulk out", + interface_num); + continue; + } + + if ((intf_desc->endpoint[1].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK || + (intf_desc->endpoint[1].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_IN) { + LOG_DEBUG("skipping interface %d, endpoint[1] is not bulk in", + interface_num); + continue; + } + + /* We can rely on the interface is really CMSIS-DAP if + * - we've seen CMSIS-DAP in the interface string + * - config asked explicitly for an interface number + * - the device has only one interface + * The later two cases should be honored only if we know + * we are on the rigt device */ + bool intf_identified_reliably = cmsis_dap_in_interface_str + || (device_identified_reliably && + (cmsis_dap_usb_interface != -1 + || config_desc->bNumInterfaces == 1)); + + if (intf_desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC || + intf_desc->bInterfaceSubClass != 0 || intf_desc->bInterfaceProtocol != 0) { + /* If the interface is reliably identified + * then we need not insist on setting USB class, subclass and protocol + * exactly as the specification requires. + * At least KitProg3 uses class 0 contrary to the specification */ + if (intf_identified_reliably) { + LOG_WARNING("Using CMSIS-DAPv2 interface %d with wrong class %" PRId8 + " subclass %" PRId8 " or protocol %" PRId8, + interface_num, + intf_desc->bInterfaceClass, + intf_desc->bInterfaceSubClass, + intf_desc->bInterfaceProtocol); + } else { + LOG_DEBUG("skipping interface %d, class %" PRId8 + " subclass %" PRId8 " protocol %" PRId8, + interface_num, + intf_desc->bInterfaceClass, + intf_desc->bInterfaceSubClass, + intf_desc->bInterfaceProtocol); + continue; + + } + } + + if (intf_identified_reliably) { + /* That's the one! */ + intf_desc_found = intf_desc; + break; + } + + if (!intf_desc_candidate && device_identified_reliably) { + /* This interface looks suitable for CMSIS-DAP. Store the pointer to it + * and keep searching for another one with CMSIS-DAP in interface string */ + intf_desc_candidate = intf_desc; + } + } + + if (!intf_desc_found) { + /* We were not able to identify reliably which interface is CMSIS-DAP. + * Let's use the first suitable if we found one */ + intf_desc_found = intf_desc_candidate; + } + + if (!intf_desc_found) { + libusb_free_config_descriptor(config_desc); + continue; + } + + /* We've chosen an interface, connect to it */ + int interface_num = intf_desc_found->bInterfaceNumber; + int packet_size = intf_desc_found->endpoint[0].wMaxPacketSize; + int ep_out = intf_desc_found->endpoint[0].bEndpointAddress; + int ep_in = intf_desc_found->endpoint[1].bEndpointAddress; + + libusb_free_config_descriptor(config_desc); + libusb_free_device_list(device_list, true); + + LOG_INFO("Using CMSIS-DAPv2 interface with VID:PID=0x%04x:0x%04x, serial=%s", + dev_desc.idVendor, dev_desc.idProduct, dev_serial); + + int current_config; + err = libusb_get_configuration(dev_handle, ¤t_config); + if (err) { + LOG_ERROR("could not find current configuration: %s", libusb_strerror(err)); + libusb_close(dev_handle); + libusb_exit(ctx); + return ERROR_FAIL; + } + + if (config_num != current_config) { + err = libusb_set_configuration(dev_handle, config_num); + if (err) { + LOG_ERROR("could not set configuration: %s", libusb_strerror(err)); + libusb_close(dev_handle); + libusb_exit(ctx); + return ERROR_FAIL; + } + } + + err = libusb_claim_interface(dev_handle, interface_num); + if (err) + LOG_WARNING("could not claim interface: %s", libusb_strerror(err)); + + dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data)); + if (dap->bdata == NULL) { + LOG_ERROR("unable to allocate memory"); + libusb_release_interface(dev_handle, interface_num); + libusb_close(dev_handle); + libusb_exit(ctx); + return ERROR_FAIL; + } + + dap->packet_size = packet_size + 1; /* "+ 1" for compatibility with the HID backend */ + dap->bdata->usb_ctx = ctx; + dap->bdata->dev_handle = dev_handle; + dap->bdata->ep_out = ep_out; + dap->bdata->ep_in = ep_in; + dap->bdata->interface = interface_num; + return ERROR_OK; + } + + libusb_close(dev_handle); + } + + libusb_free_device_list(device_list, true); + + libusb_exit(ctx); + return ERROR_FAIL; +} + +static void cmsis_dap_usb_close(struct cmsis_dap *dap) +{ + libusb_release_interface(dap->bdata->dev_handle, dap->bdata->interface); + libusb_close(dap->bdata->dev_handle); + libusb_exit(dap->bdata->usb_ctx); + free(dap->bdata); + dap->bdata = NULL; +} + +static int cmsis_dap_usb_read(struct cmsis_dap *dap, int timeout_ms) +{ + int transferred = 0; + int err; + + err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_in, + dap->packet_buffer, dap->packet_size, &transferred, timeout_ms); + if (err) { + if (err == LIBUSB_ERROR_TIMEOUT) { + return ERROR_TIMEOUT_REACHED; + } else { + LOG_ERROR("error reading data: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + } + + memset(&dap->packet_buffer[transferred], 0, dap->packet_size - transferred); + + return transferred; +} + +static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen, int timeout_ms) +{ + int transferred = 0; + int err; + + /* skip the first byte that is only used by the HID backend */ + err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_out, + dap->packet_buffer + 1, txlen - 1, &transferred, timeout_ms); + if (err) { + if (err == LIBUSB_ERROR_TIMEOUT) { + return ERROR_TIMEOUT_REACHED; + } else { + LOG_ERROR("error writing data: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + } + + return transferred; +} + +COMMAND_HANDLER(cmsis_dap_handle_usb_interface_command) +{ + if (CMD_ARGC == 1) + cmsis_dap_usb_interface = strtoul(CMD_ARGV[0], NULL, 10); + else + LOG_ERROR("expected exactly one argument to cmsis_dap_usb_interface <interface_number>"); + + return ERROR_OK; +} + +const struct command_registration cmsis_dap_usb_subcommand_handlers[] = { + { + .name = "interface", + .handler = &cmsis_dap_handle_usb_interface_command, + .mode = COMMAND_CONFIG, + .help = "set the USB interface number to use (for USB bulk backend only)", + .usage = "<interface_number>", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct cmsis_dap_backend cmsis_dap_usb_backend = { + .name = "usb_bulk", + .open = cmsis_dap_usb_open, + .close = cmsis_dap_usb_close, + .read = cmsis_dap_usb_read, + .write = cmsis_dap_usb_write, +}; diff --git a/src/jtag/drivers/cmsis_dap_usb_hid.c b/src/jtag/drivers/cmsis_dap_usb_hid.c new file mode 100644 index 0000000..681aef1 --- /dev/null +++ b/src/jtag/drivers/cmsis_dap_usb_hid.c @@ -0,0 +1,208 @@ +/*************************************************************************** + * Copyright (C) 2018 by Mickaël Thomas * + * mickael9@gmail.com * + * * + * Copyright (C) 2016 by Maksym Hilliaka * + * oter@frozen-team.com * + * * + * Copyright (C) 2016 by Phillip Pearson * + * pp@myelin.co.nz * + * * + * Copyright (C) 2014 by Paul Fertser * + * fercerpav@gmail.com * + * * + * Copyright (C) 2013 by mike brown * + * mike@theshedworks.org.uk * + * * + * Copyright (C) 2013 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <hidapi.h> +#include <helper/log.h> + +#include "cmsis_dap.h" + +#define PACKET_SIZE (64 + 1) /* 64 bytes plus report id */ + +struct cmsis_dap_backend_data { + hid_device *dev_handle; +}; + +static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial) +{ + hid_device *dev = NULL; + int i; + struct hid_device_info *devs, *cur_dev; + unsigned short target_vid, target_pid; + + target_vid = 0; + target_pid = 0; + + if (hid_init() != 0) { + LOG_ERROR("unable to open HIDAPI"); + return ERROR_FAIL; + } + + /* + * The CMSIS-DAP specification stipulates: + * "The Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the + * debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer." + */ + devs = hid_enumerate(0x0, 0x0); + cur_dev = devs; + while (NULL != cur_dev) { + bool found = false; + + if (0 == vids[0]) { + if (NULL == cur_dev->product_string) { + LOG_DEBUG("Cannot read product string of device 0x%x:0x%x", + cur_dev->vendor_id, cur_dev->product_id); + } else if (wcsstr(cur_dev->product_string, L"CMSIS-DAP")) { + /* if the user hasn't specified VID:PID *and* + * product string contains "CMSIS-DAP", pick it + */ + found = true; + } + } else { + /* otherwise, exhaustively compare against all VID:PID in list */ + for (i = 0; vids[i] || pids[i]; i++) { + if ((vids[i] == cur_dev->vendor_id) && (pids[i] == cur_dev->product_id)) + found = true; + } + } + + /* LPC-LINK2 has cmsis-dap on interface 0 and other HID functions on other interfaces */ + if (cur_dev->vendor_id == 0x1fc9 && cur_dev->product_id == 0x0090 && cur_dev->interface_number != 0) + found = false; + + if (found) { + /* check serial number matches if given */ + if (serial == NULL) + break; + + if (cur_dev->serial_number != NULL) { + size_t len = (strlen(serial) + 1) * sizeof(wchar_t); + wchar_t *wserial = malloc(len); + mbstowcs(wserial, serial, len); + + if (wcscmp(wserial, cur_dev->serial_number) == 0) { + free(wserial); + break; + } else { + free(wserial); + wserial = NULL; + } + } + } + + cur_dev = cur_dev->next; + } + + if (NULL != cur_dev) { + target_vid = cur_dev->vendor_id; + target_pid = cur_dev->product_id; + } + + if (target_vid == 0 && target_pid == 0) { + hid_free_enumeration(devs); + return ERROR_FAIL; + } + + dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data)); + if (dap->bdata == NULL) { + LOG_ERROR("unable to allocate memory"); + return ERROR_FAIL; + } + + dev = hid_open_path(cur_dev->path); + hid_free_enumeration(devs); + + if (dev == NULL) { + LOG_ERROR("unable to open CMSIS-DAP device 0x%x:0x%x", target_vid, target_pid); + return ERROR_FAIL; + } + + /* allocate default packet buffer, may be changed later. + * currently with HIDAPI we have no way of getting the output report length + * without this info we cannot communicate with the adapter. + * For the moment we have to hard code the packet size */ + + dap->packet_size = PACKET_SIZE; + + /* atmel cmsis-dap uses 512 byte reports */ + /* except when it doesn't e.g. with mEDBG on SAMD10 Xplained + * board */ + /* TODO: HID report descriptor should be parsed instead of + * hardcoding a match by VID */ + if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175) + dap->packet_size = 512 + 1; + + dap->bdata->dev_handle = dev; + + return ERROR_OK; +} + +static void cmsis_dap_hid_close(struct cmsis_dap *dap) +{ + hid_close(dap->bdata->dev_handle); + hid_exit(); + free(dap->bdata); + dap->bdata = NULL; +} + +static int cmsis_dap_hid_read(struct cmsis_dap *dap, int timeout_ms) +{ + int retval = hid_read_timeout(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_size, timeout_ms); + + if (retval == 0) { + return ERROR_TIMEOUT_REACHED; + } else if (retval == -1) { + LOG_ERROR("error reading data: %ls", hid_error(dap->bdata->dev_handle)); + return ERROR_FAIL; + } + + return retval; +} + +static int cmsis_dap_hid_write(struct cmsis_dap *dap, int txlen, int timeout_ms) +{ + (void) timeout_ms; + + /* Pad the rest of the TX buffer with 0's */ + memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen); + + /* write data to device */ + int retval = hid_write(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_size); + if (retval == -1) { + LOG_ERROR("error writing data: %ls", hid_error(dap->bdata->dev_handle)); + return ERROR_FAIL; + } + + return retval; +} + +const struct cmsis_dap_backend cmsis_dap_hid_backend = { + .name = "hid", + .open = cmsis_dap_hid_open, + .close = cmsis_dap_hid_close, + .read = cmsis_dap_hid_read, + .write = cmsis_dap_hid_write, +}; diff --git a/src/jtag/drivers/ft232r.c b/src/jtag/drivers/ft232r.c index d97db56..a63480c 100644 --- a/src/jtag/drivers/ft232r.c +++ b/src/jtag/drivers/ft232r.c @@ -170,7 +170,7 @@ static int ft232r_send_recv(void) return ERROR_OK; } -void ft232r_increase_buf_size(size_t new_buf_size) +static void ft232r_increase_buf_size(size_t new_buf_size) { uint8_t *new_buf_ptr; if (new_buf_size >= ft232r_buf_size) { diff --git a/src/jtag/drivers/gw16012.c b/src/jtag/drivers/gw16012.c index ef4b5d9..db0a677 100644 --- a/src/jtag/drivers/gw16012.c +++ b/src/jtag/drivers/gw16012.c @@ -63,7 +63,7 @@ #endif /* configuration */ -uint16_t gw16012_port; +static uint16_t gw16012_port; /* interface variables */ diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index ae8ce49..b915707 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -1270,7 +1270,7 @@ static bool calculate_swo_prescaler(unsigned int traceclkin_freq, uint32_t trace_freq, uint16_t *prescaler) { unsigned int presc = (traceclkin_freq + trace_freq / 2) / trace_freq; - if (presc > TPIU_ACPR_MAX_SWOSCALER) + if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1) return false; /* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */ @@ -1296,7 +1296,7 @@ static bool detect_swo_freq_and_prescaler(struct jaylink_swo_speed speed, *trace_freq = speed.freq / divider; presc = ((1.0 - SWO_MAX_FREQ_DEV) * traceclkin_freq) / *trace_freq + 1; - if (presc > TPIU_ACPR_MAX_SWOSCALER) + if (presc > TPIU_ACPR_MAX_SWOSCALER + 1) break; deviation = fabs(1.0 - ((double)*trace_freq * presc / traceclkin_freq)); diff --git a/src/jtag/drivers/libftdi_helper.h b/src/jtag/drivers/libftdi_helper.h new file mode 100644 index 0000000..e187b57 --- /dev/null +++ b/src/jtag/drivers/libftdi_helper.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_JTAG_DRIVERS_LIBFTDI_HELPER_H +#define OPENOCD_JTAG_DRIVERS_LIBFTDI_HELPER_H + +#include <ftdi.h> + +#ifndef HAVE_LIBFTDI_TCIOFLUSH +/* Backward compatibility with libftdi pre 1.5 */ + +static inline int ftdi_tciflush(struct ftdi_context *ftdi) +{ + return ftdi_usb_purge_rx_buffer(ftdi); +} + +static inline int ftdi_tcoflush(struct ftdi_context *ftdi) +{ + return ftdi_usb_purge_tx_buffer(ftdi); +} + +static inline int ftdi_tcioflush(struct ftdi_context *ftdi) +{ + return ftdi_usb_purge_buffers(ftdi); +} +#endif + +#endif /* OPENOCD_JTAG_DRIVERS_LIBFTDI_HELPER_H */ diff --git a/src/jtag/drivers/nulink_usb.c b/src/jtag/drivers/nulink_usb.c index 5fdbed3..00738ee 100644 --- a/src/jtag/drivers/nulink_usb.c +++ b/src/jtag/drivers/nulink_usb.c @@ -411,7 +411,7 @@ static int nulink_usb_step(void *handle) return res; } -static int nulink_usb_read_reg(void *handle, int num, uint32_t *val) +static int nulink_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val) { struct nulink_usb_handle_s *h = handle; @@ -434,7 +434,7 @@ static int nulink_usb_read_reg(void *handle, int num, uint32_t *val) h->cmdbuf[h->cmdidx] = 0; h->cmdidx += 1; /* u32Addr */ - h_u32_to_le(h->cmdbuf + h->cmdidx, num); + h_u32_to_le(h->cmdbuf + h->cmdidx, regsel); h->cmdidx += 4; /* u32Data */ h_u32_to_le(h->cmdbuf + h->cmdidx, 0); @@ -450,7 +450,7 @@ static int nulink_usb_read_reg(void *handle, int num, uint32_t *val) return res; } -static int nulink_usb_write_reg(void *handle, int num, uint32_t val) +static int nulink_usb_write_reg(void *handle, unsigned int regsel, uint32_t val) { struct nulink_usb_handle_s *h = handle; @@ -473,7 +473,7 @@ static int nulink_usb_write_reg(void *handle, int num, uint32_t val) h->cmdbuf[h->cmdidx] = 0; h->cmdidx += 1; /* u32Addr */ - h_u32_to_le(h->cmdbuf + h->cmdidx, num); + h_u32_to_le(h->cmdbuf + h->cmdidx, regsel); h->cmdidx += 4; /* u32Data */ h_u32_to_le(h->cmdbuf + h->cmdidx, val); diff --git a/src/jtag/drivers/opendous.c b/src/jtag/drivers/opendous.c index f0e4f56..82fcbc1 100644 --- a/src/jtag/drivers/opendous.c +++ b/src/jtag/drivers/opendous.c @@ -144,7 +144,7 @@ static int opendous_usb_write(struct opendous_jtag *opendous_jtag, int out_lengt static int opendous_usb_read(struct opendous_jtag *opendous_jtag); /* helper functions */ -int opendous_get_version_info(void); +static int opendous_get_version_info(void); #ifdef _DEBUG_USB_COMMS_ static void opendous_debug_buffer(uint8_t *buffer, int length); @@ -544,7 +544,7 @@ int opendous_get_status(void) return ERROR_OK; } -int opendous_get_version_info(void) +static int opendous_get_version_info(void) { return ERROR_OK; } diff --git a/src/jtag/drivers/openjtag.c b/src/jtag/drivers/openjtag.c index 2cf5751..6940c88 100644 --- a/src/jtag/drivers/openjtag.c +++ b/src/jtag/drivers/openjtag.c @@ -82,7 +82,7 @@ typedef enum openjtag_tap_state { } openjtag_tap_state_t; /* OPENJTAG access library includes */ -#include <ftdi.h> +#include "libftdi_helper.h" /* OpenJTAG vid/pid */ static uint16_t openjtag_vid = 0x0403; @@ -436,8 +436,8 @@ static int openjtag_init_standard(void) return ERROR_JTAG_DEVICE_ERROR; } - if (ftdi_usb_purge_buffers(&ftdic) < 0) { - LOG_ERROR("ftdi_purge_buffers: %s", ftdic.error_str); + if (ftdi_tcioflush(&ftdic) < 0) { + LOG_ERROR("ftdi flush: %s", ftdic.error_str); return ERROR_JTAG_INIT_FAILED; } diff --git a/src/jtag/drivers/presto.c b/src/jtag/drivers/presto.c index 6c3a187..43d669e 100644 --- a/src/jtag/drivers/presto.c +++ b/src/jtag/drivers/presto.c @@ -34,7 +34,7 @@ #include "bitq.h" /* PRESTO access library includes */ -#include <ftdi.h> +#include "libftdi_helper.h" /* -------------------------------------------------------------------------- */ @@ -160,8 +160,8 @@ static int presto_open_libftdi(char *req_serial) return ERROR_JTAG_DEVICE_ERROR; } - if (ftdi_usb_purge_buffers(&presto->ftdic) < 0) { - LOG_ERROR("unable to purge PRESTO buffers"); + if (ftdi_tcioflush(&presto->ftdic) < 0) { + LOG_ERROR("unable to flush PRESTO buffers"); return ERROR_JTAG_DEVICE_ERROR; } @@ -174,7 +174,7 @@ static int presto_open_libftdi(char *req_serial) if (presto_read(&presto_data, 1) != ERROR_OK) { LOG_DEBUG("no response from PRESTO, retrying"); - if (ftdi_usb_purge_buffers(&presto->ftdic) < 0) + if (ftdi_tcioflush(&presto->ftdic) < 0) return ERROR_JTAG_DEVICE_ERROR; presto_data = 0xD0; diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index ce55b94..4545bcb 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -299,6 +299,7 @@ struct stlink_usb_handle_s { #define STLINK_TRACE_SIZE 4096 #define STLINK_TRACE_MAX_HZ 2000000 +#define STLINK_V3_TRACE_MAX_HZ 24000000 #define STLINK_V3_MAX_FREQ_NB 10 @@ -323,6 +324,9 @@ struct stlink_usb_handle_s { /* aliases */ #define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE +#define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2 + +#define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F) struct speed_map { int speed; @@ -358,7 +362,7 @@ static const struct speed_map stlink_khz_to_speed_map_jtag[] = { static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size); static int stlink_swim_status(void *handle); -void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size); +static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size); static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map); static int stlink_speed(void *handle, int khz, bool query); static int stlink_usb_open_ap(void *handle, unsigned short apsel); @@ -2015,13 +2019,22 @@ static int stlink_usb_read_regs(void *handle) } /** */ -static int stlink_usb_read_reg(void *handle, int num, uint32_t *val) +static int stlink_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val) { int res; struct stlink_usb_handle_s *h = handle; assert(handle != NULL); + if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) { + res = stlink_usb_write_debug_reg(h, DCB_DCRSR, regsel & 0x7f); + if (res != ERROR_OK) + return res; + + /* FIXME: poll DHCSR.S_REGRDY before read DCRDR */ + return stlink_usb_v2_read_debug_reg(h, DCB_DCRDR, val); + } + stlink_usb_init_buffer(handle, h->rx_ep, h->version.jtag_api == STLINK_JTAG_API_V1 ? 4 : 8); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; @@ -2029,7 +2042,7 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READREG; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG; - h->cmdbuf[h->cmdidx++] = num; + h->cmdbuf[h->cmdidx++] = regsel; if (h->version.jtag_api == STLINK_JTAG_API_V1) { res = stlink_usb_xfer_noerrcheck(handle, h->databuf, 4); @@ -2047,12 +2060,21 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val) } /** */ -static int stlink_usb_write_reg(void *handle, int num, uint32_t val) +static int stlink_usb_write_reg(void *handle, unsigned int regsel, uint32_t val) { struct stlink_usb_handle_s *h = handle; assert(handle != NULL); + if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) { + int res = stlink_usb_write_debug_reg(h, DCB_DCRDR, val); + if (res != ERROR_OK) + return res; + + return stlink_usb_write_debug_reg(h, DCB_DCRSR, DCRSR_WnR | (regsel & 0x7f)); + /* FIXME: poll DHCSR.S_REGRDY after write DCRSR */ + } + stlink_usb_init_buffer(handle, h->rx_ep, 2); h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; @@ -2060,7 +2082,7 @@ static int stlink_usb_write_reg(void *handle, int num, uint32_t val) h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEREG; else h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEREG; - h->cmdbuf[h->cmdidx++] = num; + h->cmdbuf[h->cmdidx++] = regsel; h_u32_to_le(h->cmdbuf+h->cmdidx, val); h->cmdidx += 4; @@ -2577,7 +2599,7 @@ static int stlink_speed_jtag(void *handle, int khz, bool query) return stlink_khz_to_speed_map_jtag[speed_index].speed; } -void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size) +static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size) { unsigned int i; @@ -2725,7 +2747,7 @@ static int stlink_usb_close(void *handle) * based on the length (0x1a = 26) we could easily decide if we have to fixup the serial * and then we have just to convert the raw data into printable characters using sprintf */ -char *stlink_usb_get_alternate_serial(libusb_device_handle *device, +static char *stlink_usb_get_alternate_serial(libusb_device_handle *device, struct libusb_device_descriptor *dev_desc) { int usb_retval; @@ -2982,13 +3004,12 @@ static int stlink_usb_hl_open(struct hl_interface_param_s *param, void **fd) return stlink_usb_open(param, stlink_get_mode(param->transport), fd); } -int stlink_config_trace(void *handle, bool enabled, +static int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq, unsigned int traceclkin_freq, uint16_t *prescaler) { struct stlink_usb_handle_s *h = handle; - uint16_t presc; if (enabled && (!(h->version.flags & STLINK_F_HAS_TRACE) || pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) { @@ -2996,34 +3017,42 @@ int stlink_config_trace(void *handle, bool enabled, return ERROR_FAIL; } - if (!enabled) { - stlink_usb_trace_disable(h); - return ERROR_OK; - } + unsigned int max_trace_freq = (h->version.stlink == 3) ? + STLINK_V3_TRACE_MAX_HZ : STLINK_TRACE_MAX_HZ; - if (*trace_freq > STLINK_TRACE_MAX_HZ) { + /* Only concern ourselves with the frequency if the STlink is processing it. */ + if (enabled && *trace_freq > max_trace_freq) { LOG_ERROR("ST-LINK doesn't support SWO frequency higher than %u", - STLINK_TRACE_MAX_HZ); + max_trace_freq); return ERROR_FAIL; } stlink_usb_trace_disable(h); if (!*trace_freq) - *trace_freq = STLINK_TRACE_MAX_HZ; + *trace_freq = max_trace_freq; - presc = traceclkin_freq / *trace_freq; - - if (traceclkin_freq % *trace_freq > 0) - presc++; + unsigned int presc = (traceclkin_freq + *trace_freq / 2) / *trace_freq; + if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1) { + LOG_ERROR("SWO frequency is not suitable. Please choose a different " + "frequency."); + return ERROR_FAIL; + } - if (presc > TPIU_ACPR_MAX_SWOSCALER) { + /* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */ + unsigned int max_deviation = (traceclkin_freq * 3) / 100; + if (presc * *trace_freq < traceclkin_freq - max_deviation || + presc * *trace_freq > traceclkin_freq + max_deviation) { LOG_ERROR("SWO frequency is not suitable. Please choose a different " "frequency."); return ERROR_FAIL; } *prescaler = presc; + + if (!enabled) + return ERROR_OK; + h->trace.source_hz = *trace_freq; return stlink_usb_trace_enable(h); diff --git a/src/jtag/drivers/ti_icdi_usb.c b/src/jtag/drivers/ti_icdi_usb.c index d276e58..00b2f96 100644 --- a/src/jtag/drivers/ti_icdi_usb.c +++ b/src/jtag/drivers/ti_icdi_usb.c @@ -31,7 +31,7 @@ #include <target/cortex_m.h> -#include <libusb.h> +#include "libusb_helper.h" #define ICDI_WRITE_ENDPOINT 0x02 #define ICDI_READ_ENDPOINT 0x83 @@ -44,8 +44,7 @@ #define PACKET_END "#" struct icdi_usb_handle_s { - libusb_context *usb_ctx; - libusb_device_handle *usb_dev; + struct libusb_device_handle *usb_dev; char *read_buffer; char *write_buffer; @@ -476,13 +475,13 @@ static int icdi_usb_read_regs(void *handle) return ERROR_OK; } -static int icdi_usb_read_reg(void *handle, int num, uint32_t *val) +static int icdi_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val) { int result; struct icdi_usb_handle_s *h = handle; char cmd[10]; - snprintf(cmd, sizeof(cmd), "p%x", num); + snprintf(cmd, sizeof(cmd), "p%x", regsel); result = icdi_send_cmd(handle, cmd); if (result != ERROR_OK) return result; @@ -505,14 +504,14 @@ static int icdi_usb_read_reg(void *handle, int num, uint32_t *val) return result; } -static int icdi_usb_write_reg(void *handle, int num, uint32_t val) +static int icdi_usb_write_reg(void *handle, unsigned int regsel, uint32_t val) { int result; char cmd[20]; uint8_t buf[4]; h_u32_to_le(buf, val); - int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", num); + int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", regsel); hexify(cmd + cmd_len, buf, 4, sizeof(cmd)); result = icdi_send_cmd(handle, cmd); @@ -657,10 +656,7 @@ static int icdi_usb_close(void *handle) return ERROR_OK; if (h->usb_dev) - libusb_close(h->usb_dev); - - if (h->usb_ctx) - libusb_exit(h->usb_ctx); + jtag_libusb_close(h->usb_dev); free(h->read_buffer); free(h->write_buffer); @@ -670,6 +666,7 @@ static int icdi_usb_close(void *handle) static int icdi_usb_open(struct hl_interface_param_s *param, void **fd) { + /* TODO: Convert remaining libusb_ calls to jtag_libusb_ */ int retval; struct icdi_usb_handle_s *h; @@ -682,19 +679,14 @@ static int icdi_usb_open(struct hl_interface_param_s *param, void **fd) return ERROR_FAIL; } - LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport, - param->vid[0], param->pid[0]); - - /* TODO: convert libusb_ calls to jtag_libusb_ */ - if (param->vid[1]) - LOG_WARNING("Bad configuration: 'hla_vid_pid' command does not accept more than one VID PID pair on ti-icdi!"); + for (uint8_t i = 0; param->vid[i] && param->pid[i]; ++i) + LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", param->transport, + param->vid[i], param->pid[i], param->serial ? param->serial : ""); - if (libusb_init(&h->usb_ctx) != 0) { - LOG_ERROR("libusb init failed"); - goto error_open; - } + /* TI (Stellaris) ICDI provides its serial number in the USB descriptor; + no need to provide a callback here. */ + jtag_libusb_open(param->vid, param->pid, param->serial, &h->usb_dev, NULL); - h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid[0], param->pid[0]); if (!h->usb_dev) { LOG_ERROR("open failed"); goto error_open; diff --git a/src/jtag/drivers/ulink.c b/src/jtag/drivers/ulink.c index 68249dc..f473ce3 100644 --- a/src/jtag/drivers/ulink.c +++ b/src/jtag/drivers/ulink.c @@ -25,6 +25,7 @@ #include <jtag/commands.h> #include <target/image.h> #include <libusb.h> +#include "libusb_helper.h" #include "OpenULINK/include/msgtypes.h" /** USB Vendor ID of ULINK device in unconfigured state (no firmware loaded @@ -148,6 +149,9 @@ struct ulink { struct libusb_device_handle *usb_device_handle; enum ulink_type type; + unsigned int ep_in; /**< IN endpoint number */ + unsigned int ep_out; /**< OUT endpoint number */ + int delay_scan_in; /**< Delay value for SCAN_IN commands */ int delay_scan_out; /**< Delay value for SCAN_OUT commands */ int delay_scan_io; /**< Delay value for SCAN_IO commands */ @@ -162,34 +166,34 @@ struct ulink { /**************************** Function Prototypes *****************************/ /* USB helper functions */ -int ulink_usb_open(struct ulink **device); -int ulink_usb_close(struct ulink **device); +static int ulink_usb_open(struct ulink **device); +static int ulink_usb_close(struct ulink **device); /* ULINK MCU (Cypress EZ-USB) specific functions */ -int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit); -int ulink_load_firmware_and_renumerate(struct ulink **device, const char *filename, +static int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit); +static int ulink_load_firmware_and_renumerate(struct ulink **device, const char *filename, uint32_t delay); -int ulink_load_firmware(struct ulink *device, const char *filename); -int ulink_write_firmware_section(struct ulink *device, +static int ulink_load_firmware(struct ulink *device, const char *filename); +static int ulink_write_firmware_section(struct ulink *device, struct image *firmware_image, int section_index); /* Generic helper functions */ -void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals); +static void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals); /* OpenULINK command generation helper functions */ -int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size, +static int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size, enum ulink_payload_direction direction); /* OpenULINK command queue helper functions */ -int ulink_get_queue_size(struct ulink *device, +static int ulink_get_queue_size(struct ulink *device, enum ulink_payload_direction direction); -void ulink_clear_queue(struct ulink *device); -int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd); -int ulink_execute_queued_commands(struct ulink *device, int timeout); +static void ulink_clear_queue(struct ulink *device); +static int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd); +static int ulink_execute_queued_commands(struct ulink *device, int timeout); static void ulink_print_queue(struct ulink *device); -int ulink_append_scan_cmd(struct ulink *device, +static int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type, int scan_size_bits, uint8_t *tdi, @@ -201,39 +205,39 @@ int ulink_append_scan_cmd(struct ulink *device, uint8_t tms_sequence_end, struct jtag_command *origin, bool postprocess); -int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count, +static int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count, uint8_t sequence); -int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count); -int ulink_append_get_signals_cmd(struct ulink *device); -int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low, +static int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count); +static int ulink_append_get_signals_cmd(struct ulink *device); +static int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low, uint8_t high); -int ulink_append_sleep_cmd(struct ulink *device, uint32_t us); -int ulink_append_configure_tck_cmd(struct ulink *device, +static int ulink_append_sleep_cmd(struct ulink *device, uint32_t us); +static int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in, int delay_scan_out, int delay_scan_io, int delay_tck, int delay_tms); -int ulink_append_led_cmd(struct ulink *device, uint8_t led_state); -int ulink_append_test_cmd(struct ulink *device); +static int __attribute__((unused)) ulink_append_led_cmd(struct ulink *device, uint8_t led_state); +static int ulink_append_test_cmd(struct ulink *device); /* OpenULINK TCK frequency helper functions */ -int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay); +static int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay); /* Interface between OpenULINK and OpenOCD */ static void ulink_set_end_state(tap_state_t endstate); -int ulink_queue_statemove(struct ulink *device); +static int ulink_queue_statemove(struct ulink *device); -int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd); -int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd); -int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd); -int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd); -int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd); -int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd); -int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd); +static int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd); -int ulink_post_process_scan(struct ulink_cmd *ulink_cmd); -int ulink_post_process_queue(struct ulink *device); +static int ulink_post_process_scan(struct ulink_cmd *ulink_cmd); +static int ulink_post_process_queue(struct ulink *device); /* adapter driver functions */ static int ulink_execute_queue(void); @@ -245,12 +249,12 @@ static int ulink_quit(void); /****************************** Global Variables ******************************/ -struct ulink *ulink_handle; +static struct ulink *ulink_handle; /**************************** USB helper functions ****************************/ /** - * Opens the ULINK device and claims its USB interface. + * Opens the ULINK device * * Currently, only the original ULINK is supported * @@ -258,7 +262,7 @@ struct ulink *ulink_handle; * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_usb_open(struct ulink **device) +static int ulink_usb_open(struct ulink **device) { ssize_t num_devices, i; bool found; @@ -288,9 +292,6 @@ int ulink_usb_open(struct ulink **device) return ERROR_FAIL; libusb_free_device_list(usb_devices, 1); - if (libusb_claim_interface(usb_device_handle, 0) != 0) - return ERROR_FAIL; - (*device)->usb_device_handle = usb_device_handle; (*device)->type = ULINK_1; @@ -304,7 +305,7 @@ int ulink_usb_open(struct ulink **device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_usb_close(struct ulink **device) +static int ulink_usb_close(struct ulink **device) { if (libusb_release_interface((*device)->usb_device_handle, 0) != 0) return ERROR_FAIL; @@ -327,7 +328,7 @@ int ulink_usb_close(struct ulink **device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit) +static int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit) { int ret; @@ -354,7 +355,7 @@ int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_load_firmware_and_renumerate(struct ulink **device, +static int ulink_load_firmware_and_renumerate(struct ulink **device, const char *filename, uint32_t delay) { int ret; @@ -390,10 +391,10 @@ int ulink_load_firmware_and_renumerate(struct ulink **device, * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_load_firmware(struct ulink *device, const char *filename) +static int ulink_load_firmware(struct ulink *device, const char *filename) { struct image ulink_firmware_image; - int ret, i; + int ret; ret = ulink_cpu_reset(device, CPU_RESET); if (ret != ERROR_OK) { @@ -402,7 +403,7 @@ int ulink_load_firmware(struct ulink *device, const char *filename) } ulink_firmware_image.base_address = 0; - ulink_firmware_image.base_address_set = 0; + ulink_firmware_image.base_address_set = false; ret = image_open(&ulink_firmware_image, filename, "ihex"); if (ret != ERROR_OK) { @@ -411,7 +412,7 @@ int ulink_load_firmware(struct ulink *device, const char *filename) } /* Download all sections in the image to ULINK */ - for (i = 0; i < ulink_firmware_image.num_sections; i++) { + for (unsigned int i = 0; i < ulink_firmware_image.num_sections; i++) { ret = ulink_write_firmware_section(device, &ulink_firmware_image, i); if (ret != ERROR_OK) return ret; @@ -439,7 +440,7 @@ int ulink_load_firmware(struct ulink *device, const char *filename) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_write_firmware_section(struct ulink *device, +static int ulink_write_firmware_section(struct ulink *device, struct image *firmware_image, int section_index) { uint16_t addr, size, bytes_remaining, chunk_size; @@ -499,7 +500,7 @@ int ulink_write_firmware_section(struct ulink *device, * @param input_signals input signal states as returned by CMD_GET_SIGNALS * @param output_signals output signal states as returned by CMD_GET_SIGNALS */ -void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals) +static void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals) { LOG_INFO("ULINK signal states: TDI: %i, TDO: %i, TMS: %i, TCK: %i, TRST: %i," " SRST: %i", @@ -522,7 +523,7 @@ void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size, +static int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size, enum ulink_payload_direction direction) { uint8_t *payload; @@ -576,7 +577,7 @@ int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size, * @return the number of bytes currently stored in the queue for the specified * direction. */ -int ulink_get_queue_size(struct ulink *device, +static int ulink_get_queue_size(struct ulink *device, enum ulink_payload_direction direction) { struct ulink_cmd *current = device->queue_start; @@ -605,7 +606,7 @@ int ulink_get_queue_size(struct ulink *device, * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -void ulink_clear_queue(struct ulink *device) +static void ulink_clear_queue(struct ulink *device) { struct ulink_cmd *current = device->queue_start; struct ulink_cmd *next = NULL; @@ -647,7 +648,7 @@ void ulink_clear_queue(struct ulink *device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd) +static int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd) { int newsize_out, newsize_in; int ret = ERROR_OK; @@ -698,7 +699,7 @@ int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_execute_queued_commands(struct ulink *device, int timeout) +static int ulink_execute_queued_commands(struct ulink *device, int timeout) { struct ulink_cmd *current; int ret, i, index_out, index_in, count_out, count_in, transferred; @@ -725,7 +726,7 @@ int ulink_execute_queued_commands(struct ulink *device, int timeout) } /* Send packet to ULINK */ - ret = libusb_bulk_transfer(device->usb_device_handle, (2 | LIBUSB_ENDPOINT_OUT), + ret = libusb_bulk_transfer(device->usb_device_handle, device->ep_out, (unsigned char *)buffer, count_out, &transferred, timeout); if (ret != 0) return ERROR_FAIL; @@ -734,7 +735,7 @@ int ulink_execute_queued_commands(struct ulink *device, int timeout) /* Wait for response if commands contain IN payload data */ if (count_in > 0) { - ret = libusb_bulk_transfer(device->usb_device_handle, (2 | LIBUSB_ENDPOINT_IN), + ret = libusb_bulk_transfer(device->usb_device_handle, device->ep_in, (unsigned char *)buffer, 64, &transferred, timeout); if (ret != 0) return ERROR_FAIL; @@ -865,7 +866,7 @@ static void ulink_print_queue(struct ulink *device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type, +static int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type, int scan_size_bits, uint8_t *tdi, uint8_t *tdo_start, uint8_t *tdo, uint8_t tms_count_start, uint8_t tms_sequence_start, uint8_t tms_count_end, uint8_t tms_sequence_end, struct jtag_command *origin, bool postprocess) @@ -966,7 +967,7 @@ int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type, * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count, +static int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count, uint8_t sequence) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); @@ -1003,7 +1004,7 @@ int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count, * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count) +static int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); int ret; @@ -1036,7 +1037,7 @@ int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_get_signals_cmd(struct ulink *device) +static int ulink_append_get_signals_cmd(struct ulink *device) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); int ret; @@ -1075,7 +1076,7 @@ int ulink_append_get_signals_cmd(struct ulink *device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low, +static int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low, uint8_t high) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); @@ -1108,7 +1109,7 @@ int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low, * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_sleep_cmd(struct ulink *device, uint32_t us) +static int ulink_append_sleep_cmd(struct ulink *device, uint32_t us) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); int ret; @@ -1144,7 +1145,7 @@ int ulink_append_sleep_cmd(struct ulink *device, uint32_t us) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in, +static int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in, int delay_scan_out, int delay_scan_io, int delay_tck, int delay_tms) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); @@ -1206,7 +1207,7 @@ int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in, * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_led_cmd(struct ulink *device, uint8_t led_state) +static int ulink_append_led_cmd(struct ulink *device, uint8_t led_state) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); int ret; @@ -1236,7 +1237,7 @@ int ulink_append_led_cmd(struct ulink *device, uint8_t led_state) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_append_test_cmd(struct ulink *device) +static int ulink_append_test_cmd(struct ulink *device) { struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd)); int ret; @@ -1292,7 +1293,7 @@ int ulink_append_test_cmd(struct ulink *device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay) +static int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay) { float t, x, x_ceil; @@ -1423,7 +1424,7 @@ static void ulink_set_end_state(tap_state_t endstate) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_statemove(struct ulink *device) +static int ulink_queue_statemove(struct ulink *device) { uint8_t tms_sequence, tms_count; int ret; @@ -1452,7 +1453,7 @@ int ulink_queue_statemove(struct ulink *device) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd) { uint32_t scan_size_bits, scan_size_bytes, bits_last_scan; uint32_t scans_max_payload, bytecount; @@ -1631,7 +1632,7 @@ int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd) { int ret; @@ -1654,7 +1655,7 @@ int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd) { int ret; @@ -1685,7 +1686,7 @@ int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd) { uint8_t low = 0, high = 0; @@ -1711,7 +1712,7 @@ int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd) { int ret, i, num_states, batch_size, state_count; tap_state_t *path; @@ -1770,7 +1771,7 @@ int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd) { /* IMPORTANT! Due to the time offset in command execution introduced by * command queueing, this needs to be implemented in the ULINK device */ @@ -1783,7 +1784,7 @@ int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd) * @param device pointer to struct ulink identifying ULINK driver instance. * @param cmd pointer to the command that shall be executed. */ -int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd) +static int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd) { int ret; unsigned num_cycles; @@ -1828,7 +1829,7 @@ int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_post_process_scan(struct ulink_cmd *ulink_cmd) +static int ulink_post_process_scan(struct ulink_cmd *ulink_cmd) { struct jtag_command *cmd = ulink_cmd->cmd_origin; int ret; @@ -1859,7 +1860,7 @@ int ulink_post_process_scan(struct ulink_cmd *ulink_cmd) * @return on success: ERROR_OK * @return on failure: ERROR_FAIL */ -int ulink_post_process_queue(struct ulink *device) +static int ulink_post_process_queue(struct ulink *device) { struct ulink_cmd *current; struct jtag_command *openocd_cmd; @@ -2156,6 +2157,12 @@ static int ulink_init(void) } else LOG_INFO("ULINK device is already running OpenULINK firmware"); + /* Get OpenULINK USB IN/OUT endpoints and claim the interface */ + ret = jtag_libusb_choose_interface(ulink_handle->usb_device_handle, + &ulink_handle->ep_in, &ulink_handle->ep_out, -1, -1, -1, -1); + if (ret != ERROR_OK) + return ret; + /* Initialize OpenULINK command queue */ ulink_clear_queue(ulink_handle); @@ -2171,7 +2178,7 @@ static int ulink_init(void) * shut down by the user via Ctrl-C. Try to retrieve this Bulk IN packet. */ dummy = calloc(64, sizeof(uint8_t)); - ret = libusb_bulk_transfer(ulink_handle->usb_device_handle, (2 | LIBUSB_ENDPOINT_IN), + ret = libusb_bulk_transfer(ulink_handle->usb_device_handle, ulink_handle->ep_in, dummy, 64, &transferred, 200); free(dummy); diff --git a/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c b/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c index f8ff66e..6f15fa7 100644 --- a/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c +++ b/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c @@ -134,7 +134,7 @@ static int load_usb_blaster_firmware(struct libusb_device_handle *libusb_dev, } ublast2_firmware_image.base_address = 0; - ublast2_firmware_image.base_address_set = 0; + ublast2_firmware_image.base_address_set = false; int ret = image_open(&ublast2_firmware_image, low->firmware_path, "ihex"); if (ret != ERROR_OK) { @@ -162,7 +162,7 @@ static int load_usb_blaster_firmware(struct libusb_device_handle *libusb_dev, 100); /* Download all sections in the image to ULINK */ - for (int i = 0; i < ublast2_firmware_image.num_sections; i++) { + for (unsigned int i = 0; i < ublast2_firmware_image.num_sections; i++) { ret = ublast2_write_firmware_section(libusb_dev, &ublast2_firmware_image, i); if (ret != ERROR_OK) { diff --git a/src/jtag/drivers/usb_blaster/usb_blaster.c b/src/jtag/drivers/usb_blaster/usb_blaster.c index 9648ba2..de3b5d5 100644 --- a/src/jtag/drivers/usb_blaster/usb_blaster.c +++ b/src/jtag/drivers/usb_blaster/usb_blaster.c @@ -275,7 +275,7 @@ static void ublast_queue_byte(uint8_t abyte) * * Returns pin value (1 means driven high, 0 mean driven low) */ -bool ublast_compute_pin(enum gpio_steer steer) +static bool ublast_compute_pin(enum gpio_steer steer) { switch (steer) { case FIXED_0: diff --git a/src/jtag/drivers/versaloon/usbtoxxx/usbtoswd.c b/src/jtag/drivers/versaloon/usbtoxxx/usbtoswd.c index ef1b675..cb4862f 100644 --- a/src/jtag/drivers/versaloon/usbtoxxx/usbtoswd.c +++ b/src/jtag/drivers/versaloon/usbtoxxx/usbtoswd.c @@ -28,7 +28,7 @@ #include "usbtoxxx.h" #include "usbtoxxx_internal.h" -RESULT usbtoswd_read_callback(void *p, uint8_t *src, uint8_t *processed) +static RESULT usbtoswd_read_callback(void *p, uint8_t *src, uint8_t *processed) { struct versaloon_pending_t *pending = (struct versaloon_pending_t *)p; @@ -38,7 +38,7 @@ RESULT usbtoswd_read_callback(void *p, uint8_t *src, uint8_t *processed) return ERROR_OK; } -RESULT usbtoswd_write_callback(void *p, uint8_t *src, uint8_t *processed) +static RESULT usbtoswd_write_callback(void *p, uint8_t *src, uint8_t *processed) { struct versaloon_pending_t *pending = (struct versaloon_pending_t *)p; diff --git a/src/jtag/drivers/versaloon/usbtoxxx/usbtoxxx.c b/src/jtag/drivers/versaloon/usbtoxxx/usbtoxxx.c index 678b097..b46bbe0 100644 --- a/src/jtag/drivers/versaloon/usbtoxxx/usbtoxxx.c +++ b/src/jtag/drivers/versaloon/usbtoxxx/usbtoxxx.c @@ -29,7 +29,7 @@ #define N_A "n/a" -const char *types_name[96] = { +static const char *types_name[96] = { "usbtousart", "usbtospi", "usbtoi2c", "usbtogpio", "usbtocan", "usbtopwm", "usbtoadc", "usbtodac", "usbtomicrowire", "usbtoswim", "usbtodusi", N_A, N_A, N_A, "usbtopower", "usbtodelay", @@ -55,8 +55,8 @@ static uint16_t usbtoxxx_buffer_index; static uint16_t usbtoxxx_current_cmd_index; static uint8_t *usbtoxxx_buffer; -uint16_t collect_index; -uint8_t collect_cmd; +static uint16_t collect_index; +static uint8_t collect_cmd; static uint8_t poll_nesting; struct usbtoxxx_context_t { @@ -86,7 +86,7 @@ static void usbtoxxx_pop_context(struct usbtoxxx_context_t *c) versaloon_pending_idx = c->versaloon_pending_idx; } -RESULT usbtoxxx_validate_current_command_type(void) +static RESULT usbtoxxx_validate_current_command_type(void) { if (type_pre > 0) { /* not the first command */ @@ -272,7 +272,7 @@ bool usbtoxxx_interface_supported(uint8_t cmd) return (usbtoxxx_abilities[cmd / 8] & (1 << (cmd % 8))) > 0; } -RESULT usbtoxxx_ensure_buffer_size(uint16_t cmdlen) +static RESULT usbtoxxx_ensure_buffer_size(uint16_t cmdlen) { /* check free space, commit if not enough */ if (((usbtoxxx_buffer_index + usbtoxxx_current_cmd_index + cmdlen) diff --git a/src/jtag/drivers/versaloon/versaloon.c b/src/jtag/drivers/versaloon/versaloon.c index 8efe443..b517795 100644 --- a/src/jtag/drivers/versaloon/versaloon.c +++ b/src/jtag/drivers/versaloon/versaloon.c @@ -38,12 +38,12 @@ uint16_t versaloon_pending_idx; libusb_device_handle *versaloon_usb_device_handle; static uint32_t versaloon_usb_to = VERSALOON_TIMEOUT; -RESULT versaloon_init(void); -RESULT versaloon_fini(void); -RESULT versaloon_get_target_voltage(uint16_t *voltage); -RESULT versaloon_set_target_voltage(uint16_t voltage); -RESULT versaloon_delay_ms(uint16_t ms); -RESULT versaloon_delay_us(uint16_t us); +static RESULT versaloon_init(void); +static RESULT versaloon_fini(void); +static RESULT versaloon_get_target_voltage(uint16_t *voltage); +static RESULT versaloon_set_target_voltage(uint16_t voltage); +static RESULT versaloon_delay_ms(uint16_t ms); +static RESULT versaloon_delay_us(uint16_t us); struct versaloon_interface_t versaloon_interface = { .init = versaloon_init, @@ -233,7 +233,7 @@ RESULT versaloon_send_command(uint16_t out_len, uint16_t *inlen) } #define VERSALOON_RETRY_CNT 10 -RESULT versaloon_init(void) +static RESULT versaloon_init(void) { uint16_t ret = 0; uint8_t retry; @@ -291,7 +291,7 @@ RESULT versaloon_init(void) return versaloon_get_target_voltage(&ret); } -RESULT versaloon_fini(void) +static RESULT versaloon_fini(void) { if (versaloon_usb_device_handle != NULL) { usbtoxxx_fini(); @@ -309,7 +309,7 @@ RESULT versaloon_fini(void) return ERROR_OK; } -RESULT versaloon_set_target_voltage(uint16_t voltage) +static RESULT versaloon_set_target_voltage(uint16_t voltage) { usbtopwr_init(0); usbtopwr_config(0); @@ -319,7 +319,7 @@ RESULT versaloon_set_target_voltage(uint16_t voltage) return usbtoxxx_execute_command(); } -RESULT versaloon_get_target_voltage(uint16_t *voltage) +static RESULT versaloon_get_target_voltage(uint16_t *voltage) { uint16_t inlen; @@ -345,12 +345,12 @@ RESULT versaloon_get_target_voltage(uint16_t *voltage) } } -RESULT versaloon_delay_ms(uint16_t ms) +static RESULT versaloon_delay_ms(uint16_t ms) { return usbtodelay_delay(ms | 0x8000); } -RESULT versaloon_delay_us(uint16_t us) +static RESULT versaloon_delay_us(uint16_t us) { return usbtodelay_delay(us & 0x7FFF); } diff --git a/src/jtag/drivers/xds110.c b/src/jtag/drivers/xds110.c index fb04d51..df1ab65 100644 --- a/src/jtag/drivers/xds110.c +++ b/src/jtag/drivers/xds110.c @@ -186,7 +186,7 @@ #define CMD_STABLECLOCKS 4 /* Array to convert from OpenOCD tap_state_t to XDS JTAG state */ -const uint32_t xds_jtag_state[] = { +static const uint32_t xds_jtag_state[] = { XDS_JTAG_STATE_EXIT2_DR, /* TAP_DREXIT2 = 0x0 */ XDS_JTAG_STATE_EXIT1_DR, /* TAP_DREXIT1 = 0x1 */ XDS_JTAG_STATE_SHIFT_DR, /* TAP_DRSHIFT = 0x2 */ diff --git a/src/jtag/drivers/xlnx-pcie-xvc.c b/src/jtag/drivers/xlnx-pcie-xvc.c index 2423a9f..22f256f 100644 --- a/src/jtag/drivers/xlnx-pcie-xvc.c +++ b/src/jtag/drivers/xlnx-pcie-xvc.c @@ -120,7 +120,7 @@ static int xlnx_pcie_xvc_transact(size_t num_bits, uint32_t tms, uint32_t tdi, return ERROR_OK; } -int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd) +static int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd) { int tms = tap_get_state() == TAP_RESET ? 1 : 0; size_t left = cmd->cmd.stableclocks->num_cycles; diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index 490eb9f..6691a9a 100644 --- a/src/jtag/hla/hla_interface.c +++ b/src/jtag/hla/hla_interface.c @@ -188,7 +188,7 @@ int hl_interface_override_target(const char **targetname) return ERROR_FAIL; } -int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, +static int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq, unsigned int traceclkin_freq, uint16_t *prescaler) { @@ -203,7 +203,7 @@ int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, return ERROR_OK; } -int hl_interface_poll_trace(uint8_t *buf, size_t *size) +static int hl_interface_poll_trace(uint8_t *buf, size_t *size) { if (hl_if.layout->api->poll_trace) return hl_if.layout->api->poll_trace(hl_if.handle, buf, size); diff --git a/src/jtag/hla/hla_layout.h b/src/jtag/hla/hla_layout.h index e0bbd0f..a8088fe 100644 --- a/src/jtag/hla/hla_layout.h +++ b/src/jtag/hla/hla_layout.h @@ -51,10 +51,25 @@ struct hl_layout_api_s { int (*step)(void *handle); /** */ int (*read_regs)(void *handle); - /** */ - int (*read_reg)(void *handle, int num, uint32_t *val); - /** */ - int (*write_reg)(void *handle, int num, uint32_t val); + /** + * Read one register from the target + * + * @param handle A pointer to the device-specific handle + * @param regsel Register selection index compatible with all the + * values allowed by armv7m DCRSR.REGSEL + * @param val A pointer to retrieve the register value + * @returns ERROR_OK on success, or an error code on failure. + */ + int (*read_reg)(void *handle, unsigned int regsel, uint32_t *val); + /** + * Write one register to the target + * @param handle A pointer to the device-specific handle + * @param regsel Register selection index compatible with all the + * values allowed by armv7m DCRSR.REGSEL + * @param val The value to be written in the register + * @returns ERROR_OK on success, or an error code on failure. + */ + int (*write_reg)(void *handle, unsigned int regsel, uint32_t val); /** */ int (*read_mem)(void *handle, uint32_t addr, uint32_t size, uint32_t count, uint8_t *buffer); diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 2fa53be..061a78f 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -134,7 +134,7 @@ extern struct adapter_driver aice_adapter_driver; #if BUILD_BCM2835GPIO == 1 extern struct adapter_driver bcm2835gpio_adapter_driver; #endif -#if BUILD_CMSIS_DAP == 1 +#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1 extern struct adapter_driver cmsis_dap_adapter_driver; #endif #if BUILD_KITPROG == 1 @@ -254,7 +254,7 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_BCM2835GPIO == 1 &bcm2835gpio_adapter_driver, #endif -#if BUILD_CMSIS_DAP == 1 +#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1 &cmsis_dap_adapter_driver, #endif #if BUILD_KITPROG == 1 diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c index 153a98e..2fa162e 100644 --- a/src/jtag/tcl.c +++ b/src/jtag/tcl.c @@ -194,6 +194,11 @@ static int Jim_Command_drscan(Jim_Interp *interp, int argc, Jim_Obj *const *args retval = jtag_execute_queue(); if (retval != ERROR_OK) { Jim_SetResultString(interp, "drscan: jtag execute failed", -1); + + for (i = 0; i < field_count; i++) + free(fields[i].in_value); + free(fields); + return JIM_ERR; } diff --git a/src/openocd.c b/src/openocd.c index 8862284..83c3545 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -38,9 +38,11 @@ #include <pld/pld.h> #include <target/arm_cti.h> #include <target/arm_adi_v5.h> +#include <rtt/rtt.h> #include <server/server.h> #include <server/gdb_server.h> +#include <server/rtt_server.h> #ifdef HAVE_STRINGS_H #include <strings.h> @@ -230,10 +232,7 @@ static int openocd_register_commands(struct command_context *cmd_ctx) struct command_context *global_cmd_ctx; -/* NB! this fn can be invoked outside this file for non PC hosted builds - * NB! do not change to 'static'!!!! - */ -struct command_context *setup_command_handler(Jim_Interp *interp) +static struct command_context *setup_command_handler(Jim_Interp *interp) { log_init(); LOG_DEBUG("log_init: complete"); @@ -247,6 +246,7 @@ struct command_context *setup_command_handler(Jim_Interp *interp) &server_register_commands, &gdb_register_commands, &log_register_commands, + &rtt_server_register_commands, &transport_register_commands, &interface_register_commands, &target_register_commands, @@ -338,6 +338,9 @@ int openocd_main(int argc, char *argv[]) if (ioutil_init(cmd_ctx) != ERROR_OK) return EXIT_FAILURE; + if (rtt_init() != ERROR_OK) + return EXIT_FAILURE; + LOG_OUTPUT("For bug reports, read\n\t" "http://openocd.org/doc/doxygen/bugs.html" "\n"); @@ -367,6 +370,7 @@ int openocd_main(int argc, char *argv[]) /* Shutdown commandline interface */ command_exit(cmd_ctx); + rtt_exit(); free_config(); if (ERROR_FAIL == ret) diff --git a/src/rtos/ThreadX.c b/src/rtos/ThreadX.c index 302641b..7d58725 100644 --- a/src/rtos/ThreadX.c +++ b/src/rtos/ThreadX.c @@ -110,7 +110,7 @@ static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_i { 16, 0x04, 32 }, /* xPSR */ }; -const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = { +static const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = { { ARM926EJS_REGISTERS_SIZE_SOLICITED, /* stack_registers_size */ -1, /* stack_growth_direction */ diff --git a/src/rtos/hwthread.c b/src/rtos/hwthread.c index 773a87d..4051a5d 100644 --- a/src/rtos/hwthread.c +++ b/src/rtos/hwthread.c @@ -37,11 +37,11 @@ static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs); static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); static int hwthread_smp_init(struct target *target); -int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value); -bool hwthread_needs_fake_step(struct target *target, int64_t thread_id); -int hwthread_read_buffer(struct rtos *rtos, target_addr_t address, +static int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value); +static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id); +static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address, uint32_t size, uint8_t *buffer); -int hwthread_write_buffer(struct rtos *rtos, target_addr_t address, +static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address, uint32_t size, const uint8_t *buffer); #define HW_THREAD_NAME_STR_SIZE (32) @@ -247,23 +247,35 @@ static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, if (!target_was_examined(curr)) return ERROR_FAIL; + int reg_list_size; struct reg **reg_list; - int retval = target_get_gdb_reg_list(curr, ®_list, rtos_reg_list_size, + int retval = target_get_gdb_reg_list(curr, ®_list, ®_list_size, REG_CLASS_GENERAL); if (retval != ERROR_OK) return retval; + int j = 0; + for (int i = 0; i < reg_list_size; i++) { + if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden) + continue; + j++; + } + *rtos_reg_list_size = j; *rtos_reg_list = calloc(*rtos_reg_list_size, sizeof(struct rtos_reg)); if (*rtos_reg_list == NULL) { free(reg_list); return ERROR_FAIL; } - for (int i = 0; i < *rtos_reg_list_size; i++) { - (*rtos_reg_list)[i].number = (*reg_list)[i].number; - (*rtos_reg_list)[i].size = (*reg_list)[i].size; - memcpy((*rtos_reg_list)[i].value, (*reg_list)[i].value, + j = 0; + for (int i = 0; i < reg_list_size; i++) { + if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden) + continue; + (*rtos_reg_list)[j].number = (*reg_list)[i].number; + (*rtos_reg_list)[j].size = (*reg_list)[i].size; + memcpy((*rtos_reg_list)[j].value, (*reg_list)[i].value, ((*reg_list)[i].size + 7) / 8); + j++; } free(reg_list); @@ -308,7 +320,7 @@ static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id, return ERROR_OK; } -int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value) +static int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value) { if (rtos == NULL) return ERROR_FAIL; @@ -395,12 +407,12 @@ static int hwthread_create(struct target *target) return 0; } -bool hwthread_needs_fake_step(struct target *target, int64_t thread_id) +static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id) { return false; } -int hwthread_read_buffer(struct rtos *rtos, target_addr_t address, +static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address, uint32_t size, uint8_t *buffer) { if (rtos == NULL) @@ -415,7 +427,7 @@ int hwthread_read_buffer(struct rtos *rtos, target_addr_t address, return target_read_buffer(curr, address, size, buffer); } -int hwthread_write_buffer(struct rtos *rtos, target_addr_t address, +static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address, uint32_t size, const uint8_t *buffer) { if (rtos == NULL) diff --git a/src/rtos/linux.c b/src/rtos/linux.c index 44e132d..0cb4b54 100644 --- a/src/rtos/linux.c +++ b/src/rtos/linux.c @@ -92,7 +92,7 @@ struct cpu_context { uint32_t PC; uint32_t preempt_count; }; -struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr, +static struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr, uint32_t *info_addr); static int insert_into_threadlist(struct target *target, struct threads *t); @@ -144,7 +144,7 @@ static int linux_read_memory(struct target *target, return ERROR_OK; } -int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer) +static int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer) { if ((addr & 0xfffffffc) != addr) @@ -155,7 +155,7 @@ int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer) } -uint32_t get_buffer(struct target *target, const uint8_t *buffer) +static uint32_t get_buffer(struct target *target, const uint8_t *buffer) { uint32_t value = 0; const uint8_t *value_ptr = buffer; @@ -293,7 +293,7 @@ int fill_task_pid(struct target *target, struct threads *t) } #endif -int fill_task(struct target *target, struct threads *t) +static int fill_task(struct target *target, struct threads *t) { int retval; uint32_t pid_addr = t->base_addr + PID; @@ -349,7 +349,7 @@ int fill_task(struct target *target, struct threads *t) return retval; } -int get_name(struct target *target, struct threads *t) +static int get_name(struct target *target, struct threads *t) { int retval; uint32_t full_name[4]; @@ -395,7 +395,7 @@ int get_name(struct target *target, struct threads *t) } -int get_current(struct target *target, int create) +static int get_current(struct target *target, int create) { struct target_list *head; head = target->head; @@ -483,7 +483,7 @@ int get_current(struct target *target, int create) return ERROR_OK; } -struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr, +static struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr, uint32_t *thread_info_addr_old) { struct cpu_context *context = calloc(1, sizeof(struct cpu_context)); @@ -579,7 +579,7 @@ retry: return context; } -uint32_t next_task(struct target *target, struct threads *t) +static uint32_t next_task(struct target *target, struct threads *t) { uint8_t *buffer = calloc(1, 4); uint32_t next_addr = t->base_addr + NEXT; @@ -598,7 +598,7 @@ uint32_t next_task(struct target *target, struct threads *t) return 0; } -struct current_thread *add_current_thread(struct current_thread *currents, +static struct current_thread *add_current_thread(struct current_thread *currents, struct current_thread *ct) { ct->next = NULL; @@ -617,7 +617,7 @@ struct current_thread *add_current_thread(struct current_thread *currents, } } -struct threads *liste_del_task(struct threads *task_list, struct threads **t, +static struct threads *liste_del_task(struct threads *task_list, struct threads **t, struct threads *prev) { LOG_INFO("del task %" PRId64, (*t)->threadid); @@ -634,7 +634,7 @@ struct threads *liste_del_task(struct threads *task_list, struct threads **t, return task_list; } -struct threads *liste_add_task(struct threads *task_list, struct threads *t, +static struct threads *liste_add_task(struct threads *task_list, struct threads *t, struct threads **last) { t->next = NULL; @@ -683,7 +683,7 @@ static int current_base_addr(struct linux_os *linux_os, uint32_t base_addr) return 0; } -int linux_get_tasks(struct target *target, int context) +static int linux_get_tasks(struct target *target, int context) { int loop = 0; int retval = 0; @@ -1033,7 +1033,7 @@ static int linux_task_update(struct target *target, int context) return ERROR_OK; } -int linux_gdb_thread_packet(struct target *target, +static int linux_gdb_thread_packet(struct target *target, struct connection *connection, char const *packet, int packet_size) { @@ -1070,7 +1070,7 @@ int linux_gdb_thread_packet(struct target *target, return ERROR_OK; } -int linux_gdb_thread_update(struct target *target, +static int linux_gdb_thread_update(struct target *target, struct connection *connection, char const *packet, int packet_size) { @@ -1117,7 +1117,7 @@ int linux_gdb_thread_update(struct target *target, return ERROR_OK; } -int linux_thread_extra_info(struct target *target, +static int linux_thread_extra_info(struct target *target, struct connection *connection, char const *packet, int packet_size) { @@ -1163,7 +1163,7 @@ int linux_thread_extra_info(struct target *target, return ERROR_OK; } -int linux_gdb_T_packet(struct connection *connection, +static int linux_gdb_T_packet(struct connection *connection, struct target *target, char const *packet, int packet_size) { int64_t threadid; @@ -1223,7 +1223,7 @@ int linux_gdb_T_packet(struct connection *connection, return retval; } -int linux_gdb_h_packet(struct connection *connection, +static int linux_gdb_h_packet(struct connection *connection, struct target *target, char const *packet, int packet_size) { struct linux_os *linux_os = (struct linux_os *) diff --git a/src/rtos/nuttx.c b/src/rtos/nuttx.c index 3c90625..19b93ba 100644 --- a/src/rtos/nuttx.c +++ b/src/rtos/nuttx.c @@ -72,7 +72,7 @@ struct tcb { uint8_t dat[512]; }; -struct { +static struct { uint32_t addr; uint32_t prio; } g_tasklist[TASK_QUEUE_NUM]; diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index 4798f04..b391b2c 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -59,6 +59,8 @@ static struct rtos_type *rtos_types[] = { NULL }; +static int rtos_try_next(struct target *target); + int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size); int rtos_smp_init(struct target *target) @@ -632,7 +634,7 @@ int rtos_generic_stack_read(struct target *target, return ERROR_OK; } -int rtos_try_next(struct target *target) +static int rtos_try_next(struct target *target) { struct rtos *os = target->rtos; struct rtos_type **type = rtos_types; diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h index 9b43afb..1bda730 100644 --- a/src/rtos/rtos.h +++ b/src/rtos/rtos.h @@ -134,7 +134,6 @@ int rtos_generic_stack_read(struct target *target, int64_t stack_ptr, 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); diff --git a/src/rtt/Makefile.am b/src/rtt/Makefile.am new file mode 100644 index 0000000..e3fcefd --- /dev/null +++ b/src/rtt/Makefile.am @@ -0,0 +1,2 @@ +noinst_LTLIBRARIES += %D%/librtt.la +%C%_librtt_la_SOURCES = %D%/rtt.c %D%/rtt.h %D%/tcl.c diff --git a/src/rtt/rtt.c b/src/rtt/rtt.c new file mode 100644 index 0000000..bf3cca5 --- /dev/null +++ b/src/rtt/rtt.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <helper/log.h> +#include <helper/list.h> +#include <target/target.h> +#include <target/rtt.h> + +#include "rtt.h" + +static struct { + struct rtt_source source; + /** Control block. */ + struct rtt_control ctrl; + struct target *target; + /** Start address to search for the control block. */ + target_addr_t addr; + /** Size of the control block search area. */ + size_t size; + /** Control block identifier. */ + char id[RTT_CB_MAX_ID_LENGTH]; + /** Whether RTT is configured. */ + bool configured; + /** Whether RTT is started. */ + bool started; + /** Whether configuration changed. */ + bool changed; + /** Whether the control block was found. */ + bool found_cb; + + struct rtt_sink_list **sink_list; + size_t sink_list_length; + + unsigned int polling_interval; +} rtt; + +int rtt_init(void) +{ + rtt.sink_list_length = 1; + rtt.sink_list = calloc(rtt.sink_list_length, + sizeof(struct rtt_sink_list *)); + + if (!rtt.sink_list) + return ERROR_FAIL; + + rtt.sink_list[0] = NULL; + rtt.started = false; + + rtt.polling_interval = 100; + + return ERROR_OK; +} + +int rtt_exit(void) +{ + free(rtt.sink_list); + + return ERROR_OK; +} + +static int read_channel_callback(void *user_data) +{ + int ret; + + ret = rtt.source.read(rtt.target, &rtt.ctrl, rtt.sink_list, + rtt.sink_list_length, NULL); + + if (ret != ERROR_OK) { + target_unregister_timer_callback(&read_channel_callback, NULL); + rtt.source.stop(rtt.target, NULL); + return ret; + } + + return ERROR_OK; +} + +int rtt_setup(target_addr_t address, size_t size, const char *id) +{ + size_t id_length = strlen(id); + + if (!id_length || id_length >= RTT_CB_MAX_ID_LENGTH) { + LOG_ERROR("rtt: Invalid control block ID"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + rtt.addr = address; + rtt.size = size; + strncpy(rtt.id, id, id_length + 1); + rtt.changed = true; + rtt.configured = true; + + return ERROR_OK; +} + +int rtt_register_source(const struct rtt_source source, + struct target *target) +{ + if (!source.find_cb || !source.read_cb || !source.read_channel_info) + return ERROR_FAIL; + + if (!source.start || !source.stop) + return ERROR_FAIL; + + if (!source.read || !source.write) + return ERROR_FAIL; + + rtt.source = source; + rtt.target = target; + + return ERROR_OK; +} + +int rtt_start(void) +{ + int ret; + target_addr_t addr = rtt.addr; + + if (rtt.started) + return ERROR_OK; + + if (!rtt.found_cb || rtt.changed) { + rtt.source.find_cb(rtt.target, &addr, rtt.size, rtt.id, + &rtt.found_cb, NULL); + + rtt.changed = false; + + if (rtt.found_cb) { + LOG_INFO("rtt: Control block found at 0x%" TARGET_PRIxADDR, + addr); + rtt.ctrl.address = addr; + } else { + LOG_INFO("rtt: No control block found"); + return ERROR_OK; + } + } + + ret = rtt.source.read_cb(rtt.target, rtt.ctrl.address, &rtt.ctrl, NULL); + + if (ret != ERROR_OK) + return ret; + + ret = rtt.source.start(rtt.target, &rtt.ctrl, NULL); + + if (ret != ERROR_OK) + return ret; + + target_register_timer_callback(&read_channel_callback, + rtt.polling_interval, 1, NULL); + rtt.started = true; + + return ERROR_OK; +} + +int rtt_stop(void) +{ + int ret; + + if (!rtt.configured) { + LOG_ERROR("rtt: Not configured"); + return ERROR_FAIL; + } + + target_unregister_timer_callback(&read_channel_callback, NULL); + rtt.started = false; + + ret = rtt.source.stop(rtt.target, NULL); + + if (ret != ERROR_OK) + return ret; + + return ERROR_OK; +} + +static int adjust_sink_list(size_t length) +{ + struct rtt_sink_list **tmp; + + if (length <= rtt.sink_list_length) + return ERROR_OK; + + tmp = realloc(rtt.sink_list, sizeof(struct rtt_sink_list *) * length); + + if (!tmp) + return ERROR_FAIL; + + for (size_t i = rtt.sink_list_length; i < length; i++) + tmp[i] = NULL; + + rtt.sink_list = tmp; + rtt.sink_list_length = length; + + return ERROR_OK; +} + +int rtt_register_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data) +{ + struct rtt_sink_list *tmp; + + if (channel_index >= rtt.sink_list_length) { + if (adjust_sink_list(channel_index + 1) != ERROR_OK) + return ERROR_FAIL; + } + + LOG_DEBUG("rtt: Registering sink for channel %u", channel_index); + + tmp = malloc(sizeof(struct rtt_sink_list)); + + if (!tmp) + return ERROR_FAIL; + + tmp->read = read; + tmp->user_data = user_data; + tmp->next = rtt.sink_list[channel_index]; + + rtt.sink_list[channel_index] = tmp; + + return ERROR_OK; +} + +int rtt_unregister_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data) +{ + struct rtt_sink_list *prev_sink; + + LOG_DEBUG("rtt: Unregistering sink for channel %u", channel_index); + + if (channel_index >= rtt.sink_list_length) + return ERROR_FAIL; + + prev_sink = rtt.sink_list[channel_index]; + + for (struct rtt_sink_list *sink = rtt.sink_list[channel_index]; sink; + prev_sink = sink, sink = sink->next) { + if (sink->read == read && sink->user_data == user_data) { + + if (sink == rtt.sink_list[channel_index]) + rtt.sink_list[channel_index] = sink->next; + else + prev_sink->next = sink->next; + + free(sink); + + return ERROR_OK; + } + } + + return ERROR_OK; +} + +int rtt_get_polling_interval(unsigned int *interval) +{ + if (!interval) + return ERROR_FAIL; + + *interval = rtt.polling_interval; + + return ERROR_OK; +} + +int rtt_set_polling_interval(unsigned int interval) +{ + if (!interval) + return ERROR_FAIL; + + if (rtt.polling_interval != interval) { + target_unregister_timer_callback(&read_channel_callback, NULL); + target_register_timer_callback(&read_channel_callback, interval, 1, + NULL); + } + + rtt.polling_interval = interval; + + return ERROR_OK; +} + +int rtt_write_channel(unsigned int channel_index, const uint8_t *buffer, + size_t *length) +{ + if (channel_index >= rtt.ctrl.num_up_channels) { + LOG_WARNING("rtt: Down-channel %u is not available", channel_index); + return ERROR_OK; + } + + return rtt.source.write(rtt.target, &rtt.ctrl, channel_index, buffer, + length, NULL); +} + +bool rtt_started(void) +{ + return rtt.started; +} + +bool rtt_configured(void) +{ + return rtt.configured; +} + +bool rtt_found_cb(void) +{ + return rtt.found_cb; +} + +const struct rtt_control *rtt_get_control(void) +{ + return &rtt.ctrl; +} + +int rtt_read_channel_info(unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info) +{ + return rtt.source.read_channel_info(rtt.target, &rtt.ctrl, + channel_index, type, info, NULL); +} diff --git a/src/rtt/rtt.h b/src/rtt/rtt.h new file mode 100644 index 0000000..597c838 --- /dev/null +++ b/src/rtt/rtt.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OPENOCD_RTT_RTT_H +#define OPENOCD_RTT_RTT_H + +#include <stdint.h> +#include <stdbool.h> + +#include <helper/command.h> +#include <target/target.h> + +/** + * Control block ID length in bytes, including the trailing null-terminator. + */ +#define RTT_CB_MAX_ID_LENGTH 16 + +/* Control block size in bytes. */ +#define RTT_CB_SIZE (RTT_CB_MAX_ID_LENGTH + 2 * sizeof(uint32_t)) + +/* Channel structure size in bytes. */ +#define RTT_CHANNEL_SIZE 24 + +/* Minimal channel buffer size in bytes. */ +#define RTT_CHANNEL_BUFFER_MIN_SIZE 2 + +/** RTT control block. */ +struct rtt_control { + /** Control block address on the target. */ + target_addr_t address; + /** Control block identifier, including trailing null-terminator. */ + char id[RTT_CB_MAX_ID_LENGTH]; + /** Maximum number of up-channels. */ + uint32_t num_up_channels; + /** Maximum number of down-channels. */ + uint32_t num_down_channels; +}; + +/** RTT channel. */ +struct rtt_channel { + /** Channel structure address on the target. */ + target_addr_t address; + /** Channel name address on the target. */ + uint32_t name_addr; + /** Buffer address on the target. */ + uint32_t buffer_addr; + /** Channel buffer size in bytes. */ + uint32_t size; + /** Write position within the buffer in bytes. */ + uint32_t write_pos; + /** Read position within the buffer in bytes. */ + uint32_t read_pos; + /** + * Buffer flags. + * + * @note: Not used at the moment. + */ + uint32_t flags; +}; + +/** RTT channel information. */ +struct rtt_channel_info { + /** Channel name. */ + char *name; + /** Length of the name in bytes, including the trailing null-terminator. */ + size_t name_length; + /** Buffer size in bytes. */ + uint32_t size; + /** + * Buffer flags. + * + * @note: Not used at the moment. + */ + uint32_t flags; +}; + +typedef int (*rtt_sink_read)(unsigned int channel, const uint8_t *buffer, + size_t length, void *user_data); + +struct rtt_sink_list { + rtt_sink_read read; + void *user_data; + + struct rtt_sink_list *next; +}; + +/** Channel type. */ +enum rtt_channel_type { + /** Up channel (target to host). */ + RTT_CHANNEL_TYPE_UP, + /** Down channel (host to target). */ + RTT_CHANNEL_TYPE_DOWN +}; + +typedef int (*rtt_source_find_ctrl_block)(struct target *target, + target_addr_t *address, size_t size, const char *id, bool *found, + void *user_data); +typedef int (*rtt_source_read_ctrl_block)(struct target *target, + target_addr_t address, struct rtt_control *ctrl_block, + void *user_data); +typedef int (*rtt_source_read_channel_info)(struct target *target, + const struct rtt_control *ctrl, unsigned int channel, + enum rtt_channel_type type, struct rtt_channel_info *info, + void *user_data); +typedef int (*rtt_source_start)(struct target *target, + const struct rtt_control *ctrl, void *user_data); +typedef int (*rtt_source_stop)(struct target *target, void *user_data); +typedef int (*rtt_source_read)(struct target *target, + const struct rtt_control *ctrl, struct rtt_sink_list **sinks, + size_t num_channels, void *user_data); +typedef int (*rtt_source_write)(struct target *target, + struct rtt_control *ctrl, unsigned int channel, + const uint8_t *buffer, size_t *length, void *user_data); + +/** RTT source. */ +struct rtt_source { + rtt_source_find_ctrl_block find_cb; + rtt_source_read_ctrl_block read_cb; + rtt_source_read_channel_info read_channel_info; + rtt_source_start start; + rtt_source_stop stop; + rtt_source_read read; + rtt_source_write write; +}; + +/** + * Initialize Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_init(void); + +/** + * Shutdown Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_exit(void); + +/** + * Register an RTT source for a target. + * + * @param[in] source RTT source. + * @param[in,out] target Target. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_register_source(const struct rtt_source source, + struct target *target); + +/** + * Setup RTT. + * + * @param[in] address Start address to search for the control block. + * @param[in] size Size of the control block search area. + * @param[in] id Identifier of the control block. Must be null-terminated. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_setup(target_addr_t address, size_t size, const char *id); + +/** + * Start Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_start(void); + +/** + * Stop Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_stop(void); + +/** + * Get the polling interval. + * + * @param[out] interval Polling interval in milliseconds. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_get_polling_interval(unsigned int *interval); + +/** + * Set the polling interval. + * + * @param[in] interval Polling interval in milliseconds. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_set_polling_interval(unsigned int interval); + +/** + * Get whether RTT is started. + * + * @returns Whether RTT is started. + */ +bool rtt_started(void); + +/** + * Get whether RTT is configured. + * + * @returns Whether RTT is configured. + */ +bool rtt_configured(void); + +/** + * Get whether RTT control block was found. + * + * @returns Whether RTT was found. + */ +bool rtt_found_cb(void); + +/** + * Get the RTT control block. + * + * @returns The RTT control block. + */ +const struct rtt_control *rtt_get_control(void); + +/** + * Read channel information. + * + * @param[in] channel_index Channel index. + * @param[in] channel_type Channel type. + * @param[out] info Channel information. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_read_channel_info(unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info); + +/** + * Register an RTT sink. + * + * @param[in] channel_index Channel index. + * @param[in] read Read callback function. + * @param[in,out] user_data User data to be passed to the callback function. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_register_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data); + +/** + * Unregister an RTT sink. + * + * @param[in] channel_index Channel index. + * @param[in] read Read callback function. + * @param[in,out] user_data User data to be passed to the callback function. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_unregister_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data); + +/** + * Write to an RTT channel. + * + * @param[in] channel_index Channel index. + * @param[in] buffer Buffer with data that should be written to the channel. + * @param[in,out] length Number of bytes to write. On success, the argument gets + * updated with the actual number of written bytes. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_write_channel(unsigned int channel_index, const uint8_t *buffer, + size_t *length); + +extern const struct command_registration rtt_target_command_handlers[]; + +#endif /* OPENOCD_RTT_RTT_H */ diff --git a/src/rtt/tcl.c b/src/rtt/tcl.c new file mode 100644 index 0000000..f5abf2e --- /dev/null +++ b/src/rtt/tcl.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2019-2020 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <helper/log.h> +#include <target/rtt.h> + +#include "rtt.h" + +#define CHANNEL_NAME_SIZE 128 + +COMMAND_HANDLER(handle_rtt_setup_command) +{ +struct rtt_source source; + + if (CMD_ARGC != 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + source.find_cb = &target_rtt_find_control_block; + source.read_cb = &target_rtt_read_control_block; + source.start = &target_rtt_start; + source.stop = &target_rtt_stop; + source.read = &target_rtt_read_callback; + source.write = &target_rtt_write_callback; + source.read_channel_info = &target_rtt_read_channel_info; + + target_addr_t address; + uint32_t size; + + COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size); + + rtt_register_source(source, get_current_target(CMD_CTX)); + + if (rtt_setup(address, size, CMD_ARGV[2]) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rtt_start_command) +{ + if (CMD_ARGC > 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!rtt_configured()) { + command_print(CMD, "RTT is not configured"); + return ERROR_FAIL; + } + + return rtt_start(); +} + +COMMAND_HANDLER(handle_rtt_stop_command) +{ + if (CMD_ARGC > 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + return rtt_stop(); +} + +COMMAND_HANDLER(handle_rtt_polling_interval_command) +{ + if (CMD_ARGC == 0) { + int ret; + unsigned int interval; + + ret = rtt_get_polling_interval(&interval); + + if (ret != ERROR_OK) { + command_print(CMD, "Failed to get polling interval"); + return ret; + } + + command_print(CMD, "%u ms", interval); + } else if (CMD_ARGC == 1) { + int ret; + unsigned int interval; + + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], interval); + ret = rtt_set_polling_interval(interval); + + if (ret != ERROR_OK) { + command_print(CMD, "Failed to set polling interval"); + return ret; + } + } else { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rtt_channels_command) +{ + int ret; + char channel_name[CHANNEL_NAME_SIZE]; + const struct rtt_control *ctrl; + struct rtt_channel_info info; + + if (!rtt_found_cb()) { + command_print(CMD, "rtt: Control block not available"); + return ERROR_FAIL; + } + + ctrl = rtt_get_control(); + + command_print(CMD, "Channels: up=%u, down=%u", ctrl->num_up_channels, + ctrl->num_down_channels); + + command_print(CMD, "Up-channels:"); + + info.name = channel_name; + info.name_length = sizeof(channel_name); + + for (unsigned int i = 0; i < ctrl->num_up_channels; i++) { + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_UP, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + command_print(CMD, "%u: %s %u %u", i, info.name, info.size, + info.flags); + } + + command_print(CMD, "Down-channels:"); + + for (unsigned int i = 0; i < ctrl->num_down_channels; i++) { + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_DOWN, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + command_print(CMD, "%u: %s %u %u", i, info.name, info.size, + info.flags); + } + + return ERROR_OK; +} + +static int jim_channel_list(Jim_Interp *interp, int argc, + Jim_Obj * const *argv) +{ + Jim_Obj *list; + Jim_Obj *channel_list; + char channel_name[CHANNEL_NAME_SIZE]; + const struct rtt_control *ctrl; + struct rtt_channel_info info; + + if (!rtt_found_cb()) { + Jim_SetResultFormatted(interp, "rtt: Control block not available"); + return ERROR_FAIL; + } + + ctrl = rtt_get_control(); + + info.name = channel_name; + info.name_length = sizeof(channel_name); + + list = Jim_NewListObj(interp, NULL, 0); + channel_list = Jim_NewListObj(interp, NULL, 0); + + for (unsigned int i = 0; i < ctrl->num_up_channels; i++) { + int ret; + Jim_Obj *tmp; + + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_UP, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + tmp = Jim_NewListObj(interp, NULL, 0); + + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + "name", -1)); + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + info.name, -1)); + + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + "size", -1)); + Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, + info.size)); + + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + "flags", -1)); + Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, + info.flags)); + + Jim_ListAppendElement(interp, channel_list, tmp); + } + + Jim_ListAppendElement(interp, list, channel_list); + + channel_list = Jim_NewListObj(interp, NULL, 0); + + for (unsigned int i = 0; i < ctrl->num_down_channels; i++) { + int ret; + Jim_Obj *tmp; + + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_DOWN, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + tmp = Jim_NewListObj(interp, NULL, 0); + + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + "name", -1)); + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + info.name, -1)); + + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + "size", -1)); + Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, + info.size)); + + Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, + "flags", -1)); + Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, + info.flags)); + + Jim_ListAppendElement(interp, channel_list, tmp); + } + + Jim_ListAppendElement(interp, list, channel_list); + Jim_SetResult(interp, list); + + return JIM_OK; +} + +static const struct command_registration rtt_subcommand_handlers[] = { + { + .name = "setup", + .handler = handle_rtt_setup_command, + .mode = COMMAND_ANY, + .help = "setup RTT", + .usage = "<address> <size> <ID>" + }, + { + .name = "start", + .handler = handle_rtt_start_command, + .mode = COMMAND_EXEC, + .help = "start RTT", + .usage = "" + }, + { + .name = "stop", + .handler = handle_rtt_stop_command, + .mode = COMMAND_EXEC, + .help = "stop RTT", + .usage = "" + }, + { + .name = "polling_interval", + .handler = handle_rtt_polling_interval_command, + .mode = COMMAND_EXEC, + .help = "show or set polling interval in ms", + .usage = "[interval]" + }, + { + .name = "channels", + .handler = handle_rtt_channels_command, + .mode = COMMAND_EXEC, + .help = "list available channels", + .usage = "" + }, + { + .name = "channellist", + .jim_handler = jim_channel_list, + .mode = COMMAND_EXEC, + .help = "list available channels", + .usage = "" + }, + COMMAND_REGISTRATION_DONE +}; + +const struct command_registration rtt_target_command_handlers[] = { + { + .name = "rtt", + .mode = COMMAND_EXEC, + .help = "RTT target commands", + .usage = "", + .chain = rtt_subcommand_handlers + }, + COMMAND_REGISTRATION_DONE +}; diff --git a/src/server/Makefile.am b/src/server/Makefile.am index 804efac..d270ee2 100644 --- a/src/server/Makefile.am +++ b/src/server/Makefile.am @@ -8,7 +8,9 @@ noinst_LTLIBRARIES += %D%/libserver.la %D%/gdb_server.h \ %D%/server_stubs.c \ %D%/tcl_server.c \ - %D%/tcl_server.h + %D%/tcl_server.h \ + %D%/rtt_server.c \ + %D%/rtt_server.h %C%_libserver_la_CFLAGS = $(AM_CFLAGS) if IS_MINGW diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index c6c74c2..a5f4acb 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -967,15 +967,6 @@ static int gdb_new_connection(struct connection *connection) breakpoint_clear_target(target); watchpoint_clear_target(target); - if (target->rtos) { - /* clean previous rtos session if supported*/ - if (target->rtos->type->clean) - target->rtos->type->clean(target); - - /* update threads */ - rtos_update_threads(target); - } - /* remove the initial ACK from the incoming buffer */ retval = gdb_get_char(connection, &initial_ack); if (retval != ERROR_OK) @@ -988,6 +979,15 @@ static int gdb_new_connection(struct connection *connection) gdb_putback_char(connection, initial_ack); target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH); + if (target->rtos) { + /* clean previous rtos session if supported*/ + if (target->rtos->type->clean) + target->rtos->type->clean(target); + + /* update threads */ + rtos_update_threads(target); + } + if (gdb_use_memory_map) { /* Connect must fail if the memory map can't be set up correctly. * @@ -1187,7 +1187,7 @@ static int gdb_get_registers_packet(struct connection *connection, return gdb_error(connection, retval); for (i = 0; i < reg_list_size; i++) { - if (reg_list[i] == NULL || reg_list[i]->exist == false) + if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden) continue; reg_packet_size += DIV_ROUND_UP(reg_list[i]->size, 8) * 2; } @@ -1201,7 +1201,7 @@ 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) + if (reg_list[i] == NULL || reg_list[i]->exist == false || reg_list[i]->hidden) continue; if (!reg_list[i]->valid) { retval = reg_list[i]->type->get(reg_list[i]); @@ -1330,7 +1330,7 @@ static int gdb_get_register_packet(struct connection *connection, } } - reg_packet = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1); /* plus one for string termination null */ + reg_packet = calloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1, 1); /* plus one for string termination null */ gdb_str_to_target(target, reg_packet, reg_list[reg_num]); @@ -2187,7 +2187,7 @@ static int get_reg_features_list(struct target *target, char const **feature_lis *feature_list = calloc(1, sizeof(char *)); for (int i = 0; i < reg_list_size; i++) { - if (reg_list[i]->exist == false) + if (reg_list[i]->exist == false || reg_list[i]->hidden) continue; if (reg_list[i]->feature != NULL @@ -2353,7 +2353,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o int i; for (i = 0; i < reg_list_size; i++) { - if (reg_list[i]->exist == false) + if (reg_list[i]->exist == false || reg_list[i]->hidden) continue; if (strcmp(reg_list[i]->feature->name, features[current_feature])) @@ -2600,7 +2600,7 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list, transfer_type = 'l'; *chunk = malloc(length + 2 + 3); - /* Allocating extra 3 bytes prevents false positive valgrind report + /* Allocating extra 3 bytes prevents false positive valgrind report * of strlen(chunk) word access: * Invalid read of size 4 * Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */ @@ -3614,8 +3614,8 @@ static int gdb_target_start(struct target *target, const char *port) target->gdb_service = gdb_service; ret = add_service("gdb", - port, 1, &gdb_new_connection, &gdb_input, - &gdb_connection_closed, gdb_service); + port, target->gdb_max_connections, &gdb_new_connection, &gdb_input, + &gdb_connection_closed, gdb_service, NULL); /* initialize all targets gdb service with the same pointer */ { struct target_list *head; diff --git a/src/server/rtt_server.c b/src/server/rtt_server.c new file mode 100644 index 0000000..3c885cc --- /dev/null +++ b/src/server/rtt_server.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2016-2017 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <rtt/rtt.h> + +#include "server.h" +#include "rtt_server.h" + +/** + * @file + * + * RTT server. + * + * This server allows access to Real Time Transfer (RTT) channels via TCP + * connections. + */ + +struct rtt_service { + unsigned int channel; +}; + +static int read_callback(unsigned int channel, const uint8_t *buffer, + size_t length, void *user_data) +{ + int ret; + struct connection *connection; + size_t offset; + + connection = (struct connection *)user_data; + offset = 0; + + while (offset < length) { + ret = connection_write(connection, buffer + offset, length - offset); + + if (ret < 0) { + LOG_ERROR("Failed to write data to socket."); + return ERROR_FAIL; + } + + offset += ret; + } + + return ERROR_OK; +} + +static int rtt_new_connection(struct connection *connection) +{ + int ret; + struct rtt_service *service; + + service = connection->service->priv; + + LOG_DEBUG("rtt: New connection for channel %u", service->channel); + + ret = rtt_register_sink(service->channel, &read_callback, connection); + + if (ret != ERROR_OK) + return ret; + + return ERROR_OK; +} + +static int rtt_connection_closed(struct connection *connection) +{ + struct rtt_service *service; + + service = (struct rtt_service *)connection->service->priv; + rtt_unregister_sink(service->channel, &read_callback, connection); + + LOG_DEBUG("rtt: Connection for channel %u closed", service->channel); + + return ERROR_OK; +} + +static int rtt_input(struct connection *connection) +{ + int bytes_read; + unsigned char buffer[1024]; + struct rtt_service *service; + size_t length; + + service = (struct rtt_service *)connection->service->priv; + bytes_read = connection_read(connection, buffer, sizeof(buffer)); + + if (!bytes_read) + return ERROR_SERVER_REMOTE_CLOSED; + else if (bytes_read < 0) { + LOG_ERROR("error during read: %s", strerror(errno)); + return ERROR_SERVER_REMOTE_CLOSED; + } + + length = bytes_read; + rtt_write_channel(service->channel, buffer, &length); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rtt_start_command) +{ + int ret; + struct rtt_service *service; + + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + service = malloc(sizeof(struct rtt_service)); + + if (!service) + return ERROR_FAIL; + + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], service->channel); + + ret = add_service("rtt", CMD_ARGV[0], CONNECTION_LIMIT_UNLIMITED, + rtt_new_connection, rtt_input, rtt_connection_closed, service, NULL); + + if (ret != ERROR_OK) { + free(service); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rtt_stop_command) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + remove_service("rtt", CMD_ARGV[0]); + + return ERROR_OK; +} + +static const struct command_registration rtt_server_subcommand_handlers[] = { + { + .name = "start", + .handler = handle_rtt_start_command, + .mode = COMMAND_ANY, + .help = "Start a RTT server", + .usage = "<port> <channel>" + }, + { + .name = "stop", + .handler = handle_rtt_stop_command, + .mode = COMMAND_ANY, + .help = "Stop a RTT server", + .usage = "<port>" + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rtt_server_command_handlers[] = { + { + .name = "server", + .mode = COMMAND_ANY, + .help = "RTT server", + .usage = "", + .chain = rtt_server_subcommand_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rtt_command_handlers[] = { + { + .name = "rtt", + .mode = COMMAND_ANY, + .help = "RTT", + .usage = "", + .chain = rtt_server_command_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +int rtt_server_register_commands(struct command_context *ctx) +{ + return register_commands(ctx, NULL, rtt_command_handlers); +} diff --git a/src/server/rtt_server.h b/src/server/rtt_server.h new file mode 100644 index 0000000..aec6f22 --- /dev/null +++ b/src/server/rtt_server.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016-2017 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OPENOCD_SERVER_RTT_SERVER_H +#define OPENOCD_SERVER_RTT_SERVER_H + +#include <helper/command.h> + +int rtt_server_register_commands(struct command_context *ctx); + +#endif /* OPENOCD_SERVER_RTT_SERVER_H */ diff --git a/src/server/server.c b/src/server/server.c index 4e970fa..4de9889 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -211,7 +211,8 @@ int add_service(char *name, new_connection_handler_t new_connection_handler, input_handler_t input_handler, connection_closed_handler_t connection_closed_handler, - void *priv) + void *priv, + struct service **new_service) { struct service *c, **p; struct hostent *hp; @@ -347,6 +348,10 @@ int add_service(char *name, ; *p = c; + /* if new_service is not NULL, return the created service into it */ + if (new_service) + *new_service = c; + return ERROR_OK; } @@ -603,7 +608,7 @@ int server_loop(struct command_context *command_context) return shutdown_openocd == SHUTDOWN_WITH_ERROR_CODE ? ERROR_FAIL : ERROR_OK; } -void sig_handler(int sig) +static void sig_handler(int sig) { /* store only first signal that hits us */ if (shutdown_openocd == CONTINUE_MAIN_LOOP) { diff --git a/src/server/server.h b/src/server/server.h index ff2ada9..99f5fe2 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -77,7 +77,7 @@ struct service { int add_service(char *name, const char *port, int max_connections, new_connection_handler_t new_connection_handler, input_handler_t in_handler, connection_closed_handler_t close_handler, - void *priv); + void *priv, struct service **new_service); int remove_service(const char *name, const char *port); int server_host_os_entry(void); diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index 1ecb827..07213ae 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -285,7 +285,7 @@ int tcl_init(void) return add_service("tcl", tcl_port, CONNECTION_LIMIT_UNLIMITED, &tcl_new_connection, &tcl_input, - &tcl_closed, NULL); + &tcl_closed, NULL, NULL); } COMMAND_HANDLER(handle_tcl_port_command) diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 0243c63..4f88d3a 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -538,7 +538,17 @@ static int telnet_input(struct connection *connection) telnet_move_cursor(connection, 0); else if (*buf_p == CTRL('E')) telnet_move_cursor(connection, t_con->line_size); - else + else if (*buf_p == CTRL('K')) { /* kill line to end */ + if (t_con->line_cursor < t_con->line_size) { + /* overwrite with space, until end of line, move back */ + for (size_t i = t_con->line_cursor; i < t_con->line_size; i++) + telnet_write(connection, " ", 1); + for (size_t i = t_con->line_cursor; i < t_con->line_size; i++) + telnet_write(connection, "\b", 1); + t_con->line[t_con->line_cursor] = '\0'; + t_con->line_size = t_con->line_cursor; + } + } else LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p); } } @@ -684,7 +694,7 @@ int telnet_init(char *banner) int ret = add_service("telnet", telnet_port, CONNECTION_LIMIT_UNLIMITED, telnet_new_connection, telnet_input, telnet_connection_closed, - telnet_service); + telnet_service, NULL); if (ret != ERROR_OK) { free(telnet_service); diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 19ba771..1d30747 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -48,7 +48,8 @@ TARGET_CORE_SRC = \ %D%/target_request.c \ %D%/testee.c \ %D%/semihosting_common.c \ - %D%/smp.c + %D%/smp.c \ + %D%/rtt.c ARMV4_5_SRC = \ %D%/armv4_5.c \ @@ -259,7 +260,8 @@ ARC_SRC = \ %D%/arc.h \ %D%/arc_cmd.h \ %D%/arc_jtag.h \ - %D%/arc_mem.h + %D%/arc_mem.h \ + %D%/rtt.h include %D%/openrisc/Makefile.am include %D%/riscv/Makefile.am diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c index c2100eb..6dede97 100644 --- a/src/target/adi_v5_jtag.c +++ b/src/target/adi_v5_jtag.c @@ -145,7 +145,7 @@ struct dap_cmd { struct dap_cmd_pool { struct list_head lh; struct dap_cmd cmd; -} dap_cmd_pool; +}; static void log_dap_cmd(const char *header, struct dap_cmd *el) { diff --git a/src/target/adi_v5_swd.c b/src/target/adi_v5_swd.c index ee30ff7..b25181e 100644 --- a/src/target/adi_v5_swd.c +++ b/src/target/adi_v5_swd.c @@ -118,26 +118,69 @@ static int swd_connect(struct adiv5_dap *dap) } } - /* Note, debugport_init() does setup too */ - swd->switch_seq(JTAG_TO_SWD); - /* Clear link state, including the SELECT cache. */ - dap->do_reconnect = false; - dap_invalidate_cache(dap); + int64_t timeout = timeval_ms() + 500; - swd_queue_dp_read(dap, DP_DPIDR, &dpidr); + do { + /* Note, debugport_init() does setup too */ + swd->switch_seq(JTAG_TO_SWD); - /* force clear all sticky faults */ - swd_clear_sticky_errors(dap); + /* Clear link state, including the SELECT cache. */ + dap->do_reconnect = false; + dap_invalidate_cache(dap); + + status = swd_queue_dp_read(dap, DP_DPIDR, &dpidr); + if (status == ERROR_OK) { + status = swd_run_inner(dap); + if (status == ERROR_OK) + break; + } + + alive_sleep(1); + + } while (timeval_ms() < timeout); + + if (status != ERROR_OK) { + LOG_ERROR("Error connecting DP: cannot read IDR"); + return status; + } + + LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); + + do { + dap->do_reconnect = false; - status = swd_run_inner(dap); + /* force clear all sticky faults */ + swd_clear_sticky_errors(dap); + + status = swd_run_inner(dap); + if (status != ERROR_WAIT) + break; + + alive_sleep(10); + + } while (timeval_ms() < timeout); + + /* IHI 0031E B4.3.2: + * "A WAIT response must not be issued to the ... + * ... writes to the ABORT register" + * swd_clear_sticky_errors() writes to the ABORT register only. + * + * Unfortunately at least Microchip SAMD51/E53/E54 returns WAIT + * in a corner case. Just try if ABORT resolves the problem. + */ + if (status == ERROR_WAIT) { + LOG_WARNING("Connecting DP: stalled AP operation, issuing ABORT"); - if (status == ERROR_OK) { - LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); dap->do_reconnect = false; + + swd->write_reg(swd_cmd(false, false, DP_ABORT), + DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); + status = swd_run_inner(dap); + } + + if (status == ERROR_OK) status = dap_dp_init(dap); - } else - dap->do_reconnect = true; return status; } diff --git a/src/target/arc.c b/src/target/arc.c index e1b5764..ffe9745 100644 --- a/src/target/arc.c +++ b/src/target/arc.c @@ -48,6 +48,8 @@ */ +static int arc_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint); void arc_reg_data_type_add(struct target *target, struct arc_reg_data_type *data_type) @@ -303,7 +305,7 @@ static int arc_init_reg(struct target *target, struct reg *reg, /* Initialize struct reg */ reg->name = reg_desc->name; reg->size = 32; /* All register in ARC are 32-bit */ - reg->value = ®_desc->reg_value; + reg->value = reg_desc->reg_value; reg->type = &arc_reg_type; reg->arch_info = reg_desc; reg->caller_save = true; /* @todo should be configurable. */ @@ -1696,6 +1698,7 @@ void arc_reset_actionpoints(struct target *target) struct arc_common *arc = target_to_arc(target); struct arc_actionpoint *ap_list = arc->actionpoints_list; struct breakpoint *next_b; + struct watchpoint *next_w; while (target->breakpoints) { next_b = target->breakpoints->next; @@ -1704,6 +1707,12 @@ void arc_reset_actionpoints(struct target *target) free(target->breakpoints); target->breakpoints = next_b; } + while (target->watchpoints) { + next_w = target->watchpoints->next; + arc_remove_watchpoint(target, target->watchpoints); + free(target->watchpoints); + target->watchpoints = next_w; + } for (unsigned int i = 0; i < arc->actionpoints_num; i++) { if ((ap_list[i].used) && (ap_list[i].reg_address)) arc_remove_auxreg_actionpoint(target, ap_list[i].reg_address); @@ -1800,6 +1809,159 @@ int arc_remove_auxreg_actionpoint(struct target *target, uint32_t auxreg_addr) return retval; } + +static int arc_set_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + unsigned int wp_num; + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + + if (watchpoint->set) { + LOG_WARNING("watchpoint already set"); + return ERROR_OK; + } + + for (wp_num = 0; wp_num < arc->actionpoints_num; wp_num++) { + if (!ap_list[wp_num].used) + break; + } + + if (wp_num >= arc->actionpoints_num) { + LOG_ERROR("No free actionpoints, maximum amount is %u", + arc->actionpoints_num); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (watchpoint->length != 4) { + LOG_ERROR("Only watchpoints of length 4 are supported"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + int enable = AP_AC_TT_DISABLE; + switch (watchpoint->rw) { + case WPT_READ: + enable = AP_AC_TT_READ; + break; + case WPT_WRITE: + enable = AP_AC_TT_WRITE; + break; + case WPT_ACCESS: + enable = AP_AC_TT_READWRITE; + break; + default: + LOG_ERROR("BUG: watchpoint->rw neither read, write nor access"); + return ERROR_FAIL; + } + + int retval = arc_configure_actionpoint(target, wp_num, + watchpoint->address, enable, AP_AC_AT_MEMORY_ADDR); + + if (retval == ERROR_OK) { + watchpoint->set = wp_num + 1; + ap_list[wp_num].used = 1; + ap_list[wp_num].bp_value = watchpoint->address; + ap_list[wp_num].type = ARC_AP_WATCHPOINT; + + LOG_DEBUG("wpid: %" PRIu32 ", wp_num %u wp_value 0x%" PRIx32, + watchpoint->unique_id, wp_num, ap_list[wp_num].bp_value); + } + + return retval; +} + +static int arc_unset_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + /* get pointers to arch-specific information */ + struct arc_common *arc = target_to_arc(target); + struct arc_actionpoint *ap_list = arc->actionpoints_list; + + if (!watchpoint->set) { + LOG_WARNING("watchpoint not set"); + return ERROR_OK; + } + + unsigned int wp_num = watchpoint->set - 1; + if ((watchpoint->set == 0) || (wp_num >= arc->actionpoints_num)) { + LOG_DEBUG("Invalid actionpoint ID: %u in watchpoint: %" PRIu32, + wp_num, watchpoint->unique_id); + return ERROR_OK; + } + + int retval = arc_configure_actionpoint(target, wp_num, + watchpoint->address, AP_AC_TT_DISABLE, AP_AC_AT_MEMORY_ADDR); + + if (retval == ERROR_OK) { + watchpoint->set = 0; + ap_list[wp_num].used = 0; + ap_list[wp_num].bp_value = 0; + + LOG_DEBUG("wpid: %" PRIu32 " - releasing actionpoint ID: %u", + watchpoint->unique_id, wp_num); + } + + return retval; +} + +static int arc_add_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + CHECK_RETVAL(arc_set_watchpoint(target, watchpoint)); + + return ERROR_OK; +} + +static int arc_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->set) + CHECK_RETVAL(arc_unset_watchpoint(target, watchpoint)); + + return ERROR_OK; +} + +static int arc_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) +{ + assert(target); + assert(hit_watchpoint); + + struct arc_actionpoint *actionpoint = NULL; + CHECK_RETVAL(get_current_actionpoint(target, &actionpoint)); + + if (actionpoint != NULL) { + if (!actionpoint->used) + LOG_WARNING("Target halted by unused actionpoint."); + + /* If this check fails - that is some sort of an error in OpenOCD. */ + if (actionpoint->type != ARC_AP_WATCHPOINT) + LOG_WARNING("Target halted by breakpoint, but is treated as a watchpoint."); + + for (struct watchpoint *watchpoint = target->watchpoints; + watchpoint != NULL; + watchpoint = watchpoint->next) { + if (actionpoint->bp_value == watchpoint->address) { + *hit_watchpoint = watchpoint; + LOG_DEBUG("Hit watchpoint, wpid: %" PRIu32 ", watchpoint num: %i", + watchpoint->unique_id, watchpoint->set - 1); + return ERROR_OK; + } + } + } + + return ERROR_FAIL; +} + /* Helper function which switches core to single_step mode by * doing aux r/w operations. */ int arc_config_step(struct target *target, int enable_step) @@ -2106,9 +2268,9 @@ struct target_type arcv2_target = { .add_context_breakpoint = NULL, .add_hybrid_breakpoint = NULL, .remove_breakpoint = arc_remove_breakpoint, - .add_watchpoint = NULL, - .remove_watchpoint = NULL, - .hit_watchpoint = NULL, + .add_watchpoint = arc_add_watchpoint, + .remove_watchpoint = arc_remove_watchpoint, + .hit_watchpoint = arc_hit_watchpoint, .run_algorithm = NULL, .start_algorithm = NULL, diff --git a/src/target/arm7_9_common.c b/src/target/arm7_9_common.c index d70d273..797f61c 100644 --- a/src/target/arm7_9_common.c +++ b/src/target/arm7_9_common.c @@ -1391,6 +1391,11 @@ static int arm7_9_full_context(struct target *target) int retval; struct arm7_9_common *arm7_9 = target_to_arm7_9(target); struct arm *arm = &arm7_9->arm; + struct { + uint32_t value; + uint8_t *reg_p; + } read_cache[6 * (16 + 1)]; + int read_cache_idx = 0; LOG_DEBUG("-"); @@ -1433,10 +1438,12 @@ static int arm7_9_full_context(struct target *target) for (j = 0; j < 15; j++) { if (!ARMV4_5_CORE_REG_MODE(arm->core_cache, armv4_5_number_to_mode(i), j).valid) { - reg_p[j] = (uint32_t *)ARMV4_5_CORE_REG_MODE( + read_cache[read_cache_idx].reg_p = ARMV4_5_CORE_REG_MODE( arm->core_cache, armv4_5_number_to_mode(i), j).value; + reg_p[j] = &read_cache[read_cache_idx].value; + read_cache_idx++; mask |= 1 << j; ARMV4_5_CORE_REG_MODE(arm->core_cache, armv4_5_number_to_mode(i), @@ -1454,9 +1461,10 @@ static int arm7_9_full_context(struct target *target) /* check if the PSR has to be read */ if (!ARMV4_5_CORE_REG_MODE(arm->core_cache, armv4_5_number_to_mode(i), 16).valid) { - arm7_9->read_xpsr(target, - (uint32_t *)ARMV4_5_CORE_REG_MODE(arm->core_cache, - armv4_5_number_to_mode(i), 16).value, 1); + read_cache[read_cache_idx].reg_p = ARMV4_5_CORE_REG_MODE(arm->core_cache, + armv4_5_number_to_mode(i), 16).value; + arm7_9->read_xpsr(target, &read_cache[read_cache_idx].value, 1); + read_cache_idx++; ARMV4_5_CORE_REG_MODE(arm->core_cache, armv4_5_number_to_mode(i), 16).valid = true; ARMV4_5_CORE_REG_MODE(arm->core_cache, armv4_5_number_to_mode(i), @@ -1472,6 +1480,14 @@ static int arm7_9_full_context(struct target *target) retval = jtag_execute_queue(); if (retval != ERROR_OK) return retval; + /* + * FIXME: regs in cache should be tagged as 'valid' only now, + * not before the jtag_execute_queue() + */ + while (read_cache_idx) { + read_cache_idx--; + buf_set_u32(read_cache[read_cache_idx].reg_p, 0, 32, read_cache[read_cache_idx].value); + } return ERROR_OK; } diff --git a/src/target/arm7tdmi.c b/src/target/arm7tdmi.c index 01685ab..10263f4 100644 --- a/src/target/arm7tdmi.c +++ b/src/target/arm7tdmi.c @@ -115,11 +115,9 @@ static inline int arm7tdmi_clock_out_inner(struct arm_jtag *jtag_info, uint32_t /* put an instruction in the ARM7TDMI pipeline or write the data bus, * and optionally read data - * - * FIXME remove the unused "deprecated" parameter */ static inline int arm7tdmi_clock_out(struct arm_jtag *jtag_info, - uint32_t out, uint32_t *deprecated, int breakpoint) + uint32_t out, int breakpoint) { int retval; retval = arm_jtag_scann(jtag_info, 0x1, TAP_DRPAUSE); @@ -246,35 +244,35 @@ static void arm7tdmi_change_to_arm(struct target *target, * to allow common handling of ARM and THUMB debugging */ /* fetch STR r0, [r0] */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* nothing fetched, STR r0, [r0] in Execute (2) */ arm7tdmi_clock_data_in(jtag_info, r0); /* MOV r0, r15 fetched, STR in Decode */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV(0, 15), NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV(0, 15), 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* nothing fetched, STR r0, [r0] in Execute (2) */ arm7tdmi_clock_data_in(jtag_info, pc); /* use pc-relative LDR to clear r0[1:0] (for switch to ARM mode) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_LDR_PCREL(0), NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_LDR_PCREL(0), 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* nothing fetched, data for LDR r0, [PC, #0] */ - arm7tdmi_clock_out(jtag_info, 0x0, NULL, 0); + arm7tdmi_clock_out(jtag_info, 0x0, 0); /* nothing fetched, data from previous cycle is written to register */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* fetch BX */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_BX(0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_BX(0), 0); /* NOP fetched, BX in Decode, MOV in Execute */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* NOP fetched, BX in Execute (1) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); jtag_execute_queue(); @@ -301,12 +299,12 @@ static void arm7tdmi_read_core_regs(struct target *target, /* STMIA r0-15, [r0] at debug speed * register values will start to appear on 4th DCLK */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), 0); /* fetch NOP, STM in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, STM in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); for (i = 0; i <= 15; i++) { if (mask & (1 << i)) @@ -329,12 +327,12 @@ static void arm7tdmi_read_core_regs_target_buffer(struct target *target, /* STMIA r0-15, [r0] at debug speed * register values will start to appear on 4th DCLK */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), 0); /* fetch NOP, STM in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, STM in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); for (i = 0; i <= 15; i++) { /* nothing fetched, STM still in EXECUTE (1 + i cycle), read databus */ @@ -360,14 +358,14 @@ static void arm7tdmi_read_xpsr(struct target *target, uint32_t *xpsr, int spsr) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* MRS r0, cpsr */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_MRS(0, spsr & 1), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_MRS(0, spsr & 1), 0); /* STR r0, [r15] */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 15), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 15), 0); /* fetch NOP, STR in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, STR in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, STR still in EXECUTE (2nd cycle) */ arm7tdmi_clock_data_in(jtag_info, xpsr); } @@ -380,25 +378,25 @@ static void arm7tdmi_write_xpsr(struct target *target, uint32_t xpsr, int spsr) LOG_DEBUG("xpsr: %8.8" PRIx32 ", spsr: %i", xpsr, spsr); /* MSR1 fetched */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr & 0xff, 0, 1, spsr), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr & 0xff, 0, 1, spsr), 0); /* MSR2 fetched, MSR1 in DECODE */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff00) >> 8, 0xc, 2, spsr), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff00) >> 8, 0xc, 2, spsr), 0); /* MSR3 fetched, MSR1 in EXECUTE (1), MSR2 in DECODE */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff0000) >> 16, 0x8, 4, spsr), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff0000) >> 16, 0x8, 4, spsr), 0); /* nothing fetched, MSR1 in EXECUTE (2) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* MSR4 fetched, MSR2 in EXECUTE (1), MSR3 in DECODE */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff000000) >> 24, 0x4, 8, spsr), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff000000) >> 24, 0x4, 8, spsr), 0); /* nothing fetched, MSR2 in EXECUTE (2) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* NOP fetched, MSR3 in EXECUTE (1), MSR4 in DECODE */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, MSR3 in EXECUTE (2) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* NOP fetched, MSR4 in EXECUTE (1) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, MSR4 in EXECUTE (2) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); } static void arm7tdmi_write_xpsr_im8(struct target *target, @@ -410,13 +408,13 @@ static void arm7tdmi_write_xpsr_im8(struct target *target, LOG_DEBUG("xpsr_im: %2.2x, rot: %i, spsr: %i", xpsr_im, rot, spsr); /* MSR fetched */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr_im, rot, 1, spsr), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr_im, rot, 1, spsr), 0); /* NOP fetched, MSR in DECODE */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* NOP fetched, MSR in EXECUTE (1) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, MSR in EXECUTE (2) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); } static void arm7tdmi_write_core_regs(struct target *target, @@ -429,7 +427,7 @@ static void arm7tdmi_write_core_regs(struct target *target, /* LDMIA r0-15, [r0] at debug speed * register values will start to appear on 4th DCLK */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), 0); /* fetch NOP, LDM in DECODE stage */ arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); @@ -450,9 +448,9 @@ static void arm7tdmi_load_word_regs(struct target *target, uint32_t mask) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* put system-speed load-multiple into the pipeline */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 1), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 1), 0); } static void arm7tdmi_load_hword_reg(struct target *target, int num) @@ -461,9 +459,9 @@ static void arm7tdmi_load_hword_reg(struct target *target, int num) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* put system-speed load half-word into the pipeline */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRH_IP(num, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRH_IP(num, 0), 0); } static void arm7tdmi_load_byte_reg(struct target *target, int num) @@ -472,9 +470,9 @@ static void arm7tdmi_load_byte_reg(struct target *target, int num) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* put system-speed load byte into the pipeline */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRB_IP(num, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRB_IP(num, 0), 0); } static void arm7tdmi_store_word_regs(struct target *target, uint32_t mask) @@ -483,9 +481,9 @@ static void arm7tdmi_store_word_regs(struct target *target, uint32_t mask) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* put system-speed store-multiple into the pipeline */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask, 0, 1), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask, 0, 1), 0); } static void arm7tdmi_store_hword_reg(struct target *target, int num) @@ -494,9 +492,9 @@ static void arm7tdmi_store_hword_reg(struct target *target, int num) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* put system-speed store half-word into the pipeline */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_STRH_IP(num, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STRH_IP(num, 0), 0); } static void arm7tdmi_store_byte_reg(struct target *target, int num) @@ -505,9 +503,9 @@ static void arm7tdmi_store_byte_reg(struct target *target, int num) struct arm_jtag *jtag_info = &arm7_9->jtag_info; /* put system-speed store byte into the pipeline */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_STRB_IP(num, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STRB_IP(num, 0), 0); } static void arm7tdmi_write_pc(struct target *target, uint32_t pc) @@ -518,7 +516,7 @@ static void arm7tdmi_write_pc(struct target *target, uint32_t pc) /* LDMIA r0-15, [r0] at debug speed * register values will start to appear on 4th DCLK */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), 0); /* fetch NOP, LDM in DECODE stage */ arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ @@ -540,7 +538,7 @@ static void arm7tdmi_branch_resume(struct target *target) struct arm7_9_common *arm7_9 = target_to_arm7_9(target); struct arm_jtag *jtag_info = &arm7_9->jtag_info; - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 1); arm7tdmi_clock_out_inner(jtag_info, ARMV4_5_B(0xfffffa, 0), 0); } @@ -556,53 +554,52 @@ static void arm7tdmi_branch_resume_thumb(struct target *target) /* LDMIA r0, [r0] at debug speed * register values will start to appear on 4th DCLK */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x1, 0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x1, 0, 0), 0); /* fetch NOP, LDM in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* nothing fetched, LDM in EXECUTE stage (2nd cycle) */ - arm7tdmi_clock_out(jtag_info, - buf_get_u32(arm->pc->value, 0, 32) | 1, NULL, 0); + arm7tdmi_clock_out(jtag_info, buf_get_u32(arm->pc->value, 0, 32) | 1, 0); /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* Branch and eXchange */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_BX(0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_BX(0), 0); embeddedice_read_reg(dbg_stat); /* fetch NOP, BX in DECODE stage */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* target is now in Thumb state */ embeddedice_read_reg(dbg_stat); /* fetch NOP, BX in EXECUTE stage (1st cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0); /* target is now in Thumb state */ embeddedice_read_reg(dbg_stat); /* load r0 value */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_LDR_PCREL(0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_LDR_PCREL(0), 0); /* fetch NOP, LDR in Decode */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* fetch NOP, LDR in Execute */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); /* nothing fetched, LDR in EXECUTE stage (2nd cycle) */ - arm7tdmi_clock_out(jtag_info, buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32), NULL, 0); + arm7tdmi_clock_out(jtag_info, buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32), 0); /* nothing fetched, LDR in EXECUTE stage (3rd cycle) */ - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0); embeddedice_read_reg(dbg_stat); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 1); - arm7tdmi_clock_out(jtag_info, ARMV4_5_T_B(0x7f8), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_B(0x7f8), 0); } static void arm7tdmi_build_reg_cache(struct target *target) diff --git a/src/target/arm926ejs.c b/src/target/arm926ejs.c index 95a4f7c..21fd689 100644 --- a/src/target/arm926ejs.c +++ b/src/target/arm926ejs.c @@ -723,7 +723,7 @@ static int arm926ejs_target_create(struct target *target, Jim_Interp *interp) return arm926ejs_init_arch_info(target, arm926ejs, target->tap); } -void arm926ejs_deinit_target(struct target *target) +static void arm926ejs_deinit_target(struct target *target) { struct arm *arm = target_to_arm(target); struct arm926ejs_common *arm926ejs = target_to_arm926(target); diff --git a/src/target/arm946e.c b/src/target/arm946e.c index 8754c86..036e8ba 100644 --- a/src/target/arm946e.c +++ b/src/target/arm946e.c @@ -51,8 +51,8 @@ */ static uint8_t arm946e_preserve_cache; -int arm946e_post_debug_entry(struct target *target); -void arm946e_pre_restore_context(struct target *target); +static int arm946e_post_debug_entry(struct target *target); +static void arm946e_pre_restore_context(struct target *target); static int arm946e_read_cp15(struct target *target, int reg_addr, uint32_t *value); int arm946e_init_arch_info(struct target *target, @@ -250,7 +250,7 @@ static uint32_t arm946e_cp15_get_csize(struct target *target, int idsel) return csize ? 1 << (12 + (csize-3)) : 0; } -uint32_t arm946e_invalidate_whole_dcache(struct target *target) +static uint32_t arm946e_invalidate_whole_dcache(struct target *target) { uint32_t csize = arm946e_cp15_get_csize(target, GET_DCACHE_SIZE); if (csize == 0) @@ -306,7 +306,7 @@ uint32_t arm946e_invalidate_whole_dcache(struct target *target) return ERROR_OK; } -uint32_t arm946e_invalidate_whole_icache(struct target *target) +static uint32_t arm946e_invalidate_whole_icache(struct target *target) { /* Check cache presence before flushing - avoid undefined behavior */ uint32_t csize = arm946e_cp15_get_csize(target, GET_ICACHE_SIZE); @@ -327,7 +327,7 @@ uint32_t arm946e_invalidate_whole_icache(struct target *target) return ERROR_OK; } -int arm946e_post_debug_entry(struct target *target) +static int arm946e_post_debug_entry(struct target *target) { uint32_t ctr_reg = 0x0; uint32_t retval = ERROR_OK; @@ -368,7 +368,7 @@ int arm946e_post_debug_entry(struct target *target) return ERROR_OK; } -void arm946e_pre_restore_context(struct target *target) +static void arm946e_pre_restore_context(struct target *target) { uint32_t ctr_reg = 0x0; uint32_t retval; @@ -393,7 +393,7 @@ void arm946e_pre_restore_context(struct target *target) } /* if preserve_cache */ } -uint32_t arm946e_invalidate_dcache(struct target *target, uint32_t address, +static uint32_t arm946e_invalidate_dcache(struct target *target, uint32_t address, uint32_t size, uint32_t count) { uint32_t cur_addr = 0x0; @@ -458,7 +458,7 @@ uint32_t arm946e_invalidate_dcache(struct target *target, uint32_t address, return ERROR_OK; } -uint32_t arm946e_invalidate_icache(struct target *target, uint32_t address, +static uint32_t arm946e_invalidate_icache(struct target *target, uint32_t address, uint32_t size, uint32_t count) { uint32_t cur_addr = 0x0; @@ -509,7 +509,7 @@ uint32_t arm946e_invalidate_icache(struct target *target, uint32_t address, } /** Writes a buffer, in the specified word size, with current MMU settings. */ -int arm946e_write_memory(struct target *target, target_addr_t address, +static int arm946e_write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { int retval; @@ -557,7 +557,7 @@ int arm946e_write_memory(struct target *target, target_addr_t address, } -int arm946e_read_memory(struct target *target, target_addr_t address, +static int arm946e_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { int retval; diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index a09e269..59bb186 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -652,35 +652,22 @@ int dap_dp_init(struct adiv5_dap *dap) LOG_DEBUG("%s", adiv5_dap_name(dap)); + dap->do_reconnect = false; 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. + * In jtag mode only, if the following queue run (in dap_dp_poll_register) + * fails and sets 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 */ - - retval = dap_dp_read_atomic(dap, DP_CTRL_STAT, NULL); - if (retval == ERROR_OK) - break; - } - /* * 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) @@ -732,6 +719,35 @@ int dap_dp_init(struct adiv5_dap *dap) } /** + * Initialize a DAP or do reconnect if DAP is not accessible. + * + * @param dap The DAP being initialized. + */ +int dap_dp_init_or_reconnect(struct adiv5_dap *dap) +{ + LOG_DEBUG("%s", adiv5_dap_name(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; + + dap->do_reconnect = false; + + dap_dp_read_atomic(dap, DP_CTRL_STAT, NULL); + if (dap->do_reconnect) { + /* dap connect calls dap_dp_init() after transport dependent initialization */ + return dap->ops->connect(dap); + } else { + return dap_dp_init(dap); + } +} + +/** * Initialize a DAP. This sets up the power domains, prepares the DP * for further use, and arranges to use AP #0 for all AP operations * until dap_ap-select() changes that policy. @@ -1479,15 +1495,118 @@ int dap_info_command(struct command_invocation *cmd, enum adiv5_cfg_param { CFG_DAP, - CFG_AP_NUM + CFG_AP_NUM, + CFG_BASEADDR, + CFG_CTIBASE, /* DEPRECATED */ }; static const Jim_Nvp nvp_config_opts[] = { - { .name = "-dap", .value = CFG_DAP }, - { .name = "-ap-num", .value = CFG_AP_NUM }, + { .name = "-dap", .value = CFG_DAP }, + { .name = "-ap-num", .value = CFG_AP_NUM }, + { .name = "-baseaddr", .value = CFG_BASEADDR }, + { .name = "-ctibase", .value = CFG_CTIBASE }, /* DEPRECATED */ { .name = NULL, .value = -1 } }; +static int adiv5_jim_spot_configure(Jim_GetOptInfo *goi, + struct adiv5_dap **dap_p, int *ap_num_p, uint32_t *base_p) +{ + if (!goi->argc) + return JIM_OK; + + Jim_SetEmptyResult(goi->interp); + + Jim_Nvp *n; + int e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts, + goi->argv[0], &n); + if (e != JIM_OK) + return JIM_CONTINUE; + + /* base_p can be NULL, then '-baseaddr' option is treated as unknown */ + if (!base_p && (n->value == CFG_BASEADDR || n->value == CFG_CTIBASE)) + return JIM_CONTINUE; + + e = Jim_GetOpt_Obj(goi, NULL); + if (e != JIM_OK) + return e; + + switch (n->value) { + case CFG_DAP: + if (goi->isconfigure) { + Jim_Obj *o_t; + struct adiv5_dap *dap; + e = Jim_GetOpt_Obj(goi, &o_t); + if (e != JIM_OK) + return e; + dap = dap_instance_by_jim_obj(goi->interp, o_t); + if (!dap) { + Jim_SetResultString(goi->interp, "DAP name invalid!", -1); + return JIM_ERR; + } + if (*dap_p && *dap_p != dap) { + Jim_SetResultString(goi->interp, + "DAP assignment cannot be changed!", -1); + return JIM_ERR; + } + *dap_p = dap; + } else { + if (goi->argc) + goto err_no_param; + if (!*dap_p) { + Jim_SetResultString(goi->interp, "DAP not configured", -1); + return JIM_ERR; + } + Jim_SetResultString(goi->interp, adiv5_dap_name(*dap_p), -1); + } + break; + + case CFG_AP_NUM: + if (goi->isconfigure) { + jim_wide ap_num; + 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; + } + *ap_num_p = ap_num; + } else { + if (goi->argc) + goto err_no_param; + if (*ap_num_p == DP_APSEL_INVALID) { + Jim_SetResultString(goi->interp, "AP number not configured", -1); + return JIM_ERR; + } + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, *ap_num_p)); + } + break; + + case CFG_CTIBASE: + LOG_WARNING("DEPRECATED! use \'-baseaddr' not \'-ctibase\'"); + /* fall through */ + case CFG_BASEADDR: + if (goi->isconfigure) { + jim_wide base; + e = Jim_GetOpt_Wide(goi, &base); + if (e != JIM_OK) + return e; + *base_p = (uint32_t)base; + } else { + if (goi->argc) + goto err_no_param; + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, *base_p)); + } + break; + }; + + return JIM_OK; + +err_no_param: + Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS"); + return JIM_ERR; +} + int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) { struct adiv5_private_config *pc; @@ -1502,90 +1621,19 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) target->has_dap = true; - if (goi->argc > 0) { - Jim_Nvp *n; - - Jim_SetEmptyResult(goi->interp); - - /* check first if topmost item is for us */ - e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts, - goi->argv[0], &n); - if (e != JIM_OK) - return JIM_CONTINUE; - - e = Jim_GetOpt_Obj(goi, NULL); - if (e != JIM_OK) - return e; - - switch (n->value) { - case CFG_DAP: - if (goi->isconfigure) { - Jim_Obj *o_t; - struct adiv5_dap *dap; - e = Jim_GetOpt_Obj(goi, &o_t); - if (e != JIM_OK) - return e; - dap = dap_instance_by_jim_obj(goi->interp, o_t); - if (dap == NULL) { - Jim_SetResultString(goi->interp, "DAP name invalid!", -1); - return JIM_ERR; - } - if (pc->dap != NULL && pc->dap != dap) { - Jim_SetResultString(goi->interp, - "DAP assignment cannot be changed after target was created!", -1); - return JIM_ERR; - } - if (target->tap_configured) { - Jim_SetResultString(goi->interp, - "-chain-position and -dap configparams are mutually exclusive!", -1); - return JIM_ERR; - } - pc->dap = dap; - target->tap = dap->tap; - target->dap_configured = true; - } else { - if (goi->argc != 0) { - Jim_WrongNumArgs(goi->interp, - goi->argc, goi->argv, - "NO PARAMS"); - return JIM_ERR; - } - - if (pc->dap == NULL) { - Jim_SetResultString(goi->interp, "DAP not configured", -1); - return JIM_ERR; - } - Jim_SetResultString(goi->interp, adiv5_dap_name(pc->dap), -1); - } - break; + e = adiv5_jim_spot_configure(goi, &pc->dap, &pc->ap_num, NULL); + if (e != JIM_OK) + return e; - case CFG_AP_NUM: - if (goi->isconfigure) { - jim_wide ap_num; - 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) { - Jim_WrongNumArgs(goi->interp, - goi->argc, goi->argv, - "NO PARAMS"); - return JIM_ERR; - } - - 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, pc->ap_num)); - } - break; + if (pc->dap && !target->dap_configured) { + if (target->tap_configured) { + pc->dap = NULL; + Jim_SetResultString(goi->interp, + "-chain-position and -dap configparams are mutually exclusive!", -1); + return JIM_ERR; } + target->tap = pc->dap->tap; + target->dap_configured = true; } return JIM_OK; @@ -1602,6 +1650,19 @@ int adiv5_verify_config(struct adiv5_private_config *pc) return ERROR_OK; } +int adiv5_jim_mem_ap_spot_configure(struct adiv5_mem_ap_spot *cfg, + Jim_GetOptInfo *goi) +{ + return adiv5_jim_spot_configure(goi, &cfg->dap, &cfg->ap_num, &cfg->base); +} + +int adiv5_mem_ap_spot_init(struct adiv5_mem_ap_spot *p) +{ + p->dap = NULL; + p->ap_num = DP_APSEL_INVALID; + p->base = 0; + return ERROR_OK; +} COMMAND_HANDLER(handle_dap_info_command) { diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index ea71551..8edfaa8 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -550,6 +550,7 @@ int mem_ap_write_buf_noincr(struct adiv5_ap *ap, /* Initialisation of the debug system, power domains and registers */ int dap_dp_init(struct adiv5_dap *dap); +int dap_dp_init_or_reconnect(struct adiv5_dap *dap); int mem_ap_init(struct adiv5_ap *ap); /* Invalidate cached DP select and cached TAR and CSW of all APs */ @@ -601,4 +602,14 @@ struct adiv5_private_config { extern int adiv5_verify_config(struct adiv5_private_config *pc); extern int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi); +struct adiv5_mem_ap_spot { + struct adiv5_dap *dap; + int ap_num; + uint32_t base; +}; + +extern int adiv5_mem_ap_spot_init(struct adiv5_mem_ap_spot *p); +extern int adiv5_jim_mem_ap_spot_configure(struct adiv5_mem_ap_spot *cfg, + Jim_GetOptInfo *goi); + #endif /* OPENOCD_TARGET_ARM_ADI_V5_H */ diff --git a/src/target/arm_cti.c b/src/target/arm_cti.c index 579bacb..689e9df 100644 --- a/src/target/arm_cti.c +++ b/src/target/arm_cti.c @@ -31,28 +31,21 @@ #include "helper/command.h" struct arm_cti { - target_addr_t base; - struct adiv5_ap *ap; -}; - -struct arm_cti_object { struct list_head lh; - struct arm_cti cti; - int ap_num; char *name; + struct adiv5_mem_ap_spot spot; }; static LIST_HEAD(all_cti); const char *arm_cti_name(struct arm_cti *self) { - struct arm_cti_object *obj = container_of(self, struct arm_cti_object, cti); - return obj->name; + return self->name; } struct arm_cti *cti_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o) { - struct arm_cti_object *obj = NULL; + struct arm_cti *obj = NULL; const char *name; bool found = false; @@ -66,16 +59,17 @@ struct arm_cti *cti_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o) } if (found) - return &obj->cti; + return obj; return NULL; } static int arm_cti_mod_reg_bits(struct arm_cti *self, unsigned int reg, uint32_t mask, uint32_t value) { + struct adiv5_ap *ap = dap_ap(self->spot.dap, self->spot.ap_num); uint32_t tmp; /* Read register */ - int retval = mem_ap_read_atomic_u32(self->ap, self->base + reg, &tmp); + int retval = mem_ap_read_atomic_u32(ap, self->spot.base + reg, &tmp); if (ERROR_OK != retval) return retval; @@ -85,26 +79,28 @@ static int arm_cti_mod_reg_bits(struct arm_cti *self, unsigned int reg, uint32_t tmp |= value & mask; /* write new value */ - return mem_ap_write_atomic_u32(self->ap, self->base + reg, tmp); + return mem_ap_write_atomic_u32(ap, self->spot.base + reg, tmp); } int arm_cti_enable(struct arm_cti *self, bool enable) { + struct adiv5_ap *ap = dap_ap(self->spot.dap, self->spot.ap_num); uint32_t val = enable ? 1 : 0; - return mem_ap_write_atomic_u32(self->ap, self->base + CTI_CTR, val); + return mem_ap_write_atomic_u32(ap, self->spot.base + CTI_CTR, val); } int arm_cti_ack_events(struct arm_cti *self, uint32_t event) { + struct adiv5_ap *ap = dap_ap(self->spot.dap, self->spot.ap_num); int retval; uint32_t tmp; - retval = mem_ap_write_atomic_u32(self->ap, self->base + CTI_INACK, event); + retval = mem_ap_write_atomic_u32(ap, self->spot.base + CTI_INACK, event); if (retval == ERROR_OK) { int64_t then = timeval_ms(); for (;;) { - retval = mem_ap_read_atomic_u32(self->ap, self->base + CTI_TROUT_STATUS, &tmp); + retval = mem_ap_read_atomic_u32(ap, self->spot.base + CTI_TROUT_STATUS, &tmp); if (retval != ERROR_OK) break; if ((tmp & event) == 0) @@ -138,15 +134,19 @@ int arm_cti_ungate_channel(struct arm_cti *self, uint32_t channel) int arm_cti_write_reg(struct arm_cti *self, unsigned int reg, uint32_t value) { - return mem_ap_write_atomic_u32(self->ap, self->base + reg, value); + struct adiv5_ap *ap = dap_ap(self->spot.dap, self->spot.ap_num); + + return mem_ap_write_atomic_u32(ap, self->spot.base + reg, value); } int arm_cti_read_reg(struct arm_cti *self, unsigned int reg, uint32_t *p_value) { + struct adiv5_ap *ap = dap_ap(self->spot.dap, self->spot.ap_num); + if (p_value == NULL) return ERROR_COMMAND_ARGUMENT_INVALID; - return mem_ap_read_atomic_u32(self->ap, self->base + reg, p_value); + return mem_ap_read_atomic_u32(ap, self->spot.base + reg, p_value); } int arm_cti_pulse_channel(struct arm_cti *self, uint32_t channel) @@ -225,7 +225,7 @@ static int cti_find_reg_offset(const char *name) int arm_cti_cleanup_all(void) { - struct arm_cti_object *obj, *tmp; + struct arm_cti *obj, *tmp; list_for_each_entry_safe(obj, tmp, &all_cti, lh) { free(obj->name); @@ -237,16 +237,16 @@ int arm_cti_cleanup_all(void) COMMAND_HANDLER(handle_cti_dump) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; + struct adiv5_ap *ap = dap_ap(cti->spot.dap, cti->spot.ap_num); int retval = ERROR_OK; for (int i = 0; (retval == ERROR_OK) && (i < (int)ARRAY_SIZE(cti_names)); i++) - retval = mem_ap_read_u32(cti->ap, - cti->base + cti_names[i].offset, cti_names[i].p_val); + retval = mem_ap_read_u32(ap, + cti->spot.base + cti_names[i].offset, cti_names[i].p_val); if (retval == ERROR_OK) - retval = dap_run(cti->ap->dap); + retval = dap_run(ap->dap); if (retval != ERROR_OK) return JIM_ERR; @@ -260,8 +260,7 @@ COMMAND_HANDLER(handle_cti_dump) COMMAND_HANDLER(handle_cti_enable) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; bool on_off; if (CMD_ARGC != 1) @@ -274,8 +273,7 @@ COMMAND_HANDLER(handle_cti_enable) COMMAND_HANDLER(handle_cti_testmode) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; bool on_off; if (CMD_ARGC != 1) @@ -288,8 +286,7 @@ COMMAND_HANDLER(handle_cti_testmode) COMMAND_HANDLER(handle_cti_write) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; int offset; uint32_t value; @@ -307,8 +304,7 @@ COMMAND_HANDLER(handle_cti_write) COMMAND_HANDLER(handle_cti_read) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; int offset; int retval; uint32_t value; @@ -331,8 +327,7 @@ COMMAND_HANDLER(handle_cti_read) COMMAND_HANDLER(handle_cti_ack) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; uint32_t event; if (CMD_ARGC != 1) @@ -351,8 +346,7 @@ COMMAND_HANDLER(handle_cti_ack) COMMAND_HANDLER(handle_cti_channel) { - struct arm_cti_object *obj = CMD_DATA; - struct arm_cti *cti = &obj->cti; + struct arm_cti *cti = CMD_DATA; int retval = ERROR_OK; uint32_t ch_num; @@ -436,83 +430,26 @@ static const struct command_registration cti_instance_command_handlers[] = { COMMAND_REGISTRATION_DONE }; -enum cti_cfg_param { - CFG_DAP, - CFG_AP_NUM, - CFG_CTIBASE -}; - -static const Jim_Nvp nvp_config_opts[] = { - { .name = "-dap", .value = CFG_DAP }, - { .name = "-ctibase", .value = CFG_CTIBASE }, - { .name = "-ap-num", .value = CFG_AP_NUM }, - { .name = NULL, .value = -1 } -}; - -static int cti_configure(Jim_GetOptInfo *goi, struct arm_cti_object *cti) +static int cti_configure(Jim_GetOptInfo *goi, struct arm_cti *cti) { - struct adiv5_dap *dap = NULL; - Jim_Nvp *n; - jim_wide w; - int e; - /* parse config or cget options ... */ while (goi->argc > 0) { - Jim_SetEmptyResult(goi->interp); - - e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n); - if (e != JIM_OK) { - Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0); + int e = adiv5_jim_mem_ap_spot_configure(&cti->spot, goi); + if (e != JIM_OK) return e; - } - switch (n->value) { - case CFG_DAP: { - Jim_Obj *o_t; - e = Jim_GetOpt_Obj(goi, &o_t); - if (e != JIM_OK) - return e; - dap = dap_instance_by_jim_obj(goi->interp, o_t); - if (dap == NULL) { - Jim_SetResultString(goi->interp, "-dap is invalid", -1); - return JIM_ERR; - } - /* loop for more */ - break; - } - case CFG_CTIBASE: - e = Jim_GetOpt_Wide(goi, &w); - if (e != JIM_OK) - return e; - cti->cti.base = (uint32_t)w; - /* loop for more */ - break; - - case CFG_AP_NUM: - e = Jim_GetOpt_Wide(goi, &w); - if (e != JIM_OK) - return e; - 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; - } } - if (dap == NULL) { + if (!cti->spot.dap) { Jim_SetResultString(goi->interp, "-dap required when creating CTI", -1); return JIM_ERR; } - cti->cti.ap = dap_ap(dap, cti->ap_num); - return JIM_OK; } - static int cti_create(Jim_GetOptInfo *goi) { struct command_context *cmd_ctx; - static struct arm_cti_object *cti; + static struct arm_cti *cti; Jim_Obj *new_cmd; Jim_Cmd *cmd; const char *cp; @@ -536,10 +473,14 @@ static int cti_create(Jim_GetOptInfo *goi) } /* Create it */ - cti = calloc(1, sizeof(struct arm_cti_object)); + cti = calloc(1, sizeof(*cti)); if (cti == NULL) return JIM_ERR; + adiv5_mem_ap_spot_init(&cti->spot); + + /* Do the rest as "configure" options */ + goi->isconfigure = 1; e = cti_configure(goi, cti); if (e != JIM_OK) { free(cti); @@ -593,7 +534,7 @@ static int jim_cti_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv) static int jim_cti_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - struct arm_cti_object *obj; + struct arm_cti *obj; if (argc != 1) { Jim_WrongNumArgs(interp, 1, argv, "Too many parameters"); diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 61f1e78..723be57 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -315,7 +315,7 @@ int arm_semihosting(struct target *target, int *retval) if (0 <= semihosting->op && semihosting->op <= 0x31) { *retval = semihosting_common(target); if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation"); + LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op); return 0; } } else { diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 7da28e3..b725853 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -856,6 +856,9 @@ COMMAND_HANDLER(handle_armv4_5_reg_command) char *sep = "\n"; char *shadow = ""; + if (!arm_mode_data[mode].n_indices) + continue; + /* label this bank of registers (or shadows) */ switch (arm_mode_data[mode].psr) { case ARM_MODE_SYS: @@ -869,6 +872,7 @@ COMMAND_HANDLER(handle_armv4_5_reg_command) continue; /* FALLTHROUGH */ case ARM_MODE_MON: + case ARM_MODE_1176_MON: if (arm->core_type != ARM_CORE_TYPE_SEC_EXT && arm->core_type != ARM_CORE_TYPE_VIRT_EXT) continue; diff --git a/src/target/armv7a.c b/src/target/armv7a.c index c36744d..abca335 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -589,7 +589,7 @@ static const struct command_registration l2_cache_commands[] = { }; -const struct command_registration l2x_cache_command_handlers[] = { +static const struct command_registration l2x_cache_command_handlers[] = { { .name = "cache_config", .mode = COMMAND_EXEC, diff --git a/src/target/armv7a_cache.c b/src/target/armv7a_cache.c index e5f1fb0..fa6df2a 100644 --- a/src/target/armv7a_cache.c +++ b/src/target/armv7a_cache.c @@ -572,7 +572,7 @@ static const struct command_registration arm7a_l1_i_cache_commands[] = { COMMAND_REGISTRATION_DONE }; -const struct command_registration arm7a_l1_di_cache_group_handlers[] = { +static const struct command_registration arm7a_l1_di_cache_group_handlers[] = { { .name = "info", .handler = arm7a_l1_cache_info_cmd, @@ -597,7 +597,7 @@ const struct command_registration arm7a_l1_di_cache_group_handlers[] = { COMMAND_REGISTRATION_DONE }; -const struct command_registration arm7a_cache_group_handlers[] = { +static const struct command_registration arm7a_cache_group_handlers[] = { { .name = "auto", .handler = arm7a_cache_disable_auto_cmd, diff --git a/src/target/armv7m.c b/src/target/armv7m.c index ea6ee61..f14ce0d 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -14,6 +14,9 @@ * Copyright (C) 2018 by Liviu Ionescu * * <ilg@livius.net> * * * + * Copyright (C) 2019 by Tomas Vanek * + * vanekt@fbl.cz * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -108,10 +111,19 @@ static const struct { { ARMV7M_MSP, "msp", 32, REG_TYPE_DATA_PTR, "system", "org.gnu.gdb.arm.m-system" }, { ARMV7M_PSP, "psp", 32, REG_TYPE_DATA_PTR, "system", "org.gnu.gdb.arm.m-system" }, + /* A working register for packing/unpacking special regs, hidden from gdb */ + { ARMV7M_PMSK_BPRI_FLTMSK_CTRL, "pmsk_bpri_fltmsk_ctrl", 32, REG_TYPE_INT, NULL, NULL }, + + /* WARNING: If you use armv7m_write_core_reg() on one of 4 following + * special registers, the new data go to ARMV7M_PMSK_BPRI_FLTMSK_CTRL + * cache only and are not flushed to CPU HW register. + * To trigger write to CPU HW register, add + * armv7m_write_core_reg(,,ARMV7M_PMSK_BPRI_FLTMSK_CTRL,); + */ { ARMV7M_PRIMASK, "primask", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" }, { ARMV7M_BASEPRI, "basepri", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" }, { ARMV7M_FAULTMASK, "faultmask", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" }, - { ARMV7M_CONTROL, "control", 2, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" }, + { ARMV7M_CONTROL, "control", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" }, { ARMV7M_D0, "d0", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" }, { ARMV7M_D1, "d1", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" }, @@ -150,6 +162,9 @@ int armv7m_restore_context(struct target *target) if (armv7m->pre_restore_context) armv7m->pre_restore_context(target); + /* The descending order of register writes is crucial for correct + * packing of ARMV7M_PMSK_BPRI_FLTMSK_CTRL! + * See also comments in the register table above */ for (i = cache->num_regs - 1; i >= 0; i--) { if (cache->reg_list[i].dirty) { armv7m->arm.write_core_reg(target, &cache->reg_list[i], i, @@ -211,87 +226,195 @@ static int armv7m_set_core_reg(struct reg *reg, uint8_t *buf) return ERROR_OK; } +static uint32_t armv7m_map_id_to_regsel(unsigned int arm_reg_id) +{ + switch (arm_reg_id) { + case ARMV7M_R0 ... ARMV7M_R14: + case ARMV7M_PC: + case ARMV7M_xPSR: + case ARMV7M_MSP: + case ARMV7M_PSP: + /* NOTE: we "know" here that the register identifiers + * match the Cortex-M DCRSR.REGSEL selectors values + * for R0..R14, PC, xPSR, MSP, and PSP. + */ + return arm_reg_id; + + case ARMV7M_PMSK_BPRI_FLTMSK_CTRL: + return ARMV7M_REGSEL_PMSK_BPRI_FLTMSK_CTRL; + + case ARMV7M_FPSCR: + return ARMV7M_REGSEL_FPSCR; + + case ARMV7M_D0 ... ARMV7M_D15: + return ARMV7M_REGSEL_S0 + 2 * (arm_reg_id - ARMV7M_D0); + + default: + LOG_ERROR("Bad register ID %u", arm_reg_id); + return arm_reg_id; + } +} + +static bool armv7m_map_reg_packing(unsigned int arm_reg_id, + unsigned int *reg32_id, uint32_t *offset) +{ + switch (arm_reg_id) { + + case ARMV7M_PRIMASK: + *reg32_id = ARMV7M_PMSK_BPRI_FLTMSK_CTRL; + *offset = 0; + return true; + case ARMV7M_BASEPRI: + *reg32_id = ARMV7M_PMSK_BPRI_FLTMSK_CTRL; + *offset = 1; + return true; + case ARMV7M_FAULTMASK: + *reg32_id = ARMV7M_PMSK_BPRI_FLTMSK_CTRL; + *offset = 2; + return true; + case ARMV7M_CONTROL: + *reg32_id = ARMV7M_PMSK_BPRI_FLTMSK_CTRL; + *offset = 3; + return true; + + default: + return false; + } +} + static int armv7m_read_core_reg(struct target *target, struct reg *r, int num, enum arm_mode mode) { uint32_t reg_value; int retval; - struct arm_reg *armv7m_core_reg; struct armv7m_common *armv7m = target_to_armv7m(target); assert(num < (int)armv7m->arm.core_cache->num_regs); + assert(num == (int)r->number); - armv7m_core_reg = armv7m->arm.core_cache->reg_list[num].arch_info; + /* If a code calls read_reg, it expects the cache is no more dirty. + * Clear the dirty flag regardless of the later read succeeds or not + * to prevent unwanted cache flush after a read error */ + r->dirty = false; + + if (r->size <= 8) { + /* any 8-bit or shorter register is packed */ + uint32_t offset = 0; /* silence false gcc warning */ + unsigned int reg32_id; + + bool is_packed = armv7m_map_reg_packing(num, ®32_id, &offset); + assert(is_packed); + struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id]; + + /* Read 32-bit container register if not cached */ + if (!r32->valid) { + retval = armv7m_read_core_reg(target, r32, reg32_id, mode); + if (retval != ERROR_OK) + return retval; + } + + /* Copy required bits of 32-bit container register */ + buf_cpy(r32->value + offset, r->value, r->size); - if ((armv7m_core_reg->num >= ARMV7M_D0) && (armv7m_core_reg->num <= ARMV7M_D15)) { - /* map D0..D15 to S0..S31 */ - size_t regidx = ARMV7M_S0 + 2 * (armv7m_core_reg->num - ARMV7M_D0); - retval = armv7m->load_core_reg_u32(target, regidx, ®_value); - if (retval != ERROR_OK) - return retval; - buf_set_u32(armv7m->arm.core_cache->reg_list[num].value, - 0, 32, reg_value); - retval = armv7m->load_core_reg_u32(target, regidx + 1, ®_value); - if (retval != ERROR_OK) - return retval; - buf_set_u32(armv7m->arm.core_cache->reg_list[num].value + 4, - 0, 32, reg_value); } else { - retval = armv7m->load_core_reg_u32(target, - armv7m_core_reg->num, ®_value); + assert(r->size == 32 || r->size == 64); + + struct arm_reg *armv7m_core_reg = r->arch_info; + uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num); + + retval = armv7m->load_core_reg_u32(target, regsel, ®_value); if (retval != ERROR_OK) return retval; - buf_set_u32(armv7m->arm.core_cache->reg_list[num].value, 0, 32, reg_value); + buf_set_u32(r->value, 0, 32, reg_value); + + if (r->size == 64) { + retval = armv7m->load_core_reg_u32(target, regsel + 1, ®_value); + if (retval != ERROR_OK) { + r->valid = false; + return retval; + } + buf_set_u32(r->value + 4, 0, 32, reg_value); + + uint64_t q = buf_get_u64(r->value, 0, 64); + LOG_DEBUG("read %s value 0x%016" PRIx64, r->name, q); + } else { + LOG_DEBUG("read %s value 0x%08" PRIx32, r->name, reg_value); + } } - armv7m->arm.core_cache->reg_list[num].valid = true; - armv7m->arm.core_cache->reg_list[num].dirty = false; + r->valid = true; - return retval; + return ERROR_OK; } static int armv7m_write_core_reg(struct target *target, struct reg *r, int num, enum arm_mode mode, uint8_t *value) { int retval; - struct arm_reg *armv7m_core_reg; + uint32_t t; struct armv7m_common *armv7m = target_to_armv7m(target); assert(num < (int)armv7m->arm.core_cache->num_regs); + assert(num == (int)r->number); - armv7m_core_reg = armv7m->arm.core_cache->reg_list[num].arch_info; + if (value != r->value) { + /* If we are not flushing the cache, store the new value to the cache */ + buf_cpy(value, r->value, r->size); + } - if ((armv7m_core_reg->num >= ARMV7M_D0) && (armv7m_core_reg->num <= ARMV7M_D15)) { - /* map D0..D15 to S0..S31 */ - size_t regidx = ARMV7M_S0 + 2 * (armv7m_core_reg->num - ARMV7M_D0); + if (r->size <= 8) { + /* any 8-bit or shorter register is packed */ + uint32_t offset = 0; /* silence false gcc warning */ + unsigned int reg32_id; - uint32_t t = buf_get_u32(value, 0, 32); - retval = armv7m->store_core_reg_u32(target, regidx, t); - if (retval != ERROR_OK) - goto out_error; + bool is_packed = armv7m_map_reg_packing(num, ®32_id, &offset); + assert(is_packed); + struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id]; + + if (!r32->valid) { + /* Before merging with other parts ensure the 32-bit register is valid */ + retval = armv7m_read_core_reg(target, r32, reg32_id, mode); + if (retval != ERROR_OK) + return retval; + } + + /* Write a part to the 32-bit container register */ + buf_cpy(value, r32->value + offset, r->size); + r32->dirty = true; - t = buf_get_u32(value + 4, 0, 32); - retval = armv7m->store_core_reg_u32(target, regidx + 1, t); - if (retval != ERROR_OK) - goto out_error; } else { - uint32_t t = buf_get_u32(value, 0, 32); + assert(r->size == 32 || r->size == 64); + + struct arm_reg *armv7m_core_reg = r->arch_info; + uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num); - LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", num, t); - retval = armv7m->store_core_reg_u32(target, armv7m_core_reg->num, t); + t = buf_get_u32(value, 0, 32); + retval = armv7m->store_core_reg_u32(target, regsel, t); if (retval != ERROR_OK) goto out_error; + + if (r->size == 64) { + t = buf_get_u32(value + 4, 0, 32); + retval = armv7m->store_core_reg_u32(target, regsel + 1, t); + if (retval != ERROR_OK) + goto out_error; + + uint64_t q = buf_get_u64(value, 0, 64); + LOG_DEBUG("write %s value 0x%016" PRIx64, r->name, q); + } else { + LOG_DEBUG("write %s value 0x%08" PRIx32, r->name, t); + } } - armv7m->arm.core_cache->reg_list[num].valid = true; - armv7m->arm.core_cache->reg_list[num].dirty = false; + r->valid = true; + r->dirty = false; return ERROR_OK; out_error: - LOG_ERROR("Error setting register"); - armv7m->arm.core_cache->reg_list[num].dirty = armv7m->arm.core_cache->reg_list[num].valid; - return ERROR_JTAG_DEVICE_ERROR; + r->dirty = true; + LOG_ERROR("Error setting register %s", r->name); + return retval; } /** @@ -370,8 +493,7 @@ int armv7m_start_algorithm(struct target *target, return ERROR_TARGET_NOT_HALTED; } - /* refresh core register cache - * Not needed if core register cache is always consistent with target process state */ + /* Store all non-debug execution registers to armv7m_algorithm_info context */ for (unsigned i = 0; i < armv7m->arm.core_cache->num_regs; i++) { armv7m_algorithm_info->context[i] = buf_get_u32( @@ -618,12 +740,10 @@ struct reg_cache *armv7m_build_reg_cache(struct target *target) reg_list[i].name = armv7m_regs[i].name; reg_list[i].size = armv7m_regs[i].bits; - size_t storage_size = DIV_ROUND_UP(armv7m_regs[i].bits, 8); - if (storage_size < 4) - storage_size = 4; - reg_list[i].value = calloc(1, storage_size); + reg_list[i].value = arch_info[i].value; reg_list[i].dirty = false; reg_list[i].valid = false; + reg_list[i].hidden = i == ARMV7M_PMSK_BPRI_FLTMSK_CTRL; reg_list[i].type = &armv7m_reg_type; reg_list[i].arch_info = &arch_info[i]; @@ -632,6 +752,9 @@ struct reg_cache *armv7m_build_reg_cache(struct target *target) reg_list[i].exist = true; reg_list[i].caller_save = true; /* gdb defaults to true */ + if (reg_list[i].hidden) + continue; + feature = calloc(1, sizeof(struct reg_feature)); if (feature) { feature->name = armv7m_regs[i].feature; @@ -671,7 +794,6 @@ void armv7m_free_reg_cache(struct target *target) free(reg->feature); free(reg->reg_data_type); - free(reg->value); } free(cache->reg_list[0].arch_info); diff --git a/src/target/armv7m.h b/src/target/armv7m.h index 01bf19e..db6f8bc 100644 --- a/src/target/armv7m.h +++ b/src/target/armv7m.h @@ -34,75 +34,115 @@ extern const int armv7m_msp_reg_map[]; const char *armv7m_exception_string(int number); +/* Cortex-M DCRSR.REGSEL selectors */ +enum { + ARMV7M_REGSEL_R0, + ARMV7M_REGSEL_R1, + ARMV7M_REGSEL_R2, + ARMV7M_REGSEL_R3, + + ARMV7M_REGSEL_R4, + ARMV7M_REGSEL_R5, + ARMV7M_REGSEL_R6, + ARMV7M_REGSEL_R7, + + ARMV7M_REGSEL_R8, + ARMV7M_REGSEL_R9, + ARMV7M_REGSEL_R10, + ARMV7M_REGSEL_R11, + + ARMV7M_REGSEL_R12, + ARMV7M_REGSEL_R13, + ARMV7M_REGSEL_R14, + ARMV7M_REGSEL_PC = 15, + + ARMV7M_REGSEL_xPSR = 16, + ARMV7M_REGSEL_MSP, + ARMV7M_REGSEL_PSP, + + ARMV7M_REGSEL_PMSK_BPRI_FLTMSK_CTRL = 0x14, + ARMV7M_REGSEL_FPSCR = 0x21, + + /* 32bit Floating-point registers */ + ARMV7M_REGSEL_S0 = 0x40, + ARMV7M_REGSEL_S1, + ARMV7M_REGSEL_S2, + ARMV7M_REGSEL_S3, + ARMV7M_REGSEL_S4, + ARMV7M_REGSEL_S5, + ARMV7M_REGSEL_S6, + ARMV7M_REGSEL_S7, + ARMV7M_REGSEL_S8, + ARMV7M_REGSEL_S9, + ARMV7M_REGSEL_S10, + ARMV7M_REGSEL_S11, + ARMV7M_REGSEL_S12, + ARMV7M_REGSEL_S13, + ARMV7M_REGSEL_S14, + ARMV7M_REGSEL_S15, + ARMV7M_REGSEL_S16, + ARMV7M_REGSEL_S17, + ARMV7M_REGSEL_S18, + ARMV7M_REGSEL_S19, + ARMV7M_REGSEL_S20, + ARMV7M_REGSEL_S21, + ARMV7M_REGSEL_S22, + ARMV7M_REGSEL_S23, + ARMV7M_REGSEL_S24, + ARMV7M_REGSEL_S25, + ARMV7M_REGSEL_S26, + ARMV7M_REGSEL_S27, + ARMV7M_REGSEL_S28, + ARMV7M_REGSEL_S29, + ARMV7M_REGSEL_S30, + ARMV7M_REGSEL_S31, +}; + /* offsets into armv7m core register cache */ enum { /* for convenience, the first set of indices match - * the Cortex-M3/-M4 DCRSR selectors + * the Cortex-M DCRSR.REGSEL selectors + */ + ARMV7M_R0 = ARMV7M_REGSEL_R0, + ARMV7M_R1 = ARMV7M_REGSEL_R1, + ARMV7M_R2 = ARMV7M_REGSEL_R2, + ARMV7M_R3 = ARMV7M_REGSEL_R3, + + ARMV7M_R4 = ARMV7M_REGSEL_R4, + ARMV7M_R5 = ARMV7M_REGSEL_R5, + ARMV7M_R6 = ARMV7M_REGSEL_R6, + ARMV7M_R7 = ARMV7M_REGSEL_R7, + + ARMV7M_R8 = ARMV7M_REGSEL_R8, + ARMV7M_R9 = ARMV7M_REGSEL_R9, + ARMV7M_R10 = ARMV7M_REGSEL_R10, + ARMV7M_R11 = ARMV7M_REGSEL_R11, + + ARMV7M_R12 = ARMV7M_REGSEL_R12, + ARMV7M_R13 = ARMV7M_REGSEL_R13, + ARMV7M_R14 = ARMV7M_REGSEL_R14, + ARMV7M_PC = ARMV7M_REGSEL_PC, + + ARMV7M_xPSR = ARMV7M_REGSEL_xPSR, + ARMV7M_MSP = ARMV7M_REGSEL_MSP, + ARMV7M_PSP = ARMV7M_REGSEL_PSP, + + /* following indices are arbitrary, do not match DCRSR.REGSEL selectors */ + + /* working register for packing/unpacking special regs, hidden from gdb */ + ARMV7M_PMSK_BPRI_FLTMSK_CTRL, + + /* WARNING: If you use armv7m_write_core_reg() on one of 4 following + * special registers, the new data go to ARMV7M_PMSK_BPRI_FLTMSK_CTRL + * cache only and are not flushed to CPU HW register. + * To trigger write to CPU HW register, add + * armv7m_write_core_reg(,,ARMV7M_PMSK_BPRI_FLTMSK_CTRL,); */ - ARMV7M_R0, - ARMV7M_R1, - ARMV7M_R2, - ARMV7M_R3, - - ARMV7M_R4, - ARMV7M_R5, - ARMV7M_R6, - ARMV7M_R7, - - ARMV7M_R8, - ARMV7M_R9, - ARMV7M_R10, - ARMV7M_R11, - - ARMV7M_R12, - ARMV7M_R13, - ARMV7M_R14, - ARMV7M_PC = 15, - - ARMV7M_xPSR = 16, - ARMV7M_MSP, - ARMV7M_PSP, - - /* this next set of indices is arbitrary */ ARMV7M_PRIMASK, ARMV7M_BASEPRI, ARMV7M_FAULTMASK, ARMV7M_CONTROL, - /* 32bit Floating-point registers */ - ARMV7M_S0, - ARMV7M_S1, - ARMV7M_S2, - ARMV7M_S3, - ARMV7M_S4, - ARMV7M_S5, - ARMV7M_S6, - ARMV7M_S7, - ARMV7M_S8, - ARMV7M_S9, - ARMV7M_S10, - ARMV7M_S11, - ARMV7M_S12, - ARMV7M_S13, - ARMV7M_S14, - ARMV7M_S15, - ARMV7M_S16, - ARMV7M_S17, - ARMV7M_S18, - ARMV7M_S19, - ARMV7M_S20, - ARMV7M_S21, - ARMV7M_S22, - ARMV7M_S23, - ARMV7M_S24, - ARMV7M_S25, - ARMV7M_S26, - ARMV7M_S27, - ARMV7M_S28, - ARMV7M_S29, - ARMV7M_S30, - ARMV7M_S31, - /* 64bit Floating-point registers */ ARMV7M_D0, ARMV7M_D1, @@ -121,10 +161,8 @@ enum { ARMV7M_D14, ARMV7M_D15, - /* Floating-point status registers */ - ARMV7M_FPSID, + /* Floating-point status register */ ARMV7M_FPSCR, - ARMV7M_FPEXC, ARMV7M_LAST_REG, }; @@ -137,7 +175,7 @@ enum { }; #define ARMV7M_NUM_CORE_REGS (ARMV7M_xPSR + 1) -#define ARMV7M_NUM_CORE_REGS_NOFP (ARMV7M_NUM_CORE_REGS + 6) +#define ARMV7M_NUM_CORE_REGS_NOFP (ARMV7M_CONTROL + 1) #define ARMV7M_COMMON_MAGIC 0x2A452A45 @@ -159,8 +197,8 @@ struct armv7m_common { struct armv7m_trace_config trace_config; /* Direct processor core register read and writes */ - int (*load_core_reg_u32)(struct target *target, uint32_t num, uint32_t *value); - int (*store_core_reg_u32)(struct target *target, uint32_t num, uint32_t value); + int (*load_core_reg_u32)(struct target *target, uint32_t regsel, uint32_t *value); + int (*store_core_reg_u32)(struct target *target, uint32_t regsel, uint32_t value); int (*examine_debug_reason)(struct target *target); int (*post_debug_entry)(struct target *target); diff --git a/src/target/armv7m_trace.c b/src/target/armv7m_trace.c index 6b368f7..10f1422 100644 --- a/src/target/armv7m_trace.c +++ b/src/target/armv7m_trace.c @@ -40,13 +40,43 @@ static int armv7m_poll_trace(void *target) target_call_trace_callbacks(target, size, buf); - if (armv7m->trace_config.trace_file != NULL) { - if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size) - fflush(armv7m->trace_config.trace_file); - else { - LOG_ERROR("Error writing to the trace destination file"); - return ERROR_FAIL; + switch (armv7m->trace_config.internal_channel) { + case TRACE_INTERNAL_CHANNEL_FILE: + if (armv7m->trace_config.trace_file != NULL) { + if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size) + fflush(armv7m->trace_config.trace_file); + else { + LOG_ERROR("Error writing to the trace destination file"); + return ERROR_FAIL; + } + } + break; + case TRACE_INTERNAL_CHANNEL_TCP: + if (armv7m->trace_config.trace_service != NULL) { + /* broadcast to all service connections */ + struct connection *connection = armv7m->trace_config.trace_service->connections; + retval = ERROR_OK; + while (connection) { + if (connection_write(connection, buf, size) != (int) size) + retval = ERROR_FAIL; + + connection = connection->next; + } + + if (retval != ERROR_OK) { + LOG_ERROR("Error streaming the trace to TCP/IP port"); + return ERROR_FAIL; + } } + break; + case TRACE_INTERNAL_CHANNEL_TCL_ONLY: + /* nothing to do : + * the trace data is sent to TCL by calling the target_call_trace_callbacks + **/ + break; + default: + LOG_ERROR("unsupported trace internal channel"); + return ERROR_FAIL; } return ERROR_OK; @@ -152,11 +182,71 @@ int armv7m_trace_itm_config(struct target *target) return ERROR_OK; } -static void close_trace_file(struct armv7m_common *armv7m) +static void close_trace_channel(struct armv7m_common *armv7m) { - if (armv7m->trace_config.trace_file) - fclose(armv7m->trace_config.trace_file); - armv7m->trace_config.trace_file = NULL; + switch (armv7m->trace_config.internal_channel) { + case TRACE_INTERNAL_CHANNEL_FILE: + if (armv7m->trace_config.trace_file) + fclose(armv7m->trace_config.trace_file); + armv7m->trace_config.trace_file = NULL; + break; + case TRACE_INTERNAL_CHANNEL_TCP: + if (armv7m->trace_config.trace_service) + remove_service(armv7m->trace_config.trace_service->name, armv7m->trace_config.trace_service->port); + armv7m->trace_config.trace_service = NULL; + break; + case TRACE_INTERNAL_CHANNEL_TCL_ONLY: + /* nothing to do: + * the trace polling is disabled in the beginning of armv7m_trace_tpiu_config + **/ + break; + default: + LOG_ERROR("unsupported trace internal channel"); + } +} + +static int trace_new_connection(struct connection *connection) +{ + /* nothing to do */ + return ERROR_OK; +} + +static int trace_input(struct connection *connection) +{ + /* create a dummy buffer to check if the connection is still active */ + const int buf_len = 100; + unsigned char buf[buf_len]; + int bytes_read = connection_read(connection, buf, buf_len); + + if (bytes_read == 0) + return ERROR_SERVER_REMOTE_CLOSED; + else if (bytes_read == -1) { + LOG_ERROR("error during read: %s", strerror(errno)); + return ERROR_SERVER_REMOTE_CLOSED; + } + + return ERROR_OK; +} + +static int trace_connection_closed(struct connection *connection) +{ + /* nothing to do, no connection->priv to free */ + return ERROR_OK; +} + +extern struct command_context *global_cmd_ctx; + +int armv7m_trace_tpiu_exit(struct target *target) +{ + struct armv7m_common *armv7m = target_to_armv7m(target); + + if (global_cmd_ctx->mode == COMMAND_CONFIG || + armv7m->trace_config.config_type == TRACE_CONFIG_TYPE_DISABLED) + return ERROR_OK; + + close_trace_channel(armv7m); + armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED; + return armv7m_trace_tpiu_config(target); } COMMAND_HANDLER(handle_tpiu_config_command) @@ -170,7 +260,7 @@ COMMAND_HANDLER(handle_tpiu_config_command) return ERROR_COMMAND_SYNTAX_ERROR; if (!strcmp(CMD_ARGV[cmd_idx], "disable")) { if (CMD_ARGC == cmd_idx + 1) { - close_trace_file(armv7m); + close_trace_channel(armv7m); armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED; if (CMD_CTX->mode == COMMAND_EXEC) @@ -180,7 +270,7 @@ COMMAND_HANDLER(handle_tpiu_config_command) } } else if (!strcmp(CMD_ARGV[cmd_idx], "external") || !strcmp(CMD_ARGV[cmd_idx], "internal")) { - close_trace_file(armv7m); + close_trace_channel(armv7m); armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL; if (!strcmp(CMD_ARGV[cmd_idx], "internal")) { @@ -189,12 +279,26 @@ COMMAND_HANDLER(handle_tpiu_config_command) return ERROR_COMMAND_SYNTAX_ERROR; armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL; + armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCL_ONLY; if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) { - armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab"); - if (!armv7m->trace_config.trace_file) { - LOG_ERROR("Can't open trace destination file"); - return ERROR_FAIL; + if (CMD_ARGV[cmd_idx][0] == ':') { + armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCP; + + int ret = add_service("armv7m_trace", &(CMD_ARGV[cmd_idx][1]), + CONNECTION_LIMIT_UNLIMITED, trace_new_connection, trace_input, + trace_connection_closed, NULL, &armv7m->trace_config.trace_service); + if (ret != ERROR_OK) { + LOG_ERROR("Can't configure trace TCP port"); + return ERROR_FAIL; + } + } else { + armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_FILE; + armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab"); + if (!armv7m->trace_config.trace_file) { + LOG_ERROR("Can't open trace destination file"); + return ERROR_FAIL; + } } } } @@ -306,7 +410,7 @@ static const struct command_registration tpiu_command_handlers[] = { .mode = COMMAND_ANY, .help = "Configure TPIU features", .usage = "(disable | " - "((external | internal <filename>) " + "((external | internal (<filename> | <:port> | -)) " "(sync <port width> | ((manchester | uart) <formatter enable>)) " "<TRACECLKIN freq> [<trace freq>]))", }, diff --git a/src/target/armv7m_trace.h b/src/target/armv7m_trace.h index e5879fb..cdf79e7 100644 --- a/src/target/armv7m_trace.h +++ b/src/target/armv7m_trace.h @@ -18,6 +18,7 @@ #ifndef OPENOCD_TARGET_ARMV7M_TRACE_H #define OPENOCD_TARGET_ARMV7M_TRACE_H +#include <server/server.h> #include <target/target.h> #include <command.h> @@ -32,8 +33,14 @@ enum trace_config_type { TRACE_CONFIG_TYPE_INTERNAL /**< trace output is handled by OpenOCD adapter driver */ }; +enum trace_internal_channel { + TRACE_INTERNAL_CHANNEL_TCL_ONLY, /** trace data is sent only to 'tcl_trace' */ + TRACE_INTERNAL_CHANNEL_FILE, /** trace data is appended to a file */ + TRACE_INTERNAL_CHANNEL_TCP /** trace data is appended to a TCP/IP port*/ +}; + enum tpiu_pin_protocol { - TPIU_PIN_PROTOCOL_SYNC, /**< synchronous trace output */ + TPIU_PIN_PROTOCOL_SYNC, /**< synchronous trace output */ TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */ TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */ }; @@ -49,6 +56,9 @@ struct armv7m_trace_config { /** Currently active trace capture mode */ enum trace_config_type config_type; + /** The used channel when internal mode is selected */ + enum trace_internal_channel internal_channel; + /** Currently active trace output mode */ enum tpiu_pin_protocol pin_protocol; /** TPIU formatter enable/disable (in async mode) */ @@ -73,8 +83,10 @@ struct armv7m_trace_config { unsigned int traceclkin_freq; /** Current frequency of trace port */ unsigned int trace_freq; - /** Handle to output trace data in INTERNAL capture mode */ + /** Handle to output trace data in INTERNAL capture mode via file */ FILE *trace_file; + /** Handle to output trace data in INTERNAL capture mode via tcp */ + struct service *trace_service; }; extern const struct command_registration armv7m_trace_command_handlers[]; @@ -84,6 +96,10 @@ extern const struct command_registration armv7m_trace_command_handlers[]; */ int armv7m_trace_tpiu_config(struct target *target); /** + * Disable TPIU data gathering at exit + */ +int armv7m_trace_tpiu_exit(struct target *target); +/** * Configure hardware accordingly to the current ITM target settings */ int armv7m_trace_itm_config(struct target *target); diff --git a/src/target/armv8.c b/src/target/armv8.c index ab60cd3..95efdc9 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -1126,7 +1126,7 @@ int armv8_init_arch_info(struct target *target, struct armv8_common *armv8) return ERROR_OK; } -int armv8_aarch64_state(struct target *target) +static int armv8_aarch64_state(struct target *target) { struct arm *arm = target_to_arm(target); diff --git a/src/target/avr32_ap7k.c b/src/target/avr32_ap7k.c index 6221059..b0c0875 100644 --- a/src/target/avr32_ap7k.c +++ b/src/target/avr32_ap7k.c @@ -63,7 +63,7 @@ static const struct avr32_core_reg static int avr32_read_core_reg(struct target *target, int num); static int avr32_write_core_reg(struct target *target, int num); -int avr32_ap7k_save_context(struct target *target) +static int avr32_ap7k_save_context(struct target *target) { int retval, i; struct avr32_ap7k_common *ap7k = target_to_ap7k(target); @@ -80,7 +80,7 @@ int avr32_ap7k_save_context(struct target *target) return ERROR_OK; } -int avr32_ap7k_restore_context(struct target *target) +static int avr32_ap7k_restore_context(struct target *target) { int i; @@ -555,7 +555,7 @@ static int avr32_ap7k_examine(struct target *target) return ERROR_OK; } -int avr32_ap7k_arch_state(struct target *target) +static int avr32_ap7k_arch_state(struct target *target) { struct avr32_ap7k_common *ap7k = target_to_ap7k(target); @@ -565,7 +565,7 @@ int avr32_ap7k_arch_state(struct target *target) return ERROR_OK; } -int avr32_ap7k_get_gdb_reg_list(struct target *target, struct reg **reg_list[], +static int avr32_ap7k_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class) { #if 0 diff --git a/src/target/avr32_jtag.c b/src/target/avr32_jtag.c index 64ebf12..62c8f98 100644 --- a/src/target/avr32_jtag.c +++ b/src/target/avr32_jtag.c @@ -55,7 +55,7 @@ static int avr32_jtag_set_instr(struct avr32_jtag *jtag_info, int new_instr) return ERROR_OK; } -int avr32_jtag_nexus_set_address(struct avr32_jtag *jtag_info, +static int avr32_jtag_nexus_set_address(struct avr32_jtag *jtag_info, uint32_t addr, int mode) { struct scan_field fields[2]; @@ -92,7 +92,7 @@ int avr32_jtag_nexus_set_address(struct avr32_jtag *jtag_info, } -int avr32_jtag_nexus_read_data(struct avr32_jtag *jtag_info, +static int avr32_jtag_nexus_read_data(struct avr32_jtag *jtag_info, uint32_t *pdata) { @@ -129,7 +129,7 @@ int avr32_jtag_nexus_read_data(struct avr32_jtag *jtag_info, return ERROR_OK; } -int avr32_jtag_nexus_write_data(struct avr32_jtag *jtag_info, +static int avr32_jtag_nexus_write_data(struct avr32_jtag *jtag_info, uint32_t data) { @@ -184,7 +184,7 @@ int avr32_jtag_nexus_write(struct avr32_jtag *jtag_info, return avr32_jtag_nexus_write_data(jtag_info, value); } -int avr32_jtag_mwa_set_address(struct avr32_jtag *jtag_info, int slave, +static int avr32_jtag_mwa_set_address(struct avr32_jtag *jtag_info, int slave, uint32_t addr, int mode) { struct scan_field fields[2]; @@ -223,7 +223,7 @@ int avr32_jtag_mwa_set_address(struct avr32_jtag *jtag_info, int slave, return ERROR_OK; } -int avr32_jtag_mwa_read_data(struct avr32_jtag *jtag_info, +static int avr32_jtag_mwa_read_data(struct avr32_jtag *jtag_info, uint32_t *pdata) { @@ -260,7 +260,7 @@ int avr32_jtag_mwa_read_data(struct avr32_jtag *jtag_info, return ERROR_OK; } -int avr32_jtag_mwa_write_data(struct avr32_jtag *jtag_info, +static int avr32_jtag_mwa_write_data(struct avr32_jtag *jtag_info, uint32_t data) { diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index bd8e49f..d27c298 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -1115,7 +1115,8 @@ static int cortex_a_post_debug_entry(struct target *target) return ERROR_OK; } -int cortex_a_set_dscr_bits(struct target *target, unsigned long bit_mask, unsigned long value) +static int cortex_a_set_dscr_bits(struct target *target, + unsigned long bit_mask, unsigned long value) { struct armv7a_common *armv7a = target_to_armv7a(target); uint32_t dscr; @@ -1680,10 +1681,10 @@ static int cortex_a_assert_reset(struct target *target) */ /* - * FIXME: fix reset when transport is SWD. This is a temporary + * FIXME: fix reset when transport is not JTAG. This is a temporary * work-around for release v0.10 that is not intended to stay! */ - if (transport_is_swd() || + if (!transport_is_jtag() || (target->reset_halt && (jtag_get_reset_config() & RESET_SRST_NO_GATING))) adapter_assert_reset(); diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index 55664a7..ac308b4 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -39,6 +39,7 @@ #include "arm_opcodes.h" #include "arm_semihosting.h" #include <helper/time_support.h> +#include <rtt/rtt.h> /* NOTE: most of this should work fine for the Cortex-M1 and * Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M. @@ -56,8 +57,8 @@ static int cortex_m_store_core_reg_u32(struct target *target, uint32_t num, uint32_t value); static void cortex_m_dwt_free(struct target *target); -static int cortexm_dap_read_coreregister_u32(struct target *target, - uint32_t *value, int regnum) +static int cortex_m_load_core_reg_u32(struct target *target, + uint32_t regsel, uint32_t *value) { struct armv7m_common *armv7m = target_to_armv7m(target); int retval; @@ -71,7 +72,7 @@ static int cortexm_dap_read_coreregister_u32(struct target *target, return retval; } - retval = mem_ap_write_u32(armv7m->debug_ap, DCB_DCRSR, regnum); + retval = mem_ap_write_u32(armv7m->debug_ap, DCB_DCRSR, regsel); if (retval != ERROR_OK) return retval; @@ -89,8 +90,8 @@ static int cortexm_dap_read_coreregister_u32(struct target *target, return retval; } -static int cortexm_dap_write_coreregister_u32(struct target *target, - uint32_t value, int regnum) +static int cortex_m_store_core_reg_u32(struct target *target, + uint32_t regsel, uint32_t value) { struct armv7m_common *armv7m = target_to_armv7m(target); int retval; @@ -108,7 +109,7 @@ static int cortexm_dap_write_coreregister_u32(struct target *target, if (retval != ERROR_OK) return retval; - retval = mem_ap_write_atomic_u32(armv7m->debug_ap, DCB_DCRSR, regnum | DCRSR_WnR); + retval = mem_ap_write_atomic_u32(armv7m->debug_ap, DCB_DCRSR, regsel | DCRSR_WnR); if (retval != ERROR_OK) return retval; @@ -527,12 +528,6 @@ static int cortex_m_debug_entry(struct target *target) r = arm->cpsr; xPSR = buf_get_u32(r->value, 0, 32); - /* For IT instructions xPSR must be reloaded on resume and clear on debug exec */ - if (xPSR & 0xf00) { - r->dirty = r->valid; - cortex_m_store_core_reg_u32(target, 16, xPSR & ~0xff); - } - /* Are we in an exception handler */ if (xPSR & 0x1FF) { armv7m->exception_number = (xPSR & 0x1FF); @@ -541,7 +536,7 @@ static int cortex_m_debug_entry(struct target *target) arm->map = armv7m_msp_reg_map; } else { unsigned control = buf_get_u32(arm->core_cache - ->reg_list[ARMV7M_CONTROL].value, 0, 2); + ->reg_list[ARMV7M_CONTROL].value, 0, 3); /* is this thread privileged? */ arm->core_mode = control & 1 @@ -656,13 +651,9 @@ static int cortex_m_poll(struct target *target) } } - /* REVISIT when S_SLEEP is set, it's in a Sleep or DeepSleep state. - * How best to model low power modes? - */ - if (target->state == TARGET_UNKNOWN) { - /* check if processor is retiring instructions */ - if (cortex_m->dcb_dhcsr & S_RETIRE_ST) { + /* check if processor is retiring instructions or sleeping */ + if (cortex_m->dcb_dhcsr & S_RETIRE_ST || cortex_m->dcb_dhcsr & S_SLEEP) { target->state = TARGET_RUNNING; retval = ERROR_OK; } @@ -827,15 +818,19 @@ static int cortex_m_resume(struct target *target, int current, * in parallel with disabled interrupts can cause local faults * to not be taken. * - * REVISIT this clearly breaks non-debug execution, since the - * PRIMASK register state isn't saved/restored... workaround - * by never resuming app code after debug execution. + * This breaks non-debug (application) execution if not + * called from armv7m_start_algorithm() which saves registers. */ buf_set_u32(r->value, 0, 1, 1); r->dirty = true; r->valid = true; - /* Make sure we are in Thumb mode */ + /* Make sure we are in Thumb mode, set xPSR.T bit */ + /* armv7m_start_algorithm() initializes entire xPSR register. + * This duplicity handles the case when cortex_m_resume() + * is used with the debug_execution flag directly, + * not called through armv7m_start_algorithm(). + */ r = armv7m->arm.cpsr; buf_set_u32(r->value, 24, 1, 1); r->dirty = true; @@ -1208,11 +1203,13 @@ static int cortex_m_assert_reset(struct target *target) if (retval3 != ERROR_OK) LOG_DEBUG("Ignoring AP write error right after reset"); - retval3 = dap_dp_init(armv7m->debug_ap->dap); - if (retval3 != ERROR_OK) + retval3 = dap_dp_init_or_reconnect(armv7m->debug_ap->dap); + if (retval3 != ERROR_OK) { LOG_ERROR("DP initialisation failed"); - - else { + /* The error return value must not be propagated in this case. + * SYSRESETREQ or VECTRESET have been possibly triggered + * so reset processing should continue */ + } else { /* I do not know why this is necessary, but it * fixes strange effects (step/resume cause NMI * after reset) on LM3S6918 -- Michael Schwingen @@ -1255,7 +1252,8 @@ static int cortex_m_deassert_reset(struct target *target) if ((jtag_reset_config & RESET_HAS_SRST) && !(jtag_reset_config & RESET_SRST_NO_GATING) && target_was_examined(target)) { - int retval = dap_dp_init(armv7m->debug_ap->dap); + + int retval = dap_dp_init_or_reconnect(armv7m->debug_ap->dap); if (retval != ERROR_OK) { LOG_ERROR("DP initialisation failed"); return retval; @@ -1413,7 +1411,7 @@ int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpo return cortex_m_unset_breakpoint(target, breakpoint); } -int cortex_m_set_watchpoint(struct target *target, struct watchpoint *watchpoint) +static int cortex_m_set_watchpoint(struct target *target, struct watchpoint *watchpoint) { int dwt_num = 0; struct cortex_m_common *cortex_m = target_to_cm(target); @@ -1496,7 +1494,7 @@ int cortex_m_set_watchpoint(struct target *target, struct watchpoint *watchpoint return ERROR_OK; } -int cortex_m_unset_watchpoint(struct target *target, struct watchpoint *watchpoint) +static int cortex_m_unset_watchpoint(struct target *target, struct watchpoint *watchpoint) { struct cortex_m_common *cortex_m = target_to_cm(target); struct cortex_m_dwt_comparator *comparator; @@ -1610,176 +1608,6 @@ void cortex_m_enable_watchpoints(struct target *target) } } -static int cortex_m_load_core_reg_u32(struct target *target, - uint32_t num, uint32_t *value) -{ - int retval; - - /* NOTE: we "know" here that the register identifiers used - * in the v7m header match the Cortex-M3 Debug Core Register - * Selector values for R0..R15, xPSR, MSP, and PSP. - */ - switch (num) { - case 0 ... 18: - /* read a normal core register */ - retval = cortexm_dap_read_coreregister_u32(target, value, num); - - if (retval != ERROR_OK) { - LOG_ERROR("JTAG failure %i", retval); - return ERROR_JTAG_DEVICE_ERROR; - } - LOG_DEBUG("load from core reg %i value 0x%" PRIx32 "", (int)num, *value); - break; - - case ARMV7M_FPSCR: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, DCB_DCRSR, 0x21); - if (retval != ERROR_OK) - return retval; - retval = target_read_u32(target, DCB_DCRDR, value); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("load from FPSCR value 0x%" PRIx32, *value); - break; - - case ARMV7M_S0 ... ARMV7M_S31: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, DCB_DCRSR, num - ARMV7M_S0 + 0x40); - if (retval != ERROR_OK) - return retval; - retval = target_read_u32(target, DCB_DCRDR, value); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("load from FPU reg S%d value 0x%" PRIx32, - (int)(num - ARMV7M_S0), *value); - break; - - case ARMV7M_PRIMASK: - case ARMV7M_BASEPRI: - case ARMV7M_FAULTMASK: - case ARMV7M_CONTROL: - /* Cortex-M3 packages these four registers as bitfields - * in one Debug Core register. So say r0 and r2 docs; - * it was removed from r1 docs, but still works. - */ - cortexm_dap_read_coreregister_u32(target, value, 20); - - switch (num) { - case ARMV7M_PRIMASK: - *value = buf_get_u32((uint8_t *)value, 0, 1); - break; - - case ARMV7M_BASEPRI: - *value = buf_get_u32((uint8_t *)value, 8, 8); - break; - - case ARMV7M_FAULTMASK: - *value = buf_get_u32((uint8_t *)value, 16, 1); - break; - - case ARMV7M_CONTROL: - *value = buf_get_u32((uint8_t *)value, 24, 2); - break; - } - - LOG_DEBUG("load from special reg %i value 0x%" PRIx32 "", (int)num, *value); - break; - - default: - return ERROR_COMMAND_SYNTAX_ERROR; - } - - return ERROR_OK; -} - -static int cortex_m_store_core_reg_u32(struct target *target, - uint32_t num, uint32_t value) -{ - int retval; - uint32_t reg; - struct armv7m_common *armv7m = target_to_armv7m(target); - - /* NOTE: we "know" here that the register identifiers used - * in the v7m header match the Cortex-M3 Debug Core Register - * Selector values for R0..R15, xPSR, MSP, and PSP. - */ - switch (num) { - case 0 ... 18: - retval = cortexm_dap_write_coreregister_u32(target, value, num); - if (retval != ERROR_OK) { - struct reg *r; - - LOG_ERROR("JTAG failure"); - r = armv7m->arm.core_cache->reg_list + num; - r->dirty = r->valid; - return ERROR_JTAG_DEVICE_ERROR; - } - LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", (int)num, value); - break; - - case ARMV7M_FPSCR: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, DCB_DCRDR, value); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, DCB_DCRSR, 0x21 | (1<<16)); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("write FPSCR value 0x%" PRIx32, value); - break; - - case ARMV7M_S0 ... ARMV7M_S31: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, DCB_DCRDR, value); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, DCB_DCRSR, (num - ARMV7M_S0 + 0x40) | (1<<16)); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("write FPU reg S%d value 0x%" PRIx32, - (int)(num - ARMV7M_S0), value); - break; - - case ARMV7M_PRIMASK: - case ARMV7M_BASEPRI: - case ARMV7M_FAULTMASK: - case ARMV7M_CONTROL: - /* Cortex-M3 packages these four registers as bitfields - * in one Debug Core register. So say r0 and r2 docs; - * it was removed from r1 docs, but still works. - */ - cortexm_dap_read_coreregister_u32(target, ®, 20); - - switch (num) { - case ARMV7M_PRIMASK: - buf_set_u32((uint8_t *)®, 0, 1, value); - break; - - case ARMV7M_BASEPRI: - buf_set_u32((uint8_t *)®, 8, 8, value); - break; - - case ARMV7M_FAULTMASK: - buf_set_u32((uint8_t *)®, 16, 1, value); - break; - - case ARMV7M_CONTROL: - buf_set_u32((uint8_t *)®, 24, 2, value); - break; - } - - cortexm_dap_write_coreregister_u32(target, reg, 20); - - LOG_DEBUG("write special reg %i value 0x%" PRIx32 " ", (int)num, value); - break; - - default: - return ERROR_COMMAND_SYNTAX_ERROR; - } - - return ERROR_OK; -} - static int cortex_m_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { @@ -1820,6 +1648,8 @@ void cortex_m_deinit_target(struct target *target) { struct cortex_m_common *cortex_m = target_to_cm(target); + armv7m_trace_tpiu_exit(target); + free(cortex_m->fp_comparator_list); cortex_m_dwt_free(target); @@ -1835,28 +1665,23 @@ int cortex_m_profiling(struct target *target, uint32_t *samples, struct timeval timeout, now; struct armv7m_common *armv7m = target_to_armv7m(target); uint32_t reg_value; - bool use_pcsr = false; - int retval = ERROR_OK; - struct reg *reg; - - gettimeofday(&timeout, NULL); - timeval_add_time(&timeout, seconds, 0); + int retval; retval = target_read_u32(target, DWT_PCSR, ®_value); if (retval != ERROR_OK) { LOG_ERROR("Error while reading PCSR"); return retval; } - - if (reg_value != 0) { - use_pcsr = true; - LOG_INFO("Starting Cortex-M profiling. Sampling DWT_PCSR as fast as we can..."); - } else { - LOG_INFO("Starting profiling. Halting and resuming the" - " target as often as we can..."); - reg = register_get_by_name(target->reg_cache, "pc", 1); + if (reg_value == 0) { + LOG_INFO("PCSR sampling not supported on this processor."); + return target_profiling_default(target, samples, max_num_samples, num_samples, seconds); } + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, seconds, 0); + + LOG_INFO("Starting Cortex-M profiling. Sampling DWT_PCSR as fast as we can..."); + /* Make sure the target is running */ target_poll(target); if (target->state == TARGET_HALTED) @@ -1870,40 +1695,21 @@ int cortex_m_profiling(struct target *target, uint32_t *samples, uint32_t sample_count = 0; for (;;) { - if (use_pcsr) { - if (armv7m && armv7m->debug_ap) { - uint32_t read_count = max_num_samples - sample_count; - if (read_count > 1024) - read_count = 1024; - - retval = mem_ap_read_buf_noincr(armv7m->debug_ap, - (void *)&samples[sample_count], - 4, read_count, DWT_PCSR); - sample_count += read_count; - } else { - target_read_u32(target, DWT_PCSR, &samples[sample_count++]); - } + if (armv7m && armv7m->debug_ap) { + uint32_t read_count = max_num_samples - sample_count; + if (read_count > 1024) + read_count = 1024; + + retval = mem_ap_read_buf_noincr(armv7m->debug_ap, + (void *)&samples[sample_count], + 4, read_count, DWT_PCSR); + sample_count += read_count; } else { - target_poll(target); - if (target->state == TARGET_HALTED) { - reg_value = buf_get_u32(reg->value, 0, 32); - /* current pc, addr = 0, do not handle breakpoints, not debugging */ - retval = target_resume(target, 1, 0, 0, 0); - samples[sample_count++] = reg_value; - target_poll(target); - alive_sleep(10); /* sleep 10ms, i.e. <100 samples/second. */ - } else if (target->state == TARGET_RUNNING) { - /* We want to quickly sample the PC. */ - retval = target_halt(target); - } else { - LOG_INFO("Target not halted or running"); - retval = ERROR_OK; - break; - } + target_read_u32(target, DWT_PCSR, &samples[sample_count++]); } if (retval != ERROR_OK) { - LOG_ERROR("Error while reading %s", use_pcsr ? "PCSR" : "target pc"); + LOG_ERROR("Error while reading PCSR"); return retval; } @@ -2013,7 +1819,7 @@ static void cortex_m_dwt_addreg(struct target *t, struct reg *r, const struct dw r->type = &dwt_reg_type; } -void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target) +static void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target) { uint32_t dwtcr; struct reg_cache *cache; @@ -2242,7 +2048,6 @@ int cortex_m_examine(struct target *target) for (idx = ARMV7M_NUM_CORE_REGS_NOFP; idx < armv7m->arm.core_cache->num_regs; idx++) { - free(armv7m->arm.core_cache->reg_list[idx].value); free(armv7m->arm.core_cache->reg_list[idx].feature); free(armv7m->arm.core_cache->reg_list[idx].reg_data_type); } @@ -2687,6 +2492,9 @@ static const struct command_registration cortex_m_command_handlers[] = { .usage = "", .chain = cortex_m_exec_command_handlers, }, + { + .chain = rtt_target_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h index 415a6c2..b470fbd 100644 --- a/src/target/cortex_m.h +++ b/src/target/cortex_m.h @@ -233,13 +233,10 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoint); int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint); int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpoint); -int cortex_m_set_watchpoint(struct target *target, struct watchpoint *watchpoint); -int cortex_m_unset_watchpoint(struct target *target, struct watchpoint *watchpoint); int cortex_m_add_watchpoint(struct target *target, struct watchpoint *watchpoint); int cortex_m_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); void cortex_m_enable_breakpoints(struct target *target); void cortex_m_enable_watchpoints(struct target *target); -void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target); void cortex_m_deinit_target(struct target *target); int cortex_m_profiling(struct target *target, uint32_t *samples, uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds); diff --git a/src/target/dsp5680xx.c b/src/target/dsp5680xx.c index d6107ab..ee26d24 100644 --- a/src/target/dsp5680xx.c +++ b/src/target/dsp5680xx.c @@ -40,7 +40,7 @@ struct dsp5680xx_common dsp5680xx_context; #define CHECK_HALT(target) if (target->state != TARGET_HALTED) HALT_FAIL #define check_halt_and_debug(target) { CHECK_HALT(target); CHECK_DBG; } -int dsp5680xx_execute_queue(void) +static int dsp5680xx_execute_queue(void) { int retval; @@ -892,12 +892,6 @@ static int dsp5680xx_arch_state(struct target *target) return ERROR_OK; } -int dsp5680xx_target_status(struct target *target, uint8_t *jtag_st, - uint16_t *eonce_st) -{ - return target->state; -} - static int dsp5680xx_assert_reset(struct target *target) { target->state = TARGET_RESET; @@ -1555,7 +1549,7 @@ static int perl_crc(const uint8_t *buff8, uint32_t word_count) * * @return */ -int dsp5680xx_f_SIM_reset(struct target *target) +static int dsp5680xx_f_SIM_reset(struct target *target) { int retval = ERROR_OK; @@ -1978,7 +1972,8 @@ int dsp5680xx_f_erase(struct target *target, int first, int last) * 0x0000001E 0xA961 bra *-30 */ -const uint16_t pgm_write_pflash[] = { 0x8A46, 0x0013, 0x807D, 0xE700, +static const uint16_t pgm_write_pflash[] = { + 0x8A46, 0x0013, 0x807D, 0xE700, 0xE700, 0x8A44, 0xFFFE, 0x017B, 0xE700, 0xF514, 0x8563, 0x8646, 0x0020, 0x0014, 0x8646, 0x0080, @@ -1988,7 +1983,7 @@ const uint16_t pgm_write_pflash[] = { 0x8A46, 0x0013, 0x807D, 0xE700, 0x0013, 0x0010, 0xA961 }; -const uint32_t pgm_write_pflash_length = 31; +static const uint32_t pgm_write_pflash_length = 31; int dsp5680xx_f_wr(struct target *t, const uint8_t *b, uint32_t a, uint32_t count, int is_flash_lock) diff --git a/src/target/etm.c b/src/target/etm.c index 5d079ff..faa941f 100644 --- a/src/target/etm.c +++ b/src/target/etm.c @@ -279,7 +279,7 @@ static void etm_reg_add(unsigned bcd_vers, struct arm_jtag *jtag_info, reg->name = r->name; reg->size = r->size; - reg->value = &ereg->value; + reg->value = ereg->value; reg->arch_info = ereg; reg->type = &etm_scan6_type; reg++; @@ -650,7 +650,6 @@ static struct etm_capture_driver *etm_capture_drivers[] = { static int etm_read_instruction(struct etm_context *ctx, struct arm_instruction *instruction) { - int i; int section = -1; size_t size_read; uint32_t opcode; @@ -660,7 +659,7 @@ static int etm_read_instruction(struct etm_context *ctx, struct arm_instruction return ERROR_TRACE_IMAGE_UNAVAILABLE; /* search for the section the current instruction belongs to */ - for (i = 0; i < ctx->image->num_sections; i++) { + for (unsigned int i = 0; i < ctx->image->num_sections; i++) { if ((ctx->image->sections[i].base_address <= ctx->current_pc) && (ctx->image->sections[i].base_address + ctx->image->sections[i].size > ctx->current_pc)) { @@ -1683,15 +1682,15 @@ COMMAND_HANDLER(handle_etm_image_command) } etm_ctx->image = malloc(sizeof(struct image)); - etm_ctx->image->base_address_set = 0; - etm_ctx->image->start_address_set = 0; + etm_ctx->image->base_address_set = false; + etm_ctx->image->start_address_set = false; /* a base address isn't always necessary, default to 0x0 (i.e. don't relocate) */ if (CMD_ARGC >= 2) { - etm_ctx->image->base_address_set = 1; + etm_ctx->image->base_address_set = true; COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], etm_ctx->image->base_address); } else - etm_ctx->image->base_address_set = 0; + etm_ctx->image->base_address_set = false; if (image_open(etm_ctx->image, CMD_ARGV[0], (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL) != ERROR_OK) { diff --git a/src/target/hla_target.c b/src/target/hla_target.c index f0dc572..3d41387 100644 --- a/src/target/hla_target.c +++ b/src/target/hla_target.c @@ -39,6 +39,7 @@ #include "cortex_m.h" #include "arm_semihosting.h" #include "target_request.h" +#include <rtt/rtt.h> #define savedDCRDR dbgbase /* FIXME: using target->dbgbase to preserve DCRDR */ @@ -51,184 +52,17 @@ static inline struct hl_interface_s *target_to_adapter(struct target *target) } static int adapter_load_core_reg_u32(struct target *target, - uint32_t num, uint32_t *value) + uint32_t regsel, uint32_t *value) { - int retval; struct hl_interface_s *adapter = target_to_adapter(target); - - LOG_DEBUG("%s", __func__); - - /* NOTE: we "know" here that the register identifiers used - * in the v7m header match the Cortex-M3 Debug Core Register - * Selector values for R0..R15, xPSR, MSP, and PSP. - */ - switch (num) { - case 0 ... 18: - /* read a normal core register */ - retval = adapter->layout->api->read_reg(adapter->handle, num, value); - - if (retval != ERROR_OK) { - LOG_ERROR("JTAG failure %i", retval); - return ERROR_JTAG_DEVICE_ERROR; - } - LOG_DEBUG("load from core reg %i value 0x%" PRIx32 "", (int)num, *value); - break; - - case ARMV7M_FPSCR: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, ARMV7M_SCS_DCRSR, 33); - if (retval != ERROR_OK) - return retval; - retval = target_read_u32(target, ARMV7M_SCS_DCRDR, value); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("load from FPSCR value 0x%" PRIx32, *value); - break; - - case ARMV7M_S0 ... ARMV7M_S31: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, ARMV7M_SCS_DCRSR, num-ARMV7M_S0+64); - if (retval != ERROR_OK) - return retval; - retval = target_read_u32(target, ARMV7M_SCS_DCRDR, value); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("load from FPU reg S%d value 0x%" PRIx32, - (int)(num - ARMV7M_S0), *value); - break; - - case ARMV7M_PRIMASK: - case ARMV7M_BASEPRI: - case ARMV7M_FAULTMASK: - case ARMV7M_CONTROL: - /* Cortex-M3 packages these four registers as bitfields - * in one Debug Core register. So say r0 and r2 docs; - * it was removed from r1 docs, but still works. - */ - retval = adapter->layout->api->read_reg(adapter->handle, 20, value); - if (retval != ERROR_OK) - return retval; - - switch (num) { - case ARMV7M_PRIMASK: - *value = buf_get_u32((uint8_t *) value, 0, 1); - break; - - case ARMV7M_BASEPRI: - *value = buf_get_u32((uint8_t *) value, 8, 8); - break; - - case ARMV7M_FAULTMASK: - *value = buf_get_u32((uint8_t *) value, 16, 1); - break; - - case ARMV7M_CONTROL: - *value = buf_get_u32((uint8_t *) value, 24, 2); - break; - } - - LOG_DEBUG("load from special reg %i value 0x%" PRIx32 "", - (int)num, *value); - break; - - default: - return ERROR_COMMAND_SYNTAX_ERROR; - } - - return ERROR_OK; + return adapter->layout->api->read_reg(adapter->handle, regsel, value); } static int adapter_store_core_reg_u32(struct target *target, - uint32_t num, uint32_t value) + uint32_t regsel, uint32_t value) { - int retval; - uint32_t reg; - struct armv7m_common *armv7m = target_to_armv7m(target); struct hl_interface_s *adapter = target_to_adapter(target); - - LOG_DEBUG("%s", __func__); - - /* NOTE: we "know" here that the register identifiers used - * in the v7m header match the Cortex-M3 Debug Core Register - * Selector values for R0..R15, xPSR, MSP, and PSP. - */ - switch (num) { - case 0 ... 18: - retval = adapter->layout->api->write_reg(adapter->handle, num, value); - - if (retval != ERROR_OK) { - struct reg *r; - - LOG_ERROR("JTAG failure"); - r = armv7m->arm.core_cache->reg_list + num; - r->dirty = r->valid; - return ERROR_JTAG_DEVICE_ERROR; - } - LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", (int)num, value); - break; - - case ARMV7M_FPSCR: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, ARMV7M_SCS_DCRDR, value); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, ARMV7M_SCS_DCRSR, 33 | (1<<16)); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("write FPSCR value 0x%" PRIx32, value); - break; - - case ARMV7M_S0 ... ARMV7M_S31: - /* Floating-point Status and Registers */ - retval = target_write_u32(target, ARMV7M_SCS_DCRDR, value); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, ARMV7M_SCS_DCRSR, (num-ARMV7M_S0+64) | (1<<16)); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("write FPU reg S%d value 0x%" PRIx32, - (int)(num - ARMV7M_S0), value); - break; - - case ARMV7M_PRIMASK: - case ARMV7M_BASEPRI: - case ARMV7M_FAULTMASK: - case ARMV7M_CONTROL: - /* Cortex-M3 packages these four registers as bitfields - * in one Debug Core register. So say r0 and r2 docs; - * it was removed from r1 docs, but still works. - */ - - adapter->layout->api->read_reg(adapter->handle, 20, ®); - - switch (num) { - case ARMV7M_PRIMASK: - buf_set_u32((uint8_t *) ®, 0, 1, value); - break; - - case ARMV7M_BASEPRI: - buf_set_u32((uint8_t *) ®, 8, 8, value); - break; - - case ARMV7M_FAULTMASK: - buf_set_u32((uint8_t *) ®, 16, 1, value); - break; - - case ARMV7M_CONTROL: - buf_set_u32((uint8_t *) ®, 24, 2, value); - break; - } - - adapter->layout->api->write_reg(adapter->handle, 20, reg); - - LOG_DEBUG("write special reg %i value 0x%" PRIx32 " ", (int)num, value); - break; - - default: - return ERROR_COMMAND_SYNTAX_ERROR; - } - - return ERROR_OK; + return adapter->layout->api->write_reg(adapter->handle, regsel, value); } static int adapter_examine_debug_reason(struct target *target) @@ -433,7 +267,7 @@ static int adapter_debug_entry(struct target *target) arm->map = armv7m_msp_reg_map; } else { unsigned control = buf_get_u32(arm->core_cache - ->reg_list[ARMV7M_CONTROL].value, 0, 2); + ->reg_list[ARMV7M_CONTROL].value, 0, 3); /* is this thread privileged? */ arm->core_mode = control & 1 @@ -793,6 +627,9 @@ static const struct command_registration adapter_command_handlers[] = { { .chain = armv7m_trace_command_handlers, }, + { + .chain = rtt_target_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/image.c b/src/target/image.c index 68262e9..fd5eff8 100644 --- a/src/target/image.c +++ b/src/target/image.c @@ -124,7 +124,6 @@ static int image_ihex_buffer_complete_inner(struct image *image, uint32_t full_address; uint32_t cooked_bytes; bool end_rec = false; - int i; /* we can't determine the number of sections that we'll have to create ahead of time, * so we locally hold them until parsing is finished */ @@ -207,7 +206,7 @@ static int image_ihex_buffer_complete_inner(struct image *image, /* copy section information */ image->sections = malloc(sizeof(struct imagesection) * image->num_sections); - for (i = 0; i < image->num_sections; i++) { + for (unsigned int i = 0; i < image->num_sections; i++) { image->sections[i].private = section[i].private; image->sections[i].base_address = section[i].base_address; image->sections[i].size = section[i].size; @@ -294,7 +293,7 @@ static int image_ihex_buffer_complete_inner(struct image *image, cal_checksum += (uint8_t)start_address; bytes_read += 8; - image->start_address_set = 1; + image->start_address_set = true; image->start_address = be_to_h_u32((uint8_t *)&start_address); } else { LOG_ERROR("unhandled IHEX record type: %i", (int)record_type); @@ -471,7 +470,7 @@ static int image_elf_read_headers(struct image *image) } } - image->start_address_set = 1; + image->start_address_set = true; image->start_address = field32(elf, elf->header->e_entry); return ERROR_OK; @@ -529,7 +528,6 @@ static int image_mot_buffer_complete_inner(struct image *image, uint32_t full_address; uint32_t cooked_bytes; bool end_rec = false; - int i; /* we can't determine the number of sections that we'll have to create ahead of time, * so we locally hold them until parsing is finished */ @@ -658,7 +656,7 @@ static int image_mot_buffer_complete_inner(struct image *image, /* copy section information */ image->sections = malloc(sizeof(struct imagesection) * image->num_sections); - for (i = 0; i < image->num_sections; i++) { + for (unsigned int i = 0; i < image->num_sections; i++) { image->sections[i].private = section[i].private; image->sections[i].base_address = section[i].base_address; image->sections[i].size = section[i].size; @@ -821,21 +819,20 @@ int image_open(struct image *image, const char *url, const char *type_string) } } else if (image->type == IMAGE_BUILDER) { image->num_sections = 0; - image->base_address_set = 0; + image->base_address_set = false; image->sections = NULL; image->type_private = NULL; } if (image->base_address_set) { /* relocate */ - int section; - for (section = 0; section < image->num_sections; section++) + for (unsigned int section = 0; section < image->num_sections; section++) image->sections[section].base_address += image->base_address; /* we're done relocating. The two statements below are mainly * for documentation purposes: stop anyone from empirically * thinking they should use these values henceforth. */ image->base_address = 0; - image->base_address_set = 0; + image->base_address_set = false; } return retval; @@ -1009,9 +1006,7 @@ void image_close(struct image *image) free(image_mot->buffer); image_mot->buffer = NULL; } else if (image->type == IMAGE_BUILDER) { - int i; - - for (i = 0; i < image->num_sections; i++) { + for (unsigned int i = 0; i < image->num_sections; i++) { free(image->sections[i].private); image->sections[i].private = NULL; } @@ -1024,7 +1019,7 @@ void image_close(struct image *image) image->sections = NULL; } -int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, uint32_t *checksum) +int image_calculate_checksum(const uint8_t *buffer, uint32_t nbytes, uint32_t *checksum) { uint32_t crc = 0xffffffff; LOG_DEBUG("Calculating checksum"); diff --git a/src/target/image.h b/src/target/image.h index 9907a5f..53c27d8 100644 --- a/src/target/image.h +++ b/src/target/image.h @@ -55,11 +55,11 @@ struct imagesection { struct image { enum image_type type; /* image type (plain, ihex, ...) */ void *type_private; /* type private data */ - int num_sections; /* number of sections contained in the image */ + unsigned int num_sections; /* number of sections contained in the image */ struct imagesection *sections; /* array of sections */ - int base_address_set; /* whether the image has a base address set (for relocation purposes) */ + bool base_address_set; /* whether the image has a base address set (for relocation purposes) */ long long base_address; /* base address, if one is set */ - int start_address_set; /* whether the image has a start address (entry point) associated */ + bool start_address_set; /* whether the image has a start address (entry point) associated */ uint32_t start_address; /* start address, if one is set */ }; @@ -99,7 +99,7 @@ void image_close(struct image *image); int image_add_section(struct image *image, uint32_t base, uint32_t size, int flags, uint8_t const *data); -int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, +int image_calculate_checksum(const uint8_t *buffer, uint32_t nbytes, uint32_t *checksum); #define ERROR_IMAGE_FORMAT_ERROR (-1400) diff --git a/src/target/mips32_pracc.c b/src/target/mips32_pracc.c index 9bac40e..d6bd1c5 100644 --- a/src/target/mips32_pracc.c +++ b/src/target/mips32_pracc.c @@ -120,7 +120,7 @@ static void mips32_pracc_finish(struct mips_ejtag *ejtag_info) mips_ejtag_drscan_32_out(ejtag_info, ctrl); } -int mips32_pracc_clean_text_jump(struct mips_ejtag *ejtag_info) +static int mips32_pracc_clean_text_jump(struct mips_ejtag *ejtag_info) { uint32_t jt_code = MIPS32_J(ejtag_info->isa, MIPS32_PRACC_TEXT); pracc_swap16_array(ejtag_info, &jt_code, 1); @@ -453,7 +453,7 @@ exit: return retval; } -int mips32_pracc_read_u32(struct mips_ejtag *ejtag_info, uint32_t addr, uint32_t *buf) +static int mips32_pracc_read_u32(struct mips_ejtag *ejtag_info, uint32_t addr, uint32_t *buf) { struct pracc_queue_info ctx = {.ejtag_info = ejtag_info}; pracc_queue_init(&ctx); diff --git a/src/target/mips_ejtag.c b/src/target/mips_ejtag.c index 7544afe..4b049fb 100644 --- a/src/target/mips_ejtag.c +++ b/src/target/mips_ejtag.c @@ -58,7 +58,7 @@ int mips_ejtag_get_idcode(struct mips_ejtag *ejtag_info) return mips_ejtag_drscan_32(ejtag_info, &ejtag_info->idcode); } -int mips_ejtag_get_impcode(struct mips_ejtag *ejtag_info) +static int mips_ejtag_get_impcode(struct mips_ejtag *ejtag_info) { mips_ejtag_set_instr(ejtag_info, EJTAG_INST_IMPCODE); @@ -119,7 +119,8 @@ int mips_ejtag_drscan_64(struct mips_ejtag *ejtag_info, uint64_t *data) return ERROR_OK; } -void mips_ejtag_drscan_32_queued(struct mips_ejtag *ejtag_info, uint32_t data_out, uint8_t *data_in) +static void mips_ejtag_drscan_32_queued(struct mips_ejtag *ejtag_info, + uint32_t data_out, uint8_t *data_in) { assert(ejtag_info->tap != NULL); struct jtag_tap *tap = ejtag_info->tap; diff --git a/src/target/mips_mips64.c b/src/target/mips_mips64.c index f941af5..0fc0897 100644 --- a/src/target/mips_mips64.c +++ b/src/target/mips_mips64.c @@ -62,7 +62,7 @@ static int mips_mips64_debug_entry(struct target *target) mips_mips64_examine_debug_reason(target); LOG_DEBUG("entered debug state at PC 0x%" PRIx64 ", target->state: %s", - *(uint64_t *)pc->value, target_state_name(target)); + buf_get_u64(pc->value, 0, 64), target_state_name(target)); return ERROR_OK; } diff --git a/src/target/nds32.c b/src/target/nds32.c index 487e19c..add66b2 100644 --- a/src/target/nds32.c +++ b/src/target/nds32.c @@ -2496,6 +2496,12 @@ int nds32_profiling(struct target *target, uint32_t *samples, struct aice_port_s *aice = target_to_aice(target); struct nds32 *nds32 = target_to_nds32(target); + /* REVISIT: can nds32 profile without halting? */ + if (target->state != TARGET_HALTED) { + LOG_WARNING("target %s is not halted (profiling)", target->cmd_name); + return ERROR_TARGET_NOT_HALTED; + } + if (max_num_samples < iteration) iteration = max_num_samples; diff --git a/src/target/nds32_tlb.c b/src/target/nds32_tlb.c index c4bce1a..93a9241 100644 --- a/src/target/nds32_tlb.c +++ b/src/target/nds32_tlb.c @@ -31,7 +31,7 @@ int nds32_probe_tlb(struct nds32 *nds32, const target_addr_t virtual_address, return aice_read_tlb(aice, virtual_address, physical_address); } -struct page_table_walker_info_s page_table_info[PAGE_SIZE_NUM] = { +static struct page_table_walker_info_s page_table_info[PAGE_SIZE_NUM] = { /* 4K page */ {0xFFC00000, 20, 0x003FF000, 10, 0x00000FFF, 0xFFFFF000, 0xFFFFF000, 0xFFFFF000}, /* 8K page */ diff --git a/src/target/nds32_v3.c b/src/target/nds32_v3.c index e5d146b..f9cd47a 100644 --- a/src/target/nds32_v3.c +++ b/src/target/nds32_v3.c @@ -404,7 +404,7 @@ static int nds32_v3_remove_watchpoint(struct target *target, return ERROR_OK; } -struct nds32_v3_common_callback nds32_v3_common_callback = { +static struct nds32_v3_common_callback nds32_v3_common_callback = { .check_interrupt_stack = nds32_v3_check_interrupt_stack, .restore_interrupt_stack = nds32_v3_restore_interrupt_stack, .activate_hardware_breakpoint = nds32_v3_activate_hardware_breakpoint, diff --git a/src/target/nds32_v3m.c b/src/target/nds32_v3m.c index 86903a5..952d0eb 100644 --- a/src/target/nds32_v3m.c +++ b/src/target/nds32_v3m.c @@ -379,7 +379,7 @@ static int nds32_v3m_remove_watchpoint(struct target *target, return ERROR_OK; } -struct nds32_v3_common_callback nds32_v3m_common_callback = { +static struct nds32_v3_common_callback nds32_v3m_common_callback = { .check_interrupt_stack = nds32_v3m_check_interrupt_stack, .restore_interrupt_stack = nds32_v3m_restore_interrupt_stack, .activate_hardware_breakpoint = nds32_v3m_activate_hardware_breakpoint, diff --git a/src/target/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c index 1d05944..b4b2566 100644 --- a/src/target/openrisc/jsp_server.c +++ b/src/target/openrisc/jsp_server.c @@ -57,7 +57,7 @@ static int telnet_write(struct connection *connection, const void *data, int len return ERROR_SERVER_REMOTE_CLOSED; } -int jsp_poll_read(void *priv) +static int jsp_poll_read(void *priv) { struct jsp_service *jsp_service = (struct jsp_service *)priv; unsigned char out_buffer[10]; @@ -207,7 +207,8 @@ int jsp_init(struct or1k_jtag *jtag_info, char *banner) jsp_new_connection, jsp_input, jsp_connection_closed, - jsp_service); + jsp_service, + NULL); } COMMAND_HANDLER(handle_jsp_port_command) diff --git a/src/target/openrisc/or1k.c b/src/target/openrisc/or1k.c index d685359..5b8d7de 100644 --- a/src/target/openrisc/or1k.c +++ b/src/target/openrisc/or1k.c @@ -1200,7 +1200,7 @@ static int or1k_get_gdb_reg_list(struct target *target, struct reg **reg_list[], } -int or1k_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info) +static int or1k_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info) { return ERROR_FAIL; } diff --git a/src/target/quark_d20xx.c b/src/target/quark_d20xx.c index 42d3b8c..9169379 100644 --- a/src/target/quark_d20xx.c +++ b/src/target/quark_d20xx.c @@ -43,7 +43,7 @@ #include "lakemont.h" #include "x86_32_common.h" -int quark_d20xx_target_create(struct target *t, Jim_Interp *interp) +static int quark_d20xx_target_create(struct target *t, Jim_Interp *interp) { struct x86_32_common *x86_32 = calloc(1, sizeof(struct x86_32_common)); if (x86_32 == NULL) { @@ -56,7 +56,7 @@ int quark_d20xx_target_create(struct target *t, Jim_Interp *interp) return ERROR_OK; } -int quark_d20xx_init_target(struct command_context *cmd_ctx, struct target *t) +static int quark_d20xx_init_target(struct command_context *cmd_ctx, struct target *t) { return lakemont_init_target(cmd_ctx, t); } diff --git a/src/target/register.h b/src/target/register.h index 7c53d6e..5f1c25f 100644 --- a/src/target/register.h +++ b/src/target/register.h @@ -127,13 +127,15 @@ struct reg { bool caller_save; /* Pointer to place where the value is stored, in the format understood by * the binarybuffer.h functions. */ - void *value; + uint8_t *value; /* The stored value needs to be written to the target. */ bool dirty; /* When true, value is valid. */ bool valid; /* When false, the register doesn't actually exist in the target. */ bool exist; + /* Hide the register from gdb and omit it in 'reg' cmd output */ + bool hidden; /* Size of the register in bits. */ uint32_t size; /* Used for generating XML description of registers. Can be set to NULL for diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 2c34795..9978931 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -4835,7 +4835,7 @@ int riscv_init_registers(struct target *target) assert(reg_name < info->reg_names + target->reg_cache->num_regs * max_reg_name_len); } - r->value = &info->reg_cache_values[number]; + r->value = info->reg_cache_values[number]; } return ERROR_OK; diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 8e098c0..aa9614a 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -101,8 +101,8 @@ typedef struct { /* OpenOCD's register cache points into here. This is not per-hart because * we just invalidate the entire cache when we change which hart is - * selected. */ - uint64_t reg_cache_values[RISCV_MAX_REGISTERS]; + * selected. Use an array of 8 uint8_t per register. */ + uint8_t reg_cache_values[RISCV_MAX_REGISTERS][8]; /* Single buffer that contains all register names, instead of calling * malloc for each register. Needs to be freed when reg_list is freed. */ diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c index 3048709..c39f030 100644 --- a/src/target/riscv/riscv_semihosting.c +++ b/src/target/riscv/riscv_semihosting.c @@ -143,7 +143,7 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval) if (0 <= semihosting->op && semihosting->op <= 0x31) { *retval = semihosting_common(target); if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation"); + LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op); return SEMI_ERROR; } } else { diff --git a/src/target/rtt.c b/src/target/rtt.c new file mode 100644 index 0000000..7e556e1 --- /dev/null +++ b/src/target/rtt.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <stdint.h> +#include <helper/log.h> +#include <helper/binarybuffer.h> +#include <helper/command.h> +#include <rtt/rtt.h> + +#include "target.h" + +static int read_rtt_channel(struct target *target, + const struct rtt_control *ctrl, unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel *channel) +{ + int ret; + uint8_t buf[RTT_CHANNEL_SIZE]; + target_addr_t address; + + address = ctrl->address + RTT_CB_SIZE + (channel_index * RTT_CHANNEL_SIZE); + + if (type == RTT_CHANNEL_TYPE_DOWN) + address += ctrl->num_up_channels * RTT_CHANNEL_SIZE; + + ret = target_read_buffer(target, address, RTT_CHANNEL_SIZE, buf); + + if (ret != ERROR_OK) + return ret; + + channel->address = address; + channel->name_addr = buf_get_u32(buf + 0, 0, 32); + channel->buffer_addr = buf_get_u32(buf + 4, 0, 32); + channel->size = buf_get_u32(buf + 8, 0, 32); + channel->write_pos = buf_get_u32(buf + 12, 0, 32); + channel->read_pos = buf_get_u32(buf + 16, 0, 32); + channel->flags = buf_get_u32(buf + 20, 0, 32); + + return ERROR_OK; +} + +int target_rtt_start(struct target *target, const struct rtt_control *ctrl, + void *user_data) +{ + return ERROR_OK; +} + +int target_rtt_stop(struct target *target, void *user_data) +{ + return ERROR_OK; +} + +static int read_channel_name(struct target *target, target_addr_t address, + char *name, size_t length) +{ + size_t offset; + + offset = 0; + + while (offset < length) { + int ret; + size_t read_length; + + read_length = MIN(32, length - offset); + ret = target_read_buffer(target, address + offset, read_length, + (uint8_t *)name + offset); + + if (ret != ERROR_OK) + return ret; + + if (memchr(name + offset, '\0', read_length)) + return ERROR_OK; + + offset += read_length; + } + + name[length - 1] = '\0'; + + return ERROR_OK; +} + +static int write_to_channel(struct target *target, + const struct rtt_channel *channel, const uint8_t *buffer, + size_t *length) +{ + int ret; + uint32_t len; + + if (!*length) + return ERROR_OK; + + if (channel->write_pos == channel->read_pos) { + uint32_t first_length; + + len = MIN(*length, channel->size - 1); + first_length = MIN(len, channel->size - channel->write_pos); + + ret = target_write_buffer(target, + channel->buffer_addr + channel->write_pos, first_length, + buffer); + + if (ret != ERROR_OK) + return ret; + + ret = target_write_buffer(target, channel->buffer_addr, + len - first_length, buffer + first_length); + + if (ret != ERROR_OK) + return ret; + } else if (channel->write_pos < channel->read_pos) { + len = MIN(*length, channel->read_pos - channel->write_pos - 1); + + if (!len) { + *length = 0; + return ERROR_OK; + } + + ret = target_write_buffer(target, + channel->buffer_addr + channel->write_pos, len, buffer); + + if (ret != ERROR_OK) + return ret; + } else { + uint32_t first_length; + + len = MIN(*length, + channel->size - channel->write_pos + channel->read_pos - 1); + + if (!len) { + *length = 0; + return ERROR_OK; + } + + first_length = MIN(len, channel->size - channel->write_pos); + + ret = target_write_buffer(target, + channel->buffer_addr + channel->write_pos, first_length, + buffer); + + if (ret != ERROR_OK) + return ret; + + buffer = buffer + first_length; + + ret = target_write_buffer(target, channel->buffer_addr, + len - first_length, buffer); + + if (ret != ERROR_OK) + return ret; + } + + ret = target_write_u32(target, channel->address + 12, + (channel->write_pos + len) % channel->size); + + if (ret != ERROR_OK) + return ret; + + *length = len; + + return ERROR_OK; +} + +static bool channel_is_active(const struct rtt_channel *channel) +{ + if (!channel) + return false; + + if (!channel->size) + return false; + + return true; +} + +int target_rtt_write_callback(struct target *target, struct rtt_control *ctrl, + unsigned int channel_index, const uint8_t *buffer, size_t *length, + void *user_data) +{ + int ret; + struct rtt_channel channel; + + ret = read_rtt_channel(target, ctrl, channel_index, + RTT_CHANNEL_TYPE_DOWN, &channel); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read down-channel %u description", + channel_index); + return ret; + } + + if (!channel_is_active(&channel)) { + LOG_WARNING("rtt: Down-channel %u is not active", channel_index); + return ERROR_OK; + } + + if (channel.size < RTT_CHANNEL_BUFFER_MIN_SIZE) { + LOG_WARNING("rtt: Down-channel %u is not large enough", + channel_index); + return ERROR_OK; + } + + ret = write_to_channel(target, &channel, buffer, length); + + if (ret != ERROR_OK) + return ret; + + LOG_DEBUG("rtt: Wrote %zu bytes into down-channel %u", *length, + channel_index); + + return ERROR_OK; +} + +int target_rtt_read_control_block(struct target *target, + target_addr_t address, struct rtt_control *ctrl, void *user_data) +{ + int ret; + uint8_t buf[RTT_CB_SIZE]; + + ret = target_read_buffer(target, address, RTT_CB_SIZE, buf); + + if (ret != ERROR_OK) + return ret; + + memcpy(ctrl->id, buf, RTT_CB_MAX_ID_LENGTH); + ctrl->id[RTT_CB_MAX_ID_LENGTH - 1] = '\0'; + ctrl->num_up_channels = buf_get_u32(buf + RTT_CB_MAX_ID_LENGTH + 0, + 0, 32); + ctrl->num_down_channels = buf_get_u32(buf + RTT_CB_MAX_ID_LENGTH + 4, + 0, 32); + + return ERROR_OK; +} + +int target_rtt_find_control_block(struct target *target, + target_addr_t *address, size_t size, const char *id, bool *found, + void *user_data) +{ + uint8_t buf[1024]; + + *found = false; + + size_t j = 0; + size_t cb_offset = 0; + const size_t id_length = strlen(id); + + LOG_INFO("rtt: Searching for control block '%s'", id); + + for (target_addr_t addr = 0; addr < size; addr = addr + sizeof(buf)) { + int ret; + + const size_t buf_size = MIN(sizeof(buf), size - addr); + ret = target_read_buffer(target, *address + addr, buf_size, buf); + + if (ret != ERROR_OK) + return ret; + + size_t start = 0; + size_t i = 0; + + while (i < buf_size) { + if (buf[i] != id[j]) { + start++; + cb_offset++; + i = start; + j = 0; + + continue; + } + + i++; + j++; + + if (j == id_length) { + *address = *address + cb_offset; + *found = true; + return ERROR_OK; + } + } + } + + return ERROR_OK; +} + +int target_rtt_read_channel_info(struct target *target, + const struct rtt_control *ctrl, unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info, + void *user_data) +{ + int ret; + struct rtt_channel channel; + + ret = read_rtt_channel(target, ctrl, channel_index, type, &channel); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read channel %u description", + channel_index); + return ret; + } + + ret = read_channel_name(target, channel.name_addr, info->name, + info->name_length); + + if (ret != ERROR_OK) + return ret; + + info->size = channel.size; + info->flags = channel.flags; + + return ERROR_OK; +} + +static int read_from_channel(struct target *target, + const struct rtt_channel *channel, uint8_t *buffer, + size_t *length) +{ + int ret; + uint32_t len; + + if (!*length) + return ERROR_OK; + + if (channel->read_pos == channel->write_pos) { + len = 0; + } else if (channel->read_pos < channel->write_pos) { + len = MIN(*length, channel->write_pos - channel->read_pos); + + ret = target_read_buffer(target, + channel->buffer_addr + channel->read_pos, len, buffer); + + if (ret != ERROR_OK) + return ret; + } else { + uint32_t first_length; + + len = MIN(*length, + channel->size - channel->read_pos + channel->write_pos); + first_length = MIN(len, channel->size - channel->read_pos); + + ret = target_read_buffer(target, + channel->buffer_addr + channel->read_pos, first_length, buffer); + + if (ret != ERROR_OK) + return ret; + + ret = target_read_buffer(target, channel->buffer_addr, + len - first_length, buffer + first_length); + + if (ret != ERROR_OK) + return ret; + } + + if (len > 0) { + ret = target_write_u32(target, channel->address + 16, + (channel->read_pos + len) % channel->size); + + if (ret != ERROR_OK) + return ret; + } + + *length = len; + + return ERROR_OK; +} + +int target_rtt_read_callback(struct target *target, + const struct rtt_control *ctrl, struct rtt_sink_list **sinks, + size_t num_channels, void *user_data) +{ + num_channels = MIN(num_channels, ctrl->num_up_channels); + + for (size_t i = 0; i < num_channels; i++) { + int ret; + struct rtt_channel channel; + uint8_t buffer[1024]; + size_t length; + + if (!sinks[i]) + continue; + + ret = read_rtt_channel(target, ctrl, i, RTT_CHANNEL_TYPE_UP, + &channel); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read up-channel %zu description", i); + return ret; + } + + if (!channel_is_active(&channel)) { + LOG_WARNING("rtt: Up-channel %zu is not active", i); + continue; + } + + if (channel.size < RTT_CHANNEL_BUFFER_MIN_SIZE) { + LOG_WARNING("rtt: Up-channel %zu is not large enough", i); + continue; + } + + length = sizeof(buffer); + ret = read_from_channel(target, &channel, buffer, &length); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read from up-channel %zu", i); + return ret; + } + + for (struct rtt_sink_list *sink = sinks[i]; sink; sink = sink->next) + sink->read(i, buffer, length, sink->user_data); + } + + return ERROR_OK; +} diff --git a/src/target/rtt.h b/src/target/rtt.h new file mode 100644 index 0000000..0122475 --- /dev/null +++ b/src/target/rtt.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OPENOCD_TARGET_RTT_H +#define OPENOCD_TARGET_RTT_H + +#include <stdint.h> +#include <stdbool.h> + +#include <target/target.h> +#include <rtt/rtt.h> + +int target_rtt_start(struct target *target, const struct rtt_control *ctrl, + void *user_data); +int target_rtt_stop(struct target *target, void *user_data); +int target_rtt_find_control_block(struct target *target, + target_addr_t *address, size_t size, const char *id, bool *found, + void *user_data); +int target_rtt_read_control_block(struct target *target, + target_addr_t address, struct rtt_control *ctrl, void *user_data); +int target_rtt_write_callback(struct target *target, + struct rtt_control *ctrl, unsigned int channel_index, + const uint8_t *buffer, size_t *length, void *user_data); +int target_rtt_read_callback(struct target *target, + const struct rtt_control *ctrl, struct rtt_sink_list **sinks, + size_t length, void *user_data); +int target_rtt_read_channel_info(struct target *target, + const struct rtt_control *ctrl, unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info, + void *user_data); + +#endif /* OPENOCD_TARGET_RTT_H */ diff --git a/src/target/stm8.c b/src/target/stm8.c index 78bf6a2..e99b3c2 100644 --- a/src/target/stm8.c +++ b/src/target/stm8.c @@ -1945,7 +1945,7 @@ static int stm8_run_algorithm(struct target *target, int num_mem_params, return ERROR_OK; } -int stm8_jim_configure(struct target *target, Jim_GetOptInfo *goi) +static int stm8_jim_configure(struct target *target, Jim_GetOptInfo *goi) { struct stm8_common *stm8 = target_to_stm8(target); jim_wide w; diff --git a/src/target/target.c b/src/target/target.c index 9e27f33..4ea6aca 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -72,8 +72,6 @@ static int target_get_gdb_fileio_info_default(struct target *target, struct gdb_fileio_info *fileio_info); static int target_gdb_fileio_end_default(struct target *target, int retcode, int fileio_errno, bool ctrl_c); -static int target_profiling_default(struct target *target, uint32_t *samples, - uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds); /* targets */ extern struct target_type arm7tdmi_target; @@ -156,8 +154,8 @@ static struct target_type *target_types[] = { struct target *all_targets; static struct target_event_callback *target_event_callbacks; static struct target_timer_callback *target_timer_callbacks; -LIST_HEAD(target_reset_callback_list); -LIST_HEAD(target_trace_callback_list); +static LIST_HEAD(target_reset_callback_list); +static LIST_HEAD(target_trace_callback_list); static const int polling_interval = TARGET_DEFAULT_POLLING_INTERVAL; static const Jim_Nvp nvp_assert[] = { @@ -768,9 +766,11 @@ int target_examine(void) if (target->defer_examine) continue; - retval = target_examine_one(target); - if (retval != ERROR_OK) - return retval; + int retval2 = target_examine_one(target); + if (retval2 != ERROR_OK) { + LOG_WARNING("target %s examination failed", target_name(target)); + retval = retval2; + } } return retval; } @@ -1031,11 +1031,11 @@ int target_run_flash_async_algorithm(struct target *target, * programming. The exact delay shouldn't matter as long as it's * less than buffer size / flash speed. This is very unlikely to * run when using high latency connections such as USB. */ - alive_sleep(10); + alive_sleep(2); /* to stop an infinite loop on some targets check and increment a timeout * this issue was observed on a stellaris using the new ICDI interface */ - if (timeout++ >= 500) { + if (timeout++ >= 2500) { LOG_ERROR("timeout waiting for algorithm, a target reset is recommended"); return ERROR_FLASH_OPERATION_FAILED; } @@ -1049,6 +1049,10 @@ int target_run_flash_async_algorithm(struct target *target, if (thisrun_bytes > count * block_size) thisrun_bytes = count * block_size; + /* Force end of large blocks to be word aligned */ + if (thisrun_bytes >= 16) + thisrun_bytes -= (rp + thisrun_bytes) & 0x03; + /* Write data to fifo */ retval = target_write_buffer(target, wp, thisrun_bytes, buffer); if (retval != ERROR_OK) @@ -1098,6 +1102,156 @@ int target_run_flash_async_algorithm(struct target *target, return retval; } +int target_run_read_async_algorithm(struct target *target, + uint8_t *buffer, uint32_t count, int block_size, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + uint32_t buffer_start, uint32_t buffer_size, + uint32_t entry_point, uint32_t exit_point, void *arch_info) +{ + int retval; + int timeout = 0; + + const uint8_t *buffer_orig = buffer; + + /* Set up working area. First word is write pointer, second word is read pointer, + * rest is fifo data area. */ + uint32_t wp_addr = buffer_start; + uint32_t rp_addr = buffer_start + 4; + uint32_t fifo_start_addr = buffer_start + 8; + uint32_t fifo_end_addr = buffer_start + buffer_size; + + uint32_t wp = fifo_start_addr; + uint32_t rp = fifo_start_addr; + + /* validate block_size is 2^n */ + assert(!block_size || !(block_size & (block_size - 1))); + + retval = target_write_u32(target, wp_addr, wp); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + /* Start up algorithm on target */ + retval = target_start_algorithm(target, num_mem_params, mem_params, + num_reg_params, reg_params, + entry_point, + exit_point, + arch_info); + + if (retval != ERROR_OK) { + LOG_ERROR("error starting target flash read algorithm"); + return retval; + } + + while (count > 0) { + retval = target_read_u32(target, wp_addr, &wp); + if (retval != ERROR_OK) { + LOG_ERROR("failed to get write pointer"); + break; + } + + LOG_DEBUG("offs 0x%zx count 0x%" PRIx32 " wp 0x%" PRIx32 " rp 0x%" PRIx32, + (size_t)(buffer - buffer_orig), count, wp, rp); + + if (wp == 0) { + LOG_ERROR("flash read algorithm aborted by target"); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + if (((wp - fifo_start_addr) & (block_size - 1)) || wp < fifo_start_addr || wp >= fifo_end_addr) { + LOG_ERROR("corrupted fifo write pointer 0x%" PRIx32, wp); + break; + } + + /* Count the number of bytes available in the fifo without + * crossing the wrap around. */ + uint32_t thisrun_bytes; + if (wp >= rp) + thisrun_bytes = wp - rp; + else + thisrun_bytes = fifo_end_addr - rp; + + if (thisrun_bytes == 0) { + /* Throttle polling a bit if transfer is (much) faster than flash + * reading. The exact delay shouldn't matter as long as it's + * less than buffer size / flash speed. This is very unlikely to + * run when using high latency connections such as USB. */ + alive_sleep(2); + + /* to stop an infinite loop on some targets check and increment a timeout + * this issue was observed on a stellaris using the new ICDI interface */ + if (timeout++ >= 2500) { + LOG_ERROR("timeout waiting for algorithm, a target reset is recommended"); + return ERROR_FLASH_OPERATION_FAILED; + } + continue; + } + + /* Reset our timeout */ + timeout = 0; + + /* Limit to the amount of data we actually want to read */ + if (thisrun_bytes > count * block_size) + thisrun_bytes = count * block_size; + + /* Force end of large blocks to be word aligned */ + if (thisrun_bytes >= 16) + thisrun_bytes -= (rp + thisrun_bytes) & 0x03; + + /* Read data from fifo */ + retval = target_read_buffer(target, rp, thisrun_bytes, buffer); + if (retval != ERROR_OK) + break; + + /* Update counters and wrap write pointer */ + buffer += thisrun_bytes; + count -= thisrun_bytes / block_size; + rp += thisrun_bytes; + if (rp >= fifo_end_addr) + rp = fifo_start_addr; + + /* Store updated write pointer to target */ + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + break; + + /* Avoid GDB timeouts */ + keep_alive(); + + } + + if (retval != ERROR_OK) { + /* abort flash write algorithm on target */ + target_write_u32(target, rp_addr, 0); + } + + int retval2 = target_wait_algorithm(target, num_mem_params, mem_params, + num_reg_params, reg_params, + exit_point, + 10000, + arch_info); + + if (retval2 != ERROR_OK) { + LOG_ERROR("error waiting for target flash write algorithm"); + retval = retval2; + } + + if (retval == ERROR_OK) { + /* check if algorithm set wp = 0 after fifo writer loop finished */ + retval = target_read_u32(target, wp_addr, &wp); + if (retval == ERROR_OK && wp == 0) { + LOG_ERROR("flash read algorithm aborted by target"); + retval = ERROR_FLASH_OPERATION_FAILED; + } + } + + return retval; +} + int target_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { @@ -1265,10 +1419,10 @@ int target_get_gdb_reg_list_noread(struct target *target, 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. + * exclude all the targets that don't provide get_gdb_reg_list + * or that have explicit gdb_max_connection == 0 */ - return !!target->type->get_gdb_reg_list; + return !!target->type->get_gdb_reg_list && !!target->gdb_max_connections; } int target_step(struct target *target, @@ -1328,13 +1482,9 @@ unsigned target_data_bits(struct target *target) return 32; } -int target_profiling(struct target *target, uint32_t *samples, +static int target_profiling(struct target *target, uint32_t *samples, uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds) { - if (target->state != TARGET_HALTED) { - LOG_WARNING("target %s is not halted (profiling)", target->cmd_name); - return ERROR_TARGET_NOT_HALTED; - } return target->type->profiling(target, samples, max_num_samples, num_samples, seconds); } @@ -2154,7 +2304,7 @@ static int target_gdb_fileio_end_default(struct target *target, return ERROR_OK; } -static int target_profiling_default(struct target *target, uint32_t *samples, +int target_profiling_default(struct target *target, uint32_t *samples, uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds) { struct timeval timeout, now; @@ -2905,7 +3055,7 @@ COMMAND_HANDLER(handle_reg_command) for (i = 0, reg = cache->reg_list; i < cache->num_regs; i++, reg++, count++) { - if (reg->exist == false) + if (reg->exist == false || reg->hidden) continue; /* only print cached values if they are valid */ if (reg->exist) { @@ -3441,11 +3591,11 @@ static COMMAND_HELPER(parse_load_image_command_CMD_ARGV, struct image *image, target_addr_t addr; COMMAND_PARSE_ADDRESS(CMD_ARGV[1], addr); image->base_address = addr; - image->base_address_set = 1; + image->base_address_set = true; } else - image->base_address_set = 0; + image->base_address_set = false; - image->start_address_set = 0; + image->start_address_set = false; if (CMD_ARGC >= 4) COMMAND_PARSE_ADDRESS(CMD_ARGV[3], *min_address); @@ -3468,7 +3618,6 @@ COMMAND_HANDLER(handle_load_image_command) uint32_t image_size; target_addr_t min_address = 0; target_addr_t max_address = -1; - int i; struct image image; int retval = CALL_COMMAND_HANDLER(parse_load_image_command_CMD_ARGV, @@ -3486,7 +3635,7 @@ COMMAND_HANDLER(handle_load_image_command) image_size = 0x0; retval = ERROR_OK; - for (i = 0; i < image.num_sections; i++) { + for (unsigned int i = 0; i < image.num_sections; i++) { buffer = malloc(image.sections[i].size); if (buffer == NULL) { command_print(CMD, @@ -3619,7 +3768,6 @@ static COMMAND_HELPER(handle_verify_image_command_internal, enum verify_mode ver uint8_t *buffer; size_t buf_cnt; uint32_t image_size; - int i; int retval; uint32_t checksum = 0; uint32_t mem_checksum = 0; @@ -3643,13 +3791,13 @@ static COMMAND_HELPER(handle_verify_image_command_internal, enum verify_mode ver target_addr_t addr; COMMAND_PARSE_ADDRESS(CMD_ARGV[1], addr); image.base_address = addr; - image.base_address_set = 1; + image.base_address_set = true; } else { - image.base_address_set = 0; + image.base_address_set = false; image.base_address = 0x0; } - image.start_address_set = 0; + image.start_address_set = false; retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL); if (retval != ERROR_OK) @@ -3658,12 +3806,12 @@ static COMMAND_HELPER(handle_verify_image_command_internal, enum verify_mode ver image_size = 0x0; int diffs = 0; retval = ERROR_OK; - for (i = 0; i < image.num_sections; i++) { + for (unsigned int i = 0; i < image.num_sections; i++) { buffer = malloc(image.sections[i].size); if (buffer == NULL) { command_print(CMD, - "error allocating buffer for section (%d bytes)", - (int)(image.sections[i].size)); + "error allocating buffer for section (%" PRIu32 " bytes)", + image.sections[i].size); break; } retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt); @@ -4134,6 +4282,7 @@ COMMAND_HANDLER(handle_profile_command) uint32_t offset; uint32_t num_of_samples; int retval = ERROR_OK; + bool halted_before_profiling = target->state == TARGET_HALTED; COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], offset); @@ -4164,12 +4313,23 @@ COMMAND_HANDLER(handle_profile_command) free(samples); return retval; } - if (target->state == TARGET_RUNNING) { + + if (target->state == TARGET_RUNNING && halted_before_profiling) { + /* The target was halted before we started and is running now. Halt it, + * for consistency. */ retval = target_halt(target); if (retval != ERROR_OK) { free(samples); return retval; } + } else if (target->state == TARGET_HALTED && !halted_before_profiling) { + /* The target was running before we started and is halted now. Resume + * it, for consistency. */ + retval = target_resume(target, 1, 0, 0, 0); + if (retval != ERROR_OK) { + free(samples); + return retval; + } } retval = target_poll(target); @@ -4678,6 +4838,7 @@ enum target_cfg_param { TCFG_RTOS, TCFG_DEFER_EXAMINE, TCFG_GDB_PORT, + TCFG_GDB_MAX_CONNECTIONS, }; static Jim_Nvp nvp_config_opts[] = { @@ -4694,6 +4855,7 @@ static Jim_Nvp nvp_config_opts[] = { { .name = "-rtos", .value = TCFG_RTOS }, { .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE }, { .name = "-gdb-port", .value = TCFG_GDB_PORT }, + { .name = "-gdb-max-connections", .value = TCFG_GDB_MAX_CONNECTIONS }, { .name = NULL, .value = -1 } }; @@ -5001,6 +5163,25 @@ no_params: Jim_SetResultString(goi->interp, target->gdb_port_override ? : "undefined", -1); /* loop for more */ break; + + case TCFG_GDB_MAX_CONNECTIONS: + if (goi->isconfigure) { + struct command_context *cmd_ctx = current_command_context(goi->interp); + if (cmd_ctx->mode != COMMAND_CONFIG) { + Jim_SetResultString(goi->interp, "-gdb-max-conenctions must be configured before 'init'", -1); + return JIM_ERR; + } + + e = Jim_GetOpt_Wide(goi, &w); + if (e != JIM_OK) + return e; + target->gdb_max_connections = (w < 0) ? CONNECTION_LIMIT_UNLIMITED : (int)w; + } else { + if (goi->argc != 0) + goto no_params; + } + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->gdb_max_connections)); + break; } } /* while (goi->argc) */ @@ -5581,6 +5762,7 @@ static int target_create(Jim_GetOptInfo *goi) target->rtos_auto_detect = false; target->gdb_port_override = NULL; + target->gdb_max_connections = 1; /* Do the rest as "configure" options */ goi->isconfigure = 1; @@ -5703,7 +5885,9 @@ static int jim_target_current(Jim_Interp *interp, int argc, Jim_Obj *const *argv struct command_context *cmd_ctx = current_command_context(interp); assert(cmd_ctx != NULL); - Jim_SetResultString(interp, target_name(get_current_target(cmd_ctx)), -1); + struct target *target = get_current_target_or_null(cmd_ctx); + if (target) + Jim_SetResultString(interp, target_name(target), -1); return JIM_OK; } @@ -5875,7 +6059,6 @@ COMMAND_HANDLER(handle_fast_load_image_command) uint32_t image_size; target_addr_t min_address = 0; target_addr_t max_address = -1; - int i; struct image image; @@ -5901,7 +6084,7 @@ COMMAND_HANDLER(handle_fast_load_image_command) return ERROR_FAIL; } memset(fastload, 0, sizeof(struct FastLoad)*image.num_sections); - for (i = 0; i < image.num_sections; i++) { + for (unsigned int i = 0; i < image.num_sections; i++) { buffer = malloc(image.sections[i].size); if (buffer == NULL) { command_print(CMD, "error allocating buffer for section (%d bytes)", diff --git a/src/target/target.h b/src/target/target.h index 4e8897c..f6044c5 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -47,7 +47,7 @@ struct gdb_fileio_info; /* * TARGET_UNKNOWN = 0: we don't know anything about the target yet - * TARGET_RUNNING = 1: the target is executing user code + * TARGET_RUNNING = 1: the target is executing or ready to execute user code * TARGET_HALTED = 2: the target is not executing code, and ready to talk to the * debugger. on an xscale it means that the debug handler is executing * TARGET_RESET = 3: the target is being held in reset (only a temporary state, @@ -211,6 +211,8 @@ struct target { char *gdb_port_override; /* target-specific override for gdb_port */ + int gdb_max_connections; /* max number of simultaneous gdb connections */ + /* The semihosting information, extracted from the target. */ struct semihosting *semihosting; }; @@ -576,6 +578,18 @@ int target_run_flash_async_algorithm(struct target *target, void *arch_info); /** + * This routine is a wrapper for asynchronous algorithms. + * + */ +int target_run_read_async_algorithm(struct target *target, + uint8_t *buffer, uint32_t count, int block_size, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + uint32_t buffer_start, uint32_t buffer_size, + uint32_t entry_point, uint32_t exit_point, + void *arch_info); + +/** * Read @a count items of @a size bytes from the memory of @a target at * the @a address given. * @@ -755,6 +769,9 @@ void target_handle_md_output(struct command_invocation *cmd, struct target *target, target_addr_t address, unsigned size, unsigned count, const uint8_t *buffer, bool include_address); +int target_profiling_default(struct target *target, uint32_t *samples, uint32_t + max_num_samples, uint32_t *num_samples, uint32_t seconds); + #define ERROR_TARGET_INVALID (-300) #define ERROR_TARGET_INIT_FAILED (-301) #define ERROR_TARGET_TIMEOUT (-302) diff --git a/src/target/xscale.c b/src/target/xscale.c index 6d1d426..b25999d 100644 --- a/src/target/xscale.c +++ b/src/target/xscale.c @@ -2582,7 +2582,6 @@ static int xscale_read_instruction(struct target *target, uint32_t pc, struct arm_instruction *instruction) { struct xscale_common *const xscale = target_to_xscale(target); - int i; int section = -1; size_t size_read; uint32_t opcode; @@ -2592,7 +2591,7 @@ static int xscale_read_instruction(struct target *target, uint32_t pc, return ERROR_TRACE_IMAGE_UNAVAILABLE; /* search for the section the current instruction belongs to */ - for (i = 0; i < xscale->trace.image->num_sections; i++) { + for (unsigned int i = 0; i < xscale->trace.image->num_sections; i++) { if ((xscale->trace.image->sections[i].base_address <= pc) && (xscale->trace.image->sections[i].base_address + xscale->trace.image->sections[i].size > pc)) { @@ -2883,7 +2882,7 @@ static void xscale_build_reg_cache(struct target *target) /* fill in values for the xscale reg cache */ (*cache_p)->name = "XScale registers"; (*cache_p)->next = NULL; - (*cache_p)->reg_list = malloc(num_regs * sizeof(struct reg)); + (*cache_p)->reg_list = calloc(num_regs, sizeof(struct reg)); (*cache_p)->num_regs = num_regs; for (i = 0; i < num_regs; i++) { @@ -3428,15 +3427,15 @@ COMMAND_HANDLER(xscale_handle_trace_image_command) } xscale->trace.image = malloc(sizeof(struct image)); - xscale->trace.image->base_address_set = 0; - xscale->trace.image->start_address_set = 0; + xscale->trace.image->base_address_set = false; + xscale->trace.image->start_address_set = false; /* a base address isn't always necessary, default to 0x0 (i.e. don't relocate) */ if (CMD_ARGC >= 2) { - xscale->trace.image->base_address_set = 1; + xscale->trace.image->base_address_set = true; COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], xscale->trace.image->base_address); } else - xscale->trace.image->base_address_set = 0; + xscale->trace.image->base_address_set = false; if (image_open(xscale->trace.image, CMD_ARGV[0], (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL) != ERROR_OK) { diff --git a/src/transport/transport.c b/src/transport/transport.c index 9214dcd..cb000ab 100644 --- a/src/transport/transport.c +++ b/src/transport/transport.c @@ -122,16 +122,6 @@ int allow_transports(struct command_context *ctx, const char * const *vector) } /** - * Used to verify correct adapter driver initialization. - * - * @returns true if the adapter declares one or more transports. - */ -bool transports_are_declared(void) -{ - return allowed_transports != NULL; -} - -/** * Registers a transport. There are general purpose transports * (such as JTAG), as well as relatively proprietary ones which are * specific to a given chip (or chip family). diff --git a/src/transport/transport.h b/src/transport/transport.h index 809564e..6bf6aac 100644 --- a/src/transport/transport.h +++ b/src/transport/transport.h @@ -92,15 +92,13 @@ COMMAND_HELPER(transport_list_parse, char ***vector); int allow_transports(struct command_context *ctx, const char * const *vector); -bool transports_are_declared(void); - bool transport_is_jtag(void); bool transport_is_swd(void); bool transport_is_dapdirect_jtag(void); bool transport_is_dapdirect_swd(void); bool transport_is_swim(void); -#if BUILD_HLADAPTER +#if BUILD_HLADAPTER && !HAVE_JTAG_MINIDRIVER_H bool transport_is_hla(void); #else static inline bool transport_is_hla(void) |