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) 是整个函数的入口,_startarch/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.mapuboot 的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址
段 .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() 宏

使用ENTRYENDPROC两个宏来定义一个名为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,我们可以在后面加上条件码组成BEQBNE组合指令。
  • 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:协处理器要执行的操作码
    • RtARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中
    • CRnCP15 协处理器的目标寄存器
    • CRm: 协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm 设置为 C0,否则结果不可预测
    • opc2:可选的协处理器特定操作码,当不需要的时候要设置为 0

例:CP15C0 寄存器的值读取到 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模式,并且关闭FIQIRQ,然后设置中断向量以及初始化 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模式 ,并禁用IRQFIQ两个中断
  • 重定位 中断向量表 ,将其定位到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 启动流程相关推荐

  1. W801单片机学习笔记——SDK的启动流程,例程使用

    目录 1.前言 2.SDK的启动流程 3.挖坑 1.前言 W801的SDK需要配套的CDK集成开发环境进行开发,该SDK具有W801单片机所有硬件的驱动程序,FreeRTOS操作系统,基于蓝牙和WiF ...

  2. IMX6ULL学习笔记(9)——通过SD卡启动Linux内核

    一.搭建环境 通过以下方式烧写一个镜像: IMX6ULL学习笔记(2)--通过SD卡烧录镜像 二.设置为SD卡启动模式 开发板插入烧录好U-Boot的SD卡. 根据以下BOOT拨码开关启动配置表,调整 ...

  3. U-BOOT学习之2014.4版Uboot启动流程分析

    一.前言 老大给我布置了一个任务:某某项目uboot开发之usb增强ic驱动. 不知道大家看到这个任务懵不懵,反正我最开始是蒙的.后来又问了一下,才明白到底要做啥. 任务是这样的:因为这个项目的usb ...

  4. IMX6ULL学习笔记(18)——GPIO中断

    一.中断简介 相比 STM32 的 NVIC,IMX6ULL 的中断控制系统更复杂,它的中断管理器使用的是 GIC V2,GIC V2 的实现方式与我们熟知的 NVIC 差别较大. 1.1 GIC G ...

  5. IMX6ULL学习笔记(20)——UART串口使用

    一.UART简介 i.MX6U 芯片具有多达 8 个 UART 外设用于串口通讯,UART 是在 USART 基础上裁剪掉了同步通信功能,只支持异步通信.简单区分同步和异步就是看通信时需不需要对外提供 ...

  6. IMX6ULL学习笔记(19)——时钟系统

    一.时钟系统简介 I.MX6U 的系统主频为 528MHz,有些型号可以跑到 696MHz,但是默认情况下内部 boot rom 会将 I.MX6U 的主频设置为 396MHz.我们在使用 I.MX6 ...

  7. U-BOOT启动流程【03】

    前言 第一次学u-boot启动流程的时候我也是一头雾水,当然啦看一遍留个大概印象就好! 要想都学透的话也要花费很大的时间精力 本系列blog分三次写.本节讲最后一讲 在虚拟机中我的Vcode比较卡,所 ...

  8. 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. ARMv8架构u-boot启动流程详细分析(一)

    文章目录 1 概述 2 armv8 u-boot的启动 3 u-boot源码整体结构和一些编译配置方式 3.1 编译配置方式 3.2 u-boot源码结构 4 u-boot armv8链接脚本 4.1 ...

最新文章

  1. 安卓环境搭建 SDK emulator directory is missing
  2. RabbitMQ 入门
  3. virtualenv wrapper安装配置
  4. ubuntu卸载openjdk-11
  5. 28个不得不看的经典编程算法!!
  6. excel条件格式详解
  7. mysql-5.7.17.msi安装
  8. Nginx默认虚拟主机、 Nginx用户认证、Nginx域名重定向、访问日志·····
  9. 在VMware Workstation 9中安装Mac OS X 10.8 Mountain Lion
  10. ROS教程(四):RVIZ使用教程(详细图文)
  11. 网络与信息安全应急处置预案
  12. win10如何扩大c盘空间【系统天地】
  13. 打开word2016文档时提示用文本恢复转换器打开文件
  14. 【Web安全笔记】之【9.0 工具与资源】
  15. (How to)使用IE9的F12开发人员工具分析模拟登陆网站(百度首页)的内部逻辑过程
  16. 汇智动力软件测试问题,汇智动力—测试工程师都是怎么写测试用例的?
  17. 计算机win7如何连接wifi网络,win7怎么连wifi,win7电脑连不上网
  18. 使用网址--java JPI代码示例
  19. 无光照渲染shader-二次元
  20. javascript 关于年龄计算

热门文章

  1. 软考中级考试报名选择哪个比较容易?
  2. 10个针对设计师/开发者的实用CSS工具推荐 转
  3. Eclipse不支持tomcat9解决方法
  4. 【opencv】调用caffe、tensorflow、darknet模型
  5. 【Blog.Idp开源】支持在线密码找回
  6. Msys2常见问题解决办法札记
  7. Visio将多页图片打印到一张纸上
  8. Encoding::UndefinedConversionError: \xE5 from ASCII-8BIT to UTF-8
  9. SQLite 附加数据库
  10. pythonapi接口开发教程_Python-接口开发入门解析