Skip to content

Commit 67ba90d

Browse files
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.
1 parent ebae47c commit 67ba90d

File tree

8 files changed

+120
-34
lines changed

8 files changed

+120
-34
lines changed

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919
[submodule "tools/esptool"]
2020
path = tools/esptool
2121
url = https://github.com/espressif/esptool.git
22+
[submodule "tools/sdk/uzlib"]
23+
path = tools/sdk/uzlib
24+
url = https://github.com/earlephilhower/uzlib.git

Diff for: bootloaders/eboot/Makefile

+19-12
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,51 @@ TARGET_DIR := ./
66

77
TARGET_OBJ_FILES := \
88
eboot.o \
9-
eboot_command.o \
10-
9+
eboot_command.o
1110

1211
TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES))
1312

13+
UZLIB_PATH := ../../tools/sdk/uzlib/src
14+
UZLIB_FLAGS := -DRUNTIME_BITS_TABLES
15+
1416
CC := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-gcc
1517
CXX := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-g++
1618
AR := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-ar
1719
LD := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-gcc
1820
OBJDUMP := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-objdump
1921

20-
INC += -I../../tools/sdk/include
22+
INC += -I../../tools/sdk/include -I../../tools/sdk/uzlib/src
23+
2124
CFLAGS += -std=gnu99
2225

23-
CFLAGS += -O0 -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals
26+
CFLAGS += -Os -g -Wall -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals -ffunction-sections -fdata-sections
2427

2528
CFLAGS += $(INC)
2629

27-
LDFLAGS += -nostdlib -Wl,--no-check-sections -umain
30+
CFLAGS += $(UZLIB_FLAGS)
31+
32+
LDFLAGS += -nostdlib -Wl,--no-check-sections -Wl,--gc-sections -umain
2833

2934
LD_SCRIPT := -Teboot.ld
3035

3136
APP_OUT:= eboot.elf
3237
APP_AR := eboot.a
3338
APP_FW := eboot.bin
3439

35-
all: $(APP_FW)
3640

37-
$(APP_AR): $(TARGET_OBJ_PATHS)
38-
$(AR) cru $@ $^
41+
all: $(APP_OUT)
3942

43+
tinflate.o: $(UZLIB_PATH)/tinflate.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h
44+
$(CC) $(CFLAGS) -c -o tinflate.o $(UZLIB_PATH)/tinflate.c
4045

41-
$(APP_OUT): $(APP_AR)
42-
$(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--whole-archive $(APP_AR) -Wl,--end-group -o $@
46+
tinfgzip.o: $(UZLIB_PATH)/tinfgzip.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h
47+
$(CC) $(CFLAGS) -c -o tinfgzip.o $(UZLIB_PATH)/tinfgzip.c
4348

44-
$(APP_FW): $(APP_OUT)
45-
$(ESPTOOL) -vvv -eo $(APP_OUT) -bo $@ -bs .text -bs .data -bs .rodata -bc -ec || true
49+
$(APP_AR): $(TARGET_OBJ_PATHS) tinflate.o tinfgzip.o
50+
$(AR) cru $@ $^
4651

52+
$(APP_OUT): $(APP_AR)
53+
$(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--whole-archive $(APP_AR) -Wl,--end-group -o $@
4754

4855
clean:
4956
rm -f *.o

Diff for: bootloaders/eboot/eboot.c

+85-17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include <string.h>
1313
#include "flash.h"
1414
#include "eboot_command.h"
15+
#include <uzlib.h>
16+
17+
extern unsigned char _gzip_dict;
1518

1619
#define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0);
1720

@@ -24,10 +27,14 @@ int print_version(const uint32_t flash_addr)
2427
if (SPIRead(flash_addr + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t), &ver, sizeof(ver))) {
2528
return 1;
2629
}
27-
const char* __attribute__ ((aligned (4))) fmtt = "v%08x\n\0\0";
28-
uint32_t fmt[2];
29-
fmt[0] = ((uint32_t*) fmtt)[0];
30-
fmt[1] = ((uint32_t*) fmtt)[1];
30+
char fmt[16];
31+
fmt[0] = 'v';
32+
fmt[1] = '%';
33+
fmt[2] = '0';
34+
fmt[3] = '8';
35+
fmt[4] = 'x';
36+
fmt[5] = '\n';
37+
fmt[6] = '0';
3138
ets_printf((const char*) fmt, ver);
3239
return 0;
3340
}
@@ -80,37 +87,96 @@ int load_app_from_flash_raw(const uint32_t flash_addr)
8087
pos += section_header.size;
8188
}
8289

83-
register uint32_t sp asm("a1") = 0x3ffffff0;
84-
register uint32_t pc asm("a3") = image_header.entry;
85-
__asm__ __volatile__ ("jx a3");
90+
asm volatile("" ::: "memory");
91+
asm volatile ("mov.n a1, %0\n"
92+
"mov.n a3, %1\n"
93+
"jx a3\n" : : "r" (0x3ffffff0), "r" (image_header.entry) );
8694

95+
__builtin_unreachable(); // Save a few bytes by letting GCC know no need to pop regs/return
8796
return 0;
8897
}
8998

99+
uint8_t read_flash_byte(const uint32_t addr)
100+
{
101+
uint8_t __attribute__((aligned(4))) buff[4];
102+
SPIRead(addr & ~3, buff, 4);
103+
return buff[addr & 3];
104+
}
105+
unsigned char __attribute__((aligned(4))) uzlib_flash_read_cb_buff[4096];
106+
uint32_t uzlib_flash_read_cb_addr;
107+
int uzlib_flash_read_cb(struct uzlib_uncomp *m)
108+
{
109+
m->source = uzlib_flash_read_cb_buff;
110+
m->source_limit = uzlib_flash_read_cb_buff + sizeof(uzlib_flash_read_cb_buff);
111+
SPIRead(uzlib_flash_read_cb_addr, uzlib_flash_read_cb_buff, sizeof(uzlib_flash_read_cb_buff));
112+
uzlib_flash_read_cb_addr += sizeof(uzlib_flash_read_cb_buff);
113+
return *(m->source++);
114+
}
90115

116+
unsigned char gzip_dict[32768];
91117

92118
int copy_raw(const uint32_t src_addr,
93119
const uint32_t dst_addr,
94120
const uint32_t size)
95121
{
96122
// require regions to be aligned
97-
if (src_addr & 0xfff != 0 ||
98-
dst_addr & 0xfff != 0) {
123+
if ((src_addr & 0xfff) != 0 ||
124+
(dst_addr & 0xfff) != 0) {
99125
return 1;
100126
}
101127

102128
const uint32_t buffer_size = FLASH_SECTOR_SIZE;
103129
uint8_t buffer[buffer_size];
104-
uint32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
130+
int32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
105131
uint32_t saddr = src_addr;
106132
uint32_t daddr = dst_addr;
107-
108-
while (left) {
133+
struct uzlib_uncomp m_uncomp;
134+
bool gzip = false;
135+
136+
// Check if we are uncompressing a GZIP upload or not
137+
if ((read_flash_byte(saddr) == 0x1f) && (read_flash_byte(saddr + 1) == 0x8b)) {
138+
// GZIP signature matched. Find real size as encoded at the end
139+
left = read_flash_byte(saddr + size - 4);
140+
left += read_flash_byte(saddr + size - 3)<<8;
141+
left += read_flash_byte(saddr + size - 2)<<16;
142+
left += read_flash_byte(saddr + size - 1)<<24;
143+
144+
uzlib_init();
145+
146+
/* all 3 fields below must be initialized by user */
147+
m_uncomp.source = NULL;
148+
m_uncomp.source_limit = NULL;
149+
uzlib_flash_read_cb_addr = src_addr;
150+
m_uncomp.source_read_cb = uzlib_flash_read_cb;
151+
uzlib_uncompress_init(&m_uncomp, gzip_dict, sizeof(gzip_dict));
152+
153+
int res = uzlib_gzip_parse_header(&m_uncomp);
154+
if (res != TINF_OK) {
155+
return 5; // Error uncompress header read
156+
}
157+
gzip = true;
158+
}
159+
while (left > 0) {
109160
if (SPIEraseSector(daddr/buffer_size)) {
110161
return 2;
111162
}
112-
if (SPIRead(saddr, buffer, buffer_size)) {
113-
return 3;
163+
if (!gzip) {
164+
if (SPIRead(saddr, buffer, buffer_size)) {
165+
return 3;
166+
}
167+
} else {
168+
m_uncomp.dest_start = buffer;
169+
m_uncomp.dest = buffer;
170+
int to_read = (left > buffer_size) ? buffer_size : left;
171+
m_uncomp.dest_limit = buffer + to_read;
172+
int res = uzlib_uncompress(&m_uncomp);
173+
if ((res != TINF_DONE) && (res != TINF_OK)) {
174+
return 6;
175+
}
176+
// Fill any remaining with 0xff
177+
for (int i = to_read; i < buffer_size; i++) {
178+
buffer[i] = 0xff;
179+
}
114180
}
115181
if (SPIWrite(daddr, buffer, buffer_size)) {
116182
return 4;
@@ -124,12 +190,11 @@ int copy_raw(const uint32_t src_addr,
124190
}
125191

126192

127-
128-
void main()
193+
int main()
129194
{
130195
int res = 9;
131196
struct eboot_command cmd;
132-
197+
133198
print_version(0);
134199

135200
if (eboot_command_read(&cmd) == 0) {
@@ -167,4 +232,7 @@ void main()
167232
}
168233

169234
while(true){}
235+
236+
__builtin_unreachable();
237+
return 0;
170238
}

Diff for: bootloaders/eboot/eboot.elf

24.1 KB
Binary file not shown.

Diff for: bootloaders/eboot/eboot.ld

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ SECTIONS
6868

6969
.data : ALIGN(4)
7070
{
71+
*(COMMON) /* Global vars */
72+
. = ALIGN(4);
7173
_heap_start = ABSOLUTE(.);
7274
/* _stack_sentry = ALIGN(0x8); */
7375
} >dram0_0_seg :dram0_0_bss_phdr
@@ -150,7 +152,6 @@ SECTIONS
150152
*(.bss)
151153
*(.bss.*)
152154
*(.gnu.linkonce.b.*)
153-
*(COMMON)
154155
. = ALIGN (8);
155156
_bss_end = ABSOLUTE(.);
156157
} >iram1_0_seg :iram1_0_phdr

Diff for: bootloaders/eboot/eboot_command.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ int eboot_command_read(struct eboot_command* cmd)
3737
}
3838

3939
uint32_t crc32 = eboot_command_calculate_crc32(cmd);
40-
if (cmd->magic & EBOOT_MAGIC_MASK != EBOOT_MAGIC ||
40+
if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC ||
4141
cmd->crc32 != crc32) {
4242
return 1;
4343
}

Diff for: cores/esp8266/Updater.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ bool UpdaterClass::_writeBuffer(){
329329
bool modifyFlashMode = false;
330330
FlashMode_t flashMode = FM_QIO;
331331
FlashMode_t bufferFlashMode = FM_QIO;
332-
if (_currentAddress == _startAddress + FLASH_MODE_PAGE) {
332+
//TODO - GZIP can't do this
333+
if ((_currentAddress == _startAddress + FLASH_MODE_PAGE) && (_buffer[0] != 0x1f)) {
333334
flashMode = ESP.getFlashChipMode();
334335
#ifdef DEBUG_UPDATER
335336
DEBUG_UPDATER.printf_P(PSTR("Header: 0x%1X %1X %1X %1X\n"), _buffer[0], _buffer[1], _buffer[2], _buffer[3]);
@@ -411,7 +412,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
411412
bool UpdaterClass::_verifyHeader(uint8_t data) {
412413
if(_command == U_FLASH) {
413414
// check for valid first magic byte (is always 0xE9)
414-
if(data != 0xE9) {
415+
if ((data != 0xE9) && (data != 0x1f)) {
415416
_currentAddress = (_startAddress + _size);
416417
_setError(UPDATE_ERROR_MAGIC_BYTE);
417418
return false;
@@ -435,7 +436,12 @@ bool UpdaterClass::_verifyEnd() {
435436
}
436437

437438
// check for valid first magic byte
438-
if(buf[0] != 0xE9) {
439+
//
440+
// TODO: GZIP compresses the chipsize flags, so can't do check here
441+
if ((buf[0] == 0x1f) && (buf[1] == 0x8b)) {
442+
// GZIP, just assume OK
443+
return true;
444+
} else if (buf[0] != 0xE9) {
439445
_currentAddress = (_startAddress);
440446
_setError(UPDATE_ERROR_MAGIC_BYTE);
441447
return false;

Diff for: tools/sdk/uzlib

Submodule uzlib added at 6101f89

0 commit comments

Comments
 (0)