历史上的今天
返回首页

历史上的今天

今天是:2025年07月30日(星期三)

正在发生

2021年07月30日 | 移植uboot-分析uboot启动流程(详解)

2021-07-30 来源:eefocus

本节总结:


uboot启动流程如下:


1)设置CPU为管理模式

2)关看门狗

3)关中断

4)设置时钟频率   

5)关mmu,初始化各个bank

6)进入board_init_f()函数 (初始化定时器,GPIO,串口等,划分内存区域)

7)重定位     复制uboot,然后修改SDRAM上的uboot链接地址)

8)清bss

9)跳转到board_init_r()函数,启动流程结束


1.首先来安装arm-linux-gcc-4.3.2交叉编译器


mkdir  arm-linux-gcc-4.3.2                 //创建目录


tar -xjf  arm-linux-gcc-4.3.2.tar.bz2 -C arm-linux-gcc-4.3.2/  //解压到arm-linux-gcc-4.3.2目录下


然后添加环境变量:


有两种方法,第一种只是临时修改,重启虚拟机便会复位:


export PATH=/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/bin:/usr/sbin:/usr/bin... ...

             //将arm-linux-gcc-4.3.2添加到环境变量


第二种,重启不复位:


vi /etc/environment

添加:

PATH=/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/bin:/usr/sbin:/usr/bin... ...

                   //将arm-linux-gcc-4.3.2添加到环境变量


2.然后进入ftp://ftp.denx.de/pub/u-boot/来下载u-boot-2012.04.01


2.1.创建source insight工程,来看代码


1)在board 目录下只添加:


board/samsung/smdk2410/               // (2410单板文件)


2)在arch 目录下只添加:



arch/arm/cpu/arm920t/                //(只添加这个目录下的*.c,*.S公用文件)                


arch/arm/cpu/arm920t/s3c24x0/        //(24x0架构所有文件)


arch/arm/include/asm/                //(只添加这个目录下的*.h公用头文件)


arch/arm/include/asm/proc-armv/      //(arm架构的文件)


arch/arm/include/asm/arch-s3c24x0/   //(24x0架构头文件)


arch/arm/lib/                        //(与arm相关的库文件)


3)在include/configs目录下只添加:


include/configs/smdk2410.h              // (用来配置2410单板的头文件)


2.2编译烧写:


tar xjf u-boot-2012.04.01.tar.bz2


cd u-boot-2012.04.01                 //进入解压后文件目录


make smdk2410_config                 //由于该uboot不支持2440板卡,所以只有配置2410板卡


make                                 //编译,生成u-boot.bin


3.最后烧写u-boot.bin,发现无法启动,接下来便来分析uboot的启动流程


4.首先查看arch/arm/cpu/u-boot.lds链接脚本


如下图所示,看到uboot最开始会进入_start:

5. _start位于arch/arm/cpu/arm920t/start.S         


所以,我们从start.S开始分析uboot启动流程:


.globl _start                                //声明_start全局符号,这个符号会被lds链接脚本用到

_start:    

b     start_code                            //跳转到start_code符号处,0x00

       ldr   pc, _undefined_instruction                    //0x04

       ldr   pc, _software_interrupt                       //0x08

       ldr   pc, _prefetch_abort                           //0x0c

       ldr   pc, _data_abort                               //0x10

       ldr   pc, _not_used                                 //0x14

       ldr   pc, _irq                                      //0x18

       ldr   pc, _fiq                                      //0x20


_undefined_instruction:  .word undefined_instruction

           //定义_undefined_instruction指向undefined_instruction(32位地址)


_software_interrupt:      .word software_interrupt

_prefetch_abort:    .word prefetch_abort

_data_abort:          .word data_abort

_not_used:             .word not_used

_irq:               .word irq

_fiq:               .word fiq


   .balignl 16,0xdeadbeef        //balignl使用,参考http://www.cnblogs.com/lifexy/p/7171507.html


其中符号保存的地址都在顶层目录/system.map中列出来了


6. 从上面看到, _start会跳转到start_code处


start_code:


    /*设置CPSR寄存器,让CPU进入管理模式*/

       mrs  r0, cpsr                 //读出cpsr的值

       bic   r0, r0, #0x1f           //清位

       orr   r0, r0, #0xd3          //位或

       msr  cpsr, r0                 //写入cpsr


#if   defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)

       /*

        * relocate exception table

        */

       ldr   r0, =_start            

       ldr   r1, =0x0                //r1等于异常向量基地址

       mov r2, #16

copyex:

       subs       r2, r2, #1           //减16次,s表示每次减都要更新条件标志位

       ldr   r3, [r0], #4       

       str   r3, [r1], #4      //将_start标号后的16个符号存到异常向量基地址0x0~0x3c处

       bne  copyex             //直到r2减为0

#endif


#ifdef CONFIG_S3C24X0


       /* 关看门狗*/

#  define pWTCON       0x53000000

#  define INTMSK 0x4A000008    /* Interrupt-Controller base addresses */

#  define INTSUBMSK  0x4A00001C

#  define CLKDIVN       0x4C000014    /* clock divisor register */


       ldr   r0, =pWTCON       

       mov r1, #0x0        

       str   r1, [r0]           //关看门狗,使WTCON寄存器=0


       /*关中断*/

       mov r1, #0xffffffff

       ldr   r0, =INTMSK

       str   r1, [r0]                  //关闭所有中断

# if defined(CONFIG_S3C2410)

       ldr   r1, =0x3ff

       ldr   r0, =INTSUBMSK

       str   r1, [r0]                  //关闭次级所有中断

# endif


    /* 设置时钟频率, FCLK:HCLK:PCLK = 1:2:4 ,而FCLK默认为120Mhz*/

       ldr   r0, =CLKDIVN

       mov r1, #3

       str   r1, [r0]


 #ifndef CONFIG_SKIP_LOWLEVEL_INIT

       bl    cpu_init_crit                         //关闭mmu,并初始化各个bank


#endif


call_board_init_f:

       ldr   sp, =(CONFIG_SYS_INIT_SP_ADDR) //CONFIG_SYS_INIT_SP_ADDR=0x30000f80

       bic   sp, sp, #7         //sp=0x30000f80

       ldr   r0,=0x00000000

       bl    board_init_f


上面的CONFIG_SYS_INIT_SP_ADDR =0x30000f80,是通过arm-linux-objdump -D u-boot>u-boot.dis生成反汇编,然后从u-boot.dis得到的,如下图所示:

7.然后进入第一个C函数:board_init_f()


该函数主要工作是:


清空gd指向的结构体、通过init_sequence函数数组,来初始化各个函数以及逐步填充gd结构体,最后划分内存区域,将数据保存在gd里,然后调用relocate_code()对uboot重定位


(gd是用来传递给内核的参数)


1)具体代码如下所示:


void board_init_f(ulong bootflag) // bootflag=0x00000000

{

       bd_t *bd;

       init_fnc_t **init_fnc_ptr;         //函数指针

       gd_t *id;

       ulong addr, addr_sp;

#ifdef CONFIG_PRAM

       ulong reg;

#endif


       bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");

       /* Pointer is writable since we allocated a register for it */

       gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);


其中gd是一个全局变量,用来传递给内核的参数用的,如下图所示,在arch/arn/include/asm/global_data.h中定义,*gd指向r8寄存器,所以r8专门提供给gd使用

而CONFIG_SYS_INIT_SP_ADDR,在6节里得到=0x30000f80,所以gd=0x30000f80


2)继续来看board_init_f():


      __asm__ __volatile__("": : :"memory");           //memory:让cpu重新读取内存的数据


      memset((void *)gd, 0, sizeof(gd_t));        //将0x30000f80地址上的gd_t结构体清0


      gd->mon_len = _bss_end_ofs;  

         // _bss_end_ofs =__bss_end__ - _start,在反汇编找到等于0xae4e0,所以mon_len等于uboot的数据长度

      gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, (uintptr_t)gd->fdt_blob);


       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)

            //调用init_sequence[]数组里的各个函数

      {

              if ((*init_fnc_ptr)() != 0)     //执行函数,若函数执行出错,则进入hang()

             {    

           hang ();   //打印错误信息,然后一直while

             }


       }


上面的init_sequence[]数组里存了各个函数,比如有:


board_early_init_f():设置系统时钟,设置各个GPIO引脚

timer_init():初始化定时器

推荐阅读

史海拾趣

Danaher Corporation公司的发展小趣事

在十九世纪80年代,丹纳赫集团面对日益严峻的市场竞争,开始推行以精益生产为核心的全新运营方式。这种管理方式不仅强化了其在行业中的领导地位,还逐渐形成了独特的丹纳赫商业系统(Danaher Business System,DBS)。DBS注重持续改进、客户导向和团队合作,成为丹纳赫集团在电子行业取得成功的关键因素。

驰芯微(CHIPWISE)公司的发展小趣事

驰芯微公司一直注重技术研发和产品创新。公司拥有一支高素质的研发团队,不断在芯片设计、制造和封装等领域取得突破。其中,车规级霍尔传感器芯片XL3600系列和车规级32位微控制器MCU芯片XL6600系列是公司的明星产品。这些产品不仅性能卓越,而且可靠性高,赢得了客户的广泛认可。此外,驰芯微还不断推出新产品,如新能源动力多节电池组监控器BMS AFE芯片XL8812/XL8820系列等,进一步丰富了公司的产品线。

E Connector Solutions公司的发展小趣事

E Connector Solutions公司自成立以来,一直注重技术创新。在早期阶段,公司研发团队通过深入的市场调研,发现电子连接器市场存在着对高速、高密度连接器日益增长的需求。公司投入大量研发资源,成功开发出一款新型连接器产品,其传输速度更快、密度更高,满足了市场的需求。随着产品的推出,E Connector Solutions公司逐渐在市场中树立了技术领先的形象,赢得了客户的信赖。

Deltrol Controls公司的发展小趣事

随着电子设备的日益复杂,对内部连接件的要求也越来越高。Deltrol Controls意识到,传统的连接方式已经无法满足行业的需求。为此,公司投入大量资金研发新型软管组件,该组件不仅具有优异的耐温、耐压性能,而且安装简便、维护方便。这一突破性的产品迅速赢得了客户的青睐,Deltrol Controls在电子行业的影响力也进一步扩大。

Electro-Films Inc Semi-Films Div公司的发展小趣事

在20世纪80年代初,Electro-Films Inc. 的创始人意识到了电子行业中对高性能薄膜材料的需求日益增加。因此,公司决定成立一个专门负责薄膜材料研发的部门,即Semi-Films Division。这个部门在成立之初就面临着巨大的挑战,但团队凭借对技术的执着追求和不懈努力,成功开发出了具有优良电气性能和机械性能的薄膜产品,为公司赢得了首批客户。

EDDING公司的发展小趣事

随着Eclipse的不断发展壮大,ECLIPSE公司也面临着越来越多的挑战和风险。其中最大的挑战之一是保持Eclipse的开放性和灵活性,同时确保其稳定性和安全性。为此,ECLIPSE公司采取了一系列措施,包括加强代码审核、引入安全漏洞奖励计划等。此外,公司还积极应对来自竞争对手的挑战和市场变化,不断调整和优化自身的战略和业务模式。

问答坊 | AI 解惑

对两个.h的疑问

我是新手,在看一些程序时,看到了如下这两个头文件,有哪位高手能告诉我它们都有哪些作用么?有没有这方面的材料给小弟介绍一下。 #include #include …

查看全部问答>

有毒气体报警器毕业设计

有没有全套的啊,原理图也行,求各位高手帮忙,小弟快急死了…

查看全部问答>

嵌入式开发教程及案例

嵌入式开发教程及案例…

查看全部问答>

IGBT保护原理分析

IGBT保护原理分析,供大家分享!…

查看全部问答>

关于移植U-boot ( LPC24系列)

小弟最近在学习U-boot移植. 由于针对的是lpc2478的CPU,U-boot里面又没有支持lpc系列的.. 所以工作一直没进展.. 请移过U-boot的大侠指点一下... 在此拜师...…

查看全部问答>

用C#做工业控制,控制IO卡,行的通吗?

用C#做工业控制,控制IO卡,行的通吗? 公司大多都用研华的io卡,可是他们没有C#的接口函数,只有VB和VC的,可不可以用C#调用他们的接口函数呢? 又没有朋友做过这样的例子啊!还望赐教!…

查看全部问答>

ARM硬件问题求助、万分火急!

最近画了块6层的ARM板,板层结构式T\\\\G\\\\S\\\\S\\\\P\\\\B,板子回来后可以烧录程序,但将程序从NORFLASH拷贝到SDRAM后就出现问题了,程序执行不了。搞了几天了,该排除的都排除了,不知道问题出在哪里。我是第一次画6层板板,没啥经验,请各位 ...…

查看全部问答>

将2700系列的SCPI应用转换为3700系列的系统开关/万用表系统脚本应用

多年来,仪器制造商已经在用“可编程仪器标准指令”或SCPI控制测量仪器系统的可编程测试和测量设备。SCPI为控制测试和测量仪器提供了一种统一、一致的语言。不论制造商还是仪器类型,都能使用相同的指令和响应控制SCPI设备中相应的仪器功能。 &n ...…

查看全部问答>

linux HDMI driver

有一块am335x系列自定义的开发板,HDMI部分的设计参考的是beaglebone的TDA19988、现在要用到HDMI显示,我的内核版本是SDK里的linux-3.2.0,现在不知道HDMI这部分的内容在哪里,网上有价值的资料搜到的也不是很多,求教论坛的各位大神…

查看全部问答>

TIVA WARE库 bug

今天弄了一天的GPIO复用功能。。。书上说要配置GPIOPCTL寄存器。我翻了好久发现只要库函数中的void GPIOPinConfigure(uint32_t ui32PinConfig) 这个函数涉及到了这个寄存器。。。。。。可是,可是,可是!!!!!!这个函数的参数该写什么啊?? ...…

查看全部问答>