ESP8266 Boot

来自Jack's Lab
2019年5月27日 (一) 12:46Comcat (讨论 | 贡献)的版本

(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到: 导航, 搜索

目录

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 参考




















个人工具
名字空间

变换
操作
导航
工具箱