Skip to content

Commit 83f5f29

Browse files
authored
Support to reboot into UART download mode (#7854)
without any external wiring. This patch introduces the new method Esp.rebootIntoUartDownloadMode() When the user calls this method the ESP8266 reboots into the UART download mode. In this mode the user can use esptool.py to flash a new firmware file. The following command was used to test it: $ esptool.py --before no_reset --after soft_reset --chip esp8266 \ --port /dev/ttyUSB0 --baud 460800 write_flash 0x0 firmware.bin The implementation is based on the original implementation in the boot ROM. Some parts of the original implementation can be found in [1]. This patch is a squashed and simplified version of [2]. The non squashed version might be helpful in case of debugging issues. [1] https://github.com/twischer/xtensa-subjects/blob/master/reversed/bootrom.c [2] https://github.com/twischer/Arduino/tree/reboot_uart_download_full Signed-off-by: Timo Wischer <twischer@freenet.de>
1 parent bc3daef commit 83f5f29

File tree

14 files changed

+246
-0
lines changed

14 files changed

+246
-0
lines changed

Diff for: cores/esp8266/Esp.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "umm_malloc/umm_malloc.h"
3232
// #include "core_esp8266_vm.h"
3333
#include <pgmspace.h>
34+
#include "reboot_uart_dwnld.h"
3435

3536
extern "C" {
3637
#include "user_interface.h"
@@ -203,6 +204,15 @@ void EspClass::restart(void)
203204
esp_yield();
204205
}
205206

207+
[[noreturn]] void EspClass::rebootIntoUartDownloadMode()
208+
{
209+
wdtDisable();
210+
/* disable hardware watchdog */
211+
CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1);
212+
213+
esp8266RebootIntoUartDownloadMode();
214+
}
215+
206216
uint16_t EspClass::getVcc(void)
207217
{
208218
esp8266::InterruptLock lock;

Diff for: cores/esp8266/Esp.h

+6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ class EspClass {
103103

104104
void reset();
105105
void restart();
106+
/**
107+
* @brief When calling this method the ESP8266 reboots into the UART download mode without
108+
* the need of any external wiring. This is the same mode which can also be entered by
109+
* pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266.
110+
*/
111+
[[noreturn]] void rebootIntoUartDownloadMode();
106112

107113
uint16_t getVcc();
108114
uint32_t getChipId();

Diff for: cores/esp8266/esp8266_undocumented.h

+35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@
66
#ifdef __cplusplus
77
extern "C" {
88
#endif
9+
#include <stdint.h>
10+
#include <eagle_soc.h>
11+
#include <spi_flash.h>
12+
13+
#define PERIPHS_DPORT_18 (PERIPHS_DPORT_BASEADDR + 0x018)
14+
#define PERIPHS_DPORT_ICACHE_ENABLE (PERIPHS_DPORT_BASEADDR + 0x024)
15+
/* When enabled 16K IRAM starting at 0x4010C000 is unmapped */
16+
#define ICACHE_ENABLE_FIRST_16K BIT3
17+
/* When enabled 16K IRAM starting at 0x40108000 is unmapped */
18+
#define ICACHE_ENABLE_SECOND_16K BIT4
19+
#define PERIPHS_HW_WDT (0x60000900)
20+
#define PERIPHS_I2C_48 (0x60000a00 + 0x348)
21+
22+
23+
extern void (*user_start_fptr)();
924

1025
#ifndef XCHAL_EXCCAUSE_NUM
1126
// 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);
1934

2035
extern int uart_baudrate_detect(int, int);
2136

37+
/* SDK/Flash contains also an implementation of this function
38+
* but for reboot into UART download mode the version from ROM
39+
* has to be used because flash is not accessible.
40+
*/
41+
extern void rom_uart_div_modify(uint8 uart_no, uint32 DivLatchValue);
42+
2243
/*
2344
ROM function, uart_buff_switch(), is used to switch printing between UART0 and
2445
UART1. It updates a structure that only controls a select group of print
@@ -40,6 +61,10 @@ extern void uart_buff_switch(uint8_t);
4061
*/
4162
extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
4263

64+
extern void user_uart_wait_tx_fifo_empty(uint32_t ch, uint32_t arg2);
65+
extern void uartAttach();
66+
extern void Uart_Init(uint32_t uart_no);
67+
4368
extern void ets_delay_us(uint32_t us);
4469

4570
#ifndef GDBSTUB_H
@@ -206,6 +231,16 @@ extern fn_c_exception_handler_t _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM];
206231
extern fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn);
207232
#endif
208233

234+
extern uint32_t Wait_SPI_Idle(SpiFlashChip *fc);
235+
extern void Cache_Read_Disable();
236+
extern int32_t system_func1(uint32_t);
237+
extern void clockgate_watchdog(uint32_t);
238+
extern void pm_open_rf();
239+
extern void ets_install_uart_printf(uint32_t uart_no);
240+
extern void UartDwnLdProc(uint8_t* ram_addr, uint32_t size, void (**user_start_ptr)());
241+
extern int boot_from_flash();
242+
extern void ets_run() __attribute__((noreturn));
243+
209244
#ifdef __cplusplus
210245
};
211246
#endif

Diff for: cores/esp8266/reboot_uart_dwnld.cpp

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
ESP8266-specific implementation of the UART download mode
3+
Copyright (c) 2021 Timo Wischer <twischer@freenet.de>
4+
All rights reserved.
5+
This file is part of the esp8266 core for Arduino environment.
6+
7+
This library is free software; you can redistribute it and/or
8+
modify it under the terms of the GNU Lesser General Public
9+
License as published by the Free Software Foundation; either
10+
version 2.1 of the License, or (at your option) any later version.
11+
12+
This library is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
Lesser General Public License for more details.
16+
17+
You should have received a copy of the GNU Lesser General Public
18+
License along with this library; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
21+
This implementation is based on the original implementation of the ROM.
22+
It was shortend to reduce the memory usage. The complete version and the
23+
development history can be found in:
24+
https://github.com/twischer/Arduino/tree/reboot_uart_download_full
25+
This might be usefull in case of issues.
26+
*/
27+
#include "reboot_uart_dwnld.h"
28+
#include <stdnoreturn.h>
29+
#include <user_interface.h>
30+
#include <esp8266_undocumented.h>
31+
32+
33+
static inline uint32_t __rsil_1() {
34+
uint32_t program_state;
35+
asm volatile("rsil %0, 1" : "=r" (program_state));
36+
return program_state;
37+
}
38+
39+
static inline void __wsr_intenable(uint32_t interupt_enable) {
40+
asm volatile("wsr.intenable %0" :: "r" (interupt_enable));
41+
}
42+
43+
static inline void __wsr_litbase(uint32_t literal_base) {
44+
asm volatile("wsr.litbase %0" :: "r" (literal_base));
45+
}
46+
47+
static inline void __wsr_ps(uint32_t program_state) {
48+
asm volatile("wsr.ps %0" :: "r" (program_state));
49+
}
50+
51+
static inline void __wsr_vecbase(uint32_t vector_base) {
52+
asm volatile("wsr.vecbase %0" :: "r" (vector_base));
53+
}
54+
55+
[[noreturn]] void ICACHE_RAM_ATTR esp8266UartDownloadMode()
56+
{
57+
/* reverse engineered from system_restart_core() */
58+
/* Before disabling instruction cache and restoring instruction RAM to a
59+
* power-on like state, SPI bus must be idle.
60+
*/
61+
Wait_SPI_Idle(flashchip);
62+
63+
Cache_Read_Disable();
64+
/* This will disable the 32kB instruction cache and extend the IRAM by 32kB.
65+
* Therefore the full 64kB of IRAM will be available for boot.
66+
* Cache_Read_Enable() sets those bits but Cache_Read_Disable() does not clear
67+
* them. On hardware reset those bits are cleared. Therefore clear them also
68+
* for this reboot.
69+
*/
70+
CLEAR_PERI_REG_MASK(PERIPHS_DPORT_ICACHE_ENABLE,
71+
ICACHE_ENABLE_FIRST_16K | ICACHE_ENABLE_SECOND_16K);
72+
73+
/* reverse engineered from _ResetHandler() */
74+
/* disable all level 1 interrupts */
75+
__wsr_intenable(0);
76+
/* Clear the literal base to use an offset of 0 for
77+
* Load 32-bit PC-Relative(L32R) instructions
78+
*/
79+
__wsr_litbase(0);
80+
asm volatile("rsync");
81+
82+
/* Set interrupt vector base address to system ROM */
83+
__wsr_vecbase(0x40000000);
84+
/* Set interrupt level to 1. Therefore disable interrupts of level 1.
85+
* Above levels like level 2,... might still be active if available
86+
* on ESP8266.
87+
*/
88+
__rsil_1();
89+
90+
/* reverse engineered from _start() */
91+
/* Set stack pointer to upper end of data RAM */
92+
const uint32_t stack_pointer = 0x40000000;
93+
asm volatile("mov a1, %0" :: "r" (stack_pointer));
94+
95+
/* Set the program state register
96+
* Name Value Description
97+
* Interrupt level disable 0 enable all interrupt levels
98+
* Exception mode 0 normal operation
99+
* User vector mode 1 user vector mode, exceptions need to switch stacks
100+
* Privilege level 0 Set to Ring 0
101+
*/
102+
__wsr_ps(0x20);
103+
asm volatile("rsync");
104+
105+
/* reverse engineered from main() */
106+
const uint32_t uart_no = 0;
107+
uartAttach();
108+
Uart_Init(uart_no);
109+
ets_install_uart_printf(uart_no);
110+
111+
/* reverse engineered from boot_from_something() */
112+
const uint16_t divlatch = uart_baudrate_detect(uart_no, 0);
113+
rom_uart_div_modify(uart_no, divlatch);
114+
UartDwnLdProc((uint8_t*)0x3fffa000, 0x2000, &user_start_fptr);
115+
116+
/* reverse engineered from main() */
117+
if (user_start_fptr == NULL) {
118+
if (boot_from_flash() != 0) {
119+
ets_printf("boot_from_flash() failed\n");
120+
while (true);
121+
}
122+
}
123+
124+
if (user_start_fptr) {
125+
user_start_fptr();
126+
}
127+
128+
ets_printf("user code done\n");
129+
ets_run();
130+
}
131+
132+
[[noreturn]] void esp8266RebootIntoUartDownloadMode()
133+
{
134+
/* reverse engineered from system_restart_local() */
135+
if (system_func1(0x4) == -1) {
136+
clockgate_watchdog(0);
137+
SET_PERI_REG_MASK(PERIPHS_DPORT_18, 0xffff00ff);
138+
pm_open_rf();
139+
}
140+
141+
user_uart_wait_tx_fifo_empty(0, 0x7a120);
142+
user_uart_wait_tx_fifo_empty(1, 0x7a120);
143+
ets_intr_lock();
144+
SET_PERI_REG_MASK(PERIPHS_DPORT_18, 0x7500);
145+
CLEAR_PERI_REG_MASK(PERIPHS_DPORT_18, 0x7500);
146+
SET_PERI_REG_MASK(PERIPHS_I2C_48, 0x2);
147+
CLEAR_PERI_REG_MASK(PERIPHS_I2C_48, 0x2);
148+
149+
esp8266UartDownloadMode();
150+
}
151+

Diff for: cores/esp8266/reboot_uart_dwnld.h

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
ESP8266-specific implementation of the UART download mode
3+
Copyright (c) 2021 Timo Wischer <twischer@freenet.de>
4+
All rights reserved.
5+
This file is part of the esp8266 core for Arduino environment.
6+
7+
This library is free software; you can redistribute it and/or
8+
modify it under the terms of the GNU Lesser General Public
9+
License as published by the Free Software Foundation; either
10+
version 2.1 of the License, or (at your option) any later version.
11+
12+
This library is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
Lesser General Public License for more details.
16+
17+
You should have received a copy of the GNU Lesser General Public
18+
License along with this library; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
#include <stdnoreturn.h>
22+
23+
[[noreturn]] void esp8266RebootIntoUartDownloadMode();

Diff for: tools/sdk/ld/eagle.rom.addr.v6.ld

+3
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ PROVIDE ( aes_decrypt_init = 0x40008ea4 );
9999
PROVIDE ( aes_unwrap = 0x40009410 );
100100
PROVIDE ( base64_decode = 0x40009648 );
101101
PROVIDE ( base64_encode = 0x400094fc );
102+
PROVIDE ( boot_from_flash = 0x40001308 );
102103
PROVIDE ( bzero = 0x4000de84 );
103104
PROVIDE ( cmd_parse = 0x40000814 );
104105
PROVIDE ( conv_str_decimal = 0x40000b24 );
@@ -278,6 +279,7 @@ PROVIDE ( rom_stop_tx_tone = 0x4000698c );
278279
PROVIDE ( rom_tx_mac_disable = 0x40006a98 );
279280
PROVIDE ( rom_tx_mac_enable = 0x40006ad4 );
280281
PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c );
282+
PROVIDE ( rom_uart_div_modify = 0x400039d8 );
281283
PROVIDE ( rom_write_rfpll_sdm = 0x400078dc );
282284
PROVIDE ( roundup2 = 0x400031b4 );
283285
PROVIDE ( rtc_enter_sleep = 0x40002870 );
@@ -353,5 +355,6 @@ PROVIDE ( Te0 = 0x3fffccf0 );
353355
PROVIDE ( Td0 = 0x3fffd100 );
354356
PROVIDE ( Td4s = 0x3fffd500);
355357
PROVIDE ( rcons = 0x3fffd0f0);
358+
PROVIDE ( user_start_fptr = 0x3fffdcd0 );
356359
PROVIDE ( UartDev = 0x3fffde10 );
357360
PROVIDE ( flashchip = 0x3fffc714);

Diff for: tools/sdk/lib/NONOSDK221/libmain.a

-100 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/NONOSDK22x_190313/libmain.a

44 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/NONOSDK22x_190703/libmain.a

44 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/NONOSDK22x_191024/libmain.a

44 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/NONOSDK22x_191105/libmain.a

44 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/NONOSDK22x_191122/libmain.a

44 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/NONOSDK3V0/libmain.a

-98 Bytes
Binary file not shown.

Diff for: tools/sdk/lib/fix_sdk_libs.sh

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
set -e
33

44
export PATH=../../xtensa-lx106-elf/bin:$PATH
5+
VERSION=$(basename ${PWD})
6+
7+
addSymbol_system_func1() {
8+
ADDRESS=$1
9+
xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o
10+
}
11+
512

613
# Remove mem_manager.o from libmain.a to use custom heap implementation,
714
# 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
1421
xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o
1522
xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o
1623
xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o
24+
25+
if [[ ${VERSION} == "NONOSDK221" ]]; then
26+
addSymbol_system_func1 "0x60"
27+
elif [[ ${VERSION} == "NONOSDK22x"* ]]; then
28+
addSymbol_system_func1 "0x54"
29+
elif [[ ${VERSION} == "NONOSDK3"* ]]; then
30+
addSymbol_system_func1 "0x60"
31+
else
32+
echo "WARN: Unknown address for system_func1() called by system_restart_local()"
33+
fi
34+
1735
xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o
1836
rm -f eagle_lwip_if.o user_interface.o

0 commit comments

Comments
 (0)