* SPDX-FileCopyrightText: 2016 Cesanta Software Limited
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
* Main flasher stub logic
*
* This stub uses the same SLIP framing and basic command/response structure
* as the in-ROM flasher program, but with some enhanced
* functions and also standardizes the flasher features between different chips.
*
* Actual command handlers are implemented in stub_commands.c
*/
#include <stdlib.h>
#include "stub_flasher.h"
#include "rom_functions.h"
#include "slip.h"
#include "stub_commands.h"
#include "stub_write_flash.h"
#include "stub_io.h"
#include "soc_support.h"
we can read into one buffer while handling data from the other one
(used for flashing throughput.) */
typedef struct {
uint8_t buf_a[MAX_WRITE_BLOCK+64];
uint8_t buf_b[MAX_WRITE_BLOCK+64];
volatile uint8_t *reading_buf;
uint16_t read;
slip_state_t state;
esp_command_req_t *command;
} uart_buf_t;
static volatile uart_buf_t ub;
data payload. */
static uint8_t calculate_checksum(uint8_t *buf, int length)
{
uint8_t res = 0xef;
for(int i = 0; i < length; i++) {
res ^= buf[i];
}
return res;
}
#if USE_MAX_CPU_FREQ
static bool can_use_max_cpu_freq()
{
#if WITH_USB_OTG && !WITH_USB_JTAG_SERIAL
return stub_uses_usb_otg();
#elif !WITH_USB_OTG && WITH_USB_JTAG_SERIAL
return stub_uses_usb_jtag_serial();
#elif WITH_USB_OTG && WITH_USB_JTAG_SERIAL
return stub_uses_usb_otg() || stub_uses_usb_jtag_serial();
#else
return false;
#endif
}
#if ESP32C6 || ESP32H2
static uint32_t pcr_sysclk_conf_reg = 0;
#else
static uint32_t cpu_per_conf_reg = 0;
static uint32_t sysclk_conf_reg = 0;
#endif
static void set_max_cpu_freq()
{
if (can_use_max_cpu_freq())
{
#if ESP32C6 || ESP32H2
pcr_sysclk_conf_reg = READ_REG(PCR_SYSCLK_CONF_REG);
WRITE_REG(PCR_SYSCLK_CONF_REG, (pcr_sysclk_conf_reg & ~PCR_SOC_CLK_SEL_M) | (PCR_SOC_CLK_MAX << PCR_SOC_CLK_SEL_S));
#else
cpu_per_conf_reg = READ_REG(SYSTEM_CPU_PER_CONF_REG);
sysclk_conf_reg = READ_REG(SYSTEM_SYSCLK_CONF_REG);
WRITE_REG(SYSTEM_SYSCLK_CONF_REG, (sysclk_conf_reg & ~SYSTEM_SOC_CLK_SEL_M) | (SYSTEM_SOC_CLK_MAX << SYSTEM_SOC_CLK_SEL_S));
ets_delay_us(100);
WRITE_REG(SYSTEM_CPU_PER_CONF_REG, (cpu_per_conf_reg & ~SYSTEM_CPUPERIOD_SEL_M) | (SYSTEM_CPUPERIOD_MAX << SYSTEM_CPUPERIOD_SEL_S));
#endif
}
}
static void reset_cpu_freq()
{
Use only if set_max_cpu_freq() has been called. */
#if ESP32C6 || ESP32H2
if (can_use_max_cpu_freq() && pcr_sysclk_conf_reg != 0)
{
WRITE_REG(PCR_SYSCLK_CONF_REG, (READ_REG(PCR_SYSCLK_CONF_REG) & ~PCR_SOC_CLK_SEL_M) | (pcr_sysclk_conf_reg & PCR_SOC_CLK_SEL_M));
}
#else
if (can_use_max_cpu_freq() && sysclk_conf_reg != 0 && cpu_per_conf_reg != 0)
{
WRITE_REG(SYSTEM_CPU_PER_CONF_REG, (READ_REG(SYSTEM_CPU_PER_CONF_REG) & ~SYSTEM_CPUPERIOD_SEL_M) | (cpu_per_conf_reg & SYSTEM_CPUPERIOD_SEL_M));
WRITE_REG(SYSTEM_SYSCLK_CONF_REG, (READ_REG(SYSTEM_SYSCLK_CONF_REG) & ~SYSTEM_SOC_CLK_SEL_M) | (sysclk_conf_reg & SYSTEM_SOC_CLK_SEL_M));
}
#endif
}
#endif
#if WITH_USB_JTAG_SERIAL
static void disable_watchdogs()
{
if (stub_uses_usb_jtag_serial())
{
WRITE_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY);
WRITE_REG(RTC_CNTL_WDTCONFIG0_REG, 0x0);
WRITE_REG(RTC_CNTL_WDTWPROTECT_REG, 0x0);
WRITE_REG(RTC_CNTL_SWD_WPROTECT_REG, RTC_CNTL_SWD_WKEY);
REG_SET_MASK(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_AUTO_FEED_EN);
WRITE_REG(RTC_CNTL_SWD_WPROTECT_REG, 0x0);
}
}
#endif
#if ESP32S3 && !ESP32S3BETA2
bool large_flash_mode = false;
bool flash_larger_than_16mb()
{
uint32_t flash_id;
esp_rom_opiflash_exec_cmd(1, SPI_FLASH_FASTRD_MODE,
CMD_RDID, 8,
0, 0,
0,
NULL, 0,
(uint8_t *)&flash_id, 24,
ESP_ROM_OPIFLASH_SEL_CS0,
false);
uint8_t flid_lowbyte = (flash_id >> 16) & 0xFF;
return ((flid_lowbyte >= 0x19 && flid_lowbyte < 0x30) || (flid_lowbyte >= 0x39));
}
#endif
static void stub_handle_rx_byte(char byte)
{
int16_t r = SLIP_recv_byte(byte, (slip_state_t *)&ub.state);
if (r >= 0) {
ub.reading_buf[ub.read++] = (uint8_t) r;
if (ub.read == MAX_WRITE_BLOCK+64) {
r = SLIP_FINISHED_FRAME;
}
}
if (r == SLIP_FINISHED_FRAME) {
to be processed by main thread */
if(ub.reading_buf == ub.buf_a) {
ub.command = (esp_command_req_t *)ub.buf_a;
ub.reading_buf = ub.buf_b;
} else {
ub.command = (esp_command_req_t *)ub.buf_b;
ub.reading_buf = ub.buf_a;
}
ub.read = 0;
}
}
static esp_command_error verify_data_len(esp_command_req_t *command, uint8_t len)
{
return (command->data_len == len) ? ESP_OK : ESP_BAD_DATA_LEN;
}
void cmd_loop() {
while(1) {
while(ub.command == NULL) {
stub_io_idle_hook();
}
esp_command_req_t *command = ub.command;
ub.command = NULL;
uint32_t *data_words = (uint32_t *)command->data_buf;
esp_command_response_t resp = {
.resp = 1,
.op_ret = command->op,
.len_ret = 2,
.value = 0,
};
switch(command->op) {
case ESP_READ_REG:
if (command->data_len == 4) {
resp.value = READ_REG(data_words[0]);
}
break;
case ESP_FLASH_VERIFY_MD5:
resp.len_ret = 16 + 2;
break;
#if ESP32S2_OR_LATER
case ESP_GET_SECURITY_INFO:
resp.len_ret = SECURITY_INFO_BYTES;
break;
#endif
default:
break;
}
SLIP_send_frame_delimiter();
SLIP_send_frame_data_buf(&resp, sizeof(esp_command_response_t));
if(command->data_len > MAX_WRITE_BLOCK+16) {
SLIP_send_frame_data(ESP_BAD_DATA_LEN);
SLIP_send_frame_data(0xEE);
SLIP_send_frame_delimiter();
continue;
}
* in-frame response data between here and when we send the
* status bytes at the end of the frame */
esp_command_error error = ESP_CMD_NOT_IMPLEMENTED;
int status = 0;
switch (command->op) {
case ESP_SYNC:
* the same way so SYNC could be possible with the flasher stub as well. This helps in cases when the chip
* cannot be reset and the flasher stub keeps running. */
error = verify_data_len(command, 36);
if (error == ESP_OK) {
resp.value = 0;
for (int i = 0; i < 7; ++i) {
SLIP_send_frame_data(error);
SLIP_send_frame_data(status);
SLIP_send_frame_delimiter();
SLIP_send_frame_delimiter();
SLIP_send_frame_data_buf(&resp, sizeof(esp_command_response_t));
}
* ended. */
}
break;
#if ESP32S2_OR_LATER
case ESP_GET_SECURITY_INFO:
error = verify_data_len(command, 0) || handle_get_security_info();
break;
#endif
case ESP_ERASE_FLASH:
error = verify_data_len(command, 0) || SPIEraseChip();
break;
case ESP_ERASE_REGION:
error = verify_data_len(command, 8) || handle_flash_erase(data_words[0], data_words[1]);
break;
case ESP_SET_BAUD:
error = verify_data_len(command, 8);
break;
case ESP_READ_FLASH:
error = verify_data_len(command, 16);
break;
case ESP_FLASH_VERIFY_MD5:
but this is in ESP32 ROM so we can't mess with it.
*/
error = verify_data_len(command, 16) || handle_flash_get_md5sum(data_words[0], data_words[1]);
break;
case ESP_FLASH_BEGIN:
0 - erase_size (used as total size to write)
1 - num_blocks (ignored)
2 - block_size (should be MAX_WRITE_BLOCK, relies on num_blocks * block_size >= erase_size)
3 - offset (used as-is)
*/
if (command->data_len == 16 && data_words[2] > MAX_WRITE_BLOCK) {
error = ESP_BAD_BLOCKSIZE;
} else {
error = verify_data_len(command, 16) || handle_flash_begin(data_words[0], data_words[3]);
}
break;
case ESP_FLASH_DEFLATED_BEGIN:
0 - uncompressed size
1 - num_blocks (based on compressed size)
2 - block_size (should be MAX_WRITE_BLOCK, total bytes over serial = num_blocks * block_size)
3 - offset (used as-is)
*/
if (command->data_len == 16 && data_words[2] > MAX_WRITE_BLOCK) {
error = ESP_BAD_BLOCKSIZE;
} else {
error = verify_data_len(command, 16) || handle_flash_deflated_begin(data_words[0], data_words[1] * data_words[2], data_words[3]);
}
break;
case ESP_FLASH_DATA:
case ESP_FLASH_DEFLATED_DATA:
#if !ESP8266
case ESP_FLASH_ENCRYPT_DATA:
#endif
allowing next command to buffer */
if(is_in_flash_mode()) {
error = get_flash_error();
int payload_len = command->data_len - 16;
if (data_words[0] != payload_len) {
error = ESP_BAD_DATA_LEN;
}
uint8_t data_checksum = calculate_checksum(command->data_buf + 16, payload_len);
if (data_checksum != command->checksum) {
error = ESP_BAD_DATA_CHECKSUM;
}
}
else {
error = ESP_NOT_IN_FLASH_MODE;
}
break;
case ESP_FLASH_END:
case ESP_FLASH_DEFLATED_END:
error = handle_flash_end();
break;
case ESP_SPI_SET_PARAMS:
error = verify_data_len(command, 24) || handle_spi_set_params(data_words, &status);
break;
case ESP_SPI_ATTACH:
error = verify_data_len(command, 4) || handle_spi_attach(data_words[0]);
break;
case ESP_WRITE_REG:
if (command->data_len % sizeof(write_reg_args_t) != 0) {
error = ESP_BAD_DATA_LEN;
} else {
error = handle_write_reg((const write_reg_args_t *)data_words, command->data_len/sizeof(write_reg_args_t));
}
break;
case ESP_READ_REG:
error = verify_data_len(command, 4);
break;
case ESP_MEM_BEGIN:
error = verify_data_len(command, 16) || handle_mem_begin(data_words[0], data_words[3]);
break;
case ESP_MEM_DATA:
error = handle_mem_data(command->data_buf + 16, command->data_len - 16);
break;
case ESP_MEM_END:
error = verify_data_len(command, 8) || handle_mem_finish();
break;
case ESP_RUN_USER_CODE:
This command does not send a response.
*/
return;
}
SLIP_send_frame_data(error);
SLIP_send_frame_data(status);
SLIP_send_frame_delimiter();
if (error == ESP_OK) {
switch(command->op) {
case ESP_SET_BAUD:
stub_io_set_baudrate(data_words[1], data_words[0]);
break;
case ESP_READ_FLASH:
handle_flash_read(data_words[0], data_words[1], data_words[2],
data_words[3]);
break;
case ESP_FLASH_DATA:
handle_flash_data(command->data_buf + 16, command->data_len - 16);
break;
#if !ESP8266
case ESP_FLASH_ENCRYPT_DATA:
handle_flash_encrypt_data(command->data_buf + 16, command->data_len -16);
break;
#endif
case ESP_FLASH_DEFLATED_DATA:
handle_flash_deflated_data(command->data_buf + 16, command->data_len - 16);
break;
case ESP_FLASH_DEFLATED_END:
case ESP_FLASH_END:
if (data_words[0] == 0) {
stub_tx_flush();
ets_delay_us(10000);
#if USE_MAX_CPU_FREQ
reset_cpu_freq();
#endif
software_reset();
}
break;
case ESP_MEM_END:
if (data_words[1] != 0) {
void (*entrypoint_fn)(void) = (void (*))data_words[1];
of the UART before we run the new code */
stub_tx_flush();
ets_delay_us(1000);
which exits the loader routine and _then_ calls this
function. But for our purposes so far, having a bit of
extra stuff on the stack doesn't really matter.
*/
#if USE_MAX_CPU_FREQ
reset_cpu_freq();
#endif
entrypoint_fn();
}
break;
}
}
}
}
extern uint32_t _bss_start;
extern uint32_t _bss_end;
void __attribute__((used)) stub_main();
#ifdef ESP8266
__asm__ (
".global stub_main_8266\n"
".literal_position\n"
".align 4\n"
"stub_main_8266:\n"
* a0, so 'return' from here runs user code.
*
* After setting a0, we jump directly to stub_main_inner() which is a
* normal C function
*
* Adapted from similar approach used by Cesanta Software for ESP8266
* flasher stub.
*
*/
"movi a0, 0x400010a8;"
"j stub_main;");
#endif
reset to point to user code. */
void stub_main()
{
const uint32_t greeting = 0x4941484f;
ets_set_user_start(0);
#if USE_MAX_CPU_FREQ
set_max_cpu_freq();
#endif
#if WITH_USB_JTAG_SERIAL
disable_watchdogs();
#endif
for(uint32_t *p = &_bss_start; p < &_bss_end; p++) {
*p = 0;
}
SLIP_send(&greeting, 4);
ub.reading_buf = ub.buf_a;
stub_io_init(&stub_handle_rx_byte);
Can be overriden later by esptool.py. */
#ifdef ESP8266
SelectSpiFunction();
spi_flash_attach();
#else
#if SUPPORT_CONFIG_SPI
uint32_t spiconfig = ets_efuse_get_spiconfig();
#else
uint32_t spiconfig = 0;
#endif
uint32_t strapping = READ_REG(GPIO_STRAP_REG);
set in efuse, assume HSPI flash mode (same as normal boot)
*/
if (spiconfig == 0 && (strapping & 0x1c) == 0x08) {
spiconfig = 1;
}
spi_flash_attach(spiconfig, 0);
#endif
#if ESP32S3 && !ESP32S3BETA2
large_flash_mode = ets_efuse_flash_octal_mode() || flash_larger_than_16mb();
if (large_flash_mode) {
static const esp_rom_opiflash_def_t flash_driver = OPIFLASH_DRIVER();
esp_rom_opiflash_legacy_driver_init(&flash_driver);
esp_rom_opiflash_wait_idle();
}
#endif
SPIParamCfg(0, FLASH_MAX_SIZE, FLASH_BLOCK_SIZE, FLASH_SECTOR_SIZE,
FLASH_PAGE_SIZE, FLASH_STATUS_MASK);
cmd_loop();
#if USE_MAX_CPU_FREQ
reset_cpu_freq();
#endif
return;
}