diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index e518755f6..4b031561a 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -31,6 +31,7 @@ #include "umm_malloc/umm_malloc.h" // #include "core_esp8266_vm.h" #include +#include "reboot_uart_dwnld.h" extern "C" { #include "user_interface.h" @@ -203,6 +204,15 @@ void EspClass::restart(void) esp_yield(); } +[[noreturn]] void EspClass::rebootIntoUartDownloadMode() +{ + wdtDisable(); + /* disable hardware watchdog */ + CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1); + + esp8266RebootIntoUartDownloadMode(); +} + uint16_t EspClass::getVcc(void) { esp8266::InterruptLock lock; diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 6d990e876..d96ec9b28 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -103,6 +103,12 @@ class EspClass { void reset(); void restart(); + /** + * @brief When calling this method the ESP8266 reboots into the UART download mode without + * the need of any external wiring. This is the same mode which can also be entered by + * pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266. + */ + [[noreturn]] void rebootIntoUartDownloadMode(); uint16_t getVcc(); uint32_t getChipId(); diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index a19835218..9a21868b3 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -6,6 +6,21 @@ #ifdef __cplusplus extern "C" { #endif +#include +#include +#include + +#define PERIPHS_DPORT_18 (PERIPHS_DPORT_BASEADDR + 0x018) +#define PERIPHS_DPORT_ICACHE_ENABLE (PERIPHS_DPORT_BASEADDR + 0x024) +/* When enabled 16K IRAM starting at 0x4010C000 is unmapped */ +#define ICACHE_ENABLE_FIRST_16K BIT3 +/* When enabled 16K IRAM starting at 0x40108000 is unmapped */ +#define ICACHE_ENABLE_SECOND_16K BIT4 +#define PERIPHS_HW_WDT (0x60000900) +#define PERIPHS_I2C_48 (0x60000a00 + 0x348) + + +extern void (*user_start_fptr)(); #ifndef XCHAL_EXCCAUSE_NUM // from tools/xtensa-lx106-elf/include/xtensa/config/core.h:629:#define XCHAL_EXCCAUSE_NUM 64 @@ -19,6 +34,12 @@ extern int rom_i2c_readReg_Mask(int, int, int, int, int); extern int uart_baudrate_detect(int, int); +/* SDK/Flash contains also an implementation of this function + * but for reboot into UART download mode the version from ROM + * has to be used because flash is not accessible. + */ +extern void rom_uart_div_modify(uint8 uart_no, uint32 DivLatchValue); + /* ROM function, uart_buff_switch(), is used to switch printing between UART0 and UART1. It updates a structure that only controls a select group of print @@ -40,6 +61,10 @@ extern void uart_buff_switch(uint8_t); */ extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +extern void user_uart_wait_tx_fifo_empty(uint32_t ch, uint32_t arg2); +extern void uartAttach(); +extern void Uart_Init(uint32_t uart_no); + extern void ets_delay_us(uint32_t us); #ifndef GDBSTUB_H @@ -206,6 +231,16 @@ extern fn_c_exception_handler_t _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM]; extern fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn); #endif +extern uint32_t Wait_SPI_Idle(SpiFlashChip *fc); +extern void Cache_Read_Disable(); +extern int32_t system_func1(uint32_t); +extern void clockgate_watchdog(uint32_t); +extern void pm_open_rf(); +extern void ets_install_uart_printf(uint32_t uart_no); +extern void UartDwnLdProc(uint8_t* ram_addr, uint32_t size, void (**user_start_ptr)()); +extern int boot_from_flash(); +extern void ets_run() __attribute__((noreturn)); + #ifdef __cplusplus }; #endif diff --git a/cores/esp8266/reboot_uart_dwnld.cpp b/cores/esp8266/reboot_uart_dwnld.cpp new file mode 100644 index 000000000..dce4cf0ae --- /dev/null +++ b/cores/esp8266/reboot_uart_dwnld.cpp @@ -0,0 +1,151 @@ +/* + ESP8266-specific implementation of the UART download mode + Copyright (c) 2021 Timo Wischer + All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + This implementation is based on the original implementation of the ROM. + It was shortend to reduce the memory usage. The complete version and the + development history can be found in: + https://github.com/twischer/Arduino/tree/reboot_uart_download_full + This might be usefull in case of issues. + */ +#include "reboot_uart_dwnld.h" +#include +#include +#include + + +static inline uint32_t __rsil_1() { + uint32_t program_state; + asm volatile("rsil %0, 1" : "=r" (program_state)); + return program_state; +} + +static inline void __wsr_intenable(uint32_t interupt_enable) { + asm volatile("wsr.intenable %0" :: "r" (interupt_enable)); +} + +static inline void __wsr_litbase(uint32_t literal_base) { + asm volatile("wsr.litbase %0" :: "r" (literal_base)); +} + +static inline void __wsr_ps(uint32_t program_state) { + asm volatile("wsr.ps %0" :: "r" (program_state)); +} + +static inline void __wsr_vecbase(uint32_t vector_base) { + asm volatile("wsr.vecbase %0" :: "r" (vector_base)); +} + +[[noreturn]] void ICACHE_RAM_ATTR esp8266UartDownloadMode() +{ + /* reverse engineered from system_restart_core() */ + /* Before disabling instruction cache and restoring instruction RAM to a + * power-on like state, SPI bus must be idle. + */ + Wait_SPI_Idle(flashchip); + + Cache_Read_Disable(); + /* This will disable the 32kB instruction cache and extend the IRAM by 32kB. + * Therefore the full 64kB of IRAM will be available for boot. + * Cache_Read_Enable() sets those bits but Cache_Read_Disable() does not clear + * them. On hardware reset those bits are cleared. Therefore clear them also + * for this reboot. + */ + CLEAR_PERI_REG_MASK(PERIPHS_DPORT_ICACHE_ENABLE, + ICACHE_ENABLE_FIRST_16K | ICACHE_ENABLE_SECOND_16K); + + /* reverse engineered from _ResetHandler() */ + /* disable all level 1 interrupts */ + __wsr_intenable(0); + /* Clear the literal base to use an offset of 0 for + * Load 32-bit PC-Relative(L32R) instructions + */ + __wsr_litbase(0); + asm volatile("rsync"); + + /* Set interrupt vector base address to system ROM */ + __wsr_vecbase(0x40000000); + /* Set interrupt level to 1. Therefore disable interrupts of level 1. + * Above levels like level 2,... might still be active if available + * on ESP8266. + */ + __rsil_1(); + + /* reverse engineered from _start() */ + /* Set stack pointer to upper end of data RAM */ + const uint32_t stack_pointer = 0x40000000; + asm volatile("mov a1, %0" :: "r" (stack_pointer)); + + /* Set the program state register + * Name Value Description + * Interrupt level disable 0 enable all interrupt levels + * Exception mode 0 normal operation + * User vector mode 1 user vector mode, exceptions need to switch stacks + * Privilege level 0 Set to Ring 0 + */ + __wsr_ps(0x20); + asm volatile("rsync"); + + /* reverse engineered from main() */ + const uint32_t uart_no = 0; + uartAttach(); + Uart_Init(uart_no); + ets_install_uart_printf(uart_no); + + /* reverse engineered from boot_from_something() */ + const uint16_t divlatch = uart_baudrate_detect(uart_no, 0); + rom_uart_div_modify(uart_no, divlatch); + UartDwnLdProc((uint8_t*)0x3fffa000, 0x2000, &user_start_fptr); + + /* reverse engineered from main() */ + if (user_start_fptr == NULL) { + if (boot_from_flash() != 0) { + ets_printf("boot_from_flash() failed\n"); + while (true); + } + } + + if (user_start_fptr) { + user_start_fptr(); + } + + ets_printf("user code done\n"); + ets_run(); +} + +[[noreturn]] void esp8266RebootIntoUartDownloadMode() +{ + /* reverse engineered from system_restart_local() */ + if (system_func1(0x4) == -1) { + clockgate_watchdog(0); + SET_PERI_REG_MASK(PERIPHS_DPORT_18, 0xffff00ff); + pm_open_rf(); + } + + user_uart_wait_tx_fifo_empty(0, 0x7a120); + user_uart_wait_tx_fifo_empty(1, 0x7a120); + ets_intr_lock(); + SET_PERI_REG_MASK(PERIPHS_DPORT_18, 0x7500); + CLEAR_PERI_REG_MASK(PERIPHS_DPORT_18, 0x7500); + SET_PERI_REG_MASK(PERIPHS_I2C_48, 0x2); + CLEAR_PERI_REG_MASK(PERIPHS_I2C_48, 0x2); + + esp8266UartDownloadMode(); +} + diff --git a/cores/esp8266/reboot_uart_dwnld.h b/cores/esp8266/reboot_uart_dwnld.h new file mode 100644 index 000000000..531471e2a --- /dev/null +++ b/cores/esp8266/reboot_uart_dwnld.h @@ -0,0 +1,23 @@ +/* + ESP8266-specific implementation of the UART download mode + Copyright (c) 2021 Timo Wischer + All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +[[noreturn]] void esp8266RebootIntoUartDownloadMode(); diff --git a/tools/sdk/ld/eagle.rom.addr.v6.ld b/tools/sdk/ld/eagle.rom.addr.v6.ld index e2cff957a..005dc4472 100644 --- a/tools/sdk/ld/eagle.rom.addr.v6.ld +++ b/tools/sdk/ld/eagle.rom.addr.v6.ld @@ -99,6 +99,7 @@ PROVIDE ( aes_decrypt_init = 0x40008ea4 ); PROVIDE ( aes_unwrap = 0x40009410 ); PROVIDE ( base64_decode = 0x40009648 ); PROVIDE ( base64_encode = 0x400094fc ); +PROVIDE ( boot_from_flash = 0x40001308 ); PROVIDE ( bzero = 0x4000de84 ); PROVIDE ( cmd_parse = 0x40000814 ); PROVIDE ( conv_str_decimal = 0x40000b24 ); @@ -278,6 +279,7 @@ PROVIDE ( rom_stop_tx_tone = 0x4000698c ); PROVIDE ( rom_tx_mac_disable = 0x40006a98 ); PROVIDE ( rom_tx_mac_enable = 0x40006ad4 ); PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c ); +PROVIDE ( rom_uart_div_modify = 0x400039d8 ); PROVIDE ( rom_write_rfpll_sdm = 0x400078dc ); PROVIDE ( roundup2 = 0x400031b4 ); PROVIDE ( rtc_enter_sleep = 0x40002870 ); @@ -353,5 +355,6 @@ PROVIDE ( Te0 = 0x3fffccf0 ); PROVIDE ( Td0 = 0x3fffd100 ); PROVIDE ( Td4s = 0x3fffd500); PROVIDE ( rcons = 0x3fffd0f0); +PROVIDE ( user_start_fptr = 0x3fffdcd0 ); PROVIDE ( UartDev = 0x3fffde10 ); PROVIDE ( flashchip = 0x3fffc714); diff --git a/tools/sdk/lib/NONOSDK221/libmain.a b/tools/sdk/lib/NONOSDK221/libmain.a index c0e70cafe..adf747690 100644 Binary files a/tools/sdk/lib/NONOSDK221/libmain.a and b/tools/sdk/lib/NONOSDK221/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK22x_190313/libmain.a b/tools/sdk/lib/NONOSDK22x_190313/libmain.a index a19b330c1..664031082 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190313/libmain.a and b/tools/sdk/lib/NONOSDK22x_190313/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK22x_190703/libmain.a b/tools/sdk/lib/NONOSDK22x_190703/libmain.a index 1213ceb8f..cd5fd1e84 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190703/libmain.a and b/tools/sdk/lib/NONOSDK22x_190703/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191024/libmain.a b/tools/sdk/lib/NONOSDK22x_191024/libmain.a index 58779578a..cd01e1a36 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191024/libmain.a and b/tools/sdk/lib/NONOSDK22x_191024/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191105/libmain.a b/tools/sdk/lib/NONOSDK22x_191105/libmain.a index 2bc985793..0f66ab6b0 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191105/libmain.a and b/tools/sdk/lib/NONOSDK22x_191105/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191122/libmain.a b/tools/sdk/lib/NONOSDK22x_191122/libmain.a index 77b7de1b9..260be180b 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191122/libmain.a and b/tools/sdk/lib/NONOSDK22x_191122/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK3V0/libmain.a b/tools/sdk/lib/NONOSDK3V0/libmain.a index e93201c52..fb5242f1b 100644 Binary files a/tools/sdk/lib/NONOSDK3V0/libmain.a and b/tools/sdk/lib/NONOSDK3V0/libmain.a differ diff --git a/tools/sdk/lib/fix_sdk_libs.sh b/tools/sdk/lib/fix_sdk_libs.sh index 178f08bd2..b6aea1275 100755 --- a/tools/sdk/lib/fix_sdk_libs.sh +++ b/tools/sdk/lib/fix_sdk_libs.sh @@ -2,6 +2,13 @@ set -e export PATH=../../xtensa-lx106-elf/bin:$PATH +VERSION=$(basename ${PWD}) + +addSymbol_system_func1() { + ADDRESS=$1 + xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o +} + # Remove mem_manager.o from libmain.a to use custom heap implementation, # and time.o to fix redefinition of time-related functions: @@ -14,5 +21,16 @@ xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_inte xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o + +if [[ ${VERSION} == "NONOSDK221" ]]; then + addSymbol_system_func1 "0x60" +elif [[ ${VERSION} == "NONOSDK22x"* ]]; then + addSymbol_system_func1 "0x54" +elif [[ ${VERSION} == "NONOSDK3"* ]]; then + addSymbol_system_func1 "0x60" +else + echo "WARN: Unknown address for system_func1() called by system_restart_local()" +fi + xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o rm -f eagle_lwip_if.o user_interface.o