#include "stub_flasher.h"
#include <stdint.h>
#include <string.h>
#include "rom_functions.h"
#if defined(ESP8266)
#include "eagle_soc.h"
#include "ets_sys.h"
#include "miniz.c"
#elif defined(ESP32)
#include "rom/efuse.h"
#include "rom/miniz.h"
#include "rom/spi_flash.h"
#include "soc/uart_reg.h"
#include "led.h"
#endif
#include "slip.h"
#include "uart.h"
uint32_t params[2] __attribute__((section(".params")));
#define FLASH_BLOCK_SIZE 65536
#define FLASH_SECTOR_SIZE 4096
#define FLASH_PAGE_SIZE 256
#define NUM_BUFS 4
#define BUF_SIZE 4096
#define FLASH_WRITE_SIZE BUF_SIZE
#define UART_RX_INTS (UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA)
extern uint32_t _bss_start, _bss_end;
#ifdef ESP8266
#define REG_SPI_BASE(i) (0x60000200 - i * 0x100)
#define SPI_CMD_REG(i) (REG_SPI_BASE(i) + 0x0)
#define SPI_FLASH_WREN (BIT(30))
#define SPI_FLASH_RDID (BIT(28))
#define SPI_FLASH_SE (BIT(24))
#define SPI_FLASH_BE (BIT(23))
#define SPI_ADDR_REG(i) (REG_SPI_BASE(i) + 0x4)
#define SPI_USER_REG(i) (REG_SPI_BASE(i) + 0x1C)
#define SPI_W0_REG(i) (REG_SPI_BASE(i) + 0x40)
#endif
enum read_state {
READ_WAIT_START = 0,
READ_FLAGS,
READ_DATA,
READ_UNESCAPE,
READ_ERROR,
};
struct data_buf {
uint32_t len;
uint8_t data[BUF_SIZE];
uint8_t flags;
};
#define FLAG_COMPRESSED 1
struct uart_buf {
enum read_state state;
struct data_buf bufs[NUM_BUFS];
uint32_t bri, bwi;
uint32_t ps;
};
static inline uint32_t ccount(void) {
uint32_t r;
__asm volatile("rsr.ccount %0" : "=a"(r));
return r;
}
struct write_progress {
uint32_t num_written;
uint32_t buf_level;
uint8_t digest[16];
};
struct write_result {
uint32_t wait_time;
uint32_t decomp_time;
uint32_t write_time;
uint32_t erase_time;
uint32_t total_time;
uint8_t digest[16];
};
static struct uart_buf ub;
static uint32_t inflate_buf[TINFL_LZ_DICT_SIZE / sizeof(uint32_t)];
static void next_write_buf(void) {
ub.bwi++;
if (ub.bwi == NUM_BUFS) ub.bwi = 0;
ub.bufs[ub.bwi].len = 0;
ub.bufs[ub.bwi].flags = 0;
}
static void add_byte(uint8_t byte) {
struct data_buf *buf = &ub.bufs[ub.bwi];
if (buf->len < BUF_SIZE) {
buf->data[buf->len++] = byte;
ub.ps++;
}
}
void uart_isr(void *arg) {
uint32_t int_st = READ_PERI_REG(UART_INT_ST_REG(0));
uint8_t fifo_len, i;
while ((fifo_len = READ_PERI_REG(UART_STATUS_REG(0))) > 0) {
for (i = 0; i < fifo_len; i++) {
uint8_t byte = READ_PERI_REG(UART_FIFO_REG(0));
switch (ub.state) {
case READ_WAIT_START: {
if (byte == 0xc0) {
ub.state = READ_FLAGS;
ub.ps = 0;
}
break;
}
case READ_FLAGS:
case READ_DATA: {
struct data_buf *buf = &ub.bufs[ub.bwi];
if (byte == 0xdb) {
ub.state = READ_UNESCAPE;
} else if (byte == 0xc0) {
next_write_buf();
if (ub.ps == 0) {
ub.state = READ_ERROR;
SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0);
goto out;
} else {
ub.state = READ_WAIT_START;
}
} else {
if (ub.state == READ_FLAGS) {
buf->flags = byte;
ub.state = READ_DATA;
} else {
add_byte(byte);
}
}
break;
}
case READ_UNESCAPE: {
if (byte == 0xdc) {
byte = 0xc0;
} else if (byte == 0xdd) {
byte = 0xdb;
} else {
ub.state = READ_ERROR;
SET_PERI_REG_MASK(UART_INT_ENA_REG(0), 0);
goto out;
}
add_byte(byte);
ub.state = READ_DATA;
break;
}
case READ_ERROR: {
goto out;
}
}
}
}
out:
WRITE_PERI_REG(UART_INT_CLR_REG(0), int_st);
(void) arg;
}
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len,
const void *pSrc_buf, size_t src_buf_len,
int flags);
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
#define TINFL_FLAG_PARSE_ZLIB_HEADER 1
#if defined(ESP8266)
int esp_rom_spiflash_erase_start(uint32_t addr, uint32_t cmd) {
SPI_write_enable(flashchip);
WRITE_PERI_REG(SPI_ADDR_REG(0), addr);
WRITE_PERI_REG(SPI_CMD_REG(0), cmd);
while (READ_PERI_REG(SPI_CMD_REG(0)) & cmd) {
}
return 0;
}
#elif defined(ESP32)
extern esp_rom_spiflash_chip_t g_rom_spiflash_chip;
esp_rom_spiflash_result_t esp_rom_spiflash_erase_start(uint32_t addr,
uint32_t cmd) {
esp_rom_spiflash_chip_t *spi = &g_rom_spiflash_chip;
esp_rom_spiflash_wait_idle(spi);
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN,
ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN);
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WREN);
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0) {
}
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr);
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, cmd);
return ESP_ROM_SPIFLASH_RESULT_OK;
}
#endif
int do_flash_write(uint32_t addr, uint32_t len, uint32_t erase) {
int ret = 0;
uint32_t num_erased = 0;
struct MD5Context ctx;
MD5Init(&ctx);
if (addr % FLASH_SECTOR_SIZE != 0) return 0x32;
if (len % FLASH_SECTOR_SIZE != 0) return 0x33;
if (esp_rom_spiflash_unlock() != 0) return 0x34;
memset(&ub, 0, sizeof(ub));
memset(&inflate_buf, 0, sizeof(inflate_buf));
ets_isr_attach(ETS_UART0_INUM, uart_isr, &ub);
uint32_t saved_conf1 = READ_PERI_REG(UART_CONF1_REG(0));
WRITE_PERI_REG(UART_CONF1_REG(0), UART_RX_TOUT_EN |
(20 << UART_RX_TOUT_THRHD_S) |
(100 << UART_RXFIFO_FULL_THRHD_S));
SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RX_INTS);
ets_isr_unmask(1 << ETS_UART0_INUM);
struct write_result wr;
memset(&wr, 0, sizeof(wr));
struct write_progress wp = {.num_written = 0, .buf_level = 0};
wp.buf_level = (uint32_t) &addr;
SLIP_send(&wp, sizeof(wp));
wr.total_time = ccount();
while (wp.num_written < len) {
uint32_t start_count = ccount();
while (erase && num_erased < wp.num_written + FLASH_WRITE_SIZE) {
const uint32_t num_left = (len - num_erased);
if (num_left >= FLASH_BLOCK_SIZE && addr % FLASH_BLOCK_SIZE == 0) {
if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_BE) != 0) {
ret = 0x35;
goto out;
}
num_erased += FLASH_BLOCK_SIZE;
} else {
if (esp_rom_spiflash_erase_start(addr, SPI_FLASH_SE) != 0) {
ret = 0x36;
goto out;
}
num_erased += FLASH_SECTOR_SIZE;
}
}
wr.erase_time += ccount() - start_count;
start_count = ccount();
wp.buf_level = 0;
for (int i = 0; i < NUM_BUFS; i++) wp.buf_level += ub.bufs[i].len;
volatile uint32_t *bwi = &ub.bwi;
while (*bwi == ub.bri && ub.state != READ_ERROR) {
}
struct data_buf *buf = &ub.bufs[ub.bri];
if (ub.state == READ_ERROR) {
ret = 0x37;
goto out;
}
wr.wait_time += ccount() - start_count;
start_count = ccount();
uint32_t *data = (uint32_t *) buf->data;
uint32_t write_len = buf->len;
if (buf->flags & FLAG_COMPRESSED) {
data = inflate_buf;
write_len = tinfl_decompress_mem_to_mem(
&inflate_buf[0], sizeof(inflate_buf), buf->data, write_len,
TINFL_FLAG_PARSE_ZLIB_HEADER);
if (write_len == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED) {
ret = 0x40;
goto out;
}
}
wr.decomp_time += ccount() - start_count;
MD5Update(&ctx, (uint8_t *) data, write_len);
start_count = ccount();
wr.erase_time += ccount() - start_count;
start_count = ccount();
if (esp_rom_spiflash_write(addr, data, write_len) != 0) {
ret = 0x38;
goto out;
}
wr.write_time += ccount() - start_count;
buf->len = 0;
ub.bri++;
if (ub.bri == NUM_BUFS) ub.bri = 0;
addr += write_len;
wp.num_written += write_len;
struct MD5Context ctx2;
memcpy(&ctx2, &ctx, sizeof(ctx));
MD5Final(wp.digest, &ctx2);
SLIP_send(&wp, sizeof(wp));
}
MD5Final(wr.digest, &ctx);
wr.total_time = ccount() - wr.total_time;
SLIP_send(&wr, sizeof(wr));
out:
WRITE_PERI_REG(UART_CONF1_REG(0), saved_conf1);
ets_isr_mask(1 << ETS_UART0_INUM);
return ret;
}
int do_flash_read(uint32_t addr, uint32_t len, uint32_t block_size,
uint32_t max_in_flight) {
uint8_t buf[FLASH_SECTOR_SIZE];
uint8_t digest[16];
struct MD5Context ctx;
uint32_t num_sent = 0, num_acked = 0;
if (block_size > sizeof(buf)) return 0x52;
MD5Init(&ctx);
while (num_acked < len) {
while (num_sent < len && num_sent - num_acked < max_in_flight) {
uint32_t n = len - num_sent;
if (n > block_size) n = block_size;
if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x53;
send_packet(buf, n);
MD5Update(&ctx, buf, n);
addr += n;
num_sent += n;
}
{
if (SLIP_recv(&num_acked, sizeof(num_acked)) != 4) return 0x54;
if (num_acked > num_sent) return 0x55;
}
}
MD5Final(digest, &ctx);
send_packet(digest, sizeof(digest));
return 0;
}
int do_flash_digest(uint32_t addr, uint32_t len, uint32_t digest_block_size) {
uint8_t buf[FLASH_SECTOR_SIZE];
uint8_t digest[16];
uint32_t read_block_size =
digest_block_size ? digest_block_size : sizeof(buf);
struct MD5Context ctx;
if (digest_block_size > sizeof(buf)) return 0x62;
MD5Init(&ctx);
while (len > 0) {
uint32_t n = len;
struct MD5Context block_ctx;
MD5Init(&block_ctx);
if (n > read_block_size) n = read_block_size;
if (esp_rom_spiflash_read(addr, (uint32_t *) buf, n) != 0) return 0x63;
MD5Update(&ctx, buf, n);
if (digest_block_size > 0) {
MD5Update(&block_ctx, buf, n);
MD5Final(digest, &block_ctx);
send_packet(digest, sizeof(digest));
}
addr += n;
len -= n;
}
MD5Final(digest, &ctx);
send_packet(digest, sizeof(digest));
return 0;
}
int do_flash_read_chip_id(void) {
uint32_t chip_id = 0;
WRITE_PERI_REG(SPI_CMD_REG(0), SPI_FLASH_RDID);
while (READ_PERI_REG(SPI_CMD_REG(0)) & SPI_FLASH_RDID) {
}
chip_id = READ_PERI_REG(SPI_W0_REG(0)) & 0xFFFFFF;
send_packet((uint8_t *) &chip_id, sizeof(chip_id));
return 0;
}
uint8_t cmd_loop(void) {
uint8_t cmd = 0x55;
do {
SET_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST);
CLEAR_PERI_REG_MASK(UART_CONF0_REG(0), UART_RXFIFO_RST);
uint32_t args[4];
uint32_t len = SLIP_recv(&cmd, 1);
if (len != 1) {
continue;
}
uint8_t resp = 0xff;
switch (cmd) {
case CMD_FLASH_WRITE: {
len = SLIP_recv(args, sizeof(args));
if (len == 12) {
resp = do_flash_write(args[0] , args[1] ,
args[2] );
} else {
resp = 0x41;
}
break;
}
case CMD_FLASH_READ: {
len = SLIP_recv(args, sizeof(args));
if (len == 16) {
resp = do_flash_read(args[0] , args[1],
args[2] ,
args[3] );
} else {
resp = 0x51;
}
break;
}
case CMD_FLASH_DIGEST: {
len = SLIP_recv(args, sizeof(args));
if (len == 12) {
resp = do_flash_digest(args[0] , args[1],
args[2] );
} else {
resp = 0x61;
}
break;
}
case CMD_FLASH_READ_CHIP_ID: {
resp = do_flash_read_chip_id();
break;
}
case CMD_FLASH_ERASE_CHIP: {
resp = esp_rom_spiflash_erase_chip();
break;
}
case CMD_BOOT_FW:
case CMD_REBOOT: {
resp = 0;
SLIP_send(&resp, 1);
return cmd;
}
case CMD_ECHO: {
len = SLIP_recv(args, sizeof(args));
SLIP_send(args, len);
resp = 0;
break;
}
case CMD_READ_REG: {
len = SLIP_recv(args, sizeof(args));
if (len == 4) {
uint32_t value = READ_PERI_REG((uint32_t *) args[0]);
SLIP_send(&value, sizeof(value));
resp = 0;
} else {
resp = 0x91;
}
break;
}
case CMD_WRITE_REG: {
len = SLIP_recv(args, sizeof(args));
if (len == 8) {
WRITE_PERI_REG((uint32_t *) args[0], args[1]);
resp = 0;
} else {
resp = 0xa1;
}
break;
}
}
SLIP_send(&resp, 1);
} while (cmd != CMD_BOOT_FW && cmd != CMD_REBOOT);
return cmd;
}
void stub_main1(void) {
uint32_t old_baud_rate = params[0], new_baud_rate = params[1];
uint8_t last_cmd;
ets_set_user_start(0);
#if defined(ESP8266)
SelectSpiFunction();
spi_flash_attach();
SET_PERI_REG_MASK(0x3FF00014, 1);
#elif defined(ESP32)
esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), 0 );
#endif
esp_rom_spiflash_config_param(
0 , 16 * 1024 * 1024 , FLASH_BLOCK_SIZE,
FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, 0xffff );
uint32_t old_div = 0;
if (new_baud_rate > 0) {
ets_delay_us(10000);
old_div = set_baud_rate(0, old_baud_rate, new_baud_rate);
}
ets_delay_us(50000);
#ifdef BAUD_TEST
while (1) {
WRITE_PERI_REG(UART_FIFO_REG(0), 0x55);
}
#else
SLIP_send(&old_div, 4);
#endif
last_cmd = cmd_loop();
ets_delay_us(10000);
if (last_cmd == CMD_BOOT_FW) {
#if defined(ESP8266)
volatile uint32_t *sp = &old_baud_rate;
while (*sp != (uint32_t) 0x40001100) sp++;
*sp = 0x400010a8;
__asm volatile("nop.n");
return;
#elif defined(ESP32)
#endif
} else {
software_reset();
}
}
uint32_t stack[3071];
uint32_t stack_end;
void stub_main(void) {
memset(&_bss_start, 0, (&_bss_end - &_bss_start));
__asm volatile("movi a1, stack_end\n");
stub_main1();
stack[0] = stack_end = 0xff;
}