This is an automated email from Gerrit.

"Erhan Kurubas <erhan.kuru...@espressif.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7759

-- gerrit

commit 3673a8a6bbe23f8fc00961451e8570223ad874db
Author: Erhan Kurubas <erhan.kuru...@espressif.com>
Date:   Mon Jul 3 11:02:13 2023 +0200

    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.kuru...@espressif.com>
    Change-Id: I5e618b960bb6566ee618d4ba261f51af97a7cb0e

diff --git a/src/target/espressif/Makefile.am b/src/target/espressif/Makefile.am
index 776818ff4d..1fbd926d9d 100644
--- a/src/target/espressif/Makefile.am
+++ b/src/target/espressif/Makefile.am
@@ -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
diff --git a/src/target/espressif/esp_algorithm.c 
b/src/target/espressif/esp_algorithm.c
new file mode 100644
index 0000000000..9624c4057b
--- /dev/null
+++ b/src/target/espressif/esp_algorithm.c
@@ -0,0 +1,590 @@
+// 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 ALGO_ALGORITHM_EXIT_TMO    40000       /* ms */
+
+static int algorithm_read_stub_logs(struct target *target, struct 
algorithm_stub *stub)
+{
+       if (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 (%d)!", 
retval);
+               return retval;
+       }
+       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 algorithm_run(struct target *target, struct algorithm_image *image,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       va_list ap)
+{
+       void **mem_handles = NULL;
+
+       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(void *));
+               if (!mem_handles) {
+                       LOG_ERROR("Failed to alloc target mem handles!");
+                       retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+                       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;
+                       if (image) {
+                               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;
+                       } else {
+                               struct algorithm_run_data alloc_run;
+                               memset(&alloc_run, 0, sizeof(alloc_run));
+                               alloc_run.hw = run->hw;
+                               alloc_run.stack_size = 
run->on_board.min_stack_size;
+                               alloc_run.on_board.min_stack_addr = 
run->on_board.min_stack_addr;
+                               alloc_run.on_board.code_buf_size = 
run->on_board.code_buf_size;
+                               alloc_run.on_board.code_buf_addr = 
run->on_board.code_buf_addr;
+                               retval = algorithm_run_onboard_func(target,
+                                       &alloc_run,
+                                       run->on_board.alloc_func,
+                                       1,
+                                       run->mem_args.params[i].size);
+                               if (retval != ERROR_OK) {
+                                       LOG_ERROR("Failed to run mem arg alloc 
onboard algo (%d)!", retval);
+                                       goto _cleanup;
+                               }
+                               if (alloc_run.ret_code == 0) {
+                                       LOG_ERROR("Failed to alloc onboard 
memory (%d)!", retval);
+                                       retval = 
ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+                                       goto _cleanup;
+                               }
+                               mem_handles[i] = (void 
*)((long)alloc_run.ret_code);
+                               run->mem_args.params[i].address = 
alloc_run.ret_code;
+                       }
+                       if (usr_param_num != UINT_MAX) /* if we need update 
some register param with mem param value */
+                               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 tmo = 0;       /* do not wait if 'usr_func' returned error */
+       if (retval == ERROR_OK)
+               tmo = run->tmo ? run->tmo : ALGO_ALGORITHM_EXIT_TMO;
+       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, tmo,
+               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() */
+       }
+       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 = 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]) {
+                               if (image) {
+                                       target_free_working_area(target, 
mem_handles[i]);
+                               } else {
+                                       struct algorithm_run_data free_run;
+                                       memset(&free_run, 0, sizeof(free_run));
+                                       free_run.hw = run->hw;
+                                       free_run.stack_size = 
run->on_board.min_stack_size;
+                                       free_run.on_board.min_stack_addr = 
run->on_board.min_stack_addr;
+                                       free_run.on_board.code_buf_size = 
run->on_board.code_buf_size;
+                                       free_run.on_board.code_buf_addr = 
run->on_board.code_buf_addr;
+                                       int ret = 
algorithm_run_onboard_func(target,
+                                               &free_run,
+                                               run->on_board.free_func,
+                                               1,
+                                               mem_handles[i]);
+                                       if (ret != ERROR_OK)
+                                               LOG_ERROR("Failed to run mem 
arg free onboard algo (%d)!", ret);
+                               }
+                       }
+               }
+               free(mem_handles);
+       }
+       run->hw->algo_cleanup(target, run);
+
+       return retval;
+}
+
+static void reverse_binary(const uint8_t *src, uint8_t *dest, size_t length)
+{
+       size_t i;
+
+       /* Put extra bytes to the beginning with padding */
+       size_t remaining = length % 4;
+       if (remaining > 0) {
+               memset(dest, 0xFF, 4);
+               for (i = 0; i < remaining; i++)
+                       dest[i] = src[length - remaining + i];
+       }
+
+       for (i = remaining; i < length - remaining; i += 4) {
+               dest[i + 0] = src[length - i - 4];
+               dest[i + 1] = src[length - i - 3];
+               dest[i + 2] = src[length - i - 2];
+               dest[i + 3] = src[length - i - 1];
+       }
+}
+
+static int load_section_from_image(struct target *target,
+       struct algorithm_run_data *run, int section_num, bool reverse)
+{
+       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);
+
+                       retval = target_write_buffer(target, 
run->image.dram_org - sec_wr - size_read, size_read, 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;
+}
+
+int algorithm_load_func_image(struct target *target, struct algorithm_run_data 
*run)
+{
+       int retval;
+       size_t tramp_sz = 0;
+       const uint8_t *tramp = NULL;
+       struct duration algo_time;
+       bool alloc_working_area = true;
+
+       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%x, %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_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_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_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_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:
+       algorithm_unload_func_image(target, run);
+       return retval;
+}
+
+int algorithm_unload_func_image(struct target *target, struct 
algorithm_run_data *run)
+{
+       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 algorithm_exec_func_image_va(struct target *target,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       va_list ap)
+{
+       if (!run->image.image.start_address_set || 
run->image.image.start_address == 0)
+               return ERROR_FAIL;
+
+       return algorithm_run(target, &run->image, run, num_args, ap);
+}
+
+int algorithm_load_onboard_func(struct target *target, target_addr_t 
func_addr, struct algorithm_run_data *run)
+{
+       int res;
+       const uint8_t *tramp = NULL;
+       size_t tramp_sz = 0;
+       struct duration algo_time;
+
+       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 %u bytes exceeds target buf size %d 
bytes!",
+                       (uint32_t)tramp_sz, run->on_board.code_buf_size);
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       if (run->stack_size > run->on_board.min_stack_size) {
+               if (run->on_board.alloc_func == 0 || run->on_board.free_func == 
0) {
+                       LOG_ERROR("No stubs memory funcs found!");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+               /* alloc stack */
+               struct algorithm_run_data alloc_run;
+               memset(&alloc_run, 0, sizeof(alloc_run));
+               alloc_run.hw = run->hw;
+               alloc_run.stack_size = run->on_board.min_stack_size;
+               alloc_run.on_board.min_stack_addr = 
run->on_board.min_stack_addr;
+               alloc_run.on_board.code_buf_size = run->on_board.code_buf_size;
+               alloc_run.on_board.code_buf_addr = run->on_board.code_buf_addr;
+               res = algorithm_run_onboard_func(target,
+                       &alloc_run,
+                       run->on_board.alloc_func,
+                       1,
+                       run->stack_size);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to run stack alloc onboard algo 
(%d)!", res);
+                       return res;
+               }
+               LOG_DEBUG("RETCODE: 0x%" PRIx32 "!", alloc_run.ret_code);
+               if (alloc_run.ret_code == 0) {
+                       LOG_ERROR("Failed to alloc onboard stack (%d)!", res);
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+               run->stub.stack_addr = alloc_run.ret_code + run->stack_size;
+       } else {
+               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!");
+                       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 algorithm_unload_onboard_func(struct target *target, struct 
algorithm_run_data *run)
+{
+       if (run->stack_size > run->on_board.min_stack_size) {
+               /* free stack */
+               struct algorithm_run_data free_run;
+               memset(&free_run, 0, sizeof(free_run));
+               free_run.hw = run->hw;
+               free_run.stack_size = run->on_board.min_stack_size;
+               free_run.on_board.min_stack_addr = run->on_board.min_stack_addr;
+               free_run.on_board.code_buf_size = run->on_board.code_buf_size;
+               free_run.on_board.code_buf_addr = run->on_board.code_buf_addr;
+               int res = algorithm_run_onboard_func(target,
+                       &free_run,
+                       run->on_board.free_func,
+                       1,
+                       run->stub.stack_addr - run->stack_size);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to run stack free onboard algo 
(%d)!", res);
+                       return res;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+int algorithm_exec_onboard_func_va(struct target *target,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       va_list ap)
+{
+       return algorithm_run(target, NULL, 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 0000000000..e707fd78da
--- /dev/null
+++ b/src/target/espressif/esp_algorithm.h
@@ -0,0 +1,421 @@
+/* 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 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 algorithm_usr_func_init_t it is 
called just before the stub starts.
+ * 4) If user specifies stub communication func 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 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 
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 
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 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 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 tramploline. */
+       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 */
+       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 algorithm_mem_args {
+       /** Memory params. */
+       struct mem_param *params;
+       /** Number of memory params. */
+       uint32_t count;
+};
+
+/**
+ * Algorithm stub register arguments.
+ */
+struct 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 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 (*algorithm_func_t)(struct target *target, struct 
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 (*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 
algorithm_usr_func_t.
+ *
+ * @return ERROR_OK on success, otherwise ERROR_XXX.
+ */
+typedef int (*algorithm_usr_func_init_t)(struct target *target, struct 
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 
algorithm_usr_func_t.
+ *
+ * @return ERROR_OK on success, otherwise ERROR_XXX.
+ */
+typedef void (*algorithm_usr_func_done_t)(struct target *target, struct 
algorithm_run_data *run, void *usr_arg);
+
+struct algorithm_hw {
+       int (*algo_init)(struct target *target, struct algorithm_run_data *run, 
uint32_t num_args, va_list ap);
+       int (*algo_cleanup)(struct target *target, struct algorithm_run_data 
*run);
+       const uint8_t *(*stub_tramp_get)(struct target *target, size_t *size);
+};
+
+/**
+ * Algorithm run data.
+ */
+struct algorithm_run_data {
+       /** Algorithm completion timeout in ms. If 0, default value will be 
used */
+       uint32_t tmo;
+       /** Algorithm stack size. */
+       uint32_t stack_size;
+       /** Algorithm register arguments. */
+       struct algorithm_reg_args reg_args;
+       /** Algorithm memory arguments. */
+       struct 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 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;
+                       /** Address of malloc-like function to allocate buffer 
on target. */
+                       target_addr_t alloc_func;
+                       /** Address of free-like function to free buffer 
allocated with on_board_alloc_func. */
+                       target_addr_t free_func;
+               } on_board;
+               struct algorithm_image image;
+       };
+       /** Host side algorithm function argument. */
+       void *usr_func_arg;
+       /** Host side algorithm function. */
+       algorithm_usr_func_t usr_func;
+       /** Host side algorithm function setup routine. */
+       algorithm_usr_func_init_t usr_func_init;
+       /** Host side algorithm function cleanup routine. */
+       algorithm_usr_func_done_t usr_func_done;
+       /** Algorithm run function: see algorithm_run_xxx for example. */
+       algorithm_func_t algo_func;
+       /** HW specific API */
+       const struct algorithm_hw *hw;
+};
+
+int algorithm_load_func_image(struct target *target, struct algorithm_run_data 
*run);
+int algorithm_unload_func_image(struct target *target, struct 
algorithm_run_data *run);
+
+int algorithm_exec_func_image_va(struct target *target,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       va_list ap);
+
+static inline int algorithm_exec_func_image(struct target *target,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       ...)
+{
+       va_list ap;
+       va_start(ap, num_args);
+       int retval = algorithm_exec_func_image_va(target, run, num_args, ap);
+       va_end(ap);
+       return retval;
+}
+
+/**
+ * @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 algorithm_run_func_image_va(struct target *target,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       va_list ap)
+{
+       int ret = algorithm_load_func_image(target, run);
+       if (ret != ERROR_OK)
+               return ret;
+       ret = algorithm_exec_func_image_va(target, run, num_args, ap);
+       int rc = algorithm_unload_func_image(target, run);
+       return ret != ERROR_OK ? ret : rc;
+}
+
+static inline int algorithm_run_func_image(struct target *target,
+       struct algorithm_run_data *run,
+       uint32_t num_args,
+       ...)
+{
+       va_list ap;
+       va_start(ap, num_args);
+       int retval = algorithm_run_func_image_va(target, run, num_args, ap);
+       va_end(ap);
+       return retval;
+}
+
+int algorithm_load_onboard_func(struct target *target, target_addr_t 
func_addr, struct algorithm_run_data *run);
+int algorithm_unload_onboard_func(struct target *target, struct 
algorithm_run_data *run);
+int algorithm_exec_onboard_func_va(struct target *target,
+       struct 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 algorithm_run_onboard_func_va(struct target *target,
+       struct algorithm_run_data *run,
+       target_addr_t func_addr,
+       uint32_t num_args,
+       va_list ap)
+{
+       int ret = algorithm_load_onboard_func(target, func_addr, run);
+       if (ret != ERROR_OK)
+               return ret;
+       ret = algorithm_exec_onboard_func_va(target, run, num_args, ap);
+       if (ret != ERROR_OK)
+               return ret;
+       return algorithm_unload_onboard_func(target, run);
+}
+
+static inline int algorithm_run_onboard_func(struct target *target,
+       struct algorithm_run_data *run,
+       target_addr_t func_addr,
+       uint32_t num_args,
+       ...)
+{
+       va_list ap;
+       va_start(ap, num_args);
+       int retval = algorithm_run_onboard_func_va(target, run, func_addr, 
num_args, ap);
+       va_end(ap);
+       return retval;
+}
+
+static inline void algorithm_user_arg_set_uint(struct 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);
+}
+
+static inline uint64_t algorithm_user_arg_get_uint(struct 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_smp.c 
b/src/target/espressif/esp_xtensa_smp.c
index 1d70be9e3c..af06bd68d4 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 
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 = 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 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 = 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,
diff --git a/src/target/espressif/esp_xtensa_smp.h 
b/src/target/espressif/esp_xtensa_smp.h
index 4e4f3b3321..6bfc713ffd 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 
algorithm_run_data *run, uint32_t num_args, ...);
+int esp_xtensa_smp_run_onboard_func(struct target *target,
+       struct 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[];

-- 


Reply via email to