From 4f19e2742d5156ebf9e246d443188fcc24a2b880 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 27 Jan 2021 15:02:49 -0800 Subject: [PATCH 1/4] eboot: .RODATA, upstream uzlib, save 112 bytes RODATA can be copied automatically by the bootrom, so no reason not to allow its use for strings and constants in eboot.c Revert to pfalcon's original uzlib since the single patch to remove RODATA is not required. Rationalize eboot.ld linker script, clean up BSS and init it in code. Saves 112 bytes of space in the bootloader sector by removing the extra code associated with literal loads. --- .gitmodules | 2 +- bootloaders/eboot/Makefile | 8 ++-- bootloaders/eboot/eboot.c | 42 ++++++++------------ bootloaders/eboot/eboot.elf | Bin 45084 -> 89124 bytes bootloaders/eboot/eboot.ld | 76 ++++++++++++------------------------ tools/elf2bin.py | 2 +- tools/sdk/uzlib | 2 +- 7 files changed, 50 insertions(+), 82 deletions(-) diff --git a/.gitmodules b/.gitmodules index 54e63d88ed..ae42999675 100644 --- a/.gitmodules +++ b/.gitmodules @@ -24,4 +24,4 @@ url = https://github.com/arduino-libraries/Ethernet.git [submodule "tools/sdk/uzlib"] path = tools/sdk/uzlib - url = https://github.com/earlephilhower/uzlib.git + url = https://github.com/pfalcon/uzlib.git diff --git a/bootloaders/eboot/Makefile b/bootloaders/eboot/Makefile index 88808117ef..3eb9fc58f4 100644 --- a/bootloaders/eboot/Makefile +++ b/bootloaders/eboot/Makefile @@ -40,17 +40,17 @@ APP_FW := eboot.bin all: $(APP_OUT) -tinflate.o: $(UZLIB_PATH)/tinflate.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h +tinflate.o: $(UZLIB_PATH)/tinflate.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h Makefile $(CC) $(CFLAGS) -c -o tinflate.o $(UZLIB_PATH)/tinflate.c -tinfgzip.o: $(UZLIB_PATH)/tinfgzip.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h +tinfgzip.o: $(UZLIB_PATH)/tinfgzip.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h Makefile $(CC) $(CFLAGS) -c -o tinfgzip.o $(UZLIB_PATH)/tinfgzip.c -$(APP_AR): $(TARGET_OBJ_PATHS) tinflate.o tinfgzip.o +$(APP_AR): $(TARGET_OBJ_PATHS) tinflate.o tinfgzip.o Makefile $(AR) cru $@ $^ $(APP_OUT): $(APP_AR) eboot.ld | Makefile - $(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--whole-archive $(APP_AR) -Wl,--end-group -o $@ + $(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--sort-common $(APP_AR) -Wl,--end-group -o $@ clean: rm -f *.o diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index 6e188d806e..6e15d137b2 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -14,28 +14,19 @@ #include "eboot_command.h" #include -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); -// Converts bit of a string into a uint32 -#define S(a,b,c,d) ( (((uint32_t)a) & 0xff) | (((uint32_t)b) << 8) | (((uint32_t)c) << 16) | (((uint32_t)d)<<24) ) - 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; } - // We don't have BSS and can't print from flash, so build up string - // 4 chars at a time. Smaller code than byte-wise assignment. - uint32_t fmt[2]; - fmt[0] = S('v', '%', '0', '8'); - fmt[1] = S('x', '\n', 0, 0); - ets_printf((const char*) fmt, ver); + ets_printf("v%08x\n", ver); return 0; } @@ -222,6 +213,16 @@ int main() bool clear_cmd = false; struct eboot_command cmd; +// BSS init commented out for now to save space. If any static variables set +// to 0 are used, need to uncomment it or else the BSS will not be cleared and +// the static vars will power on with random values. +#if 0 + // Clear BSS ourselves, we don't have handy C runtime + extern char _bss_start; + extern char _bss_end; + ets_bzero(&_bss_start, &_bss_end - &_bss_start); +#endif + print_version(0); if (eboot_command_read(&cmd) == 0) { @@ -236,32 +237,26 @@ int main() } if (cmd.action == ACTION_COPY_RAW) { - uint32_t cp = S('c', 'p', ':', 0); - ets_printf((const char *)&cp); + ets_printf("cp:"); ets_wdt_disable(); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], false); ets_wdt_enable(); - cp = S('0' + res, '\n', 0, 0 ); - ets_printf((const char *)&cp); + ets_printf("%d\n", res); #if 0 //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the //beginning of the image in the empty area, see #7458. Disabling for now. //TODO: replace the below verify with hash type, crc, or similar. // Verify the copy - uint32_t v[2]; - v[0] = S('c', 'm', 'p', ':'); - v[1] = 0; - ets_printf(const char *)v); + ets_printf("cmp:"); if (res == 0) { ets_wdt_disable(); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], true); ets_wdt_enable(); } - cp = S('0' + res, '\n', 0, 0 ); - ets_printf((const char *)&cp); + ets_printf("%d\n", res); #endif if (res == 0) { cmd.action = ACTION_LOAD_APP; @@ -274,13 +269,10 @@ int main() } if (cmd.action == ACTION_LOAD_APP) { - ets_putc('l'); ets_putc('d'); ets_putc('\n'); + ets_printf("ld\n"); res = load_app_from_flash_raw(cmd.args[0]); // We will get to this only on load fail - uint32_t e[2]; - e[0] = S('e', ':', '0' + res, '\n' ); - e[1] = 0; - ets_printf((const char*)e); + ets_printf("e:%d\n", res); } if (res) { diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 79820d7c533c3b5aed9e10b6ded11d0320ccf8f1..b7810c81ce95b3ddc4e6155fd39edba63f06e5c9 100755 GIT binary patch delta 18180 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
    |7UPb`_{e{ZWsow(E}AA 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 F`(KtDUpW8( diff --git a/bootloaders/eboot/eboot.ld b/bootloaders/eboot/eboot.ld index c664daf660..7359a8ca5f 100644 --- a/bootloaders/eboot/eboot.ld +++ b/bootloaders/eboot/eboot.ld @@ -42,53 +42,13 @@ PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); SECTIONS { - .dport0.rodata : ALIGN(4) - { - _dport0_rodata_start = ABSOLUTE(.); - *(.dport0.rodata) - *(.dport.rodata) - _dport0_rodata_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .dport0.literal : ALIGN(4) - { - _dport0_literal_start = ABSOLUTE(.); - *(.dport0.literal) - *(.dport.literal) - _dport0_literal_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .dport0.data : ALIGN(4) - { - _dport0_data_start = ABSOLUTE(.); - *(.dport0.data) - *(.dport.data) - _dport0_data_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .data : ALIGN(4) + .globals : ALIGN(4) { *(COMMON) /* Global vars */ - . = ALIGN(4); - _heap_start = ABSOLUTE(.); -/* _stack_sentry = ALIGN(0x8); */ } >dram0_0_seg :dram0_0_bss_phdr -/* __stack = 0x3ffc8000; */ - .text : ALIGN(4) + .data : ALIGN(4) { - _stext = .; - _text_start = ABSOLUTE(.); - *(.entry.text) - *(.init.literal) - *(.init) - *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) - *(.fini.literal) - *(.fini) - *(.gnu.version) - _text_end = ABSOLUTE(.); - _etext = .; - . = ALIGN (8); _data_start = ABSOLUTE(.); *(.data) *(.data.*) @@ -102,7 +62,10 @@ SECTIONS *(.gnu.linkonce.s2.*) *(.jcr) _data_end = ABSOLUTE(.); - . = ALIGN (8); + } >dram0_0_seg :dram0_0_bss_phdr + + .rodata : ALIGN(4) + { _rodata_start = ABSOLUTE(.); *(.rodata) *(.rodata.*) @@ -131,14 +94,11 @@ SECTIONS *(.xt_except_desc_end) *(.dynamic) *(.gnu.version_d) - . = ALIGN(4); /* this table MUST be 4-byte aligned */ - _bss_table_start = ABSOLUTE(.); - LONG(_bss_start) - LONG(_bss_end) - _bss_table_end = ABSOLUTE(.); _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_bss_phdr - . = ALIGN (8); + .bss : ALIGN(4) + { _bss_start = ABSOLUTE(.); *(.dynsbss) *(.sbss) @@ -152,8 +112,24 @@ SECTIONS *(.bss) *(.bss.*) *(.gnu.linkonce.b.*) - . = ALIGN (8); _bss_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_bss_phdr + + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + . = ALIGN (4); /* Ensure 32b alignment since this is written to IRAM */ _free_space = 4096 - 17 - (. - _stext); /* The boot loader checksum must be before the CRC, which is written by elf2bin.py. diff --git a/tools/elf2bin.py b/tools/elf2bin.py index 231bd5e5d0..e8b7c3f890 100755 --- a/tools/elf2bin.py +++ b/tools/elf2bin.py @@ -188,7 +188,7 @@ def wrapper(**kwargs): wrapper( elf=args.eboot, - segments=[".text"], + segments=[".text", ".rodata"], to_addr=4096 ) diff --git a/tools/sdk/uzlib b/tools/sdk/uzlib index 42398df66c..27e4f4c15b 160000 --- a/tools/sdk/uzlib +++ b/tools/sdk/uzlib @@ -1 +1 @@ -Subproject commit 42398df66c02da1cec387b634b6d2f285007a7af +Subproject commit 27e4f4c15ba30c2cfc89575159e8efb50f95037e From 32104914c1214a8b9ec2da19064458e07662ef05 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Fri, 29 Jan 2021 13:34:53 -0800 Subject: [PATCH 2/4] Move CRC out of bootload sector We added protection to only erase the bootload sector when flashing an image when the new sector != the old sector. This was intended to minimize the chance of bricking (i.e. if there was a powerfail during flashing of the boot sector the chip would be dead). Unfortunately, by placing the CRC inside the eboot sector *every* application will have a unique eboot sector (due to the crc/len), so this protection doesn't work. Move the CRC into the first 8 bytes of IROM itself. This frees up extra space in the boot sector and ensures that eboot won't be reflashed unless there really is an eboot change. --- bootloaders/eboot/eboot.elf | Bin 89124 -> 89020 bytes bootloaders/eboot/eboot.ld | 18 ------------------ cores/esp8266/Esp.cpp | 10 ++++++---- tools/elf2bin.py | 4 ++-- tools/sdk/ld/eagle.app.v6.common.ld.h | 7 +++++++ 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index b7810c81ce95b3ddc4e6155fd39edba63f06e5c9..3a1815fac5d380912bd8b3888f45d04ddfec6ff2 100755 GIT binary patch delta 711 zcmYk4KWI}y9LImZ_wsNGUebVc(fmUcgh!{+BIH~}Xff0V;*dHN$GYeuzSgA$hon$A z5QMfmh;*tgFGGcrp+zjkL7Z$FM~Bh|sv?*?zXuoJ-E#N2-}iUF`~L3UG`*eBYh*dI zklW7j#R+bzkqM6Yu^O4=*Fid$&l85AZd7-OI@zc+;CJAJ01KiNF>Qj0igFNdft!~8 z5d843kT)PmlS^&z73=(-=57=X@`qgtsMr(omPxqg61{Dc90+`{U0@7sUvLs!`zhq8 z$*+R#0#Cq=@9}xhz+2Ey8U3;wz#$ym#D;eG3WAk$AwLB&B5u9j; zAHYlB;sSf%t*(&QP5xIf&G-Lz@DwZ%Uo-IwL4rtH7f6Bamigd=Vq9PxOpSg?Px``T zELQzE{s3%uY)%WUCwmUI&!1VxhM$lBz^+a>6H=!MINS%X;7#jb?f4k{1@SS%C*Teo zy*C~C1F|90%~_!I>X4i|} z+{40%ry>y{=+epvbqS)2Q4~Q0{XNyOiGl)i`o5fP-duJ+@Av1u-|T~nO!8yjp5%zvefuKc^$sH;36CIymQzHXS<5Fp1xU^&9k7Ri^e1>#)8-10J-JZ{n58|CUVf7OvwR zZ(vy+ID}s#em-^tFMdz*J50PX45+>( (17 + (. - _stext))), "Error: No space for CS and CRC in bootloader sector."); - ASSERT((_crc_size > _cs_here), "Error: CRC must be located after CS."); } >iram1_0_seg :iram1_0_phdr .lit4 : ALIGN(4) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 51bdfe8e12..9ff66162dc 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -444,21 +444,23 @@ bool EspClass::checkFlashConfig(bool needsEquals) { return false; } +extern "C" uint32_t __crc_len; +extern "C" uint32_t __crc_val; bool EspClass::checkFlashCRC() { // The CRC and total length are placed in extra space at the end of the 4K chunk // of flash occupied by the bootloader. If the bootloader grows to >4K-8 bytes, // we'll need to adjust this. - uint32_t flashsize = *((uint32_t*)(0x40200000 + 4088)); // Start of PROGMEM plus 4K-8 - uint32_t flashcrc = *((uint32_t*)(0x40200000 + 4092)); // Start of PROGMEM plus 4K-4 + uint32_t flashsize = __crc_len; + uint32_t flashcrc = __crc_val; uint32_t z[2]; z[0] = z[1] = 0; // Start the checksum - uint32_t crc = crc32((const void*)0x40200000, 4096-8, 0xffffffff); + uint32_t crc = crc32((const void*)0x40200000, 4096 + 16, 0xffffffff); // Pretend the 2 words of crc/len are zero to be idempotent crc = crc32(z, 8, crc); // Finish the CRC calculation over the rest of flash - crc = crc32((const void*)0x40201000, flashsize-4096, crc); + crc = crc32((const void*)0x40201018, flashsize - 4096 - 16 - 8, crc); return crc == flashcrc; } diff --git a/tools/elf2bin.py b/tools/elf2bin.py index e8b7c3f890..035f7a93ae 100755 --- a/tools/elf2bin.py +++ b/tools/elf2bin.py @@ -30,8 +30,8 @@ ffreqb = { '40': 0, '26': 1, '20': 2, '80': 15 } fsizeb = { '512K': 0, '256K': 1, '1M': 2, '2M': 3, '4M': 4, '8M': 8, '16M': 9 } -crcsize_offset = 4088 -crcval_offset = 4092 +crcsize_offset = 4096 + 16 +crcval_offset = 4096 + 16 + 4 def get_elf_entry(elf, path): p = subprocess.Popen([path + "/xtensa-lx106-elf-readelf", '-h', elf], stdout=subprocess.PIPE, universal_newlines=True ) diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index c51de98408..77c834ae19 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -147,6 +147,13 @@ SECTIONS .irom0.text : ALIGN(4) { _irom0_text_start = ABSOLUTE(.); + + /* Stuff the CRC in well known symbols at a well known location */ + __crc_len = ABSOLUTE(.); + LONG(0x00000000); + __crc_val = ABSOLUTE(.); + LONG(0x00000000); + *(.ver_number) *.c.o(.literal*, .text*) *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal*, EXCLUDE_FILE (umm_malloc.cpp.o) .text*) From 6a50ae7d0944cdc3993d5afbe38d6441056d8c1c Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 4 Feb 2021 16:20:47 -0800 Subject: [PATCH 3/4] Use symbol locate CRC vals, not hardcoded offsets Also clean up obsolete comment --- cores/esp8266/Esp.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 9ff66162dc..e518755f6b 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -444,24 +444,24 @@ bool EspClass::checkFlashConfig(bool needsEquals) { return false; } +// These are defined in the linker script, and filled in by the elf2bin.py util extern "C" uint32_t __crc_len; extern "C" uint32_t __crc_val; + bool EspClass::checkFlashCRC() { - // The CRC and total length are placed in extra space at the end of the 4K chunk - // of flash occupied by the bootloader. If the bootloader grows to >4K-8 bytes, - // we'll need to adjust this. - uint32_t flashsize = __crc_len; - uint32_t flashcrc = __crc_val; + // Dummy CRC fill uint32_t z[2]; z[0] = z[1] = 0; + uint32_t firstPart = (uintptr_t)&__crc_len - 0x40200000; // How many bytes to check before the 1st CRC val + // Start the checksum - uint32_t crc = crc32((const void*)0x40200000, 4096 + 16, 0xffffffff); + uint32_t crc = crc32((const void*)0x40200000, firstPart, 0xffffffff); // Pretend the 2 words of crc/len are zero to be idempotent crc = crc32(z, 8, crc); // Finish the CRC calculation over the rest of flash - crc = crc32((const void*)0x40201018, flashsize - 4096 - 16 - 8, crc); - return crc == flashcrc; + crc = crc32((const void*)(0x40200000 + firstPart + 8), __crc_len - (firstPart + 8), crc); + return crc == __crc_val; } From 28e562b673f8da06ec954db37cf1ddeb5a918035 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Fri, 5 Feb 2021 12:40:51 -0800 Subject: [PATCH 4/4] Remove rogue SWSerial accidental change --- libraries/SoftwareSerial | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index adec3fe4f1..27477f7e29 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit adec3fe4f16ef3a61463c0b4eaeafebcb5487a45 +Subproject commit 27477f7e29b01c99aa5665b3d5aef692fec06a9c