1,BootLoader功能分析:BootLoader最终要达到的目标是把Linux内核引导起来(相当于用户空间的Shell程序启动用户进程,但是Shell不仅仅具有启动用户进程的作用)
为了实现这个最终的目标,BootLoader具有以下必须的功能(以U-Boot为例);
Step-1:
1),设置CPU;
2),关闭Watchdog;
3),设置Clock;
4),屏蔽所有的Interrupt;
5),初始化SDRAM;
6),初始化nandflash(如果u-boot代码在nandflash上);
7);重定位BootLoader的代码到SDRAM;
8);设置栈(C语言函数调用需要先设置栈,栈要在SDRAM初始化之后设置);
9),清BSS段;
10),进入BootLoader的Step-2阶段启动内核;
Step-2:
11),设置BootLoader要传递给内核的参数;
12),从Nor 或 Nand flash 上复制内核代码到SDRAM的0x30007FC0的地方(这个地址不是固定的,可以任意指定,只要不破坏u-boot使用的内存空间,u-boot会根据uImage的Header中的Load Addr判断内核是否装载到Load Addr的地址处,Load Addr在u-boot中默认为0x30007FC0,如果不在,u-boot会移动内核代码到改地址处);
13),跳转到uImage的Header中Entry Point指定的入口地址处开始执行内核代码(内核执行后,u-boot不在执行);
在第二阶段中还可以包括一些辅助开发的功能(不分先后次序):
14),Nor 或者 Nand flash烧写功能;
15),USB初始化;
16),UART串口初始化;
17),网络功能初始化;
2,自制BootLoader思路:
实现上述必须功能;
以S3C2440平台为例;
硬件参数查S3C2440 Datasheet;
内存布局以及内核参数传递参考u-boot.1.1.6版本代码;
总体功能实现(start.S , init.c , boot.c , u-boot.lds , Makefile 附件会提供完整源码):
1,第一阶段的实现:
设置CPU为管理模式;
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
开启CPU的I-Cache,经测试开启之前和开启之后复制内核代码时间缩短8s;
mrc p15, 0, r1, c1, c0, 0
orr r1,r1,#0x1000
mcr p15, 0, r1, c1, c0, 0
关闭Watchdog;
ldr r0,=0x53000000
mov r1,#0
str r1,[r0]
屏蔽所有中断;
ldr r0,=0x4A000008
ldr r1,=0xFFFFFFFF
str r1,[r0]
ldr r1,=0x3FF
ldr r0,=0x4A00001C
str r1,[r0]
初始化时钟;
1,设置 FCLK:HCLK:PCLK=1:2:4
ldr r0,=0x4C000014
mov r1,#0x3
str r1,[r0]
2,设置总线模式为异步模式(手册中要求);
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
3,设置MPLL寄存器,将12M晶振频率变为200M;
ldr r0,=0x4c000004
ldr r2,=0x0005c012
str r2,[r0]
初始化内存;
ldr r0,=SDRDATA
ldr r1,=_text_start
sub r0,r0,r1 @data address for initing sdram start;
add r2,r0,#13*4 @data address end;
ldr r1,=0x48000000 @sdram control registers address start;
0: 循环设置内存控制器13个寄存器的值;
ldr r3,[r0],#4
str r3,[r1],#4
cmp r0,r2
bne 0b
设置栈,后面的初始化由C语言来完成所以需要设置栈;
ldr sp,=0x34000000
初始化Nand Flash控制器;
bl nand_init
重定位u-boot代码;
bl copy_to_sdram
进入u-boot第二阶段准备启动内核;
ldr lr,=halt
ldr pc,=main
@ bl main @jump to step 2 for starting linux kernel;
halt:
bl halt
设置内存控制器寄存器时使用的值;
.align 4
SDRDATA:
.long 0x32333330
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00018001
.long 0x00018001
.long 0x008404f5
.long 0x000000b1
.long 0x00000020
.long 0x00000020
第二阶段主要介绍传递给内核的参数设置部分(参考u-boot源码中的struct tag结构体):
参数的内存分布如图:
最重要的几个参数设置如下
/* 起始tag设置 */
static unsigned long * setup_start_tag (unsigned long *params)
{
*params = 0x5; //start tag的大小,以4字节对齐;
*(params+1) = ATAG_CORE; //start tag的name标示;
*(params+2) = 0;
*(params+3) = 0;
*(params+4) = 0;
return (params+5);
}
/* 内存tag设置 */
static unsigned long * setup_memory_tags (unsigned long *params)
{
*params = 4;
*(params+1) = ATAG_MEM;
*(params+2) = 0x4000000; //内存空间大小;
*(params+3) = 0x30000000; //内存起始地址;
return (params+4);
}
/* 命令行tag设置 */
static unsigned long * setup_commandline_tag (unsigned long *params,char *commandline)
{
int len,slen=strlen(commandline); //命令行参数长度
char *tmp;
len = (8+slen+4)>>2; //命令行参数tag的总大小,4字节对齐;
*params = len;
*(params+1) = ATAG_CMDLINE;
tmp = (char *)(params+2);
strcpy(tmp,commandline); //将命令行参数内容放入tag内存空间;
*(tmp+slen)='\0';
return params+len;
}
/* 结束tag设置 */
static void setup_end_tag (unsigned long *params)
{
*params = 0x0;
*(params+1) = ATAG_NONE;
}
完整源代码下载地址
start.S 第一阶段启动实现;
init.c 主要硬件初始化及相关操作实现(串口 , Nand Flash);
boot.c 从Nand Flash的Kernel分区(0x60040–不包括uImage的header部分所以0x60000需要加上64字节)中复制Kernel代码到SDRAM的0x30008000的地址处,设置传递给内核的参数,参数存放在内存的0x30000100地址处,然后启动内核;