您的位置:首页 > 运维架构 > Linux

自制简易BootLoader思路分析及实现

2017-08-31 16:53 465 查看
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地址处,然后启动内核;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息