Skip to content

Commit

Permalink
feat: add stub-support
Browse files Browse the repository at this point in the history
This adds initial support for the flasher stub. The stubs get pulled
from the original esptool repository and converted to .h/.c files using
a Python script. This script can be run using the custom CMake target
gen_esp_stubs.

Additions to the public API:
- esp_loader_connect_to_stub
  Upload stub loader, then connect to it instead of the internal ROM
  loader.
- esp_loader_change_transmission_rate_stub
  Like esp_loader_change_transmission_rate but has a second parameter
  for the stub version of the CHANGE_BAUDRATE command.

Additions to private API:
- loader_run_stub
  Upload and run the stub loader.

Closes #103
  • Loading branch information
higaski authored and DNedic committed Jul 2, 2024
1 parent 92dfdeb commit ec1fc06
Show file tree
Hide file tree
Showing 11 changed files with 620 additions and 99 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
build
sdkconfig
sdkconfig.old
Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ endif()

if (DEFINED SERIAL_FLASHER_INTERFACE_UART OR CONFIG_SERIAL_FLASHER_INTERFACE_UART STREQUAL "y")
list(APPEND srcs
src/esp_stubs.c
src/protocol_uart.c
src/slip.c
)
Expand All @@ -58,6 +59,7 @@ if (DEFINED SERIAL_FLASHER_INTERFACE_UART OR CONFIG_SERIAL_FLASHER_INTERFACE_UAR

elseif(DEFINED SERIAL_FLASHER_INTERFACE_USB OR CONFIG_SERIAL_FLASHER_INTERFACE_USB STREQUAL "y")
list(APPEND srcs
src/esp_stubs.c
src/protocol_uart.c
src/slip.c
)
Expand Down Expand Up @@ -141,3 +143,7 @@ else()
endif()

target_compile_definitions(${target} PUBLIC ${defs})

# esp_stubs.[ch] codegen
include(cmake/add_gen_esp_stubs_target.cmake)
add_gen_esp_stubs_target(VERSION 4.7.0)
21 changes: 21 additions & 0 deletions cmake/add_gen_esp_stubs_target.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
macro(add_gen_esp_stubs_target)
set(ONE_VALUE_KEYWORDS VERSION)
cmake_parse_arguments(ESPTOOL "" "${ONE_VALUE_KEYWORDS}" "" "${ARGN}")

# Don't make this mandatory, some users might not have Python installed
find_package(Python COMPONENTS Interpreter)
if(NOT Python_Interpreter_FOUND)
message(WARNING "Python not found, gen_esp_stubs target not created")
return()
endif()

# Run python script to generate esp_stubs.h and esp_stubs.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/stub.c
COMMAND
${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gen_esp_stubs.py
${ESPTOOL_VERSION} ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gen_esp_stubs.py)
add_custom_target(gen_esp_stubs
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/stub.c)
endmacro()
149 changes: 149 additions & 0 deletions cmake/gen_esp_stubs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import base64
import json
import os
import sys
import urllib.request

"""
Generate flasher stubs from https://github.com/espressif/esptool/tree/master/esptool/targets/stub_flasher
"""

# Paths
esptool_version = sys.argv[1]
root_path = sys.argv[2]
priv_inc_path = os.path.join(root_path, "private_include")
src_path = os.path.join(root_path, "src")
hfile_path = os.path.join(priv_inc_path, "esp_stubs.h")
cfile_path = os.path.join(src_path, "esp_stubs.c")

# Matches order of target_chip_t enumeration
files_to_download = [
"stub_flasher_8266.json", # ESP8266_CHIP
"stub_flasher_32.json", # ESP32_CHIP
"stub_flasher_32s2.json", # ESP32S2_CHIP
"stub_flasher_32c3.json", # ESP32C3_CHIP
"stub_flasher_32s3.json", # ESP32S3_CHIP
"stub_flasher_32c2.json", # ESP32C2_CHIP
None, # ESP32_RESERVED0_CHIP
"stub_flasher_32h2.json", # ESP32H2_CHIP
"stub_flasher_32c6.json", # ESP32C6_CHIP
]

# .h and .c file to generate
hfile = open(hfile_path, "w")
cfile = open(cfile_path, "w")
codegen_notice = "// auto-generated stubs from esptool v" + esptool_version + "\n"

# Write .h file
hfile.write(codegen_notice)
hfile.write(
"\n"
"#pragma once\n"
"\n"
"#include <stdint.h>\n"
"#include <stdbool.h>\n"
'#include "esp_loader.h"\n'
"\n"
"#ifdef __cplusplus\n"
'extern "C" {\n'
"#endif\n"
"\n"
"extern bool esp_stub_running;\n"
"\n"
"#if (defined SERIAL_FLASHER_INTERFACE_UART) || (defined SERIAL_FLASHER_INTERFACE_USB)\n"
"\n"
"typedef struct {\n"
" esp_loader_bin_header_t header;\n"
" esp_loader_bin_segment_t segments[2];\n"
"} esp_stub_t;\n"
"\n"
"extern const esp_stub_t esp_stub[ESP_MAX_CHIP];\n"
"\n"
"#endif\n"
"\n"
"#ifdef __cplusplus\n"
"}\n"
"#endif\n"
)

# Write .c file
cfile.write(codegen_notice)
cfile.write(
"\n"
'#include "esp_stubs.h"\n'
"\n"
"bool esp_stub_running = false;\n"
"\n"
"#if (defined SERIAL_FLASHER_INTERFACE_UART) || (defined SERIAL_FLASHER_INTERFACE_USB)\n"
"\n"
"#if __STDC_VERSION__ >= 201112L\n"
'_Static_assert(ESP8266_CHIP == 0, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32_CHIP == 1, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32S2_CHIP == 2, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32C3_CHIP == 3, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32S3_CHIP == 4, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32C2_CHIP == 5, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32_RESERVED0_CHIP == 6, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32H2_CHIP == 7, "Stub order matches target_chip_t enumeration");\n'
'_Static_assert(ESP32C6_CHIP == 8, "Stub order matches target_chip_t enumeration");\n'
"_Static_assert(ESP_MAX_CHIP == "
+ str(len(files_to_download))
+ ', "Stub order matches target_chip_t enumeration");\n'
"#endif\n"
"\n"
"const esp_stub_t esp_stub[ESP_MAX_CHIP] = {\n"
"\n"
)

for file_to_download in files_to_download:
if file_to_download is None:
cfile.write(" // placeholder\n" " {},\n" "\n")
else:
with urllib.request.urlopen(
"https://raw.githubusercontent.com/espressif/esptool/v"
+ esptool_version
+ "/esptool/targets/stub_flasher/"
+ file_to_download
) as url:
# Read stub_flasher*.json
stub = json.load(url)
entry = stub["entry"]
text = base64.b64decode(stub["text"])
text_start = stub["text_start"]
try:
data = base64.b64decode(stub["data"])
data_start = stub["data_start"]
except KeyError:
data = None
data_start = None
bss_start = stub["bss_start"]

# According to esptool source those data could potentially be None
text_str = ",".join([hex(b) for b in text])
data_str = "" if data is None else ",".join([hex(b) for b in data])
data_size_str = str(0) if data is None else str(len(data))
data_start_str = str(0) if data_start is None else str(data_start)

cfile.write(
" // " + file_to_download + "\n"
" {\n"
" .header = {\n"
" .entrypoint = " + str(entry) + ",\n"
" },\n"
" .segments = {\n"
" {\n"
" .addr = " + str(text_start) + ",\n"
" .size = " + str(len(text)) + ",\n"
" .data = (uint8_t[]){" + text_str + "},\n"
" },\n"
" {\n"
" .addr = " + data_start_str + ",\n"
" .size = " + data_size_str + ",\n"
" .data = (uint8_t[]){" + data_str + "},\n"
" },\n"
" },\n"
" },\n"
"\n"
)

cfile.write("};\n" "\n" "#endif\n")
29 changes: 29 additions & 0 deletions include/esp_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,35 @@ esp_loader_error_t esp_loader_flash_finish(bool reboot);
* - ESP_LOADER_ERROR_UNSUPPORTED_CHIP The target flash chip is not known
*/
esp_loader_error_t esp_loader_flash_detect_size(uint32_t *flash_size);

/**
* @brief Connects to the stub running on the target
*
* @param connect_args[in] Timing parameters to be used for connecting to target.
*
* @return
* - ESP_LOADER_SUCCESS Success
* - ESP_LOADER_ERROR_TIMEOUT Timeout
* - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
*/
esp_loader_error_t esp_loader_connect_to_stub(esp_loader_connect_args_t *connect_args);

/**
* @brief Change baud rate of the stub running on the target
*
* @note Baud rate has to be also adjusted accordingly on host MCU, as
* target's baud rate is changed upon return from this function.
*
* @param new_transmission_rate[in] new baud rate to be set.
* @param old_transmission_rate[in] old baud rate to be replaced.
*
* @return
* - ESP_LOADER_SUCCESS Success
* - ESP_LOADER_ERROR_TIMEOUT Timeout
* - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
* - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target
*/
esp_loader_error_t esp_loader_change_transmission_rate_stub(uint32_t new_transmission_rate, uint32_t old_transmission_rate);
#endif /* SERIAL_FLASHER_INTERFACE_UART || SERIAL_FLASHER_INTERFACE_USB */


Expand Down
28 changes: 28 additions & 0 deletions private_include/esp_stubs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// auto-generated stubs from esptool v4.7.0

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "esp_loader.h"

#ifdef __cplusplus
extern "C" {
#endif

extern bool esp_stub_running;

#if (defined SERIAL_FLASHER_INTERFACE_UART) || (defined SERIAL_FLASHER_INTERFACE_USB)

typedef struct {
esp_loader_bin_header_t header;
esp_loader_bin_segment_t segments[2];
} esp_stub_t;

extern const esp_stub_t esp_stub[ESP_MAX_CHIP];

#endif

#ifdef __cplusplus
}
#endif
19 changes: 18 additions & 1 deletion private_include/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ extern "C" {

#define MD5_SIZE 32

// Maximum block sized for RAM and Flash writes, respectively.
#define ESP_RAM_BLOCK 0x1800

#ifndef MAX
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
#endif

#ifndef MIN
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
#endif

#ifndef ROUNDUP
#define ROUNDUP(a, b) (((int)a + (int)b - 1) / (int)b)
#endif

typedef enum __attribute__((packed))
{
FLASH_BEGIN = 0x02,
Expand Down Expand Up @@ -211,6 +226,8 @@ esp_loader_error_t loader_spi_attach_cmd(uint32_t config);
esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_out);

esp_loader_error_t loader_spi_parameters(uint32_t total_size);

esp_loader_error_t loader_run_stub(target_chip_t target);
#endif /* SERIAL_FLASHER_INTERFACE_UART || SERIAL_FLASHER_INTERFACE_USB */

esp_loader_error_t loader_mem_begin_cmd(uint32_t offset, uint32_t size, uint32_t blocks_to_write, uint32_t block_size);
Expand All @@ -223,7 +240,7 @@ esp_loader_error_t loader_write_reg_cmd(uint32_t address, uint32_t value, uint32

esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg);

esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate);
esp_loader_error_t loader_change_baudrate_cmd(uint32_t new_baudrate, uint32_t old_baudrate);

#ifdef __cplusplus
}
Expand Down
Loading

0 comments on commit ec1fc06

Please sign in to comment.