本节总结:

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.bz2cd 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符号处,0x00ldr   pc, _undefined_instruction                    //0x04ldr   pc, _software_interrupt                       //0x08ldr   pc, _prefetch_abort                           //0x0cldr   pc, _data_abort                               //0x10ldr   pc, _not_used                                 //0x14ldr   pc, _irq                                      //0x18ldr   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, #0xffffffffldr   r0, =INTMSKstr   r1, [r0]                  //关闭所有中断
# if defined(CONFIG_S3C2410)ldr   r1, =0x3ffldr   r0, =INTSUBMSKstr   r1, [r0]                  //关闭次级所有中断
# endif/* 设置时钟频率, FCLK:HCLK:PCLK = 1:2:4 ,而FCLK默认为120Mhz*/ldr   r0, =CLKDIVNmov r1, #3str   r1, [r0]#ifndef CONFIG_SKIP_LOWLEVEL_INITbl    cpu_init_crit                         //关闭mmu,并初始化各个bank#endifcall_board_init_f:ldr   sp, =(CONFIG_SYS_INIT_SP_ADDR) //CONFIG_SYS_INIT_SP_ADDR=0x30000f80bic   sp, sp, #7         //sp=0x30000f80ldr   r0,=0x00000000bl    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_PRAMulong reg;
#endifbootstage_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结构体清0gd->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():初始化定时器
  • env_init():设置gd的成员变量
  • init_baudrate():设置波特率
  • dram_init():设置gd->ram_size= 0x04000000(64MB)

3)继续来看board_init_f():

addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;  // addr=0x34000000
// CONFIG_SYS_SDRAM_BASE:  SDRAM基地址,为0X30000000
// gd->ram_size:          等于0x04000000 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))/* reserve TLB table */addr -= (4096 * 4);        //addr=33FFC000addr &= ~(0x10000 - 1);  // addr=33FF0000,   gd->tlb_addr = addr;   //将64kB分配给TLB,所以TLB地址为33FF0000~33FFFFFF
#endif/* round down to next 4 kB limit */addr &= ~(4096 - 1);                    //4kb对齐, addr=33FF0000debug("Top of RAM usable for U-Boot at: %08lx\n", addr);/** reserve memory for U-Boot code, data & bss* round down to next 4 kB limit*/addr -= gd->mon_len; // 在前面分析过gd->mon_len=0xae4e0,//所以addr=33FF0000 -0xae4e0=33F41B20,addr &= ~(4096 - 1);  //4095=0xfff,4kb对齐, addr=33F41000//所以分配给uboot各个段的重定位地址为33F41000~33FFFFFFdebug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);#ifndef CONFIG_SPL_BUILDaddr_sp = addr - TOTAL_MALLOC_LEN; //分配一段malloc空间给addr_sp//TOTAL_MALLOC_LEN=1024*1024*4,所以malloc空间为33BF1000~33F40FFFaddr_sp -= sizeof (bd_t);            //分配一段bd_t结构体大的空间bd = (bd_t *) addr_sp;               //bd指向刚刚分配出来的bd_t结构体gd->bd = bd;                         // 0x30000f80处的gd变量的成员bd等于bd_t基地址addr_sp -= sizeof (gd_t);              //分配一个gd_t结构体大的空间id = (gd_t *) addr_sp;                 //id指向刚刚分配的gd_t结构体gd->irq_sp = addr_sp;                 //0x30000f80处的gd变量的成员irq_sp等于gd_t基地址addr_sp -= 12;addr_sp &= ~0x07;... ...relocate_code(addr_sp, id, addr);  //进入relocate_code()函数,重定位代码,以及各个符号// addr_sp: 栈顶,该栈顶向上的位置用来存放gd->irq_sp、id 、gd->bd、malloc、uboot、TLB(64kb),//id:       存放 gd_t结构体的首地址// addr:    等于存放uboot重定位地址33F41000
}

执行完board_init_f()后,最终内存会划分如下图所示:

其实此时uboot还在flash中运行,然后会进入start.S的relocate_code()里进行uboot重定位

8.接下来进入重定位

1)start.S的relocate_code()代码如下所示

relocate_code:mov r4, r0      /* save addr_sp */              // addr_sp栈顶值mov r5, r1      /* save addr of gd */           // id值mov r6, r2      /* save addr of destination */  // addr值:uboot重定位地址/* Set up the stack        */
stack_setup:mov sp, r4                //设置栈addr_spadr  r0, _start           //在顶层目录下system.map符号文件中找到_start =0,所以r0=0cmp r0, r6                //判断_start(uboot重定位之前的地址)和addr(重定位地址)是否一样beq clear_bss             /* skip relocation */ mov r1, r6             /* r1 <- scratch for copy_loop */ //r1= addr(重定位地址)ldr   r3, _bss_start_ofs               //_bss_start_ofs=__bss_start - _start(uboot代码大小)add r2, r0, r3         /* r2 <- source end address*/   //r2= uboot重定位之前的结束地址copy_loop:ldmia      r0!, {r9-r10}  /* copy from source address [r0] *///将r0处的两个32位数据拷到r9-r10中,然后r0+=8stmia      r1!, {r9-r10}  /* copy to   target address [r1]*///将拷出来的两个数据放入r1(重定位地址)处,然后r1+=8cmp r0, r2  /* until source end address [r2]*/   //判断拷贝的数据是否到结束地址blo  copy_loop

上面只是把代码复制到SDRAM上,而链接地址内容却没有改变,比如异常向量0x04的代码内容还是0x1e0,

我们以异常向量0x04为例,来看它的反汇编:

如上图所示,即使uboot在SDRAM运行,由于代码没修改,PC也会跳到0x1e0(flash地址)

和之前老的uboot有很大区别,以前老的uboot直接是使用的SDRAM链接地址,如下图所示:

所以,新的uboot采用了动态链接地址的方法,在链接脚本uboot.lds中,可以看到这两个段(.rel.dyn、.dynsym):

该两个段里,便是保存了各个文件的相对动态信息(.rel.dyn)、动态链接地址的符号(.dynsym)

以上图的.rel.dyn段为例来分析,找到__rel_dyn_start符号处:

如上图所示,其中0x17表示的是符号的结束标志位,我们以0x20为例来讲解:

在之前,我们讲过0x20里面保存的是异常向量0x04跳转的地址(0x1e0),如下图所示:

所以,接下来的代码,便会根据0x20里的值0x1e0(flash地址),将SDRAM的33F41000+0x20的内容改为33F41000+0x1e0(SDRAM地址),来改变uboot的链接地址

2)重定位的剩余代码,如下所示:

#ifndef CONFIG_SPL_BUILD/** fix .rel.dyn relocations*/ldr   r0, _TEXT_BASE             /* r0 <- Text base */  //r0=text段基地址=0sub  r9, r6, r0         /* r9 <- relocation offset */   //r9= 重定位后的偏移值=33F41000ldr   r10, _dynsym_start_ofs  /* r10 <- sym table ofs */ //_dynsym_start_ofs =__dynsym_start - _start=0x73608//所以r10=动态符号表的起始偏移值=0x73608add r10, r10, r0            /* r10 <- sym table in FLASH *///r10=flash上的动态符号表基地址=0x73608ldr   r2, _rel_dyn_start_ofs     /* r2 <- rel dyn start ofs *///r2=__rel_dyn_start - _start=0x6b568//所以r2=相对动态信息的起始偏移值=0x6b568add r2, r2, r0         /* r2 <- rel dyn start in FLASH *///r2=flash上的相对动态信息基地址=0x6b568ldr   r3, _rel_dyn_end_ofs      /* r3 <- rel dyn end ofs */// _rel_dyn_end_ofs=__rel_dyn_end - _start=00073608//所以r3=相对动态信息的结束偏移值=00073608add r3, r3, r0         /* r3 <- rel dyn end in FLASH *///r3=flash上的相对动态信息结束地址=0x6b568fixloop:ldr   r0, [r2]           /* r0 <- location to fix up, IN FLASH! *///以0x20为例,r0=0x6b568地址处的内容= 0x20add r0, r0, r9         /* r0 <- location to fix up in RAM *///r0=33F41000+0x20=33F41020ldr   r1, [r2, #4]             //r1= 33F41024地址处的内容=0x17and  r7, r1, #0xff       cmp r7, #23                  /* relative fixup? */  //0x17=23,所以相等beq fixrel                                       //跳到:fixerlcmp r7, #2                    /* absolute fixup? */beq fixabs/* ignore unknown type of fixup */b     fixnext
fixabs:/* absolute fix: set location to (offset) symbol value */mov r1, r1, LSR #4         /* r1 <- symbol index in .dynsym */add r1, r10, r1              /* r1 <- address of symbol in table */ldr   r1, [r1, #4]             /* r1 <- symbol value */add r1, r1, r9         /* r1 <- relocated sym addr */b     fixnextfixrel:/* relative fix: increase location by offset */ldr   r1, [r0]                  //r1=33F41020地址处的内容=0x1e0add r1, r1, r9                //r1=0x1e0+33F41000= 33F411e0fixnext:str   r1, [r0]             //改变链接地址里的内容, 33F41020=33F411e0  (之前为0x1e0)   add r2, r2, #8             //r2等于下一个相对动态信息(0x24)的地址cmp r2, r3                //若没到尾部__rel_dyn_end,便继续执行: fixloopblo  fixloop
#endif

9.清除bss段

/*重定位完成后,清除bss段*/
clear_bss:#ifndef CONFIG_SPL_BUILDldr   r0, _bss_start_ofs                        //获取flash上的bss段起始位置ldr   r1, _bss_end_ofs                          //获取flash上的bss段结束位置mov r4, r6                    /* reloc addr */     //获取r6(SDRAM上的uboot基地址)add r0, r0, r4                                  //加上重定位偏移值,得到SDRAM上的bss段起始位置add r1, r1, r4                                     //得到SDRAM上的bss段结束位置mov r2, #0x00000000           /* clear*/clbss_l:str    r2, [r0]           /* clear loop...       */                 //开始清除SDRAM上的bss段add r0, r0, #4cmp r0, r1bne  clbss_lbl coloured_LED_initbl red_led_on
#endif

9.1继续往下分析

#ifdef CONFIG_NAND_SPL                   //未定义,所以不执行... ...
#else                                   //执行elseldr   r0, _board_init_r_ofs         //r0=flash上的board_init_r()函数地址偏移值adr  r1, _start                    //0add lr, r0, r1                     //返回地址lr=flash上的board_init_r()函数add lr, lr, r9                     //加上重定位偏移值(r9)后,lr=SDRAM上的board_init_r()函数/* setup parameters for board_init_r */mov r0, r5             /* gd_t */              //r0=id值mov r1, r6             /* dest_addr */         //r1=uboot重定位地址/* jump to it ... */mov pc, lr              //跳转:  board_init_r()函数_board_init_r_ofs:.word board_init_r - _start        //获取在flash上的board_init_r()函数地址偏移值#endif

从上面代码看出, 接下来便会进入uboot的board_init_r()函数,该函数会对各个外设初始化、环境变量初始化等.

uboot的启动过程到此便结束了.

移植uboot-分析uboot启动流程(详解)相关推荐

  1. Android进阶——Small源码分析之启动流程详解

    前言 插件化现在已经是Android工程师必备的技能之一,只是学会怎么使用是不行的,所以蹭有时间研究一下Small的源码.对于插件化主要解决的问题是四大组件的加载和资源的加载,读懂所有Small源码需 ...

  2. 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  3. U-Boot启动流程详解

    参考:U-Boot顶层目录链接脚本文件(u-boot.lds)介绍 作者:一只青木呀 发布时间: 2020-10-23 13:52:23 网址:https://blog.csdn.net/weixin ...

  4. golang程序启动流程详解

    golang程序启动流程详解 环境 go1.16.5 linux/amd64 用例 package mainimport "fmt"func main() {fmt.Println ...

  5. 【Autosar 启动流程详解】

    Autosar 启动流程详解 1. vLinkGen_Template.lsl 2. BrsHwStartup.c 3.BrsMainStartup.c 4.BrsMain.c 链接文件: 1. vL ...

  6. android zygote启动流程,Android zygote启动流程详解

    对zygote的理解 在Android系统中,zygote是一个native进程,是所有应用进程的父进程.而zygote则是Linux系统用户空间的第一个进程--init进程,通过fork的方式创建并 ...

  7. 【线上沙龙直播报名】App 启动流程详解及其优化

    点击上方"公众号"可以订阅哦 [美团点评技术沙龙Online]是美团点评技术团队推出的线上分享课程,每月2-3期,采用目前最火热的线上直播形式,邀请美团点评技术专家,面向互联网技术 ...

  8. uboot启动流程详解

    要分析boot启动流程,首先要找到程序入口地址,可以通过编译uboot生成u-boot.lds,通过查看链接脚本u-boot.lds知道入口点是 arch/arm/lib/vectors.S 文件中的 ...

  9. Android App启动流程详解

    前言:在之前的文章中已经写了apk的打包流程.安装流程,今天就是梳理一下apk系列的最后的流程--app启动流程.经过今天的梳理以后咱们就可以对apk包是怎么编译生成的.apk是怎么被安装到安卓手机的 ...

  10. Springboot启动流程详解

    SpringMVC请求流程详解 SpringMVC框架是一个基于请求驱动的Web框架,并且使用了'前端控制器'模型来进行设计,再根据'请求映射规则'分发给相应的页面控制器进行处理. (一)整体流程 每 ...

最新文章

  1. c#sort升序还是降序_被玩坏的数组排序之sort函数
  2. 响应文件是不是标书_什么是标书?投标书有哪些分类?标书和投标书的不同?...
  3. 判断CPU是大端还是小端
  4. [转]IntelliJ IDEA 2020.1 正式发布,15 项重大特性、官方支持中文了!
  5. 【HDU - 6081】度度熊的王国战略(SW算法,全局最小割)
  6. MachineLearning(8)-PCA,LDA基础+sklearn 简单实践
  7. Android 为你的应用程序添加快捷方式【优先级高的快捷方式】
  8. tcxgrid主从结构显示多行_快速跟随型主从结构多电机同步控制
  9. 天然黑糖行业调研报告 - 市场现状分析与发展前景预测
  10. Topological Spaces(拓扑空间)
  11. 微波网络中插入相移插入衰减和输入驻波比
  12. 计算机物质与结构,计算机辅助高中化学物质结构教学的探索与实践
  13. 使用Java驱动ACR122U对IC卡进行读写
  14. flash 独立播放器
  15. 呼叫中心来电弹屏功能怎么样
  16. 基于wavesurfer,regions 封装的可视化音标标注控件
  17. 5G时代的到来--5G技术介绍
  18. python os.getcwd用法_os.getcwd()
  19. android 适配红米,小米MIUI放出Android Q适配计划,11款手机参与,包括红米Note 7
  20. Twebbrowser从内存中加载页面

热门文章

  1. 计算机学生英语面试自我介绍ppt,[英语自我介绍ppt模板]面试英语自我介绍免费模板(一)...
  2. 税后月薪一万在北京是什么样的体验?
  3. 【Python 有用的知识】——简单易操作的python小知识
  4. Android动画之补间动画
  5. SDN(五) Netgear R6220从刷机到配置 OpenvSwitch交换机
  6. SSM整合的 一个简单的贴吧项目
  7. python生成随机数和随机矩阵
  8. “善”用区块链:善是初心,链是保障
  9. 使用docker-compose部署php-apche、Mysql、phpmyadmin
  10. 【微信小程序】微信小程序入门与实战-个人笔记