linux 启动 拷贝代码,详解Cortex-A9 uboot启动代码:常用命令分享

前言

我们在前面的arm系列课程,已经讲解了arm的架构、汇编指令、异常、常用外设的控制器驱动,那么我们已经具备开发arm系列产品的基本技能。

本篇给大家介绍一款比较常用的bootloader:uboot,通过uboot的介绍以及源代码的详细分析,让大家把之前所有ARM相关的知识点融会贯通起来。

一、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命令比较多,下面只列举网络启动要用到的命令:

431843bd0c5a4dbdac5ca7512be3a0c5.png

4. 配置参数举例

以下以网络下载内核、网络挂载nfs为例。

1)ubuntu环境

ubuntu ip:192.168.6.186

nfs配置:

配置文件如下:

/etc/exports

配置信息如下:

576c60b4b6351b023d1749a582813eb9.png

nfs

2)开发板设置

开发板ip:192.168.6.187

配置命令:

setenvipaddr192.168.6.187;板子的ip

setenvserverip192.168.6.186;虚拟机的ip

setenvgatewayip192.168.1.1;网关

saveenv;保存配置

加载内核和设备树

setenvbootcmdtftp41000000uImage\;tftp42000000exynos4412-fs4412.dtb\;bootm41000000-42000000

bootcmd:uboot2启动之后,首先先执行找到这个参数,执行后面的命令。

从tftp服务器下载内核镜像uImage到地址41000000,设备树文件exynos4412-fs4412.dtb到42000000,并通过命令bootm加载启动内核。

挂载nfs

setenvbootargsroot=/dev/nfsnfsroot=192.168.6.186:/rootfsrwconsole=ttySAC2,115200init=/linuxrcip=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的内存在厂家设计的时候就已经规定死了,对于使用者来说,我们无法改变。

c9d06789e92a58c7298972379cab5fcb.png

我们只关心和启动相关的一个地址,

iROM 在soc内部,出厂时厂家固化了特定的程序,iROM中程序对应用户来说不可改变

iRAM 在soc内部,速度较快,但空间不大

DMC RAM控制器,位于SOC内部,用于驱动RAM,大容量的RAM都需要连接到该控制器

2. Booting Sequence

不同的厂家的启动顺序是不太一样的,本篇主要以三星的exynos-4412 soc为基础,讲解该基于该板子的uboot启动顺序。

67c93be6a46fffcd17f95929c69638ae.png

根据上图,系统启动的大概顺序:

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. 内核启动流程 概述

7bb257cd6188a4ca147310b94238d5cf.png

uboot启动流程

如上图所示:

设备上电之后,先执行iROM中的出厂代码,先进行必要硬件的初始化 去执行uboot,

通常把kernel、设备树文件放到flash中

程序启动之后,往往先从flash启动,运行uboot

第一步:先进行硬件的初始化(svc模式栈、clock、内存、串口) 第二步:自搬移:把uboot从flash中拷贝到RAM中,跳转到RAM中执行剩下的uboot代码

第三步:把内核拷贝到RAM中,执行内核,把控制权交给内核。

2. 内核启动详细流程

0397739f281c34c620ff64dfb3f1db1a.png

开发板从上电到启动内核的过程

四、uboot启动流程代码详解

1. lds文件

要想了解uboot整个项目的代码流程,必须首先了解链接脚本【链接脚本参考《7. 从0开始学ARM-GNU伪指令,lds使用》】。

该文件决定了uboot最终生成的镜像文件,各个段的布局。

uboot链接脚本如下:

u-boot-2013.01/arch/arm/cpu/u-boot.lds

文件内容:

26OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")

27OUTPUT_ARCH(arm)

28ENTRY(_start)

29SECTIONS

30{

31.=0x00000000;

32

33.=ALIGN(4);

34.text:

35{

36__image_copy_start=.;

37CPUDIR/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

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:thisMMUsectionisusedbypxaatpresentbut

77*shouldnotbeusedbynewboards/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

核心内容解释:

27OUTPUT_ARCH(arm):该镜像运行在arm架构的硬件上

28ENTRY(_start):程序的入口是_start

29SECTIONS

30{

31.=0x00000000;:程序的链接地址,不是运行地址【uboot一定是位置无关码】

34.text:

35{

36__image_copy_start=.;:宏对应整个程序编译好后首地址,自搬移代码的初始位置

37CPUDIR/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()位置。

c32eb2c7bd419ba49b0cbbefeb73e06c.png

3. 启动代码详细分析

_start入口位于以下文件:

u-boot-2013.01/arch/arm/cpu/armv7/start.S

第一阶段:

f5d322c697c5b4f317b6bc2f5f80a088.png

第二阶段

第二阶段代码从_main开始:

5c0094b98f4d345b72c5b6b79d25269d.png

以上代码详细解释,请结合B站视频同步学习。

五、uboot启动的几个关键知识点

1.如何判断第一条机器指令的位置?

链接脚本决定了内存的布局。

uboot链接脚本如下:

u-boot-2013.01/arch/arm/cpu/u-boot.lds

文件内容:

28ENTRY(_start)

29SECTIONS

30{

31.=0x00000000;

32

uboot的入口是_start

链接地址是0x00000000

2.uboot如何搬运代码?

代码位于:

u-boot-2013.01/arch/arm/cpu/armv7/start.S

搬移代码如下:

ENTRY(relocate_code)

movr4,r0/*saveaddr_sp*/

movr5,r1/*saveaddrofgd*/

movr6,r2/*saveaddrofdestination*/

adrr0,_start

cmpr0,r6

moveqr9,#0/*norelocation.relocationoffset(r9)=0*/

beqrelocate_done/*skiprelocation*/

movr1,r6/*r1

ldrr3,_image_copy_end_ofs

addr2,r0,r3/*r2

copy_loop:

ldmiar0!,{r9-r10}/*copyfromsourceaddress[r0]*/

stmiar1!,{r9-r10}/*copytotargetaddress[r1]*/

cmpr0,r2/*untilsourceendaddress[r2]*/

blocopy_loop

详情参考第四章,第3节。

3.uboot中,如何判断此次开机是从断电状态开机还是从休眠状态启动的?

board/samsung/fs4412/lowlevel_init.S

代码如下:

41lowlevel_init:

54/*AFTRwakeupreset*/

55ldrr2,=S5P_CHECK_DIDLE

56cmpr1,r2

57beqexit_wakeup

58

59/*LPAwakeupreset*/

60ldrr2,=S5P_CHECK_LPA

61cmpr1,r2

62beqexit_wakeup

63

64/*Sleepwakeupreset*/

65ldrr2,=S5P_CHECK_SLEEP

66cmpr1,r2

67beqwakeup_reset

112wakeup_reset:

113blsystem_clock_init

114blmem_ctrl_asm_init

115bltzpc_init

116

117exit_wakeup:

118/*Loadreturnaddressandjumptokernel*/

119ldrr0,=(EXYNOS4_POWER_BASE+INFORM0_OFFSET)

120

121/*r1=physicaladdressofexynos4210_cpu_resumefunction*/

122ldrr1,[r0]

123

124/*Jumptokernel*/

125movpc,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*IfU-bootisalreadyrunninginram,noneedtorelocateU-Boot.

87*MemorycontrollermustbeconfiguredbeforerelocatingU-Boot

88*inram.

89*/

90ldrr0,=0x0ffffff/*r0

91bicr1,pc,r0/*pc

92/*r1

93ldrr2,_TEXT_BASE/*r2

94bicr2,r2,r0/*r2

95cmpr1,r2/*comparer1,r2*/

96beq1f/*r0==r1thenskipsdraminit*/

原理:RAM地址空间是:0x40000000-0xA0000000 0xA0000000-0x00000000 而iROM/iRAM地址的bit:28-31均是0,所以只需要读取出执行到lowlevel_init时pc的值,判断其bit:28-31是否是0即可知道现在代码是否运行在RAM中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值