flashing over http ugh

master
George Hotz 2017-04-29 00:09:08 -07:00
parent 8fd0d7f01c
commit 674fa669df
11 changed files with 251 additions and 602 deletions

View File

@ -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
------

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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)