U-Boot全称是Universal Bootloader,也是一款开源项目。从名字就可以看出该项目雄心勃勃,希望这个bootloader能够适用于所有的主流CPU。下载地址:ftp://ftp.denx.de/pub/u-boot/
1. 目录结构
/arch Architecture specific files
/arm Files generic to ARM architecture
/cpu CPU specific files
/arm720t Files specific to ARM 720 CPUs
/arm920t Files specific to ARM 920 CPUs
/at91 Files specific to Atmel AT91RM9200 CPU
/imx Files specific to Freescale MC9328 i.MX CPUs
/s3c24x0 Files specific to Samsung S3C24X0 CPUs
/arm925t Files specific to ARM 925 CPUs
/arm926ejs Files specific to ARM 926 CPUs
/arm1136 Files specific to ARM 1136 CPUs
/ixp Files specific to Intel XScale IXP CPUs
/pxa Files specific to Intel XScale PXA CPUs
/s3c44b0 Files specific to Samsung S3C44B0 CPUs
/sa1100 Files specific to Intel StrongARM SA1100 CPUs
/lib Architecture specific library files
/avr32 Files generic to AVR32 architecture
/cpu CPU specific files
/lib Architecture specific library files
/blackfin Files generic to Analog Devices Blackfin architecture
/cpu CPU specific files
/lib Architecture specific library files
/x86 Files generic to x86 architecture
/cpu CPU specific files
/lib Architecture specific library files
/m68k Files generic to m68k architecture
/cpu CPU specific files
/mcf52x2 Files specific to Freescale ColdFire MCF52x2 CPUs
/mcf5227x Files specific to Freescale ColdFire MCF5227x CPUs
/mcf532x Files specific to Freescale ColdFire MCF5329 CPUs
/mcf5445x Files specific to Freescale ColdFire MCF5445x CPUs
/mcf547x_8x Files specific to Freescale ColdFire MCF547x_8x CPUs
/lib Architecture specific library files
/microblaze Files generic to microblaze architecture
/cpu CPU specific files
/lib Architecture specific library files
/mips Files generic to MIPS architecture
/cpu CPU specific files
/mips32 Files specific to MIPS32 CPUs
/xburst Files specific to Ingenic XBurst CPUs
/lib Architecture specific library files
/nds32 Files generic to NDS32 architecture
/cpu CPU specific files
/n1213 Files specific to Andes Technology N1213 CPUs
/lib Architecture specific library files
/nios2 Files generic to Altera NIOS2 architecture
/cpu CPU specific files
/lib Architecture specific library files
/powerpc Files generic to PowerPC architecture
/cpu CPU specific files
/74xx_7xx Files specific to Freescale MPC74xx and 7xx CPUs
/mpc5xx Files specific to Freescale MPC5xx CPUs
/mpc5xxx Files specific to Freescale MPC5xxx CPUs
/mpc8xx Files specific to Freescale MPC8xx CPUs
/mpc8220 Files specific to Freescale MPC8220 CPUs
/mpc824x Files specific to Freescale MPC824x CPUs
/mpc8260 Files specific to Freescale MPC8260 CPUs
/mpc85xx Files specific to Freescale MPC85xx CPUs
/ppc4xx Files specific to AMCC PowerPC 4xx CPUs
/lib Architecture specific library files
/sh Files generic to SH architecture
/cpu CPU specific files
/sh2 Files specific to sh2 CPUs
/sh3 Files specific to sh3 CPUs
/sh4 Files specific to sh4 CPUs
/lib Architecture specific library files
/sparc Files generic to SPARC architecture
/cpu CPU specific files
/leon2 Files specific to Gaisler LEON2 SPARC CPU
/leon3 Files specific to Gaisler LEON3 SPARC CPU
/lib Architecture specific library files
/api Machine/arch independent API for external apps
/board Board dependent files
/common Misc architecture independent functions
/disk Code for disk drive partition handling
/doc Documentation (don't expect too much)
/drivers Commonly used device drivers
/examples Example code for standalone applications, etc.
/fs Filesystem code (cramfs, ext2, jffs2, etc.)
/include Header Files
/lib Files generic to all architectures
/libfdt Library files to support flattened device trees
/lzma Library files to support LZMA decompression
/lzo Library files to support LZO decompression
/net Networking code
/post Power On Self Test
/rtc Real Time Clock drivers
/tools Tools to build S-Record or U-Boot images, etc.
2. 启动过程
作为一个Bootloader,无论兼容了多少种硬件,增加了多少种实用的功能。最基本的启动流程仍然会是那些(暂不考虑CP的启动),硬件设备初始化,内存初始化,加载stage2的代码等等等等。
U-Boot的启动也分成stage1和stage2,stage1使用汇编语言编写,stage2使用C语言编写。
2.1 stage1
在arch\arm\cpu\arm920t\start.S文件中
.globl _start
_start: b start_code
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
1. CPU进入SVC模式
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
2. 关闭看门狗
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
3. 关中断
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
4. 初始化系统时钟
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
5. cpu_init_crit函数
主要完成了两个工作:
首先使ICache and Dcache,TLBs中早期内容失效,再设置p15 control register c1,关闭MMU,Dcache,但是打开了Icache和Fault checking,(要求mmu和Dcache是必须要关闭的,而Icache可以打开可以关闭);
其次建立对SDRAM的访问时序。
6. 建立堆栈,调用主板初始化
7. 复制代码,清除bss段,跳转运行代码
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
ldr r0, _nand_boot_ofs
mov pc, r0
_nand_boot_ofs:
.word nand_boot
#else
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs:
.word board_init_r - _start
#endif
2.2 stage2
第二阶段完成本阶段使用的硬件设备、指定启动参数地址,开启中断,运行命令
在Board.c文件中的board_init_r函数,根据用户的配置,进行各种初始化之后,打开中断,运行
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
}
这个就是在标准输入设备中接受命令,然后分析、查找和执行。
去掉所有无关紧要的宏和代码,main_loop()函数如下:
void main_loop()
{
static char lastcommand[CFG_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
char *s;
int bootdelay;
s = getenv ("bootdelay"); //自动启动内核等待延时
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
s = getenv ("bootcmd"); //取得环境中设置的启动命令行
if (bootdelay != -1 && s && !abortboot(bootdelay)){
run_command_list(s, -1, 0);
}
for (;;) {
len = readline(CFG_PROMPT); //读取键入的命令行到console_buffer
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer); //拷贝命令行到lastcommand.
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
if (len == -1)
puts ("\n");
else
rc = run_command (lastcommand, flag); //执行这个命令行。
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
在上面的main_loop函数中,通常在开发完成的阶段都会设置一个bootcmd的环境变量,然后将延时bootdelay设置成0,这样当u-boot跑到这里的时候就不会因为用户按下了任意键就进入了命令行模式,可以直接运行bootcmd的命令来直接加载kernel的Image然后移交控制权。如果进入了命令行模式,我们也可以手动输入命令来启动系统,输入的命令也是基本和bootcmd一样。
3. U-Boot和内核的关系
U-Boot作为bootloader,具备多种引导内核启动的方式.常用的go和bootm命令直接引导内核Image启动.
go命令的解析函数是do_go()函数.
bootm命令的解析函数是do_bootm()函数. 这个函数专门用来引导各种操作系统Image,引导Linux时调用do_bootm_linux()函数
static boot_os_fn *boot_os[] = {
#ifdef CONFIG_BOOTM_LINUX
[IH_OS_LINUX] = do_bootm_linux,
#endif
#ifdef CONFIG_BOOTM_NETBSD
[IH_OS_NETBSD] = do_bootm_netbsd,
#endif
#ifdef CONFIG_LYNXKDI
[IH_OS_LYNXOS] = do_bootm_lynxkdi,
#endif
#ifdef CONFIG_BOOTM_RTEMS
[IH_OS_RTEMS] = do_bootm_rtems,
#endif
#if defined(CONFIG_BOOTM_OSE)
[IH_OS_OSE] = do_bootm_ose,
#endif
#if defined(CONFIG_CMD_ELF)
[IH_OS_VXWORKS] = do_bootm_vxworks,
[IH_OS_QNX] = do_bootm_qnxelf,
#endif
#ifdef CONFIG_INTEGRITY
[IH_OS_INTEGRITY] = do_bootm_integrity,
#endif
};
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
/* No need for those on ARM */
if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
return -1;
if (flag & BOOTM_STATE_OS_PREP) {
boot_prep_linux(images);
return 0;
}
if (flag & BOOTM_STATE_OS_GO) {
boot_jump_linux(images);
return 0;
}
boot_prep_linux(images);
boot_jump_linux(images);
return 0;
}
do_bootm_linux函数最后调用了boot_jump_linux,这个函数最后一句正是kernel_entry(0, machid, r2);
4. 其他
U-Boot为了兼容各种CPU,各种操作系统,复杂度确实是比较高的,我也仅仅理解一个皮毛。另外,这里仅仅考虑系统只有AP,但现在越来越多的系统除了AP之外还有CP了,这样的硬件结构,bootloader中也是需要变化的。
1. 目录结构
/arch Architecture specific files
/arm Files generic to ARM architecture
/cpu CPU specific files
/arm720t Files specific to ARM 720 CPUs
/arm920t Files specific to ARM 920 CPUs
/at91 Files specific to Atmel AT91RM9200 CPU
/imx Files specific to Freescale MC9328 i.MX CPUs
/s3c24x0 Files specific to Samsung S3C24X0 CPUs
/arm925t Files specific to ARM 925 CPUs
/arm926ejs Files specific to ARM 926 CPUs
/arm1136 Files specific to ARM 1136 CPUs
/ixp Files specific to Intel XScale IXP CPUs
/pxa Files specific to Intel XScale PXA CPUs
/s3c44b0 Files specific to Samsung S3C44B0 CPUs
/sa1100 Files specific to Intel StrongARM SA1100 CPUs
/lib Architecture specific library files
/avr32 Files generic to AVR32 architecture
/cpu CPU specific files
/lib Architecture specific library files
/blackfin Files generic to Analog Devices Blackfin architecture
/cpu CPU specific files
/lib Architecture specific library files
/x86 Files generic to x86 architecture
/cpu CPU specific files
/lib Architecture specific library files
/m68k Files generic to m68k architecture
/cpu CPU specific files
/mcf52x2 Files specific to Freescale ColdFire MCF52x2 CPUs
/mcf5227x Files specific to Freescale ColdFire MCF5227x CPUs
/mcf532x Files specific to Freescale ColdFire MCF5329 CPUs
/mcf5445x Files specific to Freescale ColdFire MCF5445x CPUs
/mcf547x_8x Files specific to Freescale ColdFire MCF547x_8x CPUs
/lib Architecture specific library files
/microblaze Files generic to microblaze architecture
/cpu CPU specific files
/lib Architecture specific library files
/mips Files generic to MIPS architecture
/cpu CPU specific files
/mips32 Files specific to MIPS32 CPUs
/xburst Files specific to Ingenic XBurst CPUs
/lib Architecture specific library files
/nds32 Files generic to NDS32 architecture
/cpu CPU specific files
/n1213 Files specific to Andes Technology N1213 CPUs
/lib Architecture specific library files
/nios2 Files generic to Altera NIOS2 architecture
/cpu CPU specific files
/lib Architecture specific library files
/powerpc Files generic to PowerPC architecture
/cpu CPU specific files
/74xx_7xx Files specific to Freescale MPC74xx and 7xx CPUs
/mpc5xx Files specific to Freescale MPC5xx CPUs
/mpc5xxx Files specific to Freescale MPC5xxx CPUs
/mpc8xx Files specific to Freescale MPC8xx CPUs
/mpc8220 Files specific to Freescale MPC8220 CPUs
/mpc824x Files specific to Freescale MPC824x CPUs
/mpc8260 Files specific to Freescale MPC8260 CPUs
/mpc85xx Files specific to Freescale MPC85xx CPUs
/ppc4xx Files specific to AMCC PowerPC 4xx CPUs
/lib Architecture specific library files
/sh Files generic to SH architecture
/cpu CPU specific files
/sh2 Files specific to sh2 CPUs
/sh3 Files specific to sh3 CPUs
/sh4 Files specific to sh4 CPUs
/lib Architecture specific library files
/sparc Files generic to SPARC architecture
/cpu CPU specific files
/leon2 Files specific to Gaisler LEON2 SPARC CPU
/leon3 Files specific to Gaisler LEON3 SPARC CPU
/lib Architecture specific library files
/api Machine/arch independent API for external apps
/board Board dependent files
/common Misc architecture independent functions
/disk Code for disk drive partition handling
/doc Documentation (don't expect too much)
/drivers Commonly used device drivers
/examples Example code for standalone applications, etc.
/fs Filesystem code (cramfs, ext2, jffs2, etc.)
/include Header Files
/lib Files generic to all architectures
/libfdt Library files to support flattened device trees
/lzma Library files to support LZMA decompression
/lzo Library files to support LZO decompression
/net Networking code
/post Power On Self Test
/rtc Real Time Clock drivers
/tools Tools to build S-Record or U-Boot images, etc.
2. 启动过程
作为一个Bootloader,无论兼容了多少种硬件,增加了多少种实用的功能。最基本的启动流程仍然会是那些(暂不考虑CP的启动),硬件设备初始化,内存初始化,加载stage2的代码等等等等。
U-Boot的启动也分成stage1和stage2,stage1使用汇编语言编写,stage2使用C语言编写。
2.1 stage1
在arch\arm\cpu\arm920t\start.S文件中
.globl _start
_start: b start_code
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
1. CPU进入SVC模式
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
2. 关闭看门狗
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
3. 关中断
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
4. 初始化系统时钟
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
5. cpu_init_crit函数
主要完成了两个工作:
首先使ICache and Dcache,TLBs中早期内容失效,再设置p15 control register c1,关闭MMU,Dcache,但是打开了Icache和Fault checking,(要求mmu和Dcache是必须要关闭的,而Icache可以打开可以关闭);
其次建立对SDRAM的访问时序。
6. 建立堆栈,调用主板初始化
7. 复制代码,清除bss段,跳转运行代码
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
ldr r0, _nand_boot_ofs
mov pc, r0
_nand_boot_ofs:
.word nand_boot
#else
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs:
.word board_init_r - _start
#endif
2.2 stage2
第二阶段完成本阶段使用的硬件设备、指定启动参数地址,开启中断,运行命令
在Board.c文件中的board_init_r函数,根据用户的配置,进行各种初始化之后,打开中断,运行
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
}
这个就是在标准输入设备中接受命令,然后分析、查找和执行。
去掉所有无关紧要的宏和代码,main_loop()函数如下:
void main_loop()
{
static char lastcommand[CFG_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
char *s;
int bootdelay;
s = getenv ("bootdelay"); //自动启动内核等待延时
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
s = getenv ("bootcmd"); //取得环境中设置的启动命令行
if (bootdelay != -1 && s && !abortboot(bootdelay)){
run_command_list(s, -1, 0);
}
for (;;) {
len = readline(CFG_PROMPT); //读取键入的命令行到console_buffer
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer); //拷贝命令行到lastcommand.
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
if (len == -1)
puts ("\n");
else
rc = run_command (lastcommand, flag); //执行这个命令行。
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
在上面的main_loop函数中,通常在开发完成的阶段都会设置一个bootcmd的环境变量,然后将延时bootdelay设置成0,这样当u-boot跑到这里的时候就不会因为用户按下了任意键就进入了命令行模式,可以直接运行bootcmd的命令来直接加载kernel的Image然后移交控制权。如果进入了命令行模式,我们也可以手动输入命令来启动系统,输入的命令也是基本和bootcmd一样。
3. U-Boot和内核的关系
U-Boot作为bootloader,具备多种引导内核启动的方式.常用的go和bootm命令直接引导内核Image启动.
go命令的解析函数是do_go()函数.
bootm命令的解析函数是do_bootm()函数. 这个函数专门用来引导各种操作系统Image,引导Linux时调用do_bootm_linux()函数
static boot_os_fn *boot_os[] = {
#ifdef CONFIG_BOOTM_LINUX
[IH_OS_LINUX] = do_bootm_linux,
#endif
#ifdef CONFIG_BOOTM_NETBSD
[IH_OS_NETBSD] = do_bootm_netbsd,
#endif
#ifdef CONFIG_LYNXKDI
[IH_OS_LYNXOS] = do_bootm_lynxkdi,
#endif
#ifdef CONFIG_BOOTM_RTEMS
[IH_OS_RTEMS] = do_bootm_rtems,
#endif
#if defined(CONFIG_BOOTM_OSE)
[IH_OS_OSE] = do_bootm_ose,
#endif
#if defined(CONFIG_CMD_ELF)
[IH_OS_VXWORKS] = do_bootm_vxworks,
[IH_OS_QNX] = do_bootm_qnxelf,
#endif
#ifdef CONFIG_INTEGRITY
[IH_OS_INTEGRITY] = do_bootm_integrity,
#endif
};
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
/* No need for those on ARM */
if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
return -1;
if (flag & BOOTM_STATE_OS_PREP) {
boot_prep_linux(images);
return 0;
}
if (flag & BOOTM_STATE_OS_GO) {
boot_jump_linux(images);
return 0;
}
boot_prep_linux(images);
boot_jump_linux(images);
return 0;
}
do_bootm_linux函数最后调用了boot_jump_linux,这个函数最后一句正是kernel_entry(0, machid, r2);
4. 其他
U-Boot为了兼容各种CPU,各种操作系统,复杂度确实是比较高的,我也仅仅理解一个皮毛。另外,这里仅仅考虑系统只有AP,但现在越来越多的系统除了AP之外还有CP了,这样的硬件结构,bootloader中也是需要变化的。