说明:第一阶段和第二阶段具体细节未做说明,旨在整个启动流程,即如何自己实现bootloader的整体思维。代码附带贴出,可以参考。
在第一阶段start.s中未完成的工作只剩下main函数。main函数的工作如下:
1、从NandFlash中把内核读入内存
2、设置需要传递的参数
3、跳转执行
第一阶段代码与下面代码一起便可启动内核,具体不在此分析,如不理解,可以参看uboot源码
boot.c(串口的初始化在第一阶段的init.c中)
- #include "setup.h"
- extern void uart0_init(void);
- extern void nand_read(unsigned int addr, unsigned charchar *buf, unsigned int len);
- extern void puts(charchar *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;
- params->hdr.size = tag_size (tag_core);
- params->u.core.flags = 0;
- params->u.core.pagesize = 0;
- params->u.core.rootdev = 0;
- params = tag_next (params);
- }
- 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;
- params = tag_next (params);
- }
- int strlen(charchar *str)
- {
- int i = 0;
- while (str[i])
- {
- i++;
- }
- return i;
- }
- void strcpy(charchar *dest, charchar *src)
- {
- while ((*dest++ = *src++) != '\0');
- }
- void setup_commandline_tag(charchar *cmdline)
- {
- int len = strlen(cmdline) + 1;
- params->hdr.tag = ATAG_CMDLINE;
- params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;
- strcpy (params->u.cmdline.cmdline, cmdline);
- params = tag_next (params);
- }
- void setup_end_tag(void)
- {
- params->hdr.tag = ATAG_NONE;
- params->hdr.size = 0;
- }
- int main(void)
- {
- void (*theKernel)(int zero, int arch, unsigned int params);
- volatile unsigned intint *p = (volatile unsigned intint *)0x30008000;
- /* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */
- uart0_init();
- /* 1. 从NAND FLASH里把内核读入内存 */
- puts("Copy kernel from nand\n\r");
- nand_read(0x60000+64, (unsigned charchar *)0x30008000, 0x200000); //uImage = 64字节头部 + zImage,内核存放在0x60000的位置
- puthex(0x1234ABCD); //用作测试
- puts("\n\r");
- puthex(*p); //打印出zImage前4字节
- 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); //要传递的参数约定放在0x30000100的位置,内核放在0x30008000的位置。中间放参数
- /*
- * mov r0, #0
- * ldr r1, =362
- * ldr r2, =0x30000100
- * mov pc, #0x30008000
- */
- puts("Error!\n\r");
- /* 如果一切正常, 不会执行到这里 */
- return -1;
- }
Makefile
- CC = arm-linux-gcc
- LD = arm-linux-ld
- AR = arm-linux-ar
- OBJCOPY = arm-linux-objcopy
- OBJDUMP = arm-linux-objdump
- CFLAGS := -Wall -O2
- CPPFLAGS := -nostdinc -nostdlib -fno-builtin
- objs := start.o init.o boot.o
- boot.bin: $(objs)
- ${LD} -Tboot.lds -o boot.elf $^
- ${OBJCOPY} -O binary -S boot.elf $@
- ${OBJDUMP} -D -m arm boot.elf > boot.dis
- %.o:%.c
- ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
- %.o:%.S
- ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
- clean:
- rm -f *.o *.bin *.elf *.dis