历史上的今天
今天是: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():初始化定时器
史海拾趣
|
小弟最近在学习U-boot移植. 由于针对的是lpc2478的CPU,U-boot里面又没有支持lpc系列的.. 所以工作一直没进展.. 请移过U-boot的大侠指点一下... 在此拜师...… 查看全部问答> |
|
用C#做工业控制,控制IO卡,行的通吗? 公司大多都用研华的io卡,可是他们没有C#的接口函数,只有VB和VC的,可不可以用C#调用他们的接口函数呢? 又没有朋友做过这样的例子啊!还望赐教!… 查看全部问答> |
|
最近画了块6层的ARM板,板层结构式T\\\\G\\\\S\\\\S\\\\P\\\\B,板子回来后可以烧录程序,但将程序从NORFLASH拷贝到SDRAM后就出现问题了,程序执行不了。搞了几天了,该排除的都排除了,不知道问题出在哪里。我是第一次画6层板板,没啥经验,请各位 ...… 查看全部问答> |
|
将2700系列的SCPI应用转换为3700系列的系统开关/万用表系统脚本应用 多年来,仪器制造商已经在用“可编程仪器标准指令”或SCPI控制测量仪器系统的可编程测试和测量设备。SCPI为控制测试和测量仪器提供了一种统一、一致的语言。不论制造商还是仪器类型,都能使用相同的指令和响应控制SCPI设备中相应的仪器功能。 &n ...… 查看全部问答> |
|
有一块am335x系列自定义的开发板,HDMI部分的设计参考的是beaglebone的TDA19988、现在要用到HDMI显示,我的内核版本是SDK里的linux-3.2.0,现在不知道HDMI这部分的内容在哪里,网上有价值的资料搜到的也不是很多,求教论坛的各位大神… 查看全部问答> |
|
今天弄了一天的GPIO复用功能。。。书上说要配置GPIOPCTL寄存器。我翻了好久发现只要库函数中的void GPIOPinConfigure(uint32_t ui32PinConfig) 这个函数涉及到了这个寄存器。。。。。。可是,可是,可是!!!!!!这个函数的参数该写什么啊?? ...… 查看全部问答> |




