From 9a471079209a5039305946d57a43c8758d94ccf9 Mon Sep 17 00:00:00 2001 From: Terry Ellison Date: Fri, 5 Apr 2019 16:01:45 +0100 Subject: [PATCH] SDK 3.0 release (#2692) * Rebaseline firmware to non-OS SDK version 3.0 * Note that SDK version 3.0 introduces the concept of a Flash Partition Table(PT). This is located at Flash offset 0x10000 in our firmware build. * The firmware is now PT aware with both LFS and SPIFFS taking their partition size and location from the PT * A new tool `tools/nodemcu-partition.py` is now used to initialise these data and can also download LFS and SPIFFS images to these partitions. --- Makefile | 61 +- app/Makefile | 3 +- app/esp-gdbstub/gdbstub.c | 17 +- app/include/lwip/mem.h | 27 +- app/include/user_config.h | 30 +- app/libc/c_math.c | 29 +- app/lua/ldblib.c | 6 +- app/lua/lflash.c | 45 +- app/lua/lflash.h | 2 +- app/lua/lgc.c | 4 - app/lua/lgc.h | 2 +- app/lua/lobject.h | 2 - app/lua/lstate.c | 5 +- app/lua/lstate.h | 3 +- app/lua/lstring.c | 2 +- app/lua/lua.c | 2 - app/lua/luac_cross/lflashimg.c | 2 - app/modules/node.c | 8 - app/modules/rtctime.c | 4 +- app/platform/flash_api.c | 176 +---- app/platform/flash_api.h | 6 +- app/platform/platform.c | 9 + app/platform/platform.h | 1 + app/platform/vfs.c | 11 +- app/spiffs/Makefile | 1 + app/spiffs/myspiffs.h | 20 - app/spiffs/spiffs.c | 176 ++--- app/spiffs/spiffs_config.h | 2 +- app/user/user_exceptions.c | 115 --- app/user/user_main.c | 287 ++++---- docs/compiling.md | 9 +- docs/getting-started.md | 31 +- docs/modules/node.md | 2 +- ld/nodemcu.ld | 14 +- sdk-overrides/include/mem.h | 11 - tools/esptool.py | 1236 -------------------------------- tools/nodemcu-partition.py | 286 ++++++++ 37 files changed, 716 insertions(+), 1931 deletions(-) delete mode 100644 app/spiffs/myspiffs.h delete mode 100644 app/user/user_exceptions.c delete mode 100644 sdk-overrides/include/mem.h delete mode 100755 tools/esptool.py create mode 100755 tools/nodemcu-partition.py diff --git a/Makefile b/Makefile index fbcbcda6..de3376b3 100644 --- a/Makefile +++ b/Makefile @@ -5,20 +5,14 @@ TOOLCHAIN_VERSION:=20181106.0 # SDK base version, as released by Espressif -SDK_BASE_VER:=2.2.1 - -# no patch: SDK_VER equals SDK_BASE_VER and sdk dir depends on sdk_extracted +SDK_BASE_VER:=3.0 SDK_VER:=$(SDK_BASE_VER) SDK_DIR_DEPENDS:=sdk_extracted -# with patch: SDK_VER differs from SDK_BASE_VER and sdk dir depends on sdk_patched -#SDK_PATCH_VER:=f8f27ce -#SDK_VER:=$(SDK_BASE_VER)-$(SDK_PATCH_VER) -#SDK_DIR_DEPENDS:=sdk_patched - SDK_FILE_VER:=$(SDK_BASE_VER) -SDK_FILE_SHA1:=48f2242d5895823709f222bf0fffce9d525996c8 -# SDK_PATCH_SHA1:=0bc21ec77b08488f04d3e1c9d161b711d07201a8 +SDK_FILE_SHA1:=029fc23fe87e03c9852de636490b2d7b9e07f01a +ESPTOOL_VER:=2.6 + # Ensure we search "our" SDK before the tool-chain's SDK (if any) TOP_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))) SDK_REL_DIR=sdk/esp_iot_sdk_v$(SDK_VER) @@ -109,6 +103,7 @@ else CPP = $(WRAPCC) xtensa-lx106-elf-gcc -E OBJCOPY = xtensa-lx106-elf-objcopy FIRMWAREDIR = ../bin/ + WGET = wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) # LINUX @@ -128,8 +123,12 @@ else endif endif ############################################################# -ESPTOOL ?= ../tools/esptool.py +GITHUB_TOOLCHAIN = https://github.com/jmattsson/esp-toolchains +GITHUB_SDK = https://github.com/espressif/ESP8266_NONOS_SDK +GITHUB_ESPTOOL = https://github.com/espressif/esptool + +ESPTOOL ?= $(TOP_DIR)/tools/toolchains/esptool.py CSRCS ?= $(wildcard *.c) CXXSRCS ?= $(wildcard *.cpp) @@ -234,18 +233,16 @@ $(BINODIR)/%.bin: $(IMAGEODIR)/%.out all: toolchain sdk_pruned pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS) .PHONY: sdk_extracted -.PHONY: sdk_patched .PHONY: sdk_pruned .PHONY: toolchain sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER) -sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER) sdk_pruned: $(SDK_DIR_DEPENDS) $(TOP_DIR)/sdk/.pruned-$(SDK_VER) ifeq ($(OS),Windows_NT) toolchain: else -toolchain: $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-lx106-elf-gcc +toolchain: $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-lx106-elf-gcc $(TOP_DIR)/tools/toolchains/esptool.py $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-lx106-elf-gcc: $(TOP_DIR)/cache/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz mkdir -p $(TOP_DIR)/tools/toolchains/ @@ -256,22 +253,34 @@ $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa- $(TOP_DIR)/cache/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz: mkdir -p $(TOP_DIR)/cache $(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@) - wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused https://github.com/jmattsson/esp-toolchains/releases/download/$(PLATFORM)-$(TOOLCHAIN_VERSION)/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz -O $@ || { rm -f "$@"; exit 1; } + $(WGET) $(GITHUB_TOOLCHAIN)/releases/download/$(PLATFORM)-$(TOOLCHAIN_VERSION)/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz -O $@ \ + || { rm -f "$@"; exit 1; } endif +$(TOP_DIR)/tools/toolchains/esptool.py: $(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz + mkdir -p $(TOP_DIR)/tools/toolchains/ + tar -C $(TOP_DIR)/tools/toolchains/ -xzf $< --strip-components=1 esptool-$(ESPTOOL_VER)/esptool.py + chmod +x $@ + touch $@ + +$(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz: + mkdir -p $(TOP_DIR)/cache/esptool/ + $(WGET) $(GITHUB_ESPTOOL)/archive/v$(ESPTOOL_VER).tar.gz -O $@ || { rm -f "$@"; exit 1; } + $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip mkdir -p "$(dir $@)" $(summary) UNZIP $(patsubst $(TOP_DIR)/%,%,$<) - (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_BASE_VER) && unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK-$(SDK_BASE_VER)/lib/* ESP8266_NONOS_SDK-$(SDK_BASE_VER)/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK-$(SDK_BASE_VER)/include/* ESP8266_NONOS_SDK-$(SDK_BASE_VER)/bin/esp_init_data_default_v05.bin) + (cd "$(dir $@)" && \ + rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_BASE_VER) && \ + unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip \ + 'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/lib/*' \ + 'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/ld/*.v6.ld' \ + 'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/include/*' \ + 'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/bin/esp_init_data_default_v05.bin' \ + ) mv $(dir $@)/ESP8266_NONOS_SDK-$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER) touch $@ -$(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/$(SDK_PATCH_VER).patch - mv $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_VER) - $(summary) APPLY $(patsubst $(TOP_DIR)/%,%,$<) - git apply --verbose -p1 --exclude='*VERSION' --exclude='*bin/at*' --directory=$(SDK_REL_DIR) $< - touch $@ - $(TOP_DIR)/sdk/.pruned-$(SDK_VER): rm -f $(SDK_DIR)/lib/liblwip.a $(SDK_DIR)/lib/libssl.a $(SDK_DIR)/lib/libmbedtls.a $(summary) PRUNE libmain.a libc.a @@ -282,15 +291,9 @@ $(TOP_DIR)/sdk/.pruned-$(SDK_VER): $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip: mkdir -p "$(dir $@)" $(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@) - wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused https://github.com/espressif/ESP8266_NONOS_SDK/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; } + $(WGET) $(GITHUB_SDK)/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; } (echo "$(SDK_FILE_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; } -$(TOP_DIR)/cache/$(SDK_PATCH_VER).patch: - mkdir -p "$(dir $@)" - $(summary) WGET $(SDK_PATCH_VER).patch - wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused "https://github.com/espressif/ESP8266_NONOS_SDK/compare/v$(SDK_BASE_VER)...$(SDK_PATCH_VER).patch" -O $@ || { rm -f "$@"; exit 1; } - (echo "$(SDK_PATCH_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; } - clean: $(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clean;) $(RM) -r $(ODIR)/$(TARGET)/$(FLAVOR) diff --git a/app/Makefile b/app/Makefile index 968e8455..222f03b3 100644 --- a/app/Makefile +++ b/app/Makefile @@ -97,7 +97,6 @@ LINKFLAGS_eagle.app.v6 = \ -T$(LD_FILE) \ -Wl,@../ld/defsym.rom \ -Wl,--no-check-sections \ - -Wl,--wrap=_xtos_set_exception_handler \ -Wl,-static \ $(addprefix -u , $(SELECTED_MODULE_SYMS)) \ -Wl,--start-group \ @@ -116,6 +115,8 @@ LINKFLAGS_eagle.app.v6 = \ $(DEP_LIBS_eagle.app.v6) \ -Wl,--end-group \ -lm +# -Wl,--cref +# -Wl,--wrap=_xtos_set_exception_handler DEPENDS_eagle.app.v6 = \ $(LD_FILE) \ diff --git a/app/esp-gdbstub/gdbstub.c b/app/esp-gdbstub/gdbstub.c index 5ee8ee92..509e9bb4 100644 --- a/app/esp-gdbstub/gdbstub.c +++ b/app/esp-gdbstub/gdbstub.c @@ -659,11 +659,22 @@ static void ATTR_GDBFN gdb_semihost_putchar1(char c) { } #if !GDBSTUB_FREERTOS -//The OS-less SDK uses the Xtensa HAL to handle exceptions. We can use those functions to catch any -//fatal exceptions and invoke the debugger when this happens. +/* The non-OS SDK uses the Xtensa HAL to handle exceptions, and the SDK now establishes exception + * handlers for EXCCAUSE errors: ILLEGAL, INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED, + * LOAD_PROHIBITED and STORE_PROHIBITED. These handlers are established in SDK/app_main.c. + * LOAD_STORE_ERROR is handled by SDK/user_exceptions.o:load_non_32_wide_handler() which is a + * fork of our version. The remaining are handled by a static function at + * SDK:app+main.c:offset 0x0348. + * + * Our SDK 2 load_non_32_wide_handler chained into the gdb stub handler if the error was anything + * other than a L8UI, L16SI or L16UI at a flash mapped address. However in this current + * implementation, we have left the Espressif handler in place and handle the other errors with + * the debugger. This means that the debugger will not capture other load store errors. I + * might revise this. + */ static void ATTR_GDBINIT install_exceptions() { int i; - int exno[]={EXCCAUSE_ILLEGAL, EXCCAUSE_SYSCALL, EXCCAUSE_INSTR_ERROR, EXCCAUSE_LOAD_STORE_ERROR, + const int exno[]={EXCCAUSE_ILLEGAL, EXCCAUSE_SYSCALL, EXCCAUSE_INSTR_ERROR, /* EXCCAUSE_LOAD_STORE_ERROR, */ EXCCAUSE_DIVIDE_BY_ZERO, EXCCAUSE_UNALIGNED, EXCCAUSE_INSTR_DATA_ERROR, EXCCAUSE_LOAD_STORE_DATA_ERROR, EXCCAUSE_INSTR_ADDR_ERROR, EXCCAUSE_LOAD_STORE_ADDR_ERROR, EXCCAUSE_INSTR_PROHIBITED, EXCCAUSE_LOAD_PROHIBITED, EXCCAUSE_STORE_PROHIBITED}; diff --git a/app/include/lwip/mem.h b/app/include/lwip/mem.h index 3e4e6149..825d2226 100644 --- a/app/include/lwip/mem.h +++ b/app/include/lwip/mem.h @@ -51,40 +51,39 @@ typedef size_t mem_size_t; * allow these defines to be overridden. */ #ifndef MEMLEAK_DEBUG + #ifndef mem_free -#define mem_free vPortFree +#define mem_free(s) vPortFree(s, "", __LINE__) #endif #ifndef mem_malloc -#define mem_malloc pvPortMalloc +#define mem_malloc(s) pvPortMalloc(s, "", __LINE__,false) #endif #ifndef mem_calloc -#define mem_calloc pvPortCalloc +#define mem_calloc(l, s) pvPortCalloc(l, s, "", __LINE__) #endif #ifndef mem_realloc -#define mem_realloc pvPortRealloc +#define mem_realloc(p, s) pvPortRealloc(p, s, "", __LINE__) #endif #ifndef mem_zalloc -#define mem_zalloc pvPortZalloc +#define mem_zalloc(s) pvPortZalloc(s, "", __LINE__) #endif + #else + #ifndef mem_free -#define mem_free(s) \ -do{\ - const char *file = mem_debug_file;\ - vPortFree(s, file, __LINE__);\ -}while(0) +#define mem_free(s) vPortFree(s, mem_debug_file, __LINE__) #endif #ifndef mem_malloc -#define mem_malloc(s) ({const char *file = mem_debug_file; pvPortMalloc(s, file, __LINE__);}) +#define mem_malloc(s) pvPortMalloc(s, mem_debug_file, __LINE__,false) #endif #ifndef mem_calloc -#define mem_calloc(l, s) ({const char *file = mem_debug_file; pvPortCalloc(l, s, file, __LINE__);}) +#define mem_calloc(l, s) pvPortCalloc(l, s, mem_debug_file, __LINE__) #endif #ifndef mem_realloc -#define mem_realloc(p, s) ({const char *file = mem_debug_file; pvPortRealloc(p, s, file, __LINE__);}) +#define mem_realloc(p, s) pvPortRealloc(p, s, mem_debug_file, __LINE__) #endif #ifndef mem_zalloc -#define mem_zalloc(s) ({const char *file = mem_debug_file; pvPortZalloc(s, file, __LINE__);}) +#define mem_zalloc(s) pvPortZalloc(s, mem_debug_file, __LINE__) #endif #endif diff --git a/app/include/user_config.h b/app/include/user_config.h index 06e52a2a..56567880 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -7,8 +7,8 @@ // this out and enabling the explicitly size, e.g. FLASH_4M. Valid sizes are // FLASH_512K, FLASH_1M, FLASH_2M, FLASH_4M, FLASH_8M, FLASH_16M. -#define FLASH_AUTOSIZE -//#define FLASH_4M +//#define FLASH_AUTOSIZE +#define FLASH_4M // The firmware now selects a baudrate of 115,200 by default, but the driver @@ -41,11 +41,8 @@ // The Lua Flash Store (LFS) allows you to store Lua code in Flash memory and // the Lua VMS will execute this code directly from flash without needing any -// RAM overhead. If you want to enable LFS then set the following define to -// the size of the store that you need. This can be any multiple of 4kB up to -// a maximum 256Kb. - -//#define LUA_FLASH_STORE 0x10000 +// RAM overhead. Note that you should now configure LFS directly in the +// System Partition Table and not at build time. // By default Lua executes the file init.lua at start up. The following @@ -71,10 +68,10 @@ // general, limiting the size of the FS only to what your application needs // gives the fastest start-up and imaging times. +// Note that you should now configure SPIFFS size and position directly in the +// System Partition Table and not at build time. + #define BUILD_SPIFFS -//#define SPIFFS_FIXED_LOCATION 0x100000 -//#define SPIFFS_MAX_FILESYSTEM_SIZE 0x20000 -//#define SPIFFS_SIZE_1M_BOUNDARY #define SPIFFS_CACHE 1 // Enable if you use you SPIFFS in R/W mode #define SPIFFS_MAX_OPEN_FILES 4 // maximum number of open files for SPIFFS #define FS_OBJ_NAME_LEN 31 // maximum length of a filename @@ -206,6 +203,19 @@ // change this if you have tracked the implications through the Firmware sources // and understand the these. +#define NODEMCU_EAGLEROM_PARTITION 1 +#define NODEMCU_IROM0TEXT_PARTITION 2 +#define NODEMCU_LFS0_PARTITION 3 +#define NODEMCU_LFS1_PARTITION 4 +#define NODEMCU_TLSCERT_PARTITION 5 +#define NODEMCU_SPIFFS0_PARTITION 6 +#define NODEMCU_SPIFFS1_PARTITION 7 + +#define LUA_FLASH_STORE 0x0 +#define SPIFFS_FIXED_LOCATION 0x0 +#define SPIFFS_MAX_FILESYSTEM_SIZE (~0x0) +//#define SPIFFS_SIZE_1M_BOUNDARY + #define LUA_TASK_PRIO USER_TASK_PRIO_0 #define LUA_PROCESS_LINE_SIG 2 #define LUA_OPTIMIZE_DEBUG 2 diff --git a/app/libc/c_math.c b/app/libc/c_math.c index 635e8b22..2eb44687 100644 --- a/app/libc/c_math.c +++ b/app/libc/c_math.c @@ -11,7 +11,7 @@ double floor(double x) #define MINEXP -2047 /* (MIN_EXP * 16) - 1 */ #define HUGE MAXFLOAT -double a1[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = +static const double a1[] = { 1.0, 0.95760328069857365, @@ -31,7 +31,7 @@ double a1[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.52213689121370692, 0.50000000000000000 }; -double a2[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = +static const double a2[] = { 0.24114209503420288E-17, 0.92291566937243079E-18, @@ -42,18 +42,19 @@ double a2[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.29306999570789681E-17, 0.11260851040933474E-17 }; -double p1 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.833333333333332114e-1; -double p2 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.125000000005037992e-1; -double p3 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.223214212859242590e-2; -double p4 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.434457756721631196e-3; -double q1 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.693147180559945296e0; -double q2 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.240226506959095371e0; -double q3 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.555041086640855953e-1; -double q4 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.961812905951724170e-2; -double q5 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.133335413135857847e-2; -double q6 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.154002904409897646e-3; -double q7 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.149288526805956082e-4; -double k ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.442695040888963407; +static const double + p1 = 0.833333333333332114e-1, + p2 = 0.125000000005037992e-1, + p3 = 0.223214212859242590e-2, + p4 = 0.434457756721631196e-3, + q1 = 0.693147180559945296e0, + q2 = 0.240226506959095371e0, + q3 = 0.555041086640855953e-1, + q4 = 0.961812905951724170e-2, + q5 = 0.133335413135857847e-2, + q6 = 0.154002904409897646e-3, + q7 = 0.149288526805956082e-4, + k = 0.442695040888963407; double pow(double x, double y) { diff --git a/app/lua/ldblib.c b/app/lua/ldblib.c index 02e1e3d1..3eef8fd4 100644 --- a/app/lua/ldblib.c +++ b/app/lua/ldblib.c @@ -28,12 +28,12 @@ static int db_getregistry (lua_State *L) { } static int db_getstrings (lua_State *L) { - size_t i,n; + size_t i,n=0; stringtable *tb; GCObject *o; -#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER) +#ifndef LUA_CROSS_COMPILER const char *opt = lua_tolstring (L, 1, &n); - if (n==3 && memcmp(opt, "ROM", 4) == 0) { + if (n==3 && c_memcmp(opt, "ROM", 4) == 0) { if (G(L)->ROstrt.hash == NULL) return 0; tb = &G(L)->ROstrt; diff --git a/app/lua/lflash.c b/app/lua/lflash.c index 4c5f42cb..17218fe1 100644 --- a/app/lua/lflash.c +++ b/app/lua/lflash.c @@ -8,7 +8,6 @@ #define LUAC_CROSS_FILE #include "lua.h" -#ifdef LUA_FLASH_STORE #include "lobject.h" #include "lauxlib.h" #include "lstate.h" @@ -31,6 +30,7 @@ */ static char *flashAddr; +static uint32_t flashSize; static uint32_t flashAddrPhys; static uint32_t flashSector; static uint32_t curOffset; @@ -38,9 +38,8 @@ static uint32_t curOffset; #define ALIGN(s) (((s)+sizeof(size_t)-1) & ((size_t) (- (signed) sizeof(size_t)))) #define ALIGN_BITS(s) (((uint32_t)s) & (sizeof(size_t)-1)) #define ALL_SET (~0) -#define FLASH_SIZE LUA_FLASH_STORE #define FLASH_PAGE_SIZE INTERNAL_FLASH_SECTOR_SIZE -#define FLASH_PAGES (FLASH_SIZE/FLASH_PAGE_SIZE) +#define FLASH_PAGES (flashSize/FLASH_PAGE_SIZE) #define READ_BLOCKSIZE 1024 #define WRITE_BLOCKSIZE 2048 #define DICTIONARY_WINDOW 16384 @@ -49,8 +48,6 @@ static uint32_t curOffset; #define WRITE_BLOCKS ((DICTIONARY_WINDOW/WRITE_BLOCKSIZE)+1) #define WRITE_BLOCK_WORDS (WRITE_BLOCKSIZE/WORDSIZE) -char flash_region_base[FLASH_SIZE] ICACHE_FLASH_RESERVED_ATTR; - struct INPUT { int fd; int len; @@ -152,11 +149,16 @@ static void flashErase(uint32_t start, uint32_t end){ * Hook in lstate.c:f_luaopen() to set up ROstrt and ROpvmain if needed */ LUAI_FUNC void luaN_init (lua_State *L) { - curOffset = 0; - flashAddr = flash_region_base; - flashAddrPhys = platform_flash_mapped2phys((uint32_t)flashAddr); + + flashSize = platform_flash_get_partition (NODEMCU_LFS0_PARTITION, &flashAddrPhys); + if (flashSize == 0) { + return; // Nothing to do if the size is zero + } + G(L)->LFSsize = flashSize; + flashAddr = cast(char *, platform_flash_phys2mapped(flashAddrPhys)); flashSector = platform_flash_get_sector_of_address(flashAddrPhys); FlashHeader *fh = cast(FlashHeader *, flashAddr); + curOffset = 0; /* * For the LFS to be valid, its signature has to be correct for this build @@ -201,6 +203,13 @@ LUALIB_API int luaN_reload_reboot (lua_State *L) { // luaL_dbgbreak(); const char *fn = lua_tostring(L, 1), *msg = ""; int status; + + if (G(L)->LFSsize == 0) { + lua_pushstring(L, "No LFS partition allocated"); + return 1; + } + + /* * Do a protected call of loadLFS. * @@ -264,13 +273,18 @@ LUAI_FUNC int luaN_index (lua_State *L) { int i; int n = lua_gettop(L); - /* Return nil + the LFS base address if the LFS isn't loaded */ - if(!(G(L)->ROpvmain)) { + /* Return nil + the LFS base address if the LFS size > 0 and it isn't loaded */ + if (!(G(L)->ROpvmain)) { lua_settop(L, 0); lua_pushnil(L); - lua_pushinteger(L, (lua_Integer) flashAddr); - lua_pushinteger(L, flashAddrPhys); - return 3; + if (G(L)->LFSsize) { + lua_pushinteger(L, (lua_Integer) flashAddr); + lua_pushinteger(L, flashAddrPhys); + lua_pushinteger(L, G(L)->LFSsize); + return 4; + } else { + return 1; + } } /* Push the LClosure of the LFS index function */ @@ -409,10 +423,10 @@ int procFirstPass (void) { flash_error("Incorrect LFS build type"); if ((fh->flash_sig & ~FLASH_SIG_ABSOLUTE) != FLASH_SIG) flash_error("incorrect LFS header signature"); - if (fh->flash_size > FLASH_SIZE) + if (fh->flash_size > flashSize) flash_error("LFS Image too big for configured LFS region"); if ((fh->flash_size & 0x3) || - fh->flash_size > FLASH_SIZE || + fh->flash_size > flashSize || out->flagsLen != 1 + (out->flashLen/WORDSIZE - 1) / BITS_PER_WORD) flash_error("LFS length mismatch"); out->flags = luaM_newvector(out->L, out->flagsLen, uint); @@ -557,4 +571,3 @@ static int loadLFSgc (lua_State *L) { } return 0; } -#endif diff --git a/app/lua/lflash.h b/app/lua/lflash.h index 5088a4a7..722d0f36 100644 --- a/app/lua/lflash.h +++ b/app/lua/lflash.h @@ -3,7 +3,7 @@ ** See Copyright Notice in lua.h */ -#if defined(LUA_FLASH_STORE) && !defined(lflash_h) +#ifndef lflash_h #define lflash_h #include "lobject.h" diff --git a/app/lua/lgc.c b/app/lua/lgc.c index b9c80a33..645703d6 100644 --- a/app/lua/lgc.c +++ b/app/lua/lgc.c @@ -28,10 +28,6 @@ #define GCSWEEPCOST 10 #define GCFINALIZECOST 100 -#if READONLYMASK != (1<marked, FIXEDSTACKBIT) #define fixedstack(x) l_setbit((x)->marked, FIXEDSTACKBIT) #define unfixedstack(x) resetbit((x)->marked, FIXEDSTACKBIT) -#ifdef LUA_FLASH_STORE +#ifndef LUA_CROSS_COMPILER #define isLFSobject(x) testbit(getmarked(x), LFSBIT) #define stringfix(s) if (!test2bits(getmarked(&(s)->tsv), FIXEDBIT, LFSBIT)) {l_setbit((s)->tsv.marked, FIXEDBIT);} #else diff --git a/app/lua/lobject.h b/app/lua/lobject.h index 708d2a9d..e2173831 100644 --- a/app/lua/lobject.h +++ b/app/lua/lobject.h @@ -24,9 +24,7 @@ #define NUM_TAGS (LAST_TAG+1) #define READONLYMASK (1<<7) /* denormalised bitmask for READONLYBIT and */ -#ifdef LUA_FLASH_STORE #define LFSMASK (1<<6) /* LFSBIT to avoid include proliferation */ -#endif /* ** Extra tags for non-values */ diff --git a/app/lua/lstate.c b/app/lua/lstate.c index ccfa2d37..55e107a6 100644 --- a/app/lua/lstate.c +++ b/app/lua/lstate.c @@ -73,7 +73,7 @@ static void f_luaopen (lua_State *L, void *ud) { sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ -#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER) +#ifndef LUA_CROSS_COMPILER luaN_init(L); /* optionally map RO string table */ #endif luaT_init(L); @@ -196,11 +196,12 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { #else g->memlimit = 0; #endif -#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER) +#ifndef LUA_CROSS_COMPILER g->ROstrt.size = 0; g->ROstrt.nuse = 0; g->ROstrt.hash = NULL; g->ROpvmain = NULL; + g->LFSsize = 0; #endif for (i=0; imt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { diff --git a/app/lua/lstate.h b/app/lua/lstate.h index 55d3d89c..88b7d57f 100644 --- a/app/lua/lstate.h +++ b/app/lua/lstate.h @@ -94,9 +94,10 @@ typedef struct global_State { UpVal uvhead; /* head of double-linked list of all open upvalues */ struct Table *mt[NUM_TAGS]; /* metatables for basic types */ TString *tmname[TM_N]; /* array with tag-method names */ -#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER) +#ifndef LUA_CROSS_COMPILER stringtable ROstrt; /* Flash-based hash table for RO strings */ Proto *ROpvmain; /* Flash-based Proto main */ + int LFSsize; /* Size of Lua Flash Store */ #endif } global_State; diff --git a/app/lua/lstring.c b/app/lua/lstring.c index 9278b5cd..be459187 100644 --- a/app/lua/lstring.c +++ b/app/lua/lstring.c @@ -112,7 +112,7 @@ LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { return ts; } } -#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER) +#ifndef LUA_CROSS_COMPILER /* * The RAM strt is searched first since RAM access is faster tham Flash access. * If a miss, then search the RO string table. diff --git a/app/lua/lua.c b/app/lua/lua.c index a30f49f8..cb226b51 100644 --- a/app/lua/lua.c +++ b/app/lua/lua.c @@ -22,9 +22,7 @@ #include "lauxlib.h" #include "lualib.h" #include "legc.h" -#ifdef LUA_FLASH_STORE #include "lflash.h" -#endif #include "os_type.h" lua_State *globalL = NULL; diff --git a/app/lua/luac_cross/lflashimg.c b/app/lua/luac_cross/lflashimg.c index 0fef0dd8..4dd5a853 100644 --- a/app/lua/luac_cross/lflashimg.c +++ b/app/lua/luac_cross/lflashimg.c @@ -16,8 +16,6 @@ #define LUA_CORE #include "lobject.h" #include "lstring.h" -#undef LUA_FLASH_STORE -#define LUA_FLASH_STORE #include "lflash.h" #include "uzlib.h" diff --git a/app/modules/node.c b/app/modules/node.c index e655d52c..7094c539 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -17,9 +17,7 @@ #include "platform.h" #include "lrodefs.h" -#ifdef LUA_FLASH_STORE #include "lflash.h" -#endif #include "c_types.h" #include "c_string.h" #include "driver/uart.h" @@ -162,10 +160,6 @@ static int node_flashid( lua_State* L ) // Lua: flashsize() static int node_flashsize( lua_State* L ) { - if (lua_type(L, 1) == LUA_TNUMBER) - { - flash_rom_set_size_byte(luaL_checkinteger(L, 1)); - } uint32_t sz = flash_rom_get_size_byte(); lua_pushinteger( L, sz ); return 1; @@ -603,10 +597,8 @@ static const LUA_REG_TYPE node_map[] = { LSTRKEY( "heap" ), LFUNCVAL( node_heap ) }, { LSTRKEY( "info" ), LFUNCVAL( node_info ) }, { LSTRKEY( "task" ), LROVAL( node_task_map ) }, -#ifdef LUA_FLASH_STORE { LSTRKEY( "flashreload" ), LFUNCVAL( luaN_reload_reboot ) }, { LSTRKEY( "flashindex" ), LFUNCVAL( luaN_index ) }, -#endif { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, { LSTRKEY( "dsleepMax" ), LFUNCVAL( dsleepMax ) }, diff --git a/app/modules/rtctime.c b/app/modules/rtctime.c index d780fd94..fc806287 100644 --- a/app/modules/rtctime.c +++ b/app/modules/rtctime.c @@ -46,10 +46,10 @@ void __attribute__((noreturn)) TEXT_SECTION_ATTR rtc_time_enter_deep_sleep_final void rtctime_early_startup (void) { - Cache_Read_Enable (0, 0, 1); +// Cache_Read_Enable (0, 0, 1); rtc_time_register_bootup (); rtc_time_switch_clocks (); - Cache_Read_Disable (); +// Cache_Read_Disable (); } void rtctime_late_startup (void) diff --git a/app/platform/flash_api.c b/app/platform/flash_api.c index 6901afc0..fb77d04b 100644 --- a/app/platform/flash_api.c +++ b/app/platform/flash_api.c @@ -39,16 +39,19 @@ uint32_t flash_detect_size_byte(void) #undef FLASH_BUFFER_SIZE_DETECT } -SPIFlashInfo flash_rom_getinfo(void) +static SPIFlashInfo spi_flash_info = {0}; + +SPIFlashInfo *flash_rom_getinfo(void) { - volatile SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR; - spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info)); - return spi_flash_info; + if (spi_flash_info.entry_point == 0) { + spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info)); + } + return &spi_flash_info; } uint8_t flash_rom_get_size_type(void) { - return flash_rom_getinfo().size; + return flash_rom_getinfo()->size; } uint32_t flash_rom_get_size_byte(void) @@ -56,7 +59,7 @@ uint32_t flash_rom_get_size_byte(void) static uint32_t flash_size = 0; if (flash_size == 0) { - switch (flash_rom_getinfo().size) + switch (flash_rom_getinfo()->size) { case SIZE_2MBIT: // 2Mbit, 256kByte @@ -107,99 +110,15 @@ uint32_t flash_rom_get_size_byte(void) return flash_size; } -bool flash_rom_set_size_type(uint8_t size) -{ - // Dangerous, here are dinosaur infested!!!!! - // Reboot required!!! - // If you don't know what you're doing, your nodemcu may turn into stone ... - NODE_DBG("\nBEGIN SET FLASH HEADER\n"); - uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; - if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nflash_rom_set_size_type(%u), was %u\n", size, ((SPIFlashInfo *)data)->size ); - ((SPIFlashInfo *)data)->size = size; - if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nSECTOR 0 ERASE SUCCESS\n"); - } - if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nWRITE SUCCESS, %u\n", size); - } - } - NODE_DBG("\nEND SET FLASH HEADER\n"); - return true; -} - -bool flash_rom_set_size_byte(uint32_t size) -{ - // Dangerous, here are dinosaur infested!!!!! - // Reboot required!!! - // If you don't know what you're doing, your nodemcu may turn into stone ... - bool result = true; - uint32_t flash_size = 0; - switch (size) - { - case 256 * 1024: - // 2Mbit, 256kByte - flash_size = SIZE_2MBIT; - flash_rom_set_size_type(flash_size); - break; - case 512 * 1024: - // 4Mbit, 512kByte - flash_size = SIZE_4MBIT; - flash_rom_set_size_type(flash_size); - break; - case 1 * 1024 * 1024: - // 8Mbit, 1MByte - flash_size = SIZE_8MBIT; - flash_rom_set_size_type(flash_size); - break; - case 2 * 1024 * 1024: - // 16Mbit, 2MByte - flash_size = SIZE_16MBIT; - flash_rom_set_size_type(flash_size); - break; - case 4 * 1024 * 1024: - // 32Mbit, 4MByte - flash_size = SIZE_32MBIT; - flash_rom_set_size_type(flash_size); - break; - case 8 * 1024 * 1024: - // 64Mbit, 8MByte - flash_size = SIZE_64MBIT; - flash_rom_set_size_type(flash_size); - break; - case 16 * 1024 * 1024: - // 128Mbit, 16MByte - flash_size = SIZE_128MBIT; - flash_rom_set_size_type(flash_size); - break; - default: - // Unknown flash size. - result = false; - break; - } - return result; -} - uint16_t flash_rom_get_sec_num(void) { - //static uint16_t sec_num = 0; - // return flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE); - // c_printf("\nflash_rom_get_size_byte()=%d\n", ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) )); - // if( sec_num == 0 ) - //{ - // sec_num = 4 * 1024 * 1024 / (SPI_FLASH_SEC_SIZE); - //} - //return sec_num; return ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) ); } uint8_t flash_rom_get_mode(void) { - SPIFlashInfo spi_flash_info = flash_rom_getinfo(); - switch (spi_flash_info.mode) + uint8_t mode = flash_rom_getinfo()->mode; + switch (mode) { // Reserved for future use case MODE_QIO: @@ -211,14 +130,14 @@ uint8_t flash_rom_get_mode(void) case MODE_DOUT: break; } - return spi_flash_info.mode; + return mode; } uint32_t flash_rom_get_speed(void) { uint32_t speed = 0; - SPIFlashInfo spi_flash_info = flash_rom_getinfo(); - switch (spi_flash_info.speed) + uint8_t spi_speed = flash_rom_getinfo()->speed; + switch (spi_speed) { case SPEED_40MHZ: // 40MHz @@ -240,47 +159,6 @@ uint32_t flash_rom_get_speed(void) return speed; } -bool flash_rom_set_speed(uint32_t speed) -{ - // Dangerous, here are dinosaur infested!!!!! - // Reboot required!!! - // If you don't know what you're doing, your nodemcu may turn into stone ... - NODE_DBG("\nBEGIN SET FLASH HEADER\n"); - uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; - uint8_t speed_type = SPEED_40MHZ; - if (speed < 26700000) - { - speed_type = SPEED_20MHZ; - } - else if (speed < 40000000) - { - speed_type = SPEED_26MHZ; - } - else if (speed < 80000000) - { - speed_type = SPEED_40MHZ; - } - else if (speed >= 80000000) - { - speed_type = SPEED_80MHZ; - } - if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) - { - ((SPIFlashInfo *)(&data[0]))->speed = speed_type; - NODE_DBG("\nflash_rom_set_speed(%u), was %u\n", speed_type, ((SPIFlashInfo *)(&data[0]))->speed ); - if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nERASE SUCCESS\n"); - } - if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nWRITE SUCCESS, %u\n", speed_type); - } - } - NODE_DBG("\nEND SET FLASH HEADER\n"); - return true; -} - uint8_t byte_of_aligned_array(const uint8_t *aligned_array, uint32_t index) { if ( (((uint32_t)aligned_array) % 4) != 0 ) @@ -303,31 +181,5 @@ uint16_t word_of_aligned_array(const uint16_t *aligned_array, uint32_t index) volatile uint32_t v = ((uint32_t *)aligned_array)[ index / 2 ]; uint16_t *p = (uint16_t *) (&v); return (index % 2 == 0) ? p[ 0 ] : p[ 1 ]; - // return p[ (index % 2) ]; // -- why error??? - // (byte_of_aligned_array((uint8_t *)aligned_array, index * 2 + 1) << 8) | byte_of_aligned_array((uint8_t *)aligned_array, index * 2); } -// uint8_t flash_rom_get_checksum(void) -// { -// // SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR = flash_rom_getinfo(); -// // uint32_t address = sizeof(spi_flash_info) + spi_flash_info.segment_size; -// // uint32_t address_aligned_4bytes = (address + 3) & 0xFFFFFFFC; -// // uint8_t buffer[64] = {0}; -// // spi_flash_read(address, (uint32 *) buffer, 64); -// // uint8_t i = 0; -// // c_printf("\nBEGIN DUMP\n"); -// // for (i = 0; i < 64; i++) -// // { -// // c_printf("%02x," , buffer[i]); -// // } -// // i = (address + 0x10) & 0x10 - 1; -// // c_printf("\nSIZE:%d CHECK SUM:%02x\n", spi_flash_info.segment_size, buffer[i]); -// // c_printf("\nEND DUMP\n"); -// // return buffer[0]; -// return 0; -// } - -// uint8_t flash_rom_calc_checksum(void) -// { -// return 0; -// } diff --git a/app/platform/flash_api.h b/app/platform/flash_api.h index b0fb16be..148c5147 100644 --- a/app/platform/flash_api.h +++ b/app/platform/flash_api.h @@ -79,18 +79,14 @@ typedef struct uint32_t segment_size; } ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo; -SPIFlashInfo flash_rom_getinfo(void); +SPIFlashInfo *flash_rom_getinfo(void); uint8_t flash_rom_get_size_type(void); uint32_t flash_rom_get_size_byte(void); uint32_t flash_detect_size_byte(void); -bool flash_rom_set_size_type(uint8_t); -bool flash_rom_set_size_byte(uint32_t); uint16_t flash_rom_get_sec_num(void); uint8_t flash_rom_get_mode(void); uint32_t flash_rom_get_speed(void); uint8_t byte_of_aligned_array(const uint8_t* aligned_array, uint32_t index); uint16_t word_of_aligned_array(const uint16_t *aligned_array, uint32_t index); -// uint8_t flash_rom_get_checksum(void); -// uint8_t flash_rom_calc_checksum(void); #endif // __FLASH_API_H__ diff --git a/app/platform/platform.c b/app/platform/platform.c index 7d45471d..0fdc66d8 100644 --- a/app/platform/platform.c +++ b/app/platform/platform.c @@ -946,6 +946,15 @@ uint32_t platform_flash_phys2mapped (uint32_t phys_addr) { return (meg&1) ? -1 : phys_addr + INTERNAL_FLASH_MAPPED_ADDRESS - meg; } +uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr) { + partition_item_t pt = {0,0,0}; + system_partition_get_item(SYSTEM_PARTITION_CUSTOMER_BEGIN + part_id, &pt); + if (addr) { + *addr = pt.addr; + } + return pt.type == 0 ? 0 : pt.size; +} + void* platform_print_deprecation_note( const char *msg, const char *time_frame) { c_printf( "Warning, deprecated API! %s. It will be removed %s. See documentation for details.\n", msg, time_frame ); diff --git a/app/platform/platform.h b/app/platform/platform.h index 06395e37..3897f4f4 100644 --- a/app/platform/platform.h +++ b/app/platform/platform.h @@ -289,6 +289,7 @@ int platform_flash_erase_sector( uint32_t sector_id ); */ uint32_t platform_flash_mapped2phys (uint32_t mapped_addr); uint32_t platform_flash_phys2mapped (uint32_t phys_addr); +uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr); // ***************************************************************************** // Allocator support diff --git a/app/platform/vfs.c b/app/platform/vfs.c index 444d4c43..76773985 100644 --- a/app/platform/vfs.c +++ b/app/platform/vfs.c @@ -33,11 +33,14 @@ sint32_t vfs_get_rtc( vfs_time *tm ) static int dir_level = 1; + +#if ! LDRV_TRAVERSAL + +#define normalize_path(p) (p) + +#else static const char *normalize_path( const char *path ) { -#if ! LDRV_TRAVERSAL - return path; -#else const char *temp = path; size_t len; @@ -63,8 +66,8 @@ static const char *normalize_path( const char *path ) // path traverses via root return temp; } -#endif } +#endif // --------------------------------------------------------------------------- diff --git a/app/spiffs/Makefile b/app/spiffs/Makefile index 2deb1efb..2b1af59f 100644 --- a/app/spiffs/Makefile +++ b/app/spiffs/Makefile @@ -23,6 +23,7 @@ endif # for a subtree within the makefile rooted therein # DEFINES += -Dprintf=c_printf +#DEFINES += -DDEVELOPMENT_TOOLS -DNODE_DEBUG -DSPIFFS_API_DBG=NODE_DBG ############################################################# # Recursion Magic - Don't touch this!! diff --git a/app/spiffs/myspiffs.h b/app/spiffs/myspiffs.h deleted file mode 100644 index a269aca9..00000000 --- a/app/spiffs/myspiffs.h +++ /dev/null @@ -1,20 +0,0 @@ -#include "spiffs.h" -bool myspiffs_mount(); -void myspiffs_unmount(); -int myspiffs_open(const char *name, int flags); -int myspiffs_close( int fd ); -size_t myspiffs_write( int fd, const void* ptr, size_t len ); -size_t myspiffs_read( int fd, void* ptr, size_t len); -int myspiffs_lseek( int fd, int off, int whence ); -int myspiffs_eof( int fd ); -int myspiffs_tell( int fd ); -int myspiffs_getc( int fd ); -int myspiffs_ungetc( int c, int fd ); -int myspiffs_flush( int fd ); -int myspiffs_error( int fd ); -void myspiffs_clearerr( int fd ); -int myspiffs_check( void ); -int myspiffs_rename( const char *old, const char *newname ); -size_t myspiffs_size( int fd ); -int myspiffs_format (void); - diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index 704685ed..794bc1a7 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -2,14 +2,31 @@ #include "platform.h" #include "spiffs.h" +/* + * With the intoduction of a unified FatFS and SPIFFS support (#1397), the SPIFFS + * interface is now abstracted through a uses a single SPIFFS entry point + * myspiffs_realm() which returns a vfs_fs_fns object (as does myfatfs_realm()). + * All other functions and data are static. + * + * Non-OS SDK V3.0 introduces a flash partition table (PT) and SPIFFS has now been + * updated to support this: + * - SPIFFS limits search to the specifed SPIFFS0 address and size. + * - Any headroom / offset from other partitions is reflected in the PT allocations. + * - Unforced mounts will attempt to mount any valid SPIFSS found in this range + * (NodeMCU uses the SPIFFS_USE_MAGIC setting to make existing FS discoverable). + * - Subject to the following, no offset or FS search is done. The FS is assumed + * to be at the first valid location at the start of the partition. + */ #include "spiffs_nucleus.h" -spiffs fs; +static spiffs fs; #define LOG_PAGE_SIZE 256 #define LOG_BLOCK_SIZE (INTERNAL_FLASH_SECTOR_SIZE * 2) #define LOG_BLOCK_SIZE_SMALL_FS (INTERNAL_FLASH_SECTOR_SIZE) #define MIN_BLOCKS_FS 4 +#define MASK_1MB (0x100000-1) +#define ALIGN (0x2000) static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES]; @@ -42,131 +59,64 @@ void myspiffs_check_callback(spiffs_check_type type, spiffs_check_report report, } /******************* -The W25Q32BV array is organized into 16,384 programmable pages of 256-bytes each. Up to 256 bytes can be programmed at a time.  -Pages can be erased in groups of 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block erase) or  -the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 64 erasable blocks respectively.  -The small 4KB sectors allow for greater flexibility in applications that require data and parameter storage.  + * Note that the W25Q32BV array is organized into 16,384 programmable pages of 256-bytes  + * each. Up to 256 bytes can be programmed at a time. Pages can be erased in groups of  + * 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block  + * erase) or the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and  + * 64 erasable blocks respectively. The small 4KB sectors allow for greater flexibility  + * in applications that require data and parameter storage.  + * + * Returns TRUE if FS was found. + */ +static bool myspiffs_set_cfg(spiffs_config *cfg, bool force_create) { + uint32 pt_start, pt_size, pt_end; -********************/ - -static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int block_size) { -#ifdef SPIFFS_FIXED_LOCATION - cfg->phys_addr = (SPIFFS_FIXED_LOCATION + block_size - 1) & ~(block_size-1); -#else - cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset; - cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1); -#endif -#ifdef SPIFFS_SIZE_1M_BOUNDARY - cfg->phys_size = ((0x100000 - (SYS_PARAM_SEC_NUM * INTERNAL_FLASH_SECTOR_SIZE) - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1)) & 0xfffff; -#else - cfg->phys_size = (INTERNAL_FLASH_SIZE - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1); -#endif - if ((int) cfg->phys_size < 0) { + pt_size = platform_flash_get_partition (NODEMCU_SPIFFS0_PARTITION, &pt_start); + if (pt_size == 0) { return FALSE; } - cfg->log_block_size = block_size; - - return (cfg->phys_size / block_size) >= MIN_BLOCKS_FS; -} - -/* - * Returns TRUE if FS was found - * align must be a power of two - */ -static bool myspiffs_set_cfg_block(spiffs_config *cfg, int align, int offset, int block_size, bool force_create) { - cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet - cfg->log_page_size = LOG_PAGE_SIZE; // as we said + pt_end = pt_start + pt_size; cfg->hal_read_f = my_spiffs_read; cfg->hal_write_f = my_spiffs_write; cfg->hal_erase_f = my_spiffs_erase; - - if (!myspiffs_set_location(cfg, align, offset, block_size)) { + cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; + cfg->log_page_size = LOG_PAGE_SIZE; + cfg->phys_addr = (pt_start + ALIGN - 1) & ~(ALIGN - 1); + cfg->phys_size = (pt_end & ~(ALIGN - 1)) - cfg->phys_addr; + + if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) { return FALSE; + } else if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) { + cfg->log_block_size = LOG_BLOCK_SIZE_SMALL_FS; + } else { + cfg->log_block_size = LOG_BLOCK_SIZE; } - NODE_DBG("fs.start:%x,max:%x\n",cfg->phys_addr,cfg->phys_size); - #ifdef SPIFFS_USE_MAGIC_LENGTH - if (force_create) { - return TRUE; - } - - int size = SPIFFS_probe_fs(cfg); - - if (size > 0 && size < cfg->phys_size) { - NODE_DBG("Overriding size:%x\n",size); - cfg->phys_size = size; - } - if (size > 0) { - return TRUE; - } - return FALSE; -#else - return TRUE; -#endif -} - -static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool force_create) { - if (force_create) { - return myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE , TRUE) || - myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS, TRUE); - } - - return myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS, FALSE) || - myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE , FALSE); -} - -static bool myspiffs_find_cfg(spiffs_config *cfg, bool force_create) { - int i; - if (!force_create) { -#ifdef SPIFFS_FIXED_LOCATION - if (myspiffs_set_cfg(cfg, 0, 0, FALSE)) { - return TRUE; - } -#else - if (INTERNAL_FLASH_SIZE >= 700000) { - for (i = 0; i < 8; i++) { - if (myspiffs_set_cfg(cfg, 0x10000, 0x10000 * i, FALSE)) { - return TRUE; - } - } - } + int size = SPIFFS_probe_fs(cfg); - for (i = 0; i < 8; i++) { - if (myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, LOG_BLOCK_SIZE * i, FALSE)) { - return TRUE; - } + if (size > 0 && size < cfg->phys_size) { + NODE_DBG("Overriding size:%x\n",size); + cfg->phys_size = size; } -#endif - } - - // No existing file system -- set up for a format - if (INTERNAL_FLASH_SIZE >= 700000) { - myspiffs_set_cfg(cfg, 0x10000, 0x10000, TRUE); -#ifndef SPIFFS_MAX_FILESYSTEM_SIZE - if (cfg->phys_size < 400000) { - // Don't waste so much in alignment - myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, LOG_BLOCK_SIZE * 4, TRUE); + if (size <= 0) { + return FALSE; } -#endif - } else { - myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, 0, TRUE); - } - -#ifdef SPIFFS_MAX_FILESYSTEM_SIZE - if (cfg->phys_size > SPIFFS_MAX_FILESYSTEM_SIZE) { - cfg->phys_size = (SPIFFS_MAX_FILESYSTEM_SIZE) & ~(cfg->log_block_size - 1); } #endif - return FALSE; + NODE_DBG("myspiffs set cfg block: %x %x %x %x %x %x\n", pt_start, pt_end, + cfg->phys_size, cfg->phys_addr, cfg->phys_size, cfg->log_block_size); + + return TRUE; } -static bool myspiffs_mount_internal(bool force_mount) { + +static bool myspiffs_mount(bool force_mount) { spiffs_config cfg; - if (!myspiffs_find_cfg(&cfg, force_mount) && !force_mount) { + if (!myspiffs_set_cfg(&cfg, force_mount) && !force_mount) { return FALSE; } @@ -189,10 +139,6 @@ static bool myspiffs_mount_internal(bool force_mount) { return res == SPIFFS_OK; } -bool myspiffs_mount() { - return myspiffs_mount_internal(FALSE); -} - void myspiffs_unmount() { SPIFFS_unmount(&fs); } @@ -202,7 +148,7 @@ void myspiffs_unmount() { int myspiffs_format( void ) { SPIFFS_unmount(&fs); - myspiffs_mount_internal(TRUE); + myspiffs_mount(TRUE); SPIFFS_unmount(&fs); NODE_DBG("Formatting: size 0x%x, addr 0x%x\n", fs.cfg.phys_size, fs.cfg.phys_addr); @@ -211,7 +157,7 @@ int myspiffs_format( void ) return 0; } - return myspiffs_mount(); + return myspiffs_mount(FALSE); } #if 0 @@ -555,7 +501,7 @@ static sint32_t myspiffs_vfs_fscfg( uint32_t *phys_addr, uint32_t *phys_size ) { static vfs_vol *myspiffs_vfs_mount( const char *name, int num ) { // volume descriptor not supported, just return TRUE / FALSE - return myspiffs_mount() ? (vfs_vol *)1 : NULL; + return myspiffs_mount(FALSE) ? (vfs_vol *)1 : NULL; } static sint32_t myspiffs_vfs_format( void ) { @@ -574,12 +520,12 @@ static void myspiffs_vfs_clearerr( void ) { // --------------------------------------------------------------------------- // VFS interface functions // + vfs_fs_fns *myspiffs_realm( const char *inname, char **outname, int set_current_drive ) { if (inname[0] == '/') { - size_t idstr_len = c_strlen( MY_LDRV_ID ); // logical drive is specified, check if it's our id - if (0 == c_strncmp( &(inname[1]), MY_LDRV_ID, idstr_len )) { - *outname = (char *)&(inname[1 + idstr_len]); + if (0 == c_strncmp(inname + 1, MY_LDRV_ID, sizeof(MY_LDRV_ID)-1)) { + *outname = (char *)(inname + sizeof(MY_LDRV_ID)); if (*outname[0] == '/') { // skip leading / (*outname)++; diff --git a/app/spiffs/spiffs_config.h b/app/spiffs/spiffs_config.h index ac7c23e8..6cb233fd 100644 --- a/app/spiffs/spiffs_config.h +++ b/app/spiffs/spiffs_config.h @@ -165,7 +165,7 @@ // Lower value generates more read/writes. No meaning having it bigger // than logical page size. #ifndef SPIFFS_COPY_BUFFER_STACK -#define SPIFFS_COPY_BUFFER_STACK (64) +#define SPIFFS_COPY_BUFFER_STACK (256) #endif // Enable this to have an identifiable spiffs filesystem. This will look for diff --git a/app/user/user_exceptions.c b/app/user/user_exceptions.c deleted file mode 100644 index f2ad11ae..00000000 --- a/app/user/user_exceptions.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2015 Dius Computing Pty Ltd. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the - * distribution. - * - Neither the name of the copyright holders nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @author Johny Mattsson - */ - -#include "user_exceptions.h" - -static exception_handler_fn load_store_handler; - -void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) -{ - uint32_t val, insn; - (void)cause; /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */ - - asm ( - /* - * Move the aligned content of the exception addr to val - */ - "rsr a6, EXCVADDR;" /* read out the faulting address */ - "movi a5, ~3;" /* prepare a mask for the EPC */ - "and a5, a5, a6;" /* apply mask for 32bit aligned base */ - "l32i a5, a5, 0;" /* load aligned value */ - "ssa8l a6;" /* set up shift register for value */ - "srl %[val], a5;" /* shift left to align value */ - /* we are done with a6 = EXCVADDR */ - /* - * Move the aligned instruction to insn - */ - "movi a5, ~3;" /* prepare a mask for the insn */ - "and a6, a5, %[epc];" /* apply mask for 32bit aligned base */ - "l32i a5, a6, 0;" /* load part 1 */ - "l32i a6, a6, 4;" /* load part 2 */ - "ssa8l %[epc];" /* set up shift register for src op */ - "src %[op], a6, a5;" /* right shift to get faulting instruction */ - :[val]"=r"(val), [op]"=r"(insn) - :[epc]"r"(ef->epc) - :"a5", "a6" - ); - -/* These instructions have the format 0xADSBII where AB = opcode and D = dest reg */ - uint32_t regno = (insn>>4)&0x0f; /* pick out nibble D*/ - uint32_t opcode = (uint8_t) (((insn>>12)<<4)|(insn&0xf)); /* and nibbles AB */ -#define L8UI 0x02u -#define L16UI 0x12u -#define L16SI 0x92u - - if (opcode == L8UI) { /* L8UI */ - val = (uint8_t) val; - } else { - val = (uint16_t) val; /* assume L16SI or L16UI */ - if (opcode == L16SI) { - val = (unsigned)((int)((sint16_t)val)); /* force signed 16->32 bit */ - } else if (opcode != L16UI) { - /* - * Anything other than L8UI, L16SI or L16UI then chain to the next handler - * if set (typically a remote GDB break). Otherwise execute the default action - * which is to trigger a system break and hang if the break doesn't get handled - */ - if (load_store_handler) { - load_store_handler(NULL, 0 /* ef , cause */); - return; - } else { - asm ("break 1, 1"); - while (1) {} - } - } - } - ef->a_reg[regno ? regno-1: regno] = val; /* carry out the load */ - ef->epc += 3; /* resume at following instruction */ -} - -/** - * The SDK's user_main function installs a debugging handler regardless - * of whether there's a proper handler installed for EXCCAUSE_LOAD_STORE_ERROR, - * which of course breaks everything if we allow that to go through. As such, - * we use the linker to wrap that call and stop the SDK from shooting itself in - * its proverbial foot. We do save the EXCCAUSE_LOAD_STORE_ERROR handler so that - * we can chain to it above. - */ -exception_handler_fn TEXT_SECTION_ATTR -__wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) -{ - if (cause != EXCCAUSE_LOAD_STORE_ERROR) - __real__xtos_set_exception_handler (cause, fn); - else - load_store_handler = fn; -} diff --git a/app/user/user_main.c b/app/user/user_main.c index 78ac3fe9..c3be3030 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -16,7 +16,6 @@ #include "vfs.h" #include "flash_api.h" #include "user_interface.h" -#include "user_exceptions.h" #include "user_modules.h" #include "ets_sys.h" @@ -24,6 +23,7 @@ #include "task/task.h" #include "mem.h" #include "espconn.h" +#include "sections.h" #ifdef LUA_USE_MODULES_RTCTIME #include "rtc/rtctime.h" @@ -36,74 +36,163 @@ static uint8 input_sig_flag = 0; extern const uint32_t init_data[]; extern const uint32_t init_data_end[]; __asm__( - /* Place in .text for same reason as user_start_trampoline */ - ".section \".rodata.dram\"\n" ".align 4\n" - "init_data:\n" - ".incbin \"" ESP_INIT_DATA_DEFAULT "\"\n" + "init_data: .incbin \"" ESP_INIT_DATA_DEFAULT "\"\n" "init_data_end:\n" - ".previous\n" ); +extern const char _irom0_text_start[], _irom0_text_end[],_flash_used_end[]; +#define IROM0_SIZE (_irom0_text_end - _irom0_text_start) -/* Note: the trampoline *must* be explicitly put into the .text segment, since - * by the time it is invoked the irom has not yet been mapped. This naturally - * also goes for anything the trampoline itself calls. +#define INIT_DATA_SIZE (init_data_end - init_data) + +#define PRE_INIT_TEXT_ATTR __attribute__((section(".p3.pre_init"))) +#define IROM_PTABLE_ATTR __attribute__((section(".irom0.ptable"))) + +#define PARTITION(n) (SYSTEM_PARTITION_CUSTOMER_BEGIN + n) + +#define SIZE_256K 0x00040000 +#define SIZE_1024K 0x00100000 +#define FLASH_BASE_ADDR ((char *) 0x40200000) + +//TODO: map the TLS server and client certs into NODEMCU_TLSCERT_PARTITION +const partition_item_t partition_init_table[] IROM_PTABLE_ATTR = { + { PARTITION(NODEMCU_EAGLEROM_PARTITION), 0x00000, 0x0B000}, + { SYSTEM_PARTITION_RF_CAL, 0x0B000, 0x1000}, + { SYSTEM_PARTITION_PHY_DATA, 0x0C000, 0x1000}, + { SYSTEM_PARTITION_SYSTEM_PARAMETER, 0x0D000, 0x3000}, + { PARTITION(NODEMCU_IROM0TEXT_PARTITION), 0x10000, 0x0000}, + { PARTITION(NODEMCU_LFS0_PARTITION), 0x0, LUA_FLASH_STORE}, + { PARTITION(NODEMCU_SPIFFS0_PARTITION), 0x0, SPIFFS_MAX_FILESYSTEM_SIZE}, + {0,(uint32_t) &_irom0_text_end,0} +}; +// The following enum must maintain the partition table order +enum partition {iram0=0, rf_call, phy_data, sys_parm, irom0, lfs, spiffs}; +#define PTABLE_SIZE ((sizeof(partition_init_table)/sizeof(partition_item_t))-1) +#define PT_CHUNK 0x8000 +#define PT_ALIGN(n) ((n + (PT_CHUNK-1)) & (~((PT_CHUNK-1)))) +/* + * The non-OS SDK prolog has been fundamentally revised in V3. See SDK EN document + * Partition Table.md for further discussion. This version of user_main.c is a + * complete rework aligned to V3, with the redundant pre-V3 features removed. + * + * SDK V3 significantly reduces the RAM footprint required by the SDK and introduces + * the use of a partition table (PT) to control flash allocation. The NodeMCU uses + * this PT for overall allocation of its flash resources. A constant copy PT is + * maintained at the start of IROM0 (flash offset 0x10000) -- see partition_init_table + * declaration above -- to facilitate its modification either in the firmware binary + * or in the flash itself. This is Flash PT used during startup to create the live PT + * in RAM that is used by the SDK. + * + * Note that user_pre_init() runs with Icache enabled -- that is the IROM0 partition + * is already mapped the address space at 0x40210000 and so that most SDK services + * are available, such as system_get_flash_size_map() which returns the valid flash + * size (including the 8Mb and 16Mb variants). + * + * We will be separately releasing a host PC-base python tool to configure the PT, + * etc., but the following code will initialise the PT to sensible defaults even if + * this tool isn't used. */ -void TEXT_SECTION_ATTR user_start_trampoline (void) -{ - __real__xtos_set_exception_handler ( - EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler); +static int setup_partition_table(partition_item_t *pt, uint32_t *n) { +// Flash size lookup is SIZE_256K*2^N where N is as follows (see SDK/user_interface.h) + static char flash_size_scaler[] = + /* 0 1 2 3 4 5 6 7 8 9 */ + /* ½M ¼M 1M 2M 4M 2M 4M 4M 8M 16M */ + "\001\000\002\003\004\003\004\004\005\006"; + enum flash_size_map fs_size_code = system_get_flash_size_map(); + uint32_t flash_size = SIZE_256K << flash_size_scaler[fs_size_code]; + uint32_t first_free_flash_addr = partition_init_table[PTABLE_SIZE].addr + - (uint32_t) FLASH_BASE_ADDR; + int i,j; + + os_memcpy(pt, partition_init_table, PTABLE_SIZE * sizeof(*pt)); + + + if (flash_size < SIZE_1024K) { + os_printf("Flash size (%u) too small to support NodeMCU\n", flash_size); + return -1; + } else { + os_printf("system SPI FI size:%u, Flash size: %u\n", fs_size_code, flash_size ); + } + +// Calculate the runtime sized partitions +// The iram0, rf_call, phy_data, sys_parm partitions are as-is. + if (pt[irom0].size == 0) { + pt[irom0].size = first_free_flash_addr - pt[irom0].addr; + } + if (pt[lfs].addr == 0) { + pt[lfs].addr = PT_ALIGN(pt[irom0].addr + pt[irom0].size); + os_printf("LFS base: %08X\n", pt[lfs].addr); + } + if (pt[lfs].size == 0) { + pt[lfs].size = 0x10000; + os_printf("LFS size: %08X\n", pt[lfs].size); + } + if (pt[spiffs].addr == 0) { + pt[spiffs].addr = PT_ALIGN(pt[lfs].addr + pt[lfs].size); + os_printf("SPIFFS base: %08X\n", pt[spiffs].addr); + } + + if (pt[spiffs].size == SPIFFS_MAX_FILESYSTEM_SIZE) { + pt[spiffs].size = flash_size - pt[spiffs].addr; + os_printf("SPIFFS size: %08X\n", pt[spiffs].size); + } + +// Check that the phys data partition has been initialised and if not then do this +// now to prevent the SDK halting on a "rf_cal[0] !=0x05,is 0xFF" error. + uint32_t init_data_hdr = 0xffffffff, data_addr = pt[phy_data].addr; + int status = spi_flash_read(data_addr, &init_data_hdr, sizeof (uint32_t)); + if (status == SPI_FLASH_RESULT_OK && *(char *)&init_data_hdr != 0x05) { + uint32_t idata[INIT_DATA_SIZE]; + os_printf("Writing Init Data to 0x%08x\n",data_addr); + spi_flash_erase_sector(data_addr/SPI_FLASH_SEC_SIZE); + os_memcpy(idata, init_data, sizeof(idata)); + spi_flash_write(data_addr, idata, sizeof(idata)); + os_delay_us(1000); + } + +// Check partitions are page aligned and remove and any zero-length partitions. +// This must be done last as this might break the enum partition ordering. + for (i = 0, j = 0; i < PTABLE_SIZE; i++) { + const partition_item_t *p = pt + i; + if ((p->addr| p->size) & (SPI_FLASH_SEC_SIZE-1)) { + os_printf("Partitions must be flash page aligned\n"); + return -1; + } + if (p->size == 0) + continue; + if (j < i) { + pt[j] = *p; + p = pt + j; + } + os_printf("%2u: %08x %08x %08x\n", j, p->type, p->addr, p->size); + j++; + } + + *n = j; + return fs_size_code; +} + +void user_pre_init(void) { #ifdef LUA_USE_MODULES_RTCTIME // Note: Keep this as close to call_user_start() as possible, since it // is where the cpu clock actually gets bumped to 80MHz. - rtctime_early_startup (); + rtctime_early_startup (); #endif + static partition_item_t pt[PTABLE_SIZE]; - /* Re-implementation of default init data deployment. The SDK does not - * appear to be laying down its own version of init data anymore, so - * we have to do it again. To see whether we need to, we read out - * the flash size and do a test for esp_init_data based on that size. - * If it's missing, we need to initialize it *right now* before the SDK - * starts up and gets stuck at "rf_cal[0] !=0x05,is 0xFF". - * If the size byte is wrong, then we'll end up fixing up the init data - * again on the next boot, after we've corrected the size byte. - * Only remaining issue is lack of spare code bytes in iram, so this - * is deliberately quite terse and not as readable as one might like. - */ - SPIFlashInfo sfi; + uint32_t pt_size; + uint32_t fs_size_code = setup_partition_table(pt, &pt_size); + if( fs_size_code > 0 && system_partition_table_regist(pt, pt_size, fs_size_code)) { + return; + } + os_printf("system_partition_table_regist fail (%u)\n", fs_size_code); + while(1); - // enable operations on >4MB flash chip - extern SpiFlashChip * flashchip; - uint32 orig_chip_size = flashchip->chip_size; - flashchip->chip_size = FLASH_SIZE_16MBYTE; +} - SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use - // handle all size entries - switch (sfi.size) { - case 0: sfi.size = 1; break; // SIZE_4MBIT - case 1: sfi.size = 0; break; // SIZE_2MBIT - case 5: sfi.size = 3; break; // SIZE_16MBIT_8M_8M - case 6: // fall-through - case 7: sfi.size = 4; break; // SIZE_32MBIT_8M_8M, SIZE_32MBIT_16M_16M - case 8: sfi.size = 5; break; // SIZE_64MBIT - case 9: sfi.size = 6; break; // SIZE_128MBIT - default: break; - } - uint32_t flash_end_addr = (256 * 1024) << sfi.size; - uint32_t init_data_hdr = 0xffffffff; - uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE; - SPIRead (init_data_addr, &init_data_hdr, sizeof (init_data_hdr)); - if (init_data_hdr == 0xffffffff) - { - SPIEraseSector (init_data_addr); - SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data)); - } - - // revert temporary setting - flashchip->chip_size = orig_chip_size; - - call_user_start (); +uint32 ICACHE_RAM_ATTR user_iram_memory_is_enabled(void) { + return FALSE; // NodeMCU runs like a dog if iRAM is enabled } // +================== New task interface ==================+ @@ -138,32 +227,17 @@ void nodemcu_init(void) { NODE_DBG("Can not init platform for modules.\n"); return; } - uint32_t size_detected = flash_detect_size_byte(); - uint32_t size_from_rom = flash_rom_get_size_byte(); - if( size_detected != size_from_rom ) { - NODE_ERR("Self adjust flash size. 0x%x (ROM) -> 0x%x (Detected)\n", - size_from_rom, size_detected); - // Fit hardware real flash size. - flash_rom_set_size_byte(size_detected); - - system_restart (); - // Don't post the start_lua task, we're about to reboot... - return; - } #ifdef BUILD_SPIFFS if (!vfs_mount("/FLASH", 0)) { // Failed to mount -- try reformat - dbg_printf("Formatting file system. Please wait...\n"); + dbg_printf("Formatting file system. Please wait...\n"); if (!vfs_format()) { NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" ); NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); } - // Note that fs_format leaves the file system mounted } - // test_spiffs(); #endif - // endpoint_setup(); if (!task_post_low(task_get_id(start_lua),'s')) NODE_ERR("Failed to post the start_lua task!\n"); @@ -179,59 +253,6 @@ void user_rf_pre_init(void) } #endif -/****************************************************************************** - * FunctionName : user_rf_cal_sector_set - * Description : SDK just reversed 4 sectors, used for rf init data and paramters. - * We add this function to force users to set rf cal sector, since - * we don't know which sector is free in user's application. - * sector map for last several sectors : ABCCC - * A : rf cal - * B : rf init data - * C : sdk parameters - * Parameters : none - * Returns : rf cal sector -*******************************************************************************/ -uint32 -user_rf_cal_sector_set(void) -{ - enum flash_size_map size_map = system_get_flash_size_map(); - uint32 rf_cal_sec = 0; - - switch (size_map) { - case FLASH_SIZE_4M_MAP_256_256: - rf_cal_sec = 128 - 5; - break; - - case FLASH_SIZE_8M_MAP_512_512: - rf_cal_sec = 256 - 5; - break; - - case FLASH_SIZE_16M_MAP_512_512: - case FLASH_SIZE_16M_MAP_1024_1024: - rf_cal_sec = 512 - 5; - break; - - case FLASH_SIZE_32M_MAP_512_512: - case FLASH_SIZE_32M_MAP_1024_1024: - case FLASH_SIZE_32M_MAP_2048_2048: - rf_cal_sec = 1024 - 5; - break; - - case FLASH_SIZE_64M_MAP_1024_1024: - rf_cal_sec = 2048 - 5; - break; - - case FLASH_SIZE_128M_MAP_1024_1024: - rf_cal_sec = 4096 - 5; - break; - - default: - rf_cal_sec = 0; - break; - } - - return rf_cal_sec; -} /****************************************************************************** * FunctionName : user_init @@ -241,6 +262,7 @@ user_rf_cal_sector_set(void) *******************************************************************************/ void user_init(void) { + #ifdef LUA_USE_MODULES_RTCTIME rtctime_late_startup (); #endif @@ -255,3 +277,18 @@ void user_init(void) #endif system_init_done_cb(nodemcu_init); } + +/* + * The SDK now establishes exception handlers for EXCCAUSE errors: ILLEGAL, + * INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED, LOAD_PROHIBITED, + * STORE_PROHIBITED. These handlers are established in SDK/app_main.c. + * LOAD_STORE_ERROR is handled by SDK/user_exceptions.o:load_non_32_wide_handler() + * which is a fork of our version. The remaining are handled by a static function + * at SDK:app+main.c:offset 0x0348. + * +void __real__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn); +void __wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) { + os_printf("Exception handler %x %x\n", cause, fn); + __real__xtos_set_exception_handler (cause, fn); +} + */ diff --git a/docs/compiling.md b/docs/compiling.md index 01fae44f..731ee547 100644 --- a/docs/compiling.md +++ b/docs/compiling.md @@ -50,19 +50,20 @@ mode is enabled by specifying the `-f`option. - **Compact relocatable**. This is selected by the `-f` option. Here the compiler compresses the compiled binary so that image is small for downloading over Wifi/WAN (e.g. a full 64Kb LFS image is compressed down to a 22Kb file.) The LVM processes such image in two passes with the integrity of the image validated on the first, and the LFS itself gets updated on the second. The LVM also checks that the image will fit in the allocated LFS region before loading, but you can also use the `-m` option to throw a compile error if the image is too large, for example `-m 0x10000` will raise an error if the image will not load into a 64Kb regions. -- **Absolute**. This is selected by the `-a ` option. Here the compiler fixes all addresses relative to the base address specified. This allows an LFS absolute image to be loaded directly into the ESP flash using a tool such as `esptool.py`. +- **Absolute**. This is selected by the `-a ` option. Here the compiler fixes all addresses relative to the base address specified. This allows an LFS absolute image to be loaded directly into the ESP flash using a tool such as `esptool.py`. _Note that the new NodeMCU loader uses the `-f` compact relocatable form and does relocation based on the Partition Table, so this option is deprecated and will be removed in future releases. These two modes target two separate use cases: the compact relocatable format facilitates simple OTA updates to an LFS based Lua application; the absolute format facilitates factory installation of LFS based applications. Also note that the `app/lua/luac_cross` make and Makefile can be executed to build -just the `luac.cross` image. You must first ensure that the following options in -`app/include/user_config.h` are matched to your target configuration: +just the `luac.cross` image. You must first ensure that the following option in +`app/include/user_config.h` is matched to your target configuration: ```c //#define LUA_NUMBER_INTEGRAL // uncomment if you want an integer build -//#define LUA_FLASH_STORE 0x10000 // uncomment if you LFS support ``` +Note that the use of LFS and the LFS region size is now configured through the partition table. + Developers have successfully built this on Linux (including docker builds), MacOS, Win10/WSL and WinX/Cygwin. diff --git a/docs/getting-started.md b/docs/getting-started.md index 8dbea439..4c3a76ae 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -226,7 +226,7 @@ Detailed instructions available in the image's README. As for available config o ### For LFS -1. In `app/include/user_config.h` uncomment `#define LUA_FLASH_STORE 0x10000` and adjust the size if necessary. +1. In `app/include/user_config.h` edit the line `#define LUA_FLASH_STORE 0x0` and adjust the size to that needed. Note that this must be a multiple of 4Kb. 2. Build as you would otherwise build with this image (i.e. see its README) [↑ back to matrix](#task-os-selector) @@ -238,24 +238,25 @@ _Note that this Docker image is not an official NodeMCU offering. It's maintaine A local copy of `luac.cross` is only needed if you want to compile the Lua files into an LFS image yourself and you are _not_ using Docker. ### Windows -Windows 10 users can install and use the Windows Subsystem for Linux (WSL). Alternatively all Windows users can [install Cygwin](https://www.cygwin.com/install.html) (only Cygwin core + **gcc-core** + **gnu make**). Either way, you will need a copy of the `luac.cross` compiler: - -- You can either download this from Terry's fileserver. The [ELF variant](http://files.ellisons.org.uk/esp8266/luac.cross) is used for all recent Linux and WSL flavours, or the [cygwin binary](http://files.ellisons.org.uk/esp8266/luac.cross.cygwin)) for the Cygwin environment. -- Or you can compile it yourself by downloading the current NodeMCU sources (this [ZIPfile](https://github.com/nodemcu/nodemcu-firmware/archive/master.zip)); edit the `app/includes/user_config.h` file and then `cd` to the `app/lua/luac_cross` and run make to build the compiler in the NodeMCU firmware root directory. Note that the `luac.cross` make only needs the host toolchain which is installed by default. - -### macOS - -TBD - -1. `$ cd app/lua/luac_cross` -2. `$ make` +Windows users can compile a local copy of the `luac.cross` executable for use on a development PC. To this you need: +- To download the current NodeMCU sources (this [dev ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/dev.zip) or [master ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/master.zip)) and unpack into a local folder, say `C:\nodemcu-firmware`; choose the master / dev versions to match the firmware version that you want to use. If you want an Integer buld then edit the `app/includes/user_config.h` file to select this. +- Choose a preferred toolchain to build your `luac.cross` executable. You have a number of options here: + - If you are a Windows 10 user with the Windows Subsystem for Linux (WSL) already installed, then this is a Linux environment so you can follow the [Linux build instructions](#Linux) below. + - A less resource intensive option which works on all Windows OS variants is to use Cygwin or MinGW, which are varaint ports of the [GNU Compiler Collection](https://gcc.gnu.org/) to Windows and which can both compile to native Windows executables. In the case of Cygwin, [install Cygwin](https://www.cygwin.com/install.html) (selecting the Cygwin core + **gcc-core** + **gnu make** in the install menu). In the case of MinGW you again only need a very basic C build environment so [install the MINGW](http://mingw.org/wiki/InstallationHOWTOforMinGW); you only need the core GCC and mingw32-make. Both both these create a **Cmd** prompt which paths in the relevant GCC toolchain. Switch to the `app/lua/luac_cross` and run make to build the compiler in the NodeMCU firmware root directory. You do this by rning `make` in Cygwin and `mingw32-make -f mingw32-Makefile.mak` in MinGW. + - If you can C development experience on the PC and a version of the MS Visual Studio on your PC then you can also simply build the image using the supplied MS project file. +- Once you have a built `luac.cross` executable, then you can use this to compile Lua code into an LFS image. You might wish to move this out of the nodemcu-firmware hierarchy, since this folder hierarchy is no longer required and can be trashed. ### Linux -TBD +- Ensure that you have a "build essential" GCC toolchain installed. +- Download the current NodeMCU sources (this [dev ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/dev.zip) or [master ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/master.zip)) and unpack into a local folder; choose the master / dev versions to match the firmware version that you want to use. If you want an Integer buld then edit the `app/includes/user_config.h` file to select this. +- Change directory to the `app/lua/luac_cross` sub-folder +- Run `make` to build the executable. +- Once you have a built `luac.cross` executable, then you can use this to compile Lua code into an LFS image. You might wish to move this out of the nodemcu-firmware hierarchy, since this folder hierarchy is no longer required and can be trashed. -1. `$ cd app/lua/luac_cross` -2. `$ make` +### macOS + +As for [Linux](#linux) [↑ back to matrix](#task-os-selector) diff --git a/docs/modules/node.md b/docs/modules/node.md index c2c29f83..5d6a132e 100644 --- a/docs/modules/node.md +++ b/docs/modules/node.md @@ -183,7 +183,7 @@ Returns the function reference for a function in the [LFS (Lua Flash Store)](../ `modulename` The name of the module to be loaded. If this is `nil` or invalid then an info list is returned #### Returns -- In the case where the LFS in not loaded, `node.flashindex` evaluates to `nil`, followed by the flash and mapped base addresss of the LFS +- In the case where the LFS in not loaded, `node.flashindex` evaluates to `nil`, followed by the flash mapped base addresss of the LFS, its flash offset, and the size of the LFS. - If the LFS is loaded and the function is called with the name of a valid module in the LFS, then the function is returned in the same way the `load()` and the other Lua load functions do. - Otherwise an extended info list is returned: the Unix time of the LFS build, the flash and mapped base addresses of the LFS and its current length, and an array of the valid module names in the LFS. diff --git a/ld/nodemcu.ld b/ld/nodemcu.ld index 154f2f67..1b329d76 100644 --- a/ld/nodemcu.ld +++ b/ld/nodemcu.ld @@ -5,6 +5,7 @@ MEMORY dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 + iram0_0_seg : org = 0x4010E000, len = 0x2000 irom0_0_seg : org = 0x40210000, len = 0xE0000 } @@ -14,12 +15,13 @@ PHDRS dram0_0_phdr PT_LOAD; dram0_0_bss_phdr PT_LOAD; iram1_0_phdr PT_LOAD; + iram0_0_phdr PT_LOAD; irom0_0_phdr PT_LOAD; } /* Default entry point: */ -ENTRY(user_start_trampoline) +ENTRY(call_user_start) EXTERN(_DebugExceptionVector) EXTERN(_DoubleExceptionVector) EXTERN(_KernelExceptionVector) @@ -221,11 +223,21 @@ SECTIONS _lit4_end = ABSOLUTE(.); } >iram1_0_seg :iram1_0_phdr + .pre_init_ram : ALIGN(0x1000) + { + _iram0_start = ABSOLUTE(.); + *(*.pre_init) + _iram0_end = ABSOLUTE(.); + } >iram0_0_seg :iram0_0_phdr + .irom0.text : ALIGN(0x1000) { _irom0_text_start = ABSOLUTE(.); + *(.irom0.ptable) + . = ALIGN(0x1000); *(.servercert.flash) *(.clientcert.flash) + . = ALIGN(0x1000); *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) *(.literal .text .literal.* .text.*) *(.rodata*) diff --git a/sdk-overrides/include/mem.h b/sdk-overrides/include/mem.h deleted file mode 100644 index 1a2f91ec..00000000 --- a/sdk-overrides/include/mem.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _SDK_OVERRIDE_MEM_H_ -#define _SDK_OVERRIDE_MEM_H_ - -void *pvPortMalloc (size_t sz, const char *, unsigned); -void vPortFree (void *p, const char *, unsigned); -void *pvPortZalloc (size_t sz, const char *, unsigned); -void *pvPortRealloc (void *p, size_t n, const char *, unsigned); - -#include_next "mem.h" - -#endif diff --git a/tools/esptool.py b/tools/esptool.py deleted file mode 100755 index 38ffb724..00000000 --- a/tools/esptool.py +++ /dev/null @@ -1,1236 +0,0 @@ -#!/usr/bin/env python -# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21 -# -# ESP8266 ROM Bootloader Utility -# https://github.com/themadinventor/esptool -# -# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, other contributors as noted. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin -# Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import argparse -import hashlib -import inspect -import json -import os -import serial -import struct -import subprocess -import sys -import tempfile -import time - - -__version__ = "1.2-dev" - - -class ESPROM(object): - # These are the currently known commands supported by the ROM - ESP_FLASH_BEGIN = 0x02 - ESP_FLASH_DATA = 0x03 - ESP_FLASH_END = 0x04 - ESP_MEM_BEGIN = 0x05 - ESP_MEM_END = 0x06 - ESP_MEM_DATA = 0x07 - ESP_SYNC = 0x08 - ESP_WRITE_REG = 0x09 - ESP_READ_REG = 0x0a - - # Maximum block sized for RAM and Flash writes, respectively. - ESP_RAM_BLOCK = 0x1800 - ESP_FLASH_BLOCK = 0x400 - - # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. - ESP_ROM_BAUD = 115200 - - # First byte of the application image - ESP_IMAGE_MAGIC = 0xe9 - - # Initial state for the checksum routine - ESP_CHECKSUM_MAGIC = 0xef - - # OTP ROM addresses - ESP_OTP_MAC0 = 0x3ff00050 - ESP_OTP_MAC1 = 0x3ff00054 - ESP_OTP_MAC3 = 0x3ff0005c - - # Flash sector size, minimum unit of erase. - ESP_FLASH_SECTOR = 0x1000 - - def __init__(self, port=0, baud=ESP_ROM_BAUD): - self._port = serial.Serial(port) - self._slip_reader = slip_reader(port) - # setting baud rate in a separate step is a workaround for - # CH341 driver on some Linux versions (this opens at 9600 then - # sets), shouldn't matter for other platforms/drivers. See - # https://github.com/themadinventor/esptool/issues/44#issuecomment-107094446 - self._port.baudrate = baud - - """ Read a SLIP packet from the serial port """ - def read(self): - return self._slip_reader.next() - - """ Write bytes to the serial port while performing SLIP escaping """ - def write(self, packet): - buf = '\xc0' \ - + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \ - + '\xc0' - self._port.write(buf) - - """ Calculate checksum of a blob, as it is defined by the ROM """ - @staticmethod - def checksum(data, state=ESP_CHECKSUM_MAGIC): - for b in data: - state ^= ord(b) - return state - - """ Send a request and read the response """ - def command(self, op=None, data=None, chk=0): - if op is not None: - pkt = struct.pack('> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) - elif ((mac1 >> 16) & 0xff) == 0: - oui = (0x18, 0xfe, 0x34) - elif ((mac1 >> 16) & 0xff) == 1: - oui = (0xac, 0xd0, 0x74) - else: - raise FatalError("Unknown OUI") - return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) - - """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ - def chip_id(self): - id0 = self.read_reg(self.ESP_OTP_MAC0) - id1 = self.read_reg(self.ESP_OTP_MAC1) - return (id0 >> 24) | ((id1 & 0xffffff) << 8) - - """ Read SPI flash manufacturer and device id """ - def flash_id(self): - self.flash_begin(0, 0) - self.write_reg(0x60000240, 0x0, 0xffffffff) - self.write_reg(0x60000200, 0x10000000, 0xffffffff) - flash_id = self.read_reg(0x60000240) - self.flash_finish(False) - return flash_id - - """ Abuse the loader protocol to force flash to be left in write mode """ - def flash_unlock_dio(self): - # Enable flash write mode - self.flash_begin(0, 0) - # Reset the chip rather than call flash_finish(), which would have - # write protected the chip again (why oh why does it do that?!) - self.mem_begin(0,0,0,0x40100000) - self.mem_finish(0x40000080) - - """ Perform a chip erase of SPI flash """ - def flash_erase(self): - # Trick ROM to initialize SFlash - self.flash_begin(0, 0) - - # This is hacky: we don't have a custom stub, instead we trick - # the bootloader to jump to the SPIEraseChip() routine and then halt/crash - # when it tries to boot an unconfigured system. - self.mem_begin(0,0,0,0x40100000) - self.mem_finish(0x40004984) - - # Yup - there's no good way to detect if we succeeded. - # It it on the other hand unlikely to fail. - - def run_stub(self, stub, params, read_output=True): - stub = dict(stub) - stub['code'] = unhexify(stub['code']) - if 'data' in stub: - stub['data'] = unhexify(stub['data']) - - if stub['num_params'] != len(params): - raise FatalError('Stub requires %d params, %d provided' - % (stub['num_params'], len(params))) - - params = struct.pack('<' + ('I' * stub['num_params']), *params) - pc = params + stub['code'] - - # Upload - self.mem_begin(len(pc), 1, len(pc), stub['params_start']) - self.mem_block(pc, 0) - if 'data' in stub: - self.mem_begin(len(stub['data']), 1, len(stub['data']), stub['data_start']) - self.mem_block(stub['data'], 0) - self.mem_finish(stub['entry']) - - if read_output: - print 'Stub executed, reading response:' - while True: - p = self.read() - print hexify(p) - if p == '': - return - - -class ESPBOOTLOADER(object): - """ These are constants related to software ESP bootloader, working with 'v2' image files """ - - # First byte of the "v2" application image - IMAGE_V2_MAGIC = 0xea - - # First 'segment' value in a "v2" application image, appears to be a constant version value? - IMAGE_V2_SEGMENT = 4 - - -def LoadFirmwareImage(filename): - """ Load a firmware image, without knowing what kind of file (v1 or v2) it is. - - Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2). - """ - with open(filename, 'rb') as f: - magic = ord(f.read(1)) - f.seek(0) - if magic == ESPROM.ESP_IMAGE_MAGIC: - return ESPFirmwareImage(f) - elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: - return OTAFirmwareImage(f) - else: - raise FatalError("Invalid image magic number: %d" % magic) - - -class BaseFirmwareImage(object): - """ Base class with common firmware image functions """ - def __init__(self): - self.segments = [] - self.entrypoint = 0 - - def add_segment(self, addr, data, pad_to=4): - """ Add a segment to the image, with specified address & data - (padded to a boundary of pad_to size) """ - # Data should be aligned on word boundary - l = len(data) - if l % pad_to: - data += b"\x00" * (pad_to - l % pad_to) - if l > 0: - self.segments.append((addr, len(data), data)) - - def load_segment(self, f, is_irom_segment=False): - """ Load the next segment from the image file """ - (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: - raise FatalError('Suspicious segment 0x%x, length %d' % (offset, size)) - segment_data = f.read(size) - if len(segment_data) < size: - raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data))) - segment = (offset, size, segment_data) - self.segments.append(segment) - return segment - - def save_segment(self, f, segment, checksum=None): - """ Save the next segment to the image file, return next checksum value if provided """ - (offset, size, data) = segment - f.write(struct.pack(' 16: - raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) - - for i in xrange(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - def save(self, filename): - with open(filename, 'wb') as f: - self.write_v1_header(f, self.segments) - checksum = ESPROM.ESP_CHECKSUM_MAGIC - for segment in self.segments: - checksum = self.save_segment(f, segment, checksum) - self.append_checksum(f, checksum) - - -class OTAFirmwareImage(BaseFirmwareImage): - """ 'Version 2' firmware image, segments loaded by software bootloader stub - (ie Espressif bootloader or rboot) - """ - def __init__(self, load_file=None): - super(OTAFirmwareImage, self).__init__() - self.version = 2 - if load_file is not None: - (magic, segments, first_flash_mode, first_flash_size_freq, first_entrypoint) = struct.unpack(' 16: - raise FatalError('Invalid V2 second header magic=%d segments=%d' % (magic, segments)) - - # load all the usual segments - for _ in xrange(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - def save(self, filename): - with open(filename, 'wb') as f: - # Save first header for irom0 segment - f.write(struct.pack(' 0: - esp._port.baudrate = baud_rate - # Read the greeting. - p = esp.read() - if p != 'OHAI': - raise FatalError('Failed to connect to the flasher (got %s)' % hexify(p)) - - def flash_write(self, addr, data, show_progress=False): - assert addr % self._esp.ESP_FLASH_SECTOR == 0, 'Address must be sector-aligned' - assert len(data) % self._esp.ESP_FLASH_SECTOR == 0, 'Length must be sector-aligned' - sys.stdout.write('Writing %d @ 0x%x... ' % (len(data), addr)) - sys.stdout.flush() - self._esp.write(struct.pack(' length: - raise FatalError('Read more than expected') - p = self._esp.read() - if len(p) != 16: - raise FatalError('Expected digest, got: %s' % hexify(p)) - expected_digest = hexify(p).upper() - digest = hashlib.md5(data).hexdigest().upper() - print - if digest != expected_digest: - raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) - p = self._esp.read() - if len(p) != 1: - raise FatalError('Expected status, got: %s' % hexify(p)) - status_code = struct.unpack(', ) or a single -# argument. - -def load_ram(esp, args): - image = LoadFirmwareImage(args.filename) - - print 'RAM boot...' - for (offset, size, data) in image.segments: - print 'Downloading %d bytes at %08x...' % (size, offset), - sys.stdout.flush() - esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) - - seq = 0 - while len(data) > 0: - esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) - data = data[esp.ESP_RAM_BLOCK:] - seq += 1 - print 'done!' - - print 'All segments done, executing at %08x' % image.entrypoint - esp.mem_finish(image.entrypoint) - - -def read_mem(esp, args): - print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) - - -def write_mem(esp, args): - esp.write_reg(args.address, args.value, args.mask, 0) - print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) - - -def dump_mem(esp, args): - f = file(args.filename, 'wb') - for i in xrange(args.size / 4): - d = esp.read_reg(args.address + (i * 4)) - f.write(struct.pack('> 8) & 0xff, (flash_id >> 16) & 0xff) - - -def read_flash(esp, args): - flasher = CesantaFlasher(esp, args.baud) - t = time.time() - data = flasher.flash_read(args.address, args.size, not args.no_progress) - t = time.time() - t - print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' - % (len(data), args.address, t, len(data) / t * 8 / 1000)) - file(args.filename, 'wb').write(data) - - -def _verify_flash(flasher, args, flash_params=None): - differences = False - for address, argfile in args.addr_filename: - image = argfile.read() - argfile.seek(0) # rewind in case we need it again - if address == 0 and image[0] == '\xe9' and flash_params is not None: - image = image[0:2] + flash_params + image[4:] - image_size = len(image) - print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name) - # Try digest first, only read if there are differences. - digest, _ = flasher.flash_digest(address, image_size) - digest = hexify(digest).upper() - expected_digest = hashlib.md5(image).hexdigest().upper() - if digest == expected_digest: - print '-- verify OK (digest matched)' - continue - else: - differences = True - if getattr(args, 'diff', 'no') != 'yes': - print '-- verify FAILED (digest mismatch)' - continue - - flash = flasher.flash_read(address, image_size) - assert flash != image - diff = [i for i in xrange(image_size) if flash[i] != image[i]] - print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0]) - for d in diff: - print ' %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d])) - if differences: - raise FatalError("Verify failed.") - - -def verify_flash(esp, args, flash_params=None): - flasher = CesantaFlasher(esp) - _verify_flash(flasher, args, flash_params) - - -def version(args): - print __version__ - -# -# End of operations functions -# - - -def main(): - parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') - - parser.add_argument( - '--port', '-p', - help='Serial port device', - default=os.environ.get('ESPTOOL_PORT', '/dev/ttyUSB0')) - - parser.add_argument( - '--baud', '-b', - help='Serial port baud rate used when flashing/reading', - type=arg_auto_int, - default=os.environ.get('ESPTOOL_BAUD', ESPROM.ESP_ROM_BAUD)) - - subparsers = parser.add_subparsers( - dest='operation', - help='Run esptool {command} -h for additional help') - - parser_load_ram = subparsers.add_parser( - 'load_ram', - help='Download an image to RAM and execute') - parser_load_ram.add_argument('filename', help='Firmware image') - - parser_dump_mem = subparsers.add_parser( - 'dump_mem', - help='Dump arbitrary memory to disk') - parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) - parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_dump_mem.add_argument('filename', help='Name of binary dump') - - parser_read_mem = subparsers.add_parser( - 'read_mem', - help='Read arbitrary memory location') - parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) - - parser_write_mem = subparsers.add_parser( - 'write_mem', - help='Read-modify-write to arbitrary memory location') - parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) - parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) - parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int) - - def add_spi_flash_subparsers(parent): - """ Add common parser arguments for SPI flash properties """ - parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', - choices=['40m', '26m', '20m', '80m'], - default=os.environ.get('ESPTOOL_FF', '40m')) - parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', - choices=['qio', 'qout', 'dio', 'dout'], - default=os.environ.get('ESPTOOL_FM', 'qio')) - parent.add_argument('--flash_size', '-fs', help='SPI Flash size in Mbit', type=str.lower, - choices=['4m', '2m', '8m', '16m', '32m', '16m-c1', '32m-c1', '32m-c2', '64m', '128m'], - default=os.environ.get('ESPTOOL_FS', '4m')) - - parser_write_flash = subparsers.add_parser( - 'write_flash', - help='Write a binary blob to flash') - parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', - action=AddrFilenamePairAction) - add_spi_flash_subparsers(parser_write_flash) - parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') - - subparsers.add_parser( - 'run', - help='Run application code in flash') - - parser_image_info = subparsers.add_parser( - 'image_info', - help='Dump headers from an application image') - parser_image_info.add_argument('filename', help='Image file to parse') - - parser_make_image = subparsers.add_parser( - 'make_image', - help='Create an application image from binary files') - parser_make_image.add_argument('output', help='Output image file') - parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') - parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) - parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) - - parser_elf2image = subparsers.add_parser( - 'elf2image', - help='Create an application image from ELF file') - parser_elf2image.add_argument('input', help='Input ELF file') - parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) - parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') - add_spi_flash_subparsers(parser_elf2image) - - subparsers.add_parser( - 'read_mac', - help='Read MAC address from OTP ROM') - - subparsers.add_parser( - 'chip_id', - help='Read Chip ID from OTP ROM') - - subparsers.add_parser( - 'flash_id', - help='Read SPI flash manufacturer and device ID') - - parser_read_flash = subparsers.add_parser( - 'read_flash', - help='Read SPI flash content') - parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) - parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_read_flash.add_argument('filename', help='Name of binary dump') - parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - - parser_verify_flash = subparsers.add_parser( - 'verify_flash', - help='Verify a binary blob against flash') - parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', - action=AddrFilenamePairAction) - parser_verify_flash.add_argument('--diff', '-d', help='Show differences', - choices=['no', 'yes'], default='no') - - subparsers.add_parser( - 'erase_flash', - help='Perform Chip Erase on SPI flash') - - subparsers.add_parser( - 'version', help='Print esptool version') - - # internal sanity check - every operation matches a module function of the same name - for operation in subparsers.choices.keys(): - assert operation in globals(), "%s should be a module function" % operation - - args = parser.parse_args() - - print 'esptool.py v%s' % __version__ - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPROM class. - - operation_func = globals()[args.operation] - operation_args,_,_,_ = inspect.getargspec(operation_func) - if operation_args[0] == 'esp': # operation function takes an ESPROM connection object - initial_baud = min(ESPROM.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate - esp = ESPROM(args.port, initial_baud) - esp.connect() - operation_func(esp, args) - else: - operation_func(args) - - -class AddrFilenamePairAction(argparse.Action): - """ Custom parser class for the address/filename pairs passed as arguments """ - def __init__(self, option_strings, dest, nargs='+', **kwargs): - super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - # validate pair arguments - pairs = [] - for i in range(0,len(values),2): - try: - address = int(values[i],0) - except ValueError as e: - raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) - try: - argfile = open(values[i + 1], 'rb') - except IOError as e: - raise argparse.ArgumentError(self, e) - except IndexError: - raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') - pairs.append((address, argfile)) - setattr(namespace, self.dest, pairs) - -# This is "wrapped" stub_flasher.c, to be loaded using run_stub. -_CESANTA_FLASHER_STUB = """\ -{"code_start": 1074790404, "code": "080000601C000060000000601000006031FCFF71FCFF\ -81FCFFC02000680332D218C020004807404074DCC48608005823C0200098081BA5A9239245005803\ -1B555903582337350129230B446604DFC6F3FF21EEFFC0200069020DF0000000010078480040004A\ -0040B449004012C1F0C921D911E901DD0209312020B4ED033C2C56C2073020B43C3C56420701F5FF\ -C000003C4C569206CD0EEADD860300202C4101F1FFC0000056A204C2DCF0C02DC0CC6CCAE2D1EAFF\ -0606002030F456D3FD86FBFF00002020F501E8FFC00000EC82D0CCC0C02EC0C73DEB2ADC46030020\ -2C4101E1FFC00000DC42C2DCF0C02DC056BCFEC602003C5C8601003C6C4600003C7C08312D0CD811\ -C821E80112C1100DF0000C180000140010400C0000607418000064180000801800008C1800008418\ -0000881800009018000018980040880F0040A80F0040349800404C4A0040740F0040800F0040980F\ -00400099004012C1E091F5FFC961CD0221EFFFE941F9310971D9519011C01A223902E2D1180C0222\ -6E1D21E4FF31E9FF2AF11A332D0F42630001EAFFC00000C030B43C2256A31621E1FF1A2228022030\ -B43C3256B31501ADFFC00000DD023C4256ED1431D6FF4D010C52D90E192E126E0101DDFFC0000021\ -D2FF32A101C020004802303420C0200039022C0201D7FFC00000463300000031CDFF1A333803D023\ -C03199FF27B31ADC7F31CBFF1A3328030198FFC0000056C20E2193FF2ADD060E000031C6FF1A3328\ -030191FFC0000056820DD2DD10460800000021BEFF1A2228029CE231BCFFC020F51A33290331BBFF\ -C02C411A332903C0F0F4222E1D22D204273D9332A3FFC02000280E27B3F721ABFF381E1A2242A400\ -01B5FFC00000381E2D0C42A40001B3FFC0000056120801B2FFC00000C02000280EC2DC0422D2FCC0\ -2000290E01ADFFC00000222E1D22D204226E1D281E22D204E7B204291E860000126E012198FF32A0\ -042A21C54C003198FF222E1D1A33380337B202C6D6FF2C02019FFFC000002191FF318CFF1A223A31\ -019CFFC00000218DFF1C031A22C549000C02060300003C528601003C624600003C72918BFF9A1108\ -71C861D851E841F83112C1200DF00010000068100000581000007010000074100000781000007C10\ -0000801000001C4B0040803C004091FDFF12C1E061F7FFC961E941F9310971D9519011C01A662906\ -21F3FFC2D1101A22390231F2FF0C0F1A33590331EAFFF26C1AED045C2247B3028636002D0C016DFF\ -C0000021E5FF41EAFF2A611A4469040622000021E4FF1A222802F0D2C0D7BE01DD0E31E0FF4D0D1A\ -3328033D0101E2FFC00000561209D03D2010212001DFFFC000004D0D2D0C3D01015DFFC0000041D5\ -FFDAFF1A444804D0648041D2FF1A4462640061D1FF106680622600673F1331D0FF10338028030C43\ -853A002642164613000041CAFF222C1A1A444804202FC047328006F6FF222C1A273F3861C2FF222C\ -1A1A6668066732B921BDFF3D0C1022800148FFC0000021BAFF1C031A2201BFFFC000000C02460300\ -5C3206020000005C424600005C5291B7FF9A110871C861D851E841F83112C1200DF0B0100000C010\ -0000D010000012C1E091FEFFC961D951E9410971F931CD039011C0ED02DD0431A1FF9C1422A06247\ -B302062D0021F4FF1A22490286010021F1FF1A223902219CFF2AF12D0F011FFFC00000461C0022D1\ -10011CFFC0000021E9FFFD0C1A222802C7B20621E6FF1A22F8022D0E3D014D0F0195FFC000008C52\ -22A063C6180000218BFF3D01102280F04F200111FFC00000AC7D22D1103D014D0F010DFFC0000021\ -D6FF32D110102280010EFFC0000021D3FF1C031A220185FFC00000FAEEF0CCC056ACF821CDFF317A\ -FF1A223A310105FFC0000021C9FF1C031A22017CFFC000002D0C91C8FF9A110871C861D851E841F8\ -3112C1200DF0000200600000001040020060FFFFFF0012C1E00C02290131FAFF21FAFF026107C961\ -C02000226300C02000C80320CC10564CFF21F5FFC02000380221F4FF20231029010C432D010163FF\ -C0000008712D0CC86112C1200DF00080FE3F8449004012C1D0C9A109B17CFC22C1110C13C51C0026\ -1202463000220111C24110B68202462B0031F5FF3022A02802A002002D011C03851A0066820A2801\ -32210105A6FF0607003C12C60500000010212032A01085180066A20F2221003811482105B3FF2241\ -10861A004C1206FDFF2D011C03C5160066B20E280138114821583185CFFF06F7FF005C1286F5FF00\ -10212032A01085140066A20D2221003811482105E1FF06EFFF0022A06146EDFF45F0FFC6EBFF0000\ -01D2FFC0000006E9FF000C022241100C1322C110C50F00220111060600000022C1100C13C50E0022\ -011132C2FA303074B6230206C8FF08B1C8A112C1300DF0000000000010404F484149007519031027\ -000000110040A8100040BC0F0040583F0040CC2E00401CE20040D83900408000004021F4FF12C1E0\ -C961C80221F2FF097129010C02D951C91101F4FFC0000001F3FFC00000AC2C22A3E801F2FFC00000\ -21EAFFC031412A233D0C01EFFFC000003D0222A00001EDFFC00000C1E4FF2D0C01E8FFC000002D01\ -32A004450400C5E7FFDD022D0C01E3FFC00000666D1F4B2131DCFF4600004B22C0200048023794F5\ -31D9FFC0200039023DF08601000001DCFFC000000871C861D85112C1200DF000000012C1F0026103\ -01EAFEC00000083112C1100DF000643B004012C1D0E98109B1C9A1D991F97129013911E2A0C001FA\ -FFC00000CD02E792F40C0DE2A0C0F2A0DB860D00000001F4FFC00000204220E71240F7921C226102\ -01EFFFC0000052A0DC482157120952A0DD571205460500004D0C3801DA234242001BDD3811379DC5\ -C6000000000C0DC2A0C001E3FFC00000C792F608B12D0DC8A1D891E881F87112C1300DF00000", "\ -entry": 1074792180, "num_params": 1, "params_start": 1074790400, "data": "FE0510\ -401A0610403B0610405A0610407A061040820610408C0610408C061040", "data_start": 10736\ -43520} -""" - -if __name__ == '__main__': - try: - main() - except FatalError as e: - print '\nA fatal error occurred: %s' % e - sys.exit(2) diff --git a/tools/nodemcu-partition.py b/tools/nodemcu-partition.py new file mode 100755 index 00000000..1c9ab461 --- /dev/null +++ b/tools/nodemcu-partition.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python +# +# ESP8266 LFS Loader Utility +# +# Copyright (C) 2019 Terry Ellison, NodeMCU Firmware Community Project. drawing +# heavily from and including content from esptool.py with full acknowledgement +# under GPL 2.0, with said content: Copyright (C) 2014-2016 Fredrik Ahlberg, Angus +# Gratton, Espressif Systems (Shanghai) PTE LTD, other contributors as noted. +# https://github.com/espressif/esptool +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import os +import sys +sys.path.append(os.path.realpath(os.path.dirname(__file__) + '/toolchains/')) +import esptool + +import io +import tempfile +import shutil + +from pprint import pprint + +import argparse +import gzip +import copy +import inspect +import struct +import string + +__version__ = '1.0' +__program__ = 'nodemcu-partition.py' +ROM0_Seg = 0x010000 +FLASH_PAGESIZE = 0x001000 +FLASH_BASE_ADDR = 0x40200000 +PARTITION_TYPES = { + 4: 'RF_CAL', + 5: 'PHY_DATA', + 6: 'SYSTEM_PARAMETER', + 101: 'EAGLEROM', + 102: 'IROM0TEXT', + 103: 'LFS0', + 104: 'LFS1', + 105: 'TLSCERT', + 106: 'SPIFFS0', + 107: 'SPIFFS1'} + +MAX_PT_SIZE = 20*3 +FLASH_SIG = 0xfafaa150 +FLASH_SIG_MASK = 0xfffffff0 +FLASH_SIG_ABSOLUTE = 0x00000001 +WORDSIZE = 4 +WORDBITS = 32 + +PACK_INT = struct.Struct(">= 1 + + return ''.join([PACK_INT.pack(i) for i in image]) + +def main(): + + def arg_auto_int(x): + ux = x.upper() + if "MB" in ux: + return int(ux[:ux.index("MB")]) * 1024 * 1024 + elif "KB" in ux: + return int(ux[:ux.index("KB")]) * 1024 + else: + return int(ux, 0) + + print('%s V%s' %(__program__, __version__)) + + # ---------- process the arguments ---------- # + + a = argparse.ArgumentParser( + description='%s V%s - ESP8266 NodeMCU Loader Utility' % + (__program__, __version__), + prog='esplfs') + a.add_argument('--port', '-p', help='Serial port device') + a.add_argument('--baud', '-b', type=arg_auto_int, + help='Serial port baud rate used when flashing/reading') + a.add_argument('--lfs-addr', '-la', dest="la", type=arg_auto_int, + help='(Overwrite) start address of LFS partition') + a.add_argument('--lfs-size', '-ls', dest="ls", type=arg_auto_int, + help='(Overwrite) length of LFS partition') + a.add_argument('--lfs-file', '-lf', dest="lf", help='LFS image file') + a.add_argument('--spiffs-addr', '-sa', dest="sa", type=arg_auto_int, + help='(Overwrite) start address of SPIFFS partition') + a.add_argument('--spiffs-size', '-ss', dest="ss", type=arg_auto_int, + help='(Overwrite) length of SPIFFS partition') + a.add_argument('--spiffs-file', '-sf', dest="sf", help='SPIFFS image file') + + arg = a.parse_args() + + if arg.lf is not None: + if not os.path.exists(arg.lf): + raise FatalError("LFS image %s does not exist" % arg.lf) + + if arg.sf is not None: + if not os.path.exists(arg.sf): + raise FatalError("SPIFFS image %s does not exist" % arg.sf) + + base = [] if arg.port is None else ['--port',arg.port] + if arg.baud is not None: base.extend(['--baud',arg.baud]) + + # ---------- Use esptool to read the PT ---------- # + + tmpdir = tempfile.mkdtemp() + pt_file = tmpdir + '/pt.dmp' + espargs = base+['--after', 'no_reset', 'read_flash', '--no-progress', + str(ROM0_Seg), str(FLASH_PAGESIZE), pt_file] + esptool.main(espargs) + + with open(pt_file,"rb") as f: + data = f.read() + + pt, pt_map, n = load_PT(data, arg) + n = n+1 + + odata = ''.join([PACK_INT.pack(pt[i]) for i in range(0,3*n)]) + \ + "\xFF" * len(data[3*4*n:]) + + # ---------- If the PT has changed then use esptool to rewrite it ---------- # + + if odata != data: + print("PT updated") + pt_file = tmpdir + '/opt.dmp' + with open(pt_file,"wb") as f: + f.write(odata) + espargs = base+['--after', 'no_reset', 'write_flash', '--no-progress', + str(ROM0_Seg), pt_file] + esptool.main(espargs) + + if arg.lf is not None: + i = pt_map['LFS0'] + la,ls = pt[i+1], pt[i+2] + + # ---------- Read and relocate the LFS image ---------- # + + with gzip.open(arg.lf) as f: + lfs = f.read() + lfs = relocate_lfs(lfs, la, ls) + + # ---------- Write to a temp file and use esptool to write it to flash ---------- # + + img_file = tmpdir + '/lfs.img' + espargs = base + ['write_flash', str(la), img_file] + with open(img_file,"wb") as f: + f.write(lfs) + esptool.main(espargs) + + if arg.sf is not None: + sa = pt[pt_map['SPIFFS0']+1] + + # ---------- Write to a temp file and use esptool to write it to flash ---------- # + + spiffs_file = arg.sf + espargs = base + ['', str(sa), spiffs_file] + esptool.main(espargs) + + # ---------- Clean up temp directory ---------- # + + espargs = base + ['--after', 'hard_reset', 'flash_id'] + esptool.main(espargs) + + shutil.rmtree(tmpdir) + +def _main(): + main() + +if __name__ == '__main__': + _main()