目标:
1 添加头文件setup.h和serial.h
2 写main函数
2.1 帮内核设置串口0, (内核启动会打印出启动信息)
2.2把内核读入到SDRAM
2.3设置参数(参考u-boot-1.1.6 /lib_arm/armlinux.C中do_bootm_linux()函数)
2.4跳转运行内核(参考u-boot-1.1.6/lib_arm/armlinux.C中do_bootm_linux()函数)
3 写tag参数函数
3.1 setup_start_tag (void);
3.2 setup_memory_tags(void);
3.3 setup_commandline_tag (char *cmdline);
3.4 setup_end_tag (void);
4 写makefile文件
1 添加头文件setup.h和serial.h
1.1 将lcd裸板程序中串口uart0初始化文件serial.c复制到my_bootloader目录中.并修改serial.c
1.2 因为TAG结构体定义是存在u-boot-1.1.6/include/asm-arm/setup.h中,所以设置TAG参数需要用到这个文件,将setup.h复制到my_bootloader目录中.
1.2.1 修改setup.h文件
删除以下不需要的代码:
#define __tag __attribute__((unused, __section__(".taglist"))) #define __tagtable(tag, fn) static struct tagtable __tagtable_##fn __tag = { tag, fn } #define tag_member_present(tag,member) ((unsigned long)(&((struct tag *)0L)->member + 1) <= (tag)->hdr.size * 4)
添加以下代码:
#define u32 unsigned long #define u16 unsigned int #define u8 unsigned char
2. 新建my_bootloader/boor.c,用于存放main函数(main:由start.S跳转过来的).
main函数代码如下:
void main(void) { void (*theKernel)(int zero, int arch, unsigned int params); /*定义一个函数指针theKernel,其中第一个参数zero:0 */ /* arch:机器ID ,由于芯片类型很多,内核为了辨别芯片而定义的机器ID,其中2440芯片的ID号是362,*/ /* params :tag参数位置,这里我们的tag起始地址=0x30000100*/
/*1 初 始 化 串 口 0 , 使 内 核 能 打 印 信 息 */ uart0_init(); //调用serial.h头文件里的uart0_init() puts(“uart0 init OK ”); //打印uart0初始化 /*2从 nand flash 里 把 内 核 复 制 到 SDRAM 中 */ puts(“copy kernel from nand ”); //打印内核复制 nand_read((0x60000+64),0X30008000,0X200000); //烧写2MB,多烧写点避免出错 /* 0x60000+64:表示内核在nand(存储)地址上位置, 0X30008000:内核在sdram(运行)地址上位置 0X200000:内核长度2MB 因为Flash上存的内核格式是:uImage(64B头部(header) + 真正的内核 ) 在uboot界面中输入mtd命令可以看到: kernel分区位于 nand的0X00060000~0x00260000 所以在nand中真正的内核地址=0x60000+64, 在uboot界面中输入boot命令可以看到: Data Size: 1848656 Bytes =1.8 MB Load Address: 30008000 所以内核目的地址=0X30008000 长度=1.8MB */ /*3 设 置 T A G 参 数 */ puts(“set boot params ”); //打印设置参数信息 setup_start_tag (void); //在0X30000100地址保存start_tag数据, setup_memory_tags (void); //保存memory_tag数据,让内核知道内存多大 setup_commandline_tag (“boottargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0”); /*保存命令行bootargs参数,让内核知道根文件系统位置在/dev/mtdblock3,指定开机运行第一个脚本/linuxrc,指定打印串口0*/ setup_end_tag (void); //初始化tag结构体结束 /* 4 跳 转 执 行 */ puts(“boot kernel ”); //打印启动内核 theKernel = (void (*)(int, int, unsigend int))0x30008000; // 设置theKernel地址=0x30008000,用于后面启动内核 theKernel(0,362,0x300000100); //362:机器ID, 0x300000100: params(tag)地址 /*传递参数跳转执行到0x30008000启动内核, */ /*相当于: mov r0,#0 */ /*ldr r1,=362 */ /*ldr r2,= 0x300000100 */ /*mov pc,#0x30008000 */
puts(“kernel ERROR ”); //打印内核启动出错 }
3.创建TAG参数函数(使main函数调用)
设置tag参数函数代码如下
#include “setup.h” static struct tag *params; //定义个tag结构体变量params指针 setup_start_tag (void) //开始tag { params = (struct tag *) 0x30000100; //tag起始地址等于0X30000100 params->hdr.tag = ATAG_CORE; //头部常量tag=0x54410001 params->hdr.size = tag_size (tag_core); //size=5, params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0;
params = tag_next (params); //parmas=( struct tag *)((u32 *)parmas+ params->hdr.size) }
// setup_start_tag (bd)保存tag参数如下: setup_memory_tags (void) //内存tag { int i; params->hdr.tag = ATAG_MEM; //头部常量tag=0x54410002 params->hdr.size = tag_size (tag_mem32); //size=4 params->u.mem.start = 0x30000000; //SDRAM起始地址 params->u.mem.size = 0x4000000; //SDRAM内存大小64M params = tag_next (params); //指向下个tag } // setup_memory_tag s(bd)保存tag参数如下:
int strlen(char *str) //uboot不依赖任何库,所以需要自己写strlen函数 { int i=0; while(str[i]) { i++; } return i; } void strcpy(char *dest, char *src) { while((*dest++=*src++)!=’0’&&*dest!=’0’); } setup_commandline_tag (char *cmdline) //命令行tag /**cmdline :指向命令行参数 */ /*一般为:“boottargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0” */ { int len=strlen(cmdline)+1; //计算cmdline长度,并加上结束符 params->hdr.tag = ATAG_CMDLINE; //头部常量tag=0x54410009 params->hdr.size =(sizeof (struct tag_header) +len+3) >> 2; /*size=(字符串长度+头部长度) >>2 */ /*“+3”表示:按4字节对齐,比如当总长度=(1,2,3,4)时,size=(总长度+3)>>2=1,实现4字节对齐 */ strcpy (params->u.cmdline.cmdline, cmdline); //复制形参字符串到params->u.cmdline.cmdline params = tag_next (params); //执行下个tag } setup_end_tag (void) //结束tag { params->hdr.tag = 0; params->hdr.size = 0; }
4 写makefile文件
4.1 首先将lcd裸板程序里的makefile复制到my_bootloader目录中,并修改.
备注:在makefile中‘=’与‘:=’的区别:
‘=’:无关位置的等于(比如:”x=a y=$(x) x=b”,那么y的值永远等于最后的值,等于 b ,而不是a)
‘:=’:有关位置的等于(比如:”x:=a y:=$(x) x:=b”,那么y的值取决于当时位置的值,等于 a ,而不是b)
代码如下:
CC = arm-linux-gcc //定义CC变量=arm-linux-gcc,简化书写,编译命令,(*.C,*.S)文件生成*.O文件 LD = arm-linux-ld //连接命令,将多个*.O文件生成 boot.elf AR = arm-linux-ar //库管理命令,这里没有用到 OBJCOPY = arm-linux-objcopy //复制/格式转换命令, boot.elf生成boot.dis OBJDUMP = arm-linux-objdump //反汇编命令,boot.bin生成boot.dis CFLAGS := -Wall -O2 //GCC编译参数,-Wall:显示所有错误和警告, -O2:采用2级编译优化 CPPFLAGS := -nostdinc -fno-builtin //添加头文件参数,-nostdinc忽略缺省目录, -fno-builtin不连接系统标准启动文件和标准库文件(表示不用自带的strlen()等库函数) objs := start.o init.o boot.o //定义objs变量,包含生成boot.bin目标文件需要的依赖文件 boot.bin: $(objs) //执行生成目标文件,首先是先满足objs所有依赖文件都拥有,才执行 ${LD} -Tuboot.lds -o boot_elf $^ ${OBJCOPY} -O binary -S boot_elf $@ ${OBJDUMP} -D -m arm boot_elf > boot.dis %.o:%.c //%通配符。生成xxx.o文件先要找到xxx.c文件 ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $< //-c编译不连接。$@表示目标文件 $<表示第一个依赖文件 %.o:%.S ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $< clean: rm -f *.bin *.elf *.dis *.o