1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Espressif chips common algorithm API for OpenOCD *
5 * Copyright (C) 2022 Espressif Systems Ltd. *
6 ***************************************************************************/
12 #include <helper/align.h>
13 #include <target/algorithm.h>
14 #include <target/target.h>
15 #include "esp_algorithm.h"
17 #define DEFAULT_ALGORITHM_TIMEOUT_MS 40000 /* ms */
19 static int esp_algorithm_read_stub_logs(struct target
*target
, struct esp_algorithm_stub
*stub
)
21 if (!stub
|| stub
->log_buff_addr
== 0 || stub
->log_buff_size
== 0)
25 int retval
= target_read_u32(target
, stub
->log_buff_addr
, &len
);
26 if (retval
!= ERROR_OK
)
29 /* sanity check. log_buff_size = sizeof(len) + sizeof(log_buff) */
30 if (len
== 0 || len
> stub
->log_buff_size
- 4)
33 uint8_t *log_buff
= calloc(1, len
);
35 LOG_ERROR("Failed to allocate memory for the stub log!");
38 retval
= target_read_memory(target
, stub
->log_buff_addr
+ 4, 1, len
, log_buff
);
39 if (retval
== ERROR_OK
)
40 LOG_OUTPUT("%*.*s", len
, len
, log_buff
);
45 static int esp_algorithm_run_image(struct target
*target
,
46 struct esp_algorithm_run_data
*run
,
50 struct working_area
**mem_handles
= NULL
;
55 int retval
= run
->hw
->algo_init(target
, run
, num_args
, ap
);
56 if (retval
!= ERROR_OK
)
59 /* allocate memory arguments and fill respective reg params */
60 if (run
->mem_args
.count
> 0) {
61 mem_handles
= calloc(run
->mem_args
.count
, sizeof(*mem_handles
));
63 LOG_ERROR("Failed to alloc target mem handles!");
67 /* alloc memory args target buffers */
68 for (uint32_t i
= 0; i
< run
->mem_args
.count
; i
++) {
69 /* small hack: if we need to update some reg param this field holds
70 * appropriate user argument number, */
71 /* otherwise should hold UINT_MAX */
72 uint32_t usr_param_num
= run
->mem_args
.params
[i
].address
;
73 static struct working_area
*area
;
74 retval
= target_alloc_working_area(target
, run
->mem_args
.params
[i
].size
, &area
);
75 if (retval
!= ERROR_OK
) {
76 LOG_ERROR("Failed to alloc target buffer!");
77 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
80 mem_handles
[i
] = area
;
81 run
->mem_args
.params
[i
].address
= area
->address
;
82 if (usr_param_num
!= UINT_MAX
) /* if we need update some register param with mem param value */
83 esp_algorithm_user_arg_set_uint(run
, usr_param_num
, run
->mem_args
.params
[i
].address
);
87 if (run
->usr_func_init
) {
88 retval
= run
->usr_func_init(target
, run
, run
->usr_func_arg
);
89 if (retval
!= ERROR_OK
) {
90 LOG_ERROR("Failed to prepare algorithm host side args stub (%d)!", retval
);
95 LOG_DEBUG("Algorithm start @ " TARGET_ADDR_FMT
", stack %d bytes @ " TARGET_ADDR_FMT
,
96 run
->stub
.tramp_mapped_addr
, run
->stack_size
, run
->stub
.stack_addr
);
97 retval
= target_start_algorithm(target
,
98 run
->mem_args
.count
, run
->mem_args
.params
,
99 run
->reg_args
.count
, run
->reg_args
.params
,
100 run
->stub
.tramp_mapped_addr
, 0,
102 if (retval
!= ERROR_OK
) {
103 LOG_ERROR("Failed to start algorithm (%d)!", retval
);
108 /* give target algorithm stub time to init itself, then user func can communicate to it safely */
110 retval
= run
->usr_func(target
, run
->usr_func_arg
);
111 if (retval
!= ERROR_OK
)
112 LOG_ERROR("Failed to exec algorithm user func (%d)!", retval
);
114 uint32_t timeout_ms
= 0; /* do not wait if 'usr_func' returned error */
115 if (retval
== ERROR_OK
)
116 timeout_ms
= run
->timeout_ms
? run
->timeout_ms
: DEFAULT_ALGORITHM_TIMEOUT_MS
;
117 LOG_DEBUG("Wait algorithm completion");
118 retval
= target_wait_algorithm(target
,
119 run
->mem_args
.count
, run
->mem_args
.params
,
120 run
->reg_args
.count
, run
->reg_args
.params
,
123 if (retval
!= ERROR_OK
) {
124 LOG_ERROR("Failed to wait algorithm (%d)!", retval
);
125 /* target has been forced to stop in target_wait_algorithm() */
127 esp_algorithm_read_stub_logs(target
, &run
->stub
);
129 if (run
->usr_func_done
)
130 run
->usr_func_done(target
, run
, run
->usr_func_arg
);
132 if (retval
!= ERROR_OK
) {
133 LOG_ERROR("Algorithm run failed (%d)!", retval
);
135 run
->ret_code
= esp_algorithm_user_arg_get_uint(run
, 0);
136 LOG_DEBUG("Got algorithm RC 0x%" PRIx32
, run
->ret_code
);
140 /* free memory arguments */
142 for (uint32_t i
= 0; i
< run
->mem_args
.count
; i
++) {
144 target_free_working_area(target
, mem_handles
[i
]);
148 run
->hw
->algo_cleanup(target
, run
);
153 static int esp_algorithm_run_debug_stub(struct target
*target
,
154 struct esp_algorithm_run_data
*run
,
158 if (!run
|| !run
->hw
)
161 int retval
= run
->hw
->algo_init(target
, run
, num_args
, ap
);
162 if (retval
!= ERROR_OK
)
165 LOG_DEBUG("Algorithm start @ " TARGET_ADDR_FMT
", stack %d bytes @ " TARGET_ADDR_FMT
,
166 run
->stub
.tramp_mapped_addr
, run
->stack_size
, run
->stub
.stack_addr
);
167 retval
= target_start_algorithm(target
,
168 run
->mem_args
.count
, run
->mem_args
.params
,
169 run
->reg_args
.count
, run
->reg_args
.params
,
170 run
->stub
.tramp_mapped_addr
, 0,
172 if (retval
!= ERROR_OK
) {
173 LOG_ERROR("Failed to start algorithm (%d)!", retval
);
177 uint32_t timeout_ms
= 0; /* do not wait if 'usr_func' returned error */
178 if (retval
== ERROR_OK
)
179 timeout_ms
= run
->timeout_ms
? run
->timeout_ms
: DEFAULT_ALGORITHM_TIMEOUT_MS
;
180 LOG_DEBUG("Wait algorithm completion");
181 retval
= target_wait_algorithm(target
,
182 run
->mem_args
.count
, run
->mem_args
.params
,
183 run
->reg_args
.count
, run
->reg_args
.params
,
186 if (retval
!= ERROR_OK
) {
187 LOG_ERROR("Failed to wait algorithm (%d)!", retval
);
188 /* target has been forced to stop in target_wait_algorithm() */
191 if (retval
!= ERROR_OK
) {
192 LOG_ERROR("Algorithm run failed (%d)!", retval
);
194 run
->ret_code
= esp_algorithm_user_arg_get_uint(run
, 0);
195 LOG_DEBUG("Got algorithm RC 0x%" PRIx32
, run
->ret_code
);
199 run
->hw
->algo_cleanup(target
, run
);
204 static void reverse_binary(const uint8_t *src
, uint8_t *dest
, size_t length
)
206 size_t remaining
= length
% 4;
208 size_t aligned_len
= ALIGN_UP(length
, 4);
211 /* Put extra bytes to the beginning with padding */
212 memset(dest
+ remaining
, 0xFF, 4 - remaining
);
213 for (size_t i
= 0; i
< remaining
; i
++)
214 dest
[i
] = src
[length
- remaining
+ i
];
215 length
-= remaining
; /* reverse the others */
219 for (size_t i
= offset
; i
< aligned_len
; i
+= 4) {
220 dest
[i
+ 0] = src
[length
- i
+ offset
- 4];
221 dest
[i
+ 1] = src
[length
- i
+ offset
- 3];
222 dest
[i
+ 2] = src
[length
- i
+ offset
- 2];
223 dest
[i
+ 3] = src
[length
- i
+ offset
- 1];
227 static int load_section_from_image(struct target
*target
,
228 struct esp_algorithm_run_data
*run
,
235 struct imagesection
*section
= &run
->image
.image
.sections
[section_num
];
239 assert(sizeof(buf
) % 4 == 0);
241 while (sec_wr
< section
->size
) {
242 uint32_t nb
= section
->size
- sec_wr
> sizeof(buf
) ? sizeof(buf
) : section
->size
- sec_wr
;
243 size_t size_read
= 0;
244 int retval
= image_read_section(&run
->image
.image
, section_num
, sec_wr
, nb
, buf
, &size_read
);
245 if (retval
!= ERROR_OK
) {
246 LOG_ERROR("Failed to read stub section (%d)!", retval
);
251 size_t aligned_len
= ALIGN_UP(size_read
, 4);
252 uint8_t reversed_buf
[aligned_len
];
254 /* Send original size to allow padding */
255 reverse_binary(buf
, reversed_buf
, size_read
);
258 The address range accessed via the instruction bus is in reverse order (word-wise) compared to access
259 via the data bus. That is to say, address
260 0x3FFE_0000 and 0x400B_FFFC access the same word
261 0x3FFE_0004 and 0x400B_FFF8 access the same word
262 0x3FFE_0008 and 0x400B_FFF4 access the same word
264 The data bus and instruction bus of the CPU are still both little-endian,
265 so the byte order of individual words is not reversed between address spaces.
267 0x3FFE_0000 accesses the least significant byte in the word accessed by 0x400B_FFFC.
268 0x3FFE_0001 accesses the second least significant byte in the word accessed by 0x400B_FFFC.
269 0x3FFE_0002 accesses the second most significant byte in the word accessed by 0x400B_FFFC.
270 For more details, please refer to ESP32 TRM, Internal SRAM1 section.
272 retval
= target_write_buffer(target
, run
->image
.dram_org
- sec_wr
- aligned_len
, aligned_len
, reversed_buf
);
273 if (retval
!= ERROR_OK
) {
274 LOG_ERROR("Failed to write stub section!");
278 retval
= target_write_buffer(target
, section
->base_address
+ sec_wr
, size_read
, buf
);
279 if (retval
!= ERROR_OK
) {
280 LOG_ERROR("Failed to write stub section!");
293 * ----------------------------
294 * The linker scripts defines the memory layout for the stub code.
295 * The OpenOCD script specifies the workarea address and it's size
296 * Sections defined in the linker are organized to share the same addresses with the workarea.
297 * code and data sections are located in Internal SRAM1 and OpenOCD fills these sections using the data bus.
299 int esp_algorithm_load_func_image(struct target
*target
, struct esp_algorithm_run_data
*run
)
303 const uint8_t *tramp
= NULL
;
304 struct duration algo_time
;
305 bool alloc_code_working_area
= true;
307 if (!run
|| !run
->hw
)
310 if (duration_start(&algo_time
) != 0) {
311 LOG_ERROR("Failed to start algo time measurement!");
315 if (run
->hw
->stub_tramp_get
) {
316 tramp
= run
->hw
->stub_tramp_get(target
, &tramp_sz
);
321 LOG_DEBUG("stub: base 0x%x, start 0x%" PRIx32
", %d sections",
322 run
->image
.image
.base_address_set
? (unsigned int)run
->image
.image
.base_address
: 0,
323 run
->image
.image
.start_address
,
324 run
->image
.image
.num_sections
);
325 run
->stub
.entry
= run
->image
.image
.start_address
;
327 /* [code + trampoline] + <padding> + [data] */
329 /* ESP32 has reversed memory region. It will use the last part of DRAM, the others will use the first part.
330 * To avoid complexity for the backup/restore process, we will allocate a workarea for all IRAM region from
331 * the beginning. In that case no need to have a padding area.
333 if (run
->image
.reverse
) {
334 if (target_alloc_working_area(target
, run
->image
.iram_len
, &run
->stub
.code
) != ERROR_OK
) {
335 LOG_ERROR("no working area available, can't alloc space for stub code!");
336 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
339 alloc_code_working_area
= false;
342 uint32_t code_size
= 0;
344 /* Load code section */
345 for (unsigned int i
= 0; i
< run
->image
.image
.num_sections
; i
++) {
346 struct imagesection
*section
= &run
->image
.image
.sections
[i
];
348 if (section
->size
== 0)
351 if (section
->flags
& ESP_IMAGE_ELF_PHF_EXEC
) {
352 LOG_DEBUG("addr " TARGET_ADDR_FMT
", sz %d, flags %" PRIx64
,
353 section
->base_address
, section
->size
, section
->flags
);
355 if (alloc_code_working_area
) {
356 retval
= target_alloc_working_area(target
, section
->size
, &run
->stub
.code
);
357 if (retval
!= ERROR_OK
) {
358 LOG_ERROR("no working area available, can't alloc space for stub code!");
359 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
364 if (section
->base_address
== 0) {
365 section
->base_address
= run
->stub
.code
->address
;
366 /* sanity check, stub is compiled to be run from working area */
367 } else if (run
->stub
.code
->address
!= section
->base_address
) {
368 LOG_ERROR("working area " TARGET_ADDR_FMT
" and stub code section " TARGET_ADDR_FMT
369 " address mismatch!",
370 section
->base_address
,
371 run
->stub
.code
->address
);
376 retval
= load_section_from_image(target
, run
, i
, run
->image
.reverse
);
377 if (retval
!= ERROR_OK
)
380 code_size
+= ALIGN_UP(section
->size
, 4);
381 break; /* Stub has one executable text section */
385 /* If exists, load trampoline to the code area */
387 if (run
->stub
.tramp_addr
== 0) {
388 if (alloc_code_working_area
) {
389 /* alloc trampoline in code working area */
390 if (target_alloc_working_area(target
, tramp_sz
, &run
->stub
.tramp
) != ERROR_OK
) {
391 LOG_ERROR("no working area available, can't alloc space for stub jumper!");
392 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
395 run
->stub
.tramp_addr
= run
->stub
.tramp
->address
;
399 size_t al_tramp_size
= ALIGN_UP(tramp_sz
, 4);
401 if (run
->image
.reverse
) {
402 target_addr_t reversed_tramp_addr
= run
->image
.dram_org
- code_size
;
403 uint8_t reversed_tramp
[al_tramp_size
];
405 /* Send original size to allow padding */
406 reverse_binary(tramp
, reversed_tramp
, tramp_sz
);
407 run
->stub
.tramp_addr
= reversed_tramp_addr
- al_tramp_size
;
408 LOG_DEBUG("Write reversed tramp to addr " TARGET_ADDR_FMT
", sz %zu", run
->stub
.tramp_addr
, al_tramp_size
);
409 retval
= target_write_buffer(target
, run
->stub
.tramp_addr
, al_tramp_size
, reversed_tramp
);
411 LOG_DEBUG("Write tramp to addr " TARGET_ADDR_FMT
", sz %zu", run
->stub
.tramp_addr
, tramp_sz
);
412 retval
= target_write_buffer(target
, run
->stub
.tramp_addr
, tramp_sz
, tramp
);
415 if (retval
!= ERROR_OK
) {
416 LOG_ERROR("Failed to write stub jumper!");
420 run
->stub
.tramp_mapped_addr
= run
->image
.iram_org
+ code_size
;
421 code_size
+= al_tramp_size
;
422 LOG_DEBUG("Tramp mapped to addr " TARGET_ADDR_FMT
, run
->stub
.tramp_mapped_addr
);
425 /* allocate dummy space until the data address */
426 if (alloc_code_working_area
) {
427 /* we dont need to restore padding area. */
428 uint32_t backup_working_area_prev
= target
->backup_working_area
;
429 target
->backup_working_area
= 0;
430 if (target_alloc_working_area(target
, run
->image
.iram_len
- code_size
, &run
->stub
.padding
) != ERROR_OK
) {
431 LOG_ERROR("no working area available, can't alloc space for stub code!");
432 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
435 target
->backup_working_area
= backup_working_area_prev
;
438 /* Load the data section */
439 for (unsigned int i
= 0; i
< run
->image
.image
.num_sections
; i
++) {
440 struct imagesection
*section
= &run
->image
.image
.sections
[i
];
442 if (section
->size
== 0)
445 if (!(section
->flags
& ESP_IMAGE_ELF_PHF_EXEC
)) {
446 LOG_DEBUG("addr " TARGET_ADDR_FMT
", sz %d, flags %" PRIx64
, section
->base_address
, section
->size
,
448 /* target_alloc_working_area() aligns the whole working area size to 4-byte boundary.
449 We alloc one area for both DATA and BSS, so align each of them ourselves. */
450 uint32_t data_sec_sz
= ALIGN_UP(section
->size
, 4);
451 LOG_DEBUG("DATA sec size %" PRIu32
" -> %" PRIu32
, section
->size
, data_sec_sz
);
452 uint32_t bss_sec_sz
= ALIGN_UP(run
->image
.bss_size
, 4);
453 LOG_DEBUG("BSS sec size %" PRIu32
" -> %" PRIu32
, run
->image
.bss_size
, bss_sec_sz
);
454 if (target_alloc_working_area(target
, data_sec_sz
+ bss_sec_sz
, &run
->stub
.data
) != ERROR_OK
) {
455 LOG_ERROR("no working area available, can't alloc space for stub data!");
456 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
459 if (section
->base_address
== 0) {
460 section
->base_address
= run
->stub
.data
->address
;
461 /* sanity check, stub is compiled to be run from working area */
462 } else if (run
->stub
.data
->address
!= section
->base_address
) {
463 LOG_ERROR("working area " TARGET_ADDR_FMT
464 " and stub data section " TARGET_ADDR_FMT
465 " address mismatch!",
466 section
->base_address
,
467 run
->stub
.data
->address
);
472 retval
= load_section_from_image(target
, run
, i
, false);
473 if (retval
!= ERROR_OK
)
479 if (run
->stub
.stack_addr
== 0 && run
->stack_size
> 0) {
480 /* allocate stack in data working area */
481 if (target_alloc_working_area(target
, run
->stack_size
, &run
->stub
.stack
) != ERROR_OK
) {
482 LOG_ERROR("no working area available, can't alloc stub stack!");
483 retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
486 run
->stub
.stack_addr
= run
->stub
.stack
->address
+ run
->stack_size
;
489 if (duration_measure(&algo_time
) != 0) {
490 LOG_ERROR("Failed to stop algo run measurement!");
494 LOG_DEBUG("Stub loaded in %g ms", duration_elapsed(&algo_time
) * 1000);
498 esp_algorithm_unload_func_image(target
, run
);
502 int esp_algorithm_unload_func_image(struct target
*target
, struct esp_algorithm_run_data
*run
)
507 target_free_all_working_areas(target
);
509 run
->stub
.tramp
= NULL
;
510 run
->stub
.stack
= NULL
;
511 run
->stub
.code
= NULL
;
512 run
->stub
.data
= NULL
;
513 run
->stub
.padding
= NULL
;
518 int esp_algorithm_exec_func_image_va(struct target
*target
,
519 struct esp_algorithm_run_data
*run
,
523 if (!run
|| !run
->image
.image
.start_address_set
|| run
->image
.image
.start_address
== 0)
526 return esp_algorithm_run_image(target
, run
, num_args
, ap
);
529 int esp_algorithm_load_onboard_func(struct target
*target
, target_addr_t func_addr
, struct esp_algorithm_run_data
*run
)
532 const uint8_t *tramp
= NULL
;
534 struct duration algo_time
;
536 if (!run
|| !run
->hw
)
539 if (duration_start(&algo_time
) != 0) {
540 LOG_ERROR("Failed to start algo time measurement!");
544 if (run
->hw
->stub_tramp_get
) {
545 tramp
= run
->hw
->stub_tramp_get(target
, &tramp_sz
);
550 if (tramp_sz
> run
->on_board
.code_buf_size
) {
551 LOG_ERROR("Stub tramp size %zu bytes exceeds target buf size %d bytes!",
552 tramp_sz
, run
->on_board
.code_buf_size
);
553 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
556 if (run
->stack_size
> run
->on_board
.min_stack_size
) {
557 LOG_ERROR("Algorithm stack size not fit into the allocated target stack!");
561 run
->stub
.stack_addr
= run
->on_board
.min_stack_addr
+ run
->stack_size
;
562 run
->stub
.tramp_addr
= run
->on_board
.code_buf_addr
;
563 run
->stub
.tramp_mapped_addr
= run
->stub
.tramp_addr
;
564 run
->stub
.entry
= func_addr
;
567 res
= target_write_buffer(target
, run
->stub
.tramp_addr
, tramp_sz
, tramp
);
568 if (res
!= ERROR_OK
) {
569 LOG_ERROR("Failed to write stub jumper!");
570 esp_algorithm_unload_onboard_func(target
, run
);
575 if (duration_measure(&algo_time
) != 0) {
576 LOG_ERROR("Failed to stop algo run measurement!");
579 LOG_DEBUG("Stub loaded in %g ms", duration_elapsed(&algo_time
) * 1000);
584 int esp_algorithm_unload_onboard_func(struct target
*target
, struct esp_algorithm_run_data
*run
)
589 int esp_algorithm_exec_onboard_func_va(struct target
*target
,
590 struct esp_algorithm_run_data
*run
,
594 return esp_algorithm_run_debug_stub(target
, run
, num_args
, ap
);
Linking to existing account procedure
If you already have an account and want to add another login method
you
MUST first sign in with your existing account and
then change URL to read
https://review.openocd.org/login/?link
to get to this page again but this time it'll work for linking. Thank you.
SSH host keys fingerprints
1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=.. |
|+o.. . |
|*.o . . |
|+B . . . |
|Bo. = o S |
|Oo.+ + = |
|oB=.* = . o |
| =+=.+ + E |
|. .=o . o |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)