1、u-boot的功能:
- 关看门狗
- 设置栈
- 初始化时钟
- 初始化SDRAM
- 从FLASH读出内核写入SDRAM
- 启动内核
此外,在开发阶段为了方便还额外添加一些其他功能
- 烧写FLASH
- 网卡
- USB
- 串口
2、u-boot主要目的:
1. 从flash上读出内核写到SDRAM中
2. 启动内核(用do_bootm函数)
即
nand read.jffs2 0x30007FC0 kernel
bootm 0x30007FC0
从nand上kernel这个地址区读写到0x30007FC0地址上去,然后从0x30007FC0上启动内核
3、u-boot的配置
执行
make 100ask24x0_config
相当于执行Makefile中的
MKCONFIG := $(SRCTREE)/mkconfig
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
即相当于执行下面的脚本文件
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
因此,创建了顶层Makefile包含的文件
include/config.mk
里面的内容是:
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
SOC = s3c24x0
创建开发板相关的头文件
include/config.h
里面的内容是:
/* Automatically generated - do not edit */
#include <configs/100ask24x0.h>
4、分析Makefile
u-boot.bin:
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
u-boot:
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
其中,LDFLAGS:
u-boot-1.1.6/config.mk:189:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
LDSCRIPT:boot-1.1.6/board/100ask24x0/u-boot.lds
u-boot-1.1.6/board/100ask24x0/config.mk:25:TEXT_BASE = 0x33F80000
执行make命令,通过结果分析目标u-boot的规则
UNDEF_SYM=`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd /home/lvxiao/Ftp/system/u-boot/tmp/u-boot-1.1.6 && arm-linux-ld -Bstatic -T /home/lvxiao/Ftp/system/u-boot/tmp/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000 $UNDEF_SYM cpu/arm920t/start.o \
--start-group lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a --end-group -L /usr/local/arm/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5 -lgcc \
-Map u-boot.map -o u-boot
这些文件如何组成u-boot.bin,如何排序,哪个是第一个运行的文件,从链接文件u-boot-1.1.6/board/100ask24x0/u-boot.lds可以看到,start.o放在起始运行地址,因此,cpu/arm920t/start.S是第一个运行的源程序
其中,u-boot.lds:
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
board/100ask24x0/boot_init.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
5、分析start.S,运行流程:
①、设置svc模式
②、关看门狗
③、屏蔽中断
④、初始化SDRAM
⑤、设置栈
⑥、时钟
⑦、重定位:将代码从Flash拷贝到SDRAM中
⑧、清BSS段
⑨、调用start_armboot
_start_armboot: .word start_armboot
6、start.S最后一步调用start_armboot函数,进入board.c中的start_armboot函数
u-boot-1.1.6\lib_arm\board.c:void start_armboot (void)
7、start_armboot函数最后调用main_loop函数,进入main.c中的main_loop函数
u-boot-1.1.6\common\main.c:void main_loop (void)
s = getenv ("bootcmd");
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CFG_HUSH_PARSER
{
printf("Booting Linux ...\n");
run_command (s, 0);
}
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
# ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
run_command("menu", 0);
如果在bootdelay时间段内没有按空格则,执行run_command (s, 0);
命令启动内核,如果按了空格则执行run_command("menu", 0);
跳转到菜单。
8、启动内核或按空格进入u-boot菜单都会从main_loop函数进入run_command函数
u-boot-1.1.6\common\main.c:int run_command (const char *cmd, int flag)
run_command函数会根据cmd参数按照include\command.h中的如下代码找到对应的函数执行
#define _CMD_REMOVE(_name, _cmd) \
int __remove_ ## _name(void) \
{ \
if (0) \
_cmd(NULL, 0, 0, NULL); \
return 0; \
}
...
...
...
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, \
_comp) \
_CMD_REMOVE(sub_ ## _name, _cmd)
#endif /* CONFIG_CMDLINE */
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \
U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
9、NOR FLASH、NAND FLASH启动选择:
u-boot-1.1.6\board\100ask24x0\boot_init.c:int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
10、从NAND FLASH里读出内核
u-boot-1.1.6\common\cmd_nand.c:int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
Flash上的 uImage=头部+真正的内核,头部:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
头部里关键参数是:
in_load:加载地址
in_eq:入口地址
如果发现内核不在加载地址就把内核移到加载地址,再跳到入口地址。
11、启动内核前的准备
do_bootm函数功能:
-根据头部移动内核到加载地址
-调用do_bootm_linux函数启动linux内核
u-boot-1.1.6\commoncmd_bootm.c:int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
12、启动内核
u-boot-1.1.6\lib_arm\Armlinux.c:void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],ulong addr, ulong *len_ptr, int verify)
- 设置启动参数:在某个地址按某种格式保存参数
setup_start_tag (bd);
setup_memory_tags (bd)
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
- 跳到入口地址:
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
bi_boot_params:启动参数所存的地址
bi_arch_number:机器ID