target/espressif: add algorithm support to execute code on target
This functionality can be useful for; 1-ESP flashing code to load flasher stub on target and write/read/erase flash. 2-ESP GCOV command uses some of these functions to run onboard routines to dump coverage info. This is high level api for the Espressif xtensa and riscv targets Signed-off-by: Erhan Kurubas <erhan.kurubas@espressif.com> Change-Id: I5e618b960bb6566ee618d4ba261f51af97a7cb0e Reviewed-on: https://review.openocd.org/c/openocd/+/7759 Reviewed-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com> Tested-by: jenkins
This commit is contained in:
parent
ba16fdc1c6
commit
d3ffcc784d
|
@ -21,4 +21,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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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,
|
||||
|
|
|
@ -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[];
|
||||
|
|
Loading…
Reference in New Issue