ESP8266 Boot
(→ESP8266 启动) |
(→Boot Mode) |
||
(未显示1个用户的39个中间版本) | |||
第20行: | 第20行: | ||
csum 0x14 | csum 0x14 | ||
</source> | </source> | ||
+ | |||
+ | |||
+ | ;;Reset Cause: | ||
+ | |||
+ | <pre> | ||
+ | 1 ---> Power On | ||
+ | 2 ---> External Reset | ||
+ | 4 ---> Hardware Watchdog Reset | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | ;;chksum 与 csum 值相等,表示启动过程中 Flash 读取正确 | ||
第25行: | 第37行: | ||
<pre> | <pre> | ||
− | localhost:hello | + | localhost:hello comcat$ ls -l build/ |
total 1488 | total 1488 | ||
− | -rw-r--r-- 1 | + | -rw-r--r-- 1 comcat staff 31712 12 5 11:09 0x00000.bin |
− | -rw-r--r-- 1 | + | -rw-r--r-- 1 comcat staff 190596 12 5 11:09 0x40000.bin |
− | -rwxr-xr-x 1 | + | -rwxr-xr-x 1 comcat staff 516521 12 5 11:09 app.out |
− | -rw-r--r-- 1 | + | -rw-r--r-- 1 comcat staff 4632 12 5 11:09 app_app.a |
− | -rw-r--r-- 1 | + | -rw-r--r-- 1 comcat staff 4480 12 5 11:09 main.o |
− | localhost:hello | + | localhost:hello comcat$ make flash |
− | ../../.. | + | ../../../toolchain/bin/esptool -cd nodemcu -cb 115200 -cp /dev/cu.SLAB_USBtoUART -ca 0x00000 -cf build/0x00000.bin |
-ca 0x40000 -cf build/0x40000.bin | -ca 0x40000 -cf build/0x40000.bin | ||
Uploading 31712 bytes from build/0x00000.bin to flash at 0x00000000 | Uploading 31712 bytes from build/0x00000.bin to flash at 0x00000000 | ||
............................... | ............................... | ||
Uploading 190596 bytes from build/0x40000.bin to flash at 0x00040000 | Uploading 190596 bytes from build/0x40000.bin to flash at 0x00040000 | ||
− | + | ................................................................................................................. | |
</pre> | </pre> | ||
<br><br> | <br><br> | ||
− | == | + | == Boot Mode == |
ESP8266 Reset 后,通过判断如下管脚的状态来决定启动模式: | ESP8266 Reset 后,通过判断如下管脚的状态来决定启动模式: | ||
第57行: | 第69行: | ||
启动时串口输出的 'boot mode:(x, y)' , x 的低三位对应 {MTDO, GPIO0, GPIO2} | 启动时串口输出的 'boot mode:(x, y)' , x 的低三位对应 {MTDO, GPIO0, GPIO2} | ||
+ | |||
+ | |||
+ | '''2019/05/27:''' | ||
+ | |||
+ | <s>正常运行时,电流消耗 70 mA,用线把 GPIO15 接 3V3,芯片立即 HOLD,电流飙升到 200 - 300 mA。释放后,芯片重启。。。 | ||
+ | |||
+ | 深睡眠时,同样现象。这样就也可以通过拉高 GPIO15 后释放来重启,虽然特别耗电。。。 | ||
+ | |||
+ | 拉高 GPIO15,如果是用线把 GPIO15 接 10K 再接 3V3,电流会降到 35mA 左右,其他现象相同。。。 </s> | ||
+ | |||
+ | 悲催的焊接问题,GPIO15 被多余的焊锡焊接到 GND 了。。。怎么发现的? TMD 程序拉高 GPIO15 怎么也拉不高。。。 | ||
+ | |||
+ | <br><br> | ||
+ | |||
+ | == 详细分析 == | ||
+ | |||
+ | === Flash === | ||
+ | |||
+ | SPI Flash 非 OTA 系统,分两部分,一部分在 Flash 片内偏移 0x00000 开始处,启动后由片内 Bootloader (XTOS) 自动加载到内存的部分;另一部分为运行时需要的时候再加载到内存的部分,这两部分内存映射地址都定义在 *.ld 链接脚本的 MEMORY 标志下,比如典型的: | ||
+ | |||
+ | <source lang=bash> | ||
+ | $ head ld/eagle.app.v6.4096.ld | ||
+ | MEMORY | ||
+ | { | ||
+ | dport0_0_seg : org = 0x3FF00000, len = 0x10 | ||
+ | dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | ||
+ | iram1_0_seg : org = 0x40100000, len = 0x8000 | ||
+ | irom0_0_seg : org = 0x40240000, len = 0xBF800 | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | |||
+ | 则其对应的 Flash 内偏移为: | ||
+ | |||
+ | 0x00000 --- 启动自动加载到 RAM 的部分 | ||
+ | 0x40000 --- 运行需要后再加载的部分 | ||
+ | |||
+ | |||
+ | OTA 系统: | ||
+ | |||
+ | 0x00000 --- 启动自动加载到 RAM 的部分,boot.bin (OTA 核心实现在这里) | ||
+ | 0x01000 --- 4KB 开始,为运行需要后再加载的部分,user1.bin | ||
+ | |||
+ | |||
+ | 这两个部分,第一部分是 ESP Firmware 格式 (Little Endian),第二部分实际就是 .irom0.text 直接写入在 Flash 上,且直接映射到内存地址空间。其中 ESP Firmware 格式为: | ||
+ | |||
+ | |||
+ | ;;File header | ||
+ | |||
+ | <pre> | ||
+ | Byte Description | ||
+ | 0 Always 0xE9 | ||
+ | 1 Number of segments | ||
+ | 2 SPI Flash Interface (0 = QIO, 1 = QOUT, 2 = DIO, 0x3 = DOUT) | ||
+ | 3 High four bits: 0 = 512KB, 1 = 256KB, 2 = 1MB, 3 = 2MB, 4 = 4MB, Low four bits: 0 = 40MHz, 1= 26MHz, 2 = 20MHz, 0xf = 80MHz | ||
+ | 4-7 Entry point | ||
+ | 8-n Segments | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | ;;Segment | ||
+ | |||
+ | <pre> | ||
+ | Byte Description | ||
+ | 0-3 Memory offset | ||
+ | 4-7 Segment size | ||
+ | 8...n Data | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | ;;Footer | ||
+ | |||
+ | The file is padded with zeros until its size is one byte less than a multiple of 16 bytes. A last byte (thus making the file size a multiple of 16) is the checksum of the data of all segments. The checksum is defined as the xor-sum of all bytes and the byte 0xEF. | ||
+ | |||
+ | <br> | ||
+ | |||
+ | === 片内 Bootloader === | ||
+ | |||
+ | 在没有外置 SPI Flash 的情形下,ESP8266 有几个外围支持元件(晶振、电容、电阻),也能启动,其片内有 ROM,带一个很小的 OS(极可能是 XTOS,xtensa 提供的最小系统),可将其理解为一个片内 Bootloader | ||
+ | |||
+ | 这个 Bootloader 实现了很多常用的函数,其内存地址也是固定的,官方 SDK 已经将其导出,参考这个链接脚本:https://github.com/icamgo/noduino-sdk/blob/master/sdk/ld/eagle.rom.addr.v6.ld | ||
+ | |||
+ | |||
+ | 这个应该是 ESP8266 最底端的,Reset 异常处理后,首先进入的就是这个 XTOS,在完成的基本系统初始化后,加载 SPI Flash 的第一部分 (0x00000 始)进入 RAM,然后串口输出这类信息: | ||
+ | |||
+ | <source lang=bash> | ||
+ | ets Jan 8 2013,rst cause:2, boot mode:(3,6) | ||
+ | |||
+ | load 0x40100000, len 30372, room 16 | ||
+ | tail 4 | ||
+ | chksum 0xe6 | ||
+ | load 0x3ffe8000, len 900, room 4 | ||
+ | tail 0 | ||
+ | chksum 0x0a | ||
+ | load 0x3ffe8388, len 392, room 8 | ||
+ | tail 0 | ||
+ | chksum 0x14 | ||
+ | csum 0x14 | ||
+ | </source> | ||
+ | |||
+ | |||
+ | xtos 加载 SPI Flash 上的固件,跳转入口在 SPI Flash 0x00000 开始的第 5 - 8 字节,一般是 call_user_start() ,call_user_start() 实现在 libmain.a 里,是乐鑫所有 wifi 初始化的调用者 | ||
+ | |||
+ | |||
+ | 而 SPI Flash 的第二部分固件,实际是 esptool 直接取 elf 文件的 .irom0.text Section,这部分被映射到内存,具体地址在 ld script 里定义: | ||
+ | |||
+ | 常见的: | ||
+ | |||
+ | <source lang=bash> | ||
+ | $ head ld/eagle.app.v6.4096.ld | ||
+ | MEMORY | ||
+ | { | ||
+ | dport0_0_seg : org = 0x3FF00000, len = 0x10 | ||
+ | dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | ||
+ | iram1_0_seg : org = 0x40100000, len = 0x8000 | ||
+ | irom0_0_seg : org = 0x40240000, len = 0xBF800 | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | 整个 flash 是映射到 0x40200000 开始的地方,这个映射窗口大小为 1MB。irom0 映射到内存地址空间的 0x40240000 开始的地方,因此其在 flash 上的地址偏移为 0x40000 | ||
+ | |||
+ | |||
+ | 更多探索,参考社区对其的逆向工程: | ||
+ | |||
+ | * http://dflund.se/~kongo/esp8266.bin/iram0.txt | ||
+ | * http://www.esp8266.com/viewtopic.php?f=13&t=214 | ||
<br><br> | <br><br> | ||
− | == | + | === 上电后的流程 === |
<source lang=bash> | <source lang=bash> | ||
第94行: | 第232行: | ||
<br><br> | <br><br> | ||
+ | |||
+ | == 参考 == | ||
+ | |||
+ | * https://github.com/tommie/esptool | ||
+ | * https://github.com/igrr/esptool-ck | ||
+ | * https://github.com/esp8266/Arduino/blob/master/bootloaders/eboot/rom.ld | ||
+ | * [https://github.com/fermatliu/open-ath9k-htc-firmware/blob/master/target_firmware/magpie_fw_dev/build/magpie_1_1/sboot/athos/src/xtos/reset-vector.S XTOS Reset Vector] | ||
+ | |||
+ | |||
+ | * https://github.com/espressif/ESP8266_MP3_DECODER/blob/master/mp3/make.sh | ||
<br><br> | <br><br> | ||
<br><br> | <br><br> |
2019年5月27日 (一) 12:46的最后版本
目录 |
[编辑] 1 ESP8266 启动
- Reset 入口在: 0x40000080
ESP8266 启动时,将从 UART0 以波特率 74880 打印:
ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x40100000, len 30372, room 16 tail 4 chksum 0xe6 load 0x3ffe8000, len 900, room 4 tail 0 chksum 0x0a load 0x3ffe8388, len 392, room 8 tail 0 chksum 0x14 csum 0x14
- Reset Cause
1 ---> Power On 2 ---> External Reset 4 ---> Hardware Watchdog Reset
- chksum 与 csum 值相等,表示启动过程中 Flash 读取正确
写入时:
localhost:hello comcat$ ls -l build/ total 1488 -rw-r--r-- 1 comcat staff 31712 12 5 11:09 0x00000.bin -rw-r--r-- 1 comcat staff 190596 12 5 11:09 0x40000.bin -rwxr-xr-x 1 comcat staff 516521 12 5 11:09 app.out -rw-r--r-- 1 comcat staff 4632 12 5 11:09 app_app.a -rw-r--r-- 1 comcat staff 4480 12 5 11:09 main.o localhost:hello comcat$ make flash ../../../toolchain/bin/esptool -cd nodemcu -cb 115200 -cp /dev/cu.SLAB_USBtoUART -ca 0x00000 -cf build/0x00000.bin -ca 0x40000 -cf build/0x40000.bin Uploading 31712 bytes from build/0x00000.bin to flash at 0x00000000 ............................... Uploading 190596 bytes from build/0x40000.bin to flash at 0x00040000 .................................................................................................................
[编辑] 2 Boot Mode
ESP8266 Reset 后,通过判断如下管脚的状态来决定启动模式:
MTDO GPIO0 GPIO2 Mode Description L L H UART 串口刷机 L H H Flash SPI Flash 正常启动 H x x SDIO SD-card 启动
MTDO 为 GPIO15
启动时串口输出的 'boot mode:(x, y)' , x 的低三位对应 {MTDO, GPIO0, GPIO2}
2019/05/27:
正常运行时,电流消耗 70 mA,用线把 GPIO15 接 3V3,芯片立即 HOLD,电流飙升到 200 - 300 mA。释放后,芯片重启。。。
深睡眠时,同样现象。这样就也可以通过拉高 GPIO15 后释放来重启,虽然特别耗电。。。
拉高 GPIO15,如果是用线把 GPIO15 接 10K 再接 3V3,电流会降到 35mA 左右,其他现象相同。。。
悲催的焊接问题,GPIO15 被多余的焊锡焊接到 GND 了。。。怎么发现的? TMD 程序拉高 GPIO15 怎么也拉不高。。。
[编辑] 3 详细分析
[编辑] 3.1 Flash
SPI Flash 非 OTA 系统,分两部分,一部分在 Flash 片内偏移 0x00000 开始处,启动后由片内 Bootloader (XTOS) 自动加载到内存的部分;另一部分为运行时需要的时候再加载到内存的部分,这两部分内存映射地址都定义在 *.ld 链接脚本的 MEMORY 标志下,比如典型的:
$ head ld/eagle.app.v6.4096.ld MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40240000, len = 0xBF800 }
则其对应的 Flash 内偏移为:
0x00000 --- 启动自动加载到 RAM 的部分 0x40000 --- 运行需要后再加载的部分
OTA 系统:
0x00000 --- 启动自动加载到 RAM 的部分,boot.bin (OTA 核心实现在这里) 0x01000 --- 4KB 开始,为运行需要后再加载的部分,user1.bin
这两个部分,第一部分是 ESP Firmware 格式 (Little Endian),第二部分实际就是 .irom0.text 直接写入在 Flash 上,且直接映射到内存地址空间。其中 ESP Firmware 格式为:
- File header
Byte Description 0 Always 0xE9 1 Number of segments 2 SPI Flash Interface (0 = QIO, 1 = QOUT, 2 = DIO, 0x3 = DOUT) 3 High four bits: 0 = 512KB, 1 = 256KB, 2 = 1MB, 3 = 2MB, 4 = 4MB, Low four bits: 0 = 40MHz, 1= 26MHz, 2 = 20MHz, 0xf = 80MHz 4-7 Entry point 8-n Segments
- Segment
Byte Description 0-3 Memory offset 4-7 Segment size 8...n Data
- Footer
The file is padded with zeros until its size is one byte less than a multiple of 16 bytes. A last byte (thus making the file size a multiple of 16) is the checksum of the data of all segments. The checksum is defined as the xor-sum of all bytes and the byte 0xEF.
[编辑] 3.2 片内 Bootloader
在没有外置 SPI Flash 的情形下,ESP8266 有几个外围支持元件(晶振、电容、电阻),也能启动,其片内有 ROM,带一个很小的 OS(极可能是 XTOS,xtensa 提供的最小系统),可将其理解为一个片内 Bootloader
这个 Bootloader 实现了很多常用的函数,其内存地址也是固定的,官方 SDK 已经将其导出,参考这个链接脚本:https://github.com/icamgo/noduino-sdk/blob/master/sdk/ld/eagle.rom.addr.v6.ld
这个应该是 ESP8266 最底端的,Reset 异常处理后,首先进入的就是这个 XTOS,在完成的基本系统初始化后,加载 SPI Flash 的第一部分 (0x00000 始)进入 RAM,然后串口输出这类信息:
ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x40100000, len 30372, room 16 tail 4 chksum 0xe6 load 0x3ffe8000, len 900, room 4 tail 0 chksum 0x0a load 0x3ffe8388, len 392, room 8 tail 0 chksum 0x14 csum 0x14
xtos 加载 SPI Flash 上的固件,跳转入口在 SPI Flash 0x00000 开始的第 5 - 8 字节,一般是 call_user_start() ,call_user_start() 实现在 libmain.a 里,是乐鑫所有 wifi 初始化的调用者
而 SPI Flash 的第二部分固件,实际是 esptool 直接取 elf 文件的 .irom0.text Section,这部分被映射到内存,具体地址在 ld script 里定义:
常见的:
$ head ld/eagle.app.v6.4096.ld MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40240000, len = 0xBF800 }
整个 flash 是映射到 0x40200000 开始的地方,这个映射窗口大小为 1MB。irom0 映射到内存地址空间的 0x40240000 开始的地方,因此其在 flash 上的地址偏移为 0x40000
更多探索,参考社区对其的逆向工程:
[编辑] 3.3 上电后的流程
ResetHandler (0x40000080) Set interrupt level 1 Set processor modes (see separate section) Copy SROM data to SRAM Goto _start _start Set Ring 0 Clear callback vector Set up stack at 3FFFFFFFh Call main main Initialize UART0 115,200 bps, 8N1 iomux.u0txd &= 0xE4F iomux.gpio2 = (iomux.gpio2 & 0xECF) | 0x100 Processor Modes Only Ring 0 used TLBs (D and I) are set to 00000000h-1FFFFFFFh: Illegal 20000000h-5FFFFFFFh: RWX, Cache Write-Through 60000000h-FFFFFFFFh: RWX, Bypass Cache
[编辑] 4 参考
- https://github.com/tommie/esptool
- https://github.com/igrr/esptool-ck
- https://github.com/esp8266/Arduino/blob/master/bootloaders/eboot/rom.ld
- XTOS Reset Vector