INCLUDES = -I$(top_srcdir)/src/helper -I$(top_srcdir)/src/jtag -I$(top_srcdir)/src/target $(all_includes)
METASOURCES = AUTO
noinst_LIBRARIES = libflash.a
-libflash_a_SOURCES = flash.c lpc2000.c cfi.c at91sam7.c str7x.c str9x.c nand.c lpc3180_nand_controller.c
-noinst_HEADERS = flash.h lpc2000.h cfi.h at91sam7.h str7x.h str9x.h nand.h lpc3180_nand_controller.h
+libflash_a_SOURCES = flash.c lpc2000.c cfi.c non_cfi.c at91sam7.c str7x.c str9x.c nand.c lpc3180_nand_controller.c
+noinst_HEADERS = flash.h lpc2000.h cfi.h non_cfi.h at91sam7.h str7x.h str9x.h nand.h lpc3180_nand_controller.h
.info = cfi_info
};
+cfi_unlock_addresses_t cfi_unlock_addresses[] =
+{
+ [CFI_UNLOCK_555_2AA] = { .unlock1 = 0x555, .unlock2 = 0x2aa },
+ [CFI_UNLOCK_5555_2AAA] = { .unlock1 = 0x5555, .unlock2 = 0x2aaa },
+};
+
/* CFI fixups foward declarations */
+void cfi_fixup_non_cfi(flash_bank_t *flash, void *param);
void cfi_fixup_0002_erase_regions(flash_bank_t *flash, void *param);
+void cfi_fixup_0002_unlock_addresses(flash_bank_t *flash, void *param);
void cfi_fixup_atmel_reversed_erase_regions(flash_bank_t *flash, void *param);
/* fixup after identifying JEDEC manufactuer and ID */
cfi_fixup_t cfi_jedec_fixups[] = {
+ {CFI_MFR_SST, 0x00D4, cfi_fixup_non_cfi, NULL},
+ {CFI_MFR_SST, 0x00D5, cfi_fixup_non_cfi, NULL},
+ {CFI_MFR_SST, 0x00D6, cfi_fixup_non_cfi, NULL},
+ {CFI_MFR_SST, 0x00D7, cfi_fixup_non_cfi, NULL},
{0, 0, NULL, NULL}
};
/* fixup after reading cmdset 0002 primary query table */
cfi_fixup_t cfi_0002_fixups[] = {
+ {CFI_MFR_SST, 0x00D4, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
+ {CFI_MFR_SST, 0x00D5, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
+ {CFI_MFR_SST, 0x00D6, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
+ {CFI_MFR_SST, 0x00D7, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
{CFI_MFR_ATMEL, 0x00C8, cfi_fixup_atmel_reversed_erase_regions, NULL},
{CFI_MFR_ANY, CFI_ID_ANY, cfi_fixup_0002_erase_regions, NULL},
{0, 0, NULL, NULL}
DEBUG("WP# protection 0x%x", pri_ext->TopBottom);
+ /* default values for implementation specific workarounds */
+ pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1;
+ pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2;
+ pri_ext->_reversed_geometry = 0;
+
return ERROR_OK;
}
cfi_info = malloc(sizeof(cfi_flash_bank_t));
bank->driver_priv = cfi_info;
- cfi_info->x16_as_x8 = 1;
+ cfi_info->x16_as_x8 = 0;
+ cfi_info->jedec_probe = 0;
+ cfi_info->not_cfi = 0;
cfi_info->target = get_target_by_num(strtoul(args[5], NULL, 0));
if (!cfi_info->target)
for (i = 6; i < argc; i++)
{
- if (strcmp(args[i], "x16_as_x8") != 0)
+ if (strcmp(args[i], "x16_as_x8") == 0)
{
- cfi_info->x16_as_x8 = 0;
+ cfi_info->x16_as_x8 = 1;
+ }
+ else if (strcmp(args[i], "jedec_probe") == 0)
+ {
+ cfi_info->jedec_probe = 1;
}
}
for (i = first; i <= last; i++)
{
cfi_command(bank, 0xaa, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
cfi_command(bank, 0x55, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x2aa), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command);
cfi_command(bank, 0x80, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
cfi_command(bank, 0xaa, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
cfi_command(bank, 0x55, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x2aa), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command);
cfi_command(bank, 0x30, command);
target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command);
armv4_5_algorithm_t armv4_5_info;
working_area_t *source;
u32 buffer_size = 32768;
- u8 write_command[CFI_MAX_BUS_WIDTH];
- u8 busy_pattern[CFI_MAX_BUS_WIDTH];
- u8 error_pattern[CFI_MAX_BUS_WIDTH];
+ u8 write_command_buf[CFI_MAX_BUS_WIDTH];
+ u8 busy_pattern_buf[CFI_MAX_BUS_WIDTH];
+ u8 error_pattern_buf[CFI_MAX_BUS_WIDTH];
+ u32 write_command_val, busy_pattern_val, error_pattern_val;
int retval;
/* algorithm register usage:
* r6: error test pattern
*/
- u32 word_32_code[] = {
+ static const u32 word_32_code[] = {
0xe4904004, /* loop: ldr r4, [r0], #4 */
0xe5813000, /* str r3, [r1] */
0xe5814000, /* str r4, [r1] */
0xeafffffe, /* done: b -2 */
};
- u32 word_16_code[] = {
+ static const u32 word_16_code[] = {
0xe0d040b2, /* loop: ldrh r4, [r0], #2 */
0xe1c130b0, /* strh r3, [r1] */
0xe1c140b0, /* strh r4, [r1] */
0xeafffffe, /* done: b -2 */
};
- u32 word_8_code[] = {
+ static const u32 word_8_code[] = {
0xe4d04001, /* loop: ldrb r4, [r0], #1 */
0xe5c13000, /* strb r3, [r1] */
0xe5c14000, /* strb r4, [r1] */
/* flash write code */
if (!cfi_info->write_algorithm)
{
+ u8 write_code_buf[14 * 4];
+ int i;
+
if (target_alloc_working_area(target, 4 * 14, &cfi_info->write_algorithm) != ERROR_OK)
{
WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
-
+
/* write algorithm code to working area */
if (bank->bus_width == 1)
{
- target_write_buffer(target, cfi_info->write_algorithm->address, 14 * 4, (u8*)word_8_code);
+ for (i = 0; i < 14; i++)
+ target_buffer_set_u32(target, write_code_buf + (i*4), word_8_code[i]);
}
else if (bank->bus_width == 2)
{
- target_write_buffer(target, cfi_info->write_algorithm->address, 14 * 4, (u8*)word_16_code);
+ for (i = 0; i < 14; i++)
+ target_buffer_set_u32(target, write_code_buf + (i*4), word_16_code[i]);
}
else if (bank->bus_width == 4)
{
- target_write_buffer(target, cfi_info->write_algorithm->address, 14 * 4, (u8*)word_32_code);
+ for (i = 0; i < 14; i++)
+ target_buffer_set_u32(target, write_code_buf + (i*4), word_32_code[i]);
}
else
{
return ERROR_FLASH_OPERATION_FAILED;
}
+
+ target_write_buffer(target, cfi_info->write_algorithm->address, 14 * 4, write_code_buf);
}
while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
init_reg_param(®_params[5], "r5", 32, PARAM_OUT);
init_reg_param(®_params[6], "r6", 32, PARAM_OUT);
- cfi_command(bank, 0x40, write_command);
- cfi_command(bank, 0x80, busy_pattern);
- cfi_command(bank, 0x7e, error_pattern);
-
+ /* prepare command and status register patterns */
+ cfi_command(bank, 0x40, write_command_buf);
+ cfi_command(bank, 0x80, busy_pattern_buf);
+ cfi_command(bank, 0x7e, error_pattern_buf);
+
+ if (bank->bus_width == 1)
+ {
+ write_command_val = write_command_buf[0];
+ busy_pattern_val = busy_pattern_buf[0];
+ error_pattern_val = error_pattern_buf[0];
+ }
+ else if (bank->bus_width == 2)
+ {
+ write_command_val = target_buffer_get_u16(target, write_command_buf);
+ busy_pattern_val = target_buffer_get_u16(target, busy_pattern_buf);
+ error_pattern_val = target_buffer_get_u16(target, error_pattern_buf);
+ }
+ else if (bank->bus_width == 4)
+ {
+ write_command_val = target_buffer_get_u32(target, write_command_buf);
+ busy_pattern_val = target_buffer_get_u32(target, busy_pattern_buf);
+ error_pattern_val = target_buffer_get_u32(target, error_pattern_buf);
+ }
+
while (count > 0)
{
u32 thisrun_count = (count > buffer_size) ? buffer_size : count;
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, address);
buf_set_u32(reg_params[2].value, 0, 32, thisrun_count / bank->bus_width);
- buf_set_u32(reg_params[3].value, 0, 32, target_buffer_get_u32(target, write_command));
- buf_set_u32(reg_params[5].value, 0, 32, target_buffer_get_u32(target, busy_pattern));
- buf_set_u32(reg_params[6].value, 0, 32, target_buffer_get_u32(target, error_pattern));
- buf_set_u32(reg_params[5].value, 0, 32, buf_get_u32(busy_pattern, 0, 32));
- buf_set_u32(reg_params[6].value, 0, 32, buf_get_u32(error_pattern, 0, 32));
+ buf_set_u32(reg_params[3].value, 0, 32, write_command_val);
+ buf_set_u32(reg_params[5].value, 0, 32, busy_pattern_val);
+ buf_set_u32(reg_params[6].value, 0, 32, error_pattern_val);
if ((retval = target->type->run_algorithm(target, 0, NULL, 7, reg_params, cfi_info->write_algorithm->address, cfi_info->write_algorithm->address + (13 * 4), 10000, &armv4_5_info)) != ERROR_OK)
{
return ERROR_FLASH_OPERATION_FAILED;
}
- if (buf_get_u32(reg_params[4].value, 0, 32) & target_buffer_get_u32(target, error_pattern))
+ if (buf_get_u32(reg_params[4].value, 0, 32) & error_pattern_val)
{
/* read status register (outputs debug inforation) */
cfi_intel_wait_status_busy(bank, 100);
int i;
int retval;
int exit_code = ERROR_OK;
- int code_size;
- void *code_p;
/* input parameters - */
/* R0 = source address */
/* unlock registers - */
/* R8 = unlock1_addr */
/* R9 = unlock1_cmd */
- /* R10 = unlock1_addr */
- /* R11 = unlock1_cmd */
+ /* R10 = unlock2_addr */
+ /* R11 = unlock2_cmd */
u32 word_32_code[] = {
/* 00008100 <sp_32_code>: */
/* flash write code */
if (!cfi_info->write_algorithm)
{
- /* write algorithm code to working area */
+ u8 *code_p;
+
+ /* convert bus-width dependent algorithm code to correct endiannes */
if (bank->bus_width == 1)
{
- code_size = sizeof(word_8_code);
- code_p = word_8_code;
+ code_p = malloc(24 * 4);
+
+ for (i = 0; i < 24; i++)
+ target_buffer_set_u32(target, code_p + (i*4), word_8_code[i]);
}
else if (bank->bus_width == 2)
{
- code_size = sizeof(word_16_code);
- code_p = word_16_code;
- }
+ code_p = malloc(24 * 4);
+
+ for (i = 0; i < 24; i++)
+ target_buffer_set_u32(target, code_p + (i*4), word_16_code[i]);
+ }
else if (bank->bus_width == 4)
{
- code_size = sizeof(word_32_code);
- code_p = word_32_code;
+ code_p = malloc(24 * 4);
+
+ for (i = 0; i < 24; i++)
+ target_buffer_set_u32(target, code_p + (i*4), word_32_code[i]);
}
else
{
return ERROR_FLASH_OPERATION_FAILED;
}
-
- if (target_alloc_working_area(target, code_size,
- &cfi_info->write_algorithm) != ERROR_OK)
+
+ /* allocate working area */
+ if (target_alloc_working_area(target, 24 * 4,
+ &cfi_info->write_algorithm) != ERROR_OK)
{
WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
-
- target_write_buffer(target, cfi_info->write_algorithm->address,
- code_size, code_p);
+
+ /* write algorithm code to working area */
+ target_write_buffer(target, cfi_info->write_algorithm->address, 24 * 4, code_p);
+
+ free(code_p);
}
while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
buf_set_u32(reg_params[3].value, 0, 32, buf_get_u32(write_command, 0, 32));
cfi_command(bank, 0x80, write_command);
buf_set_u32(reg_params[4].value, 0, 32, buf_get_u32(write_command, 0, 32));
- buf_set_u32(reg_params[6].value, 0, 32, flash_address(bank, 0, 0x555));
+ buf_set_u32(reg_params[6].value, 0, 32, flash_address(bank, 0, pri_ext->_unlock1));
buf_set_u32(reg_params[7].value, 0, 32, 0xaa);
- buf_set_u32(reg_params[8].value, 0, 32, flash_address(bank, 0, 0xaaa));
+ buf_set_u32(reg_params[8].value, 0, 32, flash_address(bank, 0, pri_ext->_unlock2));
buf_set_u32(reg_params[9].value, 0, 32, 0x55);
retval = target->type->run_algorithm(target, 0, NULL, 10, reg_params,
cfi_info->write_algorithm->address,
- cfi_info->write_algorithm->address + (code_size - 4),
+ cfi_info->write_algorithm->address + ((24 * 4) - 4),
10000, &armv4_5_info);
status = buf_get_u32(reg_params[5].value, 0, 32);
count -= thisrun_count;
}
+ target_free_working_area(target, source);
+
destroy_reg_param(®_params[0]);
destroy_reg_param(®_params[1]);
destroy_reg_param(®_params[2]);
u8 command[8];
cfi_command(bank, 0xaa, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
cfi_command(bank, 0x55, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x2aa), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command);
cfi_command(bank, 0xa0, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
target->type->write_memory(target, address, bank->bus_width, 1, word);
}
}
+void cfi_fixup_0002_unlock_addresses(flash_bank_t *bank, void *param)
+{
+ cfi_flash_bank_t *cfi_info = bank->driver_priv;
+ cfi_spansion_pri_ext_t *pri_ext = cfi_info->pri_ext;
+ cfi_unlock_addresses_t *unlock_addresses = param;
+
+ pri_ext->_unlock1 = unlock_addresses->unlock1;
+ pri_ext->_unlock2 = unlock_addresses->unlock2;
+}
+
int cfi_probe(struct flash_bank_s *bank)
{
cfi_flash_bank_t *cfi_info = bank->driver_priv;
int i;
int sector = 0;
u32 offset = 0;
-
+ u32 unlock1 = 0x555;
+ u32 unlock2 = 0x2aa;
+
+ /* JEDEC standard JESD21C uses 0x5555 and 0x2aaa as unlock addresses,
+ * while CFI compatible AMD/Spansion flashes use 0x555 and 0x2aa
+ */
+ if (cfi_info->jedec_probe)
+ {
+ unlock1 = 0x5555;
+ unlock2 = 0x2aaa;
+ }
+
/* switch to read identifier codes mode ("AUTOSELECT") */
cfi_command(bank, 0xaa, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, unlock1), bank->bus_width, 1, command);
cfi_command(bank, 0x55, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x2aa), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, unlock2), bank->bus_width, 1, command);
cfi_command(bank, 0x90, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, unlock1), bank->bus_width, 1, command);
if (bank->chip_width == 1)
{
cfi_fixup(bank, cfi_jedec_fixups);
- /* enter CFI query mode
- * according to JEDEC Standard No. 68.01,
- * a single bus sequence with address = 0x55, data = 0x98 should put
- * the device into CFI query mode.
- *
- * SST flashes clearly violate this, and we will consider them incompatbile for now
+ /* query only if this is a CFI compatible flash,
+ * otherwise the relevant info has already been filled in
*/
- cfi_command(bank, 0x98, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command);
-
- cfi_info->qry[0] = cfi_query_u8(bank, 0, 0x10);
- cfi_info->qry[1] = cfi_query_u8(bank, 0, 0x11);
- cfi_info->qry[2] = cfi_query_u8(bank, 0, 0x12);
-
- DEBUG("CFI qry returned: 0x%2.2x 0x%2.2x 0x%2.2x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2]);
-
- if ((cfi_info->qry[0] != 'Q') || (cfi_info->qry[1] != 'R') || (cfi_info->qry[2] != 'Y'))
+ if (cfi_info->not_cfi == 0)
{
+ /* enter CFI query mode
+ * according to JEDEC Standard No. 68.01,
+ * a single bus sequence with address = 0x55, data = 0x98 should put
+ * the device into CFI query mode.
+ *
+ * SST flashes clearly violate this, and we will consider them incompatbile for now
+ */
+ cfi_command(bank, 0x98, command);
+ target->type->write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command);
+
+ cfi_info->qry[0] = cfi_query_u8(bank, 0, 0x10);
+ cfi_info->qry[1] = cfi_query_u8(bank, 0, 0x11);
+ cfi_info->qry[2] = cfi_query_u8(bank, 0, 0x12);
+
+ DEBUG("CFI qry returned: 0x%2.2x 0x%2.2x 0x%2.2x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2]);
+
+ if ((cfi_info->qry[0] != 'Q') || (cfi_info->qry[1] != 'R') || (cfi_info->qry[2] != 'Y'))
+ {
+ cfi_command(bank, 0xf0, command);
+ target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
+ cfi_command(bank, 0xff, command);
+ target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ cfi_info->pri_id = cfi_query_u16(bank, 0, 0x13);
+ cfi_info->pri_addr = cfi_query_u16(bank, 0, 0x15);
+ cfi_info->alt_id = cfi_query_u16(bank, 0, 0x17);
+ cfi_info->alt_addr = cfi_query_u16(bank, 0, 0x19);
+
+ DEBUG("qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr);
+
+ cfi_info->vcc_min = cfi_query_u8(bank, 0, 0x1b);
+ cfi_info->vcc_max = cfi_query_u8(bank, 0, 0x1c);
+ cfi_info->vpp_min = cfi_query_u8(bank, 0, 0x1d);
+ cfi_info->vpp_max = cfi_query_u8(bank, 0, 0x1e);
+ cfi_info->word_write_timeout_typ = cfi_query_u8(bank, 0, 0x1f);
+ cfi_info->buf_write_timeout_typ = cfi_query_u8(bank, 0, 0x20);
+ cfi_info->block_erase_timeout_typ = cfi_query_u8(bank, 0, 0x21);
+ cfi_info->chip_erase_timeout_typ = cfi_query_u8(bank, 0, 0x22);
+ cfi_info->word_write_timeout_max = cfi_query_u8(bank, 0, 0x23);
+ cfi_info->buf_write_timeout_max = cfi_query_u8(bank, 0, 0x24);
+ cfi_info->block_erase_timeout_max = cfi_query_u8(bank, 0, 0x25);
+ cfi_info->chip_erase_timeout_max = cfi_query_u8(bank, 0, 0x26);
+
+ DEBUG("Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x",
+ (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f,
+ (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f,
+ (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f,
+ (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f);
+ DEBUG("typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ,
+ 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ);
+ DEBUG("max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ),
+ (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ),
+ (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ),
+ (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ));
+
+ cfi_info->dev_size = cfi_query_u8(bank, 0, 0x27);
+ cfi_info->interface_desc = cfi_query_u16(bank, 0, 0x28);
+ cfi_info->max_buf_write_size = cfi_query_u16(bank, 0, 0x2a);
+ cfi_info->num_erase_regions = cfi_query_u8(bank, 0, 0x2c);
+
+ DEBUG("size: 0x%x, interface desc: %i, max buffer write size: %x", 1 << cfi_info->dev_size, cfi_info->interface_desc, (1 << cfi_info->max_buf_write_size));
+
+ if (((1 << cfi_info->dev_size) * bank->bus_width / bank->chip_width) != bank->size)
+ {
+ WARNING("configuration specifies 0x%x size, but a 0x%x size flash was found", bank->size, 1 << cfi_info->dev_size);
+ }
+
+ if (cfi_info->num_erase_regions)
+ {
+ cfi_info->erase_region_info = malloc(4 * cfi_info->num_erase_regions);
+ for (i = 0; i < cfi_info->num_erase_regions; i++)
+ {
+ cfi_info->erase_region_info[i] = cfi_query_u32(bank, 0, 0x2d + (4 * i));
+ DEBUG("erase region[%i]: %i blocks of size 0x%x", i, (cfi_info->erase_region_info[i] & 0xffff) + 1, (cfi_info->erase_region_info[i] >> 16) * 256);
+ }
+ }
+ else
+ {
+ cfi_info->erase_region_info = NULL;
+ }
+
+ /* We need to read the primary algorithm extended query table before calculating
+ * the sector layout to be able to apply fixups
+ */
+ switch(cfi_info->pri_id)
+ {
+ /* Intel command set (standard and extended) */
+ case 0x0001:
+ case 0x0003:
+ cfi_read_intel_pri_ext(bank);
+ break;
+ /* AMD/Spansion, Atmel, ... command set */
+ case 0x0002:
+ cfi_read_0002_pri_ext(bank);
+ break;
+ default:
+ ERROR("cfi primary command set %i unsupported", cfi_info->pri_id);
+ break;
+ }
+
+ /* return to read array mode
+ * we use both reset commands, as some Intel flashes fail to recognize the 0xF0 command
+ */
cfi_command(bank, 0xf0, command);
target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
cfi_command(bank, 0xff, command);
target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
- return ERROR_FLASH_BANK_INVALID;
- }
-
- cfi_info->pri_id = cfi_query_u16(bank, 0, 0x13);
- cfi_info->pri_addr = cfi_query_u16(bank, 0, 0x15);
- cfi_info->alt_id = cfi_query_u16(bank, 0, 0x17);
- cfi_info->alt_addr = cfi_query_u16(bank, 0, 0x19);
-
- DEBUG("qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr);
-
- cfi_info->vcc_min = cfi_query_u8(bank, 0, 0x1b);
- cfi_info->vcc_max = cfi_query_u8(bank, 0, 0x1c);
- cfi_info->vpp_min = cfi_query_u8(bank, 0, 0x1d);
- cfi_info->vpp_max = cfi_query_u8(bank, 0, 0x1e);
- cfi_info->word_write_timeout_typ = cfi_query_u8(bank, 0, 0x1f);
- cfi_info->buf_write_timeout_typ = cfi_query_u8(bank, 0, 0x20);
- cfi_info->block_erase_timeout_typ = cfi_query_u8(bank, 0, 0x21);
- cfi_info->chip_erase_timeout_typ = cfi_query_u8(bank, 0, 0x22);
- cfi_info->word_write_timeout_max = cfi_query_u8(bank, 0, 0x23);
- cfi_info->buf_write_timeout_max = cfi_query_u8(bank, 0, 0x24);
- cfi_info->block_erase_timeout_max = cfi_query_u8(bank, 0, 0x25);
- cfi_info->chip_erase_timeout_max = cfi_query_u8(bank, 0, 0x26);
-
- DEBUG("Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x",
- (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f,
- (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f,
- (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f,
- (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f);
- DEBUG("typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ,
- 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ);
- DEBUG("max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ),
- (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ),
- (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ),
- (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ));
-
- cfi_info->dev_size = cfi_query_u8(bank, 0, 0x27);
- cfi_info->interface_desc = cfi_query_u16(bank, 0, 0x28);
- cfi_info->max_buf_write_size = cfi_query_u16(bank, 0, 0x2a);
- cfi_info->num_erase_regions = cfi_query_u8(bank, 0, 0x2c);
-
- DEBUG("size: 0x%x, interface desc: %i, max buffer write size: %x", 1 << cfi_info->dev_size, cfi_info->interface_desc, (1 << cfi_info->max_buf_write_size));
-
- if (((1 << cfi_info->dev_size) * bank->bus_width / bank->chip_width) != bank->size)
- {
- WARNING("configuration specifies 0x%x size, but a 0x%x size flash was found", bank->size, 1 << cfi_info->dev_size);
}
- if (cfi_info->num_erase_regions)
- {
- cfi_info->erase_region_info = malloc(4 * cfi_info->num_erase_regions);
- for (i = 0; i < cfi_info->num_erase_regions; i++)
- {
- cfi_info->erase_region_info[i] = cfi_query_u32(bank, 0, 0x2d + (4 * i));
- DEBUG("erase region[%i]: %i blocks of size 0x%x", i, (cfi_info->erase_region_info[i] & 0xffff) + 1, (cfi_info->erase_region_info[i] >> 16) * 256);
-
- num_sectors += (cfi_info->erase_region_info[i] & 0xffff) + 1;
- }
- }
- else
- {
- cfi_info->erase_region_info = NULL;
- }
-
- /* We need to read the primary algorithm extended query table before calculating
- * the sector layout to be able to apply fixups
- */
+ /* apply fixups depending on the primary command set */
switch(cfi_info->pri_id)
{
/* Intel command set (standard and extended) */
case 0x0001:
case 0x0003:
- cfi_read_intel_pri_ext(bank);
cfi_fixup(bank, cfi_0001_fixups);
break;
/* AMD/Spansion, Atmel, ... command set */
- case 0x0002:
- cfi_read_0002_pri_ext(bank);
+ case 0x0002:
cfi_fixup(bank, cfi_0002_fixups);
break;
default:
}
else
{
+ for (i = 0; i < cfi_info->num_erase_regions; i++)
+ {
+ num_sectors += (cfi_info->erase_region_info[i] & 0xffff) + 1;
+ }
+
bank->num_sectors = num_sectors;
bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);
}
}
- /* return to read array mode
- * we use both reset commands, as some Intel flashes fail to recognize the 0xF0 command
- */
- cfi_command(bank, 0xf0, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
- cfi_command(bank, 0xff, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
-
return ERROR_OK;
}
int i;
cfi_command(bank, 0xaa, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
cfi_command(bank, 0x55, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x2aa), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command);
cfi_command(bank, 0x90, command);
- target->type->write_memory(target, flash_address(bank, 0, 0x555), bank->bus_width, 1, command);
+ target->type->write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command);
for (i = 0; i < bank->num_sectors; i++)
{
working_area_t *erase_check_algorithm;
int x16_as_x8;
+ int jedec_probe;
+ int not_cfi;
u16 manufacturer;
u16 device_id;
-
+
char qry[3];
/* identification string */
u8 VppMax;
u8 TopBottom;
int _reversed_geometry;
+ u32 _unlock1;
+ u32 _unlock2;
} cfi_spansion_pri_ext_t;
/* Atmel primary extended query table as defined for and used by
u8 page_mode;
} cfi_atmel_pri_ext_t;
+enum {
+ CFI_UNLOCK_555_2AA,
+ CFI_UNLOCK_5555_2AAA,
+};
+
+typedef struct cfi_unlock_addresses_s
+{
+ u32 unlock1;
+ u32 unlock2;
+} cfi_unlock_addresses_t;
+
typedef struct cfi_fixup_s
{
u16 mfr;
#define CFI_MFR_AMD 0x0001
#define CFI_MFR_ATMEL 0x001F
#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
+#define CFI_MFR_SST 0x00BF
#define CFI_MFR_ANY 0xffff
#define CFI_ID_ANY 0xffff
#include <errno.h>
#include <fileio.h>
+#include <image.h>
/* command handlers */
int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
u8 *buffer;
u32 buf_cnt;
- fileio_t file;
- fileio_image_t image_info;
- enum fileio_sec_type sec_type;
+ image_t image;
duration_t duration;
char *duration_text;
duration_start_measure(&duration);
- fileio_identify_image_type(&sec_type, (argc == 4) ? args[3] : NULL);
+ identify_image_type(&image.type, (argc == 4) ? args[3] : NULL);
+
+ image.base_address_set = 1;
+ image.base_address = strtoul(args[1], NULL, 0);
+
+ image.start_address_set = 0;
offset = strtoul(args[2], NULL, 0);
p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
return ERROR_OK;
}
- image_info.base_address = strtoul(args[2], NULL, 0);
- image_info.has_start_address = 0;
-
- if (fileio_open(&file, args[1], FILEIO_READ,
- FILEIO_IMAGE, &image_info, sec_type) != ERROR_OK)
+ if (image_open(&image, args[1], FILEIO_READ) != ERROR_OK)
{
- command_print(cmd_ctx, "flash write error: %s", file.error_str);
+ command_print(cmd_ctx, "flash write error: %s", image.error_str);
return ERROR_OK;
}
- binary_size = file.size;
+ binary_size = image.size;
buffer = malloc(binary_size);
- fileio_read(&file, binary_size, buffer, &buf_cnt);
+ image_read(&image, binary_size, buffer, &buf_cnt);
if ((retval = p->driver->write(p, buffer, offset, buf_cnt)) != ERROR_OK)
{
{
duration_stop_measure(&duration, &duration_text);
command_print(cmd_ctx, "wrote file %s to flash bank %i at offset 0x%8.8x in %s",
- file.url, strtoul(args[0], NULL, 0), offset, duration_text);
+ args[1], strtoul(args[0], NULL, 0), offset, duration_text);
free(duration_text);
}
free(buffer);
- fileio_close(&file);
+ image_close(&image);
return ERROR_OK;
}
#include "flash.h"
#include "time_support.h"
#include "fileio.h"
+#include "image.h"
int nand_register_commands(struct command_context_s *cmd_ctx);
int handle_nand_list_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
u32 buf_cnt;
enum oob_formats oob_format = NAND_OOB_NONE;
- fileio_t file;
- fileio_image_t image_info;
- int sec_type_identified = 0;
- enum fileio_sec_type sec_type;
+ image_t image;
+ int image_type_identified = 0;
duration_t duration;
char *duration_text;
oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;
else
{
- if (fileio_identify_image_type(&sec_type, args[i]) == ERROR_OK)
+ if (identify_image_type(&image.type, args[i]) == ERROR_OK)
{
- sec_type_identified = 1;
+ image_type_identified = 1;
}
else
{
}
/* if no image type option was encountered, set the default */
- if (!sec_type_identified)
+ if (!image_type_identified)
{
- fileio_identify_image_type(&sec_type, NULL);
- sec_type_identified = 1;
+ identify_image_type(&image.type, NULL);
+ image_type_identified = 1;
}
- image_info.base_address = strtoul(args[2], NULL, 0);
- image_info.has_start_address = 0;
+ image.base_address_set = 1;
+ image.base_address = strtoul(args[2], NULL, 0);
+ image.start_address_set = 0;
- if (fileio_open(&file, args[1], FILEIO_READ,
- FILEIO_IMAGE, &image_info, sec_type) != ERROR_OK)
+ if (image_open(&image, args[1], FILEIO_READ) != ERROR_OK)
{
- command_print(cmd_ctx, "flash write error: %s", file.error_str);
+ command_print(cmd_ctx, "flash write error: %s", image.error_str);
return ERROR_OK;
}
/* the offset might have been overwritten by the image base address */
- offset = image_info.base_address;
+ offset = image.base_address;
- buf_cnt = binary_size = file.size;
+ buf_cnt = binary_size = image.size;
if (!(oob_format & NAND_OOB_ONLY))
{
if (page)
{
- fileio_read(&file, page_size, page, &size_read);
+ image_read(&image, page_size, page, &size_read);
buf_cnt -= size_read;
if (size_read < page_size)
{
if (oob)
{
- fileio_read(&file, oob_size, oob, &size_read);
+ image_read(&image, oob_size, oob, &size_read);
buf_cnt -= size_read;
if (size_read < oob_size)
{
if (nand_write_page(p, offset / p->page_size, page, page_size, oob, oob_size) != ERROR_OK)
{
command_print(cmd_ctx, "failed writing file %s to NAND flash %s at offset 0x%8.8x",
- file.url, args[0], offset);
+ args[1], args[0], offset);
return ERROR_OK;
}
offset += page_size;
duration_stop_measure(&duration, &duration_text);
command_print(cmd_ctx, "wrote file %s to NAND flash %s at offset 0x%8.8x in %s",
- file.url, args[0], image_info.base_address, duration_text);
+ args[1], args[0], image.base_address, duration_text);
free(duration_text);
}
else
{
if (p->device)
{
- fileio_t file;
- fileio_image_t image_info;
+ fileio_t fileio;
duration_t duration;
char *duration_text;
int retval;
oob_size = 64;
oob = malloc(oob_size);
}
-
- image_info.base_address = address;
- image_info.has_start_address = 0;
- if (fileio_open(&file, args[1], FILEIO_WRITE,
- FILEIO_IMAGE, &image_info, FILEIO_PLAIN) != ERROR_OK)
+ if (fileio_open(&fileio, args[1], FILEIO_WRITE, FILEIO_BINARY) != ERROR_OK)
{
- command_print(cmd_ctx, "dump_image error: %s", file.error_str);
+ command_print(cmd_ctx, "dump_image error: %s", fileio.error_str);
return ERROR_OK;
}
if (page)
{
- fileio_write(&file, page_size, page, &size_written);
+ fileio_write(&fileio, page_size, page, &size_written);
bytes_done += page_size;
}
if (oob)
{
- fileio_write(&file, oob_size, oob, &size_written);
+ fileio_write(&fileio, oob_size, oob, &size_written);
bytes_done += oob_size;
}
if (oob)
free(oob);
- fileio_close(&file);
+ fileio_close(&fileio);
duration_stop_measure(&duration, &duration_text);
- command_print(cmd_ctx, "dumped %lli byte in %s", file.size, duration_text);
+ command_print(cmd_ctx, "dumped %lli byte in %s", fileio.size, duration_text);
free(duration_text);
}
else
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2007 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "log.h"
+
+#include "flash.h"
+#include "cfi.h"
+#include "non_cfi.h"
+
+/* non-CFI compatible flashes */
+non_cfi_t non_cfi_flashes[] = {
+ {
+ .mfr = CFI_MFR_SST,
+ .id = 0xd4,
+ .pri_id = 0x02,
+ .dev_size = 0x10,
+ .interface_desc = 0x0,
+ .max_buf_write_size = 0x0,
+ .num_erase_regions = 1,
+ .erase_region_info =
+ {
+ 0x0010000f,
+ 0x00000000
+ }
+ },
+ {
+ .mfr = CFI_MFR_SST,
+ .id = 0xd5,
+ .pri_id = 0x02,
+ .dev_size = 0x11,
+ .interface_desc = 0x0,
+ .max_buf_write_size = 0x0,
+ .num_erase_regions = 1,
+ .erase_region_info =
+ {
+ 0x0010001f,
+ 0x00000000
+ }
+ },
+ {
+ .mfr = CFI_MFR_SST,
+ .id = 0xd6,
+ .pri_id = 0x02,
+ .dev_size = 0x12,
+ .interface_desc = 0x0,
+ .max_buf_write_size = 0x0,
+ .num_erase_regions = 1,
+ .erase_region_info =
+ {
+ 0x0010003f,
+ 0x00000000
+ }
+ },
+ {
+ .mfr = CFI_MFR_SST,
+ .id = 0xd7,
+ .pri_id = 0x02,
+ .dev_size = 0x13,
+ .interface_desc = 0x0,
+ .max_buf_write_size = 0x0,
+ .num_erase_regions = 1,
+ .erase_region_info =
+ {
+ 0x0010007f,
+ 0x00000000
+ }
+ },
+ {
+ .mfr = 0,
+ .id = 0,
+ }
+};
+
+void cfi_fixup_non_cfi(flash_bank_t *bank, void *param)
+{
+ cfi_flash_bank_t *cfi_info = bank->driver_priv;
+ non_cfi_t *non_cfi = non_cfi_flashes;
+
+ while (non_cfi->mfr)
+ {
+ if ((cfi_info->manufacturer == non_cfi->mfr)
+ && (cfi_info->device_id == non_cfi->id))
+ {
+ break;
+ }
+ non_cfi++;
+ }
+
+ cfi_info->not_cfi = 1;
+
+ /* fill in defaults for non-critical data */
+ cfi_info->vcc_min = 0x0;
+ cfi_info->vcc_max = 0x0;
+ cfi_info->vpp_min = 0x0;
+ cfi_info->vpp_max = 0x0;
+ cfi_info->word_write_timeout_typ = 0x0;
+ cfi_info->buf_write_timeout_typ = 0x0;
+ cfi_info->block_erase_timeout_typ = 0x0;
+ cfi_info->chip_erase_timeout_typ = 0x0;
+ cfi_info->word_write_timeout_max = 0x0;
+ cfi_info->buf_write_timeout_max = 0x0;
+ cfi_info->block_erase_timeout_max = 0x0;
+ cfi_info->chip_erase_timeout_max = 0x0;
+
+ cfi_info->qry[0] = 'Q';
+ cfi_info->qry[1] = 'R';
+ cfi_info->qry[2] = 'Y';
+
+ cfi_info->pri_id = non_cfi->pri_id;
+ cfi_info->pri_addr = 0x0;
+ cfi_info->alt_id = 0x0;
+ cfi_info->alt_addr = 0x0;
+ cfi_info->alt_ext = NULL;
+
+ cfi_info->interface_desc = non_cfi->interface_desc;
+ cfi_info->max_buf_write_size = non_cfi->max_buf_write_size;
+ cfi_info->num_erase_regions = non_cfi->num_erase_regions;
+ cfi_info->erase_region_info = non_cfi->erase_region_info;
+
+ if (cfi_info->pri_id == 0x2)
+ {
+ cfi_spansion_pri_ext_t *pri_ext = malloc(sizeof(cfi_spansion_pri_ext_t));
+
+ pri_ext->pri[0] = 'P';
+ pri_ext->pri[1] = 'R';
+ pri_ext->pri[2] = 'I';
+
+ pri_ext->major_version = '1';
+ pri_ext->minor_version = '0';
+
+ pri_ext->SiliconRevision = 0x0;
+ pri_ext->EraseSuspend = 0x0;
+ pri_ext->EraseSuspend = 0x0;
+ pri_ext->BlkProt = 0x0;
+ pri_ext->TmpBlkUnprotect = 0x0;
+ pri_ext->BlkProtUnprot = 0x0;
+ pri_ext->SimultaneousOps = 0x0;
+ pri_ext->BurstMode = 0x0;
+ pri_ext->PageMode = 0x0;
+ pri_ext->VppMin = 0x0;
+ pri_ext->VppMax = 0x0;
+ pri_ext->TopBottom = 0x0;
+
+ pri_ext->_reversed_geometry = 0;
+
+ cfi_info->pri_ext = pri_ext;
+ } else if ((cfi_info->pri_id == 0x1) || (cfi_info->pri_id == 0x3))
+ {
+ ERROR("BUG: non-CFI flashes using the Intel commandset are not yet supported");
+ exit(-1);
+ }
+}
+
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2007 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef NON_CFI_H
+#define NON_CFI_H
+
+#include "types.h"
+
+typedef struct non_cfi_s
+{
+ u16 mfr;
+ u16 id;
+ u16 pri_id;
+ u8 dev_size;
+ u16 interface_desc;
+ u16 max_buf_write_size;
+ u8 num_erase_regions;
+ u32 erase_region_info[6];
+} non_cfi_t;
+
+extern non_cfi_t non_cfi_flashes[];
+extern void cfi_fixup_non_cfi(flash_bank_t *bank, void *param);
+
+#endif /* NON_CFI_H */
+
}
}
- if (fileio->pri_type == FILEIO_IMAGE)
+ if (fileio->type == FILEIO_BINARY)
strcat(access, "b");
if (!(fileio_local->file = fopen(fileio->url, access)))
return ERROR_OK;
}
-//#ifdef FILEIO_BUFFER_COMPLETE_IHEX
-int fileio_ihex_buffer_complete(fileio_t *fileio)
-{
- fileio_image_t *image = fileio->pri_type_private;
- fileio_ihex_t *ihex = fileio->sec_type_private;
- u32 raw_bytes_read, raw_bytes;
- int retval;
- u32 full_address = image->base_address;
- char *buffer = malloc(ihex->raw_size);
- u32 cooked_bytes = 0x0;
-
- ihex->raw_size = fileio->size;
- ihex->buffer = malloc(ihex->raw_size >> 1);
-
- if ((retval = fileio_dispatch_read(fileio, ihex->raw_size, (u8*)buffer, &raw_bytes_read)) != ERROR_OK)
- {
- free(buffer);
- ERROR("failed buffering IHEX file, read failed");
- return ERROR_FILEIO_OPERATION_FAILED;
- }
-
- if (raw_bytes_read != ihex->raw_size)
- {
- free(buffer);
- ERROR("failed buffering complete IHEX file, only partially read");
- return ERROR_FILEIO_OPERATION_FAILED;
- }
-
- raw_bytes = 0x0;
- while (raw_bytes < raw_bytes_read)
- {
- u32 count;
- u32 address;
- u32 record_type;
- u32 checksum;
-
- if (sscanf(&buffer[raw_bytes], ":%2x%4x%2x", &count, &address, &record_type) != 3)
- {
- snprintf(fileio->error_str, FILEIO_MAX_ERROR_STRING, "invalid IHEX record");
- return ERROR_FILEIO_OPERATION_FAILED;
- }
- raw_bytes += 9;
-
- if (record_type == 0)
- {
- if ((full_address & 0xffff) != address)
- {
- free(buffer);
- ERROR("can't handle non-linear IHEX file");
- snprintf(fileio->error_str, FILEIO_MAX_ERROR_STRING, "can't handle non-linear IHEX file");
- return ERROR_FILEIO_OPERATION_FAILED;
- }
-
- while (count-- > 0)
- {
- sscanf(&buffer[raw_bytes], "%2hhx", &ihex->buffer[cooked_bytes]);
- raw_bytes += 2;
- cooked_bytes += 1;
- full_address++;
- }
- }
- else if (record_type == 1)
- {
- free(buffer);
- fileio->size = cooked_bytes;
- return ERROR_OK;
- }
- else if (record_type == 4)
- {
- u16 upper_address;
-
- sscanf(&buffer[raw_bytes], "%4hx", &upper_address);
- raw_bytes += 4;
-
- if ((full_address >> 16) != upper_address)
- {
- free(buffer);
- ERROR("can't handle non-linear IHEX file");
- snprintf(fileio->error_str, FILEIO_MAX_ERROR_STRING, "can't handle non-linear IHEX file");
- return ERROR_FILEIO_OPERATION_FAILED;
- }
- }
- else if (record_type == 5)
- {
- u32 start_address;
-
- sscanf(&buffer[raw_bytes], "%8x", &start_address);
- raw_bytes += 8;
-
- image->has_start_address = 1;
- image->start_address = be_to_h_u32((u8*)&start_address);
- }
- else
- {
- free(buffer);
- ERROR("unhandled IHEX record type: %i", record_type);
- snprintf(fileio->error_str, FILEIO_MAX_ERROR_STRING, "unhandled IHEX record type: %i", record_type);
- return ERROR_FILEIO_OPERATION_FAILED;
- }
-
- sscanf(&buffer[raw_bytes], "%2x", &checksum);
- raw_bytes += 2;
-
- /* consume new-line character(s) */
- if ((buffer[raw_bytes] == '\n') || (buffer[raw_bytes] == '\r'))
- raw_bytes++;
-
- if ((buffer[raw_bytes] == '\n') || (buffer[raw_bytes] == '\r'))
- raw_bytes++;
- }
-
- free(buffer);
- ERROR("premature end of IHEX file, no end-of-file record found");
- snprintf(fileio->error_str, FILEIO_MAX_ERROR_STRING, "premature end of IHEX file, no end-of-file record found");
- return ERROR_FILEIO_OPERATION_FAILED;
-}
-//#endif
-
-int fileio_open(fileio_t *fileio, char *url, enum fileio_access access,
- enum fileio_pri_type pri_type, void *pri_info, enum fileio_sec_type sec_type)
+int fileio_open(fileio_t *fileio, char *url, enum fileio_access access, enum fileio_type type)
{
int retval = ERROR_OK;
char *resource_identifier = NULL;
fileio->location = FILEIO_LOCAL;
}
+ fileio->type = type;
fileio->access = access;
- fileio->pri_type = pri_type;
- fileio->sec_type = sec_type;
fileio->url = strdup(url);
switch (fileio->location)
if (retval != ERROR_OK)
return retval;
- if (fileio->pri_type == FILEIO_TEXT)
- {
- /* do nothing for now */
- return ERROR_OK;
- }
- else if (fileio->pri_type == FILEIO_IMAGE)
- {
- fileio_image_t *image = malloc(sizeof(fileio_image_t));
- fileio_image_t *image_info = pri_info;
-
- fileio->pri_type_private = image;
- *image = *image_info;
-
- if (fileio->sec_type == FILEIO_PLAIN)
- {
- fileio->sec_type_private = NULL;
- }
- else if (fileio->sec_type == FILEIO_IHEX)
- {
- fileio_ihex_t *fileio_ihex;
-
- if (fileio->access != FILEIO_READ)
- {
- ERROR("can't write/append to a IHEX file");
- snprintf(fileio->error_str, FILEIO_MAX_ERROR_STRING, "can't write/append to a IHEX file");
- fileio_close(fileio);
- return ERROR_FILEIO_OPERATION_FAILED;
- }
-
- fileio_ihex = malloc(sizeof(fileio_ihex_t));
- fileio->sec_type_private = fileio_ihex;
-
- fileio_ihex->position = 0;
- fileio_ihex->raw_size = fileio->size;
-#ifdef FILEIO_BUFFER_COMPLETE_IHEX
- if (fileio_ihex_buffer_complete(fileio) != ERROR_OK)
- {
- fileio_close(fileio);
- return ERROR_FILEIO_OPERATION_FAILED;
- }
-#endif
- }
- }
-
return ERROR_OK;
}
free(fileio->url);
- if (fileio->pri_type == FILEIO_TEXT)
- {
- /* do nothing for now */
- }
- else if (fileio->pri_type == FILEIO_IMAGE)
- {
- if (fileio->sec_type == FILEIO_PLAIN)
- {
- /* nothing special to do for plain binary */
- }
- else if (fileio->sec_type == FILEIO_IHEX)
- {
- fileio_ihex_t *fileio_ihex = fileio->sec_type_private;
-
- if (fileio_ihex->buffer)
- free(fileio_ihex->buffer);
-
- free(fileio->sec_type_private);
- }
-
- free(fileio->pri_type_private);
- }
-
return ERROR_OK;
}
return ERROR_OK;
}
-int fileio_dispatch_read(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read)
+int fileio_read(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read)
{
switch (fileio->location)
{
}
}
-int fileio_read_ihex(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read)
-{
- fileio_ihex_t *fileio_ihex = fileio->sec_type_private;
-
- if ((fileio_ihex->position + size) > fileio->size)
- {
- /* don't read past the end of the file */
- size = (fileio->size - fileio_ihex->position);
- }
-
-#ifdef FILEIO_BUFFER_COMPLETE_IHEX
- memcpy(buffer, fileio_ihex->buffer + fileio_ihex->position, size);
- *size_read = size;
-#endif
-
- return ERROR_OK;
-}
-
-int fileio_read(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read)
-{
- if (fileio->sec_type == FILEIO_PLAIN)
- {
- return fileio_dispatch_read(fileio, size, buffer, size_read);
- }
- else if (fileio->sec_type == FILEIO_IHEX)
- {
- return fileio_read_ihex(fileio, size, buffer, size_read);
- }
-
- return ERROR_OK;
-}
-
int fileio_local_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_written)
{
fileio_local_t *fileio_local = fileio->location_private;
return ERROR_OK;
}
-int fileio_dispatch_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_written)
+int fileio_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_written)
{
switch (fileio->location)
{
return ERROR_OK;
}
-
-int fileio_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_written)
-{
- int retval = ERROR_FILEIO_OPERATION_NOT_SUPPORTED;
- if (fileio->sec_type == FILEIO_PLAIN)
- {
- retval = fileio_dispatch_write(fileio, size, buffer, size_written);
- }
- else if (fileio->sec_type == FILEIO_IHEX)
- {
- return ERROR_FILEIO_OPERATION_NOT_SUPPORTED;
- }
-
- if (retval != ERROR_OK)
- return retval;
-
- fileio->size += size;
-
- return ERROR_OK;
-}
-
-int fileio_identify_image_type(enum fileio_sec_type *sec_type, char *type_string)
-{
- if (type_string)
- {
- if (!strcmp(type_string, "bin"))
- {
- *sec_type = FILEIO_PLAIN;
- }
- else if (!strcmp(type_string, "ihex"))
- {
- *sec_type = FILEIO_IHEX;
- }
- else
- {
- return ERROR_FILEIO_RESOURCE_TYPE_UNKNOWN;
- }
- }
- else
- {
- *sec_type = FILEIO_PLAIN;
- }
-
- return ERROR_OK;
-}
#define FILEIO_MAX_ERROR_STRING (128)
-/* make buffering of complete intel-hex format files optional
- * to account for resource-limited hosts
- */
-#define FILEIO_BUFFER_COMPLETE_IHEX
+#include "types.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
-enum fileio_pri_type
-{
- FILEIO_TEXT = 0x1,
- FILEIO_IMAGE = 0x2,
-};
-
-enum fileio_sec_type
+enum fileio_type
{
- FILEIO_PLAIN = 0x10,
- FILEIO_IHEX = 0x20,
-/*
- * Possible future enhancements:
- * FILEIO_ELF,
- * FILEIO_SRECORD,
- */
+ FILEIO_TEXT,
+ FILEIO_BINARY,
};
enum fileio_location
char *url;
char error_str[FILEIO_MAX_ERROR_STRING];
long long size;
- enum fileio_pri_type pri_type;
- enum fileio_sec_type sec_type;
+ enum fileio_type type;
enum fileio_location location;
enum fileio_access access;
void *location_private;
- void *pri_type_private;
- void *sec_type_private;
} fileio_t;
-typedef struct fileio_text_s
-{
-} fileio_text_t;
-
-typedef struct fileio_image_s
-{
- u32 base_address;
- int has_start_address;
- u32 start_address;
-} fileio_image_t;
-
typedef struct fileio_local_s
{
FILE *file;
struct stat file_stat;
} fileio_local_t;
-typedef struct fileio_ihex_s
-{
- u32 position;
- u32 raw_size;
-#ifdef FILEIO_BUFFER_COMPLETE_IHEX
- u8 *buffer;
-#endif
-} fileio_ihex_t;
-
-extern int fileio_identify_image_type(enum fileio_sec_type *sec_type, char *type_string);
extern int fileio_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_written);
extern int fileio_read(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read);
extern int fileio_seek(fileio_t *fileio, u32 position);
extern int fileio_close(fileio_t *fileio);
-extern int fileio_open(fileio_t *fileio, char *url, enum fileio_access access,
- enum fileio_pri_type pri_type, void *pri_info, enum fileio_sec_type sec_type);
+extern int fileio_open(fileio_t *fileio, char *url, enum fileio_access access, enum fileio_type type);
#define ERROR_FILEIO_LOCATION_UNKNOWN (-1200)
#define ERROR_FILEIO_NOT_FOUND (-1201)
{
ERROR("number of discovered devices in JTAG chain (%i) doesn't match configuration (%i)",
device_count, jtag_num_devices);
+ ERROR("check the config file and ensure proper JTAG communication (connections, speed, ...)");
exit(-1);
}
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
-#define OPENOCD_VERSION "Open On-Chip Debugger (2007-04-26 16:40 CEST)"
+#define OPENOCD_VERSION "Open On-Chip Debugger (2007-05-29 13:15 CEST)"
#ifdef HAVE_CONFIG_H
#include "config.h"
noinst_LIBRARIES = libtarget.a
libtarget_a_SOURCES = target.c register.c breakpoints.c armv4_5.c embeddedice.c etm.c arm7tdmi.c arm9tdmi.c \
arm_jtag.c arm7_9_common.c algorithm.c arm920t.c arm720t.c armv4_5_mmu.c armv4_5_cache.c arm_disassembler.c \
- arm966e.c arm926ejs.c etb.c xscale.c arm_simulator.c
+ arm966e.c arm926ejs.c etb.c xscale.c arm_simulator.c image.c
noinst_HEADERS = target.h register.h armv4_5.h embeddedice.h etm.h arm7tdmi.h arm9tdmi.h \
arm_jtag.h arm7_9_common.h arm920t.h arm720t.h armv4_5_mmu.h armv4_5_cache.h breakpoints.h algorithm.h \
- arm_disassembler.h arm966e.h arm926ejs.h etb.h xscale.h arm_simulator.h
+ arm_disassembler.h arm966e.h arm926ejs.h etb.h xscale.h arm_simulator.h image.h
arm7_9_cmd = register_command(cmd_ctx, NULL, "arm7_9", NULL, COMMAND_ANY, "arm7/9 specific commands");
- register_command(cmd_ctx, arm7_9_cmd, "etm", handle_arm7_9_etm_command, COMMAND_CONFIG, NULL);
-
register_command(cmd_ctx, arm7_9_cmd, "write_xpsr", handle_arm7_9_write_xpsr_command, COMMAND_EXEC, "write program status register <value> <not cpsr|spsr>");
register_command(cmd_ctx, arm7_9_cmd, "write_xpsr_im8", handle_arm7_9_write_xpsr_im8_command, COMMAND_EXEC, "write program status register <8bit immediate> <rotate> <not cpsr|spsr>");
COMMAND_ANY, "use DCC downloads for larger memory writes <enable|disable>");
armv4_5_register_commands(cmd_ctx);
- etb_register_commands(cmd_ctx, arm7_9_cmd);
+
+ etm_register_commands(cmd_ctx);
return ERROR_OK;
}
return ERROR_OK;
}
-int handle_arm7_9_etm_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- target_t *target;
- armv4_5_common_t *armv4_5;
- arm7_9_common_t *arm7_9;
-
- if (argc != 1)
- {
- ERROR("incomplete 'arm7_9 etm <target>' command");
- exit(-1);
- }
-
- target = get_target_by_num(strtoul(args[0], NULL, 0));
-
- if (!target)
- {
- ERROR("target number '%s' not defined", args[0]);
- exit(-1);
- }
-
- if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
- {
- command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
- return ERROR_OK;
- }
-
- arm7_9->has_etm = 1;
-
- return ERROR_OK;
-}
-
-int handle_arm7_9_etb_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- target_t *target;
- jtag_device_t *jtag_device;
- armv4_5_common_t *armv4_5;
- arm7_9_common_t *arm7_9;
-
- if (argc != 2)
- {
- ERROR("incomplete 'arm7_9 etb <target> <chain_pos>' command");
- exit(-1);
- }
-
- target = get_target_by_num(strtoul(args[0], NULL, 0));
-
- if (!target)
- {
- ERROR("target number '%s' not defined", args[0]);
- exit(-1);
- }
-
- if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
- {
- command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
- return ERROR_OK;
- }
-
- jtag_device = jtag_get_device(strtoul(args[1], NULL, 0));
-
- if (!jtag_device)
- {
- ERROR("jtag device number '%s' not defined", args[1]);
- exit(-1);
- }
-
- arm7_9->etb = malloc(sizeof(etb_t));
-
- arm7_9->etb->chain_pos = strtoul(args[1], NULL, 0);
- arm7_9->etb->cur_scan_chain = -1;
- arm7_9->etb->reg_cache = NULL;
- arm7_9->etb->RAM_width = 0;
- arm7_9->etb->RAM_depth = 0;
-
- return ERROR_OK;
-}
-
int arm7_9_init_arch_info(target_t *target, arm7_9_common_t *arm7_9)
{
armv4_5_common_t *armv4_5 = &arm7_9->armv4_5_common;
arm7_9->force_hw_bkpts = 0;
arm7_9->use_dbgrq = 0;
- arm7_9->has_etm = 0;
- arm7_9->etb = NULL;
+ arm7_9->etm_ctx = NULL;
arm7_9->has_single_step = 0;
arm7_9->has_monitor_mode = 0;
arm7_9->has_vector_catch = 0;
#include "breakpoints.h"
#include "target.h"
-#include "etb.h"
+#include "etm.h"
#define ARM7_9_COMMON_MAGIC 0x0a790a79
arm_jtag_t jtag_info;
reg_cache_t *eice_cache;
- reg_cache_t *etm_cache;
u32 arm_bkpt;
u16 thumb_bkpt;
int dbgreq_adjust_pc;
int use_dbgrq;
- int has_etm;
- etb_t *etb;
+ etm_context_t *etm_ctx;
+
int has_single_step;
int has_monitor_mode;
int has_vector_catch;
(*cache_p)->next = embeddedice_build_reg_cache(target, arm7_9);
arm7_9->eice_cache = (*cache_p)->next;
- if (arm7_9->has_etm)
+ if (arm7_9->etm_ctx)
{
- (*cache_p)->next->next = etm_build_reg_cache(target, jtag_info, 0);
- arm7_9->etm_cache = (*cache_p)->next->next;
+ (*cache_p)->next->next = etm_build_reg_cache(target, jtag_info, arm7_9->etm_ctx);
+ arm7_9->etm_ctx->reg_cache = (*cache_p)->next->next;
}
}
(*cache_p)->next = embeddedice_build_reg_cache(target, arm7_9);
arm7_9->eice_cache = (*cache_p)->next;
- if (arm7_9->has_etm)
+ if (arm7_9->etm_ctx)
{
- (*cache_p)->next->next = etm_build_reg_cache(target, jtag_info, 0);
- arm7_9->etm_cache = (*cache_p)->next->next;
- }
-
- if (arm7_9->etb)
- {
- (*cache_p)->next->next->next = etb_build_reg_cache(arm7_9->etb);
- arm7_9->etb->reg_cache = (*cache_p)->next->next->next;
+ (*cache_p)->next->next = etm_build_reg_cache(target, jtag_info, arm7_9->etm_ctx);
+ arm7_9->etm_ctx->reg_cache = (*cache_p)->next->next;
}
}
if (!I) /* #+-<offset_12> */
{
u32 offset_12 = (opcode & 0xfff);
- snprintf(offset, 32, "#%s0x%x", (U) ? "" : "-", offset_12);
+ if (offset_12)
+ snprintf(offset, 32, ", #%s0x%x", (U) ? "" : "-", offset_12);
+ else
+ snprintf(offset, 32, "");
instruction->info.load_store.offset_mode = 0;
instruction->info.load_store.offset.offset = offset_12;
if ((shift_imm == 0x0) && (shift == 0x0)) /* +-<Rm> */
{
- snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm);
+ snprintf(offset, 32, ", %sr%i", (U) ? "" : "-", Rm);
}
else /* +-<Rm>, <Shift>, #<shift_imm> */
{
switch (shift)
{
case 0x0: /* LSL */
- snprintf(offset, 32, "%sr%i, LSL #0x%x", (U) ? "" : "-", Rm, shift_imm);
+ snprintf(offset, 32, ", %sr%i, LSL #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x1: /* LSR */
- snprintf(offset, 32, "%sr%i, LSR #0x%x", (U) ? "" : "-", Rm, shift_imm);
+ snprintf(offset, 32, ", %sr%i, LSR #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x2: /* ASR */
- snprintf(offset, 32, "%sr%i, ASR #0x%x", (U) ? "" : "-", Rm, shift_imm);
+ snprintf(offset, 32, ", %sr%i, ASR #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x3: /* ROR */
- snprintf(offset, 32, "%sr%i, ROR #0x%x", (U) ? "" : "-", Rm, shift_imm);
+ snprintf(offset, 32, ", %sr%i, ROR #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x4: /* RRX */
- snprintf(offset, 32, "%sr%i, RRX", (U) ? "" : "-", Rm);
+ snprintf(offset, 32, ", %sr%i, RRX", (U) ? "" : "-", Rm);
break;
}
}
{
if (W == 0) /* offset */
{
- snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]",
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i%s]",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
}
else /* pre-indexed */
{
- snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]!",
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i%s]!",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
}
else /* post-indexed */
{
- snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i], %s",
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i]%s",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
}
else if ((op == 0xd) || (op == 0xf)) /* <opcode1>{<cond>}{S} <Rd>, <shifter_operand> */
{
- snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, %s",
+ if (opcode==0xe1a00000) /* print MOV r0,r0 as NOP */
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tNOP",address, opcode);
+ else
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, %s",
address, opcode, mnemonic, COND(opcode),
(S) ? "S" : "", Rd, shifter_operand);
}
return -1;
}
+int evaluate_b_bl_blx_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 offset = opcode & 0x7ff;
+ u32 opc = (opcode >> 11) & 0x3;
+ u32 target_address;
+ char *mnemonic = NULL;
+
+ /* sign extend 11-bit offset */
+ if (((opc==0) || (opc==2)) && (offset & 0x00000400))
+ offset = 0xfffff800 | offset;
+
+ target_address = address + 4 + (offset<<1);
+
+ switch(opc)
+ {
+ /* unconditional branch */
+ case 0:
+ instruction->type = ARM_B;
+ mnemonic = "B";
+ break;
+ /* BLX suffix */
+ case 1:
+ instruction->type = ARM_BLX;
+ mnemonic = "BLX";
+ break;
+ /* BL/BLX prefix */
+ case 2:
+ instruction->type = ARM_UNKNOWN_INSTUCTION;
+ mnemonic = "prefix";
+ target_address = offset<<12;
+ break;
+ /* BL suffix */
+ case 3:
+ instruction->type = ARM_BL;
+ mnemonic = "BL";
+ break;
+ }
+ /* TODO: deals correctly with dual opcodes BL/BLX ... */
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s 0x%8.8x", address, opcode,mnemonic, target_address);
+
+ instruction->info.b_bl_bx_blx.reg_operand = -1;
+ instruction->info.b_bl_bx_blx.target_address = target_address;
+
+ return ERROR_OK;
+}
+
+int evaluate_add_sub_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u8 Rd = (opcode >> 0) & 0x7;
+ u8 Rn = (opcode >> 3) & 0x7;
+ u8 Rm_imm = (opcode >> 6) & 0x7;
+ u32 opc = opcode & (1<<9);
+ u32 reg_imm = opcode & (1<<10);
+ char *mnemonic;
+
+ if (opc)
+ {
+ instruction->type = ARM_SUB;
+ mnemonic = "SUBS";
+ }
+ else
+ {
+ instruction->type = ARM_ADD;
+ mnemonic = "ADDS";
+ }
+
+ instruction->info.data_proc.Rd = Rd;
+ instruction->info.data_proc.Rn = Rn;
+ instruction->info.data_proc.S = 1;
+
+ if (reg_imm)
+ {
+ instruction->info.data_proc.variant = 0; /*immediate*/
+ instruction->info.data_proc.shifter_operand.immediate.immediate = Rm_imm;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i, #%d",
+ address, opcode, mnemonic, Rd, Rn, Rm_imm);
+ }
+ else
+ {
+ instruction->info.data_proc.variant = 1; /*immediate shift*/
+ instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm_imm;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i, r%i",
+ address, opcode, mnemonic, Rd, Rn, Rm_imm);
+ }
+
+ return ERROR_OK;
+}
+
+int evaluate_shift_imm_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u8 Rd = (opcode >> 0) & 0x7;
+ u8 Rm = (opcode >> 3) & 0x7;
+ u8 imm = (opcode >> 6) & 0x1f;
+ u8 opc = (opcode >> 11) & 0x3;
+ char *mnemonic = NULL;
+
+ switch(opc)
+ {
+ case 0:
+ instruction->type = ARM_MOV;
+ mnemonic = "LSLS";
+ instruction->info.data_proc.shifter_operand.immediate_shift.shift = 0;
+ break;
+ case 1:
+ instruction->type = ARM_MOV;
+ mnemonic = "LSRS";
+ instruction->info.data_proc.shifter_operand.immediate_shift.shift = 1;
+ break;
+ case 2:
+ instruction->type = ARM_MOV;
+ mnemonic = "ASRS";
+ instruction->info.data_proc.shifter_operand.immediate_shift.shift = 2;
+ break;
+ }
+
+ if ((imm==0) && (opc!=0))
+ imm = 32;
+
+ instruction->info.data_proc.Rd = Rd;
+ instruction->info.data_proc.Rn = -1;
+ instruction->info.data_proc.S = 1;
+
+ instruction->info.data_proc.variant = 1; /*immediate_shift*/
+ instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm;
+ instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = imm;
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i, #0x%02x",
+ address, opcode, mnemonic, Rd, Rm, imm);
+
+ return ERROR_OK;
+}
+
+int evaluate_data_proc_imm_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u8 imm = opcode & 0xff;
+ u8 Rd = (opcode >> 8) & 0x7;
+ u32 opc = (opcode >> 11) & 0x3;
+ char *mnemonic = NULL;
+
+ instruction->info.data_proc.Rd = Rd;
+ instruction->info.data_proc.Rn = Rd;
+ instruction->info.data_proc.S = 1;
+ instruction->info.data_proc.variant = 0; /*immediate*/
+ instruction->info.data_proc.shifter_operand.immediate.immediate = imm;
+
+ switch(opc)
+ {
+ case 0:
+ instruction->type = ARM_MOV;
+ mnemonic = "MOVS";
+ instruction->info.data_proc.Rn = -1;
+ break;
+ case 1:
+ instruction->type = ARM_CMP;
+ mnemonic = "CMP";
+ instruction->info.data_proc.Rd = -1;
+ break;
+ case 2:
+ instruction->type = ARM_ADD;
+ mnemonic = "ADDS";
+ break;
+ case 3:
+ instruction->type = ARM_SUB;
+ mnemonic = "SUBS";
+ break;
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, #0x%02x",
+ address, opcode, mnemonic, Rd, imm);
+
+ return ERROR_OK;
+}
+
+int evaluate_data_proc_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u8 high_reg, op, Rm, Rd,H1,H2;
+ char *mnemonic = NULL;
+
+ high_reg = (opcode & 0x0400) >> 10;
+ op = (opcode & 0x03C0) >> 6;
+
+ Rd = (opcode & 0x0007);
+ Rm = (opcode & 0x0038) >> 3;
+ H1 = (opcode & 0x0080) >> 7;
+ H2 = (opcode & 0x0040) >> 6;
+
+ instruction->info.data_proc.Rd = Rd;
+ instruction->info.data_proc.Rn = Rd;
+ instruction->info.data_proc.S = (!high_reg || (instruction->type == ARM_CMP));
+ instruction->info.data_proc.variant = 1 /*immediate shift*/;
+ instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm;
+
+ if (high_reg)
+ {
+ Rd |= H1 << 3;
+ Rm |= H2 << 3;
+ op >>= 2;
+
+ switch (op)
+ {
+ case 0x0:
+ instruction->type = ARM_ADD;
+ mnemonic = "ADD";
+ break;
+ case 0x1:
+ instruction->type = ARM_CMP;
+ mnemonic = "CMP";
+ break;
+ case 0x2:
+ instruction->type = ARM_MOV;
+ mnemonic = "MOV";
+ break;
+ case 0x3:
+ if ((opcode & 0x7) == 0x0)
+ {
+ instruction->info.b_bl_bx_blx.reg_operand = Rm;
+ if (H1)
+ {
+ instruction->type = ARM_BLX;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tBLX r%i", address, opcode, Rm);
+ }
+ else
+ {
+ instruction->type = ARM_BX;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tBX r%i", address, opcode, Rm);
+ }
+ }
+ else
+ {
+ instruction->type = ARM_UNDEFINED_INSTRUCTION;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode);
+ }
+ return ERROR_OK;
+ break;
+ }
+ }
+ else
+ {
+ switch (op)
+ {
+ case 0x0:
+ instruction->type = ARM_AND;
+ mnemonic = "ANDS";
+ break;
+ case 0x1:
+ instruction->type = ARM_EOR;
+ mnemonic = "EORS";
+ break;
+ case 0x2:
+ instruction->type = ARM_MOV;
+ mnemonic = "LSLS";
+ instruction->info.data_proc.variant = 2 /*register shift*/;
+ instruction->info.data_proc.shifter_operand.register_shift.shift = 0;
+ instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd;
+ instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm;
+ break;
+ case 0x3:
+ instruction->type = ARM_MOV;
+ mnemonic = "LSRS";
+ instruction->info.data_proc.variant = 2 /*register shift*/;
+ instruction->info.data_proc.shifter_operand.register_shift.shift = 1;
+ instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd;
+ instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm;
+ break;
+ case 0x4:
+ instruction->type = ARM_MOV;
+ mnemonic = "ASRS";
+ instruction->info.data_proc.variant = 2 /*register shift*/;
+ instruction->info.data_proc.shifter_operand.register_shift.shift = 2;
+ instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd;
+ instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm;
+ break;
+ case 0x5:
+ instruction->type = ARM_ADC;
+ mnemonic = "ADCS";
+ break;
+ case 0x6:
+ instruction->type = ARM_SBC;
+ mnemonic = "SBCS";
+ break;
+ case 0x7:
+ instruction->type = ARM_MOV;
+ mnemonic = "RORS";
+ instruction->info.data_proc.variant = 2 /*register shift*/;
+ instruction->info.data_proc.shifter_operand.register_shift.shift = 3;
+ instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd;
+ instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm;
+ break;
+ case 0x8:
+ instruction->type = ARM_TST;
+ mnemonic = "TST";
+ break;
+ case 0x9:
+ instruction->type = ARM_RSB;
+ mnemonic = "NEGS";
+ instruction->info.data_proc.variant = 0 /*immediate*/;
+ instruction->info.data_proc.shifter_operand.immediate.immediate = 0;
+ instruction->info.data_proc.Rn = Rm;
+ break;
+ case 0xA:
+ instruction->type = ARM_CMP;
+ mnemonic = "CMP";
+ break;
+ case 0xB:
+ instruction->type = ARM_CMN;
+ mnemonic = "CMN";
+ break;
+ case 0xC:
+ instruction->type = ARM_ORR;
+ mnemonic = "ORRS";
+ break;
+ case 0xD:
+ instruction->type = ARM_MUL;
+ mnemonic = "MULS";
+ break;
+ case 0xE:
+ instruction->type = ARM_BIC;
+ mnemonic = "BICS";
+ break;
+ case 0xF:
+ instruction->type = ARM_MVN;
+ mnemonic = "MVNS";
+ break;
+ }
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i",
+ address, opcode, mnemonic, Rd, Rm);
+
+ return ERROR_OK;
+}
+
+int evaluate_load_literal_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 immediate;
+ u8 Rd = (opcode >> 8) & 0x7;
+
+ instruction->type = ARM_LDR;
+ immediate = opcode & 0x000000ff;
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tLDR r%i, [PC, #0x%x]", address, opcode, Rd, immediate*4);
+
+ instruction->info.load_store.Rd = Rd;
+ instruction->info.load_store.Rn = 15 /*PC*/;
+ instruction->info.load_store.index_mode = 0; /*offset*/
+ instruction->info.load_store.offset_mode = 0; /*immediate*/
+ instruction->info.load_store.offset.offset = immediate*4;
+
+ return ERROR_OK;
+}
+
+int evaluate_load_store_reg_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u8 Rd = (opcode >> 0) & 0x7;
+ u8 Rn = (opcode >> 3) & 0x7;
+ u8 Rm = (opcode >> 6) & 0x7;
+ u8 opc = (opcode >> 9) & 0x7;
+ char *mnemonic = NULL;
+
+ switch(opc)
+ {
+ case 0:
+ instruction->type = ARM_STR;
+ mnemonic = "STR";
+ break;
+ case 1:
+ instruction->type = ARM_STRH;
+ mnemonic = "STRH";
+ break;
+ case 2:
+ instruction->type = ARM_STRB;
+ mnemonic = "STRB";
+ break;
+ case 3:
+ instruction->type = ARM_LDRSB;
+ mnemonic = "LDRSB";
+ break;
+ case 4:
+ instruction->type = ARM_LDR;
+ mnemonic = "LDR";
+ break;
+ case 5:
+ instruction->type = ARM_LDRH;
+ mnemonic = "LDRH";
+ break;
+ case 6:
+ instruction->type = ARM_LDRB;
+ mnemonic = "LDRB";
+ break;
+ case 7:
+ instruction->type = ARM_LDRSH;
+ mnemonic = "LDRSH";
+ break;
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, [r%i, r%i]", address, opcode, mnemonic, Rd, Rn, Rm);
+
+ instruction->info.load_store.Rd = Rd;
+ instruction->info.load_store.Rn = Rn;
+ instruction->info.load_store.index_mode = 0; /*offset*/
+ instruction->info.load_store.offset_mode = 1; /*register*/
+ instruction->info.load_store.offset.reg.Rm = Rm;
+
+ return ERROR_OK;
+}
+
+int evaluate_load_store_imm_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 offset = (opcode >> 6) & 0x1f;
+ u8 Rd = (opcode >> 0) & 0x7;
+ u8 Rn = (opcode >> 3) & 0x7;
+ u32 L = opcode & (1<<11);
+ u32 B = opcode & (1<<12);
+ char *mnemonic;
+ char suffix = ' ';
+ u32 shift = 2;
+
+ if (L)
+ {
+ instruction->type = ARM_LDR;
+ mnemonic = "LDR";
+ }
+ else
+ {
+ instruction->type = ARM_STR;
+ mnemonic = "STR";
+ }
+
+ if ((opcode&0xF000)==0x8000)
+ {
+ suffix = 'H';
+ shift = 1;
+ }
+ else if (B)
+ {
+ suffix = 'B';
+ shift = 0;
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s%c r%i, [r%i, #0x%x]", address, opcode, mnemonic, suffix, Rd, Rn, offset<<shift);
+
+ instruction->info.load_store.Rd = Rd;
+ instruction->info.load_store.Rn = Rn;
+ instruction->info.load_store.index_mode = 0; /*offset*/
+ instruction->info.load_store.offset_mode = 0; /*immediate*/
+ instruction->info.load_store.offset.offset = offset<<shift;
+
+ return ERROR_OK;
+}
+
+int evaluate_load_store_stack_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 offset = opcode & 0xff;
+ u8 Rd = (opcode >> 8) & 0x7;
+ u32 L = opcode & (1<<11);
+ char *mnemonic;
+
+ if (L)
+ {
+ instruction->type = ARM_LDR;
+ mnemonic = "LDR";
+ }
+ else
+ {
+ instruction->type = ARM_STR;
+ mnemonic = "STR";
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, [SP, #0x%x]", address, opcode, mnemonic, Rd, offset*4);
+
+ instruction->info.load_store.Rd = Rd;
+ instruction->info.load_store.Rn = 13 /*SP*/;
+ instruction->info.load_store.index_mode = 0; /*offset*/
+ instruction->info.load_store.offset_mode = 0; /*immediate*/
+ instruction->info.load_store.offset.offset = offset*4;
+
+ return ERROR_OK;
+}
+
+int evaluate_add_sp_pc_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 imm = opcode & 0xff;
+ u8 Rd = (opcode >> 8) & 0x7;
+ u8 Rn;
+ u32 SP = opcode & (1<<11);
+ char *reg_name;
+
+ instruction->type = ARM_ADD;
+
+ if (SP)
+ {
+ reg_name = "SP";
+ Rn = 13;
+ }
+ else
+ {
+ reg_name = "PC";
+ Rn = 15;
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tADD r%i, %s, #0x%x", address, opcode, Rd,reg_name, imm*4);
+
+ instruction->info.data_proc.variant = 0 /* immediate */;
+ instruction->info.data_proc.Rd = Rd;
+ instruction->info.data_proc.Rn = Rn;
+ instruction->info.data_proc.shifter_operand.immediate.immediate = imm*4;
+
+ return ERROR_OK;
+}
+
+int evaluate_adjust_stack_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 imm = opcode & 0x7f;
+ u8 opc = opcode & (1<<7);
+ char *mnemonic;
+
+
+ if (opc)
+ {
+ instruction->type = ARM_SUB;
+ mnemonic = "SUB";
+ }
+ else
+ {
+ instruction->type = ARM_ADD;
+ mnemonic = "ADD";
+ }
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s SP, #0x%x", address, opcode, mnemonic, imm*4);
+
+ instruction->info.data_proc.variant = 0 /* immediate */;
+ instruction->info.data_proc.Rd = 13 /*SP*/;
+ instruction->info.data_proc.Rn = 13 /*SP*/;
+ instruction->info.data_proc.shifter_operand.immediate.immediate = imm*4;
+
+ return ERROR_OK;
+}
+
+int evaluate_breakpoint_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 imm = opcode & 0xff;
+
+ instruction->type = ARM_BKPT;
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tBKPT 0x%02x", address, opcode, imm);
+
+ return ERROR_OK;
+}
+
+int evaluate_load_store_multiple_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 reg_list = opcode & 0xff;
+ u32 L = opcode & (1<<11);
+ u32 R = opcode & (1<<8);
+ u8 Rn = (opcode >> 8) & 7;
+ u8 addr_mode = 0 /* IA */;
+ char reg_names[40];
+ char *reg_names_p;
+ char *mnemonic;
+ char ptr_name[7] = "";
+ int i;
+
+ if ((opcode & 0xf000) == 0xc000)
+ { /* generic load/store multiple */
+ if (L)
+ {
+ instruction->type = ARM_LDM;
+ mnemonic = "LDMIA";
+ }
+ else
+ {
+ instruction->type = ARM_STM;
+ mnemonic = "STMIA";
+ }
+ snprintf(ptr_name,7,"r%i!, ",Rn);
+ }
+ else
+ { /* push/pop */
+ Rn = 13; /* SP */
+ if (L)
+ {
+ instruction->type = ARM_LDM;
+ mnemonic = "POP";
+ if (R)
+ reg_list |= (1<<15) /*PC*/;
+ }
+ else
+ {
+ instruction->type = ARM_STM;
+ mnemonic = "PUSH";
+ addr_mode = 3; /*DB*/
+ if (R)
+ reg_list |= (1<<14) /*LR*/;
+ }
+ }
+
+ reg_names_p = reg_names;
+ for (i = 0; i <= 15; i++)
+ {
+ if (reg_list & (1<<i))
+ reg_names_p += snprintf(reg_names_p, (reg_names + 40 - reg_names_p), "r%i, ", i);
+ }
+ if (reg_names_p>reg_names)
+ reg_names_p[-2] = '\0';
+ else /* invalid op : no registers */
+ reg_names[0] = '\0';
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s %s{%s}", address, opcode, mnemonic, ptr_name,reg_names);
+
+ instruction->info.load_store_multiple.register_list = reg_list;
+ instruction->info.load_store_multiple.Rn = Rn;
+ instruction->info.load_store_multiple.addressing_mode = addr_mode;
+
+ return ERROR_OK;
+}
+
+int evaluate_cond_branch_thumb(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ u32 offset = opcode & 0xff;
+ u8 cond = (opcode >> 8) & 0xf;
+ u32 target_address;
+
+ if (cond == 0xf)
+ {
+ instruction->type = ARM_SWI;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tSWI 0x%02x", address, opcode, offset);
+ return ERROR_OK;
+ }
+ else if (cond == 0xe)
+ {
+ instruction->type = ARM_UNDEFINED_INSTRUCTION;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode);
+ return ERROR_OK;
+ }
+
+ /* sign extend 8-bit offset */
+ if (offset & 0x00000080)
+ offset = 0xffffff00 | offset;
+
+ target_address = address + 4 + (offset<<1);
+
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tB%s 0x%8.8x", address, opcode,
+ arm_condition_strings[cond], target_address);
+
+ instruction->type = ARM_B;
+ instruction->info.b_bl_bx_blx.reg_operand = -1;
+ instruction->info.b_bl_bx_blx.target_address = target_address;
+
+ return ERROR_OK;
+}
+
+int thumb_evaluate_opcode(u16 opcode, u32 address, arm_instruction_t *instruction)
+{
+ /* clear fields, to avoid confusion */
+ memset(instruction, 0, sizeof(arm_instruction_t));
+ instruction->opcode = opcode;
+
+ if ((opcode & 0xe000) == 0x0000)
+ {
+ /* add/substract register or immediate */
+ if ((opcode & 0x1800) == 0x1800)
+ return evaluate_add_sub_thumb(opcode, address, instruction);
+ /* shift by immediate */
+ else
+ return evaluate_shift_imm_thumb(opcode, address, instruction);
+ }
+
+ /* Add/substract/compare/move immediate */
+ if ((opcode & 0xe000) == 0x2000)
+ {
+ return evaluate_data_proc_imm_thumb(opcode, address, instruction);
+ }
+
+ /* Data processing instructions */
+ if ((opcode & 0xf800) == 0x4000)
+ {
+ return evaluate_data_proc_thumb(opcode, address, instruction);
+ }
+
+ /* Load from literal pool */
+ if ((opcode & 0xf800) == 0x4800)
+ {
+ return evaluate_load_literal_thumb(opcode, address, instruction);
+ }
+
+ /* Load/Store register offset */
+ if ((opcode & 0xf000) == 0x5000)
+ {
+ return evaluate_load_store_reg_thumb(opcode, address, instruction);
+ }
+
+ /* Load/Store immediate offset */
+ if (((opcode & 0xe000) == 0x6000)
+ ||((opcode & 0xf000) == 0x8000))
+ {
+ return evaluate_load_store_imm_thumb(opcode, address, instruction);
+ }
+
+ /* Load/Store from/to stack */
+ if ((opcode & 0xf000) == 0x9000)
+ {
+ return evaluate_load_store_stack_thumb(opcode, address, instruction);
+ }
+
+ /* Add to SP/PC */
+ if ((opcode & 0xf000) == 0xa000)
+ {
+ return evaluate_add_sp_pc_thumb(opcode, address, instruction);
+ }
+
+ /* Misc */
+ if ((opcode & 0xf000) == 0xb000)
+ {
+ if ((opcode & 0x0f00) == 0x0000)
+ return evaluate_adjust_stack_thumb(opcode, address, instruction);
+ else if ((opcode & 0x0f00) == 0x0e00)
+ return evaluate_breakpoint_thumb(opcode, address, instruction);
+ else if ((opcode & 0x0600) == 0x0400) /* push pop */
+ return evaluate_load_store_multiple_thumb(opcode, address, instruction);
+ else
+ {
+ instruction->type = ARM_UNDEFINED_INSTRUCTION;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode);
+ return ERROR_OK;
+ }
+ }
+
+ /* Load/Store multiple */
+ if ((opcode & 0xf000) == 0xc000)
+ {
+ return evaluate_load_store_multiple_thumb(opcode, address, instruction);
+ }
+
+ /* Conditional branch + SWI */
+ if ((opcode & 0xf000) == 0xd000)
+ {
+ return evaluate_cond_branch_thumb(opcode, address, instruction);
+ }
+
+ if ((opcode & 0xe000) == 0xe000)
+ {
+ /* Undefined instructions */
+ if ((opcode & 0xf801) == 0xe801)
+ {
+ instruction->type = ARM_UNDEFINED_INSTRUCTION;
+ snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode);
+ return ERROR_OK;
+ }
+ else
+ { /* Branch to offset */
+ return evaluate_b_bl_blx_thumb(opcode, address, instruction);
+ }
+ }
+
+ ERROR("should never reach this point (opcode=%04x)",opcode);
+ return -1;
+}
+
} immediate;
struct {
u8 Rm;
- u8 shift;
+ u8 shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
u8 shift_imm;
} immediate_shift;
struct {
u32 offset;
struct {
u8 Rm;
- u8 shift;
+ u8 shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
u8 shift_imm;
} reg;
} offset;
} arm_instruction_t;
extern int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction);
+extern int thumb_evaluate_opcode(u16 opcode, u32 address, arm_instruction_t *instruction);
#define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000)>>28])
return 0;
}
+int thumb_pass_branch_condition(u32 cpsr, u16 opcode)
+{
+ return pass_condition(cpsr, (opcode & 0x0f00) << 20);
+}
+
/* simulate a single step (if possible)
* if the dry_run_pc argument is provided, no state is changed,
* but the new pc is stored in the variable pointed at by the argument
target_read_u32(target, current_pc, &opcode);
arm_evaluate_opcode(opcode, current_pc, &instruction);
instruction_size = 4;
+
+ /* check condition code (for all instructions) */
+ if (!pass_condition(buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), opcode))
+ {
+ if (dry_run_pc)
+ {
+ *dry_run_pc = current_pc + instruction_size;
+ }
+ else
+ {
+ buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size);
+ }
+
+ return ERROR_OK;
+ }
}
else
{
- /* TODO: add support for Thumb instruction set */
+ target_read_u32(target, current_pc, &opcode);
+ arm_evaluate_opcode(opcode, current_pc, &instruction);
instruction_size = 2;
- }
-
- /* check condition code */
- if (!pass_condition(buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), opcode))
- {
- if (dry_run_pc)
- {
- *dry_run_pc = current_pc + instruction_size;
- }
- else
+
+ /* check condition code (only for branch instructions) */
+ if ((!thumb_pass_branch_condition(buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), opcode)) &&
+ (instruction.type == ARM_B))
{
- buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size);
+ if (dry_run_pc)
+ {
+ *dry_run_pc = current_pc + instruction_size;
+ }
+ else
+ {
+ buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size);
+ }
+
+ return ERROR_OK;
}
-
- return ERROR_OK;
}
/* examine instruction type */
#include "register.h"
#include "target.h"
-enum armv4_5_mode
+typedef enum armv4_5_mode
{
ARMV4_5_MODE_USR = 16,
ARMV4_5_MODE_FIQ = 17,
ARMV4_5_MODE_UND = 27,
ARMV4_5_MODE_SYS = 31,
ARMV4_5_MODE_ANY = -1
-};
+} armv4_5_mode_t;
extern char* armv4_5_mode_strings[];
-enum armv4_5_state
+typedef enum armv4_5_state
{
ARMV4_5_STATE_ARM,
ARMV4_5_STATE_THUMB,
ARMV4_5_STATE_JAZELLE,
-};
+} armv4_5_state_t;
extern char* armv4_5_state_strings[];
#include "config.h"
#endif
+#include <string.h>
+
#include "arm7_9_common.h"
#include "etb.h"
+#include "etm.h"
#include "log.h"
#include "types.h"
int etb_write_reg(reg_t *reg, u32 value);
int etb_read_reg(reg_t *reg);
-int handle_arm7_9_etb_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-int handle_arm7_9_etb_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-
-char *etmv1_branch_reason_string[] =
-{
- "normal pc change", "tracing enabled", "restart after FIFO overflow",
- "exit from debug state", "peridoic synchronization point",
- "reserved", "reserved", "reserved"
-};
-
+int handle_etb_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
int etb_set_instr(etb_t *etb, u32 new_instr)
{
return ERROR_OK;
}
+int etb_read_ram(etb_t *etb, u32 *data, int num_frames)
+{
+ scan_field_t fields[3];
+ int i;
+
+ jtag_add_end_state(TAP_RTI);
+ etb_scann(etb, 0x0);
+ etb_set_instr(etb, 0xc);
+
+ fields[0].device = etb->chain_pos;
+ fields[0].num_bits = 32;
+ fields[0].out_value = NULL;
+ fields[0].out_mask = NULL;
+ fields[0].in_value = NULL;
+ fields[0].in_check_value = NULL;
+ fields[0].in_check_mask = NULL;
+ fields[0].in_handler = NULL;
+ fields[0].in_handler_priv = NULL;
+
+ fields[1].device = etb->chain_pos;
+ fields[1].num_bits = 7;
+ fields[1].out_value = malloc(1);
+ buf_set_u32(fields[1].out_value, 0, 7, 4);
+ fields[1].out_mask = NULL;
+ fields[1].in_value = NULL;
+ fields[1].in_check_value = NULL;
+ fields[1].in_check_mask = NULL;
+ fields[1].in_handler = NULL;
+ fields[1].in_handler_priv = NULL;
+
+ fields[2].device = etb->chain_pos;
+ fields[2].num_bits = 1;
+ fields[2].out_value = malloc(1);
+ buf_set_u32(fields[2].out_value, 0, 1, 0);
+ fields[2].out_mask = NULL;
+ fields[2].in_value = NULL;
+ fields[2].in_check_value = NULL;
+ fields[2].in_check_mask = NULL;
+ fields[2].in_handler = NULL;
+ fields[2].in_handler_priv = NULL;
+
+ jtag_add_dr_scan(3, fields, -1, NULL);
+
+ fields[0].in_handler = buf_to_u32_handler;
+
+ for (i = 0; i < num_frames; i++)
+ {
+ /* ensure nR/W reamins set to read */
+ buf_set_u32(fields[2].out_value, 0, 1, 0);
+
+ /* address remains set to 0x4 (RAM data) until we read the last frame */
+ if (i < num_frames - 1)
+ buf_set_u32(fields[1].out_value, 0, 7, 4);
+ else
+ buf_set_u32(fields[1].out_value, 0, 7, 0);
+
+ fields[0].in_handler_priv = &data[i];
+ jtag_add_dr_scan(3, fields, -1, NULL);
+ }
+
+ jtag_execute_queue();
+
+ free(fields[1].out_value);
+ free(fields[2].out_value);
+
+ return ERROR_OK;
+}
+
int etb_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask)
{
etb_reg_t *etb_reg = reg->arch_info;
return etb_write_reg(reg, buf_get_u32(reg->value, 0, reg->size));
}
-int etb_register_commands(struct command_context_s *cmd_ctx, command_t *arm7_9_cmd)
+int etb_register_commands(struct command_context_s *cmd_ctx)
{
- register_command(cmd_ctx, arm7_9_cmd, "etb", handle_arm7_9_etb_command, COMMAND_CONFIG, NULL);
+ command_t *etb_cmd;
+
+ etb_cmd = register_command(cmd_ctx, NULL, "etb", NULL, COMMAND_ANY, "Embedded Trace Buffer");
+
+ register_command(cmd_ctx, etb_cmd, "config", handle_etb_config_command, COMMAND_CONFIG, NULL);
+
+ return ERROR_OK;
+}
- register_command(cmd_ctx, arm7_9_cmd, "etb_dump", handle_arm7_9_etb_dump_command, COMMAND_EXEC, "dump current ETB content");
+int handle_etb_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ jtag_device_t *jtag_device;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+
+ if (argc != 2)
+ {
+ ERROR("incomplete 'etb config <target> <chain_pos>' command");
+ exit(-1);
+ }
+
+ target = get_target_by_num(strtoul(args[0], NULL, 0));
+
+ if (!target)
+ {
+ ERROR("target number '%s' not defined", args[0]);
+ exit(-1);
+ }
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ jtag_device = jtag_get_device(strtoul(args[1], NULL, 0));
+
+ if (!jtag_device)
+ {
+ ERROR("jtag device number '%s' not defined", args[1]);
+ exit(-1);
+ }
+
+ if (arm7_9->etm_ctx)
+ {
+ etb_t *etb = malloc(sizeof(etb_t));
+
+ arm7_9->etm_ctx->capture_driver_priv = etb;
+
+ etb->chain_pos = strtoul(args[1], NULL, 0);
+ etb->cur_scan_chain = -1;
+ etb->reg_cache = NULL;
+ etb->ram_width = 0;
+ etb->ram_depth = 0;
+ }
+ else
+ {
+ ERROR("target has no ETM defined, ETB left unconfigured");
+ }
return ERROR_OK;
}
-#define PIPESTAT(x) ((x) & 0x7)
-#define TRACEPKT(x) (((x) & 0x7fff8) >> 3)
-#define TRACESYNC(x) (((x) & 0x80000) >> 19)
+int etb_init(etm_context_t *etm_ctx)
+{
+ etb_t *etb = etm_ctx->capture_driver_priv;
+
+ etb->etm_ctx = etm_ctx;
+
+ /* identify ETB RAM depth and width */
+ etb_read_reg(&etb->reg_cache->reg_list[ETB_RAM_DEPTH]);
+ etb_read_reg(&etb->reg_cache->reg_list[ETB_RAM_WIDTH]);
+ jtag_execute_queue();
+
+ etb->ram_depth = buf_get_u32(etb->reg_cache->reg_list[ETB_RAM_DEPTH].value, 0, 32);
+ etb->ram_width = buf_get_u32(etb->reg_cache->reg_list[ETB_RAM_WIDTH].value, 0, 32);
+
+ return ERROR_OK;
+}
-int etmv1_next_packet(int trace_depth, u32 *trace_data, int frame, int *port_half, int apo, u8 *packet)
+trace_status_t etb_status(etm_context_t *etm_ctx)
{
- while (frame < trace_depth)
+ etb_t *etb = etm_ctx->capture_driver_priv;
+
+ etb->etm_ctx = etm_ctx;
+
+ /* if tracing is currently idle, return this information */
+ if (etm_ctx->capture_status == TRACE_IDLE)
{
- if (apo > 0)
- {
- if (TRACESYNC(trace_data[frame]))
- apo--;
- }
- else
+ return etm_ctx->capture_status;
+ }
+ else if (etm_ctx->capture_status & TRACE_RUNNING)
+ {
+ reg_t *etb_status_reg = &etb->reg_cache->reg_list[ETB_STATUS];
+ int etb_timeout = 100;
+
+ /* trace is running, check the ETB status flags */
+ etb_get_reg(etb_status_reg);
+
+ /* check Full bit to identify an overflow */
+ if (buf_get_u32(etb_status_reg->value, 0, 1) == 1)
+ etm_ctx->capture_status |= TRACE_OVERFLOWED;
+
+ /* check Triggered bit to identify trigger condition */
+ if (buf_get_u32(etb_status_reg->value, 1, 1) == 1)
+ etm_ctx->capture_status |= TRACE_TRIGGERED;
+
+ /* check AcqComp to identify trace completion */
+ if (buf_get_u32(etb_status_reg->value, 2, 1) == 1)
{
- /* we're looking for a branch address, skip if TRACESYNC isn't set */
- if ((apo == 0) && (!TRACESYNC(trace_data[frame])))
+ while (etb_timeout-- && (buf_get_u32(etb_status_reg->value, 3, 1) == 0))
{
- frame++;
- continue;
+ /* wait for data formatter idle */
+ etb_get_reg(etb_status_reg);
}
-
- /* TRACEPKT is valid if this isn't a TD nor a TRIGGER cycle */
- if (((PIPESTAT(trace_data[frame]) != 0x7) && (PIPESTAT(trace_data[frame]) != 0x6))
- && !((apo == 0) && (!TRACESYNC(trace_data[frame]))))
+
+ if (etb_timeout == 0)
+ {
+ ERROR("AcqComp set but DFEmpty won't go high, ETB status: 0x%x",
+ buf_get_u32(etb_status_reg->value, 0, etb_status_reg->size));
+ }
+
+ if (!(etm_ctx->capture_status && TRACE_TRIGGERED))
{
- if (*port_half == 0)
- {
- *packet = TRACEPKT(trace_data[frame]) & 0xff;
- *port_half = 1;
- }
- else
- {
- *packet = (TRACEPKT(trace_data[frame]) & 0xff00) >> 8;
- *port_half = 0;
- frame++;
- }
- return frame;
+ ERROR("trace completed, but no trigger condition detected");
}
+
+ etm_ctx->capture_status &= ~TRACE_RUNNING;
+ etm_ctx->capture_status |= TRACE_COMPLETED;
}
- frame++;
}
- /* we reached the end of the trace without finding the packet we're looking for
- * tracing is finished
- */
- return -1;
+ return etm_ctx->capture_status;
}
-int handle_arm7_9_etb_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+int etb_read_trace(etm_context_t *etm_ctx)
{
- int retval;
- target_t *target = get_current_target(cmd_ctx);
- armv4_5_common_t *armv4_5;
- arm7_9_common_t *arm7_9;
- int i, j, k;
+ etb_t *etb = etm_ctx->capture_driver_priv;
int first_frame = 0;
- int last_frame;
- int addressbits_valid = 0;
- u32 address = 0x0;
- u32 *trace_data;
- int port_half = 0;
- int last_instruction = -1;
- u8 branch_reason;
- u8 packet;
- char trace_output[256];
- int trace_output_len;
- u8 apo;
-
- if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
- {
- command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
- return ERROR_OK;
- }
+ int num_frames = etb->ram_depth;
+ u32 *trace_data = NULL;
+ int i, j;
+
+ etb_read_reg(&etb->reg_cache->reg_list[ETB_STATUS]);
+ etb_read_reg(&etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER]);
+ jtag_execute_queue();
- if (!arm7_9->etb)
+ /* check if we overflowed, and adjust first frame of the trace accordingly
+ * if we didn't overflow, read only up to the frame that would be written next,
+ * i.e. don't read invalid entries
+ */
+ if (buf_get_u32(etb->reg_cache->reg_list[ETB_STATUS].value, 0, 1))
{
- command_print(cmd_ctx, "no ETB configured for current target");
- return ERROR_OK;
+ first_frame = buf_get_u32(etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
}
-
- if (!(arm7_9->etb->RAM_depth && arm7_9->etb->RAM_width))
+ else
{
- /* identify ETB RAM depth and width */
- etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_DEPTH]);
- etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_WIDTH]);
- jtag_execute_queue();
-
- arm7_9->etb->RAM_depth = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_DEPTH].value, 0, 32);
- arm7_9->etb->RAM_width = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WIDTH].value, 0, 32);
+ num_frames = buf_get_u32(etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
}
- trace_data = malloc(sizeof(u32) * arm7_9->etb->RAM_depth);
-
- etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_STATUS]);
- etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER]);
- jtag_execute_queue();
-
- /* check if we overflowed, and adjust first and last frame of the trace accordingly */
- if (buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_STATUS].value, 1, 1))
+ etb_write_reg(&etb->reg_cache->reg_list[ETB_RAM_READ_POINTER], first_frame);
+
+ /* read data into temporary array for unpacking */
+ trace_data = malloc(sizeof(u32) * num_frames);
+ etb_read_ram(etb, trace_data, num_frames);
+
+ if (etm_ctx->trace_depth > 0)
{
- first_frame = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
+ free(etm_ctx->trace_data);
}
- last_frame = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32) - 1;
-
- etb_write_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_READ_POINTER], first_frame);
+ if ((etm_ctx->portmode & ETM_PORT_MODE_MASK) == ETM_PORT_DEMUXED)
+ etm_ctx->trace_depth = num_frames * 2;
+ else
+ etm_ctx->trace_depth = num_frames;
- /* read trace data from ETB */
- i = first_frame;
- j = 0;
- do {
- etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_DATA]);
- jtag_execute_queue();
- trace_data[j++] = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_DATA].value, 0, 32);
- i++;
- } while ((i % arm7_9->etb->RAM_depth) != (first_frame % arm7_9->etb->RAM_depth));
+ etm_ctx->trace_data= malloc(sizeof(etmv1_trace_data_t) * etm_ctx->trace_depth);
- for (i = 0, j = 0; i < arm7_9->etb->RAM_depth; i++)
+ for (i = 0, j = 0; i < num_frames; i++)
{
- int trigger = 0;
-
- trace_output_len = 0;
-
- /* catch trigger, actual PIPESTAT is encoded in TRACEPKT[2:0] */
- if (PIPESTAT(trace_data[i]) == 0x6)
+ if ((etm_ctx->portmode & ETM_PORT_MODE_MASK) == ETM_PORT_DEMUXED)
{
- trigger = 1;
- trace_data[i] &= ~0x7;
- trace_data[i] |= TRACEPKT(trace_data[i]) & 0x7;
- }
-
- if (addressbits_valid == 32)
- {
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "%i: 0x%8.8x %s", i, address, (trigger) ? "(TRIGGER) " : "");
- }
- else if (addressbits_valid != 0)
- {
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "%i: 0x...%x %s", i, address, (trigger) ? "(TRIGGER) " : "");
+ etm_ctx->trace_data[j].pipestat = trace_data[i] & 0x7;
+ etm_ctx->trace_data[j].packet = (trace_data[i] & 0x7f8) >> 3;
+ etm_ctx->trace_data[j].tracesync = (trace_data[i] & 0x800) >> 11;
+
+ etm_ctx->trace_data[j+1].pipestat = (trace_data[i] & 0x7000) >> 12;
+ etm_ctx->trace_data[j+1].packet = (trace_data[i] & 0x7f8000) >> 15;
+ etm_ctx->trace_data[j+1].tracesync = (trace_data[i] & 0x800000) >> 23;
+
+ j += 2;
}
else
{
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "%i: 0xUNK %s", i, (trigger) ? "(TRIGGER) " : "");
- }
-
- switch (PIPESTAT(trace_data[i]))
- {
- case 0x0:
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "IE");
- break;
- case 0x1:
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "ID");
- break;
- case 0x2:
- /* Instruction exectued - TRACEPKT might be valid, but belongs to another cycle */
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "IN");
- break;
- case 0x3:
- /* WAIT cycle - TRACEPKT is valid, but belongs to another cycle */
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "WT");
- break;
- case 0x4:
- /* following a branch two APO cycles are output on PIPESTAT[1:0]
- * but another BE/BD could overwrite the current branch,
- * or a trigger could cause the APO to be output on TRACEPKT[1:0]
- */
- if ((PIPESTAT(trace_data[i + 1]) == 0x4)
- || (PIPESTAT(trace_data[i + 1]) == 0x5))
- {
- /* another branch occured, we ignore this one */
- j = (j < i + 1) ? i + 1 : j;
- break;
- }
- else if (PIPESTAT(trace_data[i + 1]) == 0x6)
- {
- apo = TRACEPKT(trace_data[i + 1]) & 0x3;
- }
- else
- {
- apo = PIPESTAT(trace_data[i + 1]) & 0x3;
- }
-
- if ((PIPESTAT(trace_data[i + 2]) == 0x4)
- || (PIPESTAT(trace_data[i + 2]) == 0x5))
- {
- j = (j < i + 2) ? i + 1 : j;
- i = i + 1;
- break;
- }
- else if (PIPESTAT(trace_data[i + 2]) == 0x6)
- {
- apo |= (TRACEPKT(trace_data[i + 2]) & 0x3) << 2;
- }
- else
- {
- apo = (PIPESTAT(trace_data[i + 1]) & 0x3) << 2;
- }
-
- branch_reason = -1;
- k = 0;
- do
- {
- if ((j = etmv1_next_packet(arm7_9->etb->RAM_depth, trace_data, j, &port_half, apo, &packet)) != -1)
- {
- address &= ~(0x7f << (k * 7));
- address |= (packet & 0x7f) << (k * 7);
- }
- else
- {
- break;
- }
- k++;
- } while ((k < 5) && (packet & 0x80));
-
- if (addressbits_valid < ((k * 7 > 32) ? 32 : k * 7))
- addressbits_valid = (k * 7 > 32) ? 32 : k * 7;
-
- if (k == 5)
- {
- branch_reason = (packet & 0x7) >> 4;
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "BE 0x%x (/%i) (%s)", address, addressbits_valid, etmv1_branch_reason_string[branch_reason]);
- }
- else
- {
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "BE 0x%x (/%i)", address, addressbits_valid);
- }
-
- break;
- case 0x5:
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "BD");
- break;
- case 0x6:
- /* We catch the trigger event before we get here */
- ERROR("TR pipestat should have been caught earlier");
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "--");
- break;
- case 0x7:
- /* TRACE disabled - TRACEPKT = invalid */
- trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
- "TD");
- break;
- }
-
- /* PIPESTAT other than WT (b011) and TD (b111) mean we executed an instruction */
- if ((PIPESTAT(trace_data[i]) & 0x3) != 0x3)
- {
- last_instruction = i;
- address += 4;
+ etm_ctx->trace_data[j].pipestat = trace_data[i] & 0x7;
+ etm_ctx->trace_data[j].packet = (trace_data[i] & 0x7fff8) >> 3;
+ etm_ctx->trace_data[j].tracesync = (trace_data[i] & 0x80000) >> 19;
+
+ j += 1;
}
+ }
+
+ free(trace_data);
+
+ return ERROR_OK;
+}
- /* The group of packets for a particular instruction cannot start on or before any
- * previous functional PIPESTAT (IE, IN, ID, BE, or BD)
- */
- if (j < last_instruction)
- {
- j = last_instruction + 1;
- }
+int etb_start_capture(etm_context_t *etm_ctx)
+{
+ etb_t *etb = etm_ctx->capture_driver_priv;
+ u32 etb_ctrl_value = 0x1;
- /* restore trigger PIPESTAT to ensure TRACEPKT is ignored */
- if (trigger == 1)
+ if ((etm_ctx->portmode & ETM_PORT_MODE_MASK) == ETM_PORT_DEMUXED)
+ {
+ if ((etm_ctx->portmode & ETM_PORT_WIDTH_MASK) == ETM_PORT_16BIT)
{
- trace_data[i] &= ~0x7;
- trace_data[i] |= 0x6;
+ DEBUG("ETB can't run in demultiplexed mode with a 16-bit port");
+ return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
}
-
- command_print(cmd_ctx, "%s (raw: 0x%8.8x)", trace_output, trace_data[i]);
+ etb_ctrl_value |= 0x2;
}
+ if ((etm_ctx->portmode & ETM_PORT_MODE_MASK) == ETM_PORT_MUXED)
+ return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
+
+ etb_write_reg(&etb->reg_cache->reg_list[ETB_TRIGGER_COUNTER], 0x600);
+ etb_write_reg(&etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER], 0x0);
+ etb_write_reg(&etb->reg_cache->reg_list[ETB_CTRL], etb_ctrl_value);
+ jtag_execute_queue();
+
+ /* we're starting a new trace, initialize capture status */
+ etm_ctx->capture_status = TRACE_RUNNING;
+
+ return ERROR_OK;
+}
+
+int etb_stop_capture(etm_context_t *etm_ctx)
+{
+ etb_t *etb = etm_ctx->capture_driver_priv;
+ reg_t *etb_ctrl_reg = &etb->reg_cache->reg_list[ETB_CTRL];
+
+ etb_write_reg(etb_ctrl_reg, 0x0);
+ jtag_execute_queue();
+
+ /* trace stopped, just clear running flag, but preserve others */
+ etm_ctx->capture_status &= ~TRACE_RUNNING;
+
return ERROR_OK;
}
+
+etm_capture_driver_t etb_capture_driver =
+{
+ .name = "etb",
+ .register_commands = etb_register_commands,
+ .init = etb_init,
+ .status = etb_status,
+ .start_capture = etb_start_capture,
+ .stop_capture = etb_stop_capture,
+ .read_trace = etb_read_trace,
+};
#include "register.h"\r
#include "arm_jtag.h"\r
\r
+#include "etb.h"\r
+#include "etm.h"\r
+\r
/* ETB registers */\r
enum\r
{\r
\r
typedef struct etb_s\r
{\r
+ etm_context_t *etm_ctx;\r
int chain_pos;\r
int cur_scan_chain;\r
reg_cache_t *reg_cache;\r
\r
/* ETB parameters */\r
- int RAM_depth;\r
- int RAM_width;\r
+ int ram_depth;\r
+ int ram_width;\r
} etb_t;\r
\r
typedef struct etb_reg_s\r
etb_t *etb;\r
} etb_reg_t;\r
\r
+extern etm_capture_driver_t etb_capture_driver;\r
+\r
extern reg_cache_t* etb_build_reg_cache(etb_t *etb);\r
extern int etb_read_reg(reg_t *reg);\r
extern int etb_write_reg(reg_t *reg, u32 value);\r
extern int etb_set_reg(reg_t *reg, u32 value);\r
extern int etb_set_reg_w_exec(reg_t *reg, u8 *buf);\r
\r
-extern int etb_register_commands(struct command_context_s *cmd_ctx, command_t *arm7_9_cmd);\r
+extern int etb_register_commands(struct command_context_s *cmd_ctx);\r
\r
#endif /* ETB_H */\r
#include "config.h"
#endif
+#include <string.h>
+
#include "etm.h"
+#include "etb.h"
#include "armv4_5.h"
#include "arm7_9_common.h"
#include "target.h"
#include "register.h"
#include "jtag.h"
+#include "fileio.h"
#include <stdlib.h>
+/* ETM register access functionality
+ *
+ */
+
bitfield_desc_t etm_comms_ctrl_bitfield_desc[] =
{
{"R", 1},
int etm_write_reg(reg_t *reg, u32 value);
int etm_read_reg(reg_t *reg);
-reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg)
+command_t *etm_cmd = NULL;
+
+reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, etm_context_t *etm_ctx)
{
reg_cache_t *reg_cache = malloc(sizeof(reg_cache_t));
reg_t *reg_list = NULL;
etm_reg_t *arch_info = NULL;
int num_regs = sizeof(etm_reg_arch_info)/sizeof(int);
int i;
+ u32 etm_ctrl_value;
/* register a register arch-type for etm registers only once */
if (etm_reg_arch_type == -1)
arch_info[i].addr = etm_reg_arch_info[i];
arch_info[i].jtag_info = jtag_info;
}
+
+ /* initialize some ETM control register settings */
+ etm_get_reg(®_list[ETM_CTRL]);
+ etm_ctrl_value = buf_get_u32(reg_list[ETM_CTRL].value, 0, reg_list[ETM_CTRL].size);
+
+ /* clear the ETM powerdown bit (0) */
+ etm_ctrl_value &= ~0x1;
+
+ /* configure port width (6:4), mode (17:16) and clocking (13) */
+ etm_ctrl_value = (etm_ctrl_value &
+ ~ETM_PORT_WIDTH_MASK & ~ETM_PORT_MODE_MASK & ~ETM_PORT_CLOCK_MASK)
+ | etm_ctx->portmode;
+
+ buf_set_u32(reg_list[ETM_CTRL].value, 0, reg_list[ETM_CTRL].size, etm_ctrl_value);
+ etm_store_reg(®_list[ETM_CTRL]);
+
+ /* the ETM might have an ETB connected */
+ if (strcmp(etm_ctx->capture_driver->name, "etb") == 0)
+ {
+ etb_t *etb = etm_ctx->capture_driver_priv;
+
+ if (!etb)
+ {
+ ERROR("etb selected as etm capture driver, but no ETB configured");
+ return ERROR_OK;
+ }
+
+ reg_cache->next = etb_build_reg_cache(etb);
+
+ etb->reg_cache = reg_cache->next;
+
+ if (etm_ctx->capture_driver->init(etm_ctx) != ERROR_OK)
+ {
+ ERROR("ETM capture driver initialization failed");
+ exit(-1);
+ }
+ }
+
return reg_cache;
}
return etm_write_reg(reg, buf_get_u32(reg->value, 0, reg->size));
}
+/* ETM trace analysis functionality
+ *
+ */
+extern etm_capture_driver_t etb_capture_driver;
+
+etm_capture_driver_t *etm_capture_drivers[] =
+{
+ &etb_capture_driver,
+ NULL
+};
+
+char *etmv1v1_branch_reason_strings[] =
+{
+ "normal PC change",
+ "tracing enabled",
+ "trace restarted after overflow",
+ "exit from debug",
+ "periodic synchronization",
+ "reserved",
+ "reserved",
+ "reserved",
+};
+
+int etmv1_next_packet(etm_context_t *ctx, u8 *packet)
+{
+
+
+ return ERROR_OK;
+}
+
+int etmv1_analyse_trace(etm_context_t *ctx)
+{
+ ctx->pipe_index = 0;
+ ctx->data_index = 0;
+
+ while (ctx->pipe_index < ctx->trace_depth)
+ {
+ switch (ctx->trace_data[ctx->pipe_index].pipestat)
+ {
+ case STAT_IE:
+ case STAT_ID:
+ break;
+ case STAT_IN:
+ DEBUG("IN");
+ break;
+ case STAT_WT:
+ DEBUG("WT");
+ break;
+ case STAT_BE:
+ case STAT_BD:
+ break;
+ case STAT_TD:
+ /* TODO: in cycle accurate trace, we have to count cycles */
+ DEBUG("TD");
+ break;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int handle_etm_tracemode_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etmv1_tracemode_t tracemode;
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!arm7_9->etm_ctx)
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ tracemode = arm7_9->etm_ctx->tracemode;
+
+ if (argc == 3)
+ {
+ if (strcmp(args[0], "none") == 0)
+ {
+ tracemode = ETMV1_TRACE_NONE;
+ }
+ else if (strcmp(args[0], "data") == 0)
+ {
+ tracemode = ETMV1_TRACE_DATA;
+ }
+ else if (strcmp(args[0], "address") == 0)
+ {
+ tracemode = ETMV1_TRACE_ADDR;
+ }
+ else if (strcmp(args[0], "all") == 0)
+ {
+ tracemode = ETMV1_TRACE_DATA | ETMV1_TRACE_ADDR;
+ }
+ else
+ {
+ command_print(cmd_ctx, "invalid option '%s'", args[0]);
+ return ERROR_OK;
+ }
+
+ switch (strtol(args[1], NULL, 0))
+ {
+ case 0:
+ tracemode |= ETMV1_CONTEXTID_NONE;
+ break;
+ case 8:
+ tracemode |= ETMV1_CONTEXTID_8;
+ break;
+ case 16:
+ tracemode |= ETMV1_CONTEXTID_16;
+ break;
+ case 32:
+ tracemode |= ETMV1_CONTEXTID_32;
+ break;
+ default:
+ command_print(cmd_ctx, "invalid option '%s'", args[1]);
+ return ERROR_OK;
+ }
+
+ if (strcmp(args[2], "enable") == 0)
+ {
+ tracemode |= ETMV1_CYCLE_ACCURATE;
+ }
+ else if (strcmp(args[2], "disable") == 0)
+ {
+ tracemode |= 0;
+ }
+ else
+ {
+ command_print(cmd_ctx, "invalid option '%s'", args[2]);
+ return ERROR_OK;
+ }
+ }
+ else if (argc != 0)
+ {
+ command_print(cmd_ctx, "usage: configure trace mode <none|data|address|all> <context id bits> <enable|disable cycle accurate>");
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "current tracemode configuration:");
+
+ switch (tracemode & ETMV1_TRACE_MASK)
+ {
+ case ETMV1_TRACE_NONE:
+ command_print(cmd_ctx, "data tracing: none");
+ break;
+ case ETMV1_TRACE_DATA:
+ command_print(cmd_ctx, "data tracing: data only");
+ break;
+ case ETMV1_TRACE_ADDR:
+ command_print(cmd_ctx, "data tracing: address only");
+ break;
+ case ETMV1_TRACE_DATA | ETMV1_TRACE_ADDR:
+ command_print(cmd_ctx, "data tracing: address and data");
+ break;
+ }
+
+ switch (tracemode & ETMV1_CONTEXTID_MASK)
+ {
+ case ETMV1_CONTEXTID_NONE:
+ command_print(cmd_ctx, "contextid tracing: none");
+ break;
+ case ETMV1_CONTEXTID_8:
+ command_print(cmd_ctx, "contextid tracing: 8 bit");
+ break;
+ case ETMV1_CONTEXTID_16:
+ command_print(cmd_ctx, "contextid tracing: 16 bit");
+ break;
+ case ETMV1_CONTEXTID_32:
+ command_print(cmd_ctx, "contextid tracing: 32 bit");
+ break;
+ }
+
+ if (tracemode & ETMV1_CYCLE_ACCURATE)
+ {
+ command_print(cmd_ctx, "cycle-accurate tracing enabled");
+ }
+ else
+ {
+ command_print(cmd_ctx, "cycle-accurate tracing disabled");
+ }
+
+ /* only update ETM_CTRL register if tracemode changed */
+ if (arm7_9->etm_ctx->tracemode != tracemode)
+ {
+ reg_t *etm_ctrl_reg = &arm7_9->etm_ctx->reg_cache->reg_list[ETM_CTRL];
+
+ etm_get_reg(etm_ctrl_reg);
+
+ buf_set_u32(etm_ctrl_reg->value, 2, 2, tracemode & ETMV1_TRACE_MASK);
+ buf_set_u32(etm_ctrl_reg->value, 14, 2, (tracemode & ETMV1_CONTEXTID_MASK) >> 4);
+ buf_set_u32(etm_ctrl_reg->value, 12, 1, (tracemode & ETMV1_CYCLE_ACCURATE) >> 8);
+
+ etm_store_reg(etm_ctrl_reg);
+
+ arm7_9->etm_ctx->tracemode = tracemode;
+
+ /* invalidate old trace data */
+ arm7_9->etm_ctx->capture_status = TRACE_IDLE;
+ if (arm7_9->etm_ctx->trace_depth > 0)
+ {
+ free(arm7_9->etm_ctx->trace_data);
+ }
+ arm7_9->etm_ctx->trace_depth = 0;
+ }
+
+ return ERROR_OK;
+}
+
+int handle_etm_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etm_portmode_t portmode = 0x0;
+ etm_context_t *etm_ctx = malloc(sizeof(etm_context_t));
+ int i;
+
+ if (argc != 5)
+ {
+ ERROR("incomplete 'etm config <target> <port_width> <port_mode> <clocking> <capture_driver>' command");
+ exit(-1);
+ }
+
+ target = get_target_by_num(strtoul(args[0], NULL, 0));
+
+ if (!target)
+ {
+ ERROR("target number '%s' not defined", args[0]);
+ exit(-1);
+ }
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ switch (strtoul(args[1], NULL, 0))
+ {
+ case 4:
+ portmode |= ETM_PORT_4BIT;
+ break;
+ case 8:
+ portmode |= ETM_PORT_8BIT;
+ break;
+ case 16:
+ portmode |= ETM_PORT_16BIT;
+ break;
+ default:
+ command_print(cmd_ctx, "unsupported ETM port width '%s', must be 4, 8 or 16", args[1]);
+ return ERROR_OK;
+ }
+
+ if (strcmp("normal", args[2]) == 0)
+ {
+ portmode |= ETM_PORT_NORMAL;
+ }
+ else if (strcmp("multiplexed", args[2]) == 0)
+ {
+ portmode |= ETM_PORT_MUXED;
+ }
+ else if (strcmp("demultiplexed", args[2]) == 0)
+ {
+ portmode |= ETM_PORT_DEMUXED;
+ }
+ else
+ {
+ command_print(cmd_ctx, "unsupported ETM port mode '%s', must be 'normal', 'multiplexed' or 'demultiplexed'", args[2]);
+ return ERROR_OK;
+ }
+
+ if (strcmp("half", args[3]) == 0)
+ {
+ portmode |= ETM_PORT_HALF_CLOCK;
+ }
+ else if (strcmp("full", args[3]) == 0)
+ {
+ portmode |= ETM_PORT_FULL_CLOCK;
+ }
+ else
+ {
+ command_print(cmd_ctx, "unsupported ETM port clocking '%s', must be 'full' or 'half'", args[3]);
+ return ERROR_OK;
+ }
+
+ for (i=0; etm_capture_drivers[i]; i++)
+ {
+ if (strcmp(args[4], etm_capture_drivers[i]->name) == 0)
+ {
+ if (etm_capture_drivers[i]->register_commands(cmd_ctx) != ERROR_OK)
+ {
+ free(etm_ctx);
+ exit(-1);
+ }
+
+ etm_ctx->capture_driver = etm_capture_drivers[i];
+
+ break;
+ }
+ }
+
+ etm_ctx->trace_data = NULL;
+ etm_ctx->trace_depth = 0;
+ etm_ctx->portmode = portmode;
+ etm_ctx->tracemode = 0x0;
+ etm_ctx->core_state = ARMV4_5_STATE_ARM;
+ etm_ctx->pipe_index = 0;
+ etm_ctx->data_index = 0;
+ etm_ctx->current_pc = 0x0;
+ etm_ctx->pc_ok = 0;
+ etm_ctx->last_branch = 0x0;
+ etm_ctx->last_ptr = 0x0;
+ etm_ctx->context_id = 0x0;
+
+ arm7_9->etm_ctx = etm_ctx;
+
+ etm_register_user_commands(cmd_ctx);
+
+ return ERROR_OK;
+}
+
+int handle_etm_status_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ trace_status_t trace_status;
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!arm7_9->etm_ctx)
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ trace_status = arm7_9->etm_ctx->capture_driver->status(arm7_9->etm_ctx);
+
+ if (trace_status == TRACE_IDLE)
+ {
+ command_print(cmd_ctx, "tracing is idle");
+ }
+ else
+ {
+ static char *completed = " completed";
+ static char *running = " is running";
+ static char *overflowed = ", trace overflowed";
+ static char *triggered = ", trace triggered";
+
+ command_print(cmd_ctx, "trace collection%s%s%s",
+ (trace_status & TRACE_RUNNING) ? running : completed,
+ (trace_status & TRACE_OVERFLOWED) ? overflowed : "",
+ (trace_status & TRACE_TRIGGERED) ? triggered : "");
+
+ if (arm7_9->etm_ctx->trace_depth > 0)
+ {
+ command_print(cmd_ctx, "%i frames of trace data read", arm7_9->etm_ctx->trace_depth);
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int handle_etm_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ fileio_t file;
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etm_context_t *etm_ctx;
+ u32 size_written;
+
+ if (argc != 1)
+ {
+ command_print(cmd_ctx, "usage: etm dump <file>");
+ return ERROR_OK;
+ }
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!(etm_ctx = arm7_9->etm_ctx))
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ if (etm_ctx->capture_driver->status == TRACE_IDLE)
+ {
+ command_print(cmd_ctx, "trace capture wasn't enabled, no trace data captured");
+ return ERROR_OK;
+ }
+
+ if (etm_ctx->capture_driver->status(etm_ctx) & TRACE_RUNNING)
+ {
+ /* TODO: if on-the-fly capture is to be supported, this needs to be changed */
+ command_print(cmd_ctx, "trace capture not completed");
+ return ERROR_OK;
+ }
+
+ /* read the trace data if it wasn't read already */
+ if (etm_ctx->trace_depth == 0)
+ etm_ctx->capture_driver->read_trace(etm_ctx);
+
+ if (fileio_open(&file, args[0], FILEIO_WRITE, FILEIO_BINARY) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "file open error: %s", file.error_str);
+ return ERROR_OK;
+ }
+
+ //fileio_write(&file, etm_ctx->trace_depth * 4, (u8*)etm_ctx->trace_data, &size_written);
+
+ fileio_close(&file);
+
+ return ERROR_OK;
+}
+
+int handle_etm_load_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ fileio_t file;
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etm_context_t *etm_ctx;
+ u32 size_read;
+
+ if (argc != 1)
+ {
+ command_print(cmd_ctx, "usage: etm load <file>");
+ return ERROR_OK;
+ }
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!(etm_ctx = arm7_9->etm_ctx))
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ if (etm_ctx->capture_driver->status(etm_ctx) & TRACE_RUNNING)
+ {
+ command_print(cmd_ctx, "trace capture running, stop first");
+ return ERROR_OK;
+ }
+
+ if (fileio_open(&file, args[0], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "file open error: %s", file.error_str);
+ return ERROR_OK;
+ }
+
+ if (file.size % 4)
+ {
+ command_print(cmd_ctx, "size isn't a multiple of 4, no valid trace data");
+ return ERROR_OK;
+ }
+
+ if (etm_ctx->trace_depth > 0)
+ {
+ free(etm_ctx->trace_data);
+ }
+
+ //fileio_read(&file, file.size, (u8*)etm_ctx->trace_data, &size_read);
+ etm_ctx->trace_depth = file.size / 4;
+ etm_ctx->capture_status = TRACE_COMPLETED;
+
+ fileio_close(&file);
+
+ return ERROR_OK;
+}
+
+int handle_etm_start_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etm_context_t *etm_ctx;
+ reg_t *etm_ctrl_reg;
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!(etm_ctx = arm7_9->etm_ctx))
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ /* invalidate old tracing data */
+ arm7_9->etm_ctx->capture_status = TRACE_IDLE;
+ if (arm7_9->etm_ctx->trace_depth > 0)
+ {
+ free(arm7_9->etm_ctx->trace_data);
+ }
+ arm7_9->etm_ctx->trace_depth = 0;
+
+ etm_ctrl_reg = &arm7_9->etm_ctx->reg_cache->reg_list[ETM_CTRL];
+ etm_get_reg(etm_ctrl_reg);
+
+ /* Clear programming bit (10), set port selection bit (11) */
+ buf_set_u32(etm_ctrl_reg->value, 10, 2, 0x2);
+
+ etm_store_reg(etm_ctrl_reg);
+ jtag_execute_queue();
+
+ etm_ctx->capture_driver->start_capture(etm_ctx);
+
+ return ERROR_OK;
+}
+
+int handle_etm_stop_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etm_context_t *etm_ctx;
+ reg_t *etm_ctrl_reg;
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!(etm_ctx = arm7_9->etm_ctx))
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ etm_ctrl_reg = &arm7_9->etm_ctx->reg_cache->reg_list[ETM_CTRL];
+ etm_get_reg(etm_ctrl_reg);
+
+ /* Set programming bit (10), clear port selection bit (11) */
+ buf_set_u32(etm_ctrl_reg->value, 10, 2, 0x1);
+
+ etm_store_reg(etm_ctrl_reg);
+ jtag_execute_queue();
+
+ etm_ctx->capture_driver->stop_capture(etm_ctx);
+
+ return ERROR_OK;
+}
+
+int handle_etm_analyse_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target;
+ armv4_5_common_t *armv4_5;
+ arm7_9_common_t *arm7_9;
+ etm_context_t *etm_ctx;
+
+ target = get_current_target(cmd_ctx);
+
+ if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
+ return ERROR_OK;
+ }
+
+ if (!(etm_ctx = arm7_9->etm_ctx))
+ {
+ command_print(cmd_ctx, "current target doesn't have an ETM configured");
+ return ERROR_OK;
+ }
+
+ etmv1_analyse_trace(etm_ctx);
+
+ return ERROR_OK;
+}
+
+int etm_register_commands(struct command_context_s *cmd_ctx)
+{
+ etm_cmd = register_command(cmd_ctx, NULL, "etm", NULL, COMMAND_ANY, "Embedded Trace Macrocell");
+
+ register_command(cmd_ctx, etm_cmd, "config", handle_etm_config_command, COMMAND_CONFIG, NULL);
+
+ return ERROR_OK;
+}
+
+int etm_register_user_commands(struct command_context_s *cmd_ctx)
+{
+ register_command(cmd_ctx, etm_cmd, "tracemode", handle_etm_tracemode_command,
+ COMMAND_EXEC, "configure trace mode <none|data|address|all> <context id bits> <enable|disable cycle accurate>");
+
+ register_command(cmd_ctx, etm_cmd, "status", handle_etm_status_command,
+ COMMAND_EXEC, "display current target's ETM status");
+ register_command(cmd_ctx, etm_cmd, "start", handle_etm_start_command,
+ COMMAND_EXEC, "start ETM trace collection");
+ register_command(cmd_ctx, etm_cmd, "stop", handle_etm_stop_command,
+ COMMAND_EXEC, "stop ETM trace collection");
+
+ register_command(cmd_ctx, etm_cmd, "analyze", handle_etm_stop_command,
+ COMMAND_EXEC, "anaylze collected ETM trace");
+
+ register_command(cmd_ctx, etm_cmd, "dump", handle_etm_dump_command,
+ COMMAND_EXEC, "dump captured trace data <file>");
+ register_command(cmd_ctx, etm_cmd, "load", handle_etm_load_command,
+ COMMAND_EXEC, "load trace data for analysis <file>");
+
+ return ERROR_OK;
+}
/***************************************************************************\r
- * Copyright (C) 2005 by Dominic Rath *\r
+ * Copyright (C) 2005, 2007 by Dominic Rath *\r
* Dominic.Rath@gmx.de *\r
* *\r
+ * Copyright (C) 2007 by Vincent Palatin *\r
+ * vincent.palatin_openocd@m4x.org *\r
+ * *\r
* This program is free software; you can redistribute it and/or modify *\r
* it under the terms of the GNU General Public License as published by *\r
* the Free Software Foundation; either version 2 of the License, or *\r
#ifndef ETM_H\r
#define ETM_H\r
\r
+#include "trace.h"\r
#include "target.h"\r
#include "register.h"\r
#include "arm_jtag.h"\r
\r
-// ETM registers (V1.2 protocol)\r
+#include "armv4_5.h"\r
+\r
+/* ETM registers (V1.3 protocol) */\r
enum\r
{\r
ETM_CTRL = 0x00,\r
ETM_CONTEXTID_COMPARATOR_MASK = 0x6f, \r
};\r
\r
-\r
typedef struct etm_reg_s\r
{\r
int addr;\r
arm_jtag_t *jtag_info;\r
} etm_reg_t;\r
\r
-extern reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg);\r
+typedef enum\r
+{\r
+ /* Port width */\r
+ ETM_PORT_4BIT = 0x00,\r
+ ETM_PORT_8BIT = 0x10,\r
+ ETM_PORT_16BIT = 0x20,\r
+ ETM_PORT_WIDTH_MASK = 0x70, \r
+ /* Port modes */\r
+ ETM_PORT_NORMAL = 0x00000,\r
+ ETM_PORT_MUXED = 0x10000,\r
+ ETM_PORT_DEMUXED = 0x20000,\r
+ ETM_PORT_MODE_MASK = 0x30000,\r
+ /* Clocking modes */\r
+ ETM_PORT_FULL_CLOCK = 0x0000,\r
+ ETM_PORT_HALF_CLOCK = 0x1000,\r
+ ETM_PORT_CLOCK_MASK = 0x1000,\r
+} etm_portmode_t;\r
+\r
+typedef enum\r
+{\r
+ /* Data trace */\r
+ ETMV1_TRACE_NONE = 0x00,\r
+ ETMV1_TRACE_DATA = 0x01,\r
+ ETMV1_TRACE_ADDR = 0x02,\r
+ ETMV1_TRACE_MASK = 0x03,\r
+ /* ContextID */\r
+ ETMV1_CONTEXTID_NONE = 0x00,\r
+ ETMV1_CONTEXTID_8 = 0x10,\r
+ ETMV1_CONTEXTID_16 = 0x20,\r
+ ETMV1_CONTEXTID_32 = 0x30,\r
+ ETMV1_CONTEXTID_MASK = 0x30,\r
+ /* Misc */\r
+ ETMV1_CYCLE_ACCURATE = 0x100\r
+} etmv1_tracemode_t;\r
+\r
+/* forward-declare ETM context */\r
+struct etm_context_s;\r
+\r
+typedef struct etm_capture_driver_s\r
+{\r
+ char *name;\r
+ int (*register_commands)(struct command_context_s *cmd_ctx);\r
+ int (*init)(struct etm_context_s *etm_ctx);\r
+ trace_status_t (*status)(struct etm_context_s *etm_ctx);\r
+ int (*read_trace)(struct etm_context_s *etm_ctx);\r
+ int (*start_capture)(struct etm_context_s *etm_ctx);\r
+ int (*stop_capture)(struct etm_context_s *etm_ctx);\r
+} etm_capture_driver_t;\r
+\r
+typedef struct etmv1_trace_data_s\r
+{\r
+ u8 pipestat; /* pipeline cycle this packet belongs to */\r
+ u16 packet; /* packet data (4, 8 or 16 bit) */\r
+ int tracesync; /* 1 if tracesync was set on this packet */\r
+} etmv1_trace_data_t;\r
+\r
+/* describe a trace context\r
+ * if support for ETMv2 or ETMv3 is to be implemented,\r
+ * this will have to be split into version independent elements\r
+ * and a version specific part\r
+ */\r
+typedef struct etm_context_s\r
+{\r
+ reg_cache_t *reg_cache; /* ETM register cache */\r
+ etm_capture_driver_t *capture_driver; /* driver used to access ETM data */\r
+ void *capture_driver_priv; /* capture driver private data */\r
+ trace_status_t capture_status; /* current state of capture run */ \r
+ etmv1_trace_data_t *trace_data; /* trace data */\r
+ u32 trace_depth; /* number of trace cycles to be analyzed, 0 if no trace data available */\r
+ etm_portmode_t portmode; /* normal, multiplexed or demultiplexed */\r
+ etmv1_tracemode_t tracemode; /* type of information the trace contains (data, addres, contextID, ...) */ \r
+ armv4_5_state_t core_state; /* current core state (ARM, Thumb, Jazelle) */\r
+// trace_image_provider_t image_provider; /* source for target opcodes */\r
+ u32 pipe_index; /* current trace cycle */\r
+ u32 data_index; /* cycle holding next data packet */\r
+ u32 current_pc; /* current program counter */\r
+ u32 pc_ok; /* full PC has been acquired */\r
+ u32 last_branch; /* last branch address output */ \r
+ u32 last_ptr; /* address of the last data access */\r
+ u32 context_id; /* context ID of the code being traced */\r
+} etm_context_t;\r
+\r
+/* PIPESTAT values */\r
+typedef enum\r
+{\r
+ STAT_IE = 0x0,\r
+ STAT_ID = 0x1,\r
+ STAT_IN = 0x2,\r
+ STAT_WT = 0x3,\r
+ STAT_BE = 0x4,\r
+ STAT_BD = 0x5,\r
+ STAT_TR = 0x6,\r
+ STAT_TD = 0x7\r
+} etmv1_pipestat_t;\r
+\r
+/* branch reason values */\r
+typedef enum\r
+{\r
+ BR_NORMAL = 0x0, /* Normal PC change : periodic synchro (ETMv1.1) */\r
+ BR_ENABLE = 0x1, /* Trace has been enabled */\r
+ BR_RESTART = 0x2, /* Trace restarted after a FIFO overflow */\r
+ BR_NODEBUG = 0x3, /* ARM has exited for debug state */\r
+ BR_PERIOD = 0x4, /* Peridioc synchronization point (ETM>=v1.2)*/\r
+ BR_RSVD5 = 0x5, /* reserved */\r
+ BR_RSVD6 = 0x6, /* reserved */\r
+ BR_RSVD7 = 0x7, /* reserved */\r
+} etmv1_branch_reason_t;\r
+\r
+extern char *etmv1v1_branch_reason_strings[];\r
+\r
+extern reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, etm_context_t *etm_ctx);\r
extern int etm_read_reg(reg_t *reg);\r
extern int etm_write_reg(reg_t *reg, u32 value);\r
extern int etm_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask);\r
extern int etm_set_reg(reg_t *reg, u32 value);\r
extern int etm_set_reg_w_exec(reg_t *reg, u8 *buf);\r
\r
+int etm_register_commands(struct command_context_s *cmd_ctx);\r
+int etm_register_user_commands(struct command_context_s *cmd_ctx);\r
+extern etm_context_t* etm_create_context(etm_portmode_t portmode, char *capture_driver_name);\r
+\r
+#define ERROR_ETM_INVALID_DRIVER (-1300)\r
+#define ERROR_ETM_PORTMODE_NOT_SUPPORTED (-1301)\r
+#define ERROR_ETM_CAPTURE_INIT_FAILED (-1302)\r
+\r
#endif /* ETM_H */\r
#include <time_support.h>
#include <fileio.h>
+#include <image.h>
int cli_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv);
u32 address;
u8 *buffer;
u32 buf_cnt;
- u32 binary_size;
-
- fileio_t file;
- enum fileio_pri_type pri_type = FILEIO_IMAGE;
- fileio_image_t image_info;
- enum fileio_sec_type sec_type;
+ u32 image_size;
+
+ image_t image;
duration_t duration;
char *duration_text;
return ERROR_OK;
}
- memset(&file, 0, sizeof(fileio_t));
- fileio_identify_image_type(&sec_type, (argc == 3) ? args[2] : NULL);
+ identify_image_type(&image.type, (argc == 3) ? args[2] : NULL);
- image_info.base_address = strtoul(args[1], NULL, 0);
- image_info.has_start_address = 0;
+ image.base_address_set = 1;
+ image.base_address = strtoul(args[1], NULL, 0);
+
+ image.start_address_set = 0;
buffer = malloc(128 * 1024);
duration_start_measure(&duration);
- if (fileio_open(&file, args[0], FILEIO_READ,
- pri_type, &image_info, sec_type) != ERROR_OK)
+ if (image_open(&image, args[0], FILEIO_READ) != ERROR_OK)
{
- command_print(cmd_ctx, "load_image error: %s", file.error_str);
+ command_print(cmd_ctx, "load_image error: %s", image.error_str);
return ERROR_OK;
}
- binary_size = file.size;
- address = image_info.base_address;
- while ((binary_size > 0) &&
- (fileio_read(&file, 128 * 1024, buffer, &buf_cnt) == ERROR_OK))
+ image_size = image.size;
+ address = image.base_address;
+
+ while ((image_size > 0) &&
+ (image_read(&image, 128 * 1024, buffer, &buf_cnt) == ERROR_OK))
{
target_write_buffer(target, address, buf_cnt, buffer);
address += buf_cnt;
- binary_size -= buf_cnt;
+ image_size -= buf_cnt;
}
free(buffer);
duration_stop_measure(&duration, &duration_text);
- command_print(cmd_ctx, "downloaded %lli byte in %s", file.size, duration_text);
+ command_print(cmd_ctx, "downloaded %u byte in %s", image.size, duration_text);
free(duration_text);
- fileio_close(&file);
+ image_close(&image);
return ERROR_OK;
int handle_dump_image_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
- fileio_t file;
- fileio_image_t image_info;
+ fileio_t fileio;
u32 address;
u32 size;
return ERROR_OK;
}
- image_info.base_address = address;
- image_info.has_start_address = 0;
-
- if (fileio_open(&file, args[0], FILEIO_WRITE,
- FILEIO_IMAGE, &image_info, FILEIO_PLAIN) != ERROR_OK)
+ if (fileio_open(&fileio, args[0], FILEIO_WRITE, FILEIO_BINARY) != ERROR_OK)
{
- command_print(cmd_ctx, "dump_image error: %s", file.error_str);
+ command_print(cmd_ctx, "dump_image error: %s", fileio.error_str);
return ERROR_OK;
}
u32 this_run_size = (size > 560) ? 560 : size;
target->type->read_memory(target, address, 4, this_run_size / 4, buffer);
- fileio_write(&file, this_run_size, buffer, &size_written);
+ fileio_write(&fileio, this_run_size, buffer, &size_written);
size -= this_run_size;
address += this_run_size;
}
- fileio_close(&file);
+ fileio_close(&fileio);
duration_stop_measure(&duration, &duration_text);
- command_print(cmd_ctx, "dumped %lli byte in %s", file.size, duration_text);
+ command_print(cmd_ctx, "dumped %lli byte in %s", fileio.size, duration_text);
free(duration_text);
return ERROR_OK;