UBOOT1.2.0分析及移植
格式弄的太乱了,不过还是希望对大家有所帮助 :D,下面是参考资料,这些都是移植uboot必看的资料。
参考资料:
(1)http://blog.chinaunix.net/u1/34474/showart.php?id=363269
这位高手的blog是每位想进去嵌入式行业的同学必看的。
(2)<<smdk2410+uboo1.2.0..>>pdf文档,百度上可以搜到的,非常感谢原作者的分享
工作环境:
uboot1.2.0
Ubuntu8.10
cross_compile3.3.2
1 绪论
描述当前嵌入式产品的发展现状;
描述sangsun公司的产品,简单介绍s3c2410,以及板载配置。
2 bootloader的介绍,不同种类优劣性比较
3 论文出发点:目前的论文主要是针对Uboot的编译与移植,侧重点在如何配置适合自己开发板的参数,并将它移植到开发板。但并没有对uboot的工作流程进行清晰的分析,如果能够从本质上掌握整个uboot的工作原理,开发嵌入式产品时的周期也将缩短。因此对uboot的分析是必要的。
4 uboot目录结构。u-boot的源码顶层目录说明
目 录 特 性 解 释 说 明
board 平台依赖 存放电路板相关的目录文件,
例如:RPXlite(mpc8xx)、
smdk2410(arm920t)、
sc520_cdp(x86) 等目录
cpu 平台依赖 存放CPU相关的目录文件
例如:mpc8xx、ppc4xx、
arm720t、arm920t、 xscale、i386等目录
lib_ppc 平台依赖 存放对PowerPC体系结构通用的文件,
主要用于实现PowerPC平台通用的函数
lib_arm 平台依赖 存放对ARM体系结构通用的文件,
主要用于实现ARM平台通用的函数
lib_i386 平台依赖 存放对X86体系结构通用的文件,
主要用于实现X86平台通用的函数
include 通用 头文件和开发板配置文件,
所有开发板的配置文件都在configs目录下
common 通用 通用的多功能函数实现
lib_generic 通用 通用库函数的实现
net 通用 存放网络的程序
fs 通用 存放文件系统的程序
post 通用 存放上电自检程序
drivers 通用 通用的设备驱动程序,主要有以太网接口的驱动
disk 通用 硬盘接口程序
rtc 通用 RTC的驱动程序
dtt 通用 数字温度测量器或者传感器的驱动
examples 应用例程 一些独立运行的应用程序的例子,例如helloworld
tools 工具 存放制作S-Record或者u-boot格式的映像等工具,
例如mkimage
doc 文档 开发使用文档
5 简单介绍一下makefile,开始着手于源码的Makefile分析。在整个uboot目录中,makefile可以分为三类,
(1)顶层makefile
(2)结构makefile
(3)子目录makefile
正式分析Makefile。makefile内容比较容易理解。
Uboot的makefile总共有50几页,但其中重要的内容折合起来也就十二三页。
6正式开始分析uboot源码。
根据/board/smdk2410目录下的u-boot.lds可知,函数入口处在/cpu/arm920t/start.S中的_start处,且从0x00000000地址处开始。
因此首先应该分析start.S文件,这是第一个执行的汇编文件,非常的重要。
(1) cpu/arm920t/start.S
uboot启动也分stage1和stage2两个阶段。Uboot加载流程。2410上电,nandflash前4kb的内容以硬件的方式被拷贝到SRAM中,SRAM是cpu核中与nandflash配合的芯片从而实现nandflash的自启动系统。在SRAM中的部分uboot代码执行并在第2阶段执行之前,将nandflash中的uboot的全部代码拷贝到SDRAM中运行。然后引导内核与文件系统。
由上可以看出,这4kb以内代码必须要能够完成一些初始化工作。其实这就是我们平时所说的Uboot运行两阶段中的第一阶段。这部分短小的代码用汇编语言编写而成,也就是我们的start.S了。下面是总结的一张流程图
.globl _start
![](http://blogimg.chinaunix.net/blog/upfile2/090420233913.jpg)
1 _start: b reset 运行以后,先跳转到reset标签处
。。。。。。
-
reset:
从reset主要完成以下的功能。
-
首先切换到svc超级用户模式。在系统复位和软件中断时可以进入这种模式。
-
关闭看门狗。看门狗可以用作16位的定时器来请求中断服务。并在固定时钟内产生复位信号所以要关闭。
-
屏蔽所有的中断。通过设置INTMSK,INTSUBMSK寄存器来实现。
-
设置时钟。2410的时钟一般采用默认的,不用另外设置。
3 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 跳转到cpu_init_crit处
bl cpu_init_crit
#endif 接下来跳转到cpu_init_crit处。Bl指令是带返回的。PC值将指向下一条指令。
cpu_init_crit:从注释中可以了解到。它主要来设置一些重要的寄存器和内存时钟的设置。
MCR.MRC指令统称为协处理器寄存器传送指令。
MCR{cond} coproc,opcode1,Rd,CRn,CRm,{,opcode2}
Coproc:指令操作的协处理器名。标准名为pn,n为0~15
mcr p15, 0, r0, c7, c7, 0 例如这条指令就是将寄存器r0中的值送到协处理器p15中c7中去。
mrc即是将协处理中的寄存器的内容送到外部的寄存器中。
在重定位之前,我们必须设置内存时序,因为内存时钟是依赖于开发版的。在lowlevel_init.S文件里可以得到解释。lowlevel_init.S主要定义了各Bank的内钟等等
之后mov ip, lr //保存当前的lr。即是当前的pc值,因为再下一条指令又要进行跳转了。。
bl lowlevel_init//同样,这是条带返回的跳转。指向下一条指令。
mov lr, ip
mov pc, lr
因为lowevel_init被声明为.globl,可以全局使用,虽然不在start.S文件中定义。因此可以直接跳转到/board/smdk2410/lowlevel.S中来执行这个命令了。
它主要完成内存控制的配置,设置当前的PC为nandflash的起始位置。
下面是Lowlevel_init中的部分代码
BWSCON:BUS WIDTH&WAIT CONTROL REGISTER 0x48000000
可以来选择哪个BANK
ldr r0, =SMRDATA //SMRDATA的地址传到r0中。
ldr r1, _TEXT_BASE //通过将_TEXT_BASE中的值加载到r1中
sub r0, r0, r1 //r0=r0-r1??
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4//SMRDATA共13条指令。
0:
ldr r3, [r0], #4//将SMDDATA中的值循环。主要是涉及了BAND0~7的时钟啊,内存刷新什么的
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr//返回到cpu_init_crit
这样返回到了cup_init_crit中,
.....
mov ip,lr //ip中保存了reset中调用cpu_init_crit的下一条指令地址
bl lowlevel_init
mov lr, ip //Lowlevel_init返回后,执行这条指令。将ip传给lr
mov pc, l //再传给pc,这样就回到了reset的下一条指令处
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
返回到reset处吧~~#endif等着我们,到这里reset就执行完成了。执行完成以后返回到原命令处。继续执行接下去的命令。
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
定义各种异常模式。每种异常模式都有对应的pc来存储异常指令地址。接着继续执行这条指令以后的汇编代码,具体还是要结合start.S源码看的。
另外在/board/smdk2410/config,mk目录下存储着这样的信息。
# SMDK2410 has 1 bank of 64 MB DRAM
#
# 3000'0000 to 3400'0000
#
# Linux-Kernel is expected to be at 3000'8000, entry 3000'8000
# optionally with a ramdisk at 3080'0000
#
# we load ourself to 33F8'0000
#
# download area is 3300'0000
#
TEXT_BASE = 0x33F80000
定义了TEXT_BASE的地址。另外UBOOT_RAM_BASE _armboot_start 都是这个地址
Start.S继续向下执行,到达relocate代码段
relocate那段代码只针对从norflash中启动的设备有效,可要可不要,因此要是设备支持从nandflash启动,需要添加能将uboot拷贝到RAM中的代码。start.S本身是在SRAM中运行的,添加的这部分拷贝代码就是用来将剩余的内容全部拷贝到RAM中去了。
我们这里只是分析阶段。具体怎么在这里添加拷贝代码,参考第二部分正式移植
在添加的拷贝代码中,执行了一条跳转到nand_read_ll的指令。此函数在board/smdk2410/nand_read,c中定义了。但是uboot的源码包中没有这个函数,我们可以从vivi中获取这个函数~通过这个函数来将uboot读取到RAM中。
执行完这个函数以后,接着从copy_myself返回!!!检验一下是否将uboot已全部读取到TEXT_BASE中,如果成功,则继续执行下一条关键指令!!
即ldr pc,_start_armboot 将这个函数的地址值赋给PC,直接实现跳转!
_start_armboot: .word start_armboot !
开始跳转到/lib_arm/board.c中定义的start_armboot函数中执行了。到现在为止就是第2阶段正式开始了。Start.S文件分析也到此结束!!
(2)board.c也非常重要,并且start_armboot()函数定义lib_arm/board.c文件中。
board.c是个非常重要的文件。包括了CS8900网卡驱动函数的声明、内存函数初始化的函数mem_malloc_init、nandflash初始化、波特率初始化函数、输出一些硬件信息....start_armboot本身主要完成网络的设置等,并最终进入到main_loop()函数。即循环等待。下面先介绍几个重要的数据结构。
1初始化函数序列init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。
init_fnc_t *init_sequence[] = {
cpu_init, /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */
board_init, /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */
interrupt_init, /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /* 初始化环境变量 -- common/env_flash.c */
init_baudrate, /* 初始化波特率设置 -- lib_arm/board.c */
serial_init, /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
console_init_f, /* 控制台初始化阶段1 -- common/console.c */
display_banner, /* 打印u-boot信息 -- lib_arm/board.c */
dram_init, /* 配置可用的RAM -- board/smdk2410/smdk2410.c */
display_dram_config, /* 显示RAM的配置大小 -- lib_arm/board.c */
NULL,
};
2 全局数据结构
typedef struct global_data {全局数据
bd_t *bd; //板子数据指针
unsigned long flags;//指示标志,如设备已经初始化等。
unsigned long baudrate;//波特率定义
unsigned long have_console; /* serial_init() was called 串口初始化*/
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct 环境参数地址*/
unsigned long env_valid; /* Checksum of Environment valid? 环境参数CRC检验有效标志*/
unsigned long fb_base; /* base address of frame buffer 帧地址*/
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t; include/asm-arm/Global_data.h中定义。全局数据变量指针,它保存了U-boot运行时需要的全局数据。
3 开发板数据结构
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress即网卡地址 */
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params启动参数 */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t; include/asm-arm/u-boot.h中定义。描述开发板的数据结构。
4 voidstart_armboot (void)函数分析
{
//全局数据变量指针gd占用r8。
DECLARE_GLOBAL_DATA_PTR;
/* 给全局数据变量gd安排空间*/
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
memset ((void*)gd, 0, sizeof (gd_t));
/* 给板子数据变量gd->bd安排空间*/
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;//取u-boot的长度。
/* 顺序执行init_sequence数组中的初始化函数 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*配置可用的Flash */
size = flash_init ();
……
/* 初始化堆空间 */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/* 重新定位环境变量, */
env_relocate ();
/* 从环境变量中获取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太网接口MAC 地址 */
……
devices_init (); /* 设备初始化 */
jumptable_init (); //跳转表初始化
console_init_r (); /* 完整地初始化控制台设备 */
enable_interrupts (); /* 使能中断处理 */
/* 通过环境变量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()循环不断执行 */
for (;;) {
main_loop (); /* 主循环函数处理执行用户命令 -- common/main.c */
}
}
在U-boot-arm.h中定义了这个变量。
extern ulong _armboot_start; /* code start */
extern ulong _bss_start; /* code + data end == BSS start */
extern ulong _bss_end; /* BSS end */
extern ulong IRQ_STACK_START; /* top of IRQ stack */
extern ulong FIQ_STACK_START; /* top of FIQ stack */
分析完start_armboot函数后,下面再分析几个比较重要的文件。
(1) include/configs/smdk2410.h
主要是定义了一些宏。用来选择处理器,设置时钟,定义内存池的大小。设置网卡CS8900,还有串口与时钟的设置。
一些命令的定义。主要是CONFIG_COMMANDS宏中。看名称就能猜到是定义命令的意思了。包括CFG_CMD_CACHE命令等等,如果要设置nandflash启动,那么必须要使CFG_CMD_NAND、CFG_CMD_PING、CFG_CMD_NET三个选项有效。接下去的程序就定义了一些关于IP地址,网关的设置。以及关于内存起始地址CFG_MEMTEST_START,CFG_MEMTEST_END的定义。还有一些栈的设置。包含了许多宏地址的定义。因此这个文件很重要!!最后一些就包括了FLASH的环境设置。要进行修改。将SDRAM的起始地址0x30000000 与flash的bank1 0x00000000 联系起来。下表是一些比较重要的宏定义。
表1
PHY_FLASH_1 | 0X00000000 |
CFG_FLASH_BASE | PHY_FLASH_1 |
CFG_PROMPT | “SMDK2410#” 开发板的用户名称 |
CFG_MEMTEST_START | 0X30000000 |
CFG_MEMTEST_END | 0X33F00000 |
CFG_LOAD_ADDR | 0X33000000默认的加载地址 |
PHYS_SDRAM_1 | OX30000000 SDRAM bank1 |
PHYS_SDRAM_1_SIZE | 0X04000000 |
CFG_ENV_IS_IN_FLASH | 1 |
CFG_ENV_SIZE | 0X10000 64K |
这个文件的主要功能就分析完了。我对源码做了比较好的分析,这里就不一一列举了。利用source insight进行查看。
(2)/include/Cmd_confdefs.h 这个文件包含了全部命令的宏定义,好像是定义宏的地址~~~~在smdk2410.h定义部分命令后,则必须将这个头文件include 进来。
(3)/include/s3c2410.h与/include/s3c24x0.h
首先先分析s3c24x0.h文件。这个文件中定义了所有的硬件资源寄存器。包括内存控制,USB控制,中断、DMA等等。主要是通过这些结构体定义的对象与s3c2410.h中定义的SFR基地址结合,定义这个某个寄存器有哪些状态寄存器。
该文件中。主要定义了特殊寄存的基地址。SFR的范围0x48000000~0x5A000000
如s3c24x0.h中的部分内容
typedef struct {
S3C24X0_REG32 BWSCON;
S3C24X0_REG32 BANKCON[8];
S3C24X0_REG32 REFRESH;
S3C24X0_REG32 BANKSIZE;
S3C24X0_REG32 MRSRB6;
S3C24X0_REG32 MRSRB7;
} /*__attribute__((__packed__))*/ S3C24X0_MEMCTL
定义一个s3c24x0_MEMCTL对象,这个结构体中都是内存的相关寄存器。
下面是s3c2410.h中的部分代码。利用24x0中的对象来获取基地址。两者结合~
static inline S3C24X0_MEMCTL * const S3C24X0_GetBase_MEMCTL(void)
{
return (S3C24X0_MEMCTL * const)S3C24X0_MEMCTL_BASE;
先定义S3C24X0_MEMCTL结构体对象,再在s3c2410.h中定义一个const函数,表示这个函数的返回值是不变的。
(4)/include/common.h、 /common/cmd_nand.c、 /common/command.c common目录下包括一些通用的函数、如网络、硬盘、串口、nandflash等等.
这个文件主要定义了uboot的所有命令的形式。
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}每个命令就是一个cmd_tbl_t的对象。即每个命令都有一个结构体来描述。包括命令名字、参数的最大个数、重复数、命令执行函数、用法、帮助。从控制台中输入的命令是又command.c完成的,而find_cmd函数用来匹配具体的某个命令。
7内存分布情况从高到低分配。
对于smdk2410,RAM范围从0x30000000~0x34000000. u-boot占用高端内存区的1M大小。从前面的分析可知内存分配大致如下:
![](http://blogimg.chinaunix.net/blog/upfile2/090420234229.jpg)
内存图的分析:在start.S中
gd和bd共占128个字节的大小。在给malloc区和bd数据结构分配后,如果定义了IRQ,则给栈分配IRQ+FIQ占8K大小的区。CONFIG_STACKSIZE_IRQ=4K。如果没有定义,则至少保存12个字节。在smdk2410.h有定义。从0x33f0000到0x34000000的1M地址,是留给uboot使用的。下面是从代码中总结出的信息,不知道对不对。需要进一步验证。DW_STACK_START,建立堆栈,栈起点0x33f00000,大小为0x8000 32K大小的栈?UBOOT_RAM_BASE 与_TEXT_BASE相同0x33f80000 _arm_boot_start也为0x33f80000.uboot源码分析暂到此结束。其实还是有许多不明白的地方。以后发现什么新的了再添加吧
未完。。。。。。。。。。见下一篇