Enable MQTTS on esp client and remove useless folder

This commit is contained in:
2026-01-04 19:28:36 +01:00
parent 90db239c21
commit 16f354ad60
19888 changed files with 46 additions and 6994315 deletions

View File

@@ -177,11 +177,55 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
}
}
static const char *root_ca_pem =
"-----BEGIN CERTIFICATE-----\n"
"MIIGKTCCBBGgAwIBAgIUJ9ZZQT+MGcSfu0GC1Srjt9cMAKswDQYJKoZIhvcNAQEL\n"
"BQAwgaMxCzAJBgNVBAYTAkJFMREwDwYDVQQIDAhMacODwqhnZTERMA8GA1UEBwwI\n"
"TGnDg8KoZ2UxDTALBgNVBAoMBEhFUEwxGDAWBgNVBAsMD0N1c3RvbWVyIHN5c3Rl\n"
"bTEXMBUGA1UEAwwOMTkyLjE2OC4xNS4xMTkxLDAqBgkqhkiG9w0BCQEWHWxhdXJl\n"
"bnQuY3JlbWFAc3R1ZGVudC5oZXBsLmJlMB4XDTI2MDEwNDE2MTEwN1oXDTM2MDEw\n"
"MjE2MTEwN1owgaMxCzAJBgNVBAYTAkJFMREwDwYDVQQIDAhMacODwqhnZTERMA8G\n"
"A1UEBwwITGnDg8KoZ2UxDTALBgNVBAoMBEhFUEwxGDAWBgNVBAsMD0N1c3RvbWVy\n"
"IHN5c3RlbTEXMBUGA1UEAwwOMTkyLjE2OC4xNS4xMTkxLDAqBgkqhkiG9w0BCQEW\n"
"HWxhdXJlbnQuY3JlbWFAc3R1ZGVudC5oZXBsLmJlMIICIjANBgkqhkiG9w0BAQEF\n"
"AAOCAg8AMIICCgKCAgEApHwj/AcN3Y8L2KzJGnIxspZ13sjQS5U2iPf6VgNPpnvX\n"
"rkfvmCL8uRnZbB9Iruq8r5T6J4XMw7vt6wLoc+DYlnMnPe6RLF2aS5KrS5/1MAto\n"
"RDmx799MvnaBSikLVINPiKBx+3YjgNkuy03ICbCkX86lX3SHyey4fVVyzgIg/b/q\n"
"CYtNTQnH/l6l2CFO7dT+R/w9rkuNVUC+KogtAg/NAHVVlfHKhLr8CI/ox74PUb5N\n"
"6xVkRYZtMplY5gKI/FrMOLp8HhswUxrQf7HKKPIR7yBNVdPwmavUKm4Cv13e2C5b\n"
"PwAY9eGTl/qYUTiSFsRxISoxxC7UPygmWxO4HTuxxguBmHc75nZ37Zhku7DhhurM\n"
"rh17MehhkO4kXIIuhjrraaeLbtNVkzmTd3kPcS+4+2LiyXyhQg+Z15TgJG4kQGQg\n"
"7xpSW0+xMKAKqi36SHv/N6epYxzf7P7xlcnc6/Fhb/Rq0wRXxub/wzUd6JsJb+QF\n"
"fxy8DGjXGj1FkF5pDTwZQaKd3Ytlt101mEDOf7iL1zZ6c+P32o31n67ogFIZROUH\n"
"5HEoNweO20Dq+tZiX1+ui1ze3rsISjKKo0r7MaDfsXNwaIDOqAZVBiKexUeIvJln\n"
"pbvb9cTxw3Uawv0QL5maQwSEXYFofHV0EJ5fn0VIiXfQ/DvOHoDi0PiiNcOacFsC\n"
"AwEAAaNTMFEwHQYDVR0OBBYEFGJ5GnHpD6/ykRscKImhlxU85DttMB8GA1UdIwQY\n"
"MBaAFGJ5GnHpD6/ykRscKImhlxU85DttMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\n"
"hvcNAQELBQADggIBAGfkVvGolMh2ehX+XwJo0hp16k5ahKMTitBDWshzyiD61Kz7\n"
"7ahfx+2D4oB/GZ89iGr4QWZlPEULE+VqZQCFySD3XTV7IbNqGjd1hSmRZmfYuabn\n"
"Um2iBVewasHj5+nVs50/R1tawYuh6+3aI75E2SPD8u/PMKnhXYgtH/h9QfQqpTL6\n"
"qv+FuAq28Nh3pusEGA0hPID36vi6oV8pGpUjLo1dWSZkBGgdIq/+FRygSIkbYxIk\n"
"ZwjefpfNLAbM3SyHnsNh35zN59h7Z8uv1pm9irSNjQbiiYVEplq4jHKdmRf6GFFD\n"
"lwVDkZbvath8MJO3soQDVdfksnFYpNNN1ncFfrkFsUx0ggnyqWO2g0Ynjte3rrmX\n"
"facQ+dJHrhANic5c+GWzsX7ODdok3gV09InKAjcoWVvSjDalsaueInnGTM31z+Ko\n"
"bJNMHkkNv1c1LH1p0G2Xpj3EnWcBmpbNP0Qb0tdtMWLm0eU9T1ATPnw+DpiQyG6d\n"
"iiKb31wyzL8KBfGIHK1G1lQKoMrSrnab/Rk95x/LWQwu9bHOlCwJ0zlgDIc7Ebpx\n"
"GSUJaXGWISrVhyN2s/B4NK9Ykn5VDcIEn7Y+a78OO84uWl840wZXzjKJMkxFPoJ2\n"
"fQ0p6wmjGuqEZw9Kx8ogL5rV+kCJBLZRtFH57/j13MGvRpAYA87bKDgR8jNP\n"
"-----END CERTIFICATE-----\n";
static esp_mqtt_client_handle_t client = NULL;
void mqtt_init(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = "mqtt://192.168.15.119:1883",
.broker = {
.address.uri = "mqtts://192.168.15.119:8883",
.verification = {
.certificate = root_ca_pem,
.certificate_len = 0,
.skip_cert_common_name_check = false,
}
},
.credentials = {
.username = "device",
.authentication = {
@@ -215,7 +259,7 @@ void send_gps_data(const char *topic, const char *json)
return;
}
int msg_id = esp_mqtt_client_publish(client, topic, json, 0, 1, 0);
int msg_id = esp_mqtt_client_publish(client, topic, json, 0, 2, 0);
if (msg_id < 0) {
ESP_LOGE(TAG, "Failed to publish GPS");
} else {

View File

@@ -1,164 +0,0 @@
# Core Components
## Overview
This document contains details about what the core components are, what they contain, and how they are organized.
## Organization
The core components are organized into two groups.
The first group (referred to as `G0`) includes `hal`, `arch` (where `arch` is either `riscv` or `xtensa` depending on the chip), `esp_rom`, `esp_common`, and `soc`. This group contains information about and provides low-level access to the underlying hardware. In the case of `esp_common`, it contains hardware-agnostic code and utilities. These components may have dependencies on each other within the group, but outside dependencies should be minimized. The reason for this approach is that these components are fundamental, and many other components may require them. Ideally, the dependency relationship only goes one way, making it easier for this group to be usable in other projects.
The second group (referred to as `G1`) operates at a higher level than the first group. `G1` includes the components `esp_hw_support`, `esp_system`, `esp_libc`, `spi_flash`, `freertos`, `log`, and `heap`. Like the first group, circular dependencies within this group are allowed, and these components can have dependencies on the first group. G1 components represent essential software mechanisms for building other components.
## Descriptions
The following is a short description of the components mentioned above.
### `G0` Components
#### `hal`
Contains the hardware abstraction layer and low-level operation implementations for the various peripherals. The low-level functions assign meaningful names to register-level manipulations; the hardware abstraction provide operations one level above this, grouping these low-level functions
into routines that achieve a meaningful action or state of the peripheral.
Example:
- `spi_flash_ll_set_address` is a low-level function part of the hardware abstraction `spi_flash_hal_read_block`
#### `arch`
Contains low-level architecture operations and definitions, including those for customizations (can be thought of on the same level as the low-level functions of `hal`).
This can also contain files provided by the architecture vendor.
Example:
- `xt_set_exception_handler`
- `rv_utils_intr_enable`
- `ERI_PERFMON_MAX`
#### `esp_common`
Contains hardware-agnostic definitions, constants, macros, utilities, 'pure' and/or algorithmic functions that is usable by all other components (that is, barring there being a more appropriate component to put them in).
Example:
- `BIT(nr)` and other bit manipulation utilities in the future
- `IDF_DEPRECATED(REASON)`
- `ESP_IDF_VERSION_MAJOR`
#### `soc`
Contains description of the underlying hardware: register structure, addresses, pins, capabilities, etc.
Example:
- `DR_REG_DPORT_BASE`
- `SOC_MCPWM_SUPPORTED`
- `uart_dev_s`
#### `esp_rom`
Contains headers, linker scripts, abstraction layer, patches, and other related files to ROM functions.
Example:
- `esp32.rom.eco3.ld`
- `rom/aes.h`
### `G1` Components
#### `spi_flash`
SPI flash device access implementation.
#### `freertos`
FreeRTOS port to targets supported by ESP-IDF.
#### `log`
Logging library.
#### `heap`
Heap implementation.
#### `esp_libc`
Some functions n the standard library are implemented here, especially those needing other `G1` components.
Example:
- `malloc` is implemented in terms of the component `heap`'s functions
- `gettimeofday` is implemented in terms of system time in `esp_system`
#### `esp_mm`
Memory management. Currently, this encompasses:
- Memory mapping for MMU supported memories
- Memory synchronisation via Cache
- Utils such as APIs to convert between virtual address and physical address
#### `esp_psram`
Contains implementation of PSRAM services
#### `esp_system`
Contains implementation of system services and controls system behavior. The implementations
here may take hardware resources and/or decide on a hardware state needed for support of a system service/feature/mechanism.
Currently, this encompasses the following, but not limited to:
- Startup and initialization
- Panic and debug
- Reset and reset reason
- Task and interrupt watchdogs
#### `esp_hw_support`
Contains implementations that provide hardware operations, arbitration, or resource sharing, especially those that
is used in the system. Unlike `esp_system`, implementations here do not decide on a hardware state or takes hardware resource, acting
merely as facilitator to hardware access. Currently, this encompasses the following, but not limited to:
- Interrupt allocation
- Sleep functions
- Memory functions (external SPIRAM, async memory, etc.)
- Clock and clock control
- Random generation
- CPU utilities
- MAC settings
### `esp_hw_support` vs `esp_system`
This section details list some implementations and the reason for placing it in either `esp_hw_support` or `esp_system`.
#### `task_wdt.c` (`esp_system`) vs `intr_alloc.c` (`esp_hw_support`)
The task watchdog fits the definition of taking and configuring hardware resources (wdt, interrupt) for implementation of a system service/mechanism.
This is in contrast with interrupt allocation that merely facilitates access to the underlying hardware for other implementations -
drivers, user code, and even the task watchdog mentioned previously!
#### `crosscore_int.c` (`esp_system`)
The current implementation of crosscore interrupts is tightly coupled with a number of interrupt reasons
associated with system services/mechanisms: REASON_YIELD (scheduler), REASON_FREQ_SWITCH (power management)
REASON_PRINT_BACKTRACE (panic and debug).
However, if an implementation exists that makes it possible to register an arbitrary interrupt reason - a
lower level inter-processor call if you will, then this implementation is a good candidate for `esp_hw_support`.
The current implementation in `esp_system` can then just register the interrupt reasons mentioned above.
#### `esp_mac.h`, `esp_chip_info.h`, `esp_random.h` (`esp_hw_support`)
The functions in these headers used to be in `esp_system.h`, but have been split-off.
The remaining functions in `esp_system.h` are those that deal with system behavior, such
as `esp_register_shutdown_handler`, or are proxy for other system components's APIs such as
`esp_get_free_heap_size`.
The functions split-off from `esp_system.h` are much more hardware manipulation oriented such as:
`esp_read_mac`, `esp_random` and `esp_chip_info`.

View File

@@ -1,41 +0,0 @@
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
if(CONFIG_ESP_TRACE_TRANSPORT_APPTRACE)
set(srcs
"app_trace.c"
"app_trace_util.c"
"host_file_io.c"
)
if(NOT CONFIG_APPTRACE_DEST_UART) # JTAG or None
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
list(APPEND srcs "port/xtensa/port_jtag.c")
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
list(APPEND srcs "port/riscv/port_jtag.c")
endif()
list(APPEND srcs "app_trace_membufs_proto.c")
endif()
if(NOT CONFIG_APPTRACE_DEST_JTAG) # UART or None
list(APPEND srcs "port/port_uart.c")
endif()
endif()
if(CONFIG_ESP_DEBUG_STUBS_ENABLE)
list(APPEND srcs "debug_stubs.c")
endif()
set(include_dirs "include")
set(priv_include_dirs "private_include" "port/include")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
PRIV_REQUIRES esp_driver_gptimer
REQUIRES esp_timer esp_driver_uart
LDFRAGMENTS linker.lf)

View File

@@ -1,134 +0,0 @@
menu "Application Level Tracing"
depends on ESP_TRACE_TRANSPORT_APPTRACE
choice APPTRACE_DESTINATION
prompt "Data Destination"
default APPTRACE_DEST_JTAG if !PM_ENABLE
default APPTRACE_DEST_UART if PM_ENABLE
help
Select destination for application trace: JTAG, UART, or both.
config APPTRACE_DEST_JTAG
bool "JTAG"
select APPTRACE_TRAX_ENABLE if IDF_TARGET_ARCH_XTENSA
depends on !PM_ENABLE
config APPTRACE_DEST_UART
bool "UART"
config APPTRACE_DEST_ALL
bool "All (runtime selection)"
help
Compile both JTAG and UART interfaces in advance (higher IRAM usage).
Allows runtime switching between JTAG and UART via esp_apptrace_get_user_params().
If esp_apptrace_get_user_params() is not provided by the
application, JTAG is used by default with the default
configuration defined in components/app_trace/include/esp_app_trace_config.h.
endchoice
config APPTRACE_BUF_SIZE
int "Size of the apptrace buffer"
depends on APPTRACE_DEST_JTAG && !APPTRACE_TRAX_ENABLE
default 16384
help
Size of the memory buffer for trace data in bytes.
config APPTRACE_DEST_UART_NUM
int "UART port number"
depends on APPTRACE_DEST_UART
range 0 1 if (SOC_UART_HP_NUM <= 2)
range 0 2 if (SOC_UART_HP_NUM <= 3)
range 0 4 if (SOC_UART_HP_NUM <= 5)
default 1
help
UART communication port number for the apptrace destination.
See UART documentation for available port numbers.
config APPTRACE_UART_TX_GPIO
int "UART TX on GPIO<num>"
depends on APPTRACE_DEST_UART
range 0 46
default 12
help
This GPIO is used for UART TX pin.
config APPTRACE_UART_RX_GPIO
int "UART RX on GPIO<num>"
depends on APPTRACE_DEST_UART
range 0 46
default 13
help
This GPIO is used for UART RX pin.
config APPTRACE_UART_BAUDRATE
int
prompt "UART baud rate" if APPTRACE_DEST_UART
depends on APPTRACE_DEST_UART
default 1000000
range 1200 8000000
range 1200 1000000
help
This baud rate is used for UART.
The app's maximum baud rate depends on the UART clock source. If Power Management is disabled,
the UART clock source is the APB clock and all baud rates in the available range will be sufficiently
accurate. If Power Management is enabled, REF_TICK clock source is used so the baud rate is divided
from 1MHz. Baud rates above 1Mbps are not possible and values between 500Kbps and 1Mbps may not be
accurate.
config APPTRACE_UART_TX_BUFF_SIZE
int
prompt "UART TX ring buffer size" if APPTRACE_DEST_UART
depends on APPTRACE_DEST_UART
default 4096
range 2048 32768
help
Size of the UART output ring buffer. Must be power of two.
This size related to the baudrate, system tick frequency and amount of data to transfer.
config APPTRACE_UART_TX_MSG_SIZE
int
prompt "UART TX message size" if APPTRACE_DEST_UART
depends on APPTRACE_DEST_UART
default 128
range 64 32768
help
Maximum size of the single message to transfer.
config APPTRACE_TRAX_ENABLE
bool
depends on IDF_TARGET_ARCH_XTENSA && !ESP32_TRAX && !ESP32S2_TRAX && !ESP32S3_TRAX
select ESP32_MEMMAP_TRACEMEM
select ESP32S2_MEMMAP_TRACEMEM
select ESP32S3_MEMMAP_TRACEMEM
select ESP32_MEMMAP_TRACEMEM_TWOBANKS
select ESP32S2_MEMMAP_TRACEMEM_TWOBANKS
select ESP32S3_MEMMAP_TRACEMEM_TWOBANKS
default n
help
Enables/disable TRAX tracing HW.
config APPTRACE_LOCK_ENABLE
bool "Internal Sync Lock Enable"
default n
help
Enables/disable application tracing module internal sync lock to prevent data corruption
when multiple tasks are writing to the same trace buffer.
Keep in mind this will slow down the trace data transfer to the host.
config APPTRACE_ONPANIC_HOST_FLUSH_TMO
int "Timeout for flushing last trace data to host on panic"
range -1 5000
default -1
help
Timeout for flushing last trace data to host in case of panic. In ms.
Use -1 to disable timeout and wait forever.
config APPTRACE_POSTMORTEM_FLUSH_THRESH
int "Threshold for flushing last trace data to host on panic"
range 0 16384
default 0
help
Threshold for flushing last trace data to host on panic in post-mortem mode.
This is minimal amount of data needed to perform flush. In bytes.
endmenu

View File

@@ -1,382 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#include <string.h>
#include "esp_cpu.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "esp_app_trace.h"
#include "esp_app_trace_port.h"
#include "esp_app_trace_types.h"
#include "esp_private/startup_internal.h"
#if CONFIG_ESP_CONSOLE_UART && CONFIG_APPTRACE_DEST_UART && (CONFIG_APPTRACE_DEST_UART_NUM == CONFIG_ESP_CONSOLE_UART_NUM)
#error "Application trace UART and console UART cannot use the same port number"
#endif
#define ESP_APPTRACE_MAX_VPRINTF_ARGS 256
#define ESP_APPTRACE_HOST_BUF_SIZE 256
const static char *TAG = "esp_apptrace";
/** tracing module internal data */
typedef struct {
esp_apptrace_hw_t *hw;
void *hw_data;
esp_apptrace_dest_t dest;
} esp_apptrace_channel_t;
static esp_apptrace_channel_t s_trace_ch;
static volatile int s_trace_ch_hw_initialized = 0;
esp_err_t esp_apptrace_init(const esp_apptrace_config_t *config)
{
__attribute__((unused)) void *hw_data = NULL;
if (esp_cpu_get_core_id() == 0) {
#if CONFIG_APPTRACE_DEST_JTAG
s_trace_ch.hw = esp_apptrace_jtag_hw_get(&hw_data);
s_trace_ch.hw_data = hw_data;
#elif CONFIG_APPTRACE_DEST_UART
const esp_apptrace_uart_config_t *uart_config = &config->dest_cfg.uart;
s_trace_ch.hw = esp_apptrace_uart_hw_get(uart_config->uart_num, &hw_data);
s_trace_ch.hw_data = hw_data;
#else // CONFIG_APPTRACE_DEST_ALL allows runtime selection between destinations
if (config->dest == ESP_APPTRACE_DEST_JTAG) {
s_trace_ch.hw = esp_apptrace_jtag_hw_get(&hw_data);
s_trace_ch.hw_data = hw_data;
} else if (config->dest == ESP_APPTRACE_DEST_UART) {
const esp_apptrace_uart_config_t *uart_config = &config->dest_cfg.uart;
s_trace_ch.hw = esp_apptrace_uart_hw_get(uart_config->uart_num, &hw_data);
s_trace_ch.hw_data = hw_data;
} else {
s_trace_ch.hw = NULL;
s_trace_ch.hw_data = NULL;
ESP_APPTRACE_LOGE("Invalid destination type (%d)!", config->dest);
return ESP_ERR_INVALID_ARG;
}
#endif
s_trace_ch.dest = config->dest;
s_trace_ch_hw_initialized = 1;
} else {
// There is NO guarantee that system init functions will execute on core 0 first
// So we need to wait for core 0 to set up the hardware interface
while (!s_trace_ch_hw_initialized) {
esp_rom_delay_us(10);
}
}
if (s_trace_ch.hw) {
int res = s_trace_ch.hw->init(s_trace_ch.hw_data, config);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to init trace channel HW interface (%d)!", res);
return res;
}
}
return ESP_OK;
}
esp_err_t esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size)
{
if (!buf || size == 0) {
return ESP_ERR_INVALID_ARG;
}
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (s_trace_ch.hw->down_buffer_config) {
s_trace_ch.hw->down_buffer_config(s_trace_ch.hw_data, buf, size);
}
return ESP_OK;
}
uint8_t *esp_apptrace_down_buffer_get(uint32_t *size, uint32_t user_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!size || *size == 0) {
return NULL;
}
if (!s_trace_ch.hw) {
return NULL;
}
if (!s_trace_ch.hw->get_down_buffer) {
return NULL;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
return s_trace_ch.hw->get_down_buffer(s_trace_ch.hw_data, size, &tmo);
}
esp_err_t esp_apptrace_down_buffer_put(uint8_t *ptr, uint32_t user_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!ptr) {
return ESP_ERR_INVALID_ARG;
}
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (!s_trace_ch.hw->get_down_buffer) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
return s_trace_ch.hw->put_down_buffer(s_trace_ch.hw_data, ptr, &tmo);
}
esp_err_t esp_apptrace_read(void *buf, uint32_t *size, uint32_t user_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!buf || !size || *size == 0) {
return ESP_ERR_INVALID_ARG;
}
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (!s_trace_ch.hw->get_down_buffer || !s_trace_ch.hw->put_down_buffer) {
return ESP_ERR_NOT_SUPPORTED;
}
//TODO: callback system
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
uint32_t act_sz = *size;
*size = 0;
uint8_t *ptr = s_trace_ch.hw->get_down_buffer(s_trace_ch.hw_data, &act_sz, &tmo);
if (ptr && act_sz > 0) {
ESP_APPTRACE_LOGD("Read %" PRIu32 " bytes from host", act_sz);
memcpy(buf, ptr, act_sz);
*size = act_sz;
return s_trace_ch.hw->put_down_buffer(s_trace_ch.hw_data, ptr, &tmo);
}
return ESP_ERR_TIMEOUT;
}
uint8_t *esp_apptrace_buffer_get(uint32_t size, uint32_t user_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (size == 0) {
return NULL;
}
if (!s_trace_ch.hw) {
return NULL;
}
if (!s_trace_ch.hw->get_up_buffer) {
return NULL;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
return s_trace_ch.hw->get_up_buffer(s_trace_ch.hw_data, size, &tmo);
}
esp_err_t esp_apptrace_buffer_put(uint8_t *ptr, uint32_t user_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!ptr) {
return ESP_ERR_INVALID_ARG;
}
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (!s_trace_ch.hw->put_up_buffer) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
return s_trace_ch.hw->put_up_buffer(s_trace_ch.hw_data, ptr, &tmo);
}
esp_err_t esp_apptrace_write(const void *data, uint32_t size, uint32_t user_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!data || size == 0) {
return ESP_ERR_INVALID_ARG;
}
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (!s_trace_ch.hw->get_up_buffer || !s_trace_ch.hw->put_up_buffer) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
uint8_t *ptr = s_trace_ch.hw->get_up_buffer(s_trace_ch.hw_data, size, &tmo);
if (!ptr) {
return ESP_ERR_NO_MEM;
}
// actually can be suspended here by higher prio tasks/ISRs
//TODO: use own memcpy with dead trace calls kick-off algo and tmo expiration check
memcpy(ptr, data, size);
// now indicate that this buffer is ready to be sent off to host
return s_trace_ch.hw->put_up_buffer(s_trace_ch.hw_data, ptr, &tmo);
}
int esp_apptrace_vprintf_to(uint32_t user_tmo, const char *fmt, va_list ap)
{
uint16_t nargs = 0;
uint8_t *pout, *p = (uint8_t *)fmt;
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!fmt) {
return -1;
}
if (!s_trace_ch.hw) {
return -1;
}
if (!s_trace_ch.hw->get_up_buffer || !s_trace_ch.hw->put_up_buffer) {
return -1;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, user_tmo);
ESP_APPTRACE_LOGD("fmt %p", fmt);
while ((p = (uint8_t *)strchr((char *)p, '%')) && nargs < ESP_APPTRACE_MAX_VPRINTF_ARGS) {
p++;
if (*p != '%' && *p != 0) {
nargs++;
}
}
ESP_APPTRACE_LOGD("nargs = %d", nargs);
if (p) {
ESP_APPTRACE_LOGE("Failed to store all printf args!");
}
pout = s_trace_ch.hw->get_up_buffer(s_trace_ch.hw_data, 1 + sizeof(char *) + nargs * sizeof(uint32_t), &tmo);
if (!pout) {
ESP_APPTRACE_LOGE("Failed to get buffer!");
return -1;
}
p = pout;
*pout = nargs;
pout++;
*(const char **)pout = fmt;
pout += sizeof(char *);
while (nargs-- > 0) {
uint32_t arg = va_arg(ap, uint32_t);
*(uint32_t *)pout = arg;
pout += sizeof(uint32_t);
ESP_APPTRACE_LOGD("arg %" PRIx32, arg);
}
int ret = s_trace_ch.hw->put_up_buffer(s_trace_ch.hw_data, p, &tmo);
if (ret != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to put printf buf (%d)!", ret);
return -1;
}
return (pout - p);
}
int esp_apptrace_vprintf(const char *fmt, va_list ap)
{
return esp_apptrace_vprintf_to(0, fmt, ap);
}
esp_err_t esp_apptrace_flush_nolock(uint32_t min_sz, uint32_t usr_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (!s_trace_ch.hw->flush_up_buffer_nolock) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, usr_tmo);
return s_trace_ch.hw->flush_up_buffer_nolock(s_trace_ch.hw_data, min_sz, &tmo);
}
esp_err_t esp_apptrace_flush(uint32_t usr_tmo)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (!s_trace_ch.hw->flush_up_buffer) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, usr_tmo);
return s_trace_ch.hw->flush_up_buffer(s_trace_ch.hw_data, &tmo);
}
bool esp_apptrace_host_is_connected(void)
{
ESP_APPTRACE_LOGV("%s(): enter", __func__);
if (!s_trace_ch.hw) {
return false;
}
if (!s_trace_ch.hw->host_is_connected) {
return false;
}
return s_trace_ch.hw->host_is_connected(s_trace_ch.hw_data);
}
esp_apptrace_dest_t esp_apptrace_get_destination(void)
{
return s_trace_ch.dest;
}
esp_err_t esp_apptrace_set_header_size(esp_apptrace_header_size_t header_size)
{
if (!s_trace_ch.hw) {
return ESP_ERR_INVALID_STATE;
}
if (s_trace_ch.hw->set_header_size) {
s_trace_ch.hw->set_header_size(s_trace_ch.hw_data, header_size);
}
return ESP_OK;
}
/* If any trace library (sysview or external) is selected with the apptrace transport,
* initialization will be handled by the esp_trace component
*/
#if CONFIG_ESP_TRACE_LIB_NONE && CONFIG_ESP_TRACE_TRANSPORT_APPTRACE
esp_apptrace_config_t __attribute__((weak)) esp_apptrace_get_user_params(void)
{
esp_apptrace_config_t default_config = APPTRACE_CONFIG_DEFAULT();
return default_config;
}
ESP_SYSTEM_INIT_FN(apptrace_early_init, SECONDARY, ESP_SYSTEM_INIT_ALL_CORES, 115)
{
esp_apptrace_config_t config = esp_apptrace_get_user_params();
return esp_apptrace_init(&config);
}
#endif

View File

@@ -1,288 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#include <sys/param.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_cpu.h"
#include "esp_app_trace_membufs_proto.h"
/** Trace data header. Every user data chunk is prepended with this header.
* User allocates block with esp_apptrace_buffer_get and then fills it with data,
* in multithreading environment it can happen that tasks gets buffer and then gets interrupted,
* so it is possible that user data are incomplete when memory block is exposed to the host.
* In this case host SW will see that wr_sz < block_sz and will report error.
*/
typedef struct {
union {
struct {
uint8_t block_sz_8;
uint8_t wr_sz_8;
};
struct {
uint16_t block_sz_16;
uint16_t wr_sz_16;
};
};
} esp_tracedata_hdr_t;
/** TODO: docs
*/
typedef struct {
uint16_t block_sz; // size of allocated block for user data
} esp_hostdata_hdr_t;
#define ESP_APPTRACE_INBLOCK_MARKER(_hw_data_) \
((_hw_data_)->state.markers[(_hw_data_)->state.in_block % 2])
#define ESP_APPTRACE_INBLOCK(_hw_data_) \
(&(_hw_data_)->blocks[(_hw_data_)->state.in_block % 2])
const static char *TAG = "esp_apptrace";
static uint32_t esp_apptrace_membufs_down_buffer_write_nolock(esp_apptrace_membufs_proto_data_t *proto,
uint8_t *data, uint32_t size);
esp_err_t esp_apptrace_membufs_init(esp_apptrace_membufs_proto_data_t *proto,
const esp_apptrace_mem_block_t blocks_cfg[2])
{
// disabled by default
esp_apptrace_rb_init(&proto->rb_down, NULL, 0);
// membufs proto init
for (unsigned int i = 0; i < 2; i++) {
proto->blocks[i].start = blocks_cfg[i].start;
proto->blocks[i].sz = blocks_cfg[i].sz;
proto->state.markers[i] = 0;
}
proto->state.in_block = 0;
return ESP_OK;
}
void esp_apptrace_membufs_down_buffer_config(esp_apptrace_membufs_proto_data_t *data, uint8_t *buf, uint32_t size)
{
esp_apptrace_rb_init(&data->rb_down, buf, size);
}
// assumed to be protected by caller from multi-core/thread access
static esp_err_t esp_apptrace_membufs_swap(esp_apptrace_membufs_proto_data_t *proto)
{
int prev_block_num = proto->state.in_block % 2;
int new_block_num = prev_block_num ? (0) : (1);
esp_err_t res = proto->hw->swap_start(proto->state.in_block);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to swap to new block: %d", res);
return res;
}
proto->state.markers[new_block_num] = 0;
// switch to new block
proto->state.in_block++;
proto->hw->swap(new_block_num, proto->state.markers[prev_block_num]);
// handle data from host
esp_hostdata_hdr_t *hdr = (esp_hostdata_hdr_t *)proto->blocks[new_block_num].start;
// ESP_APPTRACE_LOGV("Host data %d, sz %d @ %p", proto->hw->host_data_pending(), hdr->block_sz, hdr);
if (proto->hw->host_data_pending() && hdr->block_sz > 0) {
// TODO: add support for multiple blocks from host, currently there is no need for that
uint8_t *p = proto->blocks[new_block_num].start + proto->blocks[new_block_num].sz;
ESP_APPTRACE_LOGD("Recvd %" PRIu16 " bytes from host (@ %p) [%x %x %x %x %x %x %x %x .. %x %x %x %x %x %x %x %x]",
hdr->block_sz, proto->blocks[new_block_num].start,
*(proto->blocks[new_block_num].start + 0), *(proto->blocks[new_block_num].start + 1),
*(proto->blocks[new_block_num].start + 2), *(proto->blocks[new_block_num].start + 3),
*(proto->blocks[new_block_num].start + 4), *(proto->blocks[new_block_num].start + 5),
*(proto->blocks[new_block_num].start + 6), *(proto->blocks[new_block_num].start + 7),
*(p - 8), *(p - 7), *(p - 6), *(p - 5), *(p - 4), *(p - 3), *(p - 2), *(p - 1));
uint32_t sz = esp_apptrace_membufs_down_buffer_write_nolock(proto, (uint8_t *)(hdr + 1), hdr->block_sz);
if (sz != hdr->block_sz) {
ESP_APPTRACE_LOGE("Failed to write %" PRIu32 " bytes to down buffer (%" PRIu16 " %" PRIu32 ")!",
hdr->block_sz - sz, hdr->block_sz, sz);
}
hdr->block_sz = 0;
}
proto->hw->swap_end(proto->state.in_block, proto->state.markers[prev_block_num]);
return res;
}
static esp_err_t esp_apptrace_membufs_swap_waitus(esp_apptrace_membufs_proto_data_t *proto, esp_apptrace_tmo_t *tmo)
{
int res;
while ((res = esp_apptrace_membufs_swap(proto)) != ESP_OK) {
res = esp_apptrace_tmo_check(tmo);
if (res != ESP_OK) {
break;
}
#if CONFIG_IDF_TARGET_ESP32S3
/*
* ESP32S3 has a serious data corruption issue with the transferred data to host.
* This delay helps reduce the failure rate by temporarily reducing heavy memory writes
* from RTOS-level tracing and giving OpenOCD more time to read trace memory before
* the current thread continues execution. While this doesn't completely prevent
* memory access from other threads/cores/ISRs, it has shown to significantly improve
* reliability when combined with CRC checks in OpenOCD. In practice, this reduces the
* number of retries needed to read an entire block without corruption.
*/
esp_rom_delay_us(100);
#endif
}
return res;
}
uint8_t *esp_apptrace_membufs_down_buffer_get(esp_apptrace_membufs_proto_data_t *proto,
uint32_t *size, esp_apptrace_tmo_t *tmo)
{
uint8_t *ptr = NULL;
while (1) {
uint32_t sz = esp_apptrace_rb_read_size_get(&proto->rb_down);
if (sz != 0) {
*size = MIN(*size, sz);
ptr = esp_apptrace_rb_consume(&proto->rb_down, *size);
if (!ptr) {
assert(false && "Failed to consume bytes from down buffer!");
}
break;
}
// may need to flush
if (proto->hw->host_data_pending()) {
ESP_APPTRACE_LOGD("force flush");
int res = esp_apptrace_membufs_swap_waitus(proto, tmo);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to switch to another block to recv data from host!");
/*do not return error because data can be in down buffer already*/
}
} else {
// check tmo only if there is no data from host
int res = esp_apptrace_tmo_check(tmo);
if (res != ESP_OK) {
return NULL;
}
}
}
return ptr;
}
esp_err_t esp_apptrace_membufs_down_buffer_put(esp_apptrace_membufs_proto_data_t *proto,
uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
/* nothing todo */
return ESP_OK;
}
static uint32_t esp_apptrace_membufs_down_buffer_write_nolock(esp_apptrace_membufs_proto_data_t *proto,
uint8_t *data, uint32_t size)
{
uint32_t total_sz = 0;
while (total_sz < size) {
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock WRS %" PRIu32 "-%" PRIu32 "-%" PRIu32 " %" PRIu32, proto->rb_down.wr, proto->rb_down.rd,
proto->rb_down.cur_size, size);
uint32_t wr_sz = esp_apptrace_rb_write_size_get(&proto->rb_down);
if (wr_sz == 0) {
break;
}
if (wr_sz > size - total_sz) {
wr_sz = size - total_sz;
}
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock wr %" PRIu32, wr_sz);
uint8_t *ptr = esp_apptrace_rb_produce(&proto->rb_down, wr_sz);
if (!ptr) {
assert(false && "Failed to produce bytes to down buffer!");
}
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock wr %" PRIu32 " to %p from %p", wr_sz, ptr, data + total_sz + wr_sz);
memcpy(ptr, data + total_sz, wr_sz);
total_sz += wr_sz;
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock wr %" PRIu32 "/%" PRIu32 "", wr_sz, total_sz);
}
return total_sz;
}
static inline uint32_t esp_apptrace_membufs_usr_data_len_max(esp_apptrace_membufs_proto_data_t *proto)
{
return proto->header_size == ESP_APPTRACE_HEADER_SIZE_32 ?
ESP_APPTRACE_INBLOCK(proto)->sz - ESP_APPTRACE_HEADER_SIZE_32 : 255;
}
uint8_t *esp_apptrace_membufs_up_buffer_get(esp_apptrace_membufs_proto_data_t *proto,
uint32_t size, esp_apptrace_tmo_t *tmo)
{
if (size > esp_apptrace_membufs_usr_data_len_max(proto)) {
ESP_APPTRACE_LOGE("Too large user data size %" PRIu32 "!", size);
return NULL;
}
if (ESP_APPTRACE_INBLOCK_MARKER(proto) + size + proto->header_size > ESP_APPTRACE_INBLOCK(proto)->sz) {
int res = esp_apptrace_membufs_swap_waitus(proto, tmo);
if (res != ESP_OK) {
return NULL;
}
}
uint8_t *buf_ptr = ESP_APPTRACE_INBLOCK(proto)->start + ESP_APPTRACE_INBLOCK_MARKER(proto);
// update cur block marker
proto->state.markers[proto->state.in_block % 2] += size + proto->header_size;
// update header
esp_tracedata_hdr_t *hdr = (esp_tracedata_hdr_t *)buf_ptr;
if (proto->header_size == ESP_APPTRACE_HEADER_SIZE_32) {
hdr->block_sz_16 = (esp_cpu_get_core_id() << 15) | size;
hdr->wr_sz_16 = 0;
} else {
hdr->block_sz_8 = size;
hdr->wr_sz_8 = 0;
}
ESP_APPTRACE_LOGD("Got %" PRIu32 " bytes from block", size);
return buf_ptr + proto->header_size;
}
esp_err_t esp_apptrace_membufs_up_buffer_put(esp_apptrace_membufs_proto_data_t *proto,
uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
// update header
esp_tracedata_hdr_t *hdr = (esp_tracedata_hdr_t *)(ptr - proto->header_size);
if (proto->header_size == ESP_APPTRACE_HEADER_SIZE_32) {
hdr->wr_sz_16 = hdr->block_sz_16;
} else {
hdr->wr_sz_8 = hdr->block_sz_8;
}
// TODO: mark block as busy in order not to reuse it for other tracing calls until it is completely written
// TODO: avoid potential situation when all memory is consumed by low prio tasks which can not complete writing due to
// higher prio tasks and the latter can not allocate buffers at all
// this is abnormal situation can be detected on host which will receive only uncompleted buffers
// workaround: use own memcpy which will kick-off dead tracing calls
return ESP_OK;
}
esp_err_t esp_apptrace_membufs_flush_nolock(esp_apptrace_membufs_proto_data_t *proto,
uint32_t min_sz, esp_apptrace_tmo_t *tmo)
{
int res = ESP_OK;
if (ESP_APPTRACE_INBLOCK_MARKER(proto) < min_sz) {
ESP_APPTRACE_LOGI("Ignore flush request for min %" PRIu32 " bytes. Bytes in block: %" PRIu32, min_sz, ESP_APPTRACE_INBLOCK_MARKER(proto));
return ESP_OK;
}
// switch block while size of data is more than min size
while (ESP_APPTRACE_INBLOCK_MARKER(proto) > min_sz) {
ESP_APPTRACE_LOGD("Try to flush %" PRIu32 " bytes", ESP_APPTRACE_INBLOCK_MARKER(proto));
res = esp_apptrace_membufs_swap_waitus(proto, tmo);
if (res != ESP_OK) {
if (res == ESP_ERR_TIMEOUT) {
ESP_APPTRACE_LOGW("Failed to switch to another block in %" PRId32 " us!", (int32_t)tmo->elapsed);
} else {
ESP_APPTRACE_LOGE("Failed to switch to another block, res: %d", res);
}
return res;
}
}
return res;
}

View File

@@ -1,190 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
//
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_app_trace_util.h"
#include "sdkconfig.h"
#define ESP_APPTRACE_PRINT_LOCK 0
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////// Locks /////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#if ESP_APPTRACE_PRINT_LOCK
static esp_apptrace_lock_t s_log_lock = { .mux = portMUX_INITIALIZER_UNLOCKED };
#endif
int esp_apptrace_log_lock(void)
{
#if ESP_APPTRACE_PRINT_LOCK
esp_apptrace_tmo_t tmo;
esp_apptrace_tmo_init(&tmo, ESP_APPTRACE_TMO_INFINITE);
int ret = esp_apptrace_lock_take(&s_log_lock, &tmo);
return ret;
#else
return 0;
#endif
}
void esp_apptrace_log_unlock(void)
{
#if ESP_APPTRACE_PRINT_LOCK
esp_apptrace_lock_give(&s_log_lock);
#endif
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////// TIMEOUT /////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo)
{
if (tmo->tmo != (int64_t) -1) {
tmo->elapsed = esp_timer_get_time() - tmo->start;
if (tmo->elapsed >= tmo->tmo) {
return ESP_ERR_TIMEOUT;
}
}
return ESP_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////// LOCK ////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void esp_apptrace_lock_init(esp_apptrace_lock_t *lock)
{
portMUX_INITIALIZE(&lock->mux);
lock->int_state = 0;
}
esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, esp_apptrace_tmo_t *tmo)
{
esp_err_t ret;
while (1) {
// Try enter a critical section (i.e., take the spinlock) with 0 timeout
if (portTRY_ENTER_CRITICAL(&(lock->mux), 0) == pdTRUE) {
return ESP_OK;
}
// Failed to enter the critical section, so interrupts are still enabled. Check if we have timed out.
ret = esp_apptrace_tmo_check(tmo);
if (ret != ESP_OK) {
break; // Timed out, exit now
}
// Haven't timed out, try again
}
return ret;
}
esp_err_t esp_apptrace_lock_give(esp_apptrace_lock_t *lock)
{
portEXIT_CRITICAL(&(lock->mux));
return ESP_OK;
}
///////////////////////////////////////////////////////////////////////////////
////////////////////////////// RING BUFFER ////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
uint8_t *esp_apptrace_rb_produce(esp_apptrace_rb_t *rb, uint32_t size)
{
uint8_t *ptr = rb->data + rb->wr;
// check for available space
if (rb->rd <= rb->wr) {
// |?R......W??|
if (rb->wr + size >= rb->size) {
if (rb->rd == 0) {
return NULL; // cannot wrap wr
}
if (rb->wr + size == rb->size) {
rb->wr = 0;
} else {
// check if we can wrap wr earlier to get space for requested size
if (size > rb->rd - 1) {
return NULL; // cannot wrap wr
}
// shrink buffer a bit, full size will be restored at rd wrapping
rb->cur_size = rb->wr;
rb->wr = 0;
ptr = rb->data;
if (rb->rd == rb->cur_size) {
rb->rd = 0;
if (rb->cur_size < rb->size) {
rb->cur_size = rb->size;
}
}
rb->wr += size;
}
} else {
rb->wr += size;
}
} else {
// |?W......R??|
if (size > rb->rd - rb->wr - 1) {
return NULL;
}
rb->wr += size;
}
return ptr;
}
uint8_t *esp_apptrace_rb_consume(esp_apptrace_rb_t *rb, uint32_t size)
{
uint8_t *ptr = rb->data + rb->rd;
if (rb->rd <= rb->wr) {
// |?R......W??|
if (rb->rd + size > rb->wr) {
return NULL;
}
rb->rd += size;
} else {
// |?W......R??|
if (rb->rd + size > rb->cur_size) {
return NULL;
} else if (rb->rd + size == rb->cur_size) {
// restore full size usage
if (rb->cur_size < rb->size) {
rb->cur_size = rb->size;
}
rb->rd = 0;
} else {
rb->rd += size;
}
}
return ptr;
}
uint32_t esp_apptrace_rb_read_size_get(esp_apptrace_rb_t *rb)
{
uint32_t size = 0;
if (rb->rd <= rb->wr) {
// |?R......W??|
size = rb->wr - rb->rd;
} else {
// |?W......R??|
size = rb->cur_size - rb->rd;
}
return size;
}
uint32_t esp_apptrace_rb_write_size_get(esp_apptrace_rb_t *rb)
{
uint32_t size = 0;
if (rb->rd <= rb->wr) {
// |?R......W??|
size = rb->size - rb->wr;
if (size && rb->rd == 0) {
size--;
}
} else {
// |?W......R??|
size = rb->rd - rb->wr - 1;
}
return size;
}

View File

@@ -1,101 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// This module implements debug/trace stubs. The stub is a piece of special code which can invoked by OpenOCD
// Currently one stub is used for GCOV functionality
//
#include "esp_private/startup_internal.h"
#include "esp_dbg_stubs.h"
#include "esp_attr.h"
/*
Debug stubs is actually a table of 4-byte entries. Every entry is equal to zero or must contain meaningful data.
The first entry is a service one and has the followinf format:
- tramp_addr, 4 bytes; Address of buffer for trampoline/code. Max size is ESP_DBG_STUBS_CODE_BUF_SIZE.
- min_stack_addr, 4 bytes; Start of the buffer for minimal onboard stack or data. Max size is ESP_DBG_STUBS_STACK_MIN_SIZE.
- data_alloc, 4 bytes; Address of function to allocate memory on target.
- data_free, 4 bytes; Address of function to free target memory.
This entry is used by OpenOCD code to invoke other stub entries and allocate memory for them.
*/
#include "esp_log.h"
const static char *TAG = "esp_dbg_stubs";
#define ESP_DBG_STUBS_CODE_BUF_SIZE 32
#define ESP_DBG_STUBS_STACK_MIN_SIZE 2048
#define DBG_STUB_TRAMP_ATTR IRAM_ATTR
static struct {
uint32_t tramp_addr;
uint32_t min_stack_addr; // minimal stack addr
uint32_t data_alloc;
uint32_t data_free;
} s_dbg_stubs_ctl_data;
static uint32_t s_stub_entry[ESP_DBG_STUB_ENTRY_MAX];
static uint8_t s_stub_min_stack[ESP_DBG_STUBS_STACK_MIN_SIZE];
static DBG_STUB_TRAMP_ATTR uint8_t s_stub_code_buf[ESP_DBG_STUBS_CODE_BUF_SIZE];
extern void esp_dbg_stubs_ll_init(void *stub_table_addr);
// TODO: all called funcs should be in IRAM to work with disabled flash cache
static void * esp_dbg_stubs_data_alloc(uint32_t size)
{
ESP_LOGV(TAG, "%s %"PRIu32, __func__, size);
void *p = malloc(size);
ESP_LOGV(TAG, "%s EXIT %p", __func__, p);
return p;
}
static void esp_dbg_stubs_data_free(void *addr)
{
ESP_LOGV(TAG, "%s %p", __func__, addr);
free(addr);
}
void esp_dbg_stubs_init(void)
{
s_dbg_stubs_ctl_data.tramp_addr = (uint32_t)s_stub_code_buf;
s_dbg_stubs_ctl_data.min_stack_addr = (uint32_t)s_stub_min_stack;
s_dbg_stubs_ctl_data.data_alloc = (uint32_t)esp_dbg_stubs_data_alloc;
s_dbg_stubs_ctl_data.data_free = (uint32_t)esp_dbg_stubs_data_free;
s_stub_entry[ESP_DBG_STUB_MAGIC_NUM] = ESP_DBG_STUB_MAGIC_NUM_VAL;
s_stub_entry[ESP_DBG_STUB_TABLE_SIZE] = ESP_DBG_STUB_ENTRY_MAX;
s_stub_entry[ESP_DBG_STUB_CONTROL_DATA] = (uint32_t)&s_dbg_stubs_ctl_data;
esp_dbg_stubs_ll_init(s_stub_entry);
}
// TODO: add lock mechanism. Not now but in the future ESP_DBG_STUB_ENTRY_CAPABILITIES can be set from different places.
esp_err_t esp_dbg_stub_entry_set(esp_dbg_stub_id_t id, uint32_t entry)
{
if (id < ESP_DBG_STUB_ENTRY_FIRST || id >= ESP_DBG_STUB_ENTRY_MAX) {
ESP_LOGE(TAG, "Invalid stub id %d!", id);
return ESP_ERR_INVALID_ARG;
}
s_stub_entry[id] = entry;
return ESP_OK;
}
esp_err_t esp_dbg_stub_entry_get(esp_dbg_stub_id_t id, uint32_t *entry)
{
if (id < ESP_DBG_STUB_ENTRY_FIRST || id >= ESP_DBG_STUB_ENTRY_MAX) {
ESP_LOGE(TAG, "Invalid stub id %d!", id);
return ESP_ERR_INVALID_ARG;
}
*entry = s_stub_entry[id];
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(init_dbg_stubs, SECONDARY, BIT(0), 140)
{
esp_dbg_stubs_init();
return ESP_OK;
}

View File

@@ -1,396 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
//
// Hot It Works
// ************
//
// This module implements host file I/O protocol on top of apptrace module.
// The protocol is enough simple. It sends command with arguments to the host and receives response from it.
// Responses contains return values of respective file I/O API. This value is returned to the caller.
// Commands has the following format:
// * Header. See esp_apptrace_fcmd_hdr_t.
// * Operation arguments. See file operation helper structures below.
#include <string.h>
#include "esp_app_trace.h"
#include "esp_log.h"
const static char *TAG = "esp_host_file_io";
#define ESP_APPTRACE_FILE_CMD_FOPEN 0x0
#define ESP_APPTRACE_FILE_CMD_FCLOSE 0x1
#define ESP_APPTRACE_FILE_CMD_FWRITE 0x2
#define ESP_APPTRACE_FILE_CMD_FREAD 0x3
#define ESP_APPTRACE_FILE_CMD_FSEEK 0x4
#define ESP_APPTRACE_FILE_CMD_FTELL 0x5
#define ESP_APPTRACE_FILE_CMD_STOP 0x6 // indicates that there is no files to transfer
#define ESP_APPTRACE_FILE_CMD_FEOF 0x7
/** File operation header */
typedef struct {
uint8_t cmd; ///< Command ID
} esp_apptrace_fcmd_hdr_t;
/** Helper structure for fopen */
typedef struct {
const char *path;
uint16_t path_len;
const char *mode;
uint16_t mode_len;
} esp_apptrace_fopen_args_t;
/** Helper structure for fclose */
typedef struct {
void *file;
} esp_apptrace_fclose_args_t;
/** Helper structure for fwrite */
typedef struct {
void * buf;
size_t size;
void * file;
} esp_apptrace_fwrite_args_t;
/** Helper structure for fread */
typedef struct {
size_t size;
void * file;
} esp_apptrace_fread_args_t;
/** Helper structure for fseek */
typedef struct {
long offset;
int whence;
void * file;
} esp_apptrace_fseek_args_t;
/** Helper structure for feof */
typedef struct {
void *file;
} esp_apptrace_feof_args_t;
/** Helper structure for ftell */
typedef struct {
void *file;
} esp_apptrace_ftell_args_t;
static esp_err_t esp_apptrace_file_cmd_send(uint8_t cmd, void (*prep_args)(uint8_t *, void *), void *args, uint32_t args_len)
{
esp_err_t ret;
esp_apptrace_fcmd_hdr_t *hdr;
ESP_EARLY_LOGV(TAG, "%s %d", __func__, cmd);
uint8_t *ptr = esp_apptrace_buffer_get(sizeof(*hdr) + args_len, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo
if (ptr == NULL) {
return ESP_ERR_NO_MEM;
}
hdr = (esp_apptrace_fcmd_hdr_t *)ptr;
hdr->cmd = cmd;
if (prep_args) {
prep_args(ptr + sizeof(hdr->cmd), args);
}
// now indicate that this buffer is ready to be sent off to host
ret = esp_apptrace_buffer_put(ptr, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to put apptrace buffer (%d)!", ret);
return ret;
}
ret = esp_apptrace_flush(ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to flush apptrace buffer (%d)!", ret);
return ret;
}
return ESP_OK;
}
static esp_err_t esp_apptrace_file_rsp_recv(uint8_t *buf, uint32_t buf_len)
{
uint32_t tot_rd = 0;
while (tot_rd < buf_len) {
uint32_t rd_size = buf_len - tot_rd;
esp_err_t ret = esp_apptrace_read(buf + tot_rd, &rd_size, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read (%d)!", ret);
return ret;
}
ESP_EARLY_LOGV(TAG, "%s read %" PRIu32 " bytes", __FUNCTION__, rd_size);
tot_rd += rd_size;
}
return ESP_OK;
}
static void esp_apptrace_fopen_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fopen_args_t *args = priv;
memcpy(buf, args->path, args->path_len);
memcpy(buf + args->path_len, args->mode, args->mode_len);
}
void *esp_apptrace_fopen(const char *path, const char *mode)
{
esp_apptrace_fopen_args_t cmd_args;
ESP_EARLY_LOGV(TAG, "esp_apptrace_fopen '%s' '%s'", path, mode);
if (path == NULL || mode == NULL) {
return 0;
}
cmd_args.path = path;
cmd_args.path_len = strlen(path) + 1;
cmd_args.mode = mode;
cmd_args.mode_len = strlen(mode) + 1;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FOPEN, esp_apptrace_fopen_args_prepare,
&cmd_args, cmd_args.path_len + cmd_args.mode_len);
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return NULL;
}
// now read the answer
void *resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return NULL;
}
return resp;
}
static void esp_apptrace_fclose_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fclose_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
}
int esp_apptrace_fclose(void *stream)
{
esp_apptrace_fclose_args_t cmd_args;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FCLOSE, esp_apptrace_fclose_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return EOF;
}
// now read the answer
int resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return EOF;
}
return resp;
}
static void esp_apptrace_fwrite_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fwrite_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
memcpy(buf + sizeof(args->file), args->buf, args->size);
}
size_t esp_apptrace_fwrite(const void *ptr, size_t size, size_t nmemb, void *stream)
{
esp_apptrace_fwrite_args_t cmd_args;
ESP_EARLY_LOGV(TAG, "esp_apptrace_fwrite f %p l %d", stream, size * nmemb);
if (ptr == NULL) {
return 0;
}
cmd_args.buf = (void *)ptr;
cmd_args.size = size * nmemb;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FWRITE, esp_apptrace_fwrite_args_prepare,
&cmd_args, sizeof(cmd_args.file) + cmd_args.size);
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return 0;
}
// now read the answer
size_t resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return 0;
}
/* OpenOCD writes it like that:
* fwrite(buf, size, 1, file);
* So, if 1 was returned that means fwrite succeed
*/
return resp == 1 ? nmemb : 0;
}
static void esp_apptrace_fread_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fread_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
memcpy(buf + sizeof(args->file), &args->size, sizeof(args->size));
}
size_t esp_apptrace_fread(void *ptr, size_t size, size_t nmemb, void *stream)
{
esp_apptrace_fread_args_t cmd_args;
ESP_EARLY_LOGV(TAG, "esp_apptrace_fread f %p l %d", stream, size * nmemb);
if (ptr == NULL) {
return 0;
}
cmd_args.size = size * nmemb;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FREAD, esp_apptrace_fread_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return 0;
}
// now read the answer
size_t resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return 0;
}
if (resp == 0) {
return 0;
}
ret = esp_apptrace_file_rsp_recv(ptr, resp);
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read file data (%d)!", ret);
return 0;
}
/* OpenOCD reads it like that:
* fread(buf, 1 ,size, file);
* So, total read bytes count returns
*/
return resp / size; // return the number of items read
}
static void esp_apptrace_fseek_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fseek_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
memcpy(buf + sizeof(args->file), &args->offset, sizeof(args->offset));
memcpy(buf + sizeof(args->file) + sizeof(args->offset), &args->whence, sizeof(args->whence));
}
int esp_apptrace_fseek(void *stream, long offset, int whence)
{
esp_apptrace_fseek_args_t cmd_args;
ESP_EARLY_LOGV(TAG, "esp_apptrace_fseek f %p o 0x%lx w %d", stream, offset, whence);
cmd_args.file = stream;
cmd_args.offset = offset;
cmd_args.whence = whence;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FSEEK, esp_apptrace_fseek_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return -1;
}
// now read the answer
int resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return -1;
}
return resp;
}
static void esp_apptrace_ftell_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_ftell_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
}
int esp_apptrace_ftell(void *stream)
{
esp_apptrace_ftell_args_t cmd_args;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FTELL, esp_apptrace_ftell_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return -1;
}
// now read the answer
int resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return -1;
}
return resp;
}
int esp_apptrace_fstop(void)
{
ESP_EARLY_LOGV(TAG, "%s", __func__);
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_STOP, NULL, NULL, 0);
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send files transfer stop cmd (%d)!", ret);
}
return ret;
}
static void esp_apptrace_feof_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_feof_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
}
int esp_apptrace_feof(void *stream)
{
esp_apptrace_feof_args_t cmd_args;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(ESP_APPTRACE_FILE_CMD_FEOF, esp_apptrace_feof_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return EOF;
}
// now read the answer
int resp;
ret = esp_apptrace_file_rsp_recv((uint8_t *)&resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Failed to read response (%d)!", ret);
return EOF;
}
return resp;
}

View File

@@ -1,294 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_APP_TRACE_H_
#define ESP_APP_TRACE_H_
#include <stdarg.h>
#include "esp_err.h"
#include "esp_app_trace_config.h"
#include "esp_app_trace_util.h" // ESP_APPTRACE_TMO_INFINITE
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initializes application tracing module for the selected destination and configuration.
*
* @note Should be called before any esp_apptrace_xxx call.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_init(const esp_apptrace_config_t *config);
/**
* @brief Configures down buffer.
*
* @note Needs to be called before attempting to receive any data using esp_apptrace_down_buffer_get and
* esp_apptrace_read. This function does not protect internal data by lock.
*
* @param buf Address of buffer to use for down channel (host to target) data.
* @param size Size of the buffer.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size);
/**
* @brief Allocates buffer for trace data.
* Once the data in the buffer is ready to be sent, esp_apptrace_buffer_put must be called to indicate it.
*
* @param size Size of data to write to trace buffer.
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return non-NULL on success, otherwise NULL.
*/
uint8_t *esp_apptrace_buffer_get(uint32_t size, uint32_t tmo);
/**
* @brief Indicates that the data in the buffer is ready to be sent.
* This function is a counterpart of and must be preceded by esp_apptrace_buffer_get.
*
* @param ptr Address of trace buffer to release. Should be the value returned by call to esp_apptrace_buffer_get.
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_buffer_put(uint8_t *ptr, uint32_t tmo);
/**
* @brief Writes data to trace buffer.
*
* @param data Address of data to write to trace buffer.
* @param size Size of data to write to trace buffer.
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_write(const void *data, uint32_t size, uint32_t tmo);
/**
* @brief vprintf-like function to send log messages to host via specified HW interface.
*
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
* @param fmt Address of format string.
* @param ap List of arguments.
*
* @return Number of bytes written.
*/
int esp_apptrace_vprintf_to(uint32_t tmo, const char *fmt, va_list ap);
/**
* @brief vprintf-like function to send log messages to host.
*
* @param fmt Address of format string.
* @param ap List of arguments.
*
* @return Number of bytes written.
*/
int esp_apptrace_vprintf(const char *fmt, va_list ap);
/**
* @brief Flushes remaining data in trace buffer to host.
*
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_flush(uint32_t tmo);
/**
* @brief Flushes remaining data in trace buffer to host without locking internal data.
* This is a special version of esp_apptrace_flush which should be called from panic handler.
*
* @param min_sz Threshold for flushing data. If current filling level is above this value, data will be flushed. JTAG destinations only.
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_flush_nolock(uint32_t min_sz, uint32_t tmo);
/**
* @brief Reads host data from trace buffer.
*
* @param data Address of buffer to put data from trace buffer.
* @param size Pointer to store size of read data. Before call to this function pointed memory must hold requested size of data
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_read(void *data, uint32_t *size, uint32_t tmo);
/**
* @brief Retrieves incoming data buffer if any.
* Once data in the buffer is processed, esp_apptrace_down_buffer_put must be called to indicate it.
*
* @param size Address to store size of available data in down buffer. Must be initialized with requested value.
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return non-NULL on success, otherwise NULL.
*/
uint8_t *esp_apptrace_down_buffer_get(uint32_t *size, uint32_t tmo);
/**
* @brief Indicates that the data in the down buffer is processed.
* This function is a counterpart of and must be preceded by esp_apptrace_down_buffer_get.
*
* @param ptr Address of trace buffer to release. Should be the value returned by call to esp_apptrace_down_buffer_get.
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_down_buffer_put(uint8_t *ptr, uint32_t tmo);
/**
* @brief Checks whether host is connected.
*
* @return true if host is connected, otherwise false
*/
bool esp_apptrace_host_is_connected(void);
/**
* @brief Gets the destination of the application trace.
*
* @return The destination of the application trace.
*/
esp_apptrace_dest_t esp_apptrace_get_destination(void);
/**
* @brief Sets the header size of the application trace packet.
*
* @param header_size The header size to set.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_apptrace_set_header_size(esp_apptrace_header_size_t header_size);
/**
* @brief Opens file on host.
* This function has the same semantic as 'fopen' except for the first argument.
*
* @param path Path to file.
* @param mode Mode string. See fopen for details.
*
* @return non zero file handle on success, otherwise 0
*/
void *esp_apptrace_fopen(const char *path, const char *mode);
/**
* @brief Closes file on host.
* This function has the same semantic as 'fclose' except for the first argument.
*
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Zero on success, otherwise non-zero. See fclose for details.
*/
int esp_apptrace_fclose(void *stream);
/**
* @brief Writes to file on host.
* This function has the same semantic as 'fwrite' except for the first argument.
*
* @param ptr Address of data to write.
* @param size Size of an item.
* @param nmemb Number of items to write.
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Number of written items. See fwrite for details.
*/
size_t esp_apptrace_fwrite(const void *ptr, size_t size, size_t nmemb, void *stream);
/**
* @brief Read file on host.
* This function has the same semantic as 'fread' except for the first argument.
*
* @param ptr Address to store read data.
* @param size Size of an item.
* @param nmemb Number of items to read.
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Number of read items. See fread for details.
*/
size_t esp_apptrace_fread(void *ptr, size_t size, size_t nmemb, void *stream);
/**
* @brief Set position indicator in file on host.
* This function has the same semantic as 'fseek' except for the first argument.
*
* @param stream File handle returned by esp_apptrace_fopen.
* @param offset Offset. See fseek for details.
* @param whence Position in file. See fseek for details.
*
* @return Zero on success, otherwise non-zero. See fseek for details.
*/
int esp_apptrace_fseek(void *stream, long offset, int whence);
/**
* @brief Get current position indicator for file on host.
* This function has the same semantic as 'ftell' except for the first argument.
*
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Current position in file. See ftell for details.
*/
int esp_apptrace_ftell(void *stream);
/**
* @brief Indicates to the host that all file operations are complete.
* This function should be called after all file operations are finished and
* indicate to the host that it can perform cleanup operations (close open files etc.).
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
int esp_apptrace_fstop(void);
/**
* @brief Test end-of-file indicator on a stream.
* This function has the same semantic as 'feof' except for the first argument.
*
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Non-Zero if end-of-file indicator is set for stream. See feof for details.
*/
int esp_apptrace_feof(void *stream);
#if !CONFIG_APPTRACE_DEST_UART // JTAG or NONE
#define APPTRACE_JTAG_CONFIG_DEFAULT() { \
.dest = ESP_APPTRACE_DEST_JTAG, \
.dest_cfg.jtag = {0}, \
.flush_tmo = CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO, \
.flush_thresh = CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, \
}
#endif
#if !CONFIG_APPTRACE_DEST_JTAG // UART or NONE
#define APPTRACE_UART_CONFIG_DEFAULT() { \
.dest = ESP_APPTRACE_DEST_UART, \
.dest_cfg.uart = { \
.uart_num = CONFIG_APPTRACE_DEST_UART_NUM, \
.tx_pin_num = CONFIG_APPTRACE_UART_TX_GPIO, \
.rx_pin_num = CONFIG_APPTRACE_UART_RX_GPIO, \
.baud_rate = CONFIG_APPTRACE_UART_BAUDRATE, \
.tx_buff_size = CONFIG_APPTRACE_UART_TX_BUFF_SIZE, \
.tx_msg_size = CONFIG_APPTRACE_UART_TX_MSG_SIZE, \
}, \
.flush_tmo = CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO, \
.flush_thresh = CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, \
}
#endif
// Default picks JTAG if available, otherwise UART
#if !CONFIG_APPTRACE_DEST_UART
#define APPTRACE_CONFIG_DEFAULT() APPTRACE_JTAG_CONFIG_DEFAULT()
#else
#define APPTRACE_CONFIG_DEFAULT() APPTRACE_UART_CONFIG_DEFAULT()
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,51 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_APP_TRACE_CONFIG_H_
#define ESP_APP_TRACE_CONFIG_H_
#include "sdkconfig.h"
/* Default configurations for runtime selection (APPTRACE_DEST_ALL)
* These values are used when building with both JTAG and UART enabled
* to allow runtime selection. You can switch between destinations
* via esp_apptrace_get_user_params(). If this function is
* not provided by the application, JTAG is used by default with the
* configuration defined below. See esp_app_trace.h for details.
*/
#if !defined(CONFIG_APPTRACE_UART_TX_GPIO) || !defined(CONFIG_APPTRACE_UART_RX_GPIO)
#include "soc/uart_pins.h"
#endif
#ifndef CONFIG_APPTRACE_BUF_SIZE
#define CONFIG_APPTRACE_BUF_SIZE 16384
#endif
#ifndef CONFIG_APPTRACE_UART_TX_BUFF_SIZE
#define CONFIG_APPTRACE_UART_TX_BUFF_SIZE 4096
#endif
#ifndef CONFIG_APPTRACE_UART_TX_MSG_SIZE
#define CONFIG_APPTRACE_UART_TX_MSG_SIZE 128
#endif
#ifndef CONFIG_APPTRACE_UART_BAUDRATE
#define CONFIG_APPTRACE_UART_BAUDRATE 1000000
#endif
#ifndef CONFIG_APPTRACE_UART_TX_GPIO
#define CONFIG_APPTRACE_UART_TX_GPIO U1TXD_GPIO_NUM
#endif
#ifndef CONFIG_APPTRACE_UART_RX_GPIO
#define CONFIG_APPTRACE_UART_RX_GPIO U1RXD_GPIO_NUM
#endif
#ifndef CONFIG_APPTRACE_DEST_UART_NUM
#define CONFIG_APPTRACE_DEST_UART_NUM 1
#endif
#endif /* ESP_APP_TRACE_CONFIG_H_ */

View File

@@ -1,95 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_APP_TRACE_TYPES_H_
#define ESP_APP_TRACE_TYPES_H_
#include <stdint.h>
#include "spinlock.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Structure which holds data necessary for measuring time intervals.
*
* After initialization via esp_apptrace_tmo_init() user needs to call esp_apptrace_tmo_check()
* periodically to check timeout for expiration.
*/
typedef struct {
int64_t start; ///< time interval start (in us)
int64_t tmo; ///< timeout value (in us)
int64_t elapsed; ///< elapsed time (in us)
} esp_apptrace_tmo_t;
/** Tracing module synchronization lock */
typedef struct {
spinlock_t mux;
unsigned int int_state;
} esp_apptrace_lock_t;
/** Ring buffer control structure.
*
* @note For purposes of application tracing module if there is no enough space for user data and write pointer can be wrapped
* current ring buffer size can be temporarily shrunk in order to provide buffer with requested size.
*/
typedef struct {
uint8_t *data; ///< pointer to data storage
volatile uint32_t size; ///< size of data storage
volatile uint32_t cur_size; ///< current size of data storage
volatile uint32_t rd; ///< read pointer
volatile uint32_t wr; ///< write pointer
} esp_apptrace_rb_t;
/**
* Application trace data destinations
*/
typedef enum {
ESP_APPTRACE_DEST_JTAG,
ESP_APPTRACE_DEST_UART,
} esp_apptrace_dest_t;
/**
* Application trace configuration for UART destination
*/
typedef struct {
int uart_num; ///< Port number
int tx_pin_num; ///< TX pin number
int rx_pin_num; ///< RX pin number
int baud_rate; ///< Baud rate
uint32_t tx_buff_size; ///< TX ring buffer size
uint32_t tx_msg_size; ///< Maximum size of the single message to transfer.
} esp_apptrace_uart_config_t;
/**
* Application trace trace header size in bytes. It is 2 bytes for SEGGER SystemView
*/
typedef enum {
ESP_APPTRACE_HEADER_SIZE_16 = 2,
ESP_APPTRACE_HEADER_SIZE_32 = 4,
} esp_apptrace_header_size_t;
/**
* Application trace configuration
*/
typedef struct {
esp_apptrace_dest_t dest; ///< Destination type (JTAG or UART)
union {
esp_apptrace_uart_config_t uart; ///< UART configuration (when dest is ESP_APPTRACE_DEST_UART)
struct { ///< Reserved for JTAG (when dest is ESP_APPTRACE_DEST_JTAG)
uint8_t _unused;
} jtag;
} dest_cfg; ///< Destination-specific configuration
uint32_t flush_tmo; ///< Flush timeout in milliseconds
uint32_t flush_thresh; ///< Flush threshold in bytes
} esp_apptrace_config_t;
#ifdef __cplusplus
}
#endif
#endif /* ESP_APP_TRACE_TYPES_H_ */

View File

@@ -1,158 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_APP_TRACE_UTIL_H_
#define ESP_APP_TRACE_UTIL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
#include "esp_timer.h"
#include "esp_app_trace_types.h"
/** Infinite waiting timeout */
#define ESP_APPTRACE_TMO_INFINITE ((uint32_t)-1)
/**
* @brief Initializes timeout structure.
*
* @param tmo Pointer to timeout structure to be initialized.
* @param user_tmo Timeout value (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
*/
static inline void esp_apptrace_tmo_init(esp_apptrace_tmo_t *tmo, uint32_t user_tmo)
{
tmo->start = esp_timer_get_time();
tmo->tmo = user_tmo == ESP_APPTRACE_TMO_INFINITE ? (int64_t) -1 : (int64_t)user_tmo;
tmo->elapsed = 0;
}
/**
* @brief Checks timeout for expiration.
*
* @param tmo Pointer to timeout structure.
*
* @return number of remaining us till tmo.
*/
esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo);
static inline uint32_t esp_apptrace_tmo_remaining_us(esp_apptrace_tmo_t *tmo)
{
return tmo->tmo != (int64_t) -1 ? (tmo->elapsed - tmo->tmo) : ESP_APPTRACE_TMO_INFINITE;
}
/**
* @brief Initializes lock structure.
*
* @param lock Pointer to lock structure to be initialized.
*/
void esp_apptrace_lock_init(esp_apptrace_lock_t *lock);
/**
* @brief Tries to acquire lock in specified time period.
*
* @param lock Pointer to lock structure.
* @param tmo Pointer to timeout struct.
*
* @return ESP_OK on success, otherwise \see esp_err_t
*/
esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, esp_apptrace_tmo_t *tmo);
/**
* @brief Releases lock.
*
* @param lock Pointer to lock structure.
*
* @return ESP_OK on success, otherwise \see esp_err_t
*/
esp_err_t esp_apptrace_lock_give(esp_apptrace_lock_t *lock);
/**
* @brief Initializes ring buffer control structure.
*
* @param rb Pointer to ring buffer structure to be initialized.
* @param data Pointer to buffer to be used as ring buffer's data storage.
* @param size Size of buffer to be used as ring buffer's data storage.
*/
static inline void esp_apptrace_rb_init(esp_apptrace_rb_t *rb, uint8_t *data, uint32_t size)
{
rb->data = data;
rb->size = rb->cur_size = size;
rb->rd = 0;
rb->wr = 0;
}
/**
* @brief Allocates memory chunk in ring buffer.
*
* @param rb Pointer to ring buffer structure.
* @param size Size of the memory to allocate.
*
* @return Pointer to the allocated memory or NULL in case of failure.
*/
uint8_t *esp_apptrace_rb_produce(esp_apptrace_rb_t *rb, uint32_t size);
/**
* @brief Consumes memory chunk in ring buffer.
*
* @param rb Pointer to ring buffer structure.
* @param size Size of the memory to consume.
*
* @return Pointer to consumed memory chunk or NULL in case of failure.
*/
uint8_t *esp_apptrace_rb_consume(esp_apptrace_rb_t *rb, uint32_t size);
/**
* @brief Gets size of memory which can consumed with single call to esp_apptrace_rb_consume().
*
* @param rb Pointer to ring buffer structure.
*
* @return Size of memory which can consumed.
*
* @note Due to read pointer wrapping returned size can be less then the total size of available data.
*/
uint32_t esp_apptrace_rb_read_size_get(esp_apptrace_rb_t *rb);
/**
* @brief Gets size of memory which can produced with single call to esp_apptrace_rb_produce().
*
* @param rb Pointer to ring buffer structure.
*
* @return Size of memory which can produced.
*
* @note Due to write pointer wrapping returned size can be less then the total size of available data.
*/
uint32_t esp_apptrace_rb_write_size_get(esp_apptrace_rb_t *rb);
int esp_apptrace_log_lock(void);
void esp_apptrace_log_unlock(void);
#define ESP_APPTRACE_LOG( format, ... ) \
do { \
esp_apptrace_log_lock(); \
esp_rom_printf(format, ##__VA_ARGS__); \
esp_apptrace_log_unlock(); \
} while(0)
#define ESP_APPTRACE_LOG_LEV( _L_, level, format, ... ) \
do { \
if (LOG_LOCAL_LEVEL >= level) { \
ESP_APPTRACE_LOG(LOG_FORMAT(_L_, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); \
} \
} while(0)
#define ESP_APPTRACE_LOGE( format, ... ) ESP_APPTRACE_LOG_LEV(E, ESP_LOG_ERROR, format, ##__VA_ARGS__)
#define ESP_APPTRACE_LOGW( format, ... ) ESP_APPTRACE_LOG_LEV(W, ESP_LOG_WARN, format, ##__VA_ARGS__)
#define ESP_APPTRACE_LOGI( format, ... ) ESP_APPTRACE_LOG_LEV(I, ESP_LOG_INFO, format, ##__VA_ARGS__)
#define ESP_APPTRACE_LOGD( format, ... ) ESP_APPTRACE_LOG_LEV(D, ESP_LOG_DEBUG, format, ##__VA_ARGS__)
#define ESP_APPTRACE_LOGV( format, ... ) ESP_APPTRACE_LOG_LEV(V, ESP_LOG_VERBOSE, format, ##__VA_ARGS__)
#define ESP_APPTRACE_LOGO( format, ... ) ESP_APPTRACE_LOG_LEV(E, ESP_LOG_NONE, format, ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif //ESP_APP_TRACE_UTIL_H_

View File

@@ -1,69 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_DBG_STUBS_H_
#define ESP_DBG_STUBS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* Debug stubs entries IDs
*/
typedef enum {
ESP_DBG_STUB_MAGIC_NUM,
ESP_DBG_STUB_TABLE_SIZE,
ESP_DBG_STUB_CONTROL_DATA, ///< stubs descriptor entry
ESP_DBG_STUB_ENTRY_FIRST,
ESP_DBG_STUB_ENTRY_GCOV ///< GCOV entry
= ESP_DBG_STUB_ENTRY_FIRST,
ESP_DBG_STUB_ENTRY_CAPABILITIES,
ESP_DBG_STUB_ENTRY_MAX
} esp_dbg_stub_id_t;
#define ESP_DBG_STUB_MAGIC_NUM_VAL 0xFEEDBEEF
#define ESP_DBG_STUB_CAP_GCOV_TASK (1 << 0)
/**
* @brief Initializes debug stubs.
*
* @note Must be called after esp_apptrace_init() if app tracing is enabled.
*/
void esp_dbg_stubs_init(void);
/**
* @brief Initializes application tracing module.
*
* @note Should be called before any esp_apptrace_xxx call.
*
* @param id Stub ID.
* @param entry Stub entry. Usually it is stub entry function address,
* but can be any value meaningful for OpenOCD command/code
* such as capabilities
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_dbg_stub_entry_set(esp_dbg_stub_id_t id, uint32_t entry);
/**
* @brief Retrieves the corresponding stub entry
*
* @param id Stub ID.
* @param entry Stub entry. Usually it is stub entry function address,
* but can be any value meaningful for OpenOCD command/code
* such as capabilities
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
esp_err_t esp_dbg_stub_entry_get(esp_dbg_stub_id_t id, uint32_t *entry);
#ifdef __cplusplus
}
#endif
#endif // ESP_DBG_STUBS_H_

View File

@@ -1,11 +0,0 @@
[mapping:app_trace]
archive: libapp_trace.a
entries:
if ESP_TRACE_TRANSPORT_APPTRACE = y:
app_trace (noflash)
app_trace_util (noflash)
if APPTRACE_DEST_JTAG = y || APPTRACE_DEST_ALL = y:
port_jtag (noflash)
app_trace_membufs_proto (noflash)
if APPTRACE_DEST_UART = y || APPTRACE_DEST_ALL = y:
port_uart (noflash)

View File

@@ -1,37 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_APP_TRACE_PORT_H_
#define ESP_APP_TRACE_PORT_H_
#include "esp_app_trace_util.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Apptrace HW interface. */
typedef struct {
esp_err_t (*init)(void *hw_data, const esp_apptrace_config_t *config);
uint8_t *(*get_up_buffer)(void *hw_data, uint32_t, esp_apptrace_tmo_t *);
esp_err_t (*put_up_buffer)(void *hw_data, uint8_t *, esp_apptrace_tmo_t *);
esp_err_t (*flush_up_buffer_nolock)(void *hw_data, uint32_t, esp_apptrace_tmo_t *);
esp_err_t (*flush_up_buffer)(void *hw_data, esp_apptrace_tmo_t *);
void (*down_buffer_config)(void *hw_data, uint8_t *buf, uint32_t size);
uint8_t *(*get_down_buffer)(void *hw_data, uint32_t *, esp_apptrace_tmo_t *);
esp_err_t (*put_down_buffer)(void *hw_data, uint8_t *, esp_apptrace_tmo_t *);
bool (*host_is_connected)(void *hw_data);
void (*set_header_size)(void *hw_data, esp_apptrace_header_size_t header_size);
} esp_apptrace_hw_t;
esp_apptrace_hw_t *esp_apptrace_jtag_hw_get(void **data);
esp_apptrace_hw_t *esp_apptrace_uart_hw_get(int num, void **data);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,522 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_cpu.h"
#include "esp_attr.h"
#include "esp_private/uart_share_hw_ctrl.h"
#include "hal/uart_hal.h"
#include "hal/gpio_hal.h"
#include "driver/uart.h"
#include "soc/uart_periph.h"
#include "esp_clk_tree.h"
#include "esp_private/esp_clk_tree_common.h"
#include "soc/gpio_periph.h"
#include "esp_rom_gpio.h"
#include "hal/uart_ll.h"
#include "esp_intr_alloc.h"
#include "esp_heap_caps.h"
#include "esp_private/esp_gpio_reserve.h"
#include "esp_app_trace_port.h"
#include "esp_app_trace_util.h"
#include "esp_app_trace_types.h"
static const char *TAG = "esp_apptrace_uart";
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
typedef struct {
uint8_t *buffer; ///< Ring buffer data
uint32_t max_size; ///< Ring buffer maximum size (must be power of 2)
volatile uint32_t count; ///< Number of bytes currently in the buffer
volatile uint32_t head; ///< Write pointer index
volatile uint32_t tail; ///< Read pointer index
} esp_apptrace_uart_rb_t;
typedef struct {
int inited;
volatile bool tx_busy; ///< TX busy flag
uart_hal_context_t hal_ctx; ///< UART HAL context
esp_apptrace_uart_rb_t tx_ring; ///< TX ring buffer
intr_handle_t intr_handle; ///< Interrupt handle
/* TX message buffer */
uint8_t *tx_msg_buff; ///< TX message buffer to provide with get_up_buffer
uint32_t tx_msg_buff_size; ///< TX message buffer size & maximum size of the single message to transfer.
uint32_t tx_pending_msg_size; ///< Pending message size to send with put_up_buffer
/* RX message buffer */
uint8_t *rx_msg_buff; ///< RX message buffer provided with down_buffer_config function
uint32_t rx_msg_buff_size; ///< RX message buffer size provided with down_buffer_config function
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_lock_t lock; ///< Sync lock
#endif
} esp_apptrace_uart_data_t;
static inline bool is_power_of_two(uint32_t n)
{
return n != 0 && (n & (n - 1)) == 0;
}
static inline uint32_t ring_buffer_mask(const esp_apptrace_uart_rb_t *rb)
{
return rb->max_size - 1;
}
/* Get the length of the data in the ring buffer */
static inline uint32_t ring_buffer_data_len(const esp_apptrace_uart_rb_t *rb)
{
return rb->count;
}
/* Get the length of the free space in the ring buffer */
static inline uint32_t ring_buffer_free_len(const esp_apptrace_uart_rb_t *rb)
{
return rb->max_size - rb->count;
}
static inline void ring_buffer_advance_tail(esp_apptrace_uart_rb_t *rb, uint32_t count)
{
rb->tail = (rb->tail + count) & ring_buffer_mask(rb);
rb->count -= count;
}
static inline void ring_buffer_advance_head(esp_apptrace_uart_rb_t *rb, uint32_t count)
{
rb->head = (rb->head + count) & ring_buffer_mask(rb);
rb->count += count;
}
static inline uint32_t ring_buffer_calc_to_send(const esp_apptrace_uart_rb_t *rb, uint32_t tx_msg_size)
{
uint32_t used = ring_buffer_data_len(rb);
if (used == 0) {
return 0;
}
uint32_t cont = rb->max_size - rb->tail;
uint32_t n = MIN(used, cont);
/* Apply message size limit if specified */
if (tx_msg_size && tx_msg_size < n) {
return tx_msg_size;
}
return n;
}
static esp_err_t esp_apptrace_uart_lock(void *hw_data, esp_apptrace_tmo_t *tmo)
{
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_uart_data_t *uart_data = hw_data;
esp_err_t ret = esp_apptrace_lock_take(&uart_data->lock, tmo);
if (ret != ESP_OK) {
return ESP_FAIL;
}
#endif
return ESP_OK;
}
static esp_err_t esp_apptrace_uart_unlock(void *hw_data)
{
esp_err_t ret = ESP_OK;
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_uart_data_t *uart_data = hw_data;
ret = esp_apptrace_lock_give(&uart_data->lock);
assert(ret == ESP_OK && "Failed to unlock apptrace uart lock!");
#endif
return ret;
}
static esp_err_t ring_buffer_put(esp_apptrace_uart_rb_t *rb, const uint8_t *data, uint32_t len)
{
/* Drop oldest. Make available space if needed */
uint32_t free_len = ring_buffer_free_len(rb);
if (len > free_len) {
uint32_t need = len - free_len;
ring_buffer_advance_tail(rb, need);
}
uint32_t head = rb->head;
uint32_t space_to_end = rb->max_size - head;
if (len <= space_to_end) {
memcpy(&rb->buffer[head], data, len);
} else {
memcpy(&rb->buffer[head], data, space_to_end);
memcpy(&rb->buffer[0], &data[space_to_end], len - space_to_end);
}
ring_buffer_advance_head(rb, len);
return ESP_OK;
}
static esp_err_t ring_buffer_init(esp_apptrace_uart_rb_t *rb, uint32_t size)
{
rb->buffer = heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (!rb->buffer) {
return ESP_ERR_NO_MEM;
}
rb->max_size = size;
rb->count = 0;
rb->head = 0;
rb->tail = 0;
return ESP_OK;
}
static void IRAM_ATTR esp_apptrace_uart_isr_handler(void *arg)
{
esp_apptrace_uart_data_t *uart_data = arg;
esp_apptrace_uart_rb_t *rb = &uart_data->tx_ring;
uint32_t intr_status = uart_hal_get_intsts_mask(&uart_data->hal_ctx);
if (intr_status & UART_INTR_TXFIFO_EMPTY) {
uart_hal_clr_intsts_mask(&uart_data->hal_ctx, UART_INTR_TXFIFO_EMPTY);
uint32_t to_send = ring_buffer_calc_to_send(rb, uart_data->tx_msg_buff_size);
if (to_send > 0) {
uint32_t written = 0;
uart_hal_write_txfifo(&uart_data->hal_ctx, &rb->buffer[rb->tail], to_send, &written);
ring_buffer_advance_tail(rb, written);
}
/* If ring buffer is empty, disable TX interrupt */
if (ring_buffer_data_len(rb) == 0) {
uart_ll_disable_intr_mask(uart_data->hal_ctx.dev, UART_INTR_TXFIFO_EMPTY);
uart_data->tx_busy = false;
}
}
}
static esp_err_t esp_apptrace_uart_init(void *hw_data, const esp_apptrace_config_t *config)
{
esp_err_t ret = ESP_ERR_INVALID_ARG;
uint64_t gpio_mask = 0;
esp_apptrace_uart_data_t *uart_data = hw_data;
const esp_apptrace_uart_config_t *uart_config = &config->dest_cfg.uart;
/* Init function is called on every core, so ensure to do main setup only once */
int core_id = esp_cpu_get_core_id();
if (core_id == 0) {
if (uart_config->uart_num == CONFIG_ESP_CONSOLE_UART_NUM) {
ESP_APPTRACE_LOGE("Application trace UART and console UART cannot use the same port number");
return ESP_ERR_INVALID_ARG;
}
if (uart_config->uart_num >= SOC_UART_HP_NUM) {
ESP_APPTRACE_LOGE("UART port number %d is not supported!", uart_config->uart_num);
return ESP_ERR_NOT_SUPPORTED;
}
if (GPIO_IS_VALID_GPIO(uart_config->tx_pin_num)) {
gpio_mask |= BIT64(uart_config->tx_pin_num);
}
if (GPIO_IS_VALID_GPIO(uart_config->rx_pin_num)) {
gpio_mask |= BIT64(uart_config->rx_pin_num);
}
if (gpio_mask == 0) {
ESP_LOGE(TAG, "No valid GPIOs to reserve");
return ESP_ERR_INVALID_STATE;
}
uint64_t r = esp_gpio_reserve(gpio_mask);
if (r & gpio_mask) {
ESP_LOGE(TAG, "GPIO(s) are already reserved: 0x%"PRIx64, r & gpio_mask);
return ESP_ERR_INVALID_STATE;
}
uart_data->hal_ctx.dev = UART_LL_GET_HW(uart_config->uart_num);
HP_UART_BUS_CLK_ATOMIC() {
uart_ll_enable_bus_clock(uart_config->uart_num, true);
uart_ll_reset_register(uart_config->uart_num);
}
HP_UART_SRC_CLK_ATOMIC() {
uart_ll_sclk_enable(uart_data->hal_ctx.dev);
}
uint32_t sclk_hz;
esp_clk_tree_src_get_freq_hz(UART_SCLK_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &sclk_hz);
/* Enable the default clock source */
esp_clk_tree_enable_src(UART_SCLK_DEFAULT, true);
/* Initialize UART HAL (sets default 8N1 mode) */
uart_hal_init(&uart_data->hal_ctx, uart_config->uart_num);
ESP_LOGI(TAG, "uart_hal_init: %d", uart_config->uart_num);
HP_UART_SRC_CLK_ATOMIC() {
uart_hal_set_sclk(&uart_data->hal_ctx, UART_SCLK_DEFAULT);
uart_hal_set_baudrate(&uart_data->hal_ctx, uart_config->baud_rate, sclk_hz);
}
/* Configure FIFO thresholds */
uart_hal_set_txfifo_empty_thr(&uart_data->hal_ctx, 16); /* Slow down IRQ rate */
uart_hal_set_rxfifo_full_thr(&uart_data->hal_ctx, 1);
/* Initialize TX ring buffer */
if (uart_config->tx_buff_size == 0 || !is_power_of_two(uart_config->tx_buff_size)) {
ESP_APPTRACE_LOGE("TX ring buffer size (%u) must be a power of two and greater than 0",
uart_config->tx_buff_size);
goto err_init_ring_buff;
}
ret = ring_buffer_init(&uart_data->tx_ring, uart_config->tx_buff_size);
if (ret != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to initialize TX ring buffer");
goto err_init_ring_buff;
}
/* Initialize TX message buffer for providing with get_up_buffer */
uart_data->tx_msg_buff_size = uart_config->tx_msg_size;
uart_data->tx_msg_buff = heap_caps_malloc(uart_data->tx_msg_buff_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (uart_data->tx_msg_buff == NULL) {
ESP_APPTRACE_LOGE("Failed to initialize TX message buffer");
ret = ESP_ERR_NO_MEM;
goto err_alloc_msg_buff;
}
/* Disable all interrupts and clear status */
uart_ll_disable_intr_mask(uart_data->hal_ctx.dev, UART_LL_INTR_MASK);
uart_ll_clr_intsts_mask(uart_data->hal_ctx.dev, UART_LL_INTR_MASK);
/* Install interrupt handler */
int intr_alloc_flags = 0;
ret = esp_intr_alloc(uart_periph_signal[uart_config->uart_num].irq, intr_alloc_flags,
esp_apptrace_uart_isr_handler, uart_data, &uart_data->intr_handle);
if (ret != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to allocate interrupt: %s", esp_err_to_name(ret));
goto err_alloc_intr;
}
/* Reset FIFOs */
uart_hal_rxfifo_rst(&uart_data->hal_ctx);
uart_hal_txfifo_rst(&uart_data->hal_ctx);
/* Configure GPIO pins for RX and TX */
const uint32_t tx_idx = UART_PERIPH_SIGNAL(uart_config->uart_num, SOC_UART_PERIPH_SIGNAL_TX);
const uint32_t rx_idx = UART_PERIPH_SIGNAL(uart_config->uart_num, SOC_UART_PERIPH_SIGNAL_RX);
/* Configure TX pin */
gpio_ll_func_sel(&GPIO, uart_config->tx_pin_num, PIN_FUNC_GPIO);
esp_rom_gpio_pad_pullup_only(uart_config->tx_pin_num);
esp_rom_gpio_connect_out_signal(uart_config->tx_pin_num, tx_idx, 0, 0);
/* Configure RX pin */
gpio_ll_input_enable(&GPIO, uart_config->rx_pin_num);
esp_rom_gpio_pad_pullup_only(uart_config->rx_pin_num);
esp_rom_gpio_connect_in_signal(uart_config->rx_pin_num, rx_idx, 0);
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_lock_init(&uart_data->lock);
#endif
}
uart_data->inited |= 1 << core_id;
uart_data->tx_busy = false;
return ESP_OK;
err_alloc_intr:
heap_caps_free(uart_data->tx_msg_buff);
err_alloc_msg_buff:
heap_caps_free(uart_data->tx_ring.buffer);
err_init_ring_buff:
esp_clk_tree_enable_src(UART_SCLK_DEFAULT, false);
HP_UART_SRC_CLK_ATOMIC() {
uart_ll_sclk_disable(uart_data->hal_ctx.dev);
}
HP_UART_BUS_CLK_ATOMIC() {
uart_ll_enable_bus_clock(uart_config->uart_num, false);
}
esp_gpio_revoke(gpio_mask);
return ret;
}
static uint8_t *esp_apptrace_uart_up_buffer_get(void *hw_data, uint32_t size, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
if (size == 0 || size > uart_data->tx_msg_buff_size) {
return NULL;
}
if (esp_apptrace_uart_lock(uart_data, tmo) != ESP_OK) {
return NULL;
}
if (uart_data->tx_pending_msg_size != 0) {
// A previous message was not sent.
esp_apptrace_uart_unlock(uart_data);
return NULL;
}
uart_data->tx_pending_msg_size = size;
esp_apptrace_uart_unlock(uart_data);
return uart_data->tx_msg_buff;
}
static esp_err_t esp_apptrace_uart_up_buffer_put(void *hw_data, uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
esp_apptrace_uart_rb_t *rb = &uart_data->tx_ring;
esp_err_t res = esp_apptrace_uart_lock(uart_data, tmo);
if (res != ESP_OK) {
return res;
}
/* Add data to ring buffer */
ring_buffer_put(rb, ptr, uart_data->tx_pending_msg_size);
uart_data->tx_pending_msg_size = 0;
esp_apptrace_uart_unlock(uart_data);
// Trigger transmission if not already in progress
if (!uart_data->tx_busy) {
uart_data->tx_busy = true;
/* Enable TX interrupt */
uart_ll_clr_intsts_mask(uart_data->hal_ctx.dev, UART_INTR_TXFIFO_EMPTY);
uart_ll_ena_intr_mask(uart_data->hal_ctx.dev, UART_INTR_TXFIFO_EMPTY);
}
return ESP_OK;
}
static void esp_apptrace_uart_down_buffer_config(void *hw_data, uint8_t *buf, uint32_t size)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
assert(buf != NULL && "Down buffer cannot be NULL");
assert(size > 0 && "Down buffer size must be greater than 0");
uart_data->rx_msg_buff = buf;
uart_data->rx_msg_buff_size = size;
}
static uint8_t *esp_apptrace_uart_down_buffer_get(void *hw_data, uint32_t *size, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
if (!size || *size == 0) {
return NULL;
}
if (!uart_data->rx_msg_buff) {
ESP_APPTRACE_LOGE("RX message buffer is not configured. Call down_buffer_config() first.");
return NULL;
}
if (esp_apptrace_uart_lock(uart_data, tmo) != ESP_OK) {
return NULL;
}
uint32_t rx_len = uart_ll_get_rxfifo_len(uart_data->hal_ctx.dev);
int to_read = MIN(rx_len, MIN(uart_data->rx_msg_buff_size, *size));
if (to_read) {
uart_hal_read_rxfifo(&uart_data->hal_ctx, uart_data->rx_msg_buff, &to_read);
}
*size = to_read;
esp_apptrace_uart_unlock(uart_data);
return (*size > 0) ? uart_data->rx_msg_buff : NULL;
}
static esp_err_t esp_apptrace_uart_down_buffer_put(void *hw_data, uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
(void)hw_data;
(void)ptr;
(void)tmo;
/* No action needed - data was already read in get function */
return ESP_OK;
}
static bool esp_apptrace_uart_host_is_connected(void *hw_data)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
return uart_data->inited & 1;
}
static esp_err_t esp_apptrace_uart_flush_nolock(void *hw_data, uint32_t min_sz, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
esp_apptrace_uart_rb_t *rb = &uart_data->tx_ring;
uint32_t pending = ring_buffer_data_len(rb);
if (pending < min_sz) {
ESP_APPTRACE_LOGD("Ignore UART flush request for min %" PRIu32 " bytes. Pending bytes: %" PRIu32, min_sz, pending);
return ESP_OK;
}
/* Trigger transmission if there's data but not busy */
if (pending > 0 && !uart_data->tx_busy) {
uart_data->tx_busy = true;
uart_ll_clr_intsts_mask(uart_data->hal_ctx.dev, UART_INTR_TXFIFO_EMPTY);
uart_ll_ena_intr_mask(uart_data->hal_ctx.dev, UART_INTR_TXFIFO_EMPTY);
}
while (uart_data->tx_busy || ring_buffer_data_len(rb) > 0) {
if (esp_apptrace_tmo_check(tmo) != ESP_OK) {
return ESP_ERR_TIMEOUT;
}
esp_rom_delay_us(100);
}
return ESP_OK;
}
static esp_err_t esp_apptrace_uart_flush(void *hw_data, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_uart_data_t *uart_data = hw_data;
esp_err_t res = esp_apptrace_uart_lock(uart_data, tmo);
if (res != ESP_OK) {
return res;
}
esp_err_t ret = esp_apptrace_uart_flush_nolock(hw_data, 0, tmo);
esp_apptrace_uart_unlock(uart_data);
return ret;
}
esp_apptrace_hw_t *esp_apptrace_uart_hw_get(int num, void **data)
{
ESP_APPTRACE_LOGD("esp_apptrace_uart_hw_get - %i", num);
static esp_apptrace_uart_data_t s_uart_hw_data;
static esp_apptrace_hw_t s_uart_hw = {
.init = esp_apptrace_uart_init,
.get_up_buffer = esp_apptrace_uart_up_buffer_get,
.put_up_buffer = esp_apptrace_uart_up_buffer_put,
.flush_up_buffer_nolock = esp_apptrace_uart_flush_nolock,
.flush_up_buffer = esp_apptrace_uart_flush,
.down_buffer_config = esp_apptrace_uart_down_buffer_config,
.get_down_buffer = esp_apptrace_uart_down_buffer_get,
.put_down_buffer = esp_apptrace_uart_down_buffer_put,
.host_is_connected = esp_apptrace_uart_host_is_connected,
};
*data = &s_uart_hw_data;
return &s_uart_hw;
}

View File

@@ -1,373 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#include "sdkconfig.h"
#include "esp_cpu.h"
#include "esp_log.h"
#include "esp_app_trace_config.h"
#include "esp_app_trace_membufs_proto.h"
#include "esp_app_trace_port.h"
#include "riscv/semihosting.h"
/** RISCV HW transport data */
typedef struct {
uint8_t inited; // initialization state flags for every core
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_lock_t lock; // sync lock
#endif
esp_apptrace_membufs_proto_data_t membufs;
} esp_apptrace_riscv_data_t;
/** RISCV memory host iface control block */
typedef struct {
uint32_t ctrl;
// - Guard field. If this register is not zero then CPU is changing this struct and
// this guard field holds address of the instruction which application will execute when CPU finishes with those modifications.
uint32_t stat;
esp_apptrace_mem_block_t * mem_blocks;
} esp_apptrace_riscv_ctrl_block_t;
#define ESP_APPTRACE_RISCV_BLOCK_LEN_MSK 0x7FFFUL
#define ESP_APPTRACE_RISCV_BLOCK_LEN(_l_) ((_l_) & ESP_APPTRACE_RISCV_BLOCK_LEN_MSK)
#define ESP_APPTRACE_RISCV_BLOCK_LEN_GET(_v_) ((_v_) & ESP_APPTRACE_RISCV_BLOCK_LEN_MSK)
#define ESP_APPTRACE_RISCV_BLOCK_ID_MSK 0x7FUL
#define ESP_APPTRACE_RISCV_BLOCK_ID(_id_) (((_id_) & ESP_APPTRACE_RISCV_BLOCK_ID_MSK) << 15)
#define ESP_APPTRACE_RISCV_BLOCK_ID_GET(_v_) (((_v_) >> 15) & ESP_APPTRACE_RISCV_BLOCK_ID_MSK)
#define ESP_APPTRACE_RISCV_HOST_DATA (1 << 22)
#define ESP_APPTRACE_RISCV_HOST_CONNECT (1 << 23)
#define ESP_APPTRACE_RISCV_INITED(_hw_) ((_hw_)->inited & (1 << 0/*esp_cpu_get_core_id()*/))
const static char *TAG = "esp_apptrace";
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#define APPTRACE_DRAM_ATTR TCM_DRAM_ATTR
#else
#define APPTRACE_DRAM_ATTR
#endif
static APPTRACE_DRAM_ATTR esp_apptrace_riscv_ctrl_block_t s_tracing_ctrl[CONFIG_FREERTOS_NUMBER_OF_CORES];
/* Advertises apptrace control block address to host.
This function can be overridden with custom implementation,
e.g. OpenOCD flasher stub use own implementation of it. */
__attribute__((weak)) int esp_apptrace_advertise_ctrl_block(void *ctrl_block_addr)
{
if (!esp_cpu_dbgr_is_attached()) {
return 0;
}
return (int) semihosting_call_noerrno(ESP_SEMIHOSTING_SYS_APPTRACE_INIT, (long*)ctrl_block_addr);
}
/* Returns up buffers config.
This function can be overridden with custom implementation,
e.g. OpenOCD flasher stub use own implementation of it. */
__attribute__((weak)) void esp_apptrace_get_up_buffers(esp_apptrace_mem_block_t mem_blocks_cfg[2])
{
static uint8_t s_mem_blocks[2][CONFIG_APPTRACE_BUF_SIZE];
mem_blocks_cfg[0].start = s_mem_blocks[0];
mem_blocks_cfg[0].sz = CONFIG_APPTRACE_BUF_SIZE;
mem_blocks_cfg[1].start = s_mem_blocks[1];
mem_blocks_cfg[1].sz = CONFIG_APPTRACE_BUF_SIZE;
}
static esp_err_t esp_apptrace_riscv_lock(void *hw_data, esp_apptrace_tmo_t *tmo)
{
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_riscv_data_t *riscv_data = hw_data;
esp_err_t ret = esp_apptrace_lock_take(&riscv_data->lock, tmo);
if (ret != ESP_OK) {
return ESP_FAIL;
}
#endif
return ESP_OK;
}
static esp_err_t esp_apptrace_riscv_unlock(void *hw_data)
{
esp_err_t ret = ESP_OK;
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_riscv_data_t *riscv_data = hw_data;
ret = esp_apptrace_lock_give(&riscv_data->lock);
#endif
return ret;
}
/*****************************************************************************************/
/***************************** Apptrace HW iface *****************************************/
/*****************************************************************************************/
static esp_err_t esp_apptrace_riscv_init(void *hw_data, const esp_apptrace_config_t *config)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
/* esp_apptrace_riscv_init() is called on every core, so ensure to do main initialization only once */
int core_id = esp_cpu_get_core_id();
if (core_id == 0) {
esp_apptrace_mem_block_t mem_blocks_cfg[2];
esp_apptrace_get_up_buffers(mem_blocks_cfg);
riscv_data->membufs.header_size = ESP_APPTRACE_HEADER_SIZE_32;
esp_err_t res = esp_apptrace_membufs_init(&riscv_data->membufs, mem_blocks_cfg);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to init membufs proto (%d)!", res);
return res;
}
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_lock_init(&riscv_data->lock);
#endif
}
riscv_data->inited |= 1 << core_id;
ESP_APPTRACE_LOGI("Apptrace initialized on CPU%d. Tracing control block @ %p.", core_id, &s_tracing_ctrl[core_id]);
s_tracing_ctrl[core_id].mem_blocks = riscv_data->membufs.blocks;
for (int i = 0; i < 2; i++) {
ESP_APPTRACE_LOGD("Mem buf[%d] %" PRIu32 " bytes @ %p (%p/%p)", i,
s_tracing_ctrl[core_id].mem_blocks[i].sz, s_tracing_ctrl[core_id].mem_blocks[i].start,
&(s_tracing_ctrl[core_id].mem_blocks[i].start), &(s_tracing_ctrl[core_id].mem_blocks[i].sz));
}
// notify host about control block address
int __attribute__((unused)) res = esp_apptrace_advertise_ctrl_block(&s_tracing_ctrl[core_id]);
assert(res == 0 && "Failed to send config to host!");
return ESP_OK;
}
static uint8_t *esp_apptrace_riscv_up_buffer_get(void *hw_data, uint32_t size, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return NULL;
}
esp_err_t res = esp_apptrace_riscv_lock(riscv_data, tmo);
if (res != ESP_OK) {
return NULL;
}
uint8_t *ptr = esp_apptrace_membufs_up_buffer_get(&riscv_data->membufs, size, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
if (esp_apptrace_riscv_unlock(riscv_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}
return ptr;
}
static esp_err_t esp_apptrace_riscv_up_buffer_put(void *hw_data, uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return ESP_ERR_INVALID_STATE;
}
// Can avoid locking because esp_apptrace_membufs_up_buffer_put() just modifies buffer's header
esp_err_t res = esp_apptrace_membufs_up_buffer_put(&riscv_data->membufs, ptr, tmo);
return res;
}
static void esp_apptrace_riscv_down_buffer_config(void *hw_data, uint8_t *buf, uint32_t size)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return;
}
esp_apptrace_membufs_down_buffer_config(&riscv_data->membufs, buf, size);
}
static uint8_t *esp_apptrace_riscv_down_buffer_get(void *hw_data, uint32_t *size, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return NULL;
}
esp_err_t res = esp_apptrace_riscv_lock(riscv_data, tmo);
if (res != ESP_OK) {
return NULL;
}
uint8_t *ptr = esp_apptrace_membufs_down_buffer_get(&riscv_data->membufs, size, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
if (esp_apptrace_riscv_unlock(hw_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}
return ptr;
}
static esp_err_t esp_apptrace_riscv_down_buffer_put(void *hw_data, uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return ESP_ERR_INVALID_STATE;
}
// Can avoid locking because esp_apptrace_membufs_down_buffer_put() does nothing
/*esp_err_t res = esp_apptrace_riscv_lock(hw_data, tmo);
if (res != ESP_OK) {
return res;
}*/
esp_err_t res = esp_apptrace_membufs_down_buffer_put(&riscv_data->membufs, ptr, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
/*if (esp_apptrace_riscv_unlock(hw_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}*/
return res;
}
static bool esp_apptrace_riscv_host_is_connected(void *hw_data)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return false;
}
return s_tracing_ctrl[esp_cpu_get_core_id()].ctrl & ESP_APPTRACE_RISCV_HOST_CONNECT ? true : false;
}
static esp_err_t esp_apptrace_riscv_flush_nolock(void *hw_data, uint32_t min_sz, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return ESP_ERR_INVALID_STATE;
}
return esp_apptrace_membufs_flush_nolock(&riscv_data->membufs, min_sz, tmo);
}
static esp_err_t esp_apptrace_riscv_flush(void *hw_data, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
if (!ESP_APPTRACE_RISCV_INITED(riscv_data)) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t res = esp_apptrace_riscv_lock(riscv_data, tmo);
if (res != ESP_OK) {
return res;
}
res = esp_apptrace_membufs_flush_nolock(&riscv_data->membufs, 0, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
if (esp_apptrace_riscv_unlock(riscv_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}
return res;
}
/*****************************************************************************************/
/************************** Membufs proto HW iface ***************************************/
/*****************************************************************************************/
static inline void esp_apptrace_riscv_buffer_swap_lock(void)
{
extern uint32_t __esp_apptrace_riscv_updated;
// indicate to host that we are about to update.
// this is used only to place CPU into streaming mode at tracing startup
// before starting streaming host can halt us after we read ESP_APPTRACE_RISCV_CTRL_REG and before we updated it
// HACK: in this case host will set breakpoint just after ESP_APPTRACE_RISCV_CTRL_REG update,
// here we set address to set bp at
// enter ERI update critical section
s_tracing_ctrl[esp_cpu_get_core_id()].stat = (uint32_t)&__esp_apptrace_riscv_updated;
}
static __attribute__((noinline)) void esp_apptrace_riscv_buffer_swap_unlock(void)
{
// exit ERI update critical section
s_tracing_ctrl[esp_cpu_get_core_id()].stat = 0;
// TODO: currently host sets breakpoint, use break instruction to stop;
// it will allow to use ESP_APPTRACE_RISCV_STAT_REG for other purposes
asm volatile(
" .global __esp_apptrace_riscv_updated\n"
"__esp_apptrace_riscv_updated:\n"); // host will set bp here to resolve collision at streaming start
}
static esp_err_t esp_apptrace_riscv_buffer_swap_start(uint32_t curr_block_id)
{
esp_err_t res = ESP_OK;
esp_apptrace_riscv_buffer_swap_lock();
uint32_t ctrl_reg = s_tracing_ctrl[esp_cpu_get_core_id()].ctrl;
uint32_t host_connected = ESP_APPTRACE_RISCV_HOST_CONNECT & ctrl_reg;
if (host_connected) {
uint32_t acked_block = ESP_APPTRACE_RISCV_BLOCK_ID_GET(ctrl_reg);
uint32_t host_to_read = ESP_APPTRACE_RISCV_BLOCK_LEN_GET(ctrl_reg);
if (host_to_read != 0 || acked_block != (curr_block_id & ESP_APPTRACE_RISCV_BLOCK_ID_MSK)) {
ESP_APPTRACE_LOGD("[%d]: Can not switch %" PRIx32 " %" PRIu32 " %" PRIx32 " %" PRIx32 "/%" PRIx32, esp_cpu_get_core_id(), ctrl_reg, host_to_read, acked_block,
curr_block_id & ESP_APPTRACE_RISCV_BLOCK_ID_MSK, curr_block_id);
res = ESP_ERR_NO_MEM;
goto _on_err;
}
}
return ESP_OK;
_on_err:
esp_apptrace_riscv_buffer_swap_unlock();
return res;
}
static esp_err_t esp_apptrace_riscv_buffer_swap_end(uint32_t new_block_id, uint32_t prev_block_len)
{
uint32_t ctrl_reg = s_tracing_ctrl[esp_cpu_get_core_id()].ctrl;
uint32_t host_connected = ESP_APPTRACE_RISCV_HOST_CONNECT & ctrl_reg;
s_tracing_ctrl[esp_cpu_get_core_id()].ctrl = ESP_APPTRACE_RISCV_BLOCK_ID(new_block_id) |
host_connected | ESP_APPTRACE_RISCV_BLOCK_LEN(prev_block_len);
esp_apptrace_riscv_buffer_swap_unlock();
return ESP_OK;
}
static esp_err_t esp_apptrace_riscv_buffer_swap(uint32_t new_block_id, uint32_t prev_block_len)
{
/* do nothing */
return ESP_OK;
}
static bool esp_apptrace_riscv_host_data_pending(void)
{
uint32_t ctrl_reg = s_tracing_ctrl[esp_cpu_get_core_id()].ctrl;
// ESP_APPTRACE_LOGV("%s() 0x%x", __func__, ctrl_reg);
return (ctrl_reg & ESP_APPTRACE_RISCV_HOST_DATA) ? true : false;
}
static void esp_apptrace_riscv_set_header_size(void *hw_data, esp_apptrace_header_size_t header_size)
{
esp_apptrace_riscv_data_t *riscv_data = hw_data;
riscv_data->membufs.header_size = header_size;
}
esp_apptrace_hw_t *esp_apptrace_jtag_hw_get(void **data)
{
static esp_apptrace_membufs_proto_hw_t s_trace_proto_hw = {
.swap_start = esp_apptrace_riscv_buffer_swap_start,
.swap = esp_apptrace_riscv_buffer_swap,
.swap_end = esp_apptrace_riscv_buffer_swap_end,
.host_data_pending = esp_apptrace_riscv_host_data_pending,
};
static esp_apptrace_riscv_data_t s_trace_hw_data = {
.membufs = {
.hw = &s_trace_proto_hw,
},
};
static esp_apptrace_hw_t s_trace_hw = {
.init = esp_apptrace_riscv_init,
.get_up_buffer = esp_apptrace_riscv_up_buffer_get,
.put_up_buffer = esp_apptrace_riscv_up_buffer_put,
.flush_up_buffer_nolock = esp_apptrace_riscv_flush_nolock,
.flush_up_buffer = esp_apptrace_riscv_flush,
.down_buffer_config = esp_apptrace_riscv_down_buffer_config,
.get_down_buffer = esp_apptrace_riscv_down_buffer_get,
.put_down_buffer = esp_apptrace_riscv_down_buffer_put,
.host_is_connected = esp_apptrace_riscv_host_is_connected,
.set_header_size = esp_apptrace_riscv_set_header_size,
};
*data = &s_trace_hw_data;
return &s_trace_hw;
}

View File

@@ -1,546 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
//
// How It Works
// ************
// 1. Components Overview
// ======================
// Xtensa has useful feature: TRAX debug module. It allows recording program execution flow at run-time without disturbing CPU.
// Execution flow data are written to configurable Trace RAM block. Besides accessing Trace RAM itself TRAX module also allows to read/write
// trace memory via its registers by means of JTAG, APB or ERI transactions.
// ESP32 has two Xtensa cores with separate TRAX modules on them and provides two special memory regions to be used as trace memory.
// Chip allows muxing access to those trace memory blocks in such a way that while one block is accessed by CPUs another one can be accessed by host
// by means of reading/writing TRAX registers via JTAG. Blocks muxing is configurable at run-time and allows switching trace memory blocks between
// accessors in round-robin fashion so they can read/write separate memory blocks without disturbing each other.
// This module implements application tracing feature based on above mechanisms. It allows to transfer arbitrary user data to/from
// host via JTAG with minimal impact on system performance. This module is implied to be used in the following tracing scheme.
// ------>------ ----- (host components) -----
// | | | |
// ------------------- ----------------------- ----------------------- ---------------- ------ --------- -----------------
// |trace data source|-->|target tracing module|<--->|TRAX_MEM0 | TRAX_MEM1|---->|TRAX_DATA_REGS|<-->|JTAG|<--->|OpenOCD|-->|trace data sink|
// ------------------- ----------------------- ----------------------- ---------------- ------ --------- -----------------
// | | | |
// | ------<------ ---------------- |
// |<------------------------------------------->|TRAX_CTRL_REGS|<---->|
// ----------------
// In general tracing goes in the following way. User application requests tracing module to send some data by calling esp_apptrace_buffer_get(),
// module allocates necessary buffer in current input trace block. Then user fills received buffer with data and calls esp_apptrace_buffer_put().
// When current input trace block is filled with app data it is exposed to host and the second block becomes input one and buffer filling restarts.
// While target application fills one TRAX block host reads another one via JTAG.
// This module also allows communication in the opposite direction: from host to target. As it was said ESP32 and host can access different TRAX blocks
// simultaneously, so while target writes trace data to one block host can write its own data (e.g. tracing commands) to another one then when
// blocks are switched host receives trace data and target receives data written by host application. Target user application can read host data
// by calling esp_apptrace_read() API.
// To control buffer switching and for other communication purposes this implementation uses some TRAX registers. It is safe since HW TRAX tracing
// can not be used along with application tracing feature so these registers are freely readable/writeable via JTAG from host and via ERI from ESP32 cores.
// Overhead of this implementation on target CPU is produced only by allocating/managing buffers and copying of data.
// On the host side special OpenOCD command must be used to read trace data.
// 2. TRAX Registers layout
// ========================
// This module uses two TRAX HW registers and one Performance Monitor register to communicate with host SW (OpenOCD).
// - Control register uses TRAX_DELAYCNT as storage. Only lower 24 bits of TRAX_DELAYCNT are writable. Control register has the following bitfields:
// | 31..XXXXXX..24 | 23 .(host_connect). 23| 22..(block_id)..15 | 14..(block_len)..0 |
// 14..0 bits - actual length of user data in trace memory block. Target updates it every time it fills memory block and exposes it to host.
// Host writes zero to this field when it finishes reading exposed block;
// 21..15 bits - trace memory block transfer ID. Block counter. It can overflow. Updated by target, host should not modify it. Actually can be 2 bits;
// 22 bit - 'host data present' flag. If set to one there is data from host, otherwise - no host data;
// 23 bit - 'host connected' flag. If zero then host is not connected and tracing module works in post-mortem mode, otherwise in streaming mode;
// - Status register uses TRAX_TRIGGERPC as storage. If this register is not zero then current CPU is changing TRAX registers and
// this register holds address of the instruction which application will execute when it finishes with those registers modifications.
// See 'Targets Connection' section for details.
// - CRC16 register uses ERI_PERFMON_PM1 as storage. This register is used to store CRC16 checksum of the exposed trace memory block.
// The register has the following format:
// | 31..16 (CRC indicator) | 15..0 (CRC16 value) |
// CRC indicator (0xA55A) is used to distinguish valid CRC values from other data that might be in the register.
// CRC16 is calculated over the entire exposed block and is updated every time a block is exposed to the host.
// This allows the host to verify data integrity of the received trace data.
// 3. Modes of operation
// =====================
// This module supports two modes of operation:
// - Post-mortem mode. This is the default mode. In this mode application tracing module does not check whether host has read all the data from block
// exposed to it and switches block in any case. The mode does not need host interaction for operation and so can be useful when only the latest
// trace data are necessary, e.g. for analyzing crashes. On panic the latest data from current input block are exposed to host and host can read them.
// It can happen that system panic occurs when there are very small amount of data which are not exposed to host yet (e.g. crash just after the
// TRAX block switch). In this case the previous 16KB of collected data will be dropped and host will see the latest, but very small piece of trace.
// It can be insufficient to diagnose the problem. To avoid such situations there is menuconfig option
// CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH
// which controls the threshold for flushing data in case of panic.
// - Streaming mode. Tracing module enters this mode when host connects to target and sets respective bits in control registers (per core).
// In this mode before switching the block tracing module waits for the host to read all the data from the previously exposed block.
// On panic tracing module also waits (timeout is configured via menuconfig via CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO) for the host to read all data.
// 4. Communication Protocol
// =========================
// 4.1 Trace Memory Blocks
// -----------------------
// Communication is controlled via special register. Host periodically polls control register on each core to find out if there are any data available.
// When current input memory block is filled it is exposed to host and 'block_len' and 'block_id' fields are updated in the control register.
// Host reads new register value and according to it's value starts reading data from exposed block. Meanwhile target starts filling another trace block.
// When host finishes reading the block it clears 'block_len' field in control register indicating to the target that it is ready to accept the next one.
// If the host has some data to transfer to the target it writes them to trace memory block before clearing 'block_len' field. Then it sets
// 'host_data_present' bit and clears 'block_len' field in control register. Upon every block switch target checks 'host_data_present' bit and if it is set
// reads them to down buffer before writing any trace data to switched TRAX block.
// 4.2 User Data Chunks Level
// --------------------------
// Since trace memory block is shared between user data chunks and data copying is performed on behalf of the API user (in its normal context) in
// multithreading environment it can happen that task/ISR which copies data is preempted by another high prio task/ISR. So it is possible situation
// that task/ISR will fail to complete filling its data chunk before the whole trace block is exposed to the host. To handle such conditions tracing
// module prepends all user data chunks with header which contains allocated buffer size and actual data length within it. OpenOCD command
// which reads application traces reports error when it reads incomplete user data block.
// Data which are transffered from host to target are also prepended with a header. Down channel data header is simple and consists of one two bytes field
// containing length of host data following the header.
// 4.3 Target Connection/Disconnection
// -----------------------------------
// When host is going to start tracing in streaming mode it needs to put both ESP32 cores into initial state when 'host connected' bit is set
// on both cores. To accomplish this host halts both cores and sets this bit in TRAX registers. But target code can be halted in state when it has read control
// register but has not updated its value. To handle such situations target code indicates to the host that it is updating control register by writing
// non-zero value to status register. Actually it writes address of the instruction which it will execute when it finishes with
// the registers update. When target is halted during control register update host sets breakpoint at the address from status register and resumes CPU.
// After target code finishes with register update it is halted on breakpoint, host detects it and safely sets 'host connected' bit. When both cores
// are set up they are resumed. Tracing starts without further intrusion into CPUs work.
// When host is going to stop tracing in streaming mode it needs to disconnect targets. Disconnection process is done using the same algorithm
// as for connecting, but 'host connected' bits are cleared on ESP32 cores.
// 5. Module Access Synchronization
// ================================
// Access to internal module's data is synchronized with custom mutex. Mutex is a wrapper for portMUX_TYPE and uses almost the same sync mechanism as in
// vPortCPUAcquireMutex/vPortCPUReleaseMutex. The mechanism uses S32C1I Xtensa instruction to implement exclusive access to module's data from tasks and
// ISRs running on both cores. Also custom mutex allows specifying timeout for locking operation. Locking routine checks underlying mutex in cycle until
// it gets its ownership or timeout expires. The differences of application tracing module's mutex implementation from vPortCPUAcquireMutex/vPortCPUReleaseMutex are:
// - Support for timeouts.
// - Local IRQs for CPU which owns the mutex are disabled till the call to unlocking routine. This is made to avoid possible task's prio inversion.
// When low prio task takes mutex and enables local IRQs gets preempted by high prio task which in its turn can try to acquire mutex using infinite timeout.
// So no local task switch occurs when mutex is locked. But this does not apply to tasks on another CPU.
// WARNING: Priority inversion can happen when low prio task works on one CPU and medium and high prio tasks work on another.
// WARNING: Care must be taken when selecting timeout values for trace calls from ISRs. Tracing module does not care about watchdogs when waiting
// on internal locks and for host to complete previous block reading, so if timeout value exceeds watchdog's one it can lead to the system reboot.
// 6. Timeouts
// ===========
// Timeout mechanism is based on xthal_get_ccount() routine and supports timeout values in microseconds.
// There are two situations when task/ISR can be delayed by tracing API call. Timeout mechanism takes into account both conditions:
// - Trace data are locked by another task/ISR. When waiting on trace data lock.
// - Current TRAX memory input block is full when working in streaming mode (host is connected). When waiting for host to complete previous block reading.
// When waiting for any of above conditions xthal_get_ccount() is called periodically to calculate time elapsed from trace API routine entry. When elapsed
// time exceeds specified timeout value operation is canceled and ESP_ERR_TIMEOUT code is returned.
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"
#include "soc/tracemem_config.h"
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#include "soc/sensitive_reg.h"
#endif
#include "eri.h"
#include "esp_private/trax.h"
#include "esp_cpu.h"
#include "esp_log.h"
#include "esp_app_trace_membufs_proto.h"
#include "esp_app_trace_port.h"
#include "esp_rom_crc.h"
// TRAX is disabled, so we use its registers for our own purposes
// | 31..XXXXXX..24 | 23 .(host_connect). 23 | 22 .(host_data). 22| 21..(block_id)..15 | 14..(block_len)..0 |
#define ESP_APPTRACE_TRAX_CTRL_REG ERI_TRAX_DELAYCNT
#define ESP_APPTRACE_TRAX_STAT_REG ERI_TRAX_TRIGGERPC
#define ESP_APPTRACE_TRAX_CRC16_REG ERI_PERFMON_PM1
#define ESP_APPTRACE_CRC_INDICATOR (0xA55AU << 16)
#define ESP_APPTRACE_TRAX_BLOCK_LEN_MSK 0x7FFFUL
#define ESP_APPTRACE_TRAX_BLOCK_LEN(_l_) ((_l_) & ESP_APPTRACE_TRAX_BLOCK_LEN_MSK)
#define ESP_APPTRACE_TRAX_BLOCK_LEN_GET(_v_) ((_v_) & ESP_APPTRACE_TRAX_BLOCK_LEN_MSK)
#define ESP_APPTRACE_TRAX_BLOCK_ID_MSK 0x7FUL
#define ESP_APPTRACE_TRAX_BLOCK_ID(_id_) (((_id_) & ESP_APPTRACE_TRAX_BLOCK_ID_MSK) << 15)
#define ESP_APPTRACE_TRAX_BLOCK_ID_GET(_v_) (((_v_) >> 15) & ESP_APPTRACE_TRAX_BLOCK_ID_MSK)
#define ESP_APPTRACE_TRAX_HOST_DATA (1 << 22)
#define ESP_APPTRACE_TRAX_HOST_CONNECT (1 << 23)
#define ESP_APPTRACE_TRAX_INITED(_hw_) ((_hw_)->inited & (1 << esp_cpu_get_core_id()))
#define ESP_APPTRACE_TRAX_BLOCK_SIZE (0x4000UL)
/** TRAX HW transport data */
typedef struct {
uint8_t inited;
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_lock_t lock; // sync lock
#endif
esp_apptrace_membufs_proto_data_t membufs;
} esp_apptrace_trax_data_t;
const static char *TAG = "esp_apptrace";
static uint8_t * const s_trax_blocks[] = {
(uint8_t *)TRACEMEM_BLK0_ADDR,
(uint8_t *)TRACEMEM_BLK1_ADDR
};
static esp_err_t esp_apptrace_trax_lock(void *hw_data, esp_apptrace_tmo_t *tmo)
{
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_trax_data_t *trax_data = hw_data;
esp_err_t ret = esp_apptrace_lock_take(&trax_data->lock, tmo);
if (ret != ESP_OK) {
return ESP_FAIL;
}
#endif
return ESP_OK;
}
static esp_err_t esp_apptrace_trax_unlock(void *hw_data)
{
esp_err_t ret = ESP_OK;
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_trax_data_t *trax_data = hw_data;
ret = esp_apptrace_lock_give(&trax_data->lock);
#endif
return ret;
}
static inline void esp_apptrace_trax_hw_init(void)
{
// Stop trace, if any (on the current CPU)
eri_write(ERI_TRAX_TRAXCTRL, TRAXCTRL_TRSTP);
eri_write(ERI_TRAX_TRAXCTRL, TRAXCTRL_TMEN);
eri_write(ESP_APPTRACE_TRAX_CTRL_REG, ESP_APPTRACE_TRAX_BLOCK_ID(0));
// this is for OpenOCD to let him know where stub entries vector is resided
// must be read by host before any transfer using TRAX
eri_write(ESP_APPTRACE_TRAX_STAT_REG, 0);
ESP_APPTRACE_LOGI("Initialized TRAX on CPU%d", esp_cpu_get_core_id());
}
static inline void esp_apptrace_trax_select_memory_block(int block_num)
{
// select memory block to be exposed to the TRAX module (accessed by host)
#if CONFIG_IDF_TARGET_ESP32
DPORT_WRITE_PERI_REG(DPORT_TRACEMEM_MUX_MODE_REG, block_num ? TRACEMEM_MUX_BLK0_ONLY : TRACEMEM_MUX_BLK1_ONLY);
#elif CONFIG_IDF_TARGET_ESP32S2
WRITE_PERI_REG(DPORT_PMS_OCCUPY_3_REG, block_num ? BIT(TRACEMEM_MUX_BLK0_NUM - 4) : BIT(TRACEMEM_MUX_BLK1_NUM - 4));
#elif CONFIG_IDF_TARGET_ESP32S3
// select memory block to be exposed to the TRAX module (accessed by host)
uint32_t block_bits = block_num ? TRACEMEM_CORE0_MUX_BLK_BITS(TRACEMEM_MUX_BLK0_NUM)
: TRACEMEM_CORE0_MUX_BLK_BITS(TRACEMEM_MUX_BLK1_NUM);
block_bits |= block_num ? TRACEMEM_CORE1_MUX_BLK_BITS(TRACEMEM_MUX_BLK0_NUM)
: TRACEMEM_CORE1_MUX_BLK_BITS(TRACEMEM_MUX_BLK1_NUM);
ESP_EARLY_LOGV(TAG, "Select block %d @ %p (bits 0x%" PRIx32 ")", block_num, s_trax_blocks[block_num], block_bits);
DPORT_WRITE_PERI_REG(SENSITIVE_INTERNAL_SRAM_USAGE_2_REG, block_bits);
#endif
}
static inline void esp_apptrace_trax_memory_enable(void)
{
#if CONFIG_IDF_TARGET_ESP32
/* Enable trace memory on PRO CPU */
DPORT_WRITE_PERI_REG(DPORT_PRO_TRACEMEM_ENA_REG, DPORT_PRO_TRACEMEM_ENA_M);
#if CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE == 0
/* Enable trace memory on APP CPU */
DPORT_WRITE_PERI_REG(DPORT_APP_TRACEMEM_ENA_REG, DPORT_APP_TRACEMEM_ENA_M);
#endif
#endif
}
/*****************************************************************************************/
/***************************** Apptrace HW iface *****************************************/
/*****************************************************************************************/
static esp_err_t esp_apptrace_trax_init(void *hw_data, const esp_apptrace_config_t *config)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
// 'esp_apptrace_trax_init()' is called on every core, so ensure to do main initialization only once
int core_id = esp_cpu_get_core_id();
if (core_id == 0) {
esp_apptrace_mem_block_t mem_blocks_cfg[2] = {
{
.start = s_trax_blocks[0],
.sz = ESP_APPTRACE_TRAX_BLOCK_SIZE
},
{
.start = s_trax_blocks[1],
.sz = ESP_APPTRACE_TRAX_BLOCK_SIZE
},
};
trax_data->membufs.header_size = ESP_APPTRACE_HEADER_SIZE_32;
esp_err_t res = esp_apptrace_membufs_init(&trax_data->membufs, mem_blocks_cfg);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to init membufs proto (%d)!", res);
return res;
}
#if CONFIG_APPTRACE_LOCK_ENABLE
esp_apptrace_lock_init(&trax_data->lock);
#endif
esp_apptrace_trax_memory_enable();
esp_apptrace_trax_select_memory_block(0);
}
// init TRAX on this CPU
esp_apptrace_trax_hw_init();
trax_data->inited |= 1 << core_id;
return ESP_OK;
}
static uint8_t *esp_apptrace_trax_up_buffer_get(void *hw_data, uint32_t size, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return NULL;
}
esp_err_t res = esp_apptrace_trax_lock(trax_data, tmo);
if (res != ESP_OK) {
return NULL;
}
uint8_t *ptr = esp_apptrace_membufs_up_buffer_get(&trax_data->membufs, size, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
if (esp_apptrace_trax_unlock(trax_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}
return ptr;
}
static esp_err_t esp_apptrace_trax_up_buffer_put(void *hw_data, uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return ESP_ERR_INVALID_STATE;
}
// Can avoid locking because esp_apptrace_membufs_up_buffer_put() just modifies buffer's header
esp_err_t res = esp_apptrace_membufs_up_buffer_put(&trax_data->membufs, ptr, tmo);
return res;
}
static void esp_apptrace_trax_down_buffer_config(void *hw_data, uint8_t *buf, uint32_t size)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return;
}
esp_apptrace_membufs_down_buffer_config(&trax_data->membufs, buf, size);
}
static uint8_t *esp_apptrace_trax_down_buffer_get(void *hw_data, uint32_t *size, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return NULL;
}
esp_err_t res = esp_apptrace_trax_lock(trax_data, tmo);
if (res != ESP_OK) {
return NULL;
}
uint8_t *ptr = esp_apptrace_membufs_down_buffer_get(&trax_data->membufs, size, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
if (esp_apptrace_trax_unlock(trax_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}
return ptr;
}
static esp_err_t esp_apptrace_trax_down_buffer_put(void *hw_data, uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return ESP_ERR_INVALID_STATE;
}
// Can avoid locking because esp_apptrace_membufs_down_buffer_put() does nothing
/*esp_err_t res = esp_apptrace_trax_lock(hw_data, tmo);
if (res != ESP_OK) {
return res;
}*/
esp_err_t res = esp_apptrace_membufs_down_buffer_put(&trax_data->membufs, ptr, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
/*if (esp_apptrace_trax_unlock(hw_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}*/
return res;
}
static bool esp_apptrace_trax_host_is_connected(void *hw_data)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return false;
}
return eri_read(ESP_APPTRACE_TRAX_CTRL_REG) & ESP_APPTRACE_TRAX_HOST_CONNECT ? true : false;
}
static esp_err_t esp_apptrace_trax_flush_nolock(void *hw_data, uint32_t min_sz, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return ESP_ERR_INVALID_STATE;
}
return esp_apptrace_membufs_flush_nolock(&trax_data->membufs, min_sz, tmo);
}
static esp_err_t esp_apptrace_trax_flush(void *hw_data, esp_apptrace_tmo_t *tmo)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
if (!ESP_APPTRACE_TRAX_INITED(trax_data)) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t res = esp_apptrace_trax_lock(trax_data, tmo);
if (res != ESP_OK) {
return res;
}
res = esp_apptrace_membufs_flush_nolock(&trax_data->membufs, 0, tmo);
// now we can safely unlock apptrace to allow other tasks/ISRs to get other buffers and write their data
if (esp_apptrace_trax_unlock(trax_data) != ESP_OK) {
assert(false && "Failed to unlock apptrace data!");
}
return res;
}
/*****************************************************************************************/
/************************** Membufs proto HW iface ***************************************/
/*****************************************************************************************/
static inline void esp_apptrace_trax_buffer_swap_lock(void)
{
extern uint32_t __esp_apptrace_trax_eri_updated;
// indicate to host that we are about to update.
// this is used only to place CPU into streaming mode at tracing startup
// before starting streaming host can halt us after we read ESP_APPTRACE_TRAX_CTRL_REG and before we updated it
// HACK: in this case host will set breakpoint just after ESP_APPTRACE_TRAX_CTRL_REG update,
// here we set address to set bp at
// enter ERI update critical section
eri_write(ESP_APPTRACE_TRAX_STAT_REG, (uint32_t)&__esp_apptrace_trax_eri_updated);
}
static __attribute__((noinline)) void esp_apptrace_trax_buffer_swap_unlock(void)
{
// exit ERI update critical section
eri_write(ESP_APPTRACE_TRAX_STAT_REG, 0x0);
// TODO: currently host sets breakpoint, use break instruction to stop;
// it will allow to use ESP_APPTRACE_TRAX_STAT_REG for other purposes
asm volatile(
" .global __esp_apptrace_trax_eri_updated\n"
"__esp_apptrace_trax_eri_updated:\n"); // host will set bp here to resolve collision at streaming start
}
static esp_err_t esp_apptrace_trax_buffer_swap_start(uint32_t curr_block_id)
{
esp_err_t res = ESP_OK;
esp_apptrace_trax_buffer_swap_lock();
uint32_t ctrl_reg = eri_read(ESP_APPTRACE_TRAX_CTRL_REG);
uint32_t host_connected = ESP_APPTRACE_TRAX_HOST_CONNECT & ctrl_reg;
if (host_connected) {
uint32_t acked_block = ESP_APPTRACE_TRAX_BLOCK_ID_GET(ctrl_reg);
uint32_t host_to_read = ESP_APPTRACE_TRAX_BLOCK_LEN_GET(ctrl_reg);
if (host_to_read != 0 || acked_block != (curr_block_id & ESP_APPTRACE_TRAX_BLOCK_ID_MSK)) {
ESP_APPTRACE_LOGD("HC[%d]: Can not switch %" PRIx32 " %" PRIu32 " %" PRIx32 " %" PRIx32 "/%" PRIx32,
esp_cpu_get_core_id(), ctrl_reg, host_to_read, acked_block,
curr_block_id & ESP_APPTRACE_TRAX_BLOCK_ID_MSK, curr_block_id);
res = ESP_ERR_NO_MEM;
goto _on_err;
}
}
return ESP_OK;
_on_err:
esp_apptrace_trax_buffer_swap_unlock();
return res;
}
static esp_err_t esp_apptrace_trax_buffer_swap_end(uint32_t new_block_id, uint32_t prev_block_len)
{
uint32_t ctrl_reg = eri_read(ESP_APPTRACE_TRAX_CTRL_REG);
uint32_t host_connected = ESP_APPTRACE_TRAX_HOST_CONNECT & ctrl_reg;
eri_write(ESP_APPTRACE_TRAX_CTRL_REG, ESP_APPTRACE_TRAX_BLOCK_ID(new_block_id) |
host_connected | ESP_APPTRACE_TRAX_BLOCK_LEN(prev_block_len));
esp_apptrace_trax_buffer_swap_unlock();
return ESP_OK;
}
static esp_err_t esp_apptrace_trax_buffer_swap(uint32_t new_block_id, uint32_t prev_block_len)
{
/* Before switching to the new block, calculate CRC16 of the current block */
if (prev_block_len > 0) {
const uint8_t *prev_block_start = s_trax_blocks[!((new_block_id % 2))];
uint16_t crc16 = esp_rom_crc16_le(0, prev_block_start, prev_block_len);
eri_write(ESP_APPTRACE_TRAX_CRC16_REG, crc16 | ESP_APPTRACE_CRC_INDICATOR);
ESP_APPTRACE_LOGD("CRC16:%x %d @%x", crc16, prev_block_len, prev_block_start);
}
esp_apptrace_trax_select_memory_block(new_block_id);
return ESP_OK;
}
static bool esp_apptrace_trax_host_data_pending(void)
{
uint32_t ctrl_reg = eri_read(ESP_APPTRACE_TRAX_CTRL_REG);
return (ctrl_reg & ESP_APPTRACE_TRAX_HOST_DATA) ? true : false;
}
static void esp_apptrace_trax_set_header_size(void *hw_data, esp_apptrace_header_size_t header_size)
{
esp_apptrace_trax_data_t *trax_data = hw_data;
trax_data->membufs.header_size = header_size;
}
esp_apptrace_hw_t *esp_apptrace_jtag_hw_get(void **data)
{
static esp_apptrace_membufs_proto_hw_t s_trax_proto_hw = {
.swap_start = esp_apptrace_trax_buffer_swap_start,
.swap = esp_apptrace_trax_buffer_swap,
.swap_end = esp_apptrace_trax_buffer_swap_end,
.host_data_pending = esp_apptrace_trax_host_data_pending,
};
static esp_apptrace_trax_data_t s_trax_hw_data = {
.membufs = {
.hw = &s_trax_proto_hw,
},
};
static esp_apptrace_hw_t s_trax_hw = {
.init = esp_apptrace_trax_init,
.get_up_buffer = esp_apptrace_trax_up_buffer_get,
.put_up_buffer = esp_apptrace_trax_up_buffer_put,
.flush_up_buffer_nolock = esp_apptrace_trax_flush_nolock,
.flush_up_buffer = esp_apptrace_trax_flush,
.down_buffer_config = esp_apptrace_trax_down_buffer_config,
.get_down_buffer = esp_apptrace_trax_down_buffer_get,
.put_down_buffer = esp_apptrace_trax_down_buffer_put,
.host_is_connected = esp_apptrace_trax_host_is_connected,
.set_header_size = esp_apptrace_trax_set_header_size,
};
*data = &s_trax_hw_data;
return &s_trax_hw;
}

View File

@@ -1,58 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_APP_TRACE_MEMBUFS_PROTO_H_
#define ESP_APP_TRACE_MEMBUFS_PROTO_H_
#include "esp_app_trace_util.h"
#ifdef __cplusplus
extern "C" {
#endif
/** TRAX HW transport state */
typedef struct {
uint32_t in_block; // input block ID
// TODO: change to uint16_t
uint32_t markers[2]; // block filling level markers
} esp_apptrace_membufs_state_t;
/** memory block parameters,
* should be packed, because it is read from the host */
typedef struct {
uint8_t *start; // start address
uint32_t sz; // size
} esp_apptrace_mem_block_t;
typedef struct {
esp_err_t (*swap_start)(uint32_t curr_block_id);
esp_err_t (*swap)(uint32_t new_block_id, uint32_t prev_block_len);
esp_err_t (*swap_end)(uint32_t new_block_id, uint32_t prev_block_len);
bool (*host_data_pending)(void);
} esp_apptrace_membufs_proto_hw_t;
typedef struct {
esp_apptrace_membufs_proto_hw_t * hw;
volatile esp_apptrace_membufs_state_t state; // state
esp_apptrace_mem_block_t blocks[2]; // memory blocks
// ring buffer control struct for data from host (down buffer)
esp_apptrace_rb_t rb_down;
int header_size; ///< Size of the trace header (2 or 4 bytes)
} esp_apptrace_membufs_proto_data_t;
esp_err_t esp_apptrace_membufs_init(esp_apptrace_membufs_proto_data_t *proto, const esp_apptrace_mem_block_t blocks_cfg[2]);
void esp_apptrace_membufs_down_buffer_config(esp_apptrace_membufs_proto_data_t *data, uint8_t *buf, uint32_t size);
uint8_t *esp_apptrace_membufs_down_buffer_get(esp_apptrace_membufs_proto_data_t *proto, uint32_t *size, esp_apptrace_tmo_t *tmo);
esp_err_t esp_apptrace_membufs_down_buffer_put(esp_apptrace_membufs_proto_data_t *proto, uint8_t *ptr, esp_apptrace_tmo_t *tmo);
uint8_t *esp_apptrace_membufs_up_buffer_get(esp_apptrace_membufs_proto_data_t *proto, uint32_t size, esp_apptrace_tmo_t *tmo);
esp_err_t esp_apptrace_membufs_up_buffer_put(esp_apptrace_membufs_proto_data_t *proto, uint8_t *ptr, esp_apptrace_tmo_t *tmo);
esp_err_t esp_apptrace_membufs_flush_nolock(esp_apptrace_membufs_proto_data_t *proto, uint32_t min_sz, esp_apptrace_tmo_t *tmo);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,12 +0,0 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_ESP32_APPTRACE_DESTINATION CONFIG_APPTRACE_DESTINATION
CONFIG_ESP32_APPTRACE_DEST_TRAX CONFIG_APPTRACE_DEST_JTAG
CONFIG_ESP32_APPTRACE_ENABLE CONFIG_ESP_TRACE_TRANSPORT_APPTRACE
CONFIG_ESP32_APPTRACE_LOCK_ENABLE CONFIG_APPTRACE_LOCK_ENABLE
CONFIG_ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO
CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH
CONFIG_SYSVIEW_TS_SOURCE_CCOUNT CONFIG_ESP_TRACE_TS_SOURCE_CCOUNT
CONFIG_SYSVIEW_TS_SOURCE_ESP_TIMER CONFIG_ESP_TRACE_TS_SOURCE_ESP_TIMER

View File

@@ -1,10 +0,0 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/app_trace/test_apps:
depends_components:
- esp_trace
- esp_driver_gptimer
disable:
- if: IDF_TARGET in ["esp32h21", "esp32h4", "esp32s31"]
temporary: true
reason: not support yet # TODO: [ESP32H21] IDF-11539 [ESP32H4] IDF-12325 [ESP32S31] IDF-14691

View File

@@ -1,9 +0,0 @@
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
list(PREPEND SDKCONFIG_DEFAULTS
"$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers"
"sdkconfig.defaults")
project(app_trace_test)

View File

@@ -1,11 +0,0 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
# app_trace test
To build and run this test app for app_trace related tests:
```bash
IDF_TARGET=esp32 idf.py @app_trace build flash monitor
```
`@app_trace` argument apply additional `idf.py` options, from [app_trace](app_trace) and file.

View File

@@ -1 +0,0 @@
-DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.app_trace" -B build/app_trace -DSDKCONFIG=build/app_trace/sdkconfig

View File

@@ -1,4 +0,0 @@
idf_component_register(SRCS "test_app_trace_main.c" "test_trace.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_trace unity esp_driver_gptimer
WHOLE_ARCHIVE)

View File

@@ -1,48 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#define TEST_MEMORY_LEAK_THRESHOLD_DEFAULT 0
static int leak_threshold = TEST_MEMORY_LEAK_THRESHOLD_DEFAULT;
void set_leak_threshold(int threshold)
{
leak_threshold = threshold;
}
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= leak_threshold, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
leak_threshold = TEST_MEMORY_LEAK_THRESHOLD_DEFAULT;
}
void app_main(void)
{
printf("Running app_trace component tests\n");
unity_run_menu();
}

View File

@@ -1,694 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "unity.h"
#include "driver/gptimer.h"
#include "esp_intr_alloc.h"
#include "esp_rom_sys.h"
#include "esp_cpu.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_app_trace.h"
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
#define ESP_APPTRACE_TEST_USE_PRINT_LOCK 0
#define ESP_APPTRACE_TEST_PRN_WRERR_MAX 5
#define ESP_APPTRACE_TEST_BLOCKS_BEFORE_CRASH 100
#define ESP_APPTRACE_TEST_BLOCK_SIZE 1024
const static char *TAG = "esp_apptrace_test";
#if ESP_APPTRACE_TEST_USE_PRINT_LOCK == 1
#define ESP_APPTRACE_TEST_LOG( format, ... ) \
do { \
BaseType_t ret; \
if (xPortInIsrContext()) \
ret = xSemaphoreTakeFromISR(s_print_lock, NULL); \
else \
ret = xSemaphoreTake(s_print_lock, portMAX_DELAY); \
if (ret == pdTRUE) { \
esp_rom_printf(format, ##__VA_ARGS__); \
if (xPortInIsrContext()) \
xSemaphoreGiveFromISR(s_print_lock, NULL); \
else \
xSemaphoreGive(s_print_lock); \
} \
} while(0)
#else
#define ESP_APPTRACE_TEST_LOG( format, ... ) \
do { \
esp_rom_printf(format, ##__VA_ARGS__); \
} while(0)
#endif
#define ESP_APPTRACE_TEST_LOG_LEVEL( _L_, level, format, ... ) \
do { \
if (LOG_LOCAL_LEVEL >= level) { \
ESP_APPTRACE_TEST_LOG(LOG_FORMAT(_L_, format), esp_log_early_timestamp(), TAG, ##__VA_ARGS__); \
} \
} while(0)
#define ESP_APPTRACE_TEST_LOGE( format, ... ) ESP_APPTRACE_TEST_LOG_LEVEL(E, ESP_LOG_ERROR, format, ##__VA_ARGS__)
#define ESP_APPTRACE_TEST_LOGW( format, ... ) ESP_APPTRACE_TEST_LOG_LEVEL(W, ESP_LOG_WARN, format, ##__VA_ARGS__)
#define ESP_APPTRACE_TEST_LOGI( format, ... ) ESP_APPTRACE_TEST_LOG_LEVEL(I, ESP_LOG_INFO, format, ##__VA_ARGS__)
#define ESP_APPTRACE_TEST_LOGD( format, ... ) ESP_APPTRACE_TEST_LOG_LEVEL(D, ESP_LOG_DEBUG, format, ##__VA_ARGS__)
#define ESP_APPTRACE_TEST_LOGV( format, ... ) ESP_APPTRACE_TEST_LOG_LEVEL(V, ESP_LOG_VERBOSE, format, ##__VA_ARGS__)
#define ESP_APPTRACE_TEST_LOGO( format, ... ) ESP_APPTRACE_TEST_LOG_LEVEL(E, ESP_LOG_NONE, format, ##__VA_ARGS__)
#define ESP_APPTRACE_TEST_WRITE(_b_, _s_) esp_apptrace_write(_b_, _s_, ESP_APPTRACE_TMO_INFINITE)
#define ESP_APPTRACE_TEST_WRITE_FROM_ISR(_b_, _s_) esp_apptrace_write(_b_, _s_, 0UL)
#define ESP_APPTRACE_TEST_WRITE_NOWAIT(_b_, _s_) esp_apptrace_write(_b_, _s_, 0)
typedef struct {
uint8_t *buf;
uint32_t buf_sz;
uint8_t mask;
uint32_t period; // trace write period in us
uint32_t wr_err;
uint32_t wr_cnt;
} esp_apptrace_test_gen_data_t;
typedef struct {
gptimer_handle_t gptimer;
bool (*isr_func)(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx);
esp_apptrace_test_gen_data_t data;
} esp_apptrace_test_timer_arg_t;
typedef struct {
int nowait;
int core;
int prio;
void (*task_func)(void *);
esp_apptrace_test_gen_data_t data;
volatile int stop;
SemaphoreHandle_t done;
uint32_t timers_num;
esp_apptrace_test_timer_arg_t *timers;
} esp_apptrace_test_task_arg_t;
typedef struct {
uint32_t tasks_num;
esp_apptrace_test_task_arg_t *tasks;
} esp_apptrace_test_cfg_t;
#if ESP_APPTRACE_TEST_USE_PRINT_LOCK == 1
static SemaphoreHandle_t s_print_lock;
#endif
static uint64_t esp_apptrace_test_ts_get(void);
static bool esp_apptrace_test_timer_isr(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
esp_apptrace_test_timer_arg_t *tim_arg = (esp_apptrace_test_timer_arg_t *)user_ctx;
uint32_t *ts = (uint32_t *)(tim_arg->data.buf + sizeof(uint32_t));
*ts = (uint32_t)esp_apptrace_test_ts_get();
memset(tim_arg->data.buf + 2 * sizeof(uint32_t), tim_arg->data.wr_cnt & tim_arg->data.mask, tim_arg->data.buf_sz - 2 * sizeof(uint32_t));
int res = ESP_APPTRACE_TEST_WRITE_FROM_ISR(tim_arg->data.buf, tim_arg->data.buf_sz);
if (res == ESP_OK) {
tim_arg->data.wr_err = 0;
}
tim_arg->data.wr_cnt++;
return true;
}
static bool esp_apptrace_test_timer_isr_crash(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
esp_apptrace_test_timer_arg_t *tim_arg = (esp_apptrace_test_timer_arg_t *)user_ctx;
if (tim_arg->data.wr_cnt < ESP_APPTRACE_TEST_BLOCKS_BEFORE_CRASH) {
uint32_t *ts = (uint32_t *)(tim_arg->data.buf + sizeof(uint32_t));
*ts = (uint32_t)esp_apptrace_test_ts_get();//xthal_get_ccount();//xTaskGetTickCount();
memset(tim_arg->data.buf + 2 * sizeof(uint32_t), tim_arg->data.wr_cnt & tim_arg->data.mask, tim_arg->data.buf_sz - 2 * sizeof(uint32_t));
int res = ESP_APPTRACE_TEST_WRITE_FROM_ISR(tim_arg->data.buf, tim_arg->data.buf_sz);
if (res != ESP_OK) {
esp_rom_printf("tim-%p: Failed to write trace %d %" PRIx32 "!\n", tim_arg->gptimer, res, tim_arg->data.wr_cnt & tim_arg->data.mask);
} else {
esp_rom_printf("tim-%p: Written chunk%" PRIu32 " %" PRIu32 " bytes, %" PRIx32 "\n",
timer, tim_arg->data.wr_cnt, tim_arg->data.buf_sz, tim_arg->data.wr_cnt & tim_arg->data.mask);
tim_arg->data.wr_cnt++;
}
} else {
uint32_t *ptr = 0;
*ptr = 1000;
}
return true;
}
static void esp_apptrace_dummy_task(void *p)
{
esp_apptrace_test_task_arg_t *arg = (esp_apptrace_test_task_arg_t *) p;
TickType_t tmo_ticks = arg->data.period / (1000 * portTICK_PERIOD_MS);
ESP_APPTRACE_TEST_LOGI("%p: run dummy task (period %" PRIu32 " us, %" PRIu32 " timers)", xTaskGetCurrentTaskHandle(), arg->data.period, arg->timers_num);
for (int i = 0; i < arg->timers_num; i++) {
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000,
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &arg->timers[i].gptimer));
*(uint32_t *)arg->timers[i].data.buf = (uint32_t)arg->timers[i].gptimer | (1 << 31);
ESP_APPTRACE_TEST_LOGI("%p: start timer %p period %" PRIu32 " us", xTaskGetCurrentTaskHandle(), arg->timers[i].gptimer, arg->timers[i].data.period);
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = arg->timers[i].data.period,
.flags.auto_reload_on_alarm = true,
};
gptimer_event_callbacks_t cbs = {
.on_alarm = arg->timers[i].isr_func,
};
TEST_ESP_OK(gptimer_register_event_callbacks(arg->timers[i].gptimer, &cbs, &arg->timers[i]));
TEST_ESP_OK(gptimer_enable(arg->timers[i].gptimer));
TEST_ESP_OK(gptimer_set_alarm_action(arg->timers[i].gptimer, &alarm_config));
TEST_ESP_OK(gptimer_start(arg->timers[i].gptimer));
}
int i = 0;
while (!arg->stop) {
ESP_APPTRACE_TEST_LOGD("%p: dummy task work %d.%d", xTaskGetCurrentTaskHandle(), esp_cpu_get_core_id(), i++);
if (tmo_ticks) {
vTaskDelay(tmo_ticks);
}
}
for (int i = 0; i < arg->timers_num; i++) {
TEST_ESP_OK(gptimer_stop(arg->timers[i].gptimer));
TEST_ESP_OK(gptimer_disable(arg->timers[i].gptimer));
TEST_ESP_OK(gptimer_del_timer(arg->timers[i].gptimer));
}
xSemaphoreGive(arg->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
static void esp_apptrace_test_task(void *p)
{
esp_apptrace_test_task_arg_t *arg = (esp_apptrace_test_task_arg_t *) p;
int res;
TickType_t tmo_ticks = arg->data.period / (1000 * portTICK_PERIOD_MS);
ESP_APPTRACE_TEST_LOGI("%p: run (period %" PRIu32 " us, stamp mask %" PRIx8 ", %" PRIu32 " timers)", xTaskGetCurrentTaskHandle(), arg->data.period, arg->data.mask, arg->timers_num);
for (int i = 0; i < arg->timers_num; i++) {
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000,
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &arg->timers[i].gptimer));
*(uint32_t *)arg->timers[i].data.buf = ((uint32_t)arg->timers[i].gptimer) | (1 << 31) | (esp_cpu_get_core_id() ? 0x1 : 0);
ESP_APPTRACE_TEST_LOGI("%p: start timer %p period %" PRIu32 " us", xTaskGetCurrentTaskHandle(), arg->timers[i].gptimer, arg->timers[i].data.period);
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = arg->timers[i].data.period,
.flags.auto_reload_on_alarm = true,
};
gptimer_event_callbacks_t cbs = {
.on_alarm = arg->timers[i].isr_func,
};
TEST_ESP_OK(gptimer_register_event_callbacks(arg->timers[i].gptimer, &cbs, &arg->timers[i]));
TEST_ESP_OK(gptimer_enable(arg->timers[i].gptimer));
TEST_ESP_OK(gptimer_set_alarm_action(arg->timers[i].gptimer, &alarm_config));
TEST_ESP_OK(gptimer_start(arg->timers[i].gptimer));
}
*(uint32_t *)arg->data.buf = (uint32_t)xTaskGetCurrentTaskHandle() | (esp_cpu_get_core_id() ? 0x1 : 0);
arg->data.wr_cnt = 0;
arg->data.wr_err = 0;
while (!arg->stop) {
uint32_t *ts = (uint32_t *)(arg->data.buf + sizeof(uint32_t));
*ts = (uint32_t)esp_apptrace_test_ts_get();
memset(arg->data.buf + 2 * sizeof(uint32_t), arg->data.wr_cnt & arg->data.mask, arg->data.buf_sz - 2 * sizeof(uint32_t));
// ESP_APPTRACE_TEST_LOGD("%x:%x: Write chunk%d %d bytes, %x", xTaskGetCurrentTaskHandle(), *ts, arg->data.wr_cnt, arg->data.buf_sz, arg->data.wr_cnt & arg->data.mask);
if (arg->nowait) {
res = ESP_APPTRACE_TEST_WRITE_NOWAIT(arg->data.buf, arg->data.buf_sz);
} else {
res = ESP_APPTRACE_TEST_WRITE(arg->data.buf, arg->data.buf_sz);
}
if (res) {
if (1) { //arg->data.wr_err++ < ESP_APPTRACE_TEST_PRN_WRERR_MAX) {
ESP_APPTRACE_TEST_LOGE("%p: Failed to write trace %d %" PRIx32 "!", xTaskGetCurrentTaskHandle(), res, arg->data.wr_cnt & arg->data.mask);
if (arg->data.wr_err == ESP_APPTRACE_TEST_PRN_WRERR_MAX) {
ESP_APPTRACE_TEST_LOGE("\n");
}
}
} else {
if (0) {
ESP_APPTRACE_TEST_LOGD("%p:%" PRIx32 ": Written chunk%" PRIu32 " %" PRIu32 " bytes, %" PRIx32, xTaskGetCurrentTaskHandle(), *ts, arg->data.wr_cnt, arg->data.buf_sz, arg->data.wr_cnt & arg->data.mask);
}
arg->data.wr_err = 0;
}
arg->data.wr_cnt++;
if (tmo_ticks) {
vTaskDelay(tmo_ticks);
}
}
for (int i = 0; i < arg->timers_num; i++) {
TEST_ESP_OK(gptimer_stop(arg->timers[i].gptimer));
TEST_ESP_OK(gptimer_disable(arg->timers[i].gptimer));
TEST_ESP_OK(gptimer_del_timer(arg->timers[i].gptimer));
}
xSemaphoreGive(arg->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
static void esp_apptrace_test_task_crash(void *p)
{
esp_apptrace_test_task_arg_t *arg = (esp_apptrace_test_task_arg_t *) p;
ESP_APPTRACE_TEST_LOGE("%p: run (period %" PRIu32 " us, stamp mask %" PRIx8 ", %" PRIu32 " timers)", xTaskGetCurrentTaskHandle(), arg->data.period, arg->data.mask, arg->timers_num);
arg->data.wr_cnt = 0;
*(uint32_t *)arg->data.buf = (uint32_t)xTaskGetCurrentTaskHandle();
for (int i = 0; i < ESP_APPTRACE_TEST_BLOCKS_BEFORE_CRASH; i++) {
uint32_t *ts = (uint32_t *)(arg->data.buf + sizeof(uint32_t));
*ts = (uint32_t)esp_apptrace_test_ts_get();
memset(arg->data.buf + sizeof(uint32_t), arg->data.wr_cnt & arg->data.mask, arg->data.buf_sz - sizeof(uint32_t));
int res = ESP_APPTRACE_TEST_WRITE(arg->data.buf, arg->data.buf_sz);
if (res) {
ESP_APPTRACE_TEST_LOGE("%p: Failed to write trace %d %" PRIx32 "!", xTaskGetCurrentTaskHandle(), res, arg->data.wr_cnt & arg->data.mask);
} else {
ESP_APPTRACE_TEST_LOGD("%p: Written chunk%" PRIu32 " %" PRIu32 " bytes, %" PRIx32, xTaskGetCurrentTaskHandle(), arg->data.wr_cnt, arg->data.buf_sz, arg->data.wr_cnt & arg->data.mask);
}
arg->data.wr_cnt++;
}
vTaskDelay(500);
uint32_t *ptr = 0;
*ptr = 1000;
xSemaphoreGive(arg->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
static gptimer_handle_t ts_gptimer;
static uint64_t esp_apptrace_test_ts_get(void)
{
uint64_t ts = 0;
gptimer_get_raw_count(ts_gptimer, &ts);
return ts;
}
static void esp_apptrace_test_ts_init(void)
{
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 10000000,
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &ts_gptimer));
ESP_APPTRACE_TEST_LOGI("Use timer %p for TS", ts_gptimer);
TEST_ESP_OK(gptimer_enable(ts_gptimer));
TEST_ESP_OK(gptimer_start(ts_gptimer));
}
static void esp_apptrace_test_ts_cleanup(void)
{
TEST_ESP_OK(gptimer_stop(ts_gptimer));
TEST_ESP_OK(gptimer_disable(ts_gptimer));
TEST_ESP_OK(gptimer_del_timer(ts_gptimer));
ts_gptimer = NULL;
}
static void esp_apptrace_test(esp_apptrace_test_cfg_t *test_cfg)
{
esp_apptrace_test_task_arg_t dummy_task_arg = {
.core = 0,
.prio = 3,
.task_func = esp_apptrace_test_task_crash,
.data.buf = NULL,
.data.buf_sz = 0,
.data.period = 500000,
.timers_num = 0,
.timers = NULL,
};
#if ESP_APPTRACE_TEST_USE_PRINT_LOCK == 1
s_print_lock = xSemaphoreCreateBinary();
if (!s_print_lock) {
esp_rom_printf("%s: Failed to create print lock!", TAG);
return;
}
xSemaphoreGive(s_print_lock);
#else
#endif
for (int i = 0; i < test_cfg->tasks_num; i++) {
test_cfg->tasks[i].data.mask = 0xFF;
test_cfg->tasks[i].stop = 0;
test_cfg->tasks[i].done = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(test_cfg->tasks[i].done);
for (int k = 0; k < test_cfg->tasks[i].timers_num; k++) {
test_cfg->tasks[i].timers[k].data.mask = 0xFF;
}
}
esp_apptrace_test_ts_init();
for (int i = 0; i < test_cfg->tasks_num; i++) {
char name[30];
TaskHandle_t thnd;
sprintf(name, "apptrace_test%d", i);
xTaskCreatePinnedToCore(test_cfg->tasks[i].task_func, name, 2048, &test_cfg->tasks[i], test_cfg->tasks[i].prio, &thnd, test_cfg->tasks[i].core);
ESP_APPTRACE_TEST_LOGI("Created task %p", thnd);
}
xTaskCreatePinnedToCore(esp_apptrace_dummy_task, "dummy0", 2048, &dummy_task_arg, dummy_task_arg.prio, NULL, 0);
#if CONFIG_FREERTOS_UNICORE == 0
xTaskCreatePinnedToCore(esp_apptrace_dummy_task, "dummy1", 2048, &dummy_task_arg, dummy_task_arg.prio, NULL, 1);
#endif
for (int i = 0; i < test_cfg->tasks_num; i++) {
//arg1.stop = 1;
xSemaphoreTake(test_cfg->tasks[i].done, portMAX_DELAY);
}
for (int i = 0; i < test_cfg->tasks_num; i++) {
if (test_cfg->tasks[i].done) {
vSemaphoreDelete(test_cfg->tasks[i].done);
}
}
esp_apptrace_test_ts_cleanup();
#if ESP_APPTRACE_TEST_USE_PRINT_LOCK == 1
vSemaphoreDelete(s_print_lock);
#else
#endif
}
static esp_apptrace_test_task_arg_t s_test_tasks[4];
static esp_apptrace_test_timer_arg_t s_test_timers[2];
static uint8_t s_bufs[6][ESP_APPTRACE_TEST_BLOCK_SIZE];
TEST_CASE("App trace test (1 task + 1 crashed timer ISR @ 1 core)", "[trace][ignore]")
{
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 1,
.tasks = s_test_tasks,
};
memset(s_test_timers, 0, sizeof(s_test_timers));
memset(s_test_tasks, 0, sizeof(s_test_tasks));
s_test_timers[0].isr_func = esp_apptrace_test_timer_isr_crash;
s_test_timers[0].data.buf = s_bufs[0];
s_test_timers[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_timers[0].data.period = 1000;
s_test_tasks[0].core = 0;
s_test_tasks[0].prio = 3;
s_test_tasks[0].task_func = esp_apptrace_dummy_task;
s_test_tasks[0].data.buf = NULL;
s_test_tasks[0].data.buf_sz = 0;
s_test_tasks[0].data.period = 1000000;
s_test_tasks[0].timers_num = 1;
s_test_tasks[0].timers = s_test_timers;
esp_apptrace_test(&test_cfg);
}
TEST_CASE("App trace test (1 crashed task)", "[trace][ignore]")
{
esp_apptrace_test_task_arg_t s_test_tasks[1];
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 1,
.tasks = s_test_tasks,
};
memset(s_test_tasks, 0, sizeof(s_test_tasks));
s_test_tasks[0].core = 0;
s_test_tasks[0].prio = 3;
s_test_tasks[0].task_func = esp_apptrace_test_task_crash;
s_test_tasks[0].data.buf = s_bufs[0];
s_test_tasks[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_tasks[0].data.period = 6000;
s_test_tasks[0].timers_num = 0;
s_test_tasks[0].timers = NULL;
esp_apptrace_test(&test_cfg);
}
#if CONFIG_FREERTOS_UNICORE == 0
TEST_CASE("App trace test (2 tasks + 1 timer @ each core", "[trace][ignore]")
{
int ntask = 0;
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 4,
.tasks = s_test_tasks,
};
memset(s_test_tasks, 0, sizeof(s_test_tasks));
memset(s_test_timers, 0, sizeof(s_test_timers));
s_test_timers[0].isr_func = esp_apptrace_test_timer_isr;
s_test_timers[0].data.buf = s_bufs[0];
s_test_timers[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_timers[0].data.period = 150;
s_test_timers[1].isr_func = esp_apptrace_test_timer_isr;
s_test_timers[1].data.buf = s_bufs[1];
s_test_timers[1].data.buf_sz = sizeof(s_bufs[1]);
s_test_timers[1].data.period = 150;
s_test_tasks[ntask].core = 0;
s_test_tasks[ntask].prio = 4;
s_test_tasks[ntask].task_func = esp_apptrace_test_task;
s_test_tasks[ntask].data.buf = s_bufs[2];
s_test_tasks[ntask].data.buf_sz = sizeof(s_bufs[2]);
s_test_tasks[ntask].data.period = 1000;
s_test_tasks[ntask].timers_num = 1;
s_test_tasks[ntask].timers = &s_test_timers[0];
ntask++;
s_test_tasks[ntask].core = 0;
s_test_tasks[ntask].prio = 3;
s_test_tasks[ntask].task_func = esp_apptrace_test_task;
s_test_tasks[ntask].data.buf = s_bufs[3];
s_test_tasks[ntask].data.buf_sz = sizeof(s_bufs[3]);
s_test_tasks[ntask].data.period = 0;
s_test_tasks[ntask].timers_num = 0;
s_test_tasks[ntask].timers = NULL;
ntask++;
s_test_tasks[ntask].core = 1;
s_test_tasks[ntask].prio = 4;
s_test_tasks[ntask].task_func = esp_apptrace_test_task;
s_test_tasks[ntask].data.buf = s_bufs[4];
s_test_tasks[ntask].data.buf_sz = sizeof(s_bufs[4]);
s_test_tasks[ntask].data.period = 1000;
s_test_tasks[ntask].timers_num = 1;
s_test_tasks[ntask].timers = &s_test_timers[1];
ntask++;
s_test_tasks[ntask].core = 1;
s_test_tasks[ntask].prio = 3;
s_test_tasks[ntask].task_func = esp_apptrace_test_task;
s_test_tasks[ntask].data.buf = s_bufs[5];
s_test_tasks[ntask].data.buf_sz = sizeof(s_bufs[5]);
s_test_tasks[ntask].data.period = 0;
s_test_tasks[ntask].timers_num = 0;
s_test_tasks[ntask].timers = NULL;
ntask++;
esp_apptrace_test(&test_cfg);
}
#endif
TEST_CASE("App trace test (1 task + 1 timer @ 1 core)", "[trace][ignore]")
{
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 1,
.tasks = s_test_tasks,
};
memset(s_test_timers, 0, sizeof(s_test_timers));
memset(s_test_tasks, 0, sizeof(s_test_tasks));
s_test_timers[0].isr_func = esp_apptrace_test_timer_isr;
s_test_timers[0].data.buf = s_bufs[0];
s_test_timers[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_timers[0].data.period = 150;
s_test_tasks[0].core = 0;
s_test_tasks[0].prio = 3;
s_test_tasks[0].task_func = esp_apptrace_test_task;
s_test_tasks[0].data.buf = s_bufs[1];
s_test_tasks[0].data.buf_sz = sizeof(s_bufs[1]);
s_test_tasks[0].data.period = 0;
s_test_tasks[0].timers_num = 1;
s_test_tasks[0].timers = s_test_timers;
esp_apptrace_test(&test_cfg);
}
#if CONFIG_FREERTOS_UNICORE == 0
TEST_CASE("App trace test (2 tasks (nowait): 1 @ each core)", "[trace][ignore]")
{
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 2,
.tasks = s_test_tasks,
};
memset(s_test_tasks, 0, sizeof(s_test_tasks));
s_test_tasks[0].nowait = 1;
s_test_tasks[0].core = 0;
s_test_tasks[0].prio = 3;
s_test_tasks[0].task_func = esp_apptrace_test_task;
s_test_tasks[0].data.buf = s_bufs[0];
s_test_tasks[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_tasks[0].data.period = 6700;
s_test_tasks[0].timers_num = 0;
s_test_tasks[0].timers = NULL;
s_test_tasks[1].nowait = 1;
s_test_tasks[1].core = 1;
s_test_tasks[1].prio = 3;
s_test_tasks[1].task_func = esp_apptrace_test_task;
s_test_tasks[1].data.buf = s_bufs[1];
s_test_tasks[1].data.buf_sz = sizeof(s_bufs[1]);
s_test_tasks[1].data.period = 6700;
s_test_tasks[1].timers_num = 0;
s_test_tasks[1].timers = NULL;
esp_apptrace_test(&test_cfg);
}
TEST_CASE("App trace test (2 tasks: 1 @ each core)", "[trace][ignore]")
{
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 2,
.tasks = s_test_tasks,
};
memset(s_test_tasks, 0, sizeof(s_test_tasks));
s_test_tasks[0].core = 0;
s_test_tasks[0].prio = 3;
s_test_tasks[0].task_func = esp_apptrace_test_task;
s_test_tasks[0].data.buf = s_bufs[0];
s_test_tasks[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_tasks[0].data.period = 0;
s_test_tasks[0].timers_num = 0;
s_test_tasks[0].timers = NULL;
s_test_tasks[1].core = 1;
s_test_tasks[1].prio = 3;
s_test_tasks[1].task_func = esp_apptrace_test_task;
s_test_tasks[1].data.buf = s_bufs[1];
s_test_tasks[1].data.buf_sz = sizeof(s_bufs[1]);
s_test_tasks[1].data.period = 0;
s_test_tasks[1].timers_num = 0;
s_test_tasks[1].timers = NULL;
esp_apptrace_test(&test_cfg);
}
#endif
TEST_CASE("App trace test (1 task)", "[trace][ignore]")
{
esp_apptrace_test_cfg_t test_cfg = {
.tasks_num = 1,
.tasks = s_test_tasks,
};
memset(s_test_tasks, 0, sizeof(s_test_tasks));
s_test_tasks[0].core = 0;
s_test_tasks[0].prio = 3;
s_test_tasks[0].task_func = esp_apptrace_test_task;
s_test_tasks[0].data.buf = s_bufs[0];
s_test_tasks[0].data.buf_sz = sizeof(s_bufs[0]);
s_test_tasks[0].data.period = 0;
s_test_tasks[0].timers_num = 0;
s_test_tasks[0].timers = NULL;
esp_apptrace_test(&test_cfg);
}
static int esp_logtrace_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = esp_apptrace_vprintf_to(ESP_APPTRACE_TMO_INFINITE, fmt, ap);
va_end(ap);
return ret;
}
typedef struct {
SemaphoreHandle_t done;
uint32_t work_count;
} esp_logtrace_task_t;
static void esp_logtrace_task(void *p)
{
esp_logtrace_task_t *arg = (esp_logtrace_task_t *) p;
ESP_APPTRACE_TEST_LOGI("%p: run log test task", xTaskGetCurrentTaskHandle());
int i = 0;
while (1) {
esp_logtrace_printf("sample print %lx %hx %c\n", 2 * i + 0x10, 2 * i + 0x20, (2 * i + 0x30) & 0xFF);
esp_logtrace_printf("sample print %lx %hx %c %lu %hu %d %d %d %d\n", i, i + 0x10, (i + 0x20) & 0xFF, i + 0x30, i + 0x40, i + 0x50, i + 0x60, i + 0x70, i + 0x80);
ESP_LOGI(TAG, "%p: sample print 1", xTaskGetCurrentTaskHandle());
ESP_LOGI(TAG, "%p: sample print 2 %u", xTaskGetCurrentTaskHandle(), (unsigned)i);
ESP_LOGI(TAG, "%p: sample print 4 %c", xTaskGetCurrentTaskHandle(), ((i & 0xFF) % 95) + 32);
ESP_LOGI(TAG, "%p: sample print 5 %f", xTaskGetCurrentTaskHandle(), 1.0);
ESP_LOGI(TAG, "%p: sample print 6 %f", xTaskGetCurrentTaskHandle(), 3.45);
ESP_LOGI(TAG, "%p: logtrace task work %d.%d", xTaskGetCurrentTaskHandle(), esp_cpu_get_core_id(), i);
if (++i == 10000) {
break;
}
}
esp_err_t ret = esp_apptrace_flush(ESP_APPTRACE_TMO_INFINITE);
if (ret != ESP_OK) {
ESP_APPTRACE_TEST_LOGE("Failed to flush printf buf (%d)!", ret);
}
ESP_APPTRACE_TEST_LOGI("%p: finished", xTaskGetCurrentTaskHandle());
xSemaphoreGive(arg->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
TEST_CASE("Log trace test (2 tasks)", "[trace][ignore]")
{
TaskHandle_t thnd;
esp_logtrace_task_t arg1 = {
.done = xSemaphoreCreateBinary(),
};
esp_logtrace_task_t arg2 = {
.done = xSemaphoreCreateBinary(),
};
xTaskCreatePinnedToCore(esp_logtrace_task, "logtrace0", 2048, &arg1, 3, &thnd, 0);
ESP_APPTRACE_TEST_LOGI("Created task %p", thnd);
#if CONFIG_FREERTOS_UNICORE == 0
xTaskCreatePinnedToCore(esp_logtrace_task, "logtrace1", 2048, &arg2, 3, &thnd, 1);
#else
xTaskCreatePinnedToCore(esp_logtrace_task, "logtrace1", 2048, &arg2, 3, &thnd, 0);
#endif
ESP_APPTRACE_TEST_LOGI("Created task %p", thnd);
xSemaphoreTake(arg1.done, portMAX_DELAY);
vSemaphoreDelete(arg1.done);
xSemaphoreTake(arg2.done, portMAX_DELAY);
vSemaphoreDelete(arg2.done);
}

View File

@@ -1,3 +0,0 @@
CONFIG_ESP_TRACE_LIB_NONE=y
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
CONFIG_APPTRACE_DEST_JTAG=y

View File

@@ -1,2 +0,0 @@
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n
CONFIG_ESP_TRACE_ENABLE=y

View File

@@ -1,77 +0,0 @@
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
idf_component_register(SRCS "esp_ota_ops.c"
INCLUDE_DIRS "include"
REQUIRES partition_table bootloader_support esp_app_format esp_bootloader_format esp_partition
PRIV_REQUIRES esptool_py efuse spi_flash)
if(NOT BOOTLOADER_BUILD)
partition_table_get_partition_info(otadata_offset "--partition-type data --partition-subtype ota" "offset")
partition_table_get_partition_info(otadata_size "--partition-type data --partition-subtype ota" "size")
# Add custom target for generating empty otadata partition for flashing
if(otadata_size AND otadata_offset)
idf_build_get_property(build_dir BUILD_DIR)
set(blank_otadata_file ${build_dir}/ota_data_initial.bin)
idf_build_get_property(idf_path IDF_PATH)
idf_build_get_property(python PYTHON)
idf_component_get_property(partition_table_dir partition_table COMPONENT_DIR)
add_custom_command(OUTPUT ${blank_otadata_file}
COMMAND ${python} ${partition_table_dir}/gen_empty_partition.py
${otadata_size} ${blank_otadata_file})
add_custom_target(blank_ota_data ALL DEPENDS ${blank_otadata_file})
add_dependencies(flash blank_ota_data)
if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
add_dependencies(encrypted-flash blank_ota_data)
endif()
set(otatool_py "${python}" "${COMPONENT_DIR}/otatool.py")
set(esptool_args --esptool-args before=${CONFIG_ESPTOOLPY_BEFORE} after=${CONFIG_ESPTOOLPY_AFTER})
set(otatool_args --partition-table-file ${PARTITION_CSV_PATH})
list(APPEND otatool_args --partition-table-offset ${PARTITION_TABLE_OFFSET})
idf_component_get_property(esptool_py_dir esptool_py COMPONENT_DIR)
add_custom_target(read-otadata DEPENDS "${PARTITION_CSV_PATH}"
COMMAND ${CMAKE_COMMAND}
-D "IDF_PATH=${idf_path}"
-D "SERIAL_TOOL=${otatool_py}"
-D "SERIAL_TOOL_ARGS=${esptool_args};${otatool_args};read_otadata"
-D "WORKING_DIRECTORY=${build_dir}"
-P ${esptool_py_dir}/run_serial_tool.cmake
WORKING_DIRECTORY "${build_dir}"
USES_TERMINAL
VERBATIM
)
add_deprecated_target_alias(read_otadata read-otadata)
add_custom_target(erase-otadata DEPENDS "${PARTITION_CSV_PATH}"
COMMAND ${CMAKE_COMMAND}
-D "IDF_PATH=${idf_path}"
-D "SERIAL_TOOL=${otatool_py}"
-D "SERIAL_TOOL_ARGS=${esptool_args};${otatool_args};erase_otadata"
-D "WORKING_DIRECTORY=${build_dir}"
-P ${esptool_py_dir}/run_serial_tool.cmake
WORKING_DIRECTORY "${build_dir}"
USES_TERMINAL
VERBATIM
)
add_deprecated_target_alias(erase_otadata erase-otadata)
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
esptool_py_flash_target(otadata-flash "${main_args}" "${sub_args}")
esptool_py_flash_target_image(otadata-flash otadata "${otadata_offset}" "${blank_otadata_file}")
esptool_py_flash_target_image(flash otadata "${otadata_offset}" "${blank_otadata_file}")
endif()
endif()

View File

@@ -1,435 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _OTA_OPS_H
#define _OTA_OPS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "esp_err.h"
#include "esp_partition.h"
#include "esp_app_desc.h"
#include "esp_bootloader_desc.h"
#include "esp_flash_partitions.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define OTA_SIZE_UNKNOWN 0xffffffff /*!< Used for esp_ota_begin() if new image size is unknown */
#define OTA_WITH_SEQUENTIAL_WRITES 0xfffffffe /*!< Used for esp_ota_begin() if new image size is unknown and erase can be done in incremental manner (assuming write operation is in continuous sequence) */
#define ESP_ERR_OTA_BASE 0x1500 /*!< Base error code for ota_ops api */
#define ESP_ERR_OTA_PARTITION_CONFLICT (ESP_ERR_OTA_BASE + 0x01) /*!< Error if request was to write or erase the current running partition */
#define ESP_ERR_OTA_SELECT_INFO_INVALID (ESP_ERR_OTA_BASE + 0x02) /*!< Error if OTA data partition contains invalid content */
#define ESP_ERR_OTA_VALIDATE_FAILED (ESP_ERR_OTA_BASE + 0x03) /*!< Error if OTA app image is invalid */
#define ESP_ERR_OTA_SMALL_SEC_VER (ESP_ERR_OTA_BASE + 0x04) /*!< Error if the firmware has a secure version less than the running firmware. */
#define ESP_ERR_OTA_ROLLBACK_FAILED (ESP_ERR_OTA_BASE + 0x05) /*!< Error if flash does not have valid firmware in passive partition and hence rollback is not possible */
#define ESP_ERR_OTA_ROLLBACK_INVALID_STATE (ESP_ERR_OTA_BASE + 0x06) /*!< Error if current active firmware is still marked in pending validation state (ESP_OTA_IMG_PENDING_VERIFY), essentially first boot of firmware image post upgrade and hence firmware upgrade is not possible */
/**
* @brief Opaque handle for an application OTA update
*
* esp_ota_begin() returns a handle which is then used for subsequent
* calls to esp_ota_write() and esp_ota_end().
*/
typedef uint32_t esp_ota_handle_t;
/**
* @brief Commence an OTA update writing to the specified partition.
* The specified partition is erased to the specified image size.
*
* If image size is not yet known, pass OTA_SIZE_UNKNOWN which will
* cause the entire partition to be erased.
*
* On success, this function allocates memory that remains in use
* until esp_ota_end() is called with the returned handle.
*
* Note: If the rollback option is enabled and the running application has the ESP_OTA_IMG_PENDING_VERIFY state then
* it will lead to the ESP_ERR_OTA_ROLLBACK_INVALID_STATE error. Confirm the running app before to run download a new app,
* use esp_ota_mark_app_valid_cancel_rollback() function for it (this should be done as early as possible when you first download a new application).
*
* Note: Rollback is applicable only for app type partitions.
* Note: For Rollback - The OTA data slot corresponding to the last boot application partition will be invalidated.
*
* @param partition Pointer to info for partition which will receive the OTA update. Required.
* This is considered as the staging partition (where OTA is downloaded), be default this also considered as the final partition which supposed to be updated.
* The final partition can be overwritten using esp_ota_set_final_partition() after calling esp_ota_begin() to relocate contents to the final destination partition.
* @param image_size Size of new OTA app image. Partition will be erased in order to receive this size of image. If 0 or OTA_SIZE_UNKNOWN, the entire partition is erased.
* @param out_handle On success, returns a handle which should be used for subsequent esp_ota_write() and esp_ota_end() calls.
* @return
* - ESP_OK: OTA operation commenced successfully.
* - ESP_ERR_INVALID_ARG: partition or out_handle arguments were NULL, or partition doesn't point to an OTA app partition.
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
* - ESP_ERR_OTA_PARTITION_CONFLICT: Partition holds the currently running firmware, cannot update in place.
* - ESP_ERR_NOT_FOUND: Partition argument not found in partition table.
* - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
* - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
* - ESP_ERR_OTA_ROLLBACK_INVALID_STATE: If the running app has not confirmed state. Before performing an update, the application must be valid.
*/
esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp_ota_handle_t* out_handle);
/**
* @brief Resume an interrupted OTA update by continuing to write to the specified partition.
*
* This function is used when an OTA update was previously started and needs to be resumed after an interruption.
* It continues the OTA process from the specified offset within the partition.
*
* Unlike esp_ota_begin(), this function does not erase the partition which receives the OTA update, but rather expects that part of the image
* has already been written correctly, and it resumes writing from the given offset.
*
* @param partition Pointer to info for the partition which is receiving the OTA update. Required.
* @param erase_size Specifies how much flash memory to erase before resuming OTA, depending on whether a sequential write or a bulk erase is being used.
* @param image_offset Offset from where to resume the OTA process. Should be set to the number of bytes already written.
* @param out_handle On success, returns a handle that should be used for subsequent esp_ota_write() and esp_ota_end() calls.
*
* @return
* - ESP_OK: OTA operation resumed successfully.
* - ESP_ERR_INVALID_ARG: partition, out_handle were NULL or image_offset arguments is negative, or partition doesn't point to an OTA app partition.
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
* - ESP_ERR_OTA_PARTITION_CONFLICT: Partition holds the currently running firmware, cannot update in place.
* - ESP_ERR_NOT_FOUND: Partition argument not found in partition table.
* - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
* - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
*/
esp_err_t esp_ota_resume(const esp_partition_t *partition, const size_t erase_size, const size_t image_offset, esp_ota_handle_t *out_handle);
/**
* @brief Set the final destination partition for OTA update
*
* This function configures the specified final partition as the destination for the OTA update.
* It also allows setting a flag to indicate if the image should be copied from the staging
* partition to the final partition after the OTA update completes. Otherwise, copying will need
* to be handled by custom code using esp_partition_copy().
*
* @note This can be called after esp_ota_begin() and before the OTA update has started (before esp_ota_write()).
*
* @param handle OTA update handle obtained from esp_ota_begin().
* @param final Pointer to the final destination partition where the new image will be verified and potentially finalized.
* This partition must not be NULL.
* @param finalize_with_copy Boolean flag indicating if the downloaded image should be copied
* from the staging partition to the final partition upon completion.
* Set to False if you intend to perform the final copy process manually later.
*
* @return
* - ESP_OK: final destination partition set successfully.
* - ESP_ERR_INVALID_STATE: Once the OTA update has started, changing the final destination partition is prohibited.
* - ESP_ERR_INVALID_ARG: Invalid arguments were passed (e.g., final partition is NULL).
* - ESP_ERR_NOT_FOUND: OTA handle not found or final partition verification failed.
*/
esp_err_t esp_ota_set_final_partition(esp_ota_handle_t handle, const esp_partition_t *final, bool finalize_with_copy);
/**
* @brief Write OTA update data to partition
*
* This function can be called multiple times as
* data is received during the OTA operation. Data is written
* sequentially to the partition.
*
* @param handle Handle obtained from esp_ota_begin
* @param data Data buffer to write
* @param size Size of data buffer in bytes.
*
* @return
* - ESP_OK: Data was written to flash successfully, or size = 0
* - ESP_ERR_INVALID_ARG: handle is invalid.
* - ESP_ERR_OTA_VALIDATE_FAILED: First byte of image contains invalid image magic byte.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
* - ESP_ERR_INVALID_SIZE: if write would go out of bounds of the partition
* - or one of error codes from lower-level flash driver.
*/
esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size);
/**
* @brief Write OTA update data to partition at an offset
*
* This function can write data in non-contiguous manner.
* If flash encryption is enabled, data should be 16 bytes aligned.
*
* @param handle Handle obtained from esp_ota_begin
* @param data Data buffer to write
* @param size Size of data buffer in bytes
* @param offset Offset in flash partition
*
* @note While performing OTA, if the packets arrive out of order, esp_ota_write_with_offset() can be used to write data in non-contiguous manner.
* Use of esp_ota_write_with_offset() in combination with esp_ota_write() is not recommended.
*
* @return
* - ESP_OK: Data was written to flash successfully.
* - ESP_ERR_INVALID_ARG: handle is invalid.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
*/
esp_err_t esp_ota_write_with_offset(esp_ota_handle_t handle, const void *data, size_t size, uint32_t offset);
/**
* @brief Finish OTA update and validate newly written app image.
*
* @param handle Handle obtained from esp_ota_begin().
*
* @note After calling esp_ota_end(), the handle is no longer valid and any memory associated with it is freed (regardless of result).
* @note If either the final or staging partitions were for the bootloader, then at the end of this function,
* the bootloader is reset to its default offset: esp_image_bootloader_offset_set(ESP_PRIMARY_BOOTLOADER_OFFSET)
*
* If the finalize_with_copy option is set, the staging partition will be copied to the final partition at the end of this function.
* Otherwise, copying will need to be handled by custom code using esp_partition_copy().
*
* @return
* - ESP_OK: Newly written OTA app image is valid.
* - ESP_ERR_NOT_FOUND: OTA handle was not found.
* - ESP_ERR_INVALID_ARG: Handle was never written to.
* - ESP_ERR_OTA_VALIDATE_FAILED: OTA image is invalid (either not a valid app image, or - if secure boot is enabled - signature failed to verify.)
* - ESP_ERR_INVALID_STATE: If flash encryption is enabled, this result indicates an internal error writing the final encrypted bytes to flash.
*/
esp_err_t esp_ota_end(esp_ota_handle_t handle);
/**
* @brief Abort OTA update, free the handle and memory associated with it.
*
* @param handle obtained from esp_ota_begin().
*
* @return
* - ESP_OK: Handle and its associated memory is freed successfully.
* - ESP_ERR_NOT_FOUND: OTA handle was not found.
*/
esp_err_t esp_ota_abort(esp_ota_handle_t handle);
/**
* @brief Configure OTA data for a new boot partition
*
* @note If this function returns ESP_OK, calling esp_restart() will boot the newly configured app partition.
*
* @param partition Pointer to info for partition containing app image to boot.
*
* @return
* - ESP_OK: OTA data updated, next reboot will use specified partition.
* - ESP_ERR_INVALID_ARG: partition argument was NULL or didn't point to a valid OTA partition of type "app".
* - ESP_ERR_OTA_VALIDATE_FAILED: Partition contained invalid app image. Also returned if secure boot is enabled and signature validation failed.
* - ESP_ERR_NOT_FOUND: OTA data partition not found.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash erase or write failed.
*/
esp_err_t esp_ota_set_boot_partition(const esp_partition_t* partition);
/**
* @brief Get partition info of currently configured boot app
*
* If esp_ota_set_boot_partition() has been called, the partition which was set by that function will be returned.
*
* If esp_ota_set_boot_partition() has not been called, the result is usually the same as esp_ota_get_running_partition().
* The two results are not equal if the configured boot partition does not contain a valid app (meaning that the running partition
* will be an app that the bootloader chose via fallback).
*
* If the OTA data partition is not present or not valid then the result is the first app partition found in the
* partition table. In priority order, this means: the factory app, the first OTA app slot, or the test app partition.
*
* Note that there is no guarantee the returned partition is a valid app. Use esp_image_verify(ESP_IMAGE_VERIFY, ...) to verify if the
* returned partition contains a bootable image.
*
* @return Pointer to info for partition structure, or NULL if partition table is invalid or a flash read operation failed. Any returned pointer is valid for the lifetime of the application.
*/
const esp_partition_t* esp_ota_get_boot_partition(void);
/**
* @brief Get partition info of currently running app
*
* This function is different to esp_ota_get_boot_partition() in that
* it ignores any change of selected boot partition caused by
* esp_ota_set_boot_partition(). Only the app whose code is currently
* running will have its partition information returned.
*
* The partition returned by this function may also differ from esp_ota_get_boot_partition() if the configured boot
* partition is somehow invalid, and the bootloader fell back to a different app partition at boot.
*
* @return Pointer to info for partition structure, or NULL if no partition is found or flash read operation failed. Returned pointer is valid for the lifetime of the application.
*/
const esp_partition_t* esp_ota_get_running_partition(void);
/**
* @brief Return the next OTA app partition which should be written with a new firmware.
*
* Call this function to find an OTA app partition which can be passed to esp_ota_begin().
*
* Finds next partition round-robin, starting from the current running partition.
*
* @param start_from If set, treat this partition info as describing the current running partition. Can be NULL, in which case esp_ota_get_running_partition() is used to find the currently running partition. The result of this function is never the same as this argument.
*
* @return Pointer to info for partition which should be updated next. NULL result indicates invalid OTA data partition, or that no eligible OTA app slot partition was found.
*
*/
const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from);
/**
* @brief Returns esp_app_desc structure for app partition. This structure includes app version.
*
* Returns a description for the requested app partition.
* @param[in] partition Pointer to app partition. (only app partition)
* @param[out] app_desc Structure of info about app.
* @return
* - ESP_OK Successful.
* - ESP_ERR_NOT_FOUND app_desc structure is not found. Magic word is incorrect.
* - ESP_ERR_NOT_SUPPORTED Partition is not application.
* - ESP_ERR_INVALID_ARG Arguments is NULL or if partition's offset exceeds partition size.
* - ESP_ERR_INVALID_SIZE Read would go out of bounds of the partition.
* - or one of error codes from lower-level flash driver.
*/
esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, esp_app_desc_t *app_desc);
/**
* @brief Returns the description structure of the bootloader.
*
* @param[in] bootloader_partition Pointer to bootloader partition.
* If NULL, then the PRIMARY bootloader is used (the default location).
* offset = CONFIG_BOOTLOADER_OFFSET_IN_FLASH,
* size = CONFIG_PARTITION_TABLE_OFFSET - CONFIG_BOOTLOADER_OFFSET_IN_FLASH,
* @param[out] desc Structure of info about bootloader.
* @return
* - ESP_OK Successful.
* - ESP_ERR_NOT_FOUND Description structure is not found in the bootloader image. Magic byte is incorrect.
* - ESP_ERR_INVALID_ARG Arguments is NULL.
* - ESP_ERR_INVALID_SIZE Read would go out of bounds of the partition.
* - or one of error codes from lower-level flash driver.
*/
esp_err_t esp_ota_get_bootloader_description(const esp_partition_t *bootloader_partition, esp_bootloader_desc_t *desc);
/**
* @brief Invalidate the OTA data slot associated with the last boot application partition.
*
* This function erases the OTA data slot corresponding to the last boot application partition,
* making the partition invalid for booting in future. The application partition itself
* is not erased, preserving its contents.
*
* @return
* - ESP_OK: Successfully invalidated the OTA data slot.
* - ESP_FAIL: Failed to invalidate the OTA data slot (e.g., invalid parameters, no OTA data partition, or other errors).
* - Other error codes from `esp_partition_erase_range`.
*/
esp_err_t esp_ota_invalidate_inactive_ota_data_slot(void);
/**
* @brief Returns number of ota partitions provided in partition table.
*
* @return
* - Number of OTA partitions
*/
uint8_t esp_ota_get_app_partition_count(void);
/**
* @brief This function is called to indicate that the running app is working well.
*
* @return
* - ESP_OK: if successful.
*/
esp_err_t esp_ota_mark_app_valid_cancel_rollback(void);
/**
* @brief This function is called to roll back to the previously workable app without reboot.
*
* Checks applications on a flash drive that can be booted in case of rollback.
* If the flash does not have at least one app (except the running app) then rollback is not possible.
* @return
* - ESP_OK: if successful.
* - ESP_FAIL: if not successful.
* - ESP_ERR_OTA_ROLLBACK_FAILED: The rollback is not possible because the available OTA partitions
* on the flash do not contain a valid application.
*/
esp_err_t esp_ota_mark_app_invalid_rollback(void);
/**
* @brief This function is called to roll back to the previously workable app with reboot.
*
* Equivalent to calling esp_ota_mark_app_invalid_rollback(), and, if successful, followed by esp_restart().
*
* @return
* - ESP_FAIL: if not successful.
* - ESP_ERR_OTA_ROLLBACK_FAILED: The rollback is not possible due to flash does not have any apps.
*/
esp_err_t esp_ota_mark_app_invalid_rollback_and_reboot(void);
/**
* @brief Returns last partition with invalid state (ESP_OTA_IMG_INVALID or ESP_OTA_IMG_ABORTED).
*
* @return partition.
*/
const esp_partition_t* esp_ota_get_last_invalid_partition(void);
/**
* @brief Returns state for given partition.
*
* @param[in] partition Pointer to partition.
* @param[out] ota_state state of partition (if this partition has a record in otadata).
* @return
* - ESP_OK: Successful.
* - ESP_ERR_INVALID_ARG: partition or ota_state arguments were NULL.
* - ESP_ERR_NOT_SUPPORTED: partition is not ota.
* - ESP_ERR_NOT_FOUND: Partition table does not have otadata or state was not found for given partition.
*/
esp_err_t esp_ota_get_state_partition(const esp_partition_t *partition, esp_ota_img_states_t *ota_state);
/**
* @brief Erase previous boot app partition and corresponding otadata select for this partition.
*
* When current app is marked to as valid then you can erase previous app partition.
* @return
* - ESP_OK: Successful, otherwise ESP_ERR.
*/
esp_err_t esp_ota_erase_last_boot_app_partition(void);
/**
* @brief Checks applications on the slots which can be booted in case of rollback.
*
* These applications should be valid (marked in otadata as not UNDEFINED, INVALID or ABORTED and crc is good) and be able booted,
* and secure_version of app >= secure_version of efuse (if anti-rollback is enabled).
*
* @return
* - True: Returns true if the slots have at least one app (except the running app).
* - False: The rollback is not possible.
*/
bool esp_ota_check_rollback_is_possible(void);
#if SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS > 1 && (CONFIG_SECURE_BOOT_V2_ENABLED || __DOXYGEN__)
/**
* Secure Boot V2 public key indexes.
*/
typedef enum {
SECURE_BOOT_PUBLIC_KEY_INDEX_0, /*!< Points to the 0th index of the Secure Boot v2 public key */
SECURE_BOOT_PUBLIC_KEY_INDEX_1, /*!< Points to the 1st index of the Secure Boot v2 public key */
SECURE_BOOT_PUBLIC_KEY_INDEX_2 /*!< Points to the 2nd index of the Secure Boot v2 public key */
} esp_ota_secure_boot_public_key_index_t;
/**
* @brief Revokes the signature digest denoted by the given index. This should be called in the application only after the rollback logic otherwise the device may end up in unrecoverable state.
*
* Relevant for Secure boot v2 on ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2 where up to 3 key digests can be stored (Key \#N-1, Key \#N, Key \#N+1).
* When a key used to sign an app is invalidated, an OTA update is to be sent with an app signed with at least one of the other two keys which has not been revoked already.
* After successfully booting the OTA app should call this function to revoke Key \#N-1.
*
* @param index - The index of the signature block to be revoked
*
* @return
* - ESP_OK: If revocation is successful.
* - ESP_ERR_INVALID_ARG: If the index of the public key to be revoked is incorrect.
* - ESP_FAIL: If secure boot v2 has not been enabled.
*/
esp_err_t esp_ota_revoke_secure_boot_public_key(esp_ota_secure_boot_public_key_index_t index);
#endif /* SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS > 1 */
#ifdef __cplusplus
}
#endif
#endif /* OTA_OPS_H */

View File

@@ -1,415 +0,0 @@
#!/usr/bin/env python
#
# otatool is used to perform ota-level operations - flashing ota partition
# erasing ota partition and switching ota partition
#
# SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import binascii
import collections
import os
import struct
import sys
import tempfile
try:
from parttool import PARTITION_TABLE_OFFSET
from parttool import PartitionName
from parttool import PartitionType
from parttool import ParttoolTarget
except ImportError:
COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components'))
PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, 'partition_table')
sys.path.append(PARTTOOL_DIR)
from parttool import PARTITION_TABLE_OFFSET
from parttool import PartitionName
from parttool import PartitionType
from parttool import ParttoolTarget
__version__ = '2.0'
SPI_FLASH_SEC_SIZE = 0x2000
quiet = False
def status(msg):
if not quiet:
print(msg)
class OtatoolTarget:
OTADATA_PARTITION = PartitionType('data', 'ota')
def __init__(
self,
port=None,
baud=None,
partition_table_offset=PARTITION_TABLE_OFFSET,
partition_table_file=None,
spi_flash_sec_size=SPI_FLASH_SEC_SIZE,
esptool_args=[],
esptool_write_args=[],
esptool_read_args=[],
esptool_erase_args=[],
):
self.target = ParttoolTarget(
port,
baud,
partition_table_offset,
partition_table_file,
esptool_args,
esptool_write_args,
esptool_read_args,
esptool_erase_args,
)
self.spi_flash_sec_size = spi_flash_sec_size
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.close()
try:
self.target.read_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name)
with open(temp_file.name, 'rb') as f:
self.otadata = f.read()
finally:
os.unlink(temp_file.name)
def _check_otadata_partition(self):
if not self.otadata:
raise Exception('No otadata partition found')
def erase_otadata(self):
self._check_otadata_partition()
self.target.erase_partition(OtatoolTarget.OTADATA_PARTITION)
def _get_otadata_info(self):
info = []
otadata_info = collections.namedtuple('otadata_info', 'seq crc')
for i in range(2):
start = i * (self.spi_flash_sec_size >> 1)
seq = bytearray(self.otadata[start : start + 4])
crc = bytearray(self.otadata[start + 28 : start + 32])
seq = struct.unpack('I', seq)
crc = struct.unpack('I', crc)
info.append(otadata_info(seq[0], crc[0]))
return info
def _get_partition_id_from_ota_id(self, ota_id):
if isinstance(ota_id, int):
return PartitionType('app', 'ota_' + str(ota_id))
else:
return PartitionName(ota_id)
def switch_ota_partition(self, ota_id):
self._check_otadata_partition()
import gen_esp32part as gen
def is_otadata_info_valid(status):
seq = status.seq % (1 << 32)
crc = binascii.crc32(struct.pack('I', seq), 0xFFFFFFFF) % (1 << 32)
return seq < (int('0xFFFFFFFF', 16) % (1 << 32)) and status.crc == crc
partition_table = self.target.partition_table
ota_partitions = list()
for i in range(gen.NUM_PARTITION_SUBTYPE_APP_OTA):
ota_partition = filter(lambda p: p.subtype == (gen.MIN_PARTITION_SUBTYPE_APP_OTA + i), partition_table)
try:
ota_partitions.append(list(ota_partition)[0])
except IndexError:
break
ota_partitions = sorted(ota_partitions, key=lambda p: p.subtype)
if not ota_partitions:
raise Exception('No ota app partitions found')
# Look for the app partition to switch to
ota_partition_next = None
try:
if isinstance(ota_id, int):
ota_partition_next = filter(
lambda p: p.subtype - gen.MIN_PARTITION_SUBTYPE_APP_OTA == ota_id, ota_partitions
)
else:
ota_partition_next = filter(lambda p: p.name == ota_id, ota_partitions)
ota_partition_next = list(ota_partition_next)[0]
except IndexError:
raise Exception('Partition to switch to not found')
otadata_info = self._get_otadata_info()
# Find the copy to base the computation for ota sequence number on
otadata_compute_base = -1
# Both are valid, take the max as computation base
if is_otadata_info_valid(otadata_info[0]) and is_otadata_info_valid(otadata_info[1]):
if otadata_info[0].seq >= otadata_info[1].seq:
otadata_compute_base = 0
else:
otadata_compute_base = 1
# Only one copy is valid, use that
elif is_otadata_info_valid(otadata_info[0]):
otadata_compute_base = 0
elif is_otadata_info_valid(otadata_info[1]):
otadata_compute_base = 1
# Both are invalid (could be initial state - all 0xFF's)
else:
pass
ota_seq_next = 0
ota_partitions_num = len(ota_partitions)
target_seq = (ota_partition_next.subtype & 0x0F) + 1
# Find the next ota sequence number
if otadata_compute_base == 0 or otadata_compute_base == 1:
base_seq = otadata_info[otadata_compute_base].seq % (1 << 32)
i = 0
while base_seq > target_seq % ota_partitions_num + i * ota_partitions_num:
i += 1
ota_seq_next = target_seq % ota_partitions_num + i * ota_partitions_num
else:
ota_seq_next = target_seq
# Create binary data from computed values
ota_seq_next = struct.pack('I', ota_seq_next)
ota_seq_crc_next = binascii.crc32(ota_seq_next, 0xFFFFFFFF) % (1 << 32)
ota_seq_crc_next = struct.pack('I', ota_seq_crc_next)
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.close()
try:
with open(temp_file.name, 'wb') as otadata_next_file:
start = (1 if otadata_compute_base == 0 else 0) * (self.spi_flash_sec_size >> 1)
otadata_next_file.write(self.otadata)
otadata_next_file.seek(start)
otadata_next_file.write(ota_seq_next)
otadata_next_file.seek(start + 28)
otadata_next_file.write(ota_seq_crc_next)
otadata_next_file.flush()
self.target.write_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name)
finally:
os.unlink(temp_file.name)
def read_ota_partition(self, ota_id, output):
self.target.read_partition(self._get_partition_id_from_ota_id(ota_id), output)
def write_ota_partition(self, ota_id, input_file):
self.target.write_partition(self._get_partition_id_from_ota_id(ota_id), input_file)
def erase_ota_partition(self, ota_id):
self.target.erase_partition(self._get_partition_id_from_ota_id(ota_id))
def _read_otadata(target):
target._check_otadata_partition()
otadata_info = target._get_otadata_info()
print(' {:8s} \t {:8s} | \t {:8s} \t {:8s}'.format('OTA_SEQ', 'CRC', 'OTA_SEQ', 'CRC'))
print(
f'Firmware: {otadata_info[0].seq:#08x} \t{otadata_info[0].crc:#08x} | '
f'\t{otadata_info[1].seq:#08x} \t {otadata_info[1].crc:#08x}'
)
def _erase_otadata(target):
target.erase_otadata()
status('Erased ota_data partition contents')
def _switch_ota_partition(target, ota_id):
target.switch_ota_partition(ota_id)
def _read_ota_partition(target, ota_id, output):
target.read_ota_partition(ota_id, output)
status(f'Read ota partition contents to file {output}')
def _write_ota_partition(target, ota_id, input_file):
target.write_ota_partition(ota_id, input_file)
status(f'Written contents of file {input_file} to ota partition')
def _erase_ota_partition(target, ota_id):
target.erase_ota_partition(ota_id)
status('Erased contents of ota partition')
def main():
global quiet
parser = argparse.ArgumentParser('ESP-IDF OTA Partitions Tool')
parser.add_argument('--quiet', '-q', help='suppress stderr messages', action='store_true')
parser.add_argument('--esptool-args', help='additional main arguments for esptool', nargs='+')
parser.add_argument(
'--esptool-write-args', help='additional subcommand arguments for esptool write-flash', nargs='+'
)
parser.add_argument('--esptool-read-args', help='additional subcommand arguments for esptool read-flash', nargs='+')
parser.add_argument(
'--esptool-erase-args', help='additional subcommand arguments for esptool erase-region', nargs='+'
)
# There are two possible sources for the partition table: a device attached to the host
# or a partition table CSV/binary file. These sources are mutually exclusive.
parser.add_argument('--port', '-p', help='port where the device to read the partition table from is attached')
parser.add_argument('--baud', '-b', help='baudrate to use', type=int)
parser.add_argument('--partition-table-offset', '-o', help='offset to read the partition table from', type=str)
parser.add_argument(
'--partition-table-file',
'-f',
help='file (CSV/binary) to read the partition table from; '
'overrides device attached to specified port as the partition table source when defined',
)
subparsers = parser.add_subparsers(dest='operation', help='run otatool -h for additional help')
spi_flash_sec_size = argparse.ArgumentParser(add_help=False)
spi_flash_sec_size.add_argument('--spi-flash-sec-size', help='value of SPI_FLASH_SEC_SIZE macro', type=str)
# Specify the supported operations
subparsers.add_parser('read_otadata', help='read otadata partition', parents=[spi_flash_sec_size])
subparsers.add_parser('erase_otadata', help='erase otadata partition')
slot_or_name_parser = argparse.ArgumentParser(add_help=False)
slot_or_name_parser_args = slot_or_name_parser.add_mutually_exclusive_group()
slot_or_name_parser_args.add_argument('--slot', help='slot number of the ota partition', type=int)
slot_or_name_parser_args.add_argument('--name', help='name of the ota partition')
subparsers.add_parser(
'switch_ota_partition', help='switch otadata partition', parents=[slot_or_name_parser, spi_flash_sec_size]
)
read_ota_partition_subparser = subparsers.add_parser(
'read_ota_partition', help='read contents of an ota partition', parents=[slot_or_name_parser]
)
read_ota_partition_subparser.add_argument(
'--output', help='file to write the contents of the ota partition to', required=True
)
write_ota_partition_subparser = subparsers.add_parser(
'write_ota_partition', help='write contents to an ota partition', parents=[slot_or_name_parser]
)
write_ota_partition_subparser.add_argument('--input', help='file whose contents to write to the ota partition')
subparsers.add_parser(
'erase_ota_partition', help='erase contents of an ota partition', parents=[slot_or_name_parser]
)
args = parser.parse_args()
quiet = args.quiet
# No operation specified, display help and exit
if args.operation is None:
if not quiet:
parser.print_help()
sys.exit(1)
target_args = {}
if args.port:
target_args['port'] = args.port
if args.partition_table_file:
target_args['partition_table_file'] = args.partition_table_file
if args.partition_table_offset:
target_args['partition_table_offset'] = int(args.partition_table_offset, 0)
try:
if args.spi_flash_sec_size:
target_args['spi_flash_sec_size'] = int(args.spi_flash_sec_size, 0)
except AttributeError:
pass
if args.esptool_args:
target_args['esptool_args'] = args.esptool_args
if args.esptool_write_args:
target_args['esptool_write_args'] = args.esptool_write_args
if args.esptool_read_args:
target_args['esptool_read_args'] = args.esptool_read_args
if args.esptool_erase_args:
target_args['esptool_erase_args'] = args.esptool_erase_args
if args.baud:
target_args['baud'] = args.baud
target = OtatoolTarget(**target_args)
# Create the operation table and execute the operation
common_args = {'target': target}
ota_id = []
try:
if args.name is not None:
ota_id = ['name']
else:
if args.slot is not None:
ota_id = ['slot']
except AttributeError:
pass
otatool_ops = {
'read_otadata': (_read_otadata, []),
'erase_otadata': (_erase_otadata, []),
'switch_ota_partition': (_switch_ota_partition, ota_id),
'read_ota_partition': (_read_ota_partition, ['output'] + ota_id),
'write_ota_partition': (_write_ota_partition, ['input'] + ota_id),
'erase_ota_partition': (_erase_ota_partition, ota_id),
}
(op, op_args) = otatool_ops[args.operation]
for op_arg in op_args:
common_args.update({op_arg: vars(args)[op_arg]})
try:
common_args['ota_id'] = common_args.pop('name')
except KeyError:
try:
common_args['ota_id'] = common_args.pop('slot')
except KeyError:
pass
if quiet:
# If exceptions occur, suppress and exit quietly
try:
op(**common_args)
except Exception:
sys.exit(2)
else:
op(**common_args)
if __name__ == '__main__':
main()

View File

@@ -1,25 +0,0 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/app_update/test_apps:
enable:
- if: CONFIG_NAME == "defaults" and IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32c61", "esp32h2", "esp32p4", "esp32s2", "esp32s3"]
- if: CONFIG_NAME == "rollback" and IDF_TARGET in ["esp32", "esp32c3", "esp32s3", "esp32p4"]
- if: CONFIG_NAME == "xip_psram" and SOC_SPIRAM_XIP_SUPPORTED == 1
# S2 doesn't have ROM for flash
- if: CONFIG_NAME == "xip_psram_with_rom_impl" and (SOC_SPIRAM_XIP_SUPPORTED == 1 and IDF_TARGET != "esp32s2")
- if: CONFIG_NAME == "recovery_bootloader" and SOC_RECOVERY_BOOTLOADER_SUPPORTED == 1
disable:
- if: IDF_TARGET in ["esp32h21", "esp32h4", "esp32s31"]
temporary: true
reason: not supported yet # TODO: [ESP32H21] IDF-11515, [ESP32H4] IDF-12279 [ESP32S31] IDF-14643
- if: IDF_TARGET == "esp32c61" and CONFIG_NAME == "xip_psram_with_rom_impl"
temporary: true
reason: not supported yet # TODO: [ESP32C61] IDF-12784
disable_test:
- if: CONFIG_NAME == "recovery_bootloader" and SOC_RECOVERY_BOOTLOADER_SUPPORTED == 1 and IDF_TARGET == "esp32c61"
temporary: true
reason: lack of runners # TODO: [ESP32C61] IDF-13165
depends_components:
- app_update
- bootloader_support
- esp_partitions

View File

@@ -1,8 +0,0 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.22)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components")
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(app_update_test)

View File

@@ -1,2 +0,0 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |

View File

@@ -1,15 +0,0 @@
idf_component_register(
SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES
cmock
test_utils
app_update
bootloader_support
nvs_flash
esp_driver_gpio
spi_flash
esp_psram
efuse
WHOLE_ARCHIVE
)

View File

@@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_err.h"
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
unity_run_menu();
ESP_ERROR_CHECK(nvs_flash_deinit());
}

View File

@@ -1,87 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Tests bootloader update.
*/
#include "unity.h"
#include "esp_log.h"
#include "esp_efuse.h"
#include "esp_flash_internal.h"
#include "esp_rom_sys.h"
#include "utils_update.h"
#include "sdkconfig.h"
#define BOOT_COUNT_NAMESPACE "boot_count"
static __attribute__((unused)) const char *TAG = "btldr_update";
#if CONFIG_BOOTLOADER_RECOVERY_ENABLE
/* @brief Checks and prepares the partition so that the factory app is launched after that.
*/
static void start_test(void)
{
ESP_LOGI(TAG, "boot count 1 - reset");
set_boot_count_in_nvs(1);
erase_ota_data();
ESP_LOGI(TAG, "ota_data erased");
ESP_LOGI(TAG, "Bootloader offset: 0x%x", esp_rom_get_bootloader_offset());
reboot_as_deep_sleep();
}
static void test_flow1(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
ESP_LOGI(TAG, "Bootloader offset: 0x%x", esp_rom_get_bootloader_offset());
const esp_partition_t *primary_bootloader;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_BOOTLOADER_OFFSET, ESP_BOOTLOADER_SIZE, "PrimaryBTLDR", ESP_PARTITION_TYPE_BOOTLOADER, ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY, &primary_bootloader));
const esp_partition_t *recovery_bootloader;
TEST_ESP_OK(esp_partition_register_external(NULL, CONFIG_BOOTLOADER_RECOVERY_OFFSET, ESP_BOOTLOADER_SIZE, "RecoveryBTLDR", ESP_PARTITION_TYPE_BOOTLOADER, ESP_PARTITION_SUBTYPE_BOOTLOADER_RECOVERY, &recovery_bootloader));
ESP_LOGI(TAG, "Bootloaders are registered");
// Remove write protection for the bootloader
esp_flash_set_dangerous_write_protection(esp_flash_default_chip, false);
switch (boot_count) {
case 2:
TEST_ASSERT_EQUAL_HEX32(ESP_PRIMARY_BOOTLOADER_OFFSET, esp_rom_get_bootloader_offset());
TEST_ESP_OK(esp_partition_erase_range(recovery_bootloader, 0, recovery_bootloader->size));
ESP_LOGI(TAG, "Erase recovery bootloader");
TEST_ESP_OK(esp_efuse_set_recovery_bootloader_offset(CONFIG_BOOTLOADER_RECOVERY_OFFSET));
ESP_LOGI(TAG, "Backup, copy <%s> -> <%s>", primary_bootloader->label, recovery_bootloader->label);
TEST_ESP_OK(esp_partition_copy(recovery_bootloader, 0, primary_bootloader, 0, primary_bootloader->size));
TEST_ESP_OK(esp_partition_erase_range(primary_bootloader, 0, primary_bootloader->size));
ESP_LOGI(TAG, "Erase primary bootloader");
reboot_as_deep_sleep();
break;
case 3:
TEST_ASSERT_EQUAL_HEX32(CONFIG_BOOTLOADER_RECOVERY_OFFSET, esp_rom_get_bootloader_offset());
ESP_LOGI(TAG, "Return to primary bootloader...");
ESP_LOGI(TAG, "Copy <%s> -> <%s>", recovery_bootloader->label, primary_bootloader->label);
TEST_ESP_OK(esp_partition_copy(primary_bootloader, 0, recovery_bootloader, 0, primary_bootloader->size));
TEST_ESP_OK(esp_partition_erase_range(recovery_bootloader, 0, recovery_bootloader->size));
ESP_LOGI(TAG, "Erase recovery bootloader");
break;
default:
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
TEST_CASE_MULTIPLE_STAGES("Recovery bootloader feature", "[recovery_bootloader][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow1, test_flow1);
#endif // CONFIG_BOOTLOADER_RECOVERY_ENABLE

View File

@@ -1,124 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <unity.h>
#include <test_utils.h>
#include <esp_ota_ops.h>
/* These OTA tests currently don't assume an OTA partition exists
on the device, so they're a bit limited
*/
TEST_CASE("esp_ota_begin() verifies arguments", "[ota]")
{
const esp_partition_t *running = esp_ota_get_running_partition();
esp_partition_t partition;
static esp_ota_handle_t handle = 0;
if (handle != 0) { /* clean up from any previous test */
esp_ota_end(handle);
handle = 0;
}
/* running partition & configured boot partition are same */
TEST_ASSERT_NOT_NULL(running);
/* trying to 'begin' on running partition fails */
TEST_ASSERT_NOT_EQUAL(ESP_OK, esp_ota_begin(running, OTA_SIZE_UNKNOWN, &handle));
TEST_ASSERT_EQUAL(0, handle);
memcpy(&partition, running, sizeof(esp_partition_t));
partition.address--;
/* non existent partition fails */
TEST_ASSERT_EQUAL_HEX(ESP_ERR_NOT_FOUND, esp_ota_begin(&partition, OTA_SIZE_UNKNOWN, &handle));
TEST_ASSERT_EQUAL(0, handle);
}
TEST_CASE("esp_ota_get_next_update_partition logic", "[ota]")
{
const esp_partition_t *running = esp_ota_get_running_partition();
const esp_partition_t *factory = esp_partition_find_first(ESP_PARTITION_TYPE_APP,
ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
const esp_partition_t *ota_0 = esp_partition_find_first(ESP_PARTITION_TYPE_APP,
ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL);
const esp_partition_t *ota_1 = esp_partition_find_first(ESP_PARTITION_TYPE_APP,
ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL);
const esp_partition_t *ota_2 = esp_partition_find_first(ESP_PARTITION_TYPE_APP,
ESP_PARTITION_SUBTYPE_APP_OTA_2, NULL);
TEST_ASSERT_NOT_NULL(running);
TEST_ASSERT_NOT_NULL(factory);
TEST_ASSERT_NOT_NULL(ota_0);
TEST_ASSERT_NOT_NULL(ota_1);
TEST_ASSERT_NULL(ota_2); /* this partition shouldn't exist in test partition table */
TEST_ASSERT_EQUAL_PTR(factory, running); /* this may not be true if/when we get OTA tests that do OTA updates */
/* (The test steps verify subtypes before verifying pointer equality, because the failure messages are more readable
this way.)
*/
/* Factory app OTA updates OTA 0 slot */
const esp_partition_t *p = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_0, p->subtype);
TEST_ASSERT_EQUAL_PTR(ota_0, p);
p = esp_ota_get_next_update_partition(factory);
TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_0, p->subtype);
TEST_ASSERT_EQUAL_PTR(ota_0, p);
/* OTA slot 0 updates OTA slot 1 */
p = esp_ota_get_next_update_partition(ota_0);
TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_1, p->subtype);
TEST_ASSERT_EQUAL_PTR(ota_1, p);
/* OTA slot 1 updates OTA slot 0 */
p = esp_ota_get_next_update_partition(ota_1);
TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_0, p->subtype);;
TEST_ASSERT_EQUAL_PTR(ota_0, p);
}
TEST_CASE("esp_ota_get_partition_description", "[ota]")
{
extern esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc);
const esp_partition_t *running = esp_ota_get_running_partition();
TEST_ASSERT_NOT_NULL(running);
esp_app_desc_t app_desc1, app_desc2;
TEST_ESP_OK(esp_ota_get_partition_description(running, &app_desc1));
const esp_partition_pos_t running_pos = {
.offset = running->address,
.size = running->size
};
TEST_ESP_OK(bootloader_common_get_partition_description(&running_pos, &app_desc2));
TEST_ASSERT_EQUAL_MEMORY_MESSAGE((uint8_t *)&app_desc1, (uint8_t *)&app_desc2, sizeof(app_desc1), "must be the same");
const esp_partition_t *not_app = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
TEST_ASSERT_NOT_NULL(not_app);
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_partition_description(not_app, &app_desc1));
const esp_partition_pos_t not_app_pos = {
.offset = not_app->address,
.size = not_app->size
};
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, bootloader_common_get_partition_description(&not_app_pos, &app_desc1));
}
TEST_CASE("esp_ota_get_running_partition points to correct address", "[spi_flash]")
{
const esp_partition_t *factory = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, "factory");
const esp_partition_t* part = esp_ota_get_running_partition();
ESP_LOGI("running bin", "0x%p", (void*)part->address);
TEST_ASSERT_EQUAL_HEX32(factory->address, part->address);
}

View File

@@ -1,256 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_flash_partitions.h"
#include "esp_flash_internal.h"
#include "spi_flash_mmap.h"
#include "esp_image_format.h"
#include "esp_system.h"
#include "esp_log.h"
#include "unity.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
ESP_LOG_ATTR_TAG(TAG, "test");
static uint8_t buffer[SPI_FLASH_SEC_SIZE];
// Find the unused offset after the last partition, checking that it is of the required size
static uint32_t find_unused_space(size_t required_size)
{
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
TEST_ASSERT_NOT_NULL(it);
const esp_partition_t* latest_partition = esp_partition_get(it);
for (; it != NULL; it = esp_partition_next(it)) {
const esp_partition_t *p = esp_partition_get(it);
if (p->address > latest_partition->address) {
latest_partition = p;
}
}
esp_partition_iterator_release(it);
TEST_ASSERT_NOT_NULL(latest_partition);
#if CONFIG_IDF_TARGET_LINUX
uint32_t flash_chip_size;
esp_flash_get_size(NULL, &flash_chip_size);
#else
uint32_t flash_chip_size = esp_flash_default_chip->size;
#endif // CONFIG_IDF_TARGET_LINUX
uint32_t unused_offset = latest_partition->address + latest_partition->size;
TEST_ASSERT_GREATER_OR_EQUAL_UINT32(required_size, flash_chip_size - unused_offset);
return unused_offset;
}
static void check_after_reboot(void)
{
ESP_LOGI(TAG, "App runs");
}
static void download_new_image_from_partition(esp_ota_handle_t update_handle, const esp_partition_t *copy_from_part)
{
uint32_t offset = 0;
ESP_LOGI(TAG, "Downloading image...");
do {
TEST_ESP_OK(esp_partition_read(copy_from_part, offset, buffer, sizeof(buffer)));
TEST_ESP_OK(esp_ota_write(update_handle, buffer, sizeof(buffer)));
offset += sizeof(buffer);
} while (offset < copy_from_part->size);
}
static void start_bootloader_ota_update_via_ota_bootloader_part(void)
{
const esp_partition_t *primary_bootloader;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_BOOTLOADER_OFFSET, ESP_BOOTLOADER_SIZE, "PrimaryBTLDR", ESP_PARTITION_TYPE_BOOTLOADER, ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY, &primary_bootloader));
const esp_partition_t *ota_bootloader;
const uint32_t ota_bootloader_offset = find_unused_space(ESP_BOOTLOADER_SIZE);
TEST_ESP_OK(esp_partition_register_external(NULL, ota_bootloader_offset, ESP_BOOTLOADER_SIZE, "OtaBTLDR", ESP_PARTITION_TYPE_BOOTLOADER, ESP_PARTITION_SUBTYPE_BOOTLOADER_OTA, &ota_bootloader));
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(ota_bootloader, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
TEST_ESP_OK(esp_ota_set_final_partition(update_handle, primary_bootloader, true));
download_new_image_from_partition(update_handle, primary_bootloader);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_partition_deregister_external(primary_bootloader));
TEST_ESP_OK(esp_partition_deregister_external(ota_bootloader));
esp_restart();
}
TEST_CASE_MULTIPLE_STAGES("OTA update of bootloader via temp partition", "[bootloader_ota][reset=SW_CPU_RESET]", start_bootloader_ota_update_via_ota_bootloader_part, check_after_reboot);
static void start_bootloader_ota_update_via_primary_bootloader_part(void)
{
const esp_partition_t *primary_bootloader;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_BOOTLOADER_OFFSET, ESP_BOOTLOADER_SIZE, "PrimaryBTLDR", ESP_PARTITION_TYPE_BOOTLOADER, ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY, &primary_bootloader));
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(primary_bootloader, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
download_new_image_from_partition(update_handle, primary_bootloader);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_partition_deregister_external(primary_bootloader));
esp_restart();
}
TEST_CASE_MULTIPLE_STAGES("OTA update of bootloader via primary partition", "[bootloader_ota][reset=SW_CPU_RESET]", start_bootloader_ota_update_via_primary_bootloader_part, check_after_reboot);
static void start_partition_table_ota_update_via_ota_part_table(void)
{
const esp_partition_t *primary_partition_table;
const esp_partition_t *ota_partition_table;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_SIZE, "PrimaryPrtTable", ESP_PARTITION_TYPE_PARTITION_TABLE, ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY, &primary_partition_table));
uint32_t ota_partition_table_offset = find_unused_space(ESP_PARTITION_TABLE_SIZE);
TEST_ESP_OK(esp_partition_register_external(NULL, ota_partition_table_offset, ESP_PARTITION_TABLE_SIZE, "OtaPrtTable", ESP_PARTITION_TYPE_PARTITION_TABLE, ESP_PARTITION_SUBTYPE_PARTITION_TABLE_OTA, &ota_partition_table));
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(ota_partition_table, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
TEST_ESP_OK(esp_ota_set_final_partition(update_handle, primary_partition_table, true));
download_new_image_from_partition(update_handle, primary_partition_table);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_partition_deregister_external(primary_partition_table));
TEST_ESP_OK(esp_partition_deregister_external(ota_partition_table));
esp_restart();
}
TEST_CASE_MULTIPLE_STAGES("OTA update of partition_table via temp partition", "[partition_table_ota][reset=SW_CPU_RESET]", start_partition_table_ota_update_via_ota_part_table, check_after_reboot);
static void start_partition_table_ota_update_via_primary_part_table(void)
{
const esp_partition_t *primary_partition_table;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_SIZE, "PrimaryPrtTable", ESP_PARTITION_TYPE_PARTITION_TABLE, ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY, &primary_partition_table));
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(primary_partition_table, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
download_new_image_from_partition(update_handle, primary_partition_table);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_partition_deregister_external(primary_partition_table));
esp_restart();
}
TEST_CASE_MULTIPLE_STAGES("OTA update of partition_table via primary partition", "[partition_table_ota][reset=SW_CPU_RESET]", start_partition_table_ota_update_via_primary_part_table, check_after_reboot);
TEST_CASE("OTA update of NVS partition", "[nvs_ota]")
{
// initialize "nvs" partition and define a var (magic_value).
TEST_ESP_OK(nvs_flash_erase());
TEST_ESP_OK(nvs_flash_init());
nvs_handle_t my_handle;
TEST_ESP_OK(nvs_open("namespace", NVS_READWRITE, &my_handle));
uint32_t magic_value = 0x0729FEED;
TEST_ESP_OK(nvs_set_u32(my_handle, "magic_value", magic_value));
TEST_ESP_OK(nvs_commit(my_handle));
magic_value = 0;
TEST_ESP_OK(nvs_get_u32(my_handle, "magic_value", &magic_value));
TEST_ASSERT_EQUAL_HEX(0x0729FEED, magic_value);
nvs_close(my_handle);
TEST_ESP_OK(nvs_flash_deinit());
// register a new "nvs2" partition
const esp_partition_t *nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs");
const esp_partition_t *nvs2_part;
TEST_ESP_OK(esp_partition_register_external(NULL, find_unused_space(nvs_part->size), nvs_part->size, "nvs2", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, &nvs2_part));
ESP_LOGI(TAG, "Use %s partition (0x%08" PRIx32 ") to load a new image", nvs2_part->label, nvs2_part->address);
TEST_ESP_OK(nvs_flash_erase_partition("nvs2"));
// OTA update of the new "nvs2" partition, taking "nvs" partition as source.
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(nvs2_part, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
download_new_image_from_partition(update_handle, nvs_part);
TEST_ESP_OK(esp_ota_end(update_handle));
// init "nvs2" partition and check if the magic_value == 0x0729FEED
TEST_ESP_OK(nvs_flash_init_partition("nvs2"));
nvs_handle_t my_handle2;
TEST_ESP_OK(nvs_open_from_partition("nvs2", "namespace", NVS_READWRITE, &my_handle2));
magic_value = 0;
TEST_ESP_OK(nvs_get_u32(my_handle2, "magic_value", &magic_value));
TEST_ASSERT_EQUAL_HEX(0x0729FEED, magic_value);
nvs_close(my_handle2);
TEST_ESP_OK(nvs_flash_deinit_partition("nvs2"));
// deregister "nvs2"
TEST_ESP_OK(esp_partition_deregister_external(nvs2_part));
TEST_ESP_OK(nvs_flash_erase());
}
static void start_bootloader_ota_update_via_app_part(void)
{
const esp_partition_t *primary_bootloader;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_BOOTLOADER_OFFSET, ESP_BOOTLOADER_SIZE, "PrimaryBTLDR", ESP_PARTITION_TYPE_BOOTLOADER, ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY, &primary_bootloader));
const esp_partition_t *free_app_ota_partition = esp_ota_get_next_update_partition(NULL);
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(free_app_ota_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
TEST_ESP_OK(esp_ota_set_final_partition(update_handle, primary_bootloader, true));
download_new_image_from_partition(update_handle, primary_bootloader);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_partition_deregister_external(primary_bootloader));
esp_restart();
}
TEST_CASE_MULTIPLE_STAGES("OTA update of bootloader via a free ota partition", "[bootloader_ota][reset=SW_CPU_RESET]", start_bootloader_ota_update_via_app_part, check_after_reboot);
static void start_partition_table_ota_update_via_app_part(void)
{
const esp_partition_t *primary_partition_table;
TEST_ESP_OK(esp_partition_register_external(NULL, ESP_PRIMARY_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_SIZE, "PrimaryPrtTable", ESP_PARTITION_TYPE_PARTITION_TABLE, ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY, &primary_partition_table));
const esp_partition_t *free_app_ota_partition = esp_ota_get_next_update_partition(NULL);
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(free_app_ota_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
TEST_ESP_OK(esp_ota_set_final_partition(update_handle, primary_partition_table, true));
download_new_image_from_partition(update_handle, primary_partition_table);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_partition_deregister_external(primary_partition_table));
esp_restart();
}
TEST_CASE_MULTIPLE_STAGES("OTA update of partition_table via a free ota partition", "[partition_table_ota][reset=SW_CPU_RESET]", start_partition_table_ota_update_via_app_part, check_after_reboot);
TEST_CASE("OTA update of NVS partition via a free ota partition", "[nvs_ota]")
{
// initialize "nvs" partition and define a var (magic_value).
const esp_partition_t *nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs");
TEST_ESP_OK(nvs_flash_erase());
TEST_ESP_OK(nvs_flash_init());
nvs_handle_t my_handle;
TEST_ESP_OK(nvs_open("namespace", NVS_READWRITE, &my_handle));
uint32_t magic_value = 0x0729FEED;
TEST_ESP_OK(nvs_set_u32(my_handle, "magic_value", magic_value));
TEST_ESP_OK(nvs_commit(my_handle));
magic_value = 0;
TEST_ESP_OK(nvs_get_u32(my_handle, "magic_value", &magic_value));
TEST_ASSERT_EQUAL_HEX(0x0729FEED, magic_value);
nvs_close(my_handle);
TEST_ESP_OK(nvs_flash_deinit());
// 1. OTA update nvs partition into free_app_ota_partition
// 2. copy free_app_ota_partition into the original nvs partition (which was erased before coping)
const esp_partition_t *free_app_ota_partition = esp_ota_get_next_update_partition(NULL);
esp_ota_handle_t update_handle;
TEST_ESP_OK(esp_ota_begin(free_app_ota_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle));
TEST_ESP_OK(esp_ota_set_final_partition(update_handle, nvs_part, true));
download_new_image_from_partition(update_handle, nvs_part);
TEST_ESP_OK(esp_ota_end(update_handle));
// Check if the magic_value == 0x0729FEED
TEST_ESP_OK(nvs_flash_init());
TEST_ESP_OK(nvs_open("namespace", NVS_READONLY, &my_handle));
magic_value = 0;
TEST_ESP_OK(nvs_get_u32(my_handle, "magic_value", &magic_value));
TEST_ASSERT_EQUAL_HEX(0x0729FEED, magic_value);
nvs_close(my_handle);
TEST_ESP_OK(nvs_flash_deinit());
TEST_ESP_OK(nvs_flash_erase());
}

View File

@@ -1,668 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Tests for switching between partitions: factory, OTAx, test.
*/
#include "esp_system.h"
#include "bootloader_common.h"
#include "../bootloader_flash/include/bootloader_flash_priv.h"
#include "esp_log.h"
#include "unity.h"
#include "utils_update.h"
#include "sdkconfig.h"
ESP_LOG_ATTR_TAG(TAG, "ota_test");
/* @brief Checks and prepares the partition so that the factory app is launched after that.
*/
static void start_test(void)
{
ESP_LOGI(TAG, "boot count 1 - reset");
set_boot_count_in_nvs(1);
erase_ota_data();
ESP_LOGI(TAG, "ota_data erased");
reboot_as_deep_sleep();
}
static void test_flow1(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
copy_current_app_to_next_part_and_reboot();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
mark_app_valid();
copy_current_app_to_next_part_and_reboot();
break;
case 4:
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
mark_app_valid();
copy_current_app_to_next_part_and_reboot();
break;
case 5:
ESP_LOGI(TAG, "OTA0");
mark_app_valid();
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
erase_ota_data();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> copy OTA0 to OTA1 -> reboot --//--
// 4 Stage: run OTA1 -> check it -> copy OTA1 to OTA0 -> reboot --//--
// 5 Stage: run OTA0 -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, OTA1, OTA0", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow1, test_flow1, test_flow1, test_flow1);
static void test_flow2(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
copy_current_app_to_next_part_and_reboot();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
mark_app_valid();
copy_current_app_to_next_part(cur_app, get_next_update_partition());
corrupt_ota_data(CORR_CRC_1_SECTOR_OTA_DATA);
reboot_as_deep_sleep();
break;
case 4:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
erase_ota_data();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> corrupt ota data -> reboot --//--
// 4 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, corrupt ota_sec1, factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow2, test_flow2, test_flow2);
static void test_flow3(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
copy_current_app_to_next_part_and_reboot();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
mark_app_valid();
copy_current_app_to_next_part_and_reboot();
break;
case 4:
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
mark_app_valid();
copy_current_app_to_next_part(cur_app, get_next_update_partition());
corrupt_ota_data(CORR_CRC_2_SECTOR_OTA_DATA);
reboot_as_deep_sleep();
break;
case 5:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
erase_ota_data();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> copy OTA0 to OTA1 -> reboot --//--
// 3 Stage: run OTA1 -> check it -> corrupt ota sector2 -> reboot --//--
// 4 Stage: run OTA0 -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, OTA1, corrupt ota_sec2, OTA0", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow3, test_flow3, test_flow3, test_flow3);
#ifdef CONFIG_BOOTLOADER_FACTORY_RESET
static void test_flow4(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
copy_current_app_to_next_part_and_reboot();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
#ifdef BOOTLOADER_RESERVE_RTC_MEM
TEST_ASSERT_FALSE(bootloader_common_get_rtc_retain_mem_factory_reset_state());
#endif
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
mark_app_valid();
set_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET);
esp_restart();
break;
case 4:
reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET);
ESP_LOGI(TAG, "Factory");
#ifdef BOOTLOADER_RESERVE_RTC_MEM
TEST_ASSERT_TRUE(bootloader_common_get_rtc_retain_mem_factory_reset_state());
TEST_ASSERT_FALSE(bootloader_common_get_rtc_retain_mem_factory_reset_state());
#endif
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
erase_ota_data();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> set_pin_factory_reset -> reboot
// 4 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, sets pin_factory_reset, factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, SW_CPU_RESET, DEEPSLEEP_RESET]", start_test, test_flow4, test_flow4, test_flow4);
#endif
#ifdef CONFIG_BOOTLOADER_APP_TEST
static void test_flow5(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
set_output_pin(CONFIG_BOOTLOADER_NUM_PIN_APP_TEST);
esp_partition_copy(esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_TEST, NULL), 0, cur_app, 0, cur_app->size);
esp_restart();
break;
case 3:
reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_APP_TEST);
ESP_LOGI(TAG, "Test");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_TEST, cur_app->subtype);
esp_restart();
break;
case 4:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
erase_ota_data();
break;
default:
reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_APP_TEST);
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to Test and set pin_test_app -> reboot
// 3 Stage: run test -> check it -> reset pin_test_app -> reboot
// 4 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, test, factory", "[app_update][timeout=90][reset=SW_CPU_RESET, SW_CPU_RESET, DEEPSLEEP_RESET]", start_test, test_flow5, test_flow5, test_flow5);
#endif
static void test_rollback1(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
esp_ota_img_states_t ota_state = 0x5555AAAA;
const esp_partition_t* update_partition = NULL;
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_state_partition(cur_app, &ota_state));
update_partition = app_update();
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_NEW, ota_state);
#endif
reboot_as_deep_sleep();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
#endif
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
reboot_as_deep_sleep();
break;
case 4:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
TEST_ESP_OK(esp_ota_mark_app_invalid_rollback_and_reboot());
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
static void test_rollback1_1(void)
{
set_boot_count_in_nvs(5);
esp_ota_img_states_t ota_state = 0x5555AAAA;
uint8_t boot_count = get_boot_count_from_nvs();
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
const esp_partition_t *invalid_partition = esp_ota_get_last_invalid_partition();
const esp_partition_t* next_update_partition = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_NULL(invalid_partition);
TEST_ASSERT_NOT_NULL(next_update_partition);
TEST_ASSERT_EQUAL_PTR(invalid_partition, next_update_partition);
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ESP_OK(esp_ota_get_state_partition(invalid_partition, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_INVALID, ota_state);
erase_ota_data();
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to next app slot -> reboot --//--
// 3 Stage: run OTA0 -> check it -> esp_ota_mark_app_valid_cancel_rollback() -> reboot --//--
// 4 Stage: run OTA0 -> check it -> esp_ota_mark_app_invalid_rollback_and_reboot() -> reboot
// 5 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Test rollback. factory, OTA0, OTA0, rollback -> factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback1, test_rollback1, test_rollback1, test_rollback1_1);
static void test_rollback2(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
esp_ota_img_states_t ota_state = 0x5555AAAA;
const esp_partition_t* update_partition = NULL;
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_state_partition(cur_app, &ota_state));
update_partition = app_update();
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_NEW, ota_state);
#endif
reboot_as_deep_sleep();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
#endif
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
update_partition = app_update();
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_NEW, ota_state);
#endif
reboot_as_deep_sleep();
break;
case 4:
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
TEST_ESP_OK(esp_ota_mark_app_invalid_rollback_and_reboot());
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
reboot_as_deep_sleep();
#endif
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
static void test_rollback2_1(void)
{
set_boot_count_in_nvs(5);
uint8_t boot_count = get_boot_count_from_nvs();
esp_ota_img_states_t ota_state = 0x5555AAAA;
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
const esp_partition_t *invalid_partition = esp_ota_get_last_invalid_partition();
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, invalid_partition->subtype);
const esp_partition_t* next_update_partition = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_NULL(invalid_partition);
TEST_ASSERT_NOT_NULL(next_update_partition);
TEST_ASSERT_EQUAL_PTR(invalid_partition, next_update_partition);
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
TEST_ESP_OK(esp_ota_get_state_partition(invalid_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ASSERT_EQUAL(ESP_OTA_IMG_INVALID, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_ABORTED, ota_state);
#endif
erase_ota_data();
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to next app slot -> reboot --//--
// 3 Stage: run OTA0 -> check it -> esp_ota_mark_app_valid_cancel_rollback(), copy to next app slot -> reboot --//--
// 4 Stage: run OTA1 -> check it -> PENDING_VERIFY/esp_ota_mark_app_invalid_rollback_and_reboot() -> reboot
// 5 Stage: run OTA0(rollback) -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Test rollback. factory, OTA0, OTA1, rollback -> OTA0", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback2, test_rollback2, test_rollback2, test_rollback2_1);
static void test_erase_last_app_flow(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
app_update();
reboot_as_deep_sleep();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
mark_app_valid();
app_update();
reboot_as_deep_sleep();
break;
case 4:
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
TEST_ESP_OK(esp_ota_erase_last_boot_app_partition());
TEST_ESP_OK(esp_ota_mark_app_invalid_rollback_and_reboot());
reboot_as_deep_sleep();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
static void test_erase_last_app_rollback(void)
{
set_boot_count_in_nvs(5);
uint8_t boot_count = get_boot_count_from_nvs();
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
ESP_LOGI(TAG, "erase_last_app");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
TEST_ESP_ERR(ESP_FAIL, esp_ota_erase_last_boot_app_partition());
erase_ota_data();
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> copy factory to OTA1 -> reboot --//--
// 4 Stage: run OTA1 -> check it -> erase OTA0 and rollback -> reboot
// 5 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Test erase_last_boot_app_partition. factory, OTA1, OTA0, factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_erase_last_app_flow, test_erase_last_app_flow, test_erase_last_app_flow, test_erase_last_app_rollback);
static void test_flow6(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
copy_current_app_to_next_part_with_offset_and_reboot();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
mark_app_valid();
erase_ota_data();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0 using esp_ota_write_with_offset", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow6, test_flow6);
TEST_CASE("Test bootloader_common_get_sha256_of_partition returns ESP_ERR_IMAGE_INVALID when image is invalid", "[partitions]")
{
const esp_partition_t *cur_app = esp_ota_get_running_partition();
ESP_LOGI(TAG, "copy current app to next part");
const esp_partition_t *other_app = get_next_update_partition();
copy_current_app_to_next_part(cur_app, other_app);
erase_ota_data();
uint8_t sha_256_cur_app[32];
uint8_t sha_256_other_app[32];
TEST_ESP_OK(bootloader_common_get_sha256_of_partition(cur_app->address, cur_app->size, cur_app->type, sha_256_cur_app));
TEST_ESP_OK(bootloader_common_get_sha256_of_partition(other_app->address, other_app->size, other_app->type, sha_256_other_app));
TEST_ASSERT_EQUAL_MEMORY_MESSAGE(sha_256_cur_app, sha_256_other_app, sizeof(sha_256_cur_app), "must be the same");
uint32_t data = 0;
bootloader_flash_write(other_app->address + 0x50, &data, sizeof(data), false);
TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, bootloader_common_get_sha256_of_partition(other_app->address, other_app->size, other_app->type, sha_256_other_app));
TEST_ASSERT_EQUAL_MEMORY_MESSAGE(sha_256_cur_app, sha_256_other_app, sizeof(sha_256_cur_app), "must be the same");
}
static void test_rollback3(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
const esp_partition_t* update_partition = NULL;
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
update_partition = app_update();
reboot_as_deep_sleep();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
update_partition = app_update();
reboot_as_deep_sleep();
break;
case 4:
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
update_partition = esp_ota_get_next_update_partition(NULL);
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
// two partitions are valid
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
esp_ota_img_states_t ota_state;
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
#endif
esp_ota_handle_t update_handle = 0;
TEST_ESP_OK(esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle));
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
// After esp_ota_begin, the only one partition is valid
// ota data slots do not have an entry about the update_partition.
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_ota_get_state_partition(update_partition, &ota_state));
#endif
copy_app_partition(update_handle, get_running_firmware());
TEST_ESP_OK(esp_ota_end(update_handle));
// esp_ota_set_boot_partition is not called, so the running app will not be changed after reboot
reboot_as_deep_sleep();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
static void test_rollback3_1(void)
{
set_boot_count_in_nvs(5);
uint8_t boot_count = get_boot_count_from_nvs();
esp_ota_img_states_t ota_state = 0x5555AAAA;
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
const esp_partition_t* next_update_partition = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_NULL(next_update_partition);
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
// ota data slots do not have an entry about the next_update_partition.
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_ota_get_state_partition(next_update_partition, &ota_state));
#endif
erase_ota_data();
}
TEST_CASE_MULTIPLE_STAGES("Test rollback. Updated partition invalidated after esp_ota_begin", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback3, test_rollback3, test_rollback3, test_rollback3_1);
static void test_rollback4(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
app_update();
reboot_as_deep_sleep();
break;
case 3:
ESP_LOGI(TAG, "OTA0");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
app_update();
// Do not reboot and call app_update again.
// This will not change the running partition since we haven't rebooted.
// The esp_rewrite_otadata() will update the otadata for the non-running partition only.
app_update();
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
// The last call to esp_rewrite_otadata should have updated the otadata for the non-running partition only.
// Therefore, calling esp_ota_get_state_partition on the running partition should succeed and not return ESP_ERR_NOT_FOUND
const esp_partition_t* running_partition;
running_partition = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
TEST_ESP_OK(esp_ota_get_state_partition(running_partition, &ota_state));
#endif
reboot_as_deep_sleep();
break;
case 4:
ESP_LOGI(TAG, "OTA1");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
TEST_CASE_MULTIPLE_STAGES("Test esp_rewrite_otadata. Updated sequence number for non-running partition always", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback4, test_rollback4, test_rollback4);

View File

@@ -1,307 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_rom_spiflash.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "esp_partition.h"
#include "esp_flash_partitions.h"
#include "esp_image_format.h"
#include "../bootloader_flash/include/bootloader_flash_priv.h"
#include "esp_sleep.h"
#include "esp_ota_ops.h"
#include "esp_err.h"
#include "esp_log.h"
#include "test_utils.h"
#include "utils_update.h"
#include "unity.h"
#include "sdkconfig.h"
#define BOOT_COUNT_NAMESPACE "boot_count"
static const char *TAG = "ota_test";
void set_boot_count_in_nvs(uint8_t boot_count)
{
nvs_handle_t boot_count_handle;
esp_err_t err = nvs_open(BOOT_COUNT_NAMESPACE, NVS_READWRITE, &boot_count_handle);
if (err != ESP_OK) {
TEST_ESP_OK(nvs_flash_erase());
TEST_ESP_OK(nvs_flash_init());
TEST_ESP_OK(nvs_open(BOOT_COUNT_NAMESPACE, NVS_READWRITE, &boot_count_handle));
}
TEST_ESP_OK(nvs_set_u8(boot_count_handle, "boot_count", boot_count));
TEST_ESP_OK(nvs_commit(boot_count_handle));
nvs_close(boot_count_handle);
}
uint8_t get_boot_count_from_nvs(void)
{
nvs_handle_t boot_count_handle;
esp_err_t err = nvs_open(BOOT_COUNT_NAMESPACE, NVS_READONLY, &boot_count_handle);
if (err == ESP_ERR_NVS_NOT_FOUND) {
set_boot_count_in_nvs(0);
}
uint8_t boot_count;
TEST_ESP_OK(nvs_get_u8(boot_count_handle, "boot_count", &boot_count));
nvs_close(boot_count_handle);
return boot_count;
}
/* @brief Copies a current app to next partition using handle.
*
* @param[in] update_handle - Handle of API ota.
* @param[in] cur_app - Current app.
*/
void copy_app_partition(esp_ota_handle_t update_handle, const esp_partition_t *curr_app)
{
const void *partition_bin = NULL;
esp_partition_mmap_handle_t data_map;
ESP_LOGI(TAG, "start the copy process");
TEST_ESP_OK(esp_partition_mmap(curr_app, 0, curr_app->size, ESP_PARTITION_MMAP_DATA, &partition_bin, &data_map));
TEST_ESP_OK(esp_ota_write(update_handle, (const void *)partition_bin, curr_app->size));
esp_partition_munmap(data_map);
ESP_LOGI(TAG, "finish the copy process");
}
/* @brief Copies a current app to next partition using handle.
*
* @param[in] update_handle - Handle of API ota.
* @param[in] cur_app - Current app.
*/
void copy_app_partition_with_offset(esp_ota_handle_t update_handle, const esp_partition_t *curr_app)
{
const void *partition_bin = NULL;
esp_partition_mmap_handle_t data_map;
ESP_LOGI(TAG, "start the copy process");
uint32_t offset = 0, bytes_to_write = curr_app->size;
uint32_t write_bytes;
while (bytes_to_write > 0) {
write_bytes = (bytes_to_write > (4 * 1024)) ? (4 * 1024) : bytes_to_write;
TEST_ESP_OK(esp_partition_mmap(curr_app, offset, write_bytes, ESP_PARTITION_MMAP_DATA, &partition_bin, &data_map));
TEST_ESP_OK(esp_ota_write_with_offset(update_handle, (const void *)partition_bin, write_bytes, offset));
esp_partition_munmap(data_map);
bytes_to_write -= write_bytes;
offset += write_bytes;
}
ESP_LOGI(TAG, "finish the copy process");
}
/* @brief Get the next partition of OTA for the update.
*
* @return The next partition of OTA(OTA0-15).
*/
const esp_partition_t * get_next_update_partition(void)
{
const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_EQUAL(NULL, update_partition);
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%"PRIx32, update_partition->subtype, update_partition->address);
return update_partition;
}
/* @brief Copies a current app to next partition (OTA0-15) and then configure OTA data for a new boot partition.
*
* @param[in] cur_app_partition - Current app.
* @param[in] next_app_partition - Next app for boot.
*/
void copy_current_app_to_next_part(const esp_partition_t *cur_app_partition, const esp_partition_t *next_app_partition)
{
esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_EQUAL(NULL, next_app_partition);
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%"PRIx32, next_app_partition->subtype, next_app_partition->address);
esp_ota_handle_t update_handle = 0;
TEST_ESP_OK(esp_ota_begin(next_app_partition, OTA_SIZE_UNKNOWN, &update_handle));
copy_app_partition(update_handle, cur_app_partition);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_ota_set_boot_partition(next_app_partition));
}
/* @brief Copies a current app to next partition (OTA0-15) and then configure OTA data for a new boot partition.
*
* @param[in] cur_app_partition - Current app.
* @param[in] next_app_partition - Next app for boot.
*/
void copy_current_app_to_next_part_with_offset(const esp_partition_t *cur_app_partition, const esp_partition_t *next_app_partition)
{
esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_EQUAL(NULL, next_app_partition);
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%"PRIx32, next_app_partition->subtype, next_app_partition->address);
esp_ota_handle_t update_handle = 0;
TEST_ESP_OK(esp_ota_begin(next_app_partition, OTA_SIZE_UNKNOWN, &update_handle));
copy_app_partition_with_offset(update_handle, cur_app_partition);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_ota_set_boot_partition(next_app_partition));
}
/* @brief Erase otadata partition
*/
void erase_ota_data(void)
{
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
TEST_ASSERT_NOT_EQUAL(NULL, data_partition);
TEST_ESP_OK(esp_partition_erase_range(data_partition, 0, 2 * data_partition->erase_size));
}
/* @brief Reboots ESP using mode deep sleep. This mode guaranty that RTC_DATA_ATTR variables is not reset.
*/
void reboot_as_deep_sleep(void)
{
ESP_LOGI(TAG, "reboot as deep sleep");
esp_deep_sleep(20000);
TEST_FAIL_MESSAGE("Should never be reachable except when sleep is rejected, abort");
}
/* @brief Copies a current app to next partition (OTA0-15), after that ESP is rebooting and run this (the next) OTAx.
*/
void copy_current_app_to_next_part_and_reboot(void)
{
const esp_partition_t *cur_app = esp_ota_get_running_partition();
ESP_LOGI(TAG, "copy current app to next part");
copy_current_app_to_next_part(cur_app, get_next_update_partition());
reboot_as_deep_sleep();
}
/* @brief Copies a current app to next partition (OTA0-15) using esp_ota_write_with_offest(), after that ESP is rebooting and run this (the next) OTAx.
*/
void copy_current_app_to_next_part_with_offset_and_reboot(void)
{
const esp_partition_t *cur_app = esp_ota_get_running_partition();
ESP_LOGI(TAG, "copy current app to next part");
copy_current_app_to_next_part_with_offset(cur_app, get_next_update_partition());
reboot_as_deep_sleep();
}
/* @brief Get running app.
*
* @return The next partition of OTA(OTA0-15).
*/
const esp_partition_t* get_running_firmware(void)
{
const esp_partition_t *configured = esp_ota_get_boot_partition();
const esp_partition_t *running = esp_ota_get_running_partition();
// If a reboot hasn't occurred after app_update(), the configured and running partitions may differ
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08"PRIx32")",
running->type, running->subtype, running->address);
ESP_LOGI(TAG, "Configured partition type %d subtype %d (offset 0x%08"PRIx32")",
configured->type, configured->subtype, configured->address);
TEST_ASSERT_NOT_EQUAL(NULL, configured);
TEST_ASSERT_NOT_EQUAL(NULL, running);
return running;
}
/* @brief Get two copies ota_data from otadata partition.
*
* @param[in] otadata_partition - otadata partition.
* @param[out] ota_data_0 - First copy from otadata_partition.
* @param[out] ota_data_1 - Second copy from otadata_partition.
*/
void get_ota_data(const esp_partition_t *otadata_partition, esp_ota_select_entry_t *ota_data_0, esp_ota_select_entry_t *ota_data_1)
{
uint32_t offset = otadata_partition->address;
uint32_t size = otadata_partition->size;
if (offset != 0) {
const esp_ota_select_entry_t *ota_select_map;
ota_select_map = bootloader_mmap(offset, size);
TEST_ASSERT_NOT_EQUAL(NULL, ota_select_map);
memcpy(ota_data_0, ota_select_map, sizeof(esp_ota_select_entry_t));
memcpy(ota_data_1, (uint8_t *)ota_select_map + otadata_partition->erase_size, sizeof(esp_ota_select_entry_t));
bootloader_munmap(ota_select_map);
}
}
/* @brief Writes a ota_data into required sector of otadata_partition.
*
* @param[in] otadata_partition - Partition information otadata.
* @param[in] ota_data - otadata structure.
* @param[in] sec_id - Sector number 0 or 1.
*/
void write_ota_data(const esp_partition_t *otadata_partition, esp_ota_select_entry_t *ota_data, int sec_id)
{
esp_partition_write(otadata_partition, otadata_partition->erase_size * sec_id, &ota_data[sec_id], sizeof(esp_ota_select_entry_t));
}
/* @brief Makes a corrupt of ota_data.
* @param[in] err - type error
*/
void corrupt_ota_data(corrupt_ota_data_t err)
{
esp_ota_select_entry_t ota_data[2];
const esp_partition_t *otadata_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
TEST_ASSERT_NOT_EQUAL(NULL, otadata_partition);
get_ota_data(otadata_partition, &ota_data[0], &ota_data[1]);
if (err & CORR_CRC_1_SECTOR_OTA_DATA) {
ota_data[0].crc = 0;
}
if (err & CORR_CRC_2_SECTOR_OTA_DATA) {
ota_data[1].crc = 0;
}
TEST_ESP_OK(esp_partition_erase_range(otadata_partition, 0, otadata_partition->size));
write_ota_data(otadata_partition, &ota_data[0], 0);
write_ota_data(otadata_partition, &ota_data[1], 1);
}
#if defined(CONFIG_BOOTLOADER_FACTORY_RESET) || defined(CONFIG_BOOTLOADER_APP_TEST)
/* @brief Sets the pin number to output and sets output level as low. After reboot (deep sleep) this pin keep the same level.
*
* The output level of the pad will be force locked and can not be changed.
* Power down or call gpio_hold_dis will disable this function.
*
* @param[in] num_pin - Pin number
*/
void set_output_pin(uint32_t num_pin)
{
TEST_ESP_OK(gpio_hold_dis(num_pin));
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << num_pin);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
TEST_ESP_OK(gpio_config(&io_conf));
TEST_ESP_OK(gpio_set_level(num_pin, 0));
TEST_ESP_OK(gpio_hold_en(num_pin));
}
/* @brief Unset the pin number hold function.
*/
void reset_output_pin(uint32_t num_pin)
{
TEST_ESP_OK(gpio_hold_dis(num_pin));
TEST_ESP_OK(gpio_reset_pin(num_pin));
}
#endif
void mark_app_valid(void)
{
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
#endif
}
const esp_partition_t* app_update(void)
{
const esp_partition_t *cur_app = get_running_firmware();
const esp_partition_t* update_partition = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_NULL(update_partition);
esp_ota_handle_t update_handle = 0;
TEST_ESP_OK(esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle));
copy_app_partition(update_handle, cur_app);
TEST_ESP_OK(esp_ota_end(update_handle));
TEST_ESP_OK(esp_ota_set_boot_partition(update_partition));
return update_partition;
}

View File

@@ -1,148 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_ota_ops.h"
#include "esp_partition.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Enumeration for specifying which OTA data sectors' CRCs to corrupt.
*/
typedef enum {
CORR_CRC_1_SECTOR_OTA_DATA = (1 << 0), /*!< Corrupt CRC only 1 sector of ota_data */
CORR_CRC_2_SECTOR_OTA_DATA = (1 << 1), /*!< Corrupt CRC only 2 sector of ota_data */
} corrupt_ota_data_t;
/**
* @brief Set boot count value in NVS.
* @param boot_count Value to set.
*/
void set_boot_count_in_nvs(uint8_t boot_count);
/**
* @brief Get boot count value from NVS.
* @return Boot count value.
*/
uint8_t get_boot_count_from_nvs(void);
/**
* @brief Copy current app to next partition using OTA handle.
* @param update_handle OTA update handle.
* @param curr_app Current app partition.
*/
void copy_app_partition(esp_ota_handle_t update_handle, const esp_partition_t *curr_app);
/**
* @brief Copy current app to next partition using OTA handle with offset.
* @param update_handle OTA update handle.
* @param curr_app Current app partition.
*/
void copy_app_partition_with_offset(esp_ota_handle_t update_handle, const esp_partition_t *curr_app);
/**
* @brief Get the next OTA update partition.
* @return Pointer to next OTA partition.
*/
const esp_partition_t * get_next_update_partition(void);
/**
* @brief Copy current app to next partition and set boot partition.
* @param cur_app_partition Current app partition.
* @param next_app_partition Next app partition.
*/
void copy_current_app_to_next_part(const esp_partition_t *cur_app_partition, const esp_partition_t *next_app_partition);
/**
* @brief Copy current app to next partition with offset and set boot partition.
* @param cur_app_partition Current app partition.
* @param next_app_partition Next app partition.
*/
void copy_current_app_to_next_part_with_offset(const esp_partition_t *cur_app_partition, const esp_partition_t *next_app_partition);
/**
* @brief Erase OTA data partition.
*/
void erase_ota_data(void);
/**
* @brief Reboot ESP using deep sleep mode.
*/
void reboot_as_deep_sleep(void);
/**
* @brief Copy current app to next partition and reboot.
*/
void copy_current_app_to_next_part_and_reboot(void);
/**
* @brief Copy current app to next partition with offset and reboot.
*/
void copy_current_app_to_next_part_with_offset_and_reboot(void);
/**
* @brief Get running firmware partition.
* @return Pointer to running firmware partition.
*/
const esp_partition_t* get_running_firmware(void);
/**
* @brief Get two OTA data copies from OTA data partition.
* @param otadata_partition OTA data partition.
* @param ota_data_0 First OTA data copy.
* @param ota_data_1 Second OTA data copy.
*/
void get_ota_data(const esp_partition_t *otadata_partition, esp_ota_select_entry_t *ota_data_0, esp_ota_select_entry_t *ota_data_1);
/**
* @brief Write OTA data into required sector of OTA data partition.
* @param otadata_partition OTA data partition.
* @param ota_data OTA data structure.
* @param sec_id Sector number (0 or 1).
*/
void write_ota_data(const esp_partition_t *otadata_partition, esp_ota_select_entry_t *ota_data, int sec_id);
/**
* @brief Corrupt OTA data for testing.
* @param err Type of corruption.
*/
void corrupt_ota_data(corrupt_ota_data_t err);
#if defined(CONFIG_BOOTLOADER_FACTORY_RESET) || defined(CONFIG_BOOTLOADER_APP_TEST)
/**
* @brief Set output pin to low and hold state.
* @param num_pin Pin number.
*/
void set_output_pin(uint32_t num_pin);
/**
* @brief Reset output pin hold function.
* @param num_pin Pin number.
*/
void reset_output_pin(uint32_t num_pin);
#endif
/**
* @brief Mark app as valid and cancel rollback.
*/
void mark_app_valid(void);
/**
* @brief Perform app update and set new boot partition.
* @return Pointer to updated partition.
*/
const esp_partition_t* app_update(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,9 +0,0 @@
# Special partition table for unit test app_update
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x4000
otadata, data, ota, , 0x2000
phy_init, data, phy, , 0x1000
factory, 0, 0, , 0xB0000
ota_0, 0, ota_0, , 0xB0000
ota_1, 0, ota_1, , 0xB0000
test, 0, test, , 0xB0000
1 # Special partition table for unit test app_update
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, , 0x4000
4 otadata, data, ota, , 0x2000
5 phy_init, data, phy, , 0x1000
6 factory, 0, 0, , 0xB0000
7 ota_0, 0, ota_0, , 0xB0000
8 ota_1, 0, ota_1, , 0xB0000
9 test, 0, test, , 0xB0000

View File

@@ -1,9 +0,0 @@
# Special partition table for unit test app_update
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x4000
otadata, data, ota, , 0x2000
phy_init, data, phy, , 0x1000
factory, 0, 0, , 0x70000
ota_0, 0, ota_0, , 0x70000
ota_1, 0, ota_1, , 0x70000
test, 0, test, , 0x70000
1 # Special partition table for unit test app_update
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, , 0x4000
4 otadata, data, ota, , 0x2000
5 phy_init, data, phy, , 0x1000
6 factory, 0, 0, , 0x70000
7 ota_0, 0, ota_0, , 0x70000
8 ota_1, 0, ota_1, , 0x70000
9 test, 0, test, , 0x70000

View File

@@ -1,77 +0,0 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import re
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
DEFAULT_TIMEOUT = 20
TEST_SUBMENU_PATTERN_PYTEST = re.compile(rb'\s+\((\d+)\)\s+"([^"]+)"\r?\n')
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'defaults',
],
indirect=True,
)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
def test_app_update(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'xip_psram',
],
indirect=True,
)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
def test_app_update_xip_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'xip_psram_with_rom_impl',
],
indirect=True,
)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
def test_app_update_xip_psram_rom_impl(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'rollback',
],
indirect=True,
)
@idf_parametrize('target', ['esp32', 'esp32c3', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_app_update_with_rollback(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.recovery_bootloader
@pytest.mark.parametrize(
'config',
['recovery_bootloader'],
indirect=True,
)
@idf_parametrize('target', ['esp32c5'], indirect=['target'])
def test_recovery_bootloader_update(dut: Dut) -> None:
try:
dut.run_all_single_board_cases(group='recovery_bootloader', timeout=90)
finally:
# Erase recovery bootloader after test because it may interfere with other tests using this runner
dut.serial.erase_flash()

View File

@@ -1,2 +0,0 @@
# don't delete.
# used for CI to compile a default config when 'sdkconfig.ci.xxxx' is exist

View File

@@ -1,3 +0,0 @@
CONFIG_BOOTLOADER_RECOVERY_ENABLE=y
CONFIG_BOOTLOADER_RECOVERY_OFFSET=0x3F0000
CONFIG_PARTITION_TABLE_OFFSET=0x9000

View File

@@ -1,2 +0,0 @@
# ESP32C5 supports the Recovery bootloader feature in ROM starting from v1.0 (ECO2)
CONFIG_IDF_TARGET="esp32c5"

View File

@@ -1,2 +0,0 @@
# ESP32C61 supports the Recovery bootloader feature in ROM starting from v1.0 (ECO3)
CONFIG_IDF_TARGET="esp32c61"

View File

@@ -1,2 +0,0 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y

View File

@@ -1,3 +0,0 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_SPI_FLASH_ROM_IMPL=y

View File

@@ -1,22 +0,0 @@
# General options for additional checks
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_COMPILER_WARN_WRITE_STRINGS=y
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_two_ota.csv"
CONFIG_PARTITION_TABLE_FILENAME="partition_table_unit_test_two_ota.csv"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_OFFSET=0x18000
CONFIG_BOOTLOADER_FACTORY_RESET=y
CONFIG_BOOTLOADER_APP_TEST=y
CONFIG_BOOTLOADER_DATA_FACTORY_RESET=""
CONFIG_BOOTLOADER_HOLD_TIME_GPIO=2
CONFIG_BOOTLOADER_OTA_DATA_ERASE=y

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=32
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,6 +0,0 @@
CONFIG_IDF_TARGET="esp32c2"
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_two_ota_2m.csv"
CONFIG_PARTITION_TABLE_FILENAME="partition_table_unit_test_two_ota_2m.csv"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32c3"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32c5"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32c6"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32h2"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=22
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=19

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32s2"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,3 +0,0 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=18
CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4

View File

@@ -1,43 +0,0 @@
idf_build_get_property(target IDF_TARGET)
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
endif()
idf_component_register(PRIV_REQUIRES partition_table esptool_py)
# Do not generate flash file when building bootloader or is in early expansion of the build
# This also applies to the ESP-TEE build, as the esp_tee component only requires the
# Kconfig options from the bootloader
if(BOOTLOADER_BUILD OR esp_tee_build OR NOT CONFIG_APP_BUILD_BOOTLOADER)
return()
endif()
add_dependencies(bootloader partition_table_bin)
# When secure boot is enabled and CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT is not enabled
# do not flash the bootloader along with the other artifacts using the command `idf.py flash`
if(NOT CONFIG_SECURE_BOOT OR CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT)
set(flash_bootloader FLASH_IN_PROJECT)
endif()
esptool_py_custom_target(bootloader-flash bootloader "bootloader")
esptool_py_flash_target_image(bootloader-flash bootloader
${CONFIG_BOOTLOADER_OFFSET_IN_FLASH}
"${BOOTLOADER_BUILD_DIR}/bootloader.bin")
# Also attach an image to the project flash target
if(NOT CONFIG_SECURE_BOOT OR CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT)
esptool_py_flash_target_image(flash bootloader
${CONFIG_BOOTLOADER_OFFSET_IN_FLASH}
"${BOOTLOADER_BUILD_DIR}/bootloader.bin")
# Add bootloader as a dependency to the flash target
add_dependencies(flash bootloader)
if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
# Also add bootloader as a dependency to the encrypted-flash target
add_dependencies(encrypted-flash bootloader)
endif()
endif()

View File

@@ -1,68 +0,0 @@
menu "Application Rollback"
config BOOTLOADER_APP_ROLLBACK_ENABLE
bool "Enable app rollback support"
default n
help
After updating the app, the bootloader runs a new app with the "ESP_OTA_IMG_PENDING_VERIFY" state set.
This state prevents the re-run of this app. After the first boot of the new app in the user code, the
function should be called to confirm the operability of the app or vice versa about its non-operability.
If the app is working, then it is marked as valid. Otherwise, it is marked as not valid and rolls back to
the previous working app. A reboot is performed, and the app is booted before the software update.
Note: If during the first boot a new app the power goes out or the WDT works, then roll back will happen.
Rollback is possible only between the apps with the same security versions.
config BOOTLOADER_APP_ANTI_ROLLBACK
bool "Enable app anti-rollback support"
depends on BOOTLOADER_APP_ROLLBACK_ENABLE
default n
help
This option prevents rollback to previous firmware/application image with lower security version.
config BOOTLOADER_APP_SECURE_VERSION
int "eFuse secure version of app"
depends on BOOTLOADER_APP_ANTI_ROLLBACK
default 0
help
The secure version is the sequence number stored in the header of each firmware.
The security version is set in the bootloader, version is recorded in the eFuse field
as the number of set ones. The allocated number of bits in the efuse field
for storing the security version is limited (see BOOTLOADER_APP_SEC_VER_SIZE_EFUSE_FIELD option).
Bootloader: When bootloader selects an app to boot, an app is selected that has
a security version greater or equal that recorded in eFuse field.
The app is booted with a higher (or equal) secure version.
The security version is worth increasing if in previous versions there is
a significant vulnerability and their use is not acceptable.
Your partition table should has a scheme with ota_0 + ota_1 (without factory).
config BOOTLOADER_APP_SEC_VER_SIZE_EFUSE_FIELD
int "Size of the efuse secure version field"
depends on BOOTLOADER_APP_ANTI_ROLLBACK
range 1 32 if IDF_TARGET_ESP32
default 32 if IDF_TARGET_ESP32
range 1 4 if IDF_TARGET_ESP32C2
default 4 if IDF_TARGET_ESP32C2
range 1 16
default 16
help
The size of the efuse secure version field.
Its length is limited to 32 bits for ESP32 and 16 bits for ESP32-S2.
This determines how many times the security version can be increased.
config BOOTLOADER_EFUSE_SECURE_VERSION_EMULATE
bool "Emulate operations with efuse secure version(only test)"
default n
depends on BOOTLOADER_APP_ANTI_ROLLBACK
select EFUSE_VIRTUAL
select EFUSE_VIRTUAL_KEEP_IN_FLASH
help
This option allows to emulate read/write operations with all eFuses and efuse secure version.
It allows to test anti-rollback implementation without permanent write eFuse bits.
There should be an entry in partition table with following details: `emul_efuse, data, efuse, , 0x2000`.
This option enables: EFUSE_VIRTUAL and EFUSE_VIRTUAL_KEEP_IN_FLASH.
endmenu

View File

@@ -1,54 +0,0 @@
menu "Recovery Bootloader and Rollback"
config BOOTLOADER_RECOVERY_ENABLE
bool "Enable Recovery Bootloader"
depends on SOC_RECOVERY_BOOTLOADER_SUPPORTED
default n
help
The recovery bootloader feature is implemented in the ROM bootloader. It is required for safe OTA
updates of the bootloader. The feature is activated when the eFuse field
(ESP_EFUSE_RECOVERY_BOOTLOADER_FLASH_SECTOR) is set, which defines the flash address of the
recovery bootloader. If activated and the primary bootloader fails to load, the ROM bootloader
will attempt to load the recovery bootloader from the address specified in eFuse.
config BOOTLOADER_RECOVERY_OFFSET
hex "Recovery Bootloader Flash Offset"
depends on BOOTLOADER_RECOVERY_ENABLE
default 0x3F0000
range 0x0 0xFFE000
help
Flash address where the recovery bootloader is stored.
This value must be written to the eFuse field (ESP_EFUSE_RECOVERY_BOOTLOADER_FLASH_SECTOR)
to activate the recovery bootloader in the ROM bootloader. The eFuse can be programmed
using espefuse or in the user application with the API esp_efuse_set_recovery_bootloader_offset().
Setting this value in the config allows parttool.py to verify that it does not overlap with existing
partitions in the partition table.
The address must be a multiple of the flash sector size (0x1000 bytes).
The eFuse field stores the offset in sectors.
If the feature is no longer needed or unused, you can burn the 0xFFF value to disable this feature in
the ROM bootloader.
config BOOTLOADER_ANTI_ROLLBACK_ENABLE
bool "Enable bootloader rollback support"
depends on BOOTLOADER_RECOVERY_ENABLE
default n
help
This option prevents rollback to previous bootloader image with lower security version.
config BOOTLOADER_SECURE_VERSION
int "Secure version of bootloader"
depends on BOOTLOADER_ANTI_ROLLBACK_ENABLE
default 0
range 0 4
help
The secure version is the sequence number stored in the header of each bootloader.
The ROM Bootloader which runs the 2nd stage bootloader (PRIMARY or RECOVERY) checks that
the security version is greater or equal that recorded in the eFuse field.
Bootloaders that have a secure version in the image < secure version in efuse will not boot.
The security version is worth increasing if in previous versions there is
a significant vulnerability and their use is not acceptable.
endmenu

View File

@@ -1,57 +0,0 @@
menu "Log"
choice BOOTLOADER_LOG_VERSION
prompt "Log version"
help
Select the log version to be used by the ESP log component.
The app log version (CONFIG_LOG_VERSION) controls the version used in the bootloader,
preventing the selection of different versions.
For description of V1 and V2 see CONFIG_LOG_VERSION.
config BOOTLOADER_LOG_VERSION_1
bool "V1" if LOG_VERSION_1
config BOOTLOADER_LOG_VERSION_2
bool "V2" if LOG_VERSION_2
endchoice
config BOOTLOADER_LOG_VERSION
int
default 1 if BOOTLOADER_LOG_VERSION_1
default 2 if BOOTLOADER_LOG_VERSION_2
help
This configuration sets the log version number based on the chosen log version.
choice BOOTLOADER_LOG_LEVEL
bool "Bootloader log verbosity"
default BOOTLOADER_LOG_LEVEL_INFO
help
Specify how much output to see in bootloader logs.
config BOOTLOADER_LOG_LEVEL_NONE
bool "No output"
config BOOTLOADER_LOG_LEVEL_ERROR
bool "Error"
config BOOTLOADER_LOG_LEVEL_WARN
bool "Warning"
config BOOTLOADER_LOG_LEVEL_INFO
bool "Info"
config BOOTLOADER_LOG_LEVEL_DEBUG
bool "Debug"
config BOOTLOADER_LOG_LEVEL_VERBOSE
bool "Verbose"
endchoice
config BOOTLOADER_LOG_LEVEL
int
default 0 if BOOTLOADER_LOG_LEVEL_NONE
default 1 if BOOTLOADER_LOG_LEVEL_ERROR
default 2 if BOOTLOADER_LOG_LEVEL_WARN
default 3 if BOOTLOADER_LOG_LEVEL_INFO
default 4 if BOOTLOADER_LOG_LEVEL_DEBUG
default 5 if BOOTLOADER_LOG_LEVEL_VERBOSE
rsource "Kconfig.log.format"
rsource "Kconfig.log.settings"
endmenu

View File

@@ -1,64 +0,0 @@
menu "Format"
config BOOTLOADER_LOG_COLORS
bool "Color"
default n
select BOOTLOADER_LOG_COLORS_SUPPORT if BOOTLOADER_LOG_VERSION_2
help
Enable ANSI terminal color codes. Logs (info, errors, warnings) will contain color codes.
In order to view these, your terminal program must support ANSI color codes.
config BOOTLOADER_LOG_COLORS_SUPPORT
bool "Allow enabling color output at run time"
depends on BOOTLOADER_LOG_VERSION_2
default n
help
Enables support for color codes in the esp_log() function. If CONFIG_LOG_COLORS is enabled, this option
is always active. If CONFIG_LOG_COLORS is disabled, this option allows you to still handle color codes
in specific files by defining ESP_LOG_COLOR_DISABLED as 0 before including esp_log.h.
Note that enabling this option may slightly increase RAM/FLASH usage due to additional color handling
functionality. It provides flexibility to manage color output even when CONFIG_LOG_COLORS is turned off.
choice BOOTLOADER_LOG_TIMESTAMP_SOURCE
prompt "Timestamp"
default BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS
help
Choose what sort of timestamp is displayed in the log output:
- "None" - The log will only contain the actual log messages themselves
without any time-related information. Avoiding timestamps can help conserve
processing power and memory. It might useful when you
perform log analysis or debugging, sometimes it's more straightforward
to work with logs that lack timestamps, especially if the time of occurrence
is not critical for understanding the issues.
"I log_test: info message"
- "Milliseconds since boot" is calculated from the RTOS tick count multiplied
by the tick period. This time will reset after a software reboot.
"I (112500) log_test: info message"
config BOOTLOADER_LOG_TIMESTAMP_SOURCE_NONE
bool "None"
depends on BOOTLOADER_LOG_VERSION_2
config BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS
bool "Milliseconds Since Boot"
select BOOTLOADER_LOG_TIMESTAMP_SUPPORT if BOOTLOADER_LOG_VERSION_2
endchoice # BOOTLOADER_LOG_TIMESTAMP_SOURCE
config BOOTLOADER_LOG_TIMESTAMP_SUPPORT
bool "Allow enabling timestamp output at run time"
depends on BOOTLOADER_LOG_VERSION_2
default y
help
Enables support for timestamp in the esp_log() function.
If CONFIG_LOG_TIMESTAMP_SOURCE_NONE, this option allows you to still handle timestamp
in specific files by defining ESP_LOG_TIMESTAMP_DISABLED as 0 before including esp_log.h.
Note that enabling this option may slightly increase RAM/FLASH usage due to additional timestamp handling
functionality. It provides flexibility to manage timestamp output even when
CONFIG_LOG_TIMESTAMP_SOURCE_NONE.
endmenu

View File

@@ -1,40 +0,0 @@
menu "Settings"
config BOOTLOADER_LOG_MODE_TEXT_EN
bool
config BOOTLOADER_LOG_MODE_BINARY_EN
bool
choice BOOTLOADER_LOG_MODE
prompt "Log Mode"
default BOOTLOADER_LOG_MODE_TEXT
config BOOTLOADER_LOG_MODE_TEXT
bool "Text Log Mode"
select BOOTLOADER_LOG_MODE_TEXT_EN
help
Enables text-based logging, where log messages are stored in a human-readable format.
This mode is useful for development and debugging, as it allows logs to be easily
read and interpreted without additional processing.
config BOOTLOADER_LOG_MODE_BINARY
bool "Binary Log Mode"
select BOOTLOADER_LOG_MODE_BINARY_EN
depends on BOOTLOADER_LOG_VERSION_2
help
Enables binary logging with host-side format string expansion. In this mode, the
format argument of ESP_LOGx, ESP_EARLY_LOG, and ESP_DRAM_LOG macros is stored in a
NOLOAD section, not included in the final binary file. This reduces flash usage by
approximately 10% - 35%. The esp_log() function uses the binary log handler to output
messages. Instead of sending the full log string, the chip transmits only the
addresses of these strings (if present in the ELF file). If the format string
cannot be found in the ELF file, the chip sends the entire string. The host-side
monitor tool, which has access to the ELF file, reconstructs the log message using
the format string.
This reduces firmware size by eliminating format strings from
flash memory and removing the usage of printf-like functions, potentially freeing up
a few kilobytes of space. To further reduce firmware size, wrap string data with ESP_LOG_ATTR_STR.
endchoice
endmenu

View File

@@ -1,160 +0,0 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH})
# Do not generate flash file when building bootloader
if(BOOTLOADER_BUILD OR esp_tee_build OR NOT CONFIG_APP_BUILD_BOOTLOADER)
return()
endif()
# Glue to build the bootloader subproject binary as an external
# cmake project under this one
#
#
idf_build_get_property(build_dir BUILD_DIR)
set(BOOTLOADER_BUILD_DIR "${build_dir}/bootloader")
set(BOOTLOADER_ELF_FILE "${BOOTLOADER_BUILD_DIR}/bootloader.elf")
set(bootloader_binary_files
"${BOOTLOADER_ELF_FILE}"
"${BOOTLOADER_BUILD_DIR}/bootloader.bin"
"${BOOTLOADER_BUILD_DIR}/bootloader.map"
)
idf_build_get_property(project_dir PROJECT_DIR)
# There are some additional processing when CONFIG_SECURE_SIGNED_APPS. This happens
# when either CONFIG_SECURE_BOOT_V1_ENABLED or CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES.
# For both cases, the user either sets binaries to be signed during build or not
# using CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES.
#
# Regardless, pass the main project's keys (signing/verification) to the bootloader subproject
# via config.
if(CONFIG_SECURE_SIGNED_APPS)
add_custom_target(gen_secure_boot_keys)
if(CONFIG_SECURE_BOOT_V1_ENABLED)
# Check that the configuration is sane
if((CONFIG_SECURE_BOOTLOADER_REFLASHABLE AND CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH) OR
(NOT CONFIG_SECURE_BOOTLOADER_REFLASHABLE AND NOT CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH))
fail_at_build_time(bootloader "Invalid bootloader target: bad sdkconfig?")
endif()
if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE)
set(bootloader_binary_files
${bootloader_binary_files}
"${BOOTLOADER_BUILD_DIR}/bootloader-reflash-digest.bin"
"${BOOTLOADER_BUILD_DIR}/secure-bootloader-key-192.bin"
"${BOOTLOADER_BUILD_DIR}/secure-bootloader-key-256.bin"
)
endif()
endif()
# Since keys are usually given relative to main project dir, get the absolute paths to the keys
# for use by the bootloader subproject. Replace the values in config with these absolute paths,
# so that bootloader subproject does not need to assume main project dir to obtain path to the keys.
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
get_filename_component(secure_boot_signing_key
"${CONFIG_SECURE_BOOT_SIGNING_KEY}"
ABSOLUTE BASE_DIR "${project_dir}")
if(NOT EXISTS ${secure_boot_signing_key})
# If the signing key is not found, create a phony gen_secure_boot_signing_key target that
# fails the build. fail_at_build_time causes a cmake run next time
# (to pick up a new signing key if one exists, etc.)
if(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
fail_at_build_time(gen_secure_boot_signing_key
"Secure Boot Signing Key ${CONFIG_SECURE_BOOT_SIGNING_KEY} does not exist. Generate using:"
"\tidf.py secure-generate-signing-key ${CONFIG_SECURE_BOOT_SIGNING_KEY}")
else()
if(CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_192_BITS)
set(scheme "ecdsa192")
elseif(CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS)
set(scheme "ecdsa256")
elseif(CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS)
set(scheme "ecdsa384")
endif()
fail_at_build_time(gen_secure_boot_signing_key
"Secure Boot Signing Key ${CONFIG_SECURE_BOOT_SIGNING_KEY} does not exist. Generate using:"
"\tidf.py secure-generate-signing-key --scheme ${scheme} ${CONFIG_SECURE_BOOT_SIGNING_KEY}")
endif()
else()
add_custom_target(gen_secure_boot_signing_key)
endif()
set(SECURE_BOOT_SIGNING_KEY ${secure_boot_signing_key}) # needed by some other components
set(sign_key_arg "-DSECURE_BOOT_SIGNING_KEY=${secure_boot_signing_key}")
set(ver_key_arg)
add_dependencies(gen_secure_boot_keys gen_secure_boot_signing_key)
elseif(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
get_filename_component(secure_boot_verification_key
${CONFIG_SECURE_BOOT_VERIFICATION_KEY}
ABSOLUTE BASE_DIR "${project_dir}")
if(NOT EXISTS ${secure_boot_verification_key})
# If the verification key is not found, create a phony gen_secure_boot_verification_key target that
# fails the build. fail_at_build_time causes a cmake run next time
# (to pick up a new verification key if one exists, etc.)
fail_at_build_time(gen_secure_boot_verification_key
"Secure Boot Verification Public Key ${CONFIG_SECURE_BOOT_VERIFICATION_KEY} does not exist."
"\tThis can be extracted from the private signing key."
"\tSee docs/security/secure-boot-v1.rst for details.")
else()
add_custom_target(gen_secure_boot_verification_key)
endif()
set(sign_key_arg)
set(ver_key_arg "-DSECURE_BOOT_VERIFICATION_KEY=${secure_boot_verification_key}")
add_dependencies(gen_secure_boot_keys gen_secure_boot_verification_key)
endif()
else()
set(sign_key_arg)
set(ver_key_arg)
endif()
idf_build_get_property(idf_path IDF_PATH)
idf_build_get_property(idf_target IDF_TARGET)
idf_build_get_property(sdkconfig SDKCONFIG)
idf_build_get_property(python PYTHON)
idf_build_get_property(extra_cmake_args EXTRA_CMAKE_ARGS)
# BOOTLOADER_EXTRA_COMPONENT_DIRS may have been set by the `main` component, do not overwrite it
idf_build_get_property(bootloader_extra_component_dirs BOOTLOADER_EXTRA_COMPONENT_DIRS)
list(APPEND bootloader_extra_component_dirs "${CMAKE_CURRENT_LIST_DIR}")
# We cannot pass lists as a parameter to the external project without modifying the ';' separator
string(REPLACE ";" "|" BOOTLOADER_IGNORE_EXTRA_COMPONENT "${BOOTLOADER_IGNORE_EXTRA_COMPONENT}")
string(REPLACE ";" "|" bootloader_extra_component_dirs "${bootloader_extra_component_dirs}")
externalproject_add(bootloader
SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/subproject"
BINARY_DIR "${BOOTLOADER_BUILD_DIR}"
# Modiying the list separator for the arguments, as such, we won't need to manually
# replace the new separator by the default ';' in the subproject
LIST_SEPARATOR |
CMAKE_ARGS -DSDKCONFIG=${sdkconfig} -DIDF_PATH=${idf_path} -DIDF_TARGET=${idf_target}
-DPYTHON_DEPS_CHECKED=1 -DPYTHON=${python}
-DEXTRA_COMPONENT_DIRS=${bootloader_extra_component_dirs}
-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
-DIGNORE_EXTRA_COMPONENT=${BOOTLOADER_IGNORE_EXTRA_COMPONENT}
${sign_key_arg} ${ver_key_arg}
${extra_cmake_args}
INSTALL_COMMAND ""
BUILD_ALWAYS 1 # no easy way around this...
BUILD_BYPRODUCTS ${bootloader_binary_files}
)
if(CONFIG_SECURE_SIGNED_APPS)
add_dependencies(bootloader gen_secure_boot_keys)
endif()
# this is a hack due to an (annoying) shortcoming in cmake, it can't
# extend the 'clean' target to the external project
# see thread: https://cmake.org/pipermail/cmake/2016-December/064660.html
#
# So for now we just have the top-level build remove the final build products...
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY
ADDITIONAL_CLEAN_FILES
${bootloader_binary_files})

View File

@@ -1,32 +0,0 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_LOG_BOOTLOADER_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL
CONFIG_LOG_BOOTLOADER_LEVEL_NONE CONFIG_BOOTLOADER_LOG_LEVEL_NONE
CONFIG_LOG_BOOTLOADER_LEVEL_ERROR CONFIG_BOOTLOADER_LOG_LEVEL_ERROR
CONFIG_LOG_BOOTLOADER_LEVEL_WARN CONFIG_BOOTLOADER_LOG_LEVEL_WARN
CONFIG_LOG_BOOTLOADER_LEVEL_INFO CONFIG_BOOTLOADER_LOG_LEVEL_INFO
CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG
CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE
CONFIG_APP_ROLLBACK_ENABLE CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
CONFIG_APP_ANTI_ROLLBACK CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK
CONFIG_APP_SECURE_VERSION CONFIG_BOOTLOADER_APP_SECURE_VERSION
CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD CONFIG_BOOTLOADER_APP_SEC_VER_SIZE_EFUSE_FIELD
CONFIG_EFUSE_SECURE_VERSION_EMULATE CONFIG_BOOTLOADER_EFUSE_SECURE_VERSION_EMULATE
CONFIG_FLASH_ENCRYPTION_ENABLED CONFIG_SECURE_FLASH_ENC_ENABLED
CONFIG_FLASH_ENCRYPTION_INSECURE CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE
# Secure Boot Scheme
CONFIG_SECURE_BOOT_ENABLED CONFIG_SECURE_BOOT_V1_ENABLED
CONFIG_SPI_FLASH_32BIT_ADDR_ENABLE CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
CONFIG_SPI_FLASH_QUAD_32BIT_ADDR_ENABLE CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
CONFIG_SPI_FLASH_OCTAL_32BIT_ADDR_ENABLE CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_OCTAL_FLASH
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG

View File

@@ -1,2 +0,0 @@
build
sdkconfig

View File

@@ -1,294 +0,0 @@
cmake_minimum_required(VERSION 3.22)
if(NOT SDKCONFIG)
message(FATAL_ERROR "Bootloader subproject expects the SDKCONFIG variable to be passed "
"in by the parent build process.")
endif()
if(NOT IDF_PATH)
message(FATAL_ERROR "Bootloader subproject expects the IDF_PATH variable to be passed "
"in by the parent build process.")
endif()
if(NOT IDF_TARGET)
message(FATAL_ERROR "Bootloader subproject expects the IDF_TARGET variable to be passed "
"in by the parent build process.")
endif()
# A number of these components are implemented as config-only when built in the bootloader
set(COMPONENTS
bootloader
esptool_py
esp_hw_support
esp_system
freertos
hal
partition_table
soc
bootloader_support
log
spi_flash
micro-ecc
main
efuse
esp_libc
esp_tee
esp_stdio)
# EXTRA_COMPONENT_DIRS can be populated with directories containing one or several components.
# Make sure this variable contains `bootloader_components` directory of the project being compiled.
set(PROJECT_EXTRA_COMPONENTS "${PROJECT_SOURCE_DIR}/bootloader_components")
if(EXISTS ${PROJECT_EXTRA_COMPONENTS})
list(APPEND EXTRA_COMPONENT_DIRS "${PROJECT_EXTRA_COMPONENTS}")
endif()
if(IGNORE_EXTRA_COMPONENT)
# Prefix all entries of the list with ${PROJECT_EXTRA_COMPONENTS} absolute path
list(TRANSFORM IGNORE_EXTRA_COMPONENT
PREPEND "${PROJECT_EXTRA_COMPONENTS}/"
OUTPUT_VARIABLE EXTRA_COMPONENT_EXCLUDE_DIRS)
endif()
# Consider each directory in the project's bootloader_components as a component to be compiled
file(GLOB proj_components RELATIVE ${PROJECT_EXTRA_COMPONENTS} ${PROJECT_EXTRA_COMPONENTS}/*)
foreach(component ${proj_components})
# Only directories are considered components
if(IS_DIRECTORY "${PROJECT_EXTRA_COMPONENTS}/${component}" AND NOT ${component} IN_LIST IGNORE_EXTRA_COMPONENT)
list(APPEND COMPONENTS ${component})
endif()
endforeach()
set(BOOTLOADER_BUILD 1)
set(NON_OS_BUILD 1)
include("${IDF_PATH}/tools/cmake/project.cmake")
set(common_req log esp_rom esp_common esp_hw_support esp_libc)
idf_build_set_property(EXTRA_COMPONENT_EXCLUDE_DIRS "${EXTRA_COMPONENT_EXCLUDE_DIRS}")
idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${common_req}")
idf_build_set_property(__OUTPUT_SDKCONFIG 0)
# Define a property for the default linker script
set(LD_DEFAULT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/main/ld/${IDF_TARGET}")
project(bootloader)
if(CONFIG_ESP32P4_REV_MIN_300)
target_linker_script("__idf_main" INTERFACE "${LD_DEFAULT_PATH}/bootloader.rev3.ld.in")
else()
target_linker_script("__idf_main" INTERFACE "${LD_DEFAULT_PATH}/bootloader.ld.in")
endif()
idf_build_set_property(COMPILE_DEFINITIONS "BOOTLOADER_BUILD=1" APPEND)
idf_build_set_property(COMPILE_DEFINITIONS "NON_OS_BUILD=1" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-fno-stack-protector" APPEND)
# Set up the bootloader binary generation targets
set(PROJECT_BIN "bootloader.bin")
if(CONFIG_SECURE_BOOT_V2_ENABLED AND CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
set(bootloader_unsigned_bin "bootloader-unsigned.bin")
else()
set(bootloader_unsigned_bin "${PROJECT_BIN}")
endif()
# Set the final binary name as a project property
idf_build_set_property(PROJECT_BIN "${PROJECT_BIN}")
# Generate the unsigned binary from the ELF file.
if(CONFIG_APP_BUILD_GENERATE_BINARIES)
set(binary_target_name "gen_bootloader_binary")
__idf_build_binary("${bootloader_unsigned_bin}" "${binary_target_name}")
else()
# If we are not building binaries, we don't need to create targets that depend on the
# bootloader binary.
return()
endif()
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
idf_component_get_property(esptool_py_cmd esptool_py ESPTOOLPY_CMD)
idf_component_get_property(espsecure_py_cmd esptool_py ESPSECUREPY_CMD)
idf_component_get_property(espefuse_py_cmd esptool_py ESPEFUSEPY_CMD)
# String for printing flash command
string(REPLACE ";" " " esptoolpy_write_flash
"${esptool_py_cmd} --port=(PORT) --baud=(BAUD) ${main_args} "
"write-flash ${sub_args}")
string(REPLACE ";" " " espsecurepy "${espsecure_py_cmd}")
string(REPLACE ";" " " espefusepy "${espefuse_py_cmd}")
# Suppress warning: "Manually-specified variables were not used by the project: SECURE_BOOT_SIGNING_KEY"
set(ignore_signing_key "${SECURE_BOOT_SIGNING_KEY}")
if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE)
if(CONFIG_SECURE_BOOTLOADER_KEY_ENCODING_192BIT)
set(key_digest_len 192)
else()
set(key_digest_len 256)
endif()
get_filename_component(bootloader_digest_bin
"bootloader-reflash-digest.bin"
ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}")
get_filename_component(secure_bootloader_key
"secure-bootloader-key-${key_digest_len}.bin"
ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}")
add_custom_command(OUTPUT "${secure_bootloader_key}"
COMMAND ${espsecure_py_cmd} digest-private-key
--keylen "${key_digest_len}"
--keyfile "${SECURE_BOOT_SIGNING_KEY}"
"${secure_bootloader_key}"
VERBATIM)
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
add_custom_target(gen_secure_bootloader_key ALL DEPENDS "${secure_bootloader_key}")
else()
if(NOT EXISTS "${secure_bootloader_key}")
message(FATAL_ERROR
"No pre-generated key for a reflashable secure bootloader is available, "
"due to signing configuration."
"\nTo generate one, you can use this command:"
"\n\t${espsecurepy} generate-flash-encryption-key ${secure_bootloader_key}"
"\nIf a signing key is present, then instead use:"
"\n\t${espsecurepy} digest-private-key "
"--keylen (192/256) --keyfile KEYFILE "
"${secure_bootloader_key}")
endif()
add_custom_target(gen_secure_bootloader_key)
endif()
add_custom_command(OUTPUT "${bootloader_digest_bin}"
COMMAND ${CMAKE_COMMAND} -E echo "DIGEST ${bootloader_digest_bin}"
COMMAND ${espsecure_py_cmd} digest-secure-bootloader --keyfile "${secure_bootloader_key}"
-o "${bootloader_digest_bin}" "${CMAKE_BINARY_DIR}/bootloader.bin"
MAIN_DEPENDENCY "${CMAKE_BINARY_DIR}/.bin_timestamp"
DEPENDS gen_secure_bootloader_key gen_project_binary
VERBATIM)
add_custom_target(gen_bootloader_digest_bin ALL DEPENDS "${bootloader_digest_bin}")
endif()
# If secure boot is enabled, generate the signed binary from the unsigned one.
if(CONFIG_SECURE_BOOT_V2_ENABLED)
set(signed_target_name "gen_signed_bootloader")
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
# The SECURE_BOOT_SIGNING_KEY is passed in from the parent build and
# is already an absolute path.
if(NOT EXISTS "${SECURE_BOOT_SIGNING_KEY}")
message(FATAL_ERROR
"Secure Boot Signing Key Not found."
"\nGenerate the Secure Boot V2 RSA-PSS 3072 Key."
"\nTo generate one, you can use this command:"
"\n\t${espsecurepy} generate-signing-key --version 2 your_key.pem"
)
endif()
set(comment "Generated the signed Bootloader")
set(key_arg KEYFILE "${SECURE_BOOT_SIGNING_KEY}")
# Post-build commands should be attached to the signed binary target.
set(post_build_target ${signed_target_name})
else()
# If we are not building signed binaries, we don't pass a key.
set(comment "Bootloader generated but not signed")
set(key_arg "")
# Post-build commands should be attached to the unsigned binary target.
set(post_build_target ${binary_target_name})
endif()
__idf_build_secure_binary("${bootloader_unsigned_bin}" "${PROJECT_BIN}" "${signed_target_name}"
COMMENT "${comment}"
${key_arg}
)
endif()
if(CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH)
add_custom_command(TARGET gen_project_binary POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
COMMAND ${CMAKE_COMMAND} -E echo
"Bootloader built. Secure boot enabled, so bootloader not flashed automatically."
COMMAND ${CMAKE_COMMAND} -E echo
"One-time flash command is:"
COMMAND ${CMAKE_COMMAND} -E echo
"\t${esptoolpy_write_flash} ${BOOTLOADER_OFFSET} ${CMAKE_BINARY_DIR}/bootloader.bin"
COMMAND ${CMAKE_COMMAND} -E echo
"* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device"
VERBATIM)
elseif(CONFIG_SECURE_BOOTLOADER_REFLASHABLE)
add_custom_command(TARGET gen_bootloader_digest_bin POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
COMMAND ${CMAKE_COMMAND} -E echo
"Bootloader built and secure digest generated."
COMMAND ${CMAKE_COMMAND} -E echo
"Secure boot enabled, so bootloader not flashed automatically."
COMMAND ${CMAKE_COMMAND} -E echo
"Burn secure boot key to efuse using:"
COMMAND ${CMAKE_COMMAND} -E echo
"\t${espefusepy} burn-key secure_boot_v1 ${secure_bootloader_key}"
COMMAND ${CMAKE_COMMAND} -E echo
"First time flash command is:"
COMMAND ${CMAKE_COMMAND} -E echo
"\t${esptoolpy_write_flash} ${BOOTLOADER_OFFSET} ${CMAKE_BINARY_DIR}/bootloader.bin"
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
COMMAND ${CMAKE_COMMAND} -E echo
"To reflash the bootloader after initial flash:"
COMMAND ${CMAKE_COMMAND} -E echo
"\t${esptoolpy_write_flash} 0x0 ${bootloader_digest_bin}"
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
COMMAND ${CMAKE_COMMAND} -E echo
"* After first boot, only re-flashes of this kind (with same key) will be accepted."
COMMAND ${CMAKE_COMMAND} -E echo
"* Not recommended to reuse the same secure boot keyfile on multiple production devices."
VERBATIM)
elseif(
CONFIG_SECURE_BOOT_V2_ENABLED AND
(CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS GREATER 1) AND
NOT CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT
)
add_custom_command(TARGET ${post_build_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
COMMAND ${CMAKE_COMMAND} -E echo
"Bootloader built. Secure boot enabled, so bootloader not flashed automatically."
COMMAND ${CMAKE_COMMAND} -E echo
"To sign the bootloader with additional private keys."
COMMAND ${CMAKE_COMMAND} -E echo
"\t${espsecurepy} sign-data -k secure_boot_signing_key2.pem -v 2 \
--append-signatures -o signed_bootloader.bin build/bootloader/bootloader.bin"
COMMAND ${CMAKE_COMMAND} -E echo
"Secure boot enabled, so bootloader not flashed automatically."
COMMAND ${CMAKE_COMMAND} -E echo
"\t${esptoolpy_write_flash} ${BOOTLOADER_OFFSET} ${CMAKE_BINARY_DIR}/bootloader.bin"
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
VERBATIM)
elseif(CONFIG_SECURE_BOOT_V2_ENABLED AND NOT CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT)
add_custom_command(TARGET ${post_build_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
COMMAND ${CMAKE_COMMAND} -E echo
"Bootloader built. Secure boot enabled, so bootloader not flashed automatically."
COMMAND ${CMAKE_COMMAND} -E echo
"\t${esptoolpy_write_flash} ${BOOTLOADER_OFFSET} ${CMAKE_BINARY_DIR}/bootloader.bin"
COMMAND ${CMAKE_COMMAND} -E echo
"=============================================================================="
VERBATIM)
endif()
# Generate bootloader post-build check of the bootloader size against the offset
partition_table_add_check_bootloader_size_target(bootloader_check_size
DEPENDS gen_project_binary
BOOTLOADER_BINARY_PATH "${CMAKE_BINARY_DIR}/${PROJECT_BIN}"
RESULT bootloader_check_size_command)
add_dependencies(app bootloader_check_size)
if(CONFIG_SECURE_BOOT_V2_ENABLED AND CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
# Check the size of the bootloader + signature block.
partition_table_add_check_bootloader_size_target(bootloader_check_size_signed
DEPENDS gen_signed_bootloader
BOOTLOADER_BINARY_PATH "${CMAKE_BINARY_DIR}/${PROJECT_BIN}"
RESULT bootloader_check_size_signed_command)
add_dependencies(app bootloader_check_size_signed)
endif()

View File

@@ -1,3 +0,0 @@
# only compile the "uECC_verify_antifault.c" file which includes the "micro-ecc/uECC.c" source file
idf_component_register(SRCS "uECC_verify_antifault.c"
INCLUDE_DIRS . micro-ecc)

View File

@@ -1,8 +0,0 @@
__build__/
__pycache__
*.pyc
*.pyo
*.pyd
*.pyz
*.egg-info/
.DS_Store

View File

@@ -1,21 +0,0 @@
Copyright (c) 2014, Kenneth MacKay
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,41 +0,0 @@
micro-ecc
==========
A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors.
The static version of micro-ecc (ie, where the curve was selected at compile-time) can be found in the "static" branch.
Features
--------
* Resistant to known side-channel attacks.
* Written in C, with optional GCC inline assembly for AVR, ARM and Thumb platforms.
* Supports 8, 32, and 64-bit architectures.
* Small code size.
* No dynamic memory allocation.
* Support for 5 standard curves: secp160r1, secp192r1, secp224r1, secp256r1, and secp256k1.
* BSD 2-clause license.
Usage Notes
-----------
### Point Representation ###
Compressed points are represented in the standard format as defined in http://www.secg.org/sec1-v2.pdf; uncompressed points are represented in standard format, but without the `0x04` prefix. All functions except `uECC_decompress()` only accept uncompressed points; use `uECC_compress()` and `uECC_decompress()` to convert between compressed and uncompressed point representations.
Private keys are represented in the standard format.
### Using the Code ###
I recommend just copying (or symlink) the uECC files into your project. Then just `#include "uECC.h"` to use the micro-ecc functions.
For use with Arduino, you can use the Library Manager to download micro-ecc (**Sketch**=>**Include Library**=>**Manage Libraries**). You can then use uECC just like any other Arduino library (uECC should show up in the **Sketch**=>**Import Library** submenu).
See uECC.h for documentation for each function.
### Compilation Notes ###
* Should compile with any C/C++ compiler that supports stdint.h (this includes Visual Studio 2013).
* If you want to change the defaults for any of the uECC compile-time options (such as `uECC_OPTIMIZATION_LEVEL`), you must change them in your Makefile or similar so that uECC.c is compiled with the desired values (ie, compile uECC.c with `-DuECC_OPTIMIZATION_LEVEL=3` or whatever).
* When compiling for a Thumb-1 platform, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher).
* When compiling for an ARM/Thumb-2 platform with `uECC_OPTIMIZATION_LEVEL` >= 3, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher).
* When compiling for AVR, you must have optimizations enabled (compile with `-O1` or higher).
* When building for Windows, you will need to link in the `advapi32.lib` system library.

View File

@@ -1,820 +0,0 @@
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#ifndef _UECC_ASM_ARM_H_
#define _UECC_ASM_ARM_H_
#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1)
#define uECC_MIN_WORDS 8
#endif
#if uECC_SUPPORTS_secp224r1
#undef uECC_MIN_WORDS
#define uECC_MIN_WORDS 7
#endif
#if uECC_SUPPORTS_secp192r1
#undef uECC_MIN_WORDS
#define uECC_MIN_WORDS 6
#endif
#if uECC_SUPPORTS_secp160r1
#undef uECC_MIN_WORDS
#define uECC_MIN_WORDS 5
#endif
#if (uECC_PLATFORM == uECC_arm_thumb)
#define REG_RW "+l"
#define REG_WRITE "=l"
#else
#define REG_RW "+r"
#define REG_WRITE "=r"
#endif
#if (uECC_PLATFORM == uECC_arm_thumb || uECC_PLATFORM == uECC_arm_thumb2)
#define REG_RW_LO "+l"
#define REG_WRITE_LO "=l"
#else
#define REG_RW_LO "+r"
#define REG_WRITE_LO "=r"
#endif
#if (uECC_PLATFORM == uECC_arm_thumb2)
#define RESUME_SYNTAX
#else
#define RESUME_SYNTAX ".syntax divided \n\t"
#endif
#if (uECC_OPTIMIZATION_LEVEL >= 2)
uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result,
const uECC_word_t *left,
const uECC_word_t *right,
wordcount_t num_words) {
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
#if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2)
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1;
#else /* ARM */
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4;
#endif
#endif
uint32_t carry;
uint32_t left_word;
uint32_t right_word;
__asm__ volatile (
".syntax unified \n\t"
"movs %[carry], #0 \n\t"
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
"adr %[left], 1f \n\t"
".align 4 \n\t"
"adds %[jump], %[left] \n\t"
#endif
"ldmia %[lptr]!, {%[left]} \n\t"
"ldmia %[rptr]!, {%[right]} \n\t"
"adds %[left], %[right] \n\t"
"stmia %[dptr]!, {%[left]} \n\t"
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
"bx %[jump] \n\t"
#endif
"1: \n\t"
REPEAT(DEC(uECC_MAX_WORDS),
"ldmia %[lptr]!, {%[left]} \n\t"
"ldmia %[rptr]!, {%[right]} \n\t"
"adcs %[left], %[right] \n\t"
"stmia %[dptr]!, {%[left]} \n\t")
"adcs %[carry], %[carry] \n\t"
RESUME_SYNTAX
: [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right),
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
[jump] REG_RW_LO (jump),
#endif
[carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word),
[right] REG_WRITE_LO (right_word)
:
: "cc", "memory"
);
return carry;
}
#define asm_add 1
uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result,
const uECC_word_t *left,
const uECC_word_t *right,
wordcount_t num_words) {
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
#if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2)
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1;
#else /* ARM */
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4;
#endif
#endif
uint32_t carry;
uint32_t left_word;
uint32_t right_word;
__asm__ volatile (
".syntax unified \n\t"
"movs %[carry], #0 \n\t"
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
"adr %[left], 1f \n\t"
".align 4 \n\t"
"adds %[jump], %[left] \n\t"
#endif
"ldmia %[lptr]!, {%[left]} \n\t"
"ldmia %[rptr]!, {%[right]} \n\t"
"subs %[left], %[right] \n\t"
"stmia %[dptr]!, {%[left]} \n\t"
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
"bx %[jump] \n\t"
#endif
"1: \n\t"
REPEAT(DEC(uECC_MAX_WORDS),
"ldmia %[lptr]!, {%[left]} \n\t"
"ldmia %[rptr]!, {%[right]} \n\t"
"sbcs %[left], %[right] \n\t"
"stmia %[dptr]!, {%[left]} \n\t")
"adcs %[carry], %[carry] \n\t"
RESUME_SYNTAX
: [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right),
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
[jump] REG_RW_LO (jump),
#endif
[carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word),
[right] REG_WRITE_LO (right_word)
:
: "cc", "memory"
);
return !carry; /* Note that on ARM, carry flag set means "no borrow" when subtracting
(for some reason...) */
}
#define asm_sub 1
#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */
#if (uECC_OPTIMIZATION_LEVEL >= 3)
#if (uECC_PLATFORM != uECC_arm_thumb)
#if uECC_ARM_USE_UMAAL
#include "asm_arm_mult_square_umaal.inc"
#else
#include "asm_arm_mult_square.inc"
#endif
#if (uECC_OPTIMIZATION_LEVEL == 3)
uECC_VLI_API void uECC_vli_mult(uint32_t *result,
const uint32_t *left,
const uint32_t *right,
wordcount_t num_words) {
register uint32_t *r0 __asm__("r0") = result;
register const uint32_t *r1 __asm__("r1") = left;
register const uint32_t *r2 __asm__("r2") = right;
register uint32_t r3 __asm__("r3") = num_words;
__asm__ volatile (
".syntax unified \n\t"
#if (uECC_MIN_WORDS == 5)
FAST_MULT_ASM_5
#if (uECC_MAX_WORDS > 5)
FAST_MULT_ASM_5_TO_6
#endif
#if (uECC_MAX_WORDS > 6)
FAST_MULT_ASM_6_TO_7
#endif
#if (uECC_MAX_WORDS > 7)
FAST_MULT_ASM_7_TO_8
#endif
#elif (uECC_MIN_WORDS == 6)
FAST_MULT_ASM_6
#if (uECC_MAX_WORDS > 6)
FAST_MULT_ASM_6_TO_7
#endif
#if (uECC_MAX_WORDS > 7)
FAST_MULT_ASM_7_TO_8
#endif
#elif (uECC_MIN_WORDS == 7)
FAST_MULT_ASM_7
#if (uECC_MAX_WORDS > 7)
FAST_MULT_ASM_7_TO_8
#endif
#elif (uECC_MIN_WORDS == 8)
FAST_MULT_ASM_8
#endif
"1: \n\t"
RESUME_SYNTAX
: "+r" (r0), "+r" (r1), "+r" (r2)
: "r" (r3)
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
}
#define asm_mult 1
#if uECC_SQUARE_FUNC
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
const uECC_word_t *left,
wordcount_t num_words) {
register uint32_t *r0 __asm__("r0") = result;
register const uint32_t *r1 __asm__("r1") = left;
register uint32_t r2 __asm__("r2") = num_words;
__asm__ volatile (
".syntax unified \n\t"
#if (uECC_MIN_WORDS == 5)
FAST_SQUARE_ASM_5
#if (uECC_MAX_WORDS > 5)
FAST_SQUARE_ASM_5_TO_6
#endif
#if (uECC_MAX_WORDS > 6)
FAST_SQUARE_ASM_6_TO_7
#endif
#if (uECC_MAX_WORDS > 7)
FAST_SQUARE_ASM_7_TO_8
#endif
#elif (uECC_MIN_WORDS == 6)
FAST_SQUARE_ASM_6
#if (uECC_MAX_WORDS > 6)
FAST_SQUARE_ASM_6_TO_7
#endif
#if (uECC_MAX_WORDS > 7)
FAST_SQUARE_ASM_7_TO_8
#endif
#elif (uECC_MIN_WORDS == 7)
FAST_SQUARE_ASM_7
#if (uECC_MAX_WORDS > 7)
FAST_SQUARE_ASM_7_TO_8
#endif
#elif (uECC_MIN_WORDS == 8)
FAST_SQUARE_ASM_8
#endif
"1: \n\t"
RESUME_SYNTAX
: "+r" (r0), "+r" (r1)
: "r" (r2)
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
}
#define asm_square 1
#endif /* uECC_SQUARE_FUNC */
#else /* (uECC_OPTIMIZATION_LEVEL > 3) */
uECC_VLI_API void uECC_vli_mult(uint32_t *result,
const uint32_t *left,
const uint32_t *right,
wordcount_t num_words) {
register uint32_t *r0 __asm__("r0") = result;
register const uint32_t *r1 __asm__("r1") = left;
register const uint32_t *r2 __asm__("r2") = right;
register uint32_t r3 __asm__("r3") = num_words;
#if uECC_SUPPORTS_secp160r1
if (num_words == 5) {
__asm__ volatile (
".syntax unified \n\t"
FAST_MULT_ASM_5
RESUME_SYNTAX
: "+r" (r0), "+r" (r1), "+r" (r2)
: "r" (r3)
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
#if uECC_SUPPORTS_secp192r1
if (num_words == 6) {
__asm__ volatile (
".syntax unified \n\t"
FAST_MULT_ASM_6
RESUME_SYNTAX
: "+r" (r0), "+r" (r1), "+r" (r2)
: "r" (r3)
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
#if uECC_SUPPORTS_secp224r1
if (num_words == 7) {
__asm__ volatile (
".syntax unified \n\t"
FAST_MULT_ASM_7
RESUME_SYNTAX
: "+r" (r0), "+r" (r1), "+r" (r2)
: "r" (r3)
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1)
if (num_words == 8) {
__asm__ volatile (
".syntax unified \n\t"
FAST_MULT_ASM_8
RESUME_SYNTAX
: "+r" (r0), "+r" (r1), "+r" (r2)
: "r" (r3)
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
}
#define asm_mult 1
#if uECC_SQUARE_FUNC
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
const uECC_word_t *left,
wordcount_t num_words) {
register uint32_t *r0 __asm__("r0") = result;
register const uint32_t *r1 __asm__("r1") = left;
register uint32_t r2 __asm__("r2") = num_words;
#if uECC_SUPPORTS_secp160r1
if (num_words == 5) {
__asm__ volatile (
".syntax unified \n\t"
FAST_SQUARE_ASM_5
RESUME_SYNTAX
: "+r" (r0), "+r" (r1)
: "r" (r2)
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
#if uECC_SUPPORTS_secp192r1
if (num_words == 6) {
__asm__ volatile (
".syntax unified \n\t"
FAST_SQUARE_ASM_6
RESUME_SYNTAX
: "+r" (r0), "+r" (r1)
: "r" (r2)
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
#if uECC_SUPPORTS_secp224r1
if (num_words == 7) {
__asm__ volatile (
".syntax unified \n\t"
FAST_SQUARE_ASM_7
RESUME_SYNTAX
: "+r" (r0), "+r" (r1)
: "r" (r2)
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1)
if (num_words == 8) {
__asm__ volatile (
".syntax unified \n\t"
FAST_SQUARE_ASM_8
RESUME_SYNTAX
: "+r" (r0), "+r" (r1)
: "r" (r2)
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
return;
}
#endif
}
#define asm_square 1
#endif /* uECC_SQUARE_FUNC */
#endif /* (uECC_OPTIMIZATION_LEVEL > 3) */
#endif /* uECC_PLATFORM != uECC_arm_thumb */
#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */
/* ---- "Small" implementations ---- */
#if !asm_add
uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result,
const uECC_word_t *left,
const uECC_word_t *right,
wordcount_t num_words) {
uint32_t carry = 0;
uint32_t left_word;
uint32_t right_word;
__asm__ volatile (
".syntax unified \n\t"
"1: \n\t"
"ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */
"ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */
"lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */
"adcs %[left], %[left], %[right] \n\t" /* Add with carry. */
"adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */
"stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */
"subs %[ctr], #1 \n\t" /* Decrement counter. */
"bne 1b \n\t" /* Loop until counter == 0. */
RESUME_SYNTAX
: [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right),
[ctr] REG_RW (num_words), [carry] REG_RW (carry),
[left] REG_WRITE (left_word), [right] REG_WRITE (right_word)
:
: "cc", "memory"
);
return carry;
}
#define asm_add 1
#endif
#if !asm_sub
uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result,
const uECC_word_t *left,
const uECC_word_t *right,
wordcount_t num_words) {
uint32_t carry = 1; /* carry = 1 initially (means don't borrow) */
uint32_t left_word;
uint32_t right_word;
__asm__ volatile (
".syntax unified \n\t"
"1: \n\t"
"ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */
"ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */
"lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */
"sbcs %[left], %[left], %[right] \n\t" /* Subtract with borrow. */
"adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */
"stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */
"subs %[ctr], #1 \n\t" /* Decrement counter. */
"bne 1b \n\t" /* Loop until counter == 0. */
RESUME_SYNTAX
: [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right),
[ctr] REG_RW (num_words), [carry] REG_RW (carry),
[left] REG_WRITE (left_word), [right] REG_WRITE (right_word)
:
: "cc", "memory"
);
return !carry;
}
#define asm_sub 1
#endif
#if !asm_mult
uECC_VLI_API void uECC_vli_mult(uECC_word_t *result,
const uECC_word_t *left,
const uECC_word_t *right,
wordcount_t num_words) {
#if (uECC_PLATFORM != uECC_arm_thumb)
uint32_t c0 = 0;
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t k = 0;
uint32_t i;
uint32_t t0, t1;
__asm__ volatile (
".syntax unified \n\t"
"1: \n\t" /* outer loop (k < num_words) */
"movs %[i], #0 \n\t" /* i = 0 */
"b 3f \n\t"
"2: \n\t" /* outer loop (k >= num_words) */
"movs %[i], %[k] \n\t" /* i = k */
"subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */
"3: \n\t" /* inner loop */
"subs %[t0], %[k], %[i] \n\t" /* t0 = k-i */
"ldr %[t1], [%[right], %[t0]] \n\t" /* t1 = right[k - i] */
"ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */
"umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */
"adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */
"adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */
"adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */
"adds %[i], #4 \n\t" /* i += 4 */
"cmp %[i], %[last_word] \n\t" /* i > (num_words - 1) (times 4)? */
"bgt 4f \n\t" /* if so, exit the loop */
"cmp %[i], %[k] \n\t" /* i <= k? */
"ble 3b \n\t" /* if so, continue looping */
"4: \n\t" /* end inner loop */
"str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */
"mov %[c0], %[c1] \n\t" /* c0 = c1 */
"mov %[c1], %[c2] \n\t" /* c1 = c2 */
"movs %[c2], #0 \n\t" /* c2 = 0 */
"adds %[k], #4 \n\t" /* k += 4 */
"cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
"cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
"ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */
/* end outer loop */
"str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */
RESUME_SYNTAX
: [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2),
[k] "+r" (k), [i] "=&r" (i), [t0] "=&r" (t0), [t1] "=&r" (t1)
: [result] "r" (result), [left] "r" (left), [right] "r" (right),
[last_word] "r" ((num_words - 1) * 4)
: "cc", "memory"
);
#else /* Thumb-1 */
uint32_t r4, r5, r6, r7;
__asm__ volatile (
".syntax unified \n\t"
"subs %[r3], #1 \n\t" /* r3 = num_words - 1 */
"lsls %[r3], #2 \n\t" /* r3 = (num_words - 1) * 4 */
"mov r8, %[r3] \n\t" /* r8 = (num_words - 1) * 4 */
"lsls %[r3], #1 \n\t" /* r3 = (num_words - 1) * 8 */
"mov r9, %[r3] \n\t" /* r9 = (num_words - 1) * 8 */
"movs %[r3], #0 \n\t" /* c0 = 0 */
"movs %[r4], #0 \n\t" /* c1 = 0 */
"movs %[r5], #0 \n\t" /* c2 = 0 */
"movs %[r6], #0 \n\t" /* k = 0 */
"push {%[r0]} \n\t" /* keep result on the stack */
"1: \n\t" /* outer loop (k < num_words) */
"movs %[r7], #0 \n\t" /* r7 = i = 0 */
"b 3f \n\t"
"2: \n\t" /* outer loop (k >= num_words) */
"movs %[r7], %[r6] \n\t" /* r7 = k */
"mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */
"subs %[r7], %[r0] \n\t" /* r7 = i = k - (num_words - 1) (times 4) */
"3: \n\t" /* inner loop */
"mov r10, %[r3] \n\t"
"mov r11, %[r4] \n\t"
"mov r12, %[r5] \n\t"
"mov r14, %[r6] \n\t"
"subs %[r0], %[r6], %[r7] \n\t" /* r0 = k - i */
"ldr %[r4], [%[r2], %[r0]] \n\t" /* r4 = right[k - i] */
"ldr %[r0], [%[r1], %[r7]] \n\t" /* r0 = left[i] */
"lsrs %[r3], %[r0], #16 \n\t" /* r3 = a1 */
"uxth %[r0], %[r0] \n\t" /* r0 = a0 */
"lsrs %[r5], %[r4], #16 \n\t" /* r5 = b1 */
"uxth %[r4], %[r4] \n\t" /* r4 = b0 */
"movs %[r6], %[r3] \n\t" /* r6 = a1 */
"muls %[r6], %[r5], %[r6] \n\t" /* r6 = a1 * b1 */
"muls %[r3], %[r4], %[r3] \n\t" /* r3 = b0 * a1 */
"muls %[r5], %[r0], %[r5] \n\t" /* r5 = a0 * b1 */
"muls %[r0], %[r4], %[r0] \n\t" /* r0 = a0 * b0 */
/* Add middle terms */
"lsls %[r4], %[r3], #16 \n\t"
"lsrs %[r3], %[r3], #16 \n\t"
"adds %[r0], %[r4] \n\t"
"adcs %[r6], %[r3] \n\t"
"lsls %[r4], %[r5], #16 \n\t"
"lsrs %[r5], %[r5], #16 \n\t"
"adds %[r0], %[r4] \n\t"
"adcs %[r6], %[r5] \n\t"
"mov %[r3], r10\n\t"
"mov %[r4], r11\n\t"
"mov %[r5], r12\n\t"
"adds %[r3], %[r0] \n\t" /* add low word to c0 */
"adcs %[r4], %[r6] \n\t" /* add high word to c1, including carry */
"movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */
"adcs %[r5], %[r0] \n\t" /* add carry to c2 */
"mov %[r6], r14\n\t" /* r6 = k */
"adds %[r7], #4 \n\t" /* i += 4 */
"cmp %[r7], r8 \n\t" /* i > (num_words - 1) (times 4)? */
"bgt 4f \n\t" /* if so, exit the loop */
"cmp %[r7], %[r6] \n\t" /* i <= k? */
"ble 3b \n\t" /* if so, continue looping */
"4: \n\t" /* end inner loop */
"ldr %[r0], [sp, #0] \n\t" /* r0 = result */
"str %[r3], [%[r0], %[r6]] \n\t" /* result[k] = c0 */
"mov %[r3], %[r4] \n\t" /* c0 = c1 */
"mov %[r4], %[r5] \n\t" /* c1 = c2 */
"movs %[r5], #0 \n\t" /* c2 = 0 */
"adds %[r6], #4 \n\t" /* k += 4 */
"cmp %[r6], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
"cmp %[r6], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
"ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */
/* end outer loop */
"str %[r3], [%[r0], %[r6]] \n\t" /* result[num_words * 2 - 1] = c0 */
"pop {%[r0]} \n\t" /* pop result off the stack */
".syntax divided \n\t"
: [r3] "+l" (num_words), [r4] "=&l" (r4),
[r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7)
: [r0] "l" (result), [r1] "l" (left), [r2] "l" (right)
: "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
#endif
}
#define asm_mult 1
#endif
#if uECC_SQUARE_FUNC
#if !asm_square
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
const uECC_word_t *left,
wordcount_t num_words) {
#if (uECC_PLATFORM != uECC_arm_thumb)
uint32_t c0 = 0;
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t k = 0;
uint32_t i, tt;
uint32_t t0, t1;
__asm__ volatile (
".syntax unified \n\t"
"1: \n\t" /* outer loop (k < num_words) */
"movs %[i], #0 \n\t" /* i = 0 */
"b 3f \n\t"
"2: \n\t" /* outer loop (k >= num_words) */
"movs %[i], %[k] \n\t" /* i = k */
"subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */
"3: \n\t" /* inner loop */
"subs %[tt], %[k], %[i] \n\t" /* tt = k-i */
"ldr %[t1], [%[left], %[tt]] \n\t" /* t1 = left[k - i] */
"ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */
"umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */
"cmp %[i], %[tt] \n\t" /* (i < k - i) ? */
"bge 4f \n\t" /* if i >= k - i, skip */
"adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */
"adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */
"adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */
"4: \n\t"
"adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */
"adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */
"adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */
"adds %[i], #4 \n\t" /* i += 4 */
"cmp %[i], %[k] \n\t" /* i >= k? */
"bge 5f \n\t" /* if so, exit the loop */
"subs %[tt], %[k], %[i] \n\t" /* tt = k - i */
"cmp %[i], %[tt] \n\t" /* i <= k - i? */
"ble 3b \n\t" /* if so, continue looping */
"5: \n\t" /* end inner loop */
"str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */
"mov %[c0], %[c1] \n\t" /* c0 = c1 */
"mov %[c1], %[c2] \n\t" /* c1 = c2 */
"movs %[c2], #0 \n\t" /* c2 = 0 */
"adds %[k], #4 \n\t" /* k += 4 */
"cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
"cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
"ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */
/* end outer loop */
"str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */
RESUME_SYNTAX
: [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2),
[k] "+r" (k), [i] "=&r" (i), [tt] "=&r" (tt), [t0] "=&r" (t0), [t1] "=&r" (t1)
: [result] "r" (result), [left] "r" (left), [last_word] "r" ((num_words - 1) * 4)
: "cc", "memory"
);
#else
uint32_t r3, r4, r5, r6, r7;
__asm__ volatile (
".syntax unified \n\t"
"subs %[r2], #1 \n\t" /* r2 = num_words - 1 */
"lsls %[r2], #2 \n\t" /* r2 = (num_words - 1) * 4 */
"mov r8, %[r2] \n\t" /* r8 = (num_words - 1) * 4 */
"lsls %[r2], #1 \n\t" /* r2 = (num_words - 1) * 8 */
"mov r9, %[r2] \n\t" /* r9 = (num_words - 1) * 8 */
"movs %[r2], #0 \n\t" /* c0 = 0 */
"movs %[r3], #0 \n\t" /* c1 = 0 */
"movs %[r4], #0 \n\t" /* c2 = 0 */
"movs %[r5], #0 \n\t" /* k = 0 */
"push {%[r0]} \n\t" /* keep result on the stack */
"1: \n\t" /* outer loop (k < num_words) */
"movs %[r6], #0 \n\t" /* r6 = i = 0 */
"b 3f \n\t"
"2: \n\t" /* outer loop (k >= num_words) */
"movs %[r6], %[r5] \n\t" /* r6 = k */
"mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */
"subs %[r6], %[r0] \n\t" /* r6 = i = k - (num_words - 1) (times 4) */
"3: \n\t" /* inner loop */
"mov r10, %[r2] \n\t"
"mov r11, %[r3] \n\t"
"mov r12, %[r4] \n\t"
"mov r14, %[r5] \n\t"
"subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */
"ldr %[r3], [%[r1], %[r7]] \n\t" /* r3 = left[k - i] */
"ldr %[r0], [%[r1], %[r6]] \n\t" /* r0 = left[i] */
"lsrs %[r2], %[r0], #16 \n\t" /* r2 = a1 */
"uxth %[r0], %[r0] \n\t" /* r0 = a0 */
"lsrs %[r4], %[r3], #16 \n\t" /* r4 = b1 */
"uxth %[r3], %[r3] \n\t" /* r3 = b0 */
"movs %[r5], %[r2] \n\t" /* r5 = a1 */
"muls %[r5], %[r4], %[r5] \n\t" /* r5 = a1 * b1 */
"muls %[r2], %[r3], %[r2] \n\t" /* r2 = b0 * a1 */
"muls %[r4], %[r0], %[r4] \n\t" /* r4 = a0 * b1 */
"muls %[r0], %[r3], %[r0] \n\t" /* r0 = a0 * b0 */
/* Add middle terms */
"lsls %[r3], %[r2], #16 \n\t"
"lsrs %[r2], %[r2], #16 \n\t"
"adds %[r0], %[r3] \n\t"
"adcs %[r5], %[r2] \n\t"
"lsls %[r3], %[r4], #16 \n\t"
"lsrs %[r4], %[r4], #16 \n\t"
"adds %[r0], %[r3] \n\t"
"adcs %[r5], %[r4] \n\t"
/* Add to acc, doubling if necessary */
"mov %[r2], r10\n\t"
"mov %[r3], r11\n\t"
"mov %[r4], r12\n\t"
"cmp %[r6], %[r7] \n\t" /* (i < k - i) ? */
"bge 4f \n\t" /* if i >= k - i, skip */
"movs %[r7], #0 \n\t" /* r7 = 0 */
"adds %[r2], %[r0] \n\t" /* add low word to c0 */
"adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */
"adcs %[r4], %[r7] \n\t" /* add carry to c2 */
"4: \n\t"
"movs %[r7], #0 \n\t" /* r7 = 0 */
"adds %[r2], %[r0] \n\t" /* add low word to c0 */
"adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */
"adcs %[r4], %[r7] \n\t" /* add carry to c2 */
"mov %[r5], r14\n\t" /* r5 = k */
"adds %[r6], #4 \n\t" /* i += 4 */
"cmp %[r6], %[r5] \n\t" /* i >= k? */
"bge 5f \n\t" /* if so, exit the loop */
"subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */
"cmp %[r6], %[r7] \n\t" /* i <= k - i? */
"ble 3b \n\t" /* if so, continue looping */
"5: \n\t" /* end inner loop */
"ldr %[r0], [sp, #0] \n\t" /* r0 = result */
"str %[r2], [%[r0], %[r5]] \n\t" /* result[k] = c0 */
"mov %[r2], %[r3] \n\t" /* c0 = c1 */
"mov %[r3], %[r4] \n\t" /* c1 = c2 */
"movs %[r4], #0 \n\t" /* c2 = 0 */
"adds %[r5], #4 \n\t" /* k += 4 */
"cmp %[r5], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
"cmp %[r5], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
"ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */
/* end outer loop */
"str %[r2], [%[r0], %[r5]] \n\t" /* result[num_words * 2 - 1] = c0 */
"pop {%[r0]} \n\t" /* pop result off the stack */
".syntax divided \n\t"
: [r2] "+l" (num_words), [r3] "=&l" (r3), [r4] "=&l" (r4),
[r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7)
: [r0] "l" (result), [r1] "l" (left)
: "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
);
#endif
}
#define asm_square 1
#endif
#endif /* uECC_SQUARE_FUNC */
#endif /* _UECC_ASM_ARM_H_ */

View File

@@ -1,132 +0,0 @@
import os
c, link, asm, utils = emk.module("c", "link", "asm", "utils")
default_compile_flags = ["-fvisibility=hidden", "-Wall", "-Wextra", "-Wshadow", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-parameter", \
"-Wno-comment", "-Wno-unused", "-Wno-unknown-pragmas"]
default_link_flags = []
opt_flags = {"dbg":["-g"], "std":["-O2"], "max":["-O3"], "small":["-Os"]}
opt_link_flags = {"dbg":[], "std":[], "max":[], "small":[]}
c_flags = ["-std=c99"]
cxx_flags = ["-std=c++11", "-Wno-reorder", "-fno-rtti", "-fno-exceptions"]
c_link_flags = []
cxx_link_flags = ["-fno-rtti", "-fno-exceptions"]
if "root" in emk.options:
root = emk.options["root"]
else:
root = "/"
def setup_build_dir():
build_arch = None
if "arch" in emk.options:
build_arch = emk.options["arch"]
elif not emk.cleaning:
build_arch = "osx"
emk.options["arch"] = build_arch
opt_level = None
if "opt" in emk.options:
level = emk.options["opt"]
if level in opt_flags:
opt_level = level
else:
emk.log.warning("Unknown optimization level '%s'" % (level))
elif not emk.cleaning:
opt_level = "dbg"
emk.options["opt"] = opt_level
dirs = ["__build__"]
if build_arch:
dirs.append(build_arch)
if opt_level:
dirs.append(opt_level)
emk.build_dir = os.path.join(*dirs)
def setup_osx():
global c
global link
flags = [("-arch", "x86_64"), "-fno-common", "-Wnewline-eof"]
c.flags.extend(flags)
c.cxx.flags += ["-stdlib=libc++"]
link.cxx.flags += ["-stdlib=libc++"]
link_flags = [("-arch", "x86_64")]
link.local_flags.extend(link_flags)
def setup_avr():
global c
global link
c.compiler = c.GccCompiler(root + "Projects/avr-tools/bin/avr-")
c.flags += ["-mmcu=atmega256rfr2", "-ffunction-sections", "-fdata-sections"]
link.linker = link.GccLinker(root + "Projects/avr-tools/bin/avr-")
link.flags += ["-mmcu=atmega256rfr2", "-mrelax", "-Wl,--gc-sections"]
link.strip = True
def setup_arm_thumb():
global c
global link
global asm
global utils
asm.assembler = asm.GccAssembler(root + "cross/arm_cortex/bin/arm-none-eabi-")
c.compiler = c.GccCompiler(root + "cross/arm_cortex/bin/arm-none-eabi-")
link.linker = link.GccLinker(root + "cross/arm_cortex/bin/arm-none-eabi-")
c.flags.extend(["-mcpu=cortex-m0", "-mthumb", "-ffunction-sections", "-fdata-sections", "-fno-builtin-fprintf", "-fno-builtin-printf"])
c.defines["LPC11XX"] = 1
link.local_flags.extend(["-mcpu=cortex-m0", "-mthumb", "-nostartfiles", "-nostdlib", "-Wl,--gc-sections"])
link.local_flags.extend(["-Tflash.lds", "-L" + root + "Projects/lpc11xx/core", root + "Projects/lpc11xx/core/" + emk.build_dir + "/board_cstartup.o"])
link.local_syslibs += ["gcc"]
link.depdirs += [root + "Projects/lpc11xx/stdlib"]
def do_objcopy(produces, requires):
utils.call(root + "cross/arm_cortex/bin/arm-none-eabi-objcopy", "-O", "binary", requires[0], produces[0])
def handle_exe(path):
emk.depend(path, root + "Projects/lpc11xx/core/" + emk.build_dir + "/board_cstartup.o")
emk.rule(do_objcopy, path + ".bin", path, cwd_safe=True, ex_safe=True)
emk.autobuild(path + ".bin")
link.exe_funcs.append(handle_exe)
link.strip = True
emk.recurse(root + "Projects/lpc11xx/core")
def setup_linux_rpi():
global c
global link
c.compiler = c.GccCompiler("/Volumes/xtools/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-")
link.linker = link.GccLinker("/Volumes/xtools/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-")
c.flags.extend(["-fomit-frame-pointer"])
setup_build_dir()
setup_funcs = {"osx":setup_osx, "avr":setup_avr, "arm_thumb":setup_arm_thumb, "rpi": setup_linux_rpi}
if not emk.cleaning:
build_arch = emk.options["arch"]
opt_level = emk.options["opt"]
c.flags.extend(default_compile_flags)
c.flags.extend(opt_flags[opt_level])
c.c.flags.extend(c_flags)
c.cxx.flags.extend(cxx_flags)
link.local_flags.extend(default_link_flags)
link.local_flags.extend(opt_link_flags[opt_level])
link.c.local_flags.extend(c_link_flags)
link.cxx.local_flags.extend(cxx_link_flags)
c.include_dirs.append("$:proj:$")
if build_arch in setup_funcs:
setup_funcs[build_arch]()
else:
raise emk.BuildError("Unknown target arch '%s'" % (build_arch))
c.defines["TARGET_ARCH_" + build_arch.upper()] = 1

View File

@@ -1,3 +0,0 @@
c, link = emk.module("c", "link")
emk.subdir("test")

View File

@@ -1,80 +0,0 @@
#include <uECC.h>
static int RNG(uint8_t *dest, unsigned size) {
// Use the least-significant bits from the ADC for an unconnected pin (or connected to a source of
// random noise). This can take a long time to generate random data if the result of analogRead(0)
// doesn't change very frequently.
while (size) {
uint8_t val = 0;
for (unsigned i = 0; i < 8; ++i) {
int init = analogRead(0);
int count = 0;
while (analogRead(0) == init) {
++count;
}
if (count == 0) {
val = (val << 1) | (init & 0x01);
} else {
val = (val << 1) | (count & 0x01);
}
}
*dest = val;
++dest;
--size;
}
// NOTE: it would be a good idea to hash the resulting random data using SHA-256 or similar.
return 1;
}
void setup() {
Serial.begin(115200);
Serial.print("Testing ecc\n");
uECC_set_rng(&RNG);
}
void loop() {
const struct uECC_Curve_t * curve = uECC_secp160r1();
uint8_t private1[21];
uint8_t private2[21];
uint8_t public1[40];
uint8_t public2[40];
uint8_t secret1[20];
uint8_t secret2[20];
unsigned long a = millis();
uECC_make_key(public1, private1, curve);
unsigned long b = millis();
Serial.print("Made key 1 in "); Serial.println(b-a);
a = millis();
uECC_make_key(public2, private2, curve);
b = millis();
Serial.print("Made key 2 in "); Serial.println(b-a);
a = millis();
int r = uECC_shared_secret(public2, private1, secret1, curve);
b = millis();
Serial.print("Shared secret 1 in "); Serial.println(b-a);
if (!r) {
Serial.print("shared_secret() failed (1)\n");
return;
}
a = millis();
r = uECC_shared_secret(public1, private2, secret2, curve);
b = millis();
Serial.print("Shared secret 2 in "); Serial.println(b-a);
if (!r) {
Serial.print("shared_secret() failed (2)\n");
return;
}
if (memcmp(secret1, secret2, 20) != 0) {
Serial.print("Shared secrets are not identical!\n");
} else {
Serial.print("Shared secrets are identical\n");
}
}

View File

@@ -1,9 +0,0 @@
name=micro-ecc
version=1.0.0
author=Kenneth MacKay
maintainer=Kenneth MacKay
sentence=uECC
paragraph=A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors.
category=Other
url=https://github.com/kmackay/micro-ecc
architectures=*

View File

@@ -1,94 +0,0 @@
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#ifndef _UECC_PLATFORM_SPECIFIC_H_
#define _UECC_PLATFORM_SPECIFIC_H_
#include "types.h"
#if (defined(_WIN32) || defined(_WIN64))
/* Windows */
// use pragma syntax to prevent tweaking the linker script for getting CryptXYZ function
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "advapi32.lib")
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
static int default_RNG(uint8_t *dest, unsigned size) {
HCRYPTPROV prov;
if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
return 0;
}
CryptGenRandom(prov, size, (BYTE *)dest);
CryptReleaseContext(prov, 0);
return 1;
}
#define default_RNG_defined 1
#elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \
(defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX)
/* Some POSIX-like system with /dev/urandom or /dev/random. */
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
static int default_RNG(uint8_t *dest, unsigned size) {
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
if (fd == -1) {
fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
if (fd == -1) {
return 0;
}
}
char *ptr = (char *)dest;
size_t left = size;
while (left > 0) {
ssize_t bytes_read = read(fd, ptr, left);
if (bytes_read <= 0) { // read failed
close(fd);
return 0;
}
left -= bytes_read;
ptr += bytes_read;
}
close(fd);
return 1;
}
#define default_RNG_defined 1
#elif defined(RIOT_VERSION)
#include <random.h>
static int default_RNG(uint8_t *dest, unsigned size) {
random_bytes(dest, size);
return 1;
}
#define default_RNG_defined 1
#elif defined(NRF52_SERIES)
#include "app_error.h"
#include "nrf_crypto_rng.h"
static int default_RNG(uint8_t *dest, unsigned size)
{
// make sure to call nrf_crypto_init and nrf_crypto_rng_init first
ret_code_t ret_code = nrf_crypto_rng_vector_generate(dest, size);
return (ret_code == NRF_SUCCESS) ? 1 : 0;
}
#define default_RNG_defined 1
#endif /* platform */
#endif /* _UECC_PLATFORM_SPECIFIC_H_ */

View File

@@ -1,188 +0,0 @@
#!/usr/bin/env python
import sys
if len(sys.argv) < 2:
print "Provide the integer size in 32-bit words"
sys.exit(1)
size = int(sys.argv[1])
full_rows = size // 3
init_size = size % 3
if init_size == 0:
full_rows = full_rows - 1
init_size = 3
def emit(line, *args):
s = '"' + line + r' \n\t"'
print s % args
rx = [3, 4, 5]
ry = [6, 7, 8]
#### set up registers
emit("add r0, %s", (size - init_size) * 4) # move z
emit("add r2, %s", (size - init_size) * 4) # move y
emit("ldmia r1!, {%s}", ", ".join(["r%s" % (rx[i]) for i in xrange(init_size)]))
emit("ldmia r2!, {%s}", ", ".join(["r%s" % (ry[i]) for i in xrange(init_size)]))
print ""
if init_size == 1:
emit("umull r9, r10, r3, r6")
emit("stmia r0!, {r9, r10}")
else:
#### first two multiplications of initial block
emit("umull r11, r12, r3, r6")
emit("stmia r0!, {r11}")
print ""
emit("mov r10, #0")
emit("umull r11, r9, r3, r7")
emit("adds r12, r12, r11")
emit("adc r9, r9, #0")
emit("umull r11, r14, r4, r6")
emit("adds r12, r12, r11")
emit("adcs r9, r9, r14")
emit("adc r10, r10, #0")
emit("stmia r0!, {r12}")
print ""
#### rest of initial block, with moving accumulator registers
acc = [9, 10, 11, 12, 14]
if init_size == 3:
emit("mov r%s, #0", acc[2])
for i in xrange(0, 3):
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[i], ry[2 - i])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("mov r%s, #0", acc[2])
for i in xrange(0, 2):
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[i + 1], ry[2 - i])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[init_size-1], ry[init_size-1])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adc r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("stmia r0!, {r%s}", acc[0])
emit("stmia r0!, {r%s}", acc[1])
print ""
#### reset y and z pointers
emit("sub r0, %s", (2 * init_size + 3) * 4)
emit("sub r2, %s", (init_size + 3) * 4)
#### load y registers
emit("ldmia r2!, {%s}", ", ".join(["r%s" % (ry[i]) for i in xrange(3)]))
#### load additional x registers
if init_size != 3:
emit("ldmia r1!, {%s}", ", ".join(["r%s" % (rx[i]) for i in xrange(init_size, 3)]))
print ""
prev_size = init_size
for row in xrange(full_rows):
emit("umull r11, r12, r3, r6")
emit("stmia r0!, {r11}")
print ""
emit("mov r10, #0")
emit("umull r11, r9, r3, r7")
emit("adds r12, r12, r11")
emit("adc r9, r9, #0")
emit("umull r11, r14, r4, r6")
emit("adds r12, r12, r11")
emit("adcs r9, r9, r14")
emit("adc r10, r10, #0")
emit("stmia r0!, {r12}")
print ""
acc = [9, 10, 11, 12, 14]
emit("mov r%s, #0", acc[2])
for i in xrange(0, 3):
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[i], ry[2 - i])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
#### now we need to start shifting x and loading from z
x_regs = [3, 4, 5]
for r in xrange(0, prev_size):
x_regs = x_regs[1:] + x_regs[:1]
emit("ldmia r1!, {r%s}", x_regs[2])
emit("mov r%s, #0", acc[2])
for i in xrange(0, 3):
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[i], ry[2 - i])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("ldr r%s, [r0]", acc[3]) # load stored value from initial block, and add to accumulator
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, #0", acc[1], acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
# done shifting x, start shifting y
y_regs = [6, 7, 8]
for r in xrange(0, prev_size):
y_regs = y_regs[1:] + y_regs[:1]
emit("ldmia r2!, {r%s}", y_regs[2])
emit("mov r%s, #0", acc[2])
for i in xrange(0, 3):
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[i], y_regs[2 - i])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("ldr r%s, [r0]", acc[3]) # load stored value from initial block, and add to accumulator
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, #0", acc[1], acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
# done both shifts, do remaining corner
emit("mov r%s, #0", acc[2])
for i in xrange(0, 2):
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[i + 1], y_regs[2 - i])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[2], y_regs[2])
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3])
emit("adc r%s, r%s, r%s", acc[1], acc[1], acc[4])
emit("stmia r0!, {r%s}", acc[0])
emit("stmia r0!, {r%s}", acc[1])
print ""
prev_size = prev_size + 3
if row < full_rows - 1:
#### reset x, y and z pointers
emit("sub r0, %s", (2 * prev_size + 3) * 4)
emit("sub r1, %s", prev_size * 4)
emit("sub r2, %s", (prev_size + 3) * 4)
#### load x and y registers
emit("ldmia r1!, {%s}", ",".join(["r%s" % (rx[i]) for i in xrange(3)]))
emit("ldmia r2!, {%s}", ",".join(["r%s" % (ry[i]) for i in xrange(3)]))
print ""

View File

@@ -1,203 +0,0 @@
#!/usr/bin/env python
import sys
if len(sys.argv) < 2:
print "Provide the integer size in bytes"
sys.exit(1)
size = int(sys.argv[1])
full_rows = size // 10
init_size = size % 10
if init_size == 0:
full_rows = full_rows - 1
init_size = 10
def rx(i):
return i + 2
def ry(i):
return i + 12
def emit(line, *args):
s = '"' + line + r' \n\t"'
print s % args
#### set up registers
emit("adiw r30, %s", size - init_size) # move z
emit("adiw r28, %s", size - init_size) # move y
for i in xrange(init_size):
emit("ld r%s, x+", rx(i))
for i in xrange(init_size):
emit("ld r%s, y+", ry(i))
emit("ldi r25, 0")
print ""
if init_size == 1:
emit("mul r2, r12")
emit("st z+, r0")
emit("st z+, r1")
else:
#### first two multiplications of initial block
emit("ldi r23, 0")
emit("mul r2, r12")
emit("st z+, r0")
emit("mov r22, r1")
print ""
emit("ldi r24, 0")
emit("mul r2, r13")
emit("add r22, r0")
emit("adc r23, r1")
emit("mul r3, r12")
emit("add r22, r0")
emit("adc r23, r1")
emit("adc r24, r25")
emit("st z+, r22")
print ""
#### rest of initial block, with moving accumulator registers
acc = [23, 24, 22]
for r in xrange(2, init_size):
emit("ldi r%s, 0", acc[2])
for i in xrange(0, r+1):
emit("mul r%s, r%s", rx(i), ry(r - i))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, r25", acc[2])
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
for r in xrange(1, init_size-1):
emit("ldi r%s, 0", acc[2])
for i in xrange(0, init_size-r):
emit("mul r%s, r%s", rx(r+i), ry((init_size-1) - i))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, r25", acc[2])
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("mul r%s, r%s", rx(init_size-1), ry(init_size-1))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("st z+, r%s", acc[0])
emit("st z+, r%s", acc[1])
print ""
#### reset y and z pointers
emit("sbiw r30, %s", 2 * init_size + 10)
emit("sbiw r28, %s", init_size + 10)
#### load y registers
for i in xrange(10):
emit("ld r%s, y+", ry(i))
#### load additional x registers
for i in xrange(init_size, 10):
emit("ld r%s, x+", rx(i))
print ""
prev_size = init_size
for row in xrange(full_rows):
#### do x = 0-9, y = 0-9 multiplications
emit("ldi r23, 0")
emit("mul r2, r12")
emit("st z+, r0")
emit("mov r22, r1")
print ""
emit("ldi r24, 0")
emit("mul r2, r13")
emit("add r22, r0")
emit("adc r23, r1")
emit("mul r3, r12")
emit("add r22, r0")
emit("adc r23, r1")
emit("adc r24, r25")
emit("st z+, r22")
print ""
acc = [23, 24, 22]
for r in xrange(2, 10):
emit("ldi r%s, 0", acc[2])
for i in xrange(0, r+1):
emit("mul r%s, r%s", rx(i), ry(r - i))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, r25", acc[2])
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
#### now we need to start shifting x and loading from z
x_regs = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
for r in xrange(0, prev_size):
x_regs = x_regs[1:] + x_regs[:1]
emit("ld r%s, x+", x_regs[9]) # load next byte of left
emit("ldi r%s, 0", acc[2])
for i in xrange(0, 10):
emit("mul r%s, r%s", x_regs[i], ry(9 - i))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, r25", acc[2])
emit("ld r0, z") # load stored value from initial block, and add to accumulator (note z does not increment)
emit("add r%s, r0", acc[0])
emit("adc r%s, r25", acc[1])
emit("adc r%s, r25", acc[2])
emit("st z+, r%s", acc[0]) # store next byte (z increments)
print ""
acc = acc[1:] + acc[:1]
# done shifting x, start shifting y
y_regs = [12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
for r in xrange(0, prev_size):
y_regs = y_regs[1:] + y_regs[:1]
emit("ld r%s, y+", y_regs[9]) # load next byte of right
emit("ldi r%s, 0", acc[2])
for i in xrange(0, 10):
emit("mul r%s, r%s", x_regs[i], y_regs[9 -i])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, r25", acc[2])
emit("ld r0, z") # load stored value from initial block, and add to accumulator (note z does not increment)
emit("add r%s, r0", acc[0])
emit("adc r%s, r25", acc[1])
emit("adc r%s, r25", acc[2])
emit("st z+, r%s", acc[0]) # store next byte (z increments)
print ""
acc = acc[1:] + acc[:1]
# done both shifts, do remaining corner
for r in xrange(1, 9):
emit("ldi r%s, 0", acc[2])
for i in xrange(0, 10-r):
emit("mul r%s, r%s", x_regs[r+i], y_regs[9 - i])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, r25", acc[2])
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("mul r%s, r%s", x_regs[9], y_regs[9])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("st z+, r%s", acc[0])
emit("st z+, r%s", acc[1])
print ""
prev_size = prev_size + 10
if row < full_rows - 1:
#### reset x, y and z pointers
emit("sbiw r30, %s", 2 * prev_size + 10)
emit("sbiw r28, %s", prev_size + 10)
emit("sbiw r26, %s", prev_size)
#### load x and y registers
for i in xrange(10):
emit("ld r%s, x+", rx(i))
emit("ld r%s, y+", ry(i))
print ""
emit("eor r1, r1")

View File

@@ -1,143 +0,0 @@
#!/usr/bin/env python
import sys
if len(sys.argv) < 2:
print "Provide the integer size in bytes"
sys.exit(1)
size = int(sys.argv[1])
def lhi(i):
return i + 2
def rhi(i):
return i + 6
left_lo = [10, 11, 12, 13]
right_lo = [14, 15, 16, 17]
def llo(i):
return left_lo[i]
def rlo(i):
return right_lo[i]
def emit(line, *args):
s = '"' + line + r' \n\t"'
print s % args
def update_low():
global left_lo
global right_lo
left_lo = left_lo[1:] + left_lo[:1]
right_lo = right_lo[1:] + right_lo[:1]
emit("ld r%s, x+", left_lo[3])
emit("ld r%s, y+", right_lo[3])
accum = [19, 20, 21]
def acc(i):
return accum[i]
def rotate_acc():
global accum
accum = accum[1:] + accum[:1]
# Load high values
for i in xrange(4):
emit("ld r%s, x+", lhi(i))
emit("ld r%s, y+", rhi(i))
emit("sbiw r26, %s", size + 4)
emit("sbiw r28, %s", size + 4)
emit("sbiw r30, %s", size)
# Load low values
for i in xrange(4):
emit("ld r%s, x+", llo(i))
emit("ld r%s, y+", rlo(i))
print ""
# Compute initial triangles
emit("mul r%s, r%s", lhi(0), rlo(0))
emit("mov r%s, r0", acc(0))
emit("mov r%s, r1", acc(1))
emit("ldi r%s, 0", acc(2))
emit("ld r0, z")
emit("add r%s, r0", acc(0))
emit("adc r%s, r25", acc(1))
emit("mul r%s, r%s", rhi(0), llo(0))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("adc r%s, r25", acc(2))
emit("st z+, r%s", acc(0))
print ""
rotate_acc()
for i in xrange(1, 4):
emit("ldi r%s, 0", acc(2))
emit("ld r0, z")
emit("add r%s, r0", acc(0))
emit("adc r%s, r25", acc(1))
for j in xrange(i + 1):
emit("mul r%s, r%s", lhi(j), rlo(i-j))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("adc r%s, r25", acc(2))
emit("mul r%s, r%s", rhi(j), llo(i-j))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("adc r%s, r25", acc(2))
emit("st z+, r%s", acc(0))
print ""
rotate_acc()
# Compute rows overlapping old block
for i in xrange(4, size):
emit("ldi r%s, 0", acc(2))
emit("ld r0, z")
emit("add r%s, r0", acc(0))
emit("adc r%s, r25", acc(1))
update_low()
for j in xrange(4):
emit("mul r%s, r%s", lhi(j), rlo(3-j))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("adc r%s, r25", acc(2))
emit("mul r%s, r%s", rhi(j), llo(3-j))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("adc r%s, r25", acc(2))
emit("st z+, r%s", acc(0))
print ""
rotate_acc()
# Compute new triangle
left_combined = [llo(1), llo(2), llo(3), lhi(0), lhi(1), lhi(2), lhi(3)]
right_combined = [rlo(1), rlo(2), rlo(3), rhi(0), rhi(1), rhi(2), rhi(3)]
def left(i):
return left_combined[i]
def right(i):
return right_combined[i]
for i in xrange(6):
emit("ldi r%s, 0", acc(2))
for j in xrange(7 - i):
emit("mul r%s, r%s", left(i+j), right(6-j))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("adc r%s, r25", acc(2))
emit("st z+, r%s", acc(0))
print ""
rotate_acc()
emit("mul r%s, r%s", left(6), right(6))
emit("add r%s, r0", acc(0))
emit("adc r%s, r1", acc(1))
emit("st z+, r%s", acc(0))
emit("st z+, r%s", acc(1))
emit("adiw r26, 4")
emit("adiw r28, 4")

View File

@@ -1,242 +0,0 @@
#!/usr/bin/env python
import sys
if len(sys.argv) < 2:
print "Provide the integer size in 32-bit words"
sys.exit(1)
size = int(sys.argv[1])
if size > 8:
print "This script doesn't work with integer size %s due to laziness" % (size)
sys.exit(1)
init_size = 0
if size > 6:
init_size = size - 6
def emit(line, *args):
s = '"' + line + r' \n\t"'
print s % args
def mulacc(acc, r1, r2):
if size <= 6:
emit("umull r1, r14, r%s, r%s", r1, r2)
emit("adds r%s, r%s, r1", acc[0], acc[0])
emit("adcs r%s, r%s, r14", acc[1], acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
else:
emit("mov r14, r%s", acc[1])
emit("umlal r%s, r%s, r%s, r%s", acc[0], acc[1], r1, r2)
emit("cmp r14, r%s", acc[1])
emit("it hi")
emit("adchi r%s, r%s, #0", acc[2], acc[2])
r = [2, 3, 4, 5, 6, 7]
s = size - init_size
if init_size == 1:
emit("ldmia r1!, {r2}")
emit("add r1, %s", (size - init_size * 2) * 4)
emit("ldmia r1!, {r5}")
emit("add r0, %s", (size - init_size) * 4)
emit("umull r8, r9, r2, r5")
emit("stmia r0!, {r8, r9}")
emit("sub r0, %s", (size + init_size) * 4)
emit("sub r1, %s", (size) * 4)
print ""
elif init_size == 2:
emit("ldmia r1!, {r2, r3}")
emit("add r1, %s", (size - init_size * 2) * 4)
emit("ldmia r1!, {r5, r6}")
emit("add r0, %s", (size - init_size) * 4)
print ""
emit("umull r8, r9, r2, r5")
emit("stmia r0!, {r8}")
print ""
emit("umull r12, r10, r2, r6")
emit("adds r9, r9, r12")
emit("adc r10, r10, #0")
emit("stmia r0!, {r9}")
print ""
emit("umull r8, r9, r3, r6")
emit("adds r10, r10, r8")
emit("adc r11, r9, #0")
emit("stmia r0!, {r10, r11}")
print ""
emit("sub r0, %s", (size + init_size) * 4)
emit("sub r1, %s", (size) * 4)
# load input words
emit("ldmia r1!, {%s}", ", ".join(["r%s" % (r[i]) for i in xrange(s)]))
print ""
emit("umull r11, r12, r2, r2")
emit("stmia r0!, {r11}")
print ""
emit("mov r9, #0")
emit("umull r10, r11, r2, r3")
emit("adds r12, r12, r10")
emit("adcs r8, r11, #0")
emit("adc r9, r9, #0")
emit("adds r12, r12, r10")
emit("adcs r8, r8, r11")
emit("adc r9, r9, #0")
emit("stmia r0!, {r12}")
print ""
emit("mov r10, #0")
emit("umull r11, r12, r2, r4")
emit("adds r11, r11, r11")
emit("adcs r12, r12, r12")
emit("adc r10, r10, #0")
emit("adds r8, r8, r11")
emit("adcs r9, r9, r12")
emit("adc r10, r10, #0")
emit("umull r11, r12, r3, r3")
emit("adds r8, r8, r11")
emit("adcs r9, r9, r12")
emit("adc r10, r10, #0")
emit("stmia r0!, {r8}")
print ""
acc = [8, 9, 10]
old_acc = [11, 12]
for i in xrange(3, s):
emit("mov r%s, #0", old_acc[1])
tmp = [acc[1], acc[2]]
acc = [acc[0], old_acc[0], old_acc[1]]
old_acc = tmp
# gather non-equal words
emit("umull r%s, r%s, r%s, r%s", acc[0], acc[1], r[0], r[i])
for j in xrange(1, (i+1)//2):
mulacc(acc, r[j], r[i-j])
# multiply by 2
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[1])
emit("adc r%s, r%s, r%s", acc[2], acc[2], acc[2])
# add equal word (if any)
if ((i+1) % 2) != 0:
mulacc(acc, r[i//2], r[i//2])
# add old accumulator
emit("adds r%s, r%s, r%s", acc[0], acc[0], old_acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
# store
emit("stmia r0!, {r%s}", acc[0])
print ""
regs = list(r)
for i in xrange(init_size):
regs = regs[1:] + regs[:1]
emit("ldmia r1!, {r%s}", regs[5])
for limit in [4, 5]:
emit("mov r%s, #0", old_acc[1])
tmp = [acc[1], acc[2]]
acc = [acc[0], old_acc[0], old_acc[1]]
old_acc = tmp
# gather non-equal words
emit("umull r%s, r%s, r%s, r%s", acc[0], acc[1], regs[0], regs[limit])
for j in xrange(1, (limit+1)//2):
mulacc(acc, regs[j], regs[limit-j])
emit("ldr r14, [r0]") # load stored value from initial block, and add to accumulator
emit("adds r%s, r%s, r14", acc[0], acc[0])
emit("adcs r%s, r%s, #0", acc[1], acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
# multiply by 2
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[1])
emit("adc r%s, r%s, r%s", acc[2], acc[2], acc[2])
# add equal word
if limit == 4:
mulacc(acc, regs[2], regs[2])
# add old accumulator
emit("adds r%s, r%s, r%s", acc[0], acc[0], old_acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
# store
emit("stmia r0!, {r%s}", acc[0])
print ""
for i in xrange(1, s-3):
emit("mov r%s, #0", old_acc[1])
tmp = [acc[1], acc[2]]
acc = [acc[0], old_acc[0], old_acc[1]]
old_acc = tmp
# gather non-equal words
emit("umull r%s, r%s, r%s, r%s", acc[0], acc[1], regs[i], regs[s - 1])
for j in xrange(1, (s-i)//2):
mulacc(acc, regs[i+j], regs[s - 1 - j])
# multiply by 2
emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[1])
emit("adc r%s, r%s, r%s", acc[2], acc[2], acc[2])
# add equal word (if any)
if ((s-i) % 2) != 0:
mulacc(acc, regs[i + (s-i)//2], regs[i + (s-i)//2])
# add old accumulator
emit("adds r%s, r%s, r%s", acc[0], acc[0], old_acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
# store
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("mov r%s, #0", acc[2])
emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 3], regs[s - 1])
emit("adds r1, r1, r1")
emit("adcs r%s, r%s, r%s", old_acc[1], old_acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("adds r%s, r%s, r1", acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 2], regs[s - 2])
emit("adds r%s, r%s, r1", acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("mov r%s, #0", acc[2])
emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 2], regs[s - 1])
emit("adds r1, r1, r1")
emit("adcs r%s, r%s, r%s", old_acc[1], old_acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("adds r%s, r%s, r1", acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("adc r%s, r%s, #0", acc[2], acc[2])
emit("stmia r0!, {r%s}", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 1], regs[s - 1])
emit("adds r%s, r%s, r1", acc[0], acc[0])
emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1])
emit("stmia r0!, {r%s}", acc[0])
emit("stmia r0!, {r%s}", acc[1])

View File

@@ -1,327 +0,0 @@
#!/usr/bin/env python
import sys
if len(sys.argv) < 2:
print "Provide the integer size in bytes"
sys.exit(1)
size = int(sys.argv[1])
if size > 40:
print "This script doesn't work with integer size %s due to laziness" % (size)
sys.exit(1)
init_size = size - 20
if size < 20:
init_size = 0
def rg(i):
return i + 2
def lo(i):
return i + 2
def hi(i):
return i + 12
def emit(line, *args):
s = '"' + line + r' \n\t"'
print s % args
#### set up registers
zero = "r25"
emit("ldi %s, 0", zero) # zero register
if init_size > 0:
emit("movw r28, r26") # y = x
h = (init_size + 1)//2
for i in xrange(h):
emit("ld r%s, x+", lo(i))
emit("adiw r28, %s", size - init_size) # move y to other end
for i in xrange(h):
emit("ld r%s, y+", hi(i))
emit("adiw r30, %s", size - init_size) # move z
if init_size == 1:
emit("mul %s, %s", lo(0), hi(0))
emit("st z+, r0")
emit("st z+, r1")
else:
#### first one
print ""
emit("ldi r23, 0")
emit("mul %s, %s", lo(0), hi(0))
emit("st z+, r0")
emit("mov r22, r1")
print ""
#### rest of initial block, with moving accumulator registers
acc = [22, 23, 24]
for r in xrange(1, h):
emit("ldi r%s, 0", acc[2])
for i in xrange(0, (r+2)//2):
emit("mul r%s, r%s", lo(i), hi(r - i))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
lo_r = range(2, 2 + h)
hi_r = range(12, 12 + h)
# now we need to start loading more from the high end
for r in xrange(h, init_size):
hi_r = hi_r[1:] + hi_r[:1]
emit("ld r%s, y+", hi_r[h-1])
emit("ldi r%s, 0", acc[2])
for i in xrange(0, (r+2)//2):
emit("mul r%s, r%s", lo(i), hi_r[h - 1 - i])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
# loaded all of the high end bytes; now need to start loading the rest of the low end
for r in xrange(1, init_size-h):
lo_r = lo_r[1:] + lo_r[:1]
emit("ld r%s, x+", lo_r[h-1])
emit("ldi r%s, 0", acc[2])
for i in xrange(0, (init_size+1 - r)//2):
emit("mul r%s, r%s", lo_r[i], hi_r[h - 1 - i])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
lo_r = lo_r[1:] + lo_r[:1]
emit("ld r%s, x+", lo_r[h-1])
# now we have loaded everything, and we just need to finish the last corner
for r in xrange(init_size-h, init_size-1):
emit("ldi r%s, 0", acc[2])
for i in xrange(0, (init_size+1 - r)//2):
emit("mul r%s, r%s", lo_r[i], hi_r[h - 1 - i])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
lo_r = lo_r[1:] + lo_r[:1] # make the indexing easy
emit("mul r%s, r%s", lo_r[0], hi_r[h - 1])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("st z+, r%s", acc[0])
emit("st z+, r%s", acc[1])
print ""
emit("sbiw r26, %s", init_size) # reset x
emit("sbiw r30, %s", size + init_size) # reset z
# TODO you could do more rows of size 20 here if your integers are larger than 40 bytes
s = size - init_size
for i in xrange(s):
emit("ld r%s, x+", rg(i))
#### first few columns
# NOTE: this is only valid if size >= 3
print ""
emit("ldi r23, 0")
emit("mul r%s, r%s", rg(0), rg(0))
emit("st z+, r0")
emit("mov r22, r1")
print ""
emit("ldi r24, 0")
emit("mul r%s, r%s", rg(0), rg(1))
emit("add r22, r0")
emit("adc r23, r1")
emit("adc r24, %s", zero)
emit("add r22, r0")
emit("adc r23, r1")
emit("adc r24, %s", zero)
emit("st z+, r22")
print ""
emit("ldi r22, 0")
emit("mul r%s, r%s", rg(0), rg(2))
emit("add r23, r0")
emit("adc r24, r1")
emit("adc r22, %s", zero)
emit("add r23, r0")
emit("adc r24, r1")
emit("adc r22, %s", zero)
emit("mul r%s, r%s", rg(1), rg(1))
emit("add r23, r0")
emit("adc r24, r1")
emit("adc r22, %s", zero)
emit("st z+, r23")
print ""
acc = [23, 24, 22]
old_acc = [28, 29]
for i in xrange(3, s):
emit("ldi r%s, 0", old_acc[1])
tmp = [acc[1], acc[2]]
acc = [acc[0], old_acc[0], old_acc[1]]
old_acc = tmp
# gather non-equal words
emit("mul r%s, r%s", rg(0), rg(i))
emit("mov r%s, r0", acc[0])
emit("mov r%s, r1", acc[1])
for j in xrange(1, (i+1)//2):
emit("mul r%s, r%s", rg(j), rg(i-j))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
# multiply by 2
emit("lsl r%s", acc[0])
emit("rol r%s", acc[1])
emit("rol r%s", acc[2])
# add equal word (if any)
if ((i+1) % 2) != 0:
emit("mul r%s, r%s", rg(i//2), rg(i//2))
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
# add old accumulator
emit("add r%s, r%s", acc[0], old_acc[0])
emit("adc r%s, r%s", acc[1], old_acc[1])
emit("adc r%s, %s", acc[2], zero)
# store
emit("st z+, r%s", acc[0])
print ""
regs = range(2, 22)
for i in xrange(init_size):
regs = regs[1:] + regs[:1]
emit("ld r%s, x+", regs[19])
for limit in [18, 19]:
emit("ldi r%s, 0", old_acc[1])
tmp = [acc[1], acc[2]]
acc = [acc[0], old_acc[0], old_acc[1]]
old_acc = tmp
# gather non-equal words
emit("mul r%s, r%s", regs[0], regs[limit])
emit("mov r%s, r0", acc[0])
emit("mov r%s, r1", acc[1])
for j in xrange(1, (limit+1)//2):
emit("mul r%s, r%s", regs[j], regs[limit-j])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("ld r0, z") # load stored value from initial block, and add to accumulator (note z does not increment)
emit("add r%s, r0", acc[0])
emit("adc r%s, r25", acc[1])
emit("adc r%s, r25", acc[2])
# multiply by 2
emit("lsl r%s", acc[0])
emit("rol r%s", acc[1])
emit("rol r%s", acc[2])
# add equal word
if limit == 18:
emit("mul r%s, r%s", regs[9], regs[9])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
# add old accumulator
emit("add r%s, r%s", acc[0], old_acc[0])
emit("adc r%s, r%s", acc[1], old_acc[1])
emit("adc r%s, %s", acc[2], zero)
# store
emit("st z+, r%s", acc[0])
print ""
for i in xrange(1, s-3):
emit("ldi r%s, 0", old_acc[1])
tmp = [acc[1], acc[2]]
acc = [acc[0], old_acc[0], old_acc[1]]
old_acc = tmp
# gather non-equal words
emit("mul r%s, r%s", regs[i], regs[s - 1])
emit("mov r%s, r0", acc[0])
emit("mov r%s, r1", acc[1])
for j in xrange(1, (s-i)//2):
emit("mul r%s, r%s", regs[i+j], regs[s - 1 - j])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
# multiply by 2
emit("lsl r%s", acc[0])
emit("rol r%s", acc[1])
emit("rol r%s", acc[2])
# add equal word (if any)
if ((s-i) % 2) != 0:
emit("mul r%s, r%s", regs[i + (s-i)//2], regs[i + (s-i)//2])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
# add old accumulator
emit("add r%s, r%s", acc[0], old_acc[0])
emit("adc r%s, r%s", acc[1], old_acc[1])
emit("adc r%s, %s", acc[2], zero)
# store
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("ldi r%s, 0", acc[2])
emit("mul r%s, r%s", regs[17], regs[19])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("mul r%s, r%s", regs[18], regs[18])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("st z+, r%s", acc[0])
print ""
acc = acc[1:] + acc[:1]
emit("ldi r%s, 0", acc[2])
emit("mul r%s, r%s", regs[18], regs[19])
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("add r%s, r0", acc[0])
emit("adc r%s, r1", acc[1])
emit("adc r%s, %s", acc[2], zero)
emit("st z+, r%s", acc[0])
print ""
emit("mul r%s, r%s", regs[19], regs[19])
emit("add r%s, r0", acc[1])
emit("adc r%s, r1", acc[2])
emit("st z+, r%s", acc[1])
emit("st z+, r%s", acc[2])
emit("eor r1, r1")

View File

@@ -1,128 +0,0 @@
/* Copyright 2020, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#include "uECC.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char* private_key;
const char* public_key;
const char* k;
const char* hash;
const char* r;
const char* s;
} Test;
Test secp256k1_tests[] = {
{
"ebb2c082fd7727890a28ac82f6bdf97bad8de9f5d7c9028692de1a255cad3e0f",
"779dd197a5df977ed2cf6cb31d82d43328b790dc6b3b7d4437a427bd5847dfcde94b724a555b6d017bb7607c3e3281daf5b1699d6ef4124975c9237b917d426f",
"49a0d7b786ec9cde0d0721d72804befd06571c974b191efb42ecf322ba9ddd9a",
"4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a",
"241097efbf8b63bf145c8961dbdf10c310efbb3b2676bbc0f8b08505c9e2f795",
"021006b7838609339e8b415a7f9acb1b661828131aef1ecbc7955dfb01f3ca0e"
},
};
extern int uECC_sign_with_k(const uint8_t *private_key,
const uint8_t *message_hash,
unsigned hash_size,
const uint8_t *k,
uint8_t *signature,
uECC_Curve curve);
void vli_print(uint8_t *vli, unsigned int size) {
for(unsigned i=0; i<size; ++i) {
printf("%02X ", (unsigned)vli[i]);
}
printf("\n");
}
void strtobytes(const char* str, uint8_t* bytes, int count) {
for (int c = 0; c < count; ++c) {
if (sscanf(str, "%2hhx", &bytes[c]) != 1) {
printf("Failed to read string to bytes");
exit(1);
}
str += 2;
}
}
int run(Test* tests, int num_tests, uECC_Curve curve) {
uint8_t private[32] = {0};
uint8_t public[64] = {0};
uint8_t k[32] = {0};
uint8_t hash[32] = {0};
uint8_t r[32] = {0};
uint8_t s[32] = {0};
uint8_t signature[64] = {0};
int result;
int i;
int private_key_size;
int public_key_size;
int all_success = 1;
private_key_size = uECC_curve_private_key_size(curve);
public_key_size = uECC_curve_public_key_size(curve);
for (i = 0; i < num_tests; ++i) {
strtobytes(tests[i].private_key, private, private_key_size);
strtobytes(tests[i].public_key, public, public_key_size);
strtobytes(tests[i].k, k, private_key_size);
strtobytes(tests[i].hash, hash, private_key_size);
strtobytes(tests[i].r, r, private_key_size);
strtobytes(tests[i].s, s, private_key_size);
result = uECC_sign_with_k(private, hash, private_key_size, k, signature, curve);
if (!result) {
all_success = 0;
printf(" Sign failed for test %d\n", i);
}
if (result) {
if (memcmp(signature, r, private_key_size) != 0) {
all_success = 0;
printf(" Got incorrect r for test %d\n", i);
printf(" Expected: ");
vli_print(r, private_key_size);
printf(" Calculated: ");
vli_print(signature, private_key_size);
}
if (memcmp(signature + private_key_size, s, private_key_size) != 0) {
all_success = 0;
printf(" Got incorrect s for test %d\n", i);
printf(" Expected: ");
vli_print(s, private_key_size);
printf(" Calculated: ");
vli_print(signature + private_key_size, private_key_size);
}
result = uECC_verify(public, hash, private_key_size, signature, curve);
if (!result) {
printf(" Verify failed for test %d\n", i);
}
}
}
return all_success;
}
#define RUN_TESTS(curve) \
printf(#curve ":\n"); \
if (run(curve##_tests, sizeof(curve##_tests) / sizeof(curve##_tests[0]), uECC_##curve()) ) { \
printf(" All passed\n"); \
} else { \
printf(" Failed\n"); \
}
int main() {
#if uECC_SUPPORTS_secp256k1
RUN_TESTS(secp256k1)
#endif
return 0;
}

View File

@@ -1,4 +0,0 @@
c, link = emk.module("c", "link")
link.depdirs += [
"$:proj:$"
]

View File

@@ -1,338 +0,0 @@
/* Copyright 2020, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#include "uECC.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char* k;
const char* Q;
int success;
} Test;
Test secp160r1_tests[] = {
/* Note, I couldn't find any test vectors for secp160r1 online, so these are just
generated on my desktop using uECC. */
{
"000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{
"000000000000000000000000000000000000000001",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{
"000000000000000000000000000000000000000002",
"02F997F33C5ED04C55D3EDF8675D3E92E8F46686F083A323482993E9440E817E21CFB7737DF8797B",
1
},
{
"000000000000000000000000000000000000000003",
"7B76FF541EF363F2DF13DE1650BD48DAA958BC59C915CA790D8C8877B55BE0079D12854FFE9F6F5A",
1
},
{ /* n - 4 */
"0100000000000000000001F4C8F927AED3CA752253",
"B4041D8683BE99F0AFE01C307B1AD4C100CF2A88C0CD35127BE0F73FF99F338B350B5A42864112F7",
1
},
{ /* n - 3 */
"0100000000000000000001F4C8F927AED3CA752254",
"7B76FF541EF363F2DF13DE1650BD48DAA958BC5936EA3586F27377884AA41FF862ED7AAF816090A5",
1
},
{ /* n - 2 */
"0100000000000000000001F4C8F927AED3CA752255",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{ /* n - 1 */
"0100000000000000000001F4C8F927AED3CA752256",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{ /* n */
"0100000000000000000001F4C8F927AED3CA752257",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
};
Test secp192r1_tests[] = {
{
"000000000000000000000000000000000000000000000000",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{
"000000000000000000000000000000000000000000000001",
"188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
0
},
{
"000000000000000000000000000000000000000000000002",
"DAFEBF5828783F2AD35534631588A3F629A70FB16982A888DD6BDA0D993DA0FA46B27BBC141B868F59331AFA5C7E93AB",
1
},
{
"000000000000000000000000000000000000000000000003",
"76E32A2557599E6EDCD283201FB2B9AADFD0D359CBB263DA782C37E372BA4520AA62E0FED121D49EF3B543660CFD05FD",
1
},
{ /* n - 4 */
"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D2282D",
"35433907297CC378B0015703374729D7A4FE46647084E4BA5D9B667B0DECA3CFE15C534F88932B0DDAC764CEE24C41CD",
1
},
{ /* n - 3 */
"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D2282E",
"76E32A2557599E6EDCD283201FB2B9AADFD0D359CBB263DA87D3C81C8D45BADF559D1F012EDE2B600C4ABC99F302FA02",
1
},
{ /* n - 2 */
"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D2282F",
"DAFEBF5828783F2AD35534631588A3F629A70FB16982A888229425F266C25F05B94D8443EBE4796FA6CCE505A3816C54",
0
},
{ /* n - 1 */
"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22830",
"188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012F8E6D46A003725879CEFEE1294DB32298C06885EE186B7EE",
0
},
{ /* n */
"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
};
Test secp224r1_tests[] = {
{
"00000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{
"00000000000000000000000000000000000000000000000000000001",
"B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
0
},
{
"00000000000000000000000000000000000000000000000000000002",
"706A46DC76DCB76798E60E6D89474788D16DC18032D268FD1A704FA61C2B76A7BC25E7702A704FA986892849FCA629487ACF3709D2E4E8BB",
1
},
{
"00000000000000000000000000000000000000000000000000000003",
"DF1B1D66A551D0D31EFF822558B9D2CC75C2180279FE0D08FD896D04A3F7F03CADD0BE444C0AA56830130DDF77D317344E1AF3591981A925",
1
},
{ /* n - 4 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A39",
"AE99FEEBB5D26945B54892092A8AEE02912930FA41CD114E40447301FB7DA7F5F13A43B81774373C879CD32D6934C05FA758EEB14FCFAB38",
1
},
{ /* n - 3 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3A",
"DF1B1D66A551D0D31EFF822558B9D2CC75C2180279FE0D08FD896D045C080FC3522F41BBB3F55A97CFECF21F882CE8CBB1E50CA6E67E56DC",
1
},
{ /* n - 2 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3B",
"706A46DC76DCB76798E60E6D89474788D16DC18032D268FD1A704FA6E3D4895843DA188FD58FB0567976D7B50359D6B78530C8F62D1B1746",
0
},
{ /* n - 1 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3C",
"B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D2142C89C774A08DC04B3DD201932BC8A5EA5F8B89BBB2A7E667AFF81CD",
0
},
{ /* n */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
};
Test secp256r1_tests[] = {
{
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{
"0000000000000000000000000000000000000000000000000000000000000001",
"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
0
},
{
"0000000000000000000000000000000000000000000000000000000000000002",
"7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC4766997807775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1",
1
},
{
"0000000000000000000000000000000000000000000000000000000000000003",
"5ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C8734640C4998FF7E374B06CE1A64A2ECD82AB036384FB83D9A79B127A27D5032",
1
},
{ /* n - 4 */
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254D",
"E2534A3532D08FBBA02DDE659EE62BD0031FE2DB785596EF509302446B0308521F0EA8A4B39CC339E62011A02579D289B103693D0CF11FFAA3BD3DC0E7B12739",
1
},
{ /* n - 3 */
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254E",
"5ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C78CB9BF2B6670082C8B4F931E59B5D1327D54FCAC7B047C265864ED85D82AFCD",
1
},
{ /* n - 2 */
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F",
"7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978F888AAEE24712FC0D6C26539608BCF244582521AC3167DD661FB4862DD878C2E",
0
},
{ /* n - 1 */
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550",
"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296B01CBD1C01E58065711814B583F061E9D431CCA994CEA1313449BF97C840AE0A",
0
},
{ /* n */
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
};
Test secp256k1_tests[] = {
{
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
{
"0000000000000000000000000000000000000000000000000000000000000001",
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",
0
},
{
"0000000000000000000000000000000000000000000000000000000000000002",
"C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE51AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A",
1
},
{
"0000000000000000000000000000000000000000000000000000000000000003",
"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9388F7B0F632DE8140FE337E62A37F3566500A99934C2231B6CB9FD7584B8E672",
1
},
{ /* n - 4 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD036413D",
"E493DBF1C10D80F3581E4904930B1404CC6C13900EE0758474FA94ABE8C4CD13AE1266C15F2BAA48A9BD1DF6715AEBB7269851CC404201BF30168422B88C630D",
1
},
{ /* n - 3 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD036413E",
"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9C77084F09CD217EBF01CC819D5C80CA99AFF5666CB3DDCE4934602897B4715BD",
1
},
{ /* n - 2 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD036413F",
"C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5E51E970159C23CC65C3A7BE6B99315110809CD9ACD992F1EDC9BCE55AF301705",
0
},
{ /* n - 1 */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140",
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798B7C52588D95C3B9AA25B0403F1EEF75702E84BB7597AABE663B82F6F04EF2777",
0
},
{ /* n */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0
},
};
void vli_print(uint8_t *vli, unsigned int size) {
for(unsigned i=0; i<size; ++i) {
printf("%02X ", (unsigned)vli[i]);
}
printf("\n");
}
void strtobytes(const char* str, uint8_t* bytes, int count) {
for (int c = 0; c < count; ++c) {
if (sscanf(str, "%2hhx", &bytes[c]) != 1) {
printf("Failed to read string to bytes");
exit(1);
}
str += 2;
}
}
int run(Test* tests, int num_tests, uECC_Curve curve) {
uint8_t private[32] = {0};
uint8_t public[64] = {0};
uint8_t expected[64] = {0};
int result;
int i;
int private_key_size;
int public_key_size;
int all_success = 1;
private_key_size = uECC_curve_private_key_size(curve);
public_key_size = uECC_curve_public_key_size(curve);
for (i = 0; i < num_tests; ++i) {
strtobytes(tests[i].k, private, private_key_size);
result = uECC_compute_public_key(private, public, curve);
if (result != tests[i].success) {
all_success = 0;
printf(" Got unexpected result from test %d: %d\n", i, result);
}
if (result) {
strtobytes(tests[i].Q, expected, public_key_size);
if (memcmp(public, expected, public_key_size) != 0) {
all_success = 0;
printf(" Got incorrect public key for test %d\n", i);
printf(" Expected: ");
vli_print(expected, public_key_size);
printf(" Calculated: ");
vli_print(public, public_key_size);
}
}
}
return all_success;
}
#define RUN_TESTS(curve) \
printf(#curve ":\n"); \
if (run(curve##_tests, sizeof(curve##_tests) / sizeof(curve##_tests[0]), uECC_##curve()) ) { \
printf(" All passed\n"); \
} else { \
printf(" Failed\n"); \
}
int main() {
#if uECC_SUPPORTS_secp160r1
RUN_TESTS(secp160r1)
#endif
#if uECC_SUPPORTS_secp192r1
RUN_TESTS(secp192r1)
#endif
#if uECC_SUPPORTS_secp224r1
RUN_TESTS(secp224r1)
#endif
#if uECC_SUPPORTS_secp256r1
RUN_TESTS(secp256r1)
#endif
#if uECC_SUPPORTS_secp256k1
RUN_TESTS(secp256k1)
#endif
return 0;
}

View File

@@ -1,79 +0,0 @@
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#include "uECC.h"
#include <stdio.h>
#include <string.h>
#ifndef uECC_TEST_NUMBER_OF_ITERATIONS
#define uECC_TEST_NUMBER_OF_ITERATIONS 256
#endif
void vli_print(char *str, uint8_t *vli, unsigned int size) {
printf("%s ", str);
for(unsigned i=0; i<size; ++i) {
printf("%02X ", (unsigned)vli[i]);
}
printf("\n");
}
int main() {
uint8_t public[64];
uint8_t private[32];
uint8_t compressed_point[33];
uint8_t decompressed_point[64];
int i;
int c;
const struct uECC_Curve_t * curves[5];
int num_curves = 0;
#if uECC_SUPPORTS_secp160r1
curves[num_curves++] = uECC_secp160r1();
#endif
#if uECC_SUPPORTS_secp192r1
curves[num_curves++] = uECC_secp192r1();
#endif
#if uECC_SUPPORTS_secp224r1
curves[num_curves++] = uECC_secp224r1();
#endif
#if uECC_SUPPORTS_secp256r1
curves[num_curves++] = uECC_secp256r1();
#endif
#if uECC_SUPPORTS_secp256k1
curves[num_curves++] = uECC_secp256k1();
#endif
printf("Testing compression and decompression of %d random EC points\n",
uECC_TEST_NUMBER_OF_ITERATIONS);
for (c = 0; c < num_curves; ++c) {
for (i = 0; i < uECC_TEST_NUMBER_OF_ITERATIONS; ++i) {
printf(".");
fflush(stdout);
memset(public, 0, sizeof(public));
memset(decompressed_point, 0, sizeof(decompressed_point));
/* Generate arbitrary EC point (public) on Curve */
if (!uECC_make_key(public, private, curves[c])) {
printf("uECC_make_key() failed\n");
continue;
}
/* compress and decompress point */
uECC_compress(public, compressed_point, curves[c]);
uECC_decompress(compressed_point, decompressed_point, curves[c]);
if (memcmp(public, decompressed_point, sizeof(public)) != 0) {
printf("Original and decompressed points are not identical!\n");
vli_print("Original point = ", public, sizeof(public));
vli_print("Compressed point = ", compressed_point, sizeof(compressed_point));
vli_print("Decompressed point = ", decompressed_point, sizeof(decompressed_point));
}
}
printf("\n");
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More