参考《嵌入式linux应用开发完全手册》
目录
一.编译体验
1.将镜像文件及源码放入ubuntu
2.解压u-boot-1.1.6.tar.bz2
tar xjf u-boot-1.1.6.tar.bz2
3.进入u-boot-1.1.6/
cd u-boot-1.1.6/
4.打补丁
patch -p1 < ../u-boot-1.1.6_jz2440.patch
查看这个补丁文件,在开头的地方可以看到补丁打入的地方,即
--- u-boot-1.1.6/board/100ask24x0/100ask24x0.c
意思是打入u-boot-1.1.6/board/100ask24x0/100ask24x0.c ,而我们已经在u-boot-1.1.6/这个目录下了,所以命令前面p1的意思是忽略u-boot-1.1.6/。
5.配置
make 100ask24x0_config
6.编译
make
这里会提示没有安装arm-linux-gcc,需要去安装,这里不讲述如何安装
然后就可以烧写生成的u-boot.bin文件。
pc机一上电,BIOS引导操作系统windows识别c、d盘。嵌入式系统上电后bootloader引导linux内核,挂载根文件系统。bootloader的最终目的就是要启动内核
启动内核之前要从flash中读出内核,之前还有硬件相关的初始化,在这一系列环节中可以完成开发功能,比如烧写flash,网卡,usb和串口等等。
二、makefile结构分析
参考“嵌入式Linux应用开发完全手册"
分析配置过程-make 100ask24x0_config
MKCONFIG := $(SRCTREE)/mkconfig
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
相当于执行:
MKCONFIG 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
p252
三、分析编译过程
总结编译过程
四、源码
这是u-boot内存使用情况图
4.1、u-boot第一阶段代码分析
硬件设备初始化,将cpu工作模式设置为管理模式,关闭看门狗。
为加载bootloader的第二阶段代码准备ram空间
复制bootloader的第二阶段代码到ram空间中
设置好栈,栈的灵活性很大,只要让sp寄存器指向一段没有使用的内存即可
跳转到第二阶段的c入口点,在跳转之前还要清除bss段
4.2、 u-boot第二阶段代码分析
第二阶段从lib_arm/board.c中的start_armboot函数开始,程序流程图如下
u-boot启动内核,先从flash中读出内核,然后启动它
读出内核:nand read.jffs2 0x30007FC0 kernel,从nandFlash把内核读到0x30007FC0
启动内核:bootm 0x30007FC0,从0x30007FC0启动内核
u-boot的核心是命令
4.3、u-boot命令格式
即使是内核的启动,也是通过u-boot命令来实现的,u-boot中每个命令都通过U_BOOT_CMD宏来定义
宏U_BOOT_CMD在include/command.h中定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
Struct_Section也是在include/command.h中定义:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
对于每个使用U_BOOT_CMD宏来定义的命令,都在.u_boot_cmd段中定义一个cmd_tbl_t结构。
总之,内核的复制和启动,可以通过如下命令来完成:bootm从内存、ROM、NOR FLASH中启动内核,bootp则通过网络来启动,而nboot从NAND Flash启动内核。他们都是先将内核映像从各种媒介中读出,存放在指定的位置,然后设置标记列表以给内核传递参数,最后跳到内核的入口点去执行。
做一个简单的测试,在common文件夹内添加一个cmd_hello.c文件
代码如下
#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <image.h>
#include <malloc.h>
#include <zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <asm/byteorder.h>
int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int i;
printf ("hello world!!,%d\n",argc);
for(i =0;i<argc;i++)
{
printf("argv[%d]: %s\n",i,argv[i]);
}
return 0;
}
U_BOOT_CMD(
hello, CFG_MAXARGS, 1, do_hello,
"hello - just for test\n",
"hello, long help ................\n"
);
然后修改Makefile,最后make,启动开发板
4.4、启动内核
介绍一下分区,PC每个硬盘都有分区表,但是对于嵌入式linux,flash没有分区表,所以这些分区是在源码里写死的,所以我们不关系这些分区的名字,只关心这些分区的地址
所以上面读出内核的这条命令:nand read.jffs2 0x30007FC0 kernel,也可以写成nand read.jffs2 0x30007FC0 0x00200000 0x00060000
u-boot要告诉内核一些参数,也就是为内核设置启动参数,然后再跳到入口地址启动内核
对于ARM架构的CPU,都是通过lib_arm/armlinux.c中的do_bootm_linux函数来启动内核。这个函数中,设置标记列表,最后通过"theKernel (0, bd->bi_arch_number, bd->bi_boot_params);"调用内核。其中theKenel指向内核存放的地址(对于ARM架构的CPU,通常是0x30008000),bi_arch_number是前面board_init函数设置的机器类型ID,而bi_boot_params就是标记列表的开始地址。