文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

详解Cortex-A9 uboot启动代码

2024-12-03 12:00

关注

 前言

我们在前面的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可支持的主要功能列表:

3. 常用命令

uboot命令比较多,下面只列举网络启动要用到的命令:

4. 配置参数举例

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

1)ubuntu环境

ubuntu ip:192.168.6.186

nfs配置:

配置文件如下:

  1. /etc/exports 

配置信息如下:

 

nfs

2)开发板设置

开发板ip:192.168.6.187

配置命令:

  1. setenv ipaddr 192.168.6.187      ;板子的ip 
  2. setenv serverip 192.168.6.186    ;虚拟机的ip 
  3. setenv gatewayip 192.168.1.1     ;网关 
  4. saveenv                          ;保存配置 

加载内核和设备树

  1. setenv bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 - 42000000 

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

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

  1. setenv bootargs root=/dev/nfs nfsroot=192.168.6.186:/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.6.187 

挂载nfs文件系统

二、exynos-4412 Soc 启动顺序

要想了解exynos-4412的启动顺序,我们首先需要了解该soc的内存布局。

1. exynos-4412内存布局

通常一款soc的内存在厂家设计的时候就已经规定死了,对于使用者来说,我们无法改变。


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

  1. iROM 在soc内部,出厂时厂家固化了特定的程序,iROM中程序对应用户来说不可改变
  2. iRAM 在soc内部,速度较快,但空间不大
  3. DMC RAM控制器,位于SOC内部,用于驱动RAM,大容量的RAM都需要连接到该控制器

2. Booting Sequence

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


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

iROM会根据OM 引脚的不同选择不同的启动设备,对应的OM寄存器需要提供对应的启动信息。

三、内核启动流程概述

1. 内核启动流程 概述

 

uboot启动流程

如上图所示:

  1. 设备上电之后,先执行iROM中的出厂代码,先进行必要硬件的初始化 去执行uboot,
  2. 通常把kernel、设备树文件放到flash中
  3. 程序启动之后,往往先从flash启动,运行uboot
  4. 第一步:先进行硬件的初始化(svc模式栈、clock、内存、串口) 第二步:自搬移:把uboot从flash中拷贝到RAM中,跳转到RAM中执行剩下的uboot代码
  5. 第三步:把内核拷贝到RAM中,执行内核,把控制权交给内核。

2. 内核启动详细流程

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

四、uboot启动流程代码详解

1. lds文件

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

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

uboot链接脚本如下:

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

文件内容:

  1. 26 OUTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm"
  2.  27 OUTPUT_ARCH(arm) 
  3.  28 ENTRY(_start) 
  4.  29 SECTIONS 
  5.  30 { 
  6.  31     . = 0x00000000; 
  7.  32  
  8.  33     . = ALIGN(4); 
  9.  34     .text : 
  10.  35     { 
  11.  36         __image_copy_start = .; 
  12.  37         CPUDIR/start.o (.text*) 
  13.  38         *(.text*) 
  14.  39     } 
  15.  40  
  16.  41     . = ALIGN(4); 
  17.  42     .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 
  18.  43  
  19.  44     . = ALIGN(4); 
  20.  45     .data : { 
  21.  46         *(.data*) 
  22.  47     } 
  23.  48  
  24.  49     . = ALIGN(4); 
  25.  50  
  26.  51     . = .; 
  27.  52  
  28.  53     . = ALIGN(4); 
  29.  54     .u_boot_list : { 
  30.  55     #include  
  31.  56     } 
  32.  57  
  33.  58     . = ALIGN(4); 
  34.  59  
  35.  60     __image_copy_end = .; 
  36.  61  
  37.  62     .rel.dyn : { 
  38.  63         __rel_dyn_start = .; 
  39.  64         *(.rel*) 
  40.  65         __rel_dyn_end = .; 
  41.  66     } 
  42.  67  
  43.  68     .dynsym : { 
  44.  69         __dynsym_start = .; 
  45.  70         *(.dynsym) 
  46.  71     } 
  47.  72  
  48.  73     _end = .; 
  49.  74  
  50.  75      
  51.  79     . = ALIGN(4096); 
  52.  80     .mmutable : { 
  53.  81         *(.mmutable) 
  54.  82     } 
  55.  83  
  56.  84     .bss __rel_dyn_start (OVERLAY) : { 
  57.  85         __bss_start = .; 
  58.  86         *(.bss*) 
  59.  87          . = ALIGN(4); 
  60.  88         __bss_end__ = .; 
  61.  89     } 
  62.  90  
  63.  91     /DISCARD/ : { *(.dynstr*) } 
  64.  92     /DISCARD/ : { *(.dynamic*) } 
  65.  93     /DISCARD/ : { *(.plt*) } 
  66.  94     /DISCARD/ : { *(.interp*) } 
  67.  95     /DISCARD/ : { *(.gnu*) } 
  68.  96 } 
  69.  97  

核心内容解释:

  1. 27 OUTPUT_ARCH(arm)       :    该镜像运行在arm架构的硬件上 
  2. 28 ENTRY(_start)          :    程序的入口是 _start 
  3. 29 SECTIONS 
  4. 30 { 
  5. 31  . = 0x00000000;      :   程序的链接地址,不是运行地址【uboot一定是位置无关码】 
  6. 34     .text : 
  7. 35     { 
  8. 36         __image_copy_start = .;    : 宏对应整个程序编译好后首地址,自搬移代码的初始位置 
  9. 37         CPUDIR/start.o (.text*)    : 第一个目标文件CPUDIR/start.o中的代码段 
  10. 38         *(.text*)                  : 剩下的目标文件的代码段 
  11. 39     } 
  12. 60     __image_copy_end = .;          : 自搬移代码的结束为止 

BSS全局未初始化变量、全局初始化为0的变量所在的段:

  1. 84     .bss __rel_dyn_start (OVERLAY) : { 
  2.  85         __bss_start = .; 
  3.  88         __bss_end__ = .; 
  4.  89     } 

2. uboot启动代码流程概要

代码只分析到uboot命令行,函数main_loop()位置。


3. 启动代码详细分析

_start入口位于以下文件:

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

第一阶段:

 

第二阶段

第二阶段代码从_main开始:


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

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

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

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

uboot链接脚本如下:

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

文件内容:

  1. 28 ENTRY(_start) 
  2. 29 SECTIONS 
  3. 30 { 
  4. 31     . = 0x00000000; 
  5. 32  

uboot的入口是_start

链接地址是0x00000000

uboot如何搬运代码?

代码位于:

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

搬移代码如下:

  1. ENTRY(relocate_code) 
  2.  mov r4, r0  
  3.  mov r5, r1  
  4.  mov r6, r2  
  5.  
  6.  adr r0, _start 
  7.  cmp r0, r6 
  8.  moveq r9, #0   
  9.  beq relocate_done   
  10.  mov r1, r6    
  11.  ldr r3, _image_copy_end_ofs 
  12.  add r2, r0, r3   
  13.  
  14. copy_loop: 
  15.  ldmia r0!, {r9-r10}   
  16.  stmia r1!, {r9-r10}   
  17.  cmp r0, r2    
  18.  blo copy_loop 

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

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

  1. board/samsung/fs4412/lowlevel_init.S 

代码如下:

  1. 41   lowlevel_init: 
  2. 54      
  3. 55     ldr r2, =S5P_CHECK_DIDLE 
  4. 56     cmp r1, r2 
  5. 57     beq exit_wakeup 
  6. 58  
  7. 59      
  8. 60     ldr r2, =S5P_CHECK_LPA 
  9. 61     cmp r1, r2 
  10. 62     beq exit_wakeup 
  11. 63  
  12. 64      
  13. 65     ldr r2, =S5P_CHECK_SLEEP 
  14. 66     cmp r1, r2 
  15. 67     beq wakeup_reset 
  16.  
  17. 112 wakeup_reset: 
  18. 113     bl system_clock_init 
  19. 114     bl mem_ctrl_asm_init 
  20. 115     bl tzpc_init 
  21. 116  
  22. 117 exit_wakeup: 
  23. 118      
  24. 119     ldr r0, =(EXYNOS4_POWER_BASE + INFORM0_OFFSET) 
  25. 120  
  26. 121      
  27. 122     ldr r1, [r0] 
  28. 123  
  29. 124      
  30. 125     mov pc, r1 

由上可知,当手机因为各种原因进入休眠时,会将当前程序执行的上下文保护起来,并向一些pmic的寄存器中写入指定的数据,以表明此次是因为何种原因进入休眠。

而手机并没有完全断电,而是处于一个低功耗模式下,此时启动RAM仍然有数据,所以在此启动后,只需要从特殊的寄存器中读取相应的值,就可以知道之前是因为什么原因休眠,进而回复休眠之前的上下文即可。

uboot代码搬到ram之后,代码的运行地址发生了变化,如何保证程序跳转不会出错?

除了要保证uboot代码是基于地址无关的,此外.rel.dyn帮我们解决了,其实主要还是编译器帮我们做了很多工作。

位置无关码参考《15. 从0学ARM-什么是位置无关码?》

设备启动的时候,有可能直接从ram启动, 如何知道当前是从flah启动还是ram启动的?

文件:

  1. board/samsung/fs4412/lowlevel_init.S 

代码:

lowlevel_init:

  1. 85      
  2.  90     ldr r0, =0x0ffffff       
  3.  91     bic r1, pc, r0       
  4.  92                      
  5.  93     ldr r2, _TEXT_BASE       
  6.  94     bic r2, r2, r0       
  7.  95     cmp r1, r2           
  8.  96     beq 1f           

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

 

来源:一口Linux内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯