版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/qianlong4526888/article/details/27698707

注意:由于文档是去年写的,内有多个图片。上传图片非常麻烦(须要截图另存插入等等)。我把文章的PDF版本号上传到了CSDN下载资源中。为了给自己赚点积分。所以标价2分,没有积分的同学能够直接留言跟我要,记得留下邮箱。

下面是文章内容,由于我懒得编辑图片了,所以文章看来会非常不爽,强烈推荐点击以上红色链接下载pdf版。

文件编号:DCC01

版本号号:1.0

 

 

 

 

 

ARM上电启动及Uboot代码分析

部    门:

                        

作    者:

                    

联系方式:

                   

日    期:

   2013.03.08                        

 

文件修订记录

时间

作者

主要修订内容

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

文件夹

文件修订记录...
1

文件夹...
2

摘要...
4

1ARM上电取第一条指令流程...
5

1.1上电后的第一条指令在哪里?...
5

1.1.1norflash和nandflash的异同...
5

2Uboot.lds链接脚本分析...
7

2.1为什么要分析uboot链接脚本?...
7

2.2连接代码详细分析...
7

3Uboot中start.S文件分析...
9

3.1start.S详细解释...
9

3.1.1_start9

3.1.2reset9

3.1.3cpu_init_cp15.
11

3.1.4cpu_init_crit11

3.1.5lowlevel_init13

3.1.6s_init14

3.1.7call_board_init_f15

3.1.8board_init_f15

3.1.9relocate_code.
16

3.1.10clear_bss.
17

3.1.11jump_2_ram..
17

3.2本章小结...
18

4板级初始化及跳入Linux内核执行...
19

4.1board_init_r19

4.1.1三级标题...
19

4.2本章小结...
19

5Uboot异常处理...
20

5.1Uboot异常向量表...
20

5.1.1异常处理入口函数...
20

5.1.2异常处理函数跳转...
21

5.1.3异常真正处理函数...
22

5.2本章总结...
24

结论...
25

參考文献...
26

问题总结及解答...
27

附录...
29

 

摘要

网上关于ARM的bootloader(以Uboot为例)的启动顺序的资料有好多,可是对于Uboot的地址映射、体系结构级操作介绍非常少,都是直接開始Start.s代码的阅读。

本文拟详细分析Uboot从上电。到第一条指令的执行。同一时候分析代码对于cache、TLB等部件的操作过程。

下面内容以u-boot-2012.04.01源代码为例,从网上非常easy下载该版本号。

1 ARM上电取第一条指令流程

1.1 上电后的第一条指令在哪里?

首先明白:对于ARM芯片。启动时pc值由CPU设计者规定,不同的ARM CPU有不同的值。比如S3C2440芯片上电后PC值被硬件设计者规定为0x0;其它ARM芯片不一定是0x0。

第一章讲述的上电取第一条指令过程以S3C2440为例,该芯片是ARMv4T架构,其它芯片在原理上相似。

S3C2440的启动时读取的第一条指令是在内存0x00地址处,无论是从nand flash还是nor flash启动。

可是上电后内存中是没有数据的,那么0x00地址处的指令是怎样放进去的?针对不同的flash(nandflash、norflash),操作方式是不同的。下面讲述从nandflash和norflash启动的不同流程。

 

1.1.1  norflash和nandflash的异同

nandflash:价格低,容量大,适合大容量数据存储,地址线和数据线共用I/O线。全部信息都通过一条线传送,类比于PC的硬盘。

norflash:价格贵。容量小。适合小容量的程序或数据存储,相似硬盘,可是能在当中执行程序;有独立地址线、数据线

sdram:主要用于程序执行时的程序存储、执行或计算。类比于PC的内存;

 

综上:norflash比較适合频繁随即读写的场合。通经常使用于存储代码并直接在当中执行。nandflash用于存储资料。

仅仅要知道以上大概差别即可。下面说明ARM从两种flash启动方式的异同。

 

1.1.1.1  ARM从nandFlash启动

若从nandflash启动,上电后nandflash控制器自己主动把nandflash存储器中的0——4K内容载入到芯片内的起步石(Steppingstone。起步石这个机制是处理器中集成的功能,对程序猿透明),即内部SRAM缓冲器中,同一时候把内部SRAM的起始地址设置为0x0(不同的CPU上电后的PC值不尽同样。对不同的CPU该值也不尽同样)。然后把这段片内SRAM映射到nGCS0片选的空间。进而CPU開始从内部SRAM的0x0处開始取得第一条指令,该过程全部是硬件自己主动完毕,不须要程序代码控制。

也许你有个疑问,为什么不能直接把nandflash映射到0x0地址处?非要经过内部SRAM缓冲?

答案是。nandflash根本没有地址线,没法直接映射,必须使用SRAM做一个载体。通过SRAM把剩余的nandflash代码(即剩余的uboot启动代码)复制到SDRAM中执行。

若想从nandflash启动,那么uboot最核心的代码必须放在前4k完毕。这4k代码要完毕ARM CPU的核心配置以及将剩余的代码复制到SDRAM中(若从norflash启动则没有4k这个大小的限制,可是还会在完毕最基本的设置后进入SDRAM中执行)。

 

1.1.1.2 ARM从norflash启动

若从norflash启动,则norflash直接被映射到内存的0x0地址处(就是nGCS0。这里就不须要片内SRAM来辅助了。所以片内SRAM的起始地址不变,还是0x40000000)。然后cpu从0x00000000開始执行(也就是在Norfalsh中执行)。

      须要说明的是。uboot代码段(.text段)起始位置必须是与上电后PC值一致,即编译uboot时,TEXT_BASE宏必须设置成0x0 。反汇编uboot文件后,文本段第一条指令的地址也是0.

总结:

1、从norflash还是从nandflash启动。是由ARM的OM1和OM0引脚组合决定

2、无论从norflash还是nandflash启动,S3C2440上电后的pc值为0x0

3、假设某芯片上电后PC值不是0x0。假如是0x38ff0000,那么从norflash启动时,硬件就要自己主动将其映射到0x38ff0000地址处;假设从nandflash启动。那么硬件就要自己主动将nandflash中的前4K内容载入到0x38ff0000地址处。

2 Uboot.lds链接脚本分析

2.1 为什么要分析uboot链接脚本?

由于u-boot.lds决定了u-boot可执行映像的链接方式,以及各个段的装载地址(装载域)和执行地址(执行域),也就是说。Uboot.lds文件指定uboot.bin可执行文件放到ROM中的哪个地址、在执行时在RAM中执行的起始地址。详细内容涉及装载域和执行域的概念。这里不详述。

2.2 连接代码详细分析

以u-boot-2012.04.01版本号为例。

 

 

总结:

1、SECTION后面的段都是依照顺序放到内存中的。比如text段后面跟着rodata段

2、该文件里没有指定段的载入地址(用AT命令),没指定的情况下载入地址和执行地址是同样的,也就是说在uboot.bin在rom和ram中的地址同样。

 

3 Uboot中start.S文件分析

3.1  start.S详细解释

上面分析的链接脚本中已经规定,首先启动的文件是arch/arm/cpu/armv7/start.S。

对于uboot的start.S,主要做的事情就是系统的各个方面的初始化。然后复制剩余代码到RAM中继续执行。

(1)    设置CPU模式

(2)    关闭cache, MMU, TLBs

(3)    设置栈,pll, mux, memory

(4)    设置watchdog, muxing,  and clocks

(5)    板级初始化

(6)    自我复制到RAM中。并跳转到RAM中继续执行。

下面内容依照程序执行流程进行解说。以ARMv7架构为例。

3.1.1 _start

文本段第一条指令就是一条跳转指令:

依据1.1章的分析。假设uboot是烧写到norflash中的,那么一上电的_start标号肯定是在0x0处。假设uboot已经启动。程序执行过了relocate(见后面),那么该标号就会被移动到TEXT_BASE标号处,该标号是编译uboot时程序猿指定的,详细数值见开发板的board/~/config.mk文件。

3.1.2 reset

 

CPSR的位域见ARM手冊。下图截图方便參考:

 

 

 

 

 

 

 

 

 

3.1.3 cpu_init_cp15

 

 

 

3.1.4 cpu_init_crit

 

 

 

 

 

 

 

 

 

 

3.1.5lowlevel_init

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3.1.6 s_init

 

 

 

 

 

 

 

3.1.7 call_board_init_f

3.1.8board_init_f

 

 

 

3.1.9 relocate_code

为什么uboot代码须要relocate?见问题总结及解答。

3.1.10 clear_bss

 

3.1.11jump_2_ram

 

 

 

 

 

 

 

 

 

 

 

 

 

3.2 本章小结

主要分析了uboot启动的第一阶段代码。

 

 

4 板级初始化及跳入Linux内核执行

4.1 board_init_r

该函数在u-boot-2012.04.01\arch\arm\cpu\armv7\omap-common\spl.c中。

其功能是:

(1)    初始化内存分配函数

(2)    假设系统有mmc设备。则初始化mmc设备

(3)    假设系统有nand设备。则初始化nand设备

(4)    进入uboot命令循环或者直接開始执行linux内核。

眼下临时不须要详细分析该部分代码。后期若须要会加上。

 

 

 

 

 

 

 

千万不要删除行尾的分节符,此行不会被打印。

“结论”曾经的全部正文内容都要编写在此行之前。


5 Uboot异常处理

5.1 Uboot异常向量表

紧跟b reset后面的就是异常向量表,发生异常后pc会被自己主动置为对应的值,进入对应异常处理程序。

5.1.1异常处理入口函数

 

 

 

5.1.2 异常处理函数跳转

/*

 * exception handlers

 */

      .align     5

undefined_instruction:

      get_bad_stack

      bad_save_user_regs

      bl    do_undefined_instruction

 

      .align     5

software_interrupt:

      get_bad_stack_swi

      bad_save_user_regs

      bl    do_software_interrupt

 

      .align     5

prefetch_abort:

      get_bad_stack

      bad_save_user_regs

      bl    do_prefetch_abort

 

      .align     5

data_abort:

      get_bad_stack

      bad_save_user_regs

      bl    do_data_abort

 

      .align     5

not_used:

      get_bad_stack

      bad_save_user_regs

      bl    do_not_used

 

#ifdef CONFIG_USE_IRQ        //假设在uboot中启用了用户中断。则跳入对应处理函

                         //数执行

      .align     5

irq:

      get_irq_stack

      irq_save_user_regs

      bl    do_irq

      irq_restore_user_regs

 

      .align     5

fiq:

      get_fiq_stack

      /* someone ought to write a more effective fiq_save_user_regs*/

      irq_save_user_regs

      bl    do_fiq

      irq_restore_user_regs

 

#else            //假设没有配置。则走还有一条路径。

 

      .align     5

irq:

      get_bad_stack

      bad_save_user_regs

      bl    do_irq

 

      .align     5

fiq:

      get_bad_stack

      bad_save_user_regs

      bl    do_fiq

 

#endif /* CONFIG_USE_IRQ */

#endif /* CONFIG_SPL_BUILD*/

 

 

5.1.3 异常真正处理函数

// u-boot-2012.04.01\arch\arm\lib\Interrupts.c

void do_undefined_instruction (struct pt_regs *pt_regs)

{

      printf ("undefinedinstruction\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_software_interrupt (struct pt_regs *pt_regs)

{

      printf ("software interrupt\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_prefetch_abort (struct pt_regs *pt_regs)

{

      printf ("prefetchabort\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_data_abort (struct pt_regs *pt_regs)

{

      printf ("dataabort\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_not_used (struct pt_regs *pt_regs)

{

      printf ("notused\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_fiq (struct pt_regs *pt_regs)

{

      printf ("fastinterrupt request\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

#ifndef CONFIG_USE_IRQ

void do_irq (struct pt_regs *pt_regs)

{

      printf ("interruptrequest\n");

      show_regs (pt_regs);

      bad_mode ();

}

#endif

void bad_mode (void)         //以上异常处理函数都跳转到bad_mode。该函数仅仅是

                         //挂起CPU,木有详细处理。

{

      panic ("Resetting CPU ...\n");

      reset_cpu (0);

}

 

5.2 本章总结

总结uboot下异常处理流程。发现ARMv7下的uboot没有实现异常处理,ARM的其它架构有。有可能是由于uboot代码不够新的原因。Uboot中的ARM异常处理流程都同样。

结论

參考文献

[1]   

[2]   

[3]   

 

 

 

 

千万不要删除行尾的分节符。此行不会被打印。

问题总结及解答

1、对于载入时地址和执行时地址不同的段。执行时它是怎么跳转到执行时地址的?

答: 连接地址<==>执行地址

      存储地址<==>载入地址

(1)对于有操作系统时,执行地址与载入地址不同。在载入过程中装载器就把段载入到它应该去的连接地址处(也就是生成该段时的执行地址)

(2)对于uboot,执行地址与载入地址不同一时候。须要它自己(比如前4k代码)将自己载入到执行地址处执行。

Uboot.lds文件里起始地址是0x00,可是config.mk中的TEXT_BASE是0x57e00000,可是生成的uboot反汇编文件里。为什么start.s的第一条指令地址也是0x57e00000?不应该是0x00么?由于start.s的载入地址和执行地址都是0x00啊。?

答:Uboot.lds的0x00:

跟在SECTION后面的第一条

location counter,总是默认初始化为0。config.mk中的TEXT_BASE就是ROM在CPU上的地址。也就是说。不同的CPU已经规定了不同的ROM地址

2、关于为何不能直接用mov指令,而非要用adr伪指令?

把全部uboot代码复制到内存新地址处。

在分析uboot的start.S中,看到一些指令,比方:

adr r0, _start

认为好像能够直接用mov指令实现即可。为啥还要这么麻烦地,去用ldr去实现?

关于此处的代码。为何要用adr指令:

adr r0, _start

其被编译器编译后。会被翻译成:sub r0, pc, #172

而不直接用mov指令直接将_start的值赋值给r0,相似于这样:

mov r0, _start

呢?

其原因主要是,

sub r0, pc, #172

这种代码。所处理的值。都是相对于PC的偏移量来说的。这种代码中,没有绝对的物理地址值。都是相对的值,利用产生位置无关代码。由于假设用mov指令:

mov r0, _start

那么就会被编译成这种代码:

mov r0, 0x33d00000

假设用了上面这种代码:

mov r0, 0x33d00000

那么,假设整个代码,即要执行的程序的指令。被移动到其它位置,那么

mov r0, 0x33d00000

这行指令,执行的功能,就是跳转到绝对的物理地址,而不是跳转到相对的_start的位置了,就不能实现我们想要的功能了,这样包括了绝对物理地址的代码,也就不是位置无关的代码了。

与此相对,这行指令:

sub r0, pc, #172

即使程序被移动到其它位置,那么该行指令还是能够跳转到相对PC往前172字节的地方。也还是我们想要的_start的位置。这样包括的都是相对的偏移位置的代码,就叫做位置无关代码。其长处就是不用操心你的代码被移动,即使程序的基地址变了,全部的代码的相对位置还是固定的。程序还是能够正常执行的。

关于,之所以不用上面的:

mov r0, 0x33d00000

相似的代码。除了上面说的,不是位置无关的代码之外,其还有个潜在的问题,那就是。关于mov指令的源操作数。此处即为0x33d00000,不一定是合法的mov 指令所同意的值。

【总结】

之所以用adr而不用mov。主要是为了生成地址无关代码。以及由于不方便推断一个数,是否是有效的mov的操作数。

 

3、为什么uboot代码须要relocate?

由于uboot启动时不在片外RAM中,为了加快执行,须要将uboot又一次复制到RAM中执行。

附录

 

千万不要删除行尾的分节符,此行不会被打印!

ARM上电启动及Uboot代码分析的更多相关文章

  1. Uboot代码分析

    (1)确定链接脚本文件:uboot根目录下Makefile中的LDSCRIPT宏值,就是指定链接脚本(如:arch/arm/cpu/u-boot.lds)路径用的.(2)从脚本文件找入口: 在链接脚本 ...

  2. 【嵌入式开发】 Bootloader 详解 ( 代码环境 | ARM 启动流程 | uboot 工作流程 | 架构设计)

    作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42462795 转载请著名出处 相关资源下载 :  -- u-boo ...

  3. arm linux kernel 从入口到start_kernel 的代码分析

    参考资料: <ARM体系结构与编程> <嵌入式Linux应用开发完全手册> Linux_Memory_Address_Mapping http://www.chinaunix. ...

  4. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  5. uboot启动阶段修改启动参数方法及分析

    作者:围补 本来启动方式这节不是什么复杂的事儿,不过想简单的说清楚明白,还真是不知道怎么组织.毕竟文字跟有声语言表达有别.但愿简单的东西别让我讲的太复杂! Arm板系统文件一般有三个——bootloa ...

  6. linux-3.2.36内核启动1-启动参数(arm平台 启动参数的获取和处理,分析setup_arch)【转】

    转自:http://blog.csdn.net/tommy_wxie/article/details/17093297 最近公司要求调试一个内核,启动时有问题,所以就花了一点时间看看内核启动. 看的过 ...

  7. uboot流程分析--修改android启动模式按键【转】

    本文转载自:http://blog.csdn.net/dkleikesa/article/details/9792747 本人用的android平台用的bootloader用的是uboot,貌似大多数 ...

  8. 【ARM-Linux开发】 uboot启动阶段修改启动参数方法及分析

    作者:围补 本来启动方式这节不是什么复杂的事儿,不过想简单的说清楚明白,还真是不知道怎么组织.毕竟文字跟有声语言表达有别.但愿简单的东西别让我讲的太复杂! Arm板系统文件一般有三个--bootloa ...

  9. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

随机推荐

  1. 【HMM】隐马尔科夫模型

    http://www.hankcs.com/nlp/hmm-and-segmentation-tagging-named-entity-recognition.html

  2. win7 自带计算机(for programmer)

    打开win7的Accessories, 看到Calculator, 点击打开计算器. 可以看到32位的2进制代码:

  3. 分布式缓存设计:一致性Hash算法

    缓存作为数据库前的一道屏障,它的可用性与缓存命中率都会直接影响到数据库,所以除了配置主从保证高可用之外还需要设计分布式缓存来扩充缓存的容量,将数据分布在多台机器上如果有一台不可用了对整体影响也比较小. ...

  4. NHibernate初学三之条件查询(Criteria Queries)与AspNetPager分页实例

    NHibernate除了SQL与HQL两种查询操作外,还有一种就是条件查询Criteria,本文将从网上整理一些Criteria的理论及小实例,最后通过一个结合AspNetPager分页来加深理解,必 ...

  5. glob模块--查询一个文件名列表

    ''' 在python中,glob模块是用来查找匹配的文件的 在查找的条件中,需要用到Unix shell中的匹配规则: * : 匹配所所有 ? : 匹配一个字符 *.* : 匹配如:[hello.t ...

  6. Linux 防火墙:Netfilter

    一.Netfilter 简介 (1) Netfilter 是 Linux 内置的一种防火墙机制,我们一般也称之为数据包过滤机制,而 iptables 只是操作 netfilter 的一个命令行工具(2 ...

  7. cut的用法【转】

    cut是一个选取命令,就是将一段数据经过分析,取出我们想要的.一般来说,选取信息通常是针对“行”来进行分析的,并不是整篇信息分析的. (1)其语法格式为:cut  [-bn] [file] 或 cut ...

  8. Android中Bitmap和Drawable详解

    一.相关概念 1.Drawable就是一个可画的对象,其可能是一张位图(BitmapDrawable),也可能是一个图形(ShapeDrawable),还有可能是一个图层(LayerDrawable) ...

  9. Nginx遇上Access Denied提示怎么解决

    这几天在摆弄linux下面的各种服务器,对nginx非常有兴趣. 于是把phpmyadmin传上去了,先是phpmyadmin配了半天,结果配好之后发现phpmyadmin一些logo.css.js文 ...

  10. __declspec的用法

    __declspec用于指定所给定类型的实例与Microsoft相关的存储方式.其它的有关存储方式的修饰符如static与extern等是C和C++语言的ANSI规范,而__declspec是一种扩展 ...