Working TFTP server based OTA updates
Tested with 16MBit flash configuration, two rboot update slots. Closes #10
This commit is contained in:
		
							parent
							
								
									6887a8119a
								
							
						
					
					
						commit
						19b8383069
					
				
					 8 changed files with 586 additions and 176 deletions
				
			
		
							
								
								
									
										21
									
								
								core/include/esp/rom.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								core/include/esp/rom.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
/* "Boot ROM" function signatures
 | 
			
		||||
 | 
			
		||||
   Note that a lot of the ROM functions used in the IoT SDK aren't
 | 
			
		||||
   referenced from the Espressif RTOS SDK, and are probably incompatible.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _ESP_ROM_H
 | 
			
		||||
#define _ESP_ROM_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
void Cache_Read_Disable(void);
 | 
			
		||||
 | 
			
		||||
/* http://esp8266-re.foogod.com/wiki/Cache_Read_Enable
 | 
			
		||||
 | 
			
		||||
   Note: when compiling non-OTA we use the ROM version of this
 | 
			
		||||
   function, but for OTA we use the version in extras/rboot-ota that
 | 
			
		||||
   maps the correct flash page for OTA support.
 | 
			
		||||
 */
 | 
			
		||||
void Cache_Read_Enable(uint32_t odd_even, uint32_t mb_count, uint32_t no_idea);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -11,73 +11,26 @@
 | 
			
		|||
#include "FreeRTOS.h"
 | 
			
		||||
#include "task.h"
 | 
			
		||||
#include "esp8266.h"
 | 
			
		||||
 | 
			
		||||
#include "ota-tftp.h"
 | 
			
		||||
#include "rboot-ota.h"
 | 
			
		||||
 | 
			
		||||
#include "lwip/err.h"
 | 
			
		||||
#include "lwip/sockets.h"
 | 
			
		||||
#include "lwip/sys.h"
 | 
			
		||||
#include "lwip/netdb.h"
 | 
			
		||||
#include "lwip/dns.h"
 | 
			
		||||
 | 
			
		||||
#define FWPORT 12550
 | 
			
		||||
 | 
			
		||||
#if !defined(WIFI_SSID) || !defined(WIFI_PASS)
 | 
			
		||||
#error "Please define macros WIFI_SSID & WIFI_PASS (here, or better in a local.h file at root level or in program dir."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void simpleOTATask(void *pvParameters)
 | 
			
		||||
{
 | 
			
		||||
    printf("Listening for firmware on port %d...\r\n", FWPORT);
 | 
			
		||||
    int s = socket(AF_INET, SOCK_STREAM, 0);
 | 
			
		||||
    if(s < 0) {
 | 
			
		||||
        printf("... Failed to allocate socket.\r\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct sockaddr_in s_addr = {
 | 
			
		||||
        .sin_family = AF_INET,
 | 
			
		||||
        .sin_addr = {
 | 
			
		||||
            .s_addr = INADDR_ANY,
 | 
			
		||||
        },
 | 
			
		||||
        .sin_port = htons(FWPORT),
 | 
			
		||||
    };
 | 
			
		||||
    if(bind(s, (struct sockaddr *) &s_addr, sizeof(s_addr)) < 0)
 | 
			
		||||
    {
 | 
			
		||||
        printf("... Failed to bind.\r\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(listen(s, 0) == -1) {
 | 
			
		||||
        printf("... Failed to listen.\r\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int client;
 | 
			
		||||
    while((client = accept(s, NULL, NULL)) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        printf("Got new socket. Trying OTA update...\r\n");
 | 
			
		||||
 | 
			
		||||
        int slot = rboot_ota_update(client, -1, false);
 | 
			
		||||
        close(client);
 | 
			
		||||
        if(slot < 0) {
 | 
			
		||||
            printf("OTA update failed. :(.\r\n");
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        printf("OTA succeeded at slot %d!\r\n", slot);
 | 
			
		||||
        //rboot_set_current_rom(slot);
 | 
			
		||||
        //sdk_system_restart();
 | 
			
		||||
    }
 | 
			
		||||
    printf("Failed to accept.\r\n");
 | 
			
		||||
    close(s);
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void user_init(void)
 | 
			
		||||
{
 | 
			
		||||
    sdk_uart_div_modify(0, UART_CLK_FREQ / 115200);
 | 
			
		||||
 | 
			
		||||
    printf("OTA Basic demo. Currently running on slot %d / %d.\r\n",
 | 
			
		||||
           rboot_get_current_rom(), RBOOT_MAX_ROMS);
 | 
			
		||||
    rboot_config_t conf = rboot_get_config();
 | 
			
		||||
    printf("\r\n\r\nOTA Basic demo.\r\nCurrently running on flash slot %d / %d.\r\n\r\n",
 | 
			
		||||
           conf.current_rom, conf.count);
 | 
			
		||||
 | 
			
		||||
    printf("Image addresses in flash:\r\n");
 | 
			
		||||
    for(int i = 0; i <conf.count; i++) {
 | 
			
		||||
        printf("%c%d: offset 0x%08lx\r\n", i == conf.current_rom ? '*':' ', i, conf.roms[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct sdk_station_config config = {
 | 
			
		||||
        .ssid = WIFI_SSID,
 | 
			
		||||
| 
						 | 
				
			
			@ -86,5 +39,5 @@ void user_init(void)
 | 
			
		|||
    sdk_wifi_set_opmode(STATION_MODE);
 | 
			
		||||
    sdk_wifi_station_set_config(&config);
 | 
			
		||||
 | 
			
		||||
    xTaskCreate(simpleOTATask, (signed char *)"simpleOTATask", 512, NULL, 2, NULL);
 | 
			
		||||
    ota_tftp_init_server(TFTP_PORT);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										326
									
								
								extras/rboot-ota/ota-tftp.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								extras/rboot-ota/ota-tftp.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,326 @@
 | 
			
		|||
/* TFTP Server OTA support
 | 
			
		||||
 *
 | 
			
		||||
 * For details of use see ota-tftp.h
 | 
			
		||||
 *
 | 
			
		||||
 * Part of esp-open-rtos
 | 
			
		||||
 * Copyright (C) 2015 Superhouse Automation Pty Ltd
 | 
			
		||||
 * BSD Licensed as described in the file LICENSE
 | 
			
		||||
 */
 | 
			
		||||
#include <FreeRTOS.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <strings.h>
 | 
			
		||||
 | 
			
		||||
#include "lwip/err.h"
 | 
			
		||||
#include "lwip/api.h"
 | 
			
		||||
#include "lwip/sys.h"
 | 
			
		||||
#include "lwip/netdb.h"
 | 
			
		||||
#include "lwip/dns.h"
 | 
			
		||||
#include "lwip/mem.h"
 | 
			
		||||
 | 
			
		||||
#include <netbuf_helpers.h>
 | 
			
		||||
#include <espressif/spi_flash.h>
 | 
			
		||||
#include <espressif/esp_system.h>
 | 
			
		||||
 | 
			
		||||
#include "ota-tftp.h"
 | 
			
		||||
#include "rboot-ota.h"
 | 
			
		||||
 | 
			
		||||
#define TFTP_FIRMWARE_FILE "firmware.bin"
 | 
			
		||||
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
 | 
			
		||||
 | 
			
		||||
#define TFTP_OP_WRQ 2
 | 
			
		||||
#define TFTP_OP_DATA 3
 | 
			
		||||
#define TFTP_OP_ACK 4
 | 
			
		||||
#define TFTP_OP_ERROR 5
 | 
			
		||||
#define TFTP_OP_OACK 6
 | 
			
		||||
 | 
			
		||||
#define TFTP_ERR_FILENOTFOUND 1
 | 
			
		||||
#define TFTP_ERR_FULL 3
 | 
			
		||||
#define TFTP_ERR_ILLEGAL 4
 | 
			
		||||
#define TFTP_ERR_BADID 5
 | 
			
		||||
 | 
			
		||||
#define MAX_IMAGE_SIZE 0x100000 /*1MB images max at the moment */
 | 
			
		||||
 | 
			
		||||
static void tftp_task(void *port_p);
 | 
			
		||||
static char *tftp_get_field(int field, struct netbuf *netbuf);
 | 
			
		||||
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len);
 | 
			
		||||
static err_t tftp_send_ack(struct netconn *nc, int block);
 | 
			
		||||
static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg);
 | 
			
		||||
 | 
			
		||||
void ota_tftp_init_server(int listen_port)
 | 
			
		||||
{
 | 
			
		||||
    xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void tftp_task(void *listen_port)
 | 
			
		||||
{
 | 
			
		||||
    struct netconn *nc = netconn_new (NETCONN_UDP);
 | 
			
		||||
    if(!nc) {
 | 
			
		||||
        printf("OTA TFTP: Failed to allocate socket.\r\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    netconn_bind(nc, IP_ADDR_ANY, (int)listen_port);
 | 
			
		||||
 | 
			
		||||
    /* We expect a WRQ packet with filename "firmware.bin" and "octet" mode,
 | 
			
		||||
    */
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
        /* wait as long as needed for a WRQ packet */
 | 
			
		||||
        netconn_set_recvtimeout(nc, 0);
 | 
			
		||||
        struct netbuf *netbuf;
 | 
			
		||||
        err_t err = netconn_recv(nc, &netbuf);
 | 
			
		||||
        if(err != ERR_OK) {
 | 
			
		||||
            printf("OTA TFTP Error: Failed to receive TFTP initial packet. err=%d\r\n", err);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        uint16_t len = netbuf_len(netbuf);
 | 
			
		||||
        if(len < 6) {
 | 
			
		||||
            printf("OTA TFTP Error: Packet too short for a valid WRQ\r\n");
 | 
			
		||||
            netbuf_delete(netbuf);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint16_t opcode = netbuf_read_u16_n(netbuf, 0);
 | 
			
		||||
        if(opcode != TFTP_OP_WRQ) {
 | 
			
		||||
            printf("OTA TFTP Error: Invalid opcode 0x%04x didn't match WRQ\r\n", opcode);
 | 
			
		||||
            netbuf_delete(netbuf);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* check filename */
 | 
			
		||||
        char *filename = tftp_get_field(0, netbuf);
 | 
			
		||||
        if(!filename || strcmp(filename, TFTP_FIRMWARE_FILE)) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_FILENOTFOUND, "File must be firmware.bin");
 | 
			
		||||
            free(filename);
 | 
			
		||||
            netbuf_delete(netbuf);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        free(filename);
 | 
			
		||||
 | 
			
		||||
        /* check mode */
 | 
			
		||||
        char *mode = tftp_get_field(1, netbuf);
 | 
			
		||||
        if(!mode || strcmp("octet", mode)) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Mode must be octet/binary");
 | 
			
		||||
            free(mode);
 | 
			
		||||
            netbuf_delete(netbuf);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        free(mode);
 | 
			
		||||
 | 
			
		||||
        /* establish a connection back to the sender from this netbuf */
 | 
			
		||||
        netconn_connect(nc, netbuf_fromaddr(netbuf), netbuf_fromport(netbuf));
 | 
			
		||||
        netbuf_delete(netbuf);
 | 
			
		||||
 | 
			
		||||
        /* Find next free slot - this requires flash unmapping so best done when no packets in flight */
 | 
			
		||||
        rboot_config_t conf;
 | 
			
		||||
        conf = rboot_get_config();
 | 
			
		||||
        int slot = (conf.current_rom + 1) % conf.count;
 | 
			
		||||
 | 
			
		||||
        if(slot == conf.current_rom) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Only one OTA slot!");
 | 
			
		||||
            netconn_disconnect(nc);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* ACK the WRQ */
 | 
			
		||||
        int ack_err = tftp_send_ack(nc, 0);
 | 
			
		||||
        if(ack_err != 0) {
 | 
			
		||||
            printf("OTA TFTP initial ACK failed\r\n");
 | 
			
		||||
            netconn_disconnect(nc);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Finished WRQ phase, start TFTP data transfer */
 | 
			
		||||
        size_t received_len;
 | 
			
		||||
        int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len);
 | 
			
		||||
 | 
			
		||||
        netconn_disconnect(nc);
 | 
			
		||||
        printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len);
 | 
			
		||||
        if(recv_err == ERR_OK) {
 | 
			
		||||
            printf("OTA TFTP result valid. Changing slot to %d\r\n", slot);
 | 
			
		||||
            vPortEnterCritical();
 | 
			
		||||
            if(!rboot_set_current_rom(slot)) {
 | 
			
		||||
                printf("OTA TFTP failed to set new rboot slot\r\n");
 | 
			
		||||
            }
 | 
			
		||||
            sdk_system_restart();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return numbered field in a TFTP RRQ/WRQ packet
 | 
			
		||||
 | 
			
		||||
   Uses dest_buf (length dest_len) for temporary storage, so dest_len must be
 | 
			
		||||
   at least as long as the longest valid/expected field.
 | 
			
		||||
 | 
			
		||||
   Result is either NULL if an error occurs, or a newly malloced string that the
 | 
			
		||||
   caller needs to free().
 | 
			
		||||
 */
 | 
			
		||||
static char *tftp_get_field(int field, struct netbuf *netbuf)
 | 
			
		||||
{
 | 
			
		||||
    int offs = 2;
 | 
			
		||||
    int field_offs = 2;
 | 
			
		||||
    int field_len = 0;
 | 
			
		||||
    /* Starting past the opcode, skip all previous fields then find start/len of ours */
 | 
			
		||||
    while(field >= 0 && offs < netbuf_len(netbuf)) {
 | 
			
		||||
        char c = netbuf_read_u8(netbuf, offs++);
 | 
			
		||||
        if(field == 0) {
 | 
			
		||||
            field_len++;
 | 
			
		||||
        }
 | 
			
		||||
        if(c == 0) {
 | 
			
		||||
            field--;
 | 
			
		||||
            if(field == 0)
 | 
			
		||||
                field_offs = offs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(field != -1)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    char * result = malloc(field_len);
 | 
			
		||||
    netbuf_copy_partial(netbuf, result, field_len, field_offs);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len)
 | 
			
		||||
{
 | 
			
		||||
    *received_len = 0;
 | 
			
		||||
    const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */
 | 
			
		||||
    uint32_t start_offs = write_offs;
 | 
			
		||||
    int block = 1;
 | 
			
		||||
 | 
			
		||||
    struct netbuf *netbuf;
 | 
			
		||||
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
        netconn_set_recvtimeout(nc, 10000);
 | 
			
		||||
        err_t err = netconn_recv(nc, &netbuf);
 | 
			
		||||
        if(err == ERR_TIMEOUT) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Timeout");
 | 
			
		||||
            return ERR_TIMEOUT;
 | 
			
		||||
        }
 | 
			
		||||
        else if(err != ERR_OK) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Failed to receive packet");
 | 
			
		||||
            return err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint16_t opcode = netbuf_read_u16_n(netbuf, 0);
 | 
			
		||||
        if(opcode != TFTP_OP_DATA) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Unknown opcode");
 | 
			
		||||
            netbuf_delete(netbuf);
 | 
			
		||||
            return ERR_VAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint16_t client_block = netbuf_read_u16_n(netbuf, 2);
 | 
			
		||||
        if(client_block != block) {
 | 
			
		||||
            netbuf_delete(netbuf);
 | 
			
		||||
            if(client_block == block-1) {
 | 
			
		||||
                /* duplicate block, means our ack got lost */
 | 
			
		||||
                tftp_send_ack(nc, block-1);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Block# out of order");
 | 
			
		||||
                return ERR_VAL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(write_offs % SECTOR_SIZE == 0) {
 | 
			
		||||
            sdk_spi_flash_erase_sector(write_offs / SECTOR_SIZE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* One UDP packet can be more than one netbuf segment, so iterate all the
 | 
			
		||||
           segments in the netbuf and write them to flash
 | 
			
		||||
        */
 | 
			
		||||
        int offset = 0;
 | 
			
		||||
        int len = netbuf_len(netbuf);
 | 
			
		||||
 | 
			
		||||
        if(write_offs + len >= limit_offs) {
 | 
			
		||||
            tftp_send_error(nc, TFTP_ERR_FULL, "Image too large");
 | 
			
		||||
            return ERR_VAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool first_chunk = true;
 | 
			
		||||
        do
 | 
			
		||||
        {
 | 
			
		||||
            uint16_t chunk_len;
 | 
			
		||||
            uint32_t *chunk;
 | 
			
		||||
            netbuf_data(netbuf, (void **)&chunk, &chunk_len);
 | 
			
		||||
            if(first_chunk) {
 | 
			
		||||
                chunk++; /* skip the 4 byte TFTP header */
 | 
			
		||||
                chunk_len -= 4; /* assuming this netbuf chunk is at least 4 bytes! */
 | 
			
		||||
                first_chunk = false;
 | 
			
		||||
            }
 | 
			
		||||
            if(chunk_len && ((uint32_t)chunk % 4)) {
 | 
			
		||||
                /* sdk_spi_flash_write requires a word aligned
 | 
			
		||||
                   buffer, so if the UDP payload is unaligned
 | 
			
		||||
                   (common) then we copy the first word to the stack
 | 
			
		||||
                   and write that to flash, then move the rest of the
 | 
			
		||||
                   buffer internally to sit on an aligned offset.
 | 
			
		||||
 | 
			
		||||
                   Assuming chunk_len is always a multiple of 4 bytes.
 | 
			
		||||
                */
 | 
			
		||||
                uint32_t first_word;
 | 
			
		||||
                memcpy(&first_word, chunk, 4);
 | 
			
		||||
                sdk_spi_flash_write(write_offs+offset, &first_word, 4);
 | 
			
		||||
                memmove(LWIP_MEM_ALIGN(chunk),&chunk[1],chunk_len-4);
 | 
			
		||||
                chunk = LWIP_MEM_ALIGN(chunk);
 | 
			
		||||
                offset += 4;
 | 
			
		||||
                chunk_len -= 4;
 | 
			
		||||
            }
 | 
			
		||||
            sdk_spi_flash_write(write_offs+offset, chunk, chunk_len);
 | 
			
		||||
            offset += chunk_len;
 | 
			
		||||
        } while(netbuf_next(netbuf) >= 0);
 | 
			
		||||
 | 
			
		||||
        netbuf_delete(netbuf);
 | 
			
		||||
 | 
			
		||||
        *received_len += len - 4;
 | 
			
		||||
 | 
			
		||||
        if(len < DATA_PACKET_SZ) {
 | 
			
		||||
            /* This was the last block, but verify the image before we ACK
 | 
			
		||||
               it so the client gets an indication if things were successful.
 | 
			
		||||
            */
 | 
			
		||||
            const char *err = "Unknown validation error";
 | 
			
		||||
            if(!rboot_verify_image(start_offs, *received_len, &err)) {
 | 
			
		||||
                tftp_send_error(nc, TFTP_ERR_ILLEGAL, err);
 | 
			
		||||
                return ERR_VAL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        err_t ack_err = tftp_send_ack(nc, block);
 | 
			
		||||
        if(ack_err != ERR_OK) {
 | 
			
		||||
            printf("OTA TFTP failed to send ACK.\r\n");
 | 
			
		||||
            return ack_err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(len < DATA_PACKET_SZ) {
 | 
			
		||||
            return ERR_OK;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        block++;
 | 
			
		||||
        write_offs += 512;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static err_t tftp_send_ack(struct netconn *nc, int block)
 | 
			
		||||
{
 | 
			
		||||
    /* Send ACK */
 | 
			
		||||
    struct netbuf *resp = netbuf_new();
 | 
			
		||||
    uint16_t *ack_buf = (uint16_t *)netbuf_alloc(resp, 4);
 | 
			
		||||
    ack_buf[0] = htons(TFTP_OP_ACK);
 | 
			
		||||
    ack_buf[1] = htons(block);
 | 
			
		||||
    err_t ack_err = netconn_send(nc, resp);
 | 
			
		||||
    netbuf_delete(resp);
 | 
			
		||||
    return ack_err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg)
 | 
			
		||||
{
 | 
			
		||||
    printf("OTA TFTP Error: %s\r\n", err_msg);
 | 
			
		||||
    struct netbuf *err = netbuf_new();
 | 
			
		||||
    uint16_t *err_buf = (uint16_t *)netbuf_alloc(err, 4+strlen(err_msg)+1);
 | 
			
		||||
    err_buf[0] = htons(TFTP_OP_ERROR);
 | 
			
		||||
    err_buf[1] = htons(err_code);
 | 
			
		||||
    strcpy((char *)&err_buf[2], err_msg);
 | 
			
		||||
    netconn_send(nc, err);
 | 
			
		||||
    netbuf_delete(err);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								extras/rboot-ota/ota-tftp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								extras/rboot-ota/ota-tftp.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
#ifndef _OTA_TFTP_H
 | 
			
		||||
#define _OTA_TFTP_H
 | 
			
		||||
/* TFTP Server OTA Support
 | 
			
		||||
 *
 | 
			
		||||
 * To use, call ota_tftp_init_server() which will start the TFTP server task
 | 
			
		||||
 * and keep it running until a valid image is sent.
 | 
			
		||||
 *
 | 
			
		||||
 * The server expects to see a valid image sent with filename "filename.bin"
 | 
			
		||||
 * and will switch "slots" and reboot if a valid image is received.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that the server will allow you to flash an "OTA" update that doesn't
 | 
			
		||||
 * support OTA itself, and possibly brick the esp requiring serial upload.
 | 
			
		||||
 *
 | 
			
		||||
 * Example client comment:
 | 
			
		||||
 * tftp -m octet ESP_IP -c put firmware/myprogram.bin firmware.bin
 | 
			
		||||
 *
 | 
			
		||||
 * TFTP protocol implemented as per RFC1350:
 | 
			
		||||
 * https://tools.ietf.org/html/rfc1350
 | 
			
		||||
 *
 | 
			
		||||
 * IMPORTANT: TFTP is not a secure protocol.
 | 
			
		||||
 * Only allow TFTP OTA updates on trusted networks.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Start a FreeRTOS task to wait to receive an OTA update from a TFTP client.
 | 
			
		||||
 *
 | 
			
		||||
 * listen_port is the UDP port number to receive TFTP connections on (default TFTP port is 69)
 | 
			
		||||
 */
 | 
			
		||||
void ota_tftp_init_server(int listen_port);
 | 
			
		||||
 | 
			
		||||
#define TFTP_PORT 69
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										78
									
								
								extras/rboot-ota/rboot-cache.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								extras/rboot-ota/rboot-cache.S
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* OTA-compatible Cache_Read_Enable function
 | 
			
		||||
 *
 | 
			
		||||
 * This gets called from the SDK when it wants to enable the flash mapping.
 | 
			
		||||
 *
 | 
			
		||||
 * In recent SDK versions it's been replaced with a Cache_Read_Enable_New
 | 
			
		||||
 * function that takes note of OTA stuff.
 | 
			
		||||
 *
 | 
			
		||||
 * For esp-open-rtos we just replace the ROM function with this wrapper.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * Part of esp-open-rtos
 | 
			
		||||
 * Copyright (C) 2015 Superhouse Automation Pty Ltd
 | 
			
		||||
 * BSD Licensed as described in the file LICENSE
 | 
			
		||||
*/
 | 
			
		||||
#ifdef OTA
 | 
			
		||||
 | 
			
		||||
#define RBOOT_CONFIG_BASE (0x40200000 + 0x1000)
 | 
			
		||||
#define RBOOT_ROMS_OFFS 0x8 /* offset of rboot_config_t.roms array in config */
 | 
			
		||||
 | 
			
		||||
#define RBOOT_MEGABYTE_DEFAULT 0x80
 | 
			
		||||
 | 
			
		||||
	.section .data
 | 
			
		||||
	.global rboot_megabyte
 | 
			
		||||
rboot_megabyte:
 | 
			
		||||
	.byte RBOOT_MEGABYTE_DEFAULT
 | 
			
		||||
 | 
			
		||||
	.section .data
 | 
			
		||||
	.local cache_return_save
 | 
			
		||||
	.align 4
 | 
			
		||||
cache_return_save:
 | 
			
		||||
	.word 0
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.section .iram1.text, "x"
 | 
			
		||||
	.literal_position
 | 
			
		||||
        .align  4
 | 
			
		||||
        .global Cache_Read_Enable
 | 
			
		||||
        .type   Cache_Read_Enable, @function /* it's not really a function, but treat it like one */
 | 
			
		||||
Cache_Read_Enable:
 | 
			
		||||
	movi a2, cache_return_save
 | 
			
		||||
	s32i a0, a2, 0 /* save a0 here */
 | 
			
		||||
	movi a2, rboot_megabyte
 | 
			
		||||
	l8ui a2, a2, 0
 | 
			
		||||
	bnei a2, RBOOT_MEGABYTE_DEFAULT, .Lalready_initialised
 | 
			
		||||
 | 
			
		||||
	/* map the first megabyte of flash */
 | 
			
		||||
	movi a2, 0
 | 
			
		||||
	movi a3, 0
 | 
			
		||||
	movi a4, 1
 | 
			
		||||
	call0 rom_Cache_Read_Enable
 | 
			
		||||
 | 
			
		||||
	movi a3, RBOOT_CONFIG_BASE
 | 
			
		||||
	l32i a2, a3, 0 	      /* 32-bit flash read */
 | 
			
		||||
	extui a2, a2, 24, 8   /* 3rd byte is 'current' field of config */
 | 
			
		||||
	slli a2, a2, 2        /* Left shift by two, becomes offset into rom array */
 | 
			
		||||
	add a4, a2, a3	      /* Add the base config address */
 | 
			
		||||
	l32i a4, a4, RBOOT_ROMS_OFFS        /* Read from the ROM array */
 | 
			
		||||
	extui a4, a4, 20, 8   /* now a4 is number of megabytes */
 | 
			
		||||
 | 
			
		||||
	/* save to rboot_megabyte */
 | 
			
		||||
	movi a3, rboot_megabyte
 | 
			
		||||
	s8i a4, a3, 0
 | 
			
		||||
 | 
			
		||||
	/* re-disable cache? */
 | 
			
		||||
	call0 Cache_Read_Disable
 | 
			
		||||
 | 
			
		||||
.Lalready_initialised:
 | 
			
		||||
	movi a4, rboot_megabyte
 | 
			
		||||
	l32i a4, a4, 0
 | 
			
		||||
	extui a2, a4, 0, 1 /* a2 is now lsb of a4 (odd/even) */
 | 
			
		||||
	srli a3, a4, 1 	   /* a3 is half value of mb */
 | 
			
		||||
	movi a4, 1
 | 
			
		||||
	call0 rom_Cache_Read_Enable
 | 
			
		||||
	movi a0, cache_return_save /* restore a0 return address */
 | 
			
		||||
	l32i a0, a0, 0
 | 
			
		||||
	ret.n
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -7,18 +7,32 @@
 | 
			
		|||
#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
 | 
			
		||||
#define CHECKSUM_INIT 0xef
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
#define RBOOT_DEBUG(f_, ...) printf((f_), __VA_ARGS__)
 | 
			
		||||
#else
 | 
			
		||||
#define RBOOT_DEBUG(f_, ...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct __attribute__((packed)) {
 | 
			
		||||
    uint8_t magic;
 | 
			
		||||
    uint8_t section_count;
 | 
			
		||||
    uint8_t val[2]; /* flash size & speed when placed @ offset 0, I think ignored otherwise */
 | 
			
		||||
    uint32_t entrypoint;
 | 
			
		||||
} image_header_t;
 | 
			
		||||
 | 
			
		||||
typedef struct __attribute__((packed)) {
 | 
			
		||||
    uint32_t load_addr;
 | 
			
		||||
    uint32_t length;
 | 
			
		||||
} section_header_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// get the rboot config
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +55,7 @@ bool rboot_set_config(rboot_config_t *conf) {
 | 
			
		|||
 | 
			
		||||
	buffer = (uint8_t*)malloc(SECTOR_SIZE);
 | 
			
		||||
	if (!buffer) {
 | 
			
		||||
		printf("Failed to allocate sector buffer\r\n");
 | 
			
		||||
		printf("rboot_set_config: Failed to allocate sector buffer\r\n");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -82,122 +96,105 @@ bool rboot_set_current_rom(uint8_t rom) {
 | 
			
		|||
	return rboot_set_config(&conf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rboot_ota_update(int fd, int slot, bool reboot_now)
 | 
			
		||||
// Check that a valid-looking rboot image is found at this offset on the flash, and
 | 
			
		||||
// takes up 'expected_length' bytes.
 | 
			
		||||
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
 | 
			
		||||
{
 | 
			
		||||
    rboot_config_t conf;
 | 
			
		||||
    conf = rboot_get_config();
 | 
			
		||||
 | 
			
		||||
    if(slot == -1) {
 | 
			
		||||
        slot = (conf.current_rom + 1) % RBOOT_MAX_ROMS;
 | 
			
		||||
    char *error = NULL;
 | 
			
		||||
    if(offset % 4) {
 | 
			
		||||
        error = "Unaligned flash offset";
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    uint32_t end_offset = offset + expected_length;
 | 
			
		||||
    image_header_t image_header;
 | 
			
		||||
    sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
 | 
			
		||||
    offset += sizeof(image_header_t);
 | 
			
		||||
 | 
			
		||||
    if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
 | 
			
		||||
        error = "Missing initial magic";
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    printf("New image going into sector %d @ 0x%lx\r\n", slot, conf.roms[slot]);
 | 
			
		||||
    bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
 | 
			
		||||
 | 
			
		||||
    /* 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 = image_header.section_count;
 | 
			
		||||
 | 
			
		||||
    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 */
 | 
			
		||||
    uint8_t checksum = CHECKSUM_INIT;
 | 
			
		||||
 | 
			
		||||
    while(remaining_sections > 0 || left_section > 0)
 | 
			
		||||
    while(remaining_sections > 0 && offset < end_offset)
 | 
			
		||||
    {
 | 
			
		||||
        if(left_section == 0) {
 | 
			
		||||
            /* No more bytes in this section, read the next section header */
 | 
			
		||||
        /* read section header */
 | 
			
		||||
        section_header_t header;
 | 
			
		||||
        sdk_spi_flash_read(offset, (uint32_t *)&header, sizeof(section_header_t));
 | 
			
		||||
        RBOOT_DEBUG("Found section @ 0x%08lx length 0x%08lx load 0x%08lx padded 0x%08lx\r\n", offset, header.length, header.load_addr, header.length);
 | 
			
		||||
        offset += sizeof(section_header_t);
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
        if(header.length+offset > end_offset) {
 | 
			
		||||
            break; /* sanity check: will reading section take us off end of expected flashregion? */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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(!is_new_header) {
 | 
			
		||||
            /* Add individual data of the section to the checksum. */
 | 
			
		||||
            char chunk[16];
 | 
			
		||||
            for(int i = 0; i < header.length; i++) {
 | 
			
		||||
                if(i % sizeof(chunk) == 0)
 | 
			
		||||
                    sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
 | 
			
		||||
                checksum ^= chunk[i % sizeof(chunk)];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        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++;
 | 
			
		||||
        offset += header.length;
 | 
			
		||||
        /* pad section to 4 byte align */
 | 
			
		||||
        offset = (offset+3) & ~3;
 | 
			
		||||
 | 
			
		||||
        remaining_sections--;
 | 
			
		||||
 | 
			
		||||
        if(is_new_header) {
 | 
			
		||||
            /* pad to a 16 byte offset */
 | 
			
		||||
            offset = (offset+15) & ~15;
 | 
			
		||||
 | 
			
		||||
            /* expect a v1.1 header here at start of "real" sections */
 | 
			
		||||
            sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
 | 
			
		||||
            offset += sizeof(image_header_t);
 | 
			
		||||
            if(image_header.magic != ROM_MAGIC_OLD) {
 | 
			
		||||
                error = "Bad second magic";
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
            remaining_sections = image_header.section_count;
 | 
			
		||||
            is_new_header = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 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();
 | 
			
		||||
    if(remaining_sections > 0) {
 | 
			
		||||
        error = "Image truncated";
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 cleanup:
 | 
			
		||||
    free(sector_buf);
 | 
			
		||||
    return slot;
 | 
			
		||||
    /* add a byte for the image checksum (actually comes after the padding) */
 | 
			
		||||
    offset++;
 | 
			
		||||
    /* pad the image length to a 16 byte boundary */
 | 
			
		||||
    offset = (offset+15) & ~15;
 | 
			
		||||
 | 
			
		||||
    uint8_t read_checksum;
 | 
			
		||||
    sdk_spi_flash_read(offset-1, (uint32_t *)&read_checksum, 1); /* not sure if this will work */
 | 
			
		||||
    if(read_checksum != checksum) {
 | 
			
		||||
        error = "Invalid checksum";
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(offset != end_offset) {
 | 
			
		||||
        error = "Wrong length";
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08lx bytes.\r\n", expected_length);
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
 fail:
 | 
			
		||||
    if(error_message)
 | 
			
		||||
        *error_message = error;
 | 
			
		||||
    printf("%s: %s\r\n", __func__, error);
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
#ifndef __RBOOT_OTA_H__
 | 
			
		||||
#define __RBOOTOTA_H__
 | 
			
		||||
#define __RBOOT_OTA_H__
 | 
			
		||||
/* rboot-ota client API
 | 
			
		||||
 *
 | 
			
		||||
 * Ported from https://github.com/raburton/esp8266/ to esp-open-rtos
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
 * Copyright (c) 2015 Richard A Burton & SuperHouse Pty Ltd
 | 
			
		||||
 */
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <rboot-config.h>
 | 
			
		||||
 | 
			
		||||
/* rboot config block structure (stored in flash offset 0x1000)
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +31,9 @@ typedef struct {
 | 
			
		|||
} rboot_config_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define SECTOR_SIZE 0x1000
 | 
			
		||||
#define BOOT_CONFIG_SECTOR 1
 | 
			
		||||
 | 
			
		||||
// timeout for the initial connect (in ms)
 | 
			
		||||
#define OTA_CONNECT_TIMEOUT  10000
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,21 +46,10 @@ typedef struct {
 | 
			
		|||
 | 
			
		||||
#define FLASH_BY_ADDR 0xff
 | 
			
		||||
 | 
			
		||||
/* Perform an OTA update.
 | 
			
		||||
 *
 | 
			
		||||
 * * 'fd' is the file descriptor to read the OTA image from.
 | 
			
		||||
 * * 'slot' is the slot to update, or -1 to automatically update next slot.
 | 
			
		||||
 * * 'reboot_now' means to reboot to the new slot immediately
 | 
			
		||||
 *   (if true, function won't return if successful).
 | 
			
		||||
 *
 | 
			
		||||
 * Returns '0' if the update fails. Returns the newly loaded slot if
 | 
			
		||||
 * reboot_now is false, and the update succeeds so the next restart
 | 
			
		||||
 * will hit the new image.
 | 
			
		||||
 */
 | 
			
		||||
int rboot_ota_update(int fd, int slot, bool reboot_now);
 | 
			
		||||
rboot_config_t rboot_get_config();
 | 
			
		||||
bool rboot_set_config(rboot_config_t *conf);
 | 
			
		||||
uint8_t rboot_get_current_rom();
 | 
			
		||||
bool rboot_set_current_rom(uint8_t rom);
 | 
			
		||||
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,9 +8,18 @@ PROVIDE ( Wait_SPI_Idle = 0x4000448c );
 | 
			
		|||
PROVIDE ( Enable_QMode = 0x400044c0 );
 | 
			
		||||
PROVIDE ( Disable_QMode = 0x40004508 );
 | 
			
		||||
 | 
			
		||||
PROVIDE ( Cache_Read_Enable = 0x40004678 );
 | 
			
		||||
PROVIDE ( rom_Cache_Read_Enable = 0x40004678 );
 | 
			
		||||
PROVIDE ( Cache_Read_Disable = 0x400047f0 );
 | 
			
		||||
 | 
			
		||||
#ifndef OTA
 | 
			
		||||
/* If not building an OTA image for boot, can use the default (simple)
 | 
			
		||||
   cache enable function from ROM.
 | 
			
		||||
 | 
			
		||||
   Otherwise, we need an OTA-aware one (see extras/rboot-ota/rboot-cache.S)
 | 
			
		||||
*/
 | 
			
		||||
Cache_Read_Enable = rom_Cache_Read_Enable;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
PROVIDE ( lldesc_build_chain = 0x40004f40 );
 | 
			
		||||
PROVIDE ( lldesc_num2link = 0x40005050 );
 | 
			
		||||
PROVIDE ( lldesc_set_owner = 0x4000507c );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue