这个简单的BootLoader一共有三个源文件,分别是start.S、init.c、boot.c,前两个的作用已经在前两篇文章中分析过了,主要起到初始化硬件的作用。那么自然地,BootLoader的另外一个重要作用——启动内核,就需要由boot.c来完成了。
这个文件是用于启动内核的,主要有四个步骤:
1. 帮内核设置串口:
2. 从NAND FLASH里把内核读入内存
3. 设置参数
4. 跳转执行
设置参数是最复杂的一部分内容:
setup_start_tag等函数的具体操作,可以直接从U-Boot中拷贝过来。setup.h拷贝过来之后稍作写修改即可使用,tag等结构体也是在setup.h中定义的。(使用SourceInsight查找)
#include "setup.h"
extern void uart0_init(void);
extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
extern void puts(char *str);
extern void puthex(unsigned int val);
static struct tag *params;
void setup_start_tag(void)
{
params = (struct tag *)0x30000100;
params->hdr.tag = ATAG_CORE;//tag结构体中有一个tag_header类型的hdr结构体,成员
params->hdr.size = tag_size (tag_core);//就是tag和size
params->u.core.flags = 0;//tag最后被命名为u
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);//以上设置完成之后,params跳到下一个tag
//#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)),
//所以就可以指向下一个tag的内存空间
}
void setup_memory_tags(void)
{
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = 0x30000000;//内存的起始地址
params->u.mem.size = 64*1024*1024;//内存大小64M
params = tag_next (params);
}
int strlen(char *str)
{
int i = 0;
while (str[i])
{
i++;
}
return i;
}
void strcpy(char *dest, char *src)
{
while ((*dest++ = *src++) != '\0');
}
void setup_commandline_tag(char *cmdline)
{
int len = strlen(cmdline) + 1;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;//之所以>>2是因为size
//都是以4字节为单位的
//像4取整应该是+3,u-boot中的代码用的是+1+4,他说不好,然而这好像一个样
strcpy (params->u.cmdline.cmdline, cmdline);//把命令拷贝到字符存放区
params = tag_next (params);
}
void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;//结束就很简单,两个0就可以了
params->hdr.size = 0;
}
int main(void)
{
void (*theKernel)(int zero, int arch, unsigned int params);
volatile unsigned int *p = (volatile unsigned int *)0x30008000;
/* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */
uart0_init();
/* 1. 从NAND FLASH里把内核读入内存 */
puts("Copy kernel from nand\n\r");
nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
puthex(0x1234ABCD);
puts("\n\r");
puthex(*p);
puts("\n\r");
/* 2. 设置参数 */
puts("Set boot params\n\r");
setup_start_tag();
setup_memory_tags();
setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
setup_end_tag();
/* 3. 跳转执行 */
puts("Boot kernel\n\r");
theKernel = (void (*)(int, int, unsigned int))0x30008000;
theKernel(0, 362, 0x30000100);
/*
* mov r0, #0
* ldr r1, =362//机器ID
* ldr r2, =0x30000100//参数的位置
* mov pc, #0x30008000 //内核的位置
*/
puts("Error!\n\r");
/* 如果一切正常, 不会执行到这里 */
return -1;
}