diff options
Diffstat (limited to 'src')
56 files changed, 2343 insertions, 346 deletions
diff --git a/src/flash/nor/pic32mx.c b/src/flash/nor/pic32mx.c index 9a1a634..0f3937c 100644 --- a/src/flash/nor/pic32mx.c +++ b/src/flash/nor/pic32mx.c @@ -866,10 +866,8 @@ COMMAND_HANDLER(pic32mx_handle_unlock_command) struct mips_ejtag *ejtag_info; int timeout = 10; - if (CMD_ARGC < 1) { - command_print(CMD, "pic32mx unlock <bank>"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -932,7 +930,7 @@ static const struct command_registration pic32mx_exec_command_handlers[] = { .name = "unlock", .handler = pic32mx_handle_unlock_command, .mode = COMMAND_EXEC, - .usage = "[bank_id]", + .usage = "bank_id", .help = "Unlock/Erase entire device.", }, COMMAND_REGISTRATION_DONE diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index 2e0d158..4e0f731 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -1540,10 +1540,8 @@ static int stm32x_mass_erase(struct flash_bank *bank) COMMAND_HANDLER(stm32x_handle_mass_erase_command) { - if (CMD_ARGC < 1) { - command_print(CMD, "stm32x mass_erase <bank>"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -1566,10 +1564,8 @@ COMMAND_HANDLER(stm32f2x_handle_options_read_command) struct flash_bank *bank; struct stm32x_flash_bank *stm32x_info = NULL; - if (CMD_ARGC != 1) { - command_print(CMD, "stm32f2x options_read <bank>"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (retval != ERROR_OK) @@ -1612,10 +1608,8 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command) struct stm32x_flash_bank *stm32x_info = NULL; uint16_t user_options, boot_addr0, boot_addr1, options_mask; - if (CMD_ARGC < 1) { - command_print(CMD, "stm32f2x options_write <bank> ..."); + if (CMD_ARGC < 1) return ERROR_COMMAND_SYNTAX_ERROR; - } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (retval != ERROR_OK) @@ -1627,19 +1621,14 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command) stm32x_info = bank->driver_priv; if (stm32x_info->has_boot_addr) { - if (CMD_ARGC != 4) { - command_print(CMD, "stm32f2x options_write <bank> <user_options>" - " <boot_addr0> <boot_addr1>"); + if (CMD_ARGC != 4) return ERROR_COMMAND_SYNTAX_ERROR; - } + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[2], boot_addr0); COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], boot_addr1); stm32x_info->option_bytes.boot_addr = boot_addr0 | (((uint32_t) boot_addr1) << 16); - } else { - if (CMD_ARGC != 2) { - command_print(CMD, "stm32f2x options_write <bank> <user_options>"); - return ERROR_COMMAND_SYNTAX_ERROR; - } + } else if (CMD_ARGC != 2) { + return ERROR_COMMAND_SYNTAX_ERROR; } COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], user_options); @@ -1674,10 +1663,8 @@ COMMAND_HANDLER(stm32f2x_handle_optcr2_write_command) struct stm32x_flash_bank *stm32x_info = NULL; uint32_t optcr2_pcrop; - if (CMD_ARGC != 2) { - command_print(CMD, "stm32f2x optcr2_write <bank> <optcr2_value>"); + if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; - } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); if (retval != ERROR_OK) @@ -1711,10 +1698,8 @@ COMMAND_HANDLER(stm32f2x_handle_optcr2_write_command) COMMAND_HANDLER(stm32x_handle_otp_command) { - if (CMD_ARGC < 2) { - command_print(CMD, "stm32x otp <bank> (enable|disable|show)"); + if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -1787,7 +1772,7 @@ static const struct command_registration stm32f2x_exec_command_handlers[] = { .name = "otp", .handler = stm32x_handle_otp_command, .mode = COMMAND_EXEC, - .usage = "bank_id", + .usage = "bank_id (enable|disable|show)", .help = "OTP (One Time Programmable) memory write enable/disable.", }, COMMAND_REGISTRATION_DONE diff --git a/src/flash/nor/stm32h7x.c b/src/flash/nor/stm32h7x.c index 21618b3..c02fae9 100644 --- a/src/flash/nor/stm32h7x.c +++ b/src/flash/nor/stm32h7x.c @@ -1080,10 +1080,8 @@ flash_lock: COMMAND_HANDLER(stm32x_handle_mass_erase_command) { - if (CMD_ARGC < 1) { - command_print(CMD, "stm32h7x mass_erase <bank>"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -1101,10 +1099,8 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command) COMMAND_HANDLER(stm32x_handle_option_read_command) { - if (CMD_ARGC < 2) { - command_print(CMD, "stm32h7x option_read <bank> <option_reg offset>"); + if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -1126,10 +1122,8 @@ COMMAND_HANDLER(stm32x_handle_option_read_command) COMMAND_HANDLER(stm32x_handle_option_write_command) { - if (CMD_ARGC < 3) { - command_print(CMD, "stm32h7x option_write <bank> <option_reg offset> <value> [mask]"); + if (CMD_ARGC != 3 && CMD_ARGC != 4) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 96757a9..0399385 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -2218,10 +2218,8 @@ err_lock: COMMAND_HANDLER(stm32l4_handle_mass_erase_command) { - if (CMD_ARGC < 1) { - command_print(CMD, "stm32l4x mass_erase <STM32L4 bank>"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -2239,10 +2237,8 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command) COMMAND_HANDLER(stm32l4_handle_option_read_command) { - if (CMD_ARGC < 2) { - command_print(CMD, "stm32l4x option_read <STM32L4 bank> <option_reg offset>"); + if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -2266,10 +2262,8 @@ COMMAND_HANDLER(stm32l4_handle_option_read_command) COMMAND_HANDLER(stm32l4_handle_option_write_command) { - if (CMD_ARGC < 3) { - command_print(CMD, "stm32l4x option_write <STM32L4 bank> <option_reg offset> <value> [mask]"); + if (CMD_ARGC != 3 && CMD_ARGC != 4) return ERROR_COMMAND_SYNTAX_ERROR; - } struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -2381,7 +2375,7 @@ COMMAND_HANDLER(stm32l4_handle_lock_command) { struct target *target = NULL; - if (CMD_ARGC < 1) + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; struct flash_bank *bank; @@ -2416,7 +2410,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command) { struct target *target = NULL; - if (CMD_ARGC < 1) + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; struct flash_bank *bank; @@ -2520,7 +2514,7 @@ COMMAND_HANDLER(stm32l4_handle_wrp_info_command) COMMAND_HANDLER(stm32l4_handle_otp_command) { - if (CMD_ARGC < 2) + if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; struct flash_bank *bank; diff --git a/src/flash/nor/stmqspi.c b/src/flash/nor/stmqspi.c index c9fc1bf..a1e1d34 100644 --- a/src/flash/nor/stmqspi.c +++ b/src/flash/nor/stmqspi.c @@ -646,21 +646,21 @@ COMMAND_HANDLER(stmqspi_handle_set) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.size_in_bytes); if (log2u(stmqspi_info->dev.size_in_bytes) < 8) { command_print(CMD, "stmqspi: device size must be 2^n with n >= 8"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.pagesize); if (stmqspi_info->dev.pagesize > stmqspi_info->dev.size_in_bytes || (log2u(stmqspi_info->dev.pagesize) < 0)) { command_print(CMD, "stmqspi: page size must be 2^n and <= device size"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.read_cmd); if ((stmqspi_info->dev.read_cmd != 0x03) && (stmqspi_info->dev.read_cmd != 0x13)) { command_print(CMD, "stmqspi: only 0x03/0x13 READ cmd allowed"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.qread_cmd); @@ -678,7 +678,7 @@ COMMAND_HANDLER(stmqspi_handle_set) (stmqspi_info->dev.qread_cmd != 0xEE)) { command_print(CMD, "stmqspi: only 0x0B/0x0C/0x3B/0x3C/" "0x6B/0x6C/0xBB/0xBC/0xEB/0xEC/0xEE QREAD allowed"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.pprog_cmd); @@ -686,7 +686,7 @@ COMMAND_HANDLER(stmqspi_handle_set) (stmqspi_info->dev.pprog_cmd != 0x12) && (stmqspi_info->dev.pprog_cmd != 0x32)) { command_print(CMD, "stmqspi: only 0x02/0x12/0x32 PPRG cmd allowed"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } if (index < CMD_ARGC) @@ -700,7 +700,7 @@ COMMAND_HANDLER(stmqspi_handle_set) (stmqspi_info->dev.sectorsize < stmqspi_info->dev.pagesize) || (log2u(stmqspi_info->dev.sectorsize) < 0)) { command_print(CMD, "stmqspi: sector size must be 2^n and <= device size"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } if (index < CMD_ARGC) @@ -786,7 +786,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd) num_write = CMD_ARGC - 2; if (num_write > max) { LOG_ERROR("at most %d bytes may be sent", max); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); @@ -811,7 +811,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd) if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) { if ((num_write & 1) == 0) { LOG_ERROR("number of data bytes to write must be even in dual mode"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } } } else { @@ -819,12 +819,12 @@ COMMAND_HANDLER(stmqspi_handle_cmd) if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) { if ((num_read & 1) != 0) { LOG_ERROR("number of bytes to read must be even in dual mode"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } } if ((num_write < 1) || (num_write > 5)) { LOG_ERROR("one cmd and up to four addr bytes must be send when reading"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } } diff --git a/src/flash/nor/xcf.c b/src/flash/nor/xcf.c index 2870725..c253b22 100644 --- a/src/flash/nor/xcf.c +++ b/src/flash/nor/xcf.c @@ -597,7 +597,7 @@ static int xcf_probe(struct flash_bank *bank) } /* check idcode and alloc memory for sector table */ - if (!bank->target->tap->hasidcode) + if (!bank->target->tap->has_idcode) return ERROR_FLASH_OPERATION_FAILED; /* guess number of blocks using chip ID */ diff --git a/src/helper/nvp.c b/src/helper/nvp.c index 7a8abc2..a938716 100644 --- a/src/helper/nvp.c +++ b/src/helper/nvp.c @@ -14,7 +14,7 @@ * Copyright 2009 David Brownell * Copyright (c) 2005-2011 Jim Tcl Project. All rights reserved. * - * This file is extracted from jim_nvp.c, originally part of jim TCL code. + * This file is extracted from jim-nvp.c, originally part of jim TCL code. */ #ifdef HAVE_CONFIG_H diff --git a/src/helper/nvp.h b/src/helper/nvp.h index 14bd9b0..1f4e3d1 100644 --- a/src/helper/nvp.h +++ b/src/helper/nvp.h @@ -14,7 +14,7 @@ * Copyright 2009 David Brownell * Copyright (c) 2005-2011 Jim Tcl Project. All rights reserved. * - * This file is extracted from jim_nvp.h, originally part of jim TCL code. + * This file is extracted from jim-nvp.h, originally part of jim TCL code. */ #ifndef OPENOCD_HELPER_NVP_H @@ -51,7 +51,7 @@ * returns &yn[0]; * result = nvp_name2value(yn, "no"); * returns &yn[1]; - * result = jim_nvp_name2value(yn, "Blah"); + * result = nvp_name2value(yn, "Blah"); * returns &yn[4]; * \endcode * diff --git a/src/jtag/core.c b/src/jtag/core.c index 5748011..e2af6c5 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -1049,7 +1049,7 @@ static int jtag_reset_callback(enum jtag_event event, void *priv) /* current instruction is either BYPASS or IDCODE */ buf_set_ones(tap->cur_instr, tap->ir_length); - tap->bypass = 1; + tap->bypass = true; } return ERROR_OK; @@ -1177,7 +1177,7 @@ static bool jtag_examine_chain_end(uint8_t *idcodes, unsigned count, unsigned ma static bool jtag_examine_chain_match_tap(const struct jtag_tap *tap) { - if (tap->expected_ids_cnt == 0 || !tap->hasidcode) + if (tap->expected_ids_cnt == 0 || !tap->has_idcode) return true; /* optionally ignore the JTAG version field - bits 28-31 of IDCODE */ @@ -1283,13 +1283,13 @@ static int jtag_examine_chain(void) /* Zero for LSB indicates a device in bypass */ LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%" PRIx32 ")", tap->dotted_name, idcode); - tap->hasidcode = false; + tap->has_idcode = false; tap->idcode = 0; bit_count += 1; } else { /* Friendly devices support IDCODE */ - tap->hasidcode = true; + tap->has_idcode = true; tap->idcode = idcode; jtag_examine_chain_display(LOG_LVL_INFO, "tap/device found", tap->dotted_name, idcode); @@ -1464,7 +1464,7 @@ void jtag_tap_init(struct jtag_tap *tap) buf_set_u32(tap->expected_mask, 0, ir_len_bits, tap->ir_capture_mask); /* TAP will be in bypass mode after jtag_validate_ircapture() */ - tap->bypass = 1; + tap->bypass = true; buf_set_ones(tap->cur_instr, tap->ir_length); /* register the reset callback for the TAP */ diff --git a/src/jtag/drivers/cmsis_dap.c b/src/jtag/drivers/cmsis_dap.c index 1e7a851..caacc9b 100644 --- a/src/jtag/drivers/cmsis_dap.c +++ b/src/jtag/drivers/cmsis_dap.c @@ -225,6 +225,12 @@ struct pending_scan_result { unsigned int buffer_offset; }; +/* Read mode */ +enum cmsis_dap_blocking { + CMSIS_DAP_NON_BLOCKING, + CMSIS_DAP_BLOCKING +}; + /* Each block in FIFO can contain up to pending_queue_len transfers */ static unsigned int pending_queue_len; static unsigned int tfer_max_command_size; @@ -315,7 +321,7 @@ static void cmsis_dap_flush_read(struct cmsis_dap *dap) * USB close/open so we need to flush up to 64 old packets * to be sure all buffers are empty */ for (i = 0; i < 64; i++) { - int retval = dap->backend->read(dap, 10); + int retval = dap->backend->read(dap, 10, NULL); if (retval == ERROR_TIMEOUT_REACHED) break; } @@ -326,10 +332,13 @@ static void cmsis_dap_flush_read(struct cmsis_dap *dap) /* Send a message and receive the reply */ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen) { + if (dap->write_count + dap->read_count) { + LOG_ERROR("internal: queue not empty before xfer"); + } if (dap->pending_fifo_block_count) { LOG_ERROR("pending %u blocks, flushing", dap->pending_fifo_block_count); while (dap->pending_fifo_block_count) { - dap->backend->read(dap, 10); + dap->backend->read(dap, 10, NULL); dap->pending_fifo_block_count--; } dap->pending_fifo_put_idx = 0; @@ -342,7 +351,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen) return retval; /* get reply */ - retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS); + retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, NULL); if (retval < 0) return retval; @@ -356,6 +365,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen) LOG_ERROR("CMSIS-DAP command mismatch. Sent 0x%" PRIx8 " received 0x%" PRIx8, current_cmd, resp[0]); + dap->backend->cancel_all(dap); cmsis_dap_flush_read(dap); return ERROR_FAIL; } @@ -749,6 +759,22 @@ static int cmsis_dap_cmd_dap_swo_data( return ERROR_OK; } +static void cmsis_dap_swd_discard_all_pending(struct cmsis_dap *dap) +{ + for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) + dap->pending_fifo[i].transfer_count = 0; + + dap->pending_fifo_put_idx = 0; + dap->pending_fifo_get_idx = 0; + dap->pending_fifo_block_count = 0; +} + +static void cmsis_dap_swd_cancel_transfers(struct cmsis_dap *dap) +{ + dap->backend->cancel_all(dap); + cmsis_dap_flush_read(dap); + cmsis_dap_swd_discard_all_pending(dap); +} static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) { @@ -770,8 +796,10 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) goto skip; } - if (block->transfer_count == 0) + if (block->transfer_count == 0) { + LOG_ERROR("internal: write an empty queue?!"); goto skip; + } bool block_cmd = !cmsis_dap_handle->swd_cmds_differ && block->transfer_count >= CMD_DAP_TFER_BLOCK_MIN_OPS; @@ -831,14 +859,13 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) if (retval < 0) { queued_retval = retval; goto skip; - } else { - queued_retval = ERROR_OK; } - dap->pending_fifo_put_idx = (dap->pending_fifo_put_idx + 1) % dap->packet_count; + unsigned int packet_count = dap->quirk_mode ? 1 : dap->packet_count; + dap->pending_fifo_put_idx = (dap->pending_fifo_put_idx + 1) % packet_count; dap->pending_fifo_block_count++; - if (dap->pending_fifo_block_count > dap->packet_count) - LOG_ERROR("too much pending writes %u", dap->pending_fifo_block_count); + if (dap->pending_fifo_block_count > packet_count) + LOG_ERROR("internal: too much pending writes %u", dap->pending_fifo_block_count); return; @@ -846,21 +873,47 @@ skip: block->transfer_count = 0; } -static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) +static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, enum cmsis_dap_blocking blocking) { + int retval; struct pending_request_block *block = &dap->pending_fifo[dap->pending_fifo_get_idx]; - if (dap->pending_fifo_block_count == 0) - LOG_ERROR("no pending write"); + if (dap->pending_fifo_block_count == 0) { + LOG_ERROR("internal: no pending write when reading?!"); + return; + } + + if (queued_retval != ERROR_OK) { + /* keep reading blocks until the pipeline is empty */ + retval = dap->backend->read(dap, 10, NULL); + if (retval == ERROR_TIMEOUT_REACHED || retval == 0) { + /* timeout means that we flushed the pipeline, + * we can safely discard remaining pending requests */ + cmsis_dap_swd_discard_all_pending(dap); + return; + } + goto skip; + } /* get reply */ - int retval = dap->backend->read(dap, timeout_ms); - if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < LIBUSB_TIMEOUT_MS) + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 0 + }; + retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, blocking ? NULL : &tv); + bool timeout = (retval == ERROR_TIMEOUT_REACHED || retval == 0); + if (timeout && blocking == CMSIS_DAP_NON_BLOCKING) return; if (retval <= 0) { - LOG_DEBUG("error reading data"); + LOG_DEBUG("error reading adapter response"); queued_retval = ERROR_FAIL; + if (timeout) { + /* timeout means that we flushed the pipeline, + * we can safely discard remaining pending requests */ + cmsis_dap_swd_discard_all_pending(dap); + return; + } goto skip; } @@ -868,8 +921,9 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) if (resp[0] != block->command) { LOG_ERROR("CMSIS-DAP command mismatch. Expected 0x%x received 0x%" PRIx8, block->command, resp[0]); + cmsis_dap_swd_cancel_transfers(dap); queued_retval = ERROR_FAIL; - goto skip; + return; } unsigned int transfer_count; @@ -895,12 +949,17 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) goto skip; } - if (block->transfer_count != transfer_count) + if (block->transfer_count != transfer_count) { LOG_ERROR("CMSIS-DAP transfer count mismatch: expected %d, got %d", block->transfer_count, transfer_count); + cmsis_dap_swd_cancel_transfers(dap); + queued_retval = ERROR_FAIL; + return; + } - LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %u timeout %i", - transfer_count, dap->pending_fifo_get_idx, timeout_ms); + LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %u, %s mode", + transfer_count, dap->pending_fifo_get_idx, + blocking ? "blocking" : "nonblocking"); for (unsigned int i = 0; i < transfer_count; i++) { struct pending_transfer_result *transfer = &(block->transfers[i]); @@ -926,19 +985,22 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) skip: block->transfer_count = 0; - dap->pending_fifo_get_idx = (dap->pending_fifo_get_idx + 1) % dap->packet_count; + if (!dap->quirk_mode && dap->packet_count > 1) + dap->pending_fifo_get_idx = (dap->pending_fifo_get_idx + 1) % dap->packet_count; dap->pending_fifo_block_count--; } static int cmsis_dap_swd_run_queue(void) { - if (cmsis_dap_handle->pending_fifo_block_count) - cmsis_dap_swd_read_process(cmsis_dap_handle, 0); + if (cmsis_dap_handle->write_count + cmsis_dap_handle->read_count) { + if (cmsis_dap_handle->pending_fifo_block_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_NON_BLOCKING); - cmsis_dap_swd_write_from_queue(cmsis_dap_handle); + cmsis_dap_swd_write_from_queue(cmsis_dap_handle); + } while (cmsis_dap_handle->pending_fifo_block_count) - cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS); + cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_BLOCKING); cmsis_dap_handle->pending_fifo_put_idx = 0; cmsis_dap_handle->pending_fifo_get_idx = 0; @@ -979,10 +1041,16 @@ static unsigned int cmsis_dap_tfer_resp_size(unsigned int write_count, static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) { + /* TARGETSEL register write cannot be queued */ + if (swd_cmd(false, false, DP_TARGETSEL) == cmd) { + queued_retval = cmsis_dap_swd_run_queue(); + + cmsis_dap_metacmd_targetsel(data); + return; + } + /* Compute sizes of the DAP Transfer command and the expected response * for all queued and this operation */ - bool targetsel_cmd = swd_cmd(false, false, DP_TARGETSEL) == cmd; - unsigned int write_count = cmsis_dap_handle->write_count; unsigned int read_count = cmsis_dap_handle->read_count; bool block_cmd; @@ -1003,20 +1071,19 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) block_cmd); unsigned int max_transfer_count = block_cmd ? 65535 : 255; - /* Does the DAP Transfer command and the expected response fit into one packet? - * Run the queue also before a targetsel - it cannot be queued */ + /* Does the DAP Transfer command and also its expected response fit into one packet? */ if (cmd_size > tfer_max_command_size || resp_size > tfer_max_response_size - || targetsel_cmd || write_count + read_count > max_transfer_count) { if (cmsis_dap_handle->pending_fifo_block_count) - cmsis_dap_swd_read_process(cmsis_dap_handle, 0); + cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_NON_BLOCKING); /* Not enough room in the queue. Run the queue. */ cmsis_dap_swd_write_from_queue(cmsis_dap_handle); - if (cmsis_dap_handle->pending_fifo_block_count >= cmsis_dap_handle->packet_count) - cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS); + unsigned int packet_count = cmsis_dap_handle->quirk_mode ? 1 : cmsis_dap_handle->packet_count; + if (cmsis_dap_handle->pending_fifo_block_count >= packet_count) + cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_BLOCKING); } assert(cmsis_dap_handle->pending_fifo[cmsis_dap_handle->pending_fifo_put_idx].transfer_count < pending_queue_len); @@ -1024,11 +1091,6 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) if (queued_retval != ERROR_OK) return; - if (targetsel_cmd) { - cmsis_dap_metacmd_targetsel(data); - return; - } - struct pending_request_block *block = &cmsis_dap_handle->pending_fifo[cmsis_dap_handle->pending_fifo_put_idx]; struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]); transfer->data = data; @@ -1160,7 +1222,10 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq) unsigned int s_len; int retval; - if (seq != LINE_RESET && + if (swd_mode) + queued_retval = cmsis_dap_swd_run_queue(); + + if (cmsis_dap_handle->quirk_mode && seq != LINE_RESET && (output_pins & (SWJ_PIN_SRST | SWJ_PIN_TRST)) == (SWJ_PIN_SRST | SWJ_PIN_TRST)) { /* Following workaround deasserts reset on most adapters. @@ -1296,7 +1361,7 @@ static int cmsis_dap_init(void) if (data[0] == 2) { /* short */ uint16_t pkt_sz = data[1] + (data[2] << 8); if (pkt_sz != cmsis_dap_handle->packet_size) { - free(cmsis_dap_handle->packet_buffer); + cmsis_dap_handle->backend->packet_buffer_free(cmsis_dap_handle); retval = cmsis_dap_handle->backend->packet_buffer_alloc(cmsis_dap_handle, pkt_sz); if (retval != ERROR_OK) goto init_err; @@ -2108,12 +2173,12 @@ COMMAND_HANDLER(cmsis_dap_handle_cmd_command) COMMAND_HANDLER(cmsis_dap_handle_vid_pid_command) { if (CMD_ARGC > MAX_USB_IDS * 2) { - LOG_WARNING("ignoring extra IDs in cmsis_dap_vid_pid " + LOG_WARNING("ignoring extra IDs in cmsis-dap vid_pid " "(maximum is %d pairs)", MAX_USB_IDS); CMD_ARGC = MAX_USB_IDS * 2; } if (CMD_ARGC < 2 || (CMD_ARGC & 1)) { - LOG_WARNING("incomplete cmsis_dap_vid_pid configuration directive"); + LOG_WARNING("incomplete cmsis-dap vid_pid configuration directive"); if (CMD_ARGC < 2) return ERROR_COMMAND_SYNTAX_ERROR; /* remove the incomplete trailing id */ @@ -2148,15 +2213,29 @@ COMMAND_HANDLER(cmsis_dap_handle_backend_command) } } - LOG_ERROR("invalid backend argument to cmsis_dap_backend <backend>"); + command_print(CMD, "invalid backend argument to cmsis-dap backend <backend>"); + return ERROR_COMMAND_ARGUMENT_INVALID; } } else { - LOG_ERROR("expected exactly one argument to cmsis_dap_backend <backend>"); + return ERROR_COMMAND_SYNTAX_ERROR; } return ERROR_OK; } +COMMAND_HANDLER(cmsis_dap_handle_quirk_command) +{ + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (CMD_ARGC == 1) + COMMAND_PARSE_ENABLE(CMD_ARGV[0], cmsis_dap_handle->quirk_mode); + + command_print(CMD, "CMSIS-DAP quirk workarounds %s", + cmsis_dap_handle->quirk_mode ? "enabled" : "disabled"); + return ERROR_OK; +} + static const struct command_registration cmsis_dap_subcommand_handlers[] = { { .name = "info", @@ -2172,35 +2251,30 @@ static const struct command_registration cmsis_dap_subcommand_handlers[] = { .usage = "", .help = "issue cmsis-dap command", }, - COMMAND_REGISTRATION_DONE -}; - - -static const struct command_registration cmsis_dap_command_handlers[] = { - { - .name = "cmsis-dap", - .mode = COMMAND_ANY, - .help = "perform CMSIS-DAP management", - .usage = "<cmd>", - .chain = cmsis_dap_subcommand_handlers, - }, { - .name = "cmsis_dap_vid_pid", + .name = "vid_pid", .handler = &cmsis_dap_handle_vid_pid_command, .mode = COMMAND_CONFIG, .help = "the vendor ID and product ID of the CMSIS-DAP device", .usage = "(vid pid)*", }, { - .name = "cmsis_dap_backend", + .name = "backend", .handler = &cmsis_dap_handle_backend_command, .mode = COMMAND_CONFIG, .help = "set the communication backend to use (USB bulk or HID).", .usage = "(auto | usb_bulk | hid)", }, + { + .name = "quirk", + .handler = &cmsis_dap_handle_quirk_command, + .mode = COMMAND_ANY, + .help = "allow expensive workarounds of known adapter quirks.", + .usage = "[enable | disable]", + }, #if BUILD_CMSIS_DAP_USB { - .name = "cmsis_dap_usb", + .name = "usb", .chain = cmsis_dap_usb_subcommand_handlers, .mode = COMMAND_ANY, .help = "USB bulk backend-specific commands", @@ -2210,6 +2284,18 @@ static const struct command_registration cmsis_dap_command_handlers[] = { COMMAND_REGISTRATION_DONE }; + +static const struct command_registration cmsis_dap_command_handlers[] = { + { + .name = "cmsis-dap", + .mode = COMMAND_ANY, + .help = "perform CMSIS-DAP management", + .usage = "<cmd>", + .chain = cmsis_dap_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + static const struct swd_driver cmsis_dap_swd_driver = { .init = cmsis_dap_swd_init, .switch_seq = cmsis_dap_swd_switch_seq, diff --git a/src/jtag/drivers/cmsis_dap.h b/src/jtag/drivers/cmsis_dap.h index 16885a5..e47697d 100644 --- a/src/jtag/drivers/cmsis_dap.h +++ b/src/jtag/drivers/cmsis_dap.h @@ -52,7 +52,8 @@ struct cmsis_dap { unsigned int pending_fifo_block_count; uint16_t caps; - uint8_t mode; + bool quirk_mode; /* enable expensive workarounds */ + uint32_t swo_buf_sz; bool trace_enabled; }; @@ -61,9 +62,12 @@ struct cmsis_dap_backend { const char *name; int (*open)(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], const char *serial); void (*close)(struct cmsis_dap *dap); - int (*read)(struct cmsis_dap *dap, int timeout_ms); + int (*read)(struct cmsis_dap *dap, int transfer_timeout_ms, + struct timeval *wait_timeout); int (*write)(struct cmsis_dap *dap, int len, int timeout_ms); int (*packet_buffer_alloc)(struct cmsis_dap *dap, unsigned int pkt_sz); + void (*packet_buffer_free)(struct cmsis_dap *dap); + void (*cancel_all)(struct cmsis_dap *dap); }; extern const struct cmsis_dap_backend cmsis_dap_hid_backend; diff --git a/src/jtag/drivers/cmsis_dap_usb_bulk.c b/src/jtag/drivers/cmsis_dap_usb_bulk.c index 6599c41..17e490f 100644 --- a/src/jtag/drivers/cmsis_dap_usb_bulk.c +++ b/src/jtag/drivers/cmsis_dap_usb_bulk.c @@ -28,8 +28,29 @@ #include <libusb.h> #include <helper/log.h> #include <helper/replacements.h> +#include <jtag/jtag.h> /* ERROR_JTAG_DEVICE_ERROR only */ #include "cmsis_dap.h" +#include "libusb_helper.h" + +#if !defined(LIBUSB_API_VERSION) || (LIBUSB_API_VERSION < 0x01000105) \ + || defined(_WIN32) || defined(__CYGWIN__) + #define libusb_dev_mem_alloc(dev, sz) malloc(sz) + #define libusb_dev_mem_free(dev, buffer, sz) free(buffer) +#endif + +enum { + CMSIS_DAP_TRANSFER_PENDING = 0, /* must be 0, used in libusb_handle_events_completed */ + CMSIS_DAP_TRANSFER_IDLE, + CMSIS_DAP_TRANSFER_COMPLETED +}; + +struct cmsis_dap_bulk_transfer { + struct libusb_transfer *transfer; + uint8_t *buffer; + int status; /* either CMSIS_DAP_TRANSFER_ enum or error code */ + int transferred; +}; struct cmsis_dap_backend_data { struct libusb_context *usb_ctx; @@ -37,12 +58,16 @@ struct cmsis_dap_backend_data { unsigned int ep_out; unsigned int ep_in; int interface; + + struct cmsis_dap_bulk_transfer command_transfers[MAX_PENDING_REQUESTS]; + struct cmsis_dap_bulk_transfer response_transfers[MAX_PENDING_REQUESTS]; }; static int cmsis_dap_usb_interface = -1; static void cmsis_dap_usb_close(struct cmsis_dap *dap); static int cmsis_dap_usb_alloc(struct cmsis_dap *dap, unsigned int pkt_sz); +static void cmsis_dap_usb_free(struct cmsis_dap *dap); static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], const char *serial) { @@ -358,6 +383,24 @@ static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t p dap->bdata->ep_in = ep_in; dap->bdata->interface = interface_num; + for (unsigned int idx = 0; idx < MAX_PENDING_REQUESTS; idx++) { + dap->bdata->command_transfers[idx].status = CMSIS_DAP_TRANSFER_IDLE; + dap->bdata->command_transfers[idx].transfer = libusb_alloc_transfer(0); + if (!dap->bdata->command_transfers[idx].transfer) { + LOG_ERROR("unable to allocate USB transfer"); + cmsis_dap_usb_close(dap); + return ERROR_FAIL; + } + + dap->bdata->response_transfers[idx].status = CMSIS_DAP_TRANSFER_IDLE; + dap->bdata->response_transfers[idx].transfer = libusb_alloc_transfer(0); + if (!dap->bdata->response_transfers[idx].transfer) { + LOG_ERROR("unable to allocate USB transfer"); + cmsis_dap_usb_close(dap); + return ERROR_FAIL; + } + } + err = cmsis_dap_usb_alloc(dap, packet_size); if (err != ERROR_OK) cmsis_dap_usb_close(dap); @@ -376,65 +419,178 @@ static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t p static void cmsis_dap_usb_close(struct cmsis_dap *dap) { + for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) { + libusb_free_transfer(dap->bdata->command_transfers[i].transfer); + libusb_free_transfer(dap->bdata->response_transfers[i].transfer); + } + cmsis_dap_usb_free(dap); libusb_release_interface(dap->bdata->dev_handle, dap->bdata->interface); libusb_close(dap->bdata->dev_handle); libusb_exit(dap->bdata->usb_ctx); free(dap->bdata); dap->bdata = NULL; - free(dap->packet_buffer); - dap->packet_buffer = NULL; } -static int cmsis_dap_usb_read(struct cmsis_dap *dap, int timeout_ms) +static void LIBUSB_CALL cmsis_dap_usb_callback(struct libusb_transfer *transfer) +{ + struct cmsis_dap_bulk_transfer *tr; + + tr = (struct cmsis_dap_bulk_transfer *)transfer->user_data; + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + tr->status = CMSIS_DAP_TRANSFER_COMPLETED; + tr->transferred = transfer->actual_length; + } else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { + tr->status = ERROR_TIMEOUT_REACHED; + } else { + tr->status = ERROR_JTAG_DEVICE_ERROR; + } +} + +static int cmsis_dap_usb_read(struct cmsis_dap *dap, int transfer_timeout_ms, + struct timeval *wait_timeout) { int transferred = 0; int err; + struct cmsis_dap_bulk_transfer *tr; + tr = &dap->bdata->response_transfers[dap->pending_fifo_get_idx]; + + if (tr->status == CMSIS_DAP_TRANSFER_IDLE) { + libusb_fill_bulk_transfer(tr->transfer, + dap->bdata->dev_handle, dap->bdata->ep_in, + tr->buffer, dap->packet_size, + &cmsis_dap_usb_callback, tr, + transfer_timeout_ms); + LOG_DEBUG_IO("submit read @ %u", dap->pending_fifo_get_idx); + tr->status = CMSIS_DAP_TRANSFER_PENDING; + err = libusb_submit_transfer(tr->transfer); + if (err) { + tr->status = CMSIS_DAP_TRANSFER_IDLE; + LOG_ERROR("error submitting USB read: %s", libusb_strerror(err)); + return ERROR_FAIL; + } + } - err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_in, - dap->packet_buffer, dap->packet_size, &transferred, timeout_ms); - if (err) { - if (err == LIBUSB_ERROR_TIMEOUT) { - return ERROR_TIMEOUT_REACHED; - } else { - LOG_ERROR("error reading data: %s", libusb_strerror(err)); + struct timeval tv = { + .tv_sec = transfer_timeout_ms / 1000, + .tv_usec = transfer_timeout_ms % 1000 * 1000 + }; + + while (tr->status == CMSIS_DAP_TRANSFER_PENDING) { + err = libusb_handle_events_timeout_completed(dap->bdata->usb_ctx, + wait_timeout ? wait_timeout : &tv, + &tr->status); + if (err) { + LOG_ERROR("error handling USB events: %s", libusb_strerror(err)); return ERROR_FAIL; } + if (wait_timeout) + break; + } + + if (tr->status < 0 || tr->status == CMSIS_DAP_TRANSFER_COMPLETED) { + /* Check related command request for an error */ + struct cmsis_dap_bulk_transfer *tr_cmd; + tr_cmd = &dap->bdata->command_transfers[dap->pending_fifo_get_idx]; + if (tr_cmd->status < 0) { + err = tr_cmd->status; + tr_cmd->status = CMSIS_DAP_TRANSFER_IDLE; + if (err != ERROR_TIMEOUT_REACHED) + LOG_ERROR("error writing USB data"); + else + LOG_DEBUG("command write USB timeout @ %u", dap->pending_fifo_get_idx); + + return err; + } + if (tr_cmd->status == CMSIS_DAP_TRANSFER_COMPLETED) + tr_cmd->status = CMSIS_DAP_TRANSFER_IDLE; } - memset(&dap->packet_buffer[transferred], 0, dap->packet_buffer_size - transferred); + if (tr->status < 0) { + err = tr->status; + tr->status = CMSIS_DAP_TRANSFER_IDLE; + if (err != ERROR_TIMEOUT_REACHED) + LOG_ERROR("error reading USB data"); + else + LOG_DEBUG("USB timeout @ %u", dap->pending_fifo_get_idx); + + return err; + } + + if (tr->status == CMSIS_DAP_TRANSFER_COMPLETED) { + transferred = tr->transferred; + LOG_DEBUG_IO("completed read @ %u, transferred %i", + dap->pending_fifo_get_idx, transferred); + memcpy(dap->packet_buffer, tr->buffer, transferred); + memset(&dap->packet_buffer[transferred], 0, dap->packet_buffer_size - transferred); + tr->status = CMSIS_DAP_TRANSFER_IDLE; + } return transferred; } static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen, int timeout_ms) { - int transferred = 0; int err; + struct cmsis_dap_bulk_transfer *tr; + tr = &dap->bdata->command_transfers[dap->pending_fifo_put_idx]; + + if (tr->status == CMSIS_DAP_TRANSFER_PENDING) { + LOG_ERROR("busy command USB transfer at %u", dap->pending_fifo_put_idx); + struct timeval tv = { + .tv_sec = timeout_ms / 1000, + .tv_usec = timeout_ms % 1000 * 1000 + }; + libusb_handle_events_timeout_completed(dap->bdata->usb_ctx, &tv, &tr->status); + } + if (tr->status < 0) { + if (tr->status != ERROR_TIMEOUT_REACHED) + LOG_ERROR("error writing USB data, late detect"); + else + LOG_DEBUG("USB write timeout @ %u, late detect", dap->pending_fifo_get_idx); + tr->status = CMSIS_DAP_TRANSFER_IDLE; + } + if (tr->status == CMSIS_DAP_TRANSFER_COMPLETED) { + LOG_ERROR("USB write: late transfer competed"); + tr->status = CMSIS_DAP_TRANSFER_IDLE; + } + if (tr->status != CMSIS_DAP_TRANSFER_IDLE) { + libusb_cancel_transfer(tr->transfer); + /* TODO: switch to less verbose errors and wait for USB working again */ + return ERROR_JTAG_DEVICE_ERROR; + } + + memcpy(tr->buffer, dap->packet_buffer, txlen); - /* skip the first byte that is only used by the HID backend */ - err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_out, - dap->packet_buffer, txlen, &transferred, timeout_ms); + libusb_fill_bulk_transfer(tr->transfer, + dap->bdata->dev_handle, dap->bdata->ep_out, + tr->buffer, txlen, + &cmsis_dap_usb_callback, tr, + timeout_ms); + + LOG_DEBUG_IO("submit write @ %u", dap->pending_fifo_put_idx); + tr->status = CMSIS_DAP_TRANSFER_PENDING; + err = libusb_submit_transfer(tr->transfer); if (err) { - if (err == LIBUSB_ERROR_TIMEOUT) { - return ERROR_TIMEOUT_REACHED; - } else { - LOG_ERROR("error writing data: %s", libusb_strerror(err)); - return ERROR_FAIL; - } + if (err == LIBUSB_ERROR_BUSY) + libusb_cancel_transfer(tr->transfer); + else + tr->status = CMSIS_DAP_TRANSFER_IDLE; + + LOG_ERROR("error submitting USB write: %s", libusb_strerror(err)); + return ERROR_FAIL; } - return transferred; + return ERROR_OK; } static int cmsis_dap_usb_alloc(struct cmsis_dap *dap, unsigned int pkt_sz) { - uint8_t *buf = malloc(pkt_sz); - if (!buf) { + dap->packet_buffer = malloc(pkt_sz); + if (!dap->packet_buffer) { LOG_ERROR("unable to allocate CMSIS-DAP packet buffer"); return ERROR_FAIL; } - dap->packet_buffer = buf; dap->packet_size = pkt_sz; dap->packet_buffer_size = pkt_sz; /* Prevent sending zero size USB packets */ @@ -443,9 +599,54 @@ static int cmsis_dap_usb_alloc(struct cmsis_dap *dap, unsigned int pkt_sz) dap->command = dap->packet_buffer; dap->response = dap->packet_buffer; + for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) { + dap->bdata->command_transfers[i].buffer = + libusb_dev_mem_alloc(dap->bdata->dev_handle, pkt_sz); + if (!dap->bdata->command_transfers[i].buffer) { + LOG_ERROR("unable to allocate CMSIS-DAP packet buffer"); + return ERROR_FAIL; + } + dap->bdata->response_transfers[i].buffer = + libusb_dev_mem_alloc(dap->bdata->dev_handle, pkt_sz); + if (!dap->bdata->response_transfers[i].buffer) { + LOG_ERROR("unable to allocate CMSIS-DAP packet buffer"); + return ERROR_FAIL; + } + } + return ERROR_OK; } +static void cmsis_dap_usb_free(struct cmsis_dap *dap) +{ + for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) { + libusb_dev_mem_free(dap->bdata->dev_handle, + dap->bdata->command_transfers[i].buffer, dap->packet_size); + dap->bdata->command_transfers[i].buffer = NULL; + libusb_dev_mem_free(dap->bdata->dev_handle, + dap->bdata->response_transfers[i].buffer, dap->packet_size); + dap->bdata->response_transfers[i].buffer = NULL; + } + + free(dap->packet_buffer); + dap->packet_buffer = NULL; + dap->command = NULL; + dap->response = NULL; +} + +static void cmsis_dap_usb_cancel_all(struct cmsis_dap *dap) +{ + for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) { + if (dap->bdata->command_transfers[i].status == CMSIS_DAP_TRANSFER_PENDING) + libusb_cancel_transfer(dap->bdata->command_transfers[i].transfer); + if (dap->bdata->response_transfers[i].status == CMSIS_DAP_TRANSFER_PENDING) + libusb_cancel_transfer(dap->bdata->response_transfers[i].transfer); + + dap->bdata->command_transfers[i].status = CMSIS_DAP_TRANSFER_IDLE; + dap->bdata->response_transfers[i].status = CMSIS_DAP_TRANSFER_IDLE; + } +} + COMMAND_HANDLER(cmsis_dap_handle_usb_interface_command) { if (CMD_ARGC == 1) @@ -474,4 +675,6 @@ const struct cmsis_dap_backend cmsis_dap_usb_backend = { .read = cmsis_dap_usb_read, .write = cmsis_dap_usb_write, .packet_buffer_alloc = cmsis_dap_usb_alloc, + .packet_buffer_free = cmsis_dap_usb_free, + .cancel_all = cmsis_dap_usb_cancel_all, }; diff --git a/src/jtag/drivers/cmsis_dap_usb_hid.c b/src/jtag/drivers/cmsis_dap_usb_hid.c index 52dfd76..98ccc3e 100644 --- a/src/jtag/drivers/cmsis_dap_usb_hid.c +++ b/src/jtag/drivers/cmsis_dap_usb_hid.c @@ -34,8 +34,39 @@ struct cmsis_dap_backend_data { hid_device *dev_handle; }; +struct cmsis_dap_report_size { + unsigned short vid; + unsigned short pid; + unsigned int report_size; +}; + +static const struct cmsis_dap_report_size report_size_quirks[] = { + /* Third gen Atmel tools use a report size of 512 */ + /* This list of PIDs comes from toolinfo.py in Microchip's pyedbglib. */ + // Atmel JTAG-ICE 3 + { .vid = 0x03eb, .pid = 0x2140, .report_size = 512 }, + // Atmel-ICE + { .vid = 0x03eb, .pid = 0x2141, .report_size = 512 }, + // Atmel Power Debugger + { .vid = 0x03eb, .pid = 0x2144, .report_size = 512 }, + // EDBG (found on Xplained Pro boards) + { .vid = 0x03eb, .pid = 0x2111, .report_size = 512 }, + // Zero (???) + { .vid = 0x03eb, .pid = 0x2157, .report_size = 512 }, + // EDBG with Mass Storage (found on Xplained Pro boards) + { .vid = 0x03eb, .pid = 0x2169, .report_size = 512 }, + // Commercially available EDBG (for third-party use) + { .vid = 0x03eb, .pid = 0x216a, .report_size = 512 }, + // Kraken (???) + { .vid = 0x03eb, .pid = 0x2170, .report_size = 512 }, + + { .vid = 0, .pid = 0, .report_size = 0 } +}; + + static void cmsis_dap_hid_close(struct cmsis_dap *dap); static int cmsis_dap_hid_alloc(struct cmsis_dap *dap, unsigned int pkt_sz); +static void cmsis_dap_hid_free(struct cmsis_dap *dap); static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], const char *serial) { @@ -138,13 +169,15 @@ static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t p unsigned int packet_size = 64; - /* atmel cmsis-dap uses 512 byte reports */ - /* except when it doesn't e.g. with mEDBG on SAMD10 Xplained - * board */ + /* Check for adapters that are known to have unusual report lengths. */ + for (i = 0; report_size_quirks[i].vid != 0; i++) { + if (report_size_quirks[i].vid == target_vid && + report_size_quirks[i].pid == target_pid) { + packet_size = report_size_quirks[i].report_size; + } + } /* TODO: HID report descriptor should be parsed instead of - * hardcoding a match by VID */ - if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175) - packet_size = 512; + * hardcoding a match by VID/PID */ dap->bdata->dev_handle = dev; @@ -165,14 +198,21 @@ static void cmsis_dap_hid_close(struct cmsis_dap *dap) hid_exit(); free(dap->bdata); dap->bdata = NULL; - free(dap->packet_buffer); - dap->packet_buffer = NULL; + cmsis_dap_hid_free(dap); } -static int cmsis_dap_hid_read(struct cmsis_dap *dap, int timeout_ms) +static int cmsis_dap_hid_read(struct cmsis_dap *dap, int transfer_timeout_ms, + struct timeval *wait_timeout) { - int retval = hid_read_timeout(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_buffer_size, timeout_ms); - + int timeout_ms; + if (wait_timeout) + timeout_ms = wait_timeout->tv_usec / 1000 + wait_timeout->tv_sec * 1000; + else + timeout_ms = transfer_timeout_ms; + + int retval = hid_read_timeout(dap->bdata->dev_handle, + dap->packet_buffer, dap->packet_buffer_size, + timeout_ms); if (retval == 0) { return ERROR_TIMEOUT_REACHED; } else if (retval == -1) { @@ -222,6 +262,16 @@ static int cmsis_dap_hid_alloc(struct cmsis_dap *dap, unsigned int pkt_sz) return ERROR_OK; } +static void cmsis_dap_hid_free(struct cmsis_dap *dap) +{ + free(dap->packet_buffer); + dap->packet_buffer = NULL; +} + +static void cmsis_dap_hid_cancel_all(struct cmsis_dap *dap) +{ +} + const struct cmsis_dap_backend cmsis_dap_hid_backend = { .name = "hid", .open = cmsis_dap_hid_open, @@ -229,4 +279,6 @@ const struct cmsis_dap_backend cmsis_dap_hid_backend = { .read = cmsis_dap_hid_read, .write = cmsis_dap_hid_write, .packet_buffer_alloc = cmsis_dap_hid_alloc, + .packet_buffer_free = cmsis_dap_hid_free, + .cancel_all = cmsis_dap_hid_cancel_all, }; diff --git a/src/jtag/drivers/driver.c b/src/jtag/drivers/driver.c index 409b800..7732315 100644 --- a/src/jtag/drivers/driver.c +++ b/src/jtag/drivers/driver.c @@ -76,13 +76,13 @@ int interface_jtag_add_ir_scan(struct jtag_tap *active, if (tap == active) { /* if TAP is listed in input fields, copy the value */ - tap->bypass = 0; + tap->bypass = false; jtag_scan_field_clone(field, in_fields); } else { /* if a TAP isn't listed in input fields, set it to BYPASS */ - tap->bypass = 1; + tap->bypass = true; field->num_bits = tap->ir_length; field->out_value = buf_set_ones(cmd_queue_alloc(DIV_ROUND_UP(tap->ir_length, 8)), tap->ir_length); diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index dd8cb16..880e90f 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -1213,7 +1213,7 @@ COMMAND_HANDLER(ftdi_handle_set_signal_command) /* fallthrough */ default: LOG_ERROR("unknown signal level '%s', use 0, 1 or z", CMD_ARGV[1]); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } return mpsse_flush(mpsse_ctx); diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index 243d1a4..5743d8d 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -966,19 +966,17 @@ COMMAND_HANDLER(jlink_usb_command) { int tmp; - if (CMD_ARGC != 1) { - command_print(CMD, "Need exactly one argument for jlink usb"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } if (sscanf(CMD_ARGV[0], "%i", &tmp) != 1) { command_print(CMD, "Invalid USB address: %s", CMD_ARGV[0]); - return ERROR_FAIL; + return ERROR_COMMAND_ARGUMENT_INVALID; } if (tmp < JAYLINK_USB_ADDRESS_0 || tmp > JAYLINK_USB_ADDRESS_3) { command_print(CMD, "Invalid USB address: %s", CMD_ARGV[0]); - return ERROR_FAIL; + return ERROR_COMMAND_ARGUMENT_INVALID; } usb_address = tmp; @@ -1059,7 +1057,7 @@ COMMAND_HANDLER(jlink_handle_jlink_jtag_command) } else if (CMD_ARGC == 1) { if (sscanf(CMD_ARGV[0], "%i", &tmp) != 1) { command_print(CMD, "Invalid argument: %s", CMD_ARGV[0]); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } switch (tmp) { @@ -1071,10 +1069,9 @@ COMMAND_HANDLER(jlink_handle_jlink_jtag_command) break; default: command_print(CMD, "Invalid argument: %s", CMD_ARGV[0]); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } } else { - command_print(CMD, "Need exactly one argument for jlink jtag"); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -1086,10 +1083,8 @@ COMMAND_HANDLER(jlink_handle_target_power_command) int ret; int enable; - if (CMD_ARGC != 1) { - command_print(CMD, "Need exactly one argument for jlink targetpower"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) { command_print(CMD, "Target power supply is not supported by the " @@ -1428,17 +1423,16 @@ COMMAND_HANDLER(jlink_handle_config_usb_address_command) } else if (CMD_ARGC == 1) { if (sscanf(CMD_ARGV[0], "%" SCNd8, &tmp) != 1) { command_print(CMD, "Invalid USB address: %s", CMD_ARGV[0]); - return ERROR_FAIL; + return ERROR_COMMAND_ARGUMENT_INVALID; } if (tmp > JAYLINK_USB_ADDRESS_3) { command_print(CMD, "Invalid USB address: %u", tmp); - return ERROR_FAIL; + return ERROR_COMMAND_ARGUMENT_INVALID; } tmp_config.usb_address = tmp; } else { - command_print(CMD, "Need exactly one argument for jlink config usb"); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -1470,13 +1464,11 @@ COMMAND_HANDLER(jlink_handle_config_target_power_command) enable = false; } else { command_print(CMD, "Invalid argument: %s", CMD_ARGV[0]); - return ERROR_FAIL; + return ERROR_COMMAND_ARGUMENT_INVALID; } tmp_config.target_power = enable; } else { - command_print(CMD, "Need exactly one argument for jlink config " - "targetpower"); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -1510,7 +1502,7 @@ COMMAND_HANDLER(jlink_handle_config_mac_address_command) if ((strlen(str) != 17) || (str[2] != ':' || str[5] != ':' || str[8] != ':' || str[11] != ':' || str[14] != ':')) { command_print(CMD, "Invalid MAC address format"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } for (i = 5; i >= 0; i--) { @@ -1520,17 +1512,16 @@ COMMAND_HANDLER(jlink_handle_config_mac_address_command) if (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) { command_print(CMD, "Invalid MAC address: zero address"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } if (!(0x01 & addr[0])) { command_print(CMD, "Invalid MAC address: multicast address"); - return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_COMMAND_ARGUMENT_INVALID; } memcpy(tmp_config.mac_address, addr, sizeof(addr)); } else { - command_print(CMD, "Need exactly one argument for jlink config mac"); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -1592,20 +1583,26 @@ COMMAND_HANDLER(jlink_handle_config_ip_address_command) if (!CMD_ARGC) { show_config_ip_address(CMD); } else { - if (!string_to_ip(CMD_ARGV[0], ip_address, &i)) - return ERROR_COMMAND_SYNTAX_ERROR; + if (!string_to_ip(CMD_ARGV[0], ip_address, &i)) { + command_print(CMD, "invalid IPv4 address"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } len = strlen(CMD_ARGV[0]); /* Check for format A.B.C.D/E. */ if (i < len) { - if (CMD_ARGV[0][i] != '/') - return ERROR_COMMAND_SYNTAX_ERROR; + if (CMD_ARGV[0][i] != '/') { + command_print(CMD, "missing network mask"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0] + i + 1, subnet_bits); } else if (CMD_ARGC > 1) { - if (!string_to_ip(CMD_ARGV[1], (uint8_t *)&subnet_mask, &i)) - return ERROR_COMMAND_SYNTAX_ERROR; + if (!string_to_ip(CMD_ARGV[1], (uint8_t *)&subnet_mask, &i)) { + command_print(CMD, "invalid subnet mask"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } } if (!subnet_mask) diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c index f19e9ab..c2b3b08 100644 --- a/src/jtag/drivers/jtag_vpi.c +++ b/src/jtag/drivers/jtag_vpi.c @@ -593,10 +593,8 @@ static int jtag_vpi_quit(void) COMMAND_HANDLER(jtag_vpi_set_port) { - if (CMD_ARGC == 0) { - LOG_ERROR("Command \"jtag_vpi set_port\" expects 1 argument (TCP port number)"); + if (CMD_ARGC == 0) return ERROR_COMMAND_SYNTAX_ERROR; - } COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port); LOG_INFO("jtag_vpi: server port set to %u", server_port); @@ -607,10 +605,8 @@ COMMAND_HANDLER(jtag_vpi_set_port) COMMAND_HANDLER(jtag_vpi_set_address) { - if (CMD_ARGC == 0) { - LOG_ERROR("Command \"jtag_vpi set_address\" expects 1 argument (IP address)"); + if (CMD_ARGC == 0) return ERROR_COMMAND_SYNTAX_ERROR; - } free(server_address); server_address = strdup(CMD_ARGV[0]); @@ -621,10 +617,8 @@ COMMAND_HANDLER(jtag_vpi_set_address) COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command \"jtag_vpi stop_sim_on_exit\" expects 1 argument (on|off)"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } COMMAND_PARSE_ON_OFF(CMD_ARGV[0], stop_sim_on_exit); return ERROR_OK; diff --git a/src/jtag/drivers/kitprog.c b/src/jtag/drivers/kitprog.c index e126a9c..c0d2adc 100644 --- a/src/jtag/drivers/kitprog.c +++ b/src/jtag/drivers/kitprog.c @@ -879,6 +879,13 @@ static const struct command_registration kitprog_subcommand_handlers[] = { .usage = "", .help = "try to acquire a PSoC", }, + { + .name = "init_acquire_psoc", + .handler = &kitprog_handle_init_acquire_psoc_command, + .mode = COMMAND_CONFIG, + .help = "try to acquire a PSoC during init", + .usage = "", + }, COMMAND_REGISTRATION_DONE }; @@ -890,13 +897,6 @@ static const struct command_registration kitprog_command_handlers[] = { .usage = "<cmd>", .chain = kitprog_subcommand_handlers, }, - { - .name = "kitprog_init_acquire_psoc", - .handler = &kitprog_handle_init_acquire_psoc_command, - .mode = COMMAND_CONFIG, - .help = "try to acquire a PSoC during init", - .usage = "", - }, COMMAND_REGISTRATION_DONE }; diff --git a/src/jtag/drivers/remote_bitbang.c b/src/jtag/drivers/remote_bitbang.c index 36bcbe1..261c30d 100644 --- a/src/jtag/drivers/remote_bitbang.c +++ b/src/jtag/drivers/remote_bitbang.c @@ -3,6 +3,8 @@ /*************************************************************************** * Copyright (C) 2011 by Richard Uhler * * ruhler@mit.edu * + * * + * Copyright (C) 2021 by Manuel Wick <manuel@matronix.de> * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -220,11 +222,35 @@ static int remote_bitbang_blink(int on) return remote_bitbang_queue(c, FLUSH_SEND_BUF); } +static void remote_bitbang_swdio_drive(bool is_output) +{ + char c = is_output ? 'O' : 'o'; + if (remote_bitbang_queue(c, FLUSH_SEND_BUF) == ERROR_FAIL) + LOG_ERROR("Error setting direction for swdio"); +} + +static int remote_bitbang_swdio_read(void) +{ + if (remote_bitbang_queue('c', FLUSH_SEND_BUF) != ERROR_FAIL) + return remote_bitbang_read_sample(); + else + return BB_ERROR; +} + +static int remote_bitbang_swd_write(int swclk, int swdio) +{ + char c = 'd' + ((swclk ? 0x2 : 0x0) | (swdio ? 0x1 : 0x0)); + return remote_bitbang_queue(c, NO_FLUSH); +} + static struct bitbang_interface remote_bitbang_bitbang = { .buf_size = sizeof(remote_bitbang_recv_buf) - 1, .sample = &remote_bitbang_sample, .read_sample = &remote_bitbang_read_sample, .write = &remote_bitbang_write, + .swdio_read = &remote_bitbang_swdio_read, + .swdio_drive = &remote_bitbang_swdio_drive, + .swd_write = &remote_bitbang_swd_write, .blink = &remote_bitbang_blink, }; @@ -349,6 +375,8 @@ COMMAND_HANDLER(remote_bitbang_handle_remote_bitbang_host_command) return ERROR_COMMAND_SYNTAX_ERROR; } +static const char * const remote_bitbang_transports[] = { "jtag", "swd", NULL }; + static const struct command_registration remote_bitbang_subcommand_handlers[] = { { .name = "port", @@ -401,7 +429,7 @@ static struct jtag_interface remote_bitbang_interface = { struct adapter_driver remote_bitbang_adapter_driver = { .name = "remote_bitbang", - .transports = jtag_only, + .transports = remote_bitbang_transports, .commands = remote_bitbang_command_handlers, .init = &remote_bitbang_init, @@ -409,4 +437,5 @@ struct adapter_driver remote_bitbang_adapter_driver = { .reset = &remote_bitbang_reset, .jtag_ops = &remote_bitbang_interface, + .swd_ops = &bitbang_swd, }; diff --git a/src/jtag/drivers/rshim.c b/src/jtag/drivers/rshim.c index 6170e86..21fc7fd 100644 --- a/src/jtag/drivers/rshim.c +++ b/src/jtag/drivers/rshim.c @@ -434,10 +434,8 @@ static void rshim_disconnect(struct adiv5_dap *dap) COMMAND_HANDLER(rshim_dap_device_command) { - if (CMD_ARGC != 1) { - command_print(CMD, "Too many arguments"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } free(rshim_dev_path); rshim_dev_path = strdup(CMD_ARGV[0]); diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index f4bfeb1..9c8d0fa 100644 --- a/src/jtag/hla/hla_interface.c +++ b/src/jtag/hla/hla_interface.c @@ -100,7 +100,7 @@ int hl_interface_init_target(struct target *t) } t->tap->priv = &hl_if; - t->tap->hasidcode = 1; + t->tap->has_idcode = true; return ERROR_OK; } diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h index 04d1b4a..1d1c495 100644 --- a/src/jtag/jtag.h +++ b/src/jtag/jtag.h @@ -115,7 +115,7 @@ struct jtag_tap { uint32_t idcode; /**< device identification code */ /** not all devices have idcode, * we'll discover this during chain examination */ - bool hasidcode; + bool has_idcode; /** Array of expected identification codes */ uint32_t *expected_ids; @@ -131,7 +131,7 @@ struct jtag_tap { /** current instruction */ uint8_t *cur_instr; /** Bypass register selected */ - int bypass; + bool bypass; struct jtag_tap_event_action *event_action; diff --git a/src/jtag/startup.tcl b/src/jtag/startup.tcl index 085c89b..4eca677 100644 --- a/src/jtag/startup.tcl +++ b/src/jtag/startup.tcl @@ -1108,6 +1108,30 @@ proc "am335xgpio led_on_state" {state} { } } +lappend _telnet_autocomplete_skip "cmsis_dap_backend" +proc "cmsis_dap_backend" {backend} { + echo "DEPRECATED! use 'cmsis-dap backend', not 'cmsis_dap_backend'" + eval cmsis-dap backend $backend +} + +lappend _telnet_autocomplete_skip "cmsis_dap_vid_pid" +proc "cmsis_dap_vid_pid" {args} { + echo "DEPRECATED! use 'cmsis-dap vid_pid', not 'cmsis_dap_vid_pid'" + eval cmsis-dap vid_pid $args +} + +lappend _telnet_autocomplete_skip "cmsis_dap_usb" +proc "cmsis_dap_usb" {args} { + echo "DEPRECATED! use 'cmsis-dap usb', not 'cmsis_dap_usb'" + eval cmsis-dap usb $args +} + +lappend _telnet_autocomplete_skip "kitprog_init_acquire_psoc" +proc "kitprog_init_acquire_psoc" {} { + echo "DEPRECATED! use 'kitprog init_acquire_psoc', not 'kitprog_init_acquire_psoc'" + eval kitprog init_acquire_psoc +} + lappend _telnet_autocomplete_skip "pld device" proc "pld device" {driver tap_name {opt 0}} { echo "DEPRECATED! use 'pld create ...', not 'pld device ...'" diff --git a/src/pld/intel.c b/src/pld/intel.c index 8422c94..a39e16c 100644 --- a/src/pld/intel.c +++ b/src/pld/intel.c @@ -157,7 +157,7 @@ static int intel_check_for_unique_id(struct intel_pld_device *intel_info) static int intel_check_config(struct intel_pld_device *intel_info) { - if (!intel_info->tap->hasidcode) { + if (!intel_info->tap->has_idcode) { LOG_ERROR("no IDCODE"); return ERROR_FAIL; } diff --git a/src/pld/lattice.c b/src/pld/lattice.c index cd72d3c..2997cdc 100644 --- a/src/pld/lattice.c +++ b/src/pld/lattice.c @@ -81,7 +81,7 @@ static int lattice_check_device_family(struct lattice_pld_device *lattice_device if (lattice_device->family != LATTICE_UNKNOWN && lattice_device->preload_length != 0) return ERROR_OK; - if (!lattice_device->tap || !lattice_device->tap->hasidcode) + if (!lattice_device->tap || !lattice_device->tap->has_idcode) return ERROR_FAIL; for (size_t i = 0; i < ARRAY_SIZE(lattice_devices); ++i) { @@ -280,7 +280,7 @@ static int lattice_load_command(struct pld_device *pld_device, const char *filen return ERROR_FAIL; struct jtag_tap *tap = lattice_device->tap; - if (!tap || !tap->hasidcode) + if (!tap || !tap->has_idcode) return ERROR_FAIL; int retval = lattice_check_device_family(lattice_device); diff --git a/src/rtt/tcl.c b/src/rtt/tcl.c index f949aa1..2b8822f 100644 --- a/src/rtt/tcl.c +++ b/src/rtt/tcl.c @@ -17,7 +17,7 @@ COMMAND_HANDLER(handle_rtt_setup_command) { -struct rtt_source source; + struct rtt_source source; if (CMD_ARGC != 3) return ERROR_COMMAND_SYNTAX_ERROR; diff --git a/src/server/rtt_server.c b/src/server/rtt_server.c index df2247b..9769153 100644 --- a/src/server/rtt_server.c +++ b/src/server/rtt_server.c @@ -25,6 +25,7 @@ struct rtt_service { unsigned int channel; + char *hello_message; }; static int read_callback(unsigned int channel, const uint8_t *buffer, @@ -65,6 +66,9 @@ static int rtt_new_connection(struct connection *connection) if (ret != ERROR_OK) return ret; + if (service->hello_message) + connection_write(connection, service->hello_message, strlen(service->hello_message)); + return ERROR_OK; } @@ -117,16 +121,30 @@ COMMAND_HANDLER(handle_rtt_start_command) int ret; struct rtt_service *service; - if (CMD_ARGC != 2) + if (CMD_ARGC < 2 || CMD_ARGC > 3) return ERROR_COMMAND_SYNTAX_ERROR; - service = malloc(sizeof(struct rtt_service)); + service = calloc(1, sizeof(struct rtt_service)); if (!service) return ERROR_FAIL; COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], service->channel); + if (CMD_ARGC >= 3) { + const char *hello_message = CMD_ARGV[2]; + size_t hello_length = strlen(hello_message); + + service->hello_message = malloc(hello_length + 2); + if (!service->hello_message) { + LOG_ERROR("Out of memory"); + free(service); + return ERROR_FAIL; + } + strcpy(service->hello_message, hello_message); + service->hello_message[hello_length] = '\n'; + service->hello_message[hello_length + 1] = '\0'; + } ret = add_service(&rtt_service_driver, CMD_ARGV[0], CONNECTION_LIMIT_UNLIMITED, service); if (ret != ERROR_OK) { @@ -153,7 +171,7 @@ static const struct command_registration rtt_server_subcommand_handlers[] = { .handler = handle_rtt_start_command, .mode = COMMAND_ANY, .help = "Start a RTT server", - .usage = "<port> <channel>" + .usage = "<port> <channel> [message]" }, { .name = "stop", diff --git a/src/target/aarch64.c b/src/target/aarch64.c index db60243..1c056a0 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -105,7 +105,7 @@ static int aarch64_restore_system_control_reg(struct target *target) if (target_mode != ARM_MODE_ANY) armv8_dpm_modeswitch(&armv8->dpm, target_mode); - retval = armv8->dpm.instr_write_data_r0(&armv8->dpm, instr, aarch64->system_control_reg); + retval = armv8->dpm.instr_write_data_r0_64(&armv8->dpm, instr, aarch64->system_control_reg); if (retval != ERROR_OK) return retval; @@ -182,7 +182,7 @@ static int aarch64_mmu_modify(struct target *target, int enable) if (target_mode != ARM_MODE_ANY) armv8_dpm_modeswitch(&armv8->dpm, target_mode); - retval = armv8->dpm.instr_write_data_r0(&armv8->dpm, instr, + retval = armv8->dpm.instr_write_data_r0_64(&armv8->dpm, instr, aarch64->system_control_reg_curr); if (target_mode != ARM_MODE_ANY) @@ -1055,14 +1055,14 @@ static int aarch64_post_debug_entry(struct target *target) if (target_mode != ARM_MODE_ANY) armv8_dpm_modeswitch(&armv8->dpm, target_mode); - retval = armv8->dpm.instr_read_data_r0(&armv8->dpm, instr, &aarch64->system_control_reg); + retval = armv8->dpm.instr_read_data_r0_64(&armv8->dpm, instr, &aarch64->system_control_reg); if (retval != ERROR_OK) return retval; if (target_mode != ARM_MODE_ANY) armv8_dpm_modeswitch(&armv8->dpm, ARM_MODE_ANY); - LOG_DEBUG("System_register: %8.8" PRIx32, aarch64->system_control_reg); + LOG_DEBUG("System_register: %8.8" PRIx64, aarch64->system_control_reg); aarch64->system_control_reg_curr = aarch64->system_control_reg; if (armv8->armv8_mmu.armv8_cache.info == -1) { diff --git a/src/target/aarch64.h b/src/target/aarch64.h index 2721fe7..b265e82 100644 --- a/src/target/aarch64.h +++ b/src/target/aarch64.h @@ -43,8 +43,8 @@ struct aarch64_common { struct armv8_common armv8_common; /* Context information */ - uint32_t system_control_reg; - uint32_t system_control_reg_curr; + uint64_t system_control_reg; + uint64_t system_control_reg_curr; /* Breakpoint register pairs */ int brp_num_context; diff --git a/src/target/armv8_cache.c b/src/target/armv8_cache.c index cf71119..66d4e00 100644 --- a/src/target/armv8_cache.c +++ b/src/target/armv8_cache.c @@ -243,7 +243,7 @@ static int armv8_flush_all_data(struct target *target) foreach_smp_target(head, target->smp_targets) { struct target *curr = head->target; if (curr->state == TARGET_HALTED) { - LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid); + LOG_TARGET_INFO(curr, "Wait flushing data l1."); retval = _armv8_flush_all_data(curr); } } @@ -286,8 +286,9 @@ static struct armv8_cachesize decode_cache_reg(uint32_t cache_reg) size.index = (cache_reg >> 13) & 0x7fff; size.way = ((cache_reg >> 3) & 0x3ff); - while (((size.way << i) & 0x80000000) == 0) - i++; + if (size.way != 0) + while (((size.way << i) & 0x80000000) == 0) + i++; size.way_shift = i; return size; diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c index d069c6b..77f7673 100644 --- a/src/target/breakpoints.c +++ b/src/target/breakpoints.c @@ -53,7 +53,7 @@ static int breakpoint_add_internal(struct target *target, * breakpoint" ... check all the parameters before * succeeding. */ - LOG_ERROR("Duplicate Breakpoint address: " TARGET_ADDR_FMT " (BP %" PRIu32 ")", + LOG_TARGET_ERROR(target, "Duplicate Breakpoint address: " TARGET_ADDR_FMT " (BP %" PRIu32 ")", address, breakpoint->unique_id); return ERROR_TARGET_DUPLICATE_BREAKPOINT; } @@ -84,16 +84,15 @@ static int breakpoint_add_internal(struct target *target, default: reason = "unknown reason"; fail: - LOG_ERROR("can't add breakpoint: %s", reason); + LOG_TARGET_ERROR(target, "can't add breakpoint: %s", reason); free((*breakpoint_p)->orig_instr); free(*breakpoint_p); *breakpoint_p = NULL; return retval; } - LOG_DEBUG("[%d] added %s breakpoint at " TARGET_ADDR_FMT + LOG_TARGET_DEBUG(target, "added %s breakpoint at " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")", - target->coreid, breakpoint_type_strings[(*breakpoint_p)->type], (*breakpoint_p)->address, (*breakpoint_p)->length, (*breakpoint_p)->unique_id); @@ -135,14 +134,14 @@ static int context_breakpoint_add_internal(struct target *target, (*breakpoint_p)->unique_id = bpwp_unique_id++; retval = target_add_context_breakpoint(target, *breakpoint_p); if (retval != ERROR_OK) { - LOG_ERROR("could not add breakpoint"); + LOG_TARGET_ERROR(target, "could not add breakpoint"); free((*breakpoint_p)->orig_instr); free(*breakpoint_p); *breakpoint_p = NULL; return retval; } - LOG_DEBUG("added %s Context breakpoint at 0x%8.8" PRIx32 " of length 0x%8.8x, (BPID: %" PRIu32 ")", + LOG_TARGET_DEBUG(target, "added %s Context breakpoint at 0x%8.8" PRIx32 " of length 0x%8.8x, (BPID: %" PRIu32 ")", breakpoint_type_strings[(*breakpoint_p)->type], (*breakpoint_p)->asid, (*breakpoint_p)->length, (*breakpoint_p)->unique_id); @@ -166,11 +165,11 @@ static int hybrid_breakpoint_add_internal(struct target *target, * breakpoint" ... check all the parameters before * succeeding. */ - LOG_ERROR("Duplicate Hybrid Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")", + LOG_TARGET_ERROR(target, "Duplicate Hybrid Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")", asid, breakpoint->unique_id); return ERROR_TARGET_DUPLICATE_BREAKPOINT; } else if ((breakpoint->address == address) && (breakpoint->asid == 0)) { - LOG_ERROR("Duplicate Breakpoint IVA: " TARGET_ADDR_FMT " (BP %" PRIu32 ")", + LOG_TARGET_ERROR(target, "Duplicate Breakpoint IVA: " TARGET_ADDR_FMT " (BP %" PRIu32 ")", address, breakpoint->unique_id); return ERROR_TARGET_DUPLICATE_BREAKPOINT; @@ -191,13 +190,13 @@ static int hybrid_breakpoint_add_internal(struct target *target, retval = target_add_hybrid_breakpoint(target, *breakpoint_p); if (retval != ERROR_OK) { - LOG_ERROR("could not add breakpoint"); + LOG_TARGET_ERROR(target, "could not add breakpoint"); free((*breakpoint_p)->orig_instr); free(*breakpoint_p); *breakpoint_p = NULL; return retval; } - LOG_DEBUG( + LOG_TARGET_DEBUG(target, "added %s Hybrid breakpoint at address " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")", breakpoint_type_strings[(*breakpoint_p)->type], (*breakpoint_p)->address, @@ -307,7 +306,7 @@ static int breakpoint_free(struct target *data_target, struct target *breakpoint return retval; } - LOG_DEBUG("free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval); + LOG_TARGET_DEBUG(data_target, "free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval); (*breakpoint_p) = breakpoint->next; free(breakpoint->orig_instr); free(breakpoint); @@ -445,7 +444,7 @@ static int watchpoint_free(struct target *target, struct watchpoint *watchpoint_ return retval; } - LOG_DEBUG("free WPID: %d --> %d", watchpoint->unique_id, retval); + LOG_TARGET_DEBUG(target, "free WPID: %d --> %d", watchpoint->unique_id, retval); (*watchpoint_p) = watchpoint->next; free(watchpoint); @@ -556,7 +555,7 @@ static int watchpoint_add_internal(struct target *target, target_addr_t address, || watchpoint->value != value || watchpoint->mask != mask || watchpoint->rw != rw) { - LOG_ERROR("address " TARGET_ADDR_FMT + LOG_TARGET_ERROR(target, "address " TARGET_ADDR_FMT " already has watchpoint %d", address, watchpoint->unique_id); return ERROR_FAIL; @@ -590,7 +589,7 @@ static int watchpoint_add_internal(struct target *target, target_addr_t address, default: reason = "unrecognized error"; bye: - LOG_ERROR("can't add %s watchpoint at " TARGET_ADDR_FMT ", %s", + LOG_TARGET_ERROR(target, "can't add %s watchpoint at " TARGET_ADDR_FMT ", %s", watchpoint_rw_strings[(*watchpoint_p)->rw], address, reason); free(*watchpoint_p); @@ -598,9 +597,8 @@ bye: return retval; } - LOG_DEBUG("[%d] added %s watchpoint at " TARGET_ADDR_FMT + LOG_TARGET_DEBUG(target, "added %s watchpoint at " TARGET_ADDR_FMT " of length 0x%8.8" PRIx32 " (WPID: %d)", - target->coreid, watchpoint_rw_strings[(*watchpoint_p)->rw], (*watchpoint_p)->address, (*watchpoint_p)->length, @@ -718,7 +716,7 @@ int watchpoint_hit(struct target *target, enum watchpoint_rw *rw, *rw = hit_watchpoint->rw; *address = hit_watchpoint->address; - LOG_DEBUG("Found hit watchpoint at " TARGET_ADDR_FMT " (WPID: %d)", + LOG_TARGET_DEBUG(target, "Found hit watchpoint at " TARGET_ADDR_FMT " (WPID: %d)", hit_watchpoint->address, hit_watchpoint->unique_id); diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index ba3349d..7fa0c4e 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -2989,29 +2989,29 @@ static int cortex_a_examine_first(struct target *target) armv7a->debug_base + CPUDBG_PRSR, &dbg_osreg); if (retval != ERROR_OK) return retval; - LOG_DEBUG("target->coreid %" PRId32 " DBGPRSR 0x%" PRIx32, target->coreid, dbg_osreg); + LOG_TARGET_DEBUG(target, "DBGPRSR 0x%" PRIx32, dbg_osreg); if ((dbg_osreg & PRSR_POWERUP_STATUS) == 0) { - LOG_ERROR("target->coreid %" PRId32 " powered down!", target->coreid); + LOG_TARGET_ERROR(target, "powered down!"); target->state = TARGET_UNKNOWN; /* TARGET_NO_POWER? */ return ERROR_TARGET_INIT_FAILED; } if (dbg_osreg & PRSR_STICKY_RESET_STATUS) - LOG_DEBUG("target->coreid %" PRId32 " was reset!", target->coreid); + LOG_TARGET_DEBUG(target, "was reset!"); /* Read DBGOSLSR and check if OSLK is implemented */ retval = mem_ap_read_atomic_u32(armv7a->debug_ap, armv7a->debug_base + CPUDBG_OSLSR, &dbg_osreg); if (retval != ERROR_OK) return retval; - LOG_DEBUG("target->coreid %" PRId32 " DBGOSLSR 0x%" PRIx32, target->coreid, dbg_osreg); + LOG_TARGET_DEBUG(target, "DBGOSLSR 0x%" PRIx32, dbg_osreg); /* check if OS Lock is implemented */ if ((dbg_osreg & OSLSR_OSLM) == OSLSR_OSLM0 || (dbg_osreg & OSLSR_OSLM) == OSLSR_OSLM1) { /* check if OS Lock is set */ if (dbg_osreg & OSLSR_OSLK) { - LOG_DEBUG("target->coreid %" PRId32 " OSLock set! Trying to unlock", target->coreid); + LOG_TARGET_DEBUG(target, "OSLock set! Trying to unlock"); retval = mem_ap_write_atomic_u32(armv7a->debug_ap, armv7a->debug_base + CPUDBG_OSLAR, @@ -3022,8 +3022,7 @@ static int cortex_a_examine_first(struct target *target) /* if we fail to access the register or cannot reset the OSLK bit, bail out */ if (retval != ERROR_OK || (dbg_osreg & OSLSR_OSLK) != 0) { - LOG_ERROR("target->coreid %" PRId32 " OSLock sticky, core not powered?", - target->coreid); + LOG_TARGET_ERROR(target, "OSLock sticky, core not powered?"); target->state = TARGET_UNKNOWN; /* TARGET_NO_POWER? */ return ERROR_TARGET_INIT_FAILED; } @@ -3036,13 +3035,11 @@ static int cortex_a_examine_first(struct target *target) return retval; if (dbg_idpfr1 & 0x000000f0) { - LOG_DEBUG("target->coreid %" PRId32 " has security extensions", - target->coreid); + LOG_TARGET_DEBUG(target, "has security extensions"); armv7a->arm.core_type = ARM_CORE_TYPE_SEC_EXT; } if (dbg_idpfr1 & 0x0000f000) { - LOG_DEBUG("target->coreid %" PRId32 " has virtualization extensions", - target->coreid); + LOG_TARGET_DEBUG(target, "has virtualization extensions"); /* * overwrite and simplify the checks. * virtualization extensions require implementation of security extension diff --git a/src/target/dsp563xx.c b/src/target/dsp563xx.c index 16fd149..1e71803 100644 --- a/src/target/dsp563xx.c +++ b/src/target/dsp563xx.c @@ -912,7 +912,7 @@ static int dsp563xx_examine(struct target *target) { uint32_t chip; - if (target->tap->hasidcode == false) { + if (!target->tap->has_idcode) { LOG_ERROR("no IDCODE present on device"); return ERROR_COMMAND_SYNTAX_ERROR; } diff --git a/src/target/espressif/Makefile.am b/src/target/espressif/Makefile.am index 776818f..cf82ee9 100644 --- a/src/target/espressif/Makefile.am +++ b/src/target/espressif/Makefile.am @@ -10,6 +10,8 @@ noinst_LTLIBRARIES += %D%/libespressif.la %D%/esp_xtensa_semihosting.h \ %D%/esp_xtensa_apptrace.c \ %D%/esp_xtensa_apptrace.h \ + %D%/esp_xtensa_algorithm.c \ + %D%/esp_xtensa_algorithm.h \ %D%/esp32_apptrace.c \ %D%/esp32_apptrace.h \ %D%/esp32.c \ @@ -21,4 +23,6 @@ noinst_LTLIBRARIES += %D%/libespressif.la %D%/esp32_sysview.h \ %D%/segger_sysview.h \ %D%/esp_semihosting.c \ - %D%/esp_semihosting.h + %D%/esp_semihosting.h \ + %D%/esp_algorithm.c \ + %D%/esp_algorithm.h diff --git a/src/target/espressif/esp.c b/src/target/espressif/esp.c index 9583d64..600f6d7 100644 --- a/src/target/espressif/esp.c +++ b/src/target/espressif/esp.c @@ -14,6 +14,16 @@ #include "target/target.h" #include "esp.h" +int esp_common_init(struct esp_common *esp, const struct esp_algorithm_hw *algo_hw) +{ + if (!esp) + return ERROR_FAIL; + + esp->algo_hw = algo_hw; + + return ERROR_OK; +} + int esp_dbgstubs_table_read(struct target *target, struct esp_dbg_stubs *dbg_stubs) { uint32_t table_size, table_start_id, desc_entry_id, gcov_entry_id; diff --git a/src/target/espressif/esp.h b/src/target/espressif/esp.h index 3ba2b8b..6e0a2d2 100644 --- a/src/target/espressif/esp.h +++ b/src/target/espressif/esp.h @@ -77,9 +77,11 @@ struct esp_dbg_stubs { }; struct esp_common { + const struct esp_algorithm_hw *algo_hw; struct esp_dbg_stubs dbg_stubs; }; +int esp_common_init(struct esp_common *esp, const struct esp_algorithm_hw *algo_hw); int esp_dbgstubs_table_read(struct target *target, struct esp_dbg_stubs *dbg_stubs); #endif /* OPENOCD_TARGET_ESP_H */ diff --git a/src/target/espressif/esp32.c b/src/target/espressif/esp32.c index b510f28..324aa39 100644 --- a/src/target/espressif/esp32.c +++ b/src/target/espressif/esp32.c @@ -484,6 +484,10 @@ struct target_type esp32_target = { .get_gdb_arch = xtensa_get_gdb_arch, .get_gdb_reg_list = xtensa_get_gdb_reg_list, + .run_algorithm = xtensa_run_algorithm, + .start_algorithm = xtensa_start_algorithm, + .wait_algorithm = xtensa_wait_algorithm, + .add_breakpoint = esp_xtensa_breakpoint_add, .remove_breakpoint = esp_xtensa_breakpoint_remove, diff --git a/src/target/espressif/esp32s2.c b/src/target/espressif/esp32s2.c index dadc130..2abde47 100644 --- a/src/target/espressif/esp32s2.c +++ b/src/target/espressif/esp32s2.c @@ -521,6 +521,10 @@ struct target_type esp32s2_target = { .get_gdb_arch = xtensa_get_gdb_arch, .get_gdb_reg_list = xtensa_get_gdb_reg_list, + .run_algorithm = xtensa_run_algorithm, + .start_algorithm = xtensa_start_algorithm, + .wait_algorithm = xtensa_wait_algorithm, + .add_breakpoint = esp_xtensa_breakpoint_add, .remove_breakpoint = esp_xtensa_breakpoint_remove, diff --git a/src/target/espressif/esp32s3.c b/src/target/espressif/esp32s3.c index 5036956..22e1630 100644 --- a/src/target/espressif/esp32s3.c +++ b/src/target/espressif/esp32s3.c @@ -405,6 +405,10 @@ struct target_type esp32s3_target = { .get_gdb_arch = xtensa_get_gdb_arch, .get_gdb_reg_list = xtensa_get_gdb_reg_list, + .run_algorithm = xtensa_run_algorithm, + .start_algorithm = xtensa_start_algorithm, + .wait_algorithm = xtensa_wait_algorithm, + .add_breakpoint = esp_xtensa_breakpoint_add, .remove_breakpoint = esp_xtensa_breakpoint_remove, diff --git a/src/target/espressif/esp_algorithm.c b/src/target/espressif/esp_algorithm.c new file mode 100644 index 0000000..79f610b --- /dev/null +++ b/src/target/espressif/esp_algorithm.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Espressif chips common algorithm API for OpenOCD * + * Copyright (C) 2022 Espressif Systems Ltd. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <helper/align.h> +#include <target/algorithm.h> +#include <target/target.h> +#include "esp_algorithm.h" + +#define DEFAULT_ALGORITHM_TIMEOUT_MS 40000 /* ms */ + +static int esp_algorithm_read_stub_logs(struct target *target, struct esp_algorithm_stub *stub) +{ + if (!stub || stub->log_buff_addr == 0 || stub->log_buff_size == 0) + return ERROR_FAIL; + + uint32_t len = 0; + int retval = target_read_u32(target, stub->log_buff_addr, &len); + if (retval != ERROR_OK) + return retval; + + /* sanity check. log_buff_size = sizeof(len) + sizeof(log_buff) */ + if (len == 0 || len > stub->log_buff_size - 4) + return ERROR_FAIL; + + uint8_t *log_buff = calloc(1, len); + if (!log_buff) { + LOG_ERROR("Failed to allocate memory for the stub log!"); + return ERROR_FAIL; + } + retval = target_read_memory(target, stub->log_buff_addr + 4, 1, len, log_buff); + if (retval == ERROR_OK) + LOG_OUTPUT("%*.*s", len, len, log_buff); + free(log_buff); + return retval; +} + +static int esp_algorithm_run_image(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap) +{ + struct working_area **mem_handles = NULL; + + if (!run || !run->hw) + return ERROR_FAIL; + + int retval = run->hw->algo_init(target, run, num_args, ap); + if (retval != ERROR_OK) + return retval; + + /* allocate memory arguments and fill respective reg params */ + if (run->mem_args.count > 0) { + mem_handles = calloc(run->mem_args.count, sizeof(*mem_handles)); + if (!mem_handles) { + LOG_ERROR("Failed to alloc target mem handles!"); + retval = ERROR_FAIL; + goto _cleanup; + } + /* alloc memory args target buffers */ + for (uint32_t i = 0; i < run->mem_args.count; i++) { + /* small hack: if we need to update some reg param this field holds + * appropriate user argument number, */ + /* otherwise should hold UINT_MAX */ + uint32_t usr_param_num = run->mem_args.params[i].address; + static struct working_area *area; + retval = target_alloc_working_area(target, run->mem_args.params[i].size, &area); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to alloc target buffer!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _cleanup; + } + mem_handles[i] = area; + run->mem_args.params[i].address = area->address; + if (usr_param_num != UINT_MAX) /* if we need update some register param with mem param value */ + esp_algorithm_user_arg_set_uint(run, usr_param_num, run->mem_args.params[i].address); + } + } + + if (run->usr_func_init) { + retval = run->usr_func_init(target, run, run->usr_func_arg); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to prepare algorithm host side args stub (%d)!", retval); + goto _cleanup; + } + } + + LOG_DEBUG("Algorithm start @ " TARGET_ADDR_FMT ", stack %d bytes @ " TARGET_ADDR_FMT, + run->stub.tramp_mapped_addr, run->stack_size, run->stub.stack_addr); + retval = target_start_algorithm(target, + run->mem_args.count, run->mem_args.params, + run->reg_args.count, run->reg_args.params, + run->stub.tramp_mapped_addr, 0, + run->stub.ainfo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to start algorithm (%d)!", retval); + goto _cleanup; + } + + if (run->usr_func) { + /* give target algorithm stub time to init itself, then user func can communicate to it safely */ + alive_sleep(100); + retval = run->usr_func(target, run->usr_func_arg); + if (retval != ERROR_OK) + LOG_ERROR("Failed to exec algorithm user func (%d)!", retval); + } + uint32_t timeout_ms = 0; /* do not wait if 'usr_func' returned error */ + if (retval == ERROR_OK) + timeout_ms = run->timeout_ms ? run->timeout_ms : DEFAULT_ALGORITHM_TIMEOUT_MS; + LOG_DEBUG("Wait algorithm completion"); + retval = target_wait_algorithm(target, + run->mem_args.count, run->mem_args.params, + run->reg_args.count, run->reg_args.params, + 0, timeout_ms, + run->stub.ainfo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to wait algorithm (%d)!", retval); + /* target has been forced to stop in target_wait_algorithm() */ + } + esp_algorithm_read_stub_logs(target, &run->stub); + + if (run->usr_func_done) + run->usr_func_done(target, run, run->usr_func_arg); + + if (retval != ERROR_OK) { + LOG_ERROR("Algorithm run failed (%d)!", retval); + } else { + run->ret_code = esp_algorithm_user_arg_get_uint(run, 0); + LOG_DEBUG("Got algorithm RC 0x%" PRIx32, run->ret_code); + } + +_cleanup: + /* free memory arguments */ + if (mem_handles) { + for (uint32_t i = 0; i < run->mem_args.count; i++) { + if (mem_handles[i]) + target_free_working_area(target, mem_handles[i]); + } + free(mem_handles); + } + run->hw->algo_cleanup(target, run); + + return retval; +} + +static int esp_algorithm_run_debug_stub(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap) +{ + if (!run || !run->hw) + return ERROR_FAIL; + + int retval = run->hw->algo_init(target, run, num_args, ap); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("Algorithm start @ " TARGET_ADDR_FMT ", stack %d bytes @ " TARGET_ADDR_FMT, + run->stub.tramp_mapped_addr, run->stack_size, run->stub.stack_addr); + retval = target_start_algorithm(target, + run->mem_args.count, run->mem_args.params, + run->reg_args.count, run->reg_args.params, + run->stub.tramp_mapped_addr, 0, + run->stub.ainfo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to start algorithm (%d)!", retval); + goto _cleanup; + } + + uint32_t timeout_ms = 0; /* do not wait if 'usr_func' returned error */ + if (retval == ERROR_OK) + timeout_ms = run->timeout_ms ? run->timeout_ms : DEFAULT_ALGORITHM_TIMEOUT_MS; + LOG_DEBUG("Wait algorithm completion"); + retval = target_wait_algorithm(target, + run->mem_args.count, run->mem_args.params, + run->reg_args.count, run->reg_args.params, + 0, timeout_ms, + run->stub.ainfo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to wait algorithm (%d)!", retval); + /* target has been forced to stop in target_wait_algorithm() */ + } + + if (retval != ERROR_OK) { + LOG_ERROR("Algorithm run failed (%d)!", retval); + } else { + run->ret_code = esp_algorithm_user_arg_get_uint(run, 0); + LOG_DEBUG("Got algorithm RC 0x%" PRIx32, run->ret_code); + } + +_cleanup: + run->hw->algo_cleanup(target, run); + + return retval; +} + +static void reverse_binary(const uint8_t *src, uint8_t *dest, size_t length) +{ + size_t remaining = length % 4; + size_t offset = 0; + size_t aligned_len = ALIGN_UP(length, 4); + + if (remaining > 0) { + /* Put extra bytes to the beginning with padding */ + memset(dest + remaining, 0xFF, 4 - remaining); + for (size_t i = 0; i < remaining; i++) + dest[i] = src[length - remaining + i]; + length -= remaining; /* reverse the others */ + offset = 4; + } + + for (size_t i = offset; i < aligned_len; i += 4) { + dest[i + 0] = src[length - i + offset - 4]; + dest[i + 1] = src[length - i + offset - 3]; + dest[i + 2] = src[length - i + offset - 2]; + dest[i + 3] = src[length - i + offset - 1]; + } +} + +static int load_section_from_image(struct target *target, + struct esp_algorithm_run_data *run, + int section_num, + bool reverse) +{ + if (!run) + return ERROR_FAIL; + + struct imagesection *section = &run->image.image.sections[section_num]; + uint32_t sec_wr = 0; + uint8_t buf[1024]; + + assert(sizeof(buf) % 4 == 0); + + while (sec_wr < section->size) { + uint32_t nb = section->size - sec_wr > sizeof(buf) ? sizeof(buf) : section->size - sec_wr; + size_t size_read = 0; + int retval = image_read_section(&run->image.image, section_num, sec_wr, nb, buf, &size_read); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read stub section (%d)!", retval); + return retval; + } + + if (reverse) { + size_t aligned_len = ALIGN_UP(size_read, 4); + uint8_t reversed_buf[aligned_len]; + + /* Send original size to allow padding */ + reverse_binary(buf, reversed_buf, size_read); + + /* + The address range accessed via the instruction bus is in reverse order (word-wise) compared to access + via the data bus. That is to say, address + 0x3FFE_0000 and 0x400B_FFFC access the same word + 0x3FFE_0004 and 0x400B_FFF8 access the same word + 0x3FFE_0008 and 0x400B_FFF4 access the same word + ... + The data bus and instruction bus of the CPU are still both little-endian, + so the byte order of individual words is not reversed between address spaces. + For example, address + 0x3FFE_0000 accesses the least significant byte in the word accessed by 0x400B_FFFC. + 0x3FFE_0001 accesses the second least significant byte in the word accessed by 0x400B_FFFC. + 0x3FFE_0002 accesses the second most significant byte in the word accessed by 0x400B_FFFC. + For more details, please refer to ESP32 TRM, Internal SRAM1 section. + */ + retval = target_write_buffer(target, run->image.dram_org - sec_wr - aligned_len, aligned_len, reversed_buf); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write stub section!"); + return retval; + } + } else { + retval = target_write_buffer(target, section->base_address + sec_wr, size_read, buf); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write stub section!"); + return retval; + } + } + + sec_wr += size_read; + } + + return ERROR_OK; +} + +/* + * Configuration: + * ---------------------------- + * The linker scripts defines the memory layout for the stub code. + * The OpenOCD script specifies the workarea address and it's size + * Sections defined in the linker are organized to share the same addresses with the workarea. + * code and data sections are located in Internal SRAM1 and OpenOCD fills these sections using the data bus. + */ +int esp_algorithm_load_func_image(struct target *target, struct esp_algorithm_run_data *run) +{ + int retval; + size_t tramp_sz = 0; + const uint8_t *tramp = NULL; + struct duration algo_time; + bool alloc_code_working_area = true; + + if (!run || !run->hw) + return ERROR_FAIL; + + if (duration_start(&algo_time) != 0) { + LOG_ERROR("Failed to start algo time measurement!"); + return ERROR_FAIL; + } + + if (run->hw->stub_tramp_get) { + tramp = run->hw->stub_tramp_get(target, &tramp_sz); + if (!tramp) + return ERROR_FAIL; + } + + LOG_DEBUG("stub: base 0x%x, start 0x%" PRIx32 ", %d sections", + run->image.image.base_address_set ? (unsigned int)run->image.image.base_address : 0, + run->image.image.start_address, + run->image.image.num_sections); + run->stub.entry = run->image.image.start_address; + + /* [code + trampoline] + <padding> + [data] */ + + /* ESP32 has reversed memory region. It will use the last part of DRAM, the others will use the first part. + * To avoid complexity for the backup/restore process, we will allocate a workarea for all IRAM region from + * the beginning. In that case no need to have a padding area. + */ + if (run->image.reverse) { + if (target_alloc_working_area(target, run->image.iram_len, &run->stub.code) != ERROR_OK) { + LOG_ERROR("no working area available, can't alloc space for stub code!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _on_error; + } + alloc_code_working_area = false; + } + + uint32_t code_size = 0; + + /* Load code section */ + for (unsigned int i = 0; i < run->image.image.num_sections; i++) { + struct imagesection *section = &run->image.image.sections[i]; + + if (section->size == 0) + continue; + + if (section->flags & ESP_IMAGE_ELF_PHF_EXEC) { + LOG_DEBUG("addr " TARGET_ADDR_FMT ", sz %d, flags %" PRIx64, + section->base_address, section->size, section->flags); + + if (alloc_code_working_area) { + retval = target_alloc_working_area(target, section->size, &run->stub.code); + if (retval != ERROR_OK) { + LOG_ERROR("no working area available, can't alloc space for stub code!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _on_error; + } + } + + if (section->base_address == 0) { + section->base_address = run->stub.code->address; + /* sanity check, stub is compiled to be run from working area */ + } else if (run->stub.code->address != section->base_address) { + LOG_ERROR("working area " TARGET_ADDR_FMT " and stub code section " TARGET_ADDR_FMT + " address mismatch!", + section->base_address, + run->stub.code->address); + retval = ERROR_FAIL; + goto _on_error; + } + + retval = load_section_from_image(target, run, i, run->image.reverse); + if (retval != ERROR_OK) + goto _on_error; + + code_size += ALIGN_UP(section->size, 4); + break; /* Stub has one executable text section */ + } + } + + /* If exists, load trampoline to the code area */ + if (tramp) { + if (run->stub.tramp_addr == 0) { + if (alloc_code_working_area) { + /* alloc trampoline in code working area */ + if (target_alloc_working_area(target, tramp_sz, &run->stub.tramp) != ERROR_OK) { + LOG_ERROR("no working area available, can't alloc space for stub jumper!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _on_error; + } + run->stub.tramp_addr = run->stub.tramp->address; + } + } + + size_t al_tramp_size = ALIGN_UP(tramp_sz, 4); + + if (run->image.reverse) { + target_addr_t reversed_tramp_addr = run->image.dram_org - code_size; + uint8_t reversed_tramp[al_tramp_size]; + + /* Send original size to allow padding */ + reverse_binary(tramp, reversed_tramp, tramp_sz); + run->stub.tramp_addr = reversed_tramp_addr - al_tramp_size; + LOG_DEBUG("Write reversed tramp to addr " TARGET_ADDR_FMT ", sz %zu", run->stub.tramp_addr, al_tramp_size); + retval = target_write_buffer(target, run->stub.tramp_addr, al_tramp_size, reversed_tramp); + } else { + LOG_DEBUG("Write tramp to addr " TARGET_ADDR_FMT ", sz %zu", run->stub.tramp_addr, tramp_sz); + retval = target_write_buffer(target, run->stub.tramp_addr, tramp_sz, tramp); + } + + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write stub jumper!"); + goto _on_error; + } + + run->stub.tramp_mapped_addr = run->image.iram_org + code_size; + code_size += al_tramp_size; + LOG_DEBUG("Tramp mapped to addr " TARGET_ADDR_FMT, run->stub.tramp_mapped_addr); + } + + /* allocate dummy space until the data address */ + if (alloc_code_working_area) { + /* we dont need to restore padding area. */ + uint32_t backup_working_area_prev = target->backup_working_area; + target->backup_working_area = 0; + if (target_alloc_working_area(target, run->image.iram_len - code_size, &run->stub.padding) != ERROR_OK) { + LOG_ERROR("no working area available, can't alloc space for stub code!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _on_error; + } + target->backup_working_area = backup_working_area_prev; + } + + /* Load the data section */ + for (unsigned int i = 0; i < run->image.image.num_sections; i++) { + struct imagesection *section = &run->image.image.sections[i]; + + if (section->size == 0) + continue; + + if (!(section->flags & ESP_IMAGE_ELF_PHF_EXEC)) { + LOG_DEBUG("addr " TARGET_ADDR_FMT ", sz %d, flags %" PRIx64, section->base_address, section->size, + section->flags); + /* target_alloc_working_area() aligns the whole working area size to 4-byte boundary. + We alloc one area for both DATA and BSS, so align each of them ourselves. */ + uint32_t data_sec_sz = ALIGN_UP(section->size, 4); + LOG_DEBUG("DATA sec size %" PRIu32 " -> %" PRIu32, section->size, data_sec_sz); + uint32_t bss_sec_sz = ALIGN_UP(run->image.bss_size, 4); + LOG_DEBUG("BSS sec size %" PRIu32 " -> %" PRIu32, run->image.bss_size, bss_sec_sz); + if (target_alloc_working_area(target, data_sec_sz + bss_sec_sz, &run->stub.data) != ERROR_OK) { + LOG_ERROR("no working area available, can't alloc space for stub data!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _on_error; + } + if (section->base_address == 0) { + section->base_address = run->stub.data->address; + /* sanity check, stub is compiled to be run from working area */ + } else if (run->stub.data->address != section->base_address) { + LOG_ERROR("working area " TARGET_ADDR_FMT + " and stub data section " TARGET_ADDR_FMT + " address mismatch!", + section->base_address, + run->stub.data->address); + retval = ERROR_FAIL; + goto _on_error; + } + + retval = load_section_from_image(target, run, i, false); + if (retval != ERROR_OK) + goto _on_error; + } + } + + /* stack */ + if (run->stub.stack_addr == 0 && run->stack_size > 0) { + /* allocate stack in data working area */ + if (target_alloc_working_area(target, run->stack_size, &run->stub.stack) != ERROR_OK) { + LOG_ERROR("no working area available, can't alloc stub stack!"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto _on_error; + } + run->stub.stack_addr = run->stub.stack->address + run->stack_size; + } + + if (duration_measure(&algo_time) != 0) { + LOG_ERROR("Failed to stop algo run measurement!"); + retval = ERROR_FAIL; + goto _on_error; + } + LOG_DEBUG("Stub loaded in %g ms", duration_elapsed(&algo_time) * 1000); + return ERROR_OK; + +_on_error: + esp_algorithm_unload_func_image(target, run); + return retval; +} + +int esp_algorithm_unload_func_image(struct target *target, struct esp_algorithm_run_data *run) +{ + if (!run) + return ERROR_FAIL; + + target_free_all_working_areas(target); + + run->stub.tramp = NULL; + run->stub.stack = NULL; + run->stub.code = NULL; + run->stub.data = NULL; + run->stub.padding = NULL; + + return ERROR_OK; +} + +int esp_algorithm_exec_func_image_va(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap) +{ + if (!run || !run->image.image.start_address_set || run->image.image.start_address == 0) + return ERROR_FAIL; + + return esp_algorithm_run_image(target, run, num_args, ap); +} + +int esp_algorithm_load_onboard_func(struct target *target, target_addr_t func_addr, struct esp_algorithm_run_data *run) +{ + int res; + const uint8_t *tramp = NULL; + size_t tramp_sz = 0; + struct duration algo_time; + + if (!run || !run->hw) + return ERROR_FAIL; + + if (duration_start(&algo_time) != 0) { + LOG_ERROR("Failed to start algo time measurement!"); + return ERROR_FAIL; + } + + if (run->hw->stub_tramp_get) { + tramp = run->hw->stub_tramp_get(target, &tramp_sz); + if (!tramp) + return ERROR_FAIL; + } + + if (tramp_sz > run->on_board.code_buf_size) { + LOG_ERROR("Stub tramp size %zu bytes exceeds target buf size %d bytes!", + tramp_sz, run->on_board.code_buf_size); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (run->stack_size > run->on_board.min_stack_size) { + LOG_ERROR("Algorithm stack size not fit into the allocated target stack!"); + return ERROR_FAIL; + } + + run->stub.stack_addr = run->on_board.min_stack_addr + run->stack_size; + run->stub.tramp_addr = run->on_board.code_buf_addr; + run->stub.tramp_mapped_addr = run->stub.tramp_addr; + run->stub.entry = func_addr; + + if (tramp) { + res = target_write_buffer(target, run->stub.tramp_addr, tramp_sz, tramp); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write stub jumper!"); + esp_algorithm_unload_onboard_func(target, run); + return res; + } + } + + if (duration_measure(&algo_time) != 0) { + LOG_ERROR("Failed to stop algo run measurement!"); + return ERROR_FAIL; + } + LOG_DEBUG("Stub loaded in %g ms", duration_elapsed(&algo_time) * 1000); + + return ERROR_OK; +} + +int esp_algorithm_unload_onboard_func(struct target *target, struct esp_algorithm_run_data *run) +{ + return ERROR_OK; +} + +int esp_algorithm_exec_onboard_func_va(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap) +{ + return esp_algorithm_run_debug_stub(target, run, num_args, ap); +} diff --git a/src/target/espressif/esp_algorithm.h b/src/target/espressif/esp_algorithm.h new file mode 100644 index 0000000..11d2757 --- /dev/null +++ b/src/target/espressif/esp_algorithm.h @@ -0,0 +1,420 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Espressif chips common algorithm API for OpenOCD * + * Copyright (C) 2022 Espressif Systems Ltd. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESP_ALGORITHM_H +#define OPENOCD_TARGET_ESP_ALGORITHM_H + +#include "helper/log.h" +#include "helper/binarybuffer.h" +#include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/image.h> + +/** + * API defined below allows executing pieces of code on target without breaking the execution of the running program. + * This functionality can be useful for various debugging and maintenance procedures. + * @note ESP flashing code to load flasher stub on target and write/read/erase flash. + * Also ESP GCOV command uses some of these functions to run onboard routines to dump coverage info. + * Stub entry function can take up to 5 arguments and should be of the following form: + * + * int stub_entry([uint32_t a1 [, uint32_t a2 [, uint32_t a3 [, uint32_t a4 [, uint32_t a5]]]]]); + * + * The general scheme of stub code execution is shown below. + * + * ------- ----------- (initial frame) ---- + * | | -------(registers, stub entry, stub args)------> |trampoline | ---(stub args)---> | | + * | | | | | | + * |OpenOCD| <----------(stub-specific communications)---------------------------------------> |stub| + * | | | | | | + * | | <---------(target halted event, ret code)------- |tramp break| <---(ret code)---- | | + * ------- ----------- ---- + * + * Procedure of executing stub on target includes: + * 1) User prepares struct esp_algorithm_run_data and calls one of algorithm_run_xxx() functions. + * 2) Routine allocates all necessary stub code and data sections. + * 3) If a user specifies an initializer func esp_algorithm_usr_func_init_t it is called just before the stub starts. + * 4) If user specifies stub communication func esp_algorithm_usr_func_t (@see esp_flash_write/read in ESP flash driver) + * it is called just after the stub starts. When communication with stub is finished this function must return. + * 5) OpenOCD waits for the stub to finish (hit exit breakpoint). + * 6) If the user specified arguments cleanup func esp_algorithm_usr_func_done_t, + * it is called just after the stub finishes. + * + * There are two options to run code on target under OpenOCD control: + * - Run externally compiled stub code. + * - Run onboard pre-compiled code. @note For ESP chips debug stubs must be enabled in target code @see ESP IDF docs. + * The main difference between the execution of external stub code and target built-in functions is that + * in the latter case working areas can not be used to allocate target memory for code and data because they can overlap + * with code and data involved in onboard function execution. For example, if memory allocated in the working area + * for the stub stack will overlap with some on-board data used by the stub the stack will get overwritten. + * The same stands for allocations in target code space. + * + * External Code Execution + * ----------------------- + * To run external code on the target user should use esp_algorithm_run_func_image(). + * In this case all necessary memory (code/data) is allocated in working areas that have fixed configuration + * defined in target TCL file. Stub code is actually a standalone program, so all its segments must have known + * addresses due to position-dependent code nature. So stub must be linked in such a way that its code segment + * starts at the beginning of the working area for code space defined in TCL. The same restriction must be applied + * to stub's data segment and base addresses of working area for data space. @see ESP stub flasher LD scripts. + * Also in order to simplify memory allocation BSS section must follow the DATA section in the stub image. + * The size of the BSS section must be specified in the bss_size field of struct algorithm_image. + * Sample stub memory map is shown below. + * ___________________________________________ + * | data space working area start | + * | | + * | <stub .data segment> | + * |___________________________________________| + * | stub .bss start | + * | | + * | <stub .bss segment of size 'bss_size'> | + * |___________________________________________| + * | stub stack base | + * | | + * | <stub stack> | + * |___________________________________________| + * | | + * | <stub mem arg1> | + * |___________________________________________| + * | | + * | <stub mem arg2> | + * |___________________________________________| + * ___________________________________________ + * | code space working area start | + * | | + * | <stub .text segment> | + * |___________________________________________| + * | | + * | <stub trampoline with exit breakpoint> | + * |___________________________________________| + * + * For example on how to execute external code with memory arguments @see esp_algo_flash_blank_check in + * ESP flash driver. + * + * On-Board Code Execution + * ----------------------- + * To run on-board code on the target user should use esp_algorithm_run_onboard_func(). + * On-board code execution process does not need to allocate target memory for stub code and data, + * Because the stub is pre-compiled to the code running on the target. + * But it still needs memory for stub trampoline, stack, and memory arguments. + * Working areas can not be used due to possible memory layout conflicts with on-board stub code and data. + * Debug stubs functionality provided by ESP IDF allows OpenOCD to overcome the above problem. + * It provides a special descriptor which provides info necessary to safely allocate memory on target. + * @see struct esp_dbg_stubs_desc. + * That info is also used to locate memory for stub trampoline code. + * User can execute target function at any address, but @see ESP IDF debug stubs also provide a way to pass to the host + * an entry address of pre-defined registered stub functions. + * For example of an on-board code execution @see esp32_cmd_gcov() in ESP32 apptrace module. +*/ + +/** + * Algorithm image data. + * Helper struct to work with algorithms consisting of code and data segments. + */ +struct esp_algorithm_image { + /** Image. */ + struct image image; + /** BSS section size. */ + uint32_t bss_size; + /** IRAM start address in the linker script */ + uint32_t iram_org; + /** Total reserved IRAM size */ + uint32_t iram_len; + /** DRAM start address in the linker script */ + uint32_t dram_org; + /** Total reserved DRAM size */ + uint32_t dram_len; + /** IRAM DRAM address range reversed or not */ + bool reverse; +}; + +#define ESP_IMAGE_ELF_PHF_EXEC 0x1 + +/** + * Algorithm stub data. + */ +struct esp_algorithm_stub { + /** Entry addr. */ + target_addr_t entry; + /** Working area for code segment. */ + struct working_area *code; + /** Working area for data segment. */ + struct working_area *data; + /** Working area for trampoline. */ + struct working_area *tramp; + /** Working area for padding between code and data area. */ + struct working_area *padding; + /** Address of the target buffer for stub trampoline. If zero tramp->address will be used. */ + target_addr_t tramp_addr; + /** Tramp code area will be filled from dbus. + * We need to map it to the ibus to be able to initialize PC register to start algorithm execution from. + */ + target_addr_t tramp_mapped_addr; + /** Working area for stack. */ + struct working_area *stack; + /** Address of the target buffer for stack. If zero tramp->address will be used. */ + target_addr_t stack_addr; + /** Address of the log buffer */ + target_addr_t log_buff_addr; + /** Size of the log buffer */ + uint32_t log_buff_size; + /** Algorithm's arch-specific info. */ + void *ainfo; +}; + +/** + * Algorithm stub in-memory arguments. + */ +struct esp_algorithm_mem_args { + /** Memory params. */ + struct mem_param *params; + /** Number of memory params. */ + uint32_t count; +}; + +/** + * Algorithm stub register arguments. + */ +struct esp_algorithm_reg_args { + /** Algorithm register params. User args start from user_first_reg_param */ + struct reg_param *params; + /** Number of register params. */ + uint32_t count; + /** The first several reg_params can be used by stub itself (e.g. for trampoline). + * This is the index of the first reg_param available for user to pass args to algorithm stub. */ + uint32_t first_user_param; +}; + +struct esp_algorithm_run_data; + +/** + * @brief Algorithm run function. + * + * @param target Pointer to target. + * @param run Pointer to algo run data. + * @param arg Function specific argument. + * + * @return ERROR_OK on success, otherwise ERROR_XXX. + */ +typedef int (*esp_algorithm_func_t)(struct target *target, struct esp_algorithm_run_data *run, void *arg); + +/** + * @brief Host part of algorithm. + * This function will be called while stub is running on target. + * It can be used for communication with stub. + * + * @param target Pointer to target. + * @param usr_arg Function specific argument. + * + * @return ERROR_OK on success, otherwise ERROR_XXX. + */ +typedef int (*esp_algorithm_usr_func_t)(struct target *target, void *usr_arg); + +/** + * @brief Algorithm's arguments setup function. + * This function will be called just before stub start. + * It must return when all operations with running stub are completed. + * It can be used to prepare stub memory parameters. + * + * @param target Pointer to target. + * @param run Pointer to algo run data. + * @param usr_arg Function specific argument. The same as for esp_algorithm_usr_func_t. + * + * @return ERROR_OK on success, otherwise ERROR_XXX. + */ +typedef int (*esp_algorithm_usr_func_init_t)(struct target *target, + struct esp_algorithm_run_data *run, + void *usr_arg); + +/** + * @brief Algorithm's arguments cleanup function. + * This function will be called just after stub exit. + * It can be used to cleanup stub memory parameters. + * + * @param target Pointer to target. + * @param run Pointer to algo run data. + * @param usr_arg Function specific argument. The same as for esp_algorithm_usr_func_t. + * + * @return ERROR_OK on success, otherwise ERROR_XXX. + */ +typedef void (*esp_algorithm_usr_func_done_t)(struct target *target, + struct esp_algorithm_run_data *run, + void *usr_arg); + +struct esp_algorithm_hw { + int (*algo_init)(struct target *target, struct esp_algorithm_run_data *run, uint32_t num_args, va_list ap); + int (*algo_cleanup)(struct target *target, struct esp_algorithm_run_data *run); + const uint8_t *(*stub_tramp_get)(struct target *target, size_t *size); +}; + +/** + * Algorithm run data. + */ +struct esp_algorithm_run_data { + /** Algorithm completion timeout in ms. If 0, default value will be used */ + uint32_t timeout_ms; + /** Algorithm stack size. */ + uint32_t stack_size; + /** Algorithm register arguments. */ + struct esp_algorithm_reg_args reg_args; + /** Algorithm memory arguments. */ + struct esp_algorithm_mem_args mem_args; + /** Algorithm arch-specific info. For Xtensa this should point to struct xtensa_algorithm. */ + void *arch_info; + /** Algorithm return code. */ + int32_t ret_code; + /** Stub. */ + struct esp_algorithm_stub stub; + union { + struct { + /** Size of the pre-alocated on-board buffer for stub's code. */ + uint32_t code_buf_size; + /** Address of pre-compiled target buffer for stub trampoline. */ + target_addr_t code_buf_addr; + /** Size of the pre-alocated on-board buffer for stub's stack. */ + uint32_t min_stack_size; + /** Pre-compiled target buffer's addr for stack. */ + target_addr_t min_stack_addr; + } on_board; + struct esp_algorithm_image image; + }; + /** Host side algorithm function argument. */ + void *usr_func_arg; + /** Host side algorithm function. */ + esp_algorithm_usr_func_t usr_func; + /** Host side algorithm function setup routine. */ + esp_algorithm_usr_func_init_t usr_func_init; + /** Host side algorithm function cleanup routine. */ + esp_algorithm_usr_func_done_t usr_func_done; + /** Algorithm run function: see algorithm_run_xxx for example. */ + esp_algorithm_func_t algo_func; + /** HW specific API */ + const struct esp_algorithm_hw *hw; +}; + +int esp_algorithm_load_func_image(struct target *target, struct esp_algorithm_run_data *run); +int esp_algorithm_unload_func_image(struct target *target, struct esp_algorithm_run_data *run); + +int esp_algorithm_exec_func_image_va(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap); + +/** + * @brief Loads and runs stub from specified image. + * This function should be used to run external stub code on target. + * + * @param target Pointer to target. + * @param run Pointer to algo run data. + * @param num_args Number of stub arguments that follow. + * + * @return ERROR_OK on success, otherwise ERROR_XXX. Stub return code is in run->ret_code. + */ +static inline int esp_algorithm_run_func_image_va(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap) +{ + int ret = esp_algorithm_load_func_image(target, run); + if (ret != ERROR_OK) + return ret; + ret = esp_algorithm_exec_func_image_va(target, run, num_args, ap); + int rc = esp_algorithm_unload_func_image(target, run); + return ret != ERROR_OK ? ret : rc; +} + +static inline int esp_algorithm_run_func_image(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + ...) +{ + va_list ap; + va_start(ap, num_args); + int retval = esp_algorithm_run_func_image_va(target, run, num_args, ap); + va_end(ap); + return retval; +} + +int esp_algorithm_load_onboard_func(struct target *target, + target_addr_t func_addr, + struct esp_algorithm_run_data *run); +int esp_algorithm_unload_onboard_func(struct target *target, struct esp_algorithm_run_data *run); +int esp_algorithm_exec_onboard_func_va(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t num_args, + va_list ap); + +/** + * @brief Runs pre-compiled on-board function. + * This function should be used to run on-board stub code. + * + * @param target Pointer to target. + * @param run Pointer to algo run data. + * @param func_entry Address of the function to run. + * @param num_args Number of function arguments that follow. + * + * @return ERROR_OK on success, otherwise ERROR_XXX. Stub return code is in run->ret_code. + */ +static inline int esp_algorithm_run_onboard_func_va(struct target *target, + struct esp_algorithm_run_data *run, + target_addr_t func_addr, + uint32_t num_args, + va_list ap) +{ + int ret = esp_algorithm_load_onboard_func(target, func_addr, run); + if (ret != ERROR_OK) + return ret; + ret = esp_algorithm_exec_onboard_func_va(target, run, num_args, ap); + if (ret != ERROR_OK) + return ret; + return esp_algorithm_unload_onboard_func(target, run); +} + +static inline int esp_algorithm_run_onboard_func(struct target *target, + struct esp_algorithm_run_data *run, + target_addr_t func_addr, + uint32_t num_args, + ...) +{ + va_list ap; + va_start(ap, num_args); + int retval = esp_algorithm_run_onboard_func_va(target, run, func_addr, num_args, ap); + va_end(ap); + return retval; +} + +/** + * @brief Set the value of an argument passed via registers to the stub main function. + */ +static inline void esp_algorithm_user_arg_set_uint(struct esp_algorithm_run_data *run, + int arg_num, + uint64_t val) +{ + struct reg_param *param = &run->reg_args.params[run->reg_args.first_user_param + arg_num]; + + assert(param->size <= 64); + + if (param->size <= 32) + buf_set_u32(param->value, 0, param->size, val); + else + buf_set_u64(param->value, 0, param->size, val); +} + +/** + * @brief Get the value of an argument passed via registers from the stub main function. + */ +static inline uint64_t esp_algorithm_user_arg_get_uint(struct esp_algorithm_run_data *run, int arg_num) +{ + struct reg_param *param = &run->reg_args.params[run->reg_args.first_user_param + arg_num]; + + assert(param->size <= 64); + + if (param->size <= 32) + return buf_get_u32(param->value, 0, param->size); + return buf_get_u64(param->value, 0, param->size); +} + +#endif /* OPENOCD_TARGET_ESP_ALGORITHM_H */ diff --git a/src/target/espressif/esp_xtensa.c b/src/target/espressif/esp_xtensa.c index 0bd2cdd..11895d2 100644 --- a/src/target/espressif/esp_xtensa.c +++ b/src/target/espressif/esp_xtensa.c @@ -12,10 +12,12 @@ #include <stdbool.h> #include <stdint.h> #include <target/smp.h> -#include "esp_xtensa_apptrace.h" #include <target/register.h> +#include "esp.h" #include "esp_xtensa.h" +#include "esp_xtensa_apptrace.h" #include "esp_semihosting.h" +#include "esp_xtensa_algorithm.h" #define ESP_XTENSA_DBGSTUBS_UPDATE_DATA_ENTRY(_e_) \ do { \ @@ -68,6 +70,10 @@ int esp_xtensa_init_arch_info(struct target *target, int ret = xtensa_init_arch_info(target, &esp_xtensa->xtensa, dm_cfg); if (ret != ERROR_OK) return ret; + ret = esp_common_init(&esp_xtensa->esp, &xtensa_algo_hw); + if (ret != ERROR_OK) + return ret; + esp_xtensa->semihost.ops = (struct esp_semihost_ops *)semihost_ops; esp_xtensa->apptrace.hw = &esp_xtensa_apptrace_hw; return ERROR_OK; diff --git a/src/target/espressif/esp_xtensa_algorithm.c b/src/target/espressif/esp_xtensa_algorithm.c new file mode 100644 index 0000000..68005cb --- /dev/null +++ b/src/target/espressif/esp_xtensa_algorithm.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Module to run arbitrary code on Xtensa using OpenOCD * + * Copyright (C) 2019 Espressif Systems Ltd. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <target/xtensa/xtensa.h> +#include "esp_xtensa_algorithm.h" + +static int esp_xtensa_algo_init(struct target *target, struct esp_algorithm_run_data *run, + uint32_t num_args, va_list ap); +static int esp_xtensa_algo_cleanup(struct target *target, struct esp_algorithm_run_data *run); +static const uint8_t *esp_xtensa_stub_tramp_get(struct target *target, size_t *size); + +const struct esp_algorithm_hw xtensa_algo_hw = { + .algo_init = esp_xtensa_algo_init, + .algo_cleanup = esp_xtensa_algo_cleanup, + .stub_tramp_get = esp_xtensa_stub_tramp_get, +}; + +/* Generated from contrib/loaders/trampoline/espressif/xtensa/esp_xtensa_stub_tramp_win.S */ +static const uint8_t esp_xtensa_stub_tramp_win[] = { +#include "../../../contrib/loaders/trampoline/espressif/xtensa/esp_xtensa_stub_tramp_win.inc" +}; + +static const uint8_t *esp_xtensa_stub_tramp_get(struct target *target, size_t *size) +{ + struct xtensa *xtensa = target_to_xtensa(target); + + if (!xtensa->core_config->windowed) { + LOG_ERROR("Running stubs is not supported for cores without windowed registers option!"); + return NULL; + } + *size = sizeof(esp_xtensa_stub_tramp_win); + return esp_xtensa_stub_tramp_win; +} + +static int esp_xtensa_algo_regs_init_start(struct target *target, struct esp_algorithm_run_data *run) +{ + uint32_t stack_addr = run->stub.stack_addr; + + LOG_TARGET_DEBUG(target, "Check stack addr 0x%x", stack_addr); + if (stack_addr & 0xFUL) { + stack_addr &= ~0xFUL; + LOG_TARGET_DEBUG(target, "Adjust stack addr to 0x%x", stack_addr); + } + stack_addr -= 16; + struct reg_param *params = run->reg_args.params; + init_reg_param(¶ms[0], "a0", 32, PARAM_OUT); /*TODO: move to tramp */ + init_reg_param(¶ms[1], "a1", 32, PARAM_OUT); + init_reg_param(¶ms[2], "a8", 32, PARAM_OUT); + init_reg_param(¶ms[3], "windowbase", 32, PARAM_OUT); /*TODO: move to tramp */ + init_reg_param(¶ms[4], "windowstart", 32, PARAM_OUT); /*TODO: move to tramp */ + init_reg_param(¶ms[5], "ps", 32, PARAM_OUT); + buf_set_u32(params[0].value, 0, 32, 0); /* a0 TODO: move to tramp */ + buf_set_u32(params[1].value, 0, 32, stack_addr); /* a1 */ + buf_set_u32(params[2].value, 0, 32, run->stub.entry); /* a8 */ + buf_set_u32(params[3].value, 0, 32, 0x0); /* initial window base TODO: move to tramp */ + buf_set_u32(params[4].value, 0, 32, 0x1); /* initial window start TODO: move to tramp */ + buf_set_u32(params[5].value, 0, 32, 0x60025); /* enable WOE, UM and debug interrupts level (6) */ + return ERROR_OK; +} + +static int esp_xtensa_algo_init(struct target *target, struct esp_algorithm_run_data *run, + uint32_t num_args, va_list ap) +{ + enum xtensa_mode core_mode = XT_MODE_ANY; + static const char *const arg_regs[] = { "a2", "a3", "a4", "a5", "a6" }; + + if (!run) + return ERROR_FAIL; + + if (num_args > ARRAY_SIZE(arg_regs)) { + LOG_ERROR("Too many algo user args %u! Max %zu args are supported.", num_args, ARRAY_SIZE(arg_regs)); + return ERROR_FAIL; + } + + struct xtensa_algorithm *ainfo = calloc(1, sizeof(struct xtensa_algorithm)); + if (!ainfo) { + LOG_ERROR("Unable to allocate memory"); + return ERROR_FAIL; + } + + if (run->arch_info) { + struct xtensa_algorithm *xtensa_algo = run->arch_info; + core_mode = xtensa_algo->core_mode; + } + + run->reg_args.first_user_param = ESP_XTENSA_STUB_ARGS_FUNC_START; + run->reg_args.count = run->reg_args.first_user_param + num_args; + if (num_args == 0) + run->reg_args.count++; /* a2 reg is used as the 1st arg and return code */ + LOG_DEBUG("reg params count %d (%d/%d).", + run->reg_args.count, + run->reg_args.first_user_param, + num_args); + run->reg_args.params = calloc(run->reg_args.count, sizeof(struct reg_param)); + if (!run->reg_args.params) { + free(ainfo); + LOG_ERROR("Unable to allocate memory"); + return ERROR_FAIL; + } + + esp_xtensa_algo_regs_init_start(target, run); + + init_reg_param(&run->reg_args.params[run->reg_args.first_user_param + 0], "a2", 32, PARAM_IN_OUT); + + if (num_args > 0) { + uint32_t arg = va_arg(ap, uint32_t); + esp_algorithm_user_arg_set_uint(run, 0, arg); + LOG_DEBUG("Set arg[0] = %d (%s)", arg, run->reg_args.params[run->reg_args.first_user_param + 0].reg_name); + } else { + esp_algorithm_user_arg_set_uint(run, 0, 0); + } + + for (unsigned int i = 1; i < num_args; i++) { + uint32_t arg = va_arg(ap, uint32_t); + init_reg_param(&run->reg_args.params[run->reg_args.first_user_param + i], (char *)arg_regs[i], 32, PARAM_OUT); + esp_algorithm_user_arg_set_uint(run, i, arg); + LOG_DEBUG("Set arg[%d] = %d (%s)", i, arg, run->reg_args.params[run->reg_args.first_user_param + i].reg_name); + } + + ainfo->core_mode = core_mode; + run->stub.ainfo = ainfo; + return ERROR_OK; +} + +static int esp_xtensa_algo_cleanup(struct target *target, struct esp_algorithm_run_data *run) +{ + free(run->stub.ainfo); + for (uint32_t i = 0; i < run->reg_args.count; i++) + destroy_reg_param(&run->reg_args.params[i]); + free(run->reg_args.params); + return ERROR_OK; +} diff --git a/src/target/espressif/esp_xtensa_algorithm.h b/src/target/espressif/esp_xtensa_algorithm.h new file mode 100644 index 0000000..36fa1a3 --- /dev/null +++ b/src/target/espressif/esp_xtensa_algorithm.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Module to run arbitrary code on Xtensa using OpenOCD * + * Copyright (C) 2019 Espressif Systems Ltd. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESP_XTENSA_ALGO_H +#define OPENOCD_TARGET_ESP_XTENSA_ALGO_H + +#include <target/xtensa/xtensa.h> +#include <target/espressif/esp_algorithm.h> + +/** Index of the first user-defined algo arg. @see algorithm_stub */ +#define ESP_XTENSA_STUB_ARGS_FUNC_START 6 + +extern const struct esp_algorithm_hw xtensa_algo_hw; + +#endif /* OPENOCD_TARGET_XTENSA_ALGO_H */ diff --git a/src/target/espressif/esp_xtensa_smp.c b/src/target/espressif/esp_xtensa_smp.c index 1d70be9..f883b1c 100644 --- a/src/target/espressif/esp_xtensa_smp.c +++ b/src/target/espressif/esp_xtensa_smp.c @@ -16,6 +16,7 @@ #include <target/semihosting_common.h> #include "esp_xtensa_smp.h" #include "esp_xtensa_semihosting.h" +#include "esp_algorithm.h" /* Multiprocessor stuff common: @@ -495,6 +496,83 @@ int esp_xtensa_smp_watchpoint_remove(struct target *target, struct watchpoint *w return ERROR_OK; } +int esp_xtensa_smp_run_func_image(struct target *target, struct esp_algorithm_run_data *run, uint32_t num_args, ...) +{ + struct target *run_target = target; + struct target_list *head; + va_list ap; + uint32_t smp_break = 0; + int res; + + if (target->smp) { + /* find first HALTED and examined core */ + foreach_smp_target(head, target->smp_targets) { + run_target = head->target; + if (target_was_examined(run_target) && run_target->state == TARGET_HALTED) + break; + } + if (!head) { + LOG_ERROR("Failed to find HALTED core!"); + return ERROR_FAIL; + } + + res = esp_xtensa_smp_smpbreak_disable(run_target, &smp_break); + if (res != ERROR_OK) + return res; + } + + va_start(ap, num_args); + int algo_res = esp_algorithm_run_func_image_va(run_target, run, num_args, ap); + va_end(ap); + + if (target->smp) { + res = esp_xtensa_smp_smpbreak_restore(run_target, smp_break); + if (res != ERROR_OK) + return res; + } + return algo_res; +} + +int esp_xtensa_smp_run_onboard_func(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t func_addr, + uint32_t num_args, + ...) +{ + struct target *run_target = target; + struct target_list *head; + va_list ap; + uint32_t smp_break = 0; + int res; + + if (target->smp) { + /* find first HALTED and examined core */ + foreach_smp_target(head, target->smp_targets) { + run_target = head->target; + if (target_was_examined(run_target) && run_target->state == TARGET_HALTED) + break; + } + if (!head) { + LOG_ERROR("Failed to find HALTED core!"); + return ERROR_FAIL; + } + res = esp_xtensa_smp_smpbreak_disable(run_target, &smp_break); + if (res != ERROR_OK) + return res; + } + + va_start(ap, num_args); + int algo_res = esp_algorithm_run_onboard_func_va(run_target, run, func_addr, num_args, ap); + va_end(ap); + + if (target->smp) { + res = esp_xtensa_smp_smpbreak_restore(run_target, smp_break); + if (res != ERROR_OK) + return res; + } + return algo_res; +} + int esp_xtensa_smp_init_arch_info(struct target *target, struct esp_xtensa_smp_common *esp_xtensa_smp, struct xtensa_debug_module_config *dm_cfg, @@ -746,7 +824,7 @@ COMMAND_HANDLER(esp_xtensa_smp_cmd_perfmon_dump) struct target *curr; foreach_smp_target(head, target->smp_targets) { curr = head->target; - LOG_INFO("CPU%d:", curr->coreid); + LOG_TARGET_INFO(curr, ":"); int ret = CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do, target_to_xtensa(curr)); if (ret != ERROR_OK) diff --git a/src/target/espressif/esp_xtensa_smp.h b/src/target/espressif/esp_xtensa_smp.h index 4e4f3b3..39afd8a 100644 --- a/src/target/espressif/esp_xtensa_smp.h +++ b/src/target/espressif/esp_xtensa_smp.h @@ -9,6 +9,7 @@ #define OPENOCD_TARGET_XTENSA_ESP_SMP_H #include "esp_xtensa.h" +#include "esp_algorithm.h" struct esp_xtensa_smp_chip_ops { int (*poll)(struct target *target); @@ -47,7 +48,12 @@ int esp_xtensa_smp_init_arch_info(struct target *target, struct xtensa_debug_module_config *dm_cfg, const struct esp_xtensa_smp_chip_ops *chip_ops, const struct esp_semihost_ops *semihost_ops); - +int esp_xtensa_smp_run_func_image(struct target *target, struct esp_algorithm_run_data *run, uint32_t num_args, ...); +int esp_xtensa_smp_run_onboard_func(struct target *target, + struct esp_algorithm_run_data *run, + uint32_t func_addr, + uint32_t num_args, + ...); extern const struct command_registration esp_xtensa_smp_command_handlers[]; extern const struct command_registration esp_xtensa_smp_xtensa_command_handlers[]; extern const struct command_registration esp_xtensa_smp_esp_command_handlers[]; diff --git a/src/target/mips32.h b/src/target/mips32.h index d072eb9..fc89624 100644 --- a/src/target/mips32.h +++ b/src/target/mips32.h @@ -380,7 +380,8 @@ struct mips32_algorithm { #define MIPS32_OP_XORI 0x0Eu #define MIPS32_OP_XOR 0x26u #define MIPS32_OP_SLTU 0x2Bu -#define MIPS32_OP_SRL 0x03u +#define MIPS32_OP_SRL 0x02u +#define MIPS32_OP_SRA 0x03u #define MIPS32_OP_SYNCI 0x1Fu #define MIPS32_OP_SLL 0x00u #define MIPS32_OP_SLTI 0x0Au @@ -439,7 +440,8 @@ struct mips32_algorithm { #define MIPS32_ISA_SLL(dst, src, sa) MIPS32_R_INST(MIPS32_OP_SPECIAL, 0, src, dst, sa, MIPS32_OP_SLL) #define MIPS32_ISA_SLTI(tar, src, val) MIPS32_I_INST(MIPS32_OP_SLTI, src, tar, val) #define MIPS32_ISA_SLTU(dst, src, tar) MIPS32_R_INST(MIPS32_OP_SPECIAL, src, tar, dst, 0, MIPS32_OP_SLTU) -#define MIPS32_ISA_SRL(reg, src, off) MIPS32_R_INST(0, 0, src, reg, off, MIPS32_OP_SRL) +#define MIPS32_ISA_SRA(reg, src, off) MIPS32_R_INST(MIPS32_OP_SPECIAL, 0, src, reg, off, MIPS32_OP_SRA) +#define MIPS32_ISA_SRL(reg, src, off) MIPS32_R_INST(MIPS32_OP_SPECIAL, 0, src, reg, off, MIPS32_OP_SRL) #define MIPS32_ISA_SYNC 0xFu #define MIPS32_ISA_SYNCI(off, base) MIPS32_I_INST(MIPS32_OP_REGIMM, base, MIPS32_OP_SYNCI, off) diff --git a/src/target/mips32_pracc.c b/src/target/mips32_pracc.c index 9f0d87c..db50ef9 100644 --- a/src/target/mips32_pracc.c +++ b/src/target/mips32_pracc.c @@ -842,12 +842,12 @@ int mips32_pracc_write_regs(struct mips32_common *mips32) }; uint32_t cp0_write_data[] = { + /* status */ + c0rs[0], /* lo */ gprs[32], /* hi */ gprs[33], - /* status */ - c0rs[0], /* badvaddr */ c0rs[1], /* cause */ @@ -856,6 +856,9 @@ int mips32_pracc_write_regs(struct mips32_common *mips32) c0rs[3], }; + /* Write CP0 Status Register first, changes on EXL or ERL bits + * may lead to different behaviour on writing to other CP0 registers. + */ for (size_t i = 0; i < ARRAY_SIZE(cp0_write_code); i++) { /* load CP0 value in $1 */ pracc_add_li32(&ctx, 1, cp0_write_data[i], 0); diff --git a/src/target/mips_m4k.c b/src/target/mips_m4k.c index 0a06bb1..ad98089 100644 --- a/src/target/mips_m4k.c +++ b/src/target/mips_m4k.c @@ -142,7 +142,7 @@ static int mips_m4k_halt_smp(struct target *target) ret = mips_m4k_halt(curr); if (ret != ERROR_OK) { - LOG_ERROR("halt failed target->coreid: %" PRId32, curr->coreid); + LOG_TARGET_ERROR(curr, "halt failed."); retval = ret; } } @@ -412,8 +412,8 @@ static int mips_m4k_restore_smp(struct target *target, uint32_t address, int han handle_breakpoints, 0); if (ret != ERROR_OK) { - LOG_ERROR("target->coreid :%" PRId32 " failed to resume at address :0x%" PRIx32, - curr->coreid, address); + LOG_TARGET_ERROR(curr, "failed to resume at address: 0x%" PRIx32, + address); retval = ret; } } diff --git a/src/target/smp.c b/src/target/smp.c index effc63f..50b19d0 100644 --- a/src/target/smp.c +++ b/src/target/smp.c @@ -132,6 +132,9 @@ COMMAND_HANDLER(handle_smp_gdb_command) { struct target *target = get_current_target(CMD_CTX); int retval = ERROR_OK; + + LOG_WARNING(DEPRECATED_MSG); + if (!list_empty(target->smp_targets)) { if (CMD_ARGC == 1) { int coreid = 0; diff --git a/src/target/target.c b/src/target/target.c index cfd0641..216dcb2 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -299,23 +299,6 @@ const char *target_reset_mode_name(enum target_reset_mode reset_mode) return cp; } -/* determine the number of the new target */ -static int new_target_number(void) -{ - struct target *t; - int x; - - /* number is 0 based */ - x = -1; - t = all_targets; - while (t) { - if (x < t->target_number) - x = t->target_number; - t = t->next; - } - return x + 1; -} - static void append_to_list_all_targets(struct target *target) { struct target **t = &all_targets; @@ -451,7 +434,7 @@ void target_buffer_set_u16_array(struct target *target, uint8_t *buffer, uint32_ target_buffer_set_u16(target, &buffer[i * 2], srcbuf[i]); } -/* return a pointer to a configured target; id is name or number */ +/* return a pointer to a configured target; id is name or index in all_targets */ struct target *get_target(const char *id) { struct target *target; @@ -464,36 +447,17 @@ struct target *get_target(const char *id) return target; } - /* It's OK to remove this fallback sometime after August 2010 or so */ - - /* no match, try as number */ - unsigned num; - if (parse_uint(id, &num) != ERROR_OK) + /* try as index */ + unsigned int index, counter; + if (parse_uint(id, &index) != ERROR_OK) return NULL; - for (target = all_targets; target; target = target->next) { - if (target->target_number == (int)num) { - LOG_WARNING("use '%s' as target identifier, not '%u'", - target_name(target), num); - return target; - } - } - - return NULL; -} + for (target = all_targets, counter = index; + target && counter; + target = target->next, --counter) + ; -/* returns a pointer to the n-th configured target */ -struct target *get_target_by_num(int num) -{ - struct target *target = all_targets; - - while (target) { - if (target->target_number == num) - return target; - target = target->next; - } - - return NULL; + return target; } struct target *get_current_target(struct command_context *cmd_ctx) @@ -712,10 +676,14 @@ static int default_check_reset(struct target *target) * Keep in sync */ int target_examine_one(struct target *target) { + LOG_TARGET_DEBUG(target, "Examination started"); + target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START); int retval = target->type->examine(target); if (retval != ERROR_OK) { + LOG_TARGET_ERROR(target, "Examination failed"); + LOG_TARGET_DEBUG(target, "examine() returned error code %d", retval); target_reset_examined(target); target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_FAIL); return retval; @@ -725,6 +693,7 @@ int target_examine_one(struct target *target) target_set_examined(target); target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END); + LOG_TARGET_INFO(target, "Examination succeed"); return ERROR_OK; } @@ -2222,6 +2191,9 @@ uint32_t target_get_working_area_avail(struct target *target) static void target_destroy(struct target *target) { + breakpoint_remove_all(target); + watchpoint_remove_all(target); + if (target->type->deinit_target) target->type->deinit_target(target); @@ -2850,10 +2822,10 @@ COMMAND_HANDLER(handle_targets_command) } } - struct target *target = all_targets; + unsigned int index = 0; command_print(CMD, " TargetName Type Endian TapName State "); command_print(CMD, "-- ------------------ ---------- ------ ------------------ ------------"); - while (target) { + for (struct target *target = all_targets; target; target = target->next, ++index) { const char *state; char marker = ' '; @@ -2868,7 +2840,7 @@ COMMAND_HANDLER(handle_targets_command) /* keep columns lined up to match the headers above */ command_print(CMD, "%2d%c %-18s %-10s %-6s %-18s %s", - target->target_number, + index, marker, target_name(target), target_type_name(target), @@ -2876,7 +2848,6 @@ COMMAND_HANDLER(handle_targets_command) target->endianness)->name, target->tap->dotted_name, state); - target = target->next; } return retval; @@ -3973,7 +3944,7 @@ static int handle_bp_command_set(struct command_invocation *cmd, } else if (addr == 0) { if (!target->type->add_context_breakpoint) { - LOG_ERROR("Context breakpoint not available"); + LOG_TARGET_ERROR(target, "Context breakpoint not available"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } retval = context_breakpoint_add(target, asid, length, hw); @@ -3983,7 +3954,7 @@ static int handle_bp_command_set(struct command_invocation *cmd, } else { if (!target->type->add_hybrid_breakpoint) { - LOG_ERROR("Hybrid breakpoint not available"); + LOG_TARGET_ERROR(target, "Hybrid breakpoint not available"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } retval = hybrid_breakpoint_add(target, addr, asid, length, hw); @@ -4120,7 +4091,7 @@ COMMAND_HANDLER(handle_wp_command) type = WPT_ACCESS; break; default: - LOG_ERROR("invalid watchpoint mode ('%c')", CMD_ARGV[2][0]); + LOG_TARGET_ERROR(target, "invalid watchpoint mode ('%c')", CMD_ARGV[2][0]); return ERROR_COMMAND_SYNTAX_ERROR; } /* fall through */ @@ -4136,7 +4107,7 @@ COMMAND_HANDLER(handle_wp_command) int retval = watchpoint_add(target, addr, length, type, data_value, data_mask); if (retval != ERROR_OK) - LOG_ERROR("Failure setting watchpoints"); + LOG_TARGET_ERROR(target, "Failure setting watchpoints"); return retval; } @@ -4329,7 +4300,7 @@ COMMAND_HANDLER(handle_profile_command) if ((CMD_ARGC != 2) && (CMD_ARGC != 4)) return ERROR_COMMAND_SYNTAX_ERROR; - const uint32_t MAX_PROFILE_SAMPLE_NUM = 10000; + const uint32_t MAX_PROFILE_SAMPLE_NUM = 1000000; uint32_t offset; uint32_t num_of_samples; int retval = ERROR_OK; @@ -5062,8 +5033,7 @@ void target_handle_event(struct target *target, enum target_event e) for (teap = target->event_action; teap; teap = teap->next) { if (teap->event == e) { - LOG_DEBUG("target(%d): %s (%s) event: %d (%s) action: %s", - target->target_number, + LOG_DEBUG("target: %s (%s) event: %d (%s) action: %s", target_name(target), target_type_name(target), e, @@ -5482,13 +5452,13 @@ no_params: e = jim_getopt_wide(goi, &w); if (e != JIM_OK) return e; - /* make this exactly 1 or 0 */ - target->backup_working_area = (!!w); + /* make this boolean */ + target->backup_working_area = (w != 0); } else { if (goi->argc != 0) goto no_params; } - Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->backup_working_area)); + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->backup_working_area ? 1 : 0)); /* loop for more e*/ break; @@ -5848,8 +5818,7 @@ COMMAND_HANDLER(handle_target_event_list) struct target *target = get_current_target(CMD_CTX); struct target_event_action *teap = target->event_action; - command_print(CMD, "Event actions for target (%d) %s\n", - target->target_number, + command_print(CMD, "Event actions for target %s\n", target_name(target)); command_print(CMD, "%-25s | Body", "Event"); command_print(CMD, "------------------------- | " @@ -5883,7 +5852,17 @@ COMMAND_HANDLER(handle_target_debug_reason) struct target *target = get_current_target(CMD_CTX); - command_print(CMD, "%s", debug_reason_name(target)); + + const char *debug_reason = nvp_value2name(nvp_target_debug_reason, + target->debug_reason)->name; + + if (!debug_reason) { + command_print(CMD, "bug: invalid debug reason (%d)", + target->debug_reason); + return ERROR_FAIL; + } + + command_print(CMD, "%s", debug_reason); return ERROR_OK; } @@ -6188,9 +6167,6 @@ static int target_create(struct jim_getopt_info *goi) /* set empty smp cluster */ target->smp_targets = &empty_smp_targets; - /* set target number */ - target->target_number = new_target_number(); - /* allocate memory for each unique target type */ target->type = malloc(sizeof(struct target_type)); if (!target->type) { @@ -6207,7 +6183,7 @@ static int target_create(struct jim_getopt_info *goi) target->working_area = 0x0; target->working_area_size = 0x0; target->working_areas = NULL; - target->backup_working_area = 0; + target->backup_working_area = false; target->state = TARGET_UNKNOWN; target->debug_reason = DBG_REASON_UNDEFINED; @@ -7095,7 +7071,7 @@ static const struct command_registration target_exec_command_handlers[] = { .handler = handle_wp_command, .mode = COMMAND_EXEC, .help = "list (no params) or create watchpoints", - .usage = "[address length [('r'|'w'|'a') value [mask]]]", + .usage = "[address length [('r'|'w'|'a') [value [mask]]]]", }, { .name = "rwp", diff --git a/src/target/target.h b/src/target/target.h index b8f3b01..8b2e362 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -118,7 +118,6 @@ enum target_register_class { struct target { struct target_type *type; /* target type definition (name, access functions) */ char *cmd_name; /* tcl Name of target */ - int target_number; /* DO NOT USE! field to be removed in 2010 */ struct jtag_tap *tap; /* where on the jtag chain is this */ int32_t coreid; /* which device on the TAP? */ @@ -152,7 +151,7 @@ struct target { bool working_area_phys_spec; /* physical address specified? */ target_addr_t working_area_phys; /* physical address */ uint32_t working_area_size; /* size in bytes */ - uint32_t backup_working_area; /* whether the content of the working area has to be preserved */ + bool backup_working_area; /* whether the content of the working area has to be preserved */ struct working_area *working_areas;/* list of allocated working areas */ enum target_debug_reason debug_reason;/* reason why the target entered debug state */ enum target_endianness endianness; /* target endianness */ @@ -418,7 +417,6 @@ int target_call_timer_callbacks_now(void); */ int64_t target_timer_next_event(void); -struct target *get_target_by_num(int num); struct target *get_current_target(struct command_context *cmd_ctx); struct target *get_current_target_or_null(struct command_context *cmd_ctx); struct target *get_target(const char *id); diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c index c575b53..d2ca32c 100644 --- a/src/target/xtensa/xtensa.c +++ b/src/target/xtensa/xtensa.c @@ -16,6 +16,7 @@ #include <helper/time_support.h> #include <helper/align.h> #include <target/register.h> +#include <target/algorithm.h> #include "xtensa_chip.h" #include "xtensa.h" @@ -822,7 +823,7 @@ int xtensa_examine(struct target *target) struct xtensa *xtensa = target_to_xtensa(target); unsigned int cmd = PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | PWRCTL_COREWAKEUP(xtensa); - LOG_DEBUG("coreid = %d", target->coreid); + LOG_TARGET_DEBUG(target, ""); if (xtensa->core_config->core_type == XT_UNDEF) { LOG_ERROR("XTensa core not configured; is xtensa-core-openocd.cfg missing?"); @@ -1096,7 +1097,7 @@ int xtensa_assert_reset(struct target *target) { struct xtensa *xtensa = target_to_xtensa(target); - LOG_TARGET_DEBUG(target, "target_number=%i, begin", target->target_number); + LOG_TARGET_DEBUG(target, " begin"); xtensa_queue_pwr_reg_write(xtensa, XDMREG_PWRCTL, PWRCTL_JTAGDEBUGUSE(xtensa) | PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | @@ -2635,6 +2636,214 @@ int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoin return ERROR_OK; } +int xtensa_start_algorithm(struct target *target, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + target_addr_t entry_point, target_addr_t exit_point, + void *arch_info) +{ + struct xtensa *xtensa = target_to_xtensa(target); + struct xtensa_algorithm *algorithm_info = arch_info; + int retval = ERROR_OK; + bool usr_ps = false; + + /* NOTE: xtensa_run_algorithm requires that each algorithm uses a software breakpoint + * at the exit point */ + + if (target->state != TARGET_HALTED) { + LOG_WARNING("Target not halted!"); + return ERROR_TARGET_NOT_HALTED; + } + + for (unsigned int i = 0; i < xtensa->core_cache->num_regs; i++) { + struct reg *reg = &xtensa->core_cache->reg_list[i]; + buf_cpy(reg->value, xtensa->algo_context_backup[i], reg->size); + } + /* save debug reason, it will be changed */ + algorithm_info->ctx_debug_reason = target->debug_reason; + /* write mem params */ + for (int i = 0; i < num_mem_params; i++) { + if (mem_params[i].direction != PARAM_IN) { + retval = target_write_buffer(target, mem_params[i].address, + mem_params[i].size, + mem_params[i].value); + if (retval != ERROR_OK) + return retval; + } + } + /* write reg params */ + for (int i = 0; i < num_reg_params; i++) { + if (reg_params[i].size > 32) { + LOG_ERROR("BUG: not supported register size (%d)", reg_params[i].size); + return ERROR_FAIL; + } + struct reg *reg = register_get_by_name(xtensa->core_cache, reg_params[i].reg_name, 0); + if (!reg) { + LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name); + return ERROR_FAIL; + } + if (reg->size != reg_params[i].size) { + LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name); + return ERROR_FAIL; + } + if (memcmp(reg_params[i].reg_name, "ps", 3)) { + usr_ps = true; + } else { + unsigned int reg_id = xtensa->eps_dbglevel_idx; + assert(reg_id < xtensa->core_cache->num_regs && "Attempt to access non-existing reg!"); + reg = &xtensa->core_cache->reg_list[reg_id]; + } + xtensa_reg_set_value(reg, buf_get_u32(reg_params[i].value, 0, reg->size)); + reg->valid = 1; + } + /* ignore custom core mode if custom PS value is specified */ + if (!usr_ps) { + unsigned int eps_reg_idx = xtensa->eps_dbglevel_idx; + xtensa_reg_val_t ps = xtensa_reg_get(target, eps_reg_idx); + enum xtensa_mode core_mode = XT_PS_RING_GET(ps); + if (algorithm_info->core_mode != XT_MODE_ANY && algorithm_info->core_mode != core_mode) { + LOG_DEBUG("setting core_mode: 0x%x", algorithm_info->core_mode); + xtensa_reg_val_t new_ps = (ps & ~XT_PS_RING_MSK) | XT_PS_RING(algorithm_info->core_mode); + /* save previous core mode */ + /* TODO: core_mode is not restored for now. Can be added to the end of wait_algorithm */ + algorithm_info->core_mode = core_mode; + xtensa_reg_set(target, eps_reg_idx, new_ps); + xtensa->core_cache->reg_list[eps_reg_idx].valid = 1; + } + } + + return xtensa_resume(target, 0, entry_point, 1, 1); +} + +/** Waits for an algorithm in the target. */ +int xtensa_wait_algorithm(struct target *target, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + target_addr_t exit_point, unsigned int timeout_ms, + void *arch_info) +{ + struct xtensa *xtensa = target_to_xtensa(target); + struct xtensa_algorithm *algorithm_info = arch_info; + int retval = ERROR_OK; + xtensa_reg_val_t pc; + + /* NOTE: xtensa_run_algorithm requires that each algorithm uses a software breakpoint + * at the exit point */ + + retval = target_wait_state(target, TARGET_HALTED, timeout_ms); + /* If the target fails to halt due to the breakpoint, force a halt */ + if (retval != ERROR_OK || target->state != TARGET_HALTED) { + retval = target_halt(target); + if (retval != ERROR_OK) + return retval; + retval = target_wait_state(target, TARGET_HALTED, 500); + if (retval != ERROR_OK) + return retval; + LOG_TARGET_ERROR(target, "not halted %d, pc 0x%" PRIx32 ", ps 0x%" PRIx32, retval, + xtensa_reg_get(target, XT_REG_IDX_PC), + xtensa_reg_get(target, xtensa->eps_dbglevel_idx)); + return ERROR_TARGET_TIMEOUT; + } + pc = xtensa_reg_get(target, XT_REG_IDX_PC); + if (exit_point && pc != exit_point) { + LOG_ERROR("failed algorithm halted at 0x%" PRIx32 ", expected " TARGET_ADDR_FMT, pc, exit_point); + return ERROR_TARGET_TIMEOUT; + } + /* Copy core register values to reg_params[] */ + for (int i = 0; i < num_reg_params; i++) { + if (reg_params[i].direction != PARAM_OUT) { + struct reg *reg = register_get_by_name(xtensa->core_cache, reg_params[i].reg_name, 0); + if (!reg) { + LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name); + return ERROR_FAIL; + } + if (reg->size != reg_params[i].size) { + LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name); + return ERROR_FAIL; + } + buf_set_u32(reg_params[i].value, 0, 32, xtensa_reg_get_value(reg)); + } + } + /* Read memory values to mem_params */ + LOG_DEBUG("Read mem params"); + for (int i = 0; i < num_mem_params; i++) { + LOG_DEBUG("Check mem param @ " TARGET_ADDR_FMT, mem_params[i].address); + if (mem_params[i].direction != PARAM_OUT) { + LOG_DEBUG("Read mem param @ " TARGET_ADDR_FMT, mem_params[i].address); + retval = target_read_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value); + if (retval != ERROR_OK) + return retval; + } + } + + /* avoid gdb keep_alive warning */ + keep_alive(); + + for (int i = xtensa->core_cache->num_regs - 1; i >= 0; i--) { + struct reg *reg = &xtensa->core_cache->reg_list[i]; + if (i == XT_REG_IDX_PS) { + continue; /* restore mapped reg number of PS depends on NDEBUGLEVEL */ + } else if (i == XT_REG_IDX_DEBUGCAUSE) { + /*FIXME: restoring DEBUGCAUSE causes exception when executing corresponding + * instruction in DIR */ + LOG_DEBUG("Skip restoring register %s: 0x%8.8" PRIx32 " -> 0x%8.8" PRIx32, + xtensa->core_cache->reg_list[i].name, + buf_get_u32(reg->value, 0, 32), + buf_get_u32(xtensa->algo_context_backup[i], 0, 32)); + buf_cpy(xtensa->algo_context_backup[i], reg->value, reg->size); + xtensa->core_cache->reg_list[i].dirty = 0; + xtensa->core_cache->reg_list[i].valid = 0; + } else if (memcmp(xtensa->algo_context_backup[i], reg->value, reg->size / 8)) { + if (reg->size <= 32) { + LOG_DEBUG("restoring register %s: 0x%8.8" PRIx32 " -> 0x%8.8" PRIx32, + xtensa->core_cache->reg_list[i].name, + buf_get_u32(reg->value, 0, reg->size), + buf_get_u32(xtensa->algo_context_backup[i], 0, reg->size)); + } else if (reg->size <= 64) { + LOG_DEBUG("restoring register %s: 0x%8.8" PRIx64 " -> 0x%8.8" PRIx64, + xtensa->core_cache->reg_list[i].name, + buf_get_u64(reg->value, 0, reg->size), + buf_get_u64(xtensa->algo_context_backup[i], 0, reg->size)); + } else { + LOG_DEBUG("restoring register %s %u-bits", xtensa->core_cache->reg_list[i].name, reg->size); + } + buf_cpy(xtensa->algo_context_backup[i], reg->value, reg->size); + xtensa->core_cache->reg_list[i].dirty = 1; + xtensa->core_cache->reg_list[i].valid = 1; + } + } + target->debug_reason = algorithm_info->ctx_debug_reason; + + retval = xtensa_write_dirty_registers(target); + if (retval != ERROR_OK) + LOG_ERROR("Failed to write dirty regs (%d)!", retval); + + return retval; +} + +int xtensa_run_algorithm(struct target *target, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + target_addr_t entry_point, target_addr_t exit_point, + unsigned int timeout_ms, void *arch_info) +{ + int retval = xtensa_start_algorithm(target, + num_mem_params, mem_params, + num_reg_params, reg_params, + entry_point, exit_point, + arch_info); + + if (retval == ERROR_OK) { + retval = xtensa_wait_algorithm(target, + num_mem_params, mem_params, + num_reg_params, reg_params, + exit_point, timeout_ms, + arch_info); + } + + return retval; +} + static int xtensa_build_reg_cache(struct target *target) { struct xtensa *xtensa = target_to_xtensa(target); @@ -3978,6 +4187,38 @@ COMMAND_HANDLER(xtensa_cmd_smpbreak) get_current_target(CMD_CTX)); } +COMMAND_HELPER(xtensa_cmd_dm_rw_do, struct xtensa *xtensa) +{ + if (CMD_ARGC == 1) { + // read: xtensa dm addr + uint32_t addr = strtoul(CMD_ARGV[0], NULL, 0); + uint32_t val; + int res = xtensa_dm_read(&xtensa->dbg_mod, addr, &val); + if (res == ERROR_OK) + command_print(CMD, "xtensa DM(0x%08" PRIx32 ") -> 0x%08" PRIx32, addr, val); + else + command_print(CMD, "xtensa DM(0x%08" PRIx32 ") : read ERROR %" PRId32, addr, res); + return res; + } else if (CMD_ARGC == 2) { + // write: xtensa dm addr value + uint32_t addr = strtoul(CMD_ARGV[0], NULL, 0); + uint32_t val = strtoul(CMD_ARGV[1], NULL, 0); + int res = xtensa_dm_write(&xtensa->dbg_mod, addr, val); + if (res == ERROR_OK) + command_print(CMD, "xtensa DM(0x%08" PRIx32 ") <- 0x%08" PRIx32, addr, val); + else + command_print(CMD, "xtensa DM(0x%08" PRIx32 ") : write ERROR %" PRId32, addr, res); + return res; + } + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(xtensa_cmd_dm_rw) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_dm_rw_do, + target_to_xtensa(get_current_target(CMD_CTX))); +} + COMMAND_HELPER(xtensa_cmd_tracestart_do, struct xtensa *xtensa) { struct xtensa_trace_status trace_status; @@ -4235,6 +4476,13 @@ static const struct command_registration xtensa_any_command_handlers[] = { .usage = "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]", }, { + .name = "dm", + .handler = xtensa_cmd_dm_rw, + .mode = COMMAND_ANY, + .help = "Xtensa DM read/write", + .usage = "addr [value]" + }, + { .name = "perfmon_enable", .handler = xtensa_cmd_perfmon_enable, .mode = COMMAND_EXEC, diff --git a/src/target/xtensa/xtensa.h b/src/target/xtensa/xtensa.h index 4216ae2..3b37122 100644 --- a/src/target/xtensa/xtensa.h +++ b/src/target/xtensa/xtensa.h @@ -222,6 +222,16 @@ struct xtensa_sw_breakpoint { uint8_t insn_sz; /* 2 or 3 bytes */ }; +/** + * Xtensa algorithm data. + */ +struct xtensa_algorithm { + /** User can set this to specify which core mode algorithm should be run in. */ + enum xtensa_mode core_mode; + /** Used internally to backup and restore debug_reason. */ + enum target_debug_reason ctx_debug_reason; +}; + #define XTENSA_COMMON_MAGIC 0x54E4E555U /** @@ -395,6 +405,21 @@ int xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint); int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint); int xtensa_watchpoint_add(struct target *target, struct watchpoint *watchpoint); int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoint); +int xtensa_start_algorithm(struct target *target, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + target_addr_t entry_point, target_addr_t exit_point, + void *arch_info); +int xtensa_wait_algorithm(struct target *target, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + target_addr_t exit_point, unsigned int timeout_ms, + void *arch_info); +int xtensa_run_algorithm(struct target *target, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + target_addr_t entry_point, target_addr_t exit_point, + unsigned int timeout_ms, void *arch_info); void xtensa_set_permissive_mode(struct target *target, bool state); const char *xtensa_get_gdb_arch(struct target *target); int xtensa_gdb_query_custom(struct target *target, const char *packet, char **response_p); diff --git a/src/target/xtensa/xtensa_debug_module.c b/src/target/xtensa/xtensa_debug_module.c index 31d7a94..8045779 100644 --- a/src/target/xtensa/xtensa_debug_module.c +++ b/src/target/xtensa/xtensa_debug_module.c @@ -34,6 +34,16 @@ static const struct xtensa_dm_pwr_reg_offsets xdm_pwr_regs[XDMREG_PWRNUM] = static const struct xtensa_dm_reg_offsets xdm_regs[XDMREG_NUM] = XTENSA_DM_REG_OFFSETS; +static enum xtensa_dm_reg xtensa_dm_regaddr_to_id(uint32_t addr) +{ + enum xtensa_dm_reg id; + uint32_t addr_masked = (addr & (XTENSA_DM_APB_ALIGN - 1)); + for (id = XDMREG_TRAXID; id < XDMREG_NUM; id++) + if (xdm_regs[id].apb == addr_masked) + break; + return id; +} + static void xtensa_dm_add_set_ir(struct xtensa_debug_module *dm, uint8_t value) { struct scan_field field; @@ -285,6 +295,34 @@ int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bit return xtensa_dm_queue_execute(dm); } +int xtensa_dm_read(struct xtensa_debug_module *dm, uint32_t addr, uint32_t *val) +{ + enum xtensa_dm_reg reg = xtensa_dm_regaddr_to_id(addr); + uint8_t buf[sizeof(uint32_t)]; + if (reg < XDMREG_NUM) { + xtensa_dm_queue_enable(dm); + dm->dbg_ops->queue_reg_read(dm, reg, buf); + xtensa_dm_queue_tdi_idle(dm); + int res = xtensa_dm_queue_execute(dm); + if (res == ERROR_OK && val) + *val = buf_get_u32(buf, 0, 32); + return res; + } + return ERROR_FAIL; +} + +int xtensa_dm_write(struct xtensa_debug_module *dm, uint32_t addr, uint32_t val) +{ + enum xtensa_dm_reg reg = xtensa_dm_regaddr_to_id(addr); + if (reg < XDMREG_NUM) { + xtensa_dm_queue_enable(dm); + dm->dbg_ops->queue_reg_write(dm, reg, val); + xtensa_dm_queue_tdi_idle(dm); + return xtensa_dm_queue_execute(dm); + } + return ERROR_FAIL; +} + int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg) { /*Turn off trace unit so we can start a new trace. */ diff --git a/src/target/xtensa/xtensa_debug_module.h b/src/target/xtensa/xtensa_debug_module.h index 46b2935..495da2a 100644 --- a/src/target/xtensa/xtensa_debug_module.h +++ b/src/target/xtensa/xtensa_debug_module.h @@ -75,6 +75,22 @@ enum xtensa_dm_reg { XDMREG_DELAYCNT, XDMREG_MEMADDRSTART, XDMREG_MEMADDREND, + XDMREG_EXTTIMELO, + XDMREG_EXTTIMEHI, + XDMREG_TRAXRSVD48, + XDMREG_TRAXRSVD4C, + XDMREG_TRAXRSVD50, + XDMREG_TRAXRSVD54, + XDMREG_TRAXRSVD58, + XDMREG_TRAXRSVD5C, + XDMREG_TRAXRSVD60, + XDMREG_TRAXRSVD64, + XDMREG_TRAXRSVD68, + XDMREG_TRAXRSVD6C, + XDMREG_TRAXRSVD70, + XDMREG_TRAXRSVD74, + XDMREG_CONFIGID0, + XDMREG_CONFIGID1, /* Performance Monitor Registers */ XDMREG_PMG, @@ -168,6 +184,22 @@ struct xtensa_dm_reg_offsets { { .nar = 0x07, .apb = 0x001c }, /* XDMREG_DELAYCNT */ \ { .nar = 0x08, .apb = 0x0020 }, /* XDMREG_MEMADDRSTART */ \ { .nar = 0x09, .apb = 0x0024 }, /* XDMREG_MEMADDREND */ \ + { .nar = 0x10, .apb = 0x0040 }, /* XDMREG_EXTTIMELO */ \ + { .nar = 0x11, .apb = 0x0044 }, /* XDMREG_EXTTIMEHI */ \ + { .nar = 0x12, .apb = 0x0048 }, /* XDMREG_TRAXRSVD48 */ \ + { .nar = 0x13, .apb = 0x004c }, /* XDMREG_TRAXRSVD4C */ \ + { .nar = 0x14, .apb = 0x0050 }, /* XDMREG_TRAXRSVD50 */ \ + { .nar = 0x15, .apb = 0x0054 }, /* XDMREG_TRAXRSVD54 */ \ + { .nar = 0x16, .apb = 0x0058 }, /* XDMREG_TRAXRSVD58 */ \ + { .nar = 0x17, .apb = 0x005c }, /* XDMREG_TRAXRSVD5C */ \ + { .nar = 0x18, .apb = 0x0060 }, /* XDMREG_TRAXRSVD60 */ \ + { .nar = 0x19, .apb = 0x0064 }, /* XDMREG_TRAXRSVD64 */ \ + { .nar = 0x1a, .apb = 0x0068 }, /* XDMREG_TRAXRSVD68 */ \ + { .nar = 0x1b, .apb = 0x006c }, /* XDMREG_TRAXRSVD6C */ \ + { .nar = 0x1c, .apb = 0x0070 }, /* XDMREG_TRAXRSVD70 */ \ + { .nar = 0x1d, .apb = 0x0074 }, /* XDMREG_TRAXRSVD74 */ \ + { .nar = 0x1e, .apb = 0x0078 }, /* XDMREG_CONFIGID0 */ \ + { .nar = 0x1f, .apb = 0x007c }, /* XDMREG_CONFIGID1 */ \ \ /* Performance Monitor Registers */ \ { .nar = 0x20, .apb = 0x1000 }, /* XDMREG_PMG */ \ @@ -297,6 +329,11 @@ struct xtensa_dm_reg_offsets { #define DEBUGCAUSE_DI BIT(5) /* Debug Interrupt */ #define DEBUGCAUSE_VALID BIT(31) /* Pseudo-value to trigger reread (NX only) */ +/* TRAXID */ +#define TRAXID_PRODNO_TRAX 0 /* TRAXID.PRODNO value for TRAX module */ +#define TRAXID_PRODNO_SHIFT 28 +#define TRAXID_PRODNO_MASK 0xf + #define TRAXCTRL_TREN BIT(0) /* Trace enable. Tracing starts on 0->1 */ #define TRAXCTRL_TRSTP BIT(1) /* Trace Stop. Make 1 to stop trace. */ #define TRAXCTRL_PCMEN BIT(2) /* PC match enable */ @@ -512,6 +549,9 @@ static inline xtensa_dsr_t xtensa_dm_core_status_get(struct xtensa_debug_module return dm->core_status.dsr; } +int xtensa_dm_read(struct xtensa_debug_module *dm, uint32_t addr, uint32_t *val); +int xtensa_dm_write(struct xtensa_debug_module *dm, uint32_t addr, uint32_t val); + int xtensa_dm_device_id_read(struct xtensa_debug_module *dm); static inline xtensa_ocdid_t xtensa_dm_device_id_get(struct xtensa_debug_module *dm) { |