diff options
author | Tim Newsome <tim@sifive.com> | 2018-05-03 16:02:59 -0700 |
---|---|---|
committer | Tim Newsome <tim@sifive.com> | 2018-05-03 16:02:59 -0700 |
commit | 487501e76137b446057a598567d6b3a0c5106407 (patch) | |
tree | 217bbaec5c3537973e98c6f9164da8b952110f0b /src | |
parent | 1fda89c3cef918a87d2b3bcd1f3158c011ef107e (diff) | |
parent | 67b4e2c5223e79c934a2522b811a4708c9294d7a (diff) | |
download | riscv-openocd-487501e76137b446057a598567d6b3a0c5106407.zip riscv-openocd-487501e76137b446057a598567d6b3a0c5106407.tar.gz riscv-openocd-487501e76137b446057a598567d6b3a0c5106407.tar.bz2 |
Merge branch 'riscv' into optimize
Change-Id: I2693eb05dee72acd2df5d8594c51e9da08ea1cc6
Diffstat (limited to 'src')
131 files changed, 7213 insertions, 1585 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index cc72088..7121412 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -18,6 +18,7 @@ NOR_DRIVERS = \ %D%/ath79.c \ %D%/atsamv.c \ %D%/avrf.c \ + %D%/bluenrg-x.c \ %D%/cfi.c \ %D%/dsp5680xx_flash.c \ %D%/efm32.c \ @@ -42,6 +43,7 @@ NOR_DRIVERS = \ %D%/ocl.c \ %D%/pic32mx.c \ %D%/psoc4.c \ + %D%/psoc6.c \ %D%/sim3x.c \ %D%/spi.c \ %D%/stmsmi.c \ 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/at91samd.c b/src/flash/nor/at91samd.c index ad88c51..64716d9 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; @@ -159,23 +162,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 +202,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 +262,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 { @@ -295,24 +309,42 @@ struct samd_info { 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 +515,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 +542,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; +} + +static int read_userrow(struct target *target, uint64_t *userrow) +{ + int res; + uint8_t buffer[8]; - return false; + 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 +607,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 +657,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 +715,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!"); @@ -944,6 +1017,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 +1199,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 +1229,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 +1239,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 }; 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/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..636d50c 100644 --- a/src/flash/nor/core.c +++ b/src/flash/nor/core.c @@ -171,6 +171,31 @@ 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); + + free(bank->name); + free(bank->sectors); + free(bank->prot_blocks); + 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); @@ -399,18 +424,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]; @@ -601,7 +629,7 @@ int flash_write_unlock(struct target *target, struct image *image, uint32_t buffer_size; 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 +645,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; @@ -652,7 +680,18 @@ int flash_write_unlock(struct target *target, struct image *image, /* 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); + target_addr_t run_next_addr = run_address + run_size; + if (sections[section_last + 1]->base_address < run_next_addr) { + LOG_ERROR("Section at " TARGET_ADDR_FMT + " overlaps section ending at " TARGET_ADDR_FMT, + sections[section_last + 1]->base_address, + run_next_addr); + LOG_ERROR("Flash write aborted."); + retval = ERROR_FAIL; + goto done; + } + + pad_bytes = sections[section_last + 1]->base_address - run_next_addr; padding[section_last] = pad_bytes; run_size += sections[++section_last]->size; run_size += pad_bytes; diff --git a/src/flash/nor/core.h b/src/flash/nor/core.h index 338363e..1bfe1ab 100644 --- a/src/flash/nor/core.h +++ b/src/flash/nor/core.h @@ -76,7 +76,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. */ @@ -153,8 +153,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..3d6dab2 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -31,6 +31,7 @@ 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 cfi_flash; extern struct flash_driver dsp5680xx_flash; extern struct flash_driver efm32_flash; @@ -55,6 +56,7 @@ 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 psoc6_flash; extern struct flash_driver sim3x_flash; extern struct flash_driver stellaris_flash; extern struct flash_driver stm32f1x_flash; @@ -88,6 +90,7 @@ static struct flash_driver *flash_drivers[] = { &ath79_flash, &atsamv_flash, &avr_flash, + &bluenrgx_flash, &cfi_flash, &dsp5680xx_flash, &efm32_flash, @@ -112,6 +115,7 @@ static struct flash_driver *flash_drivers[] = { &ocl_flash, &pic32mx_flash, &psoc4_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..6df0d38 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) { 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_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/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..31dd5aa 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -155,6 +155,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 +180,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 +204,6 @@ 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), }; static int nrf5_bank_is_probed(struct flash_bank *bank) 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/psoc6.c b/src/flash/nor/psoc6.c new file mode 100644 index 0000000..e5c4197 --- /dev/null +++ b/src/flash/nor/psoc6.c @@ -0,0 +1,986 @@ +/*************************************************************************** + * * + * Copyright (C) 2017 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 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; +/************************************************************************************************** + * Initializes timeout_s structure with given timeout in milliseconds + *************************************************************************************************/ +static void timeout_init(struct timeout *to, long timeout_ms) +{ + to->start_time = timeval_ms(); + to->timeout_ms = timeout_ms; +} + +/************************************************************************************************** + * Returns true if given timeout_s object has expired + *************************************************************************************************/ +static bool timeout_expired(struct timeout *to) +{ + return (timeval_ms() - to->start_time) > to->timeout_ms; +} + +/************************************************************************************************** + * Prepares PSoC6 for running pseudo flash algorithm. This function allocates Working Area for + * the algorithm and for CPU Stack. + *************************************************************************************************/ +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; + + /* 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; + + /* 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) + goto exit_free_wa; + + return ERROR_OK; + +exit_free_wa: + /* Something went wrong, free allocated area */ + if (g_stack_area) { + target_free_working_area(target, g_stack_area); + g_stack_area = NULL; + } + + return hr; +} + +/************************************************************************************************** + * Releases working area + *************************************************************************************************/ +static int sromalgo_release(struct target *target) +{ + int hr = ERROR_OK; + + /* Free Stack/Flash algorithm working area */ + if (g_stack_area) { + hr = target_free_working_area(target, g_stack_area); + g_stack_area = NULL; + } + + return hr; +} + +/************************************************************************************************** + * Runs pseudo flash algorithm. Algorithm itself consist of couple of NOPs followed by BKPT + * instruction. The trick here is that NMI has already been posted to CM0 via IPC structure + * prior to calling this function. CM0 will immediately jump to NMI handler and execute + * SROM API code. + * This approach is borrowed from PSoC4 Flash Driver. + *************************************************************************************************/ +static int sromalgo_run(struct target *target) +{ + int hr; + + struct armv7m_algorithm armv7m_info; + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + 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); + + /* mov r8, r8; mov r8, r8 */ + hr = target_write_u32(target, g_stack_area->address + 0, 0x46C046C0); + if (hr != ERROR_OK) + return hr; + + /* mov r8, r8; bkpt #0 */ + hr = target_write_u32(target, g_stack_area->address + 4, 0xBE0046C0); + if (hr != ERROR_OK) + return hr; + + hr = target_run_algorithm(target, 0, NULL, 1, ®_params, g_stack_area->address, + 0, SROMAPI_CALL_TIMEOUT_MS, &armv7m_info); + + destroy_reg_param(®_params); + + return hr; +} + +/************************************************************************************************** + * 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 + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * 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. + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Invokes SROM API functions which are responsible for Flash operations + *************************************************************************************************/ +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; + + hr = sromalgo_run(target); + 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; +} + +/************************************************************************************************** + * Retrieves SiliconID and Protection status of the target device + *************************************************************************************************/ +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) + return hr; + + /* Read FamilyID and Revision */ + hr = call_sromapi(target, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev); + if (hr != ERROR_OK) + return hr; + + /* Read SiliconID and Protection */ + hr = call_sromapi(target, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot); + if (hr != ERROR_OK) + return hr; + + *si_id = (siid_prot & 0x0000FFFF) << 16; + *si_id |= (family_rev & 0x00FF0000) >> 8; + *si_id |= (family_rev & 0x000000FF) >> 0; + + *protection = (siid_prot & 0x000F0000) >> 0x10; + + hr = sromalgo_release(target); + return hr; +} + +/************************************************************************************************** + * Translates Protection status to openocd-friendly boolean value + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Life Cycle transition is not currently supported + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Translates Protection status to string + *************************************************************************************************/ +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; + } +} + +/************************************************************************************************** + * Displays human-readable information about acquired device + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Returns true if flash bank name represents 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; +} + +/************************************************************************************************** + * Returns true if flash bank name represents Work Flash + *************************************************************************************************/ +static inline bool is_wflash_bank(struct flash_bank *bank) +{ + return (bank->base == MEM_BASE_WFLASH); +} + +/************************************************************************************************** + * Returns true if flash bank name represents Main Flash + *************************************************************************************************/ +static inline bool is_mflash_bank(struct flash_bank *bank) +{ + return (bank->base == MEM_BASE_MFLASH); +} + +/************************************************************************************************** + * 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) + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Probes target device only if it hasn't been probed yet + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Erases single sector (256k) on target device + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Erases single row (512b) on target device + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * Performs Erase operation. + * Function will try to use biggest erase block possible to speedup the operation + *************************************************************************************************/ +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) + return hr; + + 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; +} + + +/************************************************************************************************** + * Programs single Flash Row + *************************************************************************************************/ +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; +} + + +/************************************************************************************************** + * Programs set of Rows + *************************************************************************************************/ +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; + + hr = sromalgo_prepare(target); + if (hr != ERROR_OK) + return hr; + + uint8_t page_buf[psoc6_info->row_sz]; + + 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); + break; + } + + buffer += row_bytes; + offset += row_bytes; + count -= row_bytes; + } + + hr = sromalgo_release(target); + return hr; +} + +/************************************************************************************************** + * Performs Mass Erase of given flash bank + * Syntax: psoc6 mass_erase bank_id + *************************************************************************************************/ +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; +} + +/************************************************************************************************** + * 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. + *************************************************************************************************/ +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); + + 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); + hr = mem_ap_write_atomic_u32(cm->debug_ap, + NVIC_AIRCR, + AIRCR_VECTKEY | AIRCR_SYSRESETREQ); + + /* Wait for bootcode and initialize DAP */ + usleep(3000); + dap_dp_init(cm->debug_ap->dap); + } else { + LOG_INFO("psoc6.cm4: bkpt @0x%08X, issuing VECTRESET", reset_addr); + hr = mem_ap_write_atomic_u32(cm->debug_ap, + NVIC_AIRCR, + AIRCR_VECTKEY | AIRCR_VECTRESET); + if (hr != ERROR_OK) + return hr; + } + + target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS); + + /* Remove the break point */ + breakpoint_remove(target, reset_addr); + + return hr; +} + +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 = NULL, + .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/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..64c9168 100644 --- a/src/flash/nor/stm32f1x.c +++ b/src/flash/nor/stm32f1x.c @@ -1647,4 +1647,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..8bca62e 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -1634,4 +1634,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..a15cd25 100644 --- a/src/flash/nor/stm32h7x.c +++ b/src/flash/nor/stm32h7x.c @@ -1180,4 +1180,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..e2710bd 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -953,4 +953,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..f4dd686 100644 --- a/src/flash/nor/stm32lx.c +++ b/src/flash/nor/stm32lx.c @@ -965,6 +965,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/tms470.c b/src/flash/nor/tms470.c index a70891e..102bf1b 100644 --- a/src/flash/nor/tms470.c +++ b/src/flash/nor/tms470.c @@ -1186,4 +1186,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..d5d688b 100644 --- a/src/flash/nor/virtual.c +++ b/src/flash/nor/virtual.c @@ -231,4 +231,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/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/core.c b/src/jtag/core.c index 8c79eb2..4522321 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -1308,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); @@ -1472,13 +1480,21 @@ 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); + } + + struct jtag_tap *t = jtag_all_taps(); + while (t) { + struct jtag_tap *n = t->next_tap; + jtag_tap_free(t); + t = n; + } - /* close the JTAG interface */ - int result = jtag->quit(); - if (ERROR_OK != result) - LOG_ERROR("failed: %d", result); + dap_cleanup_all(); return ERROR_OK; } diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 3e5974d..b3b4d21 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 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/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/kitprog.c b/src/jtag/drivers/kitprog.c index db5b62e..522eb17 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) 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..99f96b9 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: diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index 62a8f59..cb9ef39 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; } 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/interfaces.c b/src/jtag/interfaces.c index 174c63a..ddeadb4 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 @@ -159,6 +162,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 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/openocd.c b/src/openocd.c index 831bd17..902528d 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,18 @@ 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); + 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..a46f7a4 100644 --- a/src/rtos/ChibiOS.c +++ b/src/rtos/ChibiOS.c @@ -247,7 +247,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/rtos.c b/src/rtos/rtos.c index a37f44a..4f23040 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -59,6 +59,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 +84,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 +349,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/server/gdb_server.c b/src/server/gdb_server.c index 4a54803..ca9da15 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" @@ -731,7 +733,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); @@ -763,12 +764,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:%016" 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); @@ -956,9 +964,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); @@ -1922,6 +1935,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: @@ -1934,6 +1949,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: @@ -1961,12 +1978,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", @@ -1974,6 +2024,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> */ @@ -1981,7 +2045,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, @@ -2007,13 +2070,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> */ @@ -2044,8 +2118,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; } @@ -2106,11 +2181,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); @@ -2163,8 +2242,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 { @@ -2214,6 +2298,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; @@ -2390,7 +2475,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; @@ -2498,7 +2587,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) ? '+' : '-'); @@ -2587,6 +2676,185 @@ 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') { + 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') { + 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) { @@ -2600,6 +2868,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) { @@ -3039,7 +3320,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; @@ -3385,3 +3671,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..1366c76 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); diff --git a/src/server/server.c b/src/server/server.c index 46c860f..4e80656 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -259,7 +259,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 +345,21 @@ 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; + } +} + static int remove_services(void) { struct service *c = services; @@ -353,6 +368,8 @@ static int remove_services(void) while (c) { struct service *next = c->next; + remove_connections(c); + if (c->name) free(c->name); @@ -624,6 +641,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 diff --git a/src/server/server.h b/src/server/server.h index 68ad16d..d4eae94 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 @@ -78,6 +82,7 @@ int add_service(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..030015a 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -89,6 +89,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 \ diff --git a/src/target/aarch64.c b/src/target/aarch64.c index 14a2da6..4641a3f 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -40,6 +40,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 +457,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); } @@ -1086,7 +1091,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 @@ -1861,7 +1866,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 +2085,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 +2203,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 +2211,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 +2290,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; @@ -2356,18 +2355,13 @@ static int aarch64_init_target(struct command_context *cmd_ctx, } 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 +2377,13 @@ 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 int aarch64_mmu(struct target *target, int *enabled) @@ -2405,6 +2403,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); @@ -2570,6 +2656,7 @@ 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, .examine = aarch64_examine, diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c index c7dc4f7..dc02379 100644 --- a/src/target/adi_v5_jtag.c +++ b/src/target/adi_v5_jtag.c @@ -574,6 +574,7 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) if ((ctrlstat & (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) != (CDBGPWRUPREQ | CDBGPWRUPACK | CSYSPWRUPREQ | CSYSPWRUPACK)) { LOG_ERROR("Debug regions are unpowered, an unexpected reset might have happened"); + dap->do_reconnect = true; } if (ctrlstat & SSTICKYERR) @@ -598,6 +599,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 +648,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 +666,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 +715,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..0de272d 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) @@ -279,6 +277,7 @@ static int swd_run(struct adiv5_dap *dap) } 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, @@ -381,15 +380,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 +403,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..62fbb73 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -77,6 +77,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 +126,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 /** @@ -145,6 +190,9 @@ struct arm { /** Flag reporting whether semihosting fileio operation is active. */ bool semihosting_hit_fileio; + /** Floating point or VFP version, 0 if disabled. */ + int arm_vfp_version; + /** Current semihosting operation. */ int semihosting_op; @@ -225,7 +273,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); diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index dfbc5ad..2b7d7b2 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 */ @@ -614,34 +615,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 +643,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); @@ -1376,7 +1345,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 +1403,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 +1548,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 +1585,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,10 +1608,7 @@ 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; - + struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA); uint32_t apsel, apid; int retval; @@ -1598,11 +1643,9 @@ COMMAND_HANDLER(dap_apsel_command) 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 sprot = 0; switch (CMD_ARGC) { case 0: @@ -1631,10 +1674,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,10 +1706,7 @@ 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; int retval; @@ -1705,10 +1742,7 @@ COMMAND_HANDLER(dap_apreg_command) COMMAND_HANDLER(dap_ti_be_32_quirks_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 enable = dap->ti_be_32_quirks; switch (CMD_ARGC) { @@ -1729,7 +1763,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, @@ -1795,14 +1829,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..aa5fa42 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -254,6 +254,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); @@ -473,9 +475,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 +508,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..547b961 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,396 @@ 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; +} + +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..181f064 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); @@ -70,4 +74,6 @@ 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 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..692feb3 --- /dev/null +++ b/src/target/arm_dap.c @@ -0,0 +1,366 @@ +/*************************************************************************** + * 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); + } + 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; + + list_for_each_entry_safe(obj, tmp, &all_dap, lh) { + free(obj->name); + free(obj); + } + + return ERROR_OK; +} + +enum dap_cfg_param { + CFG_CHAIN_POSITION, +}; + +static const Jim_Nvp nvp_config_opts[] = { + { .name = "-chain-position", .value = CFG_CHAIN_POSITION }, + { .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; + } + 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..8e783d3 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -2978,6 +2978,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..6579099 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; @@ -540,6 +628,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 +650,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 +694,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/armv4_5.c b/src/target/armv4_5.c index 48050b0..a6fadaa 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -340,6 +340,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 +611,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 +630,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 +651,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 +703,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; } @@ -1229,6 +1310,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 +1334,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; diff --git a/src/target/armv7a.c b/src/target/armv7a.c index db72afd..fab7363 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -129,9 +129,13 @@ static 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 */ @@ -740,6 +729,8 @@ int armv7a_arch_state(struct target *target) arm_arch_state(target); + armv7a_read_ttbcr(target); + if (armv7a->is_armv7r) { LOG_USER("D-Cache: %s, I-Cache: %s", state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled], @@ -786,9 +777,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..33f6f5d 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]; diff --git a/src/target/armv7a_cache.c b/src/target/armv7a_cache.c index 7af3e6d..3e5f8d6 100644 --- a/src/target/armv7a_cache.c +++ b/src/target/armv7a_cache.c @@ -148,10 +148,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); } diff --git a/src/target/armv7m.c b/src/target/armv7m.c index e0911c3..a8ddfe8 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -843,8 +843,5 @@ const struct command_registration armv7m_command_handlers[] = { { .chain = arm_command_handlers, }, - { - .chain = dap_command_handlers, - }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/armv8.c b/src/target/armv8.c index 1b8e450..3321dd6 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -135,6 +135,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 +194,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 +251,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 +314,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 +408,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 +424,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 +542,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 +556,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 +620,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 +628,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 +650,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; } @@ -897,6 +1085,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 +1239,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 +1344,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 +1416,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 +1481,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; } @@ -1122,11 +1547,15 @@ struct reg_cache *armv8_build_reg_cache(struct target *target) } else 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 - LOG_ERROR("unable to allocate reg type list"); + if (armv8_regs[i].data_type == NULL) { + 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 + LOG_ERROR("unable to allocate reg type list"); + } else + reg_list[i].reg_data_type = armv8_regs[i].data_type; + } arm->cpsr = reg_list + ARMV8_xPSR; @@ -1142,7 +1571,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; @@ -1180,13 +1609,9 @@ struct reg *armv8_reg_current(struct arm *arm, unsigned regnum) } 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 +1625,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 +1652,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..6525d26 100644 --- a/src/target/armv8.h +++ b/src/target/armv8.h @@ -63,17 +63,52 @@ 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, }; @@ -179,6 +214,12 @@ struct armv8_common { 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); 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/cortex_a.c b/src/target/cortex_a.c index 5d90e34..8985051 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 */ @@ -1496,10 +1500,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 +2947,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 +3128,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 +3160,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 +3198,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); } @@ -3466,6 +3483,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 +3496,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 +3517,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 +3559,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..a356350 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -1854,6 +1854,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 }; @@ -1887,6 +1899,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 +2005,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); @@ -2228,25 +2235,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 +2265,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); - - 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; + struct adiv5_private_config *pc; + + pc = (struct adiv5_private_config *)target->private_config; + if (adiv5_verify_config(pc) != ERROR_OK) + return ERROR_FAIL; + + cortex_m->apsel = pc->ap_num; + + cortex_m_init_arch_info(target, cortex_m, pc->dap); return ERROR_OK; } 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..9f56bea 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; + } } /** 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/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/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 b450d2b..4322f7b 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -455,12 +455,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 +680,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); diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 4d23cd6..1869038 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); @@ -253,9 +253,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; @@ -284,8 +283,11 @@ static void decode_dmi(char *text, unsigned address, unsigned data) { DMI_DMCONTROL, ((1L<<10)-1) << DMI_DMCONTROL_HARTSEL_OFFSET, "hartsel" }, { 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" }, @@ -483,58 +485,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; } @@ -542,60 +565,38 @@ 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 " @@ -605,6 +606,13 @@ int dmstatus_read(struct target *target, uint32_t *dmstatus, 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); @@ -1163,10 +1171,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); @@ -1392,7 +1399,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) @@ -1401,6 +1409,11 @@ 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_field(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, + hartsel_mask(target), 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); @@ -1571,7 +1584,7 @@ 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)) @@ -1592,20 +1605,8 @@ static int assert_reset(struct target *target) 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; @@ -1619,57 +1620,77 @@ 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_field(control, hartsel_mask(target), 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; - if (time(NULL) - start > riscv_reset_timeout_sec) { - LOG_ERROR("Hart didn't halt 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; - } - target->state = TARGET_HALTED; - } while (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0); - - control = set_field(control, DMI_DMCONTROL_HALTREQ, 0); - dmi_write(target, DMI_DMCONTROL, control); + 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_field(control, hartsel_mask(target), index)); + } else { + index = r->current_hartid; + } - } 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); + char *operation; + uint32_t expected_field; + uint32_t unexpected_field; + if (target->reset_halt) { + operation = "halt"; + expected_field = DMI_DMSTATUS_ALLHALTED; + unexpected_field = DMI_DMSTATUS_ANYRUNNING; + } else { + operation = "run"; + expected_field = DMI_DMSTATUS_ALLRUNNING; + unexpected_field = DMI_DMSTATUS_ANYHALTED; + } + 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, unexpected_field)) { + LOG_ERROR("Unexpected hart %d status during reset. dmstatus=0x%x", + index, dmstatus); return ERROR_FAIL; } + if (get_field(dmstatus, expected_field)) + break; if (time(NULL) - start > riscv_reset_timeout_sec) { - LOG_ERROR("Hart didn't run 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; } - } while (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING) == 0); - target->state = TARGET_RUNNING; + } + target->state = TARGET_HALTED; + + if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) { + /* Ack reset. */ + dmi_write(target, DMI_DMCONTROL, + set_field(control, hartsel_mask(target), index) | + DMI_DMCONTROL_ACKHAVERESET); + } + + if (!target->rtos) + break; } info->dmi_busy_delay = dmi_busy_delay; return ERROR_OK; @@ -1798,19 +1819,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); } @@ -1826,8 +1844,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; } } @@ -1955,6 +1973,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); @@ -2393,6 +2412,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); @@ -2697,23 +2717,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) { - LOG_DEBUG(">>> nothing to be done"); - return; + return ERROR_OK; } - LOG_DEBUG(">>> change it"); - uint32_t dmcontrol; - dmi_read(target, &dmcontrol, DMI_DMCONTROL); + if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) + return ERROR_FAIL; dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid); - dmi_write(target, DMI_DMCONTROL, dmcontrol); + 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) @@ -2783,9 +2802,25 @@ 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_field(dmcontrol, hartsel_mask(target), 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); } @@ -2914,11 +2949,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_field(dmcontrol, hartsel_mask(target), r->current_hartid); + dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ); uint32_t dmstatus; for (size_t i = 0; i < 256; ++i) { @@ -2930,17 +2963,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) { @@ -2965,7 +2997,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 aea6d7a..bd09ef7 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -741,19 +741,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); } @@ -761,7 +762,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); } @@ -779,7 +781,8 @@ 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: @@ -968,50 +971,61 @@ int riscv_blank_check_memory(struct target *target, /*** 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 @@ -1022,15 +1036,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; @@ -1046,11 +1064,13 @@ 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; } target->state = TARGET_HALTED; @@ -1105,16 +1125,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; @@ -1217,7 +1235,7 @@ COMMAND_HANDLER(riscv_set_scratch_ram) return ERROR_OK; } - // TODO: use COMMAND_PARSE_NUMBER + /* 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])) { @@ -1584,7 +1602,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; @@ -1610,7 +1629,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; @@ -1631,7 +1651,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)) { @@ -1681,33 +1702,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_FAIL; 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) @@ -1797,7 +1820,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; @@ -2357,6 +2381,72 @@ int riscv_init_registers(struct target *target) 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; } if (!r->exist && expose_csr) { diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 8e06bd5..63a3b79 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -31,7 +31,8 @@ enum riscv_halt_reason { RISCV_HALT_BREAKPOINT, RISCV_HALT_SINGLESTEP, RISCV_HALT_TRIGGER, - RISCV_HALT_UNKNOWN + RISCV_HALT_UNKNOWN, + RISCV_HALT_ERROR }; typedef struct { @@ -92,7 +93,7 @@ typedef struct { 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); @@ -192,7 +193,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 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..4556fd9 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)) { diff --git a/src/target/target.c b/src/target/target.c index ce7782e..482dc74 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,26 @@ static void target_destroy(struct target *target) if (target->type->deinit_target) target->type->deinit_target(target); + 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); + } + free(target->type); free(target->trace_info); + free(target->fileio_info); free(target->cmd_name); free(target); } @@ -2475,7 +2528,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 +2559,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 +2976,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; @@ -4424,17 +4483,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 +4534,6 @@ enum target_cfg_param { TCFG_COREID, TCFG_CHAIN_POSITION, TCFG_DBGBASE, - TCFG_CTIBASE, TCFG_RTOS, TCFG_DEFER_EXAMINE, }; @@ -4480,7 +4549,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 +4785,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 +4799,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 +4822,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 */ { @@ -5512,7 +5573,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 +5599,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 +5618,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 +5649,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 */ { diff --git a/src/target/target.h b/src/target/target.h index 0096cae..7a8a80f 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 */ diff --git a/src/transport/transport.h b/src/transport/transport.h index 6c57067..d0a77dd 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,19 @@ 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); + +/* FIXME: ZY1000 test build on jenkins is configured with enabled hla adapters + * but jtag/hla/hla_*.c files are not compiled. To workaround the problem we assume hla + * is broken if BUILD_ZY1000 is set */ +#if BUILD_HLADAPTER && !BUILD_ZY1000 +bool transport_is_hla(void); +#else +static inline bool transport_is_hla(void) +{ + return false; +} +#endif + #endif /* OPENOCD_TRANSPORT_TRANSPORT_H */ |