|
@@ -50,16 +50,15 @@ mindmap
|
|
|
arch/arm/
|
|
arch/arm/
|
|
|
+-- cpu/
|
|
+-- cpu/
|
|
|
| +-- armv7/
|
|
| +-- armv7/
|
|
|
-| | +-- start.S # 启动入口
|
|
|
|
|
-| | +-- lowlevel_init.S
|
|
|
|
|
-| | +-- u-boot.lds # 链接脚本模板
|
|
|
|
|
-| +-- u-boot.lds
|
|
|
|
|
|
|
+| | +-- start.S # reset、save_boot_params、cpu_init_cp15、cpu_init_crit
|
|
|
|
|
+| | +-- lowlevel_init.S# 低级初始化(设置 SP、调用 s_init)
|
|
|
|
|
+| +-- u-boot.lds # 链接脚本模板(编译后生成根目录 u-boot.lds)
|
|
|
+-- imx-common/ # NXP i.MX系列通用代码
|
|
+-- imx-common/ # NXP i.MX系列通用代码
|
|
|
+-- mach-mx6/ # i.MX6系列SoC支持
|
|
+-- mach-mx6/ # i.MX6系列SoC支持
|
|
|
+-- lib/
|
|
+-- lib/
|
|
|
-| +-- vectors.S # 中断向量表
|
|
|
|
|
-| +-- crt0.S # C运行时初始化
|
|
|
|
|
-| +-- board.c
|
|
|
|
|
|
|
+| +-- vectors.S # _start 与中断向量表
|
|
|
|
|
+| +-- crt0.S # _main 与 C 运行时初始化
|
|
|
|
|
+| +-- relocate.S # relocate_code / relocate_vectors
|
|
|
+-- include/asm/arch-mx6/ # i.MX6寄存器定义
|
|
+-- include/asm/arch-mx6/ # i.MX6寄存器定义
|
|
|
```
|
|
```
|
|
|
|
|
|
|
@@ -112,8 +111,8 @@ cmd_u-boot.bin := cp u-boot-nodtb.bin u-boot.bin
|
|
|
|
|
|
|
|
# .u-boot-nodtb.bin.cmd - ELF转二进制
|
|
# .u-boot-nodtb.bin.cmd - ELF转二进制
|
|
|
cmd_u-boot-nodtb.bin := arm-linux-gnueabihf-objcopy \
|
|
cmd_u-boot-nodtb.bin := arm-linux-gnueabihf-objcopy \
|
|
|
- --gap-fill=0xff -j .text -j .rodata -j .data \
|
|
|
|
|
- -j .got -j .got.plt -j .u_boot_list -j .rel.dyn \
|
|
|
|
|
|
|
+ --gap-fill=0xff -j .text -j .secure_text -j .rodata -j .hash \
|
|
|
|
|
+ -j .data -j .got -j .got.plt -j .u_boot_list -j .rel.dyn \
|
|
|
-O binary u-boot u-boot-nodtb.bin
|
|
-O binary u-boot u-boot-nodtb.bin
|
|
|
|
|
|
|
|
# .u-boot.imx.cmd - 生成NXP专用镜像
|
|
# .u-boot.imx.cmd - 生成NXP专用镜像
|
|
@@ -129,23 +128,32 @@ cmd_u-boot.imx := ./tools/mkimage \
|
|
|
### 2.1 Makefile 核心变量
|
|
### 2.1 Makefile 核心变量
|
|
|
|
|
|
|
|
```makefile
|
|
```makefile
|
|
|
-# 版本信息
|
|
|
|
|
|
|
+# 版本信息(顶层 Makefile 第 5~9 行)
|
|
|
VERSION = 2016
|
|
VERSION = 2016
|
|
|
PATCHLEVEL = 03
|
|
PATCHLEVEL = 03
|
|
|
-SUBLEVEL = 00
|
|
|
|
|
-EXTRAVERSION = -alientek
|
|
|
|
|
|
|
+SUBLEVEL =
|
|
|
|
|
+EXTRAVERSION =
|
|
|
|
|
+NAME =
|
|
|
|
|
+# VERSION/PATCHLEVEL/SUBLEVEL 共同构成 2016.03;EXTRAVERSION、NAME 一般不使用
|
|
|
|
|
+
|
|
|
|
|
+# 交叉编译工具链(第 245~246 行:主机架构==目标架构时 CROSS_COMPILE 为空)
|
|
|
|
|
+ifeq ($(HOSTARCH),$(ARCH))
|
|
|
|
|
+CROSS_COMPILE ?=
|
|
|
|
|
+endif
|
|
|
|
|
+# 实际编译时通过 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 传入,
|
|
|
|
|
+# 也可直接在顶层 Makefile 中写死 ARCH=arm / CROSS_COMPILE=arm-linux-gnueabihf-
|
|
|
|
|
+```
|
|
|
|
|
|
|
|
-# 交叉编译工具链
|
|
|
|
|
-CROSS_COMPILE ?= arm-linux-gnueabihf-
|
|
|
|
|
|
|
+以下`CONFIG_SYS_*`来自板级配置头文件(经`make xxx_defconfig`写入`.config`),而非顶层 Makefile:
|
|
|
|
|
|
|
|
-# 架构和CPU配置
|
|
|
|
|
-ARCH ?= arm
|
|
|
|
|
|
|
+```text
|
|
|
CONFIG_SYS_ARCH="arm"
|
|
CONFIG_SYS_ARCH="arm"
|
|
|
CONFIG_SYS_CPU="armv7"
|
|
CONFIG_SYS_CPU="armv7"
|
|
|
CONFIG_SYS_SOC="mx6"
|
|
CONFIG_SYS_SOC="mx6"
|
|
|
CONFIG_SYS_VENDOR="freescale"
|
|
CONFIG_SYS_VENDOR="freescale"
|
|
|
CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
|
|
CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
|
|
|
CONFIG_SYS_CONFIG_NAME="mx6ull_alientek_emmc"
|
|
CONFIG_SYS_CONFIG_NAME="mx6ull_alientek_emmc"
|
|
|
|
|
+CONFIG_SYS_TEXT_BASE=0x87800000
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
### 2.2 主要Makefile目标
|
|
### 2.2 主要Makefile目标
|
|
@@ -174,32 +182,47 @@ flowchart TD
|
|
|
### 2.4 编译规则详解
|
|
### 2.4 编译规则详解
|
|
|
|
|
|
|
|
```makefile
|
|
```makefile
|
|
|
-u-boot: $(OBJS) $(LIBS) u-boot.lds FORCE
|
|
|
|
|
- $(LD) -pie --gc-sections -Bstatic \
|
|
|
|
|
- -Ttext 0x87800000 \
|
|
|
|
|
- -o u-boot -T u-boot.lds \
|
|
|
|
|
- arch/arm/cpu/armv7/start.o \
|
|
|
|
|
- --start-group $(OBJS) $(LIBS) --end-group
|
|
|
|
|
|
|
+# 目标依赖关系(顶层 Makefile)
|
|
|
|
|
+u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
|
|
|
|
|
+ $(call if_changed,u-boot__)
|
|
|
|
|
|
|
|
u-boot-nodtb.bin: u-boot FORCE
|
|
u-boot-nodtb.bin: u-boot FORCE
|
|
|
- $(OBJCOPY) --gap-fill=0xff \
|
|
|
|
|
- -j .text -j .rodata -j .data -j .got -j .u_boot_list \
|
|
|
|
|
- -O binary u-boot u-boot-nodtb.bin
|
|
|
|
|
|
|
+ $(call if_changed,objcopy)
|
|
|
|
|
+ $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
|
|
|
|
|
+
|
|
|
|
|
+u-boot.bin: u-boot-nodtb.bin FORCE
|
|
|
|
|
+ $(call if_changed,copy) # 即 cp u-boot-nodtb.bin u-boot.bin
|
|
|
|
|
|
|
|
-u-boot.bin: u-boot-nodtb.bin
|
|
|
|
|
- cp u-boot-nodtb.bin u-boot.bin
|
|
|
|
|
|
|
+# u-boot-init = $(head-y) = arch/arm/cpu/armv7/start.o
|
|
|
|
|
+# u-boot-main = $(libs-y) = 所有子目录的 built-in.o
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+展开后实际执行的链接命令(来自 `.u-boot.cmd`):
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x87800000 \
|
|
|
|
|
+ -o u-boot -T u-boot.lds arch/arm/cpu/armv7/start.o \
|
|
|
|
|
+ --start-group arch/arm/cpu/built-in.o arch/arm/cpu/armv7/built-in.o \
|
|
|
|
|
+ arch/arm/imx-common/built-in.o arch/arm/lib/built-in.o \
|
|
|
|
|
+ board/freescale/common/built-in.o \
|
|
|
|
|
+ board/freescale/mx6ull_alientek_emmc/built-in.o \
|
|
|
|
|
+ cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o \
|
|
|
|
|
+ ... fs/built-in.o lib/built-in.o net/built-in.o ... --end-group \
|
|
|
|
|
+ arch/arm/lib/eabi_compat.o -lgcc -Map u-boot.map
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+`u-boot.lds` 的生成规则:`u-boot.lds: $(LDSCRIPT) prepare FORCE`(`$(LDSCRIPT)` 即 `arch/arm/cpu/u-boot.lds`)。
|
|
|
|
|
+
|
|
|
### 2.5 链接脚本关键配置
|
|
### 2.5 链接脚本关键配置
|
|
|
|
|
|
|
|
```text
|
|
```text
|
|
|
-OUTPUT_FORMAT("elf32-littlearm")
|
|
|
|
|
|
|
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
|
|
|
OUTPUT_ARCH(arm)
|
|
OUTPUT_ARCH(arm)
|
|
|
ENTRY(_start)
|
|
ENTRY(_start)
|
|
|
-
|
|
|
|
|
SECTIONS
|
|
SECTIONS
|
|
|
{
|
|
{
|
|
|
- . = 0x87800000; /* U-Boot加载地址 */
|
|
|
|
|
|
|
+ . = 0x00000000; /* 注意:链接脚本里是 0x00000000,不是 0x87800000 */
|
|
|
|
|
+ . = ALIGN(4);
|
|
|
.text :
|
|
.text :
|
|
|
{
|
|
{
|
|
|
*(.__image_copy_start)
|
|
*(.__image_copy_start)
|
|
@@ -210,35 +233,53 @@ SECTIONS
|
|
|
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
|
|
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
|
|
|
.data : { *(.data*) }
|
|
.data : { *(.data*) }
|
|
|
.u_boot_list : { KEEP(*(SORT(.u_boot_list*))); }
|
|
.u_boot_list : { KEEP(*(SORT(.u_boot_list*))); }
|
|
|
- .bss : { *(.bss*) }
|
|
|
|
|
|
|
+ .image_copy_end : { *(.__image_copy_end) }
|
|
|
|
|
+ .rel_dyn_start : { *(.__rel_dyn_start) }
|
|
|
|
|
+ .rel.dyn : { *(.rel*) }
|
|
|
|
|
+ .rel_dyn_end : { *(.__rel_dyn_end) }
|
|
|
|
|
+ .end : { *(.__end) }
|
|
|
|
|
+ _image_binary_end = .;
|
|
|
|
|
+ .bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start)); __bss_base = .; }
|
|
|
|
|
+ .bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; }
|
|
|
|
|
+ .bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); }
|
|
|
|
|
+ ...
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**关键地址变量:**
|
|
|
|
|
|
|
+> 真正的加载地址由链接时的 `-Ttext 0x87800000` 决定,链接脚本本身从 `0x00000000` 开始。入口点是 `_start`,定义在 `arch/arm/lib/vectors.S` 中,其后紧跟中断向量表(`.vectors` 段)。
|
|
|
|
|
|
|
|
-| 变量 | 数值 | 描述 |
|
|
|
|
|
-| -------------------- | ---------- | ------------------ |
|
|
|
|
|
-| `__image_copy_start` | 0x87800000 | U-Boot拷贝首地址 |
|
|
|
|
|
-| `__image_copy_end` | 0x8785dd54 | U-Boot拷贝结束地址 |
|
|
|
|
|
-| `__rel_dyn_start` | 0x8785dd54 | .rel.dyn段起始 |
|
|
|
|
|
-| `__bss_start` | 0x8785dd54 | BSS段起始 |
|
|
|
|
|
-| `__bss_end` | 0x878a8e74 | BSS段结束 |
|
|
|
|
|
|
|
+**关键地址变量(示例编译值,以实际编译为准):**
|
|
|
|
|
+
|
|
|
|
|
+| 变量 | 数值 | 描述 |
|
|
|
|
|
+| -------------------- | ---------- | ----------------------- |
|
|
|
|
|
+| `__image_copy_start` | 0x87800000 | U-Boot拷贝首地址 |
|
|
|
|
|
+| `__image_copy_end` | 0x8785dd54 | U-Boot拷贝结束地址 |
|
|
|
|
|
+| `__rel_dyn_start` | 0x8785dd54 | .rel.dyn段起始 |
|
|
|
|
|
+| `__rel_dyn_end` | 0x878668f4 | .rel.dyn段结束 |
|
|
|
|
|
+| `_image_binary_end` | 0x878668f4 | 镜像结束地址 |
|
|
|
|
|
+| `__bss_start` | 0x8785dd54 | BSS段起始 |
|
|
|
|
|
+| `__bss_end` | 0x878a8e74 | BSS段结束 |
|
|
|
|
|
|
|
|
### 2.6 编译选项详解
|
|
### 2.6 编译选项详解
|
|
|
|
|
|
|
|
```text
|
|
```text
|
|
|
-# 链接选项
|
|
|
|
|
--DCONFIG_SYS_TEXT_BASE=0x87800000
|
|
|
|
|
--pie
|
|
|
|
|
---gc-sections
|
|
|
|
|
-
|
|
|
|
|
-# 编译选项
|
|
|
|
|
--O2
|
|
|
|
|
--fpie
|
|
|
|
|
-
|
|
|
|
|
-# 安全选项
|
|
|
|
|
--fstack-protector-strong
|
|
|
|
|
--D_FORTIFY_SOURCE=2
|
|
|
|
|
|
|
+# 链接选项(.u-boot.cmd)
|
|
|
|
|
+-pie # 位置无关可执行文件
|
|
|
|
|
+--gc-sections # 回收未使用的段
|
|
|
|
|
+-Bstatic # 静态链接
|
|
|
|
|
+-Ttext 0x87800000 # U-Boot 链接/加载地址
|
|
|
|
|
+
|
|
|
|
|
+# 生成 u-boot-nodtb.bin 时(.u-boot-nodtb.bin.cmd)
|
|
|
|
|
+--gap-fill=0xff
|
|
|
|
|
+$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) # 修正 .rel.dyn 重定位表
|
|
|
|
|
+
|
|
|
|
|
+# 交叉工具变量(顶层 Makefile 第 333~347 行)
|
|
|
|
|
+CC = $(CROSS_COMPILE)gcc
|
|
|
|
|
+LD = $(CROSS_COMPILE)ld.bfd(若 ld.bfd 不可用则回退为 $(CROSS_COMPILE)ld)
|
|
|
|
|
+OBJCOPY = $(CROSS_COMPILE)objcopy
|
|
|
|
|
+AR = $(CROSS_COMPILE)ar
|
|
|
|
|
+NM = $(CROSS_COMPILE)nm
|
|
|
|
|
+OBJDUMP = $(CROSS_COMPILE)objdump
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
### 2.7 Kbuild系统
|
|
### 2.7 Kbuild系统
|
|
@@ -300,93 +341,80 @@ flowchart TD
|
|
|
|
|
|
|
|
### 3.2 启动阶段划分
|
|
### 3.2 启动阶段划分
|
|
|
|
|
|
|
|
|
|
+> ⚠️ **来源说明**:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识(书中未讲 SPL;此处用于说明 I.MX6U 与常见 SPL 架构的区别)。
|
|
|
|
|
+
|
|
|
|
|
+I.MX6U 的 U-Boot **不是 SPL 架构**(默认直接使用完整的`u-boot.imx`)。上电后由芯片内部的 Boot ROM 把`u-boot.imx`加载到 DDR 的`0x87800000`处执行,之后 U-Boot 自身再经历“重定位前 → 重定位 → 重定位后”三个阶段:
|
|
|
|
|
+
|
|
|
```mermaid
|
|
```mermaid
|
|
|
flowchart LR
|
|
flowchart LR
|
|
|
- subgraph SPL
|
|
|
|
|
- A1[初始化DDR] --> A2[初始化串口]
|
|
|
|
|
- A2 --> A3[加载U-Boot]
|
|
|
|
|
|
|
+ subgraph ROM[Boot ROM]
|
|
|
|
|
+ A1[解析 u-boot.imx 头部] --> A2[加载到 DDR 0x87800000]
|
|
|
|
|
+ end
|
|
|
|
|
+ subgraph F[board_init_f(重定位前)]
|
|
|
|
|
+ B1[初始化串口/定时器/DDR] --> B2[规划内存并计算重定位地址]
|
|
|
end
|
|
end
|
|
|
- subgraph U-Boot
|
|
|
|
|
- B1[初始化外设] --> B2[网络初始化]
|
|
|
|
|
- B2 --> B3[命令初始化]
|
|
|
|
|
- B3 --> B4[环境变量]
|
|
|
|
|
|
|
+ subgraph R[board_init_r(重定位后)]
|
|
|
|
|
+ C1[初始化外设/MMC/网络] --> C2[环境变量/控制台] --> C3[main_loop 命令行]
|
|
|
end
|
|
end
|
|
|
subgraph Linux
|
|
subgraph Linux
|
|
|
- C1[加载内核] --> C2[加载设备树]
|
|
|
|
|
- C2 --> C3[传递参数]
|
|
|
|
|
- C3 --> C4[跳转执行]
|
|
|
|
|
|
|
+ D1[加载内核与设备树] --> D2[传递参数] --> D3[跳转执行]
|
|
|
end
|
|
end
|
|
|
- A3 --> B1
|
|
|
|
|
- B4 --> C1
|
|
|
|
|
|
|
+ A2 --> B1
|
|
|
|
|
+ B2 --> R
|
|
|
|
|
+ C3 --> D1
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**三个阶段职责:**
|
|
|
|
|
|
|
+**各阶段职责:**
|
|
|
|
|
|
|
|
-| 阶段 | 职责 | 关键函数 |
|
|
|
|
|
-| ------ | --------------------------- | ---------------------------------- |
|
|
|
|
|
-| SPL | 初始化DDR、串口,加载U-Boot | `board_init_f()`, `board_init_r()` |
|
|
|
|
|
-| U-Boot | 初始化更多外设,网络,命令 | `board_init_r()`, `main_loop()` |
|
|
|
|
|
-| Linux | 挂载rootfs,启动init | `start_kernel()` |
|
|
|
|
|
|
|
+| 阶段 | 职责 | 关键函数 |
|
|
|
|
|
+| ---- | ---- | -------- |
|
|
|
|
|
+| Boot ROM | 从启动设备加载`u-boot.imx`到 DDR | 芯片固化代码 |
|
|
|
|
|
+| board_init_f(重定位前) | 初始化串口/定时器/DDR,`init_sequence_f[]`,计算重定位目的地址 | `board_init_f()` |
|
|
|
|
|
+| 重定位 | 将 U-Boot 拷贝到 DDR 高地址,修正`.rel.dyn`,重定位向量表 | `relocate_code()`、`relocate_vectors()` |
|
|
|
|
|
+| board_init_r(重定位后) | 初始化 MMC/NAND/网络/环境变量,进入命令行 | `board_init_r()`、`main_loop()` |
|
|
|
|
|
+| Linux | 挂载rootfs,启动init | `boot_jump_linux()`、`kernel_entry()` |
|
|
|
|
|
|
|
|
### 3.3 内存布局
|
|
### 3.3 内存布局
|
|
|
|
|
|
|
|
```text
|
|
```text
|
|
|
-OCRAM (内部RAM):
|
|
|
|
|
-0x00900000 +---------------------+
|
|
|
|
|
- | OCRAM起始 |
|
|
|
|
|
-0x0091FF00 +---------------------+ <- CONFIG_SYS_INIT_SP_ADDR
|
|
|
|
|
- | 256B GD结构体 |
|
|
|
|
|
-0x00920000 +---------------------+ OCRAM结束 (128KB)
|
|
|
|
|
-
|
|
|
|
|
-DRAM (外部DDR):
|
|
|
|
|
-0x80000000 +---------------------+
|
|
|
|
|
- | DRAM起始 |
|
|
|
|
|
-0x80800000 +---------------------+ <- Linux内核加载地址
|
|
|
|
|
- | |
|
|
|
|
|
-0x83000000 +---------------------+ <- 设备树加载地址
|
|
|
|
|
- | |
|
|
|
|
|
-0x84000000 +---------------------+ <- 根文件系统
|
|
|
|
|
- | |
|
|
|
|
|
-0x87800000 +---------------------+ <- U-Boot加载地址
|
|
|
|
|
|
|
+OCRAM (内部RAM, 128KB):
|
|
|
|
|
+0x00900000 +---------------------+ <- IRAM_BASE_ADDR
|
|
|
|
|
+ | OCRAM |
|
|
|
|
|
+0x0091FE08 +---------------------+ <- lowlevel_init 中 sp -= GD_SIZE(248)
|
|
|
|
|
+0x0091FF00 +---------------------+ <- CONFIG_SYS_INIT_SP_ADDR (sp 初始值)
|
|
|
|
|
+0x00920000 +---------------------+ OCRAM结束 (0x20000, 128KB)
|
|
|
|
|
+
|
|
|
|
|
+DRAM (外部DDR, 512MB):
|
|
|
|
|
+0x80000000 +---------------------+ <- DRAM起始 (gd->ram_size=0x20000000)
|
|
|
|
|
+0x80800000 +---------------------+ <- Linux内核(zImage)加载地址
|
|
|
|
|
+0x83000000 +---------------------+ <- 设备树(dtb)加载地址
|
|
|
|
|
+0x87800000 +---------------------+ <- U-Boot链接/初始加载地址
|
|
|
| U-Boot代码 |
|
|
| U-Boot代码 |
|
|
|
-0x878a8e74 +---------------------+ <- BSS段结束
|
|
|
|
|
- +---------------------+
|
|
|
|
|
|
|
+0x878a8e74 +---------------------+ <- BSS段结束 (__bss_end)
|
|
|
|
|
+ | ... |
|
|
|
|
|
+0x9EF44E90 +---------------------+ <- 重定位后最终的 sp (gd->start_addr_sp)
|
|
|
|
|
+0x9FF47000 +---------------------+ <- U-Boot重定位后的运行地址 (gd->relocaddr)
|
|
|
|
|
+ | ... |
|
|
|
|
|
+0xA0000000 +---------------------+ <- DRAM最高地址 (gd->ram_top)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### 3.4 SPL阶段详解
|
|
|
|
|
|
|
+### 3.4 重定位前阶段(board_init_f)
|
|
|
|
|
+
|
|
|
|
|
+`_main`中先调用`board_init_f_alloc_reserve`预留早期 malloc 与 gd 区域,再调用`board_init_f`运行`init_sequence_f[]`,主要工作是初始化串口、定时器、DDR 等,并“自顶向下”规划内存,最终算出重定位目的地址。
|
|
|
|
|
|
|
|
```text
|
|
```text
|
|
|
-SPL (Secondary Program Loader) 阶段职责:
|
|
|
|
|
-+-- 1. 初始化CPU时钟
|
|
|
|
|
-+-- 2. 初始化DDR控制器
|
|
|
|
|
-+-- 3. 初始化串口控制台
|
|
|
|
|
-+-- 4. 从存储介质加载U-Boot
|
|
|
|
|
-| +-- MMC/SD卡
|
|
|
|
|
-| +-- NAND Flash
|
|
|
|
|
-| +-- SPI Flash
|
|
|
|
|
-| +-- USB
|
|
|
|
|
-+-- 5. 跳转到U-Boot执行
|
|
|
|
|
|
|
+board_init_f(重定位前)职责:
|
|
|
|
|
++-- 1. setup_mon_len:设置 gd->mon_len = __bss_end - _start
|
|
|
|
|
++-- 2. arch_cpu_init / board_early_init_f:CPU 与板级早期初始化(串口 IO)
|
|
|
|
|
++-- 3. timer_init / get_clocks:定时器与时钟
|
|
|
|
|
++-- 4. env_init / init_baud_rate / serial_init / console_init_f:环境变量、波特率、串口、控制台
|
|
|
|
|
++-- 5. dram_init:设置 gd->ram_size(正点原子 EMMC 版为 512MB)
|
|
|
|
|
++-- 6. setup_dest_addr:设置 gd->ram_size、gd->ram_top=0xA0000000、gd->relocaddr=0xA0000000
|
|
|
|
|
++-- 7. reserve_*:从高地址向下依次预留 MMU TLB、U-Boot、malloc、bd、gd、栈等
|
|
|
|
|
++-- 8. setup_reloc:把旧 gd 拷贝到 gd->new_gd
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**SPL启动时序:**
|
|
|
|
|
-
|
|
|
|
|
-```mermaid
|
|
|
|
|
-sequenceDiagram
|
|
|
|
|
- participant ROM as Boot ROM
|
|
|
|
|
- participant SPL as SPL
|
|
|
|
|
- participant DDR as DDR控制器
|
|
|
|
|
- participant UART as 串口
|
|
|
|
|
- participant MMC as MMC/SD卡
|
|
|
|
|
- participant UBoot as U-Boot
|
|
|
|
|
- ROM->>SPL: 加载SPL到OCRAM
|
|
|
|
|
- SPL->>SPL: 初始化CPU时钟
|
|
|
|
|
- SPL->>DDR: 初始化DDR控制器
|
|
|
|
|
- DDR-->>SPL: DDR就绪
|
|
|
|
|
- SPL->>UART: 初始化串口
|
|
|
|
|
- SPL->>MMC: 从MMC读取U-Boot
|
|
|
|
|
- MMC-->>SPL: U-Boot镜像
|
|
|
|
|
- SPL->>UBoot: 跳转到U-Boot入口
|
|
|
|
|
-```
|
|
|
|
|
|
|
+关键中间结果(示例):`gd->relocaddr = 0xA0000000` → `reserve_mmu`后 `0x9FFF0000` → `reserve_uboot`后 `0x9FF47000` → `reserve_malloc`(16MB+8KB)后 `0x9EF45000` → `reserve_board`(80B)→ `reserve_global_data`(248B)→ `reserve_stacks` 后最终 `gd->start_addr_sp = 0x9EF44E90`。
|
|
|
|
|
|
|
|
### 3.5 U-Boot主阶段详解
|
|
### 3.5 U-Boot主阶段详解
|
|
|
|
|
|
|
@@ -439,70 +467,106 @@ boot // 执行bootcmd
|
|
|
|
|
|
|
|
## 四、关键代码分析
|
|
## 四、关键代码分析
|
|
|
|
|
|
|
|
-### 4.1 start.S - 启动入口
|
|
|
|
|
|
|
+### 4.1 vectors.S / start.S - 启动入口
|
|
|
|
|
|
|
|
-```armasm
|
|
|
|
|
-/ * arch/arm/cpu/armv7/start.S * /
|
|
|
|
|
|
|
+入口`_start`定义在`arch/arm/lib/vectors.S`,其后就是中断向量表(`.vectors` 段),第一个向量`b reset`跳到`arch/arm/cpu/armv7/start.S`中的`reset`:
|
|
|
|
|
|
|
|
|
|
+```armasm
|
|
|
|
|
+/ * arch/arm/lib/vectors.S * /
|
|
|
_start:
|
|
_start:
|
|
|
- b reset @ 0x00: 跳转到reset向量处理
|
|
|
|
|
- ldr pc, _undefined_instruction @ 0x04: 未定义指令异常
|
|
|
|
|
- ldr pc, _software_interrupt @ 0x08: 软件中断
|
|
|
|
|
- ldr pc, _prefetch_abort @ 0x0c: 取指令中断
|
|
|
|
|
- ldr pc, _data_abort @ 0x10: 数据中断
|
|
|
|
|
- ldr pc, _not_used @ 0x14: 未使用
|
|
|
|
|
- ldr pc, _irq @ 0x18: IRQ中断
|
|
|
|
|
- ldr pc, _fiq @ 0x1c: FIQ中断
|
|
|
|
|
|
|
+ b reset @ 0x00: 复位向量
|
|
|
|
|
+ ldr pc, _undefined_instruction @ 0x04
|
|
|
|
|
+ ldr pc, _software_interrupt @ 0x08
|
|
|
|
|
+ ldr pc, _prefetch_abort @ 0x0c
|
|
|
|
|
+ ldr pc, _data_abort @ 0x10
|
|
|
|
|
+ ldr pc, _not_used @ 0x14
|
|
|
|
|
+ ldr pc, _irq @ 0x18
|
|
|
|
|
+ ldr pc, _fiq @ 0x1c
|
|
|
|
|
|
|
|
|
|
+/ * arch/arm/cpu/armv7/start.S * /
|
|
|
reset:
|
|
reset:
|
|
|
- mrs r0, cpsr @ 读取CPSR寄存器
|
|
|
|
|
- orr r0, r0, #0x1f @ 设置模式为SVC模式 (0x13 | 0x1c = 0x1f)
|
|
|
|
|
- msr cpsr, r0 @ 禁止IRQ和FIQ中断
|
|
|
|
|
-
|
|
|
|
|
- bl save_boot_params @ 保存启动参数
|
|
|
|
|
- bl cpu_init_cp15 @ 初始化CP15协处理器
|
|
|
|
|
- bl cpu_init_crit @ 初始化关键硬件
|
|
|
|
|
- b _main @ 跳转到_main入口
|
|
|
|
|
|
|
+ b save_boot_params @ 允许板子保存重要寄存器
|
|
|
|
|
+
|
|
|
|
|
+ENTRY(save_boot_params)
|
|
|
|
|
+ b save_boot_params_ret @ back to my caller
|
|
|
|
|
+
|
|
|
|
|
+save_boot_params_ret:
|
|
|
|
|
+ mrs r0, cpsr
|
|
|
|
|
+ and r1, r0, #0x1f @ mask mode bits
|
|
|
|
|
+ teq r1, #0x1a @ test for HYP mode
|
|
|
|
|
+ bicne r0, r0, #0x1f @ clear all mode bits
|
|
|
|
|
+ orrne r0, r0, #0x13 @ set SVC mode
|
|
|
|
|
+ orr r0, r0, #0xc0 @ disable FIQ and IRQ
|
|
|
|
|
+ msr cpsr, r0
|
|
|
|
|
+
|
|
|
|
|
+ @ 设置向量表重定位:清 SCTLR.V,把 _start 写入 VBAR
|
|
|
|
|
+ mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR
|
|
|
|
|
+ bic r0, #CR_V @ CR_V = (1 << 13)
|
|
|
|
|
+ mcr p15, 0, r0, c1, c0, 0
|
|
|
|
|
+ ldr r0, =_start
|
|
|
|
|
+ mcr p15, 0, r0, c12, c0, 0 @ Set VBAR
|
|
|
|
|
+
|
|
|
|
|
+#ifndef CONFIG_SKIP_LOWLEVEL_INIT
|
|
|
|
|
+ bl cpu_init_cp15
|
|
|
|
|
+ bl cpu_init_crit
|
|
|
|
|
+#endif
|
|
|
|
|
+ bl _main
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+
|
|
|
### 4.2 cpu_init_cp15 - 协处理器初始化
|
|
### 4.2 cpu_init_cp15 - 协处理器初始化
|
|
|
|
|
|
|
|
```armasm
|
|
```armasm
|
|
|
-cpu_init_cp15:
|
|
|
|
|
- @ 禁用MMU
|
|
|
|
|
- mcr p15, 0, r0, c8, c7, 0 @ 清零TLB表
|
|
|
|
|
- mcr p15, 0, r0, c7, c5, 0 @ 清零指令缓存
|
|
|
|
|
- mcr p15, 0, r0, c7, c5, 6 @ 清零BP表
|
|
|
|
|
- mrc p15, 0, r0, c1, c0, 0 @ 读取SCTLR
|
|
|
|
|
- bic r0, r0, #0x000d @ 关闭MMU和数据缓存
|
|
|
|
|
- bic r0, r0, #0x1100 @ 关闭I-Cache和切换缓存
|
|
|
|
|
- mcr p15, 0, r0, c1, c0, 0 @ 写入SCTLR
|
|
|
|
|
- @ 启用I-Cache
|
|
|
|
|
- mrc p15, 0, r0, c1, c0, 0 @ 读取SCTLR
|
|
|
|
|
- orr r0, r0, #0x0004 @ 启用D-Cache作为普通内存
|
|
|
|
|
- mcr p15, 0, r0, c1, c0, 0 @ 写入SCTLR
|
|
|
|
|
- mov pc, lr @ 返回
|
|
|
|
|
|
|
+ENTRY(cpu_init_cp15)
|
|
|
|
|
+ /* Invalidate L1 I/D */
|
|
|
|
|
+ mov r0, #0
|
|
|
|
|
+ mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
|
|
|
|
|
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
|
|
|
|
|
+ mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
|
|
|
|
|
+ mcr p15, 0, r0, c7, c10, 4 @ DSB
|
|
|
|
|
+ mcr p15, 0, r0, c7, c5, 4 @ ISB
|
|
|
|
|
+
|
|
|
|
|
+ /* disable MMU stuff and caches */
|
|
|
|
|
+ mrc p15, 0, r0, c1, c0, 0
|
|
|
|
|
+ bic r0, r0, #0x00002000 @ clear bit 13 (--V-)
|
|
|
|
|
+ bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
|
|
|
|
|
+ orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
|
|
|
|
|
+ orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
|
|
|
|
|
+#ifdef CONFIG_SYS_ICACHE_OFF
|
|
|
|
|
+ bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
|
|
|
|
|
+#else
|
|
|
|
|
+ orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
|
|
|
|
|
+#endif
|
|
|
|
|
+ mcr p15, 0, r0, c1, c0, 0
|
|
|
|
|
+ ...
|
|
|
|
|
+ mov pc, r5 @ back to my caller
|
|
|
|
|
+ENDPROC(cpu_init_cp15)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+> 说明:I.MX6U 的 U-Boot 默认**打开 I-Cache**(未定义 `CONFIG_SYS_ICACHE_OFF`),并关闭 MMU、D-Cache。
|
|
|
|
|
+
|
|
|
### 4.3 lowlevel_init - 低级初始化
|
|
### 4.3 lowlevel_init - 低级初始化
|
|
|
|
|
|
|
|
```armasm
|
|
```armasm
|
|
|
-lowlevel_init:
|
|
|
|
|
- @ 检查是否在OCRAM中执行
|
|
|
|
|
- mov r9, lr
|
|
|
|
|
- bl is_in_ocram
|
|
|
|
|
- cmp r0, #1
|
|
|
|
|
- beq 1f
|
|
|
|
|
- @ 不在OCRAM中,跳转到基杼地址执行
|
|
|
|
|
|
|
+/ * arch/arm/cpu/armv7/lowlevel_init.S * /
|
|
|
|
|
+ENTRY(lowlevel_init)
|
|
|
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
|
|
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
|
|
|
- push {r0-r3, r12, lr}
|
|
|
|
|
-1:
|
|
|
|
|
- ldr sp, =CONFIG_SYS_INIT_SP_ADDR
|
|
|
|
|
- push {lr}
|
|
|
|
|
- bl board_early_init_f @ 板级早期初始化
|
|
|
|
|
- pop {pc}
|
|
|
|
|
|
|
+ bic sp, sp, #7 @ 8 字节对齐
|
|
|
|
|
+#ifdef CONFIG_SPL_BUILD
|
|
|
|
|
+ ldr r9, =gdata
|
|
|
|
|
+#else
|
|
|
|
|
+ sub sp, sp, #GD_SIZE @ 为 gd 预留空间(GD_SIZE=248)
|
|
|
|
|
+ bic sp, sp, #7
|
|
|
|
|
+ mov r9, sp @ r9 保存 gd 指针
|
|
|
|
|
+#endif
|
|
|
|
|
+ push {ip, lr}
|
|
|
|
|
+ bl s_init @ 早期初始化(I.MX6ULL 为空函数)
|
|
|
|
|
+ pop {ip, pc}
|
|
|
|
|
+ENDPROC(lowlevel_init)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+`CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_INIT_RAM_ADDR + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) = 0x00900000 + (0x20000 - 256) = 0x0091FF00`,再减去 `GD_SIZE`(248) 后为 `0x0091FE08`。
|
|
|
|
|
+
|
|
|
**CPSR模式位对照表:**
|
|
**CPSR模式位对照表:**
|
|
|
|
|
|
|
|
| 位置 | 模式 | 值 | 说明 |
|
|
| 位置 | 模式 | 值 | 说明 |
|
|
@@ -518,119 +582,248 @@ lowlevel_init:
|
|
|
### 4.4 _main - C运行时初始化
|
|
### 4.4 _main - C运行时初始化
|
|
|
|
|
|
|
|
```armasm
|
|
```armasm
|
|
|
-_main:
|
|
|
|
|
- ldr sp, =CONFIG_SYS_INIT_SP_ADDR
|
|
|
|
|
- bl board_init_f_alloc_reserve @ 预留GD和栈空间
|
|
|
|
|
- mov sp, r0 @ sp指向栈顶
|
|
|
|
|
- bl board_init_f @ 初始化全局数据结构
|
|
|
|
|
- @ relocate_code重定位完成后返回
|
|
|
|
|
- bl board_init_r @ 主阶段初始化
|
|
|
|
|
|
|
+/ * arch/arm/lib/crt0.S * /
|
|
|
|
|
+ENTRY(_main)
|
|
|
|
|
+ ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) @ sp = 0x0091FF00
|
|
|
|
|
+ bic sp, sp, #7 @ 8 字节对齐
|
|
|
|
|
+ mov r0, sp
|
|
|
|
|
+ bl board_init_f_alloc_reserve @ 预留 malloc 与 gd 区域,返回新 top
|
|
|
|
|
+ mov sp, r0
|
|
|
|
|
+ mov r9, r0 @ r9 = gd 指针
|
|
|
|
|
+ bl board_init_f_init_reserve @ 清零 gd 区域
|
|
|
|
|
+ mov r0, #0
|
|
|
|
|
+ bl board_init_f @ 重定位前初始化
|
|
|
|
|
+
|
|
|
|
|
+ ldr sp, [r9, #GD_START_ADDR_SP] @ sp = gd->start_addr_sp
|
|
|
|
|
+ bic sp, sp, #7
|
|
|
|
|
+ ldr r9, [r9, #GD_BD] @ r9 = gd->bd
|
|
|
|
|
+ sub r9, r9, #GD_SIZE @ 新 gd 位于 bd 之下
|
|
|
|
|
+ adr lr, here
|
|
|
|
|
+ ldr r0, [r9, #GD_RELOC_OFF] @ r0 = gd->reloc_off
|
|
|
|
|
+ add lr, lr, r0
|
|
|
|
|
+ ldr r0, [r9, #GD_RELOCADDR] @ r0 = gd->relocaddr
|
|
|
|
|
+ b relocate_code @ 代码重定位
|
|
|
|
|
+here:
|
|
|
|
|
+ bl relocate_vectors @ 向量表重定位
|
|
|
|
|
+ bl c_runtime_cpu_setup
|
|
|
|
|
+ ldr r0, =__bss_start
|
|
|
|
|
+ ... @ 清零 BSS(memset 或 clbss_l 循环)
|
|
|
|
|
+ mov r0, r9 @ r0 = gd_t
|
|
|
|
|
+ ldr r1, [r9, #GD_RELOCADDR] @ r1 = dest_addr
|
|
|
|
|
+ ldr pc, =board_init_r @ 跳转 board_init_r(不返回)
|
|
|
|
|
+ENDPROC(_main)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**board_init_f_alloc_reserve 内存布局:**
|
|
|
|
|
-
|
|
|
|
|
-```text
|
|
|
|
|
-CONFIG_SYS_INIT_SP_ADDR:
|
|
|
|
|
-+---------------------+ <- sp初始值 (0x0091FF00)
|
|
|
|
|
-| |
|
|
|
|
|
-| 栈空间 |
|
|
|
|
|
-| (512B) |
|
|
|
|
|
-+---------------------+
|
|
|
|
|
-| 256B GD结构 |
|
|
|
|
|
-| (global_data_t) |
|
|
|
|
|
-+---------------------+ <- sp更新后
|
|
|
|
|
-| 512B GD->malloc |
|
|
|
|
|
-| 缓冲区 |
|
|
|
|
|
-+---------------------+
|
|
|
|
|
-```
|
|
|
|
|
|
|
+`board_init_f_alloc_reserve`自栈顶向下预留早期 malloc 区(`CONFIG_SYS_MALLOC_F_LEN`)与 gd 区并返回新的栈顶,`board_init_f_init_reserve`再把该区域清零并把 gd 指针存入 `r9`。
|
|
|
|
|
|
|
|
### 4.5 board_init_f - 全局数据初始化
|
|
### 4.5 board_init_f - 全局数据初始化
|
|
|
|
|
|
|
|
```c
|
|
```c
|
|
|
|
|
+/* common/board_f.c */
|
|
|
void board_init_f(ulong boot_flags)
|
|
void board_init_f(ulong boot_flags)
|
|
|
{
|
|
{
|
|
|
- gd_t *id;
|
|
|
|
|
- gd = (gd_t *)_text; // 设置全局数据指针
|
|
|
|
|
-
|
|
|
|
|
- // 清零GD结构
|
|
|
|
|
- memset(gd, 0, sizeof(gd_t));
|
|
|
|
|
-
|
|
|
|
|
- // 执行编程时初始化回调
|
|
|
|
|
- init_sequence_f[] = {
|
|
|
|
|
- setup_mon_len, // 设置监控器长度
|
|
|
|
|
- init_fnc_t, // 初始化函数表
|
|
|
|
|
- console_init_f, // 控制台早期初始化
|
|
|
|
|
- serial_init, // 串口初始化
|
|
|
|
|
- dram_init, // DRAM初始化
|
|
|
|
|
- NULL
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 计算重定位后地址
|
|
|
|
|
- fdt_register_boot_chart(gd->fdt_blob);
|
|
|
|
|
|
|
+ gd_t data;
|
|
|
|
|
+
|
|
|
|
|
+ gd = &data;
|
|
|
|
|
+ gd->flags = boot_flags;
|
|
|
|
|
+ gd->have_console = 0;
|
|
|
|
|
+
|
|
|
|
|
+ /* 通过 initcall_run_list 依次执行 init_sequence_f[] */
|
|
|
|
|
+ if (initcall_run_list(init_sequence_f))
|
|
|
|
|
+ hang();
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+`init_sequence_f[]`(去掉条件编译后的主要函数,按执行顺序):
|
|
|
|
|
+
|
|
|
|
|
+```c
|
|
|
|
|
+static init_fnc_t init_sequence_f[] = {
|
|
|
|
|
+ setup_mon_len, /* gd->mon_len = __bss_end - _start */
|
|
|
|
|
+ initf_malloc, /* gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN = 0x400 */
|
|
|
|
|
+ initf_console_record, /* I.MX6ULL 未定义 CONFIG_CONSOLE_RECORD,直接返回 */
|
|
|
|
|
+ arch_cpu_init,
|
|
|
|
|
+ initf_dm,
|
|
|
|
|
+ arch_cpu_init_dm, /* 未实现 */
|
|
|
|
|
+ mark_bootstage,
|
|
|
|
|
+ board_early_init_f, /* 板级早期初始化(I.MX6ULL 初始化串口 IO) */
|
|
|
|
|
+ timer_init, /* 初始化 Cortex-A 内核定时器 */
|
|
|
|
|
+ board_postclk_init, /* I.MX6ULL:设置 VDDSOC 电压 */
|
|
|
|
|
+ get_clocks, /* I.MX6ULL:获取 sdhc_clk */
|
|
|
|
|
+ env_init, /* 设置 gd->env_addr */
|
|
|
|
|
+ init_baud_rate, /* 根据 baudrate 环境变量设置 gd->baudrate */
|
|
|
|
|
+ serial_init,
|
|
|
|
|
+ console_init_f, /* gd->have_console = 1 */
|
|
|
|
|
+ display_options,
|
|
|
|
|
+ display_text_info,
|
|
|
|
|
+ print_cpuinfo,
|
|
|
|
|
+ show_board_info,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_INIT,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ init_func_i2c,
|
|
|
|
|
+ announce_dram_init, /* 输出 "DRAM:" */
|
|
|
|
|
+ dram_init, /* 设置 gd->ram_size,EMMC 版为 512MB */
|
|
|
|
|
+ post_init_f,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ testdram, /* 空函数 */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ setup_dest_addr, /* gd->ram_size/ram_top/relocaddr */
|
|
|
|
|
+ reserve_round_4k,
|
|
|
|
|
+ reserve_mmu, /* TLB 表,64K 对齐 */
|
|
|
|
|
+ reserve_trace,
|
|
|
|
|
+ reserve_uboot, /* 预留 U-Boot 空间(gd->mon_len),4K 对齐 */
|
|
|
|
|
+ reserve_malloc, /* TOTAL_MALLOC_LEN = 16MB + 8KB */
|
|
|
|
|
+ reserve_board, /* bd_t 大小 80B */
|
|
|
|
|
+ setup_machine, /* I.MX6ULL 走设备树,此函数无效 */
|
|
|
|
|
+ reserve_global_data, /* gd_t 大小 248B */
|
|
|
|
|
+ reserve_fdt, /* I.MX6ULL 未用到 */
|
|
|
|
|
+ reserve_arch, /* 空函数 */
|
|
|
|
|
+ reserve_stacks, /* 预留栈空间,16 字节对齐 */
|
|
|
|
|
+ setup_dram_config, /* 设置 gd->bd->bi_dram[0].start/size */
|
|
|
|
|
+ show_dram_config,
|
|
|
|
|
+ display_new_sp,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ reloc_fdt, /* 未用到 */
|
|
|
|
|
+ setup_reloc, /* 拷贝 gd 到 gd->new_gd */
|
|
|
|
|
+ NULL,
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+执行完毕后关键值(示例):`gd->ram_size=0x20000000`、`gd->ram_top=0xA0000000`、`gd->relocaddr=0x9FF47000`、`gd->start_addr_sp=0x9EF44E90`、`gd->reloc_off=0x18747000`、`gd->new_gd=0x9EF44EB8`。
|
|
|
|
|
+
|
|
|
### 4.6 board_init_r - 主阶段初始化
|
|
### 4.6 board_init_r - 主阶段初始化
|
|
|
|
|
|
|
|
```c
|
|
```c
|
|
|
-void board_init_r(gd_t *gd, ulong dest_addr)
|
|
|
|
|
|
|
+/* common/board_r.c */
|
|
|
|
|
+void board_init_r(gd_t *new_gd, ulong dest_addr)
|
|
|
{
|
|
{
|
|
|
- // 重定位后的初始化
|
|
|
|
|
- // 初始化回调函数表
|
|
|
|
|
- initr_serial, // 串口初始化
|
|
|
|
|
- initr_console, // 控制台初始化
|
|
|
|
|
- initr_board, // 板级外设初始化
|
|
|
|
|
- initr_ide, // IDE初始化
|
|
|
|
|
- initr_nand, // NAND初始化
|
|
|
|
|
- initr_mmc, // MMC初始化
|
|
|
|
|
- initr_net, // 网络初始化
|
|
|
|
|
- initr_env, // 环境变量初始化
|
|
|
|
|
- initr_std, // 标准IO初始化
|
|
|
|
|
- initr_boot, // 启动脚本初始化
|
|
|
|
|
- NULL
|
|
|
|
|
|
|
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
|
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
|
|
|
|
|
+ init_sequence_r[i] += gd->reloc_off;
|
|
|
|
|
+#endif
|
|
|
|
|
+ if (initcall_run_list(init_sequence_r))
|
|
|
|
|
+ hang();
|
|
|
|
|
+ /* NOTREACHED - run_main_loop() does not return */
|
|
|
|
|
+ hang();
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+`init_sequence_r[]`(去掉条件编译后的主要函数):
|
|
|
|
|
+
|
|
|
|
|
+```c
|
|
|
|
|
+init_fnc_t init_sequence_r[] = {
|
|
|
|
|
+ initr_trace,
|
|
|
|
|
+ initr_reloc, /* 设置 gd->flags,标记重定位完成 */
|
|
|
|
|
+ initr_caches, /* 使能 cache */
|
|
|
|
|
+ initr_reloc_global_data,
|
|
|
|
|
+ initr_barrier, /* I.MX6ULL 未用到 */
|
|
|
|
|
+ initr_malloc,
|
|
|
|
|
+ initr_console_record, /* 空函数 */
|
|
|
|
|
+ bootstage_relocate,
|
|
|
|
|
+ initr_bootstage,
|
|
|
|
|
+ board_init, /* 板级初始化:I2C/FEC/USB/QSPI 等 */
|
|
|
|
|
+ stdio_init_tables,
|
|
|
|
|
+ initr_serial,
|
|
|
|
|
+ initr_announce,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ power_init_board, /* 正点原子板未用到 */
|
|
|
|
|
+ initr_flash, /* 定义了 CONFIG_SYS_NO_FLASH,无效 */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ initr_nand, /* NAND 版本核心板初始化 NAND */
|
|
|
|
|
+ initr_mmc, /* EMMC 版本核心板初始化 EMMC */
|
|
|
|
|
+ initr_env, /* 初始化环境变量 */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ initr_secondary_cpu, /* 单核,未用 */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ stdio_add_devices, /* 初始化 LCD 等设备 */
|
|
|
|
|
+ initr_jumptable,
|
|
|
|
|
+ console_init_r, /* 控制台初始化 */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ interrupt_init,
|
|
|
|
|
+ initr_enable_interrupts,
|
|
|
|
|
+ initr_ethaddr, /* 读取环境变量 ethaddr 作为 MAC */
|
|
|
|
|
+ board_late_init, /* 定义在 mx6ull_alientek_emmc.c,切换 MMC 设备 */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ initr_net, /* initr_net -> eth_initialize -> board_eth_init */
|
|
|
|
|
+ INIT_FUNC_WATCHDOG_RESET,
|
|
|
|
|
+ run_main_loop, /* 进入主循环 */
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
### 4.7 main_loop - 主循环
|
|
### 4.7 main_loop - 主循环
|
|
|
|
|
|
|
|
```c
|
|
```c
|
|
|
|
|
+/* common/board_r.c */
|
|
|
|
|
+static int run_main_loop(void)
|
|
|
|
|
+{
|
|
|
|
|
+ /* main_loop() can return to retry autoboot, if so just run it again */
|
|
|
|
|
+ for (;;)
|
|
|
|
|
+ main_loop();
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+```c
|
|
|
|
|
+/* common/main.c */
|
|
|
void main_loop(void)
|
|
void main_loop(void)
|
|
|
{
|
|
{
|
|
|
- // 处理环境变量
|
|
|
|
|
- process_fdt_options();
|
|
|
|
|
|
|
+ const char *s;
|
|
|
|
|
|
|
|
- // 执行启动命令
|
|
|
|
|
- if (bootdelay >= 0) {
|
|
|
|
|
- autoboot_command(); // 执行bootcmd
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
|
|
|
|
|
+
|
|
|
|
|
+#ifdef CONFIG_VERSION_VARIABLE
|
|
|
|
|
+ setenv("ver", version_string); /* 设置 version 环境变量 */
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
- // 启动脚本
|
|
|
|
|
- run_command_list();
|
|
|
|
|
|
|
+ cli_init(); /* 初始化 hush shell 相关变量 */
|
|
|
|
|
+ run_preboot_environment_command(); /* 执行 preboot 环境变量(一般不用) */
|
|
|
|
|
|
|
|
- // 进入交互式命令行
|
|
|
|
|
- cli_loop();
|
|
|
|
|
|
|
+ s = bootdelay_process(); /* 读取 bootdelay 和 bootcmd,返回 bootcmd */
|
|
|
|
|
+ if (cli_process_fdt(&s))
|
|
|
|
|
+ cli_secure_boot_cmd(s);
|
|
|
|
|
+
|
|
|
|
|
+ autoboot_command(s); /* 倒计时;未打断则运行 bootcmd */
|
|
|
|
|
+
|
|
|
|
|
+ cli_loop(); /* 进入命令行 */
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+`autoboot_command`核心:`stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)`三条都成立时执行`run_command_list(s, -1, 0)`;否则(按键打断)走`cli_loop()`。
|
|
|
|
|
+
|
|
|
### 4.8 boot_jump_linux - 跳转内核
|
|
### 4.8 boot_jump_linux - 跳转内核
|
|
|
|
|
|
|
|
```c
|
|
```c
|
|
|
-void boot_jump_linux(bootm_headers_t *images)
|
|
|
|
|
|
|
+/* arch/arm/lib/bootm.c */
|
|
|
|
|
+static void boot_jump_linux(bootm_headers_t *images, int flag)
|
|
|
{
|
|
{
|
|
|
- // 获取内核入口函数指针
|
|
|
|
|
- void (*kernel_entry)(int zero, int machid, void *fdt);
|
|
|
|
|
- kernel_entry = (void (*)(int, int, void *))images->ep;
|
|
|
|
|
|
|
+ unsigned long machid = gd->bd->bi_arch_number; /* 机器 ID */
|
|
|
|
|
+ const char *s;
|
|
|
|
|
+ void (*kernel_entry)(int zero, int arch, uint params);
|
|
|
|
|
+ unsigned long r2;
|
|
|
|
|
+
|
|
|
|
|
+ kernel_entry = (void (*)(int, int, uint))images->ep; /* 内核入口 = images->ep */
|
|
|
|
|
|
|
|
- // 打印启动信息
|
|
|
|
|
- printf("Starting kernel ...\n");
|
|
|
|
|
|
|
+ s = getenv("machid");
|
|
|
|
|
+ if (s) {
|
|
|
|
|
+ if (strict_strtoul(s, 16, &machid) < 0)
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (images->ft_len)
|
|
|
|
|
+ r2 = (unsigned long)images->ft_addr; /* 使用设备树:r2 = DTB 地址 */
|
|
|
|
|
+ else
|
|
|
|
|
+ r2 = gd->bd->bi_boot_params; /* 不使用设备树:r2 = 参数地址 */
|
|
|
|
|
|
|
|
- // 调用内核入口函数
|
|
|
|
|
- kernel_entry(0, machid, r2);
|
|
|
|
|
- // r2指向设备树DTB
|
|
|
|
|
- // machid = MACH_TYPE_MX6ULL
|
|
|
|
|
|
|
+ kernel_entry(0, machid, r2); /* 进入 Linux 内核,一去不复返 */
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+> 使用设备树后,内核靠 DTB 的 "compatible" 属性匹配机器,`machid` 已不再关键;I.MX6U 实际就使用设备树方式启动。
|
|
|
|
|
+
|
|
|
**调用关系图:**
|
|
**调用关系图:**
|
|
|
|
|
|
|
|
```mermaid
|
|
```mermaid
|
|
@@ -653,33 +846,34 @@ flowchart TD
|
|
|
### 4.9 bootz_setup - 内核启动准备
|
|
### 4.9 bootz_setup - 内核启动准备
|
|
|
|
|
|
|
|
```c
|
|
```c
|
|
|
-int bootz_setup(ulong image, ulong *initrd_start, ulong *initrd_end,
|
|
|
|
|
- ulong *fdt_start)
|
|
|
|
|
|
|
+/* arch/arm/lib/bootm.c */
|
|
|
|
|
+#define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
|
|
|
|
|
+
|
|
|
|
|
+int bootz_setup(ulong image, ulong *start, ulong *end)
|
|
|
{
|
|
{
|
|
|
- struct zimage_headers *hdr = (struct zimage_headers *)image;
|
|
|
|
|
|
|
+ struct zimage_header *zi;
|
|
|
|
|
|
|
|
- // 验证内核魔数
|
|
|
|
|
- if (hdr->code0 != ZIMAGE_MAGIC) {
|
|
|
|
|
- printf("Bad zImage magic number\n");
|
|
|
|
|
- return -1;
|
|
|
|
|
|
|
+ zi = (struct zimage_header *)map_sysmem(image, 0);
|
|
|
|
|
+ if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {
|
|
|
|
|
+ puts("Bad Linux ARM zImage magic!\n");
|
|
|
|
|
+ return 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 获取内核起始地址
|
|
|
|
|
- *initrd_start = hdr->start;
|
|
|
|
|
|
|
+ *start = zi->zi_start;
|
|
|
|
|
+ *end = zi->zi_end;
|
|
|
|
|
|
|
|
- // 获取DTB地址
|
|
|
|
|
- *fdt_start = hdr->start + hdr->offset;
|
|
|
|
|
|
|
+ printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n",
|
|
|
|
|
+ image, *start, *end);
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**关键魔数:**
|
|
**关键魔数:**
|
|
|
|
|
|
|
|
-| 魔数 | 值 | 说明 |
|
|
|
|
|
-| ---------------- | ---------- | ---------------- |
|
|
|
|
|
-| ZIMAGE_MAGIC | 0x016f2818 | zImage标识 |
|
|
|
|
|
-| MACH_TYPE_MX6ULL | 0xFFFFFFFF | i.MX6ULL机器类型 |
|
|
|
|
|
-| DTB_MAGIC | 0xd00dfeed | 设备树魔数 |
|
|
|
|
|
|
|
+| 魔数 | 值 | 说明 |
|
|
|
|
|
+| ------------------------ | ---------- | ------------------ |
|
|
|
|
|
+| `LINUX_ARM_ZIMAGE_MAGIC` | 0x016f2818 | ARM Linux zImage 标识 |
|
|
|
|
|
+| `ZIMAGE_MAGIC`(旧书稿) | 0x016f2818 | 同一魔数,命名不同 |
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -741,28 +935,34 @@ make mrproper # 彻底清理(含.config)
|
|
|
|
|
|
|
|
### 5.5 烧录流程
|
|
### 5.5 烧录流程
|
|
|
|
|
|
|
|
|
|
+> ⚠️ **来源说明**:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识(书中第三十章使用`imxdownload`烧写`u-boot.bin`;此处为通用 dd 方式)。
|
|
|
|
|
+
|
|
|
|
|
+`u-boot.imx`需烧写到 SD 卡偏移 **1024 字节**处(即跳过前 2 个 512 字节扇区):
|
|
|
|
|
+
|
|
|
```mermaid
|
|
```mermaid
|
|
|
flowchart LR
|
|
flowchart LR
|
|
|
A[获取u-boot.imx] --> B[插入SD卡]
|
|
A[获取u-boot.imx] --> B[插入SD卡]
|
|
|
- B --> C[sudo dd if=u-boot.imx of=/dev/sdb bs=1M seek=2]
|
|
|
|
|
- C --> D[确认刷录完成]
|
|
|
|
|
|
|
+ B --> C[sudo dd if=u-boot.imx of=/dev/sdb bs=512 seek=2 conv=fsync]
|
|
|
|
|
+ C --> D[确认烧录完成]
|
|
|
D --> E[插入开发板启动]
|
|
D --> E[插入开发板启动]
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**dd命令参数说明:**
|
|
**dd命令参数说明:**
|
|
|
|
|
|
|
|
-| 参数 | 说明 |
|
|
|
|
|
-| --------------- | ------------ |
|
|
|
|
|
-| `if=u-boot.imx` | 输入文件 |
|
|
|
|
|
-| `of=/dev/sdb` | 输出设备 |
|
|
|
|
|
-| `bs=1M` | 块大小 |
|
|
|
|
|
-| `seek=2` | 跳过2个块 |
|
|
|
|
|
-| `conv=notrunc` | 不截断原文件 |
|
|
|
|
|
|
|
+| 参数 | 说明 |
|
|
|
|
|
+| --------------- | -------------------------------------- |
|
|
|
|
|
+| `if=u-boot.imx` | 输入文件 |
|
|
|
|
|
+| `of=/dev/sdb` | 输出设备(切勿写系统盘) |
|
|
|
|
|
+| `bs=512` | 块大小 = 一个扇区 |
|
|
|
|
|
+| `seek=2` | 跳过 2 个扇区 = 1024 字节偏移 |
|
|
|
|
|
+| `conv=fsync` | 同步写入,确保数据落盘(可选 notrunc) |
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
## 六、跨平台对比
|
|
## 六、跨平台对比
|
|
|
|
|
|
|
|
|
|
+> ⚠️ **来源说明**:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
|
|
|
|
|
+
|
|
|
### 6.1 i.MX6ULL vs STM32MP1 vs RK3568
|
|
### 6.1 i.MX6ULL vs STM32MP1 vs RK3568
|
|
|
|
|
|
|
|
| 对比项 | i.MX6ULL | STM32MP1 | RK3568 |
|
|
| 对比项 | i.MX6ULL | STM32MP1 | RK3568 |
|
|
@@ -801,14 +1001,13 @@ flowchart LR
|
|
|
|
|
|
|
|
## 七、面试精选
|
|
## 七、面试精选
|
|
|
|
|
|
|
|
|
|
+> ⚠️ **来源说明**:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识(答案基于书中事实整理)。
|
|
|
|
|
+
|
|
|
### Q1: 为什么U-Boot加载地址是0x87800000?
|
|
### Q1: 为什么U-Boot加载地址是0x87800000?
|
|
|
|
|
|
|
|
-> 这是基于i.MX6ULL SoC的设计约束:
|
|
|
|
|
|
|
+> `0x87800000`是 NXP 为 i.MX6 系列定义的`CONFIG_SYS_TEXT_BASE`(写在板级头文件 mx6ullevk.h / mx6ull_alientek_emmc.h 中),编译时通过`-Ttext 0x87800000`链接,也是上电后 Boot ROM 把 U-Boot 加载到 DDR 的位置。正点原子裸机例程的链接起始地址同样选`0x87800000`,目的是与 U-Boot 保持一致。
|
|
|
>
|
|
>
|
|
|
-> 1. OCRAM范围是0x00900000-0x00920000(128KB),只能作为启动缓存
|
|
|
|
|
-> 2. DRAM起始于0x80000000,需要留出空间给内核和设备树
|
|
|
|
|
-> 3. 0x87800000位于DRAM中间位置,既不会冲突内核(0x80800000),
|
|
|
|
|
-> 也不会冲突设备树(0x83000000)
|
|
|
|
|
|
|
+> 注意:这是 U-Boot 的**初始加载/链接地址**;重定位后 U-Boot 实际运行在 DDR 高地址(示例为`0x9FF47000`)。
|
|
|
|
|
|
|
|
### Q2: U-Boot重定位的作用是什么?为什么要重定位?
|
|
### Q2: U-Boot重定位的作用是什么?为什么要重定位?
|
|
|
|
|
|
|
@@ -827,21 +1026,24 @@ flowchart LR
|
|
|
### Q3: CONFIG_SYS_INIT_SP_ADDR是如何计算的?
|
|
### Q3: CONFIG_SYS_INIT_SP_ADDR是如何计算的?
|
|
|
|
|
|
|
|
> ```text
|
|
> ```text
|
|
|
-> OCRAM结束地址 = 0x00900000 + 128KB = 0x00920000
|
|
|
|
|
-> GD大小 = 248字节(对齐后256字节)
|
|
|
|
|
-> 留出栈空间 = 512字节
|
|
|
|
|
-> SP = 0x00920000 - 256 - 512 = 0x0091FF00
|
|
|
|
|
|
|
+> CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = 0x00900000
|
|
|
|
|
+> CONFIG_SYS_INIT_RAM_SIZE = IRAM_SIZE = 0x00020000 (128KB)
|
|
|
|
|
+> GENERATED_GBL_DATA_SIZE = (sizeof(struct global_data) + 15) & ~15 = 256
|
|
|
|
|
+> CONFIG_SYS_INIT_SP_OFFSET = 0x20000 - 256 = 0x1FF00
|
|
|
|
|
+> CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0x1FF00 = 0x0091FF00
|
|
|
> ```
|
|
> ```
|
|
|
|
|
+>
|
|
|
|
|
+> 随后在`lowlevel_init`中执行`sub sp, sp, #GD_SIZE`(GD_SIZE=248)并 8 字节对齐,得到`0x0091FE08`。
|
|
|
|
|
|
|
|
### Q4: U-Boot如何传递参数给Linux内核?
|
|
### Q4: U-Boot如何传递参数给Linux内核?
|
|
|
|
|
|
|
|
> 1. **r0 = 0**: 未使用
|
|
> 1. **r0 = 0**: 未使用
|
|
|
-> 2. **r1 = machid**: 机器类型ID,如MACH_TYPE_MX6ULL
|
|
|
|
|
-> 3. **r2 = dtb地址**: 设备树DTB在DRAM中的地址
|
|
|
|
|
-> 4. **bootargs**: 通过bootargs环境变量传递
|
|
|
|
|
-> - console=ttyS0,115200 串控台配置
|
|
|
|
|
-> - root=/dev/mmcblk0p2 根文件系统
|
|
|
|
|
-> - rdinit=/sbin/init 初始化程序
|
|
|
|
|
|
|
+> 2. **r1 = machid**: 机器类型ID(`gd->bd->bi_arch_number`);使用设备树后内核靠 DTB 的 compatible 匹配,machid 已不再关键
|
|
|
|
|
+> 3. **r2 = dtb地址**: 使用设备树时为`images->ft_addr`(DTB 在 DRAM 中的地址),否则为`gd->bd->bi_boot_params`
|
|
|
|
|
+> 4. **bootargs**: 通过 bootargs 环境变量传递,I.MX6ULL 上典型值:
|
|
|
|
|
+> - `console=ttymxc0,115200` 串口控制台配置
|
|
|
|
|
+> - `root=/dev/mmcblk1p2` 根文件系统分区
|
|
|
|
|
+> - `rootwait`、`rw`、`rootfstype=ext4` 等
|
|
|
|
|
|
|
|
### Q5: 如何分析U-Boot启动失败?
|
|
### Q5: 如何分析U-Boot启动失败?
|
|
|
|
|
|
|
@@ -861,3 +1063,7 @@ flowchart LR
|
|
|
> - U-Boot官方文档: https://docs.u-boot.org/
|
|
> - U-Boot官方文档: https://docs.u-boot.org/
|
|
|
> - 正点原子STM32MP157开发板教程
|
|
> - 正点原子STM32MP157开发板教程
|
|
|
> - 正点原子电子网: https://www.alientek.com/
|
|
> - 正点原子电子网: https://www.alientek.com/
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+**内容来源**: 《I.MX6U嵌入式Linux驱动开发指南》第31章 U-Boot顶层Makefile详解、第32章 U-Boot启动流程详解
|