mirror of
https://github.com/esp8266/Arduino.git
synced 2025-09-06 19:08:12 +03:00
* Allow GZIP compressed flash updates Modified the bootloader to be able to take stored updates in compressed GZIP format (i.e. the output of "gzip -9 xxx.bin") and decompress them on-the-fly to their final destination. This can work for apps and for filesystems (when used with the 2-step update option). Allow eboot to be built using -Os/2 optimizations by fixing some portions which failed when any optimizations were used. Add -Wall and use data and function sections to reduce size. Use -Os to minimize size. Remove obsolete esptool-ck calls to build a .ROM image, we don't use it. Move all uninitted variables to RAM from IRAM, allowing 8-bit access. Hook in @d-a-v and @pfalcon's uzlib port to actually do the decompression. Do not use any CRC checking which saves space. Since we have overwritten all of flash by the time we know id the CRC matches, there's nothing we could have done anyway. Adjust the Updater class to support GZIP files and not attempt to patch them. Bootloader builds to 0xd90 out of 0xfff bytes. * Add @d-a-v's patch for httpupdate https://github.com/esp8266/Arduino/pull/6820#pullrequestreview-326541014 * Update uzlib to point to pfalcon++ For now, because there are some self-test failures with @d-a-v's esp8266 branch (whose cool new features we don't actually use in eboot now) start with pfalcon's 2.9 release and add the 2 patches (clcidx to code from IRAM/RODATA, and the Windows test file renaming) needed to build and run successfully. * Add (c) notice for uzlib to README
244 lines
6.5 KiB
C
244 lines
6.5 KiB
C
/* Copyright (c) 2015-2016 Ivan Grokhotkov. All rights reserved.
|
|
* This file is part of eboot bootloader.
|
|
*
|
|
* Redistribution and use is permitted according to the conditions of the
|
|
* 3-clause BSD license to be found in the LICENSE file.
|
|
*/
|
|
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "flash.h"
|
|
#include "eboot_command.h"
|
|
#include <uzlib.h>
|
|
|
|
extern unsigned char _gzip_dict;
|
|
|
|
#define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0);
|
|
|
|
extern void ets_wdt_enable(void);
|
|
extern void ets_wdt_disable(void);
|
|
|
|
int print_version(const uint32_t flash_addr)
|
|
{
|
|
uint32_t ver;
|
|
if (SPIRead(flash_addr + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t), &ver, sizeof(ver))) {
|
|
return 1;
|
|
}
|
|
char fmt[16];
|
|
fmt[0] = 'v';
|
|
fmt[1] = '%';
|
|
fmt[2] = '0';
|
|
fmt[3] = '8';
|
|
fmt[4] = 'x';
|
|
fmt[5] = '\n';
|
|
fmt[6] = '0';
|
|
ets_printf((const char*) fmt, ver);
|
|
return 0;
|
|
}
|
|
|
|
int load_app_from_flash_raw(const uint32_t flash_addr)
|
|
{
|
|
image_header_t image_header;
|
|
uint32_t pos = flash_addr + APP_START_OFFSET;
|
|
|
|
if (SPIRead(pos, &image_header, sizeof(image_header))) {
|
|
return 1;
|
|
}
|
|
pos += sizeof(image_header);
|
|
|
|
|
|
for (uint32_t section_index = 0;
|
|
section_index < image_header.num_segments;
|
|
++section_index)
|
|
{
|
|
section_header_t section_header = {0};
|
|
if (SPIRead(pos, §ion_header, sizeof(section_header))) {
|
|
return 2;
|
|
}
|
|
pos += sizeof(section_header);
|
|
|
|
const uint32_t address = section_header.address;
|
|
|
|
bool load = false;
|
|
|
|
if (address < 0x40000000) {
|
|
load = true;
|
|
}
|
|
|
|
if (address >= 0x40100000 && address < 0x40108000) {
|
|
load = true;
|
|
}
|
|
|
|
if (address >= 0x60000000) {
|
|
load = true;
|
|
}
|
|
|
|
if (!load) {
|
|
pos += section_header.size;
|
|
continue;
|
|
}
|
|
|
|
if (SPIRead(pos, (void*)address, section_header.size))
|
|
return 3;
|
|
|
|
pos += section_header.size;
|
|
}
|
|
|
|
asm volatile("" ::: "memory");
|
|
asm volatile ("mov.n a1, %0\n"
|
|
"mov.n a3, %1\n"
|
|
"jx a3\n" : : "r" (0x3ffffff0), "r" (image_header.entry) );
|
|
|
|
__builtin_unreachable(); // Save a few bytes by letting GCC know no need to pop regs/return
|
|
return 0;
|
|
}
|
|
|
|
uint8_t read_flash_byte(const uint32_t addr)
|
|
{
|
|
uint8_t __attribute__((aligned(4))) buff[4];
|
|
SPIRead(addr & ~3, buff, 4);
|
|
return buff[addr & 3];
|
|
}
|
|
unsigned char __attribute__((aligned(4))) uzlib_flash_read_cb_buff[4096];
|
|
uint32_t uzlib_flash_read_cb_addr;
|
|
int uzlib_flash_read_cb(struct uzlib_uncomp *m)
|
|
{
|
|
m->source = uzlib_flash_read_cb_buff;
|
|
m->source_limit = uzlib_flash_read_cb_buff + sizeof(uzlib_flash_read_cb_buff);
|
|
SPIRead(uzlib_flash_read_cb_addr, uzlib_flash_read_cb_buff, sizeof(uzlib_flash_read_cb_buff));
|
|
uzlib_flash_read_cb_addr += sizeof(uzlib_flash_read_cb_buff);
|
|
return *(m->source++);
|
|
}
|
|
|
|
unsigned char gzip_dict[32768];
|
|
|
|
int copy_raw(const uint32_t src_addr,
|
|
const uint32_t dst_addr,
|
|
const uint32_t size)
|
|
{
|
|
// require regions to be aligned
|
|
if ((src_addr & 0xfff) != 0 ||
|
|
(dst_addr & 0xfff) != 0) {
|
|
return 1;
|
|
}
|
|
|
|
const uint32_t buffer_size = FLASH_SECTOR_SIZE;
|
|
uint8_t buffer[buffer_size];
|
|
int32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
|
|
uint32_t saddr = src_addr;
|
|
uint32_t daddr = dst_addr;
|
|
struct uzlib_uncomp m_uncomp;
|
|
bool gzip = false;
|
|
|
|
// Check if we are uncompressing a GZIP upload or not
|
|
if ((read_flash_byte(saddr) == 0x1f) && (read_flash_byte(saddr + 1) == 0x8b)) {
|
|
// GZIP signature matched. Find real size as encoded at the end
|
|
left = read_flash_byte(saddr + size - 4);
|
|
left += read_flash_byte(saddr + size - 3)<<8;
|
|
left += read_flash_byte(saddr + size - 2)<<16;
|
|
left += read_flash_byte(saddr + size - 1)<<24;
|
|
|
|
uzlib_init();
|
|
|
|
/* all 3 fields below must be initialized by user */
|
|
m_uncomp.source = NULL;
|
|
m_uncomp.source_limit = NULL;
|
|
uzlib_flash_read_cb_addr = src_addr;
|
|
m_uncomp.source_read_cb = uzlib_flash_read_cb;
|
|
uzlib_uncompress_init(&m_uncomp, gzip_dict, sizeof(gzip_dict));
|
|
|
|
int res = uzlib_gzip_parse_header(&m_uncomp);
|
|
if (res != TINF_OK) {
|
|
return 5; // Error uncompress header read
|
|
}
|
|
gzip = true;
|
|
}
|
|
while (left > 0) {
|
|
if (SPIEraseSector(daddr/buffer_size)) {
|
|
return 2;
|
|
}
|
|
if (!gzip) {
|
|
if (SPIRead(saddr, buffer, buffer_size)) {
|
|
return 3;
|
|
}
|
|
} else {
|
|
m_uncomp.dest_start = buffer;
|
|
m_uncomp.dest = buffer;
|
|
int to_read = (left > buffer_size) ? buffer_size : left;
|
|
m_uncomp.dest_limit = buffer + to_read;
|
|
int res = uzlib_uncompress(&m_uncomp);
|
|
if ((res != TINF_DONE) && (res != TINF_OK)) {
|
|
return 6;
|
|
}
|
|
// Fill any remaining with 0xff
|
|
for (int i = to_read; i < buffer_size; i++) {
|
|
buffer[i] = 0xff;
|
|
}
|
|
}
|
|
if (SPIWrite(daddr, buffer, buffer_size)) {
|
|
return 4;
|
|
}
|
|
saddr += buffer_size;
|
|
daddr += buffer_size;
|
|
left -= buffer_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main()
|
|
{
|
|
int res = 9;
|
|
bool clear_cmd = false;
|
|
struct eboot_command cmd;
|
|
|
|
print_version(0);
|
|
|
|
if (eboot_command_read(&cmd) == 0) {
|
|
// valid command was passed via RTC_MEM
|
|
clear_cmd = true;
|
|
ets_putc('@');
|
|
} else {
|
|
// no valid command found
|
|
cmd.action = ACTION_LOAD_APP;
|
|
cmd.args[0] = 0;
|
|
ets_putc('~');
|
|
}
|
|
|
|
if (cmd.action == ACTION_COPY_RAW) {
|
|
ets_putc('c'); ets_putc('p'); ets_putc(':');
|
|
ets_wdt_disable();
|
|
res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]);
|
|
ets_wdt_enable();
|
|
ets_putc('0'+res); ets_putc('\n');
|
|
if (res == 0) {
|
|
cmd.action = ACTION_LOAD_APP;
|
|
cmd.args[0] = cmd.args[1];
|
|
}
|
|
}
|
|
|
|
if (clear_cmd) {
|
|
eboot_command_clear();
|
|
}
|
|
|
|
if (cmd.action == ACTION_LOAD_APP) {
|
|
ets_putc('l'); ets_putc('d'); ets_putc('\n');
|
|
res = load_app_from_flash_raw(cmd.args[0]);
|
|
//we will get to this only on load fail
|
|
ets_putc('e'); ets_putc(':'); ets_putc('0'+res); ets_putc('\n');
|
|
}
|
|
|
|
if (res) {
|
|
SWRST;
|
|
}
|
|
|
|
while(true){}
|
|
|
|
__builtin_unreachable();
|
|
return 0;
|
|
}
|