- split fileio handling into fileio part and image handling
- reworked etm/etb into a generic etm part with trace capture drivers (currently only etb supported) - added XScale debug handler binary to repository - added Thumb disassembling (thanks to Vincent Palatin for this patch) - added support for non-CFI compatible flashes to cfi driver (currently only SST39VFxxx devices supported) This checkin is experimental, not suitable for general use git-svn-id: svn://svn.berlios.de/openocd/trunk@155 b42882b7-edfa-0310-969c-e2dbd0fdcd60__archive__
parent
e8af4de0a7
commit
237e894805
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
|
|
410
src/flash/cfi.c
410
src/flash/cfi.c
|
@ -66,17 +66,33 @@ flash_driver_t cfi_flash =
|
|||
.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}
|
||||
|
@ -421,6 +437,11 @@ int cfi_read_spansion_pri_ext(flash_bank_t *bank)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -594,7 +615,9 @@ int cfi_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **
|
|||
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)
|
||||
|
@ -605,9 +628,13 @@ int cfi_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -665,19 +692,19 @@ int cfi_spansion_erase(struct flash_bank_s *bank, int first, int last)
|
|||
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);
|
||||
|
@ -891,9 +918,10 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
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:
|
||||
|
@ -906,7 +934,7 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
* 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] */
|
||||
|
@ -923,7 +951,7 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
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] */
|
||||
|
@ -940,7 +968,7 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
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] */
|
||||
|
@ -966,29 +994,37 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
/* 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)
|
||||
|
@ -1013,10 +1049,30 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
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;
|
||||
|
@ -1026,11 +1082,9 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
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)
|
||||
{
|
||||
|
@ -1038,7 +1092,7 @@ int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u3
|
|||
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);
|
||||
|
@ -1078,8 +1132,6 @@ int cfi_spansion_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address,
|
|||
int i;
|
||||
int retval;
|
||||
int exit_code = ERROR_OK;
|
||||
int code_size;
|
||||
void *code_p;
|
||||
|
||||
/* input parameters - */
|
||||
/* R0 = source address */
|
||||
|
@ -1095,8 +1147,8 @@ int cfi_spansion_write_block(struct flash_bank_s *bank, u8 *buffer, u32 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>: */
|
||||
|
@ -1207,36 +1259,47 @@ int cfi_spansion_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address,
|
|||
/* 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)
|
||||
|
@ -1277,14 +1340,14 @@ int cfi_spansion_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address,
|
|||
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);
|
||||
|
@ -1301,6 +1364,8 @@ int cfi_spansion_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address,
|
|||
count -= thisrun_count;
|
||||
}
|
||||
|
||||
target_free_working_area(target, source);
|
||||
|
||||
destroy_reg_param(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
|
@ -1347,13 +1412,13 @@ int cfi_spansion_write_word(struct flash_bank_s *bank, u8 *word, u32 address)
|
|||
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);
|
||||
|
||||
|
@ -1554,6 +1619,16 @@ void cfi_fixup_0002_erase_regions(flash_bank_t *bank, void *param)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -1563,14 +1638,25 @@ int cfi_probe(struct flash_bank_s *bank)
|
|||
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)
|
||||
{
|
||||
|
@ -1594,105 +1680,132 @@ int cfi_probe(struct flash_bank_s *bank)
|
|||
|
||||
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:
|
||||
|
@ -1713,6 +1826,11 @@ int cfi_probe(struct flash_bank_s *bank)
|
|||
}
|
||||
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);
|
||||
|
||||
|
@ -1731,14 +1849,6 @@ int cfi_probe(struct flash_bank_s *bank)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -1893,13 +2003,13 @@ int cfi_spansion_protect_check(struct flash_bank_s *bank)
|
|||
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++)
|
||||
{
|
||||
|
|
|
@ -30,10 +30,12 @@ typedef struct cfi_flash_bank_s
|
|||
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 */
|
||||
|
@ -108,6 +110,8 @@ typedef struct cfi_spansion_pri_ext_s
|
|||
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
|
||||
|
@ -124,6 +128,17 @@ typedef struct cfi_atmel_pri_ext_s
|
|||
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;
|
||||
|
@ -135,6 +150,7 @@ typedef struct cfi_fixup_s
|
|||
#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
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#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);
|
||||
|
@ -493,9 +494,7 @@ int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, cha
|
|||
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;
|
||||
|
@ -511,7 +510,12 @@ int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, cha
|
|||
|
||||
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));
|
||||
|
@ -521,20 +525,16 @@ int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, cha
|
|||
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)
|
||||
{
|
||||
|
@ -571,12 +571,12 @@ int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, cha
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#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);
|
||||
|
@ -1163,10 +1164,8 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
|
@ -1201,9 +1200,9 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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
|
||||
{
|
||||
|
@ -1214,27 +1213,27 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
}
|
||||
|
||||
/* 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))
|
||||
{
|
||||
|
@ -1263,7 +1262,7 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -1273,7 +1272,7 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -1284,7 +1283,7 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
|
@ -1292,7 +1291,7 @@ int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
|
||||
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
|
||||
|
@ -1318,8 +1317,7 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
{
|
||||
if (p->device)
|
||||
{
|
||||
fileio_t file;
|
||||
fileio_image_t image_info;
|
||||
fileio_t fileio;
|
||||
duration_t duration;
|
||||
char *duration_text;
|
||||
int retval;
|
||||
|
@ -1367,14 +1365,10 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1391,13 +1385,13 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1411,10 +1405,10 @@ int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/***************************************************************************
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/***************************************************************************
|
||||
* 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 */
|
||||
|
|
@ -98,7 +98,7 @@ int fileio_open_local(fileio_t *fileio)
|
|||
}
|
||||
}
|
||||
|
||||
if (fileio->pri_type == FILEIO_IMAGE)
|
||||
if (fileio->type == FILEIO_BINARY)
|
||||
strcat(access, "b");
|
||||
|
||||
if (!(fileio_local->file = fopen(fileio->url, access)))
|
||||
|
@ -120,126 +120,7 @@ int fileio_open_local(fileio_t *fileio)
|
|||
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;
|
||||
|
@ -261,9 +142,8 @@ int fileio_open(fileio_t *fileio, char *url, enum fileio_access access,
|
|||
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)
|
||||
|
@ -279,50 +159,6 @@ int fileio_open(fileio_t *fileio, char *url, enum fileio_access access,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -369,29 +205,6 @@ int fileio_close(fileio_t *fileio)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -432,7 +245,7 @@ int fileio_local_read(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read)
|
|||
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)
|
||||
{
|
||||
|
@ -445,38 +258,6 @@ int fileio_dispatch_read(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_read)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -486,7 +267,7 @@ int fileio_local_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_written
|
|||
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)
|
||||
{
|
||||
|
@ -499,48 +280,3 @@ int fileio_dispatch_write(fileio_t *fileio, u32 size, u8 *buffer, u32 *size_writ
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -22,28 +22,20 @@
|
|||
|
||||
#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
|
||||
enum fileio_type
|
||||
{
|
||||
FILEIO_TEXT = 0x1,
|
||||
FILEIO_IMAGE = 0x2,
|
||||
};
|
||||
|
||||
enum fileio_sec_type
|
||||
{
|
||||
FILEIO_PLAIN = 0x10,
|
||||
FILEIO_IHEX = 0x20,
|
||||
/*
|
||||
* Possible future enhancements:
|
||||
* FILEIO_ELF,
|
||||
* FILEIO_SRECORD,
|
||||
*/
|
||||
FILEIO_TEXT,
|
||||
FILEIO_BINARY,
|
||||
};
|
||||
|
||||
enum fileio_location
|
||||
|
@ -73,48 +65,23 @@ typedef struct fileio_s
|
|||
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)
|
||||
|
|
|
@ -1282,6 +1282,7 @@ int jtag_examine_chain()
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
* 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"
|
||||
|
|
|
@ -3,7 +3,7 @@ METASOURCES = AUTO
|
|||
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
|
||||
|
|
|
@ -2096,8 +2096,6 @@ int arm7_9_register_commands(struct command_context_s *cmd_ctx)
|
|||
|
||||
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>");
|
||||
|
||||
|
@ -2115,7 +2113,8 @@ int arm7_9_register_commands(struct command_context_s *cmd_ctx)
|
|||
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;
|
||||
}
|
||||
|
@ -2425,83 +2424,6 @@ int handle_arm7_9_dcc_downloads_command(struct command_context_s *cmd_ctx, char
|
|||
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;
|
||||
|
@ -2515,8 +2437,7 @@ int arm7_9_init_arch_info(target_t *target, arm7_9_common_t *arm7_9)
|
|||
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;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "breakpoints.h"
|
||||
#include "target.h"
|
||||
|
||||
#include "etb.h"
|
||||
#include "etm.h"
|
||||
|
||||
#define ARM7_9_COMMON_MAGIC 0x0a790a79
|
||||
|
||||
|
@ -35,7 +35,6 @@ typedef struct arm7_9_common_s
|
|||
|
||||
arm_jtag_t jtag_info;
|
||||
reg_cache_t *eice_cache;
|
||||
reg_cache_t *etm_cache;
|
||||
|
||||
u32 arm_bkpt;
|
||||
u16 thumb_bkpt;
|
||||
|
@ -48,8 +47,8 @@ typedef struct arm7_9_common_s
|
|||
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;
|
||||
|
|
|
@ -744,10 +744,10 @@ void arm7tdmi_build_reg_cache(target_t *target)
|
|||
(*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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -874,16 +874,10 @@ void arm9tdmi_build_reg_cache(target_t *target)
|
|||
(*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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -343,7 +343,10 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
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;
|
||||
|
@ -376,26 +379,26 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +408,7 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
{
|
||||
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);
|
||||
|
||||
|
@ -413,7 +416,7 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
}
|
||||
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);
|
||||
|
||||
|
@ -422,7 +425,7 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
}
|
||||
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);
|
||||
|
||||
|
@ -1157,7 +1160,10 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
@ -1315,3 +1321,762 @@ int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ union arm_shifter_operand
|
|||
} immediate;
|
||||
struct {
|
||||
u8 Rm;
|
||||
u8 shift;
|
||||
u8 shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
|
||||
u8 shift_imm;
|
||||
} immediate_shift;
|
||||
struct {
|
||||
|
@ -164,7 +164,7 @@ typedef struct arm_load_store_instr_s
|
|||
u32 offset;
|
||||
struct {
|
||||
u8 Rm;
|
||||
u8 shift;
|
||||
u8 shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
|
||||
u8 shift_imm;
|
||||
} reg;
|
||||
} offset;
|
||||
|
@ -195,6 +195,7 @@ typedef struct arm_instruction_s
|
|||
} 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])
|
||||
|
||||
|
|
|
@ -257,6 +257,11 @@ int pass_condition(u32 cpsr, u32 opcode)
|
|||
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
|
||||
|
@ -275,26 +280,43 @@ int arm_simulate_step(target_t *target, u32 *dry_run_pc)
|
|||
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
|
||||
{
|
||||
buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size);
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
/* 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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* examine instruction type */
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#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,
|
||||
|
@ -33,16 +33,16 @@ enum armv4_5_mode
|
|||
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[];
|
||||
|
||||
|
|
583
src/target/etb.c
583
src/target/etb.c
|
@ -21,8 +21,11 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "arm7_9_common.h"
|
||||
#include "etb.h"
|
||||
#include "etm.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "types.h"
|
||||
|
@ -55,16 +58,7 @@ int etb_set_reg_w_exec(reg_t *reg, u8 *buf);
|
|||
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)
|
||||
{
|
||||
|
@ -180,6 +174,74 @@ int etb_get_reg(reg_t *reg)
|
|||
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;
|
||||
|
@ -333,293 +395,266 @@ int etb_store_reg(reg_t *reg)
|
|||
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);
|
||||
|
||||
register_command(cmd_ctx, arm7_9_cmd, "etb_dump", handle_arm7_9_etb_dump_command, COMMAND_EXEC, "dump current ETB content");
|
||||
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;
|
||||
}
|
||||
|
||||
#define PIPESTAT(x) ((x) & 0x7)
|
||||
#define TRACEPKT(x) (((x) & 0x7fff8) >> 3)
|
||||
#define TRACESYNC(x) (((x) & 0x80000) >> 19)
|
||||
|
||||
int etmv1_next_packet(int trace_depth, u32 *trace_data, int frame, int *port_half, int apo, u8 *packet)
|
||||
int handle_etb_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
|
||||
{
|
||||
while (frame < trace_depth)
|
||||
{
|
||||
if (apo > 0)
|
||||
{
|
||||
if (TRACESYNC(trace_data[frame]))
|
||||
apo--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we're looking for a branch address, skip if TRACESYNC isn't set */
|
||||
if ((apo == 0) && (!TRACESYNC(trace_data[frame])))
|
||||
{
|
||||
frame++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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 (*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;
|
||||
}
|
||||
}
|
||||
frame++;
|
||||
}
|
||||
|
||||
/* we reached the end of the trace without finding the packet we're looking for
|
||||
* tracing is finished
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
int handle_arm7_9_etb_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
|
||||
{
|
||||
int retval;
|
||||
target_t *target = get_current_target(cmd_ctx);
|
||||
target_t *target;
|
||||
jtag_device_t *jtag_device;
|
||||
armv4_5_common_t *armv4_5;
|
||||
arm7_9_common_t *arm7_9;
|
||||
int i, j, k;
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (!arm7_9->etb)
|
||||
jtag_device = jtag_get_device(strtoul(args[1], NULL, 0));
|
||||
|
||||
if (!jtag_device)
|
||||
{
|
||||
command_print(cmd_ctx, "no ETB configured for current target");
|
||||
return ERROR_OK;
|
||||
ERROR("jtag device number '%s' not defined", args[1]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!(arm7_9->etb->RAM_depth && arm7_9->etb->RAM_width))
|
||||
if (arm7_9->etm_ctx)
|
||||
{
|
||||
/* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
int etb_init(etm_context_t *etm_ctx)
|
||||
{
|
||||
etb_t *etb = etm_ctx->capture_driver_priv;
|
||||
|
||||
trace_data = malloc(sizeof(u32) * arm7_9->etb->RAM_depth);
|
||||
etb->etm_ctx = etm_ctx;
|
||||
|
||||
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]);
|
||||
/* 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();
|
||||
|
||||
/* 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))
|
||||
{
|
||||
first_frame = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 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));
|
||||
|
||||
for (i = 0, j = 0; i < arm7_9->etb->RAM_depth; i++)
|
||||
{
|
||||
int trigger = 0;
|
||||
|
||||
trace_output_len = 0;
|
||||
|
||||
/* catch trigger, actual PIPESTAT is encoded in TRACEPKT[2:0] */
|
||||
if (PIPESTAT(trace_data[i]) == 0x6)
|
||||
{
|
||||
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) " : "");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* restore trigger PIPESTAT to ensure TRACEPKT is ignored */
|
||||
if (trigger == 1)
|
||||
{
|
||||
trace_data[i] &= ~0x7;
|
||||
trace_data[i] |= 0x6;
|
||||
}
|
||||
|
||||
command_print(cmd_ctx, "%s (raw: 0x%8.8x)", trace_output, trace_data[i]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
trace_status_t etb_status(etm_context_t *etm_ctx)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while (etb_timeout-- && (buf_get_u32(etb_status_reg->value, 3, 1) == 0))
|
||||
{
|
||||
/* wait for data formatter idle */
|
||||
etb_get_reg(etb_status_reg);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
ERROR("trace completed, but no trigger condition detected");
|
||||
}
|
||||
|
||||
etm_ctx->capture_status &= ~TRACE_RUNNING;
|
||||
etm_ctx->capture_status |= TRACE_COMPLETED;
|
||||
}
|
||||
}
|
||||
|
||||
return etm_ctx->capture_status;
|
||||
}
|
||||
|
||||
int etb_read_trace(etm_context_t *etm_ctx)
|
||||
{
|
||||
etb_t *etb = etm_ctx->capture_driver_priv;
|
||||
int first_frame = 0;
|
||||
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();
|
||||
|
||||
/* 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))
|
||||
{
|
||||
first_frame = buf_get_u32(etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
num_frames = buf_get_u32(etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
free(etm_ctx->trace_data);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
etm_ctx->trace_data= malloc(sizeof(etmv1_trace_data_t) * etm_ctx->trace_depth);
|
||||
|
||||
for (i = 0, j = 0; i < num_frames; i++)
|
||||
{
|
||||
if ((etm_ctx->portmode & ETM_PORT_MODE_MASK) == ETM_PORT_DEMUXED)
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int etb_start_capture(etm_context_t *etm_ctx)
|
||||
{
|
||||
etb_t *etb = etm_ctx->capture_driver_priv;
|
||||
u32 etb_ctrl_value = 0x1;
|
||||
|
||||
if ((etm_ctx->portmode & ETM_PORT_MODE_MASK) == ETM_PORT_DEMUXED)
|
||||
{
|
||||
if ((etm_ctx->portmode & ETM_PORT_WIDTH_MASK) == ETM_PORT_16BIT)
|
||||
{
|
||||
DEBUG("ETB can't run in demultiplexed mode with a 16-bit port");
|
||||
return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
|
||||
}
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
#include "register.h"
|
||||
#include "arm_jtag.h"
|
||||
|
||||
#include "etb.h"
|
||||
#include "etm.h"
|
||||
|
||||
/* ETB registers */
|
||||
enum
|
||||
{
|
||||
|
@ -41,13 +44,14 @@ enum
|
|||
|
||||
typedef struct etb_s
|
||||
{
|
||||
etm_context_t *etm_ctx;
|
||||
int chain_pos;
|
||||
int cur_scan_chain;
|
||||
reg_cache_t *reg_cache;
|
||||
|
||||
/* ETB parameters */
|
||||
int RAM_depth;
|
||||
int RAM_width;
|
||||
int ram_depth;
|
||||
int ram_width;
|
||||
} etb_t;
|
||||
|
||||
typedef struct etb_reg_s
|
||||
|
@ -56,6 +60,8 @@ typedef struct etb_reg_s
|
|||
etb_t *etb;
|
||||
} etb_reg_t;
|
||||
|
||||
extern etm_capture_driver_t etb_capture_driver;
|
||||
|
||||
extern reg_cache_t* etb_build_reg_cache(etb_t *etb);
|
||||
extern int etb_read_reg(reg_t *reg);
|
||||
extern int etb_write_reg(reg_t *reg, u32 value);
|
||||
|
@ -64,6 +70,6 @@ extern int etb_store_reg(reg_t *reg);
|
|||
extern int etb_set_reg(reg_t *reg, u32 value);
|
||||
extern int etb_set_reg_w_exec(reg_t *reg, u8 *buf);
|
||||
|
||||
extern int etb_register_commands(struct command_context_s *cmd_ctx, command_t *arm7_9_cmd);
|
||||
extern int etb_register_commands(struct command_context_s *cmd_ctx);
|
||||
|
||||
#endif /* ETB_H */
|
||||
|
|
686
src/target/etm.c
686
src/target/etm.c
|
@ -21,7 +21,10 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "etm.h"
|
||||
#include "etb.h"
|
||||
|
||||
#include "armv4_5.h"
|
||||
#include "arm7_9_common.h"
|
||||
|
@ -33,9 +36,14 @@
|
|||
#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},
|
||||
|
@ -204,13 +212,16 @@ int etm_set_reg_w_exec(reg_t *reg, u8 *buf);
|
|||
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)
|
||||
|
@ -242,6 +253,44 @@ reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int ex
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -410,3 +459,638 @@ int etm_store_reg(reg_t *reg)
|
|||
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;
|
||||
}
|
||||
|
|
131
src/target/etm.h
131
src/target/etm.h
|
@ -1,7 +1,10 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2005 by Dominic Rath *
|
||||
* Copyright (C) 2005, 2007 by Dominic Rath *
|
||||
* Dominic.Rath@gmx.de *
|
||||
* *
|
||||
* Copyright (C) 2007 by Vincent Palatin *
|
||||
* vincent.palatin_openocd@m4x.org *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -20,11 +23,14 @@
|
|||
#ifndef ETM_H
|
||||
#define ETM_H
|
||||
|
||||
#include "trace.h"
|
||||
#include "target.h"
|
||||
#include "register.h"
|
||||
#include "arm_jtag.h"
|
||||
|
||||
// ETM registers (V1.2 protocol)
|
||||
#include "armv4_5.h"
|
||||
|
||||
/* ETM registers (V1.3 protocol) */
|
||||
enum
|
||||
{
|
||||
ETM_CTRL = 0x00,
|
||||
|
@ -58,14 +64,123 @@ enum
|
|||
ETM_CONTEXTID_COMPARATOR_MASK = 0x6f,
|
||||
};
|
||||
|
||||
|
||||
typedef struct etm_reg_s
|
||||
{
|
||||
int addr;
|
||||
arm_jtag_t *jtag_info;
|
||||
} etm_reg_t;
|
||||
|
||||
extern reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg);
|
||||
typedef enum
|
||||
{
|
||||
/* Port width */
|
||||
ETM_PORT_4BIT = 0x00,
|
||||
ETM_PORT_8BIT = 0x10,
|
||||
ETM_PORT_16BIT = 0x20,
|
||||
ETM_PORT_WIDTH_MASK = 0x70,
|
||||
/* Port modes */
|
||||
ETM_PORT_NORMAL = 0x00000,
|
||||
ETM_PORT_MUXED = 0x10000,
|
||||
ETM_PORT_DEMUXED = 0x20000,
|
||||
ETM_PORT_MODE_MASK = 0x30000,
|
||||
/* Clocking modes */
|
||||
ETM_PORT_FULL_CLOCK = 0x0000,
|
||||
ETM_PORT_HALF_CLOCK = 0x1000,
|
||||
ETM_PORT_CLOCK_MASK = 0x1000,
|
||||
} etm_portmode_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* Data trace */
|
||||
ETMV1_TRACE_NONE = 0x00,
|
||||
ETMV1_TRACE_DATA = 0x01,
|
||||
ETMV1_TRACE_ADDR = 0x02,
|
||||
ETMV1_TRACE_MASK = 0x03,
|
||||
/* ContextID */
|
||||
ETMV1_CONTEXTID_NONE = 0x00,
|
||||
ETMV1_CONTEXTID_8 = 0x10,
|
||||
ETMV1_CONTEXTID_16 = 0x20,
|
||||
ETMV1_CONTEXTID_32 = 0x30,
|
||||
ETMV1_CONTEXTID_MASK = 0x30,
|
||||
/* Misc */
|
||||
ETMV1_CYCLE_ACCURATE = 0x100
|
||||
} etmv1_tracemode_t;
|
||||
|
||||
/* forward-declare ETM context */
|
||||
struct etm_context_s;
|
||||
|
||||
typedef struct etm_capture_driver_s
|
||||
{
|
||||
char *name;
|
||||
int (*register_commands)(struct command_context_s *cmd_ctx);
|
||||
int (*init)(struct etm_context_s *etm_ctx);
|
||||
trace_status_t (*status)(struct etm_context_s *etm_ctx);
|
||||
int (*read_trace)(struct etm_context_s *etm_ctx);
|
||||
int (*start_capture)(struct etm_context_s *etm_ctx);
|
||||
int (*stop_capture)(struct etm_context_s *etm_ctx);
|
||||
} etm_capture_driver_t;
|
||||
|
||||
typedef struct etmv1_trace_data_s
|
||||
{
|
||||
u8 pipestat; /* pipeline cycle this packet belongs to */
|
||||
u16 packet; /* packet data (4, 8 or 16 bit) */
|
||||
int tracesync; /* 1 if tracesync was set on this packet */
|
||||
} etmv1_trace_data_t;
|
||||
|
||||
/* describe a trace context
|
||||
* if support for ETMv2 or ETMv3 is to be implemented,
|
||||
* this will have to be split into version independent elements
|
||||
* and a version specific part
|
||||
*/
|
||||
typedef struct etm_context_s
|
||||
{
|
||||
reg_cache_t *reg_cache; /* ETM register cache */
|
||||
etm_capture_driver_t *capture_driver; /* driver used to access ETM data */
|
||||
void *capture_driver_priv; /* capture driver private data */
|
||||
trace_status_t capture_status; /* current state of capture run */
|
||||
etmv1_trace_data_t *trace_data; /* trace data */
|
||||
u32 trace_depth; /* number of trace cycles to be analyzed, 0 if no trace data available */
|
||||
etm_portmode_t portmode; /* normal, multiplexed or demultiplexed */
|
||||
etmv1_tracemode_t tracemode; /* type of information the trace contains (data, addres, contextID, ...) */
|
||||
armv4_5_state_t core_state; /* current core state (ARM, Thumb, Jazelle) */
|
||||
// trace_image_provider_t image_provider; /* source for target opcodes */
|
||||
u32 pipe_index; /* current trace cycle */
|
||||
u32 data_index; /* cycle holding next data packet */
|
||||
u32 current_pc; /* current program counter */
|
||||
u32 pc_ok; /* full PC has been acquired */
|
||||
u32 last_branch; /* last branch address output */
|
||||
u32 last_ptr; /* address of the last data access */
|
||||
u32 context_id; /* context ID of the code being traced */
|
||||
} etm_context_t;
|
||||
|
||||
/* PIPESTAT values */
|
||||
typedef enum
|
||||
{
|
||||
STAT_IE = 0x0,
|
||||
STAT_ID = 0x1,
|
||||
STAT_IN = 0x2,
|
||||
STAT_WT = 0x3,
|
||||
STAT_BE = 0x4,
|
||||
STAT_BD = 0x5,
|
||||
STAT_TR = 0x6,
|
||||
STAT_TD = 0x7
|
||||
} etmv1_pipestat_t;
|
||||
|
||||
/* branch reason values */
|
||||
typedef enum
|
||||
{
|
||||
BR_NORMAL = 0x0, /* Normal PC change : periodic synchro (ETMv1.1) */
|
||||
BR_ENABLE = 0x1, /* Trace has been enabled */
|
||||
BR_RESTART = 0x2, /* Trace restarted after a FIFO overflow */
|
||||
BR_NODEBUG = 0x3, /* ARM has exited for debug state */
|
||||
BR_PERIOD = 0x4, /* Peridioc synchronization point (ETM>=v1.2)*/
|
||||
BR_RSVD5 = 0x5, /* reserved */
|
||||
BR_RSVD6 = 0x6, /* reserved */
|
||||
BR_RSVD7 = 0x7, /* reserved */
|
||||
} etmv1_branch_reason_t;
|
||||
|
||||
extern char *etmv1v1_branch_reason_strings[];
|
||||
|
||||
extern reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, etm_context_t *etm_ctx);
|
||||
extern int etm_read_reg(reg_t *reg);
|
||||
extern int etm_write_reg(reg_t *reg, u32 value);
|
||||
extern int etm_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask);
|
||||
|
@ -73,4 +188,12 @@ extern int etm_store_reg(reg_t *reg);
|
|||
extern int etm_set_reg(reg_t *reg, u32 value);
|
||||
extern int etm_set_reg_w_exec(reg_t *reg, u8 *buf);
|
||||
|
||||
int etm_register_commands(struct command_context_s *cmd_ctx);
|
||||
int etm_register_user_commands(struct command_context_s *cmd_ctx);
|
||||
extern etm_context_t* etm_create_context(etm_portmode_t portmode, char *capture_driver_name);
|
||||
|
||||
#define ERROR_ETM_INVALID_DRIVER (-1300)
|
||||
#define ERROR_ETM_PORTMODE_NOT_SUPPORTED (-1301)
|
||||
#define ERROR_ETM_CAPTURE_INIT_FAILED (-1302)
|
||||
|
||||
#endif /* ETM_H */
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#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);
|
||||
|
||||
|
@ -1656,12 +1657,9 @@ int handle_load_image_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
|
@ -1674,40 +1672,41 @@ int handle_load_image_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
|
||||
|
@ -1715,8 +1714,7 @@ int handle_load_image_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
|
||||
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;
|
||||
|
@ -1742,13 +1740,9 @@ int handle_dump_image_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1760,16 +1754,16 @@ int handle_dump_image_command(struct command_context_s *cmd_ctx, char *cmd, char
|
|||
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;
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue