IMX6ULL学习笔记(四) —— uboot 启动流程
IMX6ULL 学习笔记
version : v1.0 「2023.4.27」
author: Y.Z.T.
摘要: 随记, 记录 I.MX6ULL 系列 SOC 的uboot 启动流程
⭐️ 目录
文章目录
- IMX6ULL 学习笔记
- 2.3 Uboot启动流程
- 2.3.1 链接脚本 u-boot.lds
- 2.3.1.1 链接脚本
- 2.3.1.2 `vectors.S` ( 入口点:`_start` )
- 2.3.1.3 映射文件 u-boot.map
- 2.3.2 U-boot启动流程
- 2.3.2.0 补充信息
- 2.3.2.0.1 ENTRY() 和 ENDPROC() 宏
- 2.3.2.0.2 ***为什么选择`SVC` 模式***
- 2.3.2.0.3 条件执行指令
- 2.3.2.0.4 CP15协处理器
- 2.3.2.1 阶段1 : 初始化外设硬件
- 2.3.2.1.1 uboot程序入口点 __start
- 2.3.2.1.2 save_boot_params_ret函数
- 2.3.2.1.3 lowlevel_init函数
- 2.3.2.1.4 _main函数
- 2.3.2.1.5 board_init_f 函数
- 2.3.2.1.6 init_sequence_f 数组
- 2.3.2.1.7 relocate_code 函数
- 2.3.2.1.8 relocate_vectors 函数
- 2.3.2.1.9 board_init_r函数
- 2.3.2.2 阶段2 : bootz启动linux 内核
- 2.3.2.2.1 images 全局变量
- 2.3.2.2.2 bootz 命令
- 2.3.2.2.3 `do_bootm_linux` 函数
- 2.3.2.2.4 补充
2.3 Uboot启动流程
2.3.1 链接脚本 u-boot.lds
2.3.1.1 链接脚本
通过链接脚本可以找到程序的入口地址 ,
uboot
的最终链接脚本是u-boot.lds
, 是通过编译boot
生成的
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{. = 0x00000000;. = ALIGN(4);.text :{*(.__image_copy_start)*(.vectors)arch/arm/cpu/armv7/start.o (.text*)*(.text*)}. = ALIGN(4);.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }. = ALIGN(4);.data : {*(.data*)}. = ALIGN(4);. = .;. = ALIGN(4);.u_boot_list : {KEEP(*(SORT(.u_boot_list*)));}. = ALIGN(4);.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 = .;. = ALIGN(4096);.mmutable : {*(.mmutable)}.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));}.dynsym _image_binary_end : { *(.dynsym) }.dynbss : { *(.dynbss) }.dynstr : { *(.dynstr*) }.dynamic : { *(.dynamic*) }.plt : { *(.plt*) }.interp : { *(.interp*) }.gnu.hash : { *(.gnu.hash) }.gnu : { *(.gnu*) }.ARM.exidx : { *(.ARM.exidx*) }.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
- 其中
ENTRY(_start)
是整个函数的入口,_start
在arch/arm/lib/vectors.S
中有定义- 注意
.text
代码段的内容
__image_copy_start
(uboot
拷贝的首地址 )vectors
段用于保存 中断向量表arch/arm/cpu/armv7/start.o (.text*)
意思是将arch/arm/cpu/armv7/start.o
编译出来的代码放到中断向量表后面*(.text*)
用于存放其他的代码段
与地址有关的变量
变量 | 数值 | 描述 |
---|---|---|
__image_copy_start
|
0x87800000
|
uboot 拷贝的首地址
|
__image_copy_end
|
0x8785dd54
|
uboot 拷贝的结束地址
|
__rel_dyn_start
|
0x8785dd54
|
.rel.dyn 段起始地址
|
__rel_dyn_end
|
0x878668f4
|
.rel.dyn 段结束地址
|
_image_binary_end
|
0x878668f4
|
镜像结束地址 |
__bss_start
|
0x8785dd54
|
.bss 段起始地址
|
__bss_end
|
0x878a8e74
|
.bss 段结束地址
|
除了
__image_copy_start
的值 , 其他变量 每次编译的时候可能会变化,如果修改了uboot
代码、修改了uboot
配置、选用不同的优化等级等等都会影响到这些值。
2.3.1.2 vectors.S
( 入口点:_start
)
- 代码当前入口点:
_star
存放在 文件arch/arm/lib/vectors.S
- _start 后面就是中断向量表
- 由
.section ".vectors", "ax"
可以知道 , 中断向量表这部分代码是存放在.vectors
段里面
2.3.1.3 映射文件 u-boot.map
u-boot.map
是uboot
的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址
段 .text 的地址设置为 0x878000000x0000000000000000 . = 0x00x0000000000000000 . = ALIGN (0x4).text 0x0000000087800000 0x3cd64*(.__image_copy_start).__image_copy_start0x0000000087800000 0x0 arch/arm/lib/built-in.o0x0000000087800000 __image_copy_start*(.vectors).vectors 0x0000000087800000 0x300 arch/arm/lib/built-in.o0x0000000087800000 _start0x0000000087800020 _undefined_instruction0x0000000087800024 _software_interruptp0x0000000087800028 _prefetch_abort0x000000008780002c _data_abort0x0000000087800030 _not_used0x0000000087800034 _irq0x0000000087800038 _fiq0x0000000087800040 IRQ_STACK_START_INarch/arm/cpu/armv7/start.o(.text*).text 0x0000000087800300 0xb0 arch/arm/cpu/armv7/start.o0x0000000087800300 reset0x0000000087800304 save_boot_params_ret0x0000000087800340 c_runtime_cpu_setup0x0000000087800350 save_boot_params0x0000000087800354 cpu_init_cp150x00000000878003a8 cpu_init_crit*(.text*).text 0x00000000878003b0 0x24 arch/arm/cpu/armv7/built-in.o0x00000000878003b0 lowlevel_init
- 可以看到
.text
的起始地址为0x87800000
- 镜像启动地址 (
.__image_copy_start
) 也是0x87800000
vectors
段 的起始地址也是0x87800000
vectors
段 后面则是arch/arm/cpu/armv7/start.s
和 其他代码段的内容
2.3.2 U-boot启动流程
Uboot 的启动流程可以大致分成两个阶段 :
- 第一阶段多使用汇编 , 主要完成一些板级的硬件初始化 , 如外设硬件初始化 , 如 DRAM , 串口 , 重定位等。
- 第二阶段通常使用C语言来实现 , 方便实现更加复杂的功能 , 主要完成
linux
内核 的启动
2.3.2.0 补充信息
2.3.2.0.1 ENTRY() 和 ENDPROC() 宏
使用
ENTRY
和ENDPROC
两个宏来定义一个名为name
的函数 , 这个伪指令实现了指定一个入口的同时数据对齐,同时提供了一个函数入口 :ENTRY(name) ... ENDPROC(name)
这两个宏定义在#include <linux/linkage.h>
中
.globl save_boot_params
.align 4 @4字节对齐
save_boot_params:
bx lr @ 带模式的返回.type save_boot_params STT_FUNC; @ 说明该标识是函数
.size save_boot_params, .-save_boot_params @ 计算整个函数的大小.weak save_boot_params @ 弱标号,如果别处有使用别处的定义,如果没有使用当前定义
2.3.2.0.2 为什么选择SVC
模式
通过 设置
CPSR
寄存器 的bit0 ~ bit4
五位来设置 处理器的工作模式
如下表所示:
在uboot的启动流程中选择SVC
模式
- 7种模式中,除用户
usr
模式外,其它模式均为 特权模式
中止
ABT
和未定义UND
模式
- 因为此时程序是正常运行的 , 所以不应该设置CPU为这两种模式的其中任何一种
快中断
FIQ
和中断IRQ
模式
- 对于快中断
FIQ
和中断IRQ
来说,此处uboot
初始化的时候,中断已经被禁用- 即使是注册了终端服务程序后,能够处理中断,那么这两种模式,也是自动切换过去的
- 所以,此处也不应该设置为这两种模式中的其中任何一种模式
用户USR模式
- 访问uboot初始化,就必须很多的硬件资源 , 而用户模式
USR
是 非特权模式 不能访问系统所有资源, 所以CPU
也不能设置成USR
系统
SYS
模式 和 管理SVC
模式
SYS
模式和USR
模式相比,所用的寄存器组,都是一样的,但是增加了一些访问一些在USR
模式下不能访问的资源SVC
模式本身就属于特权模式,本身就可以访问那些受控资源 , 相比SYS
多了 专属寄存器R13(sp)
、R14(lr)
以及 备份程序状态寄存器 (SPSR_svc
)- 所以 , 相对
SYS
模式来说,可以 访问资源的能力相同,但是拥有 更多的硬件资源- 因为在初始化
uboot
的过程中 , 要做的事情是初始化系统相关硬件资源,需要 获取尽量多的权限,以方便操作硬件,初始化硬件 , 所以最终选择SVC
模式
2.3.2.0.3 条件执行指令
为了提高代码密度,减少ARM指令的数量,几乎所有的ARM指令都可以根据CPSR寄存器中的标志位,通过指令组合实现条件执行。
如:
- 无条件跳转指令
B
,我们可以在后面加上条件码组成BEQ
、BNE
组合指令。 BEQ
指令表示两个数比较,结果相等时跳转;BNE
指令则表示结果不相等时跳转bicne
指令表示 标志位Z= 0
的时候 , 执行清零指令bic
ARM指令的条件码
BL跳转指令
格式 : BL{条件} 目标地址
作用 :
- 但跳转之前,会在寄存器
RL
(即R14
)中保存PC的当前内容- BL指令一般用在函数调用的场合
例
BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中
... ; 子程序返回后接着从此处继续执行
2.3.2.0.4 CP15协处理器
CP15
协处理器一般用于存储系统管理,但是在中断中也会使用到,CP15
协处理器一共有16 个 32 位寄存器
( c0~c15 )
。CP15
协处理器的访问通过如下另个指令完成:
MRC: 将
CP15
协处理器中的寄存器数据读到ARM
寄存器中MCR: 将
ARM
寄存器的数据写入到CP15
协处理器寄存器中MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
- **cond:**指令执行的条件码,如果忽略的话就表示无条件执行
- opc1:协处理器要执行的操作码
- Rt:
ARM
源寄存器,要写入到CP15
寄存器的数据就保存在此寄存器中- CRn:
CP15
协处理器的目标寄存器- CRm: 协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将
CRm
设置为C0
,否则结果不可预测- opc2:可选的协处理器特定操作码,当不需要的时候要设置为 0
例: 将 CP15
中 C0
寄存器的值读取到 R0
寄存器中,
MRC p15, 0, r0, c0, c0, 0
其中四个寄存器
- 通过
c0
寄存器可以获取到处理器内核信息 - 通过
c1
寄存器可以使能或禁止 MMU、I/D Cache 等; - 通过
c12
寄存器可以设置中断向量偏移 ( 如设置中断向量表偏移的时候就需要将新的中断向量表基地址写入VBAR
中 ) - 通过
c15
寄存器可以获取GIC (中断控制器)
基地址
例 :
/** 设置中断向量表:* (OMAP4 spl TEXT_BASE is not 32 byte aligned.* Continue to use ROM code vector only in OMAP4 spl)*/
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))/* 在CP15 SCTLR寄存器中设置V=0,并 用 VBAR 重新定位向量表 */mrc p15, 0, r0, c1, c0, 0 @ 将CP15协处理器的 C1寄存器值读到r0寄存器bic r0, #CR_V @ 将SCTLR寄存器的bit13位V 清零 , (即此时向量表基地址为 0X00000000,软件可以重定位向量表)mcr p15, 0, r0, c1, c0, 0 @ 将CP15协处理器的 C1寄存器值写到r0寄存器/* 在CP15 VBAR寄存器中设置向量地址 */ldr r0, =_startmcr p15, 0, r0, c12, c0, 0 @重定位向量表 将VBAR寄存器值设置为 _start , 即整个uboot 的入口地址
#endif
2.3.2.1 阶段1 : 初始化外设硬件
2.3.2.1.1 uboot程序入口点 __start
位置:
arch/arm/lib/vectors.S
上电启动后,代码执行到
_start
函数,调用reset
函数
reset
的函数目的是将处理器设置为SVC
模式,并且关闭FIQ
和IRQ
,然后设置中断向量以及初始化CP15
协处理器
_start:#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endifb resetldr pc, _undefined_instructionldr pc, _software_interruptldr pc, _prefetch_abortldr pc, _data_abortldr pc, _not_usedldr pc, _irqldr pc, _fiq
下面的
8~14 行
就是是 中断向量表可以看到 直接跳到
reset
函数 (reset
函数直接跳转到save_boot_params
函数)reset:/* Allow the board to save important registers */b save_boot_params
save_boot_params
也同样是直接跳转到save_boot_params_ret
函数
2.3.2.1.2 save_boot_params_ret函数
位置 :
arch/arm/lib/vectors.S
save_boot_params_ret
函数主要完成以下功能:
- 当前处理器模式不为
HYP
模式时 , 将处理器模式设置为SVC
模式 ,并禁用IRQ
和FIQ
两个中断- 重定位 中断向量表 ,将其定位到
uboot
的起始地址 ( 这里取0x8780 0000
)- 调用
cpu_init_cp15
函数 , 设置其他和CP15
有关的设置(cache, MMU, tlb)
, 打开I-cache
- 调用
cpu_init_crit
函数 , 并最终生成一个属于IMX6ULL
内部RAM
的临时堆栈- 调用
_main
函数 ,
save_boot_params_ret:/** 当前系统不处于 HYP 模式时* 禁用中断(FIQ和IRQ),也将cpu设置为SVC (管理)模式*/mrs r0, cpsr @ 读cpsr的值 , 并保存到 r0寄存器中and r1, r0, #0x1f @ 使用位与操作 , 提取 CPSR寄存器的 bit0 ~ bit4 四位, 即用于设置 处理器工作模式的四位 teq r1, #0x1a @ 检查当前是否是 HYP模式 , 使用teq将 r1 与 0x1a进行异或运算 ,并将结果更新 CPSR标志位 bicne r0, r0, #0x1f @ 当 CPSR寄存器的标志位Z != 1 (即之前运算结果不为0 , 即不处于HYP模式),清除r0的低5位orrne r0, r0, #0x13 @ 设置处理器模式为 SVC模式orr r0, r0, #0xc0 @ 禁用 FIQ 和 IRQ (SPCR寄存器的 I为和F位 控制IRQ和FIQ,设置为1则禁用)msr cpsr,r0 @ 将寄存器的值写回CPSR/** 设置中断向量表* c1寄存器 的bit13位是 'V' (向量表控制位), * 为0时,向量表基地址为0x00000000(可重定位),* 为1时,向量表基地址为0xFFFF0000(不可重定位)*/
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))mrc p15, 0, r0, c1, c0, 0 @ 读取CP15协处理器的c1寄存器,即SCTLRbic r0, #CR_V @ CR_V = (1 << 13) 所以是清除c1寄存器 的bit13位(V) mcr p15, 0, r0, c1, c0, 0 @ 写SCTLR/* 在CP15协处理器的 VBAR寄存器(C12)中 设置向量表的重定位地址 , */ldr r0, =_start @ 设置向量表的重定位地址 , 即整个uboot起始地址 (0x8780 0000)mcr p15, 0, r0, c12, c0, 0 @ 将r0的值写入 VBAR
#endif/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 @ 调用cpu_init_cp15函数, 用来设置和CP15有关的设置(cache, MMU, tlb),打开I-cachebl cpu_init_crit @ 调用cpu_init_crit函数 , 再调用lowlevel_init函数
#endifbl _main @ 调用_main函数,
- 在第33行 , 调用
cpu_init_crit
, 这个函数内部仅仅调用了lowlevel_init
函数lowlevel_init
用于创建一个IMX6ULL
内部的 临时堆栈
补充:
IMX6ULL学习笔记(四) —— uboot 启动流程相关推荐
- W801单片机学习笔记——SDK的启动流程,例程使用
目录 1.前言 2.SDK的启动流程 3.挖坑 1.前言 W801的SDK需要配套的CDK集成开发环境进行开发,该SDK具有W801单片机所有硬件的驱动程序,FreeRTOS操作系统,基于蓝牙和WiF ...
- IMX6ULL学习笔记(9)——通过SD卡启动Linux内核
一.搭建环境 通过以下方式烧写一个镜像: IMX6ULL学习笔记(2)--通过SD卡烧录镜像 二.设置为SD卡启动模式 开发板插入烧录好U-Boot的SD卡. 根据以下BOOT拨码开关启动配置表,调整 ...
- U-BOOT学习之2014.4版Uboot启动流程分析
一.前言 老大给我布置了一个任务:某某项目uboot开发之usb增强ic驱动. 不知道大家看到这个任务懵不懵,反正我最开始是蒙的.后来又问了一下,才明白到底要做啥. 任务是这样的:因为这个项目的usb ...
- IMX6ULL学习笔记(18)——GPIO中断
一.中断简介 相比 STM32 的 NVIC,IMX6ULL 的中断控制系统更复杂,它的中断管理器使用的是 GIC V2,GIC V2 的实现方式与我们熟知的 NVIC 差别较大. 1.1 GIC G ...
- IMX6ULL学习笔记(20)——UART串口使用
一.UART简介 i.MX6U 芯片具有多达 8 个 UART 外设用于串口通讯,UART 是在 USART 基础上裁剪掉了同步通信功能,只支持异步通信.简单区分同步和异步就是看通信时需不需要对外提供 ...
- IMX6ULL学习笔记(19)——时钟系统
一.时钟系统简介 I.MX6U 的系统主频为 528MHz,有些型号可以跑到 696MHz,但是默认情况下内部 boot rom 会将 I.MX6U 的主频设置为 396MHz.我们在使用 I.MX6 ...
- U-BOOT启动流程【03】
前言 第一次学u-boot启动流程的时候我也是一头雾水,当然啦看一遍留个大概印象就好! 要想都学透的话也要花费很大的时间精力 本系列blog分三次写.本节讲最后一讲 在虚拟机中我的Vcode比较卡,所 ...
- 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- ARMv8架构u-boot启动流程详细分析(一)
文章目录 1 概述 2 armv8 u-boot的启动 3 u-boot源码整体结构和一些编译配置方式 3.1 编译配置方式 3.2 u-boot源码结构 4 u-boot armv8链接脚本 4.1 ...
最新文章
- 安卓环境搭建 SDK emulator directory is missing
- RabbitMQ 入门
- virtualenv wrapper安装配置
- ubuntu卸载openjdk-11
- 28个不得不看的经典编程算法!!
- excel条件格式详解
- mysql-5.7.17.msi安装
- Nginx默认虚拟主机、 Nginx用户认证、Nginx域名重定向、访问日志·····
- 在VMware Workstation 9中安装Mac OS X 10.8 Mountain Lion
- ROS教程(四):RVIZ使用教程(详细图文)
- 网络与信息安全应急处置预案
- win10如何扩大c盘空间【系统天地】
- 打开word2016文档时提示用文本恢复转换器打开文件
- 【Web安全笔记】之【9.0 工具与资源】
- (How to)使用IE9的F12开发人员工具分析模拟登陆网站(百度首页)的内部逻辑过程
- 汇智动力软件测试问题,汇智动力—测试工程师都是怎么写测试用例的?
- 计算机win7如何连接wifi网络,win7怎么连wifi,win7电脑连不上网
- 使用网址--java JPI代码示例
- 无光照渲染shader-二次元
- javascript 关于年龄计算
热门文章
- 软考中级考试报名选择哪个比较容易?
- 10个针对设计师/开发者的实用CSS工具推荐 转
- Eclipse不支持tomcat9解决方法
- 【opencv】调用caffe、tensorflow、darknet模型
- 【Blog.Idp开源】支持在线密码找回
- Msys2常见问题解决办法札记
- Visio将多页图片打印到一张纸上
- Encoding::UndefinedConversionError: \xE5 from ASCII-8BIT to UTF-8
- SQLite 附加数据库
- pythonapi接口开发教程_Python-接口开发入门解析