共计 7019 个字符,预计需要花费 18 分钟才能阅读完成。
丸趣 TV 小编给大家分享一下如何实现 Cortex-A9 uboot 启动代码,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!
一、uboot
1. 概念
U-Boot 是一个主要用于嵌入式系统的引导加载程序,可以支持多种不同的计算机系统结构,包括 PPC、ARM、AVR32、MIPS、x86、68k、Nios 与 MicroBlaze。这也是一套在 GNU 通用公共许可证之下发布的自由软件。
U-Boot 不仅仅支持嵌入式 Linux 系统的引导,它还支持 NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android 嵌入式操作系统。其目前要支持的目标操作系统是 OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS, android。
2. uboot 基本功能
U-Boot 可支持的主要功能列表:
系统引导支持 NFS 挂载、RAMDISK(压缩或非压缩) 形式的根文件系统; 支持 NFS 挂载、从 FLASH 中引导压缩或非压缩系统内核;
基本辅助功能强大的操作系统接口功能; 可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤以 Linux 支持最为强劲; 支持目标板环境参数多种存储方式,如 FLASH、NVRAM、EEPROM;
CRC32 校验可校验 FLASH 中内核、RAMDISK 镜像文件是否完好;
设备驱动串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC 等驱动支持;
上电自检功能 SDRAM、FLASH 大小自动检测;SDRAM 故障检测;CPU 型号。
3. 常用命令
uboot 命令比较多,下面只列举网络启动要用到的命令:
4. 配置参数举例
以下以网络下载内核、网络挂载 nfs 为例。
1)ubuntu 环境
ubuntu ip:192.168.6.186
nfs 配置:
配置文件如下:
/etc/exports
配置信息如下:
nfs
2) 开发板设置
开发板 ip:192.168.6.187
配置命令:
setenv ipaddr 192.168.6.187 ; 板子的 ip setenv serverip 192.168.6.186 ; 虚拟机的 ip setenv gatewayip 192.168.1.1 ; 网关 saveenv ; 保存配置
加载内核和设备树
setenv bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 - 42000000
bootcmd:uboot2 启动之后,首先先执行找到这个参数,执行后面的命令。
从 tftp 服务器下载内核镜像 uImage 到地址 41000000,设备树文件 exynos4412-fs4412.dtb 到 42000000,并通过命令 bootm 加载启动内核。
挂载 nfs
setenv bootargs root=/dev/nfs nfsroot=192.168.6.186:/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.6.187
挂载 nfs 文件系统
root=/dev/nfs
nfsroot=192.168.6.186:/rootfs nfs 服务器地址 192.168.6.186,目录为 /rootfs,
rw 文件系统操作权限为可续写
console=ttySAC2,115200 串口名称和波特率
init=/linuxrc 内核启动后运行的进程为 linuxrc
ip=192.168.6.187 开发板地址
二、exynos-4412 Soc 启动顺序
要想了解 exynos-4412 的启动顺序,我们首先需要了解该 soc 的内存布局。
1. exynos-4412 内存布局
通常一款 soc 的内存在厂家设计的时候就已经规定死了,对于使用者来说,我们无法改变。
我们只关心和启动相关的一个地址,
鸿蒙官方战略合作共建——HarmonyOS 技术社区
iROM 在 soc 内部,出厂时厂家固化了特定的程序,iROM 中程序对应用户来说不可改变
iRAM 在 soc 内部,速度较快,但空间不大
DMC RAM 控制器,位于 SOC 内部,用于驱动 RAM,大容量的 RAM 都需要连接到该控制器
2. Booting Sequence
不同的厂家的启动顺序是不太一样的,本篇主要以三星的 exynos-4412 soc 为基础,讲解该基于该板子的 uboot 启动顺序。
根据上图,系统启动的大概顺序:
iROM 在 SOC 内部,是一个 64KB 的 ROM,他树池化一些系统启动必须的功能。比如:时钟、栈。
iROM 负责从特殊的启动外设加载 BL1 的 image 到 soc 内部的 256KB 的 SRAM 中。启动的外设由操作按钮来决定的。根据不同按键的值,iROM 将会对 bl1 的 image 做不同的校验。
BL1 初始化系统时钟和 DRAM 控制器,然后从启动外设加载 OS image 到 DRAM 中。根据启动按钮的值的不同,BL1 会对 OS 做不同的校验。
启动完成之后,BL1 跳转到操作系统 (kernel)。
iROM 会根据 OM 引脚的不同选择不同的启动设备,对应的 OM 寄存器需要提供对应的启动信息。
三、内核启动流程概述
1. 内核启动流程 概述
uboot 启动流程
如上图所示:
鸿蒙官方战略合作共建——HarmonyOS 技术社区
设备上电之后,先执行 iROM 中的出厂代码,先进行必要硬件的初始化 去执行 uboot,
通常把 kernel、设备树文件放到 flash 中
程序启动之后,往往先从 flash 启动,运行 uboot
第一步:先进行硬件的初始化 (svc 模式栈、clock、内存、串口) 第二步:自搬移:把 uboot 从 flash 中拷贝到 RAM 中,跳转到 RAM 中执行剩下的 uboot 代码
第三步:把内核拷贝到 RAM 中,执行内核,把控制权交给内核。
2. 内核启动详细流程
开发板从上电到启动内核的过程
四、uboot 启动流程代码详解
1. lds 文件
要想了解 uboot 整个项目的代码流程,必须首先了解链接脚本【链接脚本参考《7. 从 0 开始学 ARM-GNU 伪指令,lds 使用》】。
该文件决定了 uboot 最终生成的镜像文件,各个段的布局。
uboot 链接脚本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件内容:
26 OUTPUT_FORMAT(elf32-littlearm , elf32-littlearm , elf32-littlearm) 27 OUTPUT_ARCH(arm) 28 ENTRY(_start) 29 SECTIONS 30 { 31 . = 0x00000000; 32
33 . = ALIGN(4); 34 .text : 35 { 36 __image_copy_start = .; 37 CPUDIR/start.o (.text*) 38 *(.text*) 39 } 40 41 . = ALIGN(4); 42 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 43 44 . = ALIGN(4); 45 .data : { 46 *(.data*) 47 } 48 49 . = ALIGN(4); 50 51 . = .; 52 53 . = ALIGN(4); 54 .u_boot_list : { 55 #include u-boot.lst 56 } 57 58 . = ALIGN(4); 59 60 __image_copy_end = .; 61 62 .rel.dyn : { 63 __rel_dyn_start = .; 64 *(.rel*) 65 __rel_dyn_end = .; 66 } 67 68 .dynsym : { 69 __dynsym_start = .; 70 *(.dynsym) 71 } 72 73 _end = .; 74 75 /* 76 * Deprecated: this MMU section is used by pxa at present but 77 * should not be used by new boards/CPUs. 78 */ 79 . = ALIGN(4096); 80 .mmutable : { 81 *(.mmutable) 82 } 83 84 .bss __rel_dyn_start (OVERLAY) : { 85 __bss_start = .; 86 *(.bss*) 87 . = ALIGN(4); 88 __bss_end__ = .; 89 } 90 91 /DISCARD/ : { *(.dynstr*) } 92 /DISCARD/ : { *(.dynamic*) } 93 /DISCARD/ : { *(.plt*) } 94 /DISCARD/ : { *(.interp*) } 95 /DISCARD/ : { *(.gnu*) } 96 } 97
核心内容解释:
27 OUTPUT_ARCH(arm) : 该镜像运行在 arm 架构的硬件上 28 ENTRY(_start) : 程序的入口是 _start 29 SECTIONS 30 { 31 . = 0x00000000; : 程序的链接地址,不是运行地址【uboot 一定是位置无关码】 34 .text : 35 { 36 __image_copy_start = .; : 宏对应整个程序编译好后首地址,自搬移代码的初始位置 37 CPUDIR/start.o (.text*) : 第一个目标文件 CPUDIR/start.o 中的代码段 38 *(.text*) : 剩下的目标文件的代码段 39 } 60 __image_copy_end = .; : 自搬移代码的结束为止
BSS 全局未初始化变量、全局初始化为 0 的变量所在的段:
84 .bss __rel_dyn_start (OVERLAY) : { 85 __bss_start = .; 88 __bss_end__ = .; 89 }
2. uboot 启动代码流程概要
代码只分析到 uboot 命令行,函数 main_loop() 位置。
3. 启动代码详细分析
_start 入口位于以下文件:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
第一阶段:
第二阶段
第二阶段代码从_main 开始:
以上代码详细解释,请结合 B 站视频同步学习。
五、uboot 启动的几个关键知识点
1. 如何判断第一条机器指令的位置?
链接脚本决定了内存的布局。
uboot 链接脚本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件内容:
28 ENTRY(_start) 29 SECTIONS 30 { 31 . = 0x00000000; 32
uboot 的入口是_start
链接地址是 0x00000000
2.uboot 如何搬运代码?
代码位于:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
搬移代码如下:
ENTRY(relocate_code) mov r4, r0 /* save addr_sp */ mov r5, r1 /* save addr of gd */ mov r6, r2 /* save addr of destination */ adr r0, _start cmp r0, r6 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq relocate_done /* skip relocation */ mov r1, r6 /* r1 - scratch for copy_loop */ ldr r3, _image_copy_end_ofs add r2, r0, r3 /* r2 - source end address */ copy_loop: ldmia r0!, {r9-r10} /* copy from source address [r0] */ stmia r1!, {r9-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end address [r2] */ blo copy_loop
详情参考第四章,第 3 节。
3.uboot 中,如何判断此次开机是从断电状态开机还是从休眠状态启动的?
board/samsung/fs4412/lowlevel_init.S
代码如下:
41 lowlevel_init: 54 /* AFTR wakeup reset */ 55 ldr r2, =S5P_CHECK_DIDLE 56 cmp r1, r2 57 beq exit_wakeup 58 59 /* LPA wakeup reset */ 60 ldr r2, =S5P_CHECK_LPA 61 cmp r1, r2 62 beq exit_wakeup 63 64 /* Sleep wakeup reset */ 65 ldr r2, =S5P_CHECK_SLEEP 66 cmp r1, r2 67 beq wakeup_reset 112 wakeup_reset: 113 bl system_clock_init 114 bl mem_ctrl_asm_init 115 bl tzpc_init 116 117 exit_wakeup: 118 /* Load return address and jump to kernel */ 119 ldr r0, =(EXYNOS4_POWER_BASE + INFORM0_OFFSET) 120 121 /* r1 = physical address of exynos4210_cpu_resume function */ 122 ldr r1, [r0] 123 124 /* Jump to kernel*/ 125 mov pc, r1
由上可知,当手机因为各种原因进入休眠时,会将当前程序执行的上下文保护起来,并向一些 pmic 的寄存器中写入指定的数据,以表明此次是因为何种原因进入休眠。
而手机并没有完全断电,而是处于一个低功耗模式下,此时启动 RAM 仍然有数据,所以在此启动后,只需要从特殊的寄存器中读取相应的值,就可以知道之前是因为什么原因休眠,进而回复休眠之前的上下文即可。
3.uboot 代码搬到 ram 之后,代码的运行地址发生了变化,如何保证程序跳转不会出错?
除了要保证 uboot 代码是基于地址无关的,此外.rel.dyn 帮我们解决了,其实主要还是编译器帮我们做了很多工作。
位置无关码参考《15. 从 0 学 ARM- 什么是位置无关码?》
4. 设备启动的时候,有可能直接从 ram 启动,如何知道当前是从 flah 启动还是 ram 启动的?
文件:
board/samsung/fs4412/lowlevel_init.S
代码:
lowlevel_init:
85 /* 86 * If U-boot is already running in ram, no need to relocate U-Boot. 87 * Memory controller must be configured before relocating U-Boot 88 * in ram. 89 */ 90 ldr r0, =0x0ffffff /* r0 - Mask Bits*/ 91 bic r1, pc, r0 /* pc - current addr of code */ 92 /* r1 - unmasked bits of pc */ 93 ldr r2, _TEXT_BASE /* r2 - original base addr in ram */ 94 bic r2, r2, r0 /* r2 - unmasked bits of r2*/ 95 cmp r1, r2 /* compare r1, r2 */ 96 beq 1f /* r0 == r1 then skip sdram init */
原理:RAM 地址空间是:0x40000000-0xA0000000 0xA0000000-0x00000000 而 iROM/iRAM 地址的 bit:28-31 均是 0, 所以只需要读取出执行到 lowlevel_init 时 pc 的值,判断其 bit:28-31 是否是 0 即可知道现在代码是否运行在 RAM 中。
看完了这篇文章,相信你对“如何实现 Cortex-A9 uboot 启动代码”有了一定的了解,如果想了解更多相关知识,欢迎关注丸趣 TV 行业资讯频道,感谢各位的阅读!