U-Boot是什么?
UBOOT可以理解为单片机程序。UBOOT目标就是启动内核:从flash上读出内核到SDRAM中去。硬件相关的初始化:关看门狗,初始化时钟“,”初始化SDRAM“,”从flash读出内核“,”启动内核“。
补丁文件中修改过的代码:
“---”表示是原来的代码
“+++”表示修改过的代码
UBOOT的源码中的readme文档中说要先配置再编译。
1.先配置 make xx_config:在Makefile中搜索100ask24x0_config
2.make
UBOOT源码结构:
UBOOT-1.1.6的基础上进行分析和移植。UBOOT-1.1.6根目录下共有26个子目录,可以分为四类:
1.平台相关或开发板相关的
2.通用的函数
3.通用的设备驱动程序
4.UBOOT工具,示例程序,文档
UBOOT配置过程:
执行make smdk2410_config
在顶层makefile中查找
$(@:_config=)的作用就是将smdk2410_config中的config去掉,结果为smdk2410
执行make smdk2410_config实际上就是执行:
./mkconfig smdk2410 arm arm920t smdk2419 NULL s3c24x0
再来看看mkconfig的作用:配置参数。
打开mkconfig文件,阅读源码。
对于传入的参数中并没有“--”,“-a”,“-n”等参数,所以这段代码没有执行。
$#统计参数个数,这里传入的参数是6个。
这里的SRCTREE和OBJTREE是相同的。执行else分支代码
进入include目录,删除asm目录,然后再次建立asm文件,并令它链接向asm-$2目录,即asm-arm。(这样可以根据输入参数的不同,动态配置文件)
对于“./mkconfig smdk2410 arm arm920t NULL s3c24x0”命令,$6为“s3c24x0”不为空,也不是NULL,所以执行else分支。
接下来重新建立 asm-arm/proc文件,并让它链接向proc-armv目录。
创建config.mk文件。
>表示新建一个文件,>>表示追加内容进文件。
总结一下:配置命令"make smdk24x0_config",实际的作用就是执行“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令。假设执行"./mkconfig $1 $2 $3 $4 $5 $6"命令,将产生如下结果
1.开发板名称 BOARD_NAME=$1
2.根据输入参数动态创建对应平台/开发板相关头文件的链接
ln -s asm-$2 asm
ln -s arch-$6 asm-$2/arch
ln -s proc-armv asm-$2/proc #如果$2不是arm,此行没有
3.创建顶层Makefile包含的文件inlcude/config.mk
ARCH =$2
CPU =$3 ...
4.创建开发板相关的头文件include/config.h
#include <config/$1.h>
配置文件中有以下两类宏。
1.一类是选项(Options),前缀为“COFIG_”,它们用于选择CPU,SOC,开发板类型,设置系统时钟,选择设备驱动等。
2.另一类是参数(settings),前缀为“CFG_”,它们用于设置malloc缓冲池的大小,U-Boot的提示符,U-Boot下载文件时默认加载地址,Flash的起始地址。
U-Boot的编译和链接过程:
配置完成后,执行“make all”即可编译。打开Makefile中与ARM相关的部分。
all:依赖于$(ALL)。而ALL中需要生成的u-boot.bin
从make之后的信息可以知道,编译的
第一个文件是cpu/arm920t/start.S .链接地址:board/100ask24x0/u-boot.lds+0x33F80000.
总结一下U-Boot的编译流程:
1.首先编译cpu/$(CPU)/start.S,对于不同的CPU,还可能编译cpu/$(CPU)下的其他文件
2.对于平台/开发板相关的每个目录,每个通用目录都使用各自的Makefile生成相应的库
3.将1,2步骤生成的.o,.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码段起始地址,board/$(BOARDDIR)/U-Boot.lds连接脚本进行连接。
4.第三步得到的是ELF格式的U-Boot,后面Makefile还会将它转换为二进制格式
U-Boot第一阶段代码分析:
1.设scv模式
2.关看门狗
3.屏蔽中断
4.初始化SDRAM
5.设置栈
6.设置时钟
7.代码:flash重定位到SDRAM
8.清BSS段
9.调用C函数 ==>开始第二阶段
U-Boot第二阶段代码分析:
uboot中核心命令:
U-Boot命令的格式(一个宏定义):
name:命令的名字,注意,它不是字符串(不要用双引号括起来)
maxargs:最大的参数个数
repeatable:命令是否可重复
command:对应的函数指针,类型为(*cmd)(struct cmd_tbl_s*,int,int,char*[])
usage:简短的使用说明
help:较详细的使用说明,这是个字符串。
对于每个使用U-Boot_CMD宏来定义的命令,其实都是在“.u_boot_cmd”段中定义一个cmd_tbl_t结构。程序就是根据命令的名字,在_u_boot_cmd_start 和_u_boot_cmd_end找到它的cmd_tbl_t结构,然后调用它的函数。
s=getenv("bootcmd")
run_commadn(s..)
2.uboot界面:
readline(独去串口数据)
run_command(s..)
nand read.jffs2 0x30007FC0 kernel;
从NAND读出内核: 从哪里读 --------kernel
放到哪里去?---0x30007FC0
bootm 0x30007FC0==>启动内核
FLASH存的内核称为Uimage :==>头部加上真正的内核。
头部:
in_load(加载地址): 内核加载到内存的地址就是加载地址
in_ep(入口地址): 运行内核的地址
ps(程序永远是从‘0’地址开始运行的)
启动内核
1.设置启动参数:在某个地址按照某种格式存放数据(uboot和内核去约定好)(地址是:0x30000000;格式是:tag)
2.跳到头部设立的入口地址
Linux2.4.x以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。标记,就是一种数据结构;标记列表,就是挨个存放着的多个标记。标记列表以标记ATAG_CORE开始,以ATAG_NONE结束。
标记的数据结构为tag,它由一个tag_header结构和一个联合(unino)组成。tag_header结构表示标记的类型和长度,比如是表示内存还是命令行参数。对于不同类型的标记使用不同的联合(union),比如表示内存时使用tag_mem32,表示命令行时使用tag_cmdline。
下面以设置ATAG_CORE标记为例说明参数的传递
标记列表以标记ATAG_CORE为开始,假设Bootloader与内核约定的参数存放地址为0x30000100
s=getenv("bootcmd") //获取环境变量
run_command(s) //