diff options
author | Tim Newsome <tim@sifive.com> | 2018-08-29 15:55:30 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-29 15:55:30 -0700 |
commit | 164415cfbe1647b19661f9e4046372dd5df2c204 (patch) | |
tree | 299273e5df105d0b16ad42dd1e9554074f4b6676 /src | |
parent | 0ed96e80d3f9b8828e6c3ca33ae91b0da6543c33 (diff) | |
parent | b4b2ec7d2d143146226e7b2f06e1399ee560148d (diff) | |
download | riscv-openocd-164415cfbe1647b19661f9e4046372dd5df2c204.zip riscv-openocd-164415cfbe1647b19661f9e4046372dd5df2c204.tar.gz riscv-openocd-164415cfbe1647b19661f9e4046372dd5df2c204.tar.bz2 |
Merge branch 'riscv' into sba_tests
Diffstat (limited to 'src')
179 files changed, 18287 insertions, 3694 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index cc72088..9a58239 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -18,6 +18,9 @@ NOR_DRIVERS = \ %D%/ath79.c \ %D%/atsamv.c \ %D%/avrf.c \ + %D%/bluenrg-x.c \ + %D%/cc3220sf.c \ + %D%/cc26xx.c \ %D%/cfi.c \ %D%/dsp5680xx_flash.c \ %D%/efm32.c \ @@ -34,6 +37,7 @@ NOR_DRIVERS = \ %D%/lpc2900.c \ %D%/lpcspifi.c \ %D%/mdr.c \ + %D%/msp432.c \ %D%/mrvlqspi.c \ %D%/niietcm4.c \ %D%/non_cfi.c \ @@ -42,6 +46,8 @@ NOR_DRIVERS = \ %D%/ocl.c \ %D%/pic32mx.c \ %D%/psoc4.c \ + %D%/psoc5lp.c \ + %D%/psoc6.c \ %D%/sim3x.c \ %D%/spi.c \ %D%/stmsmi.c \ @@ -62,9 +68,12 @@ NOR_DRIVERS = \ NORHEADERS = \ %D%/core.h \ + %D%/cc3220sf.h \ + %D%/cc26xx.h \ %D%/cfi.h \ %D%/driver.h \ %D%/imp.h \ %D%/non_cfi.h \ %D%/ocl.h \ - %D%/spi.h + %D%/spi.h \ + %D%/msp432.h diff --git a/src/flash/nor/ambiqmicro.c b/src/flash/nor/ambiqmicro.c index b2c30e6..13b2b26 100644 --- a/src/flash/nor/ambiqmicro.c +++ b/src/flash/nor/ambiqmicro.c @@ -901,4 +901,5 @@ struct flash_driver ambiqmicro_flash = { .erase_check = default_flash_blank_check, .protect_check = ambiqmicro_protect_check, .info = get_ambiqmicro_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/at91sam3.c b/src/flash/nor/at91sam3.c index 1536378..d80b6fe 100644 --- a/src/flash/nor/at91sam3.c +++ b/src/flash/nor/at91sam3.c @@ -3117,6 +3117,22 @@ FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command) return ERROR_OK; } +/** + * Remove all chips from the internal list without distingushing which one + * is owned by this bank. This simplification works only for one shot + * deallocation like current flash_free_all_banks() + */ +void sam3_free_driver_priv(struct flash_bank *bank) +{ + struct sam3_chip *chip = all_sam3_chips; + while (chip) { + struct sam3_chip *next = chip->next; + free(chip); + chip = next; + } + all_sam3_chips = NULL; +} + static int sam3_GetDetails(struct sam3_bank_private *pPrivate) { const struct sam3_chip_details *pDetails; @@ -3771,4 +3787,5 @@ struct flash_driver at91sam3_flash = { .auto_probe = sam3_auto_probe, .erase_check = sam3_erase_check, .protect_check = sam3_protect_check, + .free_driver_priv = sam3_free_driver_priv, }; diff --git a/src/flash/nor/at91sam4.c b/src/flash/nor/at91sam4.c index d101c9b..c5b31e9 100644 --- a/src/flash/nor/at91sam4.c +++ b/src/flash/nor/at91sam4.c @@ -682,6 +682,40 @@ static const struct sam4_chip_details all_sam4_details[] = { }, }, }, + /*at91sam4sa16c - TFBGA100/VFBGA100/LQFP100*/ + { + .chipid_cidr = 0x28a70ce0, + .name = "at91sam4sa16c", + .total_flash_size = 1024 * 1024, + .total_sram_size = 160 * 1024, + .n_gpnvms = 2, + .n_banks = 1, + +/* .bank[0] = { */ + { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK_BASE_S, + .controller_address = 0x400e0a00, + .flash_wait_states = 5, + .present = 1, + .size_bytes = 1024 * 1024, + .nsectors = 128, + .sector_size = 8192, + .page_size = 512, + }, +/* .bank[1] = {*/ + { + .present = 0, + .probed = 0, + .bank_number = 1, + + }, + }, + }, /*atsam4s16b - LQFP64/QFN64/WLCSP64*/ { .chipid_cidr = 0x289C0CE0, @@ -1261,50 +1295,6 @@ static const struct sam4_chip_details all_sam4_details[] = { }, }, - /*at91sam4sa16c*/ - { - .chipid_cidr = 0x28a70ce0, - .name = "at91sam4sa16c", - .total_flash_size = 1024 * 1024, - .total_sram_size = 160 * 1024, - .n_gpnvms = 3, - .n_banks = 2, - -/* .bank[0] = { */ - { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE_SD, - .controller_address = 0x400e0a00, - .flash_wait_states = 5, - .present = 1, - .size_bytes = 512 * 1024, - .nsectors = 64, - .sector_size = 8192, - .page_size = 512, - }, - -/* .bank[1] = { */ - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 1, - .base_address = FLASH_BANK1_BASE_1024K_SD, - .controller_address = 0x400e0c00, - .flash_wait_states = 5, - .present = 1, - .size_bytes = 512 * 1024, - .nsectors = 64, - .sector_size = 8192, - .page_size = 512, - }, - }, - }, - /* atsamg53n19 */ { .chipid_cidr = 0x247e0ae0, @@ -2514,6 +2504,22 @@ FLASH_BANK_COMMAND_HANDLER(sam4_flash_bank_command) return ERROR_OK; } +/** + * Remove all chips from the internal list without distingushing which one + * is owned by this bank. This simplification works only for one shot + * deallocation like current flash_free_all_banks() + */ +static void sam4_free_driver_priv(struct flash_bank *bank) +{ + struct sam4_chip *chip = all_sam4_chips; + while (chip) { + struct sam4_chip *next = chip->next; + free(chip); + chip = next; + } + all_sam4_chips = NULL; +} + static int sam4_GetDetails(struct sam4_bank_private *pPrivate) { const struct sam4_chip_details *pDetails; @@ -2538,6 +2544,8 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate) pPrivate->pChip->cfg.CHIPID_CIDR); sam4_explain_chipid_cidr(pPrivate->pChip); return ERROR_FAIL; + } else { + LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr); } /* DANGER: THERE ARE DRAGONS HERE */ @@ -2608,6 +2616,7 @@ static int _sam4_probe(struct flash_bank *bank, int noise) for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) { if (bank->base == pPrivate->pChip->details.bank[x].base_address) { bank->size = pPrivate->pChip->details.bank[x].size_bytes; + LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x); break; } } @@ -3194,4 +3203,5 @@ struct flash_driver at91sam4_flash = { .auto_probe = sam4_auto_probe, .erase_check = default_flash_blank_check, .protect_check = sam4_protect_check, + .free_driver_priv = sam4_free_driver_priv, }; diff --git a/src/flash/nor/at91sam4l.c b/src/flash/nor/at91sam4l.c index 0a605d5..794ccbb 100644 --- a/src/flash/nor/at91sam4l.c +++ b/src/flash/nor/at91sam4l.c @@ -129,10 +129,8 @@ struct sam4l_info { bool probed; struct target *target; - struct sam4l_info *next; }; -static struct sam4l_info *sam4l_chips; static int sam4l_flash_wait_until_ready(struct target *target) { @@ -204,30 +202,6 @@ static int sam4l_flash_command(struct target *target, uint8_t cmd, int page) FLASH_BANK_COMMAND_HANDLER(sam4l_flash_bank_command) { - struct sam4l_info *chip = sam4l_chips; - - while (chip) { - if (chip->target == bank->target) - break; - chip = chip->next; - } - - if (!chip) { - /* Create a new chip */ - chip = calloc(1, sizeof(*chip)); - if (!chip) - return ERROR_FAIL; - - chip->target = bank->target; - chip->probed = false; - - bank->driver_priv = chip; - - /* Insert it into the chips list (at head) */ - chip->next = sam4l_chips; - sam4l_chips = chip; - } - if (bank->base != SAM4L_FLASH) { LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32 "[at91sam4l series] )", @@ -235,6 +209,18 @@ FLASH_BANK_COMMAND_HANDLER(sam4l_flash_bank_command) return ERROR_FAIL; } + struct sam4l_info *chip; + chip = calloc(1, sizeof(*chip)); + if (!chip) { + LOG_ERROR("No memory for flash bank chip info"); + return ERROR_FAIL; + } + + chip->target = bank->target; + chip->probed = false; + + bank->driver_priv = chip; + return ERROR_OK; } @@ -396,7 +382,7 @@ static int sam4l_protect_check(struct flash_bank *bank) static int sam4l_protect(struct flash_bank *bank, int set, int first, int last) { - struct sam4l_info *chip = sam4l_chips; + struct sam4l_info *chip = (struct sam4l_info *)bank->driver_priv; if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -709,4 +695,5 @@ struct flash_driver at91sam4l_flash = { .auto_probe = sam4l_probe, .erase_check = default_flash_blank_check, .protect_check = sam4l_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/at91sam7.c b/src/flash/nor/at91sam7.c index 03f771c..9de8293 100644 --- a/src/flash/nor/at91sam7.c +++ b/src/flash/nor/at91sam7.c @@ -639,14 +639,6 @@ static int at91sam7_read_part_info(struct flash_bank *bank) static int at91sam7_erase_check(struct flash_bank *bank) { - struct target *target = bank->target; - uint16_t retval; - uint32_t blank; - uint16_t fast_check; - uint8_t *buffer; - uint16_t nSector; - uint16_t nByte; - if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -656,45 +648,7 @@ static int at91sam7_erase_check(struct flash_bank *bank) at91sam7_read_clock_info(bank); at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); - fast_check = 1; - for (nSector = 0; nSector < bank->num_sectors; nSector++) { - retval = target_blank_check_memory(target, - bank->base + bank->sectors[nSector].offset, - bank->sectors[nSector].size, - &blank, bank->erased_value); - if (retval != ERROR_OK) { - fast_check = 0; - break; - } - if (blank == 0xFF) - bank->sectors[nSector].is_erased = 1; - else - bank->sectors[nSector].is_erased = 0; - } - - if (fast_check) - return ERROR_OK; - - LOG_USER("Running slow fallback erase check - add working memory"); - - buffer = malloc(bank->sectors[0].size); - for (nSector = 0; nSector < bank->num_sectors; nSector++) { - bank->sectors[nSector].is_erased = 1; - retval = target_read_memory(target, bank->base + bank->sectors[nSector].offset, 4, - bank->sectors[nSector].size/4, buffer); - if (retval != ERROR_OK) - return retval; - - for (nByte = 0; nByte < bank->sectors[nSector].size; nByte++) { - if (buffer[nByte] != 0xFF) { - bank->sectors[nSector].is_erased = 0; - break; - } - } - } - free(buffer); - - return ERROR_OK; + return default_flash_blank_check(bank); } static int at91sam7_protect_check(struct flash_bank *bank) diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index ad88c51..017d144 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -83,6 +83,9 @@ #define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F)) #define SAMD_GET_DEVSEL(id) (id & 0xFF) +/* Bits to mask out lockbits in user row */ +#define NVMUSERROW_LOCKBIT_MASK ((uint64_t)0x0000FFFFFFFFFFFF) + struct samd_part { uint8_t id; const char *name; @@ -112,15 +115,16 @@ static const struct samd_part samd10_parts[] = { /* Known SAMD11 parts */ static const struct samd_part samd11_parts[] = { - { 0x0, "SAMD11D14AMU", 16, 4 }, + { 0x0, "SAMD11D14AM", 16, 4 }, { 0x1, "SAMD11D13AMU", 8, 4 }, { 0x2, "SAMD11D12AMU", 4, 4 }, - { 0x3, "SAMD11D14ASU", 16, 4 }, + { 0x3, "SAMD11D14ASS", 16, 4 }, { 0x4, "SAMD11D13ASU", 8, 4 }, { 0x5, "SAMD11D12ASU", 4, 4 }, { 0x6, "SAMD11C14A", 16, 4 }, { 0x7, "SAMD11C13A", 8, 4 }, { 0x8, "SAMD11C12A", 4, 4 }, + { 0x9, "SAMD11D14AU", 16, 4 }, }; /* Known SAMD20 parts. See Table 12-8 in 42129F–SAM–10/2013 */ @@ -159,23 +163,22 @@ static const struct samd_part samd21_parts[] = { { 0xC, "SAMD21E16A", 64, 8 }, { 0xD, "SAMD21E15A", 32, 4 }, { 0xE, "SAMD21E14A", 16, 2 }, - /* Below are B Variants (Table 3-7 from rev I of datasheet) */ - { 0x20, "SAMD21J16B", 64, 8 }, - { 0x21, "SAMD21J15B", 32, 4 }, - { 0x23, "SAMD21G16B", 64, 8 }, - { 0x24, "SAMD21G15B", 32, 4 }, - { 0x26, "SAMD21E16B", 64, 8 }, - { 0x27, "SAMD21E15B", 32, 4 }, -}; -/* Known SAMR21 parts. */ -static const struct samd_part samr21_parts[] = { + /* SAMR21 parts have integrated SAMD21 with a radio */ { 0x19, "SAMR21G18A", 256, 32 }, { 0x1A, "SAMR21G17A", 128, 32 }, { 0x1B, "SAMR21G16A", 64, 32 }, { 0x1C, "SAMR21E18A", 256, 32 }, { 0x1D, "SAMR21E17A", 128, 32 }, { 0x1E, "SAMR21E16A", 64, 32 }, + + /* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */ + { 0x20, "SAMD21J16B", 64, 8 }, + { 0x21, "SAMD21J15B", 32, 4 }, + { 0x23, "SAMD21G16B", 64, 8 }, + { 0x24, "SAMD21G15B", 32, 4 }, + { 0x26, "SAMD21E16B", 64, 8 }, + { 0x27, "SAMD21E15B", 32, 4 }, }; /* Known SAML21 parts. */ @@ -200,6 +203,10 @@ static const struct samd_part saml21_parts[] = { { 0x1A, "SAML21E17B", 128, 16 }, { 0x1B, "SAML21E16B", 64, 8 }, { 0x1C, "SAML21E15B", 32, 4 }, + + /* SAMR30 parts have integrated SAML21 with a radio */ + { 0x1E, "SAMR30G18A", 256, 32 }, + { 0x1F, "SAMR30E18A", 256, 32 }, }; /* Known SAML22 parts. */ @@ -256,30 +263,38 @@ struct samd_family { uint8_t series; const struct samd_part *parts; size_t num_parts; + uint64_t nvm_userrow_res_mask; /* protect bits which are reserved, 0 -> protect */ }; /* Known SAMD families */ static const struct samd_family samd_families[] = { { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20, - samd20_parts, ARRAY_SIZE(samd20_parts) }, - { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21, - samd21_parts, ARRAY_SIZE(samd21_parts) }, + samd20_parts, ARRAY_SIZE(samd20_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21, - samr21_parts, ARRAY_SIZE(samr21_parts) }, + samd21_parts, ARRAY_SIZE(samd21_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09, - samd09_parts, ARRAY_SIZE(samd09_parts) }, + samd09_parts, ARRAY_SIZE(samd09_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10, - samd10_parts, ARRAY_SIZE(samd10_parts) }, + samd10_parts, ARRAY_SIZE(samd10_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11, - samd11_parts, ARRAY_SIZE(samd11_parts) }, + samd11_parts, ARRAY_SIZE(samd11_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_21, - saml21_parts, ARRAY_SIZE(saml21_parts) }, + saml21_parts, ARRAY_SIZE(saml21_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_22, - saml22_parts, ARRAY_SIZE(saml22_parts) }, + saml22_parts, ARRAY_SIZE(saml22_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_20, - samc20_parts, ARRAY_SIZE(samc20_parts) }, + samc20_parts, ARRAY_SIZE(samc20_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_21, - samc21_parts, ARRAY_SIZE(samc21_parts) }, + samc21_parts, ARRAY_SIZE(samc21_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, }; struct samd_info { @@ -290,29 +305,45 @@ struct samd_info { bool probed; struct target *target; - struct samd_info *next; }; -static struct samd_info *samd_chips; - - -static const struct samd_part *samd_find_part(uint32_t id) +/** + * Gives the family structure to specific device id. + * @param id The id of the device. + * @return On failure NULL, otherwise a pointer to the structure. + */ +static const struct samd_family *samd_find_family(uint32_t id) { uint8_t processor = SAMD_GET_PROCESSOR(id); uint8_t family = SAMD_GET_FAMILY(id); uint8_t series = SAMD_GET_SERIES(id); - uint8_t devsel = SAMD_GET_DEVSEL(id); for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) { if (samd_families[i].processor == processor && samd_families[i].series == series && - samd_families[i].family == family) { - for (unsigned j = 0; j < samd_families[i].num_parts; j++) { - if (samd_families[i].parts[j].id == devsel) - return &samd_families[i].parts[j]; - } - } + samd_families[i].family == family) + return &samd_families[i]; + } + + return NULL; +} + +/** + * Gives the part structure to specific device id. + * @param id The id of the device. + * @return On failure NULL, otherwise a pointer to the structure. + */ +static const struct samd_part *samd_find_part(uint32_t id) +{ + uint8_t devsel = SAMD_GET_DEVSEL(id); + const struct samd_family *family = samd_find_family(id); + if (family == NULL) + return NULL; + + for (unsigned i = 0; i < family->num_parts; i++) { + if (family->parts[i].id == devsel) + return &family->parts[i]; } return NULL; @@ -483,6 +514,12 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd) return samd_check_error(target); } +/** + * Erases a flash-row at the given address. + * @param target Pointer to the target structure. + * @param address The address of the row. + * @return On success ERROR_OK, on failure an errorcode. + */ static int samd_erase_row(struct target *target, uint32_t address) { int res; @@ -504,49 +541,62 @@ static int samd_erase_row(struct target *target, uint32_t address) return ERROR_OK; } -static bool is_user_row_reserved_bit(uint8_t bit) +/** + * Returns the bitmask of reserved bits in register. + * @param target Pointer to the target structure. + * @param mask Bitmask, 0 -> value stays untouched. + * @return On success ERROR_OK, on failure an errorcode. + */ +static int samd_get_reservedmask(struct target *target, uint64_t *mask) { - /* See Table 9-3 in the SAMD20 datasheet for more information. */ - switch (bit) { - /* Reserved bits */ - case 3: - case 7: - /* Voltage regulator internal configuration with default value of 0x70, - * may not be changed. */ - case 17 ... 24: - /* 41 is voltage regulator internal configuration and must not be - * changed. 42 through 47 are reserved. */ - case 41 ... 47: - return true; - default: - break; + int res; + /* Get the devicetype */ + uint32_t id; + res = target_read_u32(target, SAMD_DSU + SAMD_DSU_DID, &id); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't read Device ID register"); + return res; } + const struct samd_family *family; + family = samd_find_family(id); + if (family == NULL) { + LOG_ERROR("Couldn't determine device family"); + return ERROR_FAIL; + } + *mask = family->nvm_userrow_res_mask; + return ERROR_OK; +} - return false; +static int read_userrow(struct target *target, uint64_t *userrow) +{ + int res; + uint8_t buffer[8]; + + res = target_read_memory(target, SAMD_USER_ROW, 4, 2, buffer); + if (res != ERROR_OK) + return res; + + *userrow = target_buffer_get_u64(target, buffer); + return ERROR_OK; } -/* Modify the contents of the User Row in Flash. These are described in Table - * 9-3 of the SAMD20 datasheet. The User Row itself has a size of one page - * and contains a combination of "fuses" and calibration data in bits 24:17. - * We therefore try not to erase the row's contents unless we absolutely have - * to and we don't permit modifying reserved bits. */ -static int samd_modify_user_row(struct target *target, uint32_t value, - uint8_t startb, uint8_t endb) +/** + * Modify the contents of the User Row in Flash. The User Row itself + * has a size of one page and contains a combination of "fuses" and + * calibration data. Bits which have a value of zero in the mask will + * not be changed. Up to now devices only use the first 64 bits. + * @param target Pointer to the target structure. + * @param value_input The value to write. + * @param value_mask Bitmask, 0 -> value stays untouched. + * @return On success ERROR_OK, on failure an errorcode. + */ +static int samd_modify_user_row_masked(struct target *target, + uint64_t value_input, uint64_t value_mask) { int res; uint32_t nvm_ctrlb; bool manual_wp = true; - if (is_user_row_reserved_bit(startb) || is_user_row_reserved_bit(endb)) { - LOG_ERROR("Can't modify bits in the requested range"); - return ERROR_FAIL; - } - - /* Check if we need to do manual page write commands */ - res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb); - if (res == ERROR_OK) - manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0; - /* Retrieve the MCU's page size, in bytes. This is also the size of the * entire User Row. */ uint32_t page_size; @@ -556,44 +606,49 @@ static int samd_modify_user_row(struct target *target, uint32_t value, return res; } - /* Make sure the size is sane before we allocate. */ - assert(page_size > 0 && page_size <= SAMD_PAGE_SIZE_MAX); - - /* Make sure we're within the single page that comprises the User Row. */ - if (startb >= (page_size * 8) || endb >= (page_size * 8)) { - LOG_ERROR("Can't modify bits outside the User Row page range"); - return ERROR_FAIL; - } - - uint8_t *buf = malloc(page_size); - if (!buf) - return ERROR_FAIL; + /* Make sure the size is sane. */ + assert(page_size <= SAMD_PAGE_SIZE_MAX && + page_size >= sizeof(value_input)); + uint8_t buf[SAMD_PAGE_SIZE_MAX]; /* Read the user row (comprising one page) by words. */ res = target_read_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); if (res != ERROR_OK) - goto out_user_row; + return res; + + uint64_t value_device; + res = read_userrow(target, &value_device); + if (res != ERROR_OK) + return res; + uint64_t value_new = (value_input & value_mask) | (value_device & ~value_mask); /* We will need to erase before writing if the new value needs a '1' in any * position for which the current value had a '0'. Otherwise we can avoid * erasing. */ - uint32_t cur = buf_get_u32(buf, startb, endb - startb + 1); - if ((~cur) & value) { + if ((~value_device) & value_new) { res = samd_erase_row(target, SAMD_USER_ROW); if (res != ERROR_OK) { LOG_ERROR("Couldn't erase user row"); - goto out_user_row; + return res; } } /* Modify */ - buf_set_u32(buf, startb, endb - startb + 1, value); + target_buffer_set_u64(target, buf, value_new); /* Write the page buffer back out to the target. */ res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); if (res != ERROR_OK) - goto out_user_row; + return res; + /* Check if we need to do manual page write commands */ + res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb); + if (res == ERROR_OK) + manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0; + else { + LOG_ERROR("Read of NVM register CTRKB failed."); + return ERROR_FAIL; + } if (manual_wp) { /* Trigger flash write */ res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_WAP); @@ -601,12 +656,28 @@ static int samd_modify_user_row(struct target *target, uint32_t value, res = samd_check_error(target); } -out_user_row: - free(buf); - return res; } +/** + * Modifies the user row register to the given value. + * @param target Pointer to the target structure. + * @param value The value to write. + * @param startb The bit-offset by which the given value is shifted. + * @param endb The bit-offset of the last bit in value to write. + * @return On success ERROR_OK, on failure an errorcode. + */ +static int samd_modify_user_row(struct target *target, uint64_t value, + uint8_t startb, uint8_t endb) +{ + uint64_t mask = 0; + int i; + for (i = startb ; i <= endb ; i++) + mask |= ((uint64_t)1) << i; + + return samd_modify_user_row_masked(target, value << startb, mask); +} + static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl) { int res = ERROR_OK; @@ -643,7 +714,8 @@ static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int * corresponding to Sector 15. A '1' means unlocked and a '0' means * locked. See Table 9-3 in the SAMD20 datasheet for more details. */ - res = samd_modify_user_row(bank->target, set ? 0x0000 : 0xFFFF, + res = samd_modify_user_row(bank->target, + set ? (uint64_t)0 : (uint64_t)UINT64_MAX, 48 + first_prot_bl, 48 + last_prot_bl); if (res != ERROR_OK) LOG_WARNING("SAMD: protect settings were not made persistent!"); @@ -803,30 +875,6 @@ free_pb: FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command) { - struct samd_info *chip = samd_chips; - - while (chip) { - if (chip->target == bank->target) - break; - chip = chip->next; - } - - if (!chip) { - /* Create a new chip */ - chip = calloc(1, sizeof(*chip)); - if (!chip) - return ERROR_FAIL; - - chip->target = bank->target; - chip->probed = false; - - bank->driver_priv = chip; - - /* Insert it into the chips list (at head) */ - chip->next = samd_chips; - samd_chips = chip; - } - if (bank->base != SAMD_FLASH) { LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32 "[at91samd series] )", @@ -834,6 +882,18 @@ FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command) return ERROR_FAIL; } + struct samd_info *chip; + chip = calloc(1, sizeof(*chip)); + if (!chip) { + LOG_ERROR("No memory for flash bank chip info"); + return ERROR_FAIL; + } + + chip->target = bank->target; + chip->probed = false; + + bank->driver_priv = chip; + return ERROR_OK; } @@ -944,6 +1004,83 @@ COMMAND_HANDLER(samd_handle_eeprom_command) return res; } +static COMMAND_HELPER(get_u64_from_hexarg, unsigned int num, uint64_t *value) +{ + if (num >= CMD_ARGC) { + command_print(CMD_CTX, "Too few Arguments."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (strlen(CMD_ARGV[num]) >= 3 && + CMD_ARGV[num][0] == '0' && + CMD_ARGV[num][1] == 'x') { + char *check = NULL; + *value = strtoull(&(CMD_ARGV[num][2]), &check, 16); + if ((value == 0 && errno == ERANGE) || + check == NULL || *check != 0) { + command_print(CMD_CTX, "Invalid 64-bit hex value in argument %d.", + num + 1); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } else { + command_print(CMD_CTX, "Argument %d needs to be a hex value.", num + 1); + return ERROR_COMMAND_SYNTAX_ERROR; + } + return ERROR_OK; +} + +COMMAND_HANDLER(samd_handle_nvmuserrow_command) +{ + int res = ERROR_OK; + struct target *target = get_current_target(CMD_CTX); + + if (target) { + if (CMD_ARGC > 2) { + command_print(CMD_CTX, "Too much Arguments given."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (CMD_ARGC > 0) { + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + uint64_t mask; + res = samd_get_reservedmask(target, &mask); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't determine the mask for reserved bits."); + return ERROR_FAIL; + } + mask &= NVMUSERROW_LOCKBIT_MASK; + + uint64_t value; + res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 0, &value); + if (res != ERROR_OK) + return res; + if (CMD_ARGC == 2) { + uint64_t mask_temp; + res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 1, &mask_temp); + if (res != ERROR_OK) + return res; + mask &= mask_temp; + } + res = samd_modify_user_row_masked(target, value, mask); + if (res != ERROR_OK) + return res; + } + + /* read register */ + uint64_t value; + res = read_userrow(target, &value); + if (res == ERROR_OK) + command_print(CMD_CTX, "NVMUSERROW: 0x%016"PRIX64, value); + else + LOG_ERROR("NVMUSERROW could not be read."); + } + return res; +} + COMMAND_HANDLER(samd_handle_bootloader_command) { int res = ERROR_OK; @@ -1049,29 +1186,29 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .name = "dsu_reset_deassert", .handler = samd_handle_reset_deassert, .mode = COMMAND_EXEC, - .help = "deasert internal reset held by DSU" + .help = "Deasert internal reset held by DSU." }, { .name = "info", .handler = samd_handle_info_command, .mode = COMMAND_EXEC, - .help = "Print information about the current at91samd chip" + .help = "Print information about the current at91samd chip " "and its flash configuration.", }, { .name = "chip-erase", .handler = samd_handle_chip_erase_command, .mode = COMMAND_EXEC, - .help = "Erase the entire Flash by using the Chip" + .help = "Erase the entire Flash by using the Chip-" "Erase feature in the Device Service Unit (DSU).", }, { .name = "set-security", .handler = samd_handle_set_security_command, .mode = COMMAND_EXEC, - .help = "Secure the chip's Flash by setting the Security Bit." - "This makes it impossible to read the Flash contents." - "The only way to undo this is to issue the chip-erase" + .help = "Secure the chip's Flash by setting the Security Bit. " + "This makes it impossible to read the Flash contents. " + "The only way to undo this is to issue the chip-erase " "command.", }, { @@ -1079,9 +1216,9 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .usage = "[size_in_bytes]", .handler = samd_handle_eeprom_command, .mode = COMMAND_EXEC, - .help = "Show or set the EEPROM size setting, stored in the User Row." - "Please see Table 20-3 of the SAMD20 datasheet for allowed values." - "Changes are stored immediately but take affect after the MCU is" + .help = "Show or set the EEPROM size setting, stored in the User Row. " + "Please see Table 20-3 of the SAMD20 datasheet for allowed values. " + "Changes are stored immediately but take affect after the MCU is " "reset.", }, { @@ -1089,11 +1226,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .usage = "[size_in_bytes]", .handler = samd_handle_bootloader_command, .mode = COMMAND_EXEC, - .help = "Show or set the bootloader size, stored in the User Row." - "Please see Table 20-2 of the SAMD20 datasheet for allowed values." - "Changes are stored immediately but take affect after the MCU is" + .help = "Show or set the bootloader size, stored in the User Row. " + "Please see Table 20-2 of the SAMD20 datasheet for allowed values. " + "Changes are stored immediately but take affect after the MCU is " "reset.", }, + { + .name = "nvmuserrow", + .usage = "[value] [mask]", + .handler = samd_handle_nvmuserrow_command, + .mode = COMMAND_EXEC, + .help = "Show or set the nvmuserrow register. It is 64 bit wide " + "and located at address 0x804000. Use the optional mask argument " + "to prevent changes at positions where the bitvalue is zero. " + "For security reasons the lock- and reserved-bits are masked out " + "in background and therefore cannot be changed.", + }, COMMAND_REGISTRATION_DONE }; @@ -1120,4 +1268,5 @@ struct flash_driver at91samd_flash = { .auto_probe = samd_probe, .erase_check = default_flash_blank_check, .protect_check = samd_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/ath79.c b/src/flash/nor/ath79.c index 451e843..c5f9eed 100644 --- a/src/flash/nor/ath79.c +++ b/src/flash/nor/ath79.c @@ -898,4 +898,5 @@ struct flash_driver ath79_flash = { .erase_check = ath79_flash_blank_check, .protect_check = ath79_protect_check, .info = get_ath79_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/atsamv.c b/src/flash/nor/atsamv.c index 73f0238..9c07bdf 100644 --- a/src/flash/nor/atsamv.c +++ b/src/flash/nor/atsamv.c @@ -739,4 +739,5 @@ struct flash_driver atsamv_flash = { .erase_check = default_flash_blank_check, .protect_check = samv_protect_check, .info = samv_get_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/avrf.c b/src/flash/nor/avrf.c index 11cc3b2..65ac601 100644 --- a/src/flash/nor/avrf.c +++ b/src/flash/nor/avrf.c @@ -487,4 +487,5 @@ struct flash_driver avr_flash = { .erase_check = default_flash_blank_check, .protect_check = avrf_protect_check, .info = avrf_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/bluenrg-x.c b/src/flash/nor/bluenrg-x.c new file mode 100644 index 0000000..2b56859 --- /dev/null +++ b/src/flash/nor/bluenrg-x.c @@ -0,0 +1,554 @@ +/*************************************************************************** + * Copyright (C) 2017 by Michele Sardo * + * msmttchr@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <target/algorithm.h> +#include <target/armv7m.h> +#include <target/cortex_m.h> +#include "imp.h" + +#define FLASH_SIZE_REG (0x40100014) +#define DIE_ID_REG (0x4090001C) +#define JTAG_IDCODE_REG (0x40900028) +#define BLUENRG2_IDCODE (0x0200A041) +#define FLASH_BASE (0x10040000) +#define FLASH_PAGE_SIZE (2048) +#define FLASH_REG_COMMAND (0x40100000) +#define FLASH_REG_IRQRAW (0x40100010) +#define FLASH_REG_ADDRESS (0x40100018) +#define FLASH_REG_DATA (0x40100040) +#define FLASH_CMD_ERASE_PAGE 0x11 +#define FLASH_CMD_MASSERASE 0x22 +#define FLASH_CMD_WRITE 0x33 +#define FLASH_CMD_BURSTWRITE 0xCC +#define FLASH_INT_CMDDONE 0x01 +#define FLASH_WORD_LEN 4 + +struct bluenrgx_flash_bank { + int probed; + uint32_t idcode; + uint32_t die_id; +}; + +static int bluenrgx_protect_check(struct flash_bank *bank) +{ + /* Nothing to do. Protection is only handled in SW. */ + return ERROR_OK; +} + +/* flash_bank bluenrg-x 0 0 0 0 <target#> */ +FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command) +{ + struct bluenrgx_flash_bank *bluenrgx_info; + /* Create the bank structure */ + bluenrgx_info = calloc(1, sizeof(*bluenrgx_info)); + + /* Check allocation */ + if (bluenrgx_info == NULL) { + LOG_ERROR("failed to allocate bank structure"); + return ERROR_FAIL; + } + + bank->driver_priv = bluenrgx_info; + + bluenrgx_info->probed = 0; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + return ERROR_OK; +} + +static int bluenrgx_erase(struct flash_bank *bank, int first, int last) +{ + int retval = ERROR_OK; + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + int num_sectors = (last - first + 1); + int mass_erase = (num_sectors == bank->num_sectors); + struct target *target = bank->target; + uint32_t address, command; + + /* check preconditions */ + if (bluenrgx_info->probed == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + /* Disable blue module */ + if (target_write_u32(target, 0x200000c0, 0) != ERROR_OK) { + LOG_ERROR("Blue disable failed"); + return ERROR_FAIL; + } + + if (mass_erase) { + command = FLASH_CMD_MASSERASE; + address = bank->base; + if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + for (int i = 0; i < 100; i++) { + uint32_t value; + if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + if (value & FLASH_INT_CMDDONE) + break; + if (i == 99) { + LOG_ERROR("Mass erase command failed (timeout)"); + retval = ERROR_FAIL; + } + } + + } else { + command = FLASH_CMD_ERASE_PAGE; + for (int i = first; i <= last; i++) { + address = bank->base+i*FLASH_PAGE_SIZE; + + if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + + if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) { + LOG_ERROR("Failed"); + return ERROR_FAIL; + } + + for (int j = 0; j < 100; j++) { + uint32_t value; + if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) { + LOG_ERROR("Register write failed"); + return ERROR_FAIL; + } + if (value & FLASH_INT_CMDDONE) + break; + if (j == 99) { + LOG_ERROR("Erase command failed (timeout)"); + retval = ERROR_FAIL; + } + } + } + } + + return retval; + +} + +static int bluenrgx_protect(struct flash_bank *bank, int set, int first, int last) +{ + /* Protection is only handled in software: no hardware write protection + available in BlueNRG-x devices */ + int sector; + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + return ERROR_OK; +} +static int bluenrgx_write_word(struct target *target, uint32_t address_base, uint8_t *values, uint32_t count) +{ + int retval = ERROR_OK; + + retval = target_write_u32(target, FLASH_REG_IRQRAW, 0x3f); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + for (uint32_t i = 0; i < count; i++) { + uint32_t address = address_base + i * FLASH_WORD_LEN; + + retval = target_write_u32(target, FLASH_REG_ADDRESS, address >> 2); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + retval = target_write_buffer(target, FLASH_REG_DATA, FLASH_WORD_LEN, values + i * FLASH_WORD_LEN); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + retval = target_write_u32(target, FLASH_REG_COMMAND, FLASH_CMD_WRITE); + if (retval != ERROR_OK) { + LOG_ERROR("Register write failed, error code: %d", retval); + return retval; + } + + for (int j = 0; j < 100; j++) { + uint32_t reg_value; + retval = target_read_u32(target, FLASH_REG_IRQRAW, ®_value); + + if (retval != ERROR_OK) { + LOG_ERROR("Register read failed, error code: %d", retval); + return retval; + } + + if (reg_value & FLASH_INT_CMDDONE) + break; + + if (j == 99) { + LOG_ERROR("Write command failed (timeout)"); + return ERROR_FAIL; + } + } + } + return retval; +} + +static int bluenrgx_write_bytes(struct target *target, uint32_t address_base, uint8_t *buffer, uint32_t count) +{ + int retval = ERROR_OK; + uint8_t *new_buffer = NULL; + uint32_t pre_bytes = 0, post_bytes = 0, pre_word, post_word, pre_address, post_address; + + if (count == 0) { + /* Just return if there are no bytes to write */ + return retval; + } + + if (address_base & 3) { + pre_bytes = address_base & 3; + pre_address = address_base - pre_bytes; + } + + if ((count + pre_bytes) & 3) { + post_bytes = ((count + pre_bytes + 3) & ~3) - (count + pre_bytes); + post_address = (address_base + count) & ~3; + } + + if (pre_bytes || post_bytes) { + uint32_t old_count = count; + + count = old_count + pre_bytes + post_bytes; + + new_buffer = malloc(count); + + if (new_buffer == NULL) { + LOG_ERROR("odd number of bytes to write and no memory " + "for padding buffer"); + return ERROR_FAIL; + } + + LOG_INFO("Requested number of bytes to write and/or address not word aligned (%" PRIu32 "), extending to %" + PRIu32 " ", old_count, count); + + if (pre_bytes) { + if (target_read_u32(target, pre_address, &pre_word)) { + LOG_ERROR("Memory read failed"); + free(new_buffer); + return ERROR_FAIL; + } + + } + + if (post_bytes) { + if (target_read_u32(target, post_address, &post_word)) { + LOG_ERROR("Memory read failed"); + free(new_buffer); + return ERROR_FAIL; + } + + } + + memcpy(new_buffer, &pre_word, pre_bytes); + memcpy((new_buffer+((pre_bytes+old_count) & ~3)), &post_word, 4); + memcpy(new_buffer+pre_bytes, buffer, old_count); + buffer = new_buffer; + } + + retval = bluenrgx_write_word(target, address_base - pre_bytes, buffer, count/4); + + if (new_buffer) + free(new_buffer); + + return retval; +} + +static int bluenrgx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t buffer_size = 16384 + 8; + struct working_area *write_algorithm; + struct working_area *write_algorithm_sp; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + uint32_t pre_size = 0, fast_size = 0, post_size = 0; + uint32_t pre_offset = 0, fast_offset = 0, post_offset = 0; + + /* See contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c for source and + * hints how to generate the data! + */ + static const uint8_t bluenrgx_flash_write_code[] = { +#include "../../../contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc" + }; + + if ((offset + count) > bank->size) { + LOG_ERROR("Requested write past beyond of flash size: (offset+count) = %d, size=%d", + (offset + count), + bank->size); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* We are good here and we need to compute pre_size, fast_size, post_size */ + pre_size = MIN(count, ((offset+0xF) & ~0xF) - offset); + pre_offset = offset; + fast_size = 16*((count - pre_size) / 16); + fast_offset = offset + pre_size; + post_size = (count-pre_size-fast_size) % 16; + post_offset = fast_offset + fast_size; + + LOG_DEBUG("pre_size = %08x, pre_offset=%08x", pre_size, pre_offset); + LOG_DEBUG("fast_size = %08x, fast_offset=%08x", fast_size, fast_offset); + LOG_DEBUG("post_size = %08x, post_offset=%08x", post_size, post_offset); + + /* Program initial chunk not 16 bytes aligned */ + retval = bluenrgx_write_bytes(target, bank->base+pre_offset, (uint8_t *) buffer, pre_size); + if (retval) { + LOG_ERROR("bluenrgx_write_bytes failed %d", retval); + return ERROR_FAIL; + } + + /* Program chunk 16 bytes aligned in fast mode */ + if (fast_size) { + + if (target_alloc_working_area(target, sizeof(bluenrgx_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(bluenrgx_flash_write_code), + bluenrgx_flash_write_code); + if (retval != ERROR_OK) + return retval; + + /* memory buffer */ + if (target_alloc_working_area(target, buffer_size, &source)) { + LOG_WARNING("no large enough working area available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* Stack pointer area */ + if (target_alloc_working_area(target, 64, + &write_algorithm_sp) != ERROR_OK) { + LOG_DEBUG("no working area for write code stack pointer"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); + init_reg_param(®_params[4], "sp", 32, PARAM_OUT); + + /* FIFO start address (first two words used for write and read pointers) */ + buf_set_u32(reg_params[0].value, 0, 32, source->address); + /* FIFO end address (first two words used for write and read pointers) */ + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + /* Flash memory address */ + buf_set_u32(reg_params[2].value, 0, 32, address+pre_size); + /* Number of bytes */ + buf_set_u32(reg_params[3].value, 0, 32, fast_size); + /* Stack pointer for program working area */ + buf_set_u32(reg_params[4].value, 0, 32, write_algorithm_sp->address); + + LOG_DEBUG("source->address = %08" TARGET_PRIxADDR, source->address); + LOG_DEBUG("source->address+ source->size = %08" TARGET_PRIxADDR, source->address+source->size); + LOG_DEBUG("write_algorithm_sp->address = %08" TARGET_PRIxADDR, write_algorithm_sp->address); + LOG_DEBUG("address = %08x", address+pre_size); + LOG_DEBUG("count = %08x", count); + + retval = target_run_flash_async_algorithm(target, + buffer+pre_size, + fast_size/16, + 16, /* Block size: we write in block of 16 bytes to enjoy burstwrite speed */ + 0, + NULL, + 5, + reg_params, + source->address, + source->size, + write_algorithm->address, + 0, + &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_ERROR("error executing bluenrg-x flash write algorithm"); + + uint32_t error = buf_get_u32(reg_params[0].value, 0, 32); + + if (error != 0) + LOG_ERROR("flash write failed = %08" PRIx32, error); + } + if (retval == ERROR_OK) { + uint32_t rp; + /* Read back rp and check that is valid */ + retval = target_read_u32(target, source->address+4, &rp); + if (retval == ERROR_OK) { + if ((rp < source->address+8) || (rp > (source->address + source->size))) { + LOG_ERROR("flash write failed = %08" PRIx32, rp); + retval = ERROR_FLASH_OPERATION_FAILED; + } + } + } + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + target_free_working_area(target, write_algorithm_sp); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + if (retval != ERROR_OK) + return retval; + + } + + /* Program chunk at end, not addressable by fast burst write algorithm */ + retval = bluenrgx_write_bytes(target, bank->base+post_offset, (uint8_t *) (buffer+pre_size+fast_size), post_size); + if (retval) { + LOG_ERROR("bluenrgx_write_bytes failed %d", retval); + return ERROR_FAIL; + } + return retval; +} + +static int bluenrgx_probe(struct flash_bank *bank) +{ + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + uint32_t idcode, size_info, die_id; + int i; + int retval = target_read_u32(bank->target, JTAG_IDCODE_REG, &idcode); + if (retval != ERROR_OK) + return retval; + retval = target_read_u32(bank->target, FLASH_SIZE_REG, &size_info); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(bank->target, DIE_ID_REG, &die_id); + if (retval != ERROR_OK) + return retval; + + bank->size = (size_info + 1) * 4; + bank->base = FLASH_BASE; + bank->num_sectors = bank->size/FLASH_PAGE_SIZE; + bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector) * bank->num_sectors); + + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * FLASH_PAGE_SIZE; + bank->sectors[i].size = FLASH_PAGE_SIZE; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + bluenrgx_info->probed = 1; + bluenrgx_info->die_id = die_id; + bluenrgx_info->idcode = idcode; + return ERROR_OK; +} + +static int bluenrgx_auto_probe(struct flash_bank *bank) +{ + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + + if (bluenrgx_info->probed) + return ERROR_OK; + + return bluenrgx_probe(bank); +} + +/* This method must return a string displaying information about the bank */ +static int bluenrgx_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv; + int mask_number, cut_number; + char *part_name; + + if (!bluenrgx_info->probed) { + int retval = bluenrgx_probe(bank); + if (retval != ERROR_OK) { + snprintf(buf, buf_size, + "Unable to find bank information."); + return retval; + } + } + + if (bluenrgx_info->idcode == BLUENRG2_IDCODE) + part_name = "BLUENRG-2"; + else + part_name = "BLUENRG-1"; + + mask_number = (bluenrgx_info->die_id >> 4) & 0xF; + cut_number = bluenrgx_info->die_id & 0xF; + + snprintf(buf, buf_size, + "%s - Rev: %d.%d", part_name, mask_number, cut_number); + return ERROR_OK; +} + +struct flash_driver bluenrgx_flash = { + .name = "bluenrg-x", + .flash_bank_command = bluenrgx_flash_bank_command, + .erase = bluenrgx_erase, + .protect = bluenrgx_protect, + .write = bluenrgx_write, + .read = default_flash_read, + .probe = bluenrgx_probe, + .erase_check = default_flash_blank_check, + .protect_check = bluenrgx_protect_check, + .auto_probe = bluenrgx_auto_probe, + .info = bluenrgx_get_info, +}; diff --git a/src/flash/nor/cc26xx.c b/src/flash/nor/cc26xx.c new file mode 100644 index 0000000..e6e9e59 --- /dev/null +++ b/src/flash/nor/cc26xx.c @@ -0,0 +1,567 @@ +/*************************************************************************** + * Copyright (C) 2017 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "cc26xx.h" +#include <helper/binarybuffer.h> +#include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/armv7m.h> +#include <target/image.h> + +#define FLASH_TIMEOUT 8000 + +struct cc26xx_bank { + const char *family_name; + uint32_t icepick_id; + uint32_t user_id; + uint32_t device_type; + uint32_t sector_length; + bool probed; + struct working_area *working_area; + struct armv7m_algorithm armv7m_info; + const uint8_t *algo_code; + uint32_t algo_size; + uint32_t algo_working_size; + uint32_t buffer_addr[2]; + uint32_t params_addr[2]; +}; + +static int cc26xx_auto_probe(struct flash_bank *bank); + +static uint32_t cc26xx_device_type(uint32_t icepick_id, uint32_t user_id) +{ + uint32_t device_type = 0; + + switch (icepick_id & ICEPICK_ID_MASK) { + case CC26X0_ICEPICK_ID: + device_type = CC26X0_TYPE; + break; + case CC26X1_ICEPICK_ID: + device_type = CC26X1_TYPE; + break; + case CC13X0_ICEPICK_ID: + device_type = CC13X0_TYPE; + break; + case CC13X2_CC26X2_ICEPICK_ID: + default: + if ((user_id & USER_ID_CC13_MASK) != 0) + device_type = CC13X2_TYPE; + else + device_type = CC26X2_TYPE; + break; + } + + return device_type; +} + +static uint32_t cc26xx_sector_length(uint32_t icepick_id) +{ + uint32_t sector_length; + + switch (icepick_id & ICEPICK_ID_MASK) { + case CC26X0_ICEPICK_ID: + case CC26X1_ICEPICK_ID: + case CC13X0_ICEPICK_ID: + /* Chameleon family device */ + sector_length = CC26X0_SECTOR_LENGTH; + break; + case CC13X2_CC26X2_ICEPICK_ID: + default: + /* Agama family device */ + sector_length = CC26X2_SECTOR_LENGTH; + break; + } + + return sector_length; +} + +static int cc26xx_wait_algo_done(struct flash_bank *bank, uint32_t params_addr) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + + uint32_t status_addr = params_addr + CC26XX_STATUS_OFFSET; + uint32_t status = CC26XX_BUFFER_FULL; + long long start_ms; + long long elapsed_ms; + + int retval = ERROR_OK; + + start_ms = timeval_ms(); + while (CC26XX_BUFFER_FULL == status) { + retval = target_read_u32(target, status_addr, &status); + if (ERROR_OK != retval) + return retval; + + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + if (elapsed_ms > FLASH_TIMEOUT) + break; + }; + + if (CC26XX_BUFFER_EMPTY != status) { + LOG_ERROR("%s: Flash operation failed", cc26xx_bank->family_name); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int cc26xx_init(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + + int retval; + + /* Make sure we've probed the flash to get the device and size */ + retval = cc26xx_auto_probe(bank); + if (ERROR_OK != retval) + return retval; + + /* Check for working area to use for flash helper algorithm */ + if (NULL != cc26xx_bank->working_area) + target_free_working_area(target, cc26xx_bank->working_area); + retval = target_alloc_working_area(target, cc26xx_bank->algo_working_size, + &cc26xx_bank->working_area); + if (ERROR_OK != retval) + return retval; + + /* Confirm the defined working address is the area we need to use */ + if (CC26XX_ALGO_BASE_ADDRESS != cc26xx_bank->working_area->address) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + /* Write flash helper algorithm into target memory */ + retval = target_write_buffer(target, CC26XX_ALGO_BASE_ADDRESS, + cc26xx_bank->algo_size, cc26xx_bank->algo_code); + if (ERROR_OK != retval) { + LOG_ERROR("%s: Failed to load flash helper algorithm", + cc26xx_bank->family_name); + target_free_working_area(target, cc26xx_bank->working_area); + return retval; + } + + /* Initialize the ARMv7 specific info to run the algorithm */ + cc26xx_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + cc26xx_bank->armv7m_info.core_mode = ARM_MODE_THREAD; + + /* Begin executing the flash helper algorithm */ + retval = target_start_algorithm(target, 0, NULL, 0, NULL, + CC26XX_ALGO_BASE_ADDRESS, 0, &cc26xx_bank->armv7m_info); + if (ERROR_OK != retval) { + LOG_ERROR("%s: Failed to start flash helper algorithm", + cc26xx_bank->family_name); + target_free_working_area(target, cc26xx_bank->working_area); + return retval; + } + + /* + * At this point, the algorithm is running on the target and + * ready to receive commands and data to flash the target + */ + + return retval; +} + +static int cc26xx_quit(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + + int retval; + + /* Regardless of the algo's status, attempt to halt the target */ + (void)target_halt(target); + + /* Now confirm target halted and clean up from flash helper algorithm */ + retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT, + &cc26xx_bank->armv7m_info); + + target_free_working_area(target, cc26xx_bank->working_area); + cc26xx_bank->working_area = NULL; + + return retval; +} + +static int cc26xx_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + struct cc26xx_algo_params algo_params; + + int retval; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = cc26xx_init(bank); + if (ERROR_OK != retval) + return retval; + + /* Initialize algorithm parameters */ + buf_set_u32(algo_params.address, 0, 32, 0); + buf_set_u32(algo_params.length, 0, 32, 4); + buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_ALL); + buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL); + + /* Issue flash helper algorithm parameters for mass erase */ + retval = target_write_buffer(target, cc26xx_bank->params_addr[0], + sizeof(algo_params), (uint8_t *)&algo_params); + + /* Wait for command to complete */ + if (ERROR_OK == retval) + retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]); + + /* Regardless of errors, try to close down algo */ + (void)cc26xx_quit(bank); + + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(cc26xx_flash_bank_command) +{ + struct cc26xx_bank *cc26xx_bank; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + cc26xx_bank = malloc(sizeof(struct cc26xx_bank)); + if (NULL == cc26xx_bank) + return ERROR_FAIL; + + /* Initialize private flash information */ + memset((void *)cc26xx_bank, 0x00, sizeof(struct cc26xx_bank)); + cc26xx_bank->family_name = "cc26xx"; + cc26xx_bank->device_type = CC26XX_NO_TYPE; + cc26xx_bank->sector_length = 0x1000; + + /* Finish initialization of bank */ + bank->driver_priv = cc26xx_bank; + bank->next = NULL; + + return ERROR_OK; +} + +static int cc26xx_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + struct cc26xx_algo_params algo_params; + + uint32_t address; + uint32_t length; + int retval; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Do a mass erase if user requested all sectors of flash */ + if ((first == 0) && (last == (bank->num_sectors - 1))) { + /* Request mass erase of flash */ + return cc26xx_mass_erase(bank); + } + + address = first * cc26xx_bank->sector_length; + length = (last - first + 1) * cc26xx_bank->sector_length; + + retval = cc26xx_init(bank); + if (ERROR_OK != retval) + return retval; + + /* Set up algorithm parameters for erase command */ + buf_set_u32(algo_params.address, 0, 32, address); + buf_set_u32(algo_params.length, 0, 32, length); + buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_SECTORS); + buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL); + + /* Issue flash helper algorithm parameters for erase */ + retval = target_write_buffer(target, cc26xx_bank->params_addr[0], + sizeof(algo_params), (uint8_t *)&algo_params); + + /* If no error, wait for erase to finish */ + if (ERROR_OK == retval) + retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]); + + /* Regardless of errors, try to close down algo */ + (void)cc26xx_quit(bank); + + return retval; +} + +static int cc26xx_protect(struct flash_bank *bank, int set, int first, + int last) +{ + return ERROR_OK; +} + +static int cc26xx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + struct cc26xx_algo_params algo_params[2]; + uint32_t size = 0; + long long start_ms; + long long elapsed_ms; + uint32_t address; + + uint32_t index; + int retval; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = cc26xx_init(bank); + if (ERROR_OK != retval) + return retval; + + /* Initialize algorithm parameters to default values */ + buf_set_u32(algo_params[0].command, 0, 32, CC26XX_CMD_PROGRAM); + buf_set_u32(algo_params[1].command, 0, 32, CC26XX_CMD_PROGRAM); + + /* Write requested data, ping-ponging between two buffers */ + index = 0; + start_ms = timeval_ms(); + address = bank->base + offset; + while (count > 0) { + + if (count > cc26xx_bank->sector_length) + size = cc26xx_bank->sector_length; + else + size = count; + + /* Put next block of data to flash into buffer */ + retval = target_write_buffer(target, cc26xx_bank->buffer_addr[index], + size, buffer); + if (ERROR_OK != retval) { + LOG_ERROR("Unable to write data to target memory"); + break; + } + + /* Update algo parameters for next block */ + buf_set_u32(algo_params[index].address, 0, 32, address); + buf_set_u32(algo_params[index].length, 0, 32, size); + buf_set_u32(algo_params[index].status, 0, 32, CC26XX_BUFFER_FULL); + + /* Issue flash helper algorithm parameters for block write */ + retval = target_write_buffer(target, cc26xx_bank->params_addr[index], + sizeof(algo_params[index]), (uint8_t *)&algo_params[index]); + if (ERROR_OK != retval) + break; + + /* Wait for next ping pong buffer to be ready */ + index ^= 1; + retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]); + if (ERROR_OK != retval) + break; + + count -= size; + buffer += size; + address += size; + + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + } + + /* If no error yet, wait for last buffer to finish */ + if (ERROR_OK == retval) { + index ^= 1; + retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]); + } + + /* Regardless of errors, try to close down algo */ + (void)cc26xx_quit(bank); + + return retval; +} + +static int cc26xx_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + + uint32_t sector_length; + uint32_t value; + int num_sectors; + int max_sectors; + + int retval; + + retval = target_read_u32(target, FCFG1_ICEPICK_ID, &value); + if (ERROR_OK != retval) + return retval; + cc26xx_bank->icepick_id = value; + + retval = target_read_u32(target, FCFG1_USER_ID, &value); + if (ERROR_OK != retval) + return retval; + cc26xx_bank->user_id = value; + + cc26xx_bank->device_type = cc26xx_device_type(cc26xx_bank->icepick_id, + cc26xx_bank->user_id); + + sector_length = cc26xx_sector_length(cc26xx_bank->icepick_id); + + /* Set up appropriate flash helper algorithm */ + switch (cc26xx_bank->icepick_id & ICEPICK_ID_MASK) { + case CC26X0_ICEPICK_ID: + case CC26X1_ICEPICK_ID: + case CC13X0_ICEPICK_ID: + /* Chameleon family device */ + cc26xx_bank->algo_code = cc26x0_algo; + cc26xx_bank->algo_size = sizeof(cc26x0_algo); + cc26xx_bank->algo_working_size = CC26X0_WORKING_SIZE; + cc26xx_bank->buffer_addr[0] = CC26X0_ALGO_BUFFER_0; + cc26xx_bank->buffer_addr[1] = CC26X0_ALGO_BUFFER_1; + cc26xx_bank->params_addr[0] = CC26X0_ALGO_PARAMS_0; + cc26xx_bank->params_addr[1] = CC26X0_ALGO_PARAMS_1; + max_sectors = CC26X0_MAX_SECTORS; + break; + case CC13X2_CC26X2_ICEPICK_ID: + default: + /* Agama family device */ + cc26xx_bank->algo_code = cc26x2_algo; + cc26xx_bank->algo_size = sizeof(cc26x2_algo); + cc26xx_bank->algo_working_size = CC26X2_WORKING_SIZE; + cc26xx_bank->buffer_addr[0] = CC26X2_ALGO_BUFFER_0; + cc26xx_bank->buffer_addr[1] = CC26X2_ALGO_BUFFER_1; + cc26xx_bank->params_addr[0] = CC26X2_ALGO_PARAMS_0; + cc26xx_bank->params_addr[1] = CC26X2_ALGO_PARAMS_1; + max_sectors = CC26X2_MAX_SECTORS; + break; + } + + retval = target_read_u32(target, CC26XX_FLASH_SIZE_INFO, &value); + if (ERROR_OK != retval) + return retval; + num_sectors = value & 0xff; + if (num_sectors > max_sectors) + num_sectors = max_sectors; + + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + if (NULL == bank->sectors) + return ERROR_FAIL; + + bank->base = CC26XX_FLASH_BASE_ADDR; + bank->num_sectors = num_sectors; + bank->size = num_sectors * sector_length; + bank->write_start_alignment = 0; + bank->write_end_alignment = 0; + cc26xx_bank->sector_length = sector_length; + + for (int i = 0; i < num_sectors; i++) { + bank->sectors[i].offset = i * sector_length; + bank->sectors[i].size = sector_length; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + /* We've successfully determined the stats on the flash bank */ + cc26xx_bank->probed = true; + + /* If we fall through to here, then all went well */ + + return ERROR_OK; +} + +static int cc26xx_auto_probe(struct flash_bank *bank) +{ + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + + int retval = ERROR_OK; + + if (bank->bank_number != 0) { + /* Invalid bank number somehow */ + return ERROR_FAIL; + } + + if (!cc26xx_bank->probed) + retval = cc26xx_probe(bank); + + return retval; +} + +static int cc26xx_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int cc26xx_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct cc26xx_bank *cc26xx_bank = bank->driver_priv; + int printed = 0; + const char *device; + + switch (cc26xx_bank->device_type) { + case CC26X0_TYPE: + device = "CC26x0"; + break; + case CC26X1_TYPE: + device = "CC26x1"; + break; + case CC13X0_TYPE: + device = "CC13x0"; + break; + case CC13X2_TYPE: + device = "CC13x2"; + break; + case CC26X2_TYPE: + device = "CC26x2"; + break; + case CC26XX_NO_TYPE: + default: + device = "Unrecognized"; + break; + } + + printed = snprintf(buf, buf_size, + "%s device: ICEPick ID 0x%08x, USER ID 0x%08x\n", + device, cc26xx_bank->icepick_id, cc26xx_bank->user_id); + + if (printed >= buf_size) + return ERROR_BUF_TOO_SMALL; + + return ERROR_OK; +} + +struct flash_driver cc26xx_flash = { + .name = "cc26xx", + .flash_bank_command = cc26xx_flash_bank_command, + .erase = cc26xx_erase, + .protect = cc26xx_protect, + .write = cc26xx_write, + .read = default_flash_read, + .probe = cc26xx_probe, + .auto_probe = cc26xx_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = cc26xx_protect_check, + .info = cc26xx_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/cc26xx.h b/src/flash/nor/cc26xx.h new file mode 100644 index 0000000..51a09f1 --- /dev/null +++ b/src/flash/nor/cc26xx.h @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2017 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_FLASH_NOR_CC26XX_H +#define OPENOCD_FLASH_NOR_CC26XX_H + +/* Addresses of FCFG1 registers to access ICEPick Device ID and User ID */ +#define FCFG1_ICEPICK_ID 0x50001318 +#define FCFG1_USER_ID 0x50001294 + +/* ICEPick device ID mask and values */ +#define ICEPICK_ID_MASK 0x0fffffff +#define ICEPICK_REV_MASK 0xf0000000 +#define CC26X0_ICEPICK_ID 0x0b99a02f +#define CC26X1_ICEPICK_ID 0x0b9bd02f +#define CC13X0_ICEPICK_ID 0x0b9be02f +#define CC13X2_CC26X2_ICEPICK_ID 0x0bb4102f + +/* User ID mask for Agama CC13x2 vs CC26x2 */ +#define USER_ID_CC13_MASK 0x00800000 + +/* Common CC26xx/CC13xx flash and memory parameters */ +#define CC26XX_FLASH_BASE_ADDR 0x00000000 +#define CC26XX_FLASH_SIZE_INFO 0x4003002c +#define CC26XX_SRAM_SIZE_INFO 0x40082250 +#define CC26XX_ALGO_BASE_ADDRESS 0x20000000 + +/* Chameleon CC26x0/CC13x0 specific parameters */ +#define CC26X0_MAX_SECTORS 32 +#define CC26X0_SECTOR_LENGTH 0x1000 +#define CC26X0_ALGO_BUFFER_0 0x20001c00 +#define CC26X0_ALGO_BUFFER_1 0x20002c00 +#define CC26X0_ALGO_PARAMS_0 0x20001bd8 +#define CC26X0_ALGO_PARAMS_1 0x20001bec +#define CC26X0_WORKING_SIZE (CC26X0_ALGO_BUFFER_1 + CC26X0_SECTOR_LENGTH - \ + CC26XX_ALGO_BASE_ADDRESS) + +/* Agama CC26x2/CC13x2 specific parameters */ +#define CC26X2_MAX_SECTORS 128 +#define CC26X2_SECTOR_LENGTH 0x2000 +#define CC26X2_ALGO_BUFFER_0 0x20002000 +#define CC26X2_ALGO_BUFFER_1 0x20004000 +#define CC26X2_ALGO_PARAMS_0 0x20001fd8 +#define CC26X2_ALGO_PARAMS_1 0x20001fec +#define CC26X2_WORKING_SIZE (CC26X2_ALGO_BUFFER_1 + CC26X2_SECTOR_LENGTH - \ + CC26XX_ALGO_BASE_ADDRESS) + +/* CC26xx flash helper algorithm buffer flags */ +#define CC26XX_BUFFER_EMPTY 0x00000000 +#define CC26XX_BUFFER_FULL 0xffffffff + +/* CC26XX flash helper algorithm commands */ +#define CC26XX_CMD_NO_ACTION 0 +#define CC26XX_CMD_ERASE_ALL 1 +#define CC26XX_CMD_PROGRAM 2 +#define CC26XX_CMD_ERASE_AND_PROGRAM 3 +#define CC26XX_CMD_ERASE_AND_PROGRAM_WITH_RETAIN 4 +#define CC26XX_CMD_ERASE_SECTORS 5 + +/* CC26xx and CC13xx device types */ +#define CC26XX_NO_TYPE 0 /* Device type not determined yet */ +#define CC26X0_TYPE 1 /* CC26x0 Chameleon device */ +#define CC26X1_TYPE 2 /* CC26x1 Chameleon device */ +#define CC26X2_TYPE 3 /* CC26x2 Agama device */ +#define CC13X0_TYPE 4 /* CC13x0 Chameleon device */ +#define CC13X2_TYPE 5 /* CC13x2 Agama device */ + +/* Flash helper algorithm parameter block struct */ +#define CC26XX_STATUS_OFFSET 0x0c +struct cc26xx_algo_params { + uint8_t address[4]; + uint8_t length[4]; + uint8_t command[4]; + uint8_t status[4]; +}; + +/* Flash helper algorithm for CC26x0 Chameleon targets */ +const uint8_t cc26x0_algo[] = { +#include "../../../contrib/loaders/flash/cc26xx/cc26x0_algo.inc" +}; + +/* Flash helper algorithm for CC26x2 Agama targets */ +const uint8_t cc26x2_algo[] = { +#include "../../../contrib/loaders/flash/cc26xx/cc26x2_algo.inc" +}; + +#endif /* OPENOCD_FLASH_NOR_CC26XX_H */ diff --git a/src/flash/nor/cc3220sf.c b/src/flash/nor/cc3220sf.c new file mode 100644 index 0000000..af45743 --- /dev/null +++ b/src/flash/nor/cc3220sf.c @@ -0,0 +1,529 @@ +/*************************************************************************** + * Copyright (C) 2017 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "cc3220sf.h" +#include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/armv7m.h> + +#define FLASH_TIMEOUT 5000 + +struct cc3220sf_bank { + bool probed; + struct armv7m_algorithm armv7m_info; +}; + +static int cc3220sf_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + bool done; + long long start_ms; + long long elapsed_ms; + uint32_t value; + + int retval = ERROR_OK; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Set starting address to erase to zero */ + retval = target_write_u32(target, FMA_REGISTER_ADDR, 0); + if (ERROR_OK != retval) + return retval; + + /* Write the MERASE bit of the FMC register */ + retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_MERASE_VALUE); + if (ERROR_OK != retval) + return retval; + + /* Poll the MERASE bit until the mass erase is complete */ + done = false; + start_ms = timeval_ms(); + while (!done) { + retval = target_read_u32(target, FMC_REGISTER_ADDR, &value); + if (ERROR_OK != retval) + return retval; + + if ((value & FMC_MERASE_BIT) == 0) { + /* Bit clears when mass erase is finished */ + done = true; + } else { + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + if (elapsed_ms > FLASH_TIMEOUT) + break; + } + } + + if (!done) { + /* Mass erase timed out waiting for confirmation */ + return ERROR_FAIL; + } + + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(cc3220sf_flash_bank_command) +{ + struct cc3220sf_bank *cc3220sf_bank; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + cc3220sf_bank = malloc(sizeof(struct cc3220sf_bank)); + if (NULL == cc3220sf_bank) + return ERROR_FAIL; + + /* Initialize private flash information */ + cc3220sf_bank->probed = false; + + /* Finish initialization of flash bank */ + bank->driver_priv = cc3220sf_bank; + bank->next = NULL; + + return ERROR_OK; +} + +static int cc3220sf_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + bool done; + long long start_ms; + long long elapsed_ms; + uint32_t address; + uint32_t value; + + int retval = ERROR_OK; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Do a mass erase if user requested all sectors of flash */ + if ((first == 0) && (last == (bank->num_sectors - 1))) { + /* Request mass erase of flash */ + return cc3220sf_mass_erase(bank); + } + + /* Erase requested sectors one by one */ + for (int i = first; i <= last; i++) { + + /* Determine address of sector to erase */ + address = FLASH_BASE_ADDR + i * FLASH_SECTOR_SIZE; + + /* Set starting address to erase */ + retval = target_write_u32(target, FMA_REGISTER_ADDR, address); + if (ERROR_OK != retval) + return retval; + + /* Write the ERASE bit of the FMC register */ + retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_ERASE_VALUE); + if (ERROR_OK != retval) + return retval; + + /* Poll the ERASE bit until the erase is complete */ + done = false; + start_ms = timeval_ms(); + while (!done) { + retval = target_read_u32(target, FMC_REGISTER_ADDR, &value); + if (ERROR_OK != retval) + return retval; + + if ((value & FMC_ERASE_BIT) == 0) { + /* Bit clears when mass erase is finished */ + done = true; + } else { + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + if (elapsed_ms > FLASH_TIMEOUT) + break; + } + } + + if (!done) { + /* Sector erase timed out waiting for confirmation */ + return ERROR_FAIL; + } + } + + return retval; +} + +static int cc3220sf_protect(struct flash_bank *bank, int set, int first, + int last) +{ + return ERROR_OK; +} + +static int cc3220sf_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv; + struct working_area *algo_working_area; + struct working_area *buffer_working_area; + struct reg_param reg_params[3]; + uint32_t algo_base_address; + uint32_t algo_buffer_address; + uint32_t algo_buffer_size; + uint32_t address; + uint32_t remaining; + uint32_t words; + uint32_t result; + + int retval = ERROR_OK; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Obtain working area to use for flash helper algorithm */ + retval = target_alloc_working_area(target, sizeof(cc3220sf_algo), + &algo_working_area); + if (ERROR_OK != retval) + return retval; + + /* Obtain working area to use for flash buffer */ + retval = target_alloc_working_area(target, + target_get_working_area_avail(target), &buffer_working_area); + if (ERROR_OK != retval) { + target_free_working_area(target, algo_working_area); + return retval; + } + + algo_base_address = algo_working_area->address; + algo_buffer_address = buffer_working_area->address; + algo_buffer_size = buffer_working_area->size; + + /* Make sure buffer size is a multiple of 32 word (0x80 byte) chunks */ + /* (algo runs more efficiently if it operates on 32 words at a time) */ + if (algo_buffer_size > 0x80) + algo_buffer_size &= ~0x7f; + + /* Write flash helper algorithm into target memory */ + retval = target_write_buffer(target, algo_base_address, + sizeof(cc3220sf_algo), cc3220sf_algo); + if (ERROR_OK != retval) { + target_free_working_area(target, algo_working_area); + target_free_working_area(target, buffer_working_area); + return retval; + } + + /* Initialize the ARMv7m specific info to run the algorithm */ + cc3220sf_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + cc3220sf_bank->armv7m_info.core_mode = ARM_MODE_THREAD; + + /* Initialize register params for flash helper algorithm */ + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT); + + /* Prepare to write to flash */ + address = FLASH_BASE_ADDR + offset; + remaining = count; + + /* The flash hardware can only write complete words to flash. If + * an unaligned address is passed in, we must do a read-modify-write + * on a word with enough bytes to align the rest of the buffer. And + * if less than a whole word remains at the end, we must also do a + * read-modify-write on a final word to finish up. + */ + + /* Do one word write to align address on 32-bit boundary if needed */ + if (0 != (address & 0x3)) { + uint8_t head[4]; + + /* Get starting offset for data to write (will be 1 to 3) */ + uint32_t head_offset = address & 0x03; + + /* Get the aligned address to write this first word to */ + uint32_t head_address = address & 0xfffffffc; + + /* Retrieve what is already in flash at the head address */ + retval = target_read_buffer(target, head_address, sizeof(head), head); + + if (ERROR_OK == retval) { + /* Substitute in the new data to write */ + while ((remaining > 0) && (head_offset < 4)) { + head[head_offset] = *buffer; + head_offset++; + address++; + buffer++; + remaining--; + } + } + + if (ERROR_OK == retval) { + /* Helper parameters are passed in registers R0-R2 */ + /* Set start of data buffer, address to write to, and word count */ + buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address); + buf_set_u32(reg_params[1].value, 0, 32, head_address); + buf_set_u32(reg_params[2].value, 0, 32, 1); + + /* Write head value into buffer to flash */ + retval = target_write_buffer(target, algo_buffer_address, + sizeof(head), head); + } + + if (ERROR_OK == retval) { + /* Execute the flash helper algorithm */ + retval = target_run_algorithm(target, 0, NULL, 3, reg_params, + algo_base_address, 0, FLASH_TIMEOUT, + &cc3220sf_bank->armv7m_info); + if (ERROR_OK != retval) + LOG_ERROR("cc3220sf: Flash algorithm failed to run"); + + /* Check that the head value was written to flash */ + result = buf_get_u32(reg_params[2].value, 0, 32); + if (0 != result) { + retval = ERROR_FAIL; + LOG_ERROR("cc3220sf: Flash operation failed"); + } + } + } + + /* Check if there's data at end of buffer that isn't a full word */ + uint32_t tail_count = remaining & 0x03; + /* Adjust remaining so it is a multiple of whole words */ + remaining -= tail_count; + + while ((ERROR_OK == retval) && (remaining > 0)) { + /* Set start of data buffer and address to write to */ + buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address); + buf_set_u32(reg_params[1].value, 0, 32, address); + + /* Download data to write into memory buffer */ + if (remaining >= algo_buffer_size) { + /* Fill up buffer with data to flash */ + retval = target_write_buffer(target, algo_buffer_address, + algo_buffer_size, buffer); + if (ERROR_OK != retval) + break; + + /* Count to write is in 32-bit words */ + words = algo_buffer_size / 4; + + /* Bump variables to next data */ + address += algo_buffer_size; + buffer += algo_buffer_size; + remaining -= algo_buffer_size; + } else { + /* Fill buffer with what's left of the data */ + retval = target_write_buffer(target, algo_buffer_address, + remaining, buffer); + if (ERROR_OK != retval) + break; + + /* Calculate the final word count to write */ + words = remaining / 4; + if (0 != (remaining % 4)) + words++; + + /* Bump variables to any final data */ + address += remaining; + buffer += remaining; + remaining = 0; + } + + /* Set number of words to write */ + buf_set_u32(reg_params[2].value, 0, 32, words); + + /* Execute the flash helper algorithm */ + retval = target_run_algorithm(target, 0, NULL, 3, reg_params, + algo_base_address, 0, FLASH_TIMEOUT, + &cc3220sf_bank->armv7m_info); + if (ERROR_OK != retval) { + LOG_ERROR("cc3220sf: Flash algorithm failed to run"); + break; + } + + /* Check that all words were written to flash */ + result = buf_get_u32(reg_params[2].value, 0, 32); + if (0 != result) { + retval = ERROR_FAIL; + LOG_ERROR("cc3220sf: Flash operation failed"); + break; + } + } + + /* Do one word write for any final bytes less than a full word */ + if ((ERROR_OK == retval) && (0 != tail_count)) { + uint8_t tail[4]; + + /* Set starting byte offset for data to write */ + uint32_t tail_offset = 0; + + /* Retrieve what is already in flash at the tail address */ + retval = target_read_buffer(target, address, sizeof(tail), tail); + + if (ERROR_OK == retval) { + /* Substitute in the new data to write */ + while (tail_count > 0) { + tail[tail_offset] = *buffer; + tail_offset++; + buffer++; + tail_count--; + } + } + + if (ERROR_OK == retval) { + /* Set start of data buffer, address to write to, and word count */ + buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address); + buf_set_u32(reg_params[1].value, 0, 32, address); + buf_set_u32(reg_params[2].value, 0, 32, 1); + + /* Write tail value into buffer to flash */ + retval = target_write_buffer(target, algo_buffer_address, + sizeof(tail), tail); + } + + if (ERROR_OK == retval) { + /* Execute the flash helper algorithm */ + retval = target_run_algorithm(target, 0, NULL, 3, reg_params, + algo_base_address, 0, FLASH_TIMEOUT, + &cc3220sf_bank->armv7m_info); + if (ERROR_OK != retval) + LOG_ERROR("cc3220sf: Flash algorithm failed to run"); + + /* Check that the tail was written to flash */ + result = buf_get_u32(reg_params[2].value, 0, 32); + if (0 != result) { + retval = ERROR_FAIL; + LOG_ERROR("cc3220sf: Flash operation failed"); + } + } + } + + /* Free resources */ + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + target_free_working_area(target, algo_working_area); + target_free_working_area(target, buffer_working_area); + + return retval; +} + +static int cc3220sf_probe(struct flash_bank *bank) +{ + struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv; + + uint32_t base; + uint32_t size; + int num_sectors; + int bank_id; + + bank_id = bank->bank_number; + + if (0 == bank_id) { + base = FLASH_BASE_ADDR; + size = FLASH_NUM_SECTORS * FLASH_SECTOR_SIZE; + num_sectors = FLASH_NUM_SECTORS; + } else { + /* Invalid bank number somehow */ + return ERROR_FAIL; + } + + if (NULL != bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + if (NULL == bank->sectors) + return ERROR_FAIL; + + bank->base = base; + bank->size = size; + bank->write_start_alignment = 0; + bank->write_end_alignment = 0; + bank->num_sectors = num_sectors; + + for (int i = 0; i < num_sectors; i++) { + bank->sectors[i].offset = i * FLASH_SECTOR_SIZE; + bank->sectors[i].size = FLASH_SECTOR_SIZE; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + /* We've successfully recorded the stats on this flash bank */ + cc3220sf_bank->probed = true; + + /* If we fall through to here, then all went well */ + + return ERROR_OK; +} + +static int cc3220sf_auto_probe(struct flash_bank *bank) +{ + struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv; + + int retval = ERROR_OK; + + if (0 != bank->bank_number) { + /* Invalid bank number somehow */ + return ERROR_FAIL; + } + + if (!cc3220sf_bank->probed) + retval = cc3220sf_probe(bank); + + return retval; +} + +static int cc3220sf_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int cc3220sf_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + + printed = snprintf(buf, buf_size, "CC3220SF with 1MB internal flash\n"); + + if (printed >= buf_size) + return ERROR_BUF_TOO_SMALL; + + return ERROR_OK; +} + +struct flash_driver cc3220sf_flash = { + .name = "cc3220sf", + .flash_bank_command = cc3220sf_flash_bank_command, + .erase = cc3220sf_erase, + .protect = cc3220sf_protect, + .write = cc3220sf_write, + .read = default_flash_read, + .probe = cc3220sf_probe, + .auto_probe = cc3220sf_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = cc3220sf_protect_check, + .info = cc3220sf_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/cc3220sf.h b/src/flash/nor/cc3220sf.h new file mode 100644 index 0000000..36c17be --- /dev/null +++ b/src/flash/nor/cc3220sf.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2017 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_FLASH_NOR_CC3220SF_H +#define OPENOCD_FLASH_NOR_CC3220SF_H + +/* CC3220SF device types */ +#define CC3220_NO_TYPE 0 /* Device type not determined yet */ +#define CC3220_OTHER 1 /* CC3220 variant without flash */ +#define CC3220SF 2 /* CC3220SF variant with flash */ + +/* Flash parameters */ +#define FLASH_BASE_ADDR 0x01000000 +#define FLASH_SECTOR_SIZE 2048 +#define FLASH_NUM_SECTORS 512 + +/* CC2200SF flash registers */ +#define FMA_REGISTER_ADDR 0x400FD000 +#define FMC_REGISTER_ADDR 0x400FD008 +#define FMC_DEFAULT_VALUE 0xA4420000 +#define FMC_ERASE_BIT 0x00000002 +#define FMC_MERASE_BIT 0x00000004 +#define FMC_ERASE_VALUE (FMC_DEFAULT_VALUE | FMC_ERASE_BIT) +#define FMC_MERASE_VALUE (FMC_DEFAULT_VALUE | FMC_MERASE_BIT) + +/* Flash helper algorithm for CC3220SF */ +const uint8_t cc3220sf_algo[] = { +#include "../../../contrib/loaders/flash/cc3220sf/cc3220sf.inc" +}; + +#endif /* OPENOCD_FLASH_NOR_CC3220SF_H */ diff --git a/src/flash/nor/cfi.c b/src/flash/nor/cfi.c index ac0db82..0ae72d4 100644 --- a/src/flash/nor/cfi.c +++ b/src/flash/nor/cfi.c @@ -3128,4 +3128,5 @@ struct flash_driver cfi_flash = { .erase_check = default_flash_blank_check, .protect_check = cfi_protect_check, .info = get_cfi_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c index ab69a32..4941281 100644 --- a/src/flash/nor/core.c +++ b/src/flash/nor/core.c @@ -4,6 +4,7 @@ * Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> * * Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> * * Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> * + * Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.cz> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -171,6 +172,39 @@ int flash_get_bank_count(void) return i; } +void default_flash_free_driver_priv(struct flash_bank *bank) +{ + free(bank->driver_priv); + bank->driver_priv = NULL; +} + +void flash_free_all_banks(void) +{ + struct flash_bank *bank = flash_banks; + while (bank) { + struct flash_bank *next = bank->next; + if (bank->driver->free_driver_priv) + bank->driver->free_driver_priv(bank); + else + LOG_WARNING("Flash driver of %s does not support free_driver_priv()", bank->name); + + /* For 'virtual' flash driver bank->sectors and bank->prot_blocks pointers are copied from + * master flash_bank structure. They point to memory locations allocated by master flash driver + * so master driver is responsible for releasing them. + * Avoid UB caused by double-free memory corruption if flash bank is 'virtual'. */ + + if (strcmp(bank->driver->name, "virtual") != 0) { + free(bank->sectors); + free(bank->prot_blocks); + } + + free(bank->name); + free(bank); + bank = next; + } + flash_banks = NULL; +} + struct flash_bank *get_flash_bank_by_name_noprobe(const char *name) { unsigned requested = get_flash_name_index(name); @@ -314,36 +348,49 @@ int default_flash_blank_check(struct flash_bank *bank) struct target *target = bank->target; int i; int retval; - int fast_check = 0; - uint32_t blank; if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } + struct target_memory_check_block *block_array; + block_array = malloc(bank->num_sectors * sizeof(struct target_memory_check_block)); + if (block_array == NULL) + return default_flash_mem_blank_check(bank); + for (i = 0; i < bank->num_sectors; i++) { - uint32_t address = bank->base + bank->sectors[i].offset; - uint32_t size = bank->sectors[i].size; + block_array[i].address = bank->base + bank->sectors[i].offset; + block_array[i].size = bank->sectors[i].size; + block_array[i].result = UINT32_MAX; /* erase state unknown */ + } - retval = target_blank_check_memory(target, address, size, &blank, bank->erased_value); - if (retval != ERROR_OK) { - fast_check = 0; + bool fast_check = true; + for (i = 0; i < bank->num_sectors; ) { + retval = target_blank_check_memory(target, + block_array + i, bank->num_sectors - i, + bank->erased_value); + if (retval < 1) { + /* Run slow fallback if the first run gives no result + * otherwise use possibly incomplete results */ + if (i == 0) + fast_check = false; break; } - if (blank == bank->erased_value) - bank->sectors[i].is_erased = 1; - else - bank->sectors[i].is_erased = 0; - fast_check = 1; + i += retval; /* add number of blocks done this round */ } - if (!fast_check) { + if (fast_check) { + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = block_array[i].result; + retval = ERROR_OK; + } else { LOG_USER("Running slow fallback erase check - add working memory"); - return default_flash_mem_blank_check(bank); + retval = default_flash_mem_blank_check(bank); } + free(block_array); - return ERROR_OK; + return retval; } /* Manipulate given flash region, selecting the bank according to target @@ -399,18 +446,21 @@ static int flash_iterate_address_range_inner(struct target *target, return ERROR_FLASH_DST_BREAKS_ALIGNMENT; } - addr -= c->base; - last_addr -= c->base; + if (c->prot_blocks == NULL || c->num_prot_blocks == 0) { + /* flash driver does not define protect blocks, use sectors instead */ + iterate_protect_blocks = false; + } - if (iterate_protect_blocks && c->prot_blocks && c->num_prot_blocks) { + if (iterate_protect_blocks) { block_array = c->prot_blocks; num_blocks = c->num_prot_blocks; } else { block_array = c->sectors; num_blocks = c->num_sectors; - iterate_protect_blocks = false; } + addr -= c->base; + last_addr -= c->base; for (i = 0; i < num_blocks; i++) { struct flash_sector *f = &block_array[i]; @@ -559,6 +609,87 @@ static int compare_section(const void *a, const void *b) return -1; } +/** + * Get aligned start address of a flash write region + */ +target_addr_t flash_write_align_start(struct flash_bank *bank, target_addr_t addr) +{ + if (addr < bank->base || addr >= bank->base + bank->size + || bank->write_start_alignment <= 1) + return addr; + + if (bank->write_start_alignment == FLASH_WRITE_ALIGN_SECTOR) { + uint32_t offset = addr - bank->base; + uint32_t aligned = 0; + int sect; + for (sect = 0; sect < bank->num_sectors; sect++) { + if (bank->sectors[sect].offset > offset) + break; + + aligned = bank->sectors[sect].offset; + } + return bank->base + aligned; + } + + return addr & ~(bank->write_start_alignment - 1); +} + +/** + * Get aligned end address of a flash write region + */ +target_addr_t flash_write_align_end(struct flash_bank *bank, target_addr_t addr) +{ + if (addr < bank->base || addr >= bank->base + bank->size + || bank->write_end_alignment <= 1) + return addr; + + if (bank->write_end_alignment == FLASH_WRITE_ALIGN_SECTOR) { + uint32_t offset = addr - bank->base; + uint32_t aligned = 0; + int sect; + for (sect = 0; sect < bank->num_sectors; sect++) { + aligned = bank->sectors[sect].offset + bank->sectors[sect].size - 1; + if (aligned >= offset) + break; + } + return bank->base + aligned; + } + + return addr | (bank->write_end_alignment - 1); +} + +/** + * Check if gap between sections is bigger than minimum required to discontinue flash write + */ +static bool flash_write_check_gap(struct flash_bank *bank, + target_addr_t addr1, target_addr_t addr2) +{ + if (bank->minimal_write_gap == FLASH_WRITE_CONTINUOUS + || addr1 < bank->base || addr1 >= bank->base + bank->size + || addr2 < bank->base || addr2 >= bank->base + bank->size) + return false; + + if (bank->minimal_write_gap == FLASH_WRITE_GAP_SECTOR) { + int sect; + uint32_t offset1 = addr1 - bank->base; + /* find the sector following the one containing addr1 */ + for (sect = 0; sect < bank->num_sectors; sect++) { + if (bank->sectors[sect].offset > offset1) + break; + } + if (sect >= bank->num_sectors) + return false; + + uint32_t offset2 = addr2 - bank->base; + return bank->sectors[sect].offset + bank->sectors[sect].size <= offset2; + } + + target_addr_t aligned1 = flash_write_align_end(bank, addr1); + target_addr_t aligned2 = flash_write_align_start(bank, addr2); + return aligned1 + bank->minimal_write_gap < aligned2; +} + + int flash_write_unlock(struct target *target, struct image *image, uint32_t *written, int erase, bool unlock) { @@ -598,10 +729,10 @@ int flash_write_unlock(struct target *target, struct image *image, /* loop until we reach end of the image */ while (section < image->num_sections) { - uint32_t buffer_size; + uint32_t buffer_idx; uint8_t *buffer; int section_last; - uint32_t run_address = sections[section]->base_address + section_offset; + target_addr_t run_address = sections[section]->base_address + section_offset; uint32_t run_size = sections[section]->size - section_offset; int pad_bytes = 0; @@ -617,7 +748,7 @@ int flash_write_unlock(struct target *target, struct image *image, if (retval != ERROR_OK) goto done; if (c == NULL) { - LOG_WARNING("no flash bank found for address %" PRIx32, run_address); + LOG_WARNING("no flash bank found for address " TARGET_ADDR_FMT, run_address); section++; /* and skip it */ section_offset = 0; continue; @@ -635,32 +766,37 @@ int flash_write_unlock(struct target *target, struct image *image, break; } - /* FIXME This needlessly touches sectors BETWEEN the - * sections it's writing. Without auto erase, it just - * writes ones. That WILL INVALIDATE data in cases - * like Stellaris Tempest chips, corrupting internal - * ECC codes; and at least FreeScale suggests issues - * with that approach (in HC11 documentation). - * - * With auto erase enabled, data in those sectors will - * be needlessly destroyed; and some of the limited - * number of flash erase cycles will be wasted... - * - * In both cases, the extra writes slow things down. - */ - /* if we have multiple sections within our image, * flash programming could fail due to alignment issues * attempt to rebuild a consecutive buffer for the flash loader */ - pad_bytes = (sections[section_last + 1]->base_address) - (run_address + run_size); - padding[section_last] = pad_bytes; - run_size += sections[++section_last]->size; - run_size += pad_bytes; + target_addr_t run_next_addr = run_address + run_size; + target_addr_t next_section_base = sections[section_last + 1]->base_address; + if (next_section_base < run_next_addr) { + LOG_ERROR("Section at " TARGET_ADDR_FMT + " overlaps section ending at " TARGET_ADDR_FMT, + next_section_base, run_next_addr); + LOG_ERROR("Flash write aborted."); + retval = ERROR_FAIL; + goto done; + } + pad_bytes = next_section_base - run_next_addr; + if (pad_bytes) { + if (flash_write_check_gap(c, run_next_addr - 1, next_section_base)) { + LOG_INFO("Flash write discontinued at " TARGET_ADDR_FMT + ", next section at " TARGET_ADDR_FMT, + run_next_addr, next_section_base); + break; + } + } if (pad_bytes > 0) - LOG_INFO("Padding image section %d with %d bytes", - section_last-1, - pad_bytes); + LOG_INFO("Padding image section %d at " TARGET_ADDR_FMT + " with %d bytes", + section_last, run_next_addr, pad_bytes); + + padding[section_last] = pad_bytes; + run_size += pad_bytes; + run_size += sections[++section_last]->size; } if (run_address + run_size - 1 > c->base + c->size - 1) { @@ -673,10 +809,38 @@ int flash_write_unlock(struct target *target, struct image *image, assert(run_size > 0); } - /* If we're applying any sector automagic, then pad this - * (maybe-combined) segment to the end of its last sector. - */ - if (unlock || erase) { + uint32_t padding_at_start = 0; + if (c->write_start_alignment || c->write_end_alignment) { + /* align write region according to bank requirements */ + target_addr_t aligned_start = flash_write_align_start(c, run_address); + padding_at_start = run_address - aligned_start; + if (padding_at_start > 0) { + LOG_WARNING("Section start address " TARGET_ADDR_FMT + " breaks the required alignment of flash bank %s", + run_address, c->name); + LOG_WARNING("Padding %d bytes from " TARGET_ADDR_FMT, + padding_at_start, aligned_start); + + run_address -= padding_at_start; + run_size += padding_at_start; + } + + target_addr_t run_end = run_address + run_size - 1; + target_addr_t aligned_end = flash_write_align_end(c, run_end); + pad_bytes = aligned_end - run_end; + if (pad_bytes > 0) { + LOG_INFO("Padding image section %d at " TARGET_ADDR_FMT + " with %d bytes (bank write end alignment)", + section_last, run_end + 1, pad_bytes); + + padding[section_last] += pad_bytes; + run_size += pad_bytes; + } + + } else if (unlock || erase) { + /* If we're applying any sector automagic, then pad this + * (maybe-combined) segment to the end of its last sector. + */ int sector; uint32_t offset_start = run_address - c->base; uint32_t offset_end = offset_start + run_size; @@ -701,13 +865,17 @@ int flash_write_unlock(struct target *target, struct image *image, retval = ERROR_FAIL; goto done; } - buffer_size = 0; + + if (padding_at_start) + memset(buffer, c->default_padded_value, padding_at_start); + + buffer_idx = padding_at_start; /* read sections to the buffer */ - while (buffer_size < run_size) { + while (buffer_idx < run_size) { size_t size_read; - size_read = run_size - buffer_size; + size_read = run_size - buffer_idx; if (size_read > sections[section]->size - section_offset) size_read = sections[section]->size - section_offset; @@ -720,23 +888,25 @@ int flash_write_unlock(struct target *target, struct image *image, int t_section_num = diff / sizeof(struct imagesection); LOG_DEBUG("image_read_section: section = %d, t_section_num = %d, " - "section_offset = %d, buffer_size = %d, size_read = %d", - (int)section, (int)t_section_num, (int)section_offset, - (int)buffer_size, (int)size_read); + "section_offset = %"PRIu32", buffer_idx = %"PRIu32", size_read = %zu", + section, t_section_num, section_offset, + buffer_idx, size_read); retval = image_read_section(image, t_section_num, section_offset, - size_read, buffer + buffer_size, &size_read); + size_read, buffer + buffer_idx, &size_read); if (retval != ERROR_OK || size_read == 0) { free(buffer); goto done; } - /* see if we need to pad the section */ - while (padding[section]--) - (buffer + buffer_size)[size_read++] = c->default_padded_value; - - buffer_size += size_read; + buffer_idx += size_read; section_offset += size_read; + /* see if we need to pad the section */ + if (padding[section]) { + memset(buffer + buffer_idx, c->default_padded_value, padding[section]); + buffer_idx += padding[section]; + } + if (section_offset >= sections[section]->size) { section++; section_offset = 0; diff --git a/src/flash/nor/core.h b/src/flash/nor/core.h index 338363e..67de94e 100644 --- a/src/flash/nor/core.h +++ b/src/flash/nor/core.h @@ -65,6 +65,13 @@ struct flash_sector { int is_protected; }; +/** Special value for write_start_alignment and write_end_alignment field */ +#define FLASH_WRITE_ALIGN_SECTOR UINT32_MAX + +/** Special values for minimal_write_gap field */ +#define FLASH_WRITE_CONTINUOUS 0 +#define FLASH_WRITE_GAP_SECTOR UINT32_MAX + /** * Provides details of a flash bank, available either on-chip or through * a major interface. @@ -76,7 +83,7 @@ struct flash_sector { * per-bank basis, if required. */ struct flash_bank { - const char *name; + char *name; struct target *target; /**< Target to which this bank belongs. */ @@ -97,6 +104,18 @@ struct flash_bank { * erased value. Defaults to 0xFF. */ uint8_t default_padded_value; + /** Required alignment of flash write start address. + * Default 0, no alignment. Can be any power of two or FLASH_WRITE_ALIGN_SECTOR */ + uint32_t write_start_alignment; + /** Required alignment of flash write end address. + * Default 0, no alignment. Can be any power of two or FLASH_WRITE_ALIGN_SECTOR */ + uint32_t write_end_alignment; + /** Minimal gap between sections to discontinue flash write + * Default FLASH_WRITE_GAP_SECTOR splits the write if one or more untouched + * sectors in between. + * Can be size in bytes or FLASH_WRITE_CONTINUOUS */ + uint32_t minimal_write_gap; + /** * The number of sectors on this chip. This value will * be set intially to 0, and the flash driver must set this to @@ -136,6 +155,22 @@ int flash_unlock_address_range(struct target *target, uint32_t addr, uint32_t length); /** + * Align start address of a flash write region according to bank requirements. + * @param bank Pointer to bank descriptor structure + * @param addr Address to align + * @returns Aligned address +*/ +target_addr_t flash_write_align_start(struct flash_bank *bank, target_addr_t addr); +/** + * Align end address of a flash write region according to bank requirements. + * Note: Use address of the last byte to write, not the next after the region. + * @param bank Pointer to bank descriptor structure + * @param addr Address to align (address of the last byte to write) + * @returns Aligned address (address of the last byte of padded region) +*/ +target_addr_t flash_write_align_end(struct flash_bank *bank, target_addr_t addr); + +/** * Writes @a image into the @a target flash. The @a written parameter * will contain the * @param target The target with the flash to be programmed. @@ -153,8 +188,15 @@ int flash_write(struct target *target, * This routine must be called when the system may modify the status. */ void flash_set_dirty(void); + /** @returns The number of flash banks currently defined. */ int flash_get_bank_count(void); + +/** Deallocates bank->driver_priv */ +void default_flash_free_driver_priv(struct flash_bank *bank); + +/** Deallocates all flash banks */ +void flash_free_all_banks(void); /** * Provides default read implementation for flash memory. * @param bank The bank to read. diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 0ae4d8e..e7b3234 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -209,6 +209,14 @@ struct flash_driver { * @returns ERROR_OK if successful; otherwise, an error code. */ int (*auto_probe)(struct flash_bank *bank); + + /** + * Deallocates private driver structures. + * Use default_flash_free_driver_priv() to simply free(bank->driver_priv) + * + * @param bank - the bank being destroyed + */ + void (*free_driver_priv)(struct flash_bank *bank); }; #define FLASH_BANK_COMMAND_HANDLER(name) \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 8168011..2b3146d 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -31,6 +31,9 @@ extern struct flash_driver at91samd_flash; extern struct flash_driver ath79_flash; extern struct flash_driver atsamv_flash; extern struct flash_driver avr_flash; +extern struct flash_driver bluenrgx_flash; +extern struct flash_driver cc3220sf_flash; +extern struct flash_driver cc26xx_flash; extern struct flash_driver cfi_flash; extern struct flash_driver dsp5680xx_flash; extern struct flash_driver efm32_flash; @@ -48,6 +51,7 @@ extern struct flash_driver lpc2900_flash; extern struct flash_driver lpcspifi_flash; extern struct flash_driver mdr_flash; extern struct flash_driver mrvlqspi_flash; +extern struct flash_driver msp432_flash; extern struct flash_driver niietcm4_flash; extern struct flash_driver nrf5_flash; extern struct flash_driver nrf51_flash; @@ -55,6 +59,10 @@ extern struct flash_driver numicro_flash; extern struct flash_driver ocl_flash; extern struct flash_driver pic32mx_flash; extern struct flash_driver psoc4_flash; +extern struct flash_driver psoc5lp_flash; +extern struct flash_driver psoc5lp_eeprom_flash; +extern struct flash_driver psoc5lp_nvl_flash; +extern struct flash_driver psoc6_flash; extern struct flash_driver sim3x_flash; extern struct flash_driver stellaris_flash; extern struct flash_driver stm32f1x_flash; @@ -88,6 +96,9 @@ static struct flash_driver *flash_drivers[] = { &ath79_flash, &atsamv_flash, &avr_flash, + &bluenrgx_flash, + &cc3220sf_flash, + &cc26xx_flash, &cfi_flash, &dsp5680xx_flash, &efm32_flash, @@ -105,6 +116,7 @@ static struct flash_driver *flash_drivers[] = { &lpcspifi_flash, &mdr_flash, &mrvlqspi_flash, + &msp432_flash, &niietcm4_flash, &nrf5_flash, &nrf51_flash, @@ -112,6 +124,10 @@ static struct flash_driver *flash_drivers[] = { &ocl_flash, &pic32mx_flash, &psoc4_flash, + &psoc5lp_flash, + &psoc5lp_eeprom_flash, + &psoc5lp_nvl_flash, + &psoc6_flash, &sim3x_flash, &stellaris_flash, &stm32f1x_flash, diff --git a/src/flash/nor/efm32.c b/src/flash/nor/efm32.c index b8453e1..1d70bd5 100644 --- a/src/flash/nor/efm32.c +++ b/src/flash/nor/efm32.c @@ -38,19 +38,8 @@ #include <target/armv7m.h> #include <target/cortex_m.h> -/* keep family IDs in decimal */ -#define EFM_FAMILY_ID_GECKO 71 #define EFM_FAMILY_ID_GIANT_GECKO 72 -#define EFM_FAMILY_ID_TINY_GECKO 73 #define EFM_FAMILY_ID_LEOPARD_GECKO 74 -#define EFM_FAMILY_ID_WONDER_GECKO 75 -#define EFM_FAMILY_ID_ZERO_GECKO 76 -#define EFM_FAMILY_ID_HAPPY_GECKO 77 -#define EZR_FAMILY_ID_WONDER_GECKO 120 -#define EZR_FAMILY_ID_LEOPARD_GECKO 121 -#define EZR_FAMILY_ID_HAPPY_GECKO 122 -#define EFR_FAMILY_ID_MIGHTY_GECKO 16 -#define EFR_FAMILY_ID_BLUE_GECKO 20 #define EFM32_FLASH_ERASE_TMO 100 #define EFM32_FLASH_WDATAREADY_TMO 100 @@ -65,7 +54,7 @@ #define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000) #define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000) -/* PAGE_SIZE is only present in Leopard, Giant and Wonder Gecko MCUs */ +/* PAGE_SIZE is not present in Zero, Happy and the original Gecko MCU */ #define EFM32_MSC_DI_PAGE_SIZE (EFM32_MSC_DEV_INFO+0x1e7) #define EFM32_MSC_DI_FLASH_SZ (EFM32_MSC_DEV_INFO+0x1f8) #define EFM32_MSC_DI_RAM_SZ (EFM32_MSC_DEV_INFO+0x1fa) @@ -74,7 +63,7 @@ #define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff) #define EFM32_MSC_REGBASE 0x400c0000 -#define EFR32_MSC_REGBASE 0x400e0000 +#define EFM32_MSC_REGBASE_SERIES1 0x400e0000 #define EFM32_MSC_REG_WRITECTRL 0x008 #define EFM32_MSC_WRITECTRL_WREN_MASK 0x1 #define EFM32_MSC_REG_WRITECMD 0x00c @@ -91,9 +80,24 @@ #define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10 #define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20 #define EFM32_MSC_REG_LOCK 0x03c -#define EFR32_MSC_REG_LOCK 0x040 +#define EFM32_MSC_REG_LOCK_SERIES1 0x040 #define EFM32_MSC_LOCK_LOCKKEY 0x1b71 +struct efm32_family_data { + int family_id; + const char *name; + + /* EFM32 series (EFM32LG995F is the "old" series 0, while EFR32MG12P132 + is the "new" series 1). Determines location of MSC registers. */ + int series; + + /* Page size in bytes, or 0 to read from EFM32_MSC_DI_PAGE_SIZE */ + int page_size; + + /* MSC register base address, or 0 to use default */ + uint32_t msc_regbase; +}; + struct efm32x_flash_bank { int probed; uint32_t lb_page[LOCKBITS_PAGE_SZ/4]; @@ -102,6 +106,7 @@ struct efm32x_flash_bank { }; struct efm32_info { + const struct efm32_family_data *family_data; uint16_t flash_sz_kib; uint16_t ram_sz_kib; uint16_t part_num; @@ -110,6 +115,64 @@ struct efm32_info { uint16_t page_size; }; +static const struct efm32_family_data efm32_families[] = { + { 16, "EFR32MG1P Mighty", .series = 1 }, + { 17, "EFR32MG1B Mighty", .series = 1 }, + { 18, "EFR32MG1V Mighty", .series = 1 }, + { 19, "EFR32MG1P Blue", .series = 1 }, + { 20, "EFR32MG1B Blue", .series = 1 }, + { 21, "EFR32MG1V Blue", .series = 1 }, + { 25, "EFR32FG1P Flex", .series = 1 }, + { 26, "EFR32FG1B Flex", .series = 1 }, + { 27, "EFR32FG1V Flex", .series = 1 }, + { 28, "EFR32MG2P Mighty", .series = 1 }, + { 29, "EFR32MG2B Mighty", .series = 1 }, + { 30, "EFR32MG2V Mighty", .series = 1 }, + { 31, "EFR32BG12P Blue", .series = 1 }, + { 32, "EFR32BG12B Blue", .series = 1 }, + { 33, "EFR32BG12V Blue", .series = 1 }, + { 37, "EFR32FG12P Flex", .series = 1 }, + { 38, "EFR32FG12B Flex", .series = 1 }, + { 39, "EFR32FG12V Flex", .series = 1 }, + { 40, "EFR32MG13P Mighty", .series = 1 }, + { 41, "EFR32MG13B Mighty", .series = 1 }, + { 42, "EFR32MG13V Mighty", .series = 1 }, + { 43, "EFR32BG13P Blue", .series = 1 }, + { 44, "EFR32BG13B Blue", .series = 1 }, + { 45, "EFR32BG13V Blue", .series = 1 }, + { 49, "EFR32FG13P Flex", .series = 1 }, + { 50, "EFR32FG13B Flex", .series = 1 }, + { 51, "EFR32FG13V Flex", .series = 1 }, + { 52, "EFR32MG14P Mighty", .series = 1 }, + { 53, "EFR32MG14B Mighty", .series = 1 }, + { 54, "EFR32MG14V Mighty", .series = 1 }, + { 55, "EFR32BG14P Blue", .series = 1 }, + { 56, "EFR32BG14B Blue", .series = 1 }, + { 57, "EFR32BG14V Blue", .series = 1 }, + { 61, "EFR32FG14P Flex", .series = 1 }, + { 62, "EFR32FG14B Flex", .series = 1 }, + { 63, "EFR32FG14V Flex", .series = 1 }, + { 71, "EFM32G", .series = 0, .page_size = 512 }, + { 72, "EFM32GG Giant", .series = 0 }, + { 73, "EFM32TG Tiny", .series = 0, .page_size = 512 }, + { 74, "EFM32LG Leopard", .series = 0 }, + { 75, "EFM32WG Wonder", .series = 0 }, + { 76, "EFM32ZG Zero", .series = 0, .page_size = 1024 }, + { 77, "EFM32HG Happy", .series = 0, .page_size = 1024 }, + { 81, "EFM32PG1B Pearl", .series = 1 }, + { 83, "EFM32JG1B Jade", .series = 1 }, + { 85, "EFM32PG12B Pearl", .series = 1 }, + { 87, "EFM32JG12B Jade", .series = 1 }, + { 89, "EFM32PG13B Pearl", .series = 1 }, + { 91, "EFM32JG13B Jade", .series = 1 }, + { 100, "EFM32GG11B Giant", .series = 1, .msc_regbase = 0x40000000 }, + { 103, "EFM32TG11B Tiny", .series = 1 }, + { 120, "EZR32WG Wonder", .series = 0 }, + { 121, "EZR32LG Leopard", .series = 0 }, + { 122, "EZR32HG Happy", .series = 0, .page_size = 1024 }, +}; + + static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count); @@ -200,51 +263,33 @@ static int efm32x_read_info(struct flash_bank *bank, if (ERROR_OK != ret) return ret; - if (EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family || - EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) { - efm32x_info->reg_base = EFR32_MSC_REGBASE; - efm32x_info->reg_lock = EFR32_MSC_REG_LOCK; - } else { - efm32x_info->reg_base = EFM32_MSC_REGBASE; - efm32x_info->reg_lock = EFM32_MSC_REG_LOCK; + for (size_t i = 0; i < ARRAY_SIZE(efm32_families); i++) { + if (efm32_families[i].family_id == efm32_info->part_family) + efm32_info->family_data = &efm32_families[i]; } - if (EFM_FAMILY_ID_GECKO == efm32_info->part_family || - EFM_FAMILY_ID_TINY_GECKO == efm32_info->part_family) - efm32_info->page_size = 512; - else if (EFM_FAMILY_ID_ZERO_GECKO == efm32_info->part_family || - EFM_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family || - EZR_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family) - efm32_info->page_size = 1024; - else if (EFM_FAMILY_ID_GIANT_GECKO == efm32_info->part_family || - EFM_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family) { - if (efm32_info->prod_rev >= 18) { - uint8_t pg_size = 0; - ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE, - &pg_size); - if (ERROR_OK != ret) - return ret; - - efm32_info->page_size = (1 << ((pg_size+10) & 0xff)); - } else { - /* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid - for MCUs with PROD_REV < 18 */ - if (efm32_info->flash_sz_kib < 512) - efm32_info->page_size = 2048; - else - efm32_info->page_size = 4096; - } + if (efm32_info->family_data == NULL) { + LOG_ERROR("Unknown MCU family %d", efm32_info->part_family); + return ERROR_FAIL; + } - if ((2048 != efm32_info->page_size) && - (4096 != efm32_info->page_size)) { - LOG_ERROR("Invalid page size %u", efm32_info->page_size); - return ERROR_FAIL; - } - } else if (EFM_FAMILY_ID_WONDER_GECKO == efm32_info->part_family || - EZR_FAMILY_ID_WONDER_GECKO == efm32_info->part_family || - EZR_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family || - EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family || - EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) { + switch (efm32_info->family_data->series) { + case 0: + efm32x_info->reg_base = EFM32_MSC_REGBASE; + efm32x_info->reg_lock = EFM32_MSC_REG_LOCK; + break; + case 1: + efm32x_info->reg_base = EFM32_MSC_REGBASE_SERIES1; + efm32x_info->reg_lock = EFM32_MSC_REG_LOCK_SERIES1; + break; + } + + if (efm32_info->family_data->msc_regbase != 0) + efm32x_info->reg_base = efm32_info->family_data->msc_regbase; + + if (efm32_info->family_data->page_size != 0) { + efm32_info->page_size = efm32_info->family_data->page_size; + } else { uint8_t pg_size = 0; ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE, &pg_size); @@ -252,13 +297,25 @@ static int efm32x_read_info(struct flash_bank *bank, return ret; efm32_info->page_size = (1 << ((pg_size+10) & 0xff)); - if (2048 != efm32_info->page_size) { + + if (efm32_info->part_family == EFM_FAMILY_ID_GIANT_GECKO || + efm32_info->part_family == EFM_FAMILY_ID_LEOPARD_GECKO) { + /* Giant or Leopard Gecko */ + if (efm32_info->prod_rev < 18) { + /* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid + for MCUs with PROD_REV < 18 */ + if (efm32_info->flash_sz_kib < 512) + efm32_info->page_size = 2048; + else + efm32_info->page_size = 4096; + } + } + + if ((efm32_info->page_size != 2048) && + (efm32_info->page_size != 4096)) { LOG_ERROR("Invalid page size %u", efm32_info->page_size); return ERROR_FAIL; } - } else { - LOG_ERROR("Unknown MCU family %d", efm32_info->part_family); - return ERROR_FAIL; } return ERROR_OK; @@ -270,71 +327,10 @@ static int efm32x_read_info(struct flash_bank *bank, static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size) { int printed = 0; + printed = snprintf(buf, buf_size, "%s Gecko, rev %d", + info->family_data->name, info->prod_rev); - switch (info->part_family) { - case EZR_FAMILY_ID_WONDER_GECKO: - case EZR_FAMILY_ID_LEOPARD_GECKO: - case EZR_FAMILY_ID_HAPPY_GECKO: - printed = snprintf(buf, buf_size, "EZR32 "); - break; - case EFR_FAMILY_ID_MIGHTY_GECKO: - case EFR_FAMILY_ID_BLUE_GECKO: - printed = snprintf(buf, buf_size, "EFR32 "); - break; - default: - printed = snprintf(buf, buf_size, "EFM32 "); - } - - buf += printed; - buf_size -= printed; - - if (0 >= buf_size) - return ERROR_BUF_TOO_SMALL; - - switch (info->part_family) { - case EFM_FAMILY_ID_GECKO: - printed = snprintf(buf, buf_size, "Gecko"); - break; - case EFM_FAMILY_ID_GIANT_GECKO: - printed = snprintf(buf, buf_size, "Giant Gecko"); - break; - case EFM_FAMILY_ID_TINY_GECKO: - printed = snprintf(buf, buf_size, "Tiny Gecko"); - break; - case EFM_FAMILY_ID_LEOPARD_GECKO: - case EZR_FAMILY_ID_LEOPARD_GECKO: - printed = snprintf(buf, buf_size, "Leopard Gecko"); - break; - case EFM_FAMILY_ID_WONDER_GECKO: - case EZR_FAMILY_ID_WONDER_GECKO: - printed = snprintf(buf, buf_size, "Wonder Gecko"); - break; - case EFM_FAMILY_ID_ZERO_GECKO: - printed = snprintf(buf, buf_size, "Zero Gecko"); - break; - case EFM_FAMILY_ID_HAPPY_GECKO: - case EZR_FAMILY_ID_HAPPY_GECKO: - printed = snprintf(buf, buf_size, "Happy Gecko"); - break; - case EFR_FAMILY_ID_BLUE_GECKO: - printed = snprintf(buf, buf_size, "Blue Gecko"); - break; - case EFR_FAMILY_ID_MIGHTY_GECKO: - printed = snprintf(buf, buf_size, "Mighty Gecko"); - break; - } - - buf += printed; - buf_size -= printed; - - if (0 >= buf_size) - return ERROR_BUF_TOO_SMALL; - - printed = snprintf(buf, buf_size, " - Rev: %d", info->prod_rev); - buf += printed; - buf_size -= printed; - - if (0 >= buf_size) + if (printed >= buf_size) return ERROR_BUF_TOO_SMALL; return ERROR_OK; @@ -522,7 +518,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank) } } - /* also, read ULW, DLW and MLW */ + /* also, read ULW, DLW, MLW, ALW and CLW words */ /* ULW, word 126 */ ptr = efm32x_info->lb_page + 126; @@ -540,7 +536,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank) return ret; } - /* MLW, word 125, present in GG and LG */ + /* MLW, word 125, present in GG, LG, PG, JG, EFR32 */ ptr = efm32x_info->lb_page + 125; ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+125*4, ptr); if (ERROR_OK != ret) { @@ -548,6 +544,30 @@ static int efm32x_read_lock_data(struct flash_bank *bank) return ret; } + /* ALW, word 124, present in GG, LG, PG, JG, EFR32 */ + ptr = efm32x_info->lb_page + 124; + ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+124*4, ptr); + if (ERROR_OK != ret) { + LOG_ERROR("Failed to read ALW"); + return ret; + } + + /* CLW1, word 123, present in EFR32 */ + ptr = efm32x_info->lb_page + 123; + ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+123*4, ptr); + if (ERROR_OK != ret) { + LOG_ERROR("Failed to read CLW1"); + return ret; + } + + /* CLW0, word 122, present in GG, LG, PG, JG, EFR32 */ + ptr = efm32x_info->lb_page + 122; + ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+122*4, ptr); + if (ERROR_OK != ret) { + LOG_ERROR("Failed to read CLW0"); + return ret; + } + return ERROR_OK; } @@ -1113,4 +1133,5 @@ struct flash_driver efm32_flash = { .erase_check = default_flash_blank_check, .protect_check = efm32x_protect_check, .info = get_efm32x_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/em357.c b/src/flash/nor/em357.c index a11743b..b14e032 100644 --- a/src/flash/nor/em357.c +++ b/src/flash/nor/em357.c @@ -941,4 +941,5 @@ struct flash_driver em357_flash = { .auto_probe = em357_auto_probe, .erase_check = default_flash_blank_check, .protect_check = em357_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/faux.c b/src/flash/nor/faux.c index 203eb6f..46eda72 100644 --- a/src/flash/nor/faux.c +++ b/src/flash/nor/faux.c @@ -136,5 +136,6 @@ struct flash_driver faux_flash = { .auto_probe = faux_probe, .erase_check = default_flash_blank_check, .protect_check = faux_protect_check, - .info = faux_info + .info = faux_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c index cedcb3b..6667d36 100644 --- a/src/flash/nor/fespi.c +++ b/src/flash/nor/fespi.c @@ -436,6 +436,12 @@ static int slow_fespi_write_buffer(struct flash_bank *bank, uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ii; + if (offset & 0xFF000000) { + LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x", + offset); + return ERROR_FAIL; + } + /* TODO!!! assert that len < page size */ fespi_tx(bank, SPIFLASH_WRITE_ENABLE); @@ -607,11 +613,11 @@ struct algorithm_steps { uint8_t **steps; }; -static struct algorithm_steps *as_new(unsigned size) +static struct algorithm_steps *as_new(void) { struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps)); - as->size = size; - as->steps = calloc(size, sizeof(as->steps[0])); + as->size = 8; + as->steps = malloc(as->size * sizeof(as->steps[0])); return as; } @@ -701,17 +707,27 @@ static unsigned as_compile(struct algorithm_steps *as, uint8_t *target, return offset; } +static void as_add_step(struct algorithm_steps *as, uint8_t *step) +{ + if (as->used == as->size) { + as->size *= 2; + as->steps = realloc(as->steps, sizeof(as->steps[0]) * as->size); + LOG_DEBUG("Increased size to 0x%x", as->size); + } + as->steps[as->used] = step; + as->used++; +} + static void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data) { LOG_DEBUG("count=%d", count); while (count > 0) { unsigned step_count = MIN(count, 255); - assert(as->used < as->size); - as->steps[as->used] = malloc(step_count + 2); - as->steps[as->used][0] = STEP_TX; - as->steps[as->used][1] = step_count; - memcpy(as->steps[as->used] + 2, data, step_count); - as->used++; + uint8_t *step = malloc(step_count + 2); + step[0] = STEP_TX; + step[1] = step_count; + memcpy(step + 2, data, step_count); + as_add_step(as, step); data += step_count; count -= step_count; } @@ -726,43 +742,45 @@ static void as_add_tx1(struct algorithm_steps *as, uint8_t byte) static void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data) { - assert(as->used < as->size); - as->steps[as->used] = malloc(3); - as->steps[as->used][0] = STEP_WRITE_REG; - as->steps[as->used][1] = offset; - as->steps[as->used][2] = data; - as->used++; + uint8_t *step = malloc(3); + step[0] = STEP_WRITE_REG; + step[1] = offset; + step[2] = data; + as_add_step(as, step); } static void as_add_txwm_wait(struct algorithm_steps *as) { - assert(as->used < as->size); - as->steps[as->used] = malloc(1); - as->steps[as->used][0] = STEP_TXWM_WAIT; - as->used++; + uint8_t *step = malloc(1); + step[0] = STEP_TXWM_WAIT; + as_add_step(as, step); } static void as_add_wip_wait(struct algorithm_steps *as) { - assert(as->used < as->size); - as->steps[as->used] = malloc(1); - as->steps[as->used][0] = STEP_WIP_WAIT; - as->used++; + uint8_t *step = malloc(1); + step[0] = STEP_WIP_WAIT; + as_add_step(as, step); } static void as_add_set_dir(struct algorithm_steps *as, bool dir) { - assert(as->used < as->size); - as->steps[as->used] = malloc(2); - as->steps[as->used][0] = STEP_SET_DIR; - as->steps[as->used][1] = FESPI_FMT_DIR(dir); - as->used++; + uint8_t *step = malloc(2); + step[0] = STEP_SET_DIR; + step[1] = FESPI_FMT_DIR(dir); + as_add_step(as, step); } /* This should write something less than or equal to a page.*/ static int steps_add_buffer_write(struct algorithm_steps *as, const uint8_t *buffer, uint32_t chip_offset, uint32_t len) { + if (chip_offset & 0xFF000000) { + LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x", + chip_offset); + return ERROR_FAIL; + } + as_add_tx1(as, SPIFLASH_WRITE_ENABLE); as_add_txwm_wait(as); as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD); @@ -910,7 +928,7 @@ static int fespi_write(struct flash_bank *bank, const uint8_t *buffer, if (retval != ERROR_OK) return retval; - struct algorithm_steps *as = as_new(count / 4); + struct algorithm_steps *as = as_new(); /* unaligned buffer head */ if (count > 0 && (offset & 3) != 0) { @@ -1161,5 +1179,6 @@ struct flash_driver fespi_flash = { .auto_probe = fespi_auto_probe, .erase_check = default_flash_blank_check, .protect_check = fespi_protect_check, - .info = get_fespi_info + .info = get_fespi_info, + .free_driver_priv = default_flash_free_driver_priv }; diff --git a/src/flash/nor/fm3.c b/src/flash/nor/fm3.c index 6269a65..6c61977 100644 --- a/src/flash/nor/fm3.c +++ b/src/flash/nor/fm3.c @@ -997,4 +997,5 @@ struct flash_driver fm3_flash = { .probe = fm3_probe, .auto_probe = fm3_auto_probe, .erase_check = default_flash_blank_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/fm4.c b/src/flash/nor/fm4.c index c8fe8b6..f5eab9c 100644 --- a/src/flash/nor/fm4.c +++ b/src/flash/nor/fm4.c @@ -719,4 +719,5 @@ struct flash_driver fm4_flash = { .erase = fm4_flash_erase, .erase_check = default_flash_blank_check, .write = fm4_flash_write, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c index a73812d..c28ad22 100644 --- a/src/flash/nor/jtagspi.c +++ b/src/flash/nor/jtagspi.c @@ -432,5 +432,6 @@ struct flash_driver jtagspi_flash = { .auto_probe = jtagspi_probe, .erase_check = default_flash_blank_check, .protect_check = jtagspi_protect_check, - .info = jtagspi_info + .info = jtagspi_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/kinetis.c b/src/flash/nor/kinetis.c index 48a5de4..86fad72 100644 --- a/src/flash/nor/kinetis.c +++ b/src/flash/nor/kinetis.c @@ -915,13 +915,29 @@ FLASH_BANK_COMMAND_HANDLER(kinetis_flash_bank_command) } +static void kinetis_free_driver_priv(struct flash_bank *bank) +{ + struct kinetis_flash_bank *k_bank = bank->driver_priv; + if (k_bank == NULL) + return; + + struct kinetis_chip *k_chip = k_bank->k_chip; + if (k_chip == NULL) + return; + + k_chip->num_banks--; + if (k_chip->num_banks == 0) + free(k_chip); +} + + static int kinetis_create_missing_banks(struct kinetis_chip *k_chip) { unsigned bank_idx; unsigned num_blocks; struct kinetis_flash_bank *k_bank; struct flash_bank *bank; - char base_name[80], name[80], num[4]; + char base_name[69], name[80], num[4]; char *class, *p; num_blocks = k_chip->num_pflash_blocks + k_chip->num_nvm_blocks; @@ -932,19 +948,21 @@ static int kinetis_create_missing_banks(struct kinetis_chip *k_chip) bank = k_chip->banks[0].bank; if (bank && bank->name) { - strncpy(base_name, bank->name, sizeof(base_name)); + strncpy(base_name, bank->name, sizeof(base_name) - 1); + base_name[sizeof(base_name) - 1] = '\0'; p = strstr(base_name, ".pflash"); if (p) { *p = '\0'; if (k_chip->num_pflash_blocks > 1) { /* rename first bank if numbering is needed */ snprintf(name, sizeof(name), "%s.pflash0", base_name); - free((void *)bank->name); + free(bank->name); bank->name = strdup(name); } } } else { - strncpy(base_name, target_name(k_chip->target), sizeof(base_name)); + strncpy(base_name, target_name(k_chip->target), sizeof(base_name) - 1); + base_name[sizeof(base_name) - 1] = '\0'; p = strstr(base_name, ".cpu"); if (p) *p = '\0'; @@ -1996,7 +2014,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip) unsigned cpu_mhz = 120; unsigned idx; bool use_nvm_marking = false; - char flash_marking[11], nvm_marking[2]; + char flash_marking[12], nvm_marking[2]; char name[40]; k_chip->probed = false; @@ -3132,4 +3150,5 @@ struct flash_driver kinetis_flash = { .erase_check = kinetis_blank_check, .protect_check = kinetis_protect_check, .info = kinetis_info, + .free_driver_priv = kinetis_free_driver_priv, }; diff --git a/src/flash/nor/kinetis_ke.c b/src/flash/nor/kinetis_ke.c index 636fcc2..8103b63 100644 --- a/src/flash/nor/kinetis_ke.c +++ b/src/flash/nor/kinetis_ke.c @@ -478,14 +478,13 @@ int kinetis_ke_stop_watchdog(struct target *target) watchdog_algorithm->address, 0, 100000, &armv7m_info); if (retval != ERROR_OK) { LOG_ERROR("Error executing Kinetis KE watchdog algorithm"); - retval = ERROR_FAIL; } else { LOG_INFO("Watchdog stopped"); } target_free_working_area(target, watchdog_algorithm); - return ERROR_OK; + return retval; } COMMAND_HANDLER(kinetis_ke_disable_wdog_handler) @@ -1311,4 +1310,5 @@ struct flash_driver kinetis_ke_flash = { .erase_check = kinetis_ke_blank_check, .protect_check = kinetis_ke_protect_check, .info = kinetis_ke_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/lpc2000.c b/src/flash/nor/lpc2000.c index 9da5da2..8e15c31 100644 --- a/src/flash/nor/lpc2000.c +++ b/src/flash/nor/lpc2000.c @@ -1579,4 +1579,5 @@ struct flash_driver lpc2000_flash = { .erase_check = lpc2000_erase_check, .protect_check = lpc2000_protect_check, .info = get_lpc2000_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/lpc288x.c b/src/flash/nor/lpc288x.c index a4d88de..2472913 100644 --- a/src/flash/nor/lpc288x.c +++ b/src/flash/nor/lpc288x.c @@ -433,4 +433,5 @@ struct flash_driver lpc288x_flash = { .auto_probe = lpc288x_probe, .erase_check = lpc288x_erase_check, .protect_check = lpc288x_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/lpc2900.c b/src/flash/nor/lpc2900.c index 515a3f7..1c65933 100644 --- a/src/flash/nor/lpc2900.c +++ b/src/flash/nor/lpc2900.c @@ -1598,4 +1598,5 @@ struct flash_driver lpc2900_flash = { .auto_probe = lpc2900_probe, .erase_check = lpc2900_erase_check, .protect_check = lpc2900_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/lpcspifi.c b/src/flash/nor/lpcspifi.c index 943c151..828c60c 100644 --- a/src/flash/nor/lpcspifi.c +++ b/src/flash/nor/lpcspifi.c @@ -942,4 +942,5 @@ struct flash_driver lpcspifi_flash = { .erase_check = default_flash_blank_check, .protect_check = lpcspifi_protect_check, .info = get_lpcspifi_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/mdr.c b/src/flash/nor/mdr.c index 8ceb1bf..f3916de 100644 --- a/src/flash/nor/mdr.c +++ b/src/flash/nor/mdr.c @@ -633,4 +633,5 @@ struct flash_driver mdr_flash = { .erase_check = default_flash_blank_check, .protect_check = mdr_protect_check, .info = get_mdr_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/mrvlqspi.c b/src/flash/nor/mrvlqspi.c index d799170..eda6cc1 100644 --- a/src/flash/nor/mrvlqspi.c +++ b/src/flash/nor/mrvlqspi.c @@ -955,4 +955,5 @@ struct flash_driver mrvlqspi_flash = { .erase_check = mrvlqspi_flash_erase_check, .protect_check = mrvlqspi_protect_check, .info = mrvlqspi_get_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/msp432.c b/src/flash/nor/msp432.c new file mode 100644 index 0000000..5caa052 --- /dev/null +++ b/src/flash/nor/msp432.c @@ -0,0 +1,1103 @@ +/*************************************************************************** + * Copyright (C) 2018 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "msp432.h" +#include <helper/binarybuffer.h> +#include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/armv7m.h> +#include <target/image.h> + +/* MSP432P4 hardware registers */ +#define P4_FLASH_MAIN_SIZE_REG 0xE0043020 +#define P4_FLASH_INFO_SIZE_REG 0xE0043024 +#define P4_DEVICE_ID_REG 0x0020100C +#define P4_HARDWARE_REV_REG 0x00201010 + +/* MSP432E4 hardware registers */ +#define E4_DID0_REG 0x400FE000 +#define E4_DID1_REG 0x400FE004 + +#define FLASH_TIMEOUT 8000 + +#define SUPPORT_MESSAGE \ + "Your pre-production MSP432P401x silicon is not fully supported\n" \ + "You can find more information at www.ti.com/product/MSP432P401R" + +struct msp432_bank { + uint32_t device_id; + uint32_t hardware_rev; + int family_type; + int device_type; + uint32_t sector_length; + bool probed[2]; + bool unlock_bsl; + struct working_area *working_area; + struct armv7m_algorithm armv7m_info; +}; + +static int msp432_auto_probe(struct flash_bank *bank); + +static int msp432_device_type(uint32_t family_type, uint32_t device_id, + uint32_t hardware_rev) +{ + int device_type = MSP432_NO_TYPE; + + if (MSP432E4 == family_type) { + /* MSP432E4 device family */ + + if (device_id == 0x180C0002) { + if (hardware_rev == 0x102DC06E) { + /* The 01Y variant */ + device_type = MSP432E401Y; + } else if (hardware_rev == 0x1032E076) { + /* The 11Y variant */ + device_type = MSP432E411Y; + } else { + /* Reasonable guess that this is a new variant */ + device_type = MSP432E4X_GUESS; + } + } else { + /* Wild guess that this is an MSP432E4 */ + device_type = MSP432E4X_GUESS; + } + } else { + /* MSP432P4 device family */ + + /* Examine the device ID and hardware revision to get the device type */ + switch (device_id) { + case 0xA000: + case 0xA001: + case 0xA002: + case 0xA003: + case 0xA004: + case 0xA005: + /* Device is definitely MSP432P401x, check hardware revision */ + if (hardware_rev == 0x41 || hardware_rev == 0x42) { + /* Rev A or B of the silicon has been deprecated */ + device_type = MSP432P401X_DEPR; + } else if (hardware_rev >= 0x43 && hardware_rev <= 0x49) { + /* Current and future revisions of the MSP432P401x device */ + device_type = MSP432P401X; + } else { + /* Unknown or unanticipated hardware revision */ + device_type = MSP432P401X_GUESS; + } + break; + case 0xA010: + case 0xA012: + case 0xA016: + case 0xA019: + case 0xA01F: + case 0xA020: + case 0xA022: + case 0xA026: + case 0xA029: + case 0xA02F: + /* Device is definitely MSP432P411x, check hardware revision */ + if (hardware_rev >= 0x41 && hardware_rev <= 0x49) { + /* Current and future revisions of the MSP432P411x device */ + device_type = MSP432P411X; + } else { + /* Unknown or unanticipated hardware revision */ + device_type = MSP432P411X_GUESS; + } + break; + case 0xFFFF: + /* Device is very early silicon that has been deprecated */ + device_type = MSP432P401X_DEPR; + break; + default: + if (device_id < 0xA010) { + /* Wild guess that this is an MSP432P401x */ + device_type = MSP432P401X_GUESS; + } else { + /* Reasonable guess that this is a new variant */ + device_type = MSP432P411X_GUESS; + } + break; + } + } + + return device_type; +} + +static const char *msp432_return_text(uint32_t return_code) +{ + switch (return_code) { + case FLASH_BUSY: + return "FLASH_BUSY"; + case FLASH_SUCCESS: + return "FLASH_SUCCESS"; + case FLASH_ERROR: + return "FLASH_ERROR"; + case FLASH_TIMEOUT_ERROR: + return "FLASH_TIMEOUT_ERROR"; + case FLASH_VERIFY_ERROR: + return "FLASH_VERIFY_WRONG"; + case FLASH_WRONG_COMMAND: + return "FLASH_WRONG_COMMAND"; + case FLASH_POWER_ERROR: + return "FLASH_POWER_ERROR"; + default: + return "UNDEFINED_RETURN_CODE"; + } +} + +static void msp432_init_params(struct msp432_algo_params *algo_params) +{ + buf_set_u32(algo_params->flash_command, 0, 32, FLASH_NO_COMMAND); + buf_set_u32(algo_params->return_code, 0, 32, 0); + buf_set_u32(algo_params->_reserved0, 0, 32, 0); + buf_set_u32(algo_params->address, 0, 32, 0); + buf_set_u32(algo_params->length, 0, 32, 0); + buf_set_u32(algo_params->buffer1_status, 0, 32, BUFFER_INACTIVE); + buf_set_u32(algo_params->buffer2_status, 0, 32, BUFFER_INACTIVE); + buf_set_u32(algo_params->erase_param, 0, 32, FLASH_ERASE_MAIN); + buf_set_u32(algo_params->unlock_bsl, 0, 32, FLASH_LOCK_BSL); +} + +static int msp432_exec_cmd(struct target *target, struct msp432_algo_params + *algo_params, uint32_t command) +{ + int retval; + + /* Make sure the given params do not include the command */ + buf_set_u32(algo_params->flash_command, 0, 32, FLASH_NO_COMMAND); + buf_set_u32(algo_params->return_code, 0, 32, 0); + buf_set_u32(algo_params->buffer1_status, 0, 32, BUFFER_INACTIVE); + buf_set_u32(algo_params->buffer2_status, 0, 32, BUFFER_INACTIVE); + + /* Write out parameters to target memory */ + retval = target_write_buffer(target, ALGO_PARAMS_BASE_ADDR, + sizeof(struct msp432_algo_params), (uint8_t *)algo_params); + if (ERROR_OK != retval) + return retval; + + /* Write out command to target memory */ + retval = target_write_buffer(target, ALGO_FLASH_COMMAND_ADDR, + sizeof(command), (uint8_t *)&command); + + return retval; +} + +static int msp432_wait_return_code(struct target *target) +{ + uint32_t return_code = 0; + long long start_ms; + long long elapsed_ms; + + int retval = ERROR_OK; + + start_ms = timeval_ms(); + while ((0 == return_code) || (FLASH_BUSY == return_code)) { + retval = target_read_buffer(target, ALGO_RETURN_CODE_ADDR, + sizeof(return_code), (uint8_t *)&return_code); + if (ERROR_OK != retval) + return retval; + + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + if (elapsed_ms > FLASH_TIMEOUT) + break; + }; + + if (FLASH_SUCCESS != return_code) { + LOG_ERROR("msp432: Flash operation failed: %s", + msp432_return_text(return_code)); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int msp432_wait_inactive(struct target *target, uint32_t buffer) +{ + uint32_t status_code = BUFFER_ACTIVE; + uint32_t status_addr; + long long start_ms; + long long elapsed_ms; + + int retval; + + switch (buffer) { + case 1: /* Buffer 1 */ + status_addr = ALGO_BUFFER1_STATUS_ADDR; + break; + case 2: /* Buffer 2 */ + status_addr = ALGO_BUFFER2_STATUS_ADDR; + break; + default: + return ERROR_FAIL; + } + + start_ms = timeval_ms(); + while (BUFFER_INACTIVE != status_code) { + retval = target_read_buffer(target, status_addr, sizeof(status_code), + (uint8_t *)&status_code); + if (ERROR_OK != retval) + return retval; + + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + if (elapsed_ms > FLASH_TIMEOUT) + break; + }; + + if (BUFFER_INACTIVE != status_code) { + LOG_ERROR( + "msp432: Flash operation failed: buffer not written to flash"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int msp432_init(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct msp432_bank *msp432_bank = bank->driver_priv; + struct msp432_algo_params algo_params; + struct reg_param reg_params[1]; + + const uint8_t *loader_code; + uint32_t loader_size; + uint32_t algo_entry_addr; + int retval; + + /* Make sure we've probed the flash to get the device and size */ + retval = msp432_auto_probe(bank); + if (ERROR_OK != retval) + return retval; + + /* Choose appropriate flash helper algorithm */ + switch (msp432_bank->device_type) { + case MSP432P401X: + case MSP432P401X_DEPR: + case MSP432P401X_GUESS: + default: + loader_code = msp432p401x_algo; + loader_size = sizeof(msp432p401x_algo); + algo_entry_addr = P4_ALGO_ENTRY_ADDR; + break; + case MSP432P411X: + case MSP432P411X_GUESS: + loader_code = msp432p411x_algo; + loader_size = sizeof(msp432p411x_algo); + algo_entry_addr = P4_ALGO_ENTRY_ADDR; + break; + case MSP432E401Y: + case MSP432E411Y: + case MSP432E4X_GUESS: + loader_code = msp432e4x_algo; + loader_size = sizeof(msp432e4x_algo); + algo_entry_addr = E4_ALGO_ENTRY_ADDR; + break; + } + + /* Issue warnings if this is a device we may not be able to flash */ + if (MSP432P401X_GUESS == msp432_bank->device_type || + MSP432P411X_GUESS == msp432_bank->device_type) { + /* Explicit device type check failed. Report this. */ + LOG_WARNING( + "msp432: Unrecognized MSP432P4 Device ID and Hardware " + "Rev (%04X, %02X)", msp432_bank->device_id, + msp432_bank->hardware_rev); + } else if (MSP432P401X_DEPR == msp432_bank->device_type) { + LOG_WARNING( + "msp432: MSP432P401x pre-production device (deprecated " + "silicon)\n" SUPPORT_MESSAGE); + } else if (MSP432E4X_GUESS == msp432_bank->device_type) { + /* Explicit device type check failed. Report this. */ + LOG_WARNING( + "msp432: Unrecognized MSP432E4 DID0 and DID1 values " + "(%08X, %08X)", msp432_bank->device_id, + msp432_bank->hardware_rev); + } + + /* Check for working area to use for flash helper algorithm */ + if (NULL != msp432_bank->working_area) + target_free_working_area(target, msp432_bank->working_area); + retval = target_alloc_working_area(target, ALGO_WORKING_SIZE, + &msp432_bank->working_area); + if (ERROR_OK != retval) + return retval; + + /* Confirm the defined working address is the area we need to use */ + if (ALGO_BASE_ADDR != msp432_bank->working_area->address) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + /* Write flash helper algorithm into target memory */ + retval = target_write_buffer(target, ALGO_BASE_ADDR, loader_size, + loader_code); + if (ERROR_OK != retval) + return retval; + + /* Initialize the ARMv7 specific info to run the algorithm */ + msp432_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + msp432_bank->armv7m_info.core_mode = ARM_MODE_THREAD; + + /* Initialize algorithm parameters to default values */ + msp432_init_params(&algo_params); + + /* Write out parameters to target memory */ + retval = target_write_buffer(target, ALGO_PARAMS_BASE_ADDR, + sizeof(algo_params), (uint8_t *)&algo_params); + if (ERROR_OK != retval) + return retval; + + /* Initialize stack pointer for flash helper algorithm */ + init_reg_param(®_params[0], "sp", 32, PARAM_OUT); + buf_set_u32(reg_params[0].value, 0, 32, ALGO_STACK_POINTER_ADDR); + + /* Begin executing the flash helper algorithm */ + retval = target_start_algorithm(target, 0, 0, 1, reg_params, + algo_entry_addr, 0, &msp432_bank->armv7m_info); + destroy_reg_param(®_params[0]); + if (ERROR_OK != retval) { + LOG_ERROR("msp432: Failed to start flash helper algorithm"); + return retval; + } + + /* + * At this point, the algorithm is running on the target and + * ready to receive commands and data to flash the target + */ + + /* Issue the init command to the flash helper algorithm */ + retval = msp432_exec_cmd(target, &algo_params, FLASH_INIT); + if (ERROR_OK != retval) + return retval; + + retval = msp432_wait_return_code(target); + + return retval; +} + +static int msp432_quit(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct msp432_bank *msp432_bank = bank->driver_priv; + struct msp432_algo_params algo_params; + + int retval; + + /* Initialize algorithm parameters to default values */ + msp432_init_params(&algo_params); + + /* Issue the exit command to the flash helper algorithm */ + retval = msp432_exec_cmd(target, &algo_params, FLASH_EXIT); + if (ERROR_OK != retval) + return retval; + + (void)msp432_wait_return_code(target); + + /* Regardless of the return code, attempt to halt the target */ + (void)target_halt(target); + + /* Now confirm target halted and clean up from flash helper algorithm */ + retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT, + &msp432_bank->armv7m_info); + + target_free_working_area(target, msp432_bank->working_area); + msp432_bank->working_area = NULL; + + return retval; +} + +static int msp432_mass_erase(struct flash_bank *bank, bool all) +{ + struct target *target = bank->target; + struct msp432_bank *msp432_bank = bank->driver_priv; + struct msp432_algo_params algo_params; + + int retval; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = msp432_init(bank); + if (ERROR_OK != retval) + return retval; + + /* Initialize algorithm parameters to default values */ + msp432_init_params(&algo_params); + if (all) { + buf_set_u32(algo_params.erase_param, 0, 32, + FLASH_ERASE_MAIN | FLASH_ERASE_INFO); + if (msp432_bank->unlock_bsl) + buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL); + } + + /* Issue the mass erase command to the flash helper algorithm */ + retval = msp432_exec_cmd(target, &algo_params, FLASH_MASS_ERASE); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + + retval = msp432_wait_return_code(target); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + + retval = msp432_quit(bank); + if (ERROR_OK != retval) + return retval; + + return retval; +} + +COMMAND_HANDLER(msp432_mass_erase_command) +{ + struct flash_bank *bank; + struct msp432_bank *msp432_bank; + bool all; + int retval; + + if (0 == CMD_ARGC) { + all = false; + } else if (1 == CMD_ARGC) { + /* Check argument for how much to erase */ + if (0 == strcmp(CMD_ARGV[0], "main")) + all = false; + else if (0 == strcmp(CMD_ARGV[0], "all")) + all = true; + else + return ERROR_COMMAND_SYNTAX_ERROR; + } else { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + retval = get_flash_bank_by_num(0, &bank); + if (ERROR_OK != retval) + return retval; + + msp432_bank = bank->driver_priv; + + if (MSP432E4 == msp432_bank->family_type) { + /* MSP432E4 does not have main vs info regions, ignore "all" */ + all = false; + } + + retval = msp432_mass_erase(bank, all); + if (ERROR_OK != retval) + return retval; + + if (MSP432E4 == msp432_bank->family_type) { + /* MSP432E4 does not have main vs info regions */ + LOG_INFO("msp432: Mass erase of flash is complete"); + } else { + LOG_INFO("msp432: Mass erase of %s is complete", + all ? "main + info flash" : "main flash"); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(msp432_bsl_command) +{ + struct flash_bank *bank; + struct msp432_bank *msp432_bank; + int retval; + + if (1 < CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = get_flash_bank_by_num(0, &bank); + if (ERROR_OK != retval) + return retval; + + msp432_bank = bank->driver_priv; + + if (MSP432E4 == msp432_bank->family_type) { + LOG_WARNING("msp432: MSP432E4 does not have a BSL region"); + return ERROR_OK; + } + + if (1 == CMD_ARGC) { + if (0 == strcmp(CMD_ARGV[0], "lock")) + msp432_bank->unlock_bsl = false; + else if (0 == strcmp(CMD_ARGV[0], "unlock")) + msp432_bank->unlock_bsl = true; + else + return ERROR_COMMAND_SYNTAX_ERROR; + } + + LOG_INFO("msp432: BSL flash region is currently %slocked", + msp432_bank->unlock_bsl ? "un" : ""); + + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(msp432_flash_bank_command) +{ + struct msp432_bank *msp432_bank; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + msp432_bank = malloc(sizeof(struct msp432_bank)); + if (NULL == msp432_bank) + return ERROR_FAIL; + + /* Initialize private flash information */ + msp432_bank->device_id = 0; + msp432_bank->hardware_rev = 0; + msp432_bank->family_type = MSP432_NO_FAMILY; + msp432_bank->device_type = MSP432_NO_TYPE; + msp432_bank->sector_length = 0x1000; + msp432_bank->probed[0] = false; + msp432_bank->probed[1] = false; + msp432_bank->unlock_bsl = false; + msp432_bank->working_area = NULL; + + /* Finish initialization of bank 0 (main flash) */ + bank->driver_priv = msp432_bank; + bank->next = NULL; + + return ERROR_OK; +} + +static int msp432_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + struct msp432_bank *msp432_bank = bank->driver_priv; + struct msp432_algo_params algo_params; + + int retval; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Do a mass erase if user requested all sectors of main flash */ + if ((0 == bank->bank_number) && (first == 0) && + (last == (bank->num_sectors - 1))) { + /* Request mass erase of main flash */ + return msp432_mass_erase(bank, false); + } + + retval = msp432_init(bank); + if (ERROR_OK != retval) + return retval; + + /* Initialize algorithm parameters to default values */ + msp432_init_params(&algo_params); + + /* Adjust params if this is the info bank */ + if (1 == bank->bank_number) { + buf_set_u32(algo_params.erase_param, 0, 32, FLASH_ERASE_INFO); + /* And flag if BSL is unlocked */ + if (msp432_bank->unlock_bsl) + buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL); + } + + /* Erase requested sectors one by one */ + for (int i = first; i <= last; i++) { + + /* Skip TVL (read-only) sector of the info bank */ + if (1 == bank->bank_number && 1 == i) + continue; + + /* Skip BSL sectors of info bank if locked */ + if (1 == bank->bank_number && (2 == i || 3 == i) && + !msp432_bank->unlock_bsl) + continue; + + /* Convert sector number to starting address of sector */ + buf_set_u32(algo_params.address, 0, 32, bank->base + + (i * msp432_bank->sector_length)); + + /* Issue the sector erase command to the flash helper algorithm */ + retval = msp432_exec_cmd(target, &algo_params, FLASH_SECTOR_ERASE); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + + retval = msp432_wait_return_code(target); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + } + + retval = msp432_quit(bank); + if (ERROR_OK != retval) + return retval; + + return retval; +} + +static int msp432_protect(struct flash_bank *bank, int set, int first, + int last) +{ + return ERROR_OK; +} + +static int msp432_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct msp432_bank *msp432_bank = bank->driver_priv; + struct msp432_algo_params algo_params; + uint32_t size; + uint32_t data_ready = BUFFER_DATA_READY; + long long start_ms; + long long elapsed_ms; + + int retval; + + if (TARGET_HALTED != target->state) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* + * Block attempts to write to read-only sectors of flash + * The TVL region in sector 1 of the info flash is always read-only + * The BSL region in sectors 2 and 3 of the info flash may be unlocked + * The helper algorithm will hang on attempts to write to TVL + */ + if (1 == bank->bank_number) { + /* Set read-only start to TVL sector */ + uint32_t start = 0x1000; + /* Set read-only end after BSL region if locked */ + uint32_t end = (msp432_bank->unlock_bsl) ? 0x2000 : 0x4000; + /* Check if request includes anything in read-only sectors */ + if ((offset + count - 1) < start || offset >= end) { + /* The request includes no bytes in read-only sectors */ + /* Fall out and process the request normally */ + } else { + /* Send a request for anything before read-only sectors */ + if (offset < start) { + uint32_t start_count = MIN(start - offset, count); + retval = msp432_write(bank, buffer, offset, start_count); + if (ERROR_OK != retval) + return retval; + } + /* Send a request for anything after read-only sectors */ + if ((offset + count - 1) >= end) { + uint32_t skip = end - offset; + count -= skip; + offset += skip; + buffer += skip; + return msp432_write(bank, buffer, offset, count); + } else { + /* Request is entirely in read-only sectors */ + return ERROR_OK; + } + } + } + + retval = msp432_init(bank); + if (ERROR_OK != retval) + return retval; + + /* Initialize algorithm parameters to default values */ + msp432_init_params(&algo_params); + + /* Set up parameters for requested flash write operation */ + buf_set_u32(algo_params.address, 0, 32, bank->base + offset); + buf_set_u32(algo_params.length, 0, 32, count); + + /* Check if this is the info bank */ + if (1 == bank->bank_number) { + /* And flag if BSL is unlocked */ + if (msp432_bank->unlock_bsl) + buf_set_u32(algo_params.unlock_bsl, 0, 32, FLASH_UNLOCK_BSL); + } + + /* Set up flash helper algorithm to continuous flash mode */ + retval = msp432_exec_cmd(target, &algo_params, FLASH_CONTINUOUS); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + + /* Write requested data, one buffer at a time */ + start_ms = timeval_ms(); + while (count > 0) { + + if (count > ALGO_BUFFER_SIZE) + size = ALGO_BUFFER_SIZE; + else + size = count; + + /* Put next block of data to flash into buffer */ + retval = target_write_buffer(target, ALGO_BUFFER1_ADDR, size, buffer); + if (ERROR_OK != retval) { + LOG_ERROR("Unable to write data to target memory"); + (void)msp432_quit(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Signal the flash helper algorithm that data is ready to flash */ + retval = target_write_buffer(target, ALGO_BUFFER1_STATUS_ADDR, + sizeof(data_ready), (uint8_t *)&data_ready); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + retval = msp432_wait_inactive(target, 1); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + + count -= size; + buffer += size; + + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + } + + /* Confirm that the flash helper algorithm is finished */ + retval = msp432_wait_return_code(target); + if (ERROR_OK != retval) { + (void)msp432_quit(bank); + return retval; + } + + retval = msp432_quit(bank); + if (ERROR_OK != retval) + return retval; + + return retval; +} + +static int msp432_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct msp432_bank *msp432_bank = bank->driver_priv; + + char *name; + + uint32_t device_id; + uint32_t hardware_rev; + + uint32_t base; + uint32_t sector_length; + uint32_t size; + int num_sectors; + int bank_id; + + int retval; + + bank_id = bank->bank_number; + + /* Read the flash size register to determine this is a P4 or not */ + /* MSP432P4s will return the size of flash. MSP432E4s will return zero */ + retval = target_read_u32(target, P4_FLASH_MAIN_SIZE_REG, &size); + if (ERROR_OK != retval) + return retval; + + if (0 == size) { + /* This is likely an MSP432E4 */ + msp432_bank->family_type = MSP432E4; + + retval = target_read_u32(target, E4_DID0_REG, &device_id); + if (ERROR_OK != retval) + return retval; + + msp432_bank->device_id = device_id; + + retval = target_read_u32(target, E4_DID1_REG, &hardware_rev); + if (ERROR_OK != retval) + return retval; + + msp432_bank->hardware_rev = hardware_rev; + } else { + /* This is likely an MSP432P4 */ + msp432_bank->family_type = MSP432P4; + + retval = target_read_u32(target, P4_DEVICE_ID_REG, &device_id); + if (ERROR_OK != retval) + return retval; + + msp432_bank->device_id = device_id & 0xFFFF; + + retval = target_read_u32(target, P4_HARDWARE_REV_REG, &hardware_rev); + if (ERROR_OK != retval) + return retval; + + msp432_bank->hardware_rev = hardware_rev & 0xFF; + } + + msp432_bank->device_type = msp432_device_type(msp432_bank->family_type, + msp432_bank->device_id, msp432_bank->hardware_rev); + + /* If not already allocated, create the info bank for MSP432P4 */ + /* We could not determine it was needed until device was probed */ + if (MSP432P4 == msp432_bank->family_type) { + /* If we've been given bank 1, then this was already done */ + if (0 == bank_id) { + /* And only allocate it if it doesn't exist yet */ + if (NULL == bank->next) { + struct flash_bank *info_bank; + info_bank = malloc(sizeof(struct flash_bank)); + if (NULL == info_bank) + return ERROR_FAIL; + + name = malloc(strlen(bank->name)+1); + if (NULL == name) { + free(info_bank); + return ERROR_FAIL; + } + strcpy(name, bank->name); + + /* Initialize bank 1 (info region) */ + info_bank->name = name; + info_bank->target = bank->target; + info_bank->driver = bank->driver; + info_bank->driver_priv = bank->driver_priv; + info_bank->bank_number = 1; + info_bank->base = 0x00200000; + info_bank->size = 0; + info_bank->chip_width = 0; + info_bank->bus_width = 0; + info_bank->erased_value = 0xff; + info_bank->default_padded_value = 0xff; + info_bank->write_start_alignment = 0; + info_bank->write_end_alignment = 0; + info_bank->minimal_write_gap = FLASH_WRITE_GAP_SECTOR; + info_bank->num_sectors = 0; + info_bank->sectors = NULL; + info_bank->num_prot_blocks = 0; + info_bank->prot_blocks = NULL; + info_bank->next = NULL; + + /* Enable the new bank */ + bank->next = info_bank; + } + } + } + + if (MSP432P4 == msp432_bank->family_type) { + /* Set up MSP432P4 specific flash parameters */ + if (0 == bank_id) { + retval = target_read_u32(target, P4_FLASH_MAIN_SIZE_REG, &size); + if (ERROR_OK != retval) + return retval; + + base = P4_FLASH_MAIN_BASE; + sector_length = P4_SECTOR_LENGTH; + num_sectors = size / sector_length; + } else if (1 == bank_id) { + if (msp432_bank->device_type == MSP432P411X || + msp432_bank->device_type == MSP432P411X_GUESS) { + /* MSP432P411x has an info size register, use that for size */ + retval = target_read_u32(target, P4_FLASH_INFO_SIZE_REG, &size); + if (ERROR_OK != retval) + return retval; + } else { + /* All other MSP432P401x devices have fixed info region size */ + size = 0x4000; /* 16 KB info region */ + } + base = P4_FLASH_INFO_BASE; + sector_length = P4_SECTOR_LENGTH; + num_sectors = size / sector_length; + } else { + /* Invalid bank number somehow */ + return ERROR_FAIL; + } + } else { + /* Set up MSP432E4 specific flash parameters */ + base = E4_FLASH_BASE; + size = E4_FLASH_SIZE; + sector_length = E4_SECTOR_LENGTH; + num_sectors = size / sector_length; + } + + if (NULL != bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + if (NULL == bank->sectors) + return ERROR_FAIL; + + bank->base = base; + bank->size = size; + bank->write_start_alignment = 0; + bank->write_end_alignment = 0; + bank->num_sectors = num_sectors; + msp432_bank->sector_length = sector_length; + + for (int i = 0; i < num_sectors; i++) { + bank->sectors[i].offset = i * sector_length; + bank->sectors[i].size = sector_length; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + /* We've successfully determined the stats on this flash bank */ + msp432_bank->probed[bank_id] = true; + + /* If we fall through to here, then all went well */ + + return ERROR_OK; +} + +static int msp432_auto_probe(struct flash_bank *bank) +{ + struct msp432_bank *msp432_bank = bank->driver_priv; + + int retval = ERROR_OK; + + if (bank->bank_number < 0 || bank->bank_number > 1) { + /* Invalid bank number somehow */ + return ERROR_FAIL; + } + + if (!msp432_bank->probed[bank->bank_number]) + retval = msp432_probe(bank); + + return retval; +} + +static int msp432_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int msp432_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct msp432_bank *msp432_bank = bank->driver_priv; + int printed = 0; + + switch (msp432_bank->device_type) { + case MSP432P401X_DEPR: + if (0xFFFF == msp432_bank->device_id) { + /* Very early pre-production silicon currently deprecated */ + printed = snprintf(buf, buf_size, + "MSP432P401x pre-production device (deprecated silicon)\n" + SUPPORT_MESSAGE); + } else { + /* Revision A or B silicon, also deprecated */ + printed = snprintf(buf, buf_size, + "MSP432P401x Device Rev %c (deprecated silicon)\n" + SUPPORT_MESSAGE, (char)msp432_bank->hardware_rev); + } + break; + case MSP432P401X: + printed = snprintf(buf, buf_size, + "MSP432P401x Device Rev %c\n", + (char)msp432_bank->hardware_rev); + break; + case MSP432P411X: + printed = snprintf(buf, buf_size, + "MSP432P411x Device Rev %c\n", + (char)msp432_bank->hardware_rev); + break; + case MSP432E401Y: + printed = snprintf(buf, buf_size, "MSP432E401Y Device\n"); + break; + case MSP432E411Y: + printed = snprintf(buf, buf_size, "MSP432E411Y Device\n"); + break; + case MSP432E4X_GUESS: + printed = snprintf(buf, buf_size, + "Unrecognized MSP432E4 DID0 and DID1 IDs (%08X, %08X)", + msp432_bank->device_id, msp432_bank->hardware_rev); + break; + case MSP432P401X_GUESS: + case MSP432P411X_GUESS: + default: + printed = snprintf(buf, buf_size, + "Unrecognized MSP432P4 Device ID and Hardware Rev (%04X, %02X)", + msp432_bank->device_id, msp432_bank->hardware_rev); + break; + } + + buf_size -= printed; + + if (0 > buf_size) + return ERROR_BUF_TOO_SMALL; + + return ERROR_OK; +} + +static void msp432_flash_free_driver_priv(struct flash_bank *bank) +{ + /* A single private struct is shared between main and info banks */ + /* Only free it on the call for main bank (#0) */ + if ((0 == bank->bank_number) && (NULL != bank->driver_priv)) + free(bank->driver_priv); + /* Forget about the private struct on both main and info banks */ + bank->driver_priv = NULL; +} + +static const struct command_registration msp432_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = msp432_mass_erase_command, + .mode = COMMAND_EXEC, + .help = "Erase entire flash memory on device.", + .usage = "['main' | 'all']", + }, + { + .name = "bsl", + .handler = msp432_bsl_command, + .mode = COMMAND_EXEC, + .help = "Allow BSL to be erased or written by flash commands.", + .usage = "['unlock' | 'lock']", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration msp432_command_handlers[] = { + { + .name = "msp432", + .mode = COMMAND_EXEC, + .help = "MSP432 flash command group", + .usage = "", + .chain = msp432_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver msp432_flash = { + .name = "msp432", + .commands = msp432_command_handlers, + .flash_bank_command = msp432_flash_bank_command, + .erase = msp432_erase, + .protect = msp432_protect, + .write = msp432_write, + .read = default_flash_read, + .probe = msp432_probe, + .auto_probe = msp432_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = msp432_protect_check, + .info = msp432_info, + .free_driver_priv = msp432_flash_free_driver_priv, +}; diff --git a/src/flash/nor/msp432.h b/src/flash/nor/msp432.h new file mode 100644 index 0000000..ffefa8f --- /dev/null +++ b/src/flash/nor/msp432.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2018 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_FLASH_NOR_MSP432_H +#define OPENOCD_FLASH_NOR_MSP432_H + +/* MSP432 family types */ +#define MSP432_NO_FAMILY 0 /* Family type not determined yet */ +#define MSP432E4 1 /* MSP432E4 family of devices */ +#define MSP432P4 2 /* MSP432P4 family of devices */ + +/* MSP432 device types */ +#define MSP432_NO_TYPE 0 /* Device type not determined yet */ +#define MSP432P401X_DEPR 1 /* Early MSP432P401x offerings, now deprecated */ +#define MSP432P401X 2 /* MSP432P401x device, revision C or higher */ +#define MSP432P411X 3 /* MSP432P411x device, revision A or higher */ +#define MSP432P401X_GUESS 4 /* Assuming it's an MSP432P401x device */ +#define MSP432P411X_GUESS 5 /* Assuming it's an MSP432P411x device */ +#define MSP432E401Y 6 /* MSP432E401Y device */ +#define MSP432E411Y 7 /* MSP432E401Y device */ +#define MSP432E4X_GUESS 8 /* Assuming it's an MSP432E4x device */ + +/* MSP432P4 flash parameters */ +#define P4_FLASH_MAIN_BASE 0x00000000 +#define P4_FLASH_INFO_BASE 0x00200000 +#define P4_SECTOR_LENGTH 0x1000 +#define P4_ALGO_ENTRY_ADDR 0x01000110 + +/* MSP432E4 flash paramters */ +#define E4_FLASH_BASE 0x00000000 +#define E4_FLASH_SIZE 0x100000 +#define E4_SECTOR_LENGTH 0x4000 +#define E4_ALGO_ENTRY_ADDR 0x20000110 + +/* Flash helper algorithm key addresses */ +#define ALGO_BASE_ADDR 0x20000000 +#define ALGO_BUFFER1_ADDR 0x20002000 +#define ALGO_BUFFER2_ADDR 0x20003000 +#define ALGO_PARAMS_BASE_ADDR 0x20000150 +#define ALGO_FLASH_COMMAND_ADDR 0x20000150 +#define ALGO_RETURN_CODE_ADDR 0x20000154 +#define ALGO_FLASH_DEST_ADDR 0x2000015c +#define ALGO_FLASH_LENGTH_ADDR 0x20000160 +#define ALGO_BUFFER1_STATUS_ADDR 0x20000164 +#define ALGO_BUFFER2_STATUS_ADDR 0x20000168 +#define ALGO_ERASE_PARAM_ADDR 0x2000016c +#define ALGO_UNLOCK_BSL_ADDR 0x20000170 +#define ALGO_STACK_POINTER_ADDR 0x20002000 + +/* Flash helper algorithm key sizes */ +#define ALGO_BUFFER_SIZE 0x1000 +#define ALGO_WORKING_SIZE (ALGO_BUFFER2_ADDR + 0x1000 - ALGO_BASE_ADDR) + +/* Flash helper algorithm flash commands */ +#define FLASH_NO_COMMAND 0 +#define FLASH_MASS_ERASE 1 +#define FLASH_SECTOR_ERASE 2 +#define FLASH_PROGRAM 4 +#define FLASH_INIT 8 +#define FLASH_EXIT 16 +#define FLASH_CONTINUOUS 32 + +/* Flash helper algorithm return codes */ +#define FLASH_BUSY 0x00000001 +#define FLASH_SUCCESS 0x00000ACE +#define FLASH_ERROR 0x0000DEAD +#define FLASH_TIMEOUT_ERROR 0xDEAD0000 +#define FLASH_VERIFY_ERROR 0xDEADDEAD +#define FLASH_WRONG_COMMAND 0x00000BAD +#define FLASH_POWER_ERROR 0x00DEAD00 + +/* Flash helper algorithm buffer status values */ +#define BUFFER_INACTIVE 0x00 +#define BUFFER_ACTIVE 0x01 +#define BUFFER_DATA_READY 0x10 + +/* Flash helper algorithm erase parameters */ +#define FLASH_ERASE_MAIN 0x01 +#define FLASH_ERASE_INFO 0x02 + +/* Flash helper algorithm lock/unlock BSL options */ +#define FLASH_LOCK_BSL 0x00 +#define FLASH_UNLOCK_BSL 0x0b + +/* Flash helper algorithm parameter block struct */ +struct msp432_algo_params { + uint8_t flash_command[4]; + uint8_t return_code[4]; + uint8_t _reserved0[4]; + uint8_t address[4]; + uint8_t length[4]; + uint8_t buffer1_status[4]; + uint8_t buffer2_status[4]; + uint8_t erase_param[4]; + uint8_t unlock_bsl[4]; +}; + +/* Flash helper algorithm for MSP432P401x targets */ +const uint8_t msp432p401x_algo[] = { +#include "../../../contrib/loaders/flash/msp432/msp432p401x_algo.inc" +}; + +/* Flash helper algorithm for MSP432P411x targets */ +const uint8_t msp432p411x_algo[] = { +#include "../../../contrib/loaders/flash/msp432/msp432p411x_algo.inc" +}; + +/* Flash helper algorithm for MSP432E4x targets */ +const uint8_t msp432e4x_algo[] = { +#include "../../../contrib/loaders/flash/msp432/msp432e4x_algo.inc" +}; + +#endif /* OPENOCD_FLASH_NOR_MSP432_H */ diff --git a/src/flash/nor/niietcm4.c b/src/flash/nor/niietcm4.c index 4a849fd..fd7d519 100644 --- a/src/flash/nor/niietcm4.c +++ b/src/flash/nor/niietcm4.c @@ -1741,4 +1741,5 @@ struct flash_driver niietcm4_flash = { .erase_check = default_flash_blank_check, .protect_check = niietcm4_protect_check, .info = get_niietcm4_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c index 4fa62e3..16459c7 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -108,6 +108,7 @@ enum nrf5_nvmc_config_bits { struct nrf5_info { uint32_t code_page_size; + uint32_t refcount; struct { bool probed; @@ -155,6 +156,11 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { NRF5_DEVICE_DEF(0x0020, "51822", "CEAA", "BA", 256), NRF5_DEVICE_DEF(0x002F, "51822", "CEAA", "B0", 256), + /* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards + with built-in jlink seem to use engineering samples not listed + in the nRF51 Series Compatibility Matrix V1.0. */ + NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256), + /* nRF51822 Devices (IC rev 2). */ NRF5_DEVICE_DEF(0x002A, "51822", "QFAA", "FA0", 256), NRF5_DEVICE_DEF(0x0044, "51822", "QFAA", "GC0", 256), @@ -175,6 +181,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { NRF5_DEVICE_DEF(0x007D, "51822", "CDAB", "A0", 128), NRF5_DEVICE_DEF(0x0079, "51822", "CEAA", "E0", 256), NRF5_DEVICE_DEF(0x0087, "51822", "CFAC", "A0", 256), + NRF5_DEVICE_DEF(0x008F, "51822", "QFAA", "H1", 256), /* nRF51422 Devices (IC rev 1). */ NRF5_DEVICE_DEF(0x001E, "51422", "QFAA", "CA", 256), @@ -198,11 +205,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { /* nRF52832 Devices */ NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512), - - /* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards - with built-in jlink seem to use engineering samples not listed - in the nRF51 Series Compatibility Matrix V1.0. */ - NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256), + NRF5_DEVICE_DEF(0x0139, "52832", "QFAA", "E0", 512), }; static int nrf5_bank_is_probed(struct flash_bank *bank) @@ -530,7 +533,6 @@ static int nrf5_probe(struct flash_bank *bank) bank->sectors[0].size = bank->size; bank->sectors[0].offset = 0; - /* mark as unknown */ bank->sectors[0].is_erased = 0; bank->sectors[0].is_protected = 0; @@ -552,17 +554,6 @@ static int nrf5_auto_probe(struct flash_bank *bank) return nrf5_probe(bank); } -static struct flash_sector *nrf5_find_sector_by_address(struct flash_bank *bank, uint32_t address) -{ - struct nrf5_info *chip = bank->driver_priv; - - for (int i = 0; i < bank->num_sectors; i++) - if (bank->sectors[i].offset <= address && - address < (bank->sectors[i].offset + chip->code_page_size)) - return &bank->sectors[i]; - return NULL; -} - static int nrf5_erase_all(struct nrf5_info *chip) { LOG_DEBUG("Erasing all non-volatile memory"); @@ -614,9 +605,6 @@ static int nrf5_erase_page(struct flash_bank *bank, sector->offset); } - if (res == ERROR_OK) - sector->is_erased = 1; - return res; } @@ -742,48 +730,22 @@ static int nrf5_write_pages(struct flash_bank *bank, uint32_t start, uint32_t en { int res = ERROR_FAIL; struct nrf5_info *chip = bank->driver_priv; - struct flash_sector *sector; - uint32_t offset; assert(start % chip->code_page_size == 0); assert(end % chip->code_page_size == 0); - /* Erase all sectors */ - for (offset = start; offset < end; offset += chip->code_page_size) { - sector = nrf5_find_sector_by_address(bank, offset); - if (!sector) { - LOG_ERROR("Invalid sector @ 0x%08"PRIx32, offset); - return ERROR_FLASH_SECTOR_INVALID; - } - - if (sector->is_protected) { - LOG_ERROR("Can't erase protected sector @ 0x%08"PRIx32, offset); - goto error; - } - - if (sector->is_erased != 1) { /* 1 = erased, 0= not erased, -1 = unknown */ - res = nrf5_erase_page(bank, chip, sector); - if (res != ERROR_OK) { - LOG_ERROR("Failed to erase sector @ 0x%08"PRIx32, sector->offset); - goto error; - } - } - sector->is_erased = 0; - } - res = nrf5_nvmc_write_enable(chip); if (res != ERROR_OK) goto error; res = nrf5_ll_flash_write(chip, start, buffer, (end - start)); if (res != ERROR_OK) - goto set_read_only; + goto error; return nrf5_nvmc_read_only(chip); -set_read_only: - nrf5_nvmc_read_only(chip); error: + nrf5_nvmc_read_only(chip); LOG_ERROR("Failed to write to nrf5 flash"); return res; } @@ -875,11 +837,9 @@ static int nrf5_uicr_flash_write(struct flash_bank *bank, if (res != ERROR_OK) return res; - if (sector->is_erased != 1) { - res = nrf5_erase_page(bank, chip, sector); - if (res != ERROR_OK) - return res; - } + res = nrf5_erase_page(bank, chip, sector); + if (res != ERROR_OK) + return res; res = nrf5_nvmc_write_enable(chip); if (res != ERROR_OK) @@ -910,6 +870,18 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer, return chip->bank[bank->bank_number].write(bank, chip, buffer, offset, count); } +static void nrf5_free_driver_priv(struct flash_bank *bank) +{ + struct nrf5_info *chip = bank->driver_priv; + if (chip == NULL) + return; + + chip->refcount--; + if (chip->refcount == 0) { + free(chip); + bank->driver_priv = NULL; + } +} FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command) { @@ -945,6 +917,7 @@ FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command) break; } + chip->refcount++; chip->bank[bank->bank_number].probed = false; bank->driver_priv = chip; @@ -991,9 +964,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command) return res; } - for (int i = 0; i < bank->num_sectors; i++) - bank->sectors[i].is_erased = 1; - res = nrf5_protect_check(bank); if (res != ERROR_OK) { LOG_ERROR("Failed to check chip's write protection"); @@ -1004,8 +974,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command) if (res != ERROR_OK) return res; - bank->sectors[0].is_erased = 1; - return ERROR_OK; } @@ -1174,6 +1142,7 @@ struct flash_driver nrf5_flash = { .auto_probe = nrf5_auto_probe, .erase_check = default_flash_blank_check, .protect_check = nrf5_protect_check, + .free_driver_priv = nrf5_free_driver_priv, }; /* We need to retain the flash-driver name as well as the commands @@ -1191,4 +1160,5 @@ struct flash_driver nrf51_flash = { .auto_probe = nrf5_auto_probe, .erase_check = default_flash_blank_check, .protect_check = nrf5_protect_check, + .free_driver_priv = nrf5_free_driver_priv, }; diff --git a/src/flash/nor/numicro.c b/src/flash/nor/numicro.c index 992baa5..4d951f0 100644 --- a/src/flash/nor/numicro.c +++ b/src/flash/nor/numicro.c @@ -1880,4 +1880,5 @@ struct flash_driver numicro_flash = { .auto_probe = numicro_auto_probe, .erase_check = default_flash_blank_check, .protect_check = numicro_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/ocl.c b/src/flash/nor/ocl.c index 4ae5652..895c4af 100644 --- a/src/flash/nor/ocl.c +++ b/src/flash/nor/ocl.c @@ -340,4 +340,5 @@ struct flash_driver ocl_flash = { .erase_check = ocl_erase_check, .protect_check = ocl_protect_check, .auto_probe = ocl_auto_probe, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/pic32mx.c b/src/flash/nor/pic32mx.c index 1f148fd..e3b8028 100644 --- a/src/flash/nor/pic32mx.c +++ b/src/flash/nor/pic32mx.c @@ -980,4 +980,5 @@ struct flash_driver pic32mx_flash = { .erase_check = default_flash_blank_check, .protect_check = pic32mx_protect_check, .info = pic32mx_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/psoc4.c b/src/flash/nor/psoc4.c index c7c8573..47d60de 100644 --- a/src/flash/nor/psoc4.c +++ b/src/flash/nor/psoc4.c @@ -41,24 +41,67 @@ Document Number: 001-87197 Rev. *B Revised August 29, 2013 PSoC 4100/4200 Family PSoC(R) 4 Architecture TRM - Document No. 001-85634 Rev. *C March 25, 2014 + Document No. 001-85634 Rev. *E June 28, 2016 PSoC(R) 4 Registers TRM Spec. Document No. 001-85847 Rev. *A June 25, 2013 + PSoC 4000 Family PSoC(R) 4 Technical Reference Manual + Document No. 001-89309 Rev. *B May 9, 2016 + + PSoC 41XX_BLE/42XX_BLE Family PSoC 4 BLE Architecture TRM + Document No. 001-92738 Rev. *C February 12, 2016 + + PSoC 4200L Family PSoC 4 Architecture TRM + Document No. 001-97952 Rev. *A December 15, 2015 + + PSoC 4200L Family PSoC 4 Registers TRM + Document No. 001-98126 Rev. *A December 16, 2015 + + PSoC 4100M/4200M Family PSoC 4 Architecture TRM + Document No. 001-95223 Rev. *B July 29, 2015 + + PSoC 4100S Family PSoC 4 Architecture TRM + Document No. 002-10621 Rev. *A July 29, 2016 + + PSoC 4100S Family PSoC 4 Registers TRM + Document No. 002-10523 Rev. *A July 20, 2016 + + PSoC Analog Coprocessor Architecture TRM + Document No. 002-10404 Rev. ** December 18, 2015 + + CY8C4Axx PSoC Analog Coprocessor Registers TRM + Document No. 002-10405 Rev. ** December 18, 2015 + CY8C41xx, CY8C42xx Programming Specifications Document No. 001-81799 Rev. *C March 4, 2014 + + CYBL10x6x, CY8C4127_BL, CY8C4247_BL Programming Specifications + Document No. 001-91508 Rev. *B September 22, 2014 + + http://dmitry.gr/index.php?r=05.Projects&proj=24.%20PSoC4%20confidential */ /* register locations */ -#define PSOC4_CPUSS_SYSREQ 0x40000004 -#define PSOC4_CPUSS_SYSARG 0x40000008 -#define PSOC4_TEST_MODE 0x40030014 -#define PSOC4_SPCIF_GEOMETRY 0x400E0000 +#define PSOC4_SFLASH_MACRO0 0x0FFFF000 + +#define PSOC4_CPUSS_SYSREQ_LEGACY 0x40000004 +#define PSOC4_CPUSS_SYSARG_LEGACY 0x40000008 +#define PSOC4_SPCIF_GEOMETRY_LEGACY 0x400E0000 + +#define PSOC4_CPUSS_SYSREQ_NEW 0x40100004 +#define PSOC4_CPUSS_SYSARG_NEW 0x40100008 +#define PSOC4_SPCIF_GEOMETRY_NEW 0x40110000 + +#define PSOC4_TEST_MODE 0x40030014 + +#define PSOC4_ROMTABLE_PID0 0xF0000FE0 -#define PSOC4_SFLASH_MACRO 0x0ffff000 /* constants */ +#define PSOC4_SFLASH_MACRO_SIZE 0x800 +#define PSOC4_ROWS_PER_MACRO 512 + #define PSOC4_SROM_KEY1 0xb6 #define PSOC4_SROM_KEY2 0xd3 #define PSOC4_SROM_SYSREQ_BIT (1<<31) @@ -66,6 +109,10 @@ #define PSOC4_SROM_PRIVILEGED_BIT (1<<28) #define PSOC4_SROM_STATUS_SUCCEEDED 0xa0000000 #define PSOC4_SROM_STATUS_FAILED 0xf0000000 +#define PSOC4_SROM_STATUS_MASK 0xf0000000 + +/* not documented in any TRM */ +#define PSOC4_SROM_ERR_IMO_NOT_IMPLEM 0xf0000013 #define PSOC4_CMD_GET_SILICON_ID 0 #define PSOC4_CMD_LOAD_LATCH 4 @@ -74,76 +121,60 @@ #define PSOC4_CMD_ERASE_ALL 0xa #define PSOC4_CMD_CHECKSUM 0xb #define PSOC4_CMD_WRITE_PROTECTION 0xd +#define PSOC4_CMD_SET_IMO48 0x15 +#define PSOC4_CMD_WRITE_SFLASH_ROW 0x18 #define PSOC4_CHIP_PROT_VIRGIN 0x0 #define PSOC4_CHIP_PROT_OPEN 0x1 #define PSOC4_CHIP_PROT_PROTECTED 0x2 #define PSOC4_CHIP_PROT_KILL 0x4 +#define PSOC4_ROMTABLE_DESIGNER_CHECK 0xb4 + +#define PSOC4_FAMILY_FLAG_LEGACY 1 -struct psoc4_chip_details { +struct psoc4_chip_family { uint16_t id; - const char *type; - const char *package; - uint32_t flash_size_in_kb; + const char *name; + uint32_t flags; }; -/* list of PSoC 4 chips - * flash_size_in_kb is not necessary as it can be decoded from SPCIF_GEOMETRY - */ -const struct psoc4_chip_details psoc4_devices[] = { - /* 4200 series */ - { 0x04A6, "CY8C4245PVI-482", "SSOP-28", .flash_size_in_kb = 32 }, - { 0x04B6, "CY8C4245LQI-483", "QFN-40", .flash_size_in_kb = 32 }, - { 0x04C8, "CY8C4245AXI-483", "TQFP-44", .flash_size_in_kb = 32 }, - { 0x04FB, "CY8C4245AXI-473", "TQFP-44", .flash_size_in_kb = 32 }, - { 0x04F0, "CY8C4244PVI-432", "SSOP-28", .flash_size_in_kb = 16 }, - { 0x04F1, "CY8C4244PVI-442", "SSOP-28", .flash_size_in_kb = 16 }, - { 0x04F6, "CY8C4244LQI-443", "QFN-40", .flash_size_in_kb = 16 }, - { 0x04FA, "CY8C4244AXI-443", "TQFP-44", .flash_size_in_kb = 16 }, - - /* 4100 series */ - { 0x0410, "CY8C4124PVI-432", "SSOP-28", .flash_size_in_kb = 16 }, - { 0x0411, "CY8C4124PVI-442", "SSOP-28", .flash_size_in_kb = 16 }, - { 0x041C, "CY8C4124LQI-443", "QFN-40", .flash_size_in_kb = 16 }, - { 0x041A, "CY8C4124AXI-443", "TQFP-44", .flash_size_in_kb = 16 }, - { 0x041B, "CY8C4125AXI-473", "TQFP-44", .flash_size_in_kb = 32 }, - { 0x0412, "CY8C4125PVI-482", "SSOP-28", .flash_size_in_kb = 32 }, - { 0x0417, "CY8C4125LQI-483", "QFN-40", .flash_size_in_kb = 32 }, - { 0x0416, "CY8C4125AXI-483", "TQFP-44", .flash_size_in_kb = 32 }, - - /* CCG1 series */ - { 0x0490, "CYPD1103-35FNXI", "CSP-35", .flash_size_in_kb = 32 }, - { 0x0489, "CYPD1121-40LQXI", "QFN-40", .flash_size_in_kb = 32 }, - { 0x048A, "CYPD1122-40LQXI", "QFN-40", .flash_size_in_kb = 32 }, - { 0x0491, "CYPD1131-35FNXI", "CSP-35", .flash_size_in_kb = 32 }, - { 0x0498, "CYPD1132-16SXI", "SOIC-16", .flash_size_in_kb = 32 }, - { 0x0481, "CYPD1134-28PVXI", "SSOP-28", .flash_size_in_kb = 32 }, - { 0x048B, "CYPD1134-40LQXI", "QFN-40", .flash_size_in_kb = 32 }, +const struct psoc4_chip_family psoc4_families[] = { + { 0x93, "PSoC4100/4200", .flags = PSOC4_FAMILY_FLAG_LEGACY }, + { 0x9A, "PSoC4000", .flags = 0 }, + { 0x9E, "PSoC/PRoC BLE (119E)", .flags = 0 }, + { 0xA0, "PSoC4200L", .flags = 0 }, + { 0xA1, "PSoC4100M/4200M", .flags = 0 }, + { 0xA3, "PSoC/PRoC BLE (11A3)", .flags = 0 }, + { 0xA9, "PSoC4000S", .flags = 0 }, + { 0xAA, "PSoC/PRoC BLE (11AA)", .flags = 0 }, + { 0xAB, "PSoC4100S", .flags = 0 }, + { 0xAC, "PSoC Analog Coprocessor", .flags = 0 }, + { 0, "Unknown", .flags = 0 } }; struct psoc4_flash_bank { uint32_t row_size; uint32_t user_bank_size; - int probed; - uint32_t silicon_id; - uint8_t chip_protection; + int num_macros; + bool probed; uint8_t cmd_program_row; + uint16_t family_id; + bool legacy_family; + uint32_t cpuss_sysreq_addr; + uint32_t cpuss_sysarg_addr; + uint32_t spcif_geometry_addr; }; -static const struct psoc4_chip_details *psoc4_details_by_id(uint32_t silicon_id) +static const struct psoc4_chip_family *psoc4_family_by_id(uint16_t family_id) { - const struct psoc4_chip_details *p = psoc4_devices; - unsigned int i; - uint16_t id = silicon_id >> 16; /* ignore die revision */ - for (i = 0; i < sizeof(psoc4_devices)/sizeof(psoc4_devices[0]); i++, p++) { - if (p->id == id) - return p; - } - LOG_DEBUG("Unknown PSoC 4 device silicon id 0x%08" PRIx32 ".", silicon_id); - return NULL; + const struct psoc4_chip_family *p = psoc4_families; + while (p->id && p->id != family_id) + p++; + + return p; } static const char *psoc4_decode_chip_protection(uint8_t protection) @@ -176,7 +207,9 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command) psoc4_info = calloc(1, sizeof(struct psoc4_flash_bank)); bank->driver_priv = psoc4_info; + bank->default_padded_value = bank->erased_value = 0x00; psoc4_info->user_bank_size = bank->size; + psoc4_info->cmd_program_row = PSOC4_CMD_WRITE_ROW; return ERROR_OK; } @@ -189,9 +222,14 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command) * Otherwise address of memory parameter block is set in CPUSS_SYSARG * and the first parameter is written to the first word of parameter block */ -static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, - uint32_t *sysreq_params, uint32_t sysreq_params_size) +static int psoc4_sysreq(struct flash_bank *bank, uint8_t cmd, + uint16_t cmd_param, + uint32_t *sysreq_params, uint32_t sysreq_params_size, + uint32_t *sysarg_out) { + struct target *target = bank->target; + struct psoc4_flash_bank *psoc4_info = bank->driver_priv; + struct working_area *sysreq_wait_algorithm; struct working_area *sysreq_mem; @@ -212,8 +250,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, const int code_words = (sizeof(psoc4_sysreq_wait_code) + 3) / 4; /* stack must be aligned */ - const int stack_size = 196; - /* tested stack sizes on PSoC 4: + const int stack_size = 256; + /* tested stack sizes on PSoC4200: ERASE_ALL 144 PROGRAM_ROW 112 other sysreq 68 @@ -238,6 +276,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, } if (sysreq_params_size) { + LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32 " size %" PRIu32, + cmd, cmd_param, param1, sysreq_params_size); /* Allocate memory for sysreq_params */ retval = target_alloc_working_area(target, sysreq_params_size, &sysreq_mem); if (retval != ERROR_OK) { @@ -250,21 +290,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, } /* Write sysreq_params */ - sysreq_params[0] = param1; + target_buffer_set_u32(target, (uint8_t *)sysreq_params, param1); retval = target_write_buffer(target, sysreq_mem->address, sysreq_params_size, (uint8_t *)sysreq_params); if (retval != ERROR_OK) goto cleanup_mem; /* Set address of sysreq parameters block */ - retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, sysreq_mem->address); + retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, sysreq_mem->address); if (retval != ERROR_OK) goto cleanup_mem; } else { /* Sysreq without memory block of parameters */ + LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32, + cmd, cmd_param, param1); /* Set register parameter */ - retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, param1); + retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, param1); if (retval != ERROR_OK) goto cleanup_mem; } @@ -279,14 +321,14 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, struct armv7m_common *armv7m = target_to_armv7m(target); if (armv7m == NULL) { - /* something is very wrong if armv7m is NULL */ LOG_ERROR("unable to get armv7m target"); + retval = ERROR_FAIL; goto cleanup; } /* Set SROM request */ - retval = target_write_u32(target, PSOC4_CPUSS_SYSREQ, + retval = target_write_u32(target, psoc4_info->cpuss_sysreq_addr, PSOC4_SROM_SYSREQ_BIT | PSOC4_SROM_HMASTER_BIT | cmd); if (retval != ERROR_OK) goto cleanup; @@ -295,9 +337,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param, retval = target_run_algorithm(target, 0, NULL, sizeof(reg_params) / sizeof(*reg_params), reg_params, sysreq_wait_algorithm->address, 0, 1000, &armv7m_info); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { LOG_ERROR("sysreq wait code execution failed"); + goto cleanup; + } + uint32_t sysarg_out_tmp; + retval = target_read_u32(target, psoc4_info->cpuss_sysarg_addr, &sysarg_out_tmp); + if (retval != ERROR_OK) + goto cleanup; + + if (sysarg_out) { + *sysarg_out = sysarg_out_tmp; + /* If result is an error, do not show now, let caller to decide */ + } else if ((sysarg_out_tmp & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) { + LOG_ERROR("sysreq error 0x%" PRIx32, sysarg_out_tmp); + retval = ERROR_FAIL; + } cleanup: destroy_reg_param(®_params[0]); @@ -313,92 +369,162 @@ cleanup_algo: /* helper routine to get silicon ID from a PSoC 4 chip */ -static int psoc4_get_silicon_id(struct target *target, uint32_t *silicon_id, uint8_t *protection) +static int psoc4_get_silicon_id(struct flash_bank *bank, uint32_t *silicon_id, uint16_t *family_id, uint8_t *protection) { - uint32_t params = PSOC4_SROM_KEY1 - | ((PSOC4_SROM_KEY2 + PSOC4_CMD_GET_SILICON_ID) << 8); - uint32_t part0, part1; + struct target *target = bank->target; + struct psoc4_flash_bank *psoc4_info = bank->driver_priv; - int retval = psoc4_sysreq(target, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0); - if (retval != ERROR_OK) - return retval; + uint32_t part0, part1; - retval = target_read_u32(target, PSOC4_CPUSS_SYSARG, &part0); + int retval = psoc4_sysreq(bank, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0, &part0); if (retval != ERROR_OK) return retval; - if (part0 == params) { - LOG_ERROR("sysreq silicon id request not served"); + if ((part0 & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) { + LOG_ERROR("sysreq error 0x%" PRIx32, part0); return ERROR_FAIL; } - retval = target_read_u32(target, PSOC4_CPUSS_SYSREQ, &part1); + retval = target_read_u32(target, psoc4_info->cpuss_sysreq_addr, &part1); if (retval != ERROR_OK) return retval; - uint32_t silicon = ((part0 & 0xffff) << 16) - | (((part0 >> 16) & 0xff) << 8) - | (part1 & 0xff); - uint8_t prot = (part1 >> 12) & 0xff; - + /* build ID as Cypress sw does: + * bit 31..16 silicon ID + * bit 15..8 revision ID (so far 0x11 for all devices) + * bit 7..0 family ID (lowes 8 bits) + */ if (silicon_id) - *silicon_id = silicon; + *silicon_id = ((part0 & 0x0000ffff) << 16) + | ((part0 & 0x00ff0000) >> 8) + | (part1 & 0x000000ff); + + if (family_id) + *family_id = part1 & 0x0fff; + if (protection) - *protection = prot; + *protection = (part1 >> 12) & 0x0f; - LOG_DEBUG("silicon id: 0x%08" PRIx32 "", silicon); - LOG_DEBUG("protection: 0x%02" PRIx8 "", prot); - return retval; + return ERROR_OK; } -static int psoc4_protect_check(struct flash_bank *bank) +static int psoc4_get_family(struct target *target, uint16_t *family_id) +{ + int retval, i; + uint32_t pidbf[3]; + uint8_t pid[3]; + + retval = target_read_memory(target, PSOC4_ROMTABLE_PID0, 4, 3, (uint8_t *)pidbf); + if (retval != ERROR_OK) + return retval; + + for (i = 0; i < 3; i++) { + uint32_t tmp = target_buffer_get_u32(target, (uint8_t *)(pidbf + i)); + if (tmp & 0xffffff00) { + LOG_ERROR("Unexpected data in ROMTABLE"); + return ERROR_FAIL; + } + pid[i] = tmp & 0xff; + } + + uint16_t family = pid[0] | ((pid[1] & 0xf) << 8); + uint32_t designer = ((pid[1] & 0xf0) >> 4) | ((pid[2] & 0xf) << 4); + + if (designer != PSOC4_ROMTABLE_DESIGNER_CHECK) { + LOG_ERROR("ROMTABLE designer is not Cypress"); + return ERROR_FAIL; + } + + *family_id = family; + return ERROR_OK; +} + + +static int psoc4_flash_prepare(struct flash_bank *bank) { struct target *target = bank->target; struct psoc4_flash_bank *psoc4_info = bank->driver_priv; - uint32_t prot_addr = PSOC4_SFLASH_MACRO; - uint32_t protection; - int i, s; - int num_bits; - int retval = ERROR_OK; + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } - num_bits = bank->num_sectors; + uint16_t family_id; + int retval; + + /* get family ID from SROM call */ + retval = psoc4_get_silicon_id(bank, NULL, &family_id, NULL); + if (retval != ERROR_OK) + return retval; - for (i = 0; i < num_bits; i += 32) { - retval = target_read_u32(target, prot_addr, &protection); + /* and check with family ID from ROMTABLE */ + if (family_id != psoc4_info->family_id) { + LOG_ERROR("Family mismatch"); + return ERROR_FAIL; + } + + if (!psoc4_info->legacy_family) { + uint32_t sysreq_status; + retval = psoc4_sysreq(bank, PSOC4_CMD_SET_IMO48, 0, NULL, 0, &sysreq_status); if (retval != ERROR_OK) return retval; - prot_addr += 4; - - for (s = 0; s < 32; s++) { - if (i + s >= num_bits) - break; - bank->sectors[i + s].is_protected = (protection & (1 << s)) ? 1 : 0; + if ((sysreq_status & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) { + /* This undocumented error code is returned probably when + * PSOC4_CMD_SET_IMO48 command is not implemented. + * Can be safely ignored, programming works. + */ + if (sysreq_status == PSOC4_SROM_ERR_IMO_NOT_IMPLEM) + LOG_INFO("PSOC4_CMD_SET_IMO48 is not implemented on this device."); + else { + LOG_ERROR("sysreq error 0x%" PRIx32, sysreq_status); + return ERROR_FAIL; + } } } - retval = psoc4_get_silicon_id(target, NULL, &(psoc4_info->chip_protection)); - return retval; + return ERROR_OK; } -static int psoc4_mass_erase(struct flash_bank *bank) +static int psoc4_protect_check(struct flash_bank *bank) { struct target *target = bank->target; - int i; + struct psoc4_flash_bank *psoc4_info = bank->driver_priv; - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; + uint32_t prot_addr = PSOC4_SFLASH_MACRO0; + int retval; + int s = 0; + int m, i; + uint8_t bf[PSOC4_ROWS_PER_MACRO/8]; + + for (m = 0; m < psoc4_info->num_macros; m++, prot_addr += PSOC4_SFLASH_MACRO_SIZE) { + retval = target_read_memory(target, prot_addr, 4, PSOC4_ROWS_PER_MACRO/32, bf); + if (retval != ERROR_OK) + return retval; + + for (i = 0; i < PSOC4_ROWS_PER_MACRO && s < bank->num_sectors; i++, s++) + bank->sectors[s].is_protected = bf[i/8] & (1 << (i%8)) ? 1 : 0; } + return ERROR_OK; +} + + +static int psoc4_mass_erase(struct flash_bank *bank) +{ + int i; + int retval = psoc4_flash_prepare(bank); + if (retval != ERROR_OK) + return retval; + /* Call "Erase All" system ROM API */ - uint32_t param; - int retval = psoc4_sysreq(target, PSOC4_CMD_ERASE_ALL, + uint32_t param = 0; + retval = psoc4_sysreq(bank, PSOC4_CMD_ERASE_ALL, 0, - ¶m, sizeof(param)); + ¶m, sizeof(param), NULL); if (retval == ERROR_OK) /* set all sectors as erased */ @@ -420,7 +546,7 @@ static int psoc4_erase(struct flash_bank *bank, int first, int last) if ((first == 0) && (last == (bank->num_sectors - 1))) return psoc4_mass_erase(bank); - LOG_ERROR("Only mass erase available"); + LOG_ERROR("Only mass erase available! Consider using 'psoc4 flash_autoerase 0 on'"); return ERROR_FAIL; } @@ -431,56 +557,63 @@ static int psoc4_protect(struct flash_bank *bank, int set, int first, int last) struct target *target = bank->target; struct psoc4_flash_bank *psoc4_info = bank->driver_priv; - if (psoc4_info->probed == 0) + if (!psoc4_info->probed) return ERROR_FAIL; - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } + int retval = psoc4_flash_prepare(bank); + if (retval != ERROR_OK) + return retval; uint32_t *sysrq_buffer = NULL; - int retval; - int num_bits = bank->num_sectors; const int param_sz = 8; - int prot_sz = num_bits / 8; int chip_prot = PSOC4_CHIP_PROT_OPEN; - int flash_macro = 0; /* PSoC 42xx has only macro 0 */ - int i; + int i, m, sect; + int num_bits = bank->num_sectors; + + if (num_bits > PSOC4_ROWS_PER_MACRO) + num_bits = PSOC4_ROWS_PER_MACRO; + + int prot_sz = num_bits / 8; - sysrq_buffer = calloc(1, param_sz + prot_sz); + sysrq_buffer = malloc(param_sz + prot_sz); if (sysrq_buffer == NULL) { LOG_ERROR("no memory for row buffer"); return ERROR_FAIL; } - for (i = first; i < num_bits && i <= last; i++) + for (i = first; i <= last && i < bank->num_sectors; i++) bank->sectors[i].is_protected = set; - uint32_t *p = sysrq_buffer + 2; - for (i = 0; i < num_bits; i++) { - if (bank->sectors[i].is_protected) - p[i / 32] |= 1 << (i % 32); - } + for (m = 0, sect = 0; m < psoc4_info->num_macros; m++) { + uint8_t *p = (uint8_t *)(sysrq_buffer + 2); + memset(p, 0, prot_sz); + for (i = 0; i < num_bits && sect < bank->num_sectors; i++, sect++) { + if (bank->sectors[sect].is_protected) + p[i/8] |= 1 << (i%8); + } - /* Call "Load Latch" system ROM API */ - sysrq_buffer[1] = prot_sz - 1; - retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH, - 0, /* Byte number in latch from what to write */ - sysrq_buffer, param_sz + psoc4_info->row_size); - if (retval != ERROR_OK) - goto cleanup; + /* Call "Load Latch" system ROM API */ + target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1), + prot_sz - 1); + retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH, + 0 /* Byte number in latch from what to write */ + | (m << 8), /* flash macro index */ + sysrq_buffer, param_sz + prot_sz, + NULL); + if (retval != ERROR_OK) + break; - /* Call "Write Protection" system ROM API */ - retval = psoc4_sysreq(target, PSOC4_CMD_WRITE_PROTECTION, - chip_prot | (flash_macro << 8), NULL, 0); -cleanup: - if (retval != ERROR_OK) - psoc4_protect_check(bank); + /* Call "Write Protection" system ROM API */ + retval = psoc4_sysreq(bank, PSOC4_CMD_WRITE_PROTECTION, + chip_prot | (m << 8), NULL, 0, NULL); + if (retval != ERROR_OK) + break; + } if (sysrq_buffer) free(sysrq_buffer); + psoc4_protect_check(bank); return retval; } @@ -516,21 +649,14 @@ COMMAND_HANDLER(psoc4_handle_flash_autoerase_command) static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { - struct psoc4_flash_bank *psoc4_info = bank->driver_priv; struct target *target = bank->target; + struct psoc4_flash_bank *psoc4_info = bank->driver_priv; uint32_t *sysrq_buffer = NULL; - int retval = ERROR_OK; const int param_sz = 8; - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset & 0x1) { - LOG_ERROR("offset 0x%08" PRIx32 " breaks required 2-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } + int retval = psoc4_flash_prepare(bank); + if (retval != ERROR_OK) + return retval; sysrq_buffer = malloc(param_sz + psoc4_info->row_size); if (sysrq_buffer == NULL) { @@ -542,7 +668,7 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t row_num = offset / psoc4_info->row_size; uint32_t row_offset = offset - row_num * psoc4_info->row_size; if (row_offset) - memset(row_buffer, 0, row_offset); + memset(row_buffer, bank->default_padded_value, row_offset); bool save_poll = jtag_poll_get_enabled(); jtag_poll_set_enabled(false); @@ -551,25 +677,31 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t chunk_size = psoc4_info->row_size - row_offset; if (chunk_size > count) { chunk_size = count; - memset(row_buffer + chunk_size, 0, psoc4_info->row_size - chunk_size); + memset(row_buffer + chunk_size, bank->default_padded_value, psoc4_info->row_size - chunk_size); } memcpy(row_buffer + row_offset, buffer, chunk_size); LOG_DEBUG("offset / row: 0x%08" PRIx32 " / %" PRIu32 ", size %" PRIu32 "", offset, row_offset, chunk_size); + uint32_t macro_idx = row_num / PSOC4_ROWS_PER_MACRO; + /* Call "Load Latch" system ROM API */ - sysrq_buffer[1] = psoc4_info->row_size - 1; - retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH, - 0, /* Byte number in latch from what to write */ - sysrq_buffer, param_sz + psoc4_info->row_size); + target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1), + psoc4_info->row_size - 1); + retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH, + 0 /* Byte number in latch from what to write */ + | (macro_idx << 8), + sysrq_buffer, param_sz + psoc4_info->row_size, + NULL); if (retval != ERROR_OK) goto cleanup; /* Call "Program Row" or "Write Row" system ROM API */ uint32_t sysrq_param; - retval = psoc4_sysreq(target, psoc4_info->cmd_program_row, + retval = psoc4_sysreq(bank, psoc4_info->cmd_program_row, row_num & 0xffff, - &sysrq_param, sizeof(sysrq_param)); + &sysrq_param, sizeof(sysrq_param), + NULL); if (retval != ERROR_OK) goto cleanup; @@ -589,84 +721,82 @@ cleanup: } +/* Due to Cypress's method of market segmentation some devices + * have accessible only 1/2, 1/4 or 1/8 of SPCIF described flash */ +static int psoc4_test_flash_wounding(struct target *target, uint32_t flash_size) +{ + int retval, i; + for (i = 3; i >= 1; i--) { + uint32_t addr = flash_size >> i; + uint32_t dummy; + retval = target_read_u32(target, addr, &dummy); + if (retval != ERROR_OK) + return i; + } + return 0; +} + + static int psoc4_probe(struct flash_bank *bank) { struct psoc4_flash_bank *psoc4_info = bank->driver_priv; struct target *target = bank->target; - uint32_t flash_size_in_kb = 0; - uint32_t max_flash_size_in_kb; - uint32_t cpu_id; - uint32_t silicon_id; - uint32_t row_size; - uint32_t base_address = 0x00000000; - uint8_t protection; - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } + int retval; + uint16_t family_id; - psoc4_info->probed = 0; - psoc4_info->cmd_program_row = PSOC4_CMD_PROGRAM_ROW; + psoc4_info->probed = false; - /* Get the CPUID from the ARM Core - * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/DDI0432C_cortex_m0_r0p0_trm.pdf 4.2.1 */ - int retval = target_read_u32(target, 0xE000ED00, &cpu_id); + retval = psoc4_get_family(target, &family_id); if (retval != ERROR_OK) return retval; - LOG_DEBUG("cpu id = 0x%08" PRIx32 "", cpu_id); + const struct psoc4_chip_family *family = psoc4_family_by_id(family_id); - /* set page size, protection granularity and max flash size depending on family */ - switch ((cpu_id >> 4) & 0xFFF) { - case 0xc20: /* M0 -> PSoC4 */ - row_size = 128; - max_flash_size_in_kb = 32; - break; - default: - LOG_WARNING("Cannot identify target as a PSoC 4 family."); + if (family->id == 0) { + LOG_ERROR("Cannot identify PSoC 4 family."); return ERROR_FAIL; } - uint32_t spcif_geometry; - retval = target_read_u32(target, PSOC4_SPCIF_GEOMETRY, &spcif_geometry); - if (retval == ERROR_OK) { - row_size = 128 * ((spcif_geometry >> 22) & 3); - flash_size_in_kb = (spcif_geometry & 0xffff) * 256 / 1024; - LOG_INFO("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.", - flash_size_in_kb, row_size); + if (family->flags & PSOC4_FAMILY_FLAG_LEGACY) { + LOG_INFO("%s legacy family detected.", family->name); + psoc4_info->legacy_family = true; + psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_LEGACY; + psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_LEGACY; + psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_LEGACY; + } else { + LOG_INFO("%s family detected.", family->name); + psoc4_info->legacy_family = false; + psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_NEW; + psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_NEW; + psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_NEW; } - /* Early revisions of ST-Link v2 have some problem reading PSOC4_SPCIF_GEOMETRY - and an error is reported late. Dummy read gets this error. */ - uint32_t dummy; - target_read_u32(target, PSOC4_CPUSS_SYSREQ, &dummy); - - /* get silicon ID from target. */ - retval = psoc4_get_silicon_id(target, &silicon_id, &protection); + uint32_t spcif_geometry; + retval = target_read_u32(target, psoc4_info->spcif_geometry_addr, &spcif_geometry); if (retval != ERROR_OK) return retval; - const struct psoc4_chip_details *details = psoc4_details_by_id(silicon_id); - if (details) { - LOG_INFO("%s device detected.", details->type); - if (flash_size_in_kb == 0) - flash_size_in_kb = details->flash_size_in_kb; - else if (flash_size_in_kb != details->flash_size_in_kb) - LOG_ERROR("Flash size mismatch"); + uint32_t flash_size_in_kb = spcif_geometry & 0x3fff; + /* TRM of legacy, M and L version describes FLASH field as 16-bit. + * S-series and PSoC Analog Coprocessor changes spec to 14-bit only. + * Impose PSoC Analog Coprocessor limit to all devices as it + * does not make any harm: flash size is safely below 4 MByte limit + */ + uint32_t row_size = (spcif_geometry >> 22) & 3; + uint32_t num_macros = (spcif_geometry >> 20) & 3; + + if (psoc4_info->legacy_family) { + flash_size_in_kb = flash_size_in_kb * 256 / 1024; + row_size *= 128; + } else { + flash_size_in_kb = (flash_size_in_kb + 1) * 256 / 1024; + row_size = 64 * (row_size + 1); + num_macros++; } - psoc4_info->row_size = row_size; - psoc4_info->silicon_id = silicon_id; - psoc4_info->chip_protection = protection; - - /* failed reading flash size or flash size invalid (early silicon), - * default to max target family */ - if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) { - LOG_WARNING("PSoC 4 flash size failed, probe inaccurate - assuming %" PRIu32 " k flash", - max_flash_size_in_kb); - flash_size_in_kb = max_flash_size_in_kb; - } + LOG_DEBUG("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.", + flash_size_in_kb, row_size); /* if the user sets the size manually then ignore the probed value * this allows us to work around devices that have a invalid flash size register value */ @@ -675,37 +805,44 @@ static int psoc4_probe(struct flash_bank *bank) flash_size_in_kb = psoc4_info->user_bank_size / 1024; } - LOG_INFO("flash size = %" PRIu32 " kbytes", flash_size_in_kb); + char macros_txt[20] = ""; + if (num_macros > 1) + snprintf(macros_txt, sizeof(macros_txt), " in %" PRIu32 " macros", num_macros); - /* did we assign flash size? */ - assert(flash_size_in_kb != 0xffff); + LOG_INFO("flash size = %" PRIu32 " kbytes%s", flash_size_in_kb, macros_txt); - /* calculate numbers of pages */ + /* calculate number of pages */ uint32_t num_rows = flash_size_in_kb * 1024 / row_size; - /* check that calculation result makes sense */ - assert(num_rows > 0); + /* check number of flash macros */ + if (num_macros != (num_rows + PSOC4_ROWS_PER_MACRO - 1) / PSOC4_ROWS_PER_MACRO) + LOG_WARNING("Number of macros does not correspond with flash size!"); + + if (!psoc4_info->legacy_family) { + int wounding = psoc4_test_flash_wounding(target, num_rows * row_size); + if (wounding > 0) { + flash_size_in_kb = flash_size_in_kb >> wounding; + num_rows = num_rows >> wounding; + LOG_INFO("WOUNDING detected: accessible flash size %" PRIu32 " kbytes", flash_size_in_kb); + } + } if (bank->sectors) { free(bank->sectors); - bank->sectors = NULL; } - bank->base = base_address; + psoc4_info->family_id = family_id; + psoc4_info->num_macros = num_macros; + psoc4_info->row_size = row_size; + bank->base = 0x00000000; bank->size = num_rows * row_size; bank->num_sectors = num_rows; - bank->sectors = malloc(sizeof(struct flash_sector) * num_rows); - - uint32_t i; - for (i = 0; i < num_rows; i++) { - bank->sectors[i].offset = i * row_size; - bank->sectors[i].size = row_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } + bank->sectors = alloc_block_array(0, row_size, num_rows); + if (bank->sectors == NULL) + return ERROR_FAIL; - LOG_INFO("flash bank set %" PRIu32 " rows", num_rows); - psoc4_info->probed = 1; + LOG_DEBUG("flash bank set %" PRIu32 " rows", num_rows); + psoc4_info->probed = true; return ERROR_OK; } @@ -721,28 +858,45 @@ static int psoc4_auto_probe(struct flash_bank *bank) static int get_psoc4_info(struct flash_bank *bank, char *buf, int buf_size) { + struct target *target = bank->target; struct psoc4_flash_bank *psoc4_info = bank->driver_priv; - int printed = 0; - if (psoc4_info->probed == 0) + if (!psoc4_info->probed) return ERROR_FAIL; - const struct psoc4_chip_details *details = psoc4_details_by_id(psoc4_info->silicon_id); + const struct psoc4_chip_family *family = psoc4_family_by_id(psoc4_info->family_id); + uint32_t size_in_kb = bank->size / 1024; + + if (target->state != TARGET_HALTED) { + snprintf(buf, buf_size, "%s, flash %" PRIu32 " kb" + " (halt target to see details)", family->name, size_in_kb); + return ERROR_OK; + } + + int retval; + int printed = 0; + uint32_t silicon_id; + uint16_t family_id; + uint8_t protection; - if (details) { - uint32_t chip_revision = psoc4_info->silicon_id & 0xffff; - printed = snprintf(buf, buf_size, "PSoC 4 %s rev 0x%04" PRIx32 " package %s", - details->type, chip_revision, details->package); - } else - printed = snprintf(buf, buf_size, "PSoC 4 silicon id 0x%08" PRIx32 "", - psoc4_info->silicon_id); + retval = psoc4_get_silicon_id(bank, &silicon_id, &family_id, &protection); + if (retval != ERROR_OK) + return retval; + + if (family_id != psoc4_info->family_id) + printed = snprintf(buf, buf_size, "Family id mismatch 0x%02" PRIx16 + "/0x%02" PRIx16 ", silicon id 0x%08" PRIx32, + psoc4_info->family_id, family_id, silicon_id); + else { + printed = snprintf(buf, buf_size, "%s silicon id 0x%08" PRIx32 "", + family->name, silicon_id); + } buf += printed; buf_size -= printed; - const char *prot_txt = psoc4_decode_chip_protection(psoc4_info->chip_protection); - uint32_t size_in_kb = bank->size / 1024; - snprintf(buf, buf_size, " flash %" PRIu32 " kb %s", size_in_kb, prot_txt); + const char *prot_txt = psoc4_decode_chip_protection(protection); + snprintf(buf, buf_size, ", flash %" PRIu32 " kb %s", size_in_kb, prot_txt); return ERROR_OK; } @@ -809,4 +963,5 @@ struct flash_driver psoc4_flash = { .erase_check = default_flash_blank_check, .protect_check = psoc4_protect_check, .info = get_psoc4_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/psoc5lp.c b/src/flash/nor/psoc5lp.c new file mode 100644 index 0000000..b88abbb --- /dev/null +++ b/src/flash/nor/psoc5lp.c @@ -0,0 +1,1586 @@ +/* + * PSoC 5LP flash driver + * + * Copyright (c) 2016 Andreas Färber + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/time_support.h> +#include <target/armv7m.h> + +#define PM_ACT_CFG0 0x400043A0 +#define PM_ACT_CFG12 0x400043AC +#define SPC_CPU_DATA 0x40004720 +#define SPC_SR 0x40004722 +#define PRT1_PC2 0x4000500A +#define PHUB_CH0_BASIC_CFG 0x40007010 +#define PHUB_CH0_ACTION 0x40007014 +#define PHUB_CH0_BASIC_STATUS 0x40007018 +#define PHUB_CH1_BASIC_CFG 0x40007020 +#define PHUB_CH1_ACTION 0x40007024 +#define PHUB_CH1_BASIC_STATUS 0x40007028 +#define PHUB_CFGMEM0_CFG0 0x40007600 +#define PHUB_CFGMEM0_CFG1 0x40007604 +#define PHUB_CFGMEM1_CFG0 0x40007608 +#define PHUB_CFGMEM1_CFG1 0x4000760C +#define PHUB_TDMEM0_ORIG_TD0 0x40007800 +#define PHUB_TDMEM0_ORIG_TD1 0x40007804 +#define PHUB_TDMEM1_ORIG_TD0 0x40007808 +#define PHUB_TDMEM1_ORIG_TD1 0x4000780C +#define PANTHER_DEVICE_ID 0x4008001C + +/* NVL is not actually mapped to the Cortex-M address space + * As we need a base addess different from other banks in the device + * we use the address of NVL programming data in Cypress images */ +#define NVL_META_BASE 0x90000000 + +#define PM_ACT_CFG12_EN_EE (1 << 4) + +#define SPC_KEY1 0xB6 +#define SPC_KEY2 0xD3 + +#define SPC_LOAD_BYTE 0x00 +#define SPC_LOAD_MULTI_BYTE 0x01 +#define SPC_LOAD_ROW 0x02 +#define SPC_READ_BYTE 0x03 +#define SPC_READ_MULTI_BYTE 0x04 +#define SPC_WRITE_ROW 0x05 +#define SPC_WRITE_USER_NVL 0x06 +#define SPC_PRG_ROW 0x07 +#define SPC_ERASE_SECTOR 0x08 +#define SPC_ERASE_ALL 0x09 +#define SPC_READ_HIDDEN_ROW 0x0A +#define SPC_PROGRAM_PROTECT_ROW 0x0B +#define SPC_GET_CHECKSUM 0x0C +#define SPC_GET_TEMP 0x0E +#define SPC_READ_VOLATILE_BYTE 0x10 + +#define SPC_ARRAY_ALL 0x3F +#define SPC_ARRAY_EEPROM 0x40 +#define SPC_ARRAY_NVL_USER 0x80 +#define SPC_ARRAY_NVL_WO 0xF8 + +#define SPC_ROW_PROTECTION 0 + +#define SPC_OPCODE_LEN 3 + +#define SPC_SR_DATA_READY (1 << 0) +#define SPC_SR_IDLE (1 << 1) + +#define PM_ACT_CFG0_EN_CLK_SPC (1 << 3) + +#define PHUB_CHx_BASIC_CFG_EN (1 << 0) +#define PHUB_CHx_BASIC_CFG_WORK_SEP (1 << 5) + +#define PHUB_CHx_ACTION_CPU_REQ (1 << 0) + +#define PHUB_CFGMEMx_CFG0 (1 << 7) + +#define PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST (0xff << 16) +#define PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR (1 << 24) + +#define NVL_3_ECCEN (1 << 3) + +#define ROW_SIZE 256 +#define ROW_ECC_SIZE 32 +#define ROWS_PER_SECTOR 64 +#define SECTOR_SIZE (ROWS_PER_SECTOR * ROW_SIZE) +#define ROWS_PER_BLOCK 256 +#define BLOCK_SIZE (ROWS_PER_BLOCK * ROW_SIZE) +#define SECTORS_PER_BLOCK (BLOCK_SIZE / SECTOR_SIZE) +#define EEPROM_ROW_SIZE 16 +#define EEPROM_SECTOR_SIZE (ROWS_PER_SECTOR * EEPROM_ROW_SIZE) +#define EEPROM_BLOCK_SIZE (ROWS_PER_BLOCK * EEPROM_ROW_SIZE) + +#define PART_NUMBER_LEN (17 + 1) + +struct psoc5lp_device { + uint32_t id; + unsigned fam; + unsigned speed_mhz; + unsigned flash_kb; + unsigned eeprom_kb; +}; + +/* + * Device information collected from datasheets. + * Different temperature ranges (C/I/Q/A) may share IDs, not differing otherwise. + */ +static const struct psoc5lp_device psoc5lp_devices[] = { + /* CY8C58LP Family Datasheet */ + { .id = 0x2E11F069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E120069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E123069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E124069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E126069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E127069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E117069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E118069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E119069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E11C069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E114069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E115069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E116069, .fam = 8, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E160069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + /* '' */ + { .id = 0x2E161069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + /* '' */ + { .id = 0x2E1D2069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E1D6069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + + /* CY8C56LP Family Datasheet */ + { .id = 0x2E10A069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E10D069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E10E069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E106069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E108069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E109069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E101069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E104069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + /* '' */ + { .id = 0x2E105069, .fam = 6, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E128069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + /* '' */ + { .id = 0x2E122069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E129069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E163069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E156069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E1D3069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + + /* CY8C54LP Family Datasheet */ + { .id = 0x2E11A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E16A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E12A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E103069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E16C069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E102069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E148069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E155069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E16B069, .fam = 4, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E12B069, .fam = 4, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 }, + { .id = 0x2E168069, .fam = 4, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 }, + { .id = 0x2E178069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E15D069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E1D4069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + + /* CY8C52LP Family Datasheet */ + { .id = 0x2E11E069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E12F069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E133069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E159069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 }, + { .id = 0x2E11D069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E121069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E184069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E196069, .fam = 2, .speed_mhz = 67, .flash_kb = 64, .eeprom_kb = 2 }, + { .id = 0x2E132069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 }, + { .id = 0x2E138069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 }, + { .id = 0x2E13A069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 }, + { .id = 0x2E152069, .fam = 2, .speed_mhz = 67, .flash_kb = 32, .eeprom_kb = 2 }, + { .id = 0x2E15F069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E15A069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, + { .id = 0x2E1D5069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 }, +}; + +static void psoc5lp_get_part_number(const struct psoc5lp_device *dev, char *str) +{ + strcpy(str, "CY8Cabcdefg-LPxxx"); + + str[4] = '5'; + str[5] = '0' + dev->fam; + + switch (dev->speed_mhz) { + case 67: + str[6] = '6'; + break; + case 80: + str[6] = '8'; + break; + default: + str[6] = '?'; + } + + switch (dev->flash_kb) { + case 32: + str[7] = '5'; + break; + case 64: + str[7] = '6'; + break; + case 128: + str[7] = '7'; + break; + case 256: + str[7] = '8'; + break; + default: + str[7] = '?'; + } + + /* Package does not matter. */ + str[8] = 'x'; + str[9] = 'x'; + + /* Temperate range cannot uniquely be identified. */ + str[10] = 'x'; +} + +static int psoc5lp_get_device_id(struct target *target, uint32_t *id) +{ + int retval; + + retval = target_read_u32(target, PANTHER_DEVICE_ID, id); /* dummy read */ + if (retval != ERROR_OK) + return retval; + retval = target_read_u32(target, PANTHER_DEVICE_ID, id); + return retval; +} + +static int psoc5lp_find_device(struct target *target, + const struct psoc5lp_device **device) +{ + uint32_t device_id; + unsigned i; + int retval; + + *device = NULL; + + retval = psoc5lp_get_device_id(target, &device_id); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("PANTHER_DEVICE_ID = 0x%08" PRIX32, device_id); + + for (i = 0; i < ARRAY_SIZE(psoc5lp_devices); i++) { + if (psoc5lp_devices[i].id == device_id) { + *device = &psoc5lp_devices[i]; + return ERROR_OK; + } + } + + LOG_ERROR("Device 0x%08" PRIX32 " not supported", device_id); + return ERROR_FLASH_OPER_UNSUPPORTED; +} + +static int psoc5lp_spc_enable_clock(struct target *target) +{ + int retval; + uint8_t pm_act_cfg0; + + retval = target_read_u8(target, PM_ACT_CFG0, &pm_act_cfg0); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read PM_ACT_CFG0"); + return retval; + } + + if (pm_act_cfg0 & PM_ACT_CFG0_EN_CLK_SPC) + return ERROR_OK; /* clock already enabled */ + + retval = target_write_u8(target, PM_ACT_CFG0, pm_act_cfg0 | PM_ACT_CFG0_EN_CLK_SPC); + if (retval != ERROR_OK) + LOG_ERROR("Cannot enable SPC clock"); + + return retval; +} + +static int psoc5lp_spc_write_opcode(struct target *target, uint8_t opcode) +{ + int retval; + + retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY2 + opcode); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, opcode); + return retval; +} + +static void psoc5lp_spc_write_opcode_buffer(struct target *target, + uint8_t *buf, uint8_t opcode) +{ + buf[0] = SPC_KEY1; + buf[1] = SPC_KEY2 + opcode; + buf[2] = opcode; +} + +static int psoc5lp_spc_busy_wait_data(struct target *target) +{ + int64_t endtime; + uint8_t sr; + int retval; + + retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */ + if (retval != ERROR_OK) + return retval; + + endtime = timeval_ms() + 1000; /* 1 second timeout */ + do { + alive_sleep(1); + retval = target_read_u8(target, SPC_SR, &sr); + if (retval != ERROR_OK) + return retval; + if (sr == SPC_SR_DATA_READY) + return ERROR_OK; + } while (timeval_ms() < endtime); + + return ERROR_FLASH_OPERATION_FAILED; +} + +static int psoc5lp_spc_busy_wait_idle(struct target *target) +{ + int64_t endtime; + uint8_t sr; + int retval; + + retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */ + if (retval != ERROR_OK) + return retval; + + endtime = timeval_ms() + 1000; /* 1 second timeout */ + do { + alive_sleep(1); + retval = target_read_u8(target, SPC_SR, &sr); + if (retval != ERROR_OK) + return retval; + if (sr == SPC_SR_IDLE) + return ERROR_OK; + } while (timeval_ms() < endtime); + + return ERROR_FLASH_OPERATION_FAILED; +} + +static int psoc5lp_spc_load_byte(struct target *target, + uint8_t array_id, uint8_t offset, uint8_t value) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_BYTE); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, offset); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, value); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_load_row(struct target *target, + uint8_t array_id, const uint8_t *data, unsigned row_size) +{ + unsigned i; + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_ROW); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + + for (i = 0; i < row_size; i++) { + retval = target_write_u8(target, SPC_CPU_DATA, data[i]); + if (retval != ERROR_OK) + return retval; + } + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_read_byte(struct target *target, + uint8_t array_id, uint8_t offset, uint8_t *data) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_READ_BYTE); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, offset); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_data(target); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u8(target, SPC_CPU_DATA, data); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_write_row(struct target *target, + uint8_t array_id, uint16_t row_id, const uint8_t *temp) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_ROW); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, row_id >> 8); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, row_id & 0xff); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, temp[0]); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, temp[1]); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_write_user_nvl(struct target *target, + uint8_t array_id) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_USER_NVL); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_erase_sector(struct target *target, + uint8_t array_id, uint8_t row_id) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_SECTOR); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, row_id); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_erase_all(struct target *target) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_ALL); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_read_hidden_row(struct target *target, + uint8_t array_id, uint8_t row_id, uint8_t *data) +{ + int i, retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_READ_HIDDEN_ROW); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, row_id); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_data(target); + if (retval != ERROR_OK) + return retval; + + for (i = 0; i < ROW_SIZE; i++) { + retval = target_read_u8(target, SPC_CPU_DATA, &data[i]); + if (retval != ERROR_OK) + return retval; + } + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples, + uint8_t *data) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_GET_TEMP); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, samples); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_data(target); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u8(target, SPC_CPU_DATA, &data[0]); + if (retval != ERROR_OK) + return retval; + retval = target_read_u8(target, SPC_CPU_DATA, &data[1]); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int psoc5lp_spc_read_volatile_byte(struct target *target, + uint8_t array_id, uint8_t offset, uint8_t *data) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_READ_VOLATILE_BYTE); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, offset); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_data(target); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u8(target, SPC_CPU_DATA, data); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +/* + * NV Latch + */ + +struct psoc5lp_nvl_flash_bank { + bool probed; + const struct psoc5lp_device *device; +}; + +static int psoc5lp_nvl_read(struct flash_bank *bank, + uint8_t *buffer, uint32_t offset, uint32_t count) +{ + int retval; + + retval = psoc5lp_spc_enable_clock(bank->target); + if (retval != ERROR_OK) + return retval; + + while (count > 0) { + retval = psoc5lp_spc_read_byte(bank->target, + SPC_ARRAY_NVL_USER, offset, buffer); + if (retval != ERROR_OK) + return retval; + buffer++; + offset++; + count--; + } + + return ERROR_OK; +} + +static int psoc5lp_nvl_erase(struct flash_bank *bank, int first, int last) +{ + LOG_WARNING("There is no erase operation for NV Latches"); + return ERROR_FLASH_OPER_UNSUPPORTED; +} + +static int psoc5lp_nvl_erase_check(struct flash_bank *bank) +{ + int i; + + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 0; + + return ERROR_OK; +} + +static int psoc5lp_nvl_write(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t byte_count) +{ + struct target *target = bank->target; + uint8_t *current_data, val; + bool write_required = false, pullup_needed = false, ecc_changed = false; + uint32_t i; + int retval; + + if (offset != 0 || byte_count != bank->size) { + LOG_ERROR("NVL can only be written in whole"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + current_data = calloc(1, bank->size); + if (!current_data) + return ERROR_FAIL; + retval = psoc5lp_nvl_read(bank, current_data, offset, byte_count); + if (retval != ERROR_OK) { + free(current_data); + return retval; + } + for (i = offset; i < byte_count; i++) { + if (current_data[i] != buffer[i]) { + write_required = true; + break; + } + } + if (((buffer[2] & 0x80) == 0x80) && ((current_data[0] & 0x0C) != 0x08)) + pullup_needed = true; + if (((buffer[3] ^ current_data[3]) & 0x08) == 0x08) + ecc_changed = true; + free(current_data); + + if (!write_required) { + LOG_INFO("Unchanged, skipping NVL write"); + return ERROR_OK; + } + if (pullup_needed) { + retval = target_read_u8(target, PRT1_PC2, &val); + if (retval != ERROR_OK) + return retval; + val &= 0xF0; + val |= 0x05; + retval = target_write_u8(target, PRT1_PC2, val); + if (retval != ERROR_OK) + return retval; + } + + for (i = offset; i < byte_count; i++) { + retval = psoc5lp_spc_load_byte(target, + SPC_ARRAY_NVL_USER, i, buffer[i]); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_read_volatile_byte(target, + SPC_ARRAY_NVL_USER, i, &val); + if (retval != ERROR_OK) + return retval; + if (val != buffer[i]) { + LOG_ERROR("Failed to load NVL byte %" PRIu32 ": " + "expected 0x%02" PRIx8 ", read 0x%02" PRIx8, + i, buffer[i], val); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + retval = psoc5lp_spc_write_user_nvl(target, SPC_ARRAY_NVL_USER); + if (retval != ERROR_OK) + return retval; + + if (ecc_changed) { + retval = target_call_reset_callbacks(target, RESET_INIT); + if (retval != ERROR_OK) + LOG_WARNING("Reset failed after enabling or disabling ECC"); + } + + return ERROR_OK; +} + +static int psoc5lp_nvl_protect_check(struct flash_bank *bank) +{ + int i; + + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = -1; + + return ERROR_OK; +} + +static int psoc5lp_nvl_get_info_command(struct flash_bank *bank, + char *buf, int buf_size) +{ + struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv; + char part_number[PART_NUMBER_LEN]; + + psoc5lp_get_part_number(psoc_nvl_bank->device, part_number); + + snprintf(buf, buf_size, "%s", part_number); + + return ERROR_OK; +} + +static int psoc5lp_nvl_probe(struct flash_bank *bank) +{ + struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv; + int retval; + + if (psoc_nvl_bank->probed) + return ERROR_OK; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = psoc5lp_find_device(bank->target, &psoc_nvl_bank->device); + if (retval != ERROR_OK) + return retval; + + bank->base = NVL_META_BASE; + bank->size = 4; + bank->num_sectors = 1; + bank->sectors = calloc(bank->num_sectors, + sizeof(struct flash_sector)); + bank->sectors[0].offset = 0; + bank->sectors[0].size = 4; + bank->sectors[0].is_erased = -1; + bank->sectors[0].is_protected = -1; + + psoc_nvl_bank->probed = true; + + return ERROR_OK; +} + +static int psoc5lp_nvl_auto_probe(struct flash_bank *bank) +{ + struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv; + + if (psoc_nvl_bank->probed) + return ERROR_OK; + + return psoc5lp_nvl_probe(bank); +} + +FLASH_BANK_COMMAND_HANDLER(psoc5lp_nvl_flash_bank_command) +{ + struct psoc5lp_nvl_flash_bank *psoc_nvl_bank; + + psoc_nvl_bank = malloc(sizeof(struct psoc5lp_nvl_flash_bank)); + if (!psoc_nvl_bank) + return ERROR_FLASH_OPERATION_FAILED; + + psoc_nvl_bank->probed = false; + + bank->driver_priv = psoc_nvl_bank; + + return ERROR_OK; +} + +static const struct command_registration psoc5lp_nvl_exec_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration psoc5lp_nvl_command_handlers[] = { + { + .name = "psoc5lp_nvl", + .mode = COMMAND_ANY, + .help = "PSoC 5LP NV Latch command group", + .usage = "", + .chain = psoc5lp_nvl_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver psoc5lp_nvl_flash = { + .name = "psoc5lp_nvl", + .commands = psoc5lp_nvl_command_handlers, + .flash_bank_command = psoc5lp_nvl_flash_bank_command, + .info = psoc5lp_nvl_get_info_command, + .probe = psoc5lp_nvl_probe, + .auto_probe = psoc5lp_nvl_auto_probe, + .protect_check = psoc5lp_nvl_protect_check, + .read = psoc5lp_nvl_read, + .erase = psoc5lp_nvl_erase, + .erase_check = psoc5lp_nvl_erase_check, + .write = psoc5lp_nvl_write, + .free_driver_priv = default_flash_free_driver_priv, +}; + +/* + * EEPROM + */ + +struct psoc5lp_eeprom_flash_bank { + bool probed; + const struct psoc5lp_device *device; +}; + +static int psoc5lp_eeprom_erase(struct flash_bank *bank, int first, int last) +{ + int i, retval; + + for (i = first; i <= last; i++) { + retval = psoc5lp_spc_erase_sector(bank->target, + SPC_ARRAY_EEPROM, i); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int psoc5lp_eeprom_write(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t byte_count) +{ + struct target *target = bank->target; + uint8_t temp[2]; + unsigned row; + int retval; + + if (offset % EEPROM_ROW_SIZE != 0) { + LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32, + offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + retval = psoc5lp_spc_get_temp(target, 3, temp); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to read Die temperature"); + return retval; + } + LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8, + temp[0], temp[1]); + + for (row = offset / EEPROM_ROW_SIZE; byte_count >= EEPROM_ROW_SIZE; row++) { + retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM, + buffer, EEPROM_ROW_SIZE); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM, + row, temp); + if (retval != ERROR_OK) + return retval; + + buffer += EEPROM_ROW_SIZE; + byte_count -= EEPROM_ROW_SIZE; + offset += EEPROM_ROW_SIZE; + } + if (byte_count > 0) { + uint8_t buf[EEPROM_ROW_SIZE]; + + memcpy(buf, buffer, byte_count); + memset(buf + byte_count, bank->default_padded_value, + EEPROM_ROW_SIZE - byte_count); + + LOG_DEBUG("Padding %d bytes", EEPROM_ROW_SIZE - byte_count); + retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM, + buf, EEPROM_ROW_SIZE); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM, + row, temp); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int psoc5lp_eeprom_protect_check(struct flash_bank *bank) +{ + int i; + + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = -1; + + return ERROR_OK; +} + +static int psoc5lp_eeprom_get_info_command(struct flash_bank *bank, char *buf, int buf_size) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv; + char part_number[PART_NUMBER_LEN]; + + psoc5lp_get_part_number(psoc_eeprom_bank->device, part_number); + + snprintf(buf, buf_size, "%s", part_number); + + return ERROR_OK; +} + +static int psoc5lp_eeprom_probe(struct flash_bank *bank) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv; + uint32_t flash_addr = bank->base; + uint32_t val; + int i, retval; + + if (psoc_eeprom_bank->probed) + return ERROR_OK; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = psoc5lp_find_device(bank->target, &psoc_eeprom_bank->device); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(bank->target, PM_ACT_CFG12, &val); + if (retval != ERROR_OK) + return retval; + if (!(val & PM_ACT_CFG12_EN_EE)) { + val |= PM_ACT_CFG12_EN_EE; + retval = target_write_u32(bank->target, PM_ACT_CFG12, val); + if (retval != ERROR_OK) + return retval; + } + + bank->size = psoc_eeprom_bank->device->eeprom_kb * 1024; + bank->num_sectors = DIV_ROUND_UP(bank->size, EEPROM_SECTOR_SIZE); + bank->sectors = calloc(bank->num_sectors, + sizeof(struct flash_sector)); + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].size = EEPROM_SECTOR_SIZE; + bank->sectors[i].offset = flash_addr - bank->base; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + + flash_addr += bank->sectors[i].size; + } + + bank->default_padded_value = bank->erased_value = 0x00; + + psoc_eeprom_bank->probed = true; + + return ERROR_OK; +} + +static int psoc5lp_eeprom_auto_probe(struct flash_bank *bank) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv; + + if (psoc_eeprom_bank->probed) + return ERROR_OK; + + return psoc5lp_eeprom_probe(bank); +} + +FLASH_BANK_COMMAND_HANDLER(psoc5lp_eeprom_flash_bank_command) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank; + + psoc_eeprom_bank = malloc(sizeof(struct psoc5lp_eeprom_flash_bank)); + if (!psoc_eeprom_bank) + return ERROR_FLASH_OPERATION_FAILED; + + psoc_eeprom_bank->probed = false; + psoc_eeprom_bank->device = NULL; + + bank->driver_priv = psoc_eeprom_bank; + + return ERROR_OK; +} + +static const struct command_registration psoc5lp_eeprom_exec_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration psoc5lp_eeprom_command_handlers[] = { + { + .name = "psoc5lp_eeprom", + .mode = COMMAND_ANY, + .help = "PSoC 5LP EEPROM command group", + .usage = "", + .chain = psoc5lp_eeprom_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver psoc5lp_eeprom_flash = { + .name = "psoc5lp_eeprom", + .commands = psoc5lp_eeprom_command_handlers, + .flash_bank_command = psoc5lp_eeprom_flash_bank_command, + .info = psoc5lp_eeprom_get_info_command, + .probe = psoc5lp_eeprom_probe, + .auto_probe = psoc5lp_eeprom_auto_probe, + .protect_check = psoc5lp_eeprom_protect_check, + .read = default_flash_read, + .erase = psoc5lp_eeprom_erase, + .erase_check = default_flash_blank_check, + .write = psoc5lp_eeprom_write, + .free_driver_priv = default_flash_free_driver_priv, +}; + +/* + * Program Flash + */ + +struct psoc5lp_flash_bank { + bool probed; + const struct psoc5lp_device *device; + bool ecc_enabled; + /* If ecc is disabled, num_sectors counts both std and ecc sectors. + * If ecc is enabled, num_sectors indicates just the number of std sectors. + * However ecc sector descriptors bank->sector[num_sectors..2*num_sectors-1] + * are used for driver private flash operations */ +}; + +static int psoc5lp_erase(struct flash_bank *bank, int first, int last) +{ + struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv; + int i, retval; + + if (!psoc_bank->ecc_enabled) { + /* Silently avoid erasing sectors twice */ + if (last >= first + bank->num_sectors / 2) { + LOG_DEBUG("Skipping duplicate erase of sectors %d to %d", + first + bank->num_sectors / 2, last); + last = first + (bank->num_sectors / 2) - 1; + } + /* Check for any remaining ECC sectors */ + if (last >= bank->num_sectors / 2) { + LOG_WARNING("Skipping erase of ECC region sectors %d to %d", + bank->num_sectors / 2, last); + last = (bank->num_sectors / 2) - 1; + } + } + + for (i = first; i <= last; i++) { + retval = psoc5lp_spc_erase_sector(bank->target, + i / SECTORS_PER_BLOCK, i % SECTORS_PER_BLOCK); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +/* Derived from core.c:default_flash_blank_check() */ +static int psoc5lp_erase_check(struct flash_bank *bank) +{ + struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv; + struct target *target = bank->target; + int i, retval; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int num_sectors = bank->num_sectors; + if (psoc_bank->ecc_enabled) + num_sectors *= 2; /* count both std and ecc sector always */ + + struct target_memory_check_block *block_array; + block_array = malloc(num_sectors * sizeof(struct target_memory_check_block)); + if (block_array == NULL) + return ERROR_FAIL; + + for (i = 0; i < num_sectors; i++) { + block_array[i].address = bank->base + bank->sectors[i].offset; + block_array[i].size = bank->sectors[i].size; + block_array[i].result = UINT32_MAX; /* erase state unknown */ + } + + bool fast_check = true; + for (i = 0; i < num_sectors; ) { + retval = armv7m_blank_check_memory(target, + block_array + i, num_sectors - i, + bank->erased_value); + if (retval < 1) { + /* Run slow fallback if the first run gives no result + * otherwise use possibly incomplete results */ + if (i == 0) + fast_check = false; + break; + } + i += retval; /* add number of blocks done this round */ + } + + if (fast_check) { + if (psoc_bank->ecc_enabled) { + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = + (block_array[i].result != 1) + ? block_array[i].result + : block_array[i + bank->num_sectors].result; + /* if std sector is erased, use status of ecc sector */ + } else { + for (i = 0; i < num_sectors; i++) + bank->sectors[i].is_erased = block_array[i].result; + } + retval = ERROR_OK; + } else { + LOG_ERROR("Can't run erase check - add working memory"); + retval = ERROR_FAIL; + } + free(block_array); + + return retval; +} + +static int psoc5lp_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t byte_count) +{ + struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv; + struct target *target = bank->target; + struct working_area *code_area, *even_row_area, *odd_row_area; + uint32_t row_size; + uint8_t temp[2], buf[12], ecc_bytes[ROW_ECC_SIZE]; + unsigned array_id, row; + int i, retval; + + if (offset + byte_count > bank->size) { + LOG_ERROR("Writing to ECC not supported"); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + if (offset % ROW_SIZE != 0) { + LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32, + offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + row_size = ROW_SIZE; + if (!psoc_bank->ecc_enabled) { + row_size += ROW_ECC_SIZE; + memset(ecc_bytes, bank->default_padded_value, ROW_ECC_SIZE); + } + + retval = psoc5lp_spc_get_temp(target, 3, temp); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to read Die temperature"); + return retval; + } + LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8, + temp[0], temp[1]); + + assert(target_get_working_area_avail(target) == target->working_area_size); + retval = target_alloc_working_area(target, + target_get_working_area_avail(target) / 2, &code_area); + if (retval != ERROR_OK) { + LOG_ERROR("Could not allocate working area for program SRAM"); + return retval; + } + assert(code_area->address < 0x20000000); + + retval = target_alloc_working_area(target, + SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 6, + &even_row_area); + if (retval != ERROR_OK) { + LOG_ERROR("Could not allocate working area for even row"); + goto err_alloc_even; + } + assert(even_row_area->address >= 0x20000000); + + retval = target_alloc_working_area(target, even_row_area->size, + &odd_row_area); + if (retval != ERROR_OK) { + LOG_ERROR("Could not allocate working area for odd row"); + goto err_alloc_odd; + } + assert(odd_row_area->address >= 0x20000000); + + for (array_id = offset / BLOCK_SIZE; byte_count > 0; array_id++) { + for (row = (offset / ROW_SIZE) % ROWS_PER_BLOCK; + row < ROWS_PER_BLOCK && byte_count > 0; row++) { + bool even_row = (row % 2 == 0); + struct working_area *data_area = even_row ? even_row_area : odd_row_area; + unsigned len = MIN(ROW_SIZE, byte_count); + + LOG_DEBUG("Writing load command for array %u row %u at 0x%08" TARGET_PRIxADDR, + array_id, row, data_area->address); + + psoc5lp_spc_write_opcode_buffer(target, buf, SPC_LOAD_ROW); + buf[SPC_OPCODE_LEN] = array_id; + retval = target_write_buffer(target, data_area->address, 4, buf); + if (retval != ERROR_OK) + goto err_write; + + retval = target_write_buffer(target, + data_area->address + SPC_OPCODE_LEN + 1, + len, buffer); + if (retval != ERROR_OK) + goto err_write; + buffer += len; + byte_count -= len; + offset += len; + + if (len < ROW_SIZE) { + uint8_t padding[ROW_SIZE]; + + memset(padding, bank->default_padded_value, ROW_SIZE); + + LOG_DEBUG("Padding %d bytes", ROW_SIZE - len); + retval = target_write_buffer(target, + data_area->address + SPC_OPCODE_LEN + 1 + len, + ROW_SIZE - len, padding); + if (retval != ERROR_OK) + goto err_write; + } + + if (!psoc_bank->ecc_enabled) { + retval = target_write_buffer(target, + data_area->address + SPC_OPCODE_LEN + 1 + ROW_SIZE, + sizeof(ecc_bytes), ecc_bytes); + if (retval != ERROR_OK) + goto err_write; + } + + for (i = 0; i < 3; i++) + buf[i] = 0x00; /* 3 NOPs for short delay */ + psoc5lp_spc_write_opcode_buffer(target, buf + 3, SPC_PRG_ROW); + buf[3 + SPC_OPCODE_LEN] = array_id; + buf[3 + SPC_OPCODE_LEN + 1] = row >> 8; + buf[3 + SPC_OPCODE_LEN + 2] = row & 0xff; + memcpy(buf + 3 + SPC_OPCODE_LEN + 3, temp, 2); + buf[3 + SPC_OPCODE_LEN + 5] = 0x00; /* padding */ + retval = target_write_buffer(target, + data_area->address + SPC_OPCODE_LEN + 1 + row_size, + 12, buf); + if (retval != ERROR_OK) + goto err_write; + + retval = target_write_u32(target, + even_row ? PHUB_CH0_BASIC_STATUS : PHUB_CH1_BASIC_STATUS, + (even_row ? 0 : 1) << 8); + if (retval != ERROR_OK) + goto err_dma; + + retval = target_write_u32(target, + even_row ? PHUB_CH0_BASIC_CFG : PHUB_CH1_BASIC_CFG, + PHUB_CHx_BASIC_CFG_WORK_SEP | PHUB_CHx_BASIC_CFG_EN); + if (retval != ERROR_OK) + goto err_dma; + + retval = target_write_u32(target, + even_row ? PHUB_CFGMEM0_CFG0 : PHUB_CFGMEM1_CFG0, + PHUB_CFGMEMx_CFG0); + if (retval != ERROR_OK) + goto err_dma; + + retval = target_write_u32(target, + even_row ? PHUB_CFGMEM0_CFG1 : PHUB_CFGMEM1_CFG1, + ((SPC_CPU_DATA >> 16) << 16) | (data_area->address >> 16)); + if (retval != ERROR_OK) + goto err_dma; + + retval = target_write_u32(target, + even_row ? PHUB_TDMEM0_ORIG_TD0 : PHUB_TDMEM1_ORIG_TD0, + PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR | + PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST | + ((SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 5) & 0xfff)); + if (retval != ERROR_OK) + goto err_dma; + + retval = target_write_u32(target, + even_row ? PHUB_TDMEM0_ORIG_TD1 : PHUB_TDMEM1_ORIG_TD1, + ((SPC_CPU_DATA & 0xffff) << 16) | (data_area->address & 0xffff)); + if (retval != ERROR_OK) + goto err_dma; + + retval = psoc5lp_spc_busy_wait_idle(target); + if (retval != ERROR_OK) + goto err_idle; + + retval = target_write_u32(target, + even_row ? PHUB_CH0_ACTION : PHUB_CH1_ACTION, + PHUB_CHx_ACTION_CPU_REQ); + if (retval != ERROR_OK) + goto err_dma_action; + } + } + + retval = psoc5lp_spc_busy_wait_idle(target); + +err_dma_action: +err_idle: +err_dma: +err_write: + target_free_working_area(target, odd_row_area); +err_alloc_odd: + target_free_working_area(target, even_row_area); +err_alloc_even: + target_free_working_area(target, code_area); + + return retval; +} + +static int psoc5lp_protect_check(struct flash_bank *bank) +{ + struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv; + uint8_t row_data[ROW_SIZE]; + const unsigned protection_bytes_per_sector = ROWS_PER_SECTOR * 2 / 8; + unsigned i, j, k, num_sectors; + int retval; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + for (i = 0; i < DIV_ROUND_UP(bank->size, BLOCK_SIZE); i++) { + retval = psoc5lp_spc_read_hidden_row(bank->target, i, + SPC_ROW_PROTECTION, row_data); + if (retval != ERROR_OK) + return retval; + + /* Last flash array may have less rows, but in practice full sectors. */ + if (i == bank->size / BLOCK_SIZE) + num_sectors = (bank->size % BLOCK_SIZE) / SECTOR_SIZE; + else + num_sectors = SECTORS_PER_BLOCK; + + for (j = 0; j < num_sectors; j++) { + int sector_nr = i * SECTORS_PER_BLOCK + j; + struct flash_sector *sector = &bank->sectors[sector_nr]; + struct flash_sector *ecc_sector; + + if (psoc_bank->ecc_enabled) + ecc_sector = &bank->sectors[bank->num_sectors + sector_nr]; + else + ecc_sector = &bank->sectors[bank->num_sectors / 2 + sector_nr]; + + sector->is_protected = ecc_sector->is_protected = 0; + for (k = protection_bytes_per_sector * j; + k < protection_bytes_per_sector * (j + 1); k++) { + assert(k < protection_bytes_per_sector * SECTORS_PER_BLOCK); + LOG_DEBUG("row[%u][%02u] = 0x%02" PRIx8, i, k, row_data[k]); + if (row_data[k] != 0x00) { + sector->is_protected = ecc_sector->is_protected = 1; + break; + } + } + } + } + + return ERROR_OK; +} + +static int psoc5lp_get_info_command(struct flash_bank *bank, char *buf, int buf_size) +{ + struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv; + char part_number[PART_NUMBER_LEN]; + const char *ecc; + + psoc5lp_get_part_number(psoc_bank->device, part_number); + ecc = psoc_bank->ecc_enabled ? "ECC enabled" : "ECC disabled"; + + snprintf(buf, buf_size, "%s %s", part_number, ecc); + + return ERROR_OK; +} + +static int psoc5lp_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv; + uint32_t flash_addr = bank->base; + uint8_t nvl[4], temp[2]; + int i, retval; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!psoc_bank->device) { + retval = psoc5lp_find_device(target, &psoc_bank->device); + if (retval != ERROR_OK) + return retval; + + bank->size = psoc_bank->device->flash_kb * 1024; + } + + bank->num_sectors = DIV_ROUND_UP(bank->size, SECTOR_SIZE); + + if (!psoc_bank->probed) { + retval = psoc5lp_spc_enable_clock(target); + if (retval != ERROR_OK) + return retval; + + /* First values read are inaccurate, so do it once now. */ + retval = psoc5lp_spc_get_temp(target, 3, temp); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to read Die temperature"); + return retval; + } + + bank->sectors = calloc(bank->num_sectors * 2, + sizeof(struct flash_sector)); + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].size = SECTOR_SIZE; + bank->sectors[i].offset = flash_addr - bank->base; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + + flash_addr += bank->sectors[i].size; + } + flash_addr = 0x48000000; + for (i = bank->num_sectors; i < bank->num_sectors * 2; i++) { + bank->sectors[i].size = ROWS_PER_SECTOR * ROW_ECC_SIZE; + bank->sectors[i].offset = flash_addr - bank->base; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + + flash_addr += bank->sectors[i].size; + } + + bank->default_padded_value = bank->erased_value = 0x00; + + psoc_bank->probed = true; + } + + retval = psoc5lp_spc_read_byte(target, SPC_ARRAY_NVL_USER, 3, &nvl[3]); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("NVL[%d] = 0x%02" PRIx8, 3, nvl[3]); + psoc_bank->ecc_enabled = nvl[3] & NVL_3_ECCEN; + + if (!psoc_bank->ecc_enabled) + bank->num_sectors *= 2; + + return ERROR_OK; +} + +static int psoc5lp_auto_probe(struct flash_bank *bank) +{ + return psoc5lp_probe(bank); +} + +COMMAND_HANDLER(psoc5lp_handle_mass_erase_command) +{ + struct flash_bank *bank; + int retval; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_erase_all(bank->target); + if (retval == ERROR_OK) + command_print(CMD_CTX, "PSoC 5LP erase succeeded"); + else + command_print(CMD_CTX, "PSoC 5LP erase failed"); + + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(psoc5lp_flash_bank_command) +{ + struct psoc5lp_flash_bank *psoc_bank; + + psoc_bank = malloc(sizeof(struct psoc5lp_flash_bank)); + if (!psoc_bank) + return ERROR_FLASH_OPERATION_FAILED; + + psoc_bank->probed = false; + psoc_bank->device = NULL; + + bank->driver_priv = psoc_bank; + + return ERROR_OK; +} + +static const struct command_registration psoc5lp_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = psoc5lp_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase all flash data and ECC/configuration bytes, " + "all flash protection rows, " + "and all row latches in all flash arrays on the device.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration psoc5lp_command_handlers[] = { + { + .name = "psoc5lp", + .mode = COMMAND_ANY, + .help = "PSoC 5LP flash command group", + .usage = "", + .chain = psoc5lp_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver psoc5lp_flash = { + .name = "psoc5lp", + .commands = psoc5lp_command_handlers, + .flash_bank_command = psoc5lp_flash_bank_command, + .info = psoc5lp_get_info_command, + .probe = psoc5lp_probe, + .auto_probe = psoc5lp_auto_probe, + .protect_check = psoc5lp_protect_check, + .read = default_flash_read, + .erase = psoc5lp_erase, + .erase_check = psoc5lp_erase_check, + .write = psoc5lp_write, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/psoc6.c b/src/flash/nor/psoc6.c new file mode 100644 index 0000000..9352ad4 --- /dev/null +++ b/src/flash/nor/psoc6.c @@ -0,0 +1,1065 @@ +/*************************************************************************** + * * + * Copyright (C) 2018 by Bohdan Tymkiv * + * bohdan.tymkiv@cypress.com bohdan200@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <time.h> + +#include "imp.h" +#include "target/target.h" +#include "target/cortex_m.h" +#include "target/breakpoints.h" +#include "target/target_type.h" +#include "time_support.h" +#include "target/algorithm.h" + +/************************************************************************************************** + * PSoC6 device definitions + *************************************************************************************************/ +#define MFLASH_SECTOR_SIZE (256u * 1024u) +#define WFLASH_SECTOR_SIZE (32u * 1024u) + +#define MEM_BASE_MFLASH 0x10000000u +#define MEM_BASE_WFLASH 0x14000000u +#define MEM_WFLASH_SIZE 32768u +#define MEM_BASE_SFLASH 0x16000000u +#define RAM_STACK_WA_SIZE 2048u +#define PSOC6_SPCIF_GEOMETRY 0x4025F00Cu + +#define PROTECTION_UNKNOWN 0x00u +#define PROTECTION_VIRGIN 0x01u +#define PROTECTION_NORMAL 0x02u +#define PROTECTION_SECURE 0x03u +#define PROTECTION_DEAD 0x04u + +#define MEM_BASE_IPC 0x40230000u +#define IPC_STRUCT_SIZE 0x20u +#define MEM_IPC(n) (MEM_BASE_IPC + (n) * IPC_STRUCT_SIZE) +#define MEM_IPC_ACQUIRE(n) (MEM_IPC(n) + 0x00u) +#define MEM_IPC_NOTIFY(n) (MEM_IPC(n) + 0x08u) +#define MEM_IPC_DATA(n) (MEM_IPC(n) + 0x0Cu) +#define MEM_IPC_LOCK_STATUS(n) (MEM_IPC(n) + 0x10u) + +#define MEM_BASE_IPC_INTR 0x40231000u +#define IPC_INTR_STRUCT_SIZE 0x20u +#define MEM_IPC_INTR(n) (MEM_BASE_IPC_INTR + (n) * IPC_INTR_STRUCT_SIZE) +#define MEM_IPC_INTR_MASK(n) (MEM_IPC_INTR(n) + 0x08u) +#define IPC_ACQUIRE_SUCCESS_MSK 0x80000000u +#define IPC_LOCK_ACQUIRED_MSK 0x80000000u + +#define IPC_ID 2u +#define IPC_INTR_ID 0u +#define IPC_TIMEOUT_MS 1000 + +#define SROMAPI_SIID_REQ 0x00000001u +#define SROMAPI_SIID_REQ_FAMILY_REVISION (SROMAPI_SIID_REQ | 0x000u) +#define SROMAPI_SIID_REQ_SIID_PROTECTION (SROMAPI_SIID_REQ | 0x100u) +#define SROMAPI_WRITEROW_REQ 0x05000100u +#define SROMAPI_PROGRAMROW_REQ 0x06000100u +#define SROMAPI_ERASESECTOR_REQ 0x14000100u +#define SROMAPI_ERASEALL_REQ 0x0A000100u +#define SROMAPI_ERASEROW_REQ 0x1C000100u + +#define SROMAPI_STATUS_MSK 0xF0000000u +#define SROMAPI_STAT_SUCCESS 0xA0000000u +#define SROMAPI_DATA_LOCATION_MSK 0x00000001u +#define SROMAPI_CALL_TIMEOUT_MS 1500 + +struct psoc6_target_info { + uint32_t silicon_id; + uint8_t protection; + uint32_t main_flash_sz; + uint32_t row_sz; + bool is_probed; +}; + +struct timeout { + int64_t start_time; + long timeout_ms; +}; + +struct row_region { + uint32_t addr; + size_t size; +}; + +static const struct row_region safe_sflash_regions[] = { + {0x16000800, 0x800}, /* SFLASH: User Data */ + {0x16001A00, 0x200}, /* SFLASH: NAR */ + {0x16005A00, 0xC00}, /* SFLASH: Public Key */ + {0x16007C00, 0x400}, /* SFLASH: TOC2 */ +}; + +#define SFLASH_NUM_REGIONS (sizeof(safe_sflash_regions) / sizeof(safe_sflash_regions[0])) + +static struct working_area *g_stack_area; +static struct armv7m_algorithm g_armv7m_info; + +/** *********************************************************************************************** + * @brief Initializes `struct timeout` structure with given timeout value + * @param to pointer to `struct timeout` structure + * @param timeout_ms timeout, in milliseconds + *************************************************************************************************/ +static void timeout_init(struct timeout *to, long timeout_ms) +{ + to->start_time = timeval_ms(); + to->timeout_ms = timeout_ms; +} + +/** *********************************************************************************************** + * @brief Returns true if given `struct timeout` structure has expired + * @param to pointer to `struct timeout` structure + * @return true if timeout expired + *************************************************************************************************/ +static bool timeout_expired(struct timeout *to) +{ + return (timeval_ms() - to->start_time) > to->timeout_ms; +} + +/** *********************************************************************************************** + * @brief Starts pseudo flash algorithm and leaves it running. Function allocates working area for + * algorithm code and CPU stack, adjusts stack pointer, uploads and starts the algorithm. + * Algorithm (a basic infinite loop) runs asynchronously while driver performs Flash operations. + * + * @param target target for the algorithm + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int sromalgo_prepare(struct target *target) +{ + int hr; + + /* Initialize Vector Table Offset register (in case FW modified it) */ + hr = target_write_u32(target, 0xE000ED08, 0x00000000); + if (hr != ERROR_OK) + return hr; + + /* Restore THUMB bit in xPSR register */ + const struct armv7m_common *cm = target_to_armv7m(target); + hr = cm->store_core_reg_u32(target, ARMV7M_xPSR, 0x01000000); + if (hr != ERROR_OK) + return hr; + + /* Allocate Working Area for Stack and Flash algorithm */ + hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &g_stack_area); + if (hr != ERROR_OK) + return hr; + + g_armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + g_armv7m_info.core_mode = ARM_MODE_THREAD; + + struct reg_param reg_params; + init_reg_param(®_params, "sp", 32, PARAM_OUT); + buf_set_u32(reg_params.value, 0, 32, g_stack_area->address + g_stack_area->size); + + /* Write basic infinite loop algorithm to target RAM */ + hr = target_write_u32(target, g_stack_area->address, 0xFEE7FEE7); + if (hr != ERROR_OK) + goto destroy_rp_free_wa; + + hr = target_start_algorithm(target, 0, NULL, 1, ®_params, g_stack_area->address, + 0, &g_armv7m_info); + if (hr != ERROR_OK) + goto destroy_rp_free_wa; + + destroy_reg_param(®_params); + + return hr; + +destroy_rp_free_wa: + /* Something went wrong, do some cleanup */ + destroy_reg_param(®_params); + + if (g_stack_area) { + target_free_working_area(target, g_stack_area); + g_stack_area = NULL; + } + + return hr; +} + +/** *********************************************************************************************** + * @brief Stops running flash algorithm and releases associated resources. + * This function is also used for cleanup in case of errors so g_stack_area may be NULL. + * These cases have to be handled gracefully. + * + * @param target current target + *************************************************************************************************/ +static void sromalgo_release(struct target *target) +{ + int hr = ERROR_OK; + + if (g_stack_area) { + /* Stop flash algorithm if it is running */ + if (target->running_alg) { + hr = target_halt(target); + if (hr != ERROR_OK) + goto exit_free_wa; + + hr = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, + IPC_TIMEOUT_MS, &g_armv7m_info); + if (hr != ERROR_OK) + goto exit_free_wa; + } + +exit_free_wa: + /* Free Stack/Flash algorithm working area */ + target_free_working_area(target, g_stack_area); + g_stack_area = NULL; + } +} + +/** *********************************************************************************************** + * @brief Waits for expected IPC lock status. PSoC6 uses IPC structures for inter-core + * communication. Same IPCs are used to invoke SROM API. IPC structure must be locked prior to + * invoking any SROM API. This ensures nothing else in the system will use same IPC thus corrupting + * our data. Locking is performed by ipc_acquire(), this function ensures that IPC is actually + * in expected state + * + * @param target current target + * @param ipc_id IPC index to poll. IPC #2 is dedicated for DAP access + * @param lock_expected expected lock status + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_expected) +{ + int hr; + uint32_t reg_val; + + struct timeout to; + timeout_init(&to, IPC_TIMEOUT_MS); + + while (!timeout_expired(&to)) { + /* Process any server requests */ + keep_alive(); + + /* Read IPC Lock status */ + hr = target_read_u32(target, MEM_IPC_LOCK_STATUS(ipc_id), ®_val); + if (hr != ERROR_OK) { + LOG_ERROR("Unable to read IPC Lock Status register"); + return hr; + } + + bool is_locked = (reg_val & IPC_LOCK_ACQUIRED_MSK) != 0; + + if (lock_expected == is_locked) + return ERROR_OK; + } + + if (target->coreid) { + LOG_WARNING("SROM API calls via CM4 target are supported on single-core PSoC6 devices only. " + "Please perform all Flash-related operations via CM0+ target on dual-core devices."); + } + + LOG_ERROR("Timeout polling IPC Lock Status"); + return ERROR_TARGET_TIMEOUT; +} + +/** *********************************************************************************************** + * @brief Acquires IPC structure. PSoC6 uses IPC structures for inter-core communication. + * Same IPCs are used to invoke SROM API. IPC structure must be locked prior to invoking any SROM API. + * This ensures nothing else in the system will use same IPC thus corrupting our data. + * This function locks the IPC. + * + * @param target current target + * @param ipc_id ipc_id IPC index to acquire. IPC #2 is dedicated for DAP access + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int ipc_acquire(struct target *target, char ipc_id) +{ + int hr = ERROR_OK; + bool is_acquired = false; + uint32_t reg_val; + + struct timeout to; + timeout_init(&to, IPC_TIMEOUT_MS); + + while (!timeout_expired(&to)) { + keep_alive(); + + hr = target_write_u32(target, MEM_IPC_ACQUIRE(ipc_id), IPC_ACQUIRE_SUCCESS_MSK); + if (hr != ERROR_OK) { + LOG_ERROR("Unable to write to IPC Acquire register"); + return hr; + } + + /* Check if data is written on first step */ + hr = target_read_u32(target, MEM_IPC_ACQUIRE(ipc_id), ®_val); + if (hr != ERROR_OK) { + LOG_ERROR("Unable to read IPC Acquire register"); + return hr; + } + + is_acquired = (reg_val & IPC_ACQUIRE_SUCCESS_MSK) != 0; + if (is_acquired) { + /* If IPC structure is acquired, the lock status should be set */ + hr = ipc_poll_lock_stat(target, ipc_id, true); + break; + } + } + + if (!is_acquired) + LOG_ERROR("Timeout acquiring IPC structure"); + + return hr; +} + +/** *********************************************************************************************** + * @brief Invokes SROM API functions which are responsible for Flash operations + * + * @param target current target + * @param req_and_params requect id of the function to invoke + * @param working_area address of memory buffer in target's memory space for SROM API parameters + * @param data_out pointer to variable which will be populated with execution status + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int call_sromapi(struct target *target, + uint32_t req_and_params, + uint32_t working_area, + uint32_t *data_out) +{ + int hr; + + bool is_data_in_ram = (req_and_params & SROMAPI_DATA_LOCATION_MSK) == 0; + + hr = ipc_acquire(target, IPC_ID); + if (hr != ERROR_OK) + return hr; + + if (is_data_in_ram) + hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), working_area); + else + hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), req_and_params); + + if (hr != ERROR_OK) + return hr; + + /* Enable notification interrupt of IPC_INTR_STRUCT0(CM0+) for IPC_STRUCT2 */ + hr = target_write_u32(target, MEM_IPC_INTR_MASK(IPC_INTR_ID), 1u << (16 + IPC_ID)); + if (hr != ERROR_OK) + return hr; + + hr = target_write_u32(target, MEM_IPC_NOTIFY(IPC_ID), 1); + if (hr != ERROR_OK) + return hr; + + /* Poll lock status */ + hr = ipc_poll_lock_stat(target, IPC_ID, false); + if (hr != ERROR_OK) + return hr; + + /* Poll Data byte */ + if (is_data_in_ram) + hr = target_read_u32(target, working_area, data_out); + else + hr = target_read_u32(target, MEM_IPC_DATA(IPC_ID), data_out); + + if (hr != ERROR_OK) { + LOG_ERROR("Error reading SROM API Status location"); + return hr; + } + + bool is_success = (*data_out & SROMAPI_STATUS_MSK) == SROMAPI_STAT_SUCCESS; + if (!is_success) { + LOG_ERROR("SROM API execution failed. Status: 0x%08X", (uint32_t)*data_out); + return ERROR_TARGET_FAILURE; + } + + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Retrieves SiliconID and Protection status of the target device + * @param target current target + * @param si_id pointer to variable, will be populated with SiliconID + * @param protection pointer to variable, will be populated with protection status + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int get_silicon_id(struct target *target, uint32_t *si_id, uint8_t *protection) +{ + int hr; + uint32_t family_rev, siid_prot; + + hr = sromalgo_prepare(target); + if (hr != ERROR_OK) + goto exit; + + /* Read FamilyID and Revision */ + hr = call_sromapi(target, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev); + if (hr != ERROR_OK) + goto exit; + + /* Read SiliconID and Protection */ + hr = call_sromapi(target, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot); + if (hr != ERROR_OK) + goto exit; + + *si_id = (siid_prot & 0x0000FFFF) << 16; + *si_id |= (family_rev & 0x00FF0000) >> 8; + *si_id |= (family_rev & 0x000000FF) >> 0; + + *protection = (siid_prot & 0x000F0000) >> 0x10; + +exit: + sromalgo_release(target); + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Translates Protection status to openocd-friendly boolean value + * @param bank current flash bank + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_protect_check(struct flash_bank *bank) +{ + int is_protected; + + struct psoc6_target_info *psoc6_info = bank->driver_priv; + int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection); + if (hr != ERROR_OK) + return hr; + + switch (psoc6_info->protection) { + case PROTECTION_VIRGIN: + case PROTECTION_NORMAL: + is_protected = 0; + break; + + case PROTECTION_UNKNOWN: + case PROTECTION_SECURE: + case PROTECTION_DEAD: + default: + is_protected = 1; + break; + } + + for (int i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = is_protected; + + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Dummy function, Life Cycle transition is not currently supported + * @return ERROR_OK always + *************************************************************************************************/ +static int psoc6_protect(struct flash_bank *bank, int set, int first, int last) +{ + (void)bank; + (void)set; + (void)first; + (void)last; + + LOG_WARNING("Life Cycle transition for PSoC6 is not supported"); + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Translates Protection status to string + * @param protection protection value + * @return pointer to const string describintg protection status + *************************************************************************************************/ +static const char *protection_to_str(uint8_t protection) +{ + switch (protection) { + case PROTECTION_VIRGIN: + return "VIRGIN"; + break; + case PROTECTION_NORMAL: + return "NORMAL"; + break; + case PROTECTION_SECURE: + return "SECURE"; + break; + case PROTECTION_DEAD: + return "DEAD"; + break; + case PROTECTION_UNKNOWN: + default: + return "UNKNOWN"; + break; + } +} + +/** *********************************************************************************************** + * @brief psoc6_get_info Displays human-readable information about acquired device + * @param bank current flash bank + * @param buf pointer to buffer for human-readable text + * @param buf_size size of the buffer + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct psoc6_target_info *psoc6_info = bank->driver_priv; + + if (psoc6_info->is_probed == false) + return ERROR_FAIL; + + int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection); + if (hr != ERROR_OK) + return hr; + + snprintf(buf, buf_size, + "PSoC6 Silicon ID: 0x%08X\n" + "Protection: %s\n" + "Main Flash size: %d kB\n" + "Work Flash size: 32 kB\n", + psoc6_info->silicon_id, + protection_to_str(psoc6_info->protection), + psoc6_info->main_flash_sz / 1024); + + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Checks if given flash bank belongs to Supervisory Flash + * @param bank current flash bank + * @return true if flash bank belongs to Supervisory Flash + *************************************************************************************************/ +static bool is_sflash_bank(struct flash_bank *bank) +{ + for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) { + if (bank->base == safe_sflash_regions[i].addr) + return true; + } + + return false; +} + +/** *********************************************************************************************** + * @brief Checks if given flash bank belongs to Work Flash + * @param bank current flash bank + * @return true if flash bank belongs to Work Flash + *************************************************************************************************/ +static inline bool is_wflash_bank(struct flash_bank *bank) +{ + return (bank->base == MEM_BASE_WFLASH); +} + +/** *********************************************************************************************** + * @brief Checks if given flash bank belongs to Main Flash + * @param bank current flash bank + * @return true if flash bank belongs to Main Flash + *************************************************************************************************/ +static inline bool is_mflash_bank(struct flash_bank *bank) +{ + return (bank->base == MEM_BASE_MFLASH); +} + +/** *********************************************************************************************** + * @brief Probes the device and populates related data structures with target flash geometry data. + * This is done in non-intrusive way, no SROM API calls are involved so GDB can safely attach to a + * running target. Function assumes that size of Work Flash is 32kB (true for all current part numbers) + * + * @param bank current flash bank + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct psoc6_target_info *psoc6_info = bank->driver_priv; + + int hr = ERROR_OK; + + /* Retrieve data from SPCIF_GEOMATRY */ + uint32_t geom; + target_read_u32(target, PSOC6_SPCIF_GEOMETRY, &geom); + uint32_t row_sz_lg2 = (geom & 0xF0) >> 4; + uint32_t row_sz = (0x01 << row_sz_lg2); + uint32_t row_cnt = 1 + ((geom & 0x00FFFF00) >> 8); + uint32_t bank_cnt = 1 + ((geom & 0xFF000000) >> 24); + + /* Calculate size of Main Flash*/ + uint32_t flash_sz_bytes = bank_cnt * row_cnt * row_sz; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + size_t bank_size = 0; + + if (is_mflash_bank(bank)) + bank_size = flash_sz_bytes; + else if (is_wflash_bank(bank)) + bank_size = MEM_WFLASH_SIZE; + else if (is_sflash_bank(bank)) { + for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) { + if (safe_sflash_regions[i].addr == bank->base) { + bank_size = safe_sflash_regions[i].size; + break; + } + } + } + + if (bank_size == 0) { + LOG_ERROR("Invalid Flash Bank base address in config file"); + return ERROR_FLASH_BANK_INVALID; + } + + size_t num_sectors = bank_size / row_sz; + bank->size = bank_size; + bank->chip_width = 4; + bank->bus_width = 4; + bank->erased_value = 0; + bank->default_padded_value = 0; + + bank->num_sectors = num_sectors; + bank->sectors = calloc(num_sectors, sizeof(struct flash_sector)); + for (size_t i = 0; i < num_sectors; i++) { + bank->sectors[i].size = row_sz; + bank->sectors[i].offset = i * row_sz; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + psoc6_info->is_probed = true; + psoc6_info->main_flash_sz = flash_sz_bytes; + psoc6_info->row_sz = row_sz; + + return hr; +} + +/** *********************************************************************************************** + * @brief Probes target device only if it hasn't been probed yet + * @param bank current flash bank + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_auto_probe(struct flash_bank *bank) +{ + struct psoc6_target_info *psoc6_info = bank->driver_priv; + int hr; + + if (psoc6_info->is_probed) + hr = ERROR_OK; + else + hr = psoc6_probe(bank); + + return hr; +} + +/** *********************************************************************************************** + * @brief Erases single sector (256k) on target device + * @param bank current flash bank + * @param wa working area for SROM API parameters + * @param addr starting address of the sector + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_erase_sector(struct flash_bank *bank, struct working_area *wa, uint32_t addr) +{ + struct target *target = bank->target; + + LOG_DEBUG("Erasing SECTOR @%08X", addr); + + int hr = target_write_u32(target, wa->address, SROMAPI_ERASESECTOR_REQ); + if (hr != ERROR_OK) + return hr; + + hr = target_write_u32(target, wa->address + 0x04, addr); + if (hr != ERROR_OK) + return hr; + + uint32_t data_out; + hr = call_sromapi(target, SROMAPI_ERASESECTOR_REQ, wa->address, &data_out); + if (hr != ERROR_OK) + LOG_ERROR("SECTOR @%08X not erased!", addr); + + return hr; +} + +/** *********************************************************************************************** + * @brief Erases single row (512b) on target device + * @param bank current flash bank + * @param wa working area for SROM API parameters + * @param addr starting address of the flash row + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_erase_row(struct flash_bank *bank, struct working_area *wa, uint32_t addr) +{ + struct target *target = bank->target; + + LOG_DEBUG("Erasing ROW @%08X", addr); + + int hr = target_write_u32(target, wa->address, SROMAPI_ERASEROW_REQ); + if (hr != ERROR_OK) + return hr; + + hr = target_write_u32(target, wa->address + 0x04, addr); + if (hr != ERROR_OK) + return hr; + + uint32_t data_out; + hr = call_sromapi(target, SROMAPI_ERASEROW_REQ, wa->address, &data_out); + if (hr != ERROR_OK) + LOG_ERROR("ROW @%08X not erased!", addr); + + return hr; +} + +/** *********************************************************************************************** + * @brief Performs Erase operation. Function will try to use biggest erase block possible to + * speedup the operation. + * + * @param bank current flash bank + * @param first first sector to erase + * @param last last sector to erase + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + struct psoc6_target_info *psoc6_info = bank->driver_priv; + const uint32_t sector_size = is_wflash_bank(bank) ? WFLASH_SECTOR_SIZE : MFLASH_SECTOR_SIZE; + + int hr; + struct working_area *wa; + + if (is_sflash_bank(bank)) { + LOG_INFO("Erase operation on Supervisory Flash is not required, skipping"); + return ERROR_OK; + } + + hr = sromalgo_prepare(target); + if (hr != ERROR_OK) + goto exit; + + hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa); + if (hr != ERROR_OK) + goto exit; + + /* Number of rows in single sector */ + const int rows_in_sector = sector_size / psoc6_info->row_sz; + + while (last >= first) { + /* Erase Sector if we are on sector boundary and erase size covers whole sector */ + if ((first % rows_in_sector) == 0 && + (last - first + 1) >= rows_in_sector) { + hr = psoc6_erase_sector(bank, wa, bank->base + first * psoc6_info->row_sz); + if (hr != ERROR_OK) + goto exit_free_wa; + + for (int i = first; i < first + rows_in_sector; i++) + bank->sectors[i].is_erased = 1; + + first += rows_in_sector; + } else { + /* Perform Row Erase otherwise */ + hr = psoc6_erase_row(bank, wa, bank->base + first * psoc6_info->row_sz); + if (hr != ERROR_OK) + goto exit_free_wa; + + bank->sectors[first].is_erased = 1; + first += 1; + } + } + +exit_free_wa: + target_free_working_area(target, wa); +exit: + sromalgo_release(target); + return hr; +} + +/** *********************************************************************************************** + * @brief Programs single Flash Row + * @param bank current flash bank + * @param addr address of the flash row + * @param buffer pointer to the buffer with data + * @param is_sflash true if current flash bank belongs to Supervisory Flash + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_program_row(struct flash_bank *bank, + uint32_t addr, + const uint8_t *buffer, + bool is_sflash) +{ + struct target *target = bank->target; + struct psoc6_target_info *psoc6_info = bank->driver_priv; + struct working_area *wa; + const uint32_t sromapi_req = is_sflash ? SROMAPI_WRITEROW_REQ : SROMAPI_PROGRAMROW_REQ; + uint32_t data_out; + int hr = ERROR_OK; + + LOG_DEBUG("Programming ROW @%08X", addr); + + hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa); + if (hr != ERROR_OK) + goto exit; + + hr = target_write_u32(target, wa->address, sromapi_req); + if (hr != ERROR_OK) + goto exit_free_wa; + + hr = target_write_u32(target, + wa->address + 0x04, + 0x106); + if (hr != ERROR_OK) + goto exit_free_wa; + + hr = target_write_u32(target, wa->address + 0x08, addr); + if (hr != ERROR_OK) + goto exit_free_wa; + + hr = target_write_u32(target, wa->address + 0x0C, wa->address + 0x10); + if (hr != ERROR_OK) + goto exit_free_wa; + + hr = target_write_buffer(target, wa->address + 0x10, psoc6_info->row_sz, buffer); + if (hr != ERROR_OK) + goto exit_free_wa; + + hr = call_sromapi(target, sromapi_req, wa->address, &data_out); + +exit_free_wa: + target_free_working_area(target, wa); + +exit: + return hr; +} + +/** *********************************************************************************************** + * @brief Performs Program operation + * @param bank current flash bank + * @param buffer pointer to the buffer with data + * @param offset starting offset in falsh bank + * @param count number of bytes in buffer + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_program(struct flash_bank *bank, + const uint8_t *buffer, + uint32_t offset, + uint32_t count) +{ + struct target *target = bank->target; + struct psoc6_target_info *psoc6_info = bank->driver_priv; + const bool is_sflash = is_sflash_bank(bank); + int hr; + + uint8_t page_buf[psoc6_info->row_sz]; + + hr = sromalgo_prepare(target); + if (hr != ERROR_OK) + goto exit; + + while (count) { + uint32_t row_offset = offset % psoc6_info->row_sz; + uint32_t aligned_addr = bank->base + offset - row_offset; + uint32_t row_bytes = MIN(psoc6_info->row_sz - row_offset, count); + + memset(page_buf, 0, sizeof(page_buf)); + memcpy(&page_buf[row_offset], buffer, row_bytes); + + hr = psoc6_program_row(bank, aligned_addr, page_buf, is_sflash); + if (hr != ERROR_OK) { + LOG_ERROR("Failed to program Flash at address 0x%08X", aligned_addr); + goto exit; + } + + buffer += row_bytes; + offset += row_bytes; + count -= row_bytes; + } + +exit: + sromalgo_release(target); + return hr; +} + +/** *********************************************************************************************** + * @brief Performs Mass Erase operation + * @param bank flash bank index to erase + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +COMMAND_HANDLER(psoc6_handle_mass_erase_command) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int hr = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (hr != ERROR_OK) + return hr; + + hr = psoc6_erase(bank, 0, bank->num_sectors - 1); + + return hr; +} + +/** *********************************************************************************************** + * @brief Simulates broken Vector Catch + * Function will try to determine entry point of user application. If it succeeds it will set HW + * breakpoint at that address, issue SW Reset and remove the breakpoint afterwards. + * In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will + * reset CM4 anyway, so using SYSRESETREQ is safe here. + * In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core. + * + * @param target current target + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +int handle_reset_halt(struct target *target) +{ + int hr; + uint32_t reset_addr; + bool is_cm0 = (target->coreid == 0); + + /* Halt target device */ + if (target->state != TARGET_HALTED) { + hr = target_halt(target); + if (hr != ERROR_OK) + return hr; + + target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS); + if (hr != ERROR_OK) + return hr; + } + + /* Read Vector Offset register */ + uint32_t vt_base; + const uint32_t vt_offset_reg = is_cm0 ? 0x402102B0 : 0x402102C0; + hr = target_read_u32(target, vt_offset_reg, &vt_base); + if (hr != ERROR_OK) + return ERROR_OK; + + /* Invalid value means flash is empty */ + vt_base &= 0xFFFFFF00; + if ((vt_base == 0) || (vt_base == 0xFFFFFF00)) + return ERROR_OK; + + /* Read Reset Vector value*/ + hr = target_read_u32(target, vt_base + 4, &reset_addr); + if (hr != ERROR_OK) + return hr; + + /* Invalid value means flash is empty */ + if ((reset_addr == 0) || (reset_addr == 0xFFFFFF00)) + return ERROR_OK; + + + /* Set breakpoint at User Application entry point */ + hr = breakpoint_add(target, reset_addr, 2, BKPT_HARD); + if (hr != ERROR_OK) + return hr; + + const struct armv7m_common *cm = target_to_armv7m(target); + + /* PSoC6 reboots immediatelly after issuing SYSRESETREQ / VECTRESET + * this disables SWD/JTAG pins momentarily and may break communication + * Ignoring return value of mem_ap_write_atomic_u32 seems to be ok here */ + if (is_cm0) { + /* Reset the CM0 by asserting SYSRESETREQ. This will also reset CM4 */ + LOG_INFO("psoc6.cm0: bkpt @0x%08X, issuing SYSRESETREQ", reset_addr); + mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR, + AIRCR_VECTKEY | AIRCR_SYSRESETREQ); + } else { + LOG_INFO("psoc6.cm4: bkpt @0x%08X, issuing VECTRESET", reset_addr); + mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR, + AIRCR_VECTKEY | AIRCR_VECTRESET); + } + + /* Wait 100ms for bootcode and reinitialize DAP */ + usleep(100000); + dap_dp_init(cm->debug_ap->dap); + + target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS); + + /* Remove the break point */ + breakpoint_remove(target, reset_addr); + + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Simulates broken Vector Catch + * Function will try to determine entry point of user application. If it succeeds it will set HW + * breakpoint at that address, issue SW Reset and remove the breakpoint afterwards. + * In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will + * reset CM4 anyway, so using SYSRESETREQ is safe here. + * In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core. + * + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +COMMAND_HANDLER(psoc6_handle_reset_halt) +{ + if (CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct target *target = get_current_target(CMD_CTX); + return handle_reset_halt(target); +} + +FLASH_BANK_COMMAND_HANDLER(psoc6_flash_bank_command) +{ + struct psoc6_target_info *psoc6_info; + int hr = ERROR_OK; + + if (CMD_ARGC < 6) + hr = ERROR_COMMAND_SYNTAX_ERROR; + else { + psoc6_info = calloc(1, sizeof(struct psoc6_target_info)); + psoc6_info->is_probed = false; + bank->driver_priv = psoc6_info; + } + return hr; +} + +static const struct command_registration psoc6_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = psoc6_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank", + .help = "Erases entire Main Flash", + }, + { + .name = "reset_halt", + .handler = psoc6_handle_reset_halt, + .mode = COMMAND_EXEC, + .usage = NULL, + .help = "Tries to simulate broken Vector Catch", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration psoc6_command_handlers[] = { + { + .name = "psoc6", + .mode = COMMAND_ANY, + .help = "PSoC 6 flash command group", + .usage = "", + .chain = psoc6_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver psoc6_flash = { + .name = "psoc6", + .commands = psoc6_command_handlers, + .flash_bank_command = psoc6_flash_bank_command, + .erase = psoc6_erase, + .protect = psoc6_protect, + .write = psoc6_program, + .read = default_flash_read, + .probe = psoc6_probe, + .auto_probe = psoc6_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = psoc6_protect_check, + .info = psoc6_get_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/sim3x.c b/src/flash/nor/sim3x.c index ce9a21e..f282ba0 100644 --- a/src/flash/nor/sim3x.c +++ b/src/flash/nor/sim3x.c @@ -1122,5 +1122,6 @@ struct flash_driver sim3x_flash = { .auto_probe = sim3x_auto_probe, .erase_check = default_flash_blank_check, .protect_check = sim3x_flash_protect_check, - .info = sim3x_flash_info + .info = sim3x_flash_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/spi.c b/src/flash/nor/spi.c index b255edb..6ac253d 100644 --- a/src/flash/nor/spi.c +++ b/src/flash/nor/spi.c @@ -54,6 +54,7 @@ const struct flash_device flash_devices[] = { FLASH_ID("sp s25fl164k", 0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 0x800000), FLASH_ID("sp s25fl128", 0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 0x1000000), FLASH_ID("sp s25fl256", 0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 0x2000000), + FLASH_ID("cy s25fl256", 0xd8, 0xc7, 0x00196001, 0x100, 0x10000, 0x2000000), FLASH_ID("atmel 25f512", 0x52, 0xc7, 0x0065001f, 0x80, 0x8000, 0x10000), FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000), FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000), diff --git a/src/flash/nor/stellaris.c b/src/flash/nor/stellaris.c index d28ceee..79aaf3b 100644 --- a/src/flash/nor/stellaris.c +++ b/src/flash/nor/stellaris.c @@ -1452,4 +1452,5 @@ struct flash_driver stellaris_flash = { .erase_check = default_flash_blank_check, .protect_check = stellaris_protect_check, .info = get_stellaris_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c index d446707..faada9a 100644 --- a/src/flash/nor/stm32f1x.c +++ b/src/flash/nor/stm32f1x.c @@ -572,45 +572,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, struct armv7m_algorithm armv7m_info; int retval = ERROR_OK; - /* see contrib/loaders/flash/stm32f1x.S for src */ - static const uint8_t stm32x_flash_write_code[] = { - /* #define STM32_FLASH_SR_OFFSET 0x0C */ - /* wait_fifo: */ - 0x16, 0x68, /* ldr r6, [r2, #0] */ - 0x00, 0x2e, /* cmp r6, #0 */ - 0x18, 0xd0, /* beq exit */ - 0x55, 0x68, /* ldr r5, [r2, #4] */ - 0xb5, 0x42, /* cmp r5, r6 */ - 0xf9, 0xd0, /* beq wait_fifo */ - 0x2e, 0x88, /* ldrh r6, [r5, #0] */ - 0x26, 0x80, /* strh r6, [r4, #0] */ - 0x02, 0x35, /* adds r5, #2 */ - 0x02, 0x34, /* adds r4, #2 */ - /* busy: */ - 0xc6, 0x68, /* ldr r6, [r0, #STM32_FLASH_SR_OFFSET] */ - 0x01, 0x27, /* movs r7, #1 */ - 0x3e, 0x42, /* tst r6, r7 */ - 0xfb, 0xd1, /* bne busy */ - 0x14, 0x27, /* movs r7, #0x14 */ - 0x3e, 0x42, /* tst r6, r7 */ - 0x08, 0xd1, /* bne error */ - 0x9d, 0x42, /* cmp r5, r3 */ - 0x01, 0xd3, /* bcc no_wrap */ - 0x15, 0x46, /* mov r5, r2 */ - 0x08, 0x35, /* adds r5, #8 */ - /* no_wrap: */ - 0x55, 0x60, /* str r5, [r2, #4] */ - 0x01, 0x39, /* subs r1, r1, #1 */ - 0x00, 0x29, /* cmp r1, #0 */ - 0x02, 0xd0, /* beq exit */ - 0xe5, 0xe7, /* b wait_fifo */ - /* error: */ - 0x00, 0x20, /* movs r0, #0 */ - 0x50, 0x60, /* str r0, [r2, #4] */ - /* exit: */ - 0x30, 0x46, /* mov r0, r6 */ - 0x00, 0xbe, /* bkpt #0 */ +#include "../../../contrib/loaders/flash/stm32/stm32f1x.inc" }; /* flash write code */ @@ -622,8 +585,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, retval = target_write_buffer(target, write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); return retval; + } /* memory buffer */ while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { @@ -1647,4 +1612,5 @@ struct flash_driver stm32f1x_flash = { .erase_check = default_flash_blank_check, .protect_check = stm32x_protect_check, .info = get_stm32x_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index b0992b4..413d04d 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -252,6 +252,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout) /* Clear but report errors */ if (status & FLASH_ERROR) { + if (retval == ERROR_OK) + retval = ERROR_FAIL; /* If this operation fails, we ignore it and report the original * retval */ @@ -584,45 +586,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, struct armv7m_algorithm armv7m_info; int retval = ERROR_OK; - /* see contrib/loaders/flash/stm32f2x.S for src */ - static const uint8_t stm32x_flash_write_code[] = { - /* wait_fifo: */ - 0xD0, 0xF8, 0x00, 0x80, /* ldr r8, [r0, #0] */ - 0xB8, 0xF1, 0x00, 0x0F, /* cmp r8, #0 */ - 0x1A, 0xD0, /* beq exit */ - 0x47, 0x68, /* ldr r7, [r0, #4] */ - 0x47, 0x45, /* cmp r7, r8 */ - 0xF7, 0xD0, /* beq wait_fifo */ - - 0xDF, 0xF8, 0x34, 0x60, /* ldr r6, STM32_PROG16 */ - 0x26, 0x61, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */ - 0x37, 0xF8, 0x02, 0x6B, /* ldrh r6, [r7], #0x02 */ - 0x22, 0xF8, 0x02, 0x6B, /* strh r6, [r2], #0x02 */ - 0xBF, 0xF3, 0x4F, 0x8F, /* dsb sy */ - /* busy: */ - 0xE6, 0x68, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */ - 0x16, 0xF4, 0x80, 0x3F, /* tst r6, #0x10000 */ - 0xFB, 0xD1, /* bne busy */ - 0x16, 0xF0, 0xF0, 0x0F, /* tst r6, #0xf0 */ - 0x07, 0xD1, /* bne error */ - - 0x8F, 0x42, /* cmp r7, r1 */ - 0x28, 0xBF, /* it cs */ - 0x00, 0xF1, 0x08, 0x07, /* addcs r7, r0, #8 */ - 0x47, 0x60, /* str r7, [r0, #4] */ - 0x01, 0x3B, /* subs r3, r3, #1 */ - 0x13, 0xB1, /* cbz r3, exit */ - 0xDF, 0xE7, /* b wait_fifo */ - /* error: */ - 0x00, 0x21, /* movs r1, #0 */ - 0x41, 0x60, /* str r1, [r0, #4] */ - /* exit: */ - 0x30, 0x46, /* mov r0, r6 */ - 0x00, 0xBE, /* bkpt #0x00 */ - - /* <STM32_PROG16>: */ - 0x01, 0x01, 0x00, 0x00, /* .word 0x00000101 */ +#include "../../../contrib/loaders/flash/stm32/stm32f2x.inc" }; if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), @@ -634,8 +599,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, retval = target_write_buffer(target, write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); return retval; + } /* memory buffer */ while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { @@ -1634,4 +1601,5 @@ struct flash_driver stm32f2x_flash = { .erase_check = default_flash_blank_check, .protect_check = stm32x_protect_check, .info = get_stm32x_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/stm32h7x.c b/src/flash/nor/stm32h7x.c index 01e6f06..fcfcf91 100644 --- a/src/flash/nor/stm32h7x.c +++ b/src/flash/nor/stm32h7x.c @@ -64,7 +64,7 @@ #define FLASH_WRPERR (1 << 17) /* Write protection error */ #define FLASH_PGSERR (1 << 18) /* Programming sequence error */ #define FLASH_STRBERR (1 << 19) /* Strobe error */ -#define FLASH_INCERR (1 << 21) /* Increment error */ +#define FLASH_INCERR (1 << 21) /* Inconsistency error */ #define FLASH_OPERR (1 << 22) /* Operation error */ #define FLASH_RDPERR (1 << 23) /* Read Protection error */ #define FLASH_RDSERR (1 << 24) /* Secure Protection error */ @@ -220,6 +220,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout) /* Clear error + EOP flags but report errors */ if (status & FLASH_ERROR) { + if (retval == ERROR_OK) + retval = ERROR_FAIL; /* If this operation fails, we ignore it and report the original retval */ target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), status); } @@ -495,7 +497,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last) retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); if (retval != ERROR_OK) { - LOG_ERROR("erase time-out error sector %d", i); + LOG_ERROR("erase time-out or operation error sector %d", i); return retval; } bank->sectors[i].is_erased = 1; @@ -568,51 +570,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; int retval = ERROR_OK; - /* see contrib/loaders/flash/smt32h7x.S for src */ static const uint8_t stm32x_flash_write_code[] = { - /* <code>: */ - 0x45, 0x68, /* ldr r5, [r0, #4] */ - /* <wait_fifo>: */ - 0x06, 0x68, /* ldr r6, [r0, #0] */ - 0x26, 0xb3, /* cbz r6, <exit> */ - 0x76, 0x1b, /* subs r6, r6, r5 */ - 0x42, 0xbf, /* ittt mi */ - 0x76, 0x18, /* addmi r6, r6, r1 */ - 0x36, 0x1a, /* submi r6, r6, r0 */ - 0x08, 0x3e, /* submi r6, #8 */ - 0x20, 0x2e, /* cmp r6, #32 */ - 0xf6, 0xd3, /* bcc.n <wait_fifo> */ - 0x4f, 0xf0, 0x32, 0x06, /* mov.w r6, #STM32_PROG */ - 0xe6, 0x60, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */ - 0x4f, 0xf0, 0x08, 0x07, /* mov.w r7, #8 */ - /* <write_flash>: */ - 0x55, 0xf8, 0x04, 0x6b, /* ldr.w r6, [r5], #4 */ - 0x42, 0xf8, 0x04, 0x6b, /* str.w r6, [r2], #4 */ - 0xbf, 0xf3, 0x4f, 0x8f, /* dsb sy */ - 0x8d, 0x42, /* cmp r5, r1 */ - 0x28, 0xbf, /* it cs */ - 0x00, 0xf1, 0x08, 0x05, /* addcs.w r5, r0, #8 */ - 0x01, 0x3f, /* subs r7, #1 */ - 0xf3, 0xd1, /* bne.n <write_flash> */ - /* <busy>: */ - 0x26, 0x69, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */ - 0x16, 0xf0, 0x01, 0x0f, /* tst.w r6, #STM32_SR_BUSY_MASK */ - 0xfb, 0xd1, /* bne.n <busy> */ - 0x05, 0x4f, /* ldr r7, [pc, #20] ; (<stm32_sr_error_mask>) */ - 0x3e, 0x42, /* tst r6, r7 */ - 0x03, 0xd1, /* bne.n <error> */ - 0x45, 0x60, /* str r5, [r0, #4] */ - 0x01, 0x3b, /* subs r3, #1 */ - 0xdb, 0xd1, /* bne.n <wait_fifo> */ - 0x01, 0xe0, /* b.n <exit> */ - /* <error>: */ - 0x00, 0x27, /* movs r7, #0 */ - 0x47, 0x60, /* str r7, [r0, #4] */ - /* <exit>: */ - 0x30, 0x46, /* mov r0, r6 */ - 0x00, 0xbe, /* bkpt 0x0000 */ - /* <stm32_sr_error_mask>: */ - 0x00, 0x00, 0xee, 0x03 /* .word 0x03ee0000 ; (STM32_SR_ERROR_MASK) */ +#include "../../../contrib/loaders/flash/stm32/stm32h7x.inc" }; if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), @@ -624,8 +583,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, retval = target_write_buffer(target, write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); return retval; + } /* memory buffer */ while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { @@ -1180,4 +1141,5 @@ struct flash_driver stm32h7x_flash = { .erase_check = default_flash_blank_check, .protect_check = stm32x_protect_check, .info = stm32x_get_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 6a1fa07..4fb7e03 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -187,6 +187,8 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout) /* Clear but report errors */ if (status & FLASH_ERROR) { + if (retval == ERROR_OK) + retval = ERROR_FAIL; /* If this operation fails, we ignore it and report the original * retval */ @@ -461,19 +463,8 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, struct armv7m_algorithm armv7m_info; int retval = ERROR_OK; - /* See contrib/loaders/flash/stm32l4x.S for source and - * hints how to generate the data! - */ - static const uint8_t stm32l4_flash_write_code[] = { - 0xd0, 0xf8, 0x00, 0x80, 0xb8, 0xf1, 0x00, 0x0f, 0x21, 0xd0, 0x45, 0x68, - 0xb8, 0xeb, 0x05, 0x06, 0x44, 0xbf, 0x76, 0x18, 0x36, 0x1a, 0x08, 0x2e, - 0xf2, 0xd3, 0xdf, 0xf8, 0x36, 0x60, 0x66, 0x61, 0xf5, 0xe8, 0x02, 0x67, - 0xe2, 0xe8, 0x02, 0x67, 0xbf, 0xf3, 0x4f, 0x8f, 0x26, 0x69, 0x16, 0xf4, - 0x80, 0x3f, 0xfb, 0xd1, 0x16, 0xf0, 0xfa, 0x0f, 0x07, 0xd1, 0x8d, 0x42, - 0x28, 0xbf, 0x00, 0xf1, 0x08, 0x05, 0x45, 0x60, 0x01, 0x3b, 0x13, 0xb1, - 0xda, 0xe7, 0x00, 0x21, 0x41, 0x60, 0x30, 0x46, 0x00, 0xbe, 0x01, 0x00, - 0x00, 0x00 +#include "../../../contrib/loaders/flash/stm32/stm32l4x.inc" }; if (target_alloc_working_area(target, sizeof(stm32l4_flash_write_code), @@ -485,8 +476,10 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, retval = target_write_buffer(target, write_algorithm->address, sizeof(stm32l4_flash_write_code), stm32l4_flash_write_code); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); return retval; + } /* memory buffer */ while (target_alloc_working_area_try(target, buffer_size, &source) != @@ -953,4 +946,5 @@ struct flash_driver stm32l4x_flash = { .erase_check = default_flash_blank_check, .protect_check = stm32l4_protect_check, .info = get_stm32l4_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c index fdfaad4..3251df3 100644 --- a/src/flash/nor/stm32lx.c +++ b/src/flash/nor/stm32lx.c @@ -146,7 +146,7 @@ static const struct stm32lx_rev stm32_425_revs[] = { { 0x1000, "A" }, { 0x2000, "B" }, { 0x2008, "Y" }, }; static const struct stm32lx_rev stm32_427_revs[] = { - { 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" }, + { 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" }, { 0x10f8, "V" }, }; static const struct stm32lx_rev stm32_429_revs[] = { { 0x1000, "A" }, { 0x1018, "Z" }, @@ -448,10 +448,8 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff int retval = ERROR_OK; - /* see contib/loaders/flash/stm32lx.S for src */ - static const uint8_t stm32lx_flash_write_code[] = { - 0x92, 0x00, 0x8A, 0x18, 0x01, 0xE0, 0x08, 0xC9, 0x08, 0xC0, 0x91, 0x42, 0xFB, 0xD1, 0x00, 0xBE +#include "../../../contrib/loaders/flash/stm32/stm32lx.inc" }; /* Make sure we're performing a half-page aligned write. */ @@ -965,6 +963,7 @@ struct flash_driver stm32lx_flash = { .erase_check = default_flash_blank_check, .protect_check = stm32lx_protect_check, .info = stm32lx_get_info, + .free_driver_priv = default_flash_free_driver_priv, }; /* Static methods implementation */ diff --git a/src/flash/nor/stmsmi.c b/src/flash/nor/stmsmi.c index 781ea3b..c839bf7 100644 --- a/src/flash/nor/stmsmi.c +++ b/src/flash/nor/stmsmi.c @@ -654,4 +654,5 @@ struct flash_driver stmsmi_flash = { .erase_check = default_flash_blank_check, .protect_check = stmsmi_protect_check, .info = get_stmsmi_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/str7x.c b/src/flash/nor/str7x.c index 11179f5..015202a 100644 --- a/src/flash/nor/str7x.c +++ b/src/flash/nor/str7x.c @@ -812,4 +812,5 @@ struct flash_driver str7x_flash = { .erase_check = default_flash_blank_check, .protect_check = str7x_protect_check, .info = get_str7x_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/str9x.c b/src/flash/nor/str9x.c index 3b7ca2a..37700ce 100644 --- a/src/flash/nor/str9x.c +++ b/src/flash/nor/str9x.c @@ -679,4 +679,5 @@ struct flash_driver str9x_flash = { .auto_probe = str9x_probe, .erase_check = default_flash_blank_check, .protect_check = str9x_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/str9xpec.c b/src/flash/nor/str9xpec.c index eb391e8..29e0977 100644 --- a/src/flash/nor/str9xpec.c +++ b/src/flash/nor/str9xpec.c @@ -1207,4 +1207,5 @@ struct flash_driver str9xpec_flash = { .auto_probe = str9xpec_probe, .erase_check = str9xpec_erase_check, .protect_check = str9xpec_protect_check, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/tcl.c b/src/flash/nor/tcl.c index e5e2801..95ca819 100644 --- a/src/flash/nor/tcl.c +++ b/src/flash/nor/tcl.c @@ -3,6 +3,7 @@ * Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> * * Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> * * Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> * + * Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.cz> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -287,24 +288,6 @@ COMMAND_HANDLER(handle_flash_erase_address_command) return retval; } -static int flash_check_sector_parameters(struct command_context *cmd_ctx, - uint32_t first, uint32_t last, uint32_t num_sectors) -{ - if (!(first <= last)) { - command_print(cmd_ctx, "ERROR: " - "first sector must be <= last sector"); - return ERROR_FAIL; - } - - if (!(last <= (num_sectors - 1))) { - command_print(cmd_ctx, "ERROR: last sector must be <= %" PRIu32, - num_sectors - 1); - return ERROR_FAIL; - } - - return ERROR_OK; -} - COMMAND_HANDLER(handle_flash_erase_command) { if (CMD_ARGC != 3) @@ -326,9 +309,18 @@ COMMAND_HANDLER(handle_flash_erase_command) else COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last); - retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors); - if (retval != ERROR_OK) - return retval; + if (!(first <= last)) { + command_print(CMD_CTX, "ERROR: " + "first sector must be <= last"); + return ERROR_FAIL; + } + + if (!(last <= (uint32_t)(p->num_sectors - 1))) { + command_print(CMD_CTX, "ERROR: " + "last sector must be <= %" PRIu32, + p->num_sectors - 1); + return ERROR_FAIL; + } struct duration bench; duration_start(&bench); @@ -374,15 +366,28 @@ COMMAND_HANDLER(handle_flash_protect_command) bool set; COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set); - retval = flash_check_sector_parameters(CMD_CTX, first, last, num_blocks); - if (retval != ERROR_OK) - return retval; + if (!(first <= last)) { + command_print(CMD_CTX, "ERROR: " + "first %s must be <= last", + (p->num_prot_blocks) ? "block" : "sector"); + return ERROR_FAIL; + } + + if (!(last <= (uint32_t)(num_blocks - 1))) { + command_print(CMD_CTX, "ERROR: " + "last %s must be <= %" PRIu32, + (p->num_prot_blocks) ? "block" : "sector", + num_blocks - 1); + return ERROR_FAIL; + } retval = flash_driver_protect(p, set, first, last); if (retval == ERROR_OK) { - command_print(CMD_CTX, "%s protection for sectors %" PRIu32 + command_print(CMD_CTX, "%s protection for %s %" PRIu32 " through %" PRIu32 " on flash bank %d", - (set) ? "set" : "cleared", first, last, p->bank_number); + (set) ? "set" : "cleared", + (p->num_prot_blocks) ? "blocks" : "sectors", + first, last, p->bank_number); } return retval; @@ -460,42 +465,29 @@ COMMAND_HANDLER(handle_flash_write_image_command) COMMAND_HANDLER(handle_flash_fill_command) { - int err = ERROR_OK; - uint32_t address; + target_addr_t address; uint32_t pattern; uint32_t count; - uint32_t wrote = 0; - uint32_t cur_size = 0; - uint32_t chunk_count; struct target *target = get_current_target(CMD_CTX); unsigned i; uint32_t wordsize; - int retval = ERROR_OK; - - static size_t const chunksize = 1024; - uint8_t *chunk = NULL, *readback = NULL; + int retval; - if (CMD_ARGC != 3) { - retval = ERROR_COMMAND_SYNTAX_ERROR; - goto done; - } + if (CMD_ARGC != 3) + return ERROR_COMMAND_SYNTAX_ERROR; +#if BUILD_TARGET64 + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], address); +#else COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); +#endif COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], pattern); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], count); - chunk = malloc(chunksize); - if (chunk == NULL) - return ERROR_FAIL; - - readback = malloc(chunksize); - if (readback == NULL) { - free(chunk); - return ERROR_FAIL; - } - - if (count == 0) - goto done; + struct flash_bank *bank; + retval = get_flash_bank_by_addr(target, address, true, &bank); + if (retval != ERROR_OK) + return retval; switch (CMD_NAME[4]) { case 'w': @@ -508,73 +500,109 @@ COMMAND_HANDLER(handle_flash_fill_command) wordsize = 1; break; default: - retval = ERROR_COMMAND_SYNTAX_ERROR; - goto done; + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (count == 0) + return ERROR_OK; + + if (address + count * wordsize > bank->base + bank->size) { + LOG_ERROR("Cannot cross flash bank borders"); + return ERROR_FAIL; } - chunk_count = MIN(count, (chunksize / wordsize)); + uint32_t size_bytes = count * wordsize; + target_addr_t aligned_start = flash_write_align_start(bank, address); + target_addr_t end_addr = address + size_bytes - 1; + target_addr_t aligned_end = flash_write_align_end(bank, end_addr); + uint32_t aligned_size = aligned_end + 1 - aligned_start; + uint32_t padding_at_start = address - aligned_start; + uint32_t padding_at_end = aligned_end - end_addr; + + uint8_t *buffer = malloc(aligned_size); + if (buffer == NULL) + return ERROR_FAIL; + + if (padding_at_start) { + memset(buffer, bank->default_padded_value, padding_at_start); + LOG_WARNING("Start address " TARGET_ADDR_FMT + " breaks the required alignment of flash bank %s", + address, bank->name); + LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT, + padding_at_start, aligned_start); + } + + uint8_t *ptr = buffer + padding_at_start; + switch (wordsize) { case 4: - for (i = 0; i < chunk_count; i++) - target_buffer_set_u32(target, chunk + i * wordsize, pattern); + for (i = 0; i < count; i++, ptr += wordsize) + target_buffer_set_u32(target, ptr, pattern); break; case 2: - for (i = 0; i < chunk_count; i++) - target_buffer_set_u16(target, chunk + i * wordsize, pattern); + for (i = 0; i < count; i++, ptr += wordsize) + target_buffer_set_u16(target, ptr, pattern); break; case 1: - memset(chunk, pattern, chunk_count); + memset(ptr, pattern, count); + ptr += count; break; default: LOG_ERROR("BUG: can't happen"); exit(-1); } + if (padding_at_end) { + memset(ptr, bank->default_padded_value, padding_at_end); + LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32 + " bytes (bank write end alignment)", + end_addr + 1, padding_at_end); + } + struct duration bench; duration_start(&bench); - for (wrote = 0; wrote < (count*wordsize); wrote += cur_size) { - struct flash_bank *bank; + retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size); + if (retval != ERROR_OK) + goto done; - retval = get_flash_bank_by_addr(target, address, true, &bank); - if (retval != ERROR_OK) - goto done; + retval = flash_driver_read(bank, buffer, address - bank->base, size_bytes); + if (retval != ERROR_OK) + goto done; - cur_size = MIN((count * wordsize - wrote), chunksize); - err = flash_driver_write(bank, chunk, address - bank->base + wrote, cur_size); - if (err != ERROR_OK) { - retval = err; - goto done; - } + for (i = 0, ptr = buffer; i < count; i++) { + uint32_t readback = 0; - err = flash_driver_read(bank, readback, address - bank->base + wrote, cur_size); - if (err != ERROR_OK) { - retval = err; - goto done; + switch (wordsize) { + case 4: + readback = target_buffer_get_u32(target, ptr); + break; + case 2: + readback = target_buffer_get_u16(target, ptr); + break; + case 1: + readback = *ptr; + break; } - - for (i = 0; i < cur_size; i++) { - if (readback[i] != chunk[i]) { - LOG_ERROR( - "Verification error address 0x%08" PRIx32 ", read back 0x%02x, expected 0x%02x", - address + wrote + i, - readback[i], - chunk[i]); - retval = ERROR_FAIL; - goto done; - } + if (readback != pattern) { + LOG_ERROR( + "Verification error address " TARGET_ADDR_FMT + ", read back 0x%02" PRIx32 ", expected 0x%02" PRIx32, + address + i * wordsize, readback, pattern); + retval = ERROR_FAIL; + goto done; } + ptr += wordsize; } if ((retval == ERROR_OK) && (duration_measure(&bench) == ERROR_OK)) { - command_print(CMD_CTX, "wrote %" PRIu32 " bytes to 0x%8.8" PRIx32 - " in %fs (%0.3f KiB/s)", wrote, address, - duration_elapsed(&bench), duration_kbps(&bench, wrote)); + command_print(CMD_CTX, "wrote %" PRIu32 " bytes to " TARGET_ADDR_FMT + " in %fs (%0.3f KiB/s)", size_bytes, address, + duration_elapsed(&bench), duration_kbps(&bench, size_bytes)); } done: - free(readback); - free(chunk); + free(buffer); return retval; } @@ -592,8 +620,8 @@ COMMAND_HANDLER(handle_flash_write_bank_command) struct duration bench; duration_start(&bench); - struct flash_bank *p; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p); + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (ERROR_OK != retval) return retval; @@ -602,7 +630,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command) if (CMD_ARGC > 2) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset); - if (offset > p->size) { + if (offset > bank->size) { LOG_ERROR("Offset 0x%8.8" PRIx32 " is out of range of the flash bank", offset); return ERROR_COMMAND_ARGUMENT_INVALID; @@ -618,7 +646,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command) return retval; } - length = MIN(filesize, p->size - offset); + length = MIN(filesize, bank->size - offset); if (!length) { LOG_INFO("Nothing to write to flash bank"); @@ -630,14 +658,33 @@ COMMAND_HANDLER(handle_flash_write_bank_command) LOG_INFO("File content exceeds flash bank size. Only writing the " "first %zu bytes of the file", length); - buffer = malloc(length); + target_addr_t start_addr = bank->base + offset; + target_addr_t aligned_start = flash_write_align_start(bank, start_addr); + target_addr_t end_addr = start_addr + length - 1; + target_addr_t aligned_end = flash_write_align_end(bank, end_addr); + uint32_t aligned_size = aligned_end + 1 - aligned_start; + uint32_t padding_at_start = start_addr - aligned_start; + uint32_t padding_at_end = aligned_end - end_addr; + + buffer = malloc(aligned_size); if (buffer == NULL) { fileio_close(fileio); LOG_ERROR("Out of memory"); return ERROR_FAIL; } + + if (padding_at_start) { + memset(buffer, bank->default_padded_value, padding_at_start); + LOG_WARNING("Start offset 0x%08" PRIx32 + " breaks the required alignment of flash bank %s", + offset, bank->name); + LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT, + padding_at_start, aligned_start); + } + + uint8_t *ptr = buffer + padding_at_start; size_t buf_cnt; - if (fileio_read(fileio, length, buffer, &buf_cnt) != ERROR_OK) { + if (fileio_read(fileio, length, ptr, &buf_cnt) != ERROR_OK) { free(buffer); fileio_close(fileio); return ERROR_FAIL; @@ -649,15 +696,23 @@ COMMAND_HANDLER(handle_flash_write_bank_command) return ERROR_FAIL; } - retval = flash_driver_write(p, buffer, offset, length); + ptr += length; + + if (padding_at_end) { + memset(ptr, bank->default_padded_value, padding_at_end); + LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32 + " bytes (bank write end alignment)", + end_addr + 1, padding_at_end); + } + + retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size); free(buffer); - buffer = NULL; if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) { command_print(CMD_CTX, "wrote %zu bytes from file %s to flash bank %u" " at offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)", - length, CMD_ARGV[1], p->bank_number, offset, + length, CMD_ARGV[1], bank->bank_number, offset, duration_elapsed(&bench), duration_kbps(&bench, length)); } @@ -1071,21 +1126,16 @@ COMMAND_HANDLER(handle_flash_bank_command) } } - struct flash_bank *c = malloc(sizeof(*c)); + struct flash_bank *c = calloc(1, sizeof(*c)); c->name = strdup(bank_name); c->target = target; c->driver = driver; - c->driver_priv = NULL; COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], c->base); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], c->size); COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], c->chip_width); COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], c->bus_width); c->default_padded_value = c->erased_value = 0xff; - c->num_sectors = 0; - c->sectors = NULL; - c->num_prot_blocks = 0; - c->prot_blocks = NULL; - c->next = NULL; + c->minimal_write_gap = FLASH_WRITE_GAP_SECTOR; int retval; retval = CALL_COMMAND_HANDLER(driver->flash_bank_command, c); diff --git a/src/flash/nor/tms470.c b/src/flash/nor/tms470.c index a70891e..2435e79 100644 --- a/src/flash/nor/tms470.c +++ b/src/flash/nor/tms470.c @@ -151,6 +151,7 @@ static int tms470_read_part_info(struct flash_bank *bank) if (bank->sectors) { free(bank->sectors); bank->sectors = NULL; + bank->num_sectors = 0; } /* @@ -754,9 +755,6 @@ static int tms470_erase_sector(struct flash_bank *bank, int sector) target_write_u32(target, 0xFFFFFFDC, glbctrl); LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl); - if (result == ERROR_OK) - bank->sectors[sector].is_erased = 1; - return result; } @@ -1044,27 +1042,17 @@ static int tms470_erase_check(struct flash_bank *bank) * an attempt to reduce the JTAG overhead. */ for (sector = 0; sector < bank->num_sectors; sector++) { - if (bank->sectors[sector].is_erased != 1) { - uint32_t i, addr = bank->base + bank->sectors[sector].offset; - - LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector); - - target_read_buffer(target, addr, bank->sectors[sector].size, buffer); - - bank->sectors[sector].is_erased = 1; - for (i = 0; i < bank->sectors[sector].size; i++) { - if (buffer[i] != 0xff) { - LOG_WARNING("tms470 bank %d, sector %d, not erased.", - tms470_info->ordinal, - sector); - LOG_WARNING( - "at location 0x%08" PRIx32 ": flash data is 0x%02x.", - addr + i, - buffer[i]); - - bank->sectors[sector].is_erased = 0; - break; - } + uint32_t i, addr = bank->base + bank->sectors[sector].offset; + + LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector); + + target_read_buffer(target, addr, bank->sectors[sector].size, buffer); + + bank->sectors[sector].is_erased = 1; + for (i = 0; i < bank->sectors[sector].size; i++) { + if (buffer[i] != 0xff) { + bank->sectors[sector].is_erased = 0; + break; } } if (bank->sectors[sector].is_erased != 1) { @@ -1186,4 +1174,5 @@ struct flash_driver tms470_flash = { .erase_check = tms470_erase_check, .protect_check = tms470_protect_check, .info = get_tms470_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/virtual.c b/src/flash/nor/virtual.c index 06981f4..15c4bff 100644 --- a/src/flash/nor/virtual.c +++ b/src/flash/nor/virtual.c @@ -46,8 +46,13 @@ static void virtual_update_bank_info(struct flash_bank *bank) bank->bus_width = master_bank->bus_width; bank->erased_value = master_bank->erased_value; bank->default_padded_value = master_bank->default_padded_value; + bank->write_start_alignment = master_bank->write_start_alignment; + bank->write_end_alignment = master_bank->write_end_alignment; + bank->minimal_write_gap = master_bank->minimal_write_gap; bank->num_sectors = master_bank->num_sectors; bank->sectors = master_bank->sectors; + bank->num_prot_blocks = master_bank->num_prot_blocks; + bank->prot_blocks = master_bank->prot_blocks; } FLASH_BANK_COMMAND_HANDLER(virtual_flash_bank_command) @@ -231,4 +236,5 @@ struct flash_driver virtual_flash = { .erase_check = virtual_blank_check, .protect_check = virtual_protect_check, .info = virtual_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/xcf.c b/src/flash/nor/xcf.c index 035791e..bc4b1be 100644 --- a/src/flash/nor/xcf.c +++ b/src/flash/nor/xcf.c @@ -636,6 +636,7 @@ static int xcf_probe(struct flash_bank *bank) fill_sector_table(bank); priv->probed = true; + /* REVISIT: Why is unchanged bank->driver_priv rewritten by same value? */ bank->driver_priv = priv; LOG_INFO("product name: %s", product_name(bank)); @@ -893,5 +894,6 @@ struct flash_driver xcf_flash = { .auto_probe = xcf_auto_probe, .erase_check = xcf_erase_check, .protect_check = xcf_protect_check, - .info = xcf_info + .info = xcf_info, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/xmc1xxx.c b/src/flash/nor/xmc1xxx.c index 0a76b21..4b25398 100644 --- a/src/flash/nor/xmc1xxx.c +++ b/src/flash/nor/xmc1xxx.c @@ -546,4 +546,5 @@ struct flash_driver xmc1xxx_flash = { .erase = xmc1xxx_erase, .erase_check = xmc1xxx_erase_check, .write = xmc1xxx_write, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/flash/nor/xmc4xxx.c b/src/flash/nor/xmc4xxx.c index 5677ef0..0b6d48c 100644 --- a/src/flash/nor/xmc4xxx.c +++ b/src/flash/nor/xmc4xxx.c @@ -1356,4 +1356,5 @@ struct flash_driver xmc4xxx_flash = { .info = xmc4xxx_get_info_command, .protect_check = xmc4xxx_protect_check, .protect = xmc4xxx_protect, + .free_driver_priv = default_flash_free_driver_priv, }; diff --git a/src/helper/command.c b/src/helper/command.c index cbd52fb..f8b731e 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -608,7 +608,23 @@ static int run_command(struct command_context *context, .argc = num_words - 1, .argv = words + 1, }; + /* Black magic of overridden current target: + * If the command we are going to handle has a target prefix, + * override the current target temporarily for the time + * of processing the command. + * current_target_override is used also for event handlers + * therefore we prevent touching it if command has no prefix. + * Previous override is saved and restored back to ensure + * correct work when run_command() is re-entered. */ + struct target *saved_target_override = context->current_target_override; + if (c->jim_handler_data) + context->current_target_override = c->jim_handler_data; + int retval = c->handler(&cmd); + + if (c->jim_handler_data) + context->current_target_override = saved_target_override; + if (retval == ERROR_COMMAND_SYNTAX_ERROR) { /* Print help for command */ char *full_name = command_name(c, ' '); @@ -643,6 +659,8 @@ int command_run_line(struct command_context *context, char *line) * happen when the Jim Tcl interpreter is provided by eCos for * instance. */ + context->current_target_override = NULL; + Jim_Interp *interp = context->interp; Jim_DeleteAssocData(interp, "context"); retcode = Jim_SetAssocData(interp, "context", NULL, context); @@ -1269,14 +1287,10 @@ static const struct command_registration command_builtin_handlers[] = { struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp) { - struct command_context *context = malloc(sizeof(struct command_context)); + struct command_context *context = calloc(1, sizeof(struct command_context)); const char *HostOs; context->mode = COMMAND_EXEC; - context->commands = NULL; - context->current_target = 0; - context->output_handler = NULL; - context->output_handler_priv = NULL; /* Create a jim interpreter if we were not handed one */ if (interp == NULL) { diff --git a/src/helper/command.h b/src/helper/command.h index f696ab8..f9c02e5 100644 --- a/src/helper/command.h +++ b/src/helper/command.h @@ -22,8 +22,12 @@ #ifndef OPENOCD_HELPER_COMMAND_H #define OPENOCD_HELPER_COMMAND_H +#include <stdint.h> +#include <stdbool.h> #include <jim-nvp.h> +#include <helper/types.h> + /* To achieve C99 printf compatibility in MinGW, gnu_printf should be * used for __attribute__((format( ... ))), with GCC v4.4 or later */ @@ -49,7 +53,15 @@ struct command_context { Jim_Interp *interp; enum command_mode mode; struct command *commands; - int current_target; + struct target *current_target; + /* The target set by 'targets xx' command or the latest created */ + struct target *current_target_override; + /* If set overrides current_target + * It happens during processing of + * 1) a target prefixed command + * 2) an event handler + * Pay attention to reentrancy when setting override. + */ command_output_handler_t output_handler; void *output_handler_priv; }; @@ -168,6 +180,11 @@ struct command { command_handler_t handler; Jim_CmdProc *jim_handler; void *jim_handler_data; + /* Currently used only for target of target-prefixed cmd. + * Native OpenOCD commands use jim_handler_data exclusively + * as a target override. + * Jim handlers outside of target cmd tree can use + * jim_handler_data for any handler specific data */ enum command_mode mode; struct command *next; }; diff --git a/src/helper/configuration.c b/src/helper/configuration.c index 2a27883..114ad2c 100644 --- a/src/helper/configuration.c +++ b/src/helper/configuration.c @@ -51,6 +51,21 @@ void add_config_command(const char *cfg) config_file_names[num_config_files] = NULL; } +void free_config(void) +{ + while (num_config_files) + free(config_file_names[--num_config_files]); + + free(config_file_names); + config_file_names = NULL; + + while (num_script_dirs) + free(script_search_dirs[--num_script_dirs]); + + free(script_search_dirs); + script_search_dirs = NULL; +} + /* return full path or NULL according to search rules */ char *find_file(const char *file) { diff --git a/src/helper/configuration.h b/src/helper/configuration.h index 3cbcd41..cc28efc 100644 --- a/src/helper/configuration.h +++ b/src/helper/configuration.h @@ -32,6 +32,8 @@ void add_config_command(const char *cfg); void add_script_search_dir(const char *dir); +void free_config(void); + int configuration_output_handler(struct command_context *cmd_ctx, const char *line); diff --git a/src/helper/fileio.c b/src/helper/fileio.c index 47494df..9dd57e7 100644 --- a/src/helper/fileio.c +++ b/src/helper/fileio.c @@ -153,6 +153,11 @@ int fileio_close(struct fileio *fileio) return retval; } +int fileio_feof(struct fileio *fileio) +{ + return feof(fileio->file); +} + int fileio_seek(struct fileio *fileio, size_t position) { int retval; diff --git a/src/helper/fileio.h b/src/helper/fileio.h index ae4a3ec..02592e2 100644 --- a/src/helper/fileio.h +++ b/src/helper/fileio.h @@ -46,6 +46,7 @@ struct fileio; int fileio_open(struct fileio **fileio, const char *url, enum fileio_access access_type, enum fileio_type type); int fileio_close(struct fileio *fileio); +int fileio_feof(struct fileio *fileio); int fileio_seek(struct fileio *fileio, size_t position); int fileio_fgets(struct fileio *fileio, size_t size, void *buffer); diff --git a/src/helper/log.h b/src/helper/log.h index 512bcc5..d60587f 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -149,6 +149,8 @@ extern int debug_level; */ #define ERROR_FAIL (-4) #define ERROR_WAIT (-5) +/* ERROR_TIMEOUT is already taken by winerror.h. */ +#define ERROR_TIMEOUT_REACHED (-6) #endif /* OPENOCD_HELPER_LOG_H */ diff --git a/src/helper/replacements.h b/src/helper/replacements.h index f43b7e0..ac50095 100644 --- a/src/helper/replacements.h +++ b/src/helper/replacements.h @@ -25,6 +25,8 @@ #ifndef OPENOCD_HELPER_REPLACEMENTS_H #define OPENOCD_HELPER_REPLACEMENTS_H +#include <stdint.h> + /* MIN,MAX macros */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) diff --git a/src/helper/types.h b/src/helper/types.h index 58c9e72..5e35c13 100644 --- a/src/helper/types.h +++ b/src/helper/types.h @@ -22,7 +22,12 @@ #ifndef OPENOCD_HELPER_TYPES_H #define OPENOCD_HELPER_TYPES_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include <stddef.h> +#include <assert.h> #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -123,17 +128,17 @@ static inline uint64_t le_to_h_u64(const uint8_t *buf) static inline uint32_t le_to_h_u32(const uint8_t* buf) { - return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24); + return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24); } static inline uint32_t le_to_h_u24(const uint8_t* buf) { - return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16); + return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16); } static inline uint16_t le_to_h_u16(const uint8_t* buf) { - return (uint16_t)(buf[0] | buf[1] << 8); + return (uint16_t)((uint16_t)buf[0] | (uint16_t)buf[1] << 8); } static inline uint64_t be_to_h_u64(const uint8_t *buf) @@ -150,17 +155,17 @@ static inline uint64_t be_to_h_u64(const uint8_t *buf) static inline uint32_t be_to_h_u32(const uint8_t* buf) { - return (uint32_t)(buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24); + return (uint32_t)((uint32_t)buf[3] | (uint32_t)buf[2] << 8 | (uint32_t)buf[1] << 16 | (uint32_t)buf[0] << 24); } static inline uint32_t be_to_h_u24(const uint8_t* buf) { - return (uint32_t)(buf[2] | buf[1] << 8 | buf[0] << 16); + return (uint32_t)((uint32_t)buf[2] | (uint32_t)buf[1] << 8 | (uint32_t)buf[0] << 16); } static inline uint16_t be_to_h_u16(const uint8_t* buf) { - return (uint16_t)(buf[1] | buf[0] << 8); + return (uint16_t)((uint16_t)buf[1] | (uint16_t)buf[0] << 8); } static inline void h_u64_to_le(uint8_t *buf, int64_t val) diff --git a/src/jtag/adapter.c b/src/jtag/adapter.c index 5953de7..2035788 100644 --- a/src/jtag/adapter.c +++ b/src/jtag/adapter.c @@ -149,14 +149,14 @@ COMMAND_HANDLER(handle_interface_command) jtag_interface = jtag_interfaces[i]; - /* LEGACY SUPPORT ... adapter drivers must declare what - * transports they allow. Until they all do so, assume - * the legacy drivers are JTAG-only - */ - if (!jtag_interface->transports) - LOG_WARNING("Adapter driver '%s' did not declare " - "which transports it allows; assuming " - "legacy JTAG-only", jtag_interface->name); + /* LEGACY SUPPORT ... adapter drivers must declare what + * transports they allow. Until they all do so, assume + * the legacy drivers are JTAG-only + */ + if (!jtag_interface->transports) + LOG_WARNING("Adapter driver '%s' did not declare " + "which transports it allows; assuming " + "legacy JTAG-only", jtag_interface->name); retval = allow_transports(CMD_CTX, jtag_interface->transports ? jtag_interface->transports : jtag_only); if (ERROR_OK != retval) diff --git a/src/jtag/aice/aice_interface.c b/src/jtag/aice/aice_interface.c index 20f1f07..c758bb4 100644 --- a/src/jtag/aice/aice_interface.c +++ b/src/jtag/aice/aice_interface.c @@ -239,6 +239,30 @@ static int aice_khz(int khz, int *jtag_speed) return ERROR_OK; } +int aice_scan_jtag_chain(void) +{ + LOG_DEBUG("=== %s ===", __func__); + uint8_t num_of_idcode = 0; + struct target *target; + + int res = aice_port->api->idcode(aice_target_id_codes, &num_of_idcode); + if (res != ERROR_OK) { + LOG_ERROR("<-- TARGET ERROR! Failed to identify AndesCore " + "JTAG Manufacture ID in the JTAG scan chain. " + "Failed to access EDM registers. -->"); + return res; + } + + for (uint32_t i = 0; i < num_of_idcode; i++) + LOG_DEBUG("id_codes[%d] = 0x%x", i, aice_target_id_codes[i]); + + /* Update tap idcode */ + for (target = all_targets; target; target = target->next) + target->tap->idcode = aice_target_id_codes[target->tap->abs_chain_position]; + + return ERROR_OK; +} + /***************************************************************************/ /* Command handlers */ COMMAND_HANDLER(aice_handle_aice_info_command) diff --git a/src/jtag/aice/aice_interface.h b/src/jtag/aice/aice_interface.h index 0e3f108..220b0b0 100644 --- a/src/jtag/aice/aice_interface.h +++ b/src/jtag/aice/aice_interface.h @@ -31,5 +31,6 @@ struct aice_interface_param_s { }; int aice_init_targets(void); +int aice_scan_jtag_chain(void); #endif /* OPENOCD_JTAG_AICE_AICE_INTERFACE_H */ diff --git a/src/jtag/aice/aice_transport.c b/src/jtag/aice/aice_transport.c index 9f07946..57c93f2 100644 --- a/src/jtag/aice/aice_transport.c +++ b/src/jtag/aice/aice_transport.c @@ -158,6 +158,59 @@ COMMAND_HANDLER(handle_aice_init_command) return jtag_init(CMD_CTX); } +COMMAND_HANDLER(handle_scan_chain_command) +{ + struct jtag_tap *tap; + char expected_id[12]; + + aice_scan_jtag_chain(); + tap = jtag_all_taps(); + command_print(CMD_CTX, + " TapName Enabled IdCode Expected IrLen IrCap IrMask"); + command_print(CMD_CTX, + "-- ------------------- -------- ---------- ---------- ----- ----- ------"); + + while (tap) { + uint32_t expected, expected_mask, ii; + + snprintf(expected_id, sizeof expected_id, "0x%08x", + (unsigned)((tap->expected_ids_cnt > 0) + ? tap->expected_ids[0] + : 0)); + if (tap->ignore_version) + expected_id[2] = '*'; + + expected = buf_get_u32(tap->expected, 0, tap->ir_length); + expected_mask = buf_get_u32(tap->expected_mask, 0, tap->ir_length); + + command_print(CMD_CTX, + "%2d %-18s %c 0x%08x %s %5d 0x%02x 0x%02x", + tap->abs_chain_position, + tap->dotted_name, + tap->enabled ? 'Y' : 'n', + (unsigned int)(tap->idcode), + expected_id, + (unsigned int)(tap->ir_length), + (unsigned int)(expected), + (unsigned int)(expected_mask)); + + for (ii = 1; ii < tap->expected_ids_cnt; ii++) { + snprintf(expected_id, sizeof expected_id, "0x%08x", + (unsigned) tap->expected_ids[ii]); + if (tap->ignore_version) + expected_id[2] = '*'; + + command_print(CMD_CTX, + " %s", + expected_id); + } + + tap = tap->next_tap; + } + + return ERROR_OK; +} + static int jim_aice_arp_init(Jim_Interp *interp, int argc, Jim_Obj * const *argv) { LOG_DEBUG("No implement: jim_aice_arp_init"); @@ -307,6 +360,13 @@ aice_transport_jtag_subcommand_handlers[] = { .jim_handler = jim_aice_names, .help = "Returns list of all JTAG tap names.", }, + { + .name = "scan_chain", + .handler = handle_scan_chain_command, + .mode = COMMAND_ANY, + .help = "print current scan chain configuration", + .usage = "" + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/jtag/aice/aice_usb.c b/src/jtag/aice/aice_usb.c index 50468f3..d77b26b 100644 --- a/src/jtag/aice/aice_usb.c +++ b/src/jtag/aice/aice_usb.c @@ -2289,37 +2289,39 @@ get_delay: static int aice_usb_set_clock(int set_clock) { - if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL, - AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK) - return ERROR_FAIL; + if (set_clock & AICE_TCK_CONTROL_TCK_SCAN) { + if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL, + AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK) + return ERROR_FAIL; - /* Read out TCK_SCAN clock value */ - uint32_t scan_clock; - if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK) - return ERROR_FAIL; + /* Read out TCK_SCAN clock value */ + uint32_t scan_clock; + if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK) + return ERROR_FAIL; - scan_clock &= 0x0F; + scan_clock &= 0x0F; - uint32_t scan_base_freq; - if (scan_clock & 0x8) - scan_base_freq = 48000; /* 48 MHz */ - else - scan_base_freq = 30000; /* 30 MHz */ + uint32_t scan_base_freq; + if (scan_clock & 0x8) + scan_base_freq = 48000; /* 48 MHz */ + else + scan_base_freq = 30000; /* 30 MHz */ - uint32_t set_base_freq; - if (set_clock & 0x8) - set_base_freq = 48000; - else - set_base_freq = 30000; + uint32_t set_base_freq; + if (set_clock & 0x8) + set_base_freq = 48000; + else + set_base_freq = 30000; - uint32_t set_freq; - uint32_t scan_freq; - set_freq = set_base_freq >> (set_clock & 0x7); - scan_freq = scan_base_freq >> (scan_clock & 0x7); + uint32_t set_freq; + uint32_t scan_freq; + set_freq = set_base_freq >> (set_clock & 0x7); + scan_freq = scan_base_freq >> (scan_clock & 0x7); - if (scan_freq < set_freq) { - LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock"); - return ERROR_FAIL; + if (scan_freq < set_freq) { + LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock"); + return ERROR_FAIL; + } } if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL, set_clock) != ERROR_OK) diff --git a/src/jtag/aice/aice_usb.h b/src/jtag/aice/aice_usb.h index 2911ae5..15cc1f6 100644 --- a/src/jtag/aice/aice_usb.h +++ b/src/jtag/aice/aice_usb.h @@ -71,6 +71,7 @@ /* Constants for AICE command WRITE_CTRL:TCK_CONTROL */ #define AICE_TCK_CONTROL_TCK3048 0x08 +#define AICE_TCK_CONTROL_TCK_SCAN 0x10 /* Constants for AICE command WRITE_CTRL:JTAG_PIN_CONTROL */ #define AICE_JTAG_PIN_CONTROL_SRST 0x01 diff --git a/src/jtag/core.c b/src/jtag/core.c index 71a731d..f90ae99 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -836,68 +836,7 @@ int default_interface_jtag_execute_queue(void) return ERROR_FAIL; } - int result = jtag->execute_queue(); - - struct jtag_command *cmd = jtag_command_queue; - while (debug_level >= LOG_LVL_DEBUG && cmd) { - switch (cmd->type) { - case JTAG_SCAN: - DEBUG_JTAG_IO("JTAG %s SCAN to %s", - cmd->cmd.scan->ir_scan ? "IR" : "DR", - tap_state_name(cmd->cmd.scan->end_state)); - for (int i = 0; i < cmd->cmd.scan->num_fields; i++) { - struct scan_field *field = cmd->cmd.scan->fields + i; - if (field->out_value) { - char *str = buf_to_str(field->out_value, field->num_bits, 16); - DEBUG_JTAG_IO(" %db out: %s", field->num_bits, str); - free(str); - } - if (field->in_value) { - char *str = buf_to_str(field->in_value, field->num_bits, 16); - DEBUG_JTAG_IO(" %db in: %s", field->num_bits, str); - free(str); - } - } - break; - case JTAG_TLR_RESET: - DEBUG_JTAG_IO("JTAG TLR RESET to %s", - tap_state_name(cmd->cmd.statemove->end_state)); - break; - case JTAG_RUNTEST: - DEBUG_JTAG_IO("JTAG RUNTEST %d cycles to %s", - cmd->cmd.runtest->num_cycles, - tap_state_name(cmd->cmd.runtest->end_state)); - break; - case JTAG_RESET: - { - const char *reset_str[3] = { - "leave", "deassert", "assert" - }; - DEBUG_JTAG_IO("JTAG RESET %s TRST, %s SRST", - reset_str[cmd->cmd.reset->trst + 1], - reset_str[cmd->cmd.reset->srst + 1]); - } - break; - case JTAG_PATHMOVE: - DEBUG_JTAG_IO("JTAG PATHMOVE (TODO)"); - break; - case JTAG_SLEEP: - DEBUG_JTAG_IO("JTAG SLEEP (TODO)"); - break; - case JTAG_STABLECLOCKS: - DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)"); - break; - case JTAG_TMS: - DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)"); - break; - default: - LOG_ERROR("Unknown JTAG command: %d", cmd->type); - break; - } - cmd = cmd->next; - } - - return result; + return jtag->execute_queue(); } void jtag_execute_queue_noclear(void) @@ -1168,8 +1107,7 @@ static int jtag_examine_chain(void) if ((idcode & 1) == 0) { /* Zero for LSB indicates a device in bypass */ - LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%x)", - tap->dotted_name, idcode); + LOG_INFO("TAP %s does not have IDCODE", tap->dotted_name); tap->hasidcode = false; tap->idcode = 0; @@ -1370,6 +1308,14 @@ void jtag_tap_free(struct jtag_tap *tap) { jtag_unregister_event_callback(&jtag_reset_callback, tap); + struct jtag_tap_event_action *jteap = tap->event_action; + while (jteap) { + struct jtag_tap_event_action *next = jteap->next; + Jim_DecrRefCount(jteap->interp, jteap->body); + free(jteap); + jteap = next; + } + free(tap->expected); free(tap->expected_mask); free(tap->expected_ids); @@ -1534,13 +1480,19 @@ int jtag_init_inner(struct command_context *cmd_ctx) int adapter_quit(void) { - if (!jtag || !jtag->quit) - return ERROR_OK; + if (jtag && jtag->quit) { + /* close the JTAG interface */ + int result = jtag->quit(); + if (ERROR_OK != result) + LOG_ERROR("failed: %d", result); + } - /* close the JTAG interface */ - int result = jtag->quit(); - if (ERROR_OK != result) - LOG_ERROR("failed: %d", result); + struct jtag_tap *t = jtag_all_taps(); + while (t) { + struct jtag_tap *n = t->next_tap; + jtag_tap_free(t); + t = n; + } return ERROR_OK; } @@ -1891,7 +1843,7 @@ void adapter_deassert_reset(void) LOG_ERROR("transport is not selected"); } -int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, +int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq) { if (jtag->config_trace) diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 3e5974d..ccef018 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -80,6 +80,9 @@ if USB_BLASTER_DRIVER %C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la include %D%/usb_blaster/Makefile.am endif +if FT232R +DRIVERFILES += %D%/ft232r.c +endif if AMTJTAGACCEL DRIVERFILES += %D%/amt_jtagaccel.c endif @@ -153,10 +156,12 @@ endif if IMX_GPIO DRIVERFILES += %D%/imx_gpio.c endif - if KITPROG DRIVERFILES += %D%/kitprog.c endif +if XDS110 +DRIVERFILES += %D%/xds110.c +endif DRIVERHEADERS = \ %D%/bitbang.h \ diff --git a/src/jtag/drivers/buspirate.c b/src/jtag/drivers/buspirate.c index aa0d8cc..35649c2 100644 --- a/src/jtag/drivers/buspirate.c +++ b/src/jtag/drivers/buspirate.c @@ -22,6 +22,7 @@ #endif #include <jtag/interface.h> +#include <jtag/swd.h> #include <jtag/commands.h> #include <termios.h> @@ -48,9 +49,21 @@ static void buspirate_stableclocks(int num_cycles); #define CMD_READ_ADCS 0x03 /*#define CMD_TAP_SHIFT 0x04 // old protocol */ #define CMD_TAP_SHIFT 0x05 +#define CMD_ENTER_RWIRE 0x05 #define CMD_ENTER_OOCD 0x06 #define CMD_UART_SPEED 0x07 #define CMD_JTAG_SPEED 0x08 +#define CMD_RAW_PERIPH 0x40 +#define CMD_RAW_SPEED 0x60 +#define CMD_RAW_MODE 0x80 + +/* raw-wire mode configuration */ +#define CMD_RAW_CONFIG_HIZ 0x00 +#define CMD_RAW_CONFIG_3V3 0x08 +#define CMD_RAW_CONFIG_2W 0x00 +#define CMD_RAW_CONFIG_3W 0x04 +#define CMD_RAW_CONFIG_MSB 0x00 +#define CMD_RAW_CONFIG_LSB 0x02 /* Not all OSes have this speed defined */ #if !defined(B1000000) @@ -81,6 +94,18 @@ enum { SERIAL_FAST = 1 }; +enum { + SPEED_RAW_5_KHZ = 0x0, + SPEED_RAW_50_KHZ = 0x1, + SPEED_RAW_100_KHZ = 0x2, + SPEED_RAW_400_KHZ = 0x3 +}; + +/* SWD mode specific */ +static bool swd_mode; +static int queued_retval; +static char swd_features; + static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */ static const cc_t NORMAL_TIMEOUT = 10; @@ -93,6 +118,12 @@ static char *buspirate_port; static enum tap_state last_tap_state = TAP_RESET; +/* SWD interface */ +static int buspirate_swd_init(void); +static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk); +static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk); +static int buspirate_swd_switch_seq(enum swd_special_seq seq); +static int buspirate_swd_run_queue(void); /* TAP interface */ static void buspirate_tap_init(void); @@ -103,23 +134,31 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer, static void buspirate_tap_make_space(int scan, int bits); static void buspirate_reset(int trst, int srst); +static void buspirate_set_feature(int, char, char); +static void buspirate_set_mode(int, char); +static void buspirate_set_speed(int, char); /* low level interface */ +static void buspirate_bbio_enable(int); static void buspirate_jtag_reset(int); -static void buspirate_jtag_enable(int); -static unsigned char buspirate_jtag_command(int, char *, int); +static unsigned char buspirate_jtag_command(int, uint8_t *, int); static void buspirate_jtag_set_speed(int, char); static void buspirate_jtag_set_mode(int, char); static void buspirate_jtag_set_feature(int, char, char); static void buspirate_jtag_get_adcs(int); +/* low level two-wire interface */ +static void buspirate_swd_set_speed(int, char); +static void buspirate_swd_set_feature(int, char, char); +static void buspirate_swd_set_mode(int, char); + /* low level HW communication interface */ static int buspirate_serial_open(char *port); static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout); -static int buspirate_serial_write(int fd, char *buf, int size); -static int buspirate_serial_read(int fd, char *buf, int size); +static int buspirate_serial_write(int fd, uint8_t *buf, int size); +static int buspirate_serial_read(int fd, uint8_t *buf, int size); static void buspirate_serial_close(int fd); -static void buspirate_print_buffer(char *buf, int size); +static void buspirate_print_buffer(uint8_t *buf, int size); static int buspirate_execute_queue(void) { @@ -216,7 +255,7 @@ static bool read_and_discard_all_data(const int fd) bool was_msg_already_printed = false; for ( ; ; ) { - char buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */ + uint8_t buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */ const ssize_t read_count = read(fd, buffer, sizeof(buffer)); @@ -295,18 +334,20 @@ static int buspirate_init(void) return ERROR_JTAG_INIT_FAILED; } - buspirate_jtag_enable(buspirate_fd); + buspirate_bbio_enable(buspirate_fd); - if (buspirate_baudrate != SERIAL_NORMAL) - buspirate_jtag_set_speed(buspirate_fd, SERIAL_FAST); + if (swd_mode || buspirate_baudrate != SERIAL_NORMAL) + buspirate_set_speed(buspirate_fd, SERIAL_FAST); - LOG_INFO("Buspirate Interface ready!"); + LOG_INFO("Buspirate %s Interface ready!", swd_mode ? "SWD" : "JTAG"); - buspirate_tap_init(); - buspirate_jtag_set_mode(buspirate_fd, buspirate_pinmode); - buspirate_jtag_set_feature(buspirate_fd, FEATURE_VREG, + if (!swd_mode) + buspirate_tap_init(); + + buspirate_set_mode(buspirate_fd, buspirate_pinmode); + buspirate_set_feature(buspirate_fd, FEATURE_VREG, (buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE); - buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP, + buspirate_set_feature(buspirate_fd, FEATURE_PULLUP, (buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE); buspirate_reset(0, 0); @@ -316,9 +357,9 @@ static int buspirate_init(void) static int buspirate_quit(void) { LOG_INFO("Shutting down buspirate."); - buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ); + buspirate_set_mode(buspirate_fd, MODE_HIZ); + buspirate_set_speed(buspirate_fd, SERIAL_NORMAL); - buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL); buspirate_jtag_reset(buspirate_fd); buspirate_serial_close(buspirate_fd); @@ -336,6 +377,10 @@ COMMAND_HANDLER(buspirate_handle_adc_command) if (buspirate_fd == -1) return ERROR_OK; + /* unavailable in SWD mode */ + if (swd_mode) + return ERROR_OK; + /* send the command */ buspirate_jtag_get_adcs(buspirate_fd); @@ -382,11 +427,11 @@ COMMAND_HANDLER(buspirate_handle_led_command) if (atoi(CMD_ARGV[0]) == 1) { /* enable led */ - buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED, + buspirate_set_feature(buspirate_fd, FEATURE_LED, ACTION_ENABLE); } else if (atoi(CMD_ARGV[0]) == 0) { /* disable led */ - buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED, + buspirate_set_feature(buspirate_fd, FEATURE_LED, ACTION_DISABLE); } else { LOG_ERROR("usage: buspirate_led <1|0>"); @@ -492,10 +537,22 @@ static const struct command_registration buspirate_command_handlers[] = { COMMAND_REGISTRATION_DONE }; +static const struct swd_driver buspirate_swd = { + .init = buspirate_swd_init, + .switch_seq = buspirate_swd_switch_seq, + .read_reg = buspirate_swd_read_reg, + .write_reg = buspirate_swd_write_reg, + .run = buspirate_swd_run_queue, +}; + +static const char * const buspirate_transports[] = { "jtag", "swd", NULL }; + struct jtag_interface buspirate_interface = { .name = "buspirate", .execute_queue = buspirate_execute_queue, .commands = buspirate_command_handlers, + .transports = buspirate_transports, + .swd = &buspirate_swd, .init = buspirate_init, .quit = buspirate_quit }; @@ -633,8 +690,8 @@ static void buspirate_stableclocks(int num_cycles) make it incompatible with the Bus Pirate firmware. */ #define BUSPIRATE_MAX_PENDING_SCANS 128 -static char tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */ -static char tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */ +static uint8_t tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */ +static uint8_t tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */ static int tap_chain_index; struct pending_scan_result /* this was stolen from arm-jtag-ew */ @@ -659,7 +716,7 @@ static int buspirate_tap_execute(void) { static const int CMD_TAP_SHIFT_HEADER_LEN = 3; - char tmp[4096]; + uint8_t tmp[4096]; uint8_t *in_buf; int i; int fill_index = 0; @@ -675,8 +732,8 @@ static int buspirate_tap_execute(void) bytes_to_send = DIV_ROUND_UP(tap_chain_index, 8); tmp[0] = CMD_TAP_SHIFT; /* this command expects number of bits */ - tmp[1] = (char)(tap_chain_index >> 8); /* high */ - tmp[2] = (char)(tap_chain_index); /* low */ + tmp[1] = tap_chain_index >> 8; /* high */ + tmp[2] = tap_chain_index; /* low */ fill_index = CMD_TAP_SHIFT_HEADER_LEN; for (i = 0; i < bytes_to_send; i++) { @@ -799,7 +856,7 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer, tap_pending_scans_num++; } -/*************** jtag wrapper functions *********************/ +/*************** wrapper functions *********************/ /* (1) assert or (0) deassert reset lines */ static void buspirate_reset(int trst, int srst) @@ -807,33 +864,148 @@ static void buspirate_reset(int trst, int srst) LOG_DEBUG("trst: %i, srst: %i", trst, srst); if (trst) - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_TRST, ACTION_DISABLE); + buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_DISABLE); else - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_TRST, ACTION_ENABLE); + buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_ENABLE); if (srst) - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_SRST, ACTION_DISABLE); + buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_DISABLE); + else + buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_ENABLE); +} + +static void buspirate_set_feature(int fd, char feat, char action) +{ + if (swd_mode) + buspirate_swd_set_feature(fd, feat, action); else - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_SRST, ACTION_ENABLE); + buspirate_jtag_set_feature(fd, feat, action); +} + +static void buspirate_set_mode(int fd, char mode) +{ + if (swd_mode) + buspirate_swd_set_mode(fd, mode); + else + buspirate_jtag_set_mode(fd, mode); +} + +static void buspirate_set_speed(int fd, char speed) +{ + if (swd_mode) + buspirate_swd_set_speed(fd, speed); + else + buspirate_jtag_set_speed(fd, speed); +} + + +/*************** swd lowlevel functions ********************/ + +static void buspirate_swd_set_speed(int fd, char speed) +{ + int ret; + uint8_t tmp[1]; + + LOG_DEBUG("Buspirate speed setting in SWD mode defaults to 400 kHz"); + + /* speed settings */ + tmp[0] = CMD_RAW_SPEED | SPEED_RAW_400_KHZ; + buspirate_serial_write(fd, tmp, 1); + ret = buspirate_serial_read(fd, tmp, 1); + if (ret != 1) { + LOG_ERROR("Buspirate did not answer correctly"); + exit(-1); + } + if (tmp[0] != 1) { + LOG_ERROR("Buspirate did not reply as expected to the speed change command"); + exit(-1); + } +} + +static void buspirate_swd_set_mode(int fd, char mode) +{ + int ret; + uint8_t tmp[1]; + + /* raw-wire mode configuration */ + if (mode == MODE_HIZ) + tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB; + else + tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB | CMD_RAW_CONFIG_3V3; + + buspirate_serial_write(fd, tmp, 1); + ret = buspirate_serial_read(fd, tmp, 1); + if (ret != 1) { + LOG_ERROR("Buspirate did not answer correctly"); + exit(-1); + } + if (tmp[0] != 1) { + LOG_ERROR("Buspirate did not reply as expected to the configure command"); + exit(-1); + } +} + +static void buspirate_swd_set_feature(int fd, char feat, char action) +{ + int ret; + uint8_t tmp[1]; + + switch (feat) { + case FEATURE_TRST: + LOG_DEBUG("Buspirate TRST feature not available in SWD mode"); + return; + case FEATURE_LED: + LOG_ERROR("Buspirate LED feature not available in SWD mode"); + return; + case FEATURE_SRST: + swd_features = (action == ACTION_ENABLE) ? swd_features | 0x02 : swd_features & 0x0D; + break; + case FEATURE_PULLUP: + swd_features = (action == ACTION_ENABLE) ? swd_features | 0x04 : swd_features & 0x0B; + break; + case FEATURE_VREG: + swd_features = (action == ACTION_ENABLE) ? swd_features | 0x08 : swd_features & 0x07; + break; + default: + LOG_DEBUG("Buspirate unknown feature %d", feat); + return; + } + + tmp[0] = CMD_RAW_PERIPH | swd_features; + buspirate_serial_write(fd, tmp, 1); + ret = buspirate_serial_read(fd, tmp, 1); + if (ret != 1) { + LOG_DEBUG("Buspirate feature %d not supported in SWD mode", feat); + } else if (tmp[0] != 1) { + LOG_ERROR("Buspirate did not reply as expected to the configure command"); + exit(-1); + } } /*************** jtag lowlevel functions ********************/ -static void buspirate_jtag_enable(int fd) +static void buspirate_bbio_enable(int fd) { int ret; - char tmp[21] = { [0 ... 20] = 0x00 }; + char command; + const char *mode_answers[2] = { "OCD1", "RAW1" }; + const char *correct_ans = NULL; + uint8_t tmp[21] = { [0 ... 20] = 0x00 }; int done = 0; int cmd_sent = 0; - LOG_DEBUG("Entering binary mode"); + if (swd_mode) { + command = CMD_ENTER_RWIRE; + correct_ans = mode_answers[1]; + } else { + command = CMD_ENTER_OOCD; + correct_ans = mode_answers[0]; + } + + LOG_DEBUG("Entering binary mode, that is %s", correct_ans); buspirate_serial_write(fd, tmp, 20); usleep(10000); - /* reads 1 to n "BBIO1"s and one "OCD1" */ + /* reads 1 to n "BBIO1"s and one "OCD1" or "RAW1" */ while (!done) { ret = buspirate_serial_read(fd, tmp, 4); if (ret != 4) { @@ -841,7 +1013,7 @@ static void buspirate_jtag_enable(int fd) "/OpenOCD support enabled?"); exit(-1); } - if (strncmp(tmp, "BBIO", 4) == 0) { + if (strncmp((char *)tmp, "BBIO", 4) == 0) { ret = buspirate_serial_read(fd, tmp, 1); if (ret != 1) { LOG_ERROR("Buspirate did not answer correctly! " @@ -854,14 +1026,14 @@ static void buspirate_jtag_enable(int fd) } if (cmd_sent == 0) { cmd_sent = 1; - tmp[0] = CMD_ENTER_OOCD; + tmp[0] = command; ret = buspirate_serial_write(fd, tmp, 1); if (ret != 1) { LOG_ERROR("error reading"); exit(-1); } } - } else if (strncmp(tmp, "OCD1", 4) == 0) + } else if (strncmp((char *)tmp, correct_ans, 4) == 0) done = 1; else { LOG_ERROR("Buspirate did not answer correctly! " @@ -874,14 +1046,14 @@ static void buspirate_jtag_enable(int fd) static void buspirate_jtag_reset(int fd) { - char tmp[5]; + uint8_t tmp[5]; tmp[0] = 0x00; /* exit OCD1 mode */ buspirate_serial_write(fd, tmp, 1); usleep(10000); /* We ignore the return value here purposly, nothing we can do */ buspirate_serial_read(fd, tmp, 5); - if (strncmp(tmp, "BBIO1", 5) == 0) { + if (strncmp((char *)tmp, "BBIO1", 5) == 0) { tmp[0] = 0x0F; /* reset BP */ buspirate_serial_write(fd, tmp, 1); } else @@ -891,8 +1063,8 @@ static void buspirate_jtag_reset(int fd) static void buspirate_jtag_set_speed(int fd, char speed) { int ret; - char tmp[2]; - char ack[2]; + uint8_t tmp[2]; + uint8_t ack[2]; ack[0] = 0xAA; ack[1] = 0x55; @@ -924,7 +1096,7 @@ static void buspirate_jtag_set_speed(int fd, char speed) static void buspirate_jtag_set_mode(int fd, char mode) { - char tmp[2]; + uint8_t tmp[2]; tmp[0] = CMD_PORT_MODE; tmp[1] = mode; buspirate_jtag_command(fd, tmp, 2); @@ -932,7 +1104,7 @@ static void buspirate_jtag_set_mode(int fd, char mode) static void buspirate_jtag_set_feature(int fd, char feat, char action) { - char tmp[3]; + uint8_t tmp[3]; tmp[0] = CMD_FEATURE; tmp[1] = feat; /* what */ tmp[2] = action; /* action */ @@ -944,7 +1116,7 @@ static void buspirate_jtag_get_adcs(int fd) uint8_t tmp[10]; uint16_t a, b, c, d; tmp[0] = CMD_READ_ADCS; - buspirate_jtag_command(fd, (char *)tmp, 1); + buspirate_jtag_command(fd, tmp, 1); a = tmp[2] << 8 | tmp[3]; b = tmp[4] << 8 | tmp[5]; c = tmp[6] << 8 | tmp[7]; @@ -957,7 +1129,7 @@ static void buspirate_jtag_get_adcs(int fd) } static unsigned char buspirate_jtag_command(int fd, - char *cmd, int cmdlen) + uint8_t *cmd, int cmdlen) { int res; int len = 0; @@ -1048,7 +1220,7 @@ static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout) return 0; } -static int buspirate_serial_write(int fd, char *buf, int size) +static int buspirate_serial_write(int fd, uint8_t *buf, int size) { int ret = 0; @@ -1063,7 +1235,7 @@ static int buspirate_serial_write(int fd, char *buf, int size) return ret; } -static int buspirate_serial_read(int fd, char *buf, int size) +static int buspirate_serial_read(int fd, uint8_t *buf, int size) { int len = 0; int ret = 0; @@ -1102,7 +1274,7 @@ static void buspirate_serial_close(int fd) #define LINE_SIZE 81 #define BYTES_PER_LINE 16 -static void buspirate_print_buffer(char *buf, int size) +static void buspirate_print_buffer(uint8_t *buf, int size) { char line[LINE_SIZE]; char tmp[10]; @@ -1124,3 +1296,240 @@ static void buspirate_print_buffer(char *buf, int size) if (line[0] != 0) LOG_DEBUG("%s", line); } + +/************************* SWD related stuff **********/ + +static int buspirate_swd_init(void) +{ + LOG_INFO("Buspirate SWD mode enabled"); + swd_mode = true; + + return ERROR_OK; +} + +static int buspirate_swd_switch_seq(enum swd_special_seq seq) +{ + const uint8_t *sequence; + int sequence_len; + uint8_t tmp[64]; + + switch (seq) { + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + sequence = swd_seq_line_reset; + sequence_len = DIV_ROUND_UP(swd_seq_line_reset_len, 8); + break; + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + sequence = swd_seq_jtag_to_swd; + sequence_len = DIV_ROUND_UP(swd_seq_jtag_to_swd_len, 8); + break; + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + sequence = swd_seq_swd_to_jtag; + sequence_len = DIV_ROUND_UP(swd_seq_swd_to_jtag_len, 8); + break; + default: + LOG_ERROR("Sequence %d not supported", seq); + return ERROR_FAIL; + } + + /* FIXME: all above sequences fit into one pirate command for now + * but it may cause trouble later + */ + + tmp[0] = 0x10 + ((sequence_len - 1) & 0x0F); + memcpy(tmp + 1, sequence, sequence_len); + + buspirate_serial_write(buspirate_fd, tmp, sequence_len + 1); + buspirate_serial_read(buspirate_fd, tmp, sequence_len + 1); + + return ERROR_OK; +} + +static uint8_t buspirate_swd_write_header(uint8_t cmd) +{ + uint8_t tmp[8]; + int to_send; + + tmp[0] = 0x10; /* bus pirate: send 1 byte */ + tmp[1] = cmd; /* swd cmd */ + tmp[2] = 0x07; /* ack __x */ + tmp[3] = 0x07; /* ack _x_ */ + tmp[4] = 0x07; /* ack x__ */ + tmp[5] = 0x07; /* write mode trn_1 */ + tmp[6] = 0x07; /* write mode trn_2 */ + + to_send = ((cmd & SWD_CMD_RnW) == 0) ? 7 : 5; + buspirate_serial_write(buspirate_fd, tmp, to_send); + + /* read ack */ + buspirate_serial_read(buspirate_fd, tmp, 2); /* drop pirate command ret vals */ + buspirate_serial_read(buspirate_fd, tmp, to_send - 2); /* ack bits */ + + return tmp[2] << 2 | tmp[1] << 1 | tmp[0]; +} + +static void buspirate_swd_idle_clocks(uint32_t no_bits) +{ + uint32_t no_bytes; + uint8_t tmp[20]; + + no_bytes = (no_bits + 7) / 8; + memset(tmp + 1, 0x00, sizeof(tmp) - 1); + + /* unfortunately bus pirate misbehaves when clocks are sent in parts + * so we need to limit at 128 clock cycles + */ + if (no_bytes > 16) + no_bytes = 16; + + while (no_bytes) { + uint8_t to_send = no_bytes > 16 ? 16 : no_bytes; + tmp[0] = 0x10 + ((to_send - 1) & 0x0F); + + buspirate_serial_write(buspirate_fd, tmp, to_send + 1); + buspirate_serial_read(buspirate_fd, tmp, to_send + 1); + + no_bytes -= to_send; + } +} + +static void buspirate_swd_clear_sticky_errors(void) +{ + buspirate_swd_write_reg(swd_cmd(false, false, DP_ABORT), + STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); +} + +static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk) +{ + uint8_t tmp[16]; + + LOG_DEBUG("buspirate_swd_read_reg"); + assert(cmd & SWD_CMD_RnW); + + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skip buspirate_swd_read_reg because queued_retval=%d", queued_retval); + return; + } + + cmd |= SWD_CMD_START | SWD_CMD_PARK; + uint8_t ack = buspirate_swd_write_header(cmd); + + /* do a read transaction */ + tmp[0] = 0x06; /* 4 data bytes */ + tmp[1] = 0x06; + tmp[2] = 0x06; + tmp[3] = 0x06; + tmp[4] = 0x07; /* parity bit */ + tmp[5] = 0x21; /* 2 turnaround clocks */ + + buspirate_serial_write(buspirate_fd, tmp, 6); + buspirate_serial_read(buspirate_fd, tmp, 6); + + /* store the data and parity */ + uint32_t data = (uint8_t) tmp[0]; + data |= (uint8_t) tmp[1] << 8; + data |= (uint8_t) tmp[2] << 16; + data |= (uint8_t) tmp[3] << 24; + int parity = tmp[4] ? 0x01 : 0x00; + + LOG_DEBUG("%s %s %s reg %X = %08"PRIx32, + ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK", + cmd & SWD_CMD_APnDP ? "AP" : "DP", + cmd & SWD_CMD_RnW ? "read" : "write", + (cmd & SWD_CMD_A32) >> 1, + data); + + switch (ack) { + case SWD_ACK_OK: + if (parity != parity_u32(data)) { + LOG_DEBUG("Read data parity mismatch %x %x", parity, parity_u32(data)); + queued_retval = ERROR_FAIL; + return; + } + if (value) + *value = data; + if (cmd & SWD_CMD_APnDP) + buspirate_swd_idle_clocks(ap_delay_clk); + return; + case SWD_ACK_WAIT: + LOG_DEBUG("SWD_ACK_WAIT"); + buspirate_swd_clear_sticky_errors(); + return; + case SWD_ACK_FAULT: + LOG_DEBUG("SWD_ACK_FAULT"); + queued_retval = ack; + return; + default: + LOG_DEBUG("No valid acknowledge: ack=%d", ack); + queued_retval = ack; + return; + } +} + +static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) +{ + uint8_t tmp[16]; + + LOG_DEBUG("buspirate_swd_write_reg"); + assert(!(cmd & SWD_CMD_RnW)); + + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skip buspirate_swd_write_reg because queued_retval=%d", queued_retval); + return; + } + + cmd |= SWD_CMD_START | SWD_CMD_PARK; + uint8_t ack = buspirate_swd_write_header(cmd); + + /* do a write transaction */ + tmp[0] = 0x10 + ((4 + 1 - 1) & 0xF); /* bus pirate: send 4+1 bytes */ + buf_set_u32((uint8_t *) tmp + 1, 0, 32, value); + /* write sequence ends with parity bit and 7 idle ticks */ + tmp[5] = parity_u32(value) ? 0x01 : 0x00; + + buspirate_serial_write(buspirate_fd, tmp, 6); + buspirate_serial_read(buspirate_fd, tmp, 6); + + LOG_DEBUG("%s %s %s reg %X = %08"PRIx32, + ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK", + cmd & SWD_CMD_APnDP ? "AP" : "DP", + cmd & SWD_CMD_RnW ? "read" : "write", + (cmd & SWD_CMD_A32) >> 1, + value); + + switch (ack) { + case SWD_ACK_OK: + if (cmd & SWD_CMD_APnDP) + buspirate_swd_idle_clocks(ap_delay_clk); + return; + case SWD_ACK_WAIT: + LOG_DEBUG("SWD_ACK_WAIT"); + buspirate_swd_clear_sticky_errors(); + return; + case SWD_ACK_FAULT: + LOG_DEBUG("SWD_ACK_FAULT"); + queued_retval = ack; + return; + default: + LOG_DEBUG("No valid acknowledge: ack=%d", ack); + queued_retval = ack; + return; + } +} + +static int buspirate_swd_run_queue(void) +{ + LOG_DEBUG("buspirate_swd_run_queue"); + /* A transaction must be followed by another transaction or at least 8 idle cycles to + * ensure that data is clocked through the AP. */ + buspirate_swd_idle_clocks(8); + + int retval = queued_retval; + queued_retval = ERROR_OK; + LOG_DEBUG("SWD queue return value: %02x", retval); + return retval; +} + + diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap_usb.c index b8181d6..4ee4836 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap_usb.c @@ -729,6 +729,20 @@ static void cmsis_dap_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_del cmsis_dap_swd_queue_cmd(cmd, value, 0); } +static int cmsis_dap_get_serial_info(void) +{ + uint8_t *data; + + int retval = cmsis_dap_cmd_DAP_Info(INFO_ID_SERNUM, &data); + if (retval != ERROR_OK) + return retval; + + if (data[0]) /* strlen */ + LOG_INFO("CMSIS-DAP: Serial# = %s", &data[1]); + + return ERROR_OK; +} + static int cmsis_dap_get_version_info(void) { uint8_t *data; @@ -802,8 +816,7 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq) /* When we are reconnecting, DAP_Connect needs to be rerun, at * least on Keil ULINK-ME */ - retval = cmsis_dap_cmd_DAP_Connect(seq == LINE_RESET || seq == JTAG_TO_SWD ? - CONNECT_SWD : CONNECT_JTAG); + retval = cmsis_dap_cmd_DAP_Connect(CONNECT_SWD); if (retval != ERROR_OK) return retval; } @@ -842,17 +855,6 @@ static int cmsis_dap_swd_open(void) { int retval; - if (cmsis_dap_handle == NULL) { - /* SWD init */ - retval = cmsis_dap_usb_open(); - if (retval != ERROR_OK) - return retval; - - retval = cmsis_dap_get_caps_info(); - if (retval != ERROR_OK) - return retval; - } - if (!(cmsis_dap_handle->caps & INFO_CAPS_SWD)) { LOG_ERROR("CMSIS-DAP: SWD not supported"); return ERROR_JTAG_DEVICE_ERROR; @@ -873,6 +875,22 @@ static int cmsis_dap_init(void) int retval; uint8_t *data; + retval = cmsis_dap_usb_open(); + if (retval != ERROR_OK) + return retval; + + retval = cmsis_dap_get_caps_info(); + if (retval != ERROR_OK) + return retval; + + retval = cmsis_dap_get_version_info(); + if (retval != ERROR_OK) + return retval; + + retval = cmsis_dap_get_serial_info(); + if (retval != ERROR_OK) + return retval; + if (swd_mode) { retval = cmsis_dap_swd_open(); if (retval != ERROR_OK) @@ -880,16 +898,6 @@ static int cmsis_dap_init(void) } if (cmsis_dap_handle == NULL) { - - /* JTAG init */ - retval = cmsis_dap_usb_open(); - if (retval != ERROR_OK) - return retval; - - retval = cmsis_dap_get_caps_info(); - if (retval != ERROR_OK) - return retval; - /* Connect in JTAG mode */ if (!(cmsis_dap_handle->caps & INFO_CAPS_JTAG)) { LOG_ERROR("CMSIS-DAP: JTAG not supported"); @@ -903,10 +911,6 @@ static int cmsis_dap_init(void) LOG_INFO("CMSIS-DAP: Interface Initialised (JTAG)"); } - retval = cmsis_dap_get_version_info(); - if (retval != ERROR_OK) - return retval; - /* INFO_ID_PKT_SZ - short */ retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data); if (retval != ERROR_OK) @@ -1458,6 +1462,12 @@ static void cmsis_dap_execute_stableclocks(struct jtag_command *cmd) cmsis_dap_stableclocks(cmd->cmd.runtest->num_cycles); } +static void cmsis_dap_execute_tms(struct jtag_command *cmd) +{ + DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits); + cmsis_dap_cmd_DAP_SWJ_Sequence(cmd->cmd.tms->num_bits, cmd->cmd.tms->bits); +} + /* TODO: Is there need to call cmsis_dap_flush() for the JTAG_PATHMOVE, * JTAG_RUNTEST, JTAG_STABLECLOCKS? */ static void cmsis_dap_execute_command(struct jtag_command *cmd) @@ -1488,6 +1498,8 @@ static void cmsis_dap_execute_command(struct jtag_command *cmd) cmsis_dap_execute_stableclocks(cmd); break; case JTAG_TMS: + cmsis_dap_execute_tms(cmd); + break; default: LOG_ERROR("BUG: unknown JTAG command type 0x%X encountered", cmd->type); exit(-1); @@ -1650,6 +1662,7 @@ static const char * const cmsis_dap_transport[] = { "swd", "jtag", NULL }; struct jtag_interface cmsis_dap_interface = { .name = "cmsis-dap", + .supported = DEBUG_CAP_TMS_SEQ, .commands = cmsis_dap_command_handlers, .swd = &cmsis_dap_swd_driver, .transports = cmsis_dap_transport, diff --git a/src/jtag/drivers/ft232r.c b/src/jtag/drivers/ft232r.c new file mode 100644 index 0000000..fc3d75f --- /dev/null +++ b/src/jtag/drivers/ft232r.c @@ -0,0 +1,669 @@ +/*************************************************************************** + * Copyright (C) 2010 Serge Vakulenko * + * serge@vak.ru * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if IS_CYGWIN == 1 +#include "windows.h" +#undef LOG_ERROR +#endif + +/* project specific includes */ +#include <jtag/interface.h> +#include <jtag/commands.h> +#include <helper/time_support.h> +#include "libusb1_common.h" + +/* system includes */ +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> + +/* + * Bit 7 (0x80, pin 6, RI ): unused. + * Bit 6 (0x40, pin 10,DCD): /SYSRST output. + * Bit 5 (0x20, pin 9, DSR): unused. + * Bit 4 (0x10, pin 2, DTR): /TRST output. + * Bit 3 (0x08, pin 11,CTS): TMS output. + * Bit 2 (0x04, pin 3, RTS): TDO input. + * Bit 1 (0x02, pin 5, RXD): TDI output. + * Bit 0 (0x01, pin 1, TXD): TCK output. + * + * Sync bit bang mode is implemented as described in FTDI Application + * Note AN232R-01: "Bit Bang Modes for the FT232R and FT245R". + */ +#define TCK (1 << 0) +#define TDI (1 << 1) +#define READ_TDO (1 << 2) +#define TMS (1 << 3) +#define NTRST (1 << 4) +#define NSYSRST (1 << 6) + +/* + * USB endpoints. + */ +#define IN_EP 0x02 +#define OUT_EP 0x81 + +/* Requests */ +#define SIO_RESET 0 /* Reset the port */ +#define SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define SIO_SET_DATA 4 /* Set the data characteristics of the port */ +#define SIO_POLL_MODEM_STATUS 5 +#define SIO_SET_EVENT_CHAR 6 +#define SIO_SET_ERROR_CHAR 7 +#define SIO_SET_LATENCY_TIMER 9 +#define SIO_GET_LATENCY_TIMER 10 +#define SIO_SET_BITMODE 11 +#define SIO_READ_PINS 12 +#define SIO_READ_EEPROM 0x90 +#define SIO_WRITE_EEPROM 0x91 +#define SIO_ERASE_EEPROM 0x92 + +#define FT232R_BUF_SIZE 4000 + +static char *ft232r_serial_desc; +static uint16_t ft232r_vid = 0x0403; /* FTDI */ +static uint16_t ft232r_pid = 0x6001; /* FT232R */ +static jtag_libusb_device_handle *adapter; + +static uint8_t *ft232r_output; +static size_t ft232r_output_len; + +/** + * Perform sync bitbang output/input transaction. + * Before call, an array ft232r_output[] should be filled with data to send. + * Counter ft232r_output_len contains the number of bytes to send. + * On return, received data is put back to array ft232r_output[]. + */ +static int ft232r_send_recv(void) +{ + /* FIFO TX buffer has 128 bytes. + * FIFO RX buffer has 256 bytes. + * First two bytes of received packet contain contain modem + * and line status and are ignored. + * Unfortunately, transfer sizes bigger than 64 bytes + * frequently cause hang ups. */ + assert(ft232r_output_len > 0); + + size_t total_written = 0; + size_t total_read = 0; + int rxfifo_free = 128; + + while (total_read < ft232r_output_len) { + /* Write */ + int bytes_to_write = ft232r_output_len - total_written; + if (bytes_to_write > 64) + bytes_to_write = 64; + if (bytes_to_write > rxfifo_free) + bytes_to_write = rxfifo_free; + + if (bytes_to_write) { + int n = jtag_libusb_bulk_write(adapter, IN_EP, + (char *) ft232r_output + total_written, + bytes_to_write, 1000); + + if (n == 0) { + LOG_ERROR("usb bulk write failed"); + return ERROR_JTAG_DEVICE_ERROR; + } + + total_written += n; + rxfifo_free -= n; + } + + /* Read */ + uint8_t reply[64]; + + int n = jtag_libusb_bulk_read(adapter, OUT_EP, + (char *) reply, + sizeof(reply), 1000); + + if (n == 0) { + LOG_ERROR("usb bulk read failed"); + return ERROR_JTAG_DEVICE_ERROR; + } + if (n > 2) { + /* Copy data, ignoring first 2 bytes. */ + memcpy(ft232r_output + total_read, reply + 2, n - 2); + int bytes_read = n - 2; + total_read += bytes_read; + rxfifo_free += bytes_read; + if (total_read > total_written) { + LOG_ERROR("read more bytes than wrote"); + return ERROR_JTAG_DEVICE_ERROR; + } + } + } + ft232r_output_len = 0; + return ERROR_OK; +} + +/** + * Add one TCK/TMS/TDI sample to send buffer. + */ +static void ft232r_write(int tck, int tms, int tdi) +{ + unsigned out_value = NTRST | NSYSRST; + if (tck) + out_value |= TCK; + if (tms) + out_value |= TMS; + if (tdi) + out_value |= TDI; + + if (ft232r_output_len >= FT232R_BUF_SIZE) { + /* FIXME: should we just execute queue here? */ + LOG_ERROR("ft232r_write: buffer overflow"); + return; + } + ft232r_output[ft232r_output_len++] = out_value; +} + +/** + * Control /TRST and /SYSRST pins. + * Perform immediate bitbang transaction. + */ +static void ft232r_reset(int trst, int srst) +{ + unsigned out_value = NTRST | NSYSRST; + LOG_DEBUG("ft232r_reset(%d,%d)", trst, srst); + + if (trst == 1) + out_value &= ~NTRST; /* switch /TRST low */ + else if (trst == 0) + out_value |= NTRST; /* switch /TRST high */ + + if (srst == 1) + out_value &= ~NSYSRST; /* switch /SYSRST low */ + else if (srst == 0) + out_value |= NSYSRST; /* switch /SYSRST high */ + + if (ft232r_output_len >= FT232R_BUF_SIZE) { + /* FIXME: should we just execute queue here? */ + LOG_ERROR("ft232r_write: buffer overflow"); + return; + } + + ft232r_output[ft232r_output_len++] = out_value; + ft232r_send_recv(); +} + +static int ft232r_speed(int divisor) +{ + int baud = (divisor == 0) ? 3000000 : + (divisor == 1) ? 2000000 : + 3000000 / divisor; + LOG_DEBUG("ft232r_speed(%d) rate %d bits/sec", divisor, baud); + + if (jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_SET_BAUD_RATE, divisor, 0, 0, 0, 1000) != 0) { + LOG_ERROR("cannot set baud rate"); + return ERROR_JTAG_DEVICE_ERROR; + } + return ERROR_OK; +} + +static int ft232r_init(void) +{ + uint16_t avids[] = {ft232r_vid, 0}; + uint16_t apids[] = {ft232r_pid, 0}; + if (jtag_libusb_open(avids, apids, ft232r_serial_desc, &adapter)) { + LOG_ERROR("ft232r not found: vid=%04x, pid=%04x, serial=%s\n", + ft232r_vid, ft232r_pid, (ft232r_serial_desc == NULL) ? "[any]" : ft232r_serial_desc); + return ERROR_JTAG_INIT_FAILED; + } + + libusb_detach_kernel_driver(adapter, 0); + + if (jtag_libusb_claim_interface(adapter, 0)) { + LOG_ERROR("unable to claim interface"); + return ERROR_JTAG_INIT_FAILED; + } + + /* Reset the device. */ + if (jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_RESET, 0, 0, 0, 0, 1000) != 0) { + LOG_ERROR("unable to reset device"); + return ERROR_JTAG_INIT_FAILED; + } + + /* Sync bit bang mode. */ + if (jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_SET_BITMODE, TCK | TDI | TMS | NTRST | NSYSRST | 0x400, + 0, 0, 0, 1000) != 0) { + LOG_ERROR("cannot set sync bitbang mode"); + return ERROR_JTAG_INIT_FAILED; + } + + /* Exactly 500 nsec between updates. */ + unsigned divisor = 1; + unsigned char latency_timer = 1; + + /* Frequency divisor is 14-bit non-zero value. */ + if (jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_SET_BAUD_RATE, divisor, + 0, 0, 0, 1000) != 0) { + LOG_ERROR("cannot set baud rate"); + return ERROR_JTAG_INIT_FAILED; + } + if (jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_SET_LATENCY_TIMER, latency_timer, 0, 0, 0, 1000) != 0) { + LOG_ERROR("unable to set latency timer"); + return ERROR_JTAG_INIT_FAILED; + } + + ft232r_output = malloc(FT232R_BUF_SIZE); + if (ft232r_output == NULL) { + LOG_ERROR("Unable to allocate memory for the buffer"); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; +} + +static int ft232r_quit(void) +{ + if (jtag_libusb_release_interface(adapter, 0) != 0) + LOG_ERROR("usb release interface failed"); + + jtag_libusb_close(adapter); + free(ft232r_output); + + return ERROR_OK; +} + +static int ft232r_speed_div(int divisor, int *khz) +{ + /* Maximum 3 Mbaud for bit bang mode. */ + if (divisor == 0) + *khz = 3000; + else if (divisor == 1) + *khz = 2000; + else + *khz = 3000 / divisor; + return ERROR_OK; +} + +static int ft232r_khz(int khz, int *divisor) +{ + if (khz == 0) { + LOG_DEBUG("RCLK not supported"); + return ERROR_FAIL; + } + + /* Calculate frequency divisor. */ + if (khz > 2500) + *divisor = 0; /* Special case: 3 MHz */ + else if (khz > 1700) + *divisor = 1; /* Special case: 2 MHz */ + else { + *divisor = (2*3000 / khz + 1) / 2; + if (*divisor > 0x3FFF) + *divisor = 0x3FFF; + } + return ERROR_OK; +} + +COMMAND_HANDLER(ft232r_handle_serial_desc_command) +{ + if (CMD_ARGC == 1) + ft232r_serial_desc = strdup(CMD_ARGV[0]); + else + LOG_ERROR("require exactly one argument to " + "ft232r_serial_desc <serial>"); + return ERROR_OK; +} + +COMMAND_HANDLER(ft232r_handle_vid_pid_command) +{ + if (CMD_ARGC > 2) { + LOG_WARNING("ignoring extra IDs in ft232r_vid_pid " + "(maximum is 1 pair)"); + CMD_ARGC = 2; + } + if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], ft232r_vid); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], ft232r_pid); + } else + LOG_WARNING("incomplete ft232r_vid_pid configuration"); + + return ERROR_OK; +} + +static const struct command_registration ft232r_command_handlers[] = { + { + .name = "ft232r_serial_desc", + .handler = ft232r_handle_serial_desc_command, + .mode = COMMAND_CONFIG, + .help = "USB serial descriptor of the adapter", + .usage = "serial string", + }, + { + .name = "ft232r_vid_pid", + .handler = ft232r_handle_vid_pid_command, + .mode = COMMAND_CONFIG, + .help = "USB VID and PID of the adapter", + .usage = "vid pid", + }, + COMMAND_REGISTRATION_DONE +}; + +/* + * Synchronous bitbang protocol implementation. + */ + +static void syncbb_end_state(tap_state_t state) +{ + if (tap_is_state_stable(state)) + tap_set_end_state(state); + else { + LOG_ERROR("BUG: %i is not a valid end state", state); + exit(-1); + } +} + +static void syncbb_state_move(int skip) +{ + int i = 0, tms = 0; + uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state()); + int tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state()); + + for (i = skip; i < tms_count; i++) { + tms = (tms_scan >> i) & 1; + ft232r_write(0, tms, 0); + ft232r_write(1, tms, 0); + } + ft232r_write(0, tms, 0); + + tap_set_state(tap_get_end_state()); +} + +/** + * Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG + * (or SWD) state machine. + */ +static int syncbb_execute_tms(struct jtag_command *cmd) +{ + unsigned num_bits = cmd->cmd.tms->num_bits; + const uint8_t *bits = cmd->cmd.tms->bits; + + DEBUG_JTAG_IO("TMS: %d bits", num_bits); + + int tms = 0; + for (unsigned i = 0; i < num_bits; i++) { + tms = ((bits[i/8] >> (i % 8)) & 1); + ft232r_write(0, tms, 0); + ft232r_write(1, tms, 0); + } + ft232r_write(0, tms, 0); + + return ERROR_OK; +} + +static void syncbb_path_move(struct pathmove_command *cmd) +{ + int num_states = cmd->num_states; + int state_count; + int tms = 0; + + state_count = 0; + while (num_states) { + if (tap_state_transition(tap_get_state(), false) == cmd->path[state_count]) { + tms = 0; + } else if (tap_state_transition(tap_get_state(), true) == cmd->path[state_count]) { + tms = 1; + } else { + LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition", + tap_state_name(tap_get_state()), + tap_state_name(cmd->path[state_count])); + exit(-1); + } + + ft232r_write(0, tms, 0); + ft232r_write(1, tms, 0); + + tap_set_state(cmd->path[state_count]); + state_count++; + num_states--; + } + + ft232r_write(0, tms, 0); + + tap_set_end_state(tap_get_state()); +} + +static void syncbb_runtest(int num_cycles) +{ + int i; + + tap_state_t saved_end_state = tap_get_end_state(); + + /* only do a state_move when we're not already in IDLE */ + if (tap_get_state() != TAP_IDLE) { + syncbb_end_state(TAP_IDLE); + syncbb_state_move(0); + } + + /* execute num_cycles */ + for (i = 0; i < num_cycles; i++) { + ft232r_write(0, 0, 0); + ft232r_write(1, 0, 0); + } + ft232r_write(0, 0, 0); + + /* finish in end_state */ + syncbb_end_state(saved_end_state); + if (tap_get_state() != tap_get_end_state()) + syncbb_state_move(0); +} + +/** + * Function syncbb_stableclocks + * issues a number of clock cycles while staying in a stable state. + * Because the TMS value required to stay in the RESET state is a 1, whereas + * the TMS value required to stay in any of the other stable states is a 0, + * this function checks the current stable state to decide on the value of TMS + * to use. + */ +static void syncbb_stableclocks(int num_cycles) +{ + int tms = (tap_get_state() == TAP_RESET ? 1 : 0); + int i; + + /* send num_cycles clocks onto the cable */ + for (i = 0; i < num_cycles; i++) { + ft232r_write(1, tms, 0); + ft232r_write(0, tms, 0); + } +} + +static void syncbb_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size) +{ + tap_state_t saved_end_state = tap_get_end_state(); + int bit_cnt, bit0_index; + + if (!((!ir_scan && (tap_get_state() == TAP_DRSHIFT)) || (ir_scan && (tap_get_state() == TAP_IRSHIFT)))) { + if (ir_scan) + syncbb_end_state(TAP_IRSHIFT); + else + syncbb_end_state(TAP_DRSHIFT); + + syncbb_state_move(0); + syncbb_end_state(saved_end_state); + } + + bit0_index = ft232r_output_len; + for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) { + int tms = (bit_cnt == scan_size-1) ? 1 : 0; + int tdi; + int bytec = bit_cnt/8; + int bcval = 1 << (bit_cnt % 8); + + /* if we're just reading the scan, but don't care about the output + * default to outputting 'low', this also makes valgrind traces more readable, + * as it removes the dependency on an uninitialised value + */ + tdi = 0; + if ((type != SCAN_IN) && (buffer[bytec] & bcval)) + tdi = 1; + + ft232r_write(0, tms, tdi); + ft232r_write(1, tms, tdi); + } + + if (tap_get_state() != tap_get_end_state()) { + /* we *KNOW* the above loop transitioned out of + * the shift state, so we skip the first state + * and move directly to the end state. + */ + syncbb_state_move(1); + } + ft232r_send_recv(); + + if (type != SCAN_OUT) + for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) { + int bytec = bit_cnt/8; + int bcval = 1 << (bit_cnt % 8); + int val = ft232r_output[bit0_index + bit_cnt*2 + 1]; + + if (val & READ_TDO) + buffer[bytec] |= bcval; + else + buffer[bytec] &= ~bcval; + } +} + +static int syncbb_execute_queue(void) +{ + struct jtag_command *cmd = jtag_command_queue; /* currently processed command */ + int scan_size; + enum scan_type type; + uint8_t *buffer; + int retval; + + /* return ERROR_OK, unless a jtag_read_buffer returns a failed check + * that wasn't handled by a caller-provided error handler + */ + retval = ERROR_OK; + +/* ft232r_blink(1);*/ + + while (cmd) { + switch (cmd->type) { + case JTAG_RESET: + LOG_DEBUG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst); + + if ((cmd->cmd.reset->trst == 1) || + (cmd->cmd.reset->srst && + (jtag_get_reset_config() & RESET_SRST_PULLS_TRST))) { + tap_set_state(TAP_RESET); + } + ft232r_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + break; + + case JTAG_RUNTEST: + LOG_DEBUG_IO("runtest %i cycles, end in %s", cmd->cmd.runtest->num_cycles, + tap_state_name(cmd->cmd.runtest->end_state)); + + syncbb_end_state(cmd->cmd.runtest->end_state); + syncbb_runtest(cmd->cmd.runtest->num_cycles); + break; + + case JTAG_STABLECLOCKS: + /* this is only allowed while in a stable state. A check for a stable + * state was done in jtag_add_clocks() + */ + syncbb_stableclocks(cmd->cmd.stableclocks->num_cycles); + break; + + case JTAG_TLR_RESET: /* renamed from JTAG_STATEMOVE */ + LOG_DEBUG_IO("statemove end in %s", tap_state_name(cmd->cmd.statemove->end_state)); + + syncbb_end_state(cmd->cmd.statemove->end_state); + syncbb_state_move(0); + break; + + case JTAG_PATHMOVE: + LOG_DEBUG_IO("pathmove: %i states, end in %s", cmd->cmd.pathmove->num_states, + tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1])); + + syncbb_path_move(cmd->cmd.pathmove); + break; + + case JTAG_SCAN: + LOG_DEBUG_IO("%s scan end in %s", (cmd->cmd.scan->ir_scan) ? "IR" : "DR", + tap_state_name(cmd->cmd.scan->end_state)); + + syncbb_end_state(cmd->cmd.scan->end_state); + scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); + type = jtag_scan_type(cmd->cmd.scan); + syncbb_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size); + if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK) + retval = ERROR_JTAG_QUEUE_FAILED; + if (buffer) + free(buffer); + break; + + case JTAG_SLEEP: + LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us); + + jtag_sleep(cmd->cmd.sleep->us); + break; + + case JTAG_TMS: + retval = syncbb_execute_tms(cmd); + break; + default: + LOG_ERROR("BUG: unknown JTAG command type encountered"); + exit(-1); + } + if (ft232r_output_len > 0) + ft232r_send_recv(); + cmd = cmd->next; + } +/* ft232r_blink(0);*/ + + return retval; +} + +struct jtag_interface ft232r_interface = { + .name = "ft232r", + .commands = ft232r_command_handlers, + .transports = jtag_only, + .supported = DEBUG_CAP_TMS_SEQ, + + .execute_queue = syncbb_execute_queue, + + .speed = ft232r_speed, + .init = ft232r_init, + .quit = ft232r_quit, + .speed_div = ft232r_speed_div, + .khz = ft232r_khz, +}; diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index e69707e..7c6709c 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -694,6 +694,18 @@ static int ftdi_quit(void) { mpsse_close(mpsse_ctx); + struct signal *sig = signals; + while (sig) { + struct signal *next = sig->next; + free((void *)sig->name); + free(sig); + sig = next; + } + + free(ftdi_device_desc); + free(ftdi_serial); + free(ftdi_location); + free(swd_cmd_queue); return ERROR_OK; diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index 132ef06..e74965e 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -1263,7 +1263,7 @@ static bool check_trace_freq(struct jaylink_swo_speed speed, return false; } -static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, +static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq) { int ret; @@ -1275,7 +1275,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, return ERROR_FAIL; } - if (pin_protocol != ASYNC_UART) { + if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) { LOG_ERROR("Selected pin protocol is not supported."); return ERROR_FAIL; } diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c index 1b0fcba..1a42b3a 100644 --- a/src/jtag/drivers/jtag_vpi.c +++ b/src/jtag/drivers/jtag_vpi.c @@ -204,23 +204,20 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift) static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift) { int nb_xfer = DIV_ROUND_UP(nb_bits, XFERT_MAX_SIZE * 8); - uint8_t *xmit_buffer = bits; - int xmit_nb_bits = nb_bits; - int i = 0; int retval; while (nb_xfer) { - if (nb_xfer == 1) { - retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], xmit_nb_bits, tap_shift); + retval = jtag_vpi_queue_tdi_xfer(bits, nb_bits, tap_shift); if (retval != ERROR_OK) return retval; } else { - retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], XFERT_MAX_SIZE * 8, NO_TAP_SHIFT); + retval = jtag_vpi_queue_tdi_xfer(bits, XFERT_MAX_SIZE * 8, NO_TAP_SHIFT); if (retval != ERROR_OK) return retval; - xmit_nb_bits -= XFERT_MAX_SIZE * 8; - i += XFERT_MAX_SIZE; + nb_bits -= XFERT_MAX_SIZE * 8; + if (bits) + bits += XFERT_MAX_SIZE; } nb_xfer--; @@ -318,7 +315,7 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state) if (retval != ERROR_OK) return retval; - retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT); + retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT); if (retval != ERROR_OK) return retval; diff --git a/src/jtag/drivers/kitprog.c b/src/jtag/drivers/kitprog.c index db5b62e..e3ad84d 100644 --- a/src/jtag/drivers/kitprog.c +++ b/src/jtag/drivers/kitprog.c @@ -741,12 +741,22 @@ static int kitprog_swd_run_queue(void) break; } - /* We use the maximum buffer size here because the KitProg sometimes - * doesn't like bulk reads of fewer than 62 bytes. (?!?!) + /* KitProg firmware does not send a zero length packet + * after the bulk-in transmission of a length divisible by bulk packet + * size (64 bytes) as required by the USB specification. + * Therefore libusb would wait for continuation of transmission. + * Workaround: Limit bulk read size to expected number of bytes + * for problematic tranfer sizes. Otherwise use the maximum buffer + * size here because the KitProg sometimes doesn't like bulk reads + * of fewer than 62 bytes. (?!?!) */ + size_t read_count_workaround = SWD_MAX_BUFFER_LENGTH; + if (read_count % 64 == 0) + read_count_workaround = read_count; + ret = jtag_libusb_bulk_read(kitprog_handle->usb_handle, BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)buffer, - SWD_MAX_BUFFER_LENGTH, 0); + read_count_workaround, 1000); if (ret > 0) { /* Handle garbage data by offsetting the initial read index */ if ((unsigned int)ret > read_count) @@ -878,13 +888,11 @@ COMMAND_HANDLER(kitprog_handle_acquire_psoc_command) COMMAND_HANDLER(kitprog_handle_serial_command) { if (CMD_ARGC == 1) { - size_t len = strlen(CMD_ARGV[0]); - kitprog_serial = calloc(len + 1, sizeof(char)); + kitprog_serial = strdup(CMD_ARGV[0]); if (kitprog_serial == NULL) { LOG_ERROR("Failed to allocate memory for the serial number"); return ERROR_FAIL; } - strncpy(kitprog_serial, CMD_ARGV[0], len + 1); } else { LOG_ERROR("expected exactly one argument to kitprog_serial <serial-number>"); return ERROR_FAIL; diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c index 924c974..06d008b 100644 --- a/src/jtag/drivers/mpsse.c +++ b/src/jtag/drivers/mpsse.c @@ -335,7 +335,13 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha ctx->write_size = 16384; ctx->read_chunk = malloc(ctx->read_chunk_size); ctx->read_buffer = malloc(ctx->read_size); - ctx->write_buffer = malloc(ctx->write_size); + + /* Use calloc to make valgrind happy: buffer_write() sets payload + * on bit basis, so some bits can be left uninitialized in write_buffer. + * Although this is perfectly ok with MPSSE, valgrind reports + * Syscall param ioctl(USBDEVFS_SUBMITURB).buffer points to uninitialised byte(s) */ + ctx->write_buffer = calloc(1, ctx->write_size); + if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer) goto error; diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 6f720b8..d9ca53e 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -457,8 +457,8 @@ static int stlink_usb_error_check(void *handle) LOG_DEBUG("Write error"); return ERROR_FAIL; case STLINK_JTAG_WRITE_VERIF_ERROR: - LOG_DEBUG("Verify error"); - return ERROR_FAIL; + LOG_DEBUG("Write verify error, ignoring"); + return ERROR_OK; case STLINK_SWD_AP_FAULT: /* git://git.ac6.fr/openocd commit 657e3e885b9ee10 * returns ERROR_OK with the comment: @@ -2194,12 +2194,13 @@ error_open: return ERROR_FAIL; } -int stlink_config_trace(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol, +int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq) { struct stlink_usb_handle_s *h = handle; - if (enabled && (h->jtag_api < 2 || pin_protocol != ASYNC_UART)) { + if (enabled && (h->jtag_api < 2 || + pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) { LOG_ERROR("The attached ST-LINK version doesn't support this trace mode"); return ERROR_FAIL; } diff --git a/src/jtag/drivers/sysfsgpio.c b/src/jtag/drivers/sysfsgpio.c index 5a4651d..5535c71 100644 --- a/src/jtag/drivers/sysfsgpio.c +++ b/src/jtag/drivers/sysfsgpio.c @@ -58,11 +58,11 @@ /* * Helper func to determine if gpio number valid * - * Assume here that there will be less than 1000 gpios on a system + * Assume here that there will be less than 10000 gpios on a system */ static int is_gpio_valid(int gpio) { - return gpio >= 0 && gpio < 1000; + return gpio >= 0 && gpio < 10000; } /* @@ -89,7 +89,7 @@ static int open_write_close(const char *name, const char *valstr) */ static void unexport_sysfs_gpio(int gpio) { - char gpiostr[4]; + char gpiostr[5]; if (!is_gpio_valid(gpio)) return; @@ -113,7 +113,7 @@ static void unexport_sysfs_gpio(int gpio) static int setup_sysfs_gpio(int gpio, int is_output, int init_high) { char buf[40]; - char gpiostr[4]; + char gpiostr[5]; int ret; if (!is_gpio_valid(gpio)) diff --git a/src/jtag/drivers/usb_blaster/usb_blaster.c b/src/jtag/drivers/usb_blaster/usb_blaster.c index df9f2a1..8ccf871 100644 --- a/src/jtag/drivers/usb_blaster/usb_blaster.c +++ b/src/jtag/drivers/usb_blaster/usb_blaster.c @@ -445,6 +445,7 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes) * ublast_tms_seq - write a TMS sequence transition to JTAG * @bits: TMS bits to be written (bit0, bit1 .. bitN) * @nb_bits: number of TMS bits (between 1 and 8) + * @skip: number of TMS bits to skip at the beginning of the series * * Write a serie of TMS transitions, where each transition consists in : * - writing out TCK=0, TMS=<new_state>, TDI=<???> @@ -452,12 +453,12 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes) * The function ensures that at the end of the sequence, the clock (TCK) is put * low. */ -static void ublast_tms_seq(const uint8_t *bits, int nb_bits) +static void ublast_tms_seq(const uint8_t *bits, int nb_bits, int skip) { int i; DEBUG_JTAG_IO("(bits=%02x..., nb_bits=%d)", bits[0], nb_bits); - for (i = 0; i < nb_bits; i++) + for (i = skip; i < nb_bits; i++) ublast_clock_tms((bits[i / 8] >> (i % 8)) & 0x01); ublast_idle_clock(); } @@ -469,7 +470,7 @@ static void ublast_tms_seq(const uint8_t *bits, int nb_bits) static void ublast_tms(struct tms_command *cmd) { DEBUG_JTAG_IO("(num_bits=%d)", cmd->num_bits); - ublast_tms_seq(cmd->bits, cmd->num_bits); + ublast_tms_seq(cmd->bits, cmd->num_bits, 0); } /** @@ -501,11 +502,12 @@ static void ublast_path_move(struct pathmove_command *cmd) /** * ublast_state_move - move JTAG state to the target state * @state: the target state + * @skip: number of bits to skip at the beginning of the path * * Input the correct TMS sequence to the JTAG TAP so that we end up in the * target state. This assumes the current state (tap_get_state()) is correct. */ -static void ublast_state_move(tap_state_t state) +static void ublast_state_move(tap_state_t state, int skip) { uint8_t tms_scan; int tms_len; @@ -516,7 +518,7 @@ static void ublast_state_move(tap_state_t state) return; tms_scan = tap_get_tms_path(tap_get_state(), state); tms_len = tap_get_tms_path_len(tap_get_state(), state); - ublast_tms_seq(&tms_scan, tms_len); + ublast_tms_seq(&tms_scan, tms_len, skip); tap_set_state(state); } @@ -688,9 +690,9 @@ static void ublast_runtest(int cycles, tap_state_t state) { DEBUG_JTAG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state); - ublast_state_move(TAP_IDLE); + ublast_state_move(TAP_IDLE, 0); ublast_queue_tdi(NULL, cycles, SCAN_OUT); - ublast_state_move(state); + ublast_state_move(state, 0); } static void ublast_stableclocks(int cycles) @@ -720,9 +722,9 @@ static int ublast_scan(struct scan_command *cmd) scan_bits = jtag_build_buffer(cmd, &buf); if (cmd->ir_scan) - ublast_state_move(TAP_IRSHIFT); + ublast_state_move(TAP_IRSHIFT, 0); else - ublast_state_move(TAP_DRSHIFT); + ublast_state_move(TAP_DRSHIFT, 0); log_buf = hexdump(buf, DIV_ROUND_UP(scan_bits, 8)); DEBUG_JTAG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", __func__, @@ -733,20 +735,15 @@ static int ublast_scan(struct scan_command *cmd) ublast_queue_tdi(buf, scan_bits, type); - /* - * As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it - * forward to a stable IRPAUSE or DRPAUSE. - */ - ublast_clock_tms(0); - if (cmd->ir_scan) - tap_set_state(TAP_IRPAUSE); - else - tap_set_state(TAP_DRPAUSE); - ret = jtag_read_buffer(buf, cmd); if (buf) free(buf); - ublast_state_move(cmd->end_state); + /* + * ublast_queue_tdi sends the last bit with TMS=1. We are therefore + * already in Exit1-DR/IR and have to skip the first step on our way + * to end_state. + */ + ublast_state_move(cmd->end_state, 1); return ret; } @@ -776,7 +773,7 @@ static void ublast_initial_wipeout(void) /* * Put JTAG in RESET state (five 1 on TMS) */ - ublast_tms_seq(&tms_reset, 5); + ublast_tms_seq(&tms_reset, 5, 0); tap_set_state(TAP_RESET); } @@ -805,7 +802,7 @@ static int ublast_execute_queue(void) ublast_stableclocks(cmd->cmd.stableclocks->num_cycles); break; case JTAG_TLR_RESET: - ublast_state_move(cmd->cmd.statemove->end_state); + ublast_state_move(cmd->cmd.statemove->end_state, 0); break; case JTAG_PATHMOVE: ublast_path_move(cmd->cmd.pathmove); diff --git a/src/jtag/drivers/xds110.c b/src/jtag/drivers/xds110.c new file mode 100644 index 0000000..b97eef2 --- /dev/null +++ b/src/jtag/drivers/xds110.c @@ -0,0 +1,1973 @@ +/*************************************************************************** + * Copyright (C) 2017 by Texas Instruments, Inc. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <transport/transport.h> +#include <jtag/swd.h> +#include <jtag/interface.h> +#include <jtag/commands.h> +#include <jtag/tcl.h> +#include <libusb.h> + +/* XDS110 USB serial number length */ +#define XDS110_SERIAL_LEN 8 + +/* Firmware version that introduced OpenOCD support via block accesses */ +#define OCD_FIRMWARE_VERSION 0x02030011 +#define OCD_FIRMWARE_UPGRADE \ + "XDS110: upgrade to version 2.3.0.11+ for improved support" + +/*************************************************************************** + * USB Connection Buffer Definitions * + ***************************************************************************/ + +/* Max USB packet size for up to USB 3.0 */ +#define MAX_PACKET 1024 + +/* + * Maximum data payload that can be handled in a single call + * Limitation is the size of the buffers in the XDS110 firmware + */ +#define MAX_DATA_BLOCK 4096 + +#ifndef USB_PAYLOAD_SIZE +/* Largest data block plus parameters */ +#define USB_PAYLOAD_SIZE (MAX_DATA_BLOCK + 60) +#endif +#define MAX_RESULT_QUEUE (MAX_DATA_BLOCK / 4) + +/*************************************************************************** + * USB Connection Endpoints * + ***************************************************************************/ + +/* Bulk endpoints used by the XDS110 debug interface */ +#define INTERFACE_DEBUG (2) +#define ENDPOINT_DEBUG_IN (3 | LIBUSB_ENDPOINT_IN) +#define ENDPOINT_DEBUG_OUT (2 | LIBUSB_ENDPOINT_OUT) + +/*************************************************************************** + * XDS110 Firmware API Definitions * + ***************************************************************************/ + +/* + * Default values controlling how the host communicates commands + * with XDS110 firmware (automatic retry count and wait timeout) + */ +#define DEFAULT_ATTEMPTS (1) +#define DEFAULT_TIMEOUT (4000) + +/* XDS110 API error codes */ +#define SC_ERR_NONE 0 +#define SC_ERR_XDS110_FAIL -261 +#define SC_ERR_SWD_WAIT -613 +#define SC_ERR_SWD_FAULT -614 +#define SC_ERR_SWD_PROTOCOL -615 +#define SC_ERR_SWD_PARITY -616 +#define SC_ERR_SWD_DEVICE_ID -617 + +/* TCK frequency limits */ +#define XDS110_MIN_TCK_SPEED 100 /* kHz */ +#define XDS110_MAX_TCK_SPEED 2500 /* kHz */ +#define XDS110_TCK_PULSE_INCREMENT 66.0 + +/* Scan mode on connect */ +#define MODE_JTAG 1 + +/* XDS110 API JTAG state definitions */ +#define XDS_JTAG_STATE_RESET 1 +#define XDS_JTAG_STATE_IDLE 2 +#define XDS_JTAG_STATE_SHIFT_DR 3 +#define XDS_JTAG_STATE_SHIFT_IR 4 +#define XDS_JTAG_STATE_PAUSE_DR 5 +#define XDS_JTAG_STATE_PAUSE_IR 6 +#define XDS_JTAG_STATE_EXIT1_DR 8 +#define XDS_JTAG_STATE_EXIT1_IR 9 +#define XDS_JTAG_STATE_EXIT2_DR 10 +#define XDS_JTAG_STATE_EXIT2_IR 11 +#define XDS_JTAG_STATE_SELECT_DR 12 +#define XDS_JTAG_STATE_SELECT_IR 13 +#define XDS_JTAG_STATE_UPDATE_DR 14 +#define XDS_JTAG_STATE_UPDATE_IR 15 +#define XDS_JTAG_STATE_CAPTURE_DR 16 +#define XDS_JTAG_STATE_CAPTURE_IR 17 + +/* XDS110 API JTAG transit definitions */ +#define XDS_JTAG_TRANSIT_QUICKEST 1 +#define XDS_JTAG_TRANSIT_VIA_CAPTURE 2 +#define XDS_JTAG_TRANSIT_VIA_IDLE 3 + +/* DAP register definitions as used by XDS110 APIs */ + +#define DAP_AP 0 /* DAP AP register type */ +#define DAP_DP 1 /* DAP DP register type */ + +#define DAP_DP_IDCODE 0x0 /* DAP DP IDCODE register (read only) */ +#define DAP_DP_ABORT 0x0 /* DAP DP ABORT register (write only) */ +#define DAP_DP_STAT 0x4 /* DAP DP STAT register (for read only) */ +#define DAP_DP_CTRL 0x4 /* DAP DP CTRL register (for write only) */ +#define DAP_DP_ADDR 0x8 /* DAP DP SELECT register (legacy name) */ +#define DAP_DP_RESEND 0x8 /* DAP DP RESEND register (read only) */ +#define DAP_DP_SELECT 0x8 /* DAP DP SELECT register (write only) */ +#define DAP_DP_RDBUFF 0xc /* DAP DP RDBUFF Read Buffer register */ + +#define DAP_AP_CSW 0x00 /* DAP AP Control Status Word */ +#define DAP_AP_TAR 0x04 /* DAP AP Transfer Address */ +#define DAP_AP_DRW 0x0C /* DAP AP Data Read/Write */ +#define DAP_AP_BD0 0x10 /* DAP AP Banked Data 0 */ +#define DAP_AP_BD1 0x14 /* DAP AP Banked Data 1 */ +#define DAP_AP_BD2 0x18 /* DAP AP Banked Data 2 */ +#define DAP_AP_BD3 0x1C /* DAP AP Banked Data 3 */ +#define DAP_AP_RTBL 0xF8 /* DAP AP Debug ROM Table */ +#define DAP_AP_IDR 0xFC /* DAP AP Identification Register */ + +/* Command packet definitions */ + +#define XDS_OUT_LEN 1 /* command (byte) */ +#define XDS_IN_LEN 4 /* error code (int) */ + +/* XDS API Commands */ +#define XDS_CONNECT 0x01 /* Connect JTAG connection */ +#define XDS_DISCONNECT 0x02 /* Disconnect JTAG connection */ +#define XDS_VERSION 0x03 /* Get firmware version and hardware ID */ +#define XDS_SET_TCK 0x04 /* Set TCK delay (to set TCK frequency) */ +#define XDS_SET_TRST 0x05 /* Assert or deassert nTRST signal */ +#define XDS_CYCLE_TCK 0x07 /* Toggle TCK for a number of cycles */ +#define XDS_GOTO_STATE 0x09 /* Go to requested JTAG state */ +#define XDS_JTAG_SCAN 0x0c /* Send and receive JTAG scan */ +#define XDS_SET_SRST 0x0e /* Assert or deassert nSRST signal */ +#define CMAPI_CONNECT 0x0f /* CMAPI connect */ +#define CMAPI_DISCONNECT 0x10 /* CMAPI disconnect */ +#define CMAPI_ACQUIRE 0x11 /* CMAPI acquire */ +#define CMAPI_RELEASE 0x12 /* CMAPI release */ +#define CMAPI_REG_READ 0x15 /* CMAPI DAP register read */ +#define CMAPI_REG_WRITE 0x16 /* CMAPI DAP register write */ +#define SWD_CONNECT 0x17 /* Switch from JTAG to SWD connection */ +#define SWD_DISCONNECT 0x18 /* Switch from SWD to JTAG connection */ +#define CJTAG_CONNECT 0x2b /* Switch from JTAG to cJTAG connection */ +#define CJTAG_DISCONNECT 0x2c /* Switch from cJTAG to JTAG connection */ +#define OCD_DAP_REQUEST 0x3a /* Handle block of DAP requests */ +#define OCD_SCAN_REQUEST 0x3b /* Handle block of JTAG scan requests */ +#define OCD_PATHMOVE 0x3c /* Handle PATHMOVE to navigate JTAG states */ + +#define CMD_IR_SCAN 1 +#define CMD_DR_SCAN 2 +#define CMD_RUNTEST 3 +#define CMD_STABLECLOCKS 4 + +/* Array to convert from OpenOCD tap_state_t to XDS JTAG state */ +const uint32_t xds_jtag_state[] = { + XDS_JTAG_STATE_EXIT2_DR, /* TAP_DREXIT2 = 0x0 */ + XDS_JTAG_STATE_EXIT1_DR, /* TAP_DREXIT1 = 0x1 */ + XDS_JTAG_STATE_SHIFT_DR, /* TAP_DRSHIFT = 0x2 */ + XDS_JTAG_STATE_PAUSE_DR, /* TAP_DRPAUSE = 0x3 */ + XDS_JTAG_STATE_SELECT_IR, /* TAP_IRSELECT = 0x4 */ + XDS_JTAG_STATE_UPDATE_DR, /* TAP_DRUPDATE = 0x5 */ + XDS_JTAG_STATE_CAPTURE_DR, /* TAP_DRCAPTURE = 0x6 */ + XDS_JTAG_STATE_SELECT_DR, /* TAP_DRSELECT = 0x7 */ + XDS_JTAG_STATE_EXIT2_IR, /* TAP_IREXIT2 = 0x8 */ + XDS_JTAG_STATE_EXIT1_IR, /* TAP_IREXIT1 = 0x9 */ + XDS_JTAG_STATE_SHIFT_IR, /* TAP_IRSHIFT = 0xa */ + XDS_JTAG_STATE_PAUSE_IR, /* TAP_IRPAUSE = 0xb */ + XDS_JTAG_STATE_IDLE, /* TAP_IDLE = 0xc */ + XDS_JTAG_STATE_UPDATE_IR, /* TAP_IRUPDATE = 0xd */ + XDS_JTAG_STATE_CAPTURE_IR, /* TAP_IRCAPTURE = 0xe */ + XDS_JTAG_STATE_RESET, /* TAP_RESET = 0xf */ +}; + +struct scan_result { + bool first; + uint8_t *buffer; + uint32_t num_bits; +}; + +struct xds110_info { + /* USB connection handles and data buffers */ + libusb_context *ctx; + libusb_device_handle *dev; + unsigned char read_payload[USB_PAYLOAD_SIZE]; + unsigned char write_packet[3]; + unsigned char write_payload[USB_PAYLOAD_SIZE]; + /* Status flags */ + bool is_connected; + bool is_cmapi_connected; + bool is_cmapi_acquired; + bool is_swd_mode; + bool is_ap_dirty; + /* DAP register caches */ + uint32_t select; + uint32_t rdbuff; + bool use_rdbuff; + /* TCK speed and delay count*/ + uint32_t speed; + uint32_t delay_count; + /* XDS110 serial number */ + char serial[XDS110_SERIAL_LEN + 1]; + /* XDS110 firmware and hardware version */ + uint32_t firmware; + uint16_t hardware; + /* Transaction queues */ + unsigned char txn_requests[MAX_DATA_BLOCK]; + uint32_t *txn_dap_results[MAX_DATA_BLOCK / 4]; + struct scan_result txn_scan_results[MAX_DATA_BLOCK / 4]; + uint32_t txn_request_size; + uint32_t txn_result_size; + uint32_t txn_result_count; +}; + +static struct xds110_info xds110 = { + .ctx = NULL, + .dev = NULL, + .is_connected = false, + .is_cmapi_connected = false, + .is_cmapi_acquired = false, + .is_swd_mode = false, + .is_ap_dirty = false, + .speed = XDS110_MAX_TCK_SPEED, + .delay_count = 0, + .serial = {0}, + .firmware = 0, + .hardware = 0, + .txn_request_size = 0, + .txn_result_size = 0, + .txn_result_count = 0 +}; + +static inline void xds110_set_u32(uint8_t *buffer, uint32_t value) +{ + buffer[3] = (value >> 24) & 0xff; + buffer[2] = (value >> 16) & 0xff; + buffer[1] = (value >> 8) & 0xff; + buffer[0] = (value >> 0) & 0xff; +} + +static inline void xds110_set_u16(uint8_t *buffer, uint16_t value) +{ + buffer[1] = (value >> 8) & 0xff; + buffer[0] = (value >> 0) & 0xff; +} + +static inline uint32_t xds110_get_u32(uint8_t *buffer) +{ + uint32_t value = (((uint32_t)buffer[3]) << 24) | + (((uint32_t)buffer[2]) << 16) | + (((uint32_t)buffer[1]) << 8) | + (((uint32_t)buffer[0]) << 0); + return value; +} + +static inline uint16_t xds110_get_u16(uint8_t *buffer) +{ + uint16_t value = (((uint32_t)buffer[1]) << 8) | + (((uint32_t)buffer[0]) << 0); + return value; +} + +/*************************************************************************** + * usb connection routines * + * * + * The following functions handle connecting, reading, and writing to * + * the XDS110 over USB using the libusb library. * + ***************************************************************************/ + +static bool usb_connect(void) +{ + libusb_context *ctx = NULL; + libusb_device **list = NULL; + libusb_device_handle *dev = NULL; + + struct libusb_device_descriptor desc; + + uint16_t vid = 0x0451; + uint16_t pid = 0xbef3; + ssize_t count = 0; + ssize_t i = 0; + int result = 0; + bool found = false; + + /* Initialize libusb context */ + result = libusb_init(&ctx); + + if (0 == result) { + /* Get list of USB devices attached to system */ + count = libusb_get_device_list(ctx, &list); + if (count <= 0) { + result = -1; + list = NULL; + } + } + + if (0 == result) { + /* Scan through list of devices for any XDS110s */ + for (i = 0; i < count; i++) { + /* Check for device VID/PID match */ + libusb_get_device_descriptor(list[i], &desc); + if (desc.idVendor == vid && desc.idProduct == pid) { + result = libusb_open(list[i], &dev); + if (0 == result) { + const int MAX_DATA = 256; + unsigned char data[MAX_DATA + 1]; + *data = '\0'; + + /* May be the requested device if serial number matches */ + if (0 == xds110.serial[0]) { + /* No serial number given; match first XDS110 found */ + found = true; + break; + } else { + /* Get the device's serial number string */ + result = libusb_get_string_descriptor_ascii(dev, + desc.iSerialNumber, data, MAX_DATA); + if (0 < result && + 0 == strcmp((char *)data, (char *)xds110.serial)) { + found = true; + break; + } + } + + /* If we fall though to here, we don't want this device */ + libusb_close(dev); + dev = NULL; + } + } + } + } + + /* + * We can fall through the for() loop with two possible exit conditions: + * 1) found the right XDS110, and that device is open + * 2) didn't find the XDS110, and no devices are currently open + */ + + if (NULL != list) { + /* Free the device list, we're done with it */ + libusb_free_device_list(list, 1); + } + + if (found) { + /* Save the context and device handles */ + xds110.ctx = ctx; + xds110.dev = dev; + + /* Set libusb to auto detach kernel */ + (void)libusb_set_auto_detach_kernel_driver(dev, 1); + + /* Claim the debug interface on the XDS110 */ + result = libusb_claim_interface(dev, INTERFACE_DEBUG); + } else { + /* Couldn't find an XDS110, flag the error */ + result = -1; + } + + /* On an error, clean up what we can */ + if (0 != result) { + if (NULL != dev) { + /* Release the debug and data interface on the XDS110 */ + (void)libusb_release_interface(dev, INTERFACE_DEBUG); + libusb_close(dev); + } + if (NULL != ctx) + libusb_exit(ctx); + xds110.ctx = NULL; + xds110.dev = NULL; + } + + /* Log the results */ + if (0 == result) + LOG_INFO("XDS110: connected"); + else + LOG_ERROR("XDS110: failed to connect"); + + return (0 == result) ? true : false; +} + +static void usb_disconnect(void) +{ + if (NULL != xds110.dev) { + /* Release the debug and data interface on the XDS110 */ + (void)libusb_release_interface(xds110.dev, INTERFACE_DEBUG); + libusb_close(xds110.dev); + xds110.dev = NULL; + } + if (NULL != xds110.ctx) { + libusb_exit(xds110.ctx); + xds110.ctx = NULL; + } + + LOG_INFO("XDS110: disconnected"); +} + +static bool usb_read(unsigned char *buffer, int size, int *bytes_read, + int timeout) +{ + int result; + + if (NULL == xds110.dev || NULL == buffer || NULL == bytes_read) + return false; + + /* Force a non-zero timeout to prevent blocking */ + if (0 == timeout) + timeout = DEFAULT_TIMEOUT; + + result = libusb_bulk_transfer(xds110.dev, ENDPOINT_DEBUG_IN, buffer, size, + bytes_read, timeout); + + return (0 == result) ? true : false; +} + +static bool usb_write(unsigned char *buffer, int size, int *written) +{ + int bytes_written = 0; + int result = LIBUSB_SUCCESS; + int retries = 0; + + if (NULL == xds110.dev || NULL == buffer) + return false; + + result = libusb_bulk_transfer(xds110.dev, ENDPOINT_DEBUG_OUT, buffer, + size, &bytes_written, 0); + + while (LIBUSB_ERROR_PIPE == result && retries < 3) { + /* Try clearing the pipe stall and retry transfer */ + libusb_clear_halt(xds110.dev, ENDPOINT_DEBUG_OUT); + result = libusb_bulk_transfer(xds110.dev, ENDPOINT_DEBUG_OUT, buffer, + size, &bytes_written, 0); + retries++; + } + + if (NULL != written) + *written = bytes_written; + + return (0 == result && size == bytes_written) ? true : false; +} + +static bool usb_get_response(uint32_t *total_bytes_read, uint32_t timeout) +{ + static unsigned char buffer[MAX_PACKET]; + int bytes_read; + uint16_t size; + uint16_t count; + bool success; + + size = 0; + success = true; + while (success) { + success = usb_read(buffer, sizeof(buffer), &bytes_read, timeout); + if (success) { + /* + * Validate that this appears to be a good response packet + * First check it contains enough data for header and error + * code, plus the first character is the start character + */ + if (bytes_read >= 7 && '*' == buffer[0]) { + /* Extract the payload size */ + size = xds110_get_u16(&buffer[1]); + /* Sanity test on payload size */ + if (USB_PAYLOAD_SIZE >= size && 4 <= size) { + /* Check we didn't get more data than expected */ + if ((bytes_read - 3) <= size) { + /* Packet appears to be valid, move on */ + break; + } + } + } + } + /* + * Somehow received an invalid packet, retry till we + * time out or a valid response packet is received + */ + } + + /* Abort now if we didn't receive a valid response */ + if (!success) { + if (NULL != total_bytes_read) + *total_bytes_read = 0; + return false; + } + + /* Build the return payload into xds110.read_payload */ + + /* Copy over payload data from received buffer (skipping header) */ + count = 0; + bytes_read -= 3; + memcpy((void *)&xds110.read_payload[count], (void *)&buffer[3], bytes_read); + count += bytes_read; + /* + * Drop timeout to just 1/2 second. Once the XDS110 starts sending + * a response, the remaining packets should arrive in short order + */ + if (timeout > 500) + timeout = 500; /* ms */ + + /* If there's more data to retrieve, get it now */ + while ((count < size) && success) { + success = usb_read(buffer, sizeof(buffer), &bytes_read, timeout); + if (success) { + if ((count + bytes_read) > size) { + /* Read too much data, not a valid packet, abort */ + success = false; + } else { + /* Copy this data over to xds110.read_payload */ + memcpy((void *)&xds110.read_payload[count], (void *)buffer, + bytes_read); + count += bytes_read; + } + } + } + + if (!success) + count = 0; + if (NULL != total_bytes_read) + *total_bytes_read = count; + + return success; +} + +static bool usb_send_command(uint16_t size) +{ + int written; + bool success = true; + + /* Check the packet length */ + if (size > USB_PAYLOAD_SIZE) + return false; + + /* Place the start character into the packet buffer */ + xds110.write_packet[0] = '*'; + + /* Place the payload size into the packet buffer */ + xds110_set_u16(&xds110.write_packet[1], size); + + /* Adjust size to include header */ + size += 3; + + /* Send the data via the USB connection */ + success = usb_write(xds110.write_packet, (int)size, &written); + + /* Check if the correct number of bytes was written */ + if (written != (int)size) + success = false; + + return success; +} + +/*************************************************************************** + * XDS110 firmware API routines * + * * + * The following functions handle calling into the XDS110 firmware to * + * perform requested debug actions. * + ***************************************************************************/ + +static bool xds_execute(uint32_t out_length, uint32_t in_length, + uint32_t attempts, uint32_t timeout) +{ + bool done = false; + bool success = true; + int error = 0; + uint32_t bytes_read = 0; + + if (NULL == xds110.dev) + return false; + + while (!done && attempts > 0) { + attempts--; + + /* Send command to XDS110 */ + success = usb_send_command(out_length); + + if (success) { + /* Get response from XDS110 */ + success = usb_get_response(&bytes_read, timeout); + } + + if (success) { + /* Check for valid response from XDS code handling */ + if (bytes_read != in_length) { + /* Unexpected amount of data returned */ + success = false; + } else { + /* Extract error code from return packet */ + error = (int)xds110_get_u32(&xds110.read_payload[0]); + done = true; + } + } + } + + if (!success) + error = SC_ERR_XDS110_FAIL; + + if (0 != error) + success = false; + + return success; +} + +static bool xds_connect(void) +{ + bool success; + + xds110.write_payload[0] = XDS_CONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool xds_disconnect(void) +{ + bool success; + + xds110.write_payload[0] = XDS_DISCONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool xds_version(uint32_t *firmware_id, uint16_t *hardware_id) +{ + uint8_t *fw_id_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */ + uint8_t *hw_id_pntr = &xds110.read_payload[XDS_IN_LEN + 4]; /* 16-bits */ + + bool success; + + xds110.write_payload[0] = XDS_VERSION; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN + 6, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + if (success) { + if (NULL != firmware_id) + *firmware_id = xds110_get_u32(fw_id_pntr); + if (NULL != hardware_id) + *hardware_id = xds110_get_u16(hw_id_pntr); + } + + return success; +} + +static bool xds_set_tck_delay(uint32_t delay) +{ + uint8_t *delay_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ + + bool success; + + xds110.write_payload[0] = XDS_SET_TCK; + + xds110_set_u32(delay_pntr, delay); + + success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool xds_set_trst(uint8_t trst) +{ + uint8_t *trst_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ + + bool success; + + xds110.write_payload[0] = XDS_SET_TRST; + + *trst_pntr = trst; + + success = xds_execute(XDS_OUT_LEN + 1, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool xds_cycle_tck(uint32_t count) +{ + uint8_t *count_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ + + bool success; + + xds110.write_payload[0] = XDS_CYCLE_TCK; + + xds110_set_u32(count_pntr, count); + + success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool xds_goto_state(uint32_t state) +{ + uint8_t *state_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ + uint8_t *transit_pntr = &xds110.write_payload[XDS_OUT_LEN+4]; /* 32-bits */ + + bool success; + + xds110.write_payload[0] = XDS_GOTO_STATE; + + xds110_set_u32(state_pntr, state); + xds110_set_u32(transit_pntr, XDS_JTAG_TRANSIT_QUICKEST); + + success = xds_execute(XDS_OUT_LEN+8, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool xds_jtag_scan(uint32_t shift_state, uint16_t shift_bits, + uint32_t end_state, uint8_t *data_out, uint8_t *data_in) +{ + uint8_t *bits_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 16-bits */ + uint8_t *path_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */ + uint8_t *trans1_pntr = &xds110.write_payload[XDS_OUT_LEN + 3]; /* 8-bits */ + uint8_t *end_pntr = &xds110.write_payload[XDS_OUT_LEN + 4]; /* 8-bits */ + uint8_t *trans2_pntr = &xds110.write_payload[XDS_OUT_LEN + 5]; /* 8-bits */ + uint8_t *pre_pntr = &xds110.write_payload[XDS_OUT_LEN + 6]; /* 16-bits */ + uint8_t *pos_pntr = &xds110.write_payload[XDS_OUT_LEN + 8]; /* 16-bits */ + uint8_t *delay_pntr = &xds110.write_payload[XDS_OUT_LEN + 10]; /* 16-bits */ + uint8_t *rep_pntr = &xds110.write_payload[XDS_OUT_LEN + 12]; /* 16-bits */ + uint8_t *out_pntr = &xds110.write_payload[XDS_OUT_LEN + 14]; /* 16-bits */ + uint8_t *in_pntr = &xds110.write_payload[XDS_OUT_LEN + 16]; /* 16-bits */ + uint8_t *data_out_pntr = &xds110.write_payload[XDS_OUT_LEN + 18]; + uint8_t *data_in_pntr = &xds110.read_payload[XDS_IN_LEN+0]; + + uint16_t total_bytes = DIV_ROUND_UP(shift_bits, 8); + + bool success; + + xds110.write_payload[0] = XDS_JTAG_SCAN; + + xds110_set_u16(bits_pntr, shift_bits); /* bits to scan */ + *path_pntr = (uint8_t)(shift_state & 0xff); /* IR vs DR path */ + *trans1_pntr = (uint8_t)XDS_JTAG_TRANSIT_QUICKEST; /* start state route */ + *end_pntr = (uint8_t)(end_state & 0xff); /* JTAG state after scan */ + *trans2_pntr = (uint8_t)XDS_JTAG_TRANSIT_QUICKEST; /* end state route */ + xds110_set_u16(pre_pntr, 0); /* number of preamble bits */ + xds110_set_u16(pos_pntr, 0); /* number of postamble bits */ + xds110_set_u16(delay_pntr, 0); /* number of extra TCKs after scan */ + xds110_set_u16(rep_pntr, 1); /* number of repetitions */ + xds110_set_u16(out_pntr, total_bytes); /* out buffer offset (if repeats) */ + xds110_set_u16(in_pntr, total_bytes); /* in buffer offset (if repeats) */ + + memcpy((void *)data_out_pntr, (void *)data_out, total_bytes); + + success = xds_execute(XDS_OUT_LEN + 18 + total_bytes, + XDS_IN_LEN + total_bytes, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); + + if (success) + memcpy((void *)data_in, (void *)data_in_pntr, total_bytes); + + return success; +} + +static bool xds_set_srst(uint8_t srst) +{ + uint8_t *srst_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ + + bool success; + + xds110.write_payload[0] = XDS_SET_SRST; + + *srst_pntr = srst; + + success = xds_execute(XDS_OUT_LEN + 1, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool cmapi_connect(uint32_t *idcode) +{ + uint8_t *idcode_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */ + + bool success; + + xds110.write_payload[0] = CMAPI_CONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN+4, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + if (success) { + if (NULL != idcode) + *idcode = xds110_get_u32(idcode_pntr); + } + + return success; +} + +static bool cmapi_disconnect(void) +{ + bool success; + + xds110.write_payload[0] = CMAPI_DISCONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool cmapi_acquire(void) +{ + bool success; + + xds110.write_payload[0] = CMAPI_ACQUIRE; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool cmapi_release(void) +{ + bool success; + + xds110.write_payload[0] = CMAPI_RELEASE; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool cmapi_read_dap_reg(uint32_t type, uint32_t ap_num, + uint32_t address, uint32_t *value) +{ + uint8_t *type_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ + uint8_t *ap_num_pntr = &xds110.write_payload[XDS_OUT_LEN + 1]; /* 8-bits */ + uint8_t *address_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */ + uint8_t *value_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */ + + bool success; + + xds110.write_payload[0] = CMAPI_REG_READ; + + *type_pntr = (uint8_t)(type & 0xff); + *ap_num_pntr = (uint8_t)(ap_num & 0xff); + *address_pntr = (uint8_t)(address & 0xff); + + success = xds_execute(XDS_OUT_LEN + 3, XDS_IN_LEN + 4, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + if (success) { + if (NULL != value) + *value = xds110_get_u32(value_pntr); + } + + return success; +} + +static bool cmapi_write_dap_reg(uint32_t type, uint32_t ap_num, + uint32_t address, uint32_t *value) +{ + uint8_t *type_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ + uint8_t *ap_num_pntr = &xds110.write_payload[XDS_OUT_LEN + 1]; /* 8-bits */ + uint8_t *address_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */ + uint8_t *value_pntr = &xds110.write_payload[XDS_OUT_LEN + 3]; /* 32-bits */ + + bool success; + + if (NULL == value) + return false; + + xds110.write_payload[0] = CMAPI_REG_WRITE; + + *type_pntr = (uint8_t)(type & 0xff); + *ap_num_pntr = (uint8_t)(ap_num & 0xff); + *address_pntr = (uint8_t)(address & 0xff); + xds110_set_u32(value_pntr, *value); + + success = xds_execute(XDS_OUT_LEN + 7, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool swd_connect(void) +{ + bool success; + + xds110.write_payload[0] = SWD_CONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool swd_disconnect(void) +{ + bool success; + + xds110.write_payload[0] = SWD_DISCONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool cjtag_connect(uint32_t format) +{ + uint8_t *format_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ + + bool success; + + xds110.write_payload[0] = CJTAG_CONNECT; + + xds110_set_u32(format_pntr, format); + + success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool cjtag_disconnect(void) +{ + bool success; + + xds110.write_payload[0] = CJTAG_DISCONNECT; + + success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + return success; +} + +static bool ocd_dap_request(uint8_t *dap_requests, uint32_t request_size, + uint32_t *dap_results, uint32_t result_count) +{ + uint8_t *request_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; + uint8_t *result_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; + + bool success; + + if (NULL == dap_requests || NULL == dap_results) + return false; + + xds110.write_payload[0] = OCD_DAP_REQUEST; + + memcpy((void *)request_pntr, (void *)dap_requests, request_size); + + success = xds_execute(XDS_OUT_LEN + request_size, + XDS_IN_LEN + (result_count * 4), DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + if (success && (result_count > 0)) + memcpy((void *)dap_results, (void *)result_pntr, result_count * 4); + + return success; +} + +static bool ocd_scan_request(uint8_t *scan_requests, uint32_t request_size, + uint8_t *scan_results, uint32_t result_size) +{ + uint8_t *request_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; + uint8_t *result_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; + + bool success; + + if (NULL == scan_requests || NULL == scan_results) + return false; + + xds110.write_payload[0] = OCD_SCAN_REQUEST; + + memcpy((void *)request_pntr, (void *)scan_requests, request_size); + + success = xds_execute(XDS_OUT_LEN + request_size, + XDS_IN_LEN + result_size, DEFAULT_ATTEMPTS, + DEFAULT_TIMEOUT); + + if (success && (result_size > 0)) + memcpy((void *)scan_results, (void *)result_pntr, result_size); + + return success; +} + +static bool ocd_pathmove(uint32_t num_states, uint8_t *path) +{ + uint8_t *num_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ + uint8_t *path_pntr = &xds110.write_payload[XDS_OUT_LEN + 4]; + + bool success; + + if (NULL == path) + return false; + + xds110.write_payload[0] = OCD_PATHMOVE; + + xds110_set_u32(num_pntr, num_states); + + memcpy((void *)path_pntr, (void *)path, num_states); + + success = xds_execute(XDS_OUT_LEN + 4 + num_states, XDS_IN_LEN, + DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); + + return success; +} + +/*************************************************************************** + * swd driver interface * + * * + * The following functions provide SWD support to OpenOCD. * + ***************************************************************************/ + +static int xds110_swd_init(void) +{ + xds110.is_swd_mode = true; + return ERROR_OK; +} + +static int xds110_swd_switch_seq(enum swd_special_seq seq) +{ + uint32_t idcode; + bool success; + + switch (seq) { + case LINE_RESET: + LOG_ERROR("Sequence SWD line reset (%d) not supported", seq); + return ERROR_FAIL; + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + xds110.is_swd_mode = false; + xds110.is_cmapi_connected = false; + xds110.is_cmapi_acquired = false; + /* Run sequence to put target in SWD mode */ + success = swd_connect(); + /* Re-iniitialize CMAPI API for DAP access */ + if (success) { + xds110.is_swd_mode = true; + success = cmapi_connect(&idcode); + if (success) { + xds110.is_cmapi_connected = true; + success = cmapi_acquire(); + } + } + break; + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + xds110.is_swd_mode = false; + xds110.is_cmapi_connected = false; + xds110.is_cmapi_acquired = false; + /* Run sequence to put target in JTAG mode */ + success = swd_disconnect(); + if (success) { + /* Re-initialize JTAG interface */ + success = cjtag_connect(MODE_JTAG); + } + break; + default: + LOG_ERROR("Sequence %d not supported", seq); + return ERROR_FAIL; + } + + if (success) + return ERROR_OK; + else + return ERROR_FAIL; +} + +static bool xds110_legacy_read_reg(uint8_t cmd, uint32_t *value) +{ + /* Make sure this is a read request */ + bool is_read_request = (0 != (SWD_CMD_RnW & cmd)); + /* Determine whether this is a DP or AP register access */ + uint32_t type = (0 != (SWD_CMD_APnDP & cmd)) ? DAP_AP : DAP_DP; + /* Determine the AP number from cached SELECT value */ + uint32_t ap_num = (xds110.select & 0xff000000) >> 24; + /* Extract register address from command */ + uint32_t address = ((cmd & SWD_CMD_A32) >> 1); + /* Extract bank address from cached SELECT value */ + uint32_t bank = (xds110.select & 0x000000f0); + + uint32_t reg_value = 0; + uint32_t temp_value = 0; + + bool success; + + if (!is_read_request) + return false; + + if (DAP_AP == type) { + /* Add bank address to register address for CMAPI call */ + address |= bank; + } + + if (DAP_DP == type && DAP_DP_RDBUFF == address && xds110.use_rdbuff) { + /* If RDBUFF is cached and this is a DP RDBUFF read, use the cache */ + reg_value = xds110.rdbuff; + success = true; + } else if (DAP_AP == type && DAP_AP_DRW == address && xds110.use_rdbuff) { + /* If RDBUFF is cached and this is an AP DRW read, use the cache, */ + /* but still call into the firmware to get the next read. */ + reg_value = xds110.rdbuff; + success = cmapi_read_dap_reg(type, ap_num, address, &temp_value); + } else { + success = cmapi_read_dap_reg(type, ap_num, address, &temp_value); + if (success) + reg_value = temp_value; + } + + /* Mark that we have consumed or invalidated the RDBUFF cache */ + xds110.use_rdbuff = false; + + /* Handle result of read attempt */ + if (!success) + LOG_ERROR("XDS110: failed to read DAP register"); + else if (NULL != value) + *value = reg_value; + + if (success && DAP_AP == type) { + /* + * On a successful DAP AP read, we actually have the value from RDBUFF, + * the firmware will have run the AP request and made the RDBUFF read + */ + xds110.use_rdbuff = true; + xds110.rdbuff = temp_value; + } + + return success; +} + +static bool xds110_legacy_write_reg(uint8_t cmd, uint32_t value) +{ + /* Make sure this isn't a read request */ + bool is_read_request = (0 != (SWD_CMD_RnW & cmd)); + /* Determine whether this is a DP or AP register access */ + uint32_t type = (0 != (SWD_CMD_APnDP & cmd)) ? DAP_AP : DAP_DP; + /* Determine the AP number from cached SELECT value */ + uint32_t ap_num = (xds110.select & 0xff000000) >> 24; + /* Extract register address from command */ + uint32_t address = ((cmd & SWD_CMD_A32) >> 1); + /* Extract bank address from cached SELECT value */ + uint32_t bank = (xds110.select & 0x000000f0); + + bool success; + + if (is_read_request) + return false; + + /* Invalidate the RDBUFF cache */ + xds110.use_rdbuff = false; + + if (DAP_AP == type) { + /* Add bank address to register address for CMAPI call */ + address |= bank; + /* Any write to an AP register invalidates the firmware's cache */ + xds110.is_ap_dirty = true; + } else if (DAP_DP_SELECT == address) { + /* Any write to the SELECT register invalidates the firmware's cache */ + xds110.is_ap_dirty = true; + } + + success = cmapi_write_dap_reg(type, ap_num, address, &value); + + if (!success) { + LOG_ERROR("XDS110: failed to write DAP register"); + } else { + /* + * If the debugger wrote to SELECT, cache the value + * to use to build the apNum and address values above + */ + if ((DAP_DP == type) && (DAP_DP_SELECT == address)) + xds110.select = value; + } + + return success; +} + +static int xds110_swd_run_queue(void) +{ + static uint32_t dap_results[MAX_RESULT_QUEUE]; + uint8_t cmd; + uint32_t request; + uint32_t result; + uint32_t value; + bool success = true; + + if (0 == xds110.txn_request_size) + return ERROR_OK; + + /* Terminate request queue */ + xds110.txn_requests[xds110.txn_request_size++] = 0; + + if (xds110.firmware >= OCD_FIRMWARE_VERSION) { + /* XDS110 firmware has the API to directly handle the queue */ + success = ocd_dap_request(xds110.txn_requests, + xds110.txn_request_size, dap_results, xds110.txn_result_count); + } else { + /* Legacy firmware needs to handle queue via discrete DAP calls */ + request = 0; + result = 0; + while (xds110.txn_requests[request] != 0) { + cmd = xds110.txn_requests[request++]; + if (0 == (SWD_CMD_RnW & cmd)) { + /* DAP register write command */ + value = (uint32_t)(xds110.txn_requests[request++]) << 0; + value |= (uint32_t)(xds110.txn_requests[request++]) << 8; + value |= (uint32_t)(xds110.txn_requests[request++]) << 16; + value |= (uint32_t)(xds110.txn_requests[request++]) << 24; + if (success) + success = xds110_legacy_write_reg(cmd, value); + } else { + /* DAP register read command */ + value = 0; + if (success) + success = xds110_legacy_read_reg(cmd, &value); + dap_results[result++] = value; + } + } + } + + /* Transfer results into caller's buffers */ + for (result = 0; result < xds110.txn_result_count; result++) + if (0 != xds110.txn_dap_results[result]) + *xds110.txn_dap_results[result] = dap_results[result]; + + xds110.txn_request_size = 0; + xds110.txn_result_size = 0; + xds110.txn_result_count = 0; + + return (success) ? ERROR_OK : ERROR_FAIL; +} + +static void xds110_swd_queue_cmd(uint8_t cmd, uint32_t *value) +{ + /* Check if this is a read or write request */ + bool is_read_request = (0 != (SWD_CMD_RnW & cmd)); + /* Determine whether this is a DP or AP register access */ + uint32_t type = (0 != (SWD_CMD_APnDP & cmd)) ? DAP_AP : DAP_DP; + /* Extract register address from command */ + uint32_t address = ((cmd & SWD_CMD_A32) >> 1); + uint32_t request_size = (is_read_request) ? 1 : 5; + + /* Check if new request would be too large to fit */ + if (((xds110.txn_request_size + request_size + 1) > MAX_DATA_BLOCK) || + ((xds110.txn_result_count + 1) > MAX_RESULT_QUEUE)) + xds110_swd_run_queue(); + + /* Set the START bit in cmd to ensure cmd is not zero */ + /* (a value of zero is used to terminate the buffer) */ + cmd |= SWD_CMD_START; + + /* Add request to queue; queue is built marshalled for XDS110 call */ + if (is_read_request) { + /* Queue read request, save pointer to pass back result */ + xds110.txn_requests[xds110.txn_request_size++] = cmd; + xds110.txn_dap_results[xds110.txn_result_count++] = value; + xds110.txn_result_size += 4; + } else { + /* Check for and prevent sticky overrun detection */ + if (DAP_DP == type && DAP_DP_CTRL == address && + (*value & CORUNDETECT)) { + LOG_DEBUG("XDS110: refusing to enable sticky overrun detection"); + *value &= ~CORUNDETECT; + } + /* Queue write request, add value directly to queue buffer */ + xds110.txn_requests[xds110.txn_request_size++] = cmd; + xds110.txn_requests[xds110.txn_request_size++] = (*value >> 0) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (*value >> 8) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (*value >> 16) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (*value >> 24) & 0xff; + } +} + +static void xds110_swd_read_reg(uint8_t cmd, uint32_t *value, + uint32_t ap_delay_clk) +{ + xds110_swd_queue_cmd(cmd, value); +} +static void xds110_swd_write_reg(uint8_t cmd, uint32_t value, + uint32_t ap_delay_clk) +{ + xds110_swd_queue_cmd(cmd, &value); +} + +/*************************************************************************** + * jtag interface * + * * + * The following functions provide XDS110 interface to OpenOCD. * + ***************************************************************************/ + +static void xds110_show_info(void) +{ + uint32_t firmware = xds110.firmware; + + LOG_INFO("XDS110: firmware version = %d.%d.%d.%d", + (((firmware >> 28) & 0xf) * 10) + ((firmware >> 24) & 0xf), + (((firmware >> 20) & 0xf) * 10) + ((firmware >> 16) & 0xf), + (((firmware >> 12) & 0xf) * 10) + ((firmware >> 8) & 0xf), + (((firmware >> 4) & 0xf) * 10) + ((firmware >> 0) & 0xf)); + LOG_INFO("XDS110: hardware version = 0x%04x", xds110.hardware); + if (0 != xds110.serial[0]) + LOG_INFO("XDS110: serial number = %s)", xds110.serial); + if (xds110.is_swd_mode) { + LOG_INFO("XDS110: connected to target via SWD"); + LOG_INFO("XDS110: SWCLK set to %d kHz", xds110.speed); + } else { + LOG_INFO("XDS110: connected to target via JTAG"); + LOG_INFO("XDS110: TCK set to %d kHz", xds110.speed); + } + + /* Alert user that there's a better firmware to use */ + if (firmware < OCD_FIRMWARE_VERSION) { + LOG_WARNING("XDS110: the firmware is not optimized for OpenOCD"); + LOG_WARNING(OCD_FIRMWARE_UPGRADE); + } +} + +static int xds110_quit(void) +{ + if (xds110.is_cmapi_acquired) { + (void)cmapi_release(); + xds110.is_cmapi_acquired = false; + } + if (xds110.is_cmapi_connected) { + (void)cmapi_disconnect(); + xds110.is_cmapi_connected = false; + } + if (xds110.is_connected) { + if (xds110.is_swd_mode) { + /* Switch out of SWD mode */ + (void)swd_disconnect(); + } else { + /* Switch out of cJTAG mode */ + (void)cjtag_disconnect(); + } + /* Tell firmware we're disconnecting */ + (void)xds_disconnect(); + xds110.is_connected = false; + } + /* Close down the USB connection to the XDS110 debug probe */ + usb_disconnect(); + + return ERROR_OK; +} + +static int xds110_init(void) +{ + bool success; + + /* Establish USB connection to the XDS110 debug probe */ + success = usb_connect(); + + if (success) { + /* Send connect message to XDS110 firmware */ + success = xds_connect(); + if (success) + xds110.is_connected = true; + } + + if (success) { + uint32_t firmware; + uint16_t hardware; + + /* Retrieve version IDs from firmware */ + /* Version numbers are stored in BCD format */ + success = xds_version(&firmware, &hardware); + if (success) { + /* Save the firmware and hardware version */ + xds110.firmware = firmware; + xds110.hardware = hardware; + } + } + + if (success) { + success = xds_set_trst(0); + if (success) + success = xds_cycle_tck(50); + if (success) + success = xds_set_trst(1); + if (success) + success = xds_cycle_tck(50); + } + + if (success) { + if (xds110.is_swd_mode) { + /* Switch to SWD if needed */ + success = swd_connect(); + } else { + success = cjtag_connect(MODE_JTAG); + } + } + + if (success && xds110.is_swd_mode) { + uint32_t idcode; + + /* Connect to CMAPI interface in XDS110 */ + success = cmapi_connect(&idcode); + + /* Acquire exclusive access to CMAPI interface */ + if (success) { + xds110.is_cmapi_connected = true; + success = cmapi_acquire(); + if (success) + xds110.is_cmapi_acquired = true; + } + } + + if (!success) + xds110_quit(); + + if (success) + xds110_show_info(); + + return (success) ? ERROR_OK : ERROR_FAIL; +} + +static void xds110_legacy_scan(uint32_t shift_state, uint32_t total_bits, + uint32_t end_state, uint8_t *data_out, uint8_t *data_in) +{ + (void)xds_jtag_scan(shift_state, total_bits, end_state, data_out, data_in); +} + +static void xds110_legacy_runtest(uint32_t clocks, uint32_t end_state) +{ + xds_goto_state(XDS_JTAG_STATE_IDLE); + xds_cycle_tck(clocks); + xds_goto_state(end_state); +} + +static void xds110_legacy_stableclocks(uint32_t clocks) +{ + xds_cycle_tck(clocks); +} + +static void xds110_flush(void) +{ + uint8_t command; + uint32_t clocks; + uint32_t shift_state; + uint32_t end_state; + uint32_t bits; + uint32_t bytes; + uint32_t request; + uint32_t result; + uint8_t *data_out; + uint8_t data_in[MAX_DATA_BLOCK]; + uint8_t *data_pntr; + + if (0 == xds110.txn_request_size) + return; + + /* Terminate request queue */ + xds110.txn_requests[xds110.txn_request_size++] = 0; + + if (xds110.firmware >= OCD_FIRMWARE_VERSION) { + /* Updated firmware has the API to directly handle the queue */ + (void)ocd_scan_request(xds110.txn_requests, xds110.txn_request_size, + data_in, xds110.txn_result_size); + } else { + /* Legacy firmware needs to handle queue via discrete JTAG calls */ + request = 0; + result = 0; + while (xds110.txn_requests[request] != 0) { + command = xds110.txn_requests[request++]; + switch (command) { + case CMD_IR_SCAN: + case CMD_DR_SCAN: + if (command == CMD_IR_SCAN) + shift_state = XDS_JTAG_STATE_SHIFT_IR; + else + shift_state = XDS_JTAG_STATE_SHIFT_DR; + end_state = (uint32_t)(xds110.txn_requests[request++]); + bits = (uint32_t)(xds110.txn_requests[request++]) << 0; + bits |= (uint32_t)(xds110.txn_requests[request++]) << 8; + data_out = &xds110.txn_requests[request]; + bytes = DIV_ROUND_UP(bits, 8); + xds110_legacy_scan(shift_state, bits, end_state, data_out, + &data_in[result]); + result += bytes; + request += bytes; + break; + case CMD_RUNTEST: + clocks = (uint32_t)(xds110.txn_requests[request++]) << 0; + clocks |= (uint32_t)(xds110.txn_requests[request++]) << 8; + clocks |= (uint32_t)(xds110.txn_requests[request++]) << 16; + clocks |= (uint32_t)(xds110.txn_requests[request++]) << 24; + end_state = (uint32_t)xds110.txn_requests[request++]; + xds110_legacy_runtest(clocks, end_state); + break; + case CMD_STABLECLOCKS: + clocks = (uint32_t)(xds110.txn_requests[request++]) << 0; + clocks |= (uint32_t)(xds110.txn_requests[request++]) << 8; + clocks |= (uint32_t)(xds110.txn_requests[request++]) << 16; + clocks |= (uint32_t)(xds110.txn_requests[request++]) << 24; + xds110_legacy_stableclocks(clocks); + break; + default: + LOG_ERROR("BUG: unknown JTAG command type 0x%x encountered", + command); + exit(-1); + break; + } + } + } + + /* Transfer results into caller's buffers from data_in buffer */ + bits = 0; /* Bit offset into current scan result */ + data_pntr = data_in; + for (result = 0; result < xds110.txn_result_count; result++) { + if (xds110.txn_scan_results[result].first) { + if (bits != 0) { + bytes = DIV_ROUND_UP(bits, 8); + data_pntr += bytes; + } + bits = 0; + } + if (xds110.txn_scan_results[result].buffer != 0) + bit_copy(xds110.txn_scan_results[result].buffer, 0, data_pntr, + bits, xds110.txn_scan_results[result].num_bits); + bits += xds110.txn_scan_results[result].num_bits; + } + + xds110.txn_request_size = 0; + xds110.txn_result_size = 0; + xds110.txn_result_count = 0; +} + +static void xds110_execute_reset(struct jtag_command *cmd) +{ + char trst; + char srst; + + if (cmd->cmd.reset->trst != -1) { + if (cmd->cmd.reset->trst == 0) { + /* Deassert nTRST (active low) */ + trst = 1; + } else { + /* Assert nTRST (active low) */ + trst = 0; + } + (void)xds_set_trst(trst); + } + + if (cmd->cmd.reset->srst != -1) { + if (cmd->cmd.reset->srst == 0) { + /* Deassert nSRST (active low) */ + srst = 1; + } else { + /* Assert nSRST (active low) */ + srst = 0; + } + (void)xds_set_srst(srst); + } +} + +static void xds110_execute_sleep(struct jtag_command *cmd) +{ + jtag_sleep(cmd->cmd.sleep->us); + return; +} + +static void xds110_execute_tlr_reset(struct jtag_command *cmd) +{ + (void)xds_goto_state(XDS_JTAG_STATE_RESET); + + return; +} + +static void xds110_execute_pathmove(struct jtag_command *cmd) +{ + uint32_t i; + uint32_t num_states; + uint8_t *path; + + num_states = (uint32_t)cmd->cmd.pathmove->num_states; + + if (num_states == 0) + return; + + path = (uint8_t *)malloc(num_states * sizeof(uint8_t)); + if (path == 0) { + LOG_ERROR("XDS110: unable to allocate memory"); + return; + } + + /* Convert requested path states into XDS API states */ + for (i = 0; i < num_states; i++) + path[i] = (uint8_t)xds_jtag_state[cmd->cmd.pathmove->path[i]]; + + if (xds110.firmware >= OCD_FIRMWARE_VERSION) { + /* Updated firmware fully supports pathmove */ + (void)ocd_pathmove(num_states, path); + } else { + /* Notify user that legacy firmware simply cannot handle pathmove */ + LOG_ERROR("XDS110: the firmware does not support pathmove command"); + LOG_ERROR(OCD_FIRMWARE_UPGRADE); + /* If pathmove is required, then debug is not possible */ + exit(-1); + } + + free((void *)path); + + return; +} + +static void xds110_queue_scan(struct jtag_command *cmd) +{ + int i; + uint32_t offset; + uint32_t total_fields; + uint32_t total_bits; + uint32_t total_bytes; + uint8_t end_state; + uint8_t *buffer; + + /* Calculate the total number of bits to scan */ + total_bits = 0; + total_fields = 0; + for (i = 0; i < cmd->cmd.scan->num_fields; i++) { + total_fields++; + total_bits += (uint32_t)cmd->cmd.scan->fields[i].num_bits; + } + + if (total_bits == 0) + return; + + total_bytes = DIV_ROUND_UP(total_bits, 8); + + /* Check if new request would be too large to fit */ + if (((xds110.txn_request_size + 1 + total_bytes + sizeof(end_state) + 1) + > MAX_DATA_BLOCK) || ((xds110.txn_result_count + total_fields) > + MAX_RESULT_QUEUE)) + xds110_flush(); + + /* Check if this single request is too large to fit */ + if ((1 + total_bytes + sizeof(end_state) + 1) > MAX_DATA_BLOCK) { + LOG_ERROR("BUG: JTAG scan request is too large to handle (%d bits)", + total_bits); + /* Failing to run this scan mucks up debug on this target */ + exit(-1); + } + + if (cmd->cmd.scan->ir_scan) + xds110.txn_requests[xds110.txn_request_size++] = CMD_IR_SCAN; + else + xds110.txn_requests[xds110.txn_request_size++] = CMD_DR_SCAN; + + end_state = (uint8_t)xds_jtag_state[cmd->cmd.scan->end_state]; + xds110.txn_requests[xds110.txn_request_size++] = end_state; + + xds110.txn_requests[xds110.txn_request_size++] = (total_bits >> 0) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (total_bits >> 8) & 0xff; + + /* Build request data by flattening fields into single buffer */ + /* also populate the results array to return the results when run */ + offset = 0; + buffer = &xds110.txn_requests[xds110.txn_request_size]; + /* Clear data out buffer to default value of all zeros */ + memset((void *)buffer, 0x00, total_bytes); + for (i = 0; i < cmd->cmd.scan->num_fields; i++) { + if (cmd->cmd.scan->fields[i].out_value != 0) { + /* Copy over data to scan out into request buffer */ + bit_copy(buffer, offset, cmd->cmd.scan->fields[i].out_value, 0, + cmd->cmd.scan->fields[i].num_bits); + } + offset += cmd->cmd.scan->fields[i].num_bits; + xds110.txn_scan_results[xds110.txn_result_count].first = (i == 0); + xds110.txn_scan_results[xds110.txn_result_count].num_bits = + cmd->cmd.scan->fields[i].num_bits; + xds110.txn_scan_results[xds110.txn_result_count++].buffer = + cmd->cmd.scan->fields[i].in_value; + } + xds110.txn_request_size += total_bytes; + xds110.txn_result_size += total_bytes; + + return; +} + +static void xds110_queue_runtest(struct jtag_command *cmd) +{ + uint32_t clocks = (uint32_t)cmd->cmd.stableclocks->num_cycles; + uint8_t end_state = (uint8_t)xds_jtag_state[cmd->cmd.runtest->end_state]; + + /* Check if new request would be too large to fit */ + if ((xds110.txn_request_size + 1 + sizeof(clocks) + sizeof(end_state) + 1) + > MAX_DATA_BLOCK) + xds110_flush(); + + /* Queue request and cycle count directly to queue buffer */ + xds110.txn_requests[xds110.txn_request_size++] = CMD_RUNTEST; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 0) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 8) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 16) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 24) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = end_state; + + return; +} + +static void xds110_queue_stableclocks(struct jtag_command *cmd) +{ + uint32_t clocks = (uint32_t)cmd->cmd.stableclocks->num_cycles; + + /* Check if new request would be too large to fit */ + if ((xds110.txn_request_size + 1 + sizeof(clocks) + 1) > MAX_DATA_BLOCK) + xds110_flush(); + + /* Queue request and cycle count directly to queue buffer */ + xds110.txn_requests[xds110.txn_request_size++] = CMD_STABLECLOCKS; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 0) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 8) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 16) & 0xff; + xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 24) & 0xff; + + return; +} + +static void xds110_execute_command(struct jtag_command *cmd) +{ + switch (cmd->type) { + case JTAG_RESET: + xds110_flush(); + xds110_execute_reset(cmd); + break; + case JTAG_SLEEP: + xds110_flush(); + xds110_execute_sleep(cmd); + break; + case JTAG_TLR_RESET: + xds110_flush(); + xds110_execute_tlr_reset(cmd); + break; + case JTAG_PATHMOVE: + xds110_flush(); + xds110_execute_pathmove(cmd); + break; + case JTAG_SCAN: + xds110_queue_scan(cmd); + break; + case JTAG_RUNTEST: + xds110_queue_runtest(cmd); + break; + case JTAG_STABLECLOCKS: + xds110_queue_stableclocks(cmd); + break; + case JTAG_TMS: + default: + LOG_ERROR("BUG: unknown JTAG command type 0x%x encountered", + cmd->type); + exit(-1); + } +} + +static int xds110_execute_queue(void) +{ + struct jtag_command *cmd = jtag_command_queue; + + while (cmd != NULL) { + xds110_execute_command(cmd); + cmd = cmd->next; + } + + xds110_flush(); + + return ERROR_OK; +} + +static int xds110_speed(int speed) +{ + bool success; + + if (speed == 0) { + LOG_INFO("XDS110: RTCK not supported"); + return ERROR_JTAG_NOT_IMPLEMENTED; + } + + if (speed > XDS110_MAX_TCK_SPEED) { + LOG_INFO("XDS110: reduce speed request: %dkHz to %dkHz maximum", + speed, XDS110_MAX_TCK_SPEED); + speed = XDS110_MAX_TCK_SPEED; + } + + if (speed < XDS110_MIN_TCK_SPEED) { + LOG_INFO("XDS110: increase speed request: %dkHz to %dkHz minimum", + speed, XDS110_MIN_TCK_SPEED); + speed = XDS110_MIN_TCK_SPEED; + } + + /* The default is the maximum frequency the XDS110 can support */ + uint32_t freq_to_use = XDS110_MAX_TCK_SPEED * 1000; /* Hz */ + uint32_t delay_count = 0; + + if (XDS110_MAX_TCK_SPEED != speed) { + freq_to_use = speed * 1000; /* Hz */ + + /* Calculate the delay count value */ + double one_giga = 1000000000; + /* Get the pulse duration for the maximum frequency supported in ns */ + double max_freq_pulse_duration = one_giga / + (XDS110_MAX_TCK_SPEED * 1000); + + /* Convert frequency to pulse duration */ + double freq_to_pulse_width_in_ns = one_giga / freq_to_use; + + /* + * Start with the pulse duration for the maximum frequency. Keep + * decrementing the time added by each count value till the requested + * frequency pulse is less than the calculated value. + */ + double current_value = max_freq_pulse_duration; + + while (current_value < freq_to_pulse_width_in_ns) { + current_value += XDS110_TCK_PULSE_INCREMENT; + ++delay_count; + } + + /* + * Determine which delay count yields the best match. + * The one obtained above or one less. + */ + if (delay_count) { + double diff_freq_1 = freq_to_use - + (one_giga / (max_freq_pulse_duration + + (XDS110_TCK_PULSE_INCREMENT * delay_count))); + double diff_freq_2 = (one_giga / (max_freq_pulse_duration + + (XDS110_TCK_PULSE_INCREMENT * (delay_count - 1)))) - + freq_to_use; + + /* One less count value yields a better match */ + if (diff_freq_1 > diff_freq_2) + --delay_count; + } + } + + /* Send the delay count to the XDS110 firmware */ + success = xds_set_tck_delay(delay_count); + + if (success) { + xds110.delay_count = delay_count; + xds110.speed = speed; + } + + return (success) ? ERROR_OK : ERROR_FAIL; +} + +static int xds110_speed_div(int speed, int *khz) +{ + *khz = speed; + return ERROR_OK; +} + +static int xds110_khz(int khz, int *jtag_speed) +{ + *jtag_speed = khz; + return ERROR_OK; +} + +static int_least32_t xds110_swd_frequency(int_least32_t hz) +{ + if (hz > 0) + xds110_speed(hz / 1000); + return hz; +} + +COMMAND_HANDLER(xds110_handle_info_command) +{ + xds110_show_info(); + return ERROR_OK; +} + +COMMAND_HANDLER(xds110_handle_serial_command) +{ + wchar_t serial[XDS110_SERIAL_LEN + 1]; + + xds110.serial[0] = 0; + + if (CMD_ARGC == 1) { + size_t len = mbstowcs(0, CMD_ARGV[0], 0); + if (len > XDS110_SERIAL_LEN) { + LOG_ERROR("XDS110: serial number is limited to %d characters", + XDS110_SERIAL_LEN); + return ERROR_FAIL; + } + if ((size_t)-1 == mbstowcs(serial, CMD_ARGV[0], len + 1)) { + LOG_ERROR("XDS110: unable to convert serial number"); + return ERROR_FAIL; + } + + for (uint32_t i = 0; i < len; i++) + xds110.serial[i] = (char)serial[i]; + + xds110.serial[len] = 0; + } else { + LOG_ERROR("XDS110: expected exactly one argument to xds110_serial " + "<serial-number>"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static const struct command_registration xds110_subcommand_handlers[] = { + { + .name = "info", + .handler = &xds110_handle_info_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "show XDS110 info", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration xds110_command_handlers[] = { + { + .name = "xds110", + .mode = COMMAND_ANY, + .help = "perform XDS110 management", + .usage = "<cmd>", + .chain = xds110_subcommand_handlers, + }, + { + .name = "xds110_serial", + .handler = &xds110_handle_serial_command, + .mode = COMMAND_CONFIG, + .help = "set the XDS110 probe serial number", + .usage = "serial_string", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct swd_driver xds110_swd_driver = { + .init = xds110_swd_init, + .frequency = xds110_swd_frequency, + .switch_seq = xds110_swd_switch_seq, + .read_reg = xds110_swd_read_reg, + .write_reg = xds110_swd_write_reg, + .run = xds110_swd_run_queue, +}; + +static const char * const xds110_transport[] = { "swd", "jtag", NULL }; + +struct jtag_interface xds110_interface = { + .name = "xds110", + .commands = xds110_command_handlers, + .swd = &xds110_swd_driver, + .transports = xds110_transport, + + .execute_queue = xds110_execute_queue, + .speed = xds110_speed, + .speed_div = xds110_speed_div, + .khz = xds110_khz, + .init = xds110_init, + .quit = xds110_quit, +}; diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index 62a8f59..2abed21 100644 --- a/src/jtag/hla/hla_interface.c +++ b/src/jtag/hla/hla_interface.c @@ -119,6 +119,11 @@ static int hl_interface_quit(void) if (hl_if.layout->api->close) hl_if.layout->api->close(hl_if.handle); + jtag_command_queue_reset(); + + free((void *)hl_if.param.device_desc); + free((void *)hl_if.param.serial); + return ERROR_OK; } @@ -186,7 +191,7 @@ int hl_interface_override_target(const char **targetname) return ERROR_FAIL; } -int hl_interface_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, +int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq) { if (hl_if.layout->api->config_trace) diff --git a/src/jtag/hla/hla_layout.h b/src/jtag/hla/hla_layout.h index 40c1321..9f41b59 100644 --- a/src/jtag/hla/hla_layout.h +++ b/src/jtag/hla/hla_layout.h @@ -91,7 +91,7 @@ struct hl_layout_api_s { * its maximum supported rate there * @returns ERROR_OK on success, an error code on failure. */ - int (*config_trace)(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol, + int (*config_trace)(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq); /** * Poll for new trace data diff --git a/src/jtag/hla/hla_tcl.c b/src/jtag/hla/hla_tcl.c index 9378427..73f78fc 100644 --- a/src/jtag/hla/hla_tcl.c +++ b/src/jtag/hla/hla_tcl.c @@ -39,20 +39,15 @@ static int jim_newtap_expected_id(Jim_Nvp *n, Jim_GetOptInfo *goi, return e; } - unsigned expected_len = sizeof(uint32_t) * pTap->expected_ids_cnt; - uint32_t *new_expected_ids = malloc(expected_len + sizeof(uint32_t)); - if (new_expected_ids == NULL) { + uint32_t *p = realloc(pTap->expected_ids, + (pTap->expected_ids_cnt + 1) * sizeof(uint32_t)); + if (!p) { Jim_SetResultFormatted(goi->interp, "no memory"); return JIM_ERR; } - memcpy(new_expected_ids, pTap->expected_ids, expected_len); - - new_expected_ids[pTap->expected_ids_cnt] = w; - - free(pTap->expected_ids); - pTap->expected_ids = new_expected_ids; - pTap->expected_ids_cnt++; + pTap->expected_ids = p; + pTap->expected_ids[pTap->expected_ids_cnt++] = w; return JIM_OK; } diff --git a/src/jtag/hla/hla_transport.c b/src/jtag/hla/hla_transport.c index 5a5671d..ddacea3 100644 --- a/src/jtag/hla/hla_transport.c +++ b/src/jtag/hla/hla_transport.c @@ -233,3 +233,11 @@ static void hl_constructor(void) transport_register(&hl_jtag_transport); transport_register(&stlink_swim_transport); } + +bool transport_is_hla(void) +{ + struct transport *t; + t = get_current_transport(); + return t == &hl_swd_transport || t == &hl_jtag_transport + || t == &stlink_swim_transport; +} diff --git a/src/jtag/interface.h b/src/jtag/interface.h index cdfc676..e6fa0ca 100644 --- a/src/jtag/interface.h +++ b/src/jtag/interface.h @@ -309,7 +309,7 @@ struct jtag_interface { * its maximum supported rate there * @returns ERROR_OK on success, an error code on failure. */ - int (*config_trace)(bool enabled, enum tpio_pin_protocol pin_protocol, + int (*config_trace)(bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq); /** @@ -328,7 +328,7 @@ extern const char * const jtag_only[]; void adapter_assert_reset(void); void adapter_deassert_reset(void); -int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, +int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, uint32_t port_size, unsigned int *trace_freq); int adapter_poll_trace(uint8_t *buf, size_t *size); diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 174c63a..286a73a 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -60,6 +60,9 @@ extern struct jtag_interface usb_blaster_interface; #if BUILD_JTAG_VPI == 1 extern struct jtag_interface jtag_vpi_interface; #endif +#if BUILD_FT232R == 1 +extern struct jtag_interface ft232r_interface; +#endif #if BUILD_AMTJTAGACCEL == 1 extern struct jtag_interface amt_jtagaccel_interface; #endif @@ -129,6 +132,9 @@ extern struct jtag_interface kitprog_interface; #if BUILD_IMX_GPIO == 1 extern struct jtag_interface imx_gpio_interface; #endif +#if BUILD_XDS110 == 1 +extern struct jtag_interface xds110_interface; +#endif #endif /* standard drivers */ /** @@ -159,6 +165,9 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_JTAG_VPI == 1 &jtag_vpi_interface, #endif +#if BUILD_FT232R == 1 + &ft232r_interface, +#endif #if BUILD_AMTJTAGACCEL == 1 &amt_jtagaccel_interface, #endif @@ -228,6 +237,9 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_IMX_GPIO == 1 &imx_gpio_interface, #endif +#if BUILD_XDS110 == 1 + &xds110_interface, +#endif #endif /* standard drivers */ NULL, }; diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h index 7702d6c..a6891c0 100644 --- a/src/jtag/jtag.h +++ b/src/jtag/jtag.h @@ -153,8 +153,6 @@ struct jtag_tap { struct jtag_tap_event_action *event_action; struct jtag_tap *next_tap; - /* dap instance if some null if no instance , initialized to 0 by calloc*/ - struct adiv5_dap *dap; /* private pointer to support none-jtag specific functions */ void *priv; }; @@ -642,8 +640,6 @@ void jtag_poll_set_enabled(bool value); * level APIs that are used in inner loops. */ #include <jtag/minidriver.h> -bool transport_is_jtag(void); - int jim_jtag_newtap(Jim_Interp *interp, int argc, Jim_Obj *const *argv); #endif /* OPENOCD_JTAG_JTAG_H */ diff --git a/src/jtag/swd.h b/src/jtag/swd.h index c888cc0..52f41d5 100644 --- a/src/jtag/swd.h +++ b/src/jtag/swd.h @@ -211,6 +211,4 @@ struct swd_driver { int swd_init_reset(struct command_context *cmd_ctx); void swd_add_reset(int req_srst); -bool transport_is_swd(void); - #endif /* OPENOCD_JTAG_SWD_H */ diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c index bc6bbf2..e32f0ca 100644 --- a/src/jtag/tcl.c +++ b/src/jtag/tcl.c @@ -42,6 +42,7 @@ #endif #include <helper/time_support.h> +#include "transport/transport.h" /** * @file diff --git a/src/jtag/zy1000/zy1000.c b/src/jtag/zy1000/zy1000.c index 67d9907..4e53dd1 100644 --- a/src/jtag/zy1000/zy1000.c +++ b/src/jtag/zy1000/zy1000.c @@ -265,8 +265,8 @@ COMMAND_HANDLER(handle_power_command) bool enable; COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable); setPower(enable); - /* fall through */ } + /* fall through */ case 0: LOG_INFO("Target power %s", savePower ? "on" : "off"); break; diff --git a/src/openocd.c b/src/openocd.c index 831bd17..f084dd4 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -37,6 +37,8 @@ #include <flash/nand/core.h> #include <pld/pld.h> #include <flash/mflash.h> +#include <target/arm_cti.h> +#include <target/arm_adi_v5.h> #include <server/server.h> #include <server/gdb_server.h> @@ -85,13 +87,13 @@ static int log_target_callback_event_handler(struct target *target, { switch (event) { case TARGET_EVENT_GDB_START: - target->display = 0; + target->verbose_halt_msg = false; break; case TARGET_EVENT_GDB_END: - target->display = 1; + target->verbose_halt_msg = true; break; case TARGET_EVENT_HALTED: - if (target->display) { + if (target->verbose_halt_msg) { /* do not display information when debugger caused the halt */ target_arch_state(target); } @@ -150,6 +152,10 @@ COMMAND_HANDLER(handle_init_command) if (ERROR_OK != retval) return ERROR_FAIL; + retval = command_run_line(CMD_CTX, "dap init"); + if (ERROR_OK != retval) + return ERROR_FAIL; + LOG_DEBUG("Examining targets..."); if (target_examine() != ERROR_OK) LOG_DEBUG("target examination failed"); @@ -252,6 +258,8 @@ struct command_context *setup_command_handler(Jim_Interp *interp) &nand_register_commands, &pld_register_commands, &mflash_register_commands, + &cti_register_commands, + &dap_register_commands, NULL }; for (unsigned i = 0; NULL != command_registrants[i]; i++) { @@ -286,10 +294,13 @@ static int openocd_thread(int argc, char *argv[], struct command_context *cmd_ct return ERROR_FAIL; ret = parse_config_file(cmd_ctx); - if (ret == ERROR_COMMAND_CLOSE_CONNECTION) + if (ret == ERROR_COMMAND_CLOSE_CONNECTION) { + server_quit(); /* gdb server may be initialized by -c init */ return ERROR_OK; - else if (ret != ERROR_OK) + } else if (ret != ERROR_OK) { + server_quit(); /* gdb server may be initialized by -c init */ return ERROR_FAIL; + } ret = server_init(cmd_ctx); if (ERROR_OK != ret) @@ -342,12 +353,22 @@ int openocd_main(int argc, char *argv[]) /* Start the executable meat that can evolve into thread in future. */ ret = openocd_thread(argc, argv, cmd_ctx); + flash_free_all_banks(); + gdb_service_free(); + server_free(); + unregister_all_commands(cmd_ctx, NULL); + /* free all DAP and CTI objects */ + dap_cleanup_all(); + arm_cti_cleanup_all(); + + adapter_quit(); + /* Shutdown commandline interface */ command_exit(cmd_ctx); - adapter_quit(); + free_config(); if (ERROR_FAIL == ret) return EXIT_FAILURE; diff --git a/src/rtos/ChibiOS.c b/src/rtos/ChibiOS.c index ef0bb16..312fc4d 100644 --- a/src/rtos/ChibiOS.c +++ b/src/rtos/ChibiOS.c @@ -69,10 +69,9 @@ struct ChibiOS_chdebug { /** * @brief ChibiOS thread states. */ -static const char * const ChibiOS_thread_states[] = { - "READY", "CURRENT", "SUSPENDED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING", - "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "WTQUEUE", - "FINAL" +static const char * const ChibiOS_thread_states[] = { "READY", "CURRENT", +"WTSTART", "SUSPENDED", "QUEUED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING", +"WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "FINAL" }; #define CHIBIOS_NUM_STATES (sizeof(ChibiOS_thread_states)/sizeof(char *)) @@ -247,7 +246,7 @@ static int ChibiOS_update_stacking(struct rtos *rtos) /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4 */ struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); if (is_armv7m(armv7m_target)) { - if (armv7m_target->fp_feature == FPv4_SP) { + if (armv7m_target->fp_feature != FP_NONE) { /* Found ARM v7m target which includes a FPU */ uint32_t cpacr; diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am index 22f7da5..6f14b42 100644 --- a/src/rtos/Makefile.am +++ b/src/rtos/Makefile.am @@ -16,6 +16,7 @@ noinst_LTLIBRARIES += %D%/librtos.la %D%/mqx.c \ %D%/riscv_debug.c \ %D%/uCOS-III.c \ + %D%/nuttx.c \ %D%/rtos.h \ %D%/rtos_standard_stackings.h \ %D%/rtos_ecos_stackings.h \ @@ -24,6 +25,7 @@ noinst_LTLIBRARIES += %D%/librtos.la %D%/rtos_embkernel_stackings.h \ %D%/rtos_mqx_stackings.h \ %D%/rtos_ucos_iii_stackings.h \ + %D%/nuttx_header.h \ %D%/riscv_debug.h %C%_librtos_la_CFLAGS = $(AM_CFLAGS) diff --git a/src/rtos/nuttx.c b/src/rtos/nuttx.c new file mode 100644 index 0000000..284b968 --- /dev/null +++ b/src/rtos/nuttx.c @@ -0,0 +1,405 @@ +/*************************************************************************** + * Copyright 2016,2017 Sony Video & Sound Products Inc. * + * Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com * + * Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <jtag/jtag.h> +#include "target/target.h" +#include "target/target_type.h" +#include "target/armv7m.h" +#include "target/cortex_m.h" +#include "rtos.h" +#include "helper/log.h" +#include "helper/types.h" +#include "server/gdb_server.h" + +#include "nuttx_header.h" + + +int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size); + +#ifdef CONFIG_DISABLE_SIGNALS +#define SIG_QUEUE_NUM 0 +#else +#define SIG_QUEUE_NUM 1 +#endif /* CONFIG_DISABLE_SIGNALS */ + +#ifdef CONFIG_DISABLE_MQUEUE +#define M_QUEUE_NUM 0 +#else +#define M_QUEUE_NUM 2 +#endif /* CONFIG_DISABLE_MQUEUE */ + +#ifdef CONFIG_PAGING +#define PAGING_QUEUE_NUM 1 +#else +#define PAGING_QUEUE_NUM 0 +#endif /* CONFIG_PAGING */ + + +#define TASK_QUEUE_NUM (6 + SIG_QUEUE_NUM + M_QUEUE_NUM + PAGING_QUEUE_NUM) + + +/* see nuttx/sched/os_start.c */ +static char *nuttx_symbol_list[] = { + "g_readytorun", /* 0: must be top of this array */ + "g_tasklisttable", + NULL +}; + +/* see nuttx/include/nuttx/sched.h */ +struct tcb { + uint32_t flink; + uint32_t blink; + uint8_t dat[512]; +}; + +struct { + uint32_t addr; + uint32_t prio; +} g_tasklist[TASK_QUEUE_NUM]; + +static char *task_state_str[] = { + "INVALID", + "PENDING", + "READYTORUN", + "RUNNING", + "INACTIVE", + "WAIT_SEM", +#ifndef CONFIG_DISABLE_SIGNALS + "WAIT_SIG", +#endif /* CONFIG_DISABLE_SIGNALS */ +#ifndef CONFIG_DISABLE_MQUEUE + "WAIT_MQNOTEMPTY", + "WAIT_MQNOTFULL", +#endif /* CONFIG_DISABLE_MQUEUE */ +#ifdef CONFIG_PAGING + "WAIT_PAGEFILL", +#endif /* CONFIG_PAGING */ +}; + +/* see arch/arm/include/armv7-m/irq_cmnvector.h */ +static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = { + { 0x28, 32 }, /* r0 */ + { 0x2c, 32 }, /* r1 */ + { 0x30, 32 }, /* r2 */ + { 0x34, 32 }, /* r3 */ + { 0x08, 32 }, /* r4 */ + { 0x0c, 32 }, /* r5 */ + { 0x10, 32 }, /* r6 */ + { 0x14, 32 }, /* r7 */ + { 0x18, 32 }, /* r8 */ + { 0x1c, 32 }, /* r9 */ + { 0x20, 32 }, /* r10 */ + { 0x24, 32 }, /* r11 */ + { 0x38, 32 }, /* r12 */ + { 0, 32 }, /* sp */ + { 0x3c, 32 }, /* lr */ + { 0x40, 32 }, /* pc */ + { 0x44, 32 }, /* xPSR */ +}; + + +static const struct rtos_register_stacking nuttx_stacking_cortex_m = { + 0x48, /* stack_registers_size */ + -1, /* stack_growth_direction */ + 17, /* num_output_registers */ + 0, /* stack_alignment */ + nuttx_stack_offsets_cortex_m /* register_offsets */ +}; + +static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = { + { 0x6c, 32 }, /* r0 */ + { 0x70, 32 }, /* r1 */ + { 0x74, 32 }, /* r2 */ + { 0x78, 32 }, /* r3 */ + { 0x08, 32 }, /* r4 */ + { 0x0c, 32 }, /* r5 */ + { 0x10, 32 }, /* r6 */ + { 0x14, 32 }, /* r7 */ + { 0x18, 32 }, /* r8 */ + { 0x1c, 32 }, /* r9 */ + { 0x20, 32 }, /* r10 */ + { 0x24, 32 }, /* r11 */ + { 0x7c, 32 }, /* r12 */ + { 0, 32 }, /* sp */ + { 0x80, 32 }, /* lr */ + { 0x84, 32 }, /* pc */ + { 0x88, 32 }, /* xPSR */ +}; + +static const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = { + 0x8c, /* stack_registers_size */ + -1, /* stack_growth_direction */ + 17, /* num_output_registers */ + 0, /* stack_alignment */ + nuttx_stack_offsets_cortex_m_fpu /* register_offsets */ +}; + +static int pid_offset = PID; +static int state_offset = STATE; +static int name_offset = NAME; +static int xcpreg_offset = XCPREG; +static int name_size = NAME_SIZE; + +static int rcmd_offset(const char *cmd, const char *name) +{ + if (strncmp(cmd, name, strlen(name))) + return -1; + + if (strlen(cmd) <= strlen(name) + 1) + return -1; + + return atoi(cmd + strlen(name)); +} + +static int nuttx_thread_packet(struct connection *connection, + char const *packet, int packet_size) +{ + char cmd[GDB_BUFFER_SIZE / 2] = ""; + + if (!strncmp(packet, "qRcmd", 5)) { + size_t len = unhexify((uint8_t *)cmd, packet + 6, sizeof(cmd)); + int offset; + + if (len <= 0) + goto pass; + + offset = rcmd_offset(cmd, "nuttx.pid_offset"); + + if (offset >= 0) { + LOG_INFO("pid_offset: %d", offset); + pid_offset = offset; + goto retok; + } + + offset = rcmd_offset(cmd, "nuttx.state_offset"); + + if (offset >= 0) { + LOG_INFO("state_offset: %d", offset); + state_offset = offset; + goto retok; + } + + offset = rcmd_offset(cmd, "nuttx.name_offset"); + + if (offset >= 0) { + LOG_INFO("name_offset: %d", offset); + name_offset = offset; + goto retok; + } + + offset = rcmd_offset(cmd, "nuttx.xcpreg_offset"); + + if (offset >= 0) { + LOG_INFO("xcpreg_offset: %d", offset); + xcpreg_offset = offset; + goto retok; + } + + offset = rcmd_offset(cmd, "nuttx.name_size"); + + if (offset >= 0) { + LOG_INFO("name_size: %d", offset); + name_size = offset; + goto retok; + } + } +pass: + return rtos_thread_packet(connection, packet, packet_size); +retok: + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; +} + + +static bool nuttx_detect_rtos(struct target *target) +{ + if ((target->rtos->symbols != NULL) && + (target->rtos->symbols[0].address != 0) && + (target->rtos->symbols[1].address != 0)) { + return true; + } + return false; +} + +static int nuttx_create(struct target *target) +{ + + target->rtos->gdb_thread_packet = nuttx_thread_packet; + LOG_INFO("target type name = %s", target->type->name); + return 0; +} + +static int nuttx_update_threads(struct rtos *rtos) +{ + uint32_t thread_count; + struct tcb tcb; + int ret; + uint32_t head; + uint32_t tcb_addr; + uint32_t i; + uint8_t state; + + if (rtos->symbols == NULL) { + LOG_ERROR("No symbols for NuttX"); + return -3; + } + + /* free previous thread details */ + rtos_free_threadlist(rtos); + + ret = target_read_buffer(rtos->target, rtos->symbols[1].address, + sizeof(g_tasklist), (uint8_t *)&g_tasklist); + if (ret) { + LOG_ERROR("target_read_buffer : ret = %d\n", ret); + return ERROR_FAIL; + } + + thread_count = 0; + + for (i = 0; i < TASK_QUEUE_NUM; i++) { + + if (g_tasklist[i].addr == 0) + continue; + + ret = target_read_u32(rtos->target, g_tasklist[i].addr, + &head); + + if (ret) { + LOG_ERROR("target_read_u32 : ret = %d\n", ret); + return ERROR_FAIL; + } + + /* readytorun head is current thread */ + if (g_tasklist[i].addr == rtos->symbols[0].address) + rtos->current_thread = head; + + + tcb_addr = head; + while (tcb_addr) { + struct thread_detail *thread; + ret = target_read_buffer(rtos->target, tcb_addr, + sizeof(tcb), (uint8_t *)&tcb); + if (ret) { + LOG_ERROR("target_read_buffer : ret = %d\n", + ret); + return ERROR_FAIL; + } + thread_count++; + + rtos->thread_details = realloc(rtos->thread_details, + sizeof(struct thread_detail) * thread_count); + thread = &rtos->thread_details[thread_count - 1]; + thread->threadid = tcb_addr; + thread->exists = true; + + state = tcb.dat[state_offset - 8]; + thread->extra_info_str = NULL; + if (state < sizeof(task_state_str)/sizeof(char *)) { + thread->extra_info_str = malloc(256); + snprintf(thread->extra_info_str, 256, "pid:%d, %s", + tcb.dat[pid_offset - 8] | + tcb.dat[pid_offset - 8 + 1] << 8, + task_state_str[state]); + } + + if (name_offset) { + thread->thread_name_str = malloc(name_size + 1); + snprintf(thread->thread_name_str, name_size, + "%s", (char *)&tcb.dat[name_offset - 8]); + } else { + thread->thread_name_str = malloc(sizeof("None")); + strcpy(thread->thread_name_str, "None"); + } + + tcb_addr = tcb.flink; + } + } + rtos->thread_count = thread_count; + + return 0; +} + + +/* + * thread_id = tcb address; + */ +static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + char **hex_reg_list) { + int retval; + + *hex_reg_list = NULL; + + /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ + bool cm4_fpu_enabled = false; + struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); + if (is_armv7m(armv7m_target)) { + if (armv7m_target->fp_feature == FPv4_SP) { + /* Found ARM v7m target which includes a FPU */ + uint32_t cpacr; + + retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read CPACR register to check FPU state"); + return -1; + } + + /* Check if CP10 and CP11 are set to full access. */ + if (cpacr & 0x00F00000) { + /* Found target with enabled FPU */ + cm4_fpu_enabled = 1; + } + } + } + + const struct rtos_register_stacking *stacking; + if (cm4_fpu_enabled) + stacking = &nuttx_stacking_cortex_m_fpu; + else + stacking = &nuttx_stacking_cortex_m; + + return rtos_generic_stack_read(rtos->target, stacking, + (uint32_t)thread_id + xcpreg_offset, hex_reg_list); +} + +static int nuttx_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) +{ + unsigned int i; + + *symbol_list = (symbol_table_elem_t *) calloc(1, + sizeof(symbol_table_elem_t) * ARRAY_SIZE(nuttx_symbol_list)); + + for (i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++) + (*symbol_list)[i].symbol_name = nuttx_symbol_list[i]; + + return 0; +} + +struct rtos_type nuttx_rtos = { + .name = "nuttx", + .detect_rtos = nuttx_detect_rtos, + .create = nuttx_create, + .update_threads = nuttx_update_threads, + .get_thread_reg_list = nuttx_get_thread_reg_list, + .get_symbol_list_to_lookup = nuttx_get_symbol_list_to_lookup, +}; + diff --git a/src/rtos/nuttx_header.h b/src/rtos/nuttx_header.h new file mode 100644 index 0000000..00b0484 --- /dev/null +++ b/src/rtos/nuttx_header.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright 2016,2017 Sony Video & Sound Products Inc. * + * Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com * + * Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_RTOS_NUTTX_HEADER_H +#define OPENOCD_RTOS_NUTTX_HEADER_H + +/* gdb script to update the header file + according to kernel version and build option + before executing function awareness + kernel symbol must be loaded : symbol nuttx + +define awareness + set logging off + set logging file nuttx_header.h + set logging on + + printf "#define PID %p\n",&((struct tcb_s *)(0))->pid + printf "#define XCPREG %p\n",&((struct tcb_s *)(0))->xcp.regs + printf "#define STATE %p\n",&((struct tcb_s *)(0))->task_state + printf "#define NAME %p\n",&((struct tcb_s *)(0))->name + printf "#define NAME_SIZE %d\n",sizeof(((struct tcb_s *)(0))->name) + end + + + OR ~/.gdbinit + + +define hookpost-file + + if &g_readytorun != 0 + eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid + eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs + eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state + eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name + eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name) + end + +end + +*/ + +/* default offset */ +#define PID 0xc +#define XCPREG 0x70 +#define STATE 0x19 +#define NAME 0xb8 +#define NAME_SIZE 32 + +/* defconfig of nuttx */ +/* #define CONFIG_DISABLE_SIGNALS */ +#define CONFIG_DISABLE_MQUEUE +/* #define CONFIG_PAGING */ + + +#endif /* OPENOCD_RTOS_NUTTX_HEADER_H */ diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c index 90caf40..6703008 100644 --- a/src/rtos/riscv_debug.c +++ b/src/rtos/riscv_debug.c @@ -22,11 +22,6 @@ static int riscv_create_rtos(struct target *target) struct riscv_rtos *r = calloc(1, sizeof(*r)); target->rtos->rtos_specific_params = r; -#if 0 - r->target_hartid = 0; - r->target_any_hart = true; - r->target_every_hart = true; -#endif target->rtos->current_threadid = 1; target->rtos->current_thread = 1; @@ -129,6 +124,13 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *pa return ERROR_OK; } + if (strcmp(packet, "qC") == 0) { + char rep_str[32]; + snprintf(rep_str, 32, "QC%" PRIx64, rtos->current_threadid); + gdb_put_packet(connection, rep_str, strlen(rep_str)); + return ERROR_OK; + } + return GDB_THREAD_PACKET_NOT_CONSUMED; case 'Q': @@ -266,12 +268,6 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char { LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1)); -#if 0 - LOG_ERROR(" Not actually updating"); - *hex_reg_list = 0; - return JIM_OK; -#endif - size_t n_regs = 32; size_t xlen = 64; size_t reg_chars = xlen / 8 * 2; diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index a37f44a..bd9da0d 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -35,6 +35,7 @@ extern struct rtos_type ChibiOS_rtos; extern struct rtos_type embKernel_rtos; extern struct rtos_type mqx_rtos; extern struct rtos_type uCOS_III_rtos; +extern struct rtos_type nuttx_rtos; extern struct rtos_type riscv_rtos; static struct rtos_type *rtos_types[] = { @@ -46,6 +47,7 @@ static struct rtos_type *rtos_types[] = { &embKernel_rtos, &mqx_rtos, &uCOS_III_rtos, + &nuttx_rtos, &riscv_rtos, NULL }; @@ -59,6 +61,15 @@ int rtos_smp_init(struct target *target) return ERROR_TARGET_INIT_FAILED; } +static int rtos_target_for_threadid(struct connection *connection, int64_t threadid, struct target **t) +{ + struct target *curr = get_target_from_connection(connection); + if (t) + *t = curr; + + return ERROR_OK; +} + static int os_alloc(struct target *target, struct rtos_type *ostype) { struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos)); @@ -75,6 +86,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype) /* RTOS drivers can override the packet handler in _create(). */ os->gdb_thread_packet = rtos_thread_packet; os->gdb_v_packet = NULL; + os->gdb_target_for_threadid = rtos_target_for_threadid; return JIM_OK; } @@ -339,8 +351,10 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa return ERROR_OK; } else if (strncmp(packet, "qSymbol", 7) == 0) { if (rtos_qsymbol(connection, packet, packet_size) == 1) { - target->rtos_auto_detect = false; - target->rtos->type->create(target); + if (target->rtos_auto_detect == true) { + target->rtos_auto_detect = false; + target->rtos->type->create(target); + } target->rtos->type->update_threads(target->rtos); } return ERROR_OK; diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h index 9da035a..c9ac1e5 100644 --- a/src/rtos/rtos.h +++ b/src/rtos/rtos.h @@ -55,6 +55,7 @@ struct rtos { int thread_count; int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size); int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size); + int (*gdb_target_for_threadid)(struct connection *connection, int64_t thread_id, struct target **p_target); void *rtos_specific_params; }; diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c index 0176c01..931cfc7 100644 --- a/src/rtos/rtos_standard_stackings.c +++ b/src/rtos/rtos_standard_stackings.c @@ -229,6 +229,25 @@ static int64_t rtos_standard_Cortex_M3_stack_align(struct target *target, stack_ptr, XPSR_OFFSET); } +static int64_t rtos_standard_Cortex_M4F_stack_align(struct target *target, + const uint8_t *stack_data, const struct rtos_register_stacking *stacking, + int64_t stack_ptr) +{ + const int XPSR_OFFSET = 0x40; + return rtos_Cortex_M_stack_align(target, stack_data, stacking, + stack_ptr, XPSR_OFFSET); +} + +static int64_t rtos_standard_Cortex_M4F_FPU_stack_align(struct target *target, + const uint8_t *stack_data, const struct rtos_register_stacking *stacking, + int64_t stack_ptr) +{ + const int XPSR_OFFSET = 0x80; + return rtos_Cortex_M_stack_align(target, stack_data, stacking, + stack_ptr, XPSR_OFFSET); +} + + const struct rtos_register_stacking rtos_standard_Cortex_M3_stacking = { 0x40, /* stack_registers_size */ -1, /* stack_growth_direction */ @@ -241,7 +260,7 @@ const struct rtos_register_stacking rtos_standard_Cortex_M4F_stacking = { 0x44, /* stack_registers_size 4 more for LR*/ -1, /* stack_growth_direction */ ARMV7M_NUM_CORE_REGS, /* num_output_registers */ - rtos_standard_Cortex_M3_stack_align, /* stack_alignment */ + rtos_standard_Cortex_M4F_stack_align, /* stack_alignment */ rtos_standard_Cortex_M4F_stack_offsets /* register_offsets */ }; @@ -249,7 +268,7 @@ const struct rtos_register_stacking rtos_standard_Cortex_M4F_FPU_stacking = { 0xcc, /* stack_registers_size 4 more for LR + 48 more for FPU S0-S15 register*/ -1, /* stack_growth_direction */ ARMV7M_NUM_CORE_REGS, /* num_output_registers */ - rtos_standard_Cortex_M3_stack_align, /* stack_alignment */ + rtos_standard_Cortex_M4F_FPU_stack_align, /* stack_alignment */ rtos_standard_Cortex_M4F_FPU_stack_offsets /* register_offsets */ }; diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 836e783..58df51a 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -41,6 +41,8 @@ #include <target/breakpoints.h> #include <target/target_request.h> #include <target/register.h> +#include <target/target.h> +#include <target/target_type.h> #include "server.h" #include <flash/nor/core.h> #include "gdb_server.h" @@ -110,6 +112,8 @@ static char *gdb_port_next; static void gdb_log_callback(void *priv, const char *file, unsigned line, const char *function, const char *string); +static void gdb_sig_halted(struct connection *connection); + /* number of gdb connections, mainly to suppress gdb related debugging spam * in helper/log.c when no gdb connections are actually active */ int gdb_actual_connections; @@ -126,6 +130,9 @@ static int gdb_flash_program = 1; * Disabled by default. */ static int gdb_report_data_abort; +/* If set, errors when accessing registers are reported to gdb. Disabled by + * default. */ +static int gdb_report_register_access_error; /* set if we are sending target descriptions to gdb * via qXfer:features:read packet */ @@ -715,7 +722,7 @@ static int gdb_output(struct command_context *context, const char *line) static void gdb_signal_reply(struct target *target, struct connection *connection) { struct gdb_connection *gdb_connection = connection->priv; - char sig_reply[45]; + char sig_reply[65]; char stop_reason[20]; char current_thread[25]; int sig_reply_len; @@ -728,7 +735,6 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio } else { if (gdb_connection->ctrl_c) { signal_var = 0x2; - gdb_connection->ctrl_c = 0; } else signal_var = gdb_last_signal(target); @@ -760,12 +766,19 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio current_thread[0] = '\0'; if (target->rtos != NULL) { - snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread); + struct target *ct; + snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";", + target->rtos->current_thread); target->rtos->current_threadid = target->rtos->current_thread; + target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct); + if (!gdb_connection->ctrl_c) + signal_var = gdb_last_signal(ct); } sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s", signal_var, stop_reason, current_thread); + + gdb_connection->ctrl_c = 0; } gdb_put_packet(connection, sig_reply, sig_reply_len); @@ -780,64 +793,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio bool program_exited = false; if (strcmp(target->fileio_info->identifier, "open") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2, target->fileio_info->param_3, target->fileio_info->param_4); else if (strcmp(target->fileio_info->identifier, "close") == 0) - sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1); else if (strcmp(target->fileio_info->identifier, "read") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2, target->fileio_info->param_3); else if (strcmp(target->fileio_info->identifier, "write") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2, target->fileio_info->param_3); else if (strcmp(target->fileio_info->identifier, "lseek") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2, target->fileio_info->param_3); else if (strcmp(target->fileio_info->identifier, "rename") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2, target->fileio_info->param_3, target->fileio_info->param_4); else if (strcmp(target->fileio_info->identifier, "unlink") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2); else if (strcmp(target->fileio_info->identifier, "stat") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2, target->fileio_info->param_3); else if (strcmp(target->fileio_info->identifier, "fstat") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2); else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2); else if (strcmp(target->fileio_info->identifier, "isatty") == 0) - sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1); else if (strcmp(target->fileio_info->identifier, "system") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, target->fileio_info->param_2); else if (strcmp(target->fileio_info->identifier, "exit") == 0) { /* If target hits exit syscall, report to GDB the program is terminated. * In addition, let target run its own exit syscall handler. */ program_exited = true; - sprintf(fileio_command, "W%02" PRIx32, target->fileio_info->param_1); + sprintf(fileio_command, "W%02" PRIx64, target->fileio_info->param_1); } else { LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier); @@ -923,6 +936,7 @@ static int gdb_new_connection(struct connection *connection) target = get_target_from_connection(connection); connection->priv = gdb_connection; + connection->cmd_ctx->current_target = target; /* initialize gdb connection information */ gdb_connection->buf_p = gdb_connection->buffer; @@ -953,9 +967,14 @@ static int gdb_new_connection(struct connection *connection) breakpoint_clear_target(target); watchpoint_clear_target(target); - /* clean previous rtos session if supported*/ - if ((target->rtos) && (target->rtos->type->clean)) - target->rtos->type->clean(target); + if (target->rtos) { + /* clean previous rtos session if supported*/ + if (target->rtos->type->clean) + target->rtos->type->clean(target); + + /* update threads */ + rtos_update_threads(target); + } /* remove the initial ACK from the incoming buffer */ retval = gdb_get_char(connection, &initial_ack); @@ -1174,7 +1193,7 @@ static int gdb_get_registers_packet(struct connection *connection, for (i = 0; i < reg_list_size; i++) { if (!reg_list[i]->valid) { retval = reg_list[i]->type->get(reg_list[i]); - if (retval != ERROR_OK && target->propagate_register_errors) { + if (retval != ERROR_OK && gdb_report_register_access_error) { LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name); free(reg_packet); free(reg_list); @@ -1242,7 +1261,7 @@ static int gdb_set_registers_packet(struct connection *connection, gdb_target_to_reg(target, packet_p, chars, bin_buf); retval = reg_list[i]->type->set(reg_list[i], bin_buf); - if (retval != ERROR_OK && target->propagate_register_errors) { + if (retval != ERROR_OK && gdb_report_register_access_error) { LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name); free(reg_list); free(bin_buf); @@ -1289,7 +1308,7 @@ static int gdb_get_register_packet(struct connection *connection, if (!reg_list[reg_num]->valid) { retval = reg_list[reg_num]->type->get(reg_list[reg_num]); - if (retval != ERROR_OK && target->propagate_register_errors) { + if (retval != ERROR_OK && gdb_report_register_access_error) { LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name); free(reg_list); return gdb_error(connection, retval); @@ -1341,7 +1360,8 @@ static int gdb_set_register_packet(struct connection *connection, int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2); if ((unsigned int)chars != strlen(separator + 1)) { - LOG_ERROR("gdb sent a packet with wrong register size"); + LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)", + strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name); free(bin_buf); return ERROR_SERVER_REMOTE_CLOSED; } @@ -1349,7 +1369,7 @@ static int gdb_set_register_packet(struct connection *connection, gdb_target_to_reg(target, separator + 1, chars, bin_buf); retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf); - if (retval != ERROR_OK && target->propagate_register_errors) { + if (retval != ERROR_OK && gdb_report_register_access_error) { LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name); free(bin_buf); free(reg_list); @@ -1790,7 +1810,7 @@ static int gdb_memory_map(struct connection *connection, int offset; int length; char *separator; - uint32_t ram_start = 0; + target_addr_t ram_start = 0; int i; int target_flash_banks = 0; @@ -1805,9 +1825,6 @@ static int gdb_memory_map(struct connection *connection, /* Sort banks in ascending order. We need to report non-flash * memory as ram (or rather read/write) by default for GDB, since * it has no concept of non-cacheable read/write memory (i/o etc). - * - * FIXME Most non-flash addresses are *NOT* RAM! Don't lie. - * Current versions of GDB assume unlisted addresses are RAM... */ banks = malloc(sizeof(struct flash_bank *)*flash_get_bank_count()); @@ -1830,14 +1847,13 @@ static int gdb_memory_map(struct connection *connection, for (i = 0; i < target_flash_banks; i++) { int j; unsigned sector_size = 0; - uint32_t start; + unsigned group_len = 0; p = banks[i]; - start = p->base; if (ram_start < p->base) xml_printf(&retval, &xml, &pos, &size, - "<memory type=\"ram\" start=\"0x%x\" " + "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" " "length=\"0x%x\"/>\n", ram_start, p->base - ram_start); @@ -1848,27 +1864,35 @@ static int gdb_memory_map(struct connection *connection, * regions with 8KB, 32KB, and 64KB sectors; etc. */ for (j = 0; j < p->num_sectors; j++) { - unsigned group_len; /* Maybe start a new group of sectors. */ if (sector_size == 0) { + if (p->sectors[j].offset + p->sectors[j].size > p->size) { + LOG_WARNING("The flash sector at offset 0x%08" PRIx32 + " overflows the end of %s bank.", + p->sectors[j].offset, p->name); + LOG_WARNING("The rest of bank will not show in gdb memory map."); + break; + } + target_addr_t start; start = p->base + p->sectors[j].offset; xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" " - "start=\"0x%x\" ", + "start=\"" TARGET_ADDR_FMT "\" ", start); sector_size = p->sectors[j].size; + group_len = sector_size; + } else { + group_len += sector_size; /* equal to p->sectors[j].size */ } /* Does this finish a group of sectors? * If not, continue an already-started group. */ - if (j == p->num_sectors - 1) - group_len = (p->base + p->size) - start; - else if (p->sectors[j + 1].size != sector_size) - group_len = p->base + p->sectors[j + 1].offset - - start; - else + if (j < p->num_sectors - 1 + && p->sectors[j + 1].size == sector_size + && p->sectors[j + 1].offset == p->sectors[j].offset + sector_size + && p->sectors[j + 1].offset + p->sectors[j + 1].size <= p->size) continue; xml_printf(&retval, &xml, &pos, &size, @@ -1886,7 +1910,7 @@ static int gdb_memory_map(struct connection *connection, if (ram_start != 0) xml_printf(&retval, &xml, &pos, &size, - "<memory type=\"ram\" start=\"0x%x\" " + "<memory type=\"ram\" start=\"" TARGET_ADDR_FMT "\" " "length=\"0x%x\"/>\n", ram_start, 0-ram_start); /* ELSE a flash chip could be at the very end of the 32 bit address @@ -1894,11 +1918,11 @@ static int gdb_memory_map(struct connection *connection, */ free(banks); - banks = NULL; xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n"); if (retval != ERROR_OK) { + free(xml); gdb_error(connection, retval); return retval; } @@ -1919,6 +1943,8 @@ static int gdb_memory_map(struct connection *connection, static const char *gdb_get_reg_type_name(enum reg_type type) { switch (type) { + case REG_TYPE_BOOL: + return "bool"; case REG_TYPE_INT: return "int"; case REG_TYPE_INT8: @@ -1931,6 +1957,8 @@ static const char *gdb_get_reg_type_name(enum reg_type type) return "int64"; case REG_TYPE_INT128: return "int128"; + case REG_TYPE_UINT: + return "uint"; case REG_TYPE_UINT8: return "uint8"; case REG_TYPE_UINT16: @@ -1958,12 +1986,45 @@ static const char *gdb_get_reg_type_name(enum reg_type type) return "int"; /* "int" as default value */ } +static int lookup_add_arch_defined_types(char const **arch_defined_types_list[], const char *type_id, + int *num_arch_defined_types) +{ + int tbl_sz = *num_arch_defined_types; + + if (type_id != NULL && (strcmp(type_id, ""))) { + for (int j = 0; j < (tbl_sz + 1); j++) { + if (!((*arch_defined_types_list)[j])) { + (*arch_defined_types_list)[tbl_sz++] = type_id; + *arch_defined_types_list = realloc(*arch_defined_types_list, + sizeof(char *) * (tbl_sz + 1)); + (*arch_defined_types_list)[tbl_sz] = NULL; + *num_arch_defined_types = tbl_sz; + return 1; + } else { + if (!strcmp((*arch_defined_types_list)[j], type_id)) + return 0; + } + } + } + + return -1; +} + static int gdb_generate_reg_type_description(struct target *target, - char **tdesc, int *pos, int *size, struct reg_data_type *type) + char **tdesc, int *pos, int *size, struct reg_data_type *type, + char const **arch_defined_types_list[], int * num_arch_defined_types) { int retval = ERROR_OK; if (type->type_class == REG_TYPE_CLASS_VECTOR) { + struct reg_data_type *data_type = type->reg_type_vector->type; + if (data_type->type == REG_TYPE_ARCH_DEFINED) { + if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id, + num_arch_defined_types)) + gdb_generate_reg_type_description(target, tdesc, pos, size, data_type, + arch_defined_types_list, + num_arch_defined_types); + } /* <vector id="id" type="type" count="count"/> */ xml_printf(&retval, tdesc, pos, size, "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n", @@ -1971,6 +2032,20 @@ static int gdb_generate_reg_type_description(struct target *target, type->reg_type_vector->count); } else if (type->type_class == REG_TYPE_CLASS_UNION) { + struct reg_data_type_union_field *field; + field = type->reg_type_union->fields; + while (field != NULL) { + struct reg_data_type *data_type = field->type; + if (data_type->type == REG_TYPE_ARCH_DEFINED) { + if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id, + num_arch_defined_types)) + gdb_generate_reg_type_description(target, tdesc, pos, size, data_type, + arch_defined_types_list, + num_arch_defined_types); + } + + field = field->next; + } /* <union id="id"> * <field name="name" type="type"/> ... * </union> */ @@ -1978,7 +2053,6 @@ static int gdb_generate_reg_type_description(struct target *target, "<union id=\"%s\">\n", type->id); - struct reg_data_type_union_field *field; field = type->reg_type_union->fields; while (field != NULL) { xml_printf(&retval, tdesc, pos, size, @@ -2004,13 +2078,24 @@ static int gdb_generate_reg_type_description(struct target *target, type->id, type->reg_type_struct->size); while (field != NULL) { xml_printf(&retval, tdesc, pos, size, - "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n", - field->name, field->bitfield->start, - field->bitfield->end); + "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n", + field->name, field->bitfield->start, field->bitfield->end, + gdb_get_reg_type_name(field->bitfield->type)); field = field->next; } } else { + while (field != NULL) { + struct reg_data_type *data_type = field->type; + if (data_type->type == REG_TYPE_ARCH_DEFINED) { + if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id, + num_arch_defined_types)) + gdb_generate_reg_type_description(target, tdesc, pos, size, data_type, + arch_defined_types_list, + num_arch_defined_types); + } + } + /* <struct id="id"> * <field name="name" type="type"/> ... * </struct> */ @@ -2041,8 +2126,9 @@ static int gdb_generate_reg_type_description(struct target *target, field = type->reg_type_flags->fields; while (field != NULL) { xml_printf(&retval, tdesc, pos, size, - "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n", - field->name, field->bitfield->start, field->bitfield->end); + "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n", + field->name, field->bitfield->start, field->bitfield->end, + gdb_get_reg_type_name(field->bitfield->type)); field = field->next; } @@ -2103,11 +2189,15 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o struct reg **reg_list = NULL; int reg_list_size; char const **features = NULL; + char const **arch_defined_types = NULL; int feature_list_size = 0; + int num_arch_defined_types = 0; char *tdesc = NULL; int pos = 0; int size = 0; + arch_defined_types = calloc(1, sizeof(char *)); + retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, REG_CLASS_ALL); @@ -2160,8 +2250,13 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o if (reg_list[i]->reg_data_type != NULL) { if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) { /* generate <type... first, if there are architecture-defined types. */ - gdb_generate_reg_type_description(target, &tdesc, &pos, &size, - reg_list[i]->reg_data_type); + if (lookup_add_arch_defined_types(&arch_defined_types, + reg_list[i]->reg_data_type->id, + &num_arch_defined_types)) + gdb_generate_reg_type_description(target, &tdesc, &pos, &size, + reg_list[i]->reg_data_type, + &arch_defined_types, + &num_arch_defined_types); type_str = reg_list[i]->reg_data_type->id; } else { @@ -2211,6 +2306,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o error: free(features); free(reg_list); + free(arch_defined_types); if (retval == ERROR_OK) *tdesc_out = tdesc; @@ -2387,7 +2483,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list, else transfer_type = 'l'; - *chunk = malloc(length + 2); + *chunk = malloc(length + 2 + 3); + /* Allocating extra 3 bytes prevents false positive valgrind report + * of strlen(chunk) word access: + * Invalid read of size 4 + * Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */ if (*chunk == NULL) { LOG_ERROR("Unable to allocate memory"); return ERROR_FAIL; @@ -2495,7 +2595,7 @@ static int gdb_query_packet(struct connection *connection, &buffer, &pos, &size, - "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+", + "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+", (GDB_BUFFER_SIZE - 1), ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-', (gdb_target_desc_supported == 1) ? '+' : '-'); @@ -2584,6 +2684,187 @@ static int gdb_query_packet(struct connection *connection, return ERROR_OK; } +static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size) +{ + struct gdb_connection *gdb_connection = connection->priv; + struct target *target = get_target_from_connection(connection); + const char *parse = packet; + int retval; + + /* query for vCont supported */ + if (parse[0] == '?') { + if (target->type->step != NULL) { + /* gdb doesn't accept c without C and s without S */ + gdb_put_packet(connection, "vCont;c;C;s;S", 13); + return true; + } + return false; + } + + if (parse[0] == ';') { + ++parse; + --packet_size; + } + + /* simple case, a continue packet */ + if (parse[0] == 'c') { + gdb_running_type = 'c'; + LOG_DEBUG("target %s continue", target_name(target)); + log_add_callback(gdb_log_callback, connection); + retval = target_resume(target, 1, 0, 0, 0); + if (retval == ERROR_TARGET_NOT_HALTED) + LOG_INFO("target %s was not halted when resume was requested", target_name(target)); + + /* poll target in an attempt to make its internal state consistent */ + if (retval != ERROR_OK) { + retval = target_poll(target); + if (retval != ERROR_OK) + LOG_DEBUG("error polling target %s after failed resume", target_name(target)); + } + + /* + * We don't report errors to gdb here, move frontend_state to + * TARGET_RUNNING to stay in sync with gdb's expectation of the + * target state + */ + gdb_connection->frontend_state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_GDB_START); + + return true; + } + + /* single-step or step-over-breakpoint */ + if (parse[0] == 's') { + gdb_running_type = 's'; + bool fake_step = false; + + if (strncmp(parse, "s:", 2) == 0) { + struct target *ct = target; + int current_pc = 1; + int64_t thread_id; + char *endp; + + parse += 2; + packet_size -= 2; + + thread_id = strtoll(parse, &endp, 16); + if (endp != NULL) { + packet_size -= endp - parse; + parse = endp; + } + + if (target->rtos != NULL) { + /* FIXME: why is this necessary? rtos state should be up-to-date here already! */ + rtos_update_threads(target); + + target->rtos->gdb_target_for_threadid(connection, thread_id, &ct); + + /* + * check if the thread to be stepped is the current rtos thread + * if not, we must fake the step + */ + if (target->rtos->current_thread != thread_id) + fake_step = true; + } + + if (parse[0] == ';') { + ++parse; + --packet_size; + + if (parse[0] == 'c') { + parse += 1; + packet_size -= 1; + + /* check if thread-id follows */ + if (parse[0] == ':') { + int64_t tid; + parse += 1; + packet_size -= 1; + + tid = strtoll(parse, &endp, 16); + if (tid == thread_id) { + /* + * Special case: only step a single thread (core), + * keep the other threads halted. Currently, only + * aarch64 target understands it. Other target types don't + * care (nobody checks the actual value of 'current') + * and it doesn't really matter. This deserves + * a symbolic constant and a formal interface documentation + * at a later time. + */ + LOG_DEBUG("request to step current core only"); + /* uncomment after checking that indeed other targets are safe */ + /*current_pc = 2;*/ + } + } + } + } + + LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id); + log_add_callback(gdb_log_callback, connection); + target_call_event_callbacks(ct, TARGET_EVENT_GDB_START); + + /* + * work around an annoying gdb behaviour: when the current thread + * is changed in gdb, it assumes that the target can follow and also + * make the thread current. This is an assumption that cannot hold + * for a real target running a multi-threading OS. We just fake + * the step to not trigger an internal error in gdb. See + * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details + */ + if (fake_step) { + int sig_reply_len; + char sig_reply[128]; + + LOG_DEBUG("fake step thread %"PRIx64, thread_id); + + sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), + "T05thread:%016"PRIx64";", thread_id); + + gdb_put_packet(connection, sig_reply, sig_reply_len); + log_remove_callback(gdb_log_callback, connection); + + return true; + } + + /* support for gdb_sync command */ + if (gdb_connection->sync) { + gdb_connection->sync = false; + if (ct->state == TARGET_HALTED) { + LOG_WARNING("stepi ignored. GDB will now fetch the register state " \ + "from the target."); + gdb_sig_halted(connection); + log_remove_callback(gdb_log_callback, connection); + } else + gdb_connection->frontend_state = TARGET_RUNNING; + return true; + } + + retval = target_step(ct, current_pc, 0, 0); + if (retval == ERROR_TARGET_NOT_HALTED) + LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + + /* if step was successful send a reply back to gdb */ + if (retval == ERROR_OK) { + retval = target_poll(ct); + if (retval != ERROR_OK) + LOG_DEBUG("error polling target %s after successful step", target_name(ct)); + /* send back signal information */ + gdb_signal_reply(ct, connection); + /* stop forwarding log packets! */ + log_remove_callback(gdb_log_callback, connection); + } else + gdb_connection->frontend_state = TARGET_RUNNING; + } else { + LOG_ERROR("Unknown vCont packet"); + return false; + } + return true; + } + + return false; +} + static int gdb_v_packet(struct connection *connection, char const *packet, int packet_size) { @@ -2597,6 +2878,19 @@ static int gdb_v_packet(struct connection *connection, return out; } + if (strncmp(packet, "vCont", 5) == 0) { + bool handled; + + packet += 5; + packet_size -= 5; + + handled = gdb_handle_vcont_packet(connection, packet, packet_size); + if (!handled) + gdb_put_packet(connection, "", 0); + + return ERROR_OK; + } + /* if flash programming disabled - send a empty reply */ if (gdb_flash_program == 0) { @@ -2728,9 +3022,12 @@ static int gdb_v_packet(struct connection *connection, static int gdb_detach(struct connection *connection) { - target_call_event_callbacks(get_target_from_connection(connection), - TARGET_EVENT_GDB_DETACH); - + /* + * Only reply "OK" to GDB + * it will close the connection and this will trigger a call to + * gdb_connection_closed() that will in turn trigger the event + * TARGET_EVENT_GDB_DETACH + */ return gdb_put_packet(connection, "OK", 2); } @@ -2796,7 +3093,7 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line, gdb_output_con(connection, string); } -void gdb_sig_halted(struct connection *connection) +static void gdb_sig_halted(struct connection *connection) { char sig_reply[4]; snprintf(sig_reply, 4, "T%2.2x", 2); @@ -3036,7 +3333,12 @@ static int gdb_input_inner(struct connection *connection) if (gdb_con->ctrl_c) { if (target->state == TARGET_RUNNING) { - retval = target_halt(target); + struct target *t = target; + if (target->rtos) + target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t); + retval = target_halt(t); + if (retval == ERROR_OK) + retval = target_poll(t); if (retval != ERROR_OK) target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT); gdb_con->ctrl_c = 0; @@ -3209,6 +3511,15 @@ COMMAND_HANDLER(handle_gdb_report_data_abort_command) return ERROR_OK; } +COMMAND_HANDLER(handle_gdb_report_register_access_error) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_ENABLE(CMD_ARGV[0], gdb_report_register_access_error); + return ERROR_OK; +} + /* gdb_breakpoint_override */ COMMAND_HANDLER(handle_gdb_breakpoint_override_command) { @@ -3331,6 +3642,13 @@ static const struct command_registration gdb_command_handlers[] = { .usage = "('enable'|'disable')" }, { + .name = "gdb_report_register_access_error", + .handler = handle_gdb_report_register_access_error, + .mode = COMMAND_CONFIG, + .help = "enable or disable reporting register access errors", + .usage = "('enable'|'disable')" + }, + { .name = "gdb_breakpoint_override", .handler = handle_gdb_breakpoint_override_command, .mode = COMMAND_ANY, @@ -3366,3 +3684,9 @@ void gdb_set_frontend_state_running(struct connection *connection) struct gdb_connection *gdb_con = connection->priv; gdb_con->frontend_state = TARGET_RUNNING; } + +void gdb_service_free(void) +{ + free(gdb_port); + free(gdb_port_next); +} diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h index 1dc30e0..0c50836 100644 --- a/src/server/gdb_server.h +++ b/src/server/gdb_server.h @@ -36,6 +36,7 @@ struct reg; int gdb_target_add_all(struct target *target); int gdb_register_commands(struct command_context *command_context); +void gdb_service_free(void); int gdb_put_packet(struct connection *connection, char *buffer, int len); @@ -46,7 +47,6 @@ static inline struct target *get_target_from_connection(struct connection *conne } void gdb_set_frontend_state_running(struct connection *connection); -void gdb_sig_halted(struct connection *connection); #define ERROR_GDB_BUFFER_TOO_SMALL (-800) #define ERROR_GDB_TIMEOUT (-801) diff --git a/src/server/server.c b/src/server/server.c index 46c860f..f8273d4 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -46,9 +46,13 @@ static struct service *services; -/* shutdown_openocd == 1: exit the main event loop, and quit the - * debugger; 2: quit with non-zero return code */ -static int shutdown_openocd; +enum shutdown_reason { + CONTINUE_MAIN_LOOP, /* stay in main event loop */ + SHUTDOWN_REQUESTED, /* set by shutdown command; exit the event loop and quit the debugger */ + SHUTDOWN_WITH_ERROR_CODE, /* set by shutdown command; quit with non-zero return code */ + SHUTDOWN_WITH_SIGNAL_CODE /* set by sig_handler; exec shutdown then exit with signal as return code */ +}; +static enum shutdown_reason shutdown_openocd = CONTINUE_MAIN_LOOP; /* store received signal to exit application by killing ourselves */ static int last_signal; @@ -259,7 +263,7 @@ int add_service(char *name, c->sin.sin_family = AF_INET; if (bindto_name == NULL) - c->sin.sin_addr.s_addr = INADDR_ANY; + c->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); else { hp = gethostbyname(bindto_name); if (hp == NULL) { @@ -345,6 +349,50 @@ int add_service(char *name, return ERROR_OK; } +static void remove_connections(struct service *service) +{ + struct connection *connection; + + connection = service->connections; + + while (connection) { + struct connection *tmp; + + tmp = connection->next; + remove_connection(service, connection); + connection = tmp; + } +} + +int remove_service(const char *name, const char *port) +{ + struct service *tmp; + struct service *prev; + + prev = services; + + for (tmp = services; tmp; prev = tmp, tmp = tmp->next) { + if (!strcmp(tmp->name, name) && !strcmp(tmp->port, port)) { + remove_connections(tmp); + + if (tmp == services) + services = tmp->next; + else + prev->next = tmp->next; + + if (tmp->type != CONNECTION_STDINOUT) + close_socket(tmp->fd); + + free(tmp->priv); + free_service(tmp); + + return ERROR_OK; + } + } + + return ERROR_OK; +} + static int remove_services(void) { struct service *c = services; @@ -353,6 +401,8 @@ static int remove_services(void) while (c) { struct service *next = c->next; + remove_connections(c); + if (c->name) free(c->name); @@ -396,7 +446,7 @@ int server_loop(struct command_context *command_context) LOG_ERROR("couldn't set SIGPIPE to SIG_IGN"); #endif - while (!shutdown_openocd) { + while (shutdown_openocd == CONTINUE_MAIN_LOOP) { /* monitor sockets for activity */ fd_max = 0; FD_ZERO(&read_fds); @@ -488,7 +538,7 @@ int server_loop(struct command_context *command_context) for (service = services; service; service = service->next) { /* handle new connections on listeners */ if ((service->fd != -1) - && (FD_ISSET(service->fd, &read_fds))) { + && (FD_ISSET(service->fd, &read_fds))) { if (service->max_connections != 0) add_connection(service, command_context); else { @@ -520,7 +570,7 @@ int server_loop(struct command_context *command_context) service->type == CONNECTION_STDINOUT) { /* if connection uses a pipe then * shutdown openocd on error */ - shutdown_openocd = 1; + shutdown_openocd = SHUTDOWN_REQUESTED; } remove_connection(service, c); LOG_INFO("dropped '%s' connection", @@ -538,29 +588,48 @@ int server_loop(struct command_context *command_context) MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) - shutdown_openocd = 1; + shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE; } #endif } - return shutdown_openocd != 2 ? ERROR_OK : ERROR_FAIL; + /* when quit for signal or CTRL-C, run (eventually user implemented) "shutdown" */ + if (shutdown_openocd == SHUTDOWN_WITH_SIGNAL_CODE) + command_run_line(command_context, "shutdown"); + + return shutdown_openocd == SHUTDOWN_WITH_ERROR_CODE ? ERROR_FAIL : ERROR_OK; } +void sig_handler(int sig) +{ + /* store only first signal that hits us */ + if (shutdown_openocd == CONTINUE_MAIN_LOOP) { + shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE; + last_signal = sig; + LOG_DEBUG("Terminating on Signal %d", sig); + } else + LOG_DEBUG("Ignored extra Signal %d", sig); +} + + #ifdef _WIN32 BOOL WINAPI ControlHandler(DWORD dwCtrlType) { - shutdown_openocd = 1; + shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE; return TRUE; } -#endif - -void sig_handler(int sig) +#else +static void sigkey_handler(int sig) { - /* store only first signal that hits us */ - if (!last_signal) - last_signal = sig; - shutdown_openocd = 1; + /* ignore keystroke generated signals if not in foreground process group */ + + if (tcgetpgrp(STDIN_FILENO) > 0) + sig_handler(sig); + else + LOG_DEBUG("Ignored Signal %d", sig); } +#endif + int server_preinit(void) { @@ -583,8 +652,13 @@ int server_preinit(void) SetConsoleCtrlHandler(ControlHandler, TRUE); signal(SIGBREAK, sig_handler); -#endif signal(SIGINT, sig_handler); +#else + signal(SIGHUP, sig_handler); + signal(SIGPIPE, sig_handler); + signal(SIGQUIT, sigkey_handler); + signal(SIGINT, sigkey_handler); +#endif signal(SIGTERM, sig_handler); signal(SIGABRT, sig_handler); @@ -624,6 +698,13 @@ int server_quit(void) return last_signal; } +void server_free(void) +{ + tcl_service_free(); + telnet_service_free(); + jsp_service_free(); +} + void exit_on_signal(int sig) { #ifndef _WIN32 @@ -658,11 +739,11 @@ COMMAND_HANDLER(handle_shutdown_command) { LOG_USER("shutdown command invoked"); - shutdown_openocd = 1; + shutdown_openocd = SHUTDOWN_REQUESTED; if (CMD_ARGC == 1) { if (!strcmp(CMD_ARGV[0], "error")) { - shutdown_openocd = 2; + shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE; return ERROR_FAIL; } } @@ -719,7 +800,7 @@ static const struct command_registration server_command_handlers[] = { .mode = COMMAND_ANY, .usage = "[name]", .help = "Specify address by name on which to listen for " - "incoming TCP/IP connections", + "incoming TCP/IP connections", }, COMMAND_REGISTRATION_DONE }; diff --git a/src/server/server.h b/src/server/server.h index 68ad16d..96e0b48 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -25,6 +25,10 @@ #ifndef OPENOCD_SERVER_SERVER_H #define OPENOCD_SERVER_SERVER_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include <helper/log.h> #ifdef HAVE_NETINET_IN_H @@ -74,10 +78,12 @@ int add_service(char *name, const char *port, int max_connections, new_connection_handler_t new_connection_handler, input_handler_t in_handler, connection_closed_handler_t close_handler, void *priv); +int remove_service(const char *name, const char *port); int server_preinit(void); int server_init(struct command_context *cmd_ctx); int server_quit(void); +void server_free(void); void exit_on_signal(int); int server_loop(struct command_context *command_context); diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index 0077339..3cb63a2 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -157,7 +157,7 @@ static int tcl_new_connection(struct connection *connection) connection->priv = tclc; - struct target *target = get_target_by_num(connection->cmd_ctx->current_target); + struct target *target = get_current_target(connection->cmd_ctx); if (target != NULL) tclc->tc_laststate = target->state; @@ -359,3 +359,8 @@ int tcl_register_commands(struct command_context *cmd_ctx) tcl_port = strdup("6666"); return register_commands(cmd_ctx, NULL, tcl_command_handlers); } + +void tcl_service_free(void) +{ + free(tcl_port); +} diff --git a/src/server/tcl_server.h b/src/server/tcl_server.h index 422c794..6ce3ab9 100644 --- a/src/server/tcl_server.h +++ b/src/server/tcl_server.h @@ -22,5 +22,6 @@ int tcl_init(void); int tcl_register_commands(struct command_context *cmd_ctx); +void tcl_service_free(void); #endif /* OPENOCD_SERVER_TCL_SERVER_H */ diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 9077b6c..a864f5f 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -719,3 +719,8 @@ int telnet_register_commands(struct command_context *cmd_ctx) telnet_port = strdup("4444"); return register_commands(cmd_ctx, NULL, telnet_command_handlers); } + +void telnet_service_free(void) +{ + free(telnet_port); +} diff --git a/src/server/telnet_server.h b/src/server/telnet_server.h index 5e238f4..27148d7 100644 --- a/src/server/telnet_server.h +++ b/src/server/telnet_server.h @@ -64,5 +64,6 @@ struct telnet_service { int telnet_init(char *banner); int telnet_register_commands(struct command_context *command_context); +void telnet_service_free(void); #endif /* OPENOCD_SERVER_TELNET_SERVER_H */ diff --git a/src/svf/svf.c b/src/svf/svf.c index 1d686ba..223af7e 100644 --- a/src/svf/svf.c +++ b/src/svf/svf.c @@ -741,6 +741,9 @@ parse_char: pos++; } + if (num == 0) + return ERROR_FAIL; + *num_of_argu = num; return ERROR_OK; @@ -1313,7 +1316,7 @@ XXR_common: * SEC]] [ENDSTATE end_state] */ /* RUNTEST [run_state] min_time SEC [MAXIMUM max_time SEC] [ENDSTATE * end_state] */ - if ((num_of_argu < 3) && (num_of_argu > 11)) { + if ((num_of_argu < 3) || (num_of_argu > 11)) { LOG_ERROR("invalid parameter of %s", argus[0]); return ERROR_FAIL; } diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 9576b23..b1119e7 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -4,7 +4,9 @@ else OOCD_TRACE_FILES = endif -%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la +%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \ + %D%/riscv/libriscv.la + STARTUP_TCL_SRCS += %D%/startup.tcl @@ -21,7 +23,6 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(NDS32_SRC) \ $(STM8_SRC) \ $(INTEL_IA32_SRC) \ - $(RISCV_SRC) \ %D%/avrt.c \ %D%/dsp563xx.c \ %D%/dsp563xx_once.c \ @@ -40,6 +41,7 @@ TARGET_CORE_SRC = \ %D%/target.c \ %D%/target_request.c \ %D%/testee.c \ + %D%/semihosting_common.c \ %D%/smp.c ARMV4_5_SRC = \ @@ -89,6 +91,7 @@ ARM_DEBUG_SRC = \ %D%/arm_simulator.c \ %D%/arm_semihosting.c \ %D%/arm_adi_v5.c \ + %D%/arm_dap.c \ %D%/armv7a_cache.c \ %D%/armv7a_cache_l2x.c \ %D%/adi_v5_jtag.c \ @@ -135,13 +138,6 @@ INTEL_IA32_SRC = \ %D%/lakemont.c \ %D%/x86_32_common.c -RISCV_SRC = \ - %D%/riscv/riscv-011.c \ - %D%/riscv/riscv-013.c \ - %D%/riscv/riscv.c \ - %D%/riscv/program.c \ - %D%/riscv/batch.c - %C%_libtarget_la_SOURCES += \ %D%/algorithm.h \ %D%/arm.h \ @@ -217,9 +213,11 @@ RISCV_SRC = \ %D%/nds32_v3.h \ %D%/nds32_v3m.h \ %D%/nds32_aice.h \ + %D%/semihosting_common.h \ %D%/stm8.h \ %D%/lakemont.h \ %D%/x86_32_common.h \ %D%/arm_cti.h include %D%/openrisc/Makefile.am +include %D%/riscv/Makefile.am diff --git a/src/target/aarch64.c b/src/target/aarch64.c index 14a2da6..454de9e 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -28,6 +28,7 @@ #include "target_type.h" #include "armv8_opcodes.h" #include "armv8_cache.h" +#include "arm_semihosting.h" #include <helper/time_support.h> enum restart_mode { @@ -40,6 +41,11 @@ enum halt_mode { HALT_SYNC, }; +struct aarch64_private_config { + struct adiv5_private_config adiv5_config; + struct arm_cti *cti; +}; + static int aarch64_poll(struct target *target); static int aarch64_debug_entry(struct target *target); static int aarch64_restore_context(struct target *target, bool bpwp); @@ -452,7 +458,7 @@ static int update_halt_gdb(struct target *target, enum target_debug_reason debug struct target *curr; if (debug_reason == DBG_REASON_NOTHALTED) { - LOG_INFO("Halting remaining targets in SMP group"); + LOG_DEBUG("Halting remaining targets in SMP group"); aarch64_halt_smp(target, true); } @@ -517,6 +523,9 @@ static int aarch64_poll(struct target *target) if (target->smp) update_halt_gdb(target, debug_reason); + if (arm_semihosting(target, &retval) != 0) + return retval; + switch (prev_target_state) { case TARGET_RUNNING: case TARGET_UNKNOWN: @@ -538,6 +547,9 @@ static int aarch64_poll(struct target *target) static int aarch64_halt(struct target *target) { + struct armv8_common *armv8 = target_to_armv8(target); + armv8->last_run_control_op = ARMV8_RUNCONTROL_HALT; + if (target->smp) return aarch64_halt_smp(target, false); @@ -826,6 +838,9 @@ static int aarch64_resume(struct target *target, int current, int retval = 0; uint64_t addr = address; + struct armv8_common *armv8 = target_to_armv8(target); + armv8->last_run_control_op = ARMV8_RUNCONTROL_RESUME; + if (target->state != TARGET_HALTED) return ERROR_TARGET_NOT_HALTED; @@ -1064,6 +1079,8 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres int retval; uint32_t edecr; + armv8->last_run_control_op = ARMV8_RUNCONTROL_STEP; + if (target->state != TARGET_HALTED) { LOG_WARNING("target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -1086,7 +1103,7 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres if (retval != ERROR_OK) return retval; - if (target->smp && !handle_breakpoints) { + if (target->smp && (current == 1)) { /* * isolate current target so that it doesn't get resumed * together with the others @@ -1677,17 +1694,19 @@ static int aarch64_deassert_reset(struct target *target) if (retval != ERROR_OK) return retval; + retval = aarch64_init_debug_access(target); + if (retval != ERROR_OK) + return retval; + if (target->reset_halt) { if (target->state != TARGET_HALTED) { LOG_WARNING("%s: ran after reset and before halt ...", target_name(target)); retval = target_halt(target); - if (retval != ERROR_OK) - return retval; } } - return aarch64_init_debug_access(target); + return retval; } static int aarch64_write_cpu_memory_slow(struct target *target, @@ -1861,7 +1880,7 @@ static int aarch64_write_cpu_memory(struct target *target, if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) { /* Abort occurred - clear it and exit */ LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr); - armv8_dpm_handle_exception(dpm); + armv8_dpm_handle_exception(dpm, true); return ERROR_FAIL; } @@ -2080,7 +2099,7 @@ static int aarch64_read_cpu_memory(struct target *target, if (dscr & (DSCR_ERR | DSCR_SYS_ERROR_PEND)) { /* Abort occurred - clear it and exit */ LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr); - armv8_dpm_handle_exception(dpm); + armv8_dpm_handle_exception(dpm, true); return ERROR_FAIL; } @@ -2198,7 +2217,7 @@ static int aarch64_examine_first(struct target *target) struct aarch64_common *aarch64 = target_to_aarch64(target); struct armv8_common *armv8 = &aarch64->armv8_common; struct adiv5_dap *swjdp = armv8->arm.dap; - uint32_t cti_base; + struct aarch64_private_config *pc; int i; int retval = ERROR_OK; uint64_t debug, ttypr; @@ -2206,10 +2225,6 @@ static int aarch64_examine_first(struct target *target) uint32_t tmp0, tmp1, tmp2, tmp3; debug = ttypr = cpuid = 0; - retval = dap_dp_init(swjdp); - if (retval != ERROR_OK) - return retval; - /* Search for the APB-AB - it is needed for access to debug registers */ retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv8->debug_ap); if (retval != ERROR_OK) { @@ -2289,17 +2304,15 @@ static int aarch64_examine_first(struct target *target) LOG_DEBUG("ttypr = 0x%08" PRIx64, ttypr); LOG_DEBUG("debug = 0x%08" PRIx64, debug); - if (target->ctibase == 0) { - /* assume a v8 rom table layout */ - cti_base = armv8->debug_base + 0x10000; - LOG_INFO("Target ctibase is not set, assuming 0x%0" PRIx32, cti_base); - } else - cti_base = target->ctibase; + if (target->private_config == NULL) + return ERROR_FAIL; - armv8->cti = arm_cti_create(armv8->debug_ap, cti_base); - if (armv8->cti == NULL) + pc = (struct aarch64_private_config *)target->private_config; + if (pc->cti == NULL) return ERROR_FAIL; + armv8->cti = pc->cti; + retval = aarch64_dpm_setup(aarch64, debug); if (retval != ERROR_OK) return retval; @@ -2352,22 +2365,18 @@ static int aarch64_init_target(struct command_context *cmd_ctx, struct target *target) { /* examine_first() does a bunch of this */ + arm_semihosting_init(target); return ERROR_OK; } static int aarch64_init_arch_info(struct target *target, - struct aarch64_common *aarch64, struct jtag_tap *tap) + struct aarch64_common *aarch64, struct adiv5_dap *dap) { struct armv8_common *armv8 = &aarch64->armv8_common; /* Setup struct aarch64_common */ aarch64->common_magic = AARCH64_COMMON_MAGIC; - /* tap has no dap initialized */ - if (!tap->dap) { - tap->dap = dap_init(); - tap->dap->tap = tap; - } - armv8->arm.dap = tap->dap; + armv8->arm.dap = dap; /* register arch-specific functions */ armv8->examine_debug_reason = NULL; @@ -2383,9 +2392,27 @@ static int aarch64_init_arch_info(struct target *target, static int aarch64_target_create(struct target *target, Jim_Interp *interp) { + struct aarch64_private_config *pc = target->private_config; struct aarch64_common *aarch64 = calloc(1, sizeof(struct aarch64_common)); - return aarch64_init_arch_info(target, aarch64, target->tap); + if (adiv5_verify_config(&pc->adiv5_config) != ERROR_OK) + return ERROR_FAIL; + + return aarch64_init_arch_info(target, aarch64, pc->adiv5_config.dap); +} + +static void aarch64_deinit_target(struct target *target) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct arm_dpm *dpm = &armv8->dpm; + + armv8_free_reg_cache(target); + free(aarch64->brp_list); + free(dpm->dbp); + free(dpm->dwp); + free(target->private_config); + free(aarch64); } static int aarch64_mmu(struct target *target, int *enabled) @@ -2405,6 +2432,94 @@ static int aarch64_virt2phys(struct target *target, target_addr_t virt, return armv8_mmu_translate_va_pa(target, virt, phys, 1); } +/* + * private target configuration items + */ +enum aarch64_cfg_param { + CFG_CTI, +}; + +static const Jim_Nvp nvp_config_opts[] = { + { .name = "-cti", .value = CFG_CTI }, + { .name = NULL, .value = -1 } +}; + +static int aarch64_jim_configure(struct target *target, Jim_GetOptInfo *goi) +{ + struct aarch64_private_config *pc; + Jim_Nvp *n; + int e; + + pc = (struct aarch64_private_config *)target->private_config; + if (pc == NULL) { + pc = calloc(1, sizeof(struct aarch64_private_config)); + target->private_config = pc; + } + + /* + * Call adiv5_jim_configure() to parse the common DAP options + * It will return JIM_CONTINUE if it didn't find any known + * options, JIM_OK if it correctly parsed the topmost option + * and JIM_ERR if an error occured during parameter evaluation. + * For JIM_CONTINUE, we check our own params. + */ + e = adiv5_jim_configure(target, goi); + if (e != JIM_CONTINUE) + return e; + + /* parse config or cget options ... */ + if (goi->argc > 0) { + Jim_SetEmptyResult(goi->interp); + + /* check first if topmost item is for us */ + e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts, + goi->argv[0], &n); + if (e != JIM_OK) + return JIM_CONTINUE; + + e = Jim_GetOpt_Obj(goi, NULL); + if (e != JIM_OK) + return e; + + switch (n->value) { + case CFG_CTI: { + if (goi->isconfigure) { + Jim_Obj *o_cti; + struct arm_cti *cti; + e = Jim_GetOpt_Obj(goi, &o_cti); + if (e != JIM_OK) + return e; + cti = cti_instance_by_jim_obj(goi->interp, o_cti); + if (cti == NULL) { + Jim_SetResultString(goi->interp, "CTI name invalid!", -1); + return JIM_ERR; + } + pc->cti = cti; + } else { + if (goi->argc != 0) { + Jim_WrongNumArgs(goi->interp, + goi->argc, goi->argv, + "NO PARAMS"); + return JIM_ERR; + } + + if (pc == NULL || pc->cti == NULL) { + Jim_SetResultString(goi->interp, "CTI not configured", -1); + return JIM_ERR; + } + Jim_SetResultString(goi->interp, arm_cti_name(pc->cti), -1); + } + break; + } + + default: + return JIM_CONTINUE; + } + } + + return JIM_OK; +} + COMMAND_HANDLER(aarch64_handle_cache_info_command) { struct target *target = get_current_target(CMD_CTX); @@ -2490,6 +2605,143 @@ COMMAND_HANDLER(aarch64_mask_interrupts_command) return ERROR_OK; } +static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv) +{ + struct command_context *context; + struct target *target; + struct arm *arm; + int retval; + bool is_mcr = false; + int arg_cnt = 0; + + if (Jim_CompareStringImmediate(interp, argv[0], "mcr")) { + is_mcr = true; + arg_cnt = 7; + } else { + arg_cnt = 6; + } + + context = current_command_context(interp); + assert(context != NULL); + + target = get_current_target(context); + if (target == NULL) { + LOG_ERROR("%s: no current target", __func__); + return JIM_ERR; + } + if (!target_was_examined(target)) { + LOG_ERROR("%s: not yet examined", target_name(target)); + return JIM_ERR; + } + + arm = target_to_arm(target); + if (!is_arm(arm)) { + LOG_ERROR("%s: not an ARM", target_name(target)); + return JIM_ERR; + } + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + if (arm->core_state == ARM_STATE_AARCH64) { + LOG_ERROR("%s: not 32-bit arm target", target_name(target)); + return JIM_ERR; + } + + if (argc != arg_cnt) { + LOG_ERROR("%s: wrong number of arguments", __func__); + return JIM_ERR; + } + + int cpnum; + uint32_t op1; + uint32_t op2; + uint32_t CRn; + uint32_t CRm; + uint32_t value; + long l; + + /* NOTE: parameter sequence matches ARM instruction set usage: + * MCR pNUM, op1, rX, CRn, CRm, op2 ; write CP from rX + * MRC pNUM, op1, rX, CRn, CRm, op2 ; read CP into rX + * The "rX" is necessarily omitted; it uses Tcl mechanisms. + */ + retval = Jim_GetLong(interp, argv[1], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0xf) { + LOG_ERROR("%s: %s %d out of range", __func__, + "coprocessor", (int) l); + return JIM_ERR; + } + cpnum = l; + + retval = Jim_GetLong(interp, argv[2], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0x7) { + LOG_ERROR("%s: %s %d out of range", __func__, + "op1", (int) l); + return JIM_ERR; + } + op1 = l; + + retval = Jim_GetLong(interp, argv[3], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0xf) { + LOG_ERROR("%s: %s %d out of range", __func__, + "CRn", (int) l); + return JIM_ERR; + } + CRn = l; + + retval = Jim_GetLong(interp, argv[4], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0xf) { + LOG_ERROR("%s: %s %d out of range", __func__, + "CRm", (int) l); + return JIM_ERR; + } + CRm = l; + + retval = Jim_GetLong(interp, argv[5], &l); + if (retval != JIM_OK) + return retval; + if (l & ~0x7) { + LOG_ERROR("%s: %s %d out of range", __func__, + "op2", (int) l); + return JIM_ERR; + } + op2 = l; + + value = 0; + + if (is_mcr == true) { + retval = Jim_GetLong(interp, argv[6], &l); + if (retval != JIM_OK) + return retval; + value = l; + + /* NOTE: parameters reordered! */ + /* ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2) */ + retval = arm->mcr(target, cpnum, op1, op2, CRn, CRm, value); + if (retval != ERROR_OK) + return JIM_ERR; + } else { + /* NOTE: parameters reordered! */ + /* ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2) */ + retval = arm->mrc(target, cpnum, op1, op2, CRn, CRm, &value); + if (retval != ERROR_OK) + return JIM_ERR; + + Jim_SetResult(interp, Jim_NewIntObj(interp, value)); + } + + return JIM_OK; +} + static const struct command_registration aarch64_exec_command_handlers[] = { { .name = "cache_info", @@ -2525,9 +2777,25 @@ static const struct command_registration aarch64_exec_command_handlers[] = { .help = "mask aarch64 interrupts during single-step", .usage = "['on'|'off']", }, + { + .name = "mcr", + .mode = COMMAND_EXEC, + .jim_handler = jim_mcrmrc, + .help = "write coprocessor register", + .usage = "cpnum op1 CRn CRm op2 value", + }, + { + .name = "mrc", + .mode = COMMAND_EXEC, + .jim_handler = jim_mcrmrc, + .help = "read coprocessor register", + .usage = "cpnum op1 CRn CRm op2", + }, + COMMAND_REGISTRATION_DONE }; + static const struct command_registration aarch64_command_handlers[] = { { .chain = armv8_command_handlers, @@ -2570,7 +2838,9 @@ struct target_type aarch64_target = { .commands = aarch64_command_handlers, .target_create = aarch64_target_create, + .target_jim_configure = aarch64_jim_configure, .init_target = aarch64_init_target, + .deinit_target = aarch64_deinit_target, .examine = aarch64_examine, .read_phys_memory = aarch64_read_phys_memory, diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c index c7dc4f7..8c20611 100644 --- a/src/target/adi_v5_jtag.c +++ b/src/target/adi_v5_jtag.c @@ -553,7 +553,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) { int retval; - uint32_t ctrlstat; + uint32_t ctrlstat, pwrmask; /* too expensive to call keep_alive() here */ @@ -571,9 +571,12 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) if (ctrlstat & SSTICKYERR) { LOG_DEBUG("jtag-dp: CTRL/STAT 0x%" PRIx32, ctrlstat); /* Check power to debug regions */ - if ((ctrlstat & (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) != - (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) { + pwrmask = CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ; + if (!dap->ignore_syspwrupack) + pwrmask |= CSYSPWRUPACK; + if ((ctrlstat & pwrmask) != pwrmask) { LOG_ERROR("Debug regions are unpowered, an unexpected reset might have happened"); + dap->do_reconnect = true; } if (ctrlstat & SSTICKYERR) @@ -598,6 +601,20 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) /*--------------------------------------------------------------------------*/ +static int jtag_connect(struct adiv5_dap *dap) +{ + dap->do_reconnect = false; + return dap_dp_init(dap); +} + +static int jtag_check_reconnect(struct adiv5_dap *dap) +{ + if (dap->do_reconnect) + return jtag_connect(dap); + + return ERROR_OK; +} + static int jtag_dp_q_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data) { @@ -633,7 +650,11 @@ static int jtag_ap_q_bankselect(struct adiv5_ap *ap, unsigned reg) static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg, uint32_t *data) { - int retval = jtag_ap_q_bankselect(ap, reg); + int retval = jtag_check_reconnect(ap->dap); + if (retval != ERROR_OK) + return retval; + + retval = jtag_ap_q_bankselect(ap, reg); if (retval != ERROR_OK) return retval; @@ -647,7 +668,11 @@ static int jtag_ap_q_read(struct adiv5_ap *ap, unsigned reg, static int jtag_ap_q_write(struct adiv5_ap *ap, unsigned reg, uint32_t data) { - int retval = jtag_ap_q_bankselect(ap, reg); + int retval = jtag_check_reconnect(ap->dap); + if (retval != ERROR_OK) + return retval; + + retval = jtag_ap_q_bankselect(ap, reg); if (retval != ERROR_OK) return retval; @@ -692,6 +717,7 @@ static int jtag_dp_sync(struct adiv5_dap *dap) * part of DAP setup */ const struct dap_ops jtag_dp_ops = { + .connect = jtag_connect, .queue_dp_read = jtag_dp_q_read, .queue_dp_write = jtag_dp_q_write, .queue_ap_read = jtag_ap_q_read, diff --git a/src/target/adi_v5_swd.c b/src/target/adi_v5_swd.c index c503f09..b520223 100644 --- a/src/target/adi_v5_swd.c +++ b/src/target/adi_v5_swd.c @@ -53,13 +53,11 @@ #include <jtag/swd.h> -/* YUK! - but this is currently a global.... */ -extern struct jtag_interface *jtag_interface; static bool do_sync; static void swd_finish_read(struct adiv5_dap *dap) { - const struct swd_driver *swd = jtag_interface->swd; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); if (dap->last_read != NULL) { swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0); dap->last_read = NULL; @@ -73,7 +71,7 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, static void swd_clear_sticky_errors(struct adiv5_dap *dap) { - const struct swd_driver *swd = jtag_interface->swd; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); assert(swd); swd->write_reg(swd_cmd(false, false, DP_ABORT), @@ -82,7 +80,7 @@ static void swd_clear_sticky_errors(struct adiv5_dap *dap) static int swd_run_inner(struct adiv5_dap *dap) { - const struct swd_driver *swd = jtag_interface->swd; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); int retval; retval = swd->run(); @@ -97,6 +95,7 @@ static int swd_run_inner(struct adiv5_dap *dap) static int swd_connect(struct adiv5_dap *dap) { + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); uint32_t dpidr; int status; @@ -120,7 +119,7 @@ static int swd_connect(struct adiv5_dap *dap) } /* Note, debugport_init() does setup too */ - jtag_interface->swd->switch_seq(JTAG_TO_SWD); + swd->switch_seq(JTAG_TO_SWD); /* Clear link state, including the SELECT cache. */ dap->do_reconnect = false; @@ -136,6 +135,7 @@ static int swd_connect(struct adiv5_dap *dap) if (status == ERROR_OK) { LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr); dap->do_reconnect = false; + status = dap_dp_init(dap); } else dap->do_reconnect = true; @@ -157,7 +157,7 @@ static int swd_check_reconnect(struct adiv5_dap *dap) static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) { - const struct swd_driver *swd = jtag_interface->swd; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); assert(swd); swd->write_reg(swd_cmd(false, false, DP_ABORT), @@ -187,7 +187,7 @@ static void swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned reg) static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data) { - const struct swd_driver *swd = jtag_interface->swd; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); assert(swd); int retval = swd_check_reconnect(dap); @@ -203,7 +203,7 @@ static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, uint32_t data) { - const struct swd_driver *swd = jtag_interface->swd; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); assert(swd); int retval = swd_check_reconnect(dap); @@ -236,10 +236,9 @@ static void swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg) static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg, uint32_t *data) { - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - struct adiv5_dap *dap = ap->dap; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); int retval = swd_check_reconnect(dap); if (retval != ERROR_OK) @@ -255,10 +254,9 @@ static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg, static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg, uint32_t data) { - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - struct adiv5_dap *dap = ap->dap; + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + assert(swd); int retval = swd_check_reconnect(dap); if (retval != ERROR_OK) @@ -278,13 +276,25 @@ static int swd_run(struct adiv5_dap *dap) return swd_run_inner(dap); } +/** Put the SWJ-DP back to JTAG mode */ +static void swd_quit(struct adiv5_dap *dap) +{ + const struct swd_driver *swd = adiv5_dap_swd_driver(dap); + + swd->switch_seq(SWD_TO_JTAG); + /* flush the queue before exit */ + swd->run(); +} + const struct dap_ops swd_dap_ops = { + .connect = swd_connect, .queue_dp_read = swd_queue_dp_read, .queue_dp_write = swd_queue_dp_write, .queue_ap_read = swd_queue_ap_read, .queue_ap_write = swd_queue_ap_write, .queue_ap_abort = swd_queue_ap_abort, .run = swd_run, + .quit = swd_quit, }; /* @@ -381,15 +391,15 @@ static const struct command_registration swd_handlers[] = { static int swd_select(struct command_context *ctx) { + /* FIXME: only place where global 'jtag_interface' is still needed */ + extern struct jtag_interface *jtag_interface; + const struct swd_driver *swd = jtag_interface->swd; int retval; retval = register_commands(ctx, NULL, swd_handlers); - if (retval != ERROR_OK) return retval; - const struct swd_driver *swd = jtag_interface->swd; - /* be sure driver is in SWD mode; start * with hardware default TRN (1), it can be changed later */ @@ -404,33 +414,14 @@ static int swd_select(struct command_context *ctx) return retval; } - /* force DAP into SWD mode (not JTAG) */ - /*retval = dap_to_swd(target);*/ - - if (ctx->current_target) { - /* force DAP into SWD mode (not JTAG) */ - struct target *target = get_current_target(ctx); - retval = dap_to_swd(target); - } - return retval; } static int swd_init(struct command_context *ctx) { - struct target *target = get_current_target(ctx); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - /* Force the DAP's ops vector for SWD mode. - * messy - is there a better way? */ - arm->dap->ops = &swd_dap_ops; - /* First connect after init is not reconnecting. */ - dap->do_reconnect = false; - - int retval = swd_connect(dap); - if (retval != ERROR_OK) - LOG_ERROR("SWD connect failed"); - return retval; + /* nothing done here, SWD is initialized + * together with the DAP */ + return ERROR_OK; } static struct transport swd_transport = { diff --git a/src/target/arm.h b/src/target/arm.h index eb4a51f..316ff9a 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -8,6 +8,9 @@ * Copyright (C) 2009 by Øyvind Harboe * oyvind.harboe@zylin.com * + * Copyright (C) 2018 by Liviu Ionescu + * <ilg@livius.net> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -28,7 +31,6 @@ #include <helper/command.h> #include "target.h" - /** * @file * Holds the interface to ARM cores. @@ -77,6 +79,43 @@ enum arm_mode { ARM_MODE_ANY = -1 }; +/* VFPv3 internal register numbers mapping to d0:31 */ +enum { + ARM_VFP_V3_D0 = 51, + ARM_VFP_V3_D1, + ARM_VFP_V3_D2, + ARM_VFP_V3_D3, + ARM_VFP_V3_D4, + ARM_VFP_V3_D5, + ARM_VFP_V3_D6, + ARM_VFP_V3_D7, + ARM_VFP_V3_D8, + ARM_VFP_V3_D9, + ARM_VFP_V3_D10, + ARM_VFP_V3_D11, + ARM_VFP_V3_D12, + ARM_VFP_V3_D13, + ARM_VFP_V3_D14, + ARM_VFP_V3_D15, + ARM_VFP_V3_D16, + ARM_VFP_V3_D17, + ARM_VFP_V3_D18, + ARM_VFP_V3_D19, + ARM_VFP_V3_D20, + ARM_VFP_V3_D21, + ARM_VFP_V3_D22, + ARM_VFP_V3_D23, + ARM_VFP_V3_D24, + ARM_VFP_V3_D25, + ARM_VFP_V3_D26, + ARM_VFP_V3_D27, + ARM_VFP_V3_D28, + ARM_VFP_V3_D29, + ARM_VFP_V3_D30, + ARM_VFP_V3_D31, + ARM_VFP_V3_FPSCR, +}; + const char *arm_mode_name(unsigned psr_mode); bool is_arm_mode(unsigned psr_mode); @@ -89,6 +128,14 @@ enum arm_state { ARM_STATE_AARCH64, }; +/** ARM vector floating point enabled, if yes which version. */ +enum arm_vfp_version { + ARM_VFP_DISABLED, + ARM_VFP_V1, + ARM_VFP_V2, + ARM_VFP_V3, +}; + #define ARM_COMMON_MAGIC 0x0A450A45 /** @@ -136,29 +183,11 @@ struct arm { /** Flag reporting armv6m based core. */ bool is_armv6m; - /** Flag reporting whether semihosting is active. */ - bool is_semihosting; - - /** Flag reporting whether semihosting fileio is active. */ - bool is_semihosting_fileio; - - /** Flag reporting whether semihosting fileio operation is active. */ - bool semihosting_hit_fileio; - - /** Current semihosting operation. */ - int semihosting_op; - - /** Current semihosting result. */ - int semihosting_result; - - /** Value to be returned by semihosting SYS_ERRNO request. */ - int semihosting_errno; + /** Floating point or VFP version, 0 if disabled. */ + int arm_vfp_version; int (*setup_semihosting)(struct target *target, int enable); - /** Semihosting command line. */ - char *semihosting_cmdline; - /** Backpointer to the target. */ struct target *target; @@ -225,7 +254,7 @@ struct arm_reg { enum arm_mode mode; struct target *target; struct arm *arm; - uint8_t value[8]; + uint8_t value[16]; }; struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm); @@ -260,7 +289,7 @@ int armv4_5_run_algorithm_inner(struct target *target, int arm_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum); int arm_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value); + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value); void arm_set_cpsr(struct arm *arm, uint32_t cpsr); struct reg *arm_reg_current(struct arm *arm, unsigned regnum); diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index dfbc5ad..302ea78 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -76,6 +76,7 @@ #include <helper/jep106.h> #include <helper/time_support.h> #include <helper/list.h> +#include <helper/jim-nvp.h> /* ARM ADI Specification requires at least 10 bits used for TAR autoincrement */ @@ -96,14 +97,15 @@ static uint32_t max_tar_block_size(uint32_t tar_autoincr_block, uint32_t address static int mem_ap_setup_csw(struct adiv5_ap *ap, uint32_t csw) { - csw = csw | CSW_DBGSWENABLE | CSW_MASTER_DEBUG | CSW_HPROT | - ap->csw_default; + csw |= ap->csw_default; if (csw != ap->csw_value) { /* LOG_DEBUG("DAP: Set CSW %x",csw); */ int retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + ap->csw_value = 0; return retval; + } ap->csw_value = csw; } return ERROR_OK; @@ -114,8 +116,10 @@ static int mem_ap_setup_tar(struct adiv5_ap *ap, uint32_t tar) if (!ap->tar_valid || tar != ap->tar_value) { /* LOG_DEBUG("DAP: Set TAR %x",tar); */ int retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR, tar); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + ap->tar_valid = false; return retval; + } ap->tar_value = tar; ap->tar_valid = true; } @@ -152,6 +156,8 @@ static uint32_t mem_ap_get_tar_increment(struct adiv5_ap *ap) return 2; case CSW_32BIT: return 4; + default: + return 0; } case CSW_ADDRINC_PACKED: return 4; @@ -614,34 +620,9 @@ int mem_ap_write_buf_noincr(struct adiv5_ap *ap, #define DAP_POWER_DOMAIN_TIMEOUT (10) -/* FIXME don't import ... just initialize as - * part of DAP transport setup -*/ -extern const struct dap_ops jtag_dp_ops; - /*--------------------------------------------------------------------------*/ /** - * Create a new DAP - */ -struct adiv5_dap *dap_init(void) -{ - struct adiv5_dap *dap = calloc(1, sizeof(struct adiv5_dap)); - int i; - /* Set up with safe defaults */ - for (i = 0; i <= 255; i++) { - dap->ap[i].dap = dap; - dap->ap[i].ap_num = i; - /* memaccess_tck max is 255 */ - dap->ap[i].memaccess_tck = 255; - /* Number of bits for tar autoincrement, impl. dep. at least 10 */ - dap->ap[i].tar_autoincr_block = (1<<10); - } - INIT_LIST_HEAD(&dap->cmd_journal); - return dap; -} - -/** * Invalidate cached DP select and cached TAR and CSW of all APs */ void dap_invalidate_cache(struct adiv5_dap *dap) @@ -667,14 +648,7 @@ int dap_dp_init(struct adiv5_dap *dap) { int retval; - LOG_DEBUG(" "); - /* JTAG-DP or SWJ-DP, in JTAG mode - * ... for SWD mode this is patched as part - * of link switchover - * FIXME: This should already be setup by the respective transport specific DAP creation. - */ - if (!dap->ops) - dap->ops = &jtag_dp_ops; + LOG_DEBUG("%s", adiv5_dap_name(dap)); dap_invalidate_cache(dap); @@ -707,12 +681,14 @@ int dap_dp_init(struct adiv5_dap *dap) if (retval != ERROR_OK) return retval; - LOG_DEBUG("DAP: wait CSYSPWRUPACK"); - retval = dap_dp_poll_register(dap, DP_CTRL_STAT, - CSYSPWRUPACK, CSYSPWRUPACK, - DAP_POWER_DOMAIN_TIMEOUT); - if (retval != ERROR_OK) - return retval; + if (!dap->ignore_syspwrupack) { + LOG_DEBUG("DAP: wait CSYSPWRUPACK"); + retval = dap_dp_poll_register(dap, DP_CTRL_STAT, + CSYSPWRUPACK, CSYSPWRUPACK, + DAP_POWER_DOMAIN_TIMEOUT); + if (retval != ERROR_OK) + return retval; + } retval = dap_queue_dp_read(dap, DP_CTRL_STAT, NULL); if (retval != ERROR_OK) @@ -1376,7 +1352,7 @@ static int dap_rom_display(struct command_context *cmd_ctx, return ERROR_OK; } -static int dap_info_command(struct command_context *cmd_ctx, +int dap_info_command(struct command_context *cmd_ctx, struct adiv5_ap *ap) { int retval; @@ -1434,46 +1410,131 @@ static int dap_info_command(struct command_context *cmd_ctx, return ERROR_OK; } +enum adiv5_cfg_param { + CFG_DAP, + CFG_AP_NUM +}; + +static const Jim_Nvp nvp_config_opts[] = { + { .name = "-dap", .value = CFG_DAP }, + { .name = "-ap-num", .value = CFG_AP_NUM }, + { .name = NULL, .value = -1 } +}; + int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi) { struct adiv5_private_config *pc; - const char *arg; - jim_wide ap_num; int e; - /* check if argv[0] is for us */ - arg = Jim_GetString(goi->argv[0], NULL); - if (strcmp(arg, "-ap-num")) - return JIM_CONTINUE; + pc = (struct adiv5_private_config *)target->private_config; + if (pc == NULL) { + pc = calloc(1, sizeof(struct adiv5_private_config)); + pc->ap_num = -1; + target->private_config = pc; + } - e = Jim_GetOpt_String(goi, &arg, NULL); - if (e != JIM_OK) - return e; + target->has_dap = true; + + if (goi->argc > 0) { + Jim_Nvp *n; + + Jim_SetEmptyResult(goi->interp); + + /* check first if topmost item is for us */ + e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts, + goi->argv[0], &n); + if (e != JIM_OK) + return JIM_CONTINUE; + + e = Jim_GetOpt_Obj(goi, NULL); + if (e != JIM_OK) + return e; + + switch (n->value) { + case CFG_DAP: + if (goi->isconfigure) { + Jim_Obj *o_t; + struct adiv5_dap *dap; + e = Jim_GetOpt_Obj(goi, &o_t); + if (e != JIM_OK) + return e; + dap = dap_instance_by_jim_obj(goi->interp, o_t); + if (dap == NULL) { + Jim_SetResultString(goi->interp, "DAP name invalid!", -1); + return JIM_ERR; + } + if (pc->dap != NULL && pc->dap != dap) { + Jim_SetResultString(goi->interp, + "DAP assignment cannot be changed after target was created!", -1); + return JIM_ERR; + } + if (target->tap_configured) { + Jim_SetResultString(goi->interp, + "-chain-position and -dap configparams are mutually exclusive!", -1); + return JIM_ERR; + } + pc->dap = dap; + target->tap = dap->tap; + target->dap_configured = true; + } else { + if (goi->argc != 0) { + Jim_WrongNumArgs(goi->interp, + goi->argc, goi->argv, + "NO PARAMS"); + return JIM_ERR; + } + + if (pc->dap == NULL) { + Jim_SetResultString(goi->interp, "DAP not configured", -1); + return JIM_ERR; + } + Jim_SetResultString(goi->interp, adiv5_dap_name(pc->dap), -1); + } + break; - if (goi->argc == 0) { - Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-ap-num ?ap-number? ..."); - return JIM_ERR; + case CFG_AP_NUM: + if (goi->isconfigure) { + jim_wide ap_num; + e = Jim_GetOpt_Wide(goi, &ap_num); + if (e != JIM_OK) + return e; + pc->ap_num = ap_num; + } else { + if (goi->argc != 0) { + Jim_WrongNumArgs(goi->interp, + goi->argc, goi->argv, + "NO PARAMS"); + return JIM_ERR; + } + + if (pc->ap_num < 0) { + Jim_SetResultString(goi->interp, "AP number not configured", -1); + return JIM_ERR; + } + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, (int)pc->ap_num)); + } + break; + } } - e = Jim_GetOpt_Wide(goi, &ap_num); - if (e != JIM_OK) - return e; + return JIM_OK; +} - if (target->private_config == NULL) { - pc = calloc(1, sizeof(struct adiv5_private_config)); - target->private_config = pc; - pc->ap_num = ap_num; - } +int adiv5_verify_config(struct adiv5_private_config *pc) +{ + if (pc == NULL) + return ERROR_FAIL; + if (pc->dap == NULL) + return ERROR_FAIL; - return JIM_OK; + return ERROR_OK; } + COMMAND_HANDLER(handle_dap_info_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t apsel; switch (CMD_ARGC) { @@ -1494,10 +1555,7 @@ COMMAND_HANDLER(handle_dap_info_command) COMMAND_HANDLER(dap_baseaddr_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t apsel, baseaddr; int retval; @@ -1534,10 +1592,7 @@ COMMAND_HANDLER(dap_baseaddr_command) COMMAND_HANDLER(dap_memaccess_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t memaccess_tck; switch (CMD_ARGC) { @@ -1560,17 +1615,13 @@ COMMAND_HANDLER(dap_memaccess_command) COMMAND_HANDLER(dap_apsel_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - - uint32_t apsel, apid; - int retval; + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); + uint32_t apsel; switch (CMD_ARGC) { case 0: - apsel = dap->apsel; - break; + command_print(CMD_CTX, "%" PRIi32, dap->apsel); + return ERROR_OK; case 1: COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); /* AP address is in bits 31:24 of DP_SELECT */ @@ -1582,42 +1633,40 @@ COMMAND_HANDLER(dap_apsel_command) } dap->apsel = apsel; - - retval = dap_queue_ap_read(dap_ap(dap, apsel), AP_REG_IDR, &apid); - if (retval != ERROR_OK) - return retval; - retval = dap_run(dap); - if (retval != ERROR_OK) - return retval; - - command_print(CMD_CTX, "ap %" PRIi32 " selected, identification register 0x%8.8" PRIx32, - apsel, apid); - - return retval; + return ERROR_OK; } COMMAND_HANDLER(dap_apcsw_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - - uint32_t apcsw = dap->ap[dap->apsel].csw_default, sprot = 0; + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); + uint32_t apcsw = dap->ap[dap->apsel].csw_default; + uint32_t csw_val, csw_mask; switch (CMD_ARGC) { case 0: - command_print(CMD_CTX, "apsel %" PRIi32 " selected, csw 0x%8.8" PRIx32, - (dap->apsel), apcsw); - break; + command_print(CMD_CTX, "ap %" PRIi32 " selected, csw 0x%8.8" PRIx32, + dap->apsel, apcsw); + return ERROR_OK; case 1: - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], sprot); - /* AP address is in bits 31:24 of DP_SELECT */ - if (sprot > 1) - return ERROR_COMMAND_SYNTAX_ERROR; - if (sprot) - apcsw |= CSW_SPROT; + if (strcmp(CMD_ARGV[0], "default") == 0) + csw_val = CSW_DEFAULT; else - apcsw &= ~CSW_SPROT; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], csw_val); + + if (csw_val & (CSW_SIZE_MASK | CSW_ADDRINC_MASK)) { + LOG_ERROR("CSW value cannot include 'Size' and 'AddrInc' bit-fields"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + apcsw = csw_val; + break; + case 2: + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], csw_val); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], csw_mask); + if (csw_mask & (CSW_SIZE_MASK | CSW_ADDRINC_MASK)) { + LOG_ERROR("CSW mask cannot include 'Size' and 'AddrInc' bit-fields"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + apcsw = (apcsw & ~csw_mask) | (csw_val & csw_mask); break; default: return ERROR_COMMAND_SYNTAX_ERROR; @@ -1631,10 +1680,7 @@ COMMAND_HANDLER(dap_apcsw_command) COMMAND_HANDLER(dap_apid_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t apsel, apid; int retval; @@ -1666,11 +1712,9 @@ COMMAND_HANDLER(dap_apid_command) COMMAND_HANDLER(dap_apreg_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t apsel, reg, value; + struct adiv5_ap *ap; int retval; if (CMD_ARGC < 2 || CMD_ARGC > 3) @@ -1680,6 +1724,7 @@ COMMAND_HANDLER(dap_apreg_command) /* AP address is in bits 31:24 of DP_SELECT */ if (apsel >= 256) return ERROR_COMMAND_SYNTAX_ERROR; + ap = dap_ap(dap, apsel); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg); if (reg >= 256 || (reg & 3)) @@ -1687,9 +1732,21 @@ COMMAND_HANDLER(dap_apreg_command) if (CMD_ARGC == 3) { COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); - retval = dap_queue_ap_write(dap_ap(dap, apsel), reg, value); + switch (reg) { + case MEM_AP_REG_CSW: + ap->csw_default = 0; /* invalid, force write */ + retval = mem_ap_setup_csw(ap, value); + break; + case MEM_AP_REG_TAR: + ap->tar_valid = false; /* invalid, force write */ + retval = mem_ap_setup_tar(ap, value); + break; + default: + retval = dap_queue_ap_write(ap, reg, value); + break; + } } else { - retval = dap_queue_ap_read(dap_ap(dap, apsel), reg, &value); + retval = dap_queue_ap_read(ap, reg, &value); } if (retval == ERROR_OK) retval = dap_run(dap); @@ -1703,12 +1760,40 @@ COMMAND_HANDLER(dap_apreg_command) return retval; } -COMMAND_HANDLER(dap_ti_be_32_quirks_command) +COMMAND_HANDLER(dap_dpreg_command) { - struct target *target = get_current_target(CMD_CTX); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); + uint32_t reg, value; + int retval; + if (CMD_ARGC < 1 || CMD_ARGC > 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], reg); + if (reg >= 256 || (reg & 3)) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + retval = dap_queue_dp_write(dap, reg, value); + } else { + retval = dap_queue_dp_read(dap, reg, &value); + } + if (retval == ERROR_OK) + retval = dap_run(dap); + + if (retval != ERROR_OK) + return retval; + + if (CMD_ARGC == 1) + command_print(CMD_CTX, "0x%08" PRIx32, value); + + return retval; +} + +COMMAND_HANDLER(dap_ti_be_32_quirks_command) +{ + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t enable = dap->ti_be_32_quirks; switch (CMD_ARGC) { @@ -1729,7 +1814,7 @@ COMMAND_HANDLER(dap_ti_be_32_quirks_command) return 0; } -static const struct command_registration dap_commands[] = { +const struct command_registration dap_instance_commands[] = { { .name = "info", .handler = handle_dap_info_command, @@ -1741,7 +1826,7 @@ static const struct command_registration dap_commands[] = { { .name = "apsel", .handler = dap_apsel_command, - .mode = COMMAND_EXEC, + .mode = COMMAND_ANY, .help = "Set the currently selected AP (default 0) " "and display the result", .usage = "[ap_num]", @@ -1749,9 +1834,9 @@ static const struct command_registration dap_commands[] = { { .name = "apcsw", .handler = dap_apcsw_command, - .mode = COMMAND_EXEC, - .help = "Set csw access bit ", - .usage = "[sprot]", + .mode = COMMAND_ANY, + .help = "Set CSW default bits", + .usage = "[value [mask]]", }, { @@ -1771,6 +1856,14 @@ static const struct command_registration dap_commands[] = { .usage = "ap_num reg [value]", }, { + .name = "dpreg", + .handler = dap_dpreg_command, + .mode = COMMAND_EXEC, + .help = "read/write a register from DP " + "(reg is byte address (bank << 4 | reg) of a word register, like 0 4 8...)", + .usage = "reg [value]", + }, + { .name = "baseaddr", .handler = dap_baseaddr_command, .mode = COMMAND_EXEC, @@ -1795,14 +1888,3 @@ static const struct command_registration dap_commands[] = { }, COMMAND_REGISTRATION_DONE }; - -const struct command_registration dap_command_handlers[] = { - { - .name = "dap", - .mode = COMMAND_EXEC, - .help = "DAP command group", - .usage = "", - .chain = dap_commands, - }, - COMMAND_REGISTRATION_DONE -}; diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index 657427b..883ac8b 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -112,13 +112,16 @@ #define CSW_ADDRINC_PACKED (2UL << 4) #define CSW_DEVICE_EN (1UL << 6) #define CSW_TRIN_PROG (1UL << 7) +/* all fields in bits 12 and above are implementation-defined! */ #define CSW_SPIDEN (1UL << 23) -/* 30:24 - implementation-defined! */ -#define CSW_HPROT (1UL << 25) /* ? */ -#define CSW_MASTER_DEBUG (1UL << 29) /* ? */ +#define CSW_HPROT1 (1UL << 25) /* AHB: Privileged */ +#define CSW_MASTER_DEBUG (1UL << 29) /* AHB: set HMASTER signals to AHB-AP ID */ #define CSW_SPROT (1UL << 30) #define CSW_DBGSWENABLE (1UL << 31) +/* initial value of csw_default used for MEM-AP transfers */ +#define CSW_DEFAULT (CSW_HPROT1 | CSW_MASTER_DEBUG | CSW_DBGSWENABLE) + /* Fields of the MEM-AP's IDR register */ #define IDR_REV (0xFUL << 28) #define IDR_JEP106 (0x7FFUL << 17) @@ -244,6 +247,10 @@ struct adiv5_dap { * should be performed before the next access. */ bool do_reconnect; + + /** Flag saying whether to ignore the syspwrupack flag in DAP. Some devices + * do not set this bit until later in the bringup sequence */ + bool ignore_syspwrupack; }; /** @@ -254,6 +261,8 @@ struct adiv5_dap { * available until run(). */ struct dap_ops { + /** connect operation for SWD */ + int (*connect)(struct adiv5_dap *dap); /** DP register read. */ int (*queue_dp_read)(struct adiv5_dap *dap, unsigned reg, uint32_t *data); @@ -277,6 +286,9 @@ struct dap_ops { /** Executes all queued DAP operations but doesn't check * sticky error conditions */ int (*sync)(struct adiv5_dap *dap); + + /** Optional; called at OpenOCD exit */ + void (*quit)(struct adiv5_dap *dap); }; /* @@ -473,9 +485,6 @@ int mem_ap_read_buf_noincr(struct adiv5_ap *ap, int mem_ap_write_buf_noincr(struct adiv5_ap *ap, const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address); -/* Create DAP struct */ -struct adiv5_dap *dap_init(void); - /* Initialisation of the debug system, power domains and registers */ int dap_dp_init(struct adiv5_dap *dap); int mem_ap_init(struct adiv5_ap *ap); @@ -509,12 +518,24 @@ int dap_to_swd(struct target *target); /* Put debug link into JTAG mode */ int dap_to_jtag(struct target *target); -extern const struct command_registration dap_command_handlers[]; +extern const struct command_registration dap_instance_commands[]; + +struct arm_dap_object; +extern struct adiv5_dap *dap_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o); +extern struct adiv5_dap *adiv5_get_dap(struct arm_dap_object *obj); +extern int dap_info_command(struct command_context *cmd_ctx, + struct adiv5_ap *ap); +extern int dap_register_commands(struct command_context *cmd_ctx); +extern const char *adiv5_dap_name(struct adiv5_dap *self); +extern const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self); +extern int dap_cleanup_all(void); struct adiv5_private_config { int ap_num; + struct adiv5_dap *dap; }; +extern int adiv5_verify_config(struct adiv5_private_config *pc); extern int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi); #endif /* OPENOCD_TARGET_ARM_ADI_V5_H */ diff --git a/src/target/arm_cti.c b/src/target/arm_cti.c index 75169b2..0d117e7 100644 --- a/src/target/arm_cti.c +++ b/src/target/arm_cti.c @@ -27,21 +27,47 @@ #include "target/arm_cti.h" #include "target/target.h" #include "helper/time_support.h" +#include "helper/list.h" +#include "helper/command.h" struct arm_cti { - uint32_t base; + target_addr_t base; struct adiv5_ap *ap; }; -struct arm_cti *arm_cti_create(struct adiv5_ap *ap, uint32_t base) +struct arm_cti_object { + struct list_head lh; + struct arm_cti cti; + int ap_num; + char *name; +}; + +static LIST_HEAD(all_cti); + +const char *arm_cti_name(struct arm_cti *self) +{ + struct arm_cti_object *obj = container_of(self, struct arm_cti_object, cti); + return obj->name; +} + +struct arm_cti *cti_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o) { - struct arm_cti *self = calloc(1, sizeof(struct arm_cti)); - if (!self) - return NULL; + struct arm_cti_object *obj = NULL; + const char *name; + bool found = false; - self->base = base; - self->ap = ap; - return self; + name = Jim_GetString(o, NULL); + + list_for_each_entry(obj, &all_cti, lh) { + if (!strcmp(name, obj->name)) { + found = true; + break; + } + } + + if (found) + return &obj->cti; + return NULL; } static int arm_cti_mod_reg_bits(struct arm_cti *self, unsigned int reg, uint32_t mask, uint32_t value) @@ -146,3 +172,408 @@ int arm_cti_clear_channel(struct arm_cti *self, uint32_t channel) return arm_cti_write_reg(self, CTI_APPCLEAR, CTI_CHNL(channel)); } + +static uint32_t cti_regs[26]; + +static const struct { + uint32_t offset; + const char *label; + uint32_t *p_val; +} cti_names[] = { + { CTI_CTR, "CTR", &cti_regs[0] }, + { CTI_GATE, "GATE", &cti_regs[1] }, + { CTI_INEN0, "INEN0", &cti_regs[2] }, + { CTI_INEN1, "INEN1", &cti_regs[3] }, + { CTI_INEN2, "INEN2", &cti_regs[4] }, + { CTI_INEN3, "INEN3", &cti_regs[5] }, + { CTI_INEN4, "INEN4", &cti_regs[6] }, + { CTI_INEN5, "INEN5", &cti_regs[7] }, + { CTI_INEN6, "INEN6", &cti_regs[8] }, + { CTI_INEN7, "INEN7", &cti_regs[9] }, + { CTI_INEN8, "INEN8", &cti_regs[10] }, + { CTI_OUTEN0, "OUTEN0", &cti_regs[11] }, + { CTI_OUTEN1, "OUTEN1", &cti_regs[12] }, + { CTI_OUTEN2, "OUTEN2", &cti_regs[13] }, + { CTI_OUTEN3, "OUTEN3", &cti_regs[14] }, + { CTI_OUTEN4, "OUTEN4", &cti_regs[15] }, + { CTI_OUTEN5, "OUTEN5", &cti_regs[16] }, + { CTI_OUTEN6, "OUTEN6", &cti_regs[17] }, + { CTI_OUTEN7, "OUTEN7", &cti_regs[18] }, + { CTI_OUTEN8, "OUTEN8", &cti_regs[19] }, + { CTI_TRIN_STATUS, "TRIN", &cti_regs[20] }, + { CTI_TROUT_STATUS, "TROUT", &cti_regs[21] }, + { CTI_CHIN_STATUS, "CHIN", &cti_regs[22] }, + { CTI_CHOU_STATUS, "CHOUT", &cti_regs[23] }, + { CTI_APPSET, "APPSET", &cti_regs[24] }, + { CTI_APPCLEAR, "APPCLR", &cti_regs[25] }, +}; + +static int cti_find_reg_offset(const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cti_names); i++) { + if (!strcmp(name, cti_names[i].label)) + return cti_names[i].offset; + } + return -1; +} + +int arm_cti_cleanup_all(void) +{ + struct arm_cti_object *obj, *tmp; + + list_for_each_entry_safe(obj, tmp, &all_cti, lh) { + free(obj->name); + free(obj); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_cti_dump) +{ + struct arm_cti_object *obj = CMD_DATA; + struct arm_cti *cti = &obj->cti; + int retval = ERROR_OK; + + for (int i = 0; (retval == ERROR_OK) && (i < (int)ARRAY_SIZE(cti_names)); i++) + retval = mem_ap_read_u32(cti->ap, + cti->base + cti_names[i].offset, cti_names[i].p_val); + + if (retval == ERROR_OK) + retval = dap_run(cti->ap->dap); + + if (retval != ERROR_OK) + return JIM_ERR; + + for (int i = 0; i < (int)ARRAY_SIZE(cti_names); i++) + command_print(CMD_CTX, "%8.8s (0x%04"PRIx32") 0x%08"PRIx32, + cti_names[i].label, cti_names[i].offset, *cti_names[i].p_val); + + return JIM_OK; +} + +COMMAND_HANDLER(handle_cti_enable) +{ + struct arm_cti_object *obj = CMD_DATA; + Jim_Interp *interp = CMD_CTX->interp; + struct arm_cti *cti = &obj->cti; + bool on_off; + + if (CMD_ARGC != 1) { + Jim_SetResultString(interp, "wrong number of args", -1); + return ERROR_FAIL; + } + + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], on_off); + + return arm_cti_enable(cti, on_off); +} + +COMMAND_HANDLER(handle_cti_testmode) +{ + struct arm_cti_object *obj = CMD_DATA; + Jim_Interp *interp = CMD_CTX->interp; + struct arm_cti *cti = &obj->cti; + bool on_off; + + if (CMD_ARGC != 1) { + Jim_SetResultString(interp, "wrong number of args", -1); + return ERROR_FAIL; + } + + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], on_off); + + return arm_cti_write_reg(cti, 0xf00, on_off ? 0x1 : 0x0); +} + +COMMAND_HANDLER(handle_cti_write) +{ + struct arm_cti_object *obj = CMD_DATA; + Jim_Interp *interp = CMD_CTX->interp; + struct arm_cti *cti = &obj->cti; + int offset; + uint32_t value; + + if (CMD_ARGC != 2) { + Jim_SetResultString(interp, "Wrong numer of args", -1); + return ERROR_FAIL; + } + + offset = cti_find_reg_offset(CMD_ARGV[0]); + if (offset < 0) + return ERROR_FAIL; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + return arm_cti_write_reg(cti, offset, value); +} + +COMMAND_HANDLER(handle_cti_read) +{ + struct arm_cti_object *obj = CMD_DATA; + Jim_Interp *interp = CMD_CTX->interp; + struct arm_cti *cti = &obj->cti; + int offset; + int retval; + uint32_t value; + + if (CMD_ARGC != 1) { + Jim_SetResultString(interp, "Wrong numer of args", -1); + return ERROR_FAIL; + } + + offset = cti_find_reg_offset(CMD_ARGV[0]); + if (offset < 0) + return ERROR_FAIL; + + retval = arm_cti_read_reg(cti, offset, &value); + if (retval != ERROR_OK) + return retval; + + command_print(CMD_CTX, "0x%08"PRIx32, value); + + return ERROR_OK; +} + +static const struct command_registration cti_instance_command_handlers[] = { + { + .name = "dump", + .mode = COMMAND_EXEC, + .handler = handle_cti_dump, + .help = "dump CTI registers", + .usage = "", + }, + { + .name = "enable", + .mode = COMMAND_EXEC, + .handler = handle_cti_enable, + .help = "enable or disable the CTI", + .usage = "'on'|'off'", + }, + { + .name = "testmode", + .mode = COMMAND_EXEC, + .handler = handle_cti_testmode, + .help = "enable or disable integration test mode", + .usage = "'on'|'off'", + }, + { + .name = "write", + .mode = COMMAND_EXEC, + .handler = handle_cti_write, + .help = "write to a CTI register", + .usage = "register_name value", + }, + { + .name = "read", + .mode = COMMAND_EXEC, + .handler = handle_cti_read, + .help = "read a CTI register", + .usage = "register_name", + }, + COMMAND_REGISTRATION_DONE +}; + +enum cti_cfg_param { + CFG_DAP, + CFG_AP_NUM, + CFG_CTIBASE +}; + +static const Jim_Nvp nvp_config_opts[] = { + { .name = "-dap", .value = CFG_DAP }, + { .name = "-ctibase", .value = CFG_CTIBASE }, + { .name = "-ap-num", .value = CFG_AP_NUM }, + { .name = NULL, .value = -1 } +}; + +static int cti_configure(Jim_GetOptInfo *goi, struct arm_cti_object *cti) +{ + struct adiv5_dap *dap = NULL; + Jim_Nvp *n; + jim_wide w; + int e; + + /* parse config or cget options ... */ + while (goi->argc > 0) { + Jim_SetEmptyResult(goi->interp); + + e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n); + if (e != JIM_OK) { + Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0); + return e; + } + switch (n->value) { + case CFG_DAP: { + Jim_Obj *o_t; + e = Jim_GetOpt_Obj(goi, &o_t); + if (e != JIM_OK) + return e; + dap = dap_instance_by_jim_obj(goi->interp, o_t); + if (dap == NULL) { + Jim_SetResultString(goi->interp, "-dap is invalid", -1); + return JIM_ERR; + } + /* loop for more */ + break; + } + case CFG_CTIBASE: + e = Jim_GetOpt_Wide(goi, &w); + if (e != JIM_OK) + return e; + cti->cti.base = (uint32_t)w; + /* loop for more */ + break; + + case CFG_AP_NUM: + e = Jim_GetOpt_Wide(goi, &w); + if (e != JIM_OK) + return e; + cti->ap_num = (uint32_t)w; + } + } + + if (dap == NULL) { + Jim_SetResultString(goi->interp, "-dap required when creating CTI", -1); + return JIM_ERR; + } + + cti->cti.ap = dap_ap(dap, cti->ap_num); + + return JIM_OK; +} + +static int cti_create(Jim_GetOptInfo *goi) +{ + struct command_context *cmd_ctx; + static struct arm_cti_object *cti; + Jim_Obj *new_cmd; + Jim_Cmd *cmd; + const char *cp; + int e; + + cmd_ctx = current_command_context(goi->interp); + assert(cmd_ctx != NULL); + + if (goi->argc < 3) { + Jim_WrongNumArgs(goi->interp, 1, goi->argv, "?name? ..options..."); + return JIM_ERR; + } + /* COMMAND */ + Jim_GetOpt_Obj(goi, &new_cmd); + /* does this command exist? */ + cmd = Jim_GetCommand(goi->interp, new_cmd, JIM_ERRMSG); + if (cmd) { + cp = Jim_GetString(new_cmd, NULL); + Jim_SetResultFormatted(goi->interp, "Command: %s Exists", cp); + return JIM_ERR; + } + + /* Create it */ + cti = calloc(1, sizeof(struct arm_cti_object)); + if (cti == NULL) + return JIM_ERR; + + e = cti_configure(goi, cti); + if (e != JIM_OK) { + free(cti); + return e; + } + + cp = Jim_GetString(new_cmd, NULL); + cti->name = strdup(cp); + + /* now - create the new cti name command */ + const struct command_registration cti_subcommands[] = { + { + .chain = cti_instance_command_handlers, + }, + COMMAND_REGISTRATION_DONE + }; + const struct command_registration cti_commands[] = { + { + .name = cp, + .mode = COMMAND_ANY, + .help = "cti instance command group", + .usage = "", + .chain = cti_subcommands, + }, + COMMAND_REGISTRATION_DONE + }; + e = register_commands(cmd_ctx, NULL, cti_commands); + if (ERROR_OK != e) + return JIM_ERR; + + struct command *c = command_find_in_context(cmd_ctx, cp); + assert(c); + command_set_handler_data(c, cti); + + list_add_tail(&cti->lh, &all_cti); + + return (ERROR_OK == e) ? JIM_OK : JIM_ERR; +} + +static int jim_cti_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_GetOptInfo goi; + Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1); + if (goi.argc < 2) { + Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv, + "<name> [<cti_options> ...]"); + return JIM_ERR; + } + return cti_create(&goi); +} + +static int jim_cti_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct arm_cti_object *obj; + + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, "Too many parameters"); + return JIM_ERR; + } + Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); + list_for_each_entry(obj, &all_cti, lh) { + Jim_ListAppendElement(interp, Jim_GetResult(interp), + Jim_NewStringObj(interp, obj->name, -1)); + } + return JIM_OK; +} + + +static const struct command_registration cti_subcommand_handlers[] = { + { + .name = "create", + .mode = COMMAND_ANY, + .jim_handler = jim_cti_create, + .usage = "name '-chain-position' name [options ...]", + .help = "Creates a new CTI object", + }, + { + .name = "names", + .mode = COMMAND_ANY, + .jim_handler = jim_cti_names, + .usage = "", + .help = "Lists all registered CTI objects by name", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration cti_command_handlers[] = { + { + .name = "cti", + .mode = COMMAND_CONFIG, + .help = "CTI commands", + .chain = cti_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +int cti_register_commands(struct command_context *cmd_ctx) +{ + return register_commands(cmd_ctx, NULL, cti_command_handlers); +} + diff --git a/src/target/arm_cti.h b/src/target/arm_cti.h index 99724c4..7c4f7eb 100644 --- a/src/target/arm_cti.h +++ b/src/target/arm_cti.h @@ -34,6 +34,7 @@ #define CTI_INEN5 0x34 #define CTI_INEN6 0x38 #define CTI_INEN7 0x3C +#define CTI_INEN8 0x40 #define CTI_INEN(n) (0x20 + 4 * n) #define CTI_OUTEN0 0xA0 #define CTI_OUTEN1 0xA4 @@ -43,6 +44,7 @@ #define CTI_OUTEN5 0xB4 #define CTI_OUTEN6 0xB8 #define CTI_OUTEN7 0xBC +#define CTI_OUTEN8 0xC0 #define CTI_OUTEN(n) (0xA0 + 4 * n) #define CTI_TRIN_STATUS 0x130 #define CTI_TROUT_STATUS 0x134 @@ -58,8 +60,10 @@ /* forward-declare arm_cti struct */ struct arm_cti; +struct adiv5_ap; -extern struct arm_cti *arm_cti_create(struct adiv5_ap *ap, uint32_t base); +extern const char *arm_cti_name(struct arm_cti *self); +extern struct arm_cti *cti_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o); extern int arm_cti_enable(struct arm_cti *self, bool enable); extern int arm_cti_ack_events(struct arm_cti *self, uint32_t event); extern int arm_cti_gate_channel(struct arm_cti *self, uint32_t channel); @@ -69,5 +73,7 @@ extern int arm_cti_read_reg(struct arm_cti *self, unsigned int reg, uint32_t *va extern int arm_cti_pulse_channel(struct arm_cti *self, uint32_t channel); extern int arm_cti_set_channel(struct arm_cti *self, uint32_t channel); extern int arm_cti_clear_channel(struct arm_cti *self, uint32_t channel); +extern int arm_cti_cleanup_all(void); +extern int cti_register_commands(struct command_context *cmd_ctx); #endif /* OPENOCD_TARGET_ARM_CTI_H */ diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c new file mode 100644 index 0000000..3be4d71 --- /dev/null +++ b/src/target/arm_dap.c @@ -0,0 +1,378 @@ +/*************************************************************************** + * Copyright (C) 2016 by Matthias Welwarsky * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdint.h> +#include "target/arm_adi_v5.h" +#include "target/arm.h" +#include "helper/list.h" +#include "helper/command.h" +#include "transport/transport.h" +#include "jtag/interface.h" + +static LIST_HEAD(all_dap); + +extern const struct dap_ops swd_dap_ops; +extern const struct dap_ops jtag_dp_ops; +extern struct jtag_interface *jtag_interface; + +/* DAP command support */ +struct arm_dap_object { + struct list_head lh; + struct adiv5_dap dap; + char *name; + const struct swd_driver *swd; +}; + +static void dap_instance_init(struct adiv5_dap *dap) +{ + int i; + /* Set up with safe defaults */ + for (i = 0; i <= 255; i++) { + dap->ap[i].dap = dap; + dap->ap[i].ap_num = i; + /* memaccess_tck max is 255 */ + dap->ap[i].memaccess_tck = 255; + /* Number of bits for tar autoincrement, impl. dep. at least 10 */ + dap->ap[i].tar_autoincr_block = (1<<10); + /* default CSW value */ + dap->ap[i].csw_default = CSW_DEFAULT; + } + INIT_LIST_HEAD(&dap->cmd_journal); +} + +const char *adiv5_dap_name(struct adiv5_dap *self) +{ + struct arm_dap_object *obj = container_of(self, struct arm_dap_object, dap); + return obj->name; +} + +const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self) +{ + struct arm_dap_object *obj = container_of(self, struct arm_dap_object, dap); + return obj->swd; +} + +struct adiv5_dap *adiv5_get_dap(struct arm_dap_object *obj) +{ + return &obj->dap; +} +struct adiv5_dap *dap_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o) +{ + struct arm_dap_object *obj = NULL; + const char *name; + bool found = false; + + name = Jim_GetString(o, NULL); + + list_for_each_entry(obj, &all_dap, lh) { + if (!strcmp(name, obj->name)) { + found = true; + break; + } + } + + if (found) + return &obj->dap; + return NULL; +} + +static int dap_init_all(void) +{ + struct arm_dap_object *obj; + int retval; + + LOG_DEBUG("Initializing all DAPs ..."); + + list_for_each_entry(obj, &all_dap, lh) { + struct adiv5_dap *dap = &obj->dap; + + /* with hla, dap is just a dummy */ + if (transport_is_hla()) + continue; + + /* skip taps that are disabled */ + if (!dap->tap->enabled) + continue; + + if (transport_is_swd()) { + dap->ops = &swd_dap_ops; + obj->swd = jtag_interface->swd; + } else + dap->ops = &jtag_dp_ops; + + retval = dap->ops->connect(dap); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +int dap_cleanup_all(void) +{ + struct arm_dap_object *obj, *tmp; + struct adiv5_dap *dap; + + list_for_each_entry_safe(obj, tmp, &all_dap, lh) { + dap = &obj->dap; + if (dap->ops && dap->ops->quit) + dap->ops->quit(dap); + + free(obj->name); + free(obj); + } + + return ERROR_OK; +} + +enum dap_cfg_param { + CFG_CHAIN_POSITION, + CFG_IGNORE_SYSPWRUPACK, +}; + +static const Jim_Nvp nvp_config_opts[] = { + { .name = "-chain-position", .value = CFG_CHAIN_POSITION }, + { .name = "-ignore-syspwrupack", .value = CFG_IGNORE_SYSPWRUPACK }, + { .name = NULL, .value = -1 } +}; + +static int dap_configure(Jim_GetOptInfo *goi, struct arm_dap_object *dap) +{ + struct jtag_tap *tap = NULL; + Jim_Nvp *n; + int e; + + /* parse config or cget options ... */ + while (goi->argc > 0) { + Jim_SetEmptyResult(goi->interp); + + e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n); + if (e != JIM_OK) { + Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0); + return e; + } + switch (n->value) { + case CFG_CHAIN_POSITION: { + Jim_Obj *o_t; + e = Jim_GetOpt_Obj(goi, &o_t); + if (e != JIM_OK) + return e; + tap = jtag_tap_by_jim_obj(goi->interp, o_t); + if (tap == NULL) { + Jim_SetResultString(goi->interp, "-chain-position is invalid", -1); + return JIM_ERR; + } + /* loop for more */ + break; + } + case CFG_IGNORE_SYSPWRUPACK: + dap->dap.ignore_syspwrupack = true; + break; + default: + break; + } + } + + if (tap == NULL) { + Jim_SetResultString(goi->interp, "-chain-position required when creating DAP", -1); + return JIM_ERR; + } + + dap_instance_init(&dap->dap); + dap->dap.tap = tap; + + return JIM_OK; +} + +static int dap_create(Jim_GetOptInfo *goi) +{ + struct command_context *cmd_ctx; + static struct arm_dap_object *dap; + Jim_Obj *new_cmd; + Jim_Cmd *cmd; + const char *cp; + int e; + + cmd_ctx = current_command_context(goi->interp); + assert(cmd_ctx != NULL); + + if (goi->argc < 3) { + Jim_WrongNumArgs(goi->interp, 1, goi->argv, "?name? ..options..."); + return JIM_ERR; + } + /* COMMAND */ + Jim_GetOpt_Obj(goi, &new_cmd); + /* does this command exist? */ + cmd = Jim_GetCommand(goi->interp, new_cmd, JIM_ERRMSG); + if (cmd) { + cp = Jim_GetString(new_cmd, NULL); + Jim_SetResultFormatted(goi->interp, "Command: %s Exists", cp); + return JIM_ERR; + } + + /* Create it */ + dap = calloc(1, sizeof(struct arm_dap_object)); + if (dap == NULL) + return JIM_ERR; + + e = dap_configure(goi, dap); + if (e != JIM_OK) { + free(dap); + return e; + } + + cp = Jim_GetString(new_cmd, NULL); + dap->name = strdup(cp); + + struct command_registration dap_commands[] = { + { + .name = cp, + .mode = COMMAND_ANY, + .help = "dap instance command group", + .usage = "", + .chain = dap_instance_commands, + }, + COMMAND_REGISTRATION_DONE + }; + + /* don't expose the instance commands when using hla */ + if (transport_is_hla()) + dap_commands[0].chain = NULL; + + e = register_commands(cmd_ctx, NULL, dap_commands); + if (ERROR_OK != e) + return JIM_ERR; + + struct command *c = command_find_in_context(cmd_ctx, cp); + assert(c); + command_set_handler_data(c, dap); + + list_add_tail(&dap->lh, &all_dap); + + return (ERROR_OK == e) ? JIM_OK : JIM_ERR; +} + +static int jim_dap_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_GetOptInfo goi; + Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1); + if (goi.argc < 2) { + Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv, + "<name> [<dap_options> ...]"); + return JIM_ERR; + } + return dap_create(&goi); +} + +static int jim_dap_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct arm_dap_object *obj; + + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, "Too many parameters"); + return JIM_ERR; + } + Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); + list_for_each_entry(obj, &all_dap, lh) { + Jim_ListAppendElement(interp, Jim_GetResult(interp), + Jim_NewStringObj(interp, obj->name, -1)); + } + return JIM_OK; +} + +COMMAND_HANDLER(handle_dap_init) +{ + return dap_init_all(); +} + +COMMAND_HANDLER(handle_dap_info_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct arm *arm = target_to_arm(target); + struct adiv5_dap *dap = arm->dap; + uint32_t apsel; + + switch (CMD_ARGC) { + case 0: + apsel = dap->apsel; + break; + case 1: + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel); + if (apsel >= 256) + return ERROR_COMMAND_SYNTAX_ERROR; + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return dap_info_command(CMD_CTX, &dap->ap[apsel]); +} + +static const struct command_registration dap_subcommand_handlers[] = { + { + .name = "create", + .mode = COMMAND_ANY, + .jim_handler = jim_dap_create, + .usage = "name '-chain-position' name", + .help = "Creates a new DAP instance", + }, + { + .name = "names", + .mode = COMMAND_ANY, + .jim_handler = jim_dap_names, + .usage = "", + .help = "Lists all registered DAP instances by name", + }, + { + .name = "init", + .mode = COMMAND_ANY, + .handler = handle_dap_init, + .usage = "", + .help = "Initialize all registered DAP instances" + }, + { + .name = "info", + .handler = handle_dap_info_command, + .mode = COMMAND_EXEC, + .help = "display ROM table for MEM-AP of current target " + "(default currently selected AP)", + .usage = "[ap_num]", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration dap_commands[] = { + { + .name = "dap", + .mode = COMMAND_CONFIG, + .help = "DAP commands", + .chain = dap_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +int dap_register_commands(struct command_context *cmd_ctx) +{ + return register_commands(cmd_ctx, NULL, dap_commands); +} diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index ef69a20..8eb8194 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -118,15 +118,78 @@ static int evaluate_pld(uint32_t opcode, uint32_t address, struct arm_instruction *instruction) { /* PLD */ - if ((opcode & 0x0d70f000) == 0x0550f000) { + if ((opcode & 0x0d30f000) == 0x0510f000) { + uint8_t Rn; + uint8_t U; + unsigned offset; + instruction->type = ARM_PLD; + Rn = (opcode & 0xf0000) >> 16; + U = (opcode & 0x00800000) >> 23; + if (Rn == 0xf) { + /* literal */ + offset = opcode & 0x0fff; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD %s%d", + address, opcode, U ? "" : "-", offset); + } else { + uint8_t I, R; - snprintf(instruction->text, - 128, - "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD ...TODO...", - address, - opcode); + I = (opcode & 0x02000000) >> 25; + R = (opcode & 0x00400000) >> 22; + + if (I) { + /* register PLD{W} [<Rn>,+/-<Rm>{, <shift>}] */ + offset = (opcode & 0x0F80) >> 7; + uint8_t Rm; + Rm = opcode & 0xf; + + if (offset == 0) { + /* No shift */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d]", + address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm); + + } else { + uint8_t shift; + shift = (opcode & 0x60) >> 5; + if (shift == 0x0) { + /* LSL */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSL #0x%x)", + address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset); + } else if (shift == 0x1) { + /* LSR */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSR #0x%x)", + address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset); + } else if (shift == 0x2) { + /* ASR */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ASR #0x%x)", + address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset); + } else if (shift == 0x3) { + /* ROR */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ROR #0x%x)", + address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset); + } + } + } else { + /* immediate PLD{W} [<Rn>, #+/-<imm12>] */ + offset = opcode & 0x0fff; + if (offset == 0) { + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d]", + address, opcode, R ? "" : "W", Rn); + } else { + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, #%s%d]", + address, opcode, R ? "" : "W", Rn, U ? "" : "-", offset); + } + } + } return ERROR_OK; } /* DSB */ @@ -1549,7 +1612,7 @@ static int evaluate_misc_instr(uint32_t opcode, } /* SMLAW < y> */ - if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) { + if (((opcode & 0x00600000) == 0x00200000) && (x == 0)) { uint8_t Rd, Rm, Rs, Rn; instruction->type = ARM_SMLAWy; Rd = (opcode & 0xf0000) >> 16; @@ -1571,7 +1634,7 @@ static int evaluate_misc_instr(uint32_t opcode, } /* SMUL < x><y> */ - if ((opcode & 0x00600000) == 0x00300000) { + if ((opcode & 0x00600000) == 0x00600000) { uint8_t Rd, Rm, Rs; instruction->type = ARM_SMULxy; Rd = (opcode & 0xf0000) >> 16; @@ -1592,7 +1655,7 @@ static int evaluate_misc_instr(uint32_t opcode, } /* SMULW < y> */ - if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) { + if (((opcode & 0x00600000) == 0x00200000) && (x == 1)) { uint8_t Rd, Rm, Rs; instruction->type = ARM_SMULWy; Rd = (opcode & 0xf0000) >> 16; @@ -2978,6 +3041,7 @@ static int t2ev_b_bl(uint32_t opcode, uint32_t address, case 0x4: inst = "BLX"; instruction->type = ARM_BLX; + address &= 0xfffffffc; break; case 0x5: inst = "BL"; diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c index 3e8180c..f9b30c1 100644 --- a/src/target/arm_dpm.c +++ b/src/target/arm_dpm.c @@ -131,6 +131,42 @@ int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) return retval; } +/* Read 64bit VFP registers */ +static int dpm_read_reg_u64(struct arm_dpm *dpm, struct reg *r, unsigned regnum) +{ + int retval = ERROR_FAIL; + uint32_t value_r0, value_r1; + + switch (regnum) { + case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31: + /* move from double word register to r0:r1: "vmov r0, r1, vm" + * then read r0 via dcc + */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_VMOV(1, 1, 0, ((regnum - ARM_VFP_V3_D0) >> 4), + ((regnum - ARM_VFP_V3_D0) & 0xf)), &value_r0); + /* read r1 via dcc */ + retval = dpm->instr_read_data_dcc(dpm, + ARMV4_5_MCR(14, 0, 1, 0, 5, 0), + &value_r1); + break; + default: + + break; + } + + if (retval == ERROR_OK) { + buf_set_u32(r->value, 0, 32, value_r0); + buf_set_u32(r->value + 4, 0, 32, value_r1); + r->valid = true; + r->dirty = false; + LOG_DEBUG("READ: %s, %8.8x, %8.8x", r->name, + (unsigned) value_r0, (unsigned) value_r1); + } + + return retval; +} + /* just read the register -- rely on the core mode being right */ static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) { @@ -171,6 +207,14 @@ static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) break; } break; + case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31: + return dpm_read_reg_u64(dpm, r, regnum); + break; + case ARM_VFP_V3_FPSCR: + /* "VMRS r0, FPSCR"; then return via DCC */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_VMRS(0), &value); + break; default: /* 16: "MRS r0, CPSR"; then return via DCC * 17: "MRS r0, SPSR"; then return via DCC @@ -191,6 +235,40 @@ static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) return retval; } +/* Write 64bit VFP registers */ +static int dpm_write_reg_u64(struct arm_dpm *dpm, struct reg *r, unsigned regnum) +{ + int retval = ERROR_FAIL; + uint32_t value_r0 = buf_get_u32(r->value, 0, 32); + uint32_t value_r1 = buf_get_u32(r->value + 4, 0, 32); + + switch (regnum) { + case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31: + /* write value_r1 to r1 via dcc */ + retval = dpm->instr_write_data_dcc(dpm, + ARMV4_5_MRC(14, 0, 1, 0, 5, 0), + value_r1); + /* write value_r0 to r0 via dcc then, + * move to double word register from r0:r1: "vmov vm, r0, r1" + */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_VMOV(0, 1, 0, ((regnum - ARM_VFP_V3_D0) >> 4), + ((regnum - ARM_VFP_V3_D0) & 0xf)), value_r0); + break; + default: + + break; + } + + if (retval == ERROR_OK) { + r->dirty = false; + LOG_DEBUG("WRITE: %s, %8.8x, %8.8x", r->name, + (unsigned) value_r0, (unsigned) value_r1); + } + + return retval; +} + /* just write the register -- rely on the core mode being right */ static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) { @@ -208,6 +286,14 @@ static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) * read r0 from DCC; then "MOV pc, r0" */ retval = dpm->instr_write_data_r0(dpm, 0xe1a0f000, value); break; + case ARM_VFP_V3_D0 ... ARM_VFP_V3_D31: + return dpm_write_reg_u64(dpm, r, regnum); + break; + case ARM_VFP_V3_FPSCR: + /* move to r0 from DCC, then "VMSR FPSCR, r0" */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_VMSR(0), value); + break; default: /* 16: read r0 from DCC, then "MSR r0, CPSR_cxsf" * 17: read r0 from DCC, then "MSR r0, SPSR_cxsf" @@ -262,14 +348,16 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) if (retval != ERROR_OK) return retval; - /* read R0 first (it's used for scratch), then CPSR */ - r = arm->core_cache->reg_list + 0; - if (!r->valid) { - retval = dpm_read_reg(dpm, r, 0); - if (retval != ERROR_OK) - goto fail; + /* read R0 and R1 first (it's used for scratch), then CPSR */ + for (unsigned i = 0; i < 2; i++) { + r = arm->core_cache->reg_list + i; + if (!r->valid) { + retval = dpm_read_reg(dpm, r, i); + if (retval != ERROR_OK) + goto fail; + } + r->dirty = true; } - r->dirty = true; retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRS(0, 0), &cpsr); if (retval != ERROR_OK) @@ -279,7 +367,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) arm_set_cpsr(arm, cpsr); /* REVISIT we can probably avoid reading R1..R14, saving time... */ - for (unsigned i = 1; i < 16; i++) { + for (unsigned i = 2; i < 16; i++) { r = arm_reg_current(arm, i); if (r->valid) continue; @@ -412,8 +500,8 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) did_write = false; - /* check everything except our scratch register R0 */ - for (unsigned i = 1; i < cache->num_regs; i++) { + /* check everything except our scratch registers R0 and R1 */ + for (unsigned i = 2; i < cache->num_regs; i++) { struct arm_reg *r; unsigned regnum; @@ -499,11 +587,13 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) goto done; arm->pc->dirty = false; - /* flush R0 -- it's *very* dirty by now */ - retval = dpm_write_reg(dpm, &cache->reg_list[0], 0); - if (retval != ERROR_OK) - goto done; - cache->reg_list[0].dirty = false; + /* flush R0 and R1 (our scratch registers) */ + for (unsigned i = 0; i < 2; i++) { + retval = dpm_write_reg(dpm, &cache->reg_list[i], i); + if (retval != ERROR_OK) + goto done; + cache->reg_list[i].dirty = false; + } /* (void) */ dpm->finish(dpm); done: @@ -540,6 +630,7 @@ static enum arm_mode dpm_mapmode(struct arm *arm, /* r13/sp, and r14/lr are always shadowed */ case 13: case 14: + case ARM_VFP_V3_D0 ... ARM_VFP_V3_FPSCR: return mode; default: LOG_WARNING("invalid register #%u", num); @@ -561,7 +652,8 @@ static int arm_dpm_read_core_reg(struct target *target, struct reg *r, struct arm_dpm *dpm = target_to_arm(target)->dpm; int retval; - if (regnum < 0 || regnum > 16) + if (regnum < 0 || (regnum > 16 && regnum < ARM_VFP_V3_D0) || + (regnum > ARM_VFP_V3_FPSCR)) return ERROR_COMMAND_SYNTAX_ERROR; if (regnum == 16) { @@ -604,7 +696,8 @@ static int arm_dpm_write_core_reg(struct target *target, struct reg *r, int retval; - if (regnum < 0 || regnum > 16) + if (regnum < 0 || (regnum > 16 && regnum < ARM_VFP_V3_D0) || + (regnum > ARM_VFP_V3_FPSCR)) return ERROR_COMMAND_SYNTAX_ERROR; if (regnum == 16) { diff --git a/src/target/arm_opcodes.h b/src/target/arm_opcodes.h index a53fee7..482abe6 100644 --- a/src/target/arm_opcodes.h +++ b/src/target/arm_opcodes.h @@ -132,6 +132,30 @@ */ #define ARMV4_5_BX(Rm) (0xe12fff10 | (Rm)) +/* Copies two words from two ARM core registers + * into a doubleword extension register, or + * from a doubleword extension register to two ARM core registers. + * See Armv7-A arch reference manual section A8.8.345 + * Rt: Arm core register 1 + * Rt2: Arm core register 2 + * Vm: The doubleword extension register + * M: m = UInt(M:Vm); + * op: to_arm_registers = (op == ‘1’); + */ +#define ARMV4_5_VMOV(op, Rt2, Rt, M, Vm) \ + (0xec400b10 | ((op) << 20) | ((Rt2) << 16) | \ + ((Rt) << 12) | ((M) << 5) | (Vm)) + +/* Moves the value of the FPSCR to an ARM core register + * Rt: Arm core register + */ +#define ARMV4_5_VMRS(Rt) (0xeef10a10 | ((Rt) << 12)) + +/* Moves the value of an ARM core register to the FPSCR. + * Rt: Arm core register + */ +#define ARMV4_5_VMSR(Rt) (0xeee10a10 | ((Rt) << 12)) + /* Store data from coprocessor to consecutive memory * See Armv7-A arch doc section A8.6.187 * P: 1=index mode (offset from Rn) diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index f31f901..9117a74 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -8,6 +8,9 @@ * Copyright (C) 2016 by Square, Inc. * * Steven Stallion <stallion@squareup.com> * * * + * Copyright (C) 2018 by Liviu Ionescu * + * <ilg@livius.net> * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -43,6 +46,7 @@ #include "arm7_9_common.h" #include "armv7m.h" #include "armv7a.h" +#include "armv8.h" #include "cortex_m.h" #include "register.h" #include "arm_opcodes.h" @@ -52,25 +56,35 @@ #include <helper/log.h> #include <sys/stat.h> -static const int open_modeflags[12] = { - O_RDONLY, - O_RDONLY | O_BINARY, - O_RDWR, - O_RDWR | O_BINARY, - O_WRONLY | O_CREAT | O_TRUNC, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - O_RDWR | O_CREAT | O_TRUNC, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - O_WRONLY | O_CREAT | O_APPEND, - O_WRONLY | O_CREAT | O_APPEND | O_BINARY, - O_RDWR | O_CREAT | O_APPEND, - O_RDWR | O_CREAT | O_APPEND | O_BINARY -}; +static int arm_semihosting_resume(struct target *target, int *retval) +{ + if (is_armv8(target_to_armv8(target))) { + struct armv8_common *armv8 = target_to_armv8(target); + if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) { + *retval = target_resume(target, 1, 0, 0, 0); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed to resume target"); + return 0; + } + } else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP) + target->debug_reason = DBG_REASON_SINGLESTEP; + } else { + *retval = target_resume(target, 1, 0, 0, 0); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed to resume target"); + return 0; + } + } + return 1; +} static int post_result(struct target *target) { struct arm *arm = target_to_arm(target); + if (!target->semihosting) + return ERROR_FAIL; + /* REVISIT this looks wrong ... ARM11 and Cortex-A8 * should work this way at least sometimes. */ @@ -79,7 +93,7 @@ static int post_result(struct target *target) uint32_t spsr; /* return value in R0 */ - buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result); + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result); arm->core_cache->reg_list[0].dirty = 1; /* LR --> PC */ @@ -100,528 +114,28 @@ static int post_result(struct target *target) if (spsr & 0x20) arm->core_state = ARM_STATE_THUMB; + } else if (is_armv8(target_to_armv8(target))) { + if (arm->core_state == ARM_STATE_AARCH64) { + /* return value in R0 */ + buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result); + arm->core_cache->reg_list[0].dirty = 1; + + uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64); + buf_set_u64(arm->pc->value, 0, 64, pc + 4); + arm->pc->dirty = 1; + } } else { /* resume execution, this will be pc+2 to skip over the * bkpt instruction */ /* return result in R0 */ - buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result); + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result); arm->core_cache->reg_list[0].dirty = 1; } return ERROR_OK; } -static int do_semihosting(struct target *target) -{ - struct arm *arm = target_to_arm(target); - struct gdb_fileio_info *fileio_info = target->fileio_info; - uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32); - uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32); - uint8_t params[16]; - int retval; - - /* - * TODO: lots of security issues are not considered yet, such as: - * - no validation on target provided file descriptors - * - no safety checks on opened/deleted/renamed file paths - * Beware the target app you use this support with. - * - * TODO: unsupported semihosting fileio operations could be - * implemented if we had a small working area at our disposal. - */ - switch ((arm->semihosting_op = r0)) { - case 0x01: /* SYS_OPEN */ - retval = target_read_memory(target, r1, 4, 3, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - uint32_t m = target_buffer_get_u32(target, params+4); - uint32_t l = target_buffer_get_u32(target, params+8); - uint8_t fn[256]; - retval = target_read_memory(target, a, 1, l, fn); - if (retval != ERROR_OK) - return retval; - fn[l] = 0; - if (arm->is_semihosting_fileio) { - if (strcmp((char *)fn, ":tt") == 0) - arm->semihosting_result = 0; - else { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "open"; - fileio_info->param_1 = a; - fileio_info->param_2 = l; - fileio_info->param_3 = open_modeflags[m]; - fileio_info->param_4 = 0644; - } - } else { - if (l <= 255 && m <= 11) { - if (strcmp((char *)fn, ":tt") == 0) { - if (m < 4) - arm->semihosting_result = dup(STDIN_FILENO); - else - arm->semihosting_result = dup(STDOUT_FILENO); - } else { - /* cygwin requires the permission setting - * otherwise it will fail to reopen a previously - * written file */ - arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644); - } - arm->semihosting_errno = errno; - } else { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } - } - } - break; - - case 0x02: /* SYS_CLOSE */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "close"; - fileio_info->param_1 = fd; - } else { - arm->semihosting_result = close(fd); - arm->semihosting_errno = errno; - } - } - break; - - case 0x03: /* SYS_WRITEC */ - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "write"; - fileio_info->param_1 = 1; - fileio_info->param_2 = r1; - fileio_info->param_3 = 1; - } else { - unsigned char c; - retval = target_read_memory(target, r1, 1, 1, &c); - if (retval != ERROR_OK) - return retval; - putchar(c); - arm->semihosting_result = 0; - } - break; - - case 0x04: /* SYS_WRITE0 */ - if (arm->is_semihosting_fileio) { - size_t count = 0; - for (uint32_t a = r1;; a++) { - unsigned char c; - retval = target_read_memory(target, a, 1, 1, &c); - if (retval != ERROR_OK) - return retval; - if (c == '\0') - break; - count++; - } - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "write"; - fileio_info->param_1 = 1; - fileio_info->param_2 = r1; - fileio_info->param_3 = count; - } else { - do { - unsigned char c; - retval = target_read_memory(target, r1++, 1, 1, &c); - if (retval != ERROR_OK) - return retval; - if (!c) - break; - putchar(c); - } while (1); - arm->semihosting_result = 0; - } - break; - - case 0x05: /* SYS_WRITE */ - retval = target_read_memory(target, r1, 4, 3, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - uint32_t a = target_buffer_get_u32(target, params+4); - size_t l = target_buffer_get_u32(target, params+8); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "write"; - fileio_info->param_1 = fd; - fileio_info->param_2 = a; - fileio_info->param_3 = l; - } else { - uint8_t *buf = malloc(l); - if (!buf) { - arm->semihosting_result = -1; - arm->semihosting_errno = ENOMEM; - } else { - retval = target_read_buffer(target, a, l, buf); - if (retval != ERROR_OK) { - free(buf); - return retval; - } - arm->semihosting_result = write(fd, buf, l); - arm->semihosting_errno = errno; - if (arm->semihosting_result >= 0) - arm->semihosting_result = l - arm->semihosting_result; - free(buf); - } - } - } - break; - - case 0x06: /* SYS_READ */ - retval = target_read_memory(target, r1, 4, 3, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - uint32_t a = target_buffer_get_u32(target, params+4); - ssize_t l = target_buffer_get_u32(target, params+8); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "read"; - fileio_info->param_1 = fd; - fileio_info->param_2 = a; - fileio_info->param_3 = l; - } else { - uint8_t *buf = malloc(l); - if (!buf) { - arm->semihosting_result = -1; - arm->semihosting_errno = ENOMEM; - } else { - arm->semihosting_result = read(fd, buf, l); - arm->semihosting_errno = errno; - if (arm->semihosting_result >= 0) { - retval = target_write_buffer(target, a, arm->semihosting_result, buf); - if (retval != ERROR_OK) { - free(buf); - return retval; - } - arm->semihosting_result = l - arm->semihosting_result; - } - free(buf); - } - } - } - break; - - case 0x07: /* SYS_READC */ - if (arm->is_semihosting_fileio) { - LOG_ERROR("SYS_READC not supported by semihosting fileio"); - return ERROR_FAIL; - } - arm->semihosting_result = getchar(); - break; - - case 0x08: /* SYS_ISERROR */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0); - break; - - case 0x09: /* SYS_ISTTY */ - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "isatty"; - fileio_info->param_1 = r1; - } else { - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0)); - } - break; - - case 0x0a: /* SYS_SEEK */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - off_t pos = target_buffer_get_u32(target, params+4); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "lseek"; - fileio_info->param_1 = fd; - fileio_info->param_2 = pos; - fileio_info->param_3 = SEEK_SET; - } else { - arm->semihosting_result = lseek(fd, pos, SEEK_SET); - arm->semihosting_errno = errno; - if (arm->semihosting_result == pos) - arm->semihosting_result = 0; - } - } - break; - - case 0x0c: /* SYS_FLEN */ - if (arm->is_semihosting_fileio) { - LOG_ERROR("SYS_FLEN not supported by semihosting fileio"); - return ERROR_FAIL; - } - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - struct stat buf; - arm->semihosting_result = fstat(fd, &buf); - if (arm->semihosting_result == -1) { - arm->semihosting_errno = errno; - arm->semihosting_result = -1; - break; - } - arm->semihosting_result = buf.st_size; - } - break; - - case 0x0e: /* SYS_REMOVE */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - uint32_t l = target_buffer_get_u32(target, params+4); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "unlink"; - fileio_info->param_1 = a; - fileio_info->param_2 = l; - } else { - if (l <= 255) { - uint8_t fn[256]; - retval = target_read_memory(target, a, 1, l, fn); - if (retval != ERROR_OK) - return retval; - fn[l] = 0; - arm->semihosting_result = remove((char *)fn); - arm->semihosting_errno = errno; - } else { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } - } - } - break; - - case 0x0f: /* SYS_RENAME */ - retval = target_read_memory(target, r1, 4, 4, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a1 = target_buffer_get_u32(target, params+0); - uint32_t l1 = target_buffer_get_u32(target, params+4); - uint32_t a2 = target_buffer_get_u32(target, params+8); - uint32_t l2 = target_buffer_get_u32(target, params+12); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "rename"; - fileio_info->param_1 = a1; - fileio_info->param_2 = l1; - fileio_info->param_3 = a2; - fileio_info->param_4 = l2; - } else { - if (l1 <= 255 && l2 <= 255) { - uint8_t fn1[256], fn2[256]; - retval = target_read_memory(target, a1, 1, l1, fn1); - if (retval != ERROR_OK) - return retval; - retval = target_read_memory(target, a2, 1, l2, fn2); - if (retval != ERROR_OK) - return retval; - fn1[l1] = 0; - fn2[l2] = 0; - arm->semihosting_result = rename((char *)fn1, (char *)fn2); - arm->semihosting_errno = errno; - } else { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } - } - } - break; - - case 0x11: /* SYS_TIME */ - arm->semihosting_result = time(NULL); - break; - - case 0x13: /* SYS_ERRNO */ - arm->semihosting_result = arm->semihosting_errno; - break; - - case 0x15: /* SYS_GET_CMDLINE */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - uint32_t l = target_buffer_get_u32(target, params+4); - char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : ""; - uint32_t s = strlen(arg) + 1; - if (l < s) - arm->semihosting_result = -1; - else { - retval = target_write_buffer(target, a, s, (uint8_t *)arg); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = 0; - } - } - break; - - case 0x16: /* SYS_HEAPINFO */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - /* tell the remote we have no idea */ - memset(params, 0, 4*4); - retval = target_write_memory(target, a, 4, 4, params); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = 0; - } - break; - - case 0x18: /* angel_SWIreason_ReportException */ - switch (r1) { - case 0x20026: /* ADP_Stopped_ApplicationExit */ - fprintf(stderr, "semihosting: *** application exited ***\n"); - break; - case 0x20000: /* ADP_Stopped_BranchThroughZero */ - case 0x20001: /* ADP_Stopped_UndefinedInstr */ - case 0x20002: /* ADP_Stopped_SoftwareInterrupt */ - case 0x20003: /* ADP_Stopped_PrefetchAbort */ - case 0x20004: /* ADP_Stopped_DataAbort */ - case 0x20005: /* ADP_Stopped_AddressException */ - case 0x20006: /* ADP_Stopped_IRQ */ - case 0x20007: /* ADP_Stopped_FIQ */ - case 0x20020: /* ADP_Stopped_BreakPoint */ - case 0x20021: /* ADP_Stopped_WatchPoint */ - case 0x20022: /* ADP_Stopped_StepComplete */ - case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */ - case 0x20024: /* ADP_Stopped_InternalError */ - case 0x20025: /* ADP_Stopped_UserInterruption */ - case 0x20027: /* ADP_Stopped_StackOverflow */ - case 0x20028: /* ADP_Stopped_DivisionByZero */ - case 0x20029: /* ADP_Stopped_OSSpecific */ - default: - fprintf(stderr, "semihosting: exception %#x\n", - (unsigned) r1); - } - return target_call_event_callbacks(target, TARGET_EVENT_HALTED); - - case 0x12: /* SYS_SYSTEM */ - /* Provide SYS_SYSTEM functionality. Uses the - * libc system command, there may be a reason *NOT* - * to use this, but as I can't think of one, I - * implemented it this way. - */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t len = target_buffer_get_u32(target, params+4); - uint32_t c_ptr = target_buffer_get_u32(target, params); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "system"; - fileio_info->param_1 = c_ptr; - fileio_info->param_2 = len; - } else { - uint8_t cmd[256]; - if (len > 255) { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } else { - memset(cmd, 0x0, 256); - retval = target_read_memory(target, c_ptr, 1, len, cmd); - if (retval != ERROR_OK) - return retval; - else - arm->semihosting_result = system((const char *)cmd); - } - } - } - break; - case 0x0d: /* SYS_TMPNAM */ - case 0x10: /* SYS_CLOCK */ - case 0x17: /* angel_SWIreason_EnterSVC */ - case 0x30: /* SYS_ELAPSED */ - case 0x31: /* SYS_TICKFREQ */ - default: - fprintf(stderr, "semihosting: unsupported call %#x\n", - (unsigned) r0); - arm->semihosting_result = -1; - arm->semihosting_errno = ENOTSUP; - } - - return ERROR_OK; -} - -static int get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info) -{ - struct arm *arm = target_to_arm(target); - - /* To avoid uneccessary duplication, semihosting prepares the - * fileio_info structure out-of-band when the target halts. See - * do_semihosting for more detail. - */ - if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio) - return ERROR_FAIL; - - return ERROR_OK; -} - -static int gdb_fileio_end(struct target *target, int result, int fileio_errno, bool ctrl_c) -{ - struct arm *arm = target_to_arm(target); - struct gdb_fileio_info *fileio_info = target->fileio_info; - - /* clear pending status */ - arm->semihosting_hit_fileio = false; - - arm->semihosting_result = result; - arm->semihosting_errno = fileio_errno; - - /* Some fileio results do not match up with what the semihosting - * operation expects; for these operations, we munge the results - * below: - */ - switch (arm->semihosting_op) { - case 0x05: /* SYS_WRITE */ - if (result < 0) - arm->semihosting_result = fileio_info->param_3; - else - arm->semihosting_result = 0; - break; - - case 0x06: /* SYS_READ */ - if (result == (int)fileio_info->param_3) - arm->semihosting_result = 0; - if (result <= 0) - arm->semihosting_result = fileio_info->param_3; - break; - - case 0x0a: /* SYS_SEEK */ - if (result > 0) - arm->semihosting_result = 0; - break; - } - - return post_result(target); -} - /** * Initialize ARM semihosting support. * @@ -630,14 +144,9 @@ static int gdb_fileio_end(struct target *target, int result, int fileio_errno, b */ int arm_semihosting_init(struct target *target) { - target->fileio_info = malloc(sizeof(*target->fileio_info)); - if (target->fileio_info == NULL) { - LOG_ERROR("out of memory"); - return ERROR_FAIL; - } - - target->type->get_gdb_fileio_info = get_gdb_fileio_info; - target->type->gdb_fileio_end = gdb_fileio_end; + struct arm *arm = target_to_arm(target); + assert(arm->setup_semihosting); + semihosting_common_init(target, arm->setup_semihosting, post_result); return ERROR_OK; } @@ -662,7 +171,11 @@ int arm_semihosting(struct target *target, int *retval) uint32_t pc, lr, spsr; struct reg *r; - if (!arm->is_semihosting) + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return 0; + + if (!semihosting->is_active) return 0; if (is_arm7_9(target_to_arm7_9(target)) || @@ -758,6 +271,24 @@ int arm_semihosting(struct target *target, int *retval) /* bkpt 0xAB */ if (insn != 0xBEAB) return 0; + } else if (is_armv8(target_to_armv8(target))) { + if (target->debug_reason != DBG_REASON_BREAKPOINT) + return 0; + + if (arm->core_state == ARM_STATE_AARCH64) { + uint32_t insn = 0; + r = arm->pc; + uint64_t pc64 = buf_get_u64(r->value, 0, 64); + *retval = target_read_u32(target, pc64, &insn); + + if (*retval != ERROR_OK) + return 1; + + /* bkpt 0xAB */ + if (insn != 0xD45E0000) + return 0; + } else + return 1; } else { LOG_ERROR("Unsupported semi-hosting Target"); return 0; @@ -766,32 +297,38 @@ int arm_semihosting(struct target *target, int *retval) /* Perform semihosting if we are not waiting on a fileio * operation to complete. */ - if (!arm->semihosting_hit_fileio) { - *retval = do_semihosting(target); - if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation"); + if (!semihosting->hit_fileio) { + if (is_armv8(target_to_armv8(target)) && + arm->core_state == ARM_STATE_AARCH64) { + /* Read op and param from register x0 and x1 respectively. */ + semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64); + semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64); + semihosting->word_size_bytes = 8; + } else { + /* Read op and param from register r0 and r1 respectively. */ + semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32); + semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32); + semihosting->word_size_bytes = 4; + } + + /* Check for ARM operation numbers. */ + if (0 <= semihosting->op && semihosting->op <= 0x31) { + *retval = semihosting_common(target); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed semihosting operation"); + return 0; + } + } else { + /* Unknown operation number, not a semihosting call. */ return 0; } } - /* Post result to target if we are not waiting on a fileio + /* Resume if target it is resumable and we are not waiting on a fileio * operation to complete: */ - if (!arm->semihosting_hit_fileio) { - *retval = post_result(target); - if (*retval != ERROR_OK) { - LOG_ERROR("Failed to post semihosting result"); - return 0; - } - - *retval = target_resume(target, 1, 0, 0, 0); - if (*retval != ERROR_OK) { - LOG_ERROR("Failed to resume target"); - return 0; - } - - return 1; - } + if (semihosting->is_resumable && !semihosting->hit_fileio) + return arm_semihosting_resume(target, retval); return 0; } diff --git a/src/target/arm_semihosting.h b/src/target/arm_semihosting.h index 011f19f..cf1f8de 100644 --- a/src/target/arm_semihosting.h +++ b/src/target/arm_semihosting.h @@ -19,6 +19,8 @@ #ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H #define OPENOCD_TARGET_ARM_SEMIHOSTING_H +#include "semihosting_common.h" + int arm_semihosting_init(struct target *target); int arm_semihosting(struct target *target, int *retval); diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 48050b0..96a63e4 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -8,6 +8,9 @@ * Copyright (C) 2008 by Oyvind Harboe * * oyvind.harboe@zylin.com * * * + * Copyright (C) 2018 by Liviu Ionescu * + * <ilg@livius.net> * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -34,6 +37,7 @@ #include <helper/binarybuffer.h> #include "algorithm.h" #include "register.h" +#include "semihosting_common.h" /* offsets into armv4_5 core register cache */ enum { @@ -340,6 +344,50 @@ static const struct { }; +static const struct { + unsigned int id; + const char *name; + uint32_t bits; + enum arm_mode mode; + enum reg_type type; + const char *group; + const char *feature; +} arm_vfp_v3_regs[] = { + { ARM_VFP_V3_D0, "d0", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D1, "d1", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D2, "d2", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D3, "d3", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D4, "d4", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D5, "d5", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D6, "d6", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D7, "d7", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D8, "d8", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D9, "d9", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D10, "d10", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D11, "d11", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D12, "d12", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D13, "d13", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D14, "d14", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D15, "d15", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D16, "d16", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D17, "d17", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D18, "d18", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D19, "d19", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D20, "d20", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D21, "d21", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D22, "d22", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D23, "d23", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D24, "d24", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D25, "d25", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D26, "d26", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D27, "d27", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D28, "d28", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D29, "d29", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D30, "d30", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_D31, "d31", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARM_VFP_V3_FPSCR, "fpscr", 32, ARM_MODE_ANY, REG_TYPE_INT, "float", "org.gnu.gdb.arm.vfp"}, +}; + /* map core mode (USR, FIQ, ...) and register number to * indices into the register cache */ @@ -567,6 +615,10 @@ static int armv4_5_set_core_reg(struct reg *reg, uint8_t *buf) } } else { buf_set_u32(reg->value, 0, 32, value); + if (reg->size == 64) { + value = buf_get_u32(buf + 4, 0, 32); + buf_set_u32(reg->value + 4, 0, 32, value); + } reg->valid = 1; } reg->dirty = 1; @@ -582,6 +634,10 @@ static const struct reg_arch_type arm_reg_type = { struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm) { int num_regs = ARRAY_SIZE(arm_core_regs); + int num_core_regs = num_regs; + if (arm->arm_vfp_version == ARM_VFP_V3) + num_regs += ARRAY_SIZE(arm_vfp_v3_regs); + struct reg_cache *cache = malloc(sizeof(struct reg_cache)); struct reg *reg_list = calloc(num_regs, sizeof(struct reg)); struct arm_reg *reg_arch_info = calloc(num_regs, sizeof(struct arm_reg)); @@ -599,7 +655,7 @@ struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm) cache->reg_list = reg_list; cache->num_regs = 0; - for (i = 0; i < num_regs; i++) { + for (i = 0; i < num_core_regs; i++) { /* Skip registers this core doesn't expose */ if (arm_core_regs[i].mode == ARM_MODE_MON && arm->core_type != ARM_MODE_MON) @@ -651,9 +707,38 @@ struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm) cache->num_regs++; } + int j; + for (i = num_core_regs, j = 0; i < num_regs; i++, j++) { + reg_arch_info[i].num = arm_vfp_v3_regs[j].id; + reg_arch_info[i].mode = arm_vfp_v3_regs[j].mode; + reg_arch_info[i].target = target; + reg_arch_info[i].arm = arm; + + reg_list[i].name = arm_vfp_v3_regs[j].name; + reg_list[i].number = arm_vfp_v3_regs[j].id; + reg_list[i].size = arm_vfp_v3_regs[j].bits; + reg_list[i].value = reg_arch_info[i].value; + reg_list[i].type = &arm_reg_type; + reg_list[i].arch_info = ®_arch_info[i]; + reg_list[i].exist = true; + + reg_list[i].caller_save = false; + + reg_list[i].reg_data_type = malloc(sizeof(struct reg_data_type)); + reg_list[i].reg_data_type->type = arm_vfp_v3_regs[j].type; + + reg_list[i].feature = malloc(sizeof(struct reg_feature)); + reg_list[i].feature->name = arm_vfp_v3_regs[j].feature; + + reg_list[i].group = arm_vfp_v3_regs[j].group; + + cache->num_regs++; + } + arm->pc = reg_list + 15; arm->cpsr = reg_list + ARMV4_5_CPSR; arm->core_cache = cache; + return cache; } @@ -667,7 +752,7 @@ int arm_arch_state(struct target *target) } /* avoid filling log waiting for fileio reply */ - if (arm->semihosting_hit_fileio) + if (target->semihosting && target->semihosting->hit_fileio) return ERROR_OK; LOG_USER("target halted in %s state due to %s, current mode: %s\n" @@ -677,8 +762,8 @@ int arm_arch_state(struct target *target) arm_mode_name(arm->core_mode), buf_get_u32(arm->cpsr->value, 0, 32), buf_get_u32(arm->pc->value, 0, 32), - arm->is_semihosting ? ", semihosting" : "", - arm->is_semihosting_fileio ? " fileio" : ""); + (target->semihosting && target->semihosting->is_active) ? ", semihosting" : "", + (target->semihosting && target->semihosting->is_fileio) ? " fileio" : ""); return ERROR_OK; } @@ -1013,119 +1098,10 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv) return JIM_OK; } -COMMAND_HANDLER(handle_arm_semihosting_command) -{ - struct target *target = get_current_target(CMD_CTX); - - if (target == NULL) { - LOG_ERROR("No target selected"); - return ERROR_FAIL; - } - - struct arm *arm = target_to_arm(target); - - if (!is_arm(arm)) { - command_print(CMD_CTX, "current target isn't an ARM"); - return ERROR_FAIL; - } - - if (!arm->setup_semihosting) { - command_print(CMD_CTX, "semihosting not supported for current target"); - return ERROR_FAIL; - } - - if (CMD_ARGC > 0) { - int semihosting; - - COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting); - - if (!target_was_examined(target)) { - LOG_ERROR("Target not examined yet"); - return ERROR_FAIL; - } - - if (arm->setup_semihosting(target, semihosting) != ERROR_OK) { - LOG_ERROR("Failed to Configure semihosting"); - return ERROR_FAIL; - } - - /* FIXME never let that "catch" be dropped! */ - arm->is_semihosting = semihosting; - } - - command_print(CMD_CTX, "semihosting is %s", - arm->is_semihosting - ? "enabled" : "disabled"); - - return ERROR_OK; -} - -COMMAND_HANDLER(handle_arm_semihosting_fileio_command) -{ - struct target *target = get_current_target(CMD_CTX); - - if (target == NULL) { - LOG_ERROR("No target selected"); - return ERROR_FAIL; - } - - struct arm *arm = target_to_arm(target); - - if (!is_arm(arm)) { - command_print(CMD_CTX, "current target isn't an ARM"); - return ERROR_FAIL; - } - - if (!arm->is_semihosting) { - command_print(CMD_CTX, "semihosting is not enabled"); - return ERROR_FAIL; - } - - if (CMD_ARGC > 0) - COMMAND_PARSE_ENABLE(CMD_ARGV[0], arm->is_semihosting_fileio); - - command_print(CMD_CTX, "semihosting fileio is %s", - arm->is_semihosting_fileio - ? "enabled" : "disabled"); - - return ERROR_OK; -} - -COMMAND_HANDLER(handle_arm_semihosting_cmdline) -{ - struct target *target = get_current_target(CMD_CTX); - unsigned int i; - - if (target == NULL) { - LOG_ERROR("No target selected"); - return ERROR_FAIL; - } - - struct arm *arm = target_to_arm(target); - - if (!is_arm(arm)) { - command_print(CMD_CTX, "current target isn't an ARM"); - return ERROR_FAIL; - } - - if (!arm->setup_semihosting) { - command_print(CMD_CTX, "semihosting not supported for current target"); - return ERROR_FAIL; - } - - free(arm->semihosting_cmdline); - arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL; - - for (i = 1; i < CMD_ARGC; i++) { - char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]); - if (cmdline == NULL) - break; - free(arm->semihosting_cmdline); - arm->semihosting_cmdline = cmdline; - } - - return ERROR_OK; -} +extern __COMMAND_HANDLER(handle_common_semihosting_command); +extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command); +extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command); +extern __COMMAND_HANDLER(handle_common_semihosting_cmdline); static const struct command_registration arm_exec_command_handlers[] = { { @@ -1164,26 +1140,32 @@ static const struct command_registration arm_exec_command_handlers[] = { }, { "semihosting", - .handler = handle_arm_semihosting_command, + .handler = handle_common_semihosting_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting operations", }, { "semihosting_cmdline", - .handler = handle_arm_semihosting_cmdline, + .handler = handle_common_semihosting_cmdline, .mode = COMMAND_EXEC, .usage = "arguments", .help = "command line arguments to be passed to program", }, { "semihosting_fileio", - .handler = handle_arm_semihosting_fileio_command, + .handler = handle_common_semihosting_fileio_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting fileio operations", }, - + { + "semihosting_resexit", + .handler = handle_common_semihosting_resumable_exit_command, + .mode = COMMAND_EXEC, + .usage = "['enable'|'disable']", + .help = "activate support for semihosting resumable exit", + }, COMMAND_REGISTRATION_DONE }; const struct command_registration arm_command_handlers[] = { @@ -1229,6 +1211,10 @@ int arm_get_gdb_reg_list(struct target *target, case REG_CLASS_ALL: *reg_list_size = (arm->core_type != ARM_MODE_MON ? 48 : 51); + unsigned int list_size_core = *reg_list_size; + if (arm->arm_vfp_version == ARM_VFP_V3) + *reg_list_size += 33; + *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size)); for (i = 0; i < 16; i++) @@ -1249,6 +1235,12 @@ int arm_get_gdb_reg_list(struct target *target, (*reg_list)[24] = &arm_gdb_dummy_fps_reg; (*reg_list)[24]->size = 0; + if (arm->arm_vfp_version == ARM_VFP_V3) { + unsigned int num_core_regs = ARRAY_SIZE(arm_core_regs); + for (i = 0; i < 33; i++) + (*reg_list)[list_size_core + i] = &(arm->core_cache->reg_list[num_core_regs + i]); + } + return ERROR_OK; break; @@ -1572,7 +1564,7 @@ cleanup: * */ int arm_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value) + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value) { struct working_area *check_algorithm; struct reg_param reg_params[3]; @@ -1615,10 +1607,10 @@ int arm_blank_check_memory(struct target *target, arm_algo.core_state = ARM_STATE_ARM; init_reg_param(®_params[0], "r0", 32, PARAM_OUT); - buf_set_u32(reg_params[0].value, 0, 32, address); + buf_set_u32(reg_params[0].value, 0, 32, blocks[0].address); init_reg_param(®_params[1], "r1", 32, PARAM_OUT); - buf_set_u32(reg_params[1].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, blocks[0].size); init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT); buf_set_u32(reg_params[2].value, 0, 32, erased_value); @@ -1633,7 +1625,7 @@ int arm_blank_check_memory(struct target *target, 10000, &arm_algo); if (retval == ERROR_OK) - *blank = buf_get_u32(reg_params[2].value, 0, 32); + blocks[0].result = buf_get_u32(reg_params[2].value, 0, 32); destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); @@ -1642,7 +1634,10 @@ int arm_blank_check_memory(struct target *target, cleanup: target_free_working_area(target, check_algorithm); - return retval; + if (retval != ERROR_OK) + return retval; + + return 1; /* only one block has been checked */ } static int arm_full_context(struct target *target) diff --git a/src/target/armv7a.c b/src/target/armv7a.c index db72afd..eecfa70 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -124,14 +124,18 @@ done: return retval; } -static int armv7a_read_ttbcr(struct target *target) +int armv7a_read_ttbcr(struct target *target) { struct armv7a_common *armv7a = target_to_armv7a(target); struct arm_dpm *dpm = armv7a->arm.dpm; uint32_t ttbcr, ttbcr_n; - int retval = dpm->prepare(dpm); + int ttbidx; + int retval; + + retval = dpm->prepare(dpm); if (retval != ERROR_OK) goto done; + /* MRC p15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/ retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRC(15, 0, 0, 2, 0, 2), @@ -145,6 +149,15 @@ static int armv7a_read_ttbcr(struct target *target) armv7a->armv7a_mmu.ttbcr = ttbcr; armv7a->armv7a_mmu.cached = 1; + for (ttbidx = 0; ttbidx < 2; ttbidx++) { + /* MRC p15,0,<Rt>,c2,c0,ttbidx */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 2, 0, ttbidx), + &armv7a->armv7a_mmu.ttbr[ttbidx]); + if (retval != ERROR_OK) + goto done; + } + /* * ARM Architecture Reference Manual (ARMv7-A and ARMv7-Redition), * document # ARM DDI 0406C @@ -182,42 +195,21 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) uint32_t second_lvl_descriptor = 0x0; int retval; struct armv7a_common *armv7a = target_to_armv7a(target); - struct arm_dpm *dpm = armv7a->arm.dpm; uint32_t ttbidx = 0; /* default to ttbr0 */ uint32_t ttb_mask; uint32_t va_mask; - uint32_t ttbcr; uint32_t ttb; - retval = dpm->prepare(dpm); - if (retval != ERROR_OK) - goto done; - - /* MRC p15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/ - retval = dpm->instr_read_data_r0(dpm, - ARMV4_5_MRC(15, 0, 0, 2, 0, 2), - &ttbcr); - if (retval != ERROR_OK) - goto done; - - /* if ttbcr has changed or was not read before, re-read the information */ - if ((armv7a->armv7a_mmu.cached == 0) || - (armv7a->armv7a_mmu.ttbcr != ttbcr)) { - armv7a_read_ttbcr(target); - } + if (target->state != TARGET_HALTED) + LOG_INFO("target not halted, using cached values for translation table!"); /* if va is above the range handled by ttbr0, select ttbr1 */ if (va > armv7a->armv7a_mmu.ttbr_range[0]) { /* select ttb 1 */ ttbidx = 1; } - /* MRC p15,0,<Rt>,c2,c0,ttbidx */ - retval = dpm->instr_read_data_r0(dpm, - ARMV4_5_MRC(15, 0, 0, 2, 0, ttbidx), - &ttb); - if (retval != ERROR_OK) - return retval; + ttb = armv7a->armv7a_mmu.ttbr[ttbidx]; ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx]; va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx]; @@ -279,9 +271,6 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) } return ERROR_OK; - -done: - return retval; } /* V7 method VA TO PA */ @@ -565,9 +554,6 @@ int armv7a_identify_cache(struct target *target) struct armv7a_cache_common *cache = &(armv7a->armv7a_mmu.armv7a_cache); - if (!armv7a->is_armv7r) - armv7a_read_ttbcr(target); - retval = dpm->prepare(dpm); if (retval != ERROR_OK) goto done; @@ -786,9 +772,6 @@ const struct command_registration l2x_cache_command_handlers[] = { const struct command_registration armv7a_command_handlers[] = { { - .chain = dap_command_handlers, - }, - { .chain = l2x_cache_command_handlers, }, { diff --git a/src/target/armv7a.h b/src/target/armv7a.h index 14112e4..57779c6 100644 --- a/src/target/armv7a.h +++ b/src/target/armv7a.h @@ -87,6 +87,7 @@ struct armv7a_mmu_common { /* following field mmu working way */ int32_t cached; /* 0: not initialized, 1: initialized */ uint32_t ttbcr; /* cache for ttbcr register */ + uint32_t ttbr[2]; uint32_t ttbr_mask[2]; uint32_t ttbr_range[2]; @@ -193,6 +194,7 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val); int armv7a_handle_cache_info_command(struct command_context *cmd_ctx, struct armv7a_cache_common *armv7a_cache); +int armv7a_read_ttbcr(struct target *target); extern const struct command_registration armv7a_command_handlers[]; diff --git a/src/target/armv7a_cache.c b/src/target/armv7a_cache.c index 7af3e6d..7435aab 100644 --- a/src/target/armv7a_cache.c +++ b/src/target/armv7a_cache.c @@ -70,6 +70,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach LOG_DEBUG("cl %" PRId32, cl); do { + keep_alive(); c_way = size->way; do { uint32_t value = (c_index << size->index_shift) @@ -89,6 +90,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach } while (c_index >= 0); done: + keep_alive(); return retval; } @@ -148,10 +150,11 @@ int armv7a_cache_auto_flush_all_data(struct target *target) } else retval = armv7a_l1_d_cache_clean_inval_all(target); - /* do outer cache flushing after inner caches have been flushed */ - retval = arm7a_l2x_flush_all_data(target); + if (retval != ERROR_OK) + return retval; - return retval; + /* do outer cache flushing after inner caches have been flushed */ + return arm7a_l2x_flush_all_data(target); } @@ -163,7 +166,7 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt, struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache; uint32_t linelen = armv7a_cache->dminline; uint32_t va_line, va_end; - int retval; + int retval, i = 0; retval = armv7a_l1_d_cache_sanity_check(target); if (retval != ERROR_OK) @@ -197,6 +200,8 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt, } while (va_line < va_end) { + if ((i++ & 0x3f) == 0) + keep_alive(); /* DCIMVAC - Invalidate data cache line by VA to PoC. */ retval = dpm->instr_write_data_r0(dpm, ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line); @@ -205,11 +210,13 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt, va_line += linelen; } + keep_alive(); dpm->finish(dpm); return retval; done: LOG_ERROR("d-cache invalidate failed"); + keep_alive(); dpm->finish(dpm); return retval; @@ -223,7 +230,7 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt, struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache; uint32_t linelen = armv7a_cache->dminline; uint32_t va_line, va_end; - int retval; + int retval, i = 0; retval = armv7a_l1_d_cache_sanity_check(target); if (retval != ERROR_OK) @@ -237,6 +244,8 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt, va_end = virt + size; while (va_line < va_end) { + if ((i++ & 0x3f) == 0) + keep_alive(); /* DCCMVAC - Data Cache Clean by MVA to PoC */ retval = dpm->instr_write_data_r0(dpm, ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line); @@ -245,11 +254,13 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt, va_line += linelen; } + keep_alive(); dpm->finish(dpm); return retval; done: LOG_ERROR("d-cache invalidate failed"); + keep_alive(); dpm->finish(dpm); return retval; @@ -263,7 +274,7 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt, struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache; uint32_t linelen = armv7a_cache->dminline; uint32_t va_line, va_end; - int retval; + int retval, i = 0; retval = armv7a_l1_d_cache_sanity_check(target); if (retval != ERROR_OK) @@ -277,6 +288,8 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt, va_end = virt + size; while (va_line < va_end) { + if ((i++ & 0x3f) == 0) + keep_alive(); /* DCCIMVAC */ retval = dpm->instr_write_data_r0(dpm, ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line); @@ -285,11 +298,13 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt, va_line += linelen; } + keep_alive(); dpm->finish(dpm); return retval; done: LOG_ERROR("d-cache invalidate failed"); + keep_alive(); dpm->finish(dpm); return retval; @@ -341,7 +356,7 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt, &armv7a->armv7a_mmu.armv7a_cache; uint32_t linelen = armv7a_cache->iminline; uint32_t va_line, va_end; - int retval; + int retval, i = 0; retval = armv7a_l1_i_cache_sanity_check(target); if (retval != ERROR_OK) @@ -355,6 +370,8 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt, va_end = virt + size; while (va_line < va_end) { + if ((i++ & 0x3f) == 0) + keep_alive(); /* ICIMVAU - Invalidate instruction cache by VA to PoU. */ retval = dpm->instr_write_data_r0(dpm, ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line); @@ -367,10 +384,13 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt, goto done; va_line += linelen; } + keep_alive(); + dpm->finish(dpm); return retval; done: LOG_ERROR("i-cache invalidate failed"); + keep_alive(); dpm->finish(dpm); return retval; diff --git a/src/target/armv7m.c b/src/target/armv7m.c index e0911c3..7d3bd73 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -11,6 +11,9 @@ * Copyright (C) 2007,2008 Øyvind Harboe * * oyvind.harboe@zylin.com * * * + * Copyright (C) 2018 by Liviu Ionescu * + * <ilg@livius.net> * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -37,6 +40,7 @@ #include "armv7m.h" #include "algorithm.h" #include "register.h" +#include "semihosting_common.h" #if 0 #define _DEBUG_INSTRUCTION_EXECUTION_ @@ -537,7 +541,7 @@ int armv7m_arch_state(struct target *target) uint32_t ctrl, sp; /* avoid filling log waiting for fileio reply */ - if (arm->semihosting_hit_fileio) + if (target->semihosting && target->semihosting->hit_fileio) return ERROR_OK; ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32); @@ -552,8 +556,8 @@ int armv7m_arch_state(struct target *target) buf_get_u32(arm->pc->value, 0, 32), (ctrl & 0x02) ? 'p' : 'm', sp, - arm->is_semihosting ? ", semihosting" : "", - arm->is_semihosting_fileio ? " fileio" : ""); + (target->semihosting && target->semihosting->is_active) ? ", semihosting" : "", + (target->semihosting && target->semihosting->is_fileio) ? " fileio" : ""); return ERROR_OK; } @@ -731,34 +735,23 @@ cleanup: return retval; } -/** Checks whether a memory region is erased. */ +/** Checks an array of memory regions whether they are erased. */ int armv7m_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value) + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value) { struct working_area *erase_check_algorithm; - struct reg_param reg_params[3]; + struct working_area *erase_check_params; + struct reg_param reg_params[2]; struct armv7m_algorithm armv7m_info; - const uint8_t *code; - uint32_t code_size; int retval; + static bool timed_out; + static const uint8_t erase_check_code[] = { #include "../../contrib/loaders/erase_check/armv7m_erase_check.inc" }; - static const uint8_t zero_erase_check_code[] = { -#include "../../contrib/loaders/erase_check/armv7m_0_erase_check.inc" - }; - switch (erased_value) { - case 0x00: - code = zero_erase_check_code; - code_size = sizeof(zero_erase_check_code); - break; - case 0xff: - default: - code = erase_check_code; - code_size = sizeof(erase_check_code); - } + const uint32_t code_size = sizeof(erase_check_code); /* make sure we have a working area */ if (target_alloc_working_area(target, code_size, @@ -766,40 +759,110 @@ int armv7m_blank_check_memory(struct target *target, return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; retval = target_write_buffer(target, erase_check_algorithm->address, - code_size, code); + code_size, erase_check_code); if (retval != ERROR_OK) - goto cleanup; + goto cleanup1; + + /* prepare blocks array for algo */ + struct algo_block { + union { + uint32_t size; + uint32_t result; + }; + uint32_t address; + }; + + uint32_t avail = target_get_working_area_avail(target); + int blocks_to_check = avail / sizeof(struct algo_block) - 1; + if (num_blocks < blocks_to_check) + blocks_to_check = num_blocks; + + struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block)); + if (params == NULL) { + retval = ERROR_FAIL; + goto cleanup1; + } + + int i; + uint32_t total_size = 0; + for (i = 0; i < blocks_to_check; i++) { + total_size += blocks[i].size; + target_buffer_set_u32(target, (uint8_t *)&(params[i].size), + blocks[i].size / sizeof(uint32_t)); + target_buffer_set_u32(target, (uint8_t *)&(params[i].address), + blocks[i].address); + } + target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0); + + uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block); + if (target_alloc_working_area(target, param_size, + &erase_check_params) != ERROR_OK) { + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto cleanup2; + } + + retval = target_write_buffer(target, erase_check_params->address, + param_size, (uint8_t *)params); + if (retval != ERROR_OK) + goto cleanup3; + + uint32_t erased_word = erased_value | (erased_value << 8) + | (erased_value << 16) | (erased_value << 24); + + LOG_DEBUG("Starting erase check of %d blocks, parameters@" + TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address); armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; armv7m_info.core_mode = ARM_MODE_THREAD; init_reg_param(®_params[0], "r0", 32, PARAM_OUT); - buf_set_u32(reg_params[0].value, 0, 32, address); + buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address); init_reg_param(®_params[1], "r1", 32, PARAM_OUT); - buf_set_u32(reg_params[1].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, erased_word); - init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT); - buf_set_u32(reg_params[2].value, 0, 32, erased_value); + /* assume CPU clk at least 1 MHz */ + int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000; retval = target_run_algorithm(target, - 0, - NULL, - 3, - reg_params, - erase_check_algorithm->address, - erase_check_algorithm->address + (code_size - 2), - 10000, - &armv7m_info); + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + erase_check_algorithm->address, + erase_check_algorithm->address + (code_size - 2), + timeout, + &armv7m_info); + + timed_out = retval == ERROR_TARGET_TIMEOUT; + if (retval != ERROR_OK && !timed_out) + goto cleanup4; + + retval = target_read_buffer(target, erase_check_params->address, + param_size, (uint8_t *)params); + if (retval != ERROR_OK) + goto cleanup4; - if (retval == ERROR_OK) - *blank = buf_get_u32(reg_params[2].value, 0, 32); + for (i = 0; i < blocks_to_check; i++) { + uint32_t result = target_buffer_get_u32(target, + (uint8_t *)&(params[i].result)); + if (result != 0 && result != 1) + break; + + blocks[i].result = result; + } + if (i && timed_out) + LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i); + + retval = i; /* return number of blocks really checked */ +cleanup4: destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); - destroy_reg_param(®_params[2]); -cleanup: +cleanup3: + target_free_working_area(target, erase_check_params); +cleanup2: + free(params); +cleanup1: target_free_working_area(target, erase_check_algorithm); return retval; @@ -843,8 +906,5 @@ const struct command_registration armv7m_command_handlers[] = { { .chain = arm_command_handlers, }, - { - .chain = dap_command_handlers, - }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/armv7m.h b/src/target/armv7m.h index 6f5d6f9..01bf19e 100644 --- a/src/target/armv7m.h +++ b/src/target/armv7m.h @@ -225,7 +225,7 @@ int armv7m_restore_context(struct target *target); int armv7m_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum); int armv7m_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value); + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value); int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found); diff --git a/src/target/armv7m_trace.c b/src/target/armv7m_trace.c index c1e4f5b..62f0f8e 100644 --- a/src/target/armv7m_trace.c +++ b/src/target/armv7m_trace.c @@ -62,7 +62,7 @@ int armv7m_trace_tpiu_config(struct target *target) target_unregister_timer_callback(armv7m_poll_trace, target); - retval = adapter_config_trace(trace_config->config_type == INTERNAL, + retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL, trace_config->pin_protocol, trace_config->port_size, &trace_config->trace_freq); @@ -83,7 +83,7 @@ int armv7m_trace_tpiu_config(struct target *target) trace_config->trace_freq, trace_config->traceclkin_freq, trace_freq); trace_config->trace_freq = trace_freq; - retval = adapter_config_trace(trace_config->config_type == INTERNAL, + retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL, trace_config->pin_protocol, trace_config->port_size, &trace_config->trace_freq); @@ -115,7 +115,7 @@ int armv7m_trace_tpiu_config(struct target *target) if (retval != ERROR_OK) return retval; - if (trace_config->config_type == INTERNAL) + if (trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL) target_register_timer_callback(armv7m_poll_trace, 1, 1, target); target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG); @@ -173,7 +173,7 @@ COMMAND_HANDLER(handle_tpiu_config_command) if (CMD_ARGC == cmd_idx + 1) { close_trace_file(armv7m); - armv7m->trace_config.config_type = DISABLED; + armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED; if (CMD_CTX->mode == COMMAND_EXEC) return armv7m_trace_tpiu_config(target); else @@ -183,13 +183,13 @@ COMMAND_HANDLER(handle_tpiu_config_command) !strcmp(CMD_ARGV[cmd_idx], "internal")) { close_trace_file(armv7m); - armv7m->trace_config.config_type = EXTERNAL; + armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL; if (!strcmp(CMD_ARGV[cmd_idx], "internal")) { cmd_idx++; if (CMD_ARGC == cmd_idx) return ERROR_COMMAND_SYNTAX_ERROR; - armv7m->trace_config.config_type = INTERNAL; + armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL; if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) { armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab"); @@ -204,7 +204,7 @@ COMMAND_HANDLER(handle_tpiu_config_command) return ERROR_COMMAND_SYNTAX_ERROR; if (!strcmp(CMD_ARGV[cmd_idx], "sync")) { - armv7m->trace_config.pin_protocol = SYNC; + armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_SYNC; cmd_idx++; if (CMD_ARGC == cmd_idx) @@ -213,9 +213,9 @@ COMMAND_HANDLER(handle_tpiu_config_command) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size); } else { if (!strcmp(CMD_ARGV[cmd_idx], "manchester")) - armv7m->trace_config.pin_protocol = ASYNC_MANCHESTER; + armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER; else if (!strcmp(CMD_ARGV[cmd_idx], "uart")) - armv7m->trace_config.pin_protocol = ASYNC_UART; + armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_UART; else return ERROR_COMMAND_SYNTAX_ERROR; @@ -237,7 +237,7 @@ COMMAND_HANDLER(handle_tpiu_config_command) COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq); cmd_idx++; } else { - if (armv7m->trace_config.config_type != INTERNAL) { + if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_INTERNAL) { LOG_ERROR("Trace port frequency can't be omitted in external capture mode"); return ERROR_COMMAND_SYNTAX_ERROR; } diff --git a/src/target/armv7m_trace.h b/src/target/armv7m_trace.h index 4f99394..c63f36d 100644 --- a/src/target/armv7m_trace.h +++ b/src/target/armv7m_trace.h @@ -27,15 +27,15 @@ */ enum trace_config_type { - DISABLED, /**< tracing is disabled */ - EXTERNAL, /**< trace output is captured externally */ - INTERNAL /**< trace output is handled by OpenOCD adapter driver */ + TRACE_CONFIG_TYPE_DISABLED, /**< tracing is disabled */ + TRACE_CONFIG_TYPE_EXTERNAL, /**< trace output is captured externally */ + TRACE_CONFIG_TYPE_INTERNAL /**< trace output is handled by OpenOCD adapter driver */ }; -enum tpio_pin_protocol { - SYNC, /**< synchronous trace output */ - ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */ - ASYNC_UART /**< asynchronous output with NRZ coding */ +enum tpiu_pin_protocol { + TPIU_PIN_PROTOCOL_SYNC, /**< synchronous trace output */ + TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */ + TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */ }; enum itm_ts_prescaler { @@ -50,7 +50,7 @@ struct armv7m_trace_config { enum trace_config_type config_type; /** Currently active trace output mode */ - enum tpio_pin_protocol pin_protocol; + enum tpiu_pin_protocol pin_protocol; /** TPIU formatter enable/disable (in async mode) */ bool formatter; /** Synchronous output port width */ diff --git a/src/target/armv8.c b/src/target/armv8.c index 1b8e450..dfa2c67 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -1,6 +1,9 @@ /*************************************************************************** * Copyright (C) 2015 by David Ung * * * + * Copyright (C) 2018 by Liviu Ionescu * + * <ilg@livius.net> * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -36,6 +39,7 @@ #include "armv8_opcodes.h" #include "target.h" #include "target_type.h" +#include "semihosting_common.h" static const char * const armv8_state_strings[] = { "AArch32", "Thumb", "Jazelle", "ThumbEE", "AArch64", @@ -135,6 +139,16 @@ static int armv8_read_reg(struct armv8_common *armv8, int regnum, uint64_t *regv ARMV8_MRS_DSPSR(0), &value); value_64 = value; break; + case ARMV8_FPSR: + retval = dpm->instr_read_data_r0(dpm, + ARMV8_MRS_FPSR(0), &value); + value_64 = value; + break; + case ARMV8_FPCR: + retval = dpm->instr_read_data_r0(dpm, + ARMV8_MRS_FPCR(0), &value); + value_64 = value; + break; case ARMV8_ELR_EL1: retval = dpm->instr_read_data_r0_64(dpm, ARMV8_MRS(SYSTEM_ELR_EL1, 0), &value_64); @@ -184,6 +198,31 @@ static int armv8_read_reg(struct armv8_common *armv8, int regnum, uint64_t *regv if (retval == ERROR_OK && regval != NULL) *regval = value_64; + else + retval = ERROR_FAIL; + + return retval; +} + +static int armv8_read_reg_simdfp_aarch64(struct armv8_common *armv8, int regnum, uint64_t *lvalue, uint64_t *hvalue) +{ + int retval = ERROR_FAIL; + struct arm_dpm *dpm = &armv8->dpm; + + switch (regnum) { + case ARMV8_V0 ... ARMV8_V31: + retval = dpm->instr_read_data_r0_64(dpm, + ARMV8_MOV_GPR_VFP(0, (regnum - ARMV8_V0), 1), hvalue); + if (retval != ERROR_OK) + return retval; + retval = dpm->instr_read_data_r0_64(dpm, + ARMV8_MOV_GPR_VFP(0, (regnum - ARMV8_V0), 0), lvalue); + break; + + default: + retval = ERROR_FAIL; + break; + } return retval; } @@ -216,6 +255,18 @@ static int armv8_write_reg(struct armv8_common *armv8, int regnum, uint64_t valu ARMV8_MSR_DSPSR(0), value); break; + case ARMV8_FPSR: + value = value_64; + retval = dpm->instr_write_data_r0(dpm, + ARMV8_MSR_FPSR(0), + value); + break; + case ARMV8_FPCR: + value = value_64; + retval = dpm->instr_write_data_r0(dpm, + ARMV8_MSR_FPCR(0), + value); + break; /* registers clobbered by taking exception in debug state */ case ARMV8_ELR_EL1: retval = dpm->instr_write_data_r0_64(dpm, @@ -267,6 +318,29 @@ static int armv8_write_reg(struct armv8_common *armv8, int regnum, uint64_t valu return retval; } +static int armv8_write_reg_simdfp_aarch64(struct armv8_common *armv8, int regnum, uint64_t lvalue, uint64_t hvalue) +{ + int retval = ERROR_FAIL; + struct arm_dpm *dpm = &armv8->dpm; + + switch (regnum) { + case ARMV8_V0 ... ARMV8_V31: + retval = dpm->instr_write_data_r0_64(dpm, + ARMV8_MOV_VFP_GPR((regnum - ARMV8_V0), 0, 1), hvalue); + if (retval != ERROR_OK) + return retval; + retval = dpm->instr_write_data_r0_64(dpm, + ARMV8_MOV_VFP_GPR((regnum - ARMV8_V0), 0, 0), lvalue); + break; + + default: + retval = ERROR_FAIL; + break; + } + + return retval; +} + static int armv8_read_reg32(struct armv8_common *armv8, int regnum, uint64_t *regval) { struct arm_dpm *dpm = &armv8->dpm; @@ -338,6 +412,11 @@ static int armv8_read_reg32(struct armv8_common *armv8, int regnum, uint64_t *re ARMV8_MRS_xPSR_T1(1, 0), &value); break; + case ARMV8_FPSR: + /* "VMRS r0, FPSCR"; then return via DCC */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_VMRS(0), &value); + break; default: retval = ERROR_FAIL; break; @@ -349,6 +428,56 @@ static int armv8_read_reg32(struct armv8_common *armv8, int regnum, uint64_t *re return retval; } +static int armv8_read_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum, uint64_t *lvalue, uint64_t *hvalue) +{ + int retval = ERROR_FAIL; + struct arm_dpm *dpm = &armv8->dpm; + struct reg *reg_r1 = dpm->arm->core_cache->reg_list + ARMV8_R1; + uint32_t value_r0 = 0, value_r1 = 0; + unsigned num = (regnum - ARMV8_V0) << 1; + + switch (regnum) { + case ARMV8_V0 ... ARMV8_V15: + /* we are going to write R1, mark it dirty */ + reg_r1->dirty = true; + /* move from double word register to r0:r1: "vmov r0, r1, vm" + * then read r0 via dcc + */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_VMOV(1, 1, 0, (num >> 4), (num & 0xf)), + &value_r0); + /* read r1 via dcc */ + retval = dpm->instr_read_data_dcc(dpm, + ARMV4_5_MCR(14, 0, 1, 0, 5, 0), + &value_r1); + if (retval == ERROR_OK) { + *lvalue = value_r1; + *lvalue = ((*lvalue) << 32) | value_r0; + } else + return retval; + + num++; + /* repeat above steps for high 64 bits of V register */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_VMOV(1, 1, 0, (num >> 4), (num & 0xf)), + &value_r0); + retval = dpm->instr_read_data_dcc(dpm, + ARMV4_5_MCR(14, 0, 1, 0, 5, 0), + &value_r1); + if (retval == ERROR_OK) { + *hvalue = value_r1; + *hvalue = ((*hvalue) << 32) | value_r0; + } else + return retval; + break; + default: + retval = ERROR_FAIL; + break; + } + + return retval; +} + static int armv8_write_reg32(struct armv8_common *armv8, int regnum, uint64_t value) { struct arm_dpm *dpm = &armv8->dpm; @@ -417,6 +546,11 @@ static int armv8_write_reg32(struct armv8_common *armv8, int regnum, uint64_t va ARMV8_MSR_GP_xPSR_T1(1, 0, 15), value); break; + case ARMV8_FPSR: + /* move to r0 from DCC, then "VMSR FPSCR, r0" */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_VMSR(0), value); + break; default: retval = ERROR_FAIL; break; @@ -426,14 +560,63 @@ static int armv8_write_reg32(struct armv8_common *armv8, int regnum, uint64_t va } +static int armv8_write_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum, uint64_t lvalue, uint64_t hvalue) +{ + int retval = ERROR_FAIL; + struct arm_dpm *dpm = &armv8->dpm; + struct reg *reg_r1 = dpm->arm->core_cache->reg_list + ARMV8_R1; + uint32_t value_r0 = 0, value_r1 = 0; + unsigned num = (regnum - ARMV8_V0) << 1; + + switch (regnum) { + case ARMV8_V0 ... ARMV8_V15: + /* we are going to write R1, mark it dirty */ + reg_r1->dirty = true; + value_r1 = lvalue >> 32; + value_r0 = lvalue & 0xFFFFFFFF; + /* write value_r1 to r1 via dcc */ + retval = dpm->instr_write_data_dcc(dpm, + ARMV4_5_MRC(14, 0, 1, 0, 5, 0), + value_r1); + /* write value_r0 to r0 via dcc then, + * move to double word register from r0:r1: "vmov vm, r0, r1" + */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_VMOV(0, 1, 0, (num >> 4), (num & 0xf)), + value_r0); + + num++; + /* repeat above steps for high 64 bits of V register */ + value_r1 = hvalue >> 32; + value_r0 = hvalue & 0xFFFFFFFF; + retval = dpm->instr_write_data_dcc(dpm, + ARMV4_5_MRC(14, 0, 1, 0, 5, 0), + value_r1); + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_VMOV(0, 1, 0, (num >> 4), (num & 0xf)), + value_r0); + break; + default: + retval = ERROR_FAIL; + break; + } + + return retval; +} + void armv8_select_reg_access(struct armv8_common *armv8, bool is_aarch64) { if (is_aarch64) { armv8->read_reg_u64 = armv8_read_reg; armv8->write_reg_u64 = armv8_write_reg; + armv8->read_reg_u128 = armv8_read_reg_simdfp_aarch64; + armv8->write_reg_u128 = armv8_write_reg_simdfp_aarch64; + } else { armv8->read_reg_u64 = armv8_read_reg32; armv8->write_reg_u64 = armv8_write_reg32; + armv8->read_reg_u128 = armv8_read_reg_simdfp_aarch32; + armv8->write_reg_u128 = armv8_write_reg_simdfp_aarch32; } } @@ -441,6 +624,7 @@ void armv8_select_reg_access(struct armv8_common *armv8, bool is_aarch64) int armv8_read_mpidr(struct armv8_common *armv8) { int retval = ERROR_FAIL; + struct arm *arm = &armv8->arm; struct arm_dpm *dpm = armv8->arm.dpm; uint32_t mpidr; @@ -448,6 +632,13 @@ int armv8_read_mpidr(struct armv8_common *armv8) if (retval != ERROR_OK) goto done; + /* check if we're in an unprivileged mode */ + if (armv8_curel_from_core_mode(arm->core_mode) < SYSTEM_CUREL_EL1) { + retval = armv8_dpm_modeswitch(dpm, ARMV8_64_EL1H); + if (retval != ERROR_OK) + return retval; + } + retval = dpm->instr_read_data_r0(dpm, armv8_opcode(armv8, READ_REG_MPIDR), &mpidr); if (retval != ERROR_OK) goto done; @@ -463,6 +654,7 @@ int armv8_read_mpidr(struct armv8_common *armv8) LOG_ERROR("mpidr not in multiprocessor format"); done: + armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); dpm->finish(dpm); return retval; } @@ -825,11 +1017,24 @@ int armv8_handle_cache_info_command(struct command_context *cmd_ctx, return ERROR_OK; } +static int armv8_setup_semihosting(struct target *target, int enable) +{ + struct arm *arm = target_to_arm(target); + + if (arm->core_state != ARM_STATE_AARCH64) { + LOG_ERROR("semihosting only supported in AArch64 state\n"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + int armv8_init_arch_info(struct target *target, struct armv8_common *armv8) { struct arm *arm = &armv8->arm; arm->arch_info = armv8; target->arch_info = &armv8->arm; + arm->setup_semihosting = armv8_setup_semihosting; /* target is useful in all function arm v4 5 compatible */ armv8->arm.target = target; armv8->arm.common_magic = ARM_COMMON_MAGIC; @@ -858,7 +1063,7 @@ int armv8_aarch64_state(struct target *target) armv8_mode_name(arm->core_mode), buf_get_u32(arm->cpsr->value, 0, 32), buf_get_u64(arm->pc->value, 0, 64), - arm->is_semihosting ? ", semihosting" : ""); + (target->semihosting && target->semihosting->is_active) ? ", semihosting" : ""); return ERROR_OK; } @@ -897,6 +1102,152 @@ int armv8_arch_state(struct target *target) return ERROR_OK; } +static struct reg_data_type aarch64_vector_base_types[] = { + {REG_TYPE_IEEE_DOUBLE, "ieee_double", 0, {NULL} }, + {REG_TYPE_UINT64, "uint64", 0, {NULL} }, + {REG_TYPE_INT64, "int64", 0, {NULL} }, + {REG_TYPE_IEEE_SINGLE, "ieee_single", 0, {NULL} }, + {REG_TYPE_UINT32, "uint32", 0, {NULL} }, + {REG_TYPE_INT32, "int32", 0, {NULL} }, + {REG_TYPE_UINT16, "uint16", 0, {NULL} }, + {REG_TYPE_INT16, "int16", 0, {NULL} }, + {REG_TYPE_UINT8, "uint8", 0, {NULL} }, + {REG_TYPE_INT8, "int8", 0, {NULL} }, + {REG_TYPE_UINT128, "uint128", 0, {NULL} }, + {REG_TYPE_INT128, "int128", 0, {NULL} } +}; + +static struct reg_data_type_vector aarch64_vector_types[] = { + {aarch64_vector_base_types + 0, 2}, + {aarch64_vector_base_types + 1, 2}, + {aarch64_vector_base_types + 2, 2}, + {aarch64_vector_base_types + 3, 4}, + {aarch64_vector_base_types + 4, 4}, + {aarch64_vector_base_types + 5, 4}, + {aarch64_vector_base_types + 6, 8}, + {aarch64_vector_base_types + 7, 8}, + {aarch64_vector_base_types + 8, 16}, + {aarch64_vector_base_types + 9, 16}, + {aarch64_vector_base_types + 10, 01}, + {aarch64_vector_base_types + 11, 01}, +}; + +static struct reg_data_type aarch64_fpu_vector[] = { + {REG_TYPE_ARCH_DEFINED, "v2d", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 0} }, + {REG_TYPE_ARCH_DEFINED, "v2u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 1} }, + {REG_TYPE_ARCH_DEFINED, "v2i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 2} }, + {REG_TYPE_ARCH_DEFINED, "v4f", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 3} }, + {REG_TYPE_ARCH_DEFINED, "v4u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 4} }, + {REG_TYPE_ARCH_DEFINED, "v4i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 5} }, + {REG_TYPE_ARCH_DEFINED, "v8u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 6} }, + {REG_TYPE_ARCH_DEFINED, "v8i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 7} }, + {REG_TYPE_ARCH_DEFINED, "v16u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 8} }, + {REG_TYPE_ARCH_DEFINED, "v16i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 9} }, + {REG_TYPE_ARCH_DEFINED, "v1u", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 10} }, + {REG_TYPE_ARCH_DEFINED, "v1i", REG_TYPE_CLASS_VECTOR, {aarch64_vector_types + 11} }, +}; + +static struct reg_data_type_union_field aarch64_union_fields_vnd[] = { + {"f", aarch64_fpu_vector + 0, aarch64_union_fields_vnd + 1}, + {"u", aarch64_fpu_vector + 1, aarch64_union_fields_vnd + 2}, + {"s", aarch64_fpu_vector + 2, NULL}, +}; + +static struct reg_data_type_union_field aarch64_union_fields_vns[] = { + {"f", aarch64_fpu_vector + 3, aarch64_union_fields_vns + 1}, + {"u", aarch64_fpu_vector + 4, aarch64_union_fields_vns + 2}, + {"s", aarch64_fpu_vector + 5, NULL}, +}; + +static struct reg_data_type_union_field aarch64_union_fields_vnh[] = { + {"u", aarch64_fpu_vector + 6, aarch64_union_fields_vnh + 1}, + {"s", aarch64_fpu_vector + 7, NULL}, +}; + +static struct reg_data_type_union_field aarch64_union_fields_vnb[] = { + {"u", aarch64_fpu_vector + 8, aarch64_union_fields_vnb + 1}, + {"s", aarch64_fpu_vector + 9, NULL}, +}; + +static struct reg_data_type_union_field aarch64_union_fields_vnq[] = { + {"u", aarch64_fpu_vector + 10, aarch64_union_fields_vnq + 1}, + {"s", aarch64_fpu_vector + 11, NULL}, +}; + +static struct reg_data_type_union aarch64_union_types[] = { + {aarch64_union_fields_vnd}, + {aarch64_union_fields_vns}, + {aarch64_union_fields_vnh}, + {aarch64_union_fields_vnb}, + {aarch64_union_fields_vnq}, +}; + +static struct reg_data_type aarch64_fpu_union[] = { + {REG_TYPE_ARCH_DEFINED, "vnd", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 0} }, + {REG_TYPE_ARCH_DEFINED, "vns", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 1} }, + {REG_TYPE_ARCH_DEFINED, "vnh", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 2} }, + {REG_TYPE_ARCH_DEFINED, "vnb", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 3} }, + {REG_TYPE_ARCH_DEFINED, "vnq", REG_TYPE_CLASS_UNION, {.reg_type_union = aarch64_union_types + 4} }, +}; + +static struct reg_data_type_union_field aarch64v_union_fields[] = { + {"d", aarch64_fpu_union + 0, aarch64v_union_fields + 1}, + {"s", aarch64_fpu_union + 1, aarch64v_union_fields + 2}, + {"h", aarch64_fpu_union + 2, aarch64v_union_fields + 3}, + {"b", aarch64_fpu_union + 3, aarch64v_union_fields + 4}, + {"q", aarch64_fpu_union + 4, NULL}, +}; + +static struct reg_data_type_union aarch64v_union[] = { + {aarch64v_union_fields} +}; + +static struct reg_data_type aarch64v[] = { + {REG_TYPE_ARCH_DEFINED, "aarch64v", REG_TYPE_CLASS_UNION, + {.reg_type_union = aarch64v_union} }, +}; + +static struct reg_data_type_bitfield aarch64_cpsr_bits[] = { + { 0, 0 , REG_TYPE_UINT8 }, + { 2, 3, REG_TYPE_UINT8 }, + { 4, 4 , REG_TYPE_UINT8 }, + { 6, 6 , REG_TYPE_BOOL }, + { 7, 7 , REG_TYPE_BOOL }, + { 8, 8 , REG_TYPE_BOOL }, + { 9, 9 , REG_TYPE_BOOL }, + { 20, 20, REG_TYPE_BOOL }, + { 21, 21, REG_TYPE_BOOL }, + { 28, 28, REG_TYPE_BOOL }, + { 29, 29, REG_TYPE_BOOL }, + { 30, 30, REG_TYPE_BOOL }, + { 31, 31, REG_TYPE_BOOL }, +}; + +static struct reg_data_type_flags_field aarch64_cpsr_fields[] = { + { "SP", aarch64_cpsr_bits + 0, aarch64_cpsr_fields + 1 }, + { "EL", aarch64_cpsr_bits + 1, aarch64_cpsr_fields + 2 }, + { "nRW", aarch64_cpsr_bits + 2, aarch64_cpsr_fields + 3 }, + { "F" , aarch64_cpsr_bits + 3, aarch64_cpsr_fields + 4 }, + { "I" , aarch64_cpsr_bits + 4, aarch64_cpsr_fields + 5 }, + { "A" , aarch64_cpsr_bits + 5, aarch64_cpsr_fields + 6 }, + { "D" , aarch64_cpsr_bits + 6, aarch64_cpsr_fields + 7 }, + { "IL" , aarch64_cpsr_bits + 7, aarch64_cpsr_fields + 8 }, + { "SS" , aarch64_cpsr_bits + 8, aarch64_cpsr_fields + 9 }, + { "V" , aarch64_cpsr_bits + 9, aarch64_cpsr_fields + 10 }, + { "C" , aarch64_cpsr_bits + 10, aarch64_cpsr_fields + 11 }, + { "Z" , aarch64_cpsr_bits + 11, aarch64_cpsr_fields + 12 }, + { "N" , aarch64_cpsr_bits + 12, NULL } +}; + +static struct reg_data_type_flags aarch64_cpsr_flags[] = { + { 4, aarch64_cpsr_fields } +}; + +static struct reg_data_type aarch64_flags_cpsr[] = { + {REG_TYPE_ARCH_DEFINED, "cpsr_flags", REG_TYPE_CLASS_FLAGS, + {.reg_type_flags = aarch64_cpsr_flags} }, +}; + static const struct { unsigned id; const char *name; @@ -905,59 +1256,104 @@ static const struct { enum reg_type type; const char *group; const char *feature; + struct reg_data_type *data_type; } armv8_regs[] = { - { ARMV8_R0, "x0", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R1, "x1", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R2, "x2", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R3, "x3", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R4, "x4", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R5, "x5", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R6, "x6", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R7, "x7", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R8, "x8", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R9, "x9", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R10, "x10", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R11, "x11", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R12, "x12", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R13, "x13", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R14, "x14", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R15, "x15", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R16, "x16", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R17, "x17", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R18, "x18", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R19, "x19", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R20, "x20", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R21, "x21", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R22, "x22", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R23, "x23", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R24, "x24", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R25, "x25", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R26, "x26", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R27, "x27", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R28, "x28", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R29, "x29", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_R30, "x30", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core" }, - - { ARMV8_SP, "sp", 64, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.aarch64.core" }, - { ARMV8_PC, "pc", 64, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.aarch64.core" }, - - { ARMV8_xPSR, "CPSR", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.aarch64.core" }, - - { ARMV8_ELR_EL1, "ELR_EL1", 64, ARMV8_64_EL1H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked" }, - { ARMV8_ESR_EL1, "ESR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" }, - { ARMV8_SPSR_EL1, "SPSR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" }, - - { ARMV8_ELR_EL2, "ELR_EL2", 64, ARMV8_64_EL2H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked" }, - { ARMV8_ESR_EL2, "ESR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" }, - { ARMV8_SPSR_EL2, "SPSR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" }, - - { ARMV8_ELR_EL3, "ELR_EL3", 64, ARMV8_64_EL3H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked" }, - { ARMV8_ESR_EL3, "ESR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" }, - { ARMV8_SPSR_EL3, "SPSR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked" }, + { ARMV8_R0, "x0", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R1, "x1", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R2, "x2", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R3, "x3", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R4, "x4", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R5, "x5", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R6, "x6", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R7, "x7", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R8, "x8", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R9, "x9", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R10, "x10", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R11, "x11", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R12, "x12", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R13, "x13", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R14, "x14", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R15, "x15", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R16, "x16", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R17, "x17", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R18, "x18", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R19, "x19", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R20, "x20", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R21, "x21", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R22, "x22", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R23, "x23", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R24, "x24", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R25, "x25", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R26, "x26", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R27, "x27", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R28, "x28", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R29, "x29", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_R30, "x30", 64, ARM_MODE_ANY, REG_TYPE_UINT64, "general", "org.gnu.gdb.aarch64.core", NULL}, + + { ARMV8_SP, "sp", 64, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_PC, "pc", 64, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.aarch64.core", NULL}, + { ARMV8_xPSR, "cpsr", 32, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, + "general", "org.gnu.gdb.aarch64.core", aarch64_flags_cpsr}, + { ARMV8_V0, "v0", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V1, "v1", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V2, "v2", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V3, "v3", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V4, "v4", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V5, "v5", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V6, "v6", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V7, "v7", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V8, "v8", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V9, "v9", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V10, "v10", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V11, "v11", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V12, "v12", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V13, "v13", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V14, "v14", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V15, "v15", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V16, "v16", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V17, "v17", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V18, "v18", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V19, "v19", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V20, "v20", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V21, "v21", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V22, "v22", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V23, "v23", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V24, "v24", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V25, "v25", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V26, "v26", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V27, "v27", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V28, "v28", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V29, "v29", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V30, "v30", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_V31, "v31", 128, ARM_MODE_ANY, REG_TYPE_ARCH_DEFINED, "simdfp", "org.gnu.gdb.aarch64.fpu", aarch64v}, + { ARMV8_FPSR, "fpsr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "simdfp", "org.gnu.gdb.aarch64.fpu", NULL}, + { ARMV8_FPCR, "fpcr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "simdfp", "org.gnu.gdb.aarch64.fpu", NULL}, + + { ARMV8_ELR_EL1, "ELR_EL1", 64, ARMV8_64_EL1H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked", + NULL}, + { ARMV8_ESR_EL1, "ESR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked", + NULL}, + { ARMV8_SPSR_EL1, "SPSR_EL1", 32, ARMV8_64_EL1H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked", + NULL}, + + { ARMV8_ELR_EL2, "ELR_EL2", 64, ARMV8_64_EL2H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked", + NULL}, + { ARMV8_ESR_EL2, "ESR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked", + NULL}, + { ARMV8_SPSR_EL2, "SPSR_EL2", 32, ARMV8_64_EL2H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked", + NULL}, + + { ARMV8_ELR_EL3, "ELR_EL3", 64, ARMV8_64_EL3H, REG_TYPE_CODE_PTR, "banked", "net.sourceforge.openocd.banked", + NULL}, + { ARMV8_ESR_EL3, "ESR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked", + NULL}, + { ARMV8_SPSR_EL3, "SPSR_EL3", 32, ARMV8_64_EL3H, REG_TYPE_UINT32, "banked", "net.sourceforge.openocd.banked", + NULL}, }; static const struct { unsigned id; + unsigned mapping; const char *name; unsigned bits; enum arm_mode mode; @@ -965,23 +1361,56 @@ static const struct { const char *group; const char *feature; } armv8_regs32[] = { - { ARMV8_R0, "r0", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R1, "r1", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R2, "r2", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R3, "r3", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R4, "r4", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R5, "r5", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R6, "r6", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R7, "r7", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R8, "r8", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R9, "r9", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R10, "r10", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R11, "r11", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R12, "r12", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R13, "sp", 32, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_R14, "lr", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_PC, "pc", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" }, - { ARMV8_xPSR, "cpsr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R0, 0, "r0", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R1, 0, "r1", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R2, 0, "r2", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R3, 0, "r3", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R4, 0, "r4", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R5, 0, "r5", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R6, 0, "r6", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R7, 0, "r7", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R8, 0, "r8", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R9, 0, "r9", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R10, 0, "r10", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R11, 0, "r11", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R12, 0, "r12", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R13, 0, "sp", 32, ARM_MODE_ANY, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_R14, 0, "lr", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_PC, 0, "pc", 32, ARM_MODE_ANY, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_xPSR, 0, "cpsr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "general", "org.gnu.gdb.arm.core" }, + { ARMV8_V0, 0, "d0", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V0, 8, "d1", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V1, 0, "d2", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V1, 8, "d3", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V2, 0, "d4", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V2, 8, "d5", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V3, 0, "d6", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V3, 8, "d7", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V4, 0, "d8", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V4, 8, "d9", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V5, 0, "d10", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V5, 8, "d11", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V6, 0, "d12", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V6, 8, "d13", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V7, 0, "d14", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V7, 8, "d15", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V8, 0, "d16", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V8, 8, "d17", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V9, 0, "d18", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V9, 8, "d19", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V10, 0, "d20", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V10, 8, "d21", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V11, 0, "d22", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V11, 8, "d23", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V12, 0, "d24", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V12, 8, "d25", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V13, 0, "d26", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V13, 8, "d27", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V14, 0, "d28", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V14, 8, "d29", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V15, 0, "d30", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_V15, 8, "d31", 64, ARM_MODE_ANY, REG_TYPE_IEEE_DOUBLE, NULL, "org.gnu.gdb.arm.vfp"}, + { ARMV8_FPSR, 0, "fpscr", 32, ARM_MODE_ANY, REG_TYPE_UINT32, "float", "org.gnu.gdb.arm.vfp"}, }; #define ARMV8_NUM_REGS ARRAY_SIZE(armv8_regs) @@ -1004,15 +1433,23 @@ static int armv8_set_core_reg(struct reg *reg, uint8_t *buf) struct arm_reg *armv8_reg = reg->arch_info; struct target *target = armv8_reg->target; struct arm *arm = target_to_arm(target); - uint64_t value = buf_get_u64(buf, 0, 64); + uint64_t value = buf_get_u64(buf, 0, reg->size); if (target->state != TARGET_HALTED) return ERROR_TARGET_NOT_HALTED; - if (reg == arm->cpsr) { - armv8_set_cpsr(arm, (uint32_t)value); - } else { + if (reg->size <= 64) { + if (reg == arm->cpsr) + armv8_set_cpsr(arm, (uint32_t)value); + else { + buf_set_u64(reg->value, 0, reg->size, value); + reg->valid = 1; + } + } else if (reg->size <= 128) { + uint64_t hvalue = buf_get_u64(buf + 8, 0, reg->size - 64); + buf_set_u64(reg->value, 0, 64, value); + buf_set_u64(reg->value + 8, 0, reg->size - 64, hvalue); reg->valid = 1; } @@ -1061,7 +1498,12 @@ static int armv8_set_core_reg32(struct reg *reg, uint8_t *buf) if (reg64 == arm->cpsr) { armv8_set_cpsr(arm, value); } else { - buf_set_u32(reg->value, 0, 32, value); + if (reg->size <= 32) + buf_set_u32(reg->value, 0, 32, value); + else if (reg->size <= 64) { + uint64_t value64 = buf_get_u64(buf, 0, 64); + buf_set_u64(reg->value, 0, 64, value64); + } reg->valid = 1; reg64->valid = 1; } @@ -1123,9 +1565,12 @@ struct reg_cache *armv8_build_reg_cache(struct target *target) LOG_ERROR("unable to allocate feature list"); reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type)); - if (reg_list[i].reg_data_type) - reg_list[i].reg_data_type->type = armv8_regs[i].type; - else + if (reg_list[i].reg_data_type) { + if (armv8_regs[i].data_type == NULL) + reg_list[i].reg_data_type->type = armv8_regs[i].type; + else + *reg_list[i].reg_data_type = *armv8_regs[i].data_type; + } else LOG_ERROR("unable to allocate reg type list"); } @@ -1142,7 +1587,7 @@ struct reg_cache *armv8_build_reg_cache(struct target *target) for (i = 0; i < num_regs32; i++) { reg_list32[i].name = armv8_regs32[i].name; reg_list32[i].size = armv8_regs32[i].bits; - reg_list32[i].value = &arch_info[armv8_regs32[i].id].value[0]; + reg_list32[i].value = &arch_info[armv8_regs32[i].id].value[armv8_regs32[i].mapping]; reg_list32[i].type = &armv8_reg32_type; reg_list32[i].arch_info = &arch_info[armv8_regs32[i].id]; reg_list32[i].group = armv8_regs32[i].group; @@ -1179,14 +1624,45 @@ struct reg *armv8_reg_current(struct arm *arm, unsigned regnum) return r; } +static void armv8_free_cache(struct reg_cache *cache, bool regs32) +{ + struct reg *reg; + unsigned int i; + + if (!cache) + return; + + for (i = 0; i < cache->num_regs; i++) { + reg = &cache->reg_list[i]; + + free(reg->feature); + free(reg->reg_data_type); + } + + if (!regs32) + free(cache->reg_list[0].arch_info); + free(cache->reg_list); + free(cache); +} + +void armv8_free_reg_cache(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + struct reg_cache *cache = NULL, *cache32 = NULL; + + cache = arm->core_cache; + if (cache != NULL) + cache32 = cache->next; + armv8_free_cache(cache32, true); + armv8_free_cache(cache, false); + arm->core_cache = NULL; +} + const struct command_registration armv8_command_handlers[] = { - { - .chain = dap_command_handlers, - }, COMMAND_REGISTRATION_DONE }; - int armv8_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class) @@ -1200,7 +1676,7 @@ int armv8_get_gdb_reg_list(struct target *target, switch (reg_class) { case REG_CLASS_GENERAL: - *reg_list_size = ARMV8_ELR_EL1; + *reg_list_size = ARMV8_V0; *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size)); for (i = 0; i < *reg_list_size; i++) @@ -1227,6 +1703,13 @@ int armv8_get_gdb_reg_list(struct target *target, switch (reg_class) { case REG_CLASS_GENERAL: + *reg_list_size = ARMV8_R14 + 3; + *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size)); + + for (i = 0; i < *reg_list_size; i++) + (*reg_list)[i] = cache32->reg_list + i; + + return ERROR_OK; case REG_CLASS_ALL: *reg_list_size = cache32->num_regs; *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size)); diff --git a/src/target/armv8.h b/src/target/armv8.h index 0f3e66f..dfd54ed 100644 --- a/src/target/armv8.h +++ b/src/target/armv8.h @@ -63,21 +63,62 @@ enum { ARMV8_PC = 32, ARMV8_xPSR = 33, - ARMV8_ELR_EL1 = 34, - ARMV8_ESR_EL1 = 35, - ARMV8_SPSR_EL1 = 36, - - ARMV8_ELR_EL2 = 37, - ARMV8_ESR_EL2 = 38, - ARMV8_SPSR_EL2 = 39, - - ARMV8_ELR_EL3 = 40, - ARMV8_ESR_EL3 = 41, - ARMV8_SPSR_EL3 = 42, + ARMV8_V0 = 34, + ARMV8_V1, + ARMV8_V2, + ARMV8_V3, + ARMV8_V4, + ARMV8_V5, + ARMV8_V6, + ARMV8_V7, + ARMV8_V8, + ARMV8_V9, + ARMV8_V10, + ARMV8_V11, + ARMV8_V12, + ARMV8_V13, + ARMV8_V14, + ARMV8_V15, + ARMV8_V16, + ARMV8_V17, + ARMV8_V18, + ARMV8_V19, + ARMV8_V20, + ARMV8_V21, + ARMV8_V22, + ARMV8_V23, + ARMV8_V24, + ARMV8_V25, + ARMV8_V26, + ARMV8_V27, + ARMV8_V28, + ARMV8_V29, + ARMV8_V30, + ARMV8_V31, + ARMV8_FPSR, + ARMV8_FPCR, + + ARMV8_ELR_EL1 = 68, + ARMV8_ESR_EL1 = 69, + ARMV8_SPSR_EL1 = 70, + + ARMV8_ELR_EL2 = 71, + ARMV8_ESR_EL2 = 72, + ARMV8_SPSR_EL2 = 73, + + ARMV8_ELR_EL3 = 74, + ARMV8_ESR_EL3 = 75, + ARMV8_SPSR_EL3 = 76, ARMV8_LAST_REG, }; +enum run_control_op { + ARMV8_RUNCONTROL_UNKNOWN = 0, + ARMV8_RUNCONTROL_RESUME = 1, + ARMV8_RUNCONTROL_HALT = 2, + ARMV8_RUNCONTROL_STEP = 3, +}; #define ARMV8_COMMON_MAGIC 0x0A450AAA @@ -175,10 +216,19 @@ struct armv8_common { struct arm_cti *cti; + /* last run-control command issued to this target (resume, halt, step) */ + enum run_control_op last_run_control_op; + /* Direct processor core register read and writes */ int (*read_reg_u64)(struct armv8_common *armv8, int num, uint64_t *value); int (*write_reg_u64)(struct armv8_common *armv8, int num, uint64_t value); + /* SIMD/FPU registers read/write interface */ + int (*read_reg_u128)(struct armv8_common *armv8, int num, + uint64_t *lvalue, uint64_t *hvalue); + int (*write_reg_u128)(struct armv8_common *armv8, int num, + uint64_t lvalue, uint64_t hvalue); + int (*examine_debug_reason)(struct target *target); int (*post_debug_entry)(struct target *target); @@ -191,6 +241,11 @@ target_to_armv8(struct target *target) return container_of(target->arch_info, struct armv8_common, arm); } +static inline bool is_armv8(struct armv8_common *armv8) +{ + return armv8->common_magic == ARMV8_COMMON_MAGIC; +} + /* register offsets from armv8.debug_base */ #define CPUV8_DBG_MAINID0 0xD00 #define CPUV8_DBG_CPUFEATURE0 0xD20 @@ -277,6 +332,8 @@ static inline unsigned int armv8_curel_from_core_mode(enum arm_mode core_mode) void armv8_select_reg_access(struct armv8_common *armv8, bool is_aarch64); int armv8_set_dbgreg_bits(struct armv8_common *armv8, unsigned int reg, unsigned long mask, unsigned long value); +extern void armv8_free_reg_cache(struct target *target); + extern const struct command_registration armv8_command_handlers[]; #endif /* OPENOCD_TARGET_ARMV8_H */ diff --git a/src/target/armv8_cache.c b/src/target/armv8_cache.c index 7f610c9..40965eb 100644 --- a/src/target/armv8_cache.c +++ b/src/target/armv8_cache.c @@ -310,6 +310,7 @@ int armv8_identify_cache(struct armv8_common *armv8) { /* read cache descriptor */ int retval = ERROR_FAIL; + struct arm *arm = &armv8->arm; struct arm_dpm *dpm = armv8->arm.dpm; uint32_t csselr, clidr, ctr; uint32_t cache_reg; @@ -320,6 +321,13 @@ int armv8_identify_cache(struct armv8_common *armv8) if (retval != ERROR_OK) goto done; + /* check if we're in an unprivileged mode */ + if (armv8_curel_from_core_mode(arm->core_mode) < SYSTEM_CUREL_EL1) { + retval = armv8_dpm_modeswitch(dpm, ARMV8_64_EL1H); + if (retval != ERROR_OK) + return retval; + } + /* retrieve CTR */ retval = dpm->instr_read_data_r0(dpm, armv8_opcode(armv8, READ_REG_CTR), &ctr); @@ -417,6 +425,7 @@ int armv8_identify_cache(struct armv8_common *armv8) } done: + armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); dpm->finish(dpm); return retval; diff --git a/src/target/armv8_dpm.c b/src/target/armv8_dpm.c index c79b1a0..3c941fa 100644 --- a/src/target/armv8_dpm.c +++ b/src/target/armv8_dpm.c @@ -258,7 +258,7 @@ static int dpmv8_exec_opcode(struct arm_dpm *dpm, if (dscr & DSCR_ERR) { LOG_ERROR("Opcode 0x%08"PRIx32", DSCR.ERR=1, DSCR.EL=%i", opcode, dpm->last_el); - armv8_dpm_handle_exception(dpm); + armv8_dpm_handle_exception(dpm, true); retval = ERROR_FAIL; } @@ -600,7 +600,7 @@ int armv8_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) armv8_opcode(armv8, ARMV8_OPC_DCPS) | target_el); /* DCPS clobbers registers just like an exception taken */ - armv8_dpm_handle_exception(dpm); + armv8_dpm_handle_exception(dpm, false); } else { core_state = armv8_dpm_get_core_state(dpm); if (core_state != ARM_STATE_AARCH64) { @@ -650,21 +650,37 @@ int armv8_dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode) static int dpmv8_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) { struct armv8_common *armv8 = dpm->arm->arch_info; - uint64_t value_64; - int retval; + int retval = ERROR_FAIL; + + if (r->size <= 64) { + uint64_t value_64; + retval = armv8->read_reg_u64(armv8, regnum, &value_64); + + if (retval == ERROR_OK) { + r->valid = true; + r->dirty = false; + buf_set_u64(r->value, 0, r->size, value_64); + if (r->size == 64) + LOG_DEBUG("READ: %s, %16.8llx", r->name, (unsigned long long) value_64); + else + LOG_DEBUG("READ: %s, %8.8x", r->name, (unsigned int) value_64); + } + } else if (r->size <= 128) { + uint64_t lvalue = 0, hvalue = 0; + retval = armv8->read_reg_u128(armv8, regnum, &lvalue, &hvalue); + + if (retval == ERROR_OK) { + r->valid = true; + r->dirty = false; - retval = armv8->read_reg_u64(armv8, regnum, &value_64); + buf_set_u64(r->value, 0, 64, lvalue); + buf_set_u64(r->value + 8, 0, r->size - 64, hvalue); - if (retval == ERROR_OK) { - r->valid = true; - r->dirty = false; - buf_set_u64(r->value, 0, r->size, value_64); - if (r->size == 64) - LOG_DEBUG("READ: %s, %16.8llx", r->name, (unsigned long long) value_64); - else - LOG_DEBUG("READ: %s, %8.8x", r->name, (unsigned int) value_64); + LOG_DEBUG("READ: %s, lvalue=%16.8llx", r->name, (unsigned long long) lvalue); + LOG_DEBUG("READ: %s, hvalue=%16.8llx", r->name, (unsigned long long) hvalue); + } } - return ERROR_OK; + return retval; } /* @@ -674,20 +690,36 @@ static int dpmv8_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum) { struct armv8_common *armv8 = dpm->arm->arch_info; int retval = ERROR_FAIL; - uint64_t value_64; - value_64 = buf_get_u64(r->value, 0, r->size); + if (r->size <= 64) { + uint64_t value_64; + + value_64 = buf_get_u64(r->value, 0, r->size); + retval = armv8->write_reg_u64(armv8, regnum, value_64); - retval = armv8->write_reg_u64(armv8, regnum, value_64); - if (retval == ERROR_OK) { - r->dirty = false; - if (r->size == 64) - LOG_DEBUG("WRITE: %s, %16.8llx", r->name, (unsigned long long)value_64); - else - LOG_DEBUG("WRITE: %s, %8.8x", r->name, (unsigned int)value_64); + if (retval == ERROR_OK) { + r->dirty = false; + if (r->size == 64) + LOG_DEBUG("WRITE: %s, %16.8llx", r->name, (unsigned long long)value_64); + else + LOG_DEBUG("WRITE: %s, %8.8x", r->name, (unsigned int)value_64); + } + } else if (r->size <= 128) { + uint64_t lvalue, hvalue; + + lvalue = buf_get_u64(r->value, 0, 64); + hvalue = buf_get_u64(r->value + 8, 0, r->size - 64); + retval = armv8->write_reg_u128(armv8, regnum, lvalue, hvalue); + + if (retval == ERROR_OK) { + r->dirty = false; + + LOG_DEBUG("WRITE: %s, lvalue=%16.8llx", r->name, (unsigned long long) lvalue); + LOG_DEBUG("WRITE: %s, hvalue=%16.8llx", r->name, (unsigned long long) hvalue); + } } - return ERROR_OK; + return retval; } /** @@ -745,6 +777,10 @@ int armv8_dpm_read_current_registers(struct arm_dpm *dpm) if (r->valid) continue; + /* Skip reading FP-SIMD registers */ + if (r->number >= ARMV8_V0 && r->number <= ARMV8_FPCR) + continue; + /* * Only read registers that are available from the * current EL (or core mode). @@ -1262,7 +1298,7 @@ void armv8_dpm_report_wfar(struct arm_dpm *dpm, uint64_t addr) * This function must not perform any actions that trigger another exception * or a recursion will happen. */ -void armv8_dpm_handle_exception(struct arm_dpm *dpm) +void armv8_dpm_handle_exception(struct arm_dpm *dpm, bool do_restore) { struct armv8_common *armv8 = dpm->arm->arch_info; struct reg_cache *cache = dpm->arm->core_cache; @@ -1308,7 +1344,8 @@ void armv8_dpm_handle_exception(struct arm_dpm *dpm) armv8_select_opcodes(armv8, core_state == ARM_STATE_AARCH64); armv8_select_reg_access(armv8, core_state == ARM_STATE_AARCH64); - armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); + if (do_restore) + armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); } /*----------------------------------------------------------------------*/ diff --git a/src/target/armv8_dpm.h b/src/target/armv8_dpm.h index c039359..f404403 100644 --- a/src/target/armv8_dpm.h +++ b/src/target/armv8_dpm.h @@ -116,7 +116,7 @@ void armv8_dpm_report_wfar(struct arm_dpm *, uint64_t wfar); #define PRCR_COREPURQ (1 << 3) void armv8_dpm_report_dscr(struct arm_dpm *dpm, uint32_t dcsr); -void armv8_dpm_handle_exception(struct arm_dpm *dpm); +void armv8_dpm_handle_exception(struct arm_dpm *dpm, bool do_restore); enum arm_state armv8_dpm_get_core_state(struct arm_dpm *dpm); #endif /* OPENOCD_TARGET_ARM_DPM_H */ diff --git a/src/target/armv8_opcodes.h b/src/target/armv8_opcodes.h index 987198a..3fda296 100644 --- a/src/target/armv8_opcodes.h +++ b/src/target/armv8_opcodes.h @@ -167,6 +167,14 @@ #define ARMV8_STRH_IP(Rd, Rn) (0x78002400 | (Rn << 5) | Rd) #define ARMV8_STRW_IP(Rd, Rn) (0xb8004400 | (Rn << 5) | Rd) +#define ARMV8_MOV_GPR_VFP(Rd, Rn, Index) (0x4e083c00 | (Index << 20) | (Rn << 5) | Rd) +#define ARMV8_MOV_VFP_GPR(Rd, Rn, Index) (0x4e081c00 | (Index << 20) | (Rn << 5) | Rd) + +#define ARMV8_MRS_FPCR(Rt) (0xd53b4400 | (Rt)) +#define ARMV8_MRS_FPSR(Rt) (0xd53b4420 | (Rt)) +#define ARMV8_MSR_FPCR(Rt) (0xd51b4400 | (Rt)) +#define ARMV8_MSR_FPSR(Rt) (0xd51b4420 | (Rt)) + #define ARMV8_SYS(System, Rt) (0xD5080000 | ((System) << 5) | Rt) enum armv8_opcode { diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c index 7cf4a69..58bcc86 100644 --- a/src/target/breakpoints.c +++ b/src/target/breakpoints.c @@ -315,11 +315,8 @@ int breakpoint_remove_internal(struct target *target, target_addr_t address) struct breakpoint *breakpoint = target->breakpoints; while (breakpoint) { - if ((breakpoint->address == address) && (breakpoint->asid == 0)) - break; - else if ((breakpoint->address == 0) && (breakpoint->asid == address)) - break; - else if ((breakpoint->address == address) && (breakpoint->asid != 0)) + if ((breakpoint->address == address) || + (breakpoint->address == 0 && breakpoint->asid == address)) break; breakpoint = breakpoint->next; } diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index 5d90e34..4aae5e4 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -54,7 +54,7 @@ #include "target_type.h" #include "arm_opcodes.h" #include "arm_semihosting.h" -#include "jtag/swd.h" +#include "transport/transport.h" #include <helper/time_support.h> static int cortex_a_poll(struct target *target); @@ -206,23 +206,27 @@ static int cortex_a_init_debug_access(struct target *target) /* lock memory-mapped access to debug registers to prevent * software interference */ - retval = mem_ap_write_atomic_u32(armv7a->debug_ap, + retval = mem_ap_write_u32(armv7a->debug_ap, armv7a->debug_base + CPUDBG_LOCKACCESS, 0); if (retval != ERROR_OK) return retval; /* Disable cacheline fills and force cache write-through in debug state */ - retval = mem_ap_write_atomic_u32(armv7a->debug_ap, + retval = mem_ap_write_u32(armv7a->debug_ap, armv7a->debug_base + CPUDBG_DSCCR, 0); if (retval != ERROR_OK) return retval; /* Disable TLB lookup and refill/eviction in debug state */ - retval = mem_ap_write_atomic_u32(armv7a->debug_ap, + retval = mem_ap_write_u32(armv7a->debug_ap, armv7a->debug_base + CPUDBG_DSMCR, 0); if (retval != ERROR_OK) return retval; + retval = dap_run(armv7a->debug_ap->dap); + if (retval != ERROR_OK) + return retval; + /* Enabling of instruction execution in debug mode is done in debug_entry code */ /* Resync breakpoint registers */ @@ -1293,6 +1297,9 @@ static int cortex_a_post_debug_entry(struct target *target) LOG_DEBUG("cp15_control_reg: %8.8" PRIx32, cortex_a->cp15_control_reg); cortex_a->cp15_control_reg_curr = cortex_a->cp15_control_reg; + if (!armv7a->is_armv7r) + armv7a_read_ttbcr(target); + if (armv7a->armv7a_mmu.armv7a_cache.info == -1) armv7a_identify_cache(target); @@ -1496,10 +1503,22 @@ static int cortex_a_set_breakpoint(struct target *target, brp_list[brp_i].value); } else if (breakpoint->type == BKPT_SOFT) { uint8_t code[4]; + /* length == 2: Thumb breakpoint */ if (breakpoint->length == 2) buf_set_u32(code, 0, 32, ARMV5_T_BKPT(0x11)); else + /* length == 3: Thumb-2 breakpoint, actual encoding is + * a regular Thumb BKPT instruction but we replace a + * 32bit Thumb-2 instruction, so fix-up the breakpoint + * length + */ + if (breakpoint->length == 3) { + buf_set_u32(code, 0, 32, ARMV5_T_BKPT(0x11)); + breakpoint->length = 4; + } else + /* length == 4, normal ARM breakpoint */ buf_set_u32(code, 0, 32, ARMV5_BKPT(0x11)); + retval = target_read_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, @@ -2931,12 +2950,6 @@ static int cortex_a_examine_first(struct target *target) int retval = ERROR_OK; uint32_t didr, cpuid, dbg_osreg; - retval = dap_dp_init(swjdp); - if (retval != ERROR_OK) { - LOG_ERROR("Could not initialize the debug port"); - return retval; - } - /* Search for the APB-AP - it is needed for access to debug registers */ retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv7a->debug_ap); if (retval != ERROR_OK) { @@ -3118,22 +3131,13 @@ static int cortex_a_init_target(struct command_context *cmd_ctx, } static int cortex_a_init_arch_info(struct target *target, - struct cortex_a_common *cortex_a, struct jtag_tap *tap) + struct cortex_a_common *cortex_a, struct adiv5_dap *dap) { struct armv7a_common *armv7a = &cortex_a->armv7a_common; /* Setup struct cortex_a_common */ cortex_a->common_magic = CORTEX_A_COMMON_MAGIC; - - /* tap has no dap initialized */ - if (!tap->dap) { - tap->dap = dap_init(); - - /* Leave (only) generic DAP stuff for debugport_init() */ - tap->dap->tap = tap; - } - - armv7a->arm.dap = tap->dap; + armv7a->arm.dap = dap; cortex_a->fast_reg_read = 0; @@ -3159,19 +3163,34 @@ static int cortex_a_init_arch_info(struct target *target, static int cortex_a_target_create(struct target *target, Jim_Interp *interp) { struct cortex_a_common *cortex_a = calloc(1, sizeof(struct cortex_a_common)); + cortex_a->common_magic = CORTEX_A_COMMON_MAGIC; + struct adiv5_private_config *pc; + + if (target->private_config == NULL) + return ERROR_FAIL; + + pc = (struct adiv5_private_config *)target->private_config; cortex_a->armv7a_common.is_armv7r = false; - return cortex_a_init_arch_info(target, cortex_a, target->tap); + cortex_a->armv7a_common.arm.arm_vfp_version = ARM_VFP_V3; + + return cortex_a_init_arch_info(target, cortex_a, pc->dap); } static int cortex_r4_target_create(struct target *target, Jim_Interp *interp) { struct cortex_a_common *cortex_a = calloc(1, sizeof(struct cortex_a_common)); + cortex_a->common_magic = CORTEX_A_COMMON_MAGIC; + struct adiv5_private_config *pc; + + pc = (struct adiv5_private_config *)target->private_config; + if (adiv5_verify_config(pc) != ERROR_OK) + return ERROR_FAIL; cortex_a->armv7a_common.is_armv7r = true; - return cortex_a_init_arch_info(target, cortex_a, target->tap); + return cortex_a_init_arch_info(target, cortex_a, pc->dap); } static void cortex_a_deinit_target(struct target *target) @@ -3182,6 +3201,7 @@ static void cortex_a_deinit_target(struct target *target) free(cortex_a->brp_list); free(dpm->dbp); free(dpm->dwp); + free(target->private_config); free(cortex_a); } @@ -3209,6 +3229,20 @@ static int cortex_a_virt2phys(struct target *target, struct armv7a_common *armv7a = target_to_armv7a(target); struct adiv5_dap *swjdp = armv7a->arm.dap; uint8_t apsel = swjdp->apsel; + int mmu_enabled = 0; + + /* + * If the MMU was not enabled at debug entry, there is no + * way of knowing if there was ever a valid configuration + * for it and thus it's not safe to enable it. In this case, + * just return the virtual address as physical. + */ + cortex_a_mmu(target, &mmu_enabled); + if (!mmu_enabled) { + *phys = virt; + return ERROR_OK; + } + if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) { uint32_t ret; retval = armv7a_mmu_translate_va(target, @@ -3404,7 +3438,7 @@ static const struct command_registration cortex_a_exec_command_handlers[] = { { .name = "dacrfixup", .handler = handle_cortex_a_dacrfixup_command, - .mode = COMMAND_EXEC, + .mode = COMMAND_ANY, .help = "set domain access control (DACR) to all-manager " "on memory access", .usage = "['on'|'off']", @@ -3466,6 +3500,7 @@ struct target_type cortexa_target = { .commands = cortex_a_command_handlers, .target_create = cortex_a_target_create, + .target_jim_configure = adiv5_jim_configure, .init_target = cortex_a_init_target, .examine = cortex_a_examine, .deinit_target = cortex_a_deinit_target, @@ -3478,13 +3513,6 @@ struct target_type cortexa_target = { static const struct command_registration cortex_r4_exec_command_handlers[] = { { - .name = "cache_info", - .handler = cortex_a_handle_cache_info_command, - .mode = COMMAND_EXEC, - .help = "display information about target caches", - .usage = "", - }, - { .name = "dbginit", .handler = cortex_a_handle_dbginit_command, .mode = COMMAND_EXEC, @@ -3506,9 +3534,6 @@ static const struct command_registration cortex_r4_command_handlers[] = { .chain = arm_command_handlers, }, { - .chain = armv7a_command_handlers, - }, - { .name = "cortex_r4", .mode = COMMAND_ANY, .help = "Cortex-R4 command group", @@ -3551,6 +3576,7 @@ struct target_type cortexr4_target = { .commands = cortex_r4_command_handlers, .target_create = cortex_r4_target_create, + .target_jim_configure = adiv5_jim_configure, .init_target = cortex_a_init_target, .examine = cortex_a_examine, .deinit_target = cortex_a_deinit_target, diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index 79af632..ca3dbec 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -51,11 +51,6 @@ * any longer. */ -/** - * Returns the type of a break point required by address location - */ -#define BKPT_TYPE_BY_ADDR(addr) ((addr) < 0x20000000 ? BKPT_HARD : BKPT_SOFT) - /* forward declarations */ static int cortex_m_store_core_reg_u32(struct target *target, uint32_t num, uint32_t value); @@ -170,7 +165,7 @@ static int cortex_m_single_step_core(struct target *target) struct armv7m_common *armv7m = &cortex_m->armv7m; int retval; - /* Mask interrupts before clearing halt, if done already. This avoids + /* Mask interrupts before clearing halt, if not done already. This avoids * Erratum 377497 (fixed in r1p0) where setting MASKINTS while clearing * HALT can put the core into an unknown state. */ @@ -242,8 +237,11 @@ static int cortex_m_endreset_event(struct target *target) return retval; } - /* clear any interrupt masking */ - cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS); + /* Restore proper interrupt masking setting. */ + if (cortex_m->isrmasking_mode == CORTEX_M_ISRMASK_ON) + cortex_m_write_debug_halt_mask(target, C_MASKINTS, 0); + else + cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS); /* Enable features controlled by ITM and DWT blocks, and catch only * the vectors we were told to pay attention to. @@ -868,7 +866,7 @@ static int cortex_m_step(struct target *target, int current, if (breakpoint) retval = cortex_m_set_breakpoint(target, breakpoint); else - retval = breakpoint_add(target, pc_value, 2, BKPT_TYPE_BY_ADDR(pc_value)); + retval = breakpoint_add(target, pc_value, 2, BKPT_HARD); bool tmp_bp_set = (retval == ERROR_OK); /* No more breakpoints left, just do a step */ @@ -1131,9 +1129,6 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint return ERROR_OK; } - if (cortex_m->auto_bp_type) - breakpoint->type = BKPT_TYPE_BY_ADDR(breakpoint->address); - if (breakpoint->type == BKPT_HARD) { uint32_t fpcr_value; while (comparator_list[fp_num].used && (fp_num < cortex_m->fp_num_code)) @@ -1145,6 +1140,10 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint breakpoint->set = fp_num + 1; fpcr_value = breakpoint->address | 1; if (cortex_m->fp_rev == 0) { + if (breakpoint->address > 0x1FFFFFFF) { + LOG_ERROR("Cortex-M Flash Patch Breakpoint rev.1 cannot handle HW breakpoint above address 0x1FFFFFFE"); + return ERROR_FAIL; + } uint32_t hilo; hilo = (breakpoint->address & 0x2) ? FPCR_REPLACE_BKPT_HIGH : FPCR_REPLACE_BKPT_LOW; fpcr_value = (fpcr_value & 0x1FFFFFFC) | hilo | 1; @@ -1253,21 +1252,6 @@ int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint { struct cortex_m_common *cortex_m = target_to_cm(target); - if (cortex_m->auto_bp_type) - breakpoint->type = BKPT_TYPE_BY_ADDR(breakpoint->address); - - if (breakpoint->type != BKPT_TYPE_BY_ADDR(breakpoint->address)) { - if (breakpoint->type == BKPT_HARD) { - LOG_INFO("flash patch comparator requested outside code memory region"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - - if (breakpoint->type == BKPT_SOFT) { - LOG_INFO("soft breakpoint requested in code (flash) memory region"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - } - if ((breakpoint->type == BKPT_HARD) && (cortex_m->fp_code_available < 1)) { LOG_INFO("no flash patch comparator unit available for hardware breakpoint"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; @@ -1299,9 +1283,6 @@ int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpo return ERROR_TARGET_NOT_HALTED; } - if (cortex_m->auto_bp_type) - breakpoint->type = BKPT_TYPE_BY_ADDR(breakpoint->address); - if (breakpoint->set) cortex_m_unset_breakpoint(target, breakpoint); @@ -1832,11 +1813,11 @@ static int cortex_m_dwt_set_reg(struct reg *reg, uint8_t *buf) struct dwt_reg { uint32_t addr; - char *name; + const char *name; unsigned size; }; -static struct dwt_reg dwt_base_regs[] = { +static const struct dwt_reg dwt_base_regs[] = { { DWT_CTRL, "dwt_ctrl", 32, }, /* NOTE that Erratum 532314 (fixed r2p0) affects CYCCNT: it wrongly * increments while the core is asleep. @@ -1845,7 +1826,7 @@ static struct dwt_reg dwt_base_regs[] = { /* plus some 8 bit counters, useful for profiling with TPIU */ }; -static struct dwt_reg dwt_comp[] = { +static const struct dwt_reg dwt_comp[] = { #define DWT_COMPARATOR(i) \ { DWT_COMP0 + 0x10 * (i), "dwt_" #i "_comp", 32, }, \ { DWT_MASK0 + 0x10 * (i), "dwt_" #i "_mask", 4, }, \ @@ -1854,6 +1835,18 @@ static struct dwt_reg dwt_comp[] = { DWT_COMPARATOR(1), DWT_COMPARATOR(2), DWT_COMPARATOR(3), + DWT_COMPARATOR(4), + DWT_COMPARATOR(5), + DWT_COMPARATOR(6), + DWT_COMPARATOR(7), + DWT_COMPARATOR(8), + DWT_COMPARATOR(9), + DWT_COMPARATOR(10), + DWT_COMPARATOR(11), + DWT_COMPARATOR(12), + DWT_COMPARATOR(13), + DWT_COMPARATOR(14), + DWT_COMPARATOR(15), #undef DWT_COMPARATOR }; @@ -1862,7 +1855,7 @@ static const struct reg_arch_type dwt_reg_type = { .set = cortex_m_dwt_set_reg, }; -static void cortex_m_dwt_addreg(struct target *t, struct reg *r, struct dwt_reg *d) +static void cortex_m_dwt_addreg(struct target *t, struct reg *r, const struct dwt_reg *d) { struct dwt_reg_state *state; @@ -1887,6 +1880,7 @@ void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target) int reg, i; target_read_u32(target, DWT_CTRL, &dwtcr); + LOG_DEBUG("DWT_CTRL: 0x%" PRIx32, dwtcr); if (!dwtcr) { LOG_DEBUG("no DWT"); return; @@ -1992,12 +1986,6 @@ int cortex_m_examine(struct target *target) /* stlink shares the examine handler but does not support * all its calls */ if (!armv7m->stlink) { - retval = dap_dp_init(swjdp); - if (retval != ERROR_OK) { - LOG_ERROR("Could not initialize the debug port"); - return retval; - } - if (cortex_m->apsel < 0) { /* Search for the MEM-AP */ retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7m->debug_ap); @@ -2095,7 +2083,7 @@ int cortex_m_examine(struct target *target) if (retval != ERROR_OK) return retval; - if (armv7m->trace_config.config_type != DISABLED) { + if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_DISABLED) { armv7m_trace_tpiu_config(target); armv7m_trace_itm_config(target); } @@ -2104,7 +2092,6 @@ int cortex_m_examine(struct target *target) /* Setup FPB */ target_read_u32(target, FP_CTRL, &fpcr); - cortex_m->auto_bp_type = 1; /* bits [14:12] and [7:4] */ cortex_m->fp_num_code = ((fpcr >> 8) & 0x70) | ((fpcr >> 4) & 0xF); cortex_m->fp_num_lit = (fpcr >> 8) & 0xF; @@ -2228,25 +2215,17 @@ static int cortex_m_handle_target_request(void *priv) } static int cortex_m_init_arch_info(struct target *target, - struct cortex_m_common *cortex_m, struct jtag_tap *tap) + struct cortex_m_common *cortex_m, struct adiv5_dap *dap) { struct armv7m_common *armv7m = &cortex_m->armv7m; armv7m_init_arch_info(target, armv7m); - /* tap has no dap initialized */ - if (!tap->dap) { - tap->dap = dap_init(); - - /* Leave (only) generic DAP stuff for debugport_init() */ - tap->dap->tap = tap; - } - /* default reset mode is to use srst if fitted * if not it will use CORTEX_M3_RESET_VECTRESET */ cortex_m->soft_reset_config = CORTEX_M_RESET_VECTRESET; - armv7m->arm.dap = tap->dap; + armv7m->arm.dap = dap; /* register arch-specific functions */ armv7m->examine_debug_reason = cortex_m_examine_debug_reason; @@ -2266,16 +2245,16 @@ static int cortex_m_init_arch_info(struct target *target, static int cortex_m_target_create(struct target *target, Jim_Interp *interp) { struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common)); - cortex_m->common_magic = CORTEX_M_COMMON_MAGIC; - cortex_m_init_arch_info(target, cortex_m, target->tap); + struct adiv5_private_config *pc; + + pc = (struct adiv5_private_config *)target->private_config; + if (adiv5_verify_config(pc) != ERROR_OK) + return ERROR_FAIL; - if (target->private_config != NULL) { - struct adiv5_private_config *pc = - (struct adiv5_private_config *)target->private_config; - cortex_m->apsel = pc->ap_num; - } else - cortex_m->apsel = -1; + cortex_m->apsel = pc->ap_num; + + cortex_m_init_arch_info(target, cortex_m, pc->dap); return ERROR_OK; } @@ -2298,20 +2277,6 @@ static int cortex_m_verify_pointer(struct command_context *cmd_ctx, * cortexm3_target structure, which is only used with CM3 targets. */ -static const struct { - char name[10]; - unsigned mask; -} vec_ids[] = { - { "hard_err", VC_HARDERR, }, - { "int_err", VC_INTERR, }, - { "bus_err", VC_BUSERR, }, - { "state_err", VC_STATERR, }, - { "chk_err", VC_CHKERR, }, - { "nocp_err", VC_NOCPERR, }, - { "mm_err", VC_MMERR, }, - { "reset", VC_CORERESET, }, -}; - COMMAND_HANDLER(handle_cortex_m_vector_catch_command) { struct target *target = get_current_target(CMD_CTX); @@ -2320,6 +2285,20 @@ COMMAND_HANDLER(handle_cortex_m_vector_catch_command) uint32_t demcr = 0; int retval; + static const struct { + char name[10]; + unsigned mask; + } vec_ids[] = { + { "hard_err", VC_HARDERR, }, + { "int_err", VC_INTERR, }, + { "bus_err", VC_BUSERR, }, + { "state_err", VC_STATERR, }, + { "chk_err", VC_CHKERR, }, + { "nocp_err", VC_NOCPERR, }, + { "mm_err", VC_MMERR, }, + { "reset", VC_CORERESET, }, + }; + retval = cortex_m_verify_pointer(CMD_CTX, cortex_m); if (retval != ERROR_OK) return retval; diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h index 9500acc..2daf4cb 100644 --- a/src/target/cortex_m.h +++ b/src/target/cortex_m.h @@ -175,7 +175,6 @@ struct cortex_m_common { int fp_code_available; int fp_rev; int fpb_enabled; - int auto_bp_type; struct cortex_m_fp_comparator *fp_comparator_list; /* Data Watchpoint and Trace (DWT) */ diff --git a/src/target/hla_target.c b/src/target/hla_target.c index a3e6835..9ebf241 100644 --- a/src/target/hla_target.c +++ b/src/target/hla_target.c @@ -271,7 +271,10 @@ static int hl_target_request_data(struct target *target, uint32_t i; for (i = 0; i < (size * 4); i++) { - hl_dcc_read(hl_if, &data, &ctrl); + int err = hl_dcc_read(hl_if, &data, &ctrl); + if (err != ERROR_OK) + return err; + buffer[i] = data; } @@ -281,6 +284,8 @@ static int hl_target_request_data(struct target *target, static int hl_handle_target_request(void *priv) { struct target *target = priv; + int err; + if (!target_was_examined(target)) return ERROR_OK; struct hl_interface_s *hl_if = target_to_adapter(target); @@ -292,7 +297,9 @@ static int hl_handle_target_request(void *priv) uint8_t data; uint8_t ctrl; - hl_dcc_read(hl_if, &data, &ctrl); + err = hl_dcc_read(hl_if, &data, &ctrl); + if (err != ERROR_OK) + return err; /* check if we have data */ if (ctrl & (1 << 0)) { @@ -300,11 +307,20 @@ static int hl_handle_target_request(void *priv) /* we assume target is quick enough */ request = data; - hl_dcc_read(hl_if, &data, &ctrl); + err = hl_dcc_read(hl_if, &data, &ctrl); + if (err != ERROR_OK) + return err; + request |= (data << 8); - hl_dcc_read(hl_if, &data, &ctrl); + err = hl_dcc_read(hl_if, &data, &ctrl); + if (err != ERROR_OK) + return err; + request |= (data << 16); - hl_dcc_read(hl_if, &data, &ctrl); + err = hl_dcc_read(hl_if, &data, &ctrl); + if (err != ERROR_OK) + return err; + request |= (data << 24); target_request(target, request); } @@ -349,12 +365,16 @@ static int adapter_target_create(struct target *target, Jim_Interp *interp) { LOG_DEBUG("%s", __func__); - + struct adiv5_private_config *pc = target->private_config; struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common)); - if (!cortex_m) return ERROR_COMMAND_SYNTAX_ERROR; + if (pc != NULL && pc->ap_num > 0) { + LOG_ERROR("hla_target: invalid parameter -ap-num (> 0)"); + return ERROR_FAIL; + } + adapter_init_arch_info(target, cortex_m, target->tap); return ERROR_OK; @@ -785,6 +805,7 @@ struct target_type hla_target = { .init_target = adapter_init_target, .deinit_target = cortex_m_deinit_target, .target_create = adapter_target_create, + .target_jim_configure = adiv5_jim_configure, .examine = cortex_m_examine, .commands = adapter_command_handlers, diff --git a/src/target/image.c b/src/target/image.c index f97d904..0d98c57 100644 --- a/src/target/image.c +++ b/src/target/image.c @@ -121,8 +121,9 @@ static int image_ihex_buffer_complete_inner(struct image *image, { struct image_ihex *ihex = image->type_private; struct fileio *fileio = ihex->fileio; - uint32_t full_address = 0x0; + uint32_t full_address; uint32_t cooked_bytes; + bool end_rec = false; int i; /* we can't determine the number of sections that we'll have to create ahead of time, @@ -137,175 +138,190 @@ static int image_ihex_buffer_complete_inner(struct image *image, ihex->buffer = malloc(filesize >> 1); cooked_bytes = 0x0; image->num_sections = 0; - section[image->num_sections].private = &ihex->buffer[cooked_bytes]; - section[image->num_sections].base_address = 0x0; - section[image->num_sections].size = 0x0; - section[image->num_sections].flags = 0; - - while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) { - uint32_t count; - uint32_t address; - uint32_t record_type; - uint32_t checksum; - uint8_t cal_checksum = 0; - size_t bytes_read = 0; - - if (lpszLine[0] == '#') - continue; - - if (sscanf(&lpszLine[bytes_read], ":%2" SCNx32 "%4" SCNx32 "%2" SCNx32, &count, - &address, &record_type) != 3) - return ERROR_IMAGE_FORMAT_ERROR; - bytes_read += 9; - - cal_checksum += (uint8_t)count; - cal_checksum += (uint8_t)(address >> 8); - cal_checksum += (uint8_t)address; - cal_checksum += (uint8_t)record_type; - - if (record_type == 0) { /* Data Record */ - if ((full_address & 0xffff) != address) { - /* we encountered a nonconsecutive location, create a new section, - * unless the current section has zero size, in which case this specifies - * the current section's base address - */ - if (section[image->num_sections].size != 0) { - image->num_sections++; - if (image->num_sections >= IMAGE_MAX_SECTIONS) { - /* too many sections */ - LOG_ERROR("Too many sections found in IHEX file"); - return ERROR_IMAGE_FORMAT_ERROR; + + while (!fileio_feof(fileio)) { + full_address = 0x0; + section[image->num_sections].private = &ihex->buffer[cooked_bytes]; + section[image->num_sections].base_address = 0x0; + section[image->num_sections].size = 0x0; + section[image->num_sections].flags = 0; + + while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) { + uint32_t count; + uint32_t address; + uint32_t record_type; + uint32_t checksum; + uint8_t cal_checksum = 0; + size_t bytes_read = 0; + + /* skip comments and blank lines */ + if ((lpszLine[0] == '#') || (strlen(lpszLine + strspn(lpszLine, "\n\t\r ")) == 0)) + continue; + + if (sscanf(&lpszLine[bytes_read], ":%2" SCNx32 "%4" SCNx32 "%2" SCNx32, &count, + &address, &record_type) != 3) + return ERROR_IMAGE_FORMAT_ERROR; + bytes_read += 9; + + cal_checksum += (uint8_t)count; + cal_checksum += (uint8_t)(address >> 8); + cal_checksum += (uint8_t)address; + cal_checksum += (uint8_t)record_type; + + if (record_type == 0) { /* Data Record */ + if ((full_address & 0xffff) != address) { + /* we encountered a nonconsecutive location, create a new section, + * unless the current section has zero size, in which case this specifies + * the current section's base address + */ + if (section[image->num_sections].size != 0) { + image->num_sections++; + if (image->num_sections >= IMAGE_MAX_SECTIONS) { + /* too many sections */ + LOG_ERROR("Too many sections found in IHEX file"); + return ERROR_IMAGE_FORMAT_ERROR; + } + section[image->num_sections].size = 0x0; + section[image->num_sections].flags = 0; + section[image->num_sections].private = + &ihex->buffer[cooked_bytes]; } - section[image->num_sections].size = 0x0; - section[image->num_sections].flags = 0; - section[image->num_sections].private = - &ihex->buffer[cooked_bytes]; + section[image->num_sections].base_address = + (full_address & 0xffff0000) | address; + full_address = (full_address & 0xffff0000) | address; } - section[image->num_sections].base_address = - (full_address & 0xffff0000) | address; - full_address = (full_address & 0xffff0000) | address; - } - - while (count-- > 0) { - unsigned value; - sscanf(&lpszLine[bytes_read], "%2x", &value); - ihex->buffer[cooked_bytes] = (uint8_t)value; - cal_checksum += (uint8_t)ihex->buffer[cooked_bytes]; - bytes_read += 2; - cooked_bytes += 1; - section[image->num_sections].size += 1; - full_address++; - } - } else if (record_type == 1) { /* End of File Record */ - /* finish the current section */ - image->num_sections++; - - /* copy section information */ - image->sections = malloc(sizeof(struct imagesection) * image->num_sections); - for (i = 0; i < image->num_sections; i++) { - image->sections[i].private = section[i].private; - image->sections[i].base_address = section[i].base_address; - image->sections[i].size = section[i].size; - image->sections[i].flags = section[i].flags; - } - return ERROR_OK; - } else if (record_type == 2) { /* Linear Address Record */ - uint16_t upper_address; - - sscanf(&lpszLine[bytes_read], "%4hx", &upper_address); - cal_checksum += (uint8_t)(upper_address >> 8); - cal_checksum += (uint8_t)upper_address; - bytes_read += 4; + while (count-- > 0) { + unsigned value; + sscanf(&lpszLine[bytes_read], "%2x", &value); + ihex->buffer[cooked_bytes] = (uint8_t)value; + cal_checksum += (uint8_t)ihex->buffer[cooked_bytes]; + bytes_read += 2; + cooked_bytes += 1; + section[image->num_sections].size += 1; + full_address++; + } + } else if (record_type == 1) { /* End of File Record */ + /* finish the current section */ + image->num_sections++; + + /* copy section information */ + image->sections = malloc(sizeof(struct imagesection) * image->num_sections); + for (i = 0; i < image->num_sections; i++) { + image->sections[i].private = section[i].private; + image->sections[i].base_address = section[i].base_address; + image->sections[i].size = section[i].size; + image->sections[i].flags = section[i].flags; + } - if ((full_address >> 4) != upper_address) { - /* we encountered a nonconsecutive location, create a new section, - * unless the current section has zero size, in which case this specifies - * the current section's base address - */ - if (section[image->num_sections].size != 0) { - image->num_sections++; - if (image->num_sections >= IMAGE_MAX_SECTIONS) { - /* too many sections */ - LOG_ERROR("Too many sections found in IHEX file"); - return ERROR_IMAGE_FORMAT_ERROR; + end_rec = true; + break; + } else if (record_type == 2) { /* Linear Address Record */ + uint16_t upper_address; + + sscanf(&lpszLine[bytes_read], "%4hx", &upper_address); + cal_checksum += (uint8_t)(upper_address >> 8); + cal_checksum += (uint8_t)upper_address; + bytes_read += 4; + + if ((full_address >> 4) != upper_address) { + /* we encountered a nonconsecutive location, create a new section, + * unless the current section has zero size, in which case this specifies + * the current section's base address + */ + if (section[image->num_sections].size != 0) { + image->num_sections++; + if (image->num_sections >= IMAGE_MAX_SECTIONS) { + /* too many sections */ + LOG_ERROR("Too many sections found in IHEX file"); + return ERROR_IMAGE_FORMAT_ERROR; + } + section[image->num_sections].size = 0x0; + section[image->num_sections].flags = 0; + section[image->num_sections].private = + &ihex->buffer[cooked_bytes]; } - section[image->num_sections].size = 0x0; - section[image->num_sections].flags = 0; - section[image->num_sections].private = - &ihex->buffer[cooked_bytes]; + section[image->num_sections].base_address = + (full_address & 0xffff) | (upper_address << 4); + full_address = (full_address & 0xffff) | (upper_address << 4); } - section[image->num_sections].base_address = - (full_address & 0xffff) | (upper_address << 4); - full_address = (full_address & 0xffff) | (upper_address << 4); - } - } else if (record_type == 3) { /* Start Segment Address Record */ - uint32_t dummy; - - /* "Start Segment Address Record" will not be supported - * but we must consume it, and do not create an error. */ - while (count-- > 0) { - sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy); - cal_checksum += (uint8_t)dummy; - bytes_read += 2; - } - } else if (record_type == 4) { /* Extended Linear Address Record */ - uint16_t upper_address; - - sscanf(&lpszLine[bytes_read], "%4hx", &upper_address); - cal_checksum += (uint8_t)(upper_address >> 8); - cal_checksum += (uint8_t)upper_address; - bytes_read += 4; - - if ((full_address >> 16) != upper_address) { - /* we encountered a nonconsecutive location, create a new section, - * unless the current section has zero size, in which case this specifies - * the current section's base address - */ - if (section[image->num_sections].size != 0) { - image->num_sections++; - if (image->num_sections >= IMAGE_MAX_SECTIONS) { - /* too many sections */ - LOG_ERROR("Too many sections found in IHEX file"); - return ERROR_IMAGE_FORMAT_ERROR; + } else if (record_type == 3) { /* Start Segment Address Record */ + uint32_t dummy; + + /* "Start Segment Address Record" will not be supported + * but we must consume it, and do not create an error. */ + while (count-- > 0) { + sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy); + cal_checksum += (uint8_t)dummy; + bytes_read += 2; + } + } else if (record_type == 4) { /* Extended Linear Address Record */ + uint16_t upper_address; + + sscanf(&lpszLine[bytes_read], "%4hx", &upper_address); + cal_checksum += (uint8_t)(upper_address >> 8); + cal_checksum += (uint8_t)upper_address; + bytes_read += 4; + + if ((full_address >> 16) != upper_address) { + /* we encountered a nonconsecutive location, create a new section, + * unless the current section has zero size, in which case this specifies + * the current section's base address + */ + if (section[image->num_sections].size != 0) { + image->num_sections++; + if (image->num_sections >= IMAGE_MAX_SECTIONS) { + /* too many sections */ + LOG_ERROR("Too many sections found in IHEX file"); + return ERROR_IMAGE_FORMAT_ERROR; + } + section[image->num_sections].size = 0x0; + section[image->num_sections].flags = 0; + section[image->num_sections].private = + &ihex->buffer[cooked_bytes]; } - section[image->num_sections].size = 0x0; - section[image->num_sections].flags = 0; - section[image->num_sections].private = - &ihex->buffer[cooked_bytes]; + section[image->num_sections].base_address = + (full_address & 0xffff) | (upper_address << 16); + full_address = (full_address & 0xffff) | (upper_address << 16); } - section[image->num_sections].base_address = - (full_address & 0xffff) | (upper_address << 16); - full_address = (full_address & 0xffff) | (upper_address << 16); + } else if (record_type == 5) { /* Start Linear Address Record */ + uint32_t start_address; + + sscanf(&lpszLine[bytes_read], "%8" SCNx32, &start_address); + cal_checksum += (uint8_t)(start_address >> 24); + cal_checksum += (uint8_t)(start_address >> 16); + cal_checksum += (uint8_t)(start_address >> 8); + cal_checksum += (uint8_t)start_address; + bytes_read += 8; + + image->start_address_set = 1; + image->start_address = be_to_h_u32((uint8_t *)&start_address); + } else { + LOG_ERROR("unhandled IHEX record type: %i", (int)record_type); + return ERROR_IMAGE_FORMAT_ERROR; } - } else if (record_type == 5) { /* Start Linear Address Record */ - uint32_t start_address; - - sscanf(&lpszLine[bytes_read], "%8" SCNx32, &start_address); - cal_checksum += (uint8_t)(start_address >> 24); - cal_checksum += (uint8_t)(start_address >> 16); - cal_checksum += (uint8_t)(start_address >> 8); - cal_checksum += (uint8_t)start_address; - bytes_read += 8; - - image->start_address_set = 1; - image->start_address = be_to_h_u32((uint8_t *)&start_address); - } else { - LOG_ERROR("unhandled IHEX record type: %i", (int)record_type); - return ERROR_IMAGE_FORMAT_ERROR; - } - sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum); + sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum); - if ((uint8_t)checksum != (uint8_t)(~cal_checksum + 1)) { - /* checksum failed */ - LOG_ERROR("incorrect record checksum found in IHEX file"); - return ERROR_IMAGE_CHECKSUM; + if ((uint8_t)checksum != (uint8_t)(~cal_checksum + 1)) { + /* checksum failed */ + LOG_ERROR("incorrect record checksum found in IHEX file"); + return ERROR_IMAGE_CHECKSUM; + } + + if (end_rec) { + end_rec = false; + LOG_WARNING("continuing after end-of-file record: %.40s", lpszLine); + } } } - LOG_ERROR("premature end of IHEX file, no end-of-file record found"); - return ERROR_IMAGE_FORMAT_ERROR; + if (end_rec) + return ERROR_OK; + else { + LOG_ERROR("premature end of IHEX file, no matching end-of-file record found"); + return ERROR_IMAGE_FORMAT_ERROR; + } } /** @@ -510,8 +526,9 @@ static int image_mot_buffer_complete_inner(struct image *image, { struct image_mot *mot = image->type_private; struct fileio *fileio = mot->fileio; - uint32_t full_address = 0x0; + uint32_t full_address; uint32_t cooked_bytes; + bool end_rec = false; int i; /* we can't determine the number of sections that we'll have to create ahead of time, @@ -526,140 +543,158 @@ static int image_mot_buffer_complete_inner(struct image *image, mot->buffer = malloc(filesize >> 1); cooked_bytes = 0x0; image->num_sections = 0; - section[image->num_sections].private = &mot->buffer[cooked_bytes]; - section[image->num_sections].base_address = 0x0; - section[image->num_sections].size = 0x0; - section[image->num_sections].flags = 0; - - while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) { - uint32_t count; - uint32_t address; - uint32_t record_type; - uint32_t checksum; - uint8_t cal_checksum = 0; - uint32_t bytes_read = 0; - - /* get record type and record length */ - if (sscanf(&lpszLine[bytes_read], "S%1" SCNx32 "%2" SCNx32, &record_type, - &count) != 2) - return ERROR_IMAGE_FORMAT_ERROR; - - bytes_read += 4; - cal_checksum += (uint8_t)count; - - /* skip checksum byte */ - count -= 1; - - if (record_type == 0) { - /* S0 - starting record (optional) */ - int iValue; - - while (count-- > 0) { - sscanf(&lpszLine[bytes_read], "%2x", &iValue); - cal_checksum += (uint8_t)iValue; - bytes_read += 2; - } - } else if (record_type >= 1 && record_type <= 3) { - switch (record_type) { - case 1: - /* S1 - 16 bit address data record */ - sscanf(&lpszLine[bytes_read], "%4" SCNx32, &address); - cal_checksum += (uint8_t)(address >> 8); - cal_checksum += (uint8_t)address; - bytes_read += 4; - count -= 2; - break; - - case 2: - /* S2 - 24 bit address data record */ - sscanf(&lpszLine[bytes_read], "%6" SCNx32, &address); - cal_checksum += (uint8_t)(address >> 16); - cal_checksum += (uint8_t)(address >> 8); - cal_checksum += (uint8_t)address; - bytes_read += 6; - count -= 3; - break; - - case 3: - /* S3 - 32 bit address data record */ - sscanf(&lpszLine[bytes_read], "%8" SCNx32, &address); - cal_checksum += (uint8_t)(address >> 24); - cal_checksum += (uint8_t)(address >> 16); - cal_checksum += (uint8_t)(address >> 8); - cal_checksum += (uint8_t)address; - bytes_read += 8; - count -= 4; - break; - } + while (!fileio_feof(fileio)) { + full_address = 0x0; + section[image->num_sections].private = &mot->buffer[cooked_bytes]; + section[image->num_sections].base_address = 0x0; + section[image->num_sections].size = 0x0; + section[image->num_sections].flags = 0; + + while (fileio_fgets(fileio, 1023, lpszLine) == ERROR_OK) { + uint32_t count; + uint32_t address; + uint32_t record_type; + uint32_t checksum; + uint8_t cal_checksum = 0; + uint32_t bytes_read = 0; + + /* skip comments and blank lines */ + if ((lpszLine[0] == '#') || (strlen(lpszLine + strspn(lpszLine, "\n\t\r ")) == 0)) + continue; + + /* get record type and record length */ + if (sscanf(&lpszLine[bytes_read], "S%1" SCNx32 "%2" SCNx32, &record_type, + &count) != 2) + return ERROR_IMAGE_FORMAT_ERROR; + + bytes_read += 4; + cal_checksum += (uint8_t)count; - if (full_address != address) { - /* we encountered a nonconsecutive location, create a new section, - * unless the current section has zero size, in which case this specifies - * the current section's base address - */ - if (section[image->num_sections].size != 0) { - image->num_sections++; - section[image->num_sections].size = 0x0; - section[image->num_sections].flags = 0; - section[image->num_sections].private = - &mot->buffer[cooked_bytes]; + /* skip checksum byte */ + count -= 1; + + if (record_type == 0) { + /* S0 - starting record (optional) */ + int iValue; + + while (count-- > 0) { + sscanf(&lpszLine[bytes_read], "%2x", &iValue); + cal_checksum += (uint8_t)iValue; + bytes_read += 2; } - section[image->num_sections].base_address = address; - full_address = address; - } + } else if (record_type >= 1 && record_type <= 3) { + switch (record_type) { + case 1: + /* S1 - 16 bit address data record */ + sscanf(&lpszLine[bytes_read], "%4" SCNx32, &address); + cal_checksum += (uint8_t)(address >> 8); + cal_checksum += (uint8_t)address; + bytes_read += 4; + count -= 2; + break; + + case 2: + /* S2 - 24 bit address data record */ + sscanf(&lpszLine[bytes_read], "%6" SCNx32, &address); + cal_checksum += (uint8_t)(address >> 16); + cal_checksum += (uint8_t)(address >> 8); + cal_checksum += (uint8_t)address; + bytes_read += 6; + count -= 3; + break; + + case 3: + /* S3 - 32 bit address data record */ + sscanf(&lpszLine[bytes_read], "%8" SCNx32, &address); + cal_checksum += (uint8_t)(address >> 24); + cal_checksum += (uint8_t)(address >> 16); + cal_checksum += (uint8_t)(address >> 8); + cal_checksum += (uint8_t)address; + bytes_read += 8; + count -= 4; + break; - while (count-- > 0) { - unsigned value; - sscanf(&lpszLine[bytes_read], "%2x", &value); - mot->buffer[cooked_bytes] = (uint8_t)value; - cal_checksum += (uint8_t)mot->buffer[cooked_bytes]; - bytes_read += 2; - cooked_bytes += 1; - section[image->num_sections].size += 1; - full_address++; - } - } else if (record_type == 5) { - /* S5 is the data count record, we ignore it */ - uint32_t dummy; - - while (count-- > 0) { - sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy); - cal_checksum += (uint8_t)dummy; - bytes_read += 2; - } - } else if (record_type >= 7 && record_type <= 9) { - /* S7, S8, S9 - ending records for 32, 24 and 16bit */ - image->num_sections++; + } - /* copy section information */ - image->sections = malloc(sizeof(struct imagesection) * image->num_sections); - for (i = 0; i < image->num_sections; i++) { - image->sections[i].private = section[i].private; - image->sections[i].base_address = section[i].base_address; - image->sections[i].size = section[i].size; - image->sections[i].flags = section[i].flags; + if (full_address != address) { + /* we encountered a nonconsecutive location, create a new section, + * unless the current section has zero size, in which case this specifies + * the current section's base address + */ + if (section[image->num_sections].size != 0) { + image->num_sections++; + section[image->num_sections].size = 0x0; + section[image->num_sections].flags = 0; + section[image->num_sections].private = + &mot->buffer[cooked_bytes]; + } + section[image->num_sections].base_address = address; + full_address = address; + } + + while (count-- > 0) { + unsigned value; + sscanf(&lpszLine[bytes_read], "%2x", &value); + mot->buffer[cooked_bytes] = (uint8_t)value; + cal_checksum += (uint8_t)mot->buffer[cooked_bytes]; + bytes_read += 2; + cooked_bytes += 1; + section[image->num_sections].size += 1; + full_address++; + } + } else if (record_type == 5) { + /* S5 is the data count record, we ignore it */ + uint32_t dummy; + + while (count-- > 0) { + sscanf(&lpszLine[bytes_read], "%2" SCNx32, &dummy); + cal_checksum += (uint8_t)dummy; + bytes_read += 2; + } + } else if (record_type >= 7 && record_type <= 9) { + /* S7, S8, S9 - ending records for 32, 24 and 16bit */ + image->num_sections++; + + /* copy section information */ + image->sections = malloc(sizeof(struct imagesection) * image->num_sections); + for (i = 0; i < image->num_sections; i++) { + image->sections[i].private = section[i].private; + image->sections[i].base_address = section[i].base_address; + image->sections[i].size = section[i].size; + image->sections[i].flags = section[i].flags; + } + + end_rec = true; + break; + } else { + LOG_ERROR("unhandled S19 record type: %i", (int)(record_type)); + return ERROR_IMAGE_FORMAT_ERROR; } - return ERROR_OK; - } else { - LOG_ERROR("unhandled S19 record type: %i", (int)(record_type)); - return ERROR_IMAGE_FORMAT_ERROR; - } + /* account for checksum, will always be 0xFF */ + sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum); + cal_checksum += (uint8_t)checksum; - /* account for checksum, will always be 0xFF */ - sscanf(&lpszLine[bytes_read], "%2" SCNx32, &checksum); - cal_checksum += (uint8_t)checksum; + if (cal_checksum != 0xFF) { + /* checksum failed */ + LOG_ERROR("incorrect record checksum found in S19 file"); + return ERROR_IMAGE_CHECKSUM; + } - if (cal_checksum != 0xFF) { - /* checksum failed */ - LOG_ERROR("incorrect record checksum found in S19 file"); - return ERROR_IMAGE_CHECKSUM; + if (end_rec) { + end_rec = false; + LOG_WARNING("continuing after end-of-file record: %.40s", lpszLine); + } } } - LOG_ERROR("premature end of S19 file, no end-of-file record found"); - return ERROR_IMAGE_FORMAT_ERROR; + if (end_rec) + return ERROR_OK; + else { + LOG_ERROR("premature end of S19 file, no matching end-of-file record found"); + return ERROR_IMAGE_FORMAT_ERROR; + } } /** @@ -1013,8 +1048,7 @@ int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, uint32_t *checksu static bool first_init; if (!first_init) { /* Initialize the CRC table and the decoding table. */ - int i, j; - unsigned int c; + unsigned int i, j, c; for (i = 0; i < 256; i++) { /* as per gdb */ for (c = i << 24, j = 8; j > 0; --j) diff --git a/src/target/mips32.c b/src/target/mips32.c index 93fb4e6..b5dbea3 100644 --- a/src/target/mips32.c +++ b/src/target/mips32.c @@ -827,7 +827,8 @@ int mips32_checksum_memory(struct target *target, target_addr_t address, /** Checks whether a memory region is erased. */ int mips32_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value) + struct target_memory_check_block *blocks, int num_blocks, + uint8_t erased_value) { struct working_area *erase_check_algorithm; struct reg_param reg_params[3]; @@ -866,16 +867,16 @@ int mips32_blank_check_memory(struct target *target, int retval = target_write_buffer(target, erase_check_algorithm->address, sizeof(erase_check_code), erase_check_code_8); if (retval != ERROR_OK) - return retval; + goto cleanup; mips32_info.common_magic = MIPS32_COMMON_MAGIC; mips32_info.isa_mode = isa ? MIPS32_ISA_MMIPS32 : MIPS32_ISA_MIPS32; init_reg_param(®_params[0], "r4", 32, PARAM_OUT); - buf_set_u32(reg_params[0].value, 0, 32, address); + buf_set_u32(reg_params[0].value, 0, 32, blocks[0].address); init_reg_param(®_params[1], "r5", 32, PARAM_OUT); - buf_set_u32(reg_params[1].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, blocks[0].size); init_reg_param(®_params[2], "r6", 32, PARAM_IN_OUT); buf_set_u32(reg_params[2].value, 0, 32, erased_value); @@ -884,15 +885,19 @@ int mips32_blank_check_memory(struct target *target, erase_check_algorithm->address + (sizeof(erase_check_code) - 4), 10000, &mips32_info); if (retval == ERROR_OK) - *blank = buf_get_u32(reg_params[2].value, 0, 32); + blocks[0].result = buf_get_u32(reg_params[2].value, 0, 32); destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); destroy_reg_param(®_params[2]); +cleanup: target_free_working_area(target, erase_check_algorithm); - return retval; + if (retval != ERROR_OK) + return retval; + + return 1; /* only one block has been checked */ } static int mips32_verify_pointer(struct command_context *cmd_ctx, diff --git a/src/target/mips32.h b/src/target/mips32.h index 928598f..4dc164e 100644 --- a/src/target/mips32.h +++ b/src/target/mips32.h @@ -428,6 +428,6 @@ int mips32_get_gdb_reg_list(struct target *target, int mips32_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum); int mips32_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value); + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value); #endif /* OPENOCD_TARGET_MIPS32_H */ diff --git a/src/target/mips_m4k.c b/src/target/mips_m4k.c index 78718ca..20c707b 100644 --- a/src/target/mips_m4k.c +++ b/src/target/mips_m4k.c @@ -344,6 +344,8 @@ static int mips_m4k_assert_reset(struct target *target) jtag_add_reset(1, 1); else if (!srst_asserted) jtag_add_reset(0, 1); + } else if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) { + target_handle_event(target, TARGET_EVENT_RESET_ASSERT); } else { if (mips_m4k->is_pic32mx) { LOG_DEBUG("Using MTAP reset to reset processor..."); diff --git a/src/target/nds32.c b/src/target/nds32.c index e4bb17f..4115ea4 100644 --- a/src/target/nds32.c +++ b/src/target/nds32.c @@ -2339,63 +2339,66 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil fileio_info->identifier = NULL; } + uint32_t reg_r0, reg_r1, reg_r2; + nds32_get_mapped_reg(nds32, R0, ®_r0); + nds32_get_mapped_reg(nds32, R1, ®_r1); + nds32_get_mapped_reg(nds32, R2, ®_r2); + switch (syscall_id) { case NDS32_SYSCALL_EXIT: fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "exit"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; break; case NDS32_SYSCALL_OPEN: { uint8_t filename[256]; fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "open"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of path */ - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3)); - nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_4)); + fileio_info->param_3 = reg_r1; + fileio_info->param_4 = reg_r2; - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; } break; case NDS32_SYSCALL_CLOSE: fileio_info->identifier = malloc(6); sprintf(fileio_info->identifier, "close"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; break; case NDS32_SYSCALL_READ: fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "read"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2)); - nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; + fileio_info->param_3 = reg_r2; break; case NDS32_SYSCALL_WRITE: fileio_info->identifier = malloc(6); sprintf(fileio_info->identifier, "write"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2)); - nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; + fileio_info->param_3 = reg_r2; break; case NDS32_SYSCALL_LSEEK: fileio_info->identifier = malloc(6); sprintf(fileio_info->identifier, "lseek"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2)); - nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; + fileio_info->param_3 = reg_r2; break; case NDS32_SYSCALL_UNLINK: { uint8_t filename[256]; fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "unlink"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of path */ - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; } break; @@ -2404,61 +2407,57 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil uint8_t filename[256]; fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "rename"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of old path */ - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3)); + fileio_info->param_3 = reg_r1; /* reserve fileio_info->param_4 for length of new path */ - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; - target->type->read_buffer(target, fileio_info->param_3, - 256, filename); + target->type->read_buffer(target, reg_r1, 256, filename); fileio_info->param_4 = strlen((char *)filename) + 1; } break; case NDS32_SYSCALL_FSTAT: fileio_info->identifier = malloc(6); sprintf(fileio_info->identifier, "fstat"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; break; case NDS32_SYSCALL_STAT: { uint8_t filename[256]; fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "stat"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of old path */ - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3)); + fileio_info->param_3 = reg_r1; - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; } break; case NDS32_SYSCALL_GETTIMEOFDAY: fileio_info->identifier = malloc(13); sprintf(fileio_info->identifier, "gettimeofday"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; break; case NDS32_SYSCALL_ISATTY: fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "isatty"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; break; case NDS32_SYSCALL_SYSTEM: { uint8_t command[256]; fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "system"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of old path */ - target->type->read_buffer(target, fileio_info->param_1, - 256, command); + target->type->read_buffer(target, reg_r0, 256, command); fileio_info->param_2 = strlen((char *)command) + 1; } break; diff --git a/src/target/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c index 2d90114..6cd53f4 100644 --- a/src/target/openrisc/jsp_server.c +++ b/src/target/openrisc/jsp_server.c @@ -242,3 +242,7 @@ int jsp_register_commands(struct command_context *cmd_ctx) return register_commands(cmd_ctx, NULL, jsp_command_handlers); } +void jsp_service_free(void) +{ + free(jsp_port); +} diff --git a/src/target/openrisc/jsp_server.h b/src/target/openrisc/jsp_server.h index f8e7121..e5cfaa8 100644 --- a/src/target/openrisc/jsp_server.h +++ b/src/target/openrisc/jsp_server.h @@ -13,5 +13,6 @@ struct jsp_service { int jsp_init(struct or1k_jtag *jtag_info, char *banner); int jsp_register_commands(struct command_context *cmd_ctx); +void jsp_service_free(void); #endif /* OPENOCD_TARGET_OPENRISC_JSP_SERVER_H */ diff --git a/src/target/register.h b/src/target/register.h index dc18e9a..32c1f39 100644 --- a/src/target/register.h +++ b/src/target/register.h @@ -25,12 +25,14 @@ struct target; enum reg_type { + REG_TYPE_BOOL, REG_TYPE_INT, REG_TYPE_INT8, REG_TYPE_INT16, REG_TYPE_INT32, REG_TYPE_INT64, REG_TYPE_INT128, + REG_TYPE_UINT, REG_TYPE_UINT8, REG_TYPE_UINT16, REG_TYPE_UINT32, @@ -66,6 +68,7 @@ struct reg_data_type_union { struct reg_data_type_bitfield { uint32_t start; uint32_t end; + enum reg_type type; }; struct reg_data_type_struct_field { diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am new file mode 100644 index 0000000..83f1a8c --- /dev/null +++ b/src/target/riscv/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES += %D%/libriscv.la +%C%_libriscv_la_SOURCES = \ + %D%/asm.h \ + %D%/batch.h \ + %D%/debug_defines.h \ + %D%/encoding.h \ + %D%/gdb_regs.h \ + %D%/opcodes.h \ + %D%/program.h \ + %D%/riscv.h \ + %D%/batch.c \ + %D%/program.c \ + %D%/riscv-011.c \ + %D%/riscv-013.c \ + %D%/riscv.c \ + %D%/riscv_semihosting.c diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index 117119d..9327cb3 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -160,11 +160,10 @@ void dump_field(const struct scan_field *field) log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, __PRETTY_FUNCTION__, - "%db %s %08x @%02x -> %s %08x @%02x [0x%p -> 0x%p]", + "%db %s %08x @%02x -> %s %08x @%02x", field->num_bits, op_string[out_op], out_data, out_address, - status_string[in_op], in_data, in_address, - field->out_value, field->in_value); + status_string[in_op], in_data, in_address); } else { log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?", diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index 7308bb9..0cabf15 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -229,7 +229,8 @@ * Explains why Debug Mode was entered. * * When there are multiple reasons to enter Debug Mode in a single -* cycle, the cause with the highest priority is the one written. +* cycle, hardware should set \Fcause to the cause with the highest +* priority. * * 1: An {\tt ebreak} instruction was executed. (priority 3) * @@ -245,6 +246,15 @@ #define CSR_DCSR_CAUSE_LENGTH 3 #define CSR_DCSR_CAUSE (0x7U << CSR_DCSR_CAUSE_OFFSET) /* +* When 1, \Fmprv in \Rmstatus takes effect during debug mode. +* When 0, it is ignored during debug mode. +* Implementing this bit is optional. +* If not implemented it should be tied to 0. + */ +#define CSR_DCSR_MPRVEN_OFFSET 4 +#define CSR_DCSR_MPRVEN_LENGTH 1 +#define CSR_DCSR_MPRVEN (0x1U << CSR_DCSR_MPRVEN_OFFSET) +/* * When set, there is a Non-Maskable-Interrupt (NMI) pending for the hart. * * Since an NMI can indicate a hardware error condition, @@ -279,14 +289,14 @@ #define CSR_DCSR_PRV (0x3U << CSR_DCSR_PRV_OFFSET) #define CSR_DPC 0x7b1 #define CSR_DPC_DPC_OFFSET 0 -#define CSR_DPC_DPC_LENGTH XLEN -#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET) +#define CSR_DPC_DPC_LENGTH MXLEN +#define CSR_DPC_DPC (((1L<<MXLEN)-1) << CSR_DPC_DPC_OFFSET) #define CSR_DSCRATCH0 0x7b2 #define CSR_DSCRATCH1 0x7b3 #define CSR_TSELECT 0x7a0 #define CSR_TSELECT_INDEX_OFFSET 0 -#define CSR_TSELECT_INDEX_LENGTH XLEN -#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET) +#define CSR_TSELECT_INDEX_LENGTH MXLEN +#define CSR_TSELECT_INDEX (((1L<<MXLEN)-1) << CSR_TSELECT_INDEX_OFFSET) #define CSR_TDATA1 0x7a1 /* * 0: There is no trigger at this \Rtselect. @@ -300,12 +310,22 @@ * 3: The trigger is an instruction count trigger. The remaining bits * in this register act as described in \Ricount. * +* 4: The trigger is an interrupt trigger. The remaining bits +* in this register act as described in \Ritrigger. +* +* 5: The trigger is an exception trigger. The remaining bits +* in this register act as described in \Retrigger. +* * 15: This trigger exists (so enumeration shouldn't terminate), but * is not currently available. * * Other values are reserved for future use. +* +* When this field is written to an unsupported value, it takes on its +* reset value instead. The reset value is any one of the types +* supported by the trigger selected by \Rtselect. */ -#define CSR_TDATA1_TYPE_OFFSET (XLEN-4) +#define CSR_TDATA1_TYPE_OFFSET (MXLEN-4) #define CSR_TDATA1_TYPE_LENGTH 4 #define CSR_TDATA1_TYPE (0xfULL << CSR_TDATA1_TYPE_OFFSET) /* @@ -317,39 +337,57 @@ * * This bit is only writable from Debug Mode. */ -#define CSR_TDATA1_DMODE_OFFSET (XLEN-5) +#define CSR_TDATA1_DMODE_OFFSET (MXLEN-5) #define CSR_TDATA1_DMODE_LENGTH 1 #define CSR_TDATA1_DMODE (0x1ULL << CSR_TDATA1_DMODE_OFFSET) /* * Trigger-specific data. */ #define CSR_TDATA1_DATA_OFFSET 0 -#define CSR_TDATA1_DATA_LENGTH (XLEN - 5) -#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET) +#define CSR_TDATA1_DATA_LENGTH (MXLEN - 5) +#define CSR_TDATA1_DATA (((1L<<MXLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET) #define CSR_TDATA2 0x7a2 #define CSR_TDATA2_DATA_OFFSET 0 -#define CSR_TDATA2_DATA_LENGTH XLEN -#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET) +#define CSR_TDATA2_DATA_LENGTH MXLEN +#define CSR_TDATA2_DATA (((1L<<MXLEN)-1) << CSR_TDATA2_DATA_OFFSET) #define CSR_TDATA3 0x7a3 #define CSR_TDATA3_DATA_OFFSET 0 -#define CSR_TDATA3_DATA_LENGTH XLEN -#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET) +#define CSR_TDATA3_DATA_LENGTH MXLEN +#define CSR_TDATA3_DATA (((1L<<MXLEN)-1) << CSR_TDATA3_DATA_OFFSET) +#define CSR_TINFO 0x7a4 +/* +* One bit for each possible \Ftype enumerated in \Rtdataone. Bit N +* corresponds to type N. If the bit is set, then that type is +* supported by the currently selected trigger. +* +* If the currently selected trigger doesn't exist, this field +* contains 1. +* +* If \Ftype is not writable, this register may be unimplemented, in +* which case reading it causes an illegal instruction exception. In +* this case the debugger can read the only supported type from +* \Rtdataone. + */ +#define CSR_TINFO_INFO_OFFSET 0 +#define CSR_TINFO_INFO_LENGTH 16 +#define CSR_TINFO_INFO (0xffffULL << CSR_TINFO_INFO_OFFSET) #define CSR_MCONTROL 0x7a1 -#define CSR_MCONTROL_TYPE_OFFSET (XLEN-4) +#define CSR_MCONTROL_TYPE_OFFSET (MXLEN-4) #define CSR_MCONTROL_TYPE_LENGTH 4 #define CSR_MCONTROL_TYPE (0xfULL << CSR_MCONTROL_TYPE_OFFSET) -#define CSR_MCONTROL_DMODE_OFFSET (XLEN-5) +#define CSR_MCONTROL_DMODE_OFFSET (MXLEN-5) #define CSR_MCONTROL_DMODE_LENGTH 1 #define CSR_MCONTROL_DMODE (0x1ULL << CSR_MCONTROL_DMODE_OFFSET) /* * Specifies the largest naturally aligned powers-of-two (NAPOT) range -* supported by the hardware. The value is the logarithm base 2 of the +* supported by the hardware when \Fmatch is 1. The value is the +* logarithm base 2 of the * number of bytes in that range. A value of 0 indicates that only * exact value matches are supported (one byte range). A value of 63 * corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in * size. */ -#define CSR_MCONTROL_MASKMAX_OFFSET (XLEN-11) +#define CSR_MCONTROL_MASKMAX_OFFSET (MXLEN-11) #define CSR_MCONTROL_MASKMAX_LENGTH 6 #define CSR_MCONTROL_MASKMAX (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET) /* @@ -400,22 +438,8 @@ #define CSR_MCONTROL_TIMING_LENGTH 1 #define CSR_MCONTROL_TIMING (0x1ULL << CSR_MCONTROL_TIMING_OFFSET) /* -* Determines what happens when this trigger matches. -* -* 0: Raise a breakpoint exception. (Used when software wants to use -* the trigger module without an external debugger attached.) -* -* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.) -* -* 2: Start tracing. -* -* 3: Stop tracing. -* -* 4: Emit trace data for this match. If it is a data access match, -* emit appropriate Load/Store Address/Data. If it is an instruction -* execution, emit its PC. -* -* Other values are reserved for future use. +* The action to take when the trigger fires. The values are explained +* in Table~\ref{tab:action}. */ #define CSR_MCONTROL_ACTION_OFFSET 12 #define CSR_MCONTROL_ACTION_LENGTH 6 @@ -425,6 +449,18 @@ * * 1: While this trigger does not match, it prevents the trigger with * the next index from matching. +* +* Because \Fchain affects the next trigger, hardware must zero it in +* writes to \Rmcontrol that set \Fdmode to 0 if the next trigger has +* \Fdmode of 1. +* In addition hardware should ignore writes to \Rmcontrol that set +* \Fdmode to 1 if the previous trigger has both \Fdmode of 0 and +* \Fchain of 1. Debuggers must avoid the latter case by checking +* \Fchain on the previous trigger if they're writing \Rmcontrol. +* +* Implementations that wish to limit the maximum length of a trigger +* chain (eg. to meet timing requirements) may do so by zeroing +* \Fchain in writes to \Rmcontrol that would make the chain too long. */ #define CSR_MCONTROL_CHAIN_OFFSET 11 #define CSR_MCONTROL_CHAIN_LENGTH 1 @@ -433,7 +469,7 @@ * 0: Matches when the value equals \Rtdatatwo. * * 1: Matches when the top M bits of the value match the top M bits of -* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant +* \Rtdatatwo. M is MXLEN-1 minus the index of the least-significant * bit containing 0 in \Rtdatatwo. * * 2: Matches when the value is greater than (unsigned) or equal to @@ -492,10 +528,10 @@ #define CSR_MCONTROL_LOAD_LENGTH 1 #define CSR_MCONTROL_LOAD (0x1ULL << CSR_MCONTROL_LOAD_OFFSET) #define CSR_ICOUNT 0x7a1 -#define CSR_ICOUNT_TYPE_OFFSET (XLEN-4) +#define CSR_ICOUNT_TYPE_OFFSET (MXLEN-4) #define CSR_ICOUNT_TYPE_LENGTH 4 #define CSR_ICOUNT_TYPE (0xfULL << CSR_ICOUNT_TYPE_OFFSET) -#define CSR_ICOUNT_DMODE_OFFSET (XLEN-5) +#define CSR_ICOUNT_DMODE_OFFSET (MXLEN-5) #define CSR_ICOUNT_DMODE_LENGTH 1 #define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET) /* @@ -539,26 +575,102 @@ #define CSR_ICOUNT_U_LENGTH 1 #define CSR_ICOUNT_U (0x1ULL << CSR_ICOUNT_U_OFFSET) /* -* Determines what happens when this trigger matches. -* -* 0: Raise a breakpoint exception. (Used when software wants to use the -* trigger module without an external debugger attached.) -* -* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.) -* -* 2: Start tracing. -* -* 3: Stop tracing. -* -* 4: Emit trace data for this match. If it is a data access match, -* emit appropriate Load/Store Address/Data. If it is an instruction -* execution, emit its PC. -* -* Other values are reserved for future use. +* The action to take when the trigger fires. The values are explained +* in Table~\ref{tab:action}. */ #define CSR_ICOUNT_ACTION_OFFSET 0 #define CSR_ICOUNT_ACTION_LENGTH 6 #define CSR_ICOUNT_ACTION (0x3fULL << CSR_ICOUNT_ACTION_OFFSET) +#define CSR_ITRIGGER 0x7a1 +#define CSR_ITRIGGER_TYPE_OFFSET (MXLEN-4) +#define CSR_ITRIGGER_TYPE_LENGTH 4 +#define CSR_ITRIGGER_TYPE (0xfULL << CSR_ITRIGGER_TYPE_OFFSET) +#define CSR_ITRIGGER_DMODE_OFFSET (MXLEN-5) +#define CSR_ITRIGGER_DMODE_LENGTH 1 +#define CSR_ITRIGGER_DMODE (0x1ULL << CSR_ITRIGGER_DMODE_OFFSET) +/* +* If this optional bit is implemented, the hardware sets it when this +* trigger matches. The trigger's user can set or clear it at any +* time. The trigger's user can use this bit to determine which +* trigger(s) matched. If the bit is not implemented, it is always 0 +* and writing it has no effect. + */ +#define CSR_ITRIGGER_HIT_OFFSET (MXLEN-6) +#define CSR_ITRIGGER_HIT_LENGTH 1 +#define CSR_ITRIGGER_HIT (0x1ULL << CSR_ITRIGGER_HIT_OFFSET) +/* +* When set, enable this trigger for interrupts that are taken from M +* mode. + */ +#define CSR_ITRIGGER_M_OFFSET 9 +#define CSR_ITRIGGER_M_LENGTH 1 +#define CSR_ITRIGGER_M (0x1ULL << CSR_ITRIGGER_M_OFFSET) +/* +* When set, enable this trigger for interrupts that are taken from S +* mode. + */ +#define CSR_ITRIGGER_S_OFFSET 7 +#define CSR_ITRIGGER_S_LENGTH 1 +#define CSR_ITRIGGER_S (0x1ULL << CSR_ITRIGGER_S_OFFSET) +/* +* When set, enable this trigger for interrupts that are taken from U +* mode. + */ +#define CSR_ITRIGGER_U_OFFSET 6 +#define CSR_ITRIGGER_U_LENGTH 1 +#define CSR_ITRIGGER_U (0x1ULL << CSR_ITRIGGER_U_OFFSET) +/* +* The action to take when the trigger fires. The values are explained +* in Table~\ref{tab:action}. + */ +#define CSR_ITRIGGER_ACTION_OFFSET 0 +#define CSR_ITRIGGER_ACTION_LENGTH 6 +#define CSR_ITRIGGER_ACTION (0x3fULL << CSR_ITRIGGER_ACTION_OFFSET) +#define CSR_ETRIGGER 0x7a1 +#define CSR_ETRIGGER_TYPE_OFFSET (MXLEN-4) +#define CSR_ETRIGGER_TYPE_LENGTH 4 +#define CSR_ETRIGGER_TYPE (0xfULL << CSR_ETRIGGER_TYPE_OFFSET) +#define CSR_ETRIGGER_DMODE_OFFSET (MXLEN-5) +#define CSR_ETRIGGER_DMODE_LENGTH 1 +#define CSR_ETRIGGER_DMODE (0x1ULL << CSR_ETRIGGER_DMODE_OFFSET) +/* +* If this optional bit is implemented, the hardware sets it when this +* trigger matches. The trigger's user can set or clear it at any +* time. The trigger's user can use this bit to determine which +* trigger(s) matched. If the bit is not implemented, it is always 0 +* and writing it has no effect. + */ +#define CSR_ETRIGGER_HIT_OFFSET (MXLEN-6) +#define CSR_ETRIGGER_HIT_LENGTH 1 +#define CSR_ETRIGGER_HIT (0x1ULL << CSR_ETRIGGER_HIT_OFFSET) +/* +* When set, enable this trigger for exceptions that are taken from M +* mode. + */ +#define CSR_ETRIGGER_M_OFFSET 9 +#define CSR_ETRIGGER_M_LENGTH 1 +#define CSR_ETRIGGER_M (0x1ULL << CSR_ETRIGGER_M_OFFSET) +/* +* When set, enable this trigger for exceptions that are taken from S +* mode. + */ +#define CSR_ETRIGGER_S_OFFSET 7 +#define CSR_ETRIGGER_S_LENGTH 1 +#define CSR_ETRIGGER_S (0x1ULL << CSR_ETRIGGER_S_OFFSET) +/* +* When set, enable this trigger for exceptions that are taken from U +* mode. + */ +#define CSR_ETRIGGER_U_OFFSET 6 +#define CSR_ETRIGGER_U_LENGTH 1 +#define CSR_ETRIGGER_U (0x1ULL << CSR_ETRIGGER_U_OFFSET) +/* +* The action to take when the trigger fires. The values are explained +* in Table~\ref{tab:action}. + */ +#define CSR_ETRIGGER_ACTION_OFFSET 0 +#define CSR_ETRIGGER_ACTION_LENGTH 6 +#define CSR_ETRIGGER_ACTION (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET) #define DMI_DMSTATUS 0x11 /* * If 1, then there is an implicit {\tt ebreak} instruction at the @@ -667,6 +779,14 @@ #define DMI_DMSTATUS_AUTHBUSY_LENGTH 1 #define DMI_DMSTATUS_AUTHBUSY (0x1U << DMI_DMSTATUS_AUTHBUSY_OFFSET) /* +* 1 if this Debug Module supports halt-on-reset functionality +* controllable by the \Fsetresethaltreq and \Fclrresethaltreq bits. +* 0 otherwise. + */ +#define DMI_DMSTATUS_HASRESETHALTREQ_OFFSET 5 +#define DMI_DMSTATUS_HASRESETHALTREQ_LENGTH 1 +#define DMI_DMSTATUS_HASRESETHALTREQ (0x1U << DMI_DMSTATUS_HASRESETHALTREQ_OFFSET) +/* * 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which * is not relevant to the Device Tree. * @@ -772,6 +892,29 @@ #define DMI_DMCONTROL_HARTSELHI_LENGTH 10 #define DMI_DMCONTROL_HARTSELHI (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET) /* +* This optional field writes the halt-on-reset request bit for all +* currently selected harts. +* When set to 1, each selected hart will halt upon the next deassertion +* of its reset. The halt-on-reset request bit is not automatically +* cleared. The debugger must write to \Fclrresethaltreq to clear it. +* +* Writes apply to the new value of \Fhartsel and \Fhasel. +* +* If \Fhasresethaltreq is 0, this field is not implemented. + */ +#define DMI_DMCONTROL_SETRESETHALTREQ_OFFSET 3 +#define DMI_DMCONTROL_SETRESETHALTREQ_LENGTH 1 +#define DMI_DMCONTROL_SETRESETHALTREQ (0x1U << DMI_DMCONTROL_SETRESETHALTREQ_OFFSET) +/* +* This optional field clears the halt-on-reset request bit for all +* currently selected harts. +* +* Writes apply to the new value of \Fhartsel and \Fhasel. + */ +#define DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET 2 +#define DMI_DMCONTROL_CLRRESETHALTREQ_LENGTH 1 +#define DMI_DMCONTROL_CLRRESETHALTREQ (0x1U << DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET) +/* * This bit controls the reset signal from the DM to the rest of the * system. The signal should reset every part of the system, including * every hart, except for the DM and any logic required to access the @@ -796,7 +939,7 @@ * Debug Module after power up, including the platform's system reset * or Debug Transport reset signals. * -* A debugger may pulse this bit low to get the debug module into a +* A debugger may pulse this bit low to get the Debug Module into a * known state. * * Implementations may use this bit to aid debugging, for example by @@ -818,7 +961,7 @@ #define DMI_HARTINFO_NSCRATCH (0xfU << DMI_HARTINFO_NSCRATCH_OFFSET) /* * 0: The {\tt data} registers are shadowed in the hart by CSR -* registers. Each CSR register is XLEN bits in size, and corresponds +* registers. Each CSR register is MXLEN bits in size, and corresponds * to a single argument, per Table~\ref{tab:datareg}. * * 1: The {\tt data} registers are shadowed in the hart's memory map. @@ -1009,7 +1152,7 @@ * it's explicitly cleared by the debugger. * * While this field is non-zero, no more system bus accesses can be -* initiated by the debug module. +* initiated by the Debug Module. */ #define DMI_SBCS_SBBUSYERROR_OFFSET 22 #define DMI_SBCS_SBBUSYERROR_LENGTH 1 @@ -1020,8 +1163,9 @@ * bit goes high immediately when a read or write is requested for any * reason, and does not go low until the access is fully completed. * -* To avoid race conditions, debuggers must not try to clear \Fsberror -* until they read \Fsbbusy as 0. +* Writes to \Rsbcs while \Fsbbusy is high result in undefined +* behavior. A debugger must not write to \Rsbcs until it reads +* \Fsbbusy as 0. */ #define DMI_SBCS_SBBUSY_OFFSET 21 #define DMI_SBCS_SBBUSY_LENGTH 1 @@ -1067,11 +1211,13 @@ #define DMI_SBCS_SBREADONDATA_LENGTH 1 #define DMI_SBCS_SBREADONDATA (0x1U << DMI_SBCS_SBREADONDATA_OFFSET) /* -* When the debug module's system bus +* When the Debug Module's system bus * master causes a bus error, this field gets set. The bits in this * field remain set until they are cleared by writing 1 to them. * While this field is non-zero, no more system bus accesses can be -* initiated by the debug module. +* initiated by the Debug Module. +* +* An implementation may report "Other" (7) for any error condition. * * An implementation may report "Other" (7) for any error condition. * @@ -1268,107 +1414,3 @@ #define VIRT_PRIV_PRV_OFFSET 0 #define VIRT_PRIV_PRV_LENGTH 2 #define VIRT_PRIV_PRV (0x3U << VIRT_PRIV_PRV_OFFSET) -#define DMI_SERCS 0x34 -/* -* Number of supported serial ports. - */ -#define DMI_SERCS_SERIALCOUNT_OFFSET 28 -#define DMI_SERCS_SERIALCOUNT_LENGTH 4 -#define DMI_SERCS_SERIALCOUNT (0xfU << DMI_SERCS_SERIALCOUNT_OFFSET) -/* -* Select which serial port is accessed by \Rserrx and \Rsertx. - */ -#define DMI_SERCS_SERIAL_OFFSET 24 -#define DMI_SERCS_SERIAL_LENGTH 3 -#define DMI_SERCS_SERIAL (0x7U << DMI_SERCS_SERIAL_OFFSET) -#define DMI_SERCS_ERROR7_OFFSET 23 -#define DMI_SERCS_ERROR7_LENGTH 1 -#define DMI_SERCS_ERROR7 (0x1U << DMI_SERCS_ERROR7_OFFSET) -#define DMI_SERCS_VALID7_OFFSET 22 -#define DMI_SERCS_VALID7_LENGTH 1 -#define DMI_SERCS_VALID7 (0x1U << DMI_SERCS_VALID7_OFFSET) -#define DMI_SERCS_FULL7_OFFSET 21 -#define DMI_SERCS_FULL7_LENGTH 1 -#define DMI_SERCS_FULL7 (0x1U << DMI_SERCS_FULL7_OFFSET) -#define DMI_SERCS_ERROR6_OFFSET 20 -#define DMI_SERCS_ERROR6_LENGTH 1 -#define DMI_SERCS_ERROR6 (0x1U << DMI_SERCS_ERROR6_OFFSET) -#define DMI_SERCS_VALID6_OFFSET 19 -#define DMI_SERCS_VALID6_LENGTH 1 -#define DMI_SERCS_VALID6 (0x1U << DMI_SERCS_VALID6_OFFSET) -#define DMI_SERCS_FULL6_OFFSET 18 -#define DMI_SERCS_FULL6_LENGTH 1 -#define DMI_SERCS_FULL6 (0x1U << DMI_SERCS_FULL6_OFFSET) -#define DMI_SERCS_ERROR5_OFFSET 17 -#define DMI_SERCS_ERROR5_LENGTH 1 -#define DMI_SERCS_ERROR5 (0x1U << DMI_SERCS_ERROR5_OFFSET) -#define DMI_SERCS_VALID5_OFFSET 16 -#define DMI_SERCS_VALID5_LENGTH 1 -#define DMI_SERCS_VALID5 (0x1U << DMI_SERCS_VALID5_OFFSET) -#define DMI_SERCS_FULL5_OFFSET 15 -#define DMI_SERCS_FULL5_LENGTH 1 -#define DMI_SERCS_FULL5 (0x1U << DMI_SERCS_FULL5_OFFSET) -#define DMI_SERCS_ERROR4_OFFSET 14 -#define DMI_SERCS_ERROR4_LENGTH 1 -#define DMI_SERCS_ERROR4 (0x1U << DMI_SERCS_ERROR4_OFFSET) -#define DMI_SERCS_VALID4_OFFSET 13 -#define DMI_SERCS_VALID4_LENGTH 1 -#define DMI_SERCS_VALID4 (0x1U << DMI_SERCS_VALID4_OFFSET) -#define DMI_SERCS_FULL4_OFFSET 12 -#define DMI_SERCS_FULL4_LENGTH 1 -#define DMI_SERCS_FULL4 (0x1U << DMI_SERCS_FULL4_OFFSET) -#define DMI_SERCS_ERROR3_OFFSET 11 -#define DMI_SERCS_ERROR3_LENGTH 1 -#define DMI_SERCS_ERROR3 (0x1U << DMI_SERCS_ERROR3_OFFSET) -#define DMI_SERCS_VALID3_OFFSET 10 -#define DMI_SERCS_VALID3_LENGTH 1 -#define DMI_SERCS_VALID3 (0x1U << DMI_SERCS_VALID3_OFFSET) -#define DMI_SERCS_FULL3_OFFSET 9 -#define DMI_SERCS_FULL3_LENGTH 1 -#define DMI_SERCS_FULL3 (0x1U << DMI_SERCS_FULL3_OFFSET) -#define DMI_SERCS_ERROR2_OFFSET 8 -#define DMI_SERCS_ERROR2_LENGTH 1 -#define DMI_SERCS_ERROR2 (0x1U << DMI_SERCS_ERROR2_OFFSET) -#define DMI_SERCS_VALID2_OFFSET 7 -#define DMI_SERCS_VALID2_LENGTH 1 -#define DMI_SERCS_VALID2 (0x1U << DMI_SERCS_VALID2_OFFSET) -#define DMI_SERCS_FULL2_OFFSET 6 -#define DMI_SERCS_FULL2_LENGTH 1 -#define DMI_SERCS_FULL2 (0x1U << DMI_SERCS_FULL2_OFFSET) -#define DMI_SERCS_ERROR1_OFFSET 5 -#define DMI_SERCS_ERROR1_LENGTH 1 -#define DMI_SERCS_ERROR1 (0x1U << DMI_SERCS_ERROR1_OFFSET) -#define DMI_SERCS_VALID1_OFFSET 4 -#define DMI_SERCS_VALID1_LENGTH 1 -#define DMI_SERCS_VALID1 (0x1U << DMI_SERCS_VALID1_OFFSET) -#define DMI_SERCS_FULL1_OFFSET 3 -#define DMI_SERCS_FULL1_LENGTH 1 -#define DMI_SERCS_FULL1 (0x1U << DMI_SERCS_FULL1_OFFSET) -/* -* 1 when the debugger-to-core queue for serial port 0 has -* over or underflowed. This bit will remain set until it is reset by -* writing 1 to this bit. - */ -#define DMI_SERCS_ERROR0_OFFSET 2 -#define DMI_SERCS_ERROR0_LENGTH 1 -#define DMI_SERCS_ERROR0 (0x1U << DMI_SERCS_ERROR0_OFFSET) -/* -* 1 when the core-to-debugger queue for serial port 0 is not empty. - */ -#define DMI_SERCS_VALID0_OFFSET 1 -#define DMI_SERCS_VALID0_LENGTH 1 -#define DMI_SERCS_VALID0 (0x1U << DMI_SERCS_VALID0_OFFSET) -/* -* 1 when the debugger-to-core queue for serial port 0 is full. - */ -#define DMI_SERCS_FULL0_OFFSET 0 -#define DMI_SERCS_FULL0_LENGTH 1 -#define DMI_SERCS_FULL0 (0x1U << DMI_SERCS_FULL0_OFFSET) -#define DMI_SERTX 0x35 -#define DMI_SERTX_DATA_OFFSET 0 -#define DMI_SERTX_DATA_LENGTH 32 -#define DMI_SERTX_DATA (0xffffffffU << DMI_SERTX_DATA_OFFSET) -#define DMI_SERRX 0x36 -#define DMI_SERRX_DATA_OFFSET 0 -#define DMI_SERRX_DATA_LENGTH 32 -#define DMI_SERRX_DATA (0xffffffffU << DMI_SERRX_DATA_OFFSET) diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h index c109ce1..e214c0c 100644 --- a/src/target/riscv/encoding.h +++ b/src/target/riscv/encoding.h @@ -1,4 +1,4 @@ -// See LICENSE for license details. +/* See LICENSE for license details. */ #ifndef RISCV_CSR_ENCODING_H #define RISCV_CSR_ENCODING_H @@ -156,16 +156,16 @@ #define EXT_IO_BASE 0x40000000 #define DRAM_BASE 0x80000000 -// page table entry (PTE) fields -#define PTE_V 0x001 // Valid -#define PTE_R 0x002 // Read -#define PTE_W 0x004 // Write -#define PTE_X 0x008 // Execute -#define PTE_U 0x010 // User -#define PTE_G 0x020 // Global -#define PTE_A 0x040 // Accessed -#define PTE_D 0x080 // Dirty -#define PTE_SOFT 0x300 // Reserved for Software +/* page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ #define PTE_PPN_SHIFT 10 @@ -191,6 +191,7 @@ #ifdef __GNUC__ +/* #define read_csr(reg) ({ unsigned long __tmp; \ asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ __tmp; }) @@ -209,6 +210,7 @@ #define clear_csr(reg, bit) ({ unsigned long __tmp; \ asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ __tmp; }) + */ #define rdtime() read_csr(time) #define rdcycle() read_csr(cycle) diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index 632567f..a77b007 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -240,6 +240,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot) case SLOT1: return 5; case SLOT_LAST: return info->dramsize-1; } + break; case 64: switch (slot) { case SLOT0: return 4; @@ -455,12 +456,12 @@ static uint64_t dbus_read(struct target *target, uint16_t address) uint64_t value; dbus_status_t status; uint16_t address_in; - + /* If the previous read/write was to the same address, we will get the read data * from the previous access. * While somewhat nonintuitive, this is an efficient way to get the data. */ - + unsigned i = 0; do { status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, address, 0); @@ -680,7 +681,7 @@ static bits_t read_bits(struct target *target) } increase_dbus_busy_delay(target); } else if (status == DBUS_STATUS_FAILED) { - // TODO: return an actual error + /* TODO: return an actual error */ return err_result; } } while (status == DBUS_STATUS_BUSY && i++ < 256); @@ -1407,12 +1408,6 @@ static int strict_step(struct target *target, bool announce) LOG_DEBUG("enter"); - struct breakpoint *breakpoint = target->breakpoints; - while (breakpoint) { - riscv_remove_breakpoint(target, breakpoint); - breakpoint = breakpoint->next; - } - struct watchpoint *watchpoint = target->watchpoints; while (watchpoint) { riscv_remove_watchpoint(target, watchpoint); @@ -1423,12 +1418,6 @@ static int strict_step(struct target *target, bool announce) if (result != ERROR_OK) return result; - breakpoint = target->breakpoints; - while (breakpoint) { - riscv_add_breakpoint(target, breakpoint); - breakpoint = breakpoint->next; - } - watchpoint = target->watchpoints; while (watchpoint) { riscv_add_watchpoint(target, watchpoint); @@ -1572,11 +1561,11 @@ static int examine(struct target *target) } LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target)); - if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) { + if (read_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) { const unsigned old_csr_misa = 0xf10; LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA, old_csr_misa); - if (read_csr(target, &r->misa, old_csr_misa) != ERROR_OK) { + if (read_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) { /* Maybe this is an old core that still has $misa at the old * address. */ LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa); @@ -1598,7 +1587,7 @@ static int examine(struct target *target) for (size_t i = 0; i < 32; ++i) reg_cache_set(target, i, -1); LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, - riscv_xlen(target), r->misa); + riscv_xlen(target), r->misa[0]); return ERROR_OK; } @@ -1787,6 +1776,8 @@ static riscv_error_t handle_halt_routine(struct target *target) break; default: assert(0); + LOG_ERROR("Got invalid register result %d", result); + goto error; } if (riscv_xlen(target) == 32) { reg_cache_set(target, reg, data & 0xffffffff); @@ -1847,7 +1838,7 @@ static int handle_halt(struct target *target, bool announce) target->debug_reason = DBG_REASON_BREAKPOINT; break; case DCSR_CAUSE_HWBP: - target->debug_reason = DBG_REASON_WPTANDBKPT; + target->debug_reason = DBG_REASON_WATCHPOINT; /* If we halted because of a data trigger, gdb doesn't know to do * the disable-breakpoints-step-enable-breakpoints dance. */ info->need_strict_step = true; @@ -1873,6 +1864,12 @@ static int handle_halt(struct target *target, bool announce) riscv_enumerate_triggers(target); } + if (target->debug_reason == DBG_REASON_BREAKPOINT) { + int retval; + if (riscv_semihosting(target, &retval) != 0) + return retval; + } + if (announce) target_call_event_callbacks(target, TARGET_EVENT_HALTED); diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 1cfda35..3ab8587 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -39,7 +39,7 @@ static void riscv013_clear_abstract_error(struct target *target); static int riscv013_get_register(struct target *target, riscv_reg_t *value, int hid, int rid); static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value); -static void riscv013_select_current_hart(struct target *target); +static int riscv013_select_current_hart(struct target *target); static int riscv013_halt_current_hart(struct target *target); static int riscv013_resume_current_hart(struct target *target); static int riscv013_step_current_hart(struct target *target); @@ -57,6 +57,7 @@ static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a); static int riscv013_dmi_write_u64_bits(struct target *target); static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf); +static int register_read(struct target *target, uint64_t *value, uint32_t number); static int register_read_direct(struct target *target, uint64_t *value, uint32_t number); static int register_write_direct(struct target *target, unsigned number, uint64_t value); @@ -149,6 +150,8 @@ typedef struct { bool was_reset; /* Targets that are connected to this DM. */ struct list_head target_list; + /* The currently selected hartid on this DM. */ + int current_hartid; } dm013_info_t; typedef struct { @@ -191,8 +194,6 @@ typedef struct { * go low. */ unsigned int ac_busy_delay; - bool need_strict_step; - bool abstract_read_csr_supported; bool abstract_write_csr_supported; bool abstract_read_fpr_supported; @@ -248,6 +249,7 @@ static dm013_info_t *get_dm(struct target *target) if (!dm) { dm = calloc(1, sizeof(dm013_info_t)); dm->abs_chain_position = abs_chain_position; + dm->current_hartid = -1; INIT_LIST_HEAD(&dm->target_list); list_add(&dm->list, &dm_list); } @@ -255,9 +257,8 @@ static dm013_info_t *get_dm(struct target *target) info->dm = dm; target_list_t *target_entry; list_for_each_entry(target_entry, &dm->target_list, list) { - if (target_entry->target == target) { + if (target_entry->target == target) return dm; - } } target_entry = calloc(1, sizeof(*target_entry)); target_entry->target = target; @@ -266,10 +267,18 @@ static dm013_info_t *get_dm(struct target *target) return dm; } -static uint32_t hartsel_mask(const struct target *target) +static uint32_t set_hartsel(uint32_t initial, uint32_t index) { - RISCV013_INFO(info); - return ((1L<<info->hartsellen)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET; + initial &= ~DMI_DMCONTROL_HARTSELLO; + initial &= ~DMI_DMCONTROL_HARTSELHI; + + uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1); + initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET; + uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH; + assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH); + initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET; + + return initial; } static void decode_dmi(char *text, unsigned address, unsigned data) @@ -283,11 +292,15 @@ static void decode_dmi(char *text, unsigned address, unsigned data) { DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" }, { DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" }, { DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" }, + { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" }, { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" }, { DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" }, { DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" }, + { DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" }, { DMI_DMSTATUS, DMI_DMSTATUS_IMPEBREAK, "impebreak" }, + { DMI_DMSTATUS, DMI_DMSTATUS_ALLHAVERESET, "allhavereset" }, + { DMI_DMSTATUS, DMI_DMSTATUS_ANYHAVERESET, "anyhavereset" }, { DMI_DMSTATUS, DMI_DMSTATUS_ALLRESUMEACK, "allresumeack" }, { DMI_DMSTATUS, DMI_DMSTATUS_ANYRESUMEACK, "anyresumeack" }, { DMI_DMSTATUS, DMI_DMSTATUS_ALLNONEXISTENT, "allnonexistent" }, @@ -485,58 +498,79 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); } -static int dmi_read(struct target *target, uint32_t *value, uint32_t address) +static int dmi_op_timeout(struct target *target, uint32_t *data_in, int dmi_op, + uint32_t address, uint32_t data_out, int timeout_sec) { select_dmi(target); dmi_status_t status; uint32_t address_in; - unsigned i = 0; + const char *op_name; + switch (dmi_op) { + case DMI_OP_NOP: + op_name = "nop"; + break; + case DMI_OP_READ: + op_name = "read"; + break; + case DMI_OP_WRITE: + op_name = "write"; + break; + default: + LOG_ERROR("Invalid DMI operation: %d", dmi_op); + return ERROR_FAIL; + } - /* This first loop ensures that the read request was actually sent - * to the target. Note that if for some reason this stays busy, - * it is actually due to the previous dmi_read or dmi_write. */ - for (i = 0; i < 256; i++) { - status = dmi_scan(target, NULL, NULL, DMI_OP_READ, address, 0, + time_t start = time(NULL); + /* This first loop performs the request. Note that if for some reason this + * stays busy, it is actually due to the previous access. */ + while (1) { + status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out, false); if (status == DMI_STATUS_BUSY) { increase_dmi_busy_delay(target); } else if (status == DMI_STATUS_SUCCESS) { break; } else { - LOG_ERROR("failed read from 0x%x, status=%d", address, status); + LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status); return ERROR_FAIL; } + if (time(NULL) - start > timeout_sec) + return ERROR_TIMEOUT_REACHED; } if (status != DMI_STATUS_SUCCESS) { - LOG_ERROR("Failed read from 0x%x; status=%d", address, status); + LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status); return ERROR_FAIL; } - /* This second loop ensures that we got the read - * data back. Note that NOP can result in a 'busy' result as well, but - * that would be noticed on the next DMI access we do. */ - for (i = 0; i < 256; i++) { - status = dmi_scan(target, &address_in, value, DMI_OP_NOP, address, 0, + /* This second loop ensures the request succeeded, and gets back data. + * Note that NOP can result in a 'busy' result as well, but that would be + * noticed on the next DMI access we do. */ + while (1) { + status = dmi_scan(target, &address_in, data_in, DMI_OP_NOP, address, 0, false); if (status == DMI_STATUS_BUSY) { increase_dmi_busy_delay(target); } else if (status == DMI_STATUS_SUCCESS) { break; } else { - LOG_ERROR("failed read (NOP) at 0x%x, status=%d", address, status); + LOG_ERROR("failed %s (NOP) at 0x%x, status=%d", op_name, address, + status); return ERROR_FAIL; } + if (time(NULL) - start > timeout_sec) + return ERROR_TIMEOUT_REACHED; } if (status != DMI_STATUS_SUCCESS) { - if (status == DMI_STATUS_FAILED) { - LOG_ERROR("Failed read (NOP) from 0x%x; status=%d", address, status); + if (status == DMI_STATUS_FAILED || !data_in) { + LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address, + status); } else { - LOG_ERROR("Failed read (NOP) from 0x%x; value=0x%x, status=%d", - address, *value, status); + LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d", + op_name, address, *data_in, status); } return ERROR_FAIL; } @@ -544,69 +578,54 @@ static int dmi_read(struct target *target, uint32_t *value, uint32_t address) return ERROR_OK; } -static int dmi_write(struct target *target, uint32_t address, uint32_t value) +static int dmi_op(struct target *target, uint32_t *data_in, int dmi_op, + uint32_t address, uint32_t data_out) { - select_dmi(target); - dmi_status_t status = DMI_STATUS_BUSY; - unsigned i = 0; - - /* The first loop ensures that we successfully sent the write request. */ - for (i = 0; i < 256; i++) { - status = dmi_scan(target, NULL, NULL, DMI_OP_WRITE, address, value, - address == DMI_COMMAND); - if (status == DMI_STATUS_BUSY) { - increase_dmi_busy_delay(target); - } else if (status == DMI_STATUS_SUCCESS) { - break; - } else { - LOG_ERROR("failed write to 0x%x, status=%d", address, status); - break; - } - } - - if (status != DMI_STATUS_SUCCESS) { - LOG_ERROR("Failed write to 0x%x;, status=%d", - address, status); + int result = dmi_op_timeout(target, data_in, dmi_op, address, data_out, + riscv_command_timeout_sec); + if (result == ERROR_TIMEOUT_REACHED) { + LOG_ERROR("DMI operation didn't complete in %d seconds. The target is " + "either really slow or broken. You could increase the " + "timeout with riscv set_command_timeout_sec.", + riscv_command_timeout_sec); return ERROR_FAIL; } + return result; +} - /* The second loop isn't strictly necessary, but would ensure that the - * write is complete/ has no non-busy errors before returning from this - * function. */ - for (i = 0; i < 256; i++) { - status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, address, 0, - false); - if (status == DMI_STATUS_BUSY) { - increase_dmi_busy_delay(target); - } else if (status == DMI_STATUS_SUCCESS) { - break; - } else { - LOG_ERROR("failed write (NOP) at 0x%x, status=%d", address, status); - break; - } - } - if (status != DMI_STATUS_SUCCESS) { - LOG_ERROR("failed to write (NOP) 0x%x to 0x%x; status=%d", value, address, status); - return ERROR_FAIL; - } +static int dmi_read(struct target *target, uint32_t *value, uint32_t address) +{ + return dmi_op(target, value, DMI_OP_READ, address, 0); +} - return ERROR_OK; +static int dmi_write(struct target *target, uint32_t address, uint32_t value) +{ + return dmi_op(target, NULL, DMI_OP_WRITE, address, value); } -int dmstatus_read(struct target *target, uint32_t *dmstatus, - bool authenticated) +int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, + bool authenticated, unsigned timeout_sec) { - if (dmi_read(target, dmstatus, DMI_DMSTATUS) != ERROR_OK) - return ERROR_FAIL; + int result = dmi_op_timeout(target, dmstatus, DMI_OP_READ, DMI_DMSTATUS, 0, + timeout_sec); + if (result != ERROR_OK) + return result; if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) { LOG_ERROR("Debugger is not authenticated to target Debug Module. " "(dmstatus=0x%x). Use `riscv authdata_read` and " - "`riscv_authdata_write` commands to authenticate.", *dmstatus); + "`riscv authdata_write` commands to authenticate.", *dmstatus); return ERROR_FAIL; } return ERROR_OK; } +int dmstatus_read(struct target *target, uint32_t *dmstatus, + bool authenticated) +{ + return dmstatus_read_timeout(target, dmstatus, authenticated, + riscv_command_timeout_sec); +} + static void increase_ac_busy_delay(struct target *target) { riscv013_info_t *info = get_info(target); @@ -676,17 +695,12 @@ static int execute_abstract_command(struct target *target, uint32_t command) LOG_DEBUG("command=0x%x", command); dmi_write(target, DMI_COMMAND, command); - { - uint32_t abstractcs = 0; - wait_for_idle(target, &abstractcs); - } + uint32_t abstractcs = 0; + wait_for_idle(target, &abstractcs); - uint32_t cs; - if (dmi_read(target, &cs, DMI_ABSTRACTCS) != ERROR_OK) - return ERROR_FAIL; - info->cmderr = get_field(cs, DMI_ABSTRACTCS_CMDERR); + info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); if (info->cmderr != 0) { - LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, cs); + LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs); /* Clear the error. */ dmi_write(target, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR, info->cmderr)); @@ -737,8 +751,8 @@ static int write_abstract_arg(struct target *target, unsigned index, /** * @size in bits */ -static uint32_t access_register_command(uint32_t number, unsigned size, - uint32_t flags) +static uint32_t access_register_command(struct target *target, uint32_t number, + unsigned size, uint32_t flags) { uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0); switch (size) { @@ -761,8 +775,13 @@ static uint32_t access_register_command(uint32_t number, unsigned size, } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { command = set_field(command, AC_ACCESS_REGISTER_REGNO, number - GDB_REGNO_CSR0); - } else { - assert(0); + } else if (number >= GDB_REGNO_COUNT) { + /* Custom register. */ + assert(target->reg_cache->reg_list[number].arch_info); + riscv_reg_info_t *reg_info = target->reg_cache->reg_list[number].arch_info; + assert(reg_info); + command = set_field(command, AC_ACCESS_REGISTER_REGNO, + 0xc000 + reg_info->custom_number); } command |= flags; @@ -782,7 +801,7 @@ static int register_read_abstract(struct target *target, uint64_t *value, !info->abstract_read_csr_supported) return ERROR_FAIL; - uint32_t command = access_register_command(number, size, + uint32_t command = access_register_command(target, number, size, AC_ACCESS_REGISTER_TRANSFER); int result = execute_abstract_command(target, command); @@ -817,7 +836,7 @@ static int register_write_abstract(struct target *target, uint32_t number, !info->abstract_write_csr_supported) return ERROR_FAIL; - uint32_t command = access_register_command(number, size, + uint32_t command = access_register_command(target, number, size, AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_WRITE); @@ -857,7 +876,7 @@ static int examine_progbuf(struct target *target) } uint64_t s0; - if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; struct riscv_program program; @@ -913,22 +932,25 @@ typedef struct { riscv_addr_t hart_address; /* Memory address to access the scratch memory from the debugger. */ riscv_addr_t debug_address; + struct working_area *area; } scratch_mem_t; /** * Find some scratch memory to be used with the given program. */ -static int scratch_find(struct target *target, +static int scratch_reserve(struct target *target, scratch_mem_t *scratch, struct riscv_program *program, unsigned size_bytes) { - riscv013_info_t *info = get_info(target); - riscv_addr_t alignment = 1; while (alignment < size_bytes) alignment *= 2; + scratch->area = NULL; + + riscv013_info_t *info = get_info(target); + if (info->dataaccess == 1) { /* Sign extend dataaddr. */ scratch->hart_address = info->dataaddr; @@ -959,8 +981,9 @@ static int scratch_find(struct target *target, return ERROR_OK; } - if (riscv_use_scratch_ram) { - scratch->hart_address = (riscv_scratch_ram_address + alignment - 1) & + if (target_alloc_working_area(target, size_bytes + alignment - 1, + &scratch->area) == ERROR_OK) { + scratch->hart_address = (scratch->area->address + alignment - 1) & ~(alignment - 1); scratch->memory_space = SPACE_DMI_RAM; scratch->debug_address = scratch->hart_address; @@ -968,10 +991,19 @@ static int scratch_find(struct target *target, } LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure " - "an address with 'riscv set_scratch_ram'.", size_bytes); + "a work area with 'configure -work-area-phys'.", size_bytes); return ERROR_FAIL; } +static int scratch_release(struct target *target, + scratch_mem_t *scratch) +{ + if (scratch->area) + return target_free_working_area(target, scratch->area); + + return ERROR_OK; +} + static int scratch_read64(struct target *target, scratch_mem_t *scratch, uint64_t *value) { @@ -1055,6 +1087,10 @@ static unsigned register_size(struct target *target, unsigned number) return riscv_xlen(target); } +/** + * Immediately write the new value to the requested register. This mechanism + * bypasses any caches. + */ static int register_write_direct(struct target *target, unsigned number, uint64_t value) { @@ -1066,6 +1102,11 @@ static int register_write_direct(struct target *target, unsigned number, int result = register_write_abstract(target, number, value, register_size(target, number)); + if (result == ERROR_OK && target->reg_cache) { + struct reg *reg = &target->reg_cache->reg_list[number]; + buf_set_u64(reg->value, 0, reg->size, value); + reg->valid = true; + } if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 || !riscv_is_halted(target)) return result; @@ -1074,33 +1115,39 @@ static int register_write_direct(struct target *target, unsigned number, riscv_program_init(&program, target); uint64_t s0; - if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; + scratch_mem_t scratch; + bool use_scratch = false; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - riscv_supports_extension(target, 'D') && + riscv_supports_extension(target, riscv_current_hartid(target), 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a register, so * we need to use some scratch RAM. */ + use_scratch = true; riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0)); - scratch_mem_t scratch; - if (scratch_find(target, &scratch, &program, 8) != ERROR_OK) + if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) return ERROR_FAIL; if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address) - != ERROR_OK) + != ERROR_OK) { + scratch_release(target, &scratch); return ERROR_FAIL; + } - if (scratch_write64(target, &scratch, value) != ERROR_OK) + if (scratch_write64(target, &scratch, value) != ERROR_OK) { + scratch_release(target, &scratch); return ERROR_FAIL; + } } else { if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK) return ERROR_FAIL; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (riscv_supports_extension(target, 'D')) + if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')) riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0)); else riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); @@ -1114,6 +1161,14 @@ static int register_write_direct(struct target *target, unsigned number, int exec_out = riscv_program_exec(&program, target); /* Don't message on error. Probably the register doesn't exist. */ + if (exec_out == ERROR_OK && target->reg_cache) { + struct reg *reg = &target->reg_cache->reg_list[number]; + buf_set_u64(reg->value, 0, reg->size, value); + reg->valid = true; + } + + if (use_scratch) + scratch_release(target, &scratch); /* Restore S0. */ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) @@ -1122,6 +1177,35 @@ static int register_write_direct(struct target *target, unsigned number, return exec_out; } +/** Return the cached value, or read from the target if necessary. */ +static int register_read(struct target *target, uint64_t *value, uint32_t number) +{ + if (number == GDB_REGNO_ZERO) { + *value = 0; + return ERROR_OK; + } + if (target->reg_cache && + (number <= GDB_REGNO_XPR31 || + (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31))) { + /* Only check the cache for registers that we know won't spontaneously + * change. */ + struct reg *reg = &target->reg_cache->reg_list[number]; + if (reg && reg->valid) { + *value = buf_get_u64(reg->value, 0, reg->size); + return ERROR_OK; + } + } + int result = register_read_direct(target, value, number); + if (result != ERROR_OK) + return ERROR_FAIL; + if (target->reg_cache) { + struct reg *reg = &target->reg_cache->reg_list[number]; + buf_set_u64(reg->value, 0, reg->size, *value); + reg->valid = true; + } + return ERROR_OK; +} + /** Actually read registers from the target right now. */ static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) { @@ -1131,10 +1215,9 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t int result = register_read_abstract(target, value, number, register_size(target, number)); - if (result != ERROR_OK && info->progbufsize + r->impebreak >= 2 && - riscv_is_halted(target)) { - assert(number != GDB_REGNO_S0); - + if (result != ERROR_OK && + info->progbufsize + r->impebreak >= 2 && + number > GDB_REGNO_XPR31) { struct riscv_program program; riscv_program_init(&program, target); @@ -1142,34 +1225,38 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t bool use_scratch = false; uint64_t s0; - if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; /* Write program to move data into s0. */ uint64_t mstatus; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (register_read_direct(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) + if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) return ERROR_FAIL; if ((mstatus & MSTATUS_FS) == 0) if (register_write_direct(target, GDB_REGNO_MSTATUS, set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK) return ERROR_FAIL; - if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { + if (riscv_supports_extension(target, riscv_current_hartid(target), 'D') + && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a * register, so we need to use some scratch RAM. */ riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0, 0)); - if (scratch_find(target, &scratch, &program, 8) != ERROR_OK) + if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) return ERROR_FAIL; use_scratch = true; if (register_write_direct(target, GDB_REGNO_S0, - scratch.hart_address) != ERROR_OK) + scratch.hart_address) != ERROR_OK) { + scratch_release(target, &scratch); return ERROR_FAIL; - } else if (riscv_supports_extension(target, 'D')) { + } + } else if (riscv_supports_extension(target, + riscv_current_hartid(target), 'D')) { riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)); } else { riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); @@ -1186,8 +1273,10 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t /* Don't message on error. Probably the register doesn't exist. */ if (use_scratch) { - if (scratch_read64(target, &scratch, value) != ERROR_OK) - return ERROR_FAIL; + result = scratch_read64(target, &scratch, value); + scratch_release(target, &scratch); + if (result != ERROR_OK) + return result; } else { /* Read S0 */ if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK) @@ -1242,6 +1331,7 @@ static void deinit_target(struct target *target) LOG_DEBUG("riscv_deinit_target()"); riscv_info_t *info = (riscv_info_t *) target->arch_info; free(info->version_specific); + /* TODO: free register arch_info */ info->version_specific = NULL; } @@ -1288,8 +1378,8 @@ static int examine(struct target *target) dm->was_reset = true; } - uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET; - dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE); + dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO | + DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE); uint32_t dmcontrol; if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) return ERROR_FAIL; @@ -1300,7 +1390,10 @@ static int examine(struct target *target) return ERROR_FAIL; } - uint32_t hartsel = get_field(dmcontrol, max_hartsel_mask); + uint32_t hartsel = + (get_field(dmcontrol, DMI_DMCONTROL_HARTSELHI) << + DMI_DMCONTROL_HARTSELLO_LENGTH) | + get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO); info->hartsellen = 0; while (hartsel & 1) { info->hartsellen++; @@ -1319,7 +1412,7 @@ static int examine(struct target *target) if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) { LOG_ERROR("Debugger is not authenticated to target Debug Module. " "(dmstatus=0x%x). Use `riscv authdata_read` and " - "`riscv_authdata_write` commands to authenticate.", dmstatus); + "`riscv authdata_write` commands to authenticate.", dmstatus); /* If we return ERROR_FAIL here, then in a multicore setup the next * core won't be examined, which means we won't set up the * authentication commands for them, which means the config script @@ -1358,7 +1451,8 @@ static int examine(struct target *target) continue; r->current_hartid = i; - riscv013_select_current_hart(target); + if (riscv013_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; uint32_t s; if (dmstatus_read(target, &s, true) != ERROR_OK) @@ -1367,6 +1461,10 @@ static int examine(struct target *target) break; r->hart_count = i + 1; + if (get_field(s, DMI_DMSTATUS_ANYHAVERESET)) + dmi_write(target, DMI_DMCONTROL, + set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i)); + if (!riscv_is_halted(target)) { if (riscv013_halt_current_hart(target) != ERROR_OK) { LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i); @@ -1384,7 +1482,7 @@ static int examine(struct target *target) else r->xlen[i] = 32; - if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) { + if (register_read(target, &r->misa[i], GDB_REGNO_MISA)) { LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i); return ERROR_FAIL; } @@ -1396,7 +1494,7 @@ static int examine(struct target *target) /* Display this as early as possible to help people who are using * really slow simulators. */ LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], - r->misa); + r->misa[i]); } LOG_DEBUG("Enumerated %d harts", r->hart_count); @@ -1406,9 +1504,6 @@ static int examine(struct target *target) return ERROR_FAIL; } - /* Then we check the number of triggers availiable to each hart. */ - riscv_enumerate_triggers(target); - /* Resumes all the harts, so the debugger can later pause them. */ /* TODO: Only do this if the harts were halted to start with. */ riscv_resume_all_harts(target); @@ -1426,8 +1521,8 @@ static int examine(struct target *target) riscv_count_harts(target)); for (int i = 0; i < riscv_count_harts(target); ++i) { if (riscv_hart_enabled(target, i)) { - LOG_INFO(" hart %d: XLEN=%d, %d triggers", i, r->xlen[i], - r->trigger_count[i]); + LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], + r->misa[i]); } else { LOG_INFO(" hart %d: currently disabled", i); } @@ -1538,13 +1633,13 @@ static int assert_reset(struct target *target) /* TODO: Try to use hasel in dmcontrol */ - /* Set haltreq/resumereq for each hart. */ + /* Set haltreq for each hart. */ uint32_t control = control_base; for (int i = 0; i < riscv_count_harts(target); ++i) { if (!riscv_hart_enabled(target, i)) continue; - control = set_field(control_base, hartsel_mask(target), i); + control = set_hartsel(control_base, i); control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); dmi_write(target, DMI_DMCONTROL, control); @@ -1555,24 +1650,11 @@ static int assert_reset(struct target *target) } else { /* Reset just this hart. */ - uint32_t control = set_field(control_base, hartsel_mask(target), - r->current_hartid); + uint32_t control = set_hartsel(control_base, r->current_hartid); control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - control = set_field(control, DMI_DMCONTROL_HARTRESET, 1); + control = set_field(control, DMI_DMCONTROL_NDMRESET, 1); dmi_write(target, DMI_DMCONTROL, control); - - /* Read back to check if hartreset is supported. */ - uint32_t rb; - if (dmi_read(target, &rb, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - if (!get_field(rb, DMI_DMCONTROL_HARTRESET)) { - /* Use ndmreset instead. That will reset the entire device, but - * that's probably what OpenOCD wants anyway. */ - control = set_field(control, DMI_DMCONTROL_HARTRESET, 0); - control = set_field(control, DMI_DMCONTROL_NDMRESET, 1); - dmi_write(target, DMI_DMCONTROL, control); - } } target->state = TARGET_RESET; @@ -1586,57 +1668,69 @@ static int deassert_reset(struct target *target) RISCV013_INFO(info); select_dmi(target); - LOG_DEBUG("%d", r->current_hartid); - /* Clear the reset, but make sure haltreq is still set */ uint32_t control = 0; control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - control = set_field(control, hartsel_mask(target), r->current_hartid); control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1); - dmi_write(target, DMI_DMCONTROL, control); + dmi_write(target, DMI_DMCONTROL, + set_hartsel(control, r->current_hartid)); uint32_t dmstatus; int dmi_busy_delay = info->dmi_busy_delay; time_t start = time(NULL); - if (target->reset_halt) { - LOG_DEBUG("Waiting for hart to be halted."); - do { - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) - return ERROR_FAIL; + for (int i = 0; i < riscv_count_harts(target); ++i) { + int index = i; + if (target->rtos) { + if (!riscv_hart_enabled(target, index)) + continue; + dmi_write(target, DMI_DMCONTROL, + set_hartsel(control, index)); + } else { + index = r->current_hartid; + } + + char *operation; + uint32_t expected_field; + if (target->reset_halt) { + operation = "halt"; + expected_field = DMI_DMSTATUS_ALLHALTED; + } else { + operation = "run"; + expected_field = DMI_DMSTATUS_ALLRUNNING; + } + LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation); + while (1) { + int result = dmstatus_read_timeout(target, &dmstatus, true, + riscv_reset_timeout_sec); + if (result == ERROR_TIMEOUT_REACHED) + LOG_ERROR("Hart %d didn't complete a DMI read coming out of " + "reset in %ds; Increase the timeout with riscv " + "set_reset_timeout_sec.", + index, riscv_reset_timeout_sec); + if (result != ERROR_OK) + return result; + if (get_field(dmstatus, expected_field)) + break; if (time(NULL) - start > riscv_reset_timeout_sec) { - LOG_ERROR("Hart didn't halt coming out of reset in %ds; " + LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; " "dmstatus=0x%x; " "Increase the timeout with riscv set_reset_timeout_sec.", - riscv_reset_timeout_sec, dmstatus); + index, operation, riscv_reset_timeout_sec, dmstatus); return ERROR_FAIL; } - target->state = TARGET_HALTED; - } while (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0); + } + target->state = TARGET_HALTED; - control = set_field(control, DMI_DMCONTROL_HALTREQ, 0); - dmi_write(target, DMI_DMCONTROL, control); + if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) { + /* Ack reset. */ + dmi_write(target, DMI_DMCONTROL, + set_hartsel(control, index) | + DMI_DMCONTROL_ACKHAVERESET); + } - } else { - LOG_DEBUG("Waiting for hart to be running."); - do { - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) - return ERROR_FAIL; - if (get_field(dmstatus, DMI_DMSTATUS_ANYHALTED) || - get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) { - LOG_ERROR("Unexpected hart status during reset. dmstatus=0x%x", - dmstatus); - return ERROR_FAIL; - } - if (time(NULL) - start > riscv_reset_timeout_sec) { - LOG_ERROR("Hart didn't run coming out of reset in %ds; " - "dmstatus=0x%x; " - "Increase the timeout with riscv set_reset_timeout_sec.", - riscv_reset_timeout_sec, dmstatus); - return ERROR_FAIL; - } - } while (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING) == 0); - target->state = TARGET_RUNNING; + if (!target->rtos) + break; } info->dmi_busy_delay = dmi_busy_delay; return ERROR_OK; @@ -1765,19 +1859,16 @@ static int sb_write_address(struct target *target, target_addr_t address) RISCV013_INFO(info); unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE); /* There currently is no support for >64-bit addresses in OpenOCD. */ - if (sbasize > 96) { + if (sbasize > 96) dmi_write(target, DMI_SBADDRESS3, 0); - } - if (sbasize > 64) { + if (sbasize > 64) dmi_write(target, DMI_SBADDRESS2, 0); - } - if (sbasize > 32) { + if (sbasize > 32) #if BUILD_TARGET64 dmi_write(target, DMI_SBADDRESS1, address >> 32); #else dmi_write(target, DMI_SBADDRESS1, 0); #endif - } return dmi_write(target, DMI_SBADDRESS0, address); } @@ -1793,8 +1884,8 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs) LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", riscv_command_timeout_sec, *sbcs); + return ERROR_FAIL; } - return ERROR_FAIL; } } @@ -1922,6 +2013,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR); next_address = sb_read_address(target); info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; + continue; } unsigned error = get_field(sbcs, DMI_SBCS_SBERROR); @@ -1958,9 +2050,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, * s1 holds the next data value to write */ uint64_t s0, s1; - if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; - if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK) + if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; if (execute_fence(target) != ERROR_OK) @@ -1993,9 +2085,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, result = register_write_direct(target, GDB_REGNO_S0, address); if (result != ERROR_OK) goto error; - uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target), - AC_ACCESS_REGISTER_TRANSFER | - AC_ACCESS_REGISTER_POSTEXEC); + uint32_t command = access_register_command(target, GDB_REGNO_S1, + riscv_xlen(target), + AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); result = execute_abstract_command(target, command); if (result != ERROR_OK) goto error; @@ -2360,6 +2452,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR); next_address = sb_read_address(target); info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1; + continue; } unsigned error = get_field(sbcs, DMI_SBCS_SBERROR); @@ -2391,9 +2484,9 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, int result = ERROR_OK; uint64_t s0, s1; - if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; - if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK) + if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; /* Write the program (store, increment) */ @@ -2480,7 +2573,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, /* Write and execute command that moves value into S1 and * executes program buffer. */ - uint32_t command = access_register_command(GDB_REGNO_S1, 32, + uint32_t command = access_register_command(target, + GDB_REGNO_S1, 32, AC_ACCESS_REGISTER_POSTEXEC | AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_WRITE); @@ -2617,14 +2711,14 @@ static int riscv013_get_register(struct target *target, int result = ERROR_OK; if (rid == GDB_REGNO_PC) { - result = register_read_direct(target, value, GDB_REGNO_DPC); + result = register_read(target, value, GDB_REGNO_DPC); LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, *value); } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; - result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + result = register_read(target, &dcsr, GDB_REGNO_DCSR); *value = get_field(dcsr, CSR_DCSR_PRV); } else { - result = register_read_direct(target, value, rid); + result = register_read(target, value, rid); if (result != ERROR_OK) *value = -1; } @@ -2654,7 +2748,7 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64 } } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; - register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + register_read(target, &dcsr, GDB_REGNO_DCSR); dcsr = set_field(dcsr, CSR_DCSR_PRV, value); return register_write_direct(target, GDB_REGNO_DCSR, dcsr); } else { @@ -2664,14 +2758,22 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64 return ERROR_OK; } -static void riscv013_select_current_hart(struct target *target) +static int riscv013_select_current_hart(struct target *target) { RISCV_INFO(r); + dm013_info_t *dm = get_dm(target); + if (r->current_hartid == dm->current_hartid) + return ERROR_OK; + uint32_t dmcontrol; - dmi_read(target, &dmcontrol, DMI_DMCONTROL); - dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid); - dmi_write(target, DMI_DMCONTROL, dmcontrol); + /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */ + if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) + return ERROR_FAIL; + dmcontrol = set_hartsel(dmcontrol, r->current_hartid); + int result = dmi_write(target, DMI_DMCONTROL, dmcontrol); + dm->current_hartid = r->current_hartid; + return result; } static int riscv013_halt_current_hart(struct target *target) @@ -2741,16 +2843,32 @@ static bool riscv013_is_halted(struct target *target) if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return false; if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) - LOG_ERROR("hart %d is unavailiable", riscv_current_hartid(target)); + LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target)); if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT)) - LOG_ERROR("hart %d doesn't exist", riscv_current_hartid(target)); + LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target)); + if (get_field(dmstatus, DMI_DMSTATUS_ANYHAVERESET)) { + int hartid = riscv_current_hartid(target); + LOG_INFO("Hart %d unexpectedly reset!", hartid); + /* TODO: Can we make this more obvious to eg. a gdb user? */ + uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE | + DMI_DMCONTROL_ACKHAVERESET; + dmcontrol = set_hartsel(dmcontrol, hartid); + /* If we had been halted when we reset, request another halt. If we + * ended up running out of reset, then the user will (hopefully) get a + * message that a reset happened, that the target is running, and then + * that it is halted again once the request goes through. + */ + if (target->state == TARGET_HALTED) + dmcontrol |= DMI_DMCONTROL_HALTREQ; + dmi_write(target, DMI_DMCONTROL, dmcontrol); + } return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED); } static enum riscv_halt_reason riscv013_halt_reason(struct target *target) { riscv_reg_t dcsr; - int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + int result = register_read(target, &dcsr, GDB_REGNO_DCSR); if (result != ERROR_OK) return RISCV_HALT_UNKNOWN; @@ -2758,6 +2876,10 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) case CSR_DCSR_CAUSE_SWBP: return RISCV_HALT_BREAKPOINT; case CSR_DCSR_CAUSE_TRIGGER: + /* We could get here before triggers are enumerated if a trigger was + * already set when we connected. Force enumeration now, which has the + * side effect of clearing any triggers we did not set. */ + riscv_enumerate_triggers(target); return RISCV_HALT_TRIGGER; case CSR_DCSR_CAUSE_STEP: return RISCV_HALT_SINGLESTEP; @@ -3175,7 +3297,7 @@ static int riscv013_on_step_or_resume(struct target *target, bool step) /* We want to twiddle some bits in the debug CSR so debugging works. */ riscv_reg_t dcsr; - int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + int result = register_read(target, &dcsr, GDB_REGNO_DCSR); if (result != ERROR_OK) return result; dcsr = set_field(dcsr, CSR_DCSR_STEP, step); @@ -3198,11 +3320,9 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step return ERROR_FAIL; /* Issue the resume command, and then wait for the current hart to resume. */ - uint32_t dmcontrol; - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1); - dmi_write(target, DMI_DMCONTROL, dmcontrol); + uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE; + dmcontrol = set_hartsel(dmcontrol, r->current_hartid); + dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ); uint32_t dmstatus; for (size_t i = 0; i < 256; ++i) { @@ -3214,17 +3334,16 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0) continue; - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0); dmi_write(target, DMI_DMCONTROL, dmcontrol); return ERROR_OK; } - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) - return ERROR_FAIL; + LOG_ERROR("unable to resume hart %d", r->current_hartid); if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) return ERROR_FAIL; - LOG_ERROR("unable to resume hart %d", r->current_hartid); LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + return ERROR_FAIL; LOG_ERROR(" dmstatus =0x%08x", dmstatus); if (step) { @@ -3249,7 +3368,7 @@ void riscv013_clear_abstract_error(struct target *target) LOG_ERROR("abstractcs.busy is not going low after %d seconds " "(abstractcs=0x%x). The target is either really slow or " "broken. You could increase the timeout with riscv " - "set_reset_timeout_sec.", + "set_command_timeout_sec.", riscv_command_timeout_sec, abstractcs); break; } diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 0f8443f..dbfaf59 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -185,18 +185,19 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; -bool riscv_use_scratch_ram; -uint64_t riscv_scratch_ram_address; - bool riscv_prefer_sba; +typedef struct { + uint16_t low, high; +} range_t; + /* In addition to the ones in the standard spec, we'll also expose additional * CSRs in this list. * The list is either NULL, or a series of ranges (inclusive), terminated with * 1,0. */ -struct { - uint16_t low, high; -} *expose_csr; +range_t *expose_csr; +/* Same, but for custom registers. */ +range_t *expose_custom; static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) { @@ -232,6 +233,11 @@ static struct target_type *get_target_type(struct target *target) { riscv_info_t *info = (riscv_info_t *) target->arch_info; + if (!info) { + LOG_ERROR("Target has not been initialized"); + return NULL; + } + switch (info->dtm_version) { case 0: return &riscv011_target; @@ -247,7 +253,6 @@ static int riscv_init_target(struct command_context *cmd_ctx, struct target *target) { LOG_DEBUG("riscv_init_target()"); - target->propagate_register_errors = true; target->arch_info = calloc(1, sizeof(riscv_info_t)); if (!target->arch_info) return ERROR_FAIL; @@ -259,6 +264,8 @@ static int riscv_init_target(struct command_context *cmd_ctx, select_dbus.num_bits = target->tap->ir_length; select_idcode.num_bits = target->tap->ir_length; + riscv_semihosting_init(target); + return ERROR_OK; } @@ -266,9 +273,19 @@ static void riscv_deinit_target(struct target *target) { LOG_DEBUG("riscv_deinit_target()"); struct target_type *tt = get_target_type(target); - tt->deinit_target(target); - riscv_info_t *info = (riscv_info_t *) target->arch_info; - free(info); + if (tt) { + tt->deinit_target(target); + riscv_info_t *info = (riscv_info_t *) target->arch_info; + free(info->reg_names); + free(info); + } + /* Free the shared structure use for most registers. */ + free(target->reg_cache->reg_list[0].arch_info); + /* Free the ones we allocated separately. */ + for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++) + free(target->reg_cache->reg_list[i].arch_info); + free(target->reg_cache->reg_list); + free(target->reg_cache); target->arch_info = NULL; } @@ -314,9 +331,12 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, tdata1 = set_field(tdata1, bpcontrol_r, trigger->read); tdata1 = set_field(tdata1, bpcontrol_w, trigger->write); tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute); - tdata1 = set_field(tdata1, bpcontrol_u, !!(r->misa & (1 << ('U' - 'A')))); - tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & (1 << ('S' - 'A')))); - tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & (1 << ('H' - 'A')))); + tdata1 = set_field(tdata1, bpcontrol_u, + !!(r->misa[hartid] & (1 << ('U' - 'A')))); + tdata1 = set_field(tdata1, bpcontrol_s, + !!(r->misa[hartid] & (1 << ('S' - 'A')))); + tdata1 = set_field(tdata1, bpcontrol_h, + !!(r->misa[hartid] & (1 << ('H' - 'A')))); tdata1 |= bpcontrol_m; tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */ tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */ @@ -359,11 +379,11 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, MCONTROL_ACTION_DEBUG_MODE); tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL); tdata1 |= MCONTROL_M; - if (r->misa & (1 << ('H' - 'A'))) + if (r->misa[hartid] & (1 << ('H' - 'A'))) tdata1 |= MCONTROL_H; - if (r->misa & (1 << ('S' - 'A'))) + if (r->misa[hartid] & (1 << ('S' - 'A'))) tdata1 |= MCONTROL_S; - if (r->misa & (1 << ('U' - 'A'))) + if (r->misa[hartid] & (1 << ('U' - 'A'))) tdata1 |= MCONTROL_U; if (trigger->execute) @@ -398,6 +418,9 @@ static int add_trigger(struct target *target, struct trigger *trigger) { RISCV_INFO(r); + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + /* In RTOS mode, we need to set the same trigger in the same slot on every * hart, to keep up the illusion that each hart is a thread running on the * same core. */ @@ -522,6 +545,9 @@ static int remove_trigger(struct target *target, struct trigger *trigger) { RISCV_INFO(r); + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + int first_hart = -1; for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { if (!riscv_hart_enabled(target, hartid)) @@ -629,6 +655,89 @@ int riscv_remove_watchpoint(struct target *target, return ERROR_OK; } +/* Sets *hit_watchpoint to the first watchpoint identified as causing the + * current halt. + * + * The GDB server uses this information to tell GDB what data address has + * been hit, which enables GDB to print the hit variable along with its old + * and new value. */ +int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) +{ + struct watchpoint *wp = target->watchpoints; + + LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); + + /*TODO instead of disassembling the instruction that we think caused the + * trigger, check the hit bit of each watchpoint first. The hit bit is + * simpler and more reliable to check but as it is optional and relatively + * new, not all hardware will implement it */ + riscv_reg_t dpc; + riscv_get_register(target, &dpc, GDB_REGNO_DPC); + const uint8_t length = 4; + LOG_DEBUG("dpc is 0x%" PRIx64, dpc); + + /* fetch the instruction at dpc */ + uint8_t buffer[length]; + if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) { + LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc); + return ERROR_FAIL; + } + + uint32_t instruction = 0; + + for (int i = 0; i < length; i++) { + LOG_DEBUG("Next byte is %x", buffer[i]); + instruction += (buffer[i] << 8 * i); + } + LOG_DEBUG("Full instruction is %x", instruction); + + /* find out which memory address is accessed by the instruction at dpc */ + /* opcode is first 7 bits of the instruction */ + uint8_t opcode = instruction & 0x7F; + uint32_t rs1; + int16_t imm; + riscv_reg_t mem_addr; + + if (opcode == MATCH_LB || opcode == MATCH_SB) { + rs1 = (instruction & 0xf8000) >> 15; + riscv_get_register(target, &mem_addr, rs1); + + if (opcode == MATCH_SB) { + LOG_DEBUG("%x is store instruction", instruction); + imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20); + } else { + LOG_DEBUG("%x is load instruction", instruction); + imm = (instruction & 0xfff00000) >> 20; + } + /* sign extend 12-bit imm to 16-bits */ + if (imm & (1 << 11)) + imm |= 0xf000; + mem_addr += imm; + LOG_DEBUG("memory address=0x%" PRIx64, mem_addr); + } else { + LOG_DEBUG("%x is not a RV32I load or store", instruction); + return ERROR_FAIL; + } + + while (wp) { + /*TODO support length/mask */ + if (wp->address == mem_addr) { + *hit_watchpoint = wp; + LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address); + return ERROR_OK; + } + wp = wp->next; + } + + /* No match found - either we hit a watchpoint caused by an instruction that + * this function does not yet disassemble, or we hit a breakpoint. + * + * OpenOCD will behave as if this function had never been implemented i.e. + * report the halt to GDB with no address information. */ + return ERROR_FAIL; +} + + static int oldriscv_step(struct target *target, int current, uint32_t address, int handle_breakpoints) { @@ -739,19 +848,20 @@ static int old_or_new_riscv_resume( return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution); } -static void riscv_select_current_hart(struct target *target) +static int riscv_select_current_hart(struct target *target) { RISCV_INFO(r); if (r->rtos_hartid != -1 && riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, r->rtos_hartid); + return riscv_set_current_hartid(target, r->rtos_hartid); else - riscv_set_current_hartid(target, target->coreid); + return riscv_set_current_hartid(target, target->coreid); } static int riscv_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { - riscv_select_current_hart(target); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; struct target_type *tt = get_target_type(target); return tt->read_memory(target, address, size, count, buffer); } @@ -759,7 +869,8 @@ static int riscv_read_memory(struct target *target, target_addr_t address, static int riscv_write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { - riscv_select_current_hart(target); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; struct target_type *tt = get_target_type(target); return tt->write_memory(target, address, size, count, buffer); } @@ -777,14 +888,15 @@ static int riscv_get_gdb_reg_list(struct target *target, return ERROR_FAIL; } - riscv_select_current_hart(target); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; switch (reg_class) { case REG_CLASS_GENERAL: *reg_list_size = 32; break; case REG_CLASS_ALL: - *reg_list_size = GDB_REGNO_COUNT; + *reg_list_size = target->reg_cache->num_regs; break; default: LOG_ERROR("Unsupported reg_class: %d", reg_class); @@ -948,68 +1060,63 @@ static int riscv_checksum_memory(struct target *target, return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } -/* Should run code on the target to check whether a memory -block holds all-ones (because this is generally called on -NOR flash which is 1 when "blank") -Not yet implemented. -*/ -int riscv_blank_check_memory(struct target *target, - target_addr_t address, - uint32_t count, - uint32_t *blank, - uint8_t erased_value) -{ - *blank = 0; - - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; -} - /*** OpenOCD Helper Functions ***/ -/* 0 means nothing happened, 1 means the hart's state changed (and thus the - * poll should terminate), and -1 means there was an error. */ -static int riscv_poll_hart(struct target *target, int hartid) +enum riscv_poll_hart { + RPH_NO_CHANGE, + RPH_DISCOVERED_HALTED, + RPH_DISCOVERED_RUNNING, + RPH_ERROR +}; +static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) { RISCV_INFO(r); - riscv_set_current_hartid(target, hartid); + if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + return RPH_ERROR; - LOG_DEBUG("polling hart %d, target->state=%d (TARGET_HALTED=%d)", hartid, target->state, TARGET_HALTED); + LOG_DEBUG("polling hart %d, target->state=%d", hartid, target->state); - /* If OpenOCD this we're running but this hart is halted then it's time + /* If OpenOCD thinks we're running but this hart is halted then it's time * to raise an event. */ - if (target->state != TARGET_HALTED && riscv_is_halted(target)) { + bool halted = riscv_is_halted(target); + if (target->state != TARGET_HALTED && halted) { LOG_DEBUG(" triggered a halt"); r->on_halt(target); - return 1; + return RPH_DISCOVERED_HALTED; + } else if (target->state != TARGET_RUNNING && !halted) { + LOG_DEBUG(" triggered running"); + target->state = TARGET_RUNNING; + return RPH_DISCOVERED_RUNNING; } - return 0; + return RPH_NO_CHANGE; } /*** OpenOCD Interface ***/ int riscv_openocd_poll(struct target *target) { LOG_DEBUG("polling all harts"); - int triggered_hart = -1; + int halted_hart = -1; if (riscv_rtos_enabled(target)) { /* Check every hart for an event. */ for (int i = 0; i < riscv_count_harts(target); ++i) { - int out = riscv_poll_hart(target, i); + enum riscv_poll_hart out = riscv_poll_hart(target, i); switch (out) { - case 0: + case RPH_NO_CHANGE: + case RPH_DISCOVERED_RUNNING: continue; - case 1: - triggered_hart = i; + case RPH_DISCOVERED_HALTED: + halted_hart = i; break; - case -1: + case RPH_ERROR: return ERROR_FAIL; } } - if (triggered_hart == -1) { + if (halted_hart == -1) { LOG_DEBUG(" no harts just halted, target->state=%d", target->state); return ERROR_OK; } - LOG_DEBUG(" hart %d halted", triggered_hart); + LOG_DEBUG(" hart %d halted", halted_hart); /* If we're here then at least one hart triggered. That means * we want to go and halt _every_ hart in the system, as that's @@ -1020,15 +1127,19 @@ int riscv_openocd_poll(struct target *target) for (int i = 0; i < riscv_count_harts(target); ++i) riscv_halt_one_hart(target, i); } else { - if (riscv_poll_hart(target, riscv_current_hartid(target)) == 0) + enum riscv_poll_hart out = riscv_poll_hart(target, + riscv_current_hartid(target)); + if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) return ERROR_OK; + else if (out == RPH_ERROR) + return ERROR_FAIL; - triggered_hart = riscv_current_hartid(target); - LOG_DEBUG(" hart %d halted", triggered_hart); + halted_hart = riscv_current_hartid(target); + LOG_DEBUG(" hart %d halted", halted_hart); } target->state = TARGET_HALTED; - switch (riscv_halt_reason(target, triggered_hart)) { + switch (riscv_halt_reason(target, halted_hart)) { case RISCV_HALT_BREAKPOINT: target->debug_reason = DBG_REASON_BREAKPOINT; break; @@ -1044,14 +1155,24 @@ int riscv_openocd_poll(struct target *target) case RISCV_HALT_UNKNOWN: target->debug_reason = DBG_REASON_UNDEFINED; break; + case RISCV_HALT_ERROR: + return ERROR_FAIL; } if (riscv_rtos_enabled(target)) { - target->rtos->current_threadid = triggered_hart + 1; - target->rtos->current_thread = triggered_hart + 1; + target->rtos->current_threadid = halted_hart + 1; + target->rtos->current_thread = halted_hart + 1; + riscv_set_rtos_hartid(target, halted_hart); } target->state = TARGET_HALTED; + + if (target->debug_reason == DBG_REASON_BREAKPOINT) { + int retval; + if (riscv_semihosting(target, &retval) != 0) + return retval; + } + target_call_event_callbacks(target, TARGET_EVENT_HALTED); return ERROR_OK; } @@ -1103,16 +1224,14 @@ int riscv_openocd_resume( while (watchpoint && result == ERROR_OK) { LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set); trigger_temporarily_cleared[i] = watchpoint->set; - if (watchpoint->set) { + if (watchpoint->set) result = riscv_remove_watchpoint(target, watchpoint); - } watchpoint = watchpoint->next; i++; } - if (result == ERROR_OK) { + if (result == ERROR_OK) result = riscv_step_rtos_hart(target); - } watchpoint = target->watchpoints; i = 0; @@ -1204,31 +1323,6 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec) return ERROR_OK; } -COMMAND_HANDLER(riscv_set_scratch_ram) -{ - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; - } - if (!strcmp(CMD_ARGV[0], "none")) { - riscv_use_scratch_ram = false; - return ERROR_OK; - } - - // TODO: use COMMAND_PARSE_NUMBER - long long unsigned int address; - int result = sscanf(CMD_ARGV[0], "%llx", &address); - if (result != (int) strlen(CMD_ARGV[0])) { - LOG_ERROR("%s is not a valid address for command.", CMD_ARGV[0]); - riscv_use_scratch_ram = false; - return ERROR_FAIL; - } - - riscv_scratch_ram_address = address; - riscv_use_scratch_ram = true; - return ERROR_OK; -} - COMMAND_HANDLER(riscv_set_prefer_sba) { if (CMD_ARGC != 1) { @@ -1252,20 +1346,15 @@ void parse_error(const char *string, char c, unsigned position) LOG_ERROR("%s", buf); } -COMMAND_HANDLER(riscv_set_expose_csrs) +int parse_ranges(range_t **ranges, const char **argv) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; - } - for (unsigned pass = 0; pass < 2; pass++) { unsigned range = 0; unsigned low = 0; bool parse_low = true; unsigned high = 0; - for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) { - char c = CMD_ARGV[0][i]; + for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) { + char c = argv[0][i]; if (isspace(c)) { /* Ignore whitespace. */ continue; @@ -1279,13 +1368,13 @@ COMMAND_HANDLER(riscv_set_expose_csrs) parse_low = false; } else if (c == ',' || c == 0) { if (pass == 1) { - expose_csr[range].low = low; - expose_csr[range].high = low; + (*ranges)[range].low = low; + (*ranges)[range].high = low; } low = 0; range++; } else { - parse_error(CMD_ARGV[0], c, i); + parse_error(argv[0], c, i); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -1296,31 +1385,52 @@ COMMAND_HANDLER(riscv_set_expose_csrs) } else if (c == ',' || c == 0) { parse_low = true; if (pass == 1) { - expose_csr[range].low = low; - expose_csr[range].high = high; + (*ranges)[range].low = low; + (*ranges)[range].high = high; } low = 0; high = 0; range++; } else { - parse_error(CMD_ARGV[0], c, i); + parse_error(argv[0], c, i); return ERROR_COMMAND_SYNTAX_ERROR; } } } if (pass == 0) { - if (expose_csr) - free(expose_csr); - expose_csr = calloc(range + 2, sizeof(*expose_csr)); + if (*ranges) + free(*ranges); + *ranges = calloc(range + 2, sizeof(range_t)); } else { - expose_csr[range].low = 1; - expose_csr[range].high = 0; + (*ranges)[range].low = 1; + (*ranges)[range].high = 0; } } + return ERROR_OK; } +COMMAND_HANDLER(riscv_set_expose_csrs) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return parse_ranges(&expose_csr, CMD_ARGV); +} + +COMMAND_HANDLER(riscv_set_expose_custom) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return parse_ranges(&expose_custom, CMD_ARGV); +} + COMMAND_HANDLER(riscv_authdata_read) { if (CMD_ARGC != 0) { @@ -1473,13 +1583,6 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "Set the wall-clock timeout (in seconds) after reset is deasserted" }, { - .name = "set_scratch_ram", - .handler = riscv_set_scratch_ram, - .mode = COMMAND_ANY, - .usage = "riscv set_scratch_ram none|[address]", - .help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'." - }, - { .name = "set_prefer_sba", .handler = riscv_set_prefer_sba, .mode = COMMAND_ANY, @@ -1497,6 +1600,15 @@ static const struct command_registration riscv_exec_command_handlers[] = { "`init`." }, { + .name = "expose_custom", + .handler = riscv_set_expose_custom, + .mode = COMMAND_ANY, + .usage = "riscv expose_custom n0[-m0][,n1[-m1]]...", + .help = "Configure a list of inclusive ranges for custom registers to " + "expose. custom0 is accessed as abstract register number 0xc000, " + "etc. This must be executed before `init`." + }, + { .name = "authdata_read", .handler = riscv_authdata_read, .mode = COMMAND_ANY, @@ -1540,6 +1652,56 @@ static const struct command_registration riscv_exec_command_handlers[] = { COMMAND_REGISTRATION_DONE }; +extern __COMMAND_HANDLER(handle_common_semihosting_command); +extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command); +extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command); +extern __COMMAND_HANDLER(handle_common_semihosting_cmdline); + +/* + * To be noted that RISC-V targets use the same semihosting commands as + * ARM targets. + * + * The main reason is compatibility with existing tools. For example the + * Eclipse OpenOCD/SEGGER J-Link/QEMU plug-ins have several widgets to + * configure semihosting, which generate commands like `arm semihosting + * enable`. + * A secondary reason is the fact that the protocol used is exactly the + * one specified by ARM. If RISC-V will ever define its own semihosting + * protocol, then a command like `riscv semihosting enable` will make + * sense, but for now all semihosting commands are prefixed with `arm`. + */ +static const struct command_registration arm_exec_command_handlers[] = { + { + "semihosting", + .handler = handle_common_semihosting_command, + .mode = COMMAND_EXEC, + .usage = "['enable'|'disable']", + .help = "activate support for semihosting operations", + }, + { + "semihosting_cmdline", + .handler = handle_common_semihosting_cmdline, + .mode = COMMAND_EXEC, + .usage = "arguments", + .help = "command line arguments to be passed to program", + }, + { + "semihosting_fileio", + .handler = handle_common_semihosting_fileio_command, + .mode = COMMAND_EXEC, + .usage = "['enable'|'disable']", + .help = "activate support for semihosting fileio operations", + }, + { + "semihosting_resexit", + .handler = handle_common_semihosting_resumable_exit_command, + .mode = COMMAND_EXEC, + .usage = "['enable'|'disable']", + .help = "activate support for semihosting resumable exit", + }, + COMMAND_REGISTRATION_DONE +}; + const struct command_registration riscv_command_handlers[] = { { .name = "riscv", @@ -1548,6 +1710,13 @@ const struct command_registration riscv_command_handlers[] = { .usage = "", .chain = riscv_exec_command_handlers }, + { + .name = "arm", + .mode = COMMAND_ANY, + .help = "ARM Command Group", + .usage = "", + .chain = arm_exec_command_handlers + }, COMMAND_REGISTRATION_DONE }; @@ -1571,7 +1740,6 @@ struct target_type riscv_target = { .read_memory = riscv_read_memory, .write_memory = riscv_write_memory, - .blank_check_memory = riscv_blank_check_memory, .checksum_memory = riscv_checksum_memory, .get_gdb_reg_list = riscv_get_gdb_reg_list, @@ -1581,6 +1749,7 @@ struct target_type riscv_target = { .add_watchpoint = riscv_add_watchpoint, .remove_watchpoint = riscv_remove_watchpoint, + .hit_watchpoint = riscv_hit_watchpoint, .arch_state = riscv_arch_state, @@ -1624,7 +1793,8 @@ int riscv_halt_one_hart(struct target *target, int hartid) { RISCV_INFO(r); LOG_DEBUG("halting hart %d", hartid); - riscv_set_current_hartid(target, hartid); + if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + return ERROR_FAIL; if (riscv_is_halted(target)) { LOG_DEBUG(" hart %d requested halt, but was already halted", hartid); return ERROR_OK; @@ -1650,7 +1820,8 @@ int riscv_resume_one_hart(struct target *target, int hartid) { RISCV_INFO(r); LOG_DEBUG("resuming hart %d", hartid); - riscv_set_current_hartid(target, hartid); + if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + return ERROR_FAIL; if (!riscv_is_halted(target)) { LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid); return ERROR_OK; @@ -1671,7 +1842,8 @@ int riscv_step_rtos_hart(struct target *target) hartid = 0; } } - riscv_set_current_hartid(target, hartid); + if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + return ERROR_FAIL; LOG_DEBUG("stepping hart %d", hartid); if (!riscv_is_halted(target)) { @@ -1691,7 +1863,7 @@ int riscv_step_rtos_hart(struct target *target) return ERROR_OK; } -bool riscv_supports_extension(struct target *target, char letter) +bool riscv_supports_extension(struct target *target, int hartid, char letter) { RISCV_INFO(r); unsigned num; @@ -1701,7 +1873,7 @@ bool riscv_supports_extension(struct target *target, char letter) num = letter - 'A'; else return false; - return r->misa & (1 << num); + return r->misa[hartid] & (1 << num); } int riscv_xlen(const struct target *target) @@ -1721,33 +1893,35 @@ bool riscv_rtos_enabled(const struct target *target) return target->rtos != NULL; } -void riscv_set_current_hartid(struct target *target, int hartid) +int riscv_set_current_hartid(struct target *target, int hartid) { RISCV_INFO(r); if (!r->select_current_hart) - return; + return ERROR_OK; int previous_hartid = riscv_current_hartid(target); r->current_hartid = hartid; assert(riscv_hart_enabled(target, hartid)); LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid); - r->select_current_hart(target); + if (r->select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; /* This might get called during init, in which case we shouldn't be * setting up the register cache. */ if (!target_was_examined(target)) - return; + return ERROR_OK; /* Avoid invalidating the register cache all the time. */ if (r->registers_initialized && (!riscv_rtos_enabled(target) || (previous_hartid == hartid)) && target->reg_cache->reg_list[GDB_REGNO_ZERO].size == (unsigned)riscv_xlen(target) && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) { - return; + return ERROR_OK; } else LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target)); riscv_invalidate_register_cache(target); + return ERROR_OK; } void riscv_invalidate_register_cache(struct target *target) @@ -1755,7 +1929,7 @@ void riscv_invalidate_register_cache(struct target *target) RISCV_INFO(r); register_cache_invalidate(target->reg_cache); - for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) { + for (size_t i = 0; i < target->reg_cache->num_regs; ++i) { struct reg *reg = &target->reg_cache->reg_list[i]; reg->valid = false; } @@ -1797,6 +1971,10 @@ bool riscv_has_register(struct target *target, int hartid, int regid) return 1; } +/** + * This function is called when the debug user wants to change the value of a + * register. The new value may be cached, and may not be written until the hart + * is resumed. */ int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) { return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v); @@ -1837,7 +2015,8 @@ bool riscv_is_halted(struct target *target) enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) { RISCV_INFO(r); - riscv_set_current_hartid(target, hartid); + if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + return RISCV_HALT_ERROR; if (!riscv_is_halted(target)) { LOG_ERROR("Hart is not halted!"); return RISCV_HALT_UNKNOWN; @@ -1914,6 +2093,11 @@ int riscv_enumerate_triggers(struct target *target) { RISCV_INFO(r); + if (r->triggers_enumerated) + return ERROR_OK; + + r->triggers_enumerated = true; /* At the very least we tried. */ + for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { if (!riscv_hart_enabled(target, hartid)) continue; @@ -2018,7 +2202,8 @@ const char *gdb_regno_name(enum gdb_regno regno) static int register_get(struct reg *reg) { - struct target *target = (struct target *) reg->arch_info; + riscv_reg_info_t *reg_info = reg->arch_info; + struct target *target = reg_info->target; uint64_t value; int result = riscv_get_register(target, &value, reg->number); if (result != ERROR_OK) @@ -2029,7 +2214,8 @@ static int register_get(struct reg *reg) static int register_set(struct reg *reg, uint8_t *buf) { - struct target *target = (struct target *) reg->arch_info; + riscv_reg_info_t *reg_info = reg->arch_info; + struct target *target = reg_info->target; uint64_t value = buf_get_u64(buf, 0, reg->size); @@ -2071,12 +2257,26 @@ int riscv_init_registers(struct target *target) target->reg_cache->name = "RISC-V Registers"; target->reg_cache->num_regs = GDB_REGNO_COUNT; - target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); + if (expose_custom) { + for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) { + for (unsigned number = expose_custom[i].low; + number <= expose_custom[i].high; + number++) + target->reg_cache->num_regs++; + } + } + + LOG_DEBUG("create register cache for %d registers", + target->reg_cache->num_regs); + + target->reg_cache->reg_list = + calloc(target->reg_cache->num_regs, sizeof(struct reg)); const unsigned int max_reg_name_len = 12; if (info->reg_names) free(info->reg_names); - info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); + info->reg_names = + calloc(target->reg_cache->num_regs, max_reg_name_len); char *reg_name = info->reg_names; static struct reg_feature feature_cpu = { @@ -2091,6 +2291,9 @@ int riscv_init_registers(struct target *target) static struct reg_feature feature_virtual = { .name = "org.gnu.gdb.riscv.virtual" }; + static struct reg_feature feature_custom = { + .name = "org.gnu.gdb.riscv.custom" + }; static struct reg_data_type type_ieee_single = { .type = REG_TYPE_IEEE_SINGLE, @@ -2109,18 +2312,24 @@ int riscv_init_registers(struct target *target) qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info); unsigned csr_info_index = 0; - /* When gdb request register N, gdb_get_register_packet() assumes that this + unsigned custom_range_index = 0; + int custom_within_range = 0; + + riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t)); + shared_reg_info->target = target; + + /* When gdb requests register N, gdb_get_register_packet() assumes that this * is register at index N in reg_list. So if there are certain registers * that don't exist, we need to leave holes in the list (or renumber, but * it would be nice not to have yet another set of numbers to translate * between). */ - for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) { + for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) { struct reg *r = &target->reg_cache->reg_list[number]; r->dirty = false; r->valid = false; r->exist = true; r->type = &riscv_reg_arch_type; - r->arch_info = target; + r->arch_info = shared_reg_info; r->number = number; r->size = riscv_xlen(target); /* r->size is set in riscv_invalidate_register_cache, maybe because the @@ -2235,10 +2444,12 @@ int riscv_init_registers(struct target *target) r->feature = &feature_cpu; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { r->caller_save = true; - if (riscv_supports_extension(target, 'D')) { + if (riscv_supports_extension(target, riscv_current_hartid(target), + 'D')) { r->reg_data_type = &type_ieee_double; r->size = 64; - } else if (riscv_supports_extension(target, 'F')) { + } else if (riscv_supports_extension(target, + riscv_current_hartid(target), 'F')) { r->reg_data_type = &type_ieee_single; r->size = 32; } else { @@ -2369,7 +2580,8 @@ int riscv_init_registers(struct target *target) case CSR_FFLAGS: case CSR_FRM: case CSR_FCSR: - r->exist = riscv_supports_extension(target, 'F'); + r->exist = riscv_supports_extension(target, + riscv_current_hartid(target), 'F'); r->group = "float"; r->feature = &feature_fpu; break; @@ -2383,7 +2595,82 @@ int riscv_init_registers(struct target *target) case CSR_SCAUSE: case CSR_STVAL: case CSR_SATP: - r->exist = riscv_supports_extension(target, 'S'); + r->exist = riscv_supports_extension(target, + riscv_current_hartid(target), 'S'); + break; + case CSR_MEDELEG: + case CSR_MIDELEG: + /* "In systems with only M-mode, or with both M-mode and + * U-mode but without U-mode trap support, the medeleg and + * mideleg registers should not exist." */ + r->exist = riscv_supports_extension(target, riscv_current_hartid(target), 'S') || + riscv_supports_extension(target, riscv_current_hartid(target), 'N'); + break; + + case CSR_CYCLEH: + case CSR_TIMEH: + case CSR_INSTRETH: + case CSR_HPMCOUNTER3H: + case CSR_HPMCOUNTER4H: + case CSR_HPMCOUNTER5H: + case CSR_HPMCOUNTER6H: + case CSR_HPMCOUNTER7H: + case CSR_HPMCOUNTER8H: + case CSR_HPMCOUNTER9H: + case CSR_HPMCOUNTER10H: + case CSR_HPMCOUNTER11H: + case CSR_HPMCOUNTER12H: + case CSR_HPMCOUNTER13H: + case CSR_HPMCOUNTER14H: + case CSR_HPMCOUNTER15H: + case CSR_HPMCOUNTER16H: + case CSR_HPMCOUNTER17H: + case CSR_HPMCOUNTER18H: + case CSR_HPMCOUNTER19H: + case CSR_HPMCOUNTER20H: + case CSR_HPMCOUNTER21H: + case CSR_HPMCOUNTER22H: + case CSR_HPMCOUNTER23H: + case CSR_HPMCOUNTER24H: + case CSR_HPMCOUNTER25H: + case CSR_HPMCOUNTER26H: + case CSR_HPMCOUNTER27H: + case CSR_HPMCOUNTER28H: + case CSR_HPMCOUNTER29H: + case CSR_HPMCOUNTER30H: + case CSR_HPMCOUNTER31H: + case CSR_MCYCLEH: + case CSR_MINSTRETH: + case CSR_MHPMCOUNTER3H: + case CSR_MHPMCOUNTER4H: + case CSR_MHPMCOUNTER5H: + case CSR_MHPMCOUNTER6H: + case CSR_MHPMCOUNTER7H: + case CSR_MHPMCOUNTER8H: + case CSR_MHPMCOUNTER9H: + case CSR_MHPMCOUNTER10H: + case CSR_MHPMCOUNTER11H: + case CSR_MHPMCOUNTER12H: + case CSR_MHPMCOUNTER13H: + case CSR_MHPMCOUNTER14H: + case CSR_MHPMCOUNTER15H: + case CSR_MHPMCOUNTER16H: + case CSR_MHPMCOUNTER17H: + case CSR_MHPMCOUNTER18H: + case CSR_MHPMCOUNTER19H: + case CSR_MHPMCOUNTER20H: + case CSR_MHPMCOUNTER21H: + case CSR_MHPMCOUNTER22H: + case CSR_MHPMCOUNTER23H: + case CSR_MHPMCOUNTER24H: + case CSR_MHPMCOUNTER25H: + case CSR_MHPMCOUNTER26H: + case CSR_MHPMCOUNTER27H: + case CSR_MHPMCOUNTER28H: + case CSR_MHPMCOUNTER29H: + case CSR_MHPMCOUNTER30H: + case CSR_MHPMCOUNTER31H: + r->exist = riscv_xlen(target) == 32; break; } @@ -2402,11 +2689,35 @@ int riscv_init_registers(struct target *target) r->group = "general"; r->feature = &feature_virtual; r->size = 8; + + } else { + /* Custom registers. */ + assert(expose_custom); + + range_t *range = &expose_custom[custom_range_index]; + assert(range->low <= range->high); + unsigned custom_number = range->low + custom_within_range; + + r->group = "custom"; + r->feature = &feature_custom; + r->arch_info = calloc(1, sizeof(riscv_reg_info_t)); + assert(r->arch_info); + ((riscv_reg_info_t *) r->arch_info)->target = target; + ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; + sprintf(reg_name, "custom%d", custom_number); + + custom_within_range++; + if (custom_within_range > range->high - range->low) { + custom_within_range = 0; + custom_range_index++; + } } + if (reg_name[0]) r->name = reg_name; reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); + assert(reg_name < info->reg_names + target->reg_cache->num_regs * + max_reg_name_len); r->value = &info->reg_cache_values[number]; } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index d724538..3123db2 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -7,7 +7,7 @@ struct riscv_program; #include "opcodes.h" #include "gdb_regs.h" -/* The register cache is staticly allocated. */ +/* The register cache is statically allocated. */ #define RISCV_MAX_HARTS 32 #define RISCV_MAX_REGISTERS 5000 #define RISCV_MAX_TRIGGERS 32 @@ -31,13 +31,17 @@ enum riscv_halt_reason { RISCV_HALT_BREAKPOINT, RISCV_HALT_SINGLESTEP, RISCV_HALT_TRIGGER, - RISCV_HALT_UNKNOWN + RISCV_HALT_UNKNOWN, + RISCV_HALT_ERROR }; typedef struct { - unsigned dtm_version; + struct target *target; + unsigned custom_number; +} riscv_reg_info_t; - riscv_reg_t misa; +typedef struct { + unsigned dtm_version; struct command_context *cmd_ctx; void *version_specific; @@ -59,7 +63,9 @@ typedef struct { uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS]; bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS]; - /* The register cache points into here. */ + /* OpenOCD's register cache points into here. This is not per-hart because + * we just invalidate the entire cache when we change which hart is + * selected. */ uint64_t reg_cache_values[RISCV_MAX_REGISTERS]; /* Single buffer that contains all register names, instead of calling @@ -68,6 +74,7 @@ typedef struct { /* It's possible that each core has a different supported ISA set. */ int xlen[RISCV_MAX_HARTS]; + riscv_reg_t misa[RISCV_MAX_HARTS]; /* The number of triggers per hart. */ unsigned trigger_count[RISCV_MAX_HARTS]; @@ -87,13 +94,15 @@ typedef struct { /* This hart contains an implicit ebreak at the end of the program buffer. */ bool impebreak; + bool triggers_enumerated; + /* Helper functions that target the various RISC-V debug spec * implementations. */ int (*get_register)(struct target *target, riscv_reg_t *value, int hid, int rid); int (*set_register)(struct target *, int hartid, int regid, uint64_t value); - void (*select_current_hart)(struct target *); + int (*select_current_hart)(struct target *); bool (*is_halted)(struct target *target); int (*halt_current_hart)(struct target *); int (*resume_current_hart)(struct target *target); @@ -128,9 +137,6 @@ extern int riscv_command_timeout_sec; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ extern int riscv_reset_timeout_sec; -extern bool riscv_use_scratch_ram; -extern uint64_t riscv_scratch_ram_address; - extern bool riscv_prefer_sba; /* Everything needs the RISC-V specific info structure, so here's a nice macro @@ -187,7 +193,7 @@ int riscv_resume_one_hart(struct target *target, int hartid); * then the only hart. */ int riscv_step_rtos_hart(struct target *target); -bool riscv_supports_extension(struct target *target, char letter); +bool riscv_supports_extension(struct target *target, int hartid, char letter); /* Returns XLEN for the given (or current) hart. */ int riscv_xlen(const struct target *target); @@ -197,7 +203,7 @@ bool riscv_rtos_enabled(const struct target *target); /* Sets the current hart, which is the hart that will actually be used when * issuing debug commands. */ -void riscv_set_current_hartid(struct target *target, int hartid); +int riscv_set_current_hartid(struct target *target, int hartid); int riscv_current_hartid(const struct target *target); /*** Support functions for the RISC-V 'RTOS', which provides multihart support @@ -256,7 +262,11 @@ int riscv_remove_breakpoint(struct target *target, int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); +int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address); int riscv_init_registers(struct target *target); +void riscv_semihosting_init(struct target *target); +int riscv_semihosting(struct target *target, int *retval); + #endif diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c new file mode 100644 index 0000000..c4b6653 --- /dev/null +++ b/src/target/riscv/riscv_semihosting.c @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * ilg@livius.net * + * * + * Copyright (C) 2009 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre <nico@marvell.com> * + * * + * Copyright (C) 2010 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2016 by Square, Inc. * + * Steven Stallion <stallion@squareup.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +/** + * @file + * Hold RISC-V semihosting support. + * + * The RISC-V code is inspired from ARM semihosting. + * + * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf + * from ARM Ltd. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "log.h" + +#include "target/target.h" +#include "target/semihosting_common.h" +#include "riscv.h" + +static int riscv_semihosting_setup(struct target *target, int enable); +static int riscv_semihosting_post_result(struct target *target); + +/** + * Initialize RISC-V semihosting. Use common ARM code. + */ +void riscv_semihosting_init(struct target *target) +{ + semihosting_common_init(target, riscv_semihosting_setup, + riscv_semihosting_post_result); +} + +/** + * Check for and process a semihosting request using the ARM protocol). This + * is meant to be called when the target is stopped due to a debug mode entry. + * If the value 0 is returned then there was nothing to process. A non-zero + * return value signifies that a request was processed and the target resumed, + * or an error was encountered, in which case the caller must return + * immediately. + * + * @param target Pointer to the target to process. + * @param retval Pointer to a location where the return code will be stored + * @return non-zero value if a request was processed or an error encountered + */ +int riscv_semihosting(struct target *target, int *retval) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return 0; + + if (!semihosting->is_active) + return 0; + + riscv_reg_t dpc; + int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC); + if (result != ERROR_OK) + return 0; + + uint8_t tmp[12]; + + /* Read the current instruction, including the bracketing */ + *retval = target_read_memory(target, dpc - 4, 2, 6, tmp); + if (*retval != ERROR_OK) + return 0; + + /* + * The instructions that trigger a semihosting call, + * always uncompressed, should look like: + * + * 01f01013 slli zero,zero,0x1f + * 00100073 ebreak + * 40705013 srai zero,zero,0x7 + */ + uint32_t pre = target_buffer_get_u32(target, tmp); + uint32_t ebreak = target_buffer_get_u32(target, tmp + 4); + uint32_t post = target_buffer_get_u32(target, tmp + 8); + LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc); + + if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) { + + /* Not the magic sequence defining semihosting. */ + return 0; + } + + /* + * Perform semihosting call if we are not waiting on a fileio + * operation to complete. + */ + if (!semihosting->hit_fileio) { + + /* RISC-V uses A0 and A1 to pass function arguments */ + riscv_reg_t r0; + riscv_reg_t r1; + + result = riscv_get_register(target, &r0, GDB_REGNO_A0); + if (result != ERROR_OK) + return 0; + + result = riscv_get_register(target, &r1, GDB_REGNO_A1); + if (result != ERROR_OK) + return 0; + + semihosting->op = r0; + semihosting->param = r1; + semihosting->word_size_bytes = riscv_xlen(target) / 8; + + /* Check for ARM operation numbers. */ + if (0 <= semihosting->op && semihosting->op <= 0x31) { + *retval = semihosting_common(target); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed semihosting operation"); + return 0; + } + } else { + /* Unknown operation number, not a semihosting call. */ + return 0; + } + } + + /* + * Resume target if we are not waiting on a fileio + * operation to complete. + */ + if (semihosting->is_resumable && !semihosting->hit_fileio) { + /* Resume right after the EBREAK 4 bytes instruction. */ + *retval = target_resume(target, 0, dpc+4, 0, 0); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed to resume target"); + return 0; + } + + return 1; + } + + return 0; +} + +/* ------------------------------------------------------------------------- + * Local functions. */ + +/** + * Called via semihosting->setup() later, after the target is known, + * usually on the first semihosting command. + */ +static int riscv_semihosting_setup(struct target *target, int enable) +{ + LOG_DEBUG("enable=%d", enable); + + struct semihosting *semihosting = target->semihosting; + if (semihosting) + semihosting->setup_time = clock(); + + return ERROR_OK; +} + +static int riscv_semihosting_post_result(struct target *target) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + /* If not enabled, silently ignored. */ + return 0; + } + + LOG_DEBUG("0x%" PRIx64, semihosting->result); + riscv_set_register(target, GDB_REGNO_A0, semihosting->result); + return 0; +} diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c new file mode 100644 index 0000000..5920789 --- /dev/null +++ b/src/target/semihosting_common.c @@ -0,0 +1,1595 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * <ilg@livius.net> * + * * + * Copyright (C) 2018 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre <nico@marvell.com> * + * * + * Copyright (C) 2010 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2016 by Square, Inc. * + * Steven Stallion <stallion@squareup.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +/** + * @file + * Common ARM semihosting support. + * + * Semihosting enables code running on a target to use some of the I/O + * facilities on the host computer. The target application must be linked + * against a library that forwards operation requests by using an + * instruction trapped by the debugger. + * + * Details can be found in + * "Semihosting for AArch32 and AArch64, Release 2.0" + * https://static.docs.arm.com/100863/0200/semihosting.pdf + * from ARM Ltd. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "target.h" +#include "target_type.h" +#include "semihosting_common.h" + +#include <helper/binarybuffer.h> +#include <helper/log.h> +#include <sys/stat.h> + +static const int open_modeflags[12] = { + O_RDONLY, + O_RDONLY | O_BINARY, + O_RDWR, + O_RDWR | O_BINARY, + O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + O_RDWR | O_CREAT | O_TRUNC, + O_RDWR | O_CREAT | O_TRUNC | O_BINARY, + O_WRONLY | O_CREAT | O_APPEND, + O_WRONLY | O_CREAT | O_APPEND | O_BINARY, + O_RDWR | O_CREAT | O_APPEND, + O_RDWR | O_CREAT | O_APPEND | O_BINARY +}; + +static int semihosting_common_fileio_info(struct target *target, + struct gdb_fileio_info *fileio_info); +static int semihosting_common_fileio_end(struct target *target, int result, + int fileio_errno, bool ctrl_c); + +static int semihosting_read_fields(struct target *target, size_t number, + uint8_t *fields); +static int semihosting_write_fields(struct target *target, size_t number, + uint8_t *fields); +static uint64_t semihosting_get_field(struct target *target, size_t index, + uint8_t *fields); +static void semihosting_set_field(struct target *target, uint64_t value, + size_t index, + uint8_t *fields); + +/* Attempts to include gdb_server.h failed. */ +extern int gdb_actual_connections; + +/** + * Initialize common semihosting support. + * + * @param target Pointer to the target to initialize. + * @return An error status if there is a problem during initialization. + */ +int semihosting_common_init(struct target *target, void *setup, + void *post_result) +{ + LOG_DEBUG(" "); + + target->fileio_info = malloc(sizeof(*target->fileio_info)); + if (target->fileio_info == NULL) { + LOG_ERROR("out of memory"); + return ERROR_FAIL; + } + memset(target->fileio_info, 0, sizeof(*target->fileio_info)); + + struct semihosting *semihosting; + semihosting = malloc(sizeof(*target->semihosting)); + if (semihosting == NULL) { + LOG_ERROR("out of memory"); + return ERROR_FAIL; + } + + semihosting->is_active = false; + semihosting->is_fileio = false; + semihosting->hit_fileio = false; + semihosting->is_resumable = false; + semihosting->has_resumable_exit = false; + semihosting->word_size_bytes = 0; + semihosting->op = -1; + semihosting->param = 0; + semihosting->result = -1; + semihosting->sys_errno = -1; + semihosting->cmdline = NULL; + + /* If possible, update it in setup(). */ + semihosting->setup_time = clock(); + + semihosting->setup = setup; + semihosting->post_result = post_result; + + target->semihosting = semihosting; + + target->type->get_gdb_fileio_info = semihosting_common_fileio_info; + target->type->gdb_fileio_end = semihosting_common_fileio_end; + + return ERROR_OK; +} + +/** + * Portable implementation of ARM semihosting calls. + * Performs the currently pending semihosting operation + * encoded in target->semihosting. + */ +int semihosting_common(struct target *target) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + /* Silently ignore if the semhosting field was not set. */ + return ERROR_OK; + } + + struct gdb_fileio_info *fileio_info = target->fileio_info; + + /* + * By default return an error. + * The actual result must be set by each function + */ + semihosting->result = -1; + + /* Most operations are resumable, except the two exit calls. */ + semihosting->is_resumable = true; + + int retval; + + /* Enough space to hold 4 long words. */ + uint8_t fields[4*8]; + + LOG_DEBUG("op=0x%x, param=0x%" PRIx64, (int)semihosting->op, + semihosting->param); + + switch (semihosting->op) { + + case SEMIHOSTING_SYS_CLOCK: /* 0x10 */ + /* + * Returns the number of centiseconds (hundredths of a second) + * since the execution started. + * + * Values returned can be of limited use for some benchmarking + * purposes because of communication overhead or other + * agent-specific factors. For example, with a debug hardware + * unit the request is passed back to the host for execution. + * This can lead to unpredictable delays in transmission and + * process scheduling. + * + * Use this function to calculate time intervals, by calculating + * differences between intervals with and without the code + * sequence to be timed. + * + * Entry + * The PARAMETER REGISTER must contain 0. There are no other + * parameters. + * + * Return + * On exit, the RETURN REGISTER contains: + * - The number of centiseconds since some arbitrary start + * point, if the call is successful. + * - –1 if the call is not successful. For example, because + * of a communications error. + */ + { + clock_t delta = clock() - semihosting->setup_time; + + semihosting->result = delta / (CLOCKS_PER_SEC / 100); + } + break; + + case SEMIHOSTING_SYS_CLOSE: /* 0x02 */ + /* + * Closes a file on the host system. The handle must reference + * a file that was opened with SYS_OPEN. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field argument block: + * - field 1 Contains a handle for an open file. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the call is successful + * - –1 if the call is not successful. + */ + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + if (semihosting->is_fileio) { + if (fd == 0 || fd == 1 || fd == 2) { + semihosting->result = 0; + break; + } + semihosting->hit_fileio = true; + fileio_info->identifier = "close"; + fileio_info->param_1 = fd; + } else { + semihosting->result = close(fd); + semihosting->sys_errno = errno; + + LOG_DEBUG("close(%d)=%d", fd, (int)semihosting->result); + } + } + break; + + case SEMIHOSTING_SYS_ERRNO: /* 0x13 */ + /* + * Returns the value of the C library errno variable that is + * associated with the semihosting implementation. The errno + * variable can be set by a number of C library semihosted + * functions, including: + * - SYS_REMOVE + * - SYS_OPEN + * - SYS_CLOSE + * - SYS_READ + * - SYS_WRITE + * - SYS_SEEK. + * + * Whether errno is set or not, and to what value, is entirely + * host-specific, except where the ISO C standard defines the + * behavior. + * + * Entry + * There are no parameters. The PARAMETER REGISTER must be 0. + * + * Return + * On exit, the RETURN REGISTER contains the value of the C + * library errno variable. + */ + semihosting->result = semihosting->sys_errno; + break; + + case SEMIHOSTING_SYS_EXIT: /* 0x18 */ + /* + * Note: SYS_EXIT was called angel_SWIreason_ReportException in + * previous versions of the documentation. + * + * An application calls this operation to report an exception + * to the debugger directly. The most common use is to report + * that execution has completed, using ADP_Stopped_ApplicationExit. + * + * Note: This semihosting operation provides no means for 32-bit + * callers to indicate an application exit with a specified exit + * code. Semihosting callers may prefer to check for the presence + * of the SH_EXT_EXTENDED_REPORT_EXCEPTION extension and use + * the SYS_REPORT_EXCEPTION_EXTENDED operation instead, if it + * is available. + * + * Entry (32-bit) + * On entry, the PARAMETER register is set to a reason code + * describing the cause of the trap. Not all semihosting client + * implementations will necessarily trap every corresponding + * event. Important reason codes are: + * + * - ADP_Stopped_ApplicationExit 0x20026 + * - ADP_Stopped_RunTimeErrorUnknown 0x20023 + * + * Entry (64-bit) + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 The exception type, which is one of the set of + * reason codes in the above tables. + * - field 2 A subcode, whose meaning depends on the reason + * code in field 1. + * In particular, if field 1 is ADP_Stopped_ApplicationExit + * then field 2 is an exit status code, as passed to the C + * standard library exit() function. A simulator receiving + * this request must notify a connected debugger, if present, + * and then exit with the specified status. + * + * Return + * No return is expected from these calls. However, it is + * possible for the debugger to request that the application + * continues by performing an RDI_Execute request or equivalent. + * In this case, execution continues with the registers as they + * were on entry to the operation, or as subsequently modified + * by the debugger. + */ + if (semihosting->word_size_bytes == 8) { + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + int type = semihosting_get_field(target, 0, fields); + int code = semihosting_get_field(target, 1, fields); + + if (type == ADP_STOPPED_APPLICATION_EXIT) { + if (!gdb_actual_connections) + exit(code); + else { + fprintf(stderr, + "semihosting: *** application exited with %d ***\n", + code); + } + } else { + fprintf(stderr, + "semihosting: application exception %#x\n", + type); + } + } + } else { + if (semihosting->param == ADP_STOPPED_APPLICATION_EXIT) { + if (!gdb_actual_connections) + exit(0); + else { + fprintf(stderr, + "semihosting: *** application exited normally ***\n"); + } + } else if (semihosting->param == ADP_STOPPED_RUN_TIME_ERROR) { + /* Chosen more or less arbitrarly to have a nicer message, + * otherwise all other return the same exit code 1. */ + if (!gdb_actual_connections) + exit(1); + else { + fprintf(stderr, + "semihosting: *** application exited with error ***\n"); + } + } else { + if (!gdb_actual_connections) + exit(1); + else { + fprintf(stderr, + "semihosting: application exception %#x\n", + (unsigned) semihosting->param); + } + } + } + if (!semihosting->has_resumable_exit) { + semihosting->is_resumable = false; + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + break; + + case SEMIHOSTING_SYS_EXIT_EXTENDED: /* 0x20 */ + /* + * This operation is only supported if the semihosting extension + * SH_EXT_EXIT_EXTENDED is implemented. SH_EXT_EXIT_EXTENDED is + * reported using feature byte 0, bit 0. If this extension is + * supported, then the implementation provides a means to + * report a normal exit with a nonzero exit status in both 32-bit + * and 64-bit semihosting APIs. + * + * The implementation must provide the semihosting call + * SYS_EXIT_EXTENDED for both A64 and A32/T32 semihosting APIs. + * + * SYS_EXIT_EXTENDED is used by an application to report an + * exception or exit to the debugger directly. The most common + * use is to report that execution has completed, using + * ADP_Stopped_ApplicationExit. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 The exception type, which should be one of the set + * of reason codes that are documented for the SYS_EXIT + * (0x18) call. For example, ADP_Stopped_ApplicationExit. + * - field 2 A subcode, whose meaning depends on the reason + * code in field 1. In particular, if field 1 is + * ADP_Stopped_ApplicationExit then field 2 is an exit status + * code, as passed to the C standard library exit() function. + * A simulator receiving this request must notify a connected + * debugger, if present, and then exit with the specified status. + * + * Return + * No return is expected from these calls. + * + * For the A64 API, this call is identical to the behavior of + * the mandatory SYS_EXIT (0x18) call. If this extension is + * supported, then both calls must be implemented. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + int type = semihosting_get_field(target, 0, fields); + int code = semihosting_get_field(target, 1, fields); + + if (type == ADP_STOPPED_APPLICATION_EXIT) { + if (!gdb_actual_connections) + exit(code); + else { + fprintf(stderr, + "semihosting: *** application exited with %d ***\n", + code); + } + } else { + fprintf(stderr, "semihosting: exception %#x\n", + type); + } + } + if (!semihosting->has_resumable_exit) { + semihosting->is_resumable = false; + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + break; + + case SEMIHOSTING_SYS_FLEN: /* 0x0C */ + /* + * Returns the length of a specified file. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field argument block: + * - field 1 A handle for a previously opened, seekable file + * object. + * + * Return + * On exit, the RETURN REGISTER contains: + * - The current length of the file object, if the call is + * successful. + * - –1 if an error occurs. + */ + if (semihosting->is_fileio) { + semihosting->result = -1; + semihosting->sys_errno = EINVAL; + } + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + struct stat buf; + semihosting->result = fstat(fd, &buf); + if (semihosting->result == -1) { + semihosting->sys_errno = errno; + LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result); + break; + } + LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result); + semihosting->result = buf.st_size; + } + break; + + case SEMIHOSTING_SYS_GET_CMDLINE: /* 0x15 */ + /* + * Returns the command line that is used for the call to the + * executable, that is, argc and argv. + * + * Entry + * On entry, the PARAMETER REGISTER points to a two-field data + * block to be used for returning the command string and its length: + * - field 1 A pointer to a buffer of at least the size that is + * specified in field 2. + * - field 2 The length of the buffer in bytes. + * + * Return + * On exit: + * If the call is successful, then the RETURN REGISTER contains 0, + * the PARAMETER REGISTER is unchanged, and the data block is + * updated as follows: + * - field 1 A pointer to a null-terminated string of the command + * line. + * - field 2 The length of the string in bytes. + * If the call is not successful, then the RETURN REGISTER + * contains -1. + * + * Note: The semihosting implementation might impose limits on + * the maximum length of the string that can be transferred. + * However, the implementation must be able to support a + * command-line length of at least 80 bytes. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + size_t size = semihosting_get_field(target, 1, fields); + + char *arg = semihosting->cmdline != NULL ? + semihosting->cmdline : ""; + uint32_t len = strlen(arg) + 1; + if (len > size) + semihosting->result = -1; + else { + semihosting_set_field(target, len, 1, fields); + retval = target_write_buffer(target, addr, len, + (uint8_t *)arg); + if (retval != ERROR_OK) + return retval; + semihosting->result = 0; + + retval = semihosting_write_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + } + LOG_DEBUG("SYS_GET_CMDLINE=[%s],%d", arg, + (int)semihosting->result); + } + break; + + case SEMIHOSTING_SYS_HEAPINFO: /* 0x16 */ + /* + * Returns the system stack and heap parameters. + * + * Entry + * On entry, the PARAMETER REGISTER contains the address of a + * pointer to a four-field data block. The contents of the data + * block are filled by the function. The following C-like + * pseudocode describes the layout of the block: + * struct block { + * void* heap_base; + * void* heap_limit; + * void* stack_base; + * void* stack_limit; + * }; + * + * Return + * On exit, the PARAMETER REGISTER is unchanged and the data + * block has been updated. + */ + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + /* tell the remote we have no idea */ + memset(fields, 0, 4 * semihosting->word_size_bytes); + retval = target_write_memory(target, addr, 4, + semihosting->word_size_bytes, + fields); + if (retval != ERROR_OK) + return retval; + semihosting->result = 0; + } + break; + + case SEMIHOSTING_SYS_ISERROR: /* 0x08 */ + /* + * Determines whether the return code from another semihosting + * call is an error status or not. + * + * This call is passed a parameter block containing the error + * code to examine. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field data block: + * - field 1 The required status word to check. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the status field is not an error indication + * - A nonzero value if the status field is an error indication. + */ + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + + uint64_t code = semihosting_get_field(target, 0, fields); + semihosting->result = (code != 0); + break; + + case SEMIHOSTING_SYS_ISTTY: /* 0x09 */ + /* + * Checks whether a file is connected to an interactive device. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field argument block: + * field 1 A handle for a previously opened file object. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 1 if the handle identifies an interactive device. + * - 0 if the handle identifies a file. + * - A value other than 1 or 0 if an error occurs. + */ + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "isatty"; + fileio_info->param_1 = semihosting->param; + } else { + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + int fd = semihosting_get_field(target, 0, fields); + semihosting->result = isatty(fd); + LOG_DEBUG("isatty(%d)=%d", fd, (int)semihosting->result); + } + break; + + case SEMIHOSTING_SYS_OPEN: /* 0x01 */ + /* + * Opens a file on the host system. + * + * The file path is specified either as relative to the current + * directory of the host process, or absolute, using the path + * conventions of the host operating system. + * + * Semihosting implementations must support opening the special + * path name :semihosting-features as part of the semihosting + * extensions reporting mechanism. + * + * ARM targets interpret the special path name :tt as meaning + * the console input stream, for an open-read or the console + * output stream, for an open-write. Opening these streams is + * performed as part of the standard startup code for those + * applications that reference the C stdio streams. The + * semihosting extension SH_EXT_STDOUT_STDERR allows the + * semihosting caller to open separate output streams + * corresponding to stdout and stderr. This extension is + * reported using feature byte 0, bit 1. Use SYS_OPEN with + * the special path name :semihosting-features to access the + * feature bits. + * + * If this extension is supported, the implementation must + * support the following additional semantics to SYS_OPEN: + * - If the special path name :tt is opened with an fopen + * mode requesting write access (w, wb, w+, or w+b), then + * this is a request to open stdout. + * - If the special path name :tt is opened with a mode + * requesting append access (a, ab, a+, or a+b), then this is + * a request to open stderr. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-field argument block: + * - field 1 A pointer to a null-terminated string containing + * a file or device name. + * - field 2 An integer that specifies the file opening mode. + * - field 3 An integer that gives the length of the string + * pointed to by field 1. + * + * The length does not include the terminating null character + * that must be present. + * + * Return + * On exit, the RETURN REGISTER contains: + * - A nonzero handle if the call is successful. + * - –1 if the call is not successful. + */ + retval = semihosting_read_fields(target, 3, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + uint32_t mode = semihosting_get_field(target, 1, fields); + size_t len = semihosting_get_field(target, 2, fields); + + if (mode > 11) { + semihosting->result = -1; + semihosting->sys_errno = EINVAL; + break; + } + uint8_t *fn = malloc(len+1); + if (!fn) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_memory(target, addr, 1, len, fn); + if (retval != ERROR_OK) { + free(fn); + return retval; + } + fn[len] = 0; + /* TODO: implement the :semihosting-features special file. + * */ + if (semihosting->is_fileio) { + if (strcmp((char *)fn, ":semihosting-features") == 0) { + semihosting->result = -1; + semihosting->sys_errno = EINVAL; + } else if (strcmp((char *)fn, ":tt") == 0) { + if (mode == 0) + semihosting->result = 0; + else if (mode == 4) + semihosting->result = 1; + else if (mode == 8) + semihosting->result = 2; + else + semihosting->result = -1; + } else { + semihosting->hit_fileio = true; + fileio_info->identifier = "open"; + fileio_info->param_1 = addr; + fileio_info->param_2 = len; + fileio_info->param_3 = open_modeflags[mode]; + fileio_info->param_4 = 0644; + } + } else { + if (strcmp((char *)fn, ":tt") == 0) { + /* Mode is: + * - 0-3 ("r") for stdin, + * - 4-7 ("w") for stdout, + * - 8-11 ("a") for stderr */ + if (mode < 4) { + semihosting->result = dup( + STDIN_FILENO); + semihosting->sys_errno = errno; + LOG_DEBUG("dup(STDIN)=%d", + (int)semihosting->result); + } else if (mode < 8) { + semihosting->result = dup( + STDOUT_FILENO); + semihosting->sys_errno = errno; + LOG_DEBUG("dup(STDOUT)=%d", + (int)semihosting->result); + } else { + semihosting->result = dup( + STDERR_FILENO); + semihosting->sys_errno = errno; + LOG_DEBUG("dup(STDERR)=%d", + (int)semihosting->result); + } + } else { + /* cygwin requires the permission setting + * otherwise it will fail to reopen a previously + * written file */ + semihosting->result = open((char *)fn, + open_modeflags[mode], + 0644); + semihosting->sys_errno = errno; + LOG_DEBUG("open('%s')=%d", fn, + (int)semihosting->result); + } + } + free(fn); + } + } + break; + + case SEMIHOSTING_SYS_READ: /* 0x06 */ + /* + * Reads the contents of a file into a buffer. The file position + * is specified either: + * - Explicitly by a SYS_SEEK. + * - Implicitly one byte beyond the previous SYS_READ or + * SYS_WRITE request. + * + * The file position is at the start of the file when it is + * opened, and is lost when the file is closed. Perform the + * file operation as a single action whenever possible. For + * example, do not split a read of 16KB into four 4KB chunks + * unless there is no alternative. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-field data block: + * - field 1 Contains a handle for a file previously opened + * with SYS_OPEN. + * - field 2 Points to a buffer. + * - field 3 Contains the number of bytes to read to the buffer + * from the file. + * + * Return + * On exit, the RETURN REGISTER contains the number of bytes not + * filled in the buffer (buffer_length - bytes_read) as follows: + * - If the RETURN REGISTER is 0, the entire buffer was + * successfully filled. + * - If the RETURN REGISTER is the same as field 3, no bytes + * were read (EOF can be assumed). + * - If the RETURN REGISTER contains a value smaller than + * field 3, the read succeeded but the buffer was only partly + * filled. For interactive devices, this is the most common + * return value. + */ + retval = semihosting_read_fields(target, 3, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + uint64_t addr = semihosting_get_field(target, 1, fields); + size_t len = semihosting_get_field(target, 2, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "read"; + fileio_info->param_1 = fd; + fileio_info->param_2 = addr; + fileio_info->param_3 = len; + } else { + uint8_t *buf = malloc(len); + if (!buf) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + semihosting->result = read(fd, buf, len); + semihosting->sys_errno = errno; + LOG_DEBUG("read(%d, 0x%" PRIx64 ", %zu)=%d", + fd, + addr, + len, + (int)semihosting->result); + if (semihosting->result >= 0) { + retval = target_write_buffer(target, addr, + semihosting->result, + buf); + if (retval != ERROR_OK) { + free(buf); + return retval; + } + /* the number of bytes NOT filled in */ + semihosting->result = len - + semihosting->result; + } + free(buf); + } + } + } + break; + + case SEMIHOSTING_SYS_READC: /* 0x07 */ + /* + * Reads a byte from the console. + * + * Entry + * The PARAMETER REGISTER must contain 0. There are no other + * parameters or values possible. + * + * Return + * On exit, the RETURN REGISTER contains the byte read from + * the console. + */ + if (semihosting->is_fileio) { + LOG_ERROR("SYS_READC not supported by semihosting fileio"); + return ERROR_FAIL; + } + semihosting->result = getchar(); + LOG_DEBUG("getchar()=%d", (int)semihosting->result); + break; + + case SEMIHOSTING_SYS_REMOVE: /* 0x0E */ + /* + * Deletes a specified file on the host filing system. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 Points to a null-terminated string that gives the + * path name of the file to be deleted. + * - field 2 The length of the string. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the delete is successful + * - A nonzero, host-specific error code if the delete fails. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + size_t len = semihosting_get_field(target, 1, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "unlink"; + fileio_info->param_1 = addr; + fileio_info->param_2 = len; + } else { + uint8_t *fn = malloc(len+1); + if (!fn) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = + target_read_memory(target, addr, 1, len, + fn); + if (retval != ERROR_OK) { + free(fn); + return retval; + } + fn[len] = 0; + semihosting->result = remove((char *)fn); + semihosting->sys_errno = errno; + LOG_DEBUG("remove('%s')=%d", fn, + (int)semihosting->result); + + free(fn); + } + } + } + break; + + case SEMIHOSTING_SYS_RENAME: /* 0x0F */ + /* + * Renames a specified file. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * four-field data block: + * - field 1 A pointer to the name of the old file. + * - field 2 The length of the old filename. + * - field 3 A pointer to the new filename. + * - field 4 The length of the new filename. Both strings are + * null-terminated. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the rename is successful. + * - A nonzero, host-specific error code if the rename fails. + */ + retval = semihosting_read_fields(target, 4, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr1 = semihosting_get_field(target, 0, fields); + size_t len1 = semihosting_get_field(target, 1, fields); + uint64_t addr2 = semihosting_get_field(target, 2, fields); + size_t len2 = semihosting_get_field(target, 3, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "rename"; + fileio_info->param_1 = addr1; + fileio_info->param_2 = len1; + fileio_info->param_3 = addr2; + fileio_info->param_4 = len2; + } else { + uint8_t *fn1 = malloc(len1+1); + uint8_t *fn2 = malloc(len2+1); + if (!fn1 || !fn2) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_memory(target, addr1, 1, len1, + fn1); + if (retval != ERROR_OK) { + free(fn1); + free(fn2); + return retval; + } + retval = target_read_memory(target, addr2, 1, len2, + fn2); + if (retval != ERROR_OK) { + free(fn1); + free(fn2); + return retval; + } + fn1[len1] = 0; + fn2[len2] = 0; + semihosting->result = rename((char *)fn1, + (char *)fn2); + semihosting->sys_errno = errno; + LOG_DEBUG("rename('%s', '%s')=%d", fn1, fn2, + (int)semihosting->result); + + free(fn1); + free(fn2); + } + } + } + break; + + case SEMIHOSTING_SYS_SEEK: /* 0x0A */ + /* + * Seeks to a specified position in a file using an offset + * specified from the start of the file. The file is assumed + * to be a byte array and the offset is given in bytes. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field data block: + * - field 1 A handle for a seekable file object. + * - field 2 The absolute byte position to seek to. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the request is successful. + * - A negative value if the request is not successful. + * Use SYS_ERRNO to read the value of the host errno variable + * describing the error. + * + * Note: The effect of seeking outside the current extent of + * the file object is undefined. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + off_t pos = semihosting_get_field(target, 1, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "lseek"; + fileio_info->param_1 = fd; + fileio_info->param_2 = pos; + fileio_info->param_3 = SEEK_SET; + } else { + semihosting->result = lseek(fd, pos, SEEK_SET); + semihosting->sys_errno = errno; + LOG_DEBUG("lseek(%d, %d)=%d", fd, (int)pos, + (int)semihosting->result); + if (semihosting->result == pos) + semihosting->result = 0; + } + } + break; + + case SEMIHOSTING_SYS_SYSTEM: /* 0x12 */ + /* + * Passes a command to the host command-line interpreter. + * This enables you to execute a system command such as dir, + * ls, or pwd. The terminal I/O is on the host, and is not + * visible to the target. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 Points to a string to be passed to the host + * command-line interpreter. + * - field 2 The length of the string. + * + * Return + * On exit, the RETURN REGISTER contains the return status. + */ + + /* Provide SYS_SYSTEM functionality. Uses the + * libc system command, there may be a reason *NOT* + * to use this, but as I can't think of one, I + * implemented it this way. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + size_t len = semihosting_get_field(target, 1, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "system"; + fileio_info->param_1 = addr; + fileio_info->param_2 = len; + } else { + uint8_t *cmd = malloc(len+1); + if (!cmd) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_memory(target, + addr, + 1, + len, + cmd); + if (retval != ERROR_OK) { + free(cmd); + return retval; + } else { + cmd[len] = 0; + semihosting->result = system( + (const char *)cmd); + LOG_DEBUG("system('%s')=%d", + cmd, + (int)semihosting->result); + } + + free(cmd); + } + } + } + break; + + case SEMIHOSTING_SYS_TIME: /* 0x11 */ + /* + * Returns the number of seconds since 00:00 January 1, 1970. + * This value is real-world time, regardless of any debug agent + * configuration. + * + * Entry + * There are no parameters. + * + * Return + * On exit, the RETURN REGISTER contains the number of seconds. + */ + semihosting->result = time(NULL); + break; + + case SEMIHOSTING_SYS_WRITE: /* 0x05 */ + /* + * Writes the contents of a buffer to a specified file at the + * current file position. The file position is specified either: + * - Explicitly, by a SYS_SEEK. + * - Implicitly as one byte beyond the previous SYS_READ or + * SYS_WRITE request. + * + * The file position is at the start of the file when the file + * is opened, and is lost when the file is closed. + * + * Perform the file operation as a single action whenever + * possible. For example, do not split a write of 16KB into + * four 4KB chunks unless there is no alternative. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-field data block: + * - field 1 Contains a handle for a file previously opened + * with SYS_OPEN. + * - field 2 Points to the memory containing the data to be written. + * - field 3 Contains the number of bytes to be written from + * the buffer to the file. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the call is successful. + * - The number of bytes that are not written, if there is an error. + */ + retval = semihosting_read_fields(target, 3, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + uint64_t addr = semihosting_get_field(target, 1, fields); + size_t len = semihosting_get_field(target, 2, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "write"; + fileio_info->param_1 = fd; + fileio_info->param_2 = addr; + fileio_info->param_3 = len; + } else { + uint8_t *buf = malloc(len); + if (!buf) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_buffer(target, addr, len, buf); + if (retval != ERROR_OK) { + free(buf); + return retval; + } + semihosting->result = write(fd, buf, len); + semihosting->sys_errno = errno; + LOG_DEBUG("write(%d, 0x%" PRIx64 ", %zu)=%d", + fd, + addr, + len, + (int)semihosting->result); + if (semihosting->result >= 0) { + /* The number of bytes that are NOT written. + * */ + semihosting->result = len - + semihosting->result; + } + + free(buf); + } + } + } + break; + + case SEMIHOSTING_SYS_WRITEC: /* 0x03 */ + /* + * Writes a character byte, pointed to by the PARAMETER REGISTER, + * to the debug channel. When executed under a semihosting + * debugger, the character appears on the host debugger console. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to the + * character. + * + * Return + * None. The RETURN REGISTER is corrupted. + */ + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "write"; + fileio_info->param_1 = 1; + fileio_info->param_2 = semihosting->param; + fileio_info->param_3 = 1; + } else { + uint64_t addr = semihosting->param; + unsigned char c; + retval = target_read_memory(target, addr, 1, 1, &c); + if (retval != ERROR_OK) + return retval; + putchar(c); + semihosting->result = 0; + } + break; + + case SEMIHOSTING_SYS_WRITE0: /* 0x04 */ + /* + * Writes a null-terminated string to the debug channel. + * When executed under a semihosting debugger, the characters + * appear on the host debugger console. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to the + * first byte of the string. + * + * Return + * None. The RETURN REGISTER is corrupted. + */ + if (semihosting->is_fileio) { + size_t count = 0; + uint64_t addr = semihosting->param; + for (;; addr++) { + unsigned char c; + retval = target_read_memory(target, addr, 1, 1, &c); + if (retval != ERROR_OK) + return retval; + if (c == '\0') + break; + count++; + } + semihosting->hit_fileio = true; + fileio_info->identifier = "write"; + fileio_info->param_1 = 1; + fileio_info->param_2 = semihosting->param; + fileio_info->param_3 = count; + } else { + uint64_t addr = semihosting->param; + do { + unsigned char c; + retval = target_read_memory(target, addr++, 1, 1, &c); + if (retval != ERROR_OK) + return retval; + if (!c) + break; + putchar(c); + } while (1); + semihosting->result = 0; + } + break; + + case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */ + /* + * Returns the number of elapsed target ticks since execution + * started. + * Use SYS_TICKFREQ to determine the tick frequency. + * + * Entry (32-bit) + * On entry, the PARAMETER REGISTER points to a two-field data + * block to be used for returning the number of elapsed ticks: + * - field 1 The least significant field and is at the low address. + * - field 2 The most significant field and is at the high address. + * + * Entry (64-bit) + * On entry the PARAMETER REGISTER points to a one-field data + * block to be used for returning the number of elapsed ticks: + * - field 1 The number of elapsed ticks as a 64-bit value. + * + * Return + * On exit: + * - On success, the RETURN REGISTER contains 0, the PARAMETER + * REGISTER is unchanged, and the data block pointed to by the + * PARAMETER REGISTER is filled in with the number of elapsed + * ticks. + * - On failure, the RETURN REGISTER contains -1, and the + * PARAMETER REGISTER contains -1. + * + * Note: Some semihosting implementations might not support this + * semihosting operation, and they always return -1 in the + * RETURN REGISTER. + */ + + case SEMIHOSTING_SYS_TICKFREQ: /* 0x31 */ + /* + * Returns the tick frequency. + * + * Entry + * The PARAMETER REGISTER must contain 0 on entry to this routine. + * + * Return + * On exit, the RETURN REGISTER contains either: + * - The number of ticks per second. + * - –1 if the target does not know the value of one tick. + * + * Note: Some semihosting implementations might not support + * this semihosting operation, and they always return -1 in the + * RETURN REGISTER. + */ + + case SEMIHOSTING_SYS_TMPNAM: /* 0x0D */ + /* + * Returns a temporary name for a file identified by a system + * file identifier. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-word argument block: + * - field 1 A pointer to a buffer. + * - field 2 A target identifier for this filename. Its value + * must be an integer in the range 0-255. + * - field 3 Contains the length of the buffer. The length must + * be at least the value of L_tmpnam on the host system. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the call is successful. + * - –1 if an error occurs. + * + * The buffer pointed to by the PARAMETER REGISTER contains + * the filename, prefixed with a suitable directory name. + * If you use the same target identifier again, the same + * filename is returned. + * + * Note: The returned string must be null-terminated. + */ + + default: + fprintf(stderr, "semihosting: unsupported call %#x\n", + (unsigned) semihosting->op); + semihosting->result = -1; + semihosting->sys_errno = ENOTSUP; + } + + if (!semihosting->hit_fileio) { + retval = semihosting->post_result(target); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to post semihosting result"); + return retval; + } + } + + return ERROR_OK; +} + +/* ------------------------------------------------------------------------- + * Local functions. */ + +static int semihosting_common_fileio_info(struct target *target, + struct gdb_fileio_info *fileio_info) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return ERROR_FAIL; + + /* + * To avoid unnecessary duplication, semihosting prepares the + * fileio_info structure out-of-band when the target halts. See + * do_semihosting for more detail. + */ + if (!semihosting->is_fileio || !semihosting->hit_fileio) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int semihosting_common_fileio_end(struct target *target, int result, + int fileio_errno, bool ctrl_c) +{ + struct gdb_fileio_info *fileio_info = target->fileio_info; + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return ERROR_FAIL; + + /* clear pending status */ + semihosting->hit_fileio = false; + + semihosting->result = result; + semihosting->sys_errno = fileio_errno; + + /* + * Some fileio results do not match up with what the semihosting + * operation expects; for these operations, we munge the results + * below: + */ + switch (semihosting->op) { + case SEMIHOSTING_SYS_WRITE: /* 0x05 */ + if (result < 0) + semihosting->result = fileio_info->param_3; + else + semihosting->result = 0; + break; + + case SEMIHOSTING_SYS_READ: /* 0x06 */ + if (result == (int)fileio_info->param_3) + semihosting->result = 0; + if (result <= 0) + semihosting->result = fileio_info->param_3; + break; + + case SEMIHOSTING_SYS_SEEK: /* 0x0a */ + if (result > 0) + semihosting->result = 0; + break; + } + + return semihosting->post_result(target); +} + +/** + * Read all fields of a command from target to buffer. + */ +static int semihosting_read_fields(struct target *target, size_t number, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + /* Use 4-byte multiples to trigger fast memory access. */ + return target_read_memory(target, semihosting->param, 4, + number * (semihosting->word_size_bytes / 4), fields); +} + +/** + * Write all fields of a command from buffer to target. + */ +static int semihosting_write_fields(struct target *target, size_t number, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + /* Use 4-byte multiples to trigger fast memory access. */ + return target_write_memory(target, semihosting->param, 4, + number * (semihosting->word_size_bytes / 4), fields); +} + +/** + * Extract a field from the buffer, considering register size and endianness. + */ +static uint64_t semihosting_get_field(struct target *target, size_t index, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + if (semihosting->word_size_bytes == 8) + return target_buffer_get_u64(target, fields + (index * 8)); + else + return target_buffer_get_u32(target, fields + (index * 4)); +} + +/** + * Store a field in the buffer, considering register size and endianness. + */ +static void semihosting_set_field(struct target *target, uint64_t value, + size_t index, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + if (semihosting->word_size_bytes == 8) + target_buffer_set_u64(target, fields + (index * 8), value); + else + target_buffer_set_u32(target, fields + (index * 4), value); +} + + +/* ------------------------------------------------------------------------- + * Common semihosting commands handlers. */ + +__COMMAND_HANDLER(handle_common_semihosting_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) { + int is_active; + + COMMAND_PARSE_ENABLE(CMD_ARGV[0], is_active); + + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_FAIL; + } + + if (semihosting && semihosting->setup(target, is_active) != ERROR_OK) { + LOG_ERROR("Failed to Configure semihosting"); + return ERROR_FAIL; + } + + /* FIXME never let that "catch" be dropped! (???) */ + semihosting->is_active = is_active; + } + + command_print(CMD_CTX, "semihosting is %s", + semihosting->is_active + ? "enabled" : "disabled"); + + return ERROR_OK; +} + + +__COMMAND_HANDLER(handle_common_semihosting_fileio_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (!semihosting->is_active) { + command_print(CMD_CTX, "semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) + COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->is_fileio); + + command_print(CMD_CTX, "semihosting fileio is %s", + semihosting->is_fileio + ? "enabled" : "disabled"); + + return ERROR_OK; +} + +__COMMAND_HANDLER(handle_common_semihosting_cmdline) +{ + struct target *target = get_current_target(CMD_CTX); + unsigned int i; + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + free(semihosting->cmdline); + semihosting->cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL; + + for (i = 1; i < CMD_ARGC; i++) { + char *cmdline = alloc_printf("%s %s", semihosting->cmdline, CMD_ARGV[i]); + if (cmdline == NULL) + break; + free(semihosting->cmdline); + semihosting->cmdline = cmdline; + } + + command_print(CMD_CTX, "semihosting command line is [%s]", + semihosting->cmdline); + + return ERROR_OK; +} + +__COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (!semihosting->is_active) { + command_print(CMD_CTX, "semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) + COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->has_resumable_exit); + + command_print(CMD_CTX, "semihosting resumable exit is %s", + semihosting->has_resumable_exit + ? "enabled" : "disabled"); + + return ERROR_OK; +} diff --git a/src/target/semihosting_common.h b/src/target/semihosting_common.h new file mode 100644 index 0000000..8fb5e0c --- /dev/null +++ b/src/target/semihosting_common.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * <ilg@livius.net> * + * * + * Copyright (C) 2009 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre <nico@marvell.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H +#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H + +#include <stdint.h> +#include <stdbool.h> +#include <time.h> + +/* + * According to: + * "Semihosting for AArch32 and AArch64, Release 2.0" + * https://static.docs.arm.com/100863/0200/semihosting.pdf + * from ARM Ltd. + * + * The available semihosting operation numbers passed in R0 are allocated + * as follows: + * - 0x00-0x31 Used by ARM. + * - 0x32-0xFF Reserved for future use by ARM. + * - 0x100-0x1FF Reserved for user applications. These are not used by ARM. + * However, if you are writing your own SVC operations, you are advised + * to use a different SVC number rather than using the semihosted + * SVC number and these operation type numbers. + * - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended + * that you do not use these. + */ + +enum semihosting_operation_numbers { + /* + * ARM semihosting operations, in lexicographic order. + */ + SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */ + + SEMIHOSTING_SYS_CLOSE = 0x02, + SEMIHOSTING_SYS_CLOCK = 0x10, + SEMIHOSTING_SYS_ELAPSED = 0x30, + SEMIHOSTING_SYS_ERRNO = 0x13, + SEMIHOSTING_SYS_EXIT = 0x18, + SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20, + SEMIHOSTING_SYS_FLEN = 0x0C, + SEMIHOSTING_SYS_GET_CMDLINE = 0x15, + SEMIHOSTING_SYS_HEAPINFO = 0x16, + SEMIHOSTING_SYS_ISERROR = 0x08, + SEMIHOSTING_SYS_ISTTY = 0x09, + SEMIHOSTING_SYS_OPEN = 0x01, + SEMIHOSTING_SYS_READ = 0x06, + SEMIHOSTING_SYS_READC = 0x07, + SEMIHOSTING_SYS_REMOVE = 0x0E, + SEMIHOSTING_SYS_RENAME = 0x0F, + SEMIHOSTING_SYS_SEEK = 0x0A, + SEMIHOSTING_SYS_SYSTEM = 0x12, + SEMIHOSTING_SYS_TICKFREQ = 0x31, + SEMIHOSTING_SYS_TIME = 0x11, + SEMIHOSTING_SYS_TMPNAM = 0x0D, + SEMIHOSTING_SYS_WRITE = 0x05, + SEMIHOSTING_SYS_WRITEC = 0x03, + SEMIHOSTING_SYS_WRITE0 = 0x04, +}; + +/* + * Codes used by SEMIHOSTING_SYS_EXIT (formerly + * SEMIHOSTING_REPORT_EXCEPTION). + * On 64-bits, the exit code is passed explicitly. + */ +enum semihosting_reported_exceptions { + /* On 32 bits, use it for exit(0) */ + ADP_STOPPED_APPLICATION_EXIT = ((2 << 16) + 38), + /* On 32 bits, use it for exit(1) */ + ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35), +}; + +struct target; + +/* + * A pointer to this structure was added to the target structure. + */ +struct semihosting { + + /** A flag reporting whether semihosting is active. */ + bool is_active; + + /** A flag reporting whether semihosting fileio is active. */ + bool is_fileio; + + /** A flag reporting whether semihosting fileio operation is active. */ + bool hit_fileio; + + /** Most are resumable, except the two exit calls. */ + bool is_resumable; + + /** + * When SEMIHOSTING_SYS_EXIT is called outside a debug session, + * things are simple, the openocd process calls exit() and passes + * the value returned by the target. + * When SEMIHOSTING_SYS_EXIT is called during a debug session, + * by default execution returns to the debugger, leaving the + * debugger in a HALT state, similar to the state entered when + * encountering a break. + * In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT + * return normally, as any semihosting call, and do not break + * to the debugger. + * The standard allows this to happen, but the condition + * to trigger it is a bit obscure ("by performing an RDI_Execute + * request or equivalent"). + * + * To make the SEMIHOSTING_SYS_EXIT call return normally, enable + * this variable via the dedicated command (default: disabled). + */ + bool has_resumable_exit; + + /** The Target (hart) word size; 8 for 64-bits targets. */ + size_t word_size_bytes; + + /** The current semihosting operation (R0 on ARM). */ + int op; + + /** The current semihosting parameter (R1 or ARM). */ + uint64_t param; + + /** + * The current semihosting result to be returned to the application. + * Usually 0 for success, -1 for error, + * but sometimes a useful value, even a pointer. + */ + int64_t result; + + /** The value to be returned by semihosting SYS_ERRNO request. */ + int sys_errno; + + /** The semihosting command line to be passed to the target. */ + char *cmdline; + + /** The current time when 'execution starts' */ + clock_t setup_time; + + int (*setup)(struct target *target, int enable); + int (*post_result)(struct target *target); +}; + +int semihosting_common_init(struct target *target, void *setup, + void *post_result); +int semihosting_common(struct target *target); + +#endif /* OPENOCD_TARGET_SEMIHOSTING_COMMON_H */ diff --git a/src/target/startup.tcl b/src/target/startup.tcl index 9bbc6e3..cf844e1 100644 --- a/src/target/startup.tcl +++ b/src/target/startup.tcl @@ -203,6 +203,7 @@ proc init_target_events {} { foreach t $targets { set_default_target_event $t gdb-flash-erase-start "reset init" set_default_target_event $t gdb-flash-write-end "reset halt" + set_default_target_event $t gdb-attach "halt" } } diff --git a/src/target/stm8.c b/src/target/stm8.c index 262497b..5a3438a 100644 --- a/src/target/stm8.c +++ b/src/target/stm8.c @@ -477,7 +477,8 @@ static int stm8_examine_debug_reason(struct target *target) uint8_t csr1, csr2; retval = stm8_read_dm_csrx(target, &csr1, &csr2); - LOG_DEBUG("csr1 = 0x%02X csr2 = 0x%02X", csr1, csr2); + if (retval == ERROR_OK) + LOG_DEBUG("csr1 = 0x%02X csr2 = 0x%02X", csr1, csr2); if ((target->debug_reason != DBG_REASON_DBGRQ) && (target->debug_reason != DBG_REASON_SINGLESTEP)) { @@ -1749,7 +1750,7 @@ static int stm8_examine(struct target *target) /** Checks whether a memory region is erased. */ static int stm8_blank_check_memory(struct target *target, - target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value) + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value) { struct working_area *erase_check_algorithm; struct reg_param reg_params[2]; @@ -1777,10 +1778,10 @@ static int stm8_blank_check_memory(struct target *target, stm8_info.common_magic = STM8_COMMON_MAGIC; init_mem_param(&mem_params[0], 0x0, 3, PARAM_OUT); - buf_set_u32(mem_params[0].value, 0, 24, address); + buf_set_u32(mem_params[0].value, 0, 24, blocks[0].address); init_mem_param(&mem_params[1], 0x3, 3, PARAM_OUT); - buf_set_u32(mem_params[1].value, 0, 24, count); + buf_set_u32(mem_params[1].value, 0, 24, blocks[0].size); init_reg_param(®_params[0], "a", 32, PARAM_IN_OUT); buf_set_u32(reg_params[0].value, 0, 32, erased_value); @@ -1794,7 +1795,7 @@ static int stm8_blank_check_memory(struct target *target, 10000, &stm8_info); if (retval == ERROR_OK) - *blank = (*(reg_params[0].value) == 0xff); + blocks[0].result = (*(reg_params[0].value) == 0xff); destroy_mem_param(&mem_params[0]); destroy_mem_param(&mem_params[1]); @@ -1802,7 +1803,10 @@ static int stm8_blank_check_memory(struct target *target, target_free_working_area(target, erase_check_algorithm); - return retval; + if (retval != ERROR_OK) + return retval; + + return 1; /* only one block has been checked */ } static int stm8_checksum_memory(struct target *target, target_addr_t address, diff --git a/src/target/target.c b/src/target/target.c index ce7782e..8240e65 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -54,6 +54,7 @@ #include "image.h" #include "rtos/rtos.h" #include "transport/transport.h" +#include "arm_cti.h" /* default halt wait timeout (ms) */ #define DEFAULT_HALT_TIMEOUT 5000 @@ -512,7 +513,9 @@ struct target *get_target_by_num(int num) struct target *get_current_target(struct command_context *cmd_ctx) { - struct target *target = get_target_by_num(cmd_ctx->current_target); + struct target *target = cmd_ctx->current_target_override + ? cmd_ctx->current_target_override + : cmd_ctx->current_target; if (target == NULL) { LOG_ERROR("BUG: current_target out of bounds"); @@ -810,8 +813,7 @@ done: } /** - * Downloads a target-specific native code algorithm to the target, - * executes and leaves it running. + * Executes a target-specific native code algorithm and leaves it running. * * @param target used to run the algorithm * @param arch_info target-specific description of the algorithm. @@ -884,12 +886,45 @@ done: } /** - * Executes a target-specific native code algorithm in the target. - * It differs from target_run_algorithm in that the algorithm is asynchronous. - * Because of this it requires an compliant algorithm: - * see contrib/loaders/flash/stm32f1x.S for example. + * Streams data to a circular buffer on target intended for consumption by code + * running asynchronously on target. + * + * This is intended for applications where target-specific native code runs + * on the target, receives data from the circular buffer, does something with + * it (most likely writing it to a flash memory), and advances the circular + * buffer pointer. + * + * This assumes that the helper algorithm has already been loaded to the target, + * but has not been started yet. Given memory and register parameters are passed + * to the algorithm. + * + * The buffer is defined by (buffer_start, buffer_size) arguments and has the + * following format: + * + * [buffer_start + 0, buffer_start + 4): + * Write Pointer address (aka head). Written and updated by this + * routine when new data is written to the circular buffer. + * [buffer_start + 4, buffer_start + 8): + * Read Pointer address (aka tail). Updated by code running on the + * target after it consumes data. + * [buffer_start + 8, buffer_start + buffer_size): + * Circular buffer contents. + * + * See contrib/loaders/flash/stm32f1x.S for an example. * * @param target used to run the algorithm + * @param buffer address on the host where data to be sent is located + * @param count number of blocks to send + * @param block_size size in bytes of each block + * @param num_mem_params count of memory-based params to pass to algorithm + * @param mem_params memory-based params to pass to algorithm + * @param num_reg_params count of register-based params to pass to algorithm + * @param reg_params memory-based params to pass to algorithm + * @param buffer_start address on the target of the circular buffer structure + * @param buffer_size size of the circular buffer structure + * @param entry_point address on the target to execute to start the algorithm + * @param exit_point address at which to set a breakpoint to catch the + * end of the algorithm; can be 0 if target triggers a breakpoint itself */ int target_run_flash_async_algorithm(struct target *target, @@ -1860,8 +1895,41 @@ static void target_destroy(struct target *target) if (target->type->deinit_target) target->type->deinit_target(target); + if (target->semihosting) + free(target->semihosting); + + jtag_unregister_event_callback(jtag_enable_callback, target); + + struct target_event_action *teap = target->event_action; + while (teap) { + struct target_event_action *next = teap->next; + Jim_DecrRefCount(teap->interp, teap->body); + free(teap); + teap = next; + } + + target_free_all_working_areas(target); + /* Now we have none or only one working area marked as free */ + if (target->working_areas) { + free(target->working_areas->backup); + free(target->working_areas); + } + + /* release the targets SMP list */ + if (target->smp) { + struct target_list *head = target->head; + while (head != NULL) { + struct target_list *pos = head->next; + head->target->smp = 0; + free(head); + head = pos; + } + target->smp = 0; + } + free(target->type); free(target->trace_info); + free(target->fileio_info); free(target->cmd_name); free(target); } @@ -2188,21 +2256,19 @@ int target_checksum_memory(struct target *target, target_addr_t address, uint32_ return retval; } -int target_blank_check_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t* blank, +int target_blank_check_memory(struct target *target, + struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value) { - int retval; if (!target_was_examined(target)) { LOG_ERROR("Target not examined yet"); return ERROR_FAIL; } - if (target->type->blank_check_memory == 0) + if (target->type->blank_check_memory == NULL) return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - retval = target->type->blank_check_memory(target, address, size, blank, erased_value); - - return retval; + return target->type->blank_check_memory(target, blocks, num_blocks, erased_value); } int target_read_u64(struct target *target, target_addr_t address, uint64_t *value) @@ -2475,7 +2541,10 @@ static int find_target(struct command_context *cmd_ctx, const char *name) return ERROR_FAIL; } - cmd_ctx->current_target = target->target_number; + cmd_ctx->current_target = target; + if (cmd_ctx->current_target_override) + cmd_ctx->current_target_override = target; + return ERROR_OK; } @@ -2503,7 +2572,7 @@ COMMAND_HANDLER(handle_targets_command) else state = "tap-disabled"; - if (CMD_CTX->current_target == target->target_number) + if (CMD_CTX->current_target == target) marker = '*'; /* keep columns lined up to match the headers above */ @@ -2920,6 +2989,9 @@ COMMAND_HANDLER(handle_halt_command) LOG_DEBUG("-"); struct target *target = get_current_target(CMD_CTX); + + target->verbose_halt_msg = true; + int retval = target_halt(target); if (ERROR_OK != retval) return retval; @@ -4074,8 +4146,9 @@ static int target_mem2array(Jim_Interp *interp, struct target *target, int argc, * argv[3] = memory address * argv[4] = count of times to read */ + if (argc < 4 || argc > 5) { - Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems [phys]"); + Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems [phys]"); return JIM_ERR; } varname = Jim_GetString(argv[0], &len); @@ -4424,17 +4497,28 @@ void target_handle_event(struct target *target, enum target_event e) for (teap = target->event_action; teap != NULL; teap = teap->next) { if (teap->event == e) { - LOG_DEBUG("target: (%d) %s (%s) event: %d (%s) action: %s", + LOG_DEBUG("target(%d): %s (%s) event: %d (%s) action: %s", target->target_number, target_name(target), target_type_name(target), e, Jim_Nvp_value2name_simple(nvp_target_event, e)->name, Jim_GetString(teap->body, NULL)); + + /* Override current target by the target an event + * is issued from (lot of scripts need it). + * Return back to previous override as soon + * as the handler processing is done */ + struct command_context *cmd_ctx = current_command_context(teap->interp); + struct target *saved_target_override = cmd_ctx->current_target_override; + cmd_ctx->current_target_override = target; + if (Jim_EvalObj(teap->interp, teap->body) != JIM_OK) { Jim_MakeErrorMessage(teap->interp); command_print(NULL, "%s\n", Jim_GetString(Jim_GetResult(teap->interp), NULL)); } + + cmd_ctx->current_target_override = saved_target_override; } } } @@ -4464,7 +4548,6 @@ enum target_cfg_param { TCFG_COREID, TCFG_CHAIN_POSITION, TCFG_DBGBASE, - TCFG_CTIBASE, TCFG_RTOS, TCFG_DEFER_EXAMINE, }; @@ -4480,7 +4563,6 @@ static Jim_Nvp nvp_config_opts[] = { { .name = "-coreid", .value = TCFG_COREID }, { .name = "-chain-position", .value = TCFG_CHAIN_POSITION }, { .name = "-dbgbase", .value = TCFG_DBGBASE }, - { .name = "-ctibase", .value = TCFG_CTIBASE }, { .name = "-rtos", .value = TCFG_RTOS }, { .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE }, { .name = NULL, .value = -1 } @@ -4717,6 +4799,13 @@ no_params: if (goi->isconfigure) { Jim_Obj *o_t; struct jtag_tap *tap; + + if (target->has_dap) { + Jim_SetResultString(goi->interp, + "target requires -dap parameter instead of -chain-position!", -1); + return JIM_ERR; + } + target_free_all_working_areas(target); e = Jim_GetOpt_Obj(goi, &o_t); if (e != JIM_OK) @@ -4724,8 +4813,8 @@ no_params: tap = jtag_tap_by_jim_obj(goi->interp, o_t); if (tap == NULL) return JIM_ERR; - /* make this exactly 1 or 0 */ target->tap = tap; + target->tap_configured = true; } else { if (goi->argc != 0) goto no_params; @@ -4747,20 +4836,6 @@ no_params: Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->dbgbase)); /* loop for more */ break; - case TCFG_CTIBASE: - if (goi->isconfigure) { - e = Jim_GetOpt_Wide(goi, &w); - if (e != JIM_OK) - return e; - target->ctibase = (uint32_t)w; - target->ctibase_set = true; - } else { - if (goi->argc != 0) - goto no_params; - } - Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->ctibase)); - /* loop for more */ - break; case TCFG_RTOS: /* RTOS */ { @@ -5377,21 +5452,19 @@ static const struct command_registration target_instance_command_handlers[] = { .mode = COMMAND_EXEC, .jim_handler = jim_target_examine, .help = "used internally for reset processing", - .usage = "arp_examine ['allow-defer']", + .usage = "['allow-defer']", }, { .name = "was_examined", .mode = COMMAND_EXEC, .jim_handler = jim_target_was_examined, .help = "used internally for reset processing", - .usage = "was_examined", }, { .name = "examine_deferred", .mode = COMMAND_EXEC, .jim_handler = jim_target_examine_deferred, .help = "used internally for reset processing", - .usage = "examine_deferred", }, { .name = "arp_halt_gdb", @@ -5512,7 +5585,7 @@ static int target_create(Jim_GetOptInfo *goi) target = calloc(1, sizeof(struct target)); /* set target number */ target->target_number = new_target_number(); - cmd_ctx->current_target = target->target_number; + cmd_ctx->current_target = target; /* allocate memory for each unique target type */ target->type = calloc(1, sizeof(struct target_type)); @@ -5538,7 +5611,7 @@ static int target_create(Jim_GetOptInfo *goi) target->next = NULL; target->arch_info = NULL; - target->display = 1; + target->verbose_halt_msg = true; target->halt_issued = false; @@ -5557,9 +5630,21 @@ static int target_create(Jim_GetOptInfo *goi) goi->isconfigure = 1; e = target_configure(goi, target); - if (target->tap == NULL) { - Jim_SetResultString(goi->interp, "-chain-position required when creating target", -1); - e = JIM_ERR; + if (e == JIM_OK) { + if (target->has_dap) { + if (!target->dap_configured) { + Jim_SetResultString(goi->interp, "-dap ?name? required when creating target", -1); + e = JIM_ERR; + } + } else { + if (!target->tap_configured) { + Jim_SetResultString(goi->interp, "-chain-position ?name? required when creating target", -1); + e = JIM_ERR; + } + } + /* tap must be set after target was configured */ + if (target->tap == NULL) + e = JIM_ERR; } if (e != JIM_OK) { @@ -5576,14 +5661,23 @@ static int target_create(Jim_GetOptInfo *goi) cp = Jim_GetString(new_cmd, NULL); target->cmd_name = strdup(cp); + if (target->type->target_create) { + e = (*(target->type->target_create))(target, goi->interp); + if (e != ERROR_OK) { + LOG_DEBUG("target_create failed"); + free(target->type); + free(target->cmd_name); + free(target); + return JIM_ERR; + } + } + /* create the target specific commands */ if (target->type->commands) { e = register_commands(cmd_ctx, NULL, target->type->commands); if (ERROR_OK != e) LOG_ERROR("unable to register '%s' commands", cp); } - if (target->type->target_create) - (*(target->type->target_create))(target, goi->interp); /* append to end of list */ { @@ -6337,7 +6431,7 @@ static const struct command_registration target_exec_command_handlers[] = { .handler = handle_bp_command, .mode = COMMAND_EXEC, .help = "list or set hardware or software breakpoint", - .usage = "<address> [<asid>]<length> ['hw'|'hw_ctx']", + .usage = "<address> [<asid>] <length> ['hw'|'hw_ctx']", }, { .name = "rbp", diff --git a/src/target/target.h b/src/target/target.h index c11a626..51a5b69 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -153,7 +153,7 @@ struct target { struct target_event_action *event_action; int reset_halt; /* attempt resetting the CPU into the halted mode? */ - uint32_t working_area; /* working area (initialised RAM). Evaluated + target_addr_t working_area; /* working area (initialised RAM). Evaluated * upon first allocation from virtual/physical address. */ bool working_area_virt_spec; /* virtual address specified? */ target_addr_t working_area_virt; /* virtual address */ @@ -176,20 +176,21 @@ struct target { void *private_config; /* pointer to target specific config data (for jim_configure hook) */ struct target *next; /* next target in list */ - int display; /* display async info in telnet session. Do not display + bool verbose_halt_msg; /* display async info in telnet session. Do not display * lots of halted/resumed info when stepping in debugger. */ bool halt_issued; /* did we transition to halted state? */ int64_t halt_issued_time; /* Note time when halt was issued */ + /* ARM v7/v8 targets with ADIv5 interface */ bool dbgbase_set; /* By default the debug base is not set */ uint32_t dbgbase; /* Really a Cortex-A specific option, but there is no * system in place to support target specific options * currently. */ + bool has_dap; /* set to true if target has ADIv5 support */ + bool dap_configured; /* set to true if ADIv5 DAP is configured */ + bool tap_configured; /* set to true if JTAG tap has been configured + * through -chain-position */ - bool ctibase_set; /* By default the debug base is not set */ - uint32_t ctibase; /* Really a Cortex-A specific option, but there is no - * system in place to support target specific options - * currently. */ struct rtos *rtos; /* Instance of Real Time Operating System support */ bool rtos_auto_detect; /* A flag that indicates that the RTOS has been specified as "auto" * and must be detected when symbols are offered */ @@ -205,13 +206,8 @@ struct target { /* file-I/O information for host to do syscall */ struct gdb_fileio_info *fileio_info; - /** - * When true, send gdb an error result when reading/writing a register - * fails. This must be false for some ARM targets (Cortex-M3), where a 'g' - * packet results in an attempt to read 'r0', which fails, which causes gdb - * to close the connection. - */ - bool propagate_register_errors; + /* The semihosting information, extracted from the target. */ + struct semihosting *semihosting; }; struct target_list { @@ -221,10 +217,10 @@ struct target_list { struct gdb_fileio_info { char *identifier; - uint32_t param_1; - uint32_t param_2; - uint32_t param_3; - uint32_t param_4; + uint64_t param_1; + uint64_t param_2; + uint64_t param_3; + uint64_t param_4; }; /** Returns the instance-specific name of the specified target. */ @@ -319,6 +315,12 @@ struct target_timer_callback { struct target_timer_callback *next; }; +struct target_memory_check_block { + target_addr_t address; + uint32_t size; + uint32_t result; +}; + int target_register_commands(struct command_context *cmd_ctx); int target_examine(void); @@ -592,7 +594,8 @@ int target_read_buffer(struct target *target, int target_checksum_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t *crc); int target_blank_check_memory(struct target *target, - target_addr_t address, uint32_t size, uint32_t *blank, uint8_t erased_value); + struct target_memory_check_block *blocks, int num_blocks, + uint8_t erased_value); int target_wait_state(struct target *target, enum target_state state, int ms); /** diff --git a/src/target/target_type.h b/src/target/target_type.h index 0ab22bd..fbbd57d 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -130,8 +130,9 @@ struct target_type { int (*checksum_memory)(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum); - int (*blank_check_memory)(struct target *target, target_addr_t address, - uint32_t count, uint32_t *blank, uint8_t erased_value); + int (*blank_check_memory)(struct target *target, + struct target_memory_check_block *blocks, int num_blocks, + uint8_t erased_value); /* * target break-/watchpoint control diff --git a/src/transport/transport.h b/src/transport/transport.h index 6c57067..140ef50 100644 --- a/src/transport/transport.h +++ b/src/transport/transport.h @@ -19,6 +19,10 @@ #ifndef OPENOCD_TRANSPORT_TRANSPORT_H #define OPENOCD_TRANSPORT_TRANSPORT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "helper/command.h" /** @@ -90,4 +94,16 @@ int allow_transports(struct command_context *ctx, const char * const *vector); bool transports_are_declared(void); +bool transport_is_jtag(void); +bool transport_is_swd(void); + +#if BUILD_HLADAPTER +bool transport_is_hla(void); +#else +static inline bool transport_is_hla(void) +{ + return false; +} +#endif + #endif /* OPENOCD_TRANSPORT_TRANSPORT_H */ |