flashing over http ugh
parent
8fd0d7f01c
commit
674fa669df
|
@ -60,6 +60,8 @@ Debugging
|
|||
|
||||
To print out the serial console from the STM32, run tests/debug_console.py
|
||||
|
||||
To print out the serial console from the ESP8266, run PORT=1 tests/debug_console.py
|
||||
|
||||
Hardware
|
||||
------
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ main: obj/$(PROJ_NAME).bin
|
|||
./tools/dfu-util-$(MACHINE) --reset-stm32 -a 0 -s 0x08000000
|
||||
|
||||
ota: obj/$(PROJ_NAME).bin
|
||||
./tools/ota2.py $<
|
||||
#./tools/ota2.py $<
|
||||
curl http://192.168.0.10/stupdate --upload-file $<
|
||||
|
||||
ifneq ($(wildcard ../.git/HEAD),)
|
||||
obj/gitversion.h: ../.git/HEAD ../.git/index
|
||||
|
|
|
@ -88,17 +88,19 @@ void spi_flasher() {
|
|||
}
|
||||
break;
|
||||
case 0x12:
|
||||
for (i = 0; i < 4; i++) {
|
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
if (spi_rx_buf[2] <= 4) {
|
||||
for (i = 0; i < spi_rx_buf[2]; i++) {
|
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
|
||||
*prog_ptr = *(uint32_t*)(spi_rx_buf+4+(i*4));
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
*prog_ptr = *(uint32_t*)(spi_rx_buf+4+(i*4));
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
|
||||
*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++;
|
||||
*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++;
|
||||
}
|
||||
spi_tx_buf[1] = 0xff;
|
||||
}
|
||||
spi_tx_buf[1] = 0xff;
|
||||
break;
|
||||
case 0x13:
|
||||
// reset
|
||||
|
|
|
@ -41,7 +41,7 @@ def main():
|
|||
# program
|
||||
for i in tqdm(range(0, len(dat), 0x10)):
|
||||
td = dat[i:i+0x10]
|
||||
cmd(sock, 0x12, 0, td)
|
||||
cmd(sock, 0x12, 4, td)
|
||||
ret = sock.recv(0x100)
|
||||
#hexdump(ret)
|
||||
assert ret[0x30:0x40] == td
|
||||
|
|
|
@ -19,14 +19,14 @@ flashall: user1.bin user2.bin
|
|||
proxy-0x00000.bin: proxy
|
||||
./tools/esptool.py elf2image $^
|
||||
|
||||
proxy: proxy.o tcp_ota.o
|
||||
proxy: proxy.o
|
||||
|
||||
proxy.o: proxy.c
|
||||
|
||||
cert.h:
|
||||
../crypto/getcertheader.py ../certs/debugesp.pub ../certs/releaseesp.pub > cert.h
|
||||
|
||||
tcp_ota.o: tcp_ota.c cert.h
|
||||
webserver.o: webserver.c cert.h
|
||||
|
||||
sha.o: ../crypto/sha.c
|
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
@ -37,7 +37,7 @@ rsa.o: ../crypto/rsa.c
|
|||
oldflash: proxy-0x00000.bin
|
||||
./tools/esptool.py write_flash 0 proxy-0x00000.bin 0x40000 proxy-0x40000.bin
|
||||
|
||||
user1.bin: proxy.o tcp_ota.o st_ota.o elm327.o webserver.o sha.o rsa.o
|
||||
user1.bin: proxy.o st_ota.o elm327.o webserver.o sha.o rsa.o
|
||||
$(CC) $(CFLAGS) $^ -o a.out -L$(SDK_BASE)/ld -T$(SDK_BASE)/ld/eagle.app.v6.new.1024.app1.ld $(LDLIBS)
|
||||
$(OBJCP) --only-section .text -O binary a.out eagle.app.v6.text.bin
|
||||
$(OBJCP) --only-section .data -O binary a.out eagle.app.v6.data.bin
|
||||
|
@ -48,7 +48,7 @@ user1.bin: proxy.o tcp_ota.o st_ota.o elm327.o webserver.o sha.o rsa.o
|
|||
mv eagle.app.flash.bin $@
|
||||
../crypto/sign.py $@ $@ $(CERT)
|
||||
|
||||
user2.bin: proxy.o tcp_ota.o st_ota.o elm327.o webserver.o sha.o rsa.o
|
||||
user2.bin: proxy.o st_ota.o elm327.o webserver.o sha.o rsa.o
|
||||
$(CC) $(CFLAGS) $^ -o a.out -L$(SDK_BASE)/ld -T$(SDK_BASE)/ld/eagle.app.v6.new.1024.app2.ld $(LDLIBS)
|
||||
$(OBJCP) --only-section .text -O binary a.out eagle.app.v6.text.bin
|
||||
$(OBJCP) --only-section .data -O binary a.out eagle.app.v6.data.bin
|
||||
|
@ -60,7 +60,8 @@ user2.bin: proxy.o tcp_ota.o st_ota.o elm327.o webserver.o sha.o rsa.o
|
|||
../crypto/sign.py $@ $@ $(CERT)
|
||||
|
||||
ota: user1.bin user2.bin
|
||||
./tools/tcp_flash.py 192.168.0.10 user1.bin user2.bin
|
||||
curl http://192.168.0.10/espupdate1 --upload-file user1.bin
|
||||
curl http://192.168.0.10/espupdate2 --upload-file user2.bin
|
||||
|
||||
clean:
|
||||
rm -f proxy proxy.o proxy-0x00000.bin proxy-0x40000.bin eagle.app.* user1.bin user2.bin a.out *.o cert.h
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* tcp_ota.h: Over The Air (OTA) firmware upgrade via direct TCP/IP connection.
|
||||
*
|
||||
* Author: Ian Marshall
|
||||
* Date: 28/05/2016
|
||||
*/
|
||||
|
||||
#ifndef TCP_OTA_H
|
||||
#define TCP_OTA_H
|
||||
|
||||
/*
|
||||
* Initialises the required connection information to listen for OTA messages.
|
||||
* WiFi must first have been set up for this to succeed.
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR ota_init();
|
||||
|
||||
#endif
|
|
@ -5,8 +5,8 @@
|
|||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
|
||||
#include "tcp_ota.h"
|
||||
#include "driver/spi_interface.h"
|
||||
#include "driver/uart.h"
|
||||
#include "crypto/sha.h"
|
||||
|
||||
#define min(a,b) \
|
||||
|
@ -74,7 +74,7 @@ static int ICACHE_FLASH_ATTR __spi_comm(char *dat, int len, uint32_t *recvData,
|
|||
return length;
|
||||
}
|
||||
|
||||
static int ICACHE_FLASH_ATTR spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen) {
|
||||
int ICACHE_FLASH_ATTR spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen) {
|
||||
// blink the led during SPI comm
|
||||
if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & (1 << pin)) {
|
||||
// set gpio low
|
||||
|
@ -248,13 +248,19 @@ void ICACHE_FLASH_ATTR user_init()
|
|||
gpio_output_set(0, 0, (1 << 5), 0);
|
||||
gpio_output_set((1 << 5), 0, 0, 0);
|
||||
|
||||
// uart init
|
||||
uart_init(BIT_RATE_115200, BIT_RATE_115200);
|
||||
|
||||
// led init
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
|
||||
gpio_output_set(0, (1 << pin), (1 << pin), 0);
|
||||
|
||||
os_printf("hello\n");
|
||||
|
||||
// needs SPI
|
||||
wifi_init();
|
||||
|
||||
// support ota upgrades
|
||||
ota_init();
|
||||
st_ota_init();
|
||||
elm327_init();
|
||||
web_init();
|
||||
|
|
|
@ -1,475 +0,0 @@
|
|||
/*
|
||||
* tcp_ota.c: Over The Air (OTA) firmware upgrade via direct TCP/IP connection.
|
||||
*
|
||||
* NOTE that this does not perform any security checks, so don't rely on this for production use!
|
||||
*
|
||||
* Author: Ian Marshall
|
||||
* Date: 28/05/2016
|
||||
*/
|
||||
#include "ets_sys.h"
|
||||
#include "osapi.h"
|
||||
#include "gpio.h"
|
||||
#include "os_type.h"
|
||||
#include "ip_addr.h"
|
||||
#include "espconn.h"
|
||||
#include "mem.h"
|
||||
#include "spi_flash.h"
|
||||
#include "user_interface.h"
|
||||
#include "upgrade.h"
|
||||
#include "driver/uart.h"
|
||||
#include "espmissingincludes.h"
|
||||
#include "tcp_ota.h"
|
||||
|
||||
#include "crypto/rsa.h"
|
||||
#include "crypto/sha.h"
|
||||
|
||||
#include "cert.h"
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define FIRMWARE_SIZE 503808
|
||||
|
||||
// The TCP port used to listen to for connections.
|
||||
#define OTA_PORT 65056
|
||||
|
||||
// The number of bytes to use for the OTA message buffer (NOT the firmware buffer).
|
||||
#define OTA_BUFFER_LEN 32
|
||||
|
||||
// Structure holding the TCP connection information for the OTA connection.
|
||||
LOCAL struct espconn ota_conn;
|
||||
|
||||
// TCP specific protocol structure for the OTA connection.
|
||||
LOCAL esp_tcp ota_proto;
|
||||
|
||||
// Timer used for rebooting the ESP8266 after an OTA upgrade is complete.
|
||||
LOCAL os_timer_t ota_reboot_timer;
|
||||
|
||||
// Buffer used to hold the new firmware until we have received it all. This is not statically allocated, to avoid
|
||||
// constantly blocking out the memory used, even when no OTA upgrade is in progress.
|
||||
LOCAL uint8_t *ota_firmware = NULL;
|
||||
|
||||
// The total number of bytes expected for the firmware image that is to be flashed.
|
||||
LOCAL uint32_t ota_firmware_size = 0;
|
||||
|
||||
// The total number of bytes received for the firmware image that is to be flashed.
|
||||
LOCAL uint32_t ota_firmware_received = 0;
|
||||
|
||||
// The number of bytes that have currently been received into the "ota_firmware" buffer, which is reset every 4KB.
|
||||
LOCAL uint32_t ota_firmware_len = 0;
|
||||
|
||||
// Buffer used for receiving header information via TCP, allowing the header information to be split over multiple
|
||||
// packets.
|
||||
LOCAL uint8_t ota_buffer[OTA_BUFFER_LEN];
|
||||
|
||||
// The number of bytes currently used in the OTA buffer.
|
||||
LOCAL uint8_t ota_buffer_len = 0;
|
||||
|
||||
// Type used to define the possible status values of OTA upgrades.
|
||||
typedef enum {
|
||||
NOT_STARTED,
|
||||
CONNECTION_ESTABLISHED,
|
||||
RECEIVING_HEADER,
|
||||
RECEIVING_FIRMWARE,
|
||||
REBOOTING,
|
||||
ERROR
|
||||
} ota_state_t;
|
||||
|
||||
// The current status of the OTA flashing. This is required as multiple transmissions will be required to send through
|
||||
// the firmware data.
|
||||
LOCAL ota_state_t ota_state = NOT_STARTED;
|
||||
|
||||
// The IP address of the host sending the OTA data to us. Needed to avoid corruption if two hosts try to OTA upgrade at
|
||||
// the same time.
|
||||
LOCAL uint32_t ota_ip = 0;
|
||||
|
||||
// The TCP port of the host sending the OTA data to us.
|
||||
LOCAL uint16_t ota_port = 0;
|
||||
|
||||
// Forward definitions.
|
||||
LOCAL uint8_t ICACHE_FLASH_ATTR parse_header_line();
|
||||
|
||||
/*
|
||||
* Handles the receiving of information for the OTA update process.
|
||||
*/
|
||||
LOCAL void ICACHE_FLASH_ATTR ota_rx_cb(void *arg, char *data, uint16_t len) {
|
||||
// Store the IP address from the sender of this data.
|
||||
struct espconn *conn = (struct espconn *)arg;
|
||||
uint8_t *addr_array = NULL;
|
||||
addr_array = conn->proto.tcp->remote_ip;
|
||||
ip_addr_t addr;
|
||||
IP4_ADDR(&addr, conn->proto.tcp->remote_ip[0], conn->proto.tcp->remote_ip[1], conn->proto.tcp->remote_ip[2],
|
||||
conn->proto.tcp->remote_ip[3]);
|
||||
if (ota_ip == 0) {
|
||||
// There is no previously stored IP address, so we have it.
|
||||
ota_ip = addr.addr;
|
||||
ota_port = conn->proto.tcp->remote_port;
|
||||
ota_state = CONNECTION_ESTABLISHED;
|
||||
} else if ((ota_ip != addr.addr) || (ota_port != conn->proto.tcp->remote_port)) {
|
||||
// This connection is not the one curently sending OTA data.
|
||||
espconn_send(conn, "ERR: Connection Already Exists\r\n", 32);
|
||||
return;
|
||||
}
|
||||
|
||||
//os_printf("Rx packet - %d bytes, state=%d, size=%d, received=%d, len=%d.\r\n",
|
||||
// len, ota_state, ota_firmware_size, ota_firmware_received, ota_firmware_len);
|
||||
// OTA message sequence:
|
||||
// Rx: "OTA\r\n"
|
||||
// Rx: "GetNextFlash\r\n"
|
||||
// Tx: "user1.bin\r\n" or "user2.bin\r\n", depending on which binary is the next one to be flashed.
|
||||
// Rx: "FirmwareLength: <len>\r\n", where "<len>" is the number of bytes (in ASCII) to be sent in the firmware.
|
||||
// Tx: "Ready\r\n"
|
||||
// Rx: <Firmware>, for "<len>" bytes.
|
||||
// Tx: "Flashing\r\n" or "Invalid\r\n".
|
||||
// Tx: "Rebooting\r\n"
|
||||
uint16_t unbuffered_start = 0;
|
||||
if ((ota_state == CONNECTION_ESTABLISHED) || (ota_state == RECEIVING_HEADER)) {
|
||||
// Store the received bytes into the buffer.
|
||||
for (uint16_t ii = 0; ii < len; ii++) {
|
||||
if (ota_buffer_len < (OTA_BUFFER_LEN - 1)) {
|
||||
ota_buffer[ota_buffer_len++] = data[ii];
|
||||
} else {
|
||||
// The buffer has overflowed, remember where we left off.
|
||||
unbuffered_start = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (ota_state == RECEIVING_FIRMWARE) {
|
||||
// Store received bytes in the firmware buffer.
|
||||
uint32_t copy_len = (uint32_t)len;
|
||||
if ((copy_len + ota_firmware_len) > SPI_FLASH_SEC_SIZE) {
|
||||
copy_len = SPI_FLASH_SEC_SIZE - ota_firmware_len;
|
||||
}
|
||||
if ((copy_len + ota_firmware_received) > ota_firmware_size) {
|
||||
copy_len = ota_firmware_size - ota_firmware_len;
|
||||
}
|
||||
os_memmove(&ota_firmware[ota_firmware_len], data, copy_len);
|
||||
ota_firmware_len += copy_len;
|
||||
ota_firmware_received += copy_len;
|
||||
if (copy_len < len) {
|
||||
unbuffered_start = copy_len;
|
||||
}
|
||||
}
|
||||
|
||||
bool repeat = true;
|
||||
while (repeat) {
|
||||
uint8_t eol = 0;
|
||||
switch (ota_state) {
|
||||
case CONNECTION_ESTABLISHED: {
|
||||
// A connection has just been established. We expect an initial line of "OTA".
|
||||
eol = parse_header_line();
|
||||
if (eol > 0) {
|
||||
// We have a line, it should be "OTA".
|
||||
if (strncmp("OTA", ota_buffer, eol - 2)) {
|
||||
// Oh dear, it's not.
|
||||
espconn_send(conn, "ERR: Invalid protocol\r\n", 23);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
} else {
|
||||
// We do, move to the next line in the header.
|
||||
ota_state = RECEIVING_HEADER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RECEIVING_HEADER: {
|
||||
// We are now receiving header lines.
|
||||
eol = parse_header_line();
|
||||
if (eol > 0) {
|
||||
// We have a line, see what it is.
|
||||
if (!strncmp("GetNextFlash", ota_buffer, eol - 2)) {
|
||||
// The remote device has requested to know what the next flash unit is.
|
||||
uint8_t unit = system_upgrade_userbin_check(); // Note, returns the current unit!
|
||||
if (unit == UPGRADE_FW_BIN1) {
|
||||
espconn_send(conn, "user2.bin\r\n", 11);
|
||||
} else {
|
||||
espconn_send(conn, "user1.bin\r\n", 11);
|
||||
}
|
||||
} else if ((eol > 17) && (!strncmp("FirmwareLength:", ota_buffer, 15))) {
|
||||
// The remote system is preparing to send the firmware. The expected length is supplied here.
|
||||
uint32_t size = 0;
|
||||
for (uint8_t ii = 16; ii < ota_buffer_len; ii++) {
|
||||
if ((ota_buffer[ii] >= '0') && (ota_buffer[ii] <= '9')) {
|
||||
size *= 10;
|
||||
size += ota_buffer[ii] - '0';
|
||||
} else if ((ota_buffer[ii] == '\r') || (ota_buffer[ii] == '\n')) {
|
||||
// We have finished the firmware size.
|
||||
break;
|
||||
} else if ((ota_buffer[ii] != ' ') && (ota_buffer[ii] != ',')) {
|
||||
// Anything that's not a number, space or new-line is invalid.
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
// We either didn't get a length, or the length is invalid.
|
||||
espconn_send(conn, "ERR: Invalid firmware length\r\n", 30);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
} else if (size > FIRMWARE_SIZE) {
|
||||
// The size of the incoming firmware image is too big to fit.
|
||||
espconn_send(conn, "ERR: Firmware length is too big\r\n", 33);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
} else {
|
||||
// Ready to begin flashing!
|
||||
ota_firmware = (uint8_t *)os_malloc(SPI_FLASH_SEC_SIZE);
|
||||
if (ota_firmware == NULL) {
|
||||
espconn_send(conn, "ERR: Unable to allocate OTA buffer.\r\n", 37);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
}
|
||||
ota_firmware_size = size;
|
||||
ota_firmware_received = 0;
|
||||
ota_firmware_len = 0;
|
||||
ota_state = RECEIVING_FIRMWARE;
|
||||
|
||||
// Copy any remaining bytes from the OTA buffer to the firmware buffer.
|
||||
uint8_t remaining = ota_buffer_len - eol - 1;
|
||||
if (remaining > 0) {
|
||||
os_memmove(ota_firmware, &ota_buffer[eol + 1], remaining);
|
||||
ota_firmware_received = ota_firmware_len = (uint32_t)remaining;
|
||||
}
|
||||
|
||||
espconn_send(conn, "Ready\r\n", 7);
|
||||
}
|
||||
} else {
|
||||
// We received an unexpected header line, abort.
|
||||
espconn_send(conn, "ERR: Unexpected header.\r\n", 25);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RECEIVING_FIRMWARE: {
|
||||
// We are now receiving the firmware image.
|
||||
if ((ota_firmware_len == SPI_FLASH_SEC_SIZE) || (ota_firmware_received == ota_firmware_size)) {
|
||||
// We have received a sector's worth of data, or the remainder of the flash image, flash it.
|
||||
if (ota_firmware_received <= SPI_FLASH_SEC_SIZE) {
|
||||
// This is the first block, check the header.
|
||||
if (ota_firmware[0] != 0xEA) {
|
||||
espconn_send(conn, "ERR: IROM magic missing.\r\n", 26);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
} else if ((ota_firmware[1] != 0x04) || (ota_firmware[2] > 0x03) ||
|
||||
((ota_firmware[3] >> 4) > 0x06)) {
|
||||
espconn_send(conn, "ERR: Flash header invalid.\r\n", 28);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
} else if (((uint16_t *)ota_firmware)[3] != 0x4010) {
|
||||
espconn_send(conn, "ERR: Invalid entry address.\r\n", 29);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
} else if (((uint32_t *)ota_firmware)[2] != 0x00000000) {
|
||||
espconn_send(conn, "ERR: Invalid start offset.\r\n", 28);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Zero out any remaining bytes in the last block, to avoid writing dirty data.
|
||||
if (ota_firmware_len < SPI_FLASH_SEC_SIZE) {
|
||||
os_memset(&ota_firmware[ota_firmware_len], 0, SPI_FLASH_SEC_SIZE - ota_firmware_len);
|
||||
}
|
||||
|
||||
// Find out the starting address for the flash write.
|
||||
int address, start_address;
|
||||
uint8_t current = system_upgrade_userbin_check();
|
||||
if (current == UPGRADE_FW_BIN1) {
|
||||
// The next flash, user2.bin, will start after 4KB boot, user1, 16KB user params, 4KB reserved.
|
||||
address = 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024;
|
||||
} else {
|
||||
// The next flash, user1.bin, will start after 4KB boot.
|
||||
address = 4*1024;
|
||||
}
|
||||
start_address = address;
|
||||
address += ota_firmware_received - ota_firmware_len;
|
||||
|
||||
|
||||
// Erase the flash block.
|
||||
if ((address % SPI_FLASH_SEC_SIZE) == 0) {
|
||||
spi_flash_erase_sector(address / SPI_FLASH_SEC_SIZE);
|
||||
}
|
||||
|
||||
// Write the new flash block.
|
||||
//os_printf("Flashing address %05x, total received = %d.\n", address, ota_firmware_received);
|
||||
SpiFlashOpResult res = spi_flash_write(address, (uint32_t *)ota_firmware, SPI_FLASH_SEC_SIZE);
|
||||
ota_firmware_len = 0;
|
||||
if (res != SPI_FLASH_RESULT_OK) {
|
||||
espconn_send(conn, "ERR: Flash failed.\r\n", 20);
|
||||
ota_state = ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ota_firmware_received == ota_firmware_size) {
|
||||
char digest[SHA_DIGEST_SIZE];
|
||||
uint32_t rsa[RSANUMBYTES/4];
|
||||
uint32_t dat[0x80/4];
|
||||
int ll;
|
||||
spi_flash_read(start_address+ota_firmware_size-RSANUMBYTES, rsa, RSANUMBYTES);
|
||||
|
||||
// 32-bit aligned accesses only
|
||||
SHA_CTX ctx;
|
||||
SHA_init(&ctx);
|
||||
for (ll = 0; ll < ota_firmware_size-RSANUMBYTES; ll += 0x80) {
|
||||
spi_flash_read(start_address + ll, dat, 0x80);
|
||||
SHA_update(&ctx, dat, min((ota_firmware_size-RSANUMBYTES)-ll, 0x80));
|
||||
}
|
||||
memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
|
||||
|
||||
/*char buf[0x20];
|
||||
os_sprintf(buf, "%d: %02x %02x %02x %02x", ota_firmware_size-RSANUMBYTES, digest[0], digest[1], digest[2], digest[3]);
|
||||
espconn_send(conn, buf, strlen(buf));*/
|
||||
|
||||
if (RSA_verify(&releaseesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE) ||
|
||||
#ifdef ALLOW_DEBUG
|
||||
RSA_verify(&debugesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE)
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
) {
|
||||
// We've flashed all of the firmware now, reboot into the new firmware.
|
||||
os_printf("Preparing to update firmware.\n");
|
||||
|
||||
espconn_send(conn, "Signature check true. Rebooting in 2s.\r\n", 41);
|
||||
os_free(ota_firmware);
|
||||
ota_firmware_size = 0;
|
||||
ota_firmware_received = 0;
|
||||
ota_firmware_len = 0;
|
||||
ota_state = REBOOTING;
|
||||
system_upgrade_flag_set(UPGRADE_FLAG_FINISH);
|
||||
os_printf("Scheduling reboot.\n");
|
||||
os_timer_disarm(&ota_reboot_timer);
|
||||
os_timer_setfn(&ota_reboot_timer, (os_timer_func_t *)system_upgrade_reboot, NULL);
|
||||
os_timer_arm(&ota_reboot_timer, 2000, 1);
|
||||
} else {
|
||||
espconn_send(conn, "Signature check FAILED. OTA fail.......\r\n", 41);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out the processed bytes from the buffer, if any.
|
||||
repeat = false;
|
||||
if ((ota_state == CONNECTION_ESTABLISHED) || (ota_state == RECEIVING_HEADER)) {
|
||||
// In these states, we're still going to be using the buffer.
|
||||
if (eol < (ota_buffer_len - 1)) {
|
||||
// There are still more characters in the buffer yet to process, move them to the start of the buffer.
|
||||
os_memmove(&ota_buffer[0], &ota_buffer[eol + 1], ota_buffer_len - eol - 1);
|
||||
ota_buffer_len = ota_buffer_len - eol - 1;
|
||||
repeat = true;
|
||||
} else {
|
||||
ota_buffer_len = 0;
|
||||
}
|
||||
|
||||
if (unbuffered_start > 0) {
|
||||
// Store unbuffered bytes to the end of the buffer.
|
||||
for (uint16_t ii = unbuffered_start; ii < len; ii++) {
|
||||
if (ota_buffer_len < (OTA_BUFFER_LEN - 1)) {
|
||||
ota_buffer[ota_buffer_len++] = data[ii];
|
||||
unbuffered_start = 0;
|
||||
repeat = true;
|
||||
} else {
|
||||
// The buffer has overflowed again, remember where we left off.
|
||||
unbuffered_start = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ota_state == RECEIVING_FIRMWARE) {
|
||||
if (unbuffered_start > 0) {
|
||||
// Store unbuffered bytes in the firmware buffer.
|
||||
uint32_t copy_len = (uint32_t)(len - unbuffered_start);
|
||||
if ((copy_len + ota_firmware_len) > SPI_FLASH_SEC_SIZE) {
|
||||
copy_len = SPI_FLASH_SEC_SIZE - ota_firmware_len;
|
||||
}
|
||||
if ((copy_len + ota_firmware_received) > ota_firmware_size) {
|
||||
copy_len = ota_firmware_size - ota_firmware_len;
|
||||
}
|
||||
os_memmove(&ota_firmware[ota_firmware_len], &data[unbuffered_start], copy_len);
|
||||
ota_firmware_len += copy_len;
|
||||
ota_firmware_received += copy_len;
|
||||
if (copy_len < (len - unbuffered_start)) {
|
||||
unbuffered_start += copy_len;
|
||||
} else {
|
||||
unbuffered_start = 0;
|
||||
}
|
||||
repeat = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of bytes in the message buffer for a single header line, or zero if no header is found.
|
||||
LOCAL uint8_t ICACHE_FLASH_ATTR parse_header_line() {
|
||||
for (uint8_t ii = 0; ii < ota_buffer_len - 1; ii++) {
|
||||
if ((ota_buffer[ii] == '\r') && (ota_buffer[ii + 1] == '\n')) {
|
||||
// We have found the end of line markers.
|
||||
return ii + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, we didn't find the end of line markers.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call-back for when a TCP connection has been disconnected.
|
||||
*/
|
||||
LOCAL void ICACHE_FLASH_ATTR ota_disc_cb(void *arg) {
|
||||
// Reset the connection information, if we haven't progressed far enough.
|
||||
if ((ota_state != NOT_STARTED) && (ota_state != REBOOTING)) {
|
||||
ota_ip = 0;
|
||||
ota_port = 0;
|
||||
ota_state = NOT_STARTED;
|
||||
|
||||
ota_buffer_len = 0;
|
||||
if (ota_firmware != NULL) {
|
||||
os_free(ota_firmware);
|
||||
ota_firmware = NULL;
|
||||
ota_firmware_size = 0;
|
||||
ota_firmware_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Call-back for when a TCP connection has failed - reconnected is a misleading name, sadly.
|
||||
*/
|
||||
LOCAL void ICACHE_FLASH_ATTR ota_recon_cb(void *arg, int8_t err) {
|
||||
// Use the disconnect call-back to process this event.
|
||||
ota_disc_cb(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call-back for when an incoming TCP connection has been established.
|
||||
*/
|
||||
LOCAL void ICACHE_FLASH_ATTR ota_tcp_connect_cb(void *arg) {
|
||||
struct espconn *conn = (struct espconn *)arg;
|
||||
os_printf("TCP OTA connection received from "IPSTR":%d\n",
|
||||
IP2STR(conn->proto.tcp->remote_ip), conn->proto.tcp->remote_port);
|
||||
|
||||
// See if this connection is allowed.
|
||||
if (ota_ip == 0) {
|
||||
// Now that we have a connection, register some call-backs.
|
||||
espconn_regist_recvcb(conn, ota_rx_cb);
|
||||
espconn_regist_disconcb(conn, ota_disc_cb);
|
||||
espconn_regist_reconcb(conn, ota_recon_cb);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialises the required connection information to listen for OTA messages.
|
||||
* WiFi must first have been set up for this to succeed.
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR ota_init() {
|
||||
ota_proto.local_port = OTA_PORT;
|
||||
ota_conn.type = ESPCONN_TCP;
|
||||
ota_conn.state = ESPCONN_NONE;
|
||||
ota_conn.proto.tcp = &ota_proto;
|
||||
espconn_regist_connectcb(&ota_conn, ota_tcp_connect_cb);
|
||||
espconn_accept(&ota_conn);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# tcp_flash.py - flashes an ESP8266 microcontroller via 'raw' TCP/IP (not HTTP).
|
||||
#
|
||||
# Usage:
|
||||
# tcp_flash.py <host|IP> <user1.bin> <user2.bin>
|
||||
#
|
||||
# Where:
|
||||
# <host|IP> the hostname or IP address of the ESP8266 to be flashed.
|
||||
# <user1.bin> the file holding the first flash format file. Used when the currently used flash is user2.bin
|
||||
# <user2.bin> the file holding the second flash format file. Used when the currently used flash is user1.bin
|
||||
#
|
||||
# Author: Ian Marshall
|
||||
# Date: 27/05/2016
|
||||
#
|
||||
|
||||
import socket
|
||||
import sys
|
||||
|
||||
PORT=65056
|
||||
|
||||
# Verify the parameters.
|
||||
if len(sys.argv) < 3:
|
||||
print 'Usage: '
|
||||
print ' Usage:'
|
||||
print ' tcp_flash.py <host|IP> <user1.bin> <user2.bin>'
|
||||
print ''
|
||||
print ' Where:'
|
||||
print ' <host|IP> the hostname or IP address of the ESP8266 to be flashed.'
|
||||
print ' <user1.bin> the file holding the first flash format file.'
|
||||
print ' Used when the currently used flash is user2.bin'
|
||||
print ' <user2.bin> the file holding the second flash format file.'
|
||||
print ' Used when the currently used flash is user1.bin'
|
||||
sys.exit(1)
|
||||
|
||||
# Copy the parameters to more descriptive variables.
|
||||
host = sys.argv[1]
|
||||
user1bin = sys.argv[2]
|
||||
user2bin = sys.argv[3]
|
||||
print 'Flashing to "{}"'.format(host)
|
||||
|
||||
# Open the connection to the ESP8266.
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(5);
|
||||
s.connect((host, PORT))
|
||||
|
||||
# Send a request for the correct user bin to be flashed.
|
||||
s.send('OTA\r\nGetNextFlash\r\n')
|
||||
|
||||
# Wait for the reply.
|
||||
f = None
|
||||
response = s.recv(128)
|
||||
if response == "user1.bin\r\n":
|
||||
print 'Flashing \"{}\"...'.format(user1bin)
|
||||
f = open(user1bin, "rb")
|
||||
elif response == "user2.bin\r\n":
|
||||
print 'Flashing \"{}\"...'.format(user2bin)
|
||||
f = open(user2bin, "rb")
|
||||
else:
|
||||
print 'Unknown binary version requested by ESP8266: "{}"'.format(response)
|
||||
sys.exit(2)
|
||||
|
||||
# Read the firmware file.
|
||||
contents = f.read()
|
||||
f.close()
|
||||
|
||||
# Send through the firmware length
|
||||
s.send('FirmwareLength: {}\r\n'.format(len(contents)))
|
||||
|
||||
# Wait until we get the go-ahead.
|
||||
response = s.recv(128)
|
||||
if response != "Ready\r\n":
|
||||
print 'Received response: {}'.format(response)
|
||||
sys.exit(3)
|
||||
|
||||
# Send the firmware.
|
||||
print 'Sending {} bytes of firmware'.format(len(contents))
|
||||
s.sendall(contents)
|
||||
response = s.recv(128)
|
||||
if len(response) > 0:
|
||||
print 'Received response: {}'.format(response)
|
||||
|
||||
# Close the connection, as we're now done.
|
||||
s.close()
|
||||
sys.exit(0)
|
|
@ -1,9 +1,18 @@
|
|||
#include "stdlib.h"
|
||||
#include "ets_sys.h"
|
||||
#include "osapi.h"
|
||||
#include "gpio.h"
|
||||
#include "os_type.h"
|
||||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
#include "upgrade.h"
|
||||
|
||||
#include "crypto/rsa.h"
|
||||
#include "crypto/sha.h"
|
||||
|
||||
#include "cert.h"
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
char staticpage[] = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"
|
||||
"<html><body><tt>This is your comma.ai panda<br/><br/>\n"
|
||||
|
@ -12,13 +21,215 @@ char staticpage[] = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"
|
|||
static struct espconn web_conn;
|
||||
static esp_tcp web_proto;
|
||||
|
||||
LOCAL os_timer_t ota_reboot_timer;
|
||||
|
||||
#define FIRMWARE_SIZE 503808
|
||||
|
||||
typedef enum {
|
||||
NOT_STARTED,
|
||||
CONNECTION_ESTABLISHED,
|
||||
RECEIVING_HEADER,
|
||||
RECEIVING_ST_FIRMWARE,
|
||||
RECEIVING_ESP_FIRMWARE,
|
||||
REBOOTING,
|
||||
ERROR
|
||||
} web_state_t;
|
||||
|
||||
web_state_t state = NOT_STARTED;
|
||||
int content_length = 0;
|
||||
int esp_address, esp_address_erase_limit, start_address;
|
||||
|
||||
void hexdump(char *data, int len) {
|
||||
int i;
|
||||
for (i=0;i<len;i++) {
|
||||
if (i!=0 && (i%0x10)==0) os_printf("\n");
|
||||
os_printf("%02X ", data[i]);
|
||||
}
|
||||
os_printf("\n");
|
||||
}
|
||||
|
||||
void st_reset() {
|
||||
// reset the ST
|
||||
gpio16_output_conf();
|
||||
gpio16_output_set(0);
|
||||
os_delay_us(10000);
|
||||
gpio16_output_set(1);
|
||||
os_delay_us(10000);
|
||||
}
|
||||
|
||||
void st_set_boot_mode(int boot_mode) {
|
||||
if (boot_mode) {
|
||||
// boot mode (pull low)
|
||||
gpio_output_set(0, (1 << 4), (1 << 4), 0);
|
||||
st_reset();
|
||||
} else {
|
||||
// no boot mode (pull high)
|
||||
gpio_output_set((1 << 4), 0, (1 << 4), 0);
|
||||
st_reset();
|
||||
}
|
||||
}
|
||||
|
||||
int st_cmd(int d1, int d2, char *data) {
|
||||
uint32_t __dat[0x14];
|
||||
char *dat = (char *)__dat;
|
||||
memset(dat, 0, 0x14);
|
||||
uint32_t recv[0x44/4];
|
||||
dat[0] = d1;
|
||||
dat[1] = 0xFF^d1;
|
||||
dat[2] = d2;
|
||||
dat[3] = 0xFF^d2;
|
||||
if (data != NULL) memcpy(dat+4, data, 0x10);
|
||||
hexdump(dat, 0x14);
|
||||
|
||||
spi_comm(dat, 0x14, recv, 0x40);
|
||||
return memcmp(recv+0x10, "\xde\xad\xd0\x0d", 4)==0;
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR web_rx_cb(void *arg, char *data, uint16_t len) {
|
||||
int i;
|
||||
struct espconn *conn = (struct espconn *)arg;
|
||||
espconn_send(&web_conn, staticpage, strlen(staticpage));
|
||||
espconn_disconnect(conn);
|
||||
if (state == CONNECTION_ESTABLISHED) {
|
||||
state = RECEIVING_HEADER;
|
||||
os_printf("%s %d\n", data, len);
|
||||
|
||||
// index
|
||||
if (memcmp(data, "GET / ", 6) == 0) {
|
||||
espconn_send(&web_conn, staticpage, strlen(staticpage));
|
||||
espconn_disconnect(conn);
|
||||
} else if (memcmp(data, "PUT /stupdate ", 14) == 0) {
|
||||
os_printf("init st firmware\n");
|
||||
char *cl = strstr(data, "Content-Length: ");
|
||||
if (cl != NULL) {
|
||||
state = RECEIVING_ST_FIRMWARE;
|
||||
|
||||
// get content length
|
||||
cl += strlen("Content-Length: ");
|
||||
content_length = skip_atoi(&cl);
|
||||
os_printf("with content length %d\n", content_length);
|
||||
|
||||
if (content_length > 0 && content_length <= 16384) {
|
||||
// boot mode
|
||||
st_set_boot_mode(1);
|
||||
|
||||
// unlock flash
|
||||
while (!st_cmd(0x10, 0, NULL));
|
||||
|
||||
// erase sector 1
|
||||
st_cmd(0x11, 1, NULL);
|
||||
|
||||
// wait for erase
|
||||
while (!st_cmd(0xf, 0, NULL));
|
||||
}
|
||||
}
|
||||
} else if ((memcmp(data, "PUT /espupdate1 ", 16) == 0) ||
|
||||
(memcmp(data, "PUT /espupdate2 ", 16) == 0)) {
|
||||
// 0x1000 = user1.bin
|
||||
// 0x81000 = user2.bin
|
||||
// 0x3FE000 = blank.bin
|
||||
os_printf("init st firmware\n");
|
||||
char *cl = strstr(data, "Content-Length: ");
|
||||
if (cl != NULL) {
|
||||
// get content length
|
||||
cl += strlen("Content-Length: ");
|
||||
content_length = skip_atoi(&cl);
|
||||
os_printf("with content length %d\n", content_length);
|
||||
|
||||
// setup flashing
|
||||
uint8_t current = system_upgrade_userbin_check();
|
||||
if (data[14] == '2' && current == UPGRADE_FW_BIN1) {
|
||||
os_printf("flashing boot2.bin\n");
|
||||
state = RECEIVING_ESP_FIRMWARE;
|
||||
esp_address = 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024;
|
||||
} else if (data[14] == '1' && current == UPGRADE_FW_BIN2) {
|
||||
os_printf("flashing boot1.bin\n");
|
||||
state = RECEIVING_ESP_FIRMWARE;
|
||||
esp_address = 4*1024;
|
||||
} else {
|
||||
espconn_send(&web_conn, "wrong!\n", 7);
|
||||
espconn_disconnect(conn);
|
||||
}
|
||||
esp_address_erase_limit = esp_address;
|
||||
start_address = esp_address;
|
||||
}
|
||||
} else {
|
||||
espconn_disconnect(conn);
|
||||
}
|
||||
} else if (state == RECEIVING_ST_FIRMWARE) {
|
||||
os_printf("receiving st firmware: %d/%d\n", len, content_length);
|
||||
content_length -= len;
|
||||
|
||||
// TODO: must be 4 bytes aligned
|
||||
for (i = 0; i < len; i += 0x10) {
|
||||
if (len-i < 0x10) {
|
||||
st_cmd(0x12, (len-i)/4, data+i);
|
||||
} else {
|
||||
st_cmd(0x12, 4, data+i);
|
||||
}
|
||||
}
|
||||
if (content_length == 0) {
|
||||
os_printf("done!\n");
|
||||
espconn_send(&web_conn, "success!\n", 9);
|
||||
espconn_disconnect(conn);
|
||||
st_set_boot_mode(0);
|
||||
}
|
||||
} else if (state == RECEIVING_ESP_FIRMWARE) {
|
||||
os_printf("receiving esp firmware: %d/%d -- 0x%x - 0x%x\n", len, content_length,
|
||||
esp_address, esp_address_erase_limit);
|
||||
content_length -= len;
|
||||
while (esp_address_erase_limit < (esp_address + len)) {
|
||||
os_printf("erasing 0x%X\n", esp_address_erase_limit);
|
||||
spi_flash_erase_sector(esp_address_erase_limit / SPI_FLASH_SEC_SIZE);
|
||||
esp_address_erase_limit += SPI_FLASH_SEC_SIZE;
|
||||
}
|
||||
SpiFlashOpResult res = spi_flash_write(esp_address, data, len);
|
||||
if (res != SPI_FLASH_RESULT_OK) {
|
||||
os_printf("flash fail @ 0x%x\n", esp_address);
|
||||
}
|
||||
esp_address += len;
|
||||
|
||||
if (content_length == 0) {
|
||||
char digest[SHA_DIGEST_SIZE];
|
||||
uint32_t rsa[RSANUMBYTES/4];
|
||||
uint32_t dat[0x80/4];
|
||||
int ll;
|
||||
spi_flash_read(esp_address-RSANUMBYTES, rsa, RSANUMBYTES);
|
||||
|
||||
// 32-bit aligned accesses only
|
||||
SHA_CTX ctx;
|
||||
SHA_init(&ctx);
|
||||
for (ll = start_address; ll < esp_address-RSANUMBYTES; ll += 0x80) {
|
||||
spi_flash_read(ll, dat, 0x80);
|
||||
SHA_update(&ctx, dat, min((esp_address-RSANUMBYTES)-ll, 0x80));
|
||||
}
|
||||
memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
|
||||
|
||||
if (RSA_verify(&releaseesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE) ||
|
||||
#ifdef ALLOW_DEBUG
|
||||
RSA_verify(&debugesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE)
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
) {
|
||||
os_printf("RSA verify success!\n");
|
||||
espconn_send(&web_conn, "success!\n", 9);
|
||||
system_upgrade_flag_set(UPGRADE_FLAG_FINISH);
|
||||
|
||||
// reboot
|
||||
os_printf("Scheduling reboot.\n");
|
||||
os_timer_disarm(&ota_reboot_timer);
|
||||
os_timer_setfn(&ota_reboot_timer, (os_timer_func_t *)system_upgrade_reboot, NULL);
|
||||
os_timer_arm(&ota_reboot_timer, 2000, 1);
|
||||
} else {
|
||||
os_printf("RSA verify FAILURE\n");
|
||||
espconn_send(&web_conn, "failure!\n", 9);
|
||||
}
|
||||
espconn_disconnect(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR web_tcp_connect_cb(void *arg) {
|
||||
state = CONNECTION_ESTABLISHED;
|
||||
struct espconn *conn = (struct espconn *)arg;
|
||||
espconn_set_opt(&web_conn, ESPCONN_NODELAY);
|
||||
espconn_regist_recvcb(conn, web_rx_cb);
|
||||
|
|
|
@ -16,12 +16,15 @@ if __name__ == "__main__":
|
|||
pandas = map(lambda x: Panda(x, False), serials)
|
||||
while 1:
|
||||
for i, panda in enumerate(pandas):
|
||||
ret = panda.serial_read(port_number)
|
||||
if len(ret) > 0:
|
||||
sys.stdout.write(setcolor[i] + ret + unsetcolor)
|
||||
sys.stdout.flush()
|
||||
while 1:
|
||||
ret = panda.serial_read(port_number)
|
||||
if len(ret) > 0:
|
||||
sys.stdout.write(setcolor[i] + ret + unsetcolor)
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
break
|
||||
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
|
||||
ln = sys.stdin.readline()
|
||||
panda.serial_write(port_number, ln)
|
||||
time.sleep(0.05)
|
||||
time.sleep(0.01)
|
||||
|
||||
|
|
Loading…
Reference in New Issue