U-Boot启动流程(Linux内核)的分析(写得好)
来源:互联网 发布:塔吊基础计算软件 编辑:程序博客网 时间:2024/05/16 15:48
(1)启动加载模式:这种模式也称为“自主”模式。也就是Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入,这种模式是在嵌入式产品发布里的通用模式。
(2) 下载模式:在这种模式下,目标机上的Bootloader将通过串口连接或网络连接等通信手段从主机下载文件,例如:下载内核映像和根文件系统映像等。从 主机下载的文件 通常首先被Bootloader保存到目标机的RAM中,然后再被Bootloader写到目标上的Flash类的固态存储设备中,Bootloader 的这种模式是在在开发时使用的工作于这种模式的Bootloader通常都 会向它的终端用户提供一个简单的命令行接口。
在嵌入式Linux系统中从软件的角度通常可以分为4个层次:
(1)引导加载程序,包括固化在固件中的boot代码(可选)和Bootloader两大部分。
有些CPU在运行Bootloader之前运行一段固化的程序 ,比如x86结构的CPU就是先运行BIOS中的固件,然后才运行硬盘的第一个分区中的BootLoader。在大多数的嵌入式系统中并没有固件,Bootloader是上电后第一个执行的程序。
(2)Linux内核
嵌入式定制的内核以及启动参数,启动参数可以是Bootloader传递给内核的,也可以是内核默认的。
(3)文件系统
包括根文件系统和建立于Flash内存设备之上的文件系统。里面包括了Linux系统能够运行所必要的应用程序和库文件等。比如可以给用户提供操作Linux的控制shell程序。
(4)用户应用程序
特定于用户的应用程序,它们也存储在文件系统中,有时在用户应用程序和内核层之间可以还会包括一个嵌入式图形用户界面。
2. Bootloader启动的两个阶段
从固态存储设备上启动的Bootloader大多都是两阶段的启动过程,第一阶段使用汇编来实现。它完成一些依赖于CPU体系结构的初始化,并调用第二阶段的代码,第二阶段则通常使用C语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。
(1) Bootloader第一阶段的功能
1)硬件设备初始化
2)为加载Bootloader的第二阶段准备RAM空间。
3)复制Bootloader的第二阶段代码到RAM空间中。
4)设置好栈
5)跳转到第二阶段代码的C入口点。
在第一阶段进行的硬件初始化一般包括:关闭WATCHDOG,关中断,设置 CPU的速度和时钟频率RAM初始化等。这些不都是必需的。
(2)Bootloader第二阶段的功能
1)初始化本阶段要使用的硬件设备
2)检测系统内存映射
3)将内核映像和根文件系映象从Flash望到RAM空间中
4)为内核设置启动参数
5)调用内核
将内核存放在适当的位置后,直接跳到它的入口点即可调用内核,调用内核之前,下列条件要满足
(1)CPU寄存器的设置
R0=0.
R1=机器类型ID;对于ARM结构的CPU,其机器类型ID在linux/arch/arm/tools/mach-types
R2=启动参数标记列表在RAM中起始基地址
(2)CPU工作模式
必须禁止中断(IRQs和FIQs)
CPU必须为SVC模式
(3)Cach和MMU的设置
MMU必须关闭
指令Cach可以打开也可以关闭
数据Cach必须关闭
U-boot.bin:二进制可执行文件,它就是可以直接烧入ROM,NORFlash的文件
u-Boot:ELF格式的可执行文件,
U-Boot.srec:Motorla S-Record格式的可执行文件
对于S3C2410的开发板,执行”make smdk2410_config“."make all"后生成的U-Boot.bin可以烧入NOR Flash中运行,启动后可以看到串口输出一些信息后进行控制界面。
...........
MKCONFIG := $(SRCTREE)/mkconfig
........
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 samsung s3c24x0
这是在根目录下的MAKEFILE文件中的两个语句,其中的MKCONFIG就是根目录下的mkconfi文件。$(@:_config=)的结 果就是将”smdk2410_config“中的_config去掉,结果为“smdk2410”.所以“make smdk2410_config”实际上就是执行如下命令:
./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
(1)确定开发板名称BOARD_NAME,相关代码如下:
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
(2)创建到平台开发板相关折头文件的链接
if [ "$SRCTREE" != "$OBJTREE" ] ; then //判断源代码目录和目标文件目录是否是一样
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else
cd ./include
rm -f asm
ln -s asm-$2 asm
fi
rm -f asm-$2/arch //删除asm-$2/arch目录,即asm-arm/arch
if [ -z "$6" -o "$6" = "NULL" ] ; then //$6="s3c24x0"不为空,也不为NULL,执行else分支
ln -s ${LNPREFIX}arch-$3 asm-$2/arch //LNPREFIX 为空,这个命令实际上等同于"ln - s arch-s3c24x0 asm-arm/arch"
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi
if [ "$2" = "arm" ] ; then //重新建立/asm-arm/proc文件,并让它链接向proc-armv目录
rm -f asm-$2/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi
#
# Create include file for Make
#
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC =s3c24x0
#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
echo "#include <asm/config.h>" >>config.h
exit 0
APPEND维持原值"NO",所以config.h被重新建立,也就是执行echo "#include <configs/$1.h>" >>config.h
#include <configs/smdk2410.h>
总之,当你执行make smdk2410_config ,实际的作用就是执行./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0,它将产生如下的
几种作用
(1) 开发板的名称 BOARD_NAME等于 $1
(2)创建到平台,开发板相关的头文件的链接,如下所示
ln -s asm-$2 asm
ln -s arch-$6 asm-S2/arch
ln - s proc-armv asmn-$2/proc 如果$2不是arm的话,此行没有
(3)创建顶层Makefile包含的incldue /config.mk,如下所示
ARCH = $2
CPU = $3
BOARD = $4
VENDOR = $ $5 为空,或者NULL的话,些行没有
SOC = $6
(4) 创建开发板相关的头文件include/config.h,如下 所示
#include <config.h/$1.h>
从上面执行完命令后的结果,可以看出来,如果要在board目录下新建一个开发板<board_name>的目录,则在 include/configs 目录下也要建立一个文件<board_name>.h,里面存放的就是开发板<board_name>的配置信息。
3.U-Boot的编译,连接过程
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
# set default to nothing for native builds
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
# load other configuration
include $(TOPDIR)/config.mk
第 一行中包含的config.mk文件,就是在第一开始配置过程中制作出来的include/conifg.mk文件,我们在一开始配置U-boot时执行 过mkconfig。mini2440 时生成的文件,其中定义了ARCH,CPU,BOARD,SOC等。4个变量的值为arm,arm920t,smdk2410,s3c24x0.我们在执 行mkconfig。mini2440时,其实执行的是如下的命令:
./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0
fdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
LDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
ifneq ($(TEXT_BASE),)
LDFLAGS += -Ttext $(TEXT_BASE)
endif
继续分析MAKEFIle文件:
OBJS = cpu/$(CPU)/start.o
LIBS = lib_generic/libgeneric.a
LIBS += lib_generic/lzma/liblzma.a
LIBS += lib_generic/lzo/liblzo.a
LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \
"board/$(VENDOR)/common/lib$(VENDOR).a"; fi)
LIBS += cpu/$(CPU)/lib$(CPU).a
$(OBJS): depend
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
$(LIBS): depend $(SUBDIRS)
$(MAKE) -C $(dir $(subst $(obj),,$@))
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.ldr: $(obj)u-boot
$(obj)tools/envcrc --binary > $(obj)env-ldr.o
$(LDR) -T $(CONFIG_BFIN_CPU) -c $@ $< $(LDR_FLAGS)
$(obj)u-boot.ldr.hex: $(obj)u-boot.ldr
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ -I binary
$(obj)u-boot.ldr.srec: $(obj)u-boot.ldr
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@ -I binary
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
................
GEN_UBOOT = \
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__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
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)$(obj)u-boot.lds
$(GEN_UBOOT)
UTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.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 (NOLOAD) : { *(.bss) . = ALIGN(4); }
_end = .;
}
总结一下U-Boot的编译流程:
(1)首先编译cpu/$(CPU)/start.s,对于不同的CPU,还可能编译cpu/$(CPU)下面的其他文件。
(2)然后,对于平台开发板相关的每个目录,每个通用目录都使用它们各自的MAKEFILE生成相应和库。
(3)将1,2步骤生成的.o.a文件按照$(BOARDDIR)/config.mk 文件中指定的代码段起始地址。$(obj)u-boot.lds 连接脚本进行连接。
(4)第3步得到的是ELF格式的U-Boot,后面MAKEFILE还会将它转换为二进制格式 S-Record格式。
(1)硬件设备初始化
依次完成如下设置:将CPU的工作模式设为管理模式(SVC),关闭WATCHDOG,设置FCLK,HCLK,PCLK的比例,关闭MMU,CACHE。代码在cpu/arm920t/start.S中,
(2)为加载Bootloader的第二阶段代码准备RAM空间。
所谓准备RAM空间,就是初始化内存芯片,使它可用,对于S3C24x0,通过在Start.S中调用lowlevel_init函数来设置存储控制器,使得外接
_TEXT_BASE:
.word TEXT_BASE //这里是获得代码段的起始地址,我的是0x33F80000(在board/xxx/config.mk中
//可到找到“TEXT_BASE=0x33F80000”
.globl lowlevel_init //这里相当于定义一个全局的lowlevel_init以方便调用
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA //SMDATA表示这 13个寄存器的值存放的开始地址,值为0x33F8xxxx,处于内
//存中,这一句的作用是把其值加载到r0中
ldr r1, _TEXT_BASE // 把代码的起始地址(0x33F80000)加载到r1中
sub r0, r0, r1 //r0减去r1其结果存入r0,也即SMDATA中的起始地址0x33F8xxxx减去
//0x33F80000,其结果就是13个寄存器的值在NOR Flash存放的开始地址
ldr r1, =BWSCON /* Bus Width Status Controller */ //存储控制器的基地址
add r2, r0, #13*4 //在计算出来的存放地址加上#13*4,然后其结果保存在r2中
//13 个寄存器,每个寄存器占4个字节
0:
ldr r3, [r0], #4 //内存中r0的值加载到r3中,然后r0加4,即下一个寄存器的
str r3, [r1], #4 //读出寄存器的值保存到r1中,然后r1也偏移4
cmp r2, r0 //比较r0与r2的值,如果不等继续返回0:执行,也即13个寄存器的值
// 是否读完
bne 0b
/* everything is fine now */
mov pc, lr //程序跳转,返回到cpu_init_crit中
.ltorg
/* the literal pools origin */
SMRDATA:
...................
这里将整个U-Boot代码都复制到SDRAM中,这在cpu/arm920t/start.s中实现
relocate: /* 将U-Boot复制到RAM中 */
adr r0, _start /* r0:当前代码的开始地址 */
ldr r1, _TEXT_BASE /* r1:代码段的连接地址*/
cmp r0, r1 /* 测试现在是在FLash中,还在是RAM中,如果要从NandFlash启动的话,这里要根据需要修改 */
beq stack_setup /*如果已经在RAM中,则不需要复制*/
ldr r2, _armboot_start /*_armboot_start在前面定义,是第一条指令的运行地址*/
ldr r3, _bss_start /*在连接脚本U-Boot.lds中定义,是代码段的结束地址*/
sub r2, r3, r2 /* r2 <- 代码段长度 */
add r2, r0, r2 /* r2 <-代码段的结束地址 */
copy_loop:
ldmia {r3-r10} /* 从地址[r0]处获得数据 */
stmia {r3-r10} /* 复制到地址[r1]处 */
cmp r0, r2 /* 判断是否复制完毕 */
ble copy_loop /*没有复制完,则继续*/
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
(4)设置好栈
stack_setup:
ldr r0, _TEXT_BASE /* _TEXT_BASE 为代码段的开始地址,值为0x33F80000 */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* 代码段下面,留出一段内存以实现malloc */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* 再留出一段内存,存一些全局参数 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* 最后,留出12字节的内存给abort异常 */
clear_bss:
ldr r0, _bss_start /* 下面的都是栈 */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000
在跳转之前,还要清除BSS段(初始值0,无初始值的全局变量,静态变量放在BSS段。
clear_bss:
ldr r0, _bss_start /* BSS段的开始地址,它的值在连接脚本中U-Boot.lds中确定 */
ldr r1, _bss_end /* BSS段的结束地址,它的值在连接脚本u-Boot.lds中确定 */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* 向BSS段中写入0值 */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot: .word start_armboot
第二阶段从lib_arm/borad.c中的start_armboot函数开始,程序的流程如下 :
board_init 函数设置MPLL,改变系统时钟,它是开发板相关函数。board\samsung\smdk2410/smdk2410.c中实现。串口的初始化函数主 要是serial_init,它设置UART控制器,是CPU相关的函数,在cpu/arm920t/s3c24x0/serial.c中实现。
int dram_init (void)
{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
return 0;
}
(3) 为内核设置启动参数
在start_armboot()函数的最后,调用main_loop()函数,进行一个无限循环,该函数在common/main.c文件中定义。
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_CMDLINE_TAG 1
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay"); //得到环境变量中bootdelay
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
#ifdef CONFIG_BOOTCOUNT_LIMIT //启动次数的限制功能,如果到达一定次数,将不能启动u-boot.
if (bootlimit && (bootcount > bootlimit)) {//检查是否超出启动次数限制
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");//启动延时
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd");// 获得启动参数
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
// 这里如果bootdelay大于0,并且中间没有被中断的话,执行命令行参数
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, 0); //运行启动的命令行,例如 可以使用tftp命令
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
#define CONFIG_BOOTM_LINUX 1
#define CONFIG_BOOTM_NETBSD 1
#define CONFIG_BOOTM_RTEMS 1
#ifdef CONFIG_BOOTM_LINUX
extern boot_os_fn do_bootm_linux;
#endif
#ifdef CONFIG_BOOTM_NETBSD
static boot_os_fn do_bootm_netbsd;
#endif
void (*theKernel)(int zero, int arch, uint params);
theKernel = (void (*)(int, int, uint))images->ep;
image是bootm_headers结构体的指针,可以在inlcude/image.h文件中看到这个结构体的定义如下:
typedef struct bootm_headers {
............................
int fit_noffset_fdt;/* FDT blob subimage node offset */
#endif
#ifndef USE_HOSTCC
image_info_t os; /* os image info */
ulong ep; /* entry point of OS */
ulong rd_start, rd_end;/* ramdisk start/end */
...............
}
theKernel (0, machid, bd->bi_boot_params);
要知道哪个地址是启动内核,哪个地址启动文件系统,要分析common/cmd_bootm.c中的函数 do_bootm,因为引导kernel就是bootm这条命令的工作,do_bootm是命令bootm的执行函数现在我们来分析一下 common/cmd_bootm.c中的函数do_bootm,这是bootm命令的处理函数.do_bootm()函数中的很多功能都是分成了函数的 形式,而在以前的版本中没有这么有结构层次,这里我们也只是分析对引导Linux内核有作用的部分,因为这是一个在common文件夹下的文件,也就意味 着,在引导别的操作系统时也会用到这个函数,而不单单是Linux操作系统.
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong load_end = 0;
int ret;
boot_os_fn *boot_fn;
#ifndef CONFIG_RELOC_FIXUP_WORKS
static int relocated = 0;
/* relocate boot function table */
if (!relocated) {
int i;
for (i = 0; i < ARRAY_SIZE(boot_os); i++)
if (boot_os[i] != NULL)
boot_os[i] += gd->reloc_off;
relocated = 1;
}
#endif
/* determine if we have a sub command */
if (argc > 1) {
char *endp;
simple_strtoul(argv[1], &endp, 16);
if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
return do_bootm_subcommand(cmdtp, flag, argc, argv);
}
if (bootm_start(cmdtp, flag, argc, argv)) //提取mkimage生成的文件头部,放到bootm_headers_t结构体中
return 1;
iflag = disable_interrupts();
#if defined(CONFIG_CMD_USB)
usb_stop();
#endif
#ifdef CONFIG_AMIGAONEG3SE
/*
* We've possible left the caches enabled during
* bios emulation, so turn them off again
*/
icache_disable();
dcache_disable();
#endif
ret = bootm_load_os(images.os, &load_end, 1); //加载操作系统的关键部分 确定使用的地址
if (ret < 0) { //出错处理
if (ret == BOOTM_ERR_RESET)
do_reset (cmdtp, flag, argc, argv);
if (ret == BOOTM_ERR_OVERLAP) {
if (images.legacy_hdr_valid) {
if (image_get_type (&images.legacy_hdr_os_copy) ==IH_TYPE_MULTI)
puts ("WARNING: legacy format multi component "
"image overwritten\n");
} else {
puts ("ERROR: new format image overwritten - "
"must RESET the board to recover\n");
show_boot_progress (-113);
do_reset (cmdtp, flag, argc, argv);
}
}
if (ret == BOOTM_ERR_UNIMPLEMENTED) {
if (iflag)
enable_interrupts();
show_boot_progress (-7);
return 1;
}
}
lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));
if (images.os.type == IH_TYPE_STANDALONE) {//独立的应用程序
if (iflag)
enable_interrupts();
/* This may return when 'autostart' is 'no' */
bootm_start_standalone(iflag, argc, argv);
return 0;
}
show_boot_progress (8);
#ifdef CONFIG_SILENT_CONSOLE //这里处理Linux操作系统
if (images.os.os == IH_OS_LINUX)
fixup_silent_linux(); //该函数中处理bootarg参数
#endif
boot_fn = boot_os[images.os.os];
if (boot_fn == NULL) {
if (iflag)
enable_interrupts();
printf ("ERROR: booting os '%s' (%d) is not supported\n",
genimg_get_os_name(images.os.os), images.os.os);
show_boot_progress (-8);
return 1;
}
arch_preboot_os();
/*下面的函数,继续引导内核的镜像,复制image header 到全局变量header;
检查header的魔数,检查数,header和image中的这两个。确定image的体系结构和类型(KERNEL or MULTI),关闭中断,加载image到header中的加载地址*/
boot_fn(0, argc, argv, &images); //调用do_bootm_linux()函数
show_boot_progress (-9);
#ifdef DEBUG
puts ("\n## Control returned to monitor - resetting...\n");
#endif
do_reset (cmdtp, flag, argc, argv);
return 1;
}
static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)
{
uint8_t comp = os.comp;
ulong load = os.load;
ulong blob_start = os.start;
ulong blob_end = os.end;
ulong image_start = os.image_start;
ulong image_len = os.image_len;
uint unc_len = CONFIG_SYS_BOOTM_LEN;
const char *type_name = genimg_get_type_name (os.type);
switch (comp) { //判断image的压缩类型
case IH_COMP_NONE:
if (load == blob_start) {
printf (" XIP %s ... ", type_name);
} else {
printf (" Loading %s ... ", type_name);
//如果在Image head中加载的地址和bootm命令参数2指定的地址相同,则不需要复制,直接执行
if (load != image_start) {
memmove_wd ((void *)load,
(void *)image_start, image_len, CHUNKSZ);
}
}
*load_end = load + image_len;
puts("OK\n");
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", type_name);
if (gunzip ((void *)load, unc_len,
(uchar *)image_start, &image_len) != 0) {
puts ("GUNZIP: uncompress, out-of-mem or overwrite error "
"- must RESET board to recover\n");
if (boot_progress)
show_boot_progress (-6);
return BOOTM_ERR_RESET;
}
*load_end = load + image_len;
break;
#ifdef CONFIG_BZIP2
case IH_COMP_BZIP2: //判断是什么类型的压缩类型
printf (" Uncompressing %s ... ", type_name);
int i = BZ2_bzBuffToBuffDecompress ((char*)load,
&unc_len, (char *)image_start, image_len,
CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
if (i != BZ_OK) {
printf ("BUNZIP2: uncompress or overwrite error %d "
"- must RESET board to recover\n", i);
if (boot_progress)
show_boot_progress (-6);
return BOOTM_ERR_RESET;
}
*load_end = load + unc_len;
break;
#endif /* CONFIG_BZIP2 */
#ifdef CONFIG_LZMA
case IH_COMP_LZMA:
printf (" Uncompressing %s ... ", type_name);
....................
return 0;
}
uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。
mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象 是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么?到这里整个U-Boot是如何启动Linux内核的,基本上也就清楚了,特别是如何向Linux内核传送的参 数。
* CPU register settings //这里也就是我们的theKernel中的作用
o r0 = 0.
o r1 = machine type number.
o r2 = physical address of tagged list in system RAM.
* CPU mode
o All forms of interrupts must be disabled (IRQs and FIQs.)
o The CPU must be in SVC mode. (A special exception exists forAngel.)
* Caches, MMUs
o The MMU must be off.
o Instruction cache may be on or off.
o Data cache must be off and must not contain any stale data.
* Devices
o DMA to/from devices should be quiesced.
* The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.
- U-Boot启动流程(Linux内核)的分析(写得好)
- U-Boot启动流程(Linux内核)的分析
- U-Boot启动流程(Linux内核)的分析
- U-boot启动流程(Linux内核)的分析(四)
- U-Boot启动流程(Linux内核)的分析(一转)
- U-Boot启动流程(Linux内核)的分析(二转)
- U-boot启动流程(Linux内核)的分析(三转)
- U-boot启动流程(Linux内核)的分析(四转)
- U-Boot启动流程(Linux内核)的分析(一)
- Linux内核启动流程分析(二) 写的非常好
- u-boot移植(一)---u-boot启动流程分析
- (一)u-boot启动流程分析
- u-boot.lds分析(u-boot-1.3.4 nandflash启动 引导linux内核)
- u-boot.lds分析(u-boot-1.3.4 nandflash启动 引导linux内核)
- <Linux>u-boot启动Linux内核分析
- 没有代码空谈流程(U-boot如何启动内核)
- U-Boot启动内核流程
- u-boot分析(三)---boot命令实现以及内核的启动
- 用VS2010 C#写DLL文件并且调用
- Linux下的同步与异步
- Eclipse格式化代码时不换行 与 自动换行
- AbstractTableModel的一般使用
- Activity的Launch mode详解 singleTask正解
- U-Boot启动流程(Linux内核)的分析(写得好)
- VS2013注册码
- Cocos2dx 自适应屏幕大小设定
- String path 、String basePath
- quick中的一些小细节,逐步更新
- 项目管理笔记
- Source Insight3.5注册码
- 屏幕宽高的取值
- Uva 10157 - Expressions 解题报告(递推)