03Uboot移植分析.docx
《03Uboot移植分析.docx》由会员分享,可在线阅读,更多相关《03Uboot移植分析.docx(35页珍藏版)》请在冰豆网上搜索。
![03Uboot移植分析.docx](https://file1.bdocx.com/fileroot1/2023-1/10/6b0964ec-d523-4d0f-b303-c3e6eddca63c/6b0964ec-d523-4d0f-b303-c3e6eddca63c1.gif)
03Uboot移植分析
U-boot分析与移植
(1)----bootloader分析
一、BootLoader概念
就是在操作系统内核运行之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境,他就是所谓的引导加载程序(BootLoader)。
嵌入式软件在Flash存储器中的分布图
二、为什么需要BootLoader?
BootLoader的终极任务是引导操作系统,所谓引导操作系统,就是启动内核,在启动内核之前所需要的环境(如初始化sdram,设置cpu模式等,下面会介绍)都是由BootLoader来完成的。
试想一下,如果你要启动内核,让内核在内存上跑,但连sdram都没有初始化,这显然不行。
在s3c2440中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。
在x86的PC机上,BootLoader=BIOS+GRUB/LILO。
三、BootLoader的选择
有些人误认为BootLoader=U-Boot,其实BootLoader只是所有引导加载程序中的一个总称。
四、启动过程
S3C2440支持两种方式的启动:
NorFlash启动和NandFlash启动。
NorFlash和NandFlash都是非易失性存储器,NorFlash的特点是芯片内执行和不能直接写操作,程序可以直接在其中运行,而不必将程序读取到RAM中运行。
NorFlash虽然具有这个优点,但是它的性价比远低于NandFlash,因而很多系统采用NandFlash启动。
NandFlash的特点是采用非线性存储模式,程序无法在其中运行,它只能作为程序或数据的存储载体,存储在其中的程序只能先拷贝到RAM中才能运行。
从NorFlash启动时,与nGCS0相连的NorFlash就被映射到nGCS0片选的空间,其地址被映射为0x00000000;从NandFlash启动时,S3C2440芯片内部自带的一块容量为4K的被称为“Steppingstone”(“起步阶石”)的BootSRAM被映射到nGCS0片选的空间,其地址被映射为0x00000000。
当系统上电或复位时,程序会从0地址处开始执行,因此我们编写的启动代码要确保存储在0地址处。
当启动方式为NorFlash启动时,没有额外需要考虑的问题,因为这种情况下程序在系统启动前就存储在NorFlash中,我们只要保证将启动代码保存在NorFlash开始的位置即可,系统上电或复位时,0地址处的启动代码就会被执行。
在启动方式为NandFlash启动的情况下,系统启动前所有的程序存储在NandFlash中,系统的启动过程稍微有点复杂:
系统上电或复位时,0地址处为S3C2440内部自带的BootSRAM,启动前里面没有任何存储内容,启动后S3C2440先通过硬件机制将NandFlash前4K的内容拷贝至其中,然后再运行里面的程序(从0地址处)。
这种情况下我们需要保证将启动代码保存在NandFlash开始的位置,并且启动代码的大小要小于4K。
这就是我们的裸机程序为什么在nandflash能跑的原因。
我们用的bootloader一般大于4k,所以,我们把用汇编编写的在sram里面执行的过程称为stage1,实现最关键的初始化后,把bootloader代码从nandflash拷贝到sdram里。
此时在sdram里实现stage2,做更具体的初始化,最后启动内核,这个阶段一般用c语言来编写。
BootLoader的stage1通常包括以下步骤(以执行的先后顺序):
∙硬件设备初始化(关看门狗,关中断,设置cpu时钟,初始化sdram,关闭CPU内部指令/数据cache)。
∙为加载BootLoader的stage2准备RAM空间。
∙拷贝BootLoader的stage2到RAM空间中。
∙设置好堆栈。
∙跳转到stage2的C入口点。
BootLoader的stage2通常包括以下步骤(以执行的先后顺序):
∙初始化本阶段要使用到的硬件设备。
∙检测系统内存映射(memorymap)。
∙将kernel映像和根文件系统映像从flash上读到RAM空间中。
∙为内核设置启动参数。
∙调用内核。
U-boot分析与移植
(2)----U-bootstage1分析
我们要生成u-boot.bin文件,它首先依赖于很多.o文件和.lds链接脚本文件
我们只要找到对应的.lds链接脚本文件就可以分析u-boot的启动流程。
1、打开u-boot-1.1.6\u-boot-1.1.6\board\smdk2410\
打开链接脚本boot.lds看看:
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm","elf32-arm","elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.=0x00000000;
.=ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
1)可以看见入口函数是_start
2)这个start代码放在cpu/arm920t/,就是start.S生成的start.o
2、打开u-boot-1.1.6\cpu\arm920t\start.S
.globl_start
_start:
b reset
ldr pc,_undefined_instruction
......
一开始跳到reset
reset:
设置CPUSVC32模式:
/*
*setthecputoSVC32mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f /*clear0atlow5bit*/
orr r0,r0,#0xd3 /*set11010011*/
/*closeirq,fiq,atarmstate,atsupervisormode*/
msr cpsr,r0 /*writecpsr*/
IF为1,关闭FIQ,IRQ
T为0,ARM模式
M[4:
0]=10011,管理模式
关闭看门狗:
向pWTCON全写0
#ifdefined(CONFIG_S3C2400)||defined(CONFIG_S3C2410)
ldr r0,=pWTCON
mov r1,#0x0
str r1,[r0]
关闭所有中断:
但默认已经是关闭的
INTMSK写1,屏蔽对应的中断(默认1)
INTSUBMSK写1,屏蔽对应的二级中断(默认1)
/*
*maskallIRQsbysettingallbitsintheINTMR-default
*/
mov r1,#0xffffffff
ldr r0,=INTMSK
str r1,[r0]
#ifdefined(CONFIG_S3C2410)
ldr r1,=0x3ff
ldr r0,=INTSUBMSK
str r1,[r0]
#endif
设置时钟分频比:
/*FCLK:
HCLK:
PCLK=1:
2:
4*/
/*defaultFCLKis120MHz!
*/
ldr r0,=CLKDIVN
mov r1,#3
str r1,[r0]
接着跳入cpu系统初始化:
#ifndefCONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
在里面再跳入sdram初始化:
bl lowlevel_init
拷贝代码到SDRAM:
十分遗憾,这是给NOR FLASH用的
首先判断_start当前第一条代码位置,如果是在内存中,就不用代码重定位了
#ifndefCONFIG_SKIP_RELOCATE_UBOOT
relocate:
/*relocateU-BoottoRAM */
adr r0,_start /*r0<-currentpositionofcode */
ldr r1,_TEXT_BASE /*testifwerunfromflashorRAM*/
cmp r0,r1 /*don'trelocduringdebug */
beq stack_setup
ldr r2,_armboot_start
ldr r3,_bss_start
sub r2,r3,r2 /*r2<-sizeofarmboot */
add r2,r0,r2 /*r2<-sourceendaddress */
copy_loop:
ldmia r0!
{r3-r10} /*copyfromsourceaddress[r0] */
stmia r1!
{r3-r10} /*copyto targetaddress[r1] */
cmp r0,r2 /*untilsourceendaddreee[r2] */
ble copy_loop
#endif /*CONFIG_SKIP_RELOCATE_UBOOT*/
设置堆栈:
stack_setup:
ldr r0,_TEXT_BASE /*upper128KiB:
relocateduboot */
sub r0,r0,#CFG_MALLOC_LEN /*mallocarea */
sub r0,r0,#CFG_GBL_DATA_SIZE/*bdinfo */
还有设置中断stack
......
清bss段:
Bss段用来存放未初始化的全局变量和静态变量,在有操作系统的c编译器里默认为0.
但移植u-boot时,所有堆栈,清bss段都是人手设置,我们定义了一个bss段,把这个区域写0,
以后我们c函数运行时,就默认0了。
clear_bss:
ldr r0,_bss_start /*findstartofbsssegment */
ldr r1,_bss_end /*stophere */
mov r2,#0x00000000 /*clear */
clbss_l:
str r2,[r0] /*clearloop... */
add r0,r0,#4
cmp r0,r1
ble clbss_l
跳到c函数入口:
ldr pc,_start_armboot
_start_armboot:
.wordstart_armboot
第一阶段基本完成
ldr绝对跳转到_start_armboot函数入口
U-boot分析与移植(3)----U-bootstage2分析.
一来到voidstart_armboot(void)函数,马上出现两个很重要的数据结构gd_t和bd_t
1、gd_t :
global data数据结构定义,位于文件 include/asm-arm/global_data.h。
其成员主要是一些全局的系统初始化参数。
[cpp]viewplaincopyprint?
1.typedef struct global_data {
2. bd_t *bd; // struct board_info宋体;">指针,保存板子信息
3. unsigned long flags; // 宋体;">指示标志,如设备已经初始化标志等
4. unsigned long baudrate;
5. unsigned long have_console; /* serial_init() was called */
6. unsigned long reloc_off; /* Relocation Offset */
7. unsigned long env_addr; /* Address of Environment struct 环境参数地址*/
8. unsigned long env_valid; /* Checksum of Environment valid?
*/
9. unsigned long fb_base; /* base address of frame buffer */
10.#ifdef CONFIG_VFD
11. unsigned char vfd_type; /* display type */
12.#endif
13.#if 0
14. unsigned long cpu_clk; /* CPU clock in Hz!
*/
15. unsigned long bus_clk;
16. unsigned long ram_size; /* RAM size */
17. unsigned long reset_status; /* reset status register at boot */
18.#endif
19. void **jt; /* jump table */
20.} gd_t;
2.、bd_t :
board info数据结构定义,位于文件 include/asm-arm/u-boot.h。
保存板子参数。
[cpp]viewplaincopyprint?
1.typedef struct bd_info {
2. int bi_baudrate; /* serial console baudrate */
3. unsigned long bi_ip_addr; /* IP Address */
4. unsigned char bi_enetaddr[6]; /* Ethernet adress */
5. struct environment_s *bi_env;
6. ulong bi_arch_number; /* unique id for this board 宋体;">板子
Times New Roman;">ID
宋体;">号
*/
7. ulong bi_boot_params; /* where this board expects params */
8. struct /* RAM configuration */
9. {
10. ulong start;
11. ulong size;
12. } bi_dram[CONFIG_NR_DRAM_BANKS];
13.#ifdef CONFIG_HAS_ETH1
14. /* second onboard ethernet port */
15. unsigned char bi_enet1addr[6];
16.#endif
17.} bd_t;
分配一个存储全局数据的区域,地址给指针gd
[cpp]viewplaincopyprint?
1.gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
清0并分配空间
[cpp]viewplaincopyprint?
1.memset ((void*)gd, 0, sizeof (gd_t));
在gd前面的位置给 gd->bd赋值地址
[cpp]viewplaincopyprint?
1.gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
清0并分配空间
[cpp]viewplaincopyprint?
1.memset (gd->bd, 0, sizeof (bd_t));
执行一系列初始化函数
[cpp]viewplaincopyprint?
1.for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
2. if ((*init_fnc_ptr)() !
= 0) {
3. hang ();
4. }
5. }
假如函数指针指向的函数返回值不为0,那么在hang()里就会死循环,初始化失败
[cpp]viewplaincopyprint?
1.void hang (void)
2.{
3. puts ("### ERROR ### Please RESET the board ###\n");
4. for (;;);
5.}
函数列表如下:
每个初始化函数正常情况下返回值是0
[cpp]viewplaincopyprint?
1.init_fnc_t *init_sequence[] = {
2. cpu_init, /* 初始化irq/fiq模式的栈*/
3. board_init, /* 设置系统时钟*/
4. interrupt_init, /*初始化定时器*/
5. env_init, /* 检查flash上的环境参数是否有效*/
6. init_baudrate, /* 初始化波特率*/
7. serial_init, /* 初始化串口*/
8. console_init_f, /*初始化串口控制台*/
9. display_banner, /* say that we are here */
接着进行一些NORFLASH,LCD,串口,控制台,sd卡,网卡等初始化,不一一列举了。
终于来到重要的时刻了--#
进入一个死循环
[cpp]viewplaincopyprint?
1.for (;;)
2.{
3. main_loop ();
4.}
继续跟踪
发现在bootdelay时间内按下键进入命令行,用run_command来解析命令
[cpp]viewplaincopyprint?
1.#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
2. s = getenv ("bootdelay");
3. bootdelay = s ?
(int)simple_strtol(s, NULL, 10) :
CONFIG_BOOTDELAY;
4.
5. debug ("### main_loop entered:
bootdelay=%d\n\n", bootdelay);
如果CONFIG_BOOTDELAY已经定义,用s得到环境变量bootdelay,然后倒数启动内核
[cpp]viewplaincopyprint?
1.#ifdef CONFIG_BOOTCOUNT_LIMIT
2. if (bootlimit && (bootcount > bootlimit)) {
3. printf ("Warning:
Bootlimit (%u) exceeded. Using altbootcmd.\n",
4. (unsigned)bootlimit);
5. s = getenv ("altbootcmd");
6. }
7. else
8.#endif /* CONFIG_BOOTCOUNT_LIMIT */
9. s = getenv ("bootcmd");
CONFIG_BOOTCOUNT_LIMIT是设置u-boot启动次数的限制
最后s=getenv("bootcmd");获得启动参数
[cpp]viewplaincopyprint?
1.run_command (s, 0);
启动命令解析
在run_command函数里最终执行命令
[cpp]viewplaincopyprint?
1./* OK - call function to do the command */
2.if ((cmdtp->cmd) (cmdtp, flag, argc, argv) !
= 0) {
3. rc = -1;
4.}
这是一个命令结构体,原型如下:
[cpp]viewplaincopyprint?
1.struct cmd_tbl_s {
2. char *name; /* Comma