esp-open-rtos/extras/rboot-ota/rboot-ota.c
Angus Gratton 147257efa4 Almost functional OTA support
ota_basic example can receive new image via TCP.

However - writing to flash with interrupts disabled causes data loss,
and the TCP flow is very slow to recover. Linux sender quickly ramps up
RTT timer to very long retry intervals, crippling performance &
throughput.

Running the update without the flash writes causes the data to be
received quickly, so this is definitely an issue with the time taken for
the erase cycle.

Progress towards #10
2015-07-29 16:50:23 +10:00

203 lines
6.1 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <espressif/spi_flash.h>
#include <espressif/esp_system.h>
#include <lwip/sockets.h>
#include <FreeRTOS.h>
#include <task.h>
#define SECTOR_SIZE 0x1000
#define BOOT_CONFIG_SECTOR 1
#include "rboot-ota.h"
#define ROM_MAGIC_OLD 0xe9
#define ROM_MAGIC_NEW 0xea
// get the rboot config
rboot_config_t rboot_get_config() {
rboot_config_t conf;
sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)&conf, sizeof(rboot_config_t));
return conf;
}
// write the rboot config
// preserves contents of rest of sector, so rest
// of sector can be used to store user data
// updates checksum automatically, if enabled
bool rboot_set_config(rboot_config_t *conf) {
uint8_t *buffer;
#ifdef RBOOT_CONFIG_CHKSUM
uint8_t chksum;
uint8_t *ptr;
#endif
buffer = (uint8_t*)malloc(SECTOR_SIZE);
if (!buffer) {
printf("Failed to allocate sector buffer\r\n");
return false;
}
#ifdef BOOT_CONFIG_CHKSUM
chksum = CHKSUM_INIT;
for (ptr = (uint8_t*)conf; ptr < &conf->chksum; ptr++) {
chksum ^= *ptr;
}
conf->chksum = chksum;
#endif
sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE);
memcpy(buffer, conf, sizeof(rboot_config_t));
vPortEnterCritical();
sdk_spi_flash_erase_sector(BOOT_CONFIG_SECTOR);
vPortExitCritical();
taskYIELD();
vPortEnterCritical();
sdk_spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE);
vPortExitCritical();
free(buffer);
return true;
}
// get current boot rom
uint8_t rboot_get_current_rom() {
rboot_config_t conf;
conf = rboot_get_config();
return conf.current_rom;
}
// set current boot rom
bool rboot_set_current_rom(uint8_t rom) {
rboot_config_t conf;
conf = rboot_get_config();
if (rom >= conf.count) return false;
conf.current_rom = rom;
return rboot_set_config(&conf);
}
int rboot_ota_update(int fd, int slot, bool reboot_now)
{
rboot_config_t conf;
conf = rboot_get_config();
if(slot == -1) {
slot = (conf.current_rom + 1) % RBOOT_MAX_ROMS;
}
size_t sector = conf.roms[slot] / SECTOR_SIZE;
uint8_t *sector_buf = (uint8_t *)malloc(SECTOR_SIZE);
if(!sector_buf) {
printf("Failed to malloc sector buffer\r\n");
return 0;
}
printf("New image going into sector %d @ 0x%lx\r\n", slot, conf.roms[slot]);
/* Read bootloader header */
int r = read(fd, sector_buf, 8);
if(r != 8 || (sector_buf[0] != ROM_MAGIC_OLD && sector_buf[0] != ROM_MAGIC_NEW)) {
printf("Failed to read ESP bootloader header r=%d magic=%d.\r\n", r, sector_buf[0]);
slot = -1;
goto cleanup;
}
/* if we saw a v1.2 header, we can expect a v1.1 header after the first section */
bool in_new_header = (sector_buf[0] == ROM_MAGIC_NEW);
int remaining_sections = sector_buf[1];
size_t offs = 8;
size_t total_read = 8;
size_t left_section = 0; /* Number of bytes left in this section */
while(remaining_sections > 0 || left_section > 0)
{
if(left_section == 0) {
/* No more bytes in this section, read the next section header */
if(offs + (in_new_header ? 16 : 8) > SECTOR_SIZE) {
printf("PANIC: reading section header overflows sector boundary. FIXME.\r\n");
slot = -1;
goto cleanup;
}
if(in_new_header && total_read > 8)
{
/* expecting an "old" style 8 byte image header here, following irom0 */
int r = read(fd, sector_buf+offs, 8);
if(r != 8 || sector_buf[offs] != ROM_MAGIC_OLD) {
printf("Failed to read second flash header r=%d magic=0x%02x.\r\n", r, sector_buf[offs]);
slot = -1;
goto cleanup;
}
/* treat this number as the reliable number of remaining sections */
remaining_sections = sector_buf[offs+1];
in_new_header = false;
}
int r = read(fd, sector_buf+offs, 8);
if(r != 8) {
printf("Failed to read section header.\r\n");
slot = -1;
goto cleanup;
}
offs += 8;
total_read += 8;
left_section = *((uint32_t *)(sector_buf+offs-4));
/* account for padding from the reported section size out to the alignment barrier */
size_t section_align = in_new_header ? 16 : 4;
left_section = (left_section+section_align-1) & ~(section_align-1);
remaining_sections--;
printf("New section @ 0x%x length 0x%x align 0x%x remaining 0x%x\r\n", total_read, left_section, section_align, remaining_sections);
}
size_t bytes_to_read = left_section > (SECTOR_SIZE-offs) ? (SECTOR_SIZE-offs) : left_section;
int r = read(fd, sector_buf+offs, bytes_to_read);
if(r < 0) {
printf("Failed to read from fd\r\n");
slot = -1;
goto cleanup;
}
if(r == 0) {
printf("EOF before end of image remaining_sections=%d this_section=%d\r\n",
remaining_sections, left_section);
slot = -1;
goto cleanup;
}
offs += r;
total_read += r;
left_section -= r;
if(offs == SECTOR_SIZE || (left_section == 0 && remaining_sections == 0)) {
/* sector buffer is full, or we're at the very end, so write sector to flash */
printf("Sector buffer full. Erasing sector 0x%02x\r\n", sector);
sdk_spi_flash_erase_sector(sector);
printf("Erase done.\r\n");
//sdk_spi_flash_write(sector * SECTOR_SIZE, (uint32_t*)sector_buf, SECTOR_SIZE);
printf("Flash done.\r\n");
offs = 0;
sector++;
}
}
/* Done reading image from fd and writing it out */
if(reboot_now)
{
close(fd);
conf.current_rom = slot;
rboot_set_config(&conf);
sdk_system_restart();
}
cleanup:
free(sector_buf);
return slot;
}