很多介绍u-boot的文章,对与刚学习u-boot的来说,不容易形成框架式的认知,本文目的是快速对 u-boot形成框架认识,希望能有所帮助。
使用过ARM7 或 contex-M3/4 的伙伴们,可以利用 集成开发环境(keil/IAR) 已知的知识,快速全面认识 u-boot。
个人总结:学习技术的本质是解决您内心与所学技术之间的距离感,你面对它感觉很亲切、如数家珍 = 。未毕正确请大家抛砖。
本章使用的 u-boot 样例是三星ARM型号2410,u-boot源码链接:
链接: https://pan.baidu.com/s/1ySnNLSQwEKiaT7F6y47pnQ 提取码: xn3s
下载后解压:tar -xvf iTop4412_uboot_20151119.tar.gz
进入源码 cd iTop4412_uboot 。
http://www.denx.de/wiki/U-Boot/SourceCode,该网址上有关于uboot的源码和文档。
可以使用FTP或者git来下载。
git 下载u-boot-2016.09 地址:
https://gitlab.denx.de/u-boot/u-boot/-/tree/u-boot-2016.09.y
FTP 下载u-boot-2016.09 地址:
ftp://ftp.denx.de/pub/u-boot/
个人认为 u-boot 与 ARM 裸程序应用,有三个点对应起来理解,会让用ARM/单片机伙伴们更容易理解:
(1).链接文件:
如 keil 工程配置 Linker 选项卡中,当使用 Scatter File 链接文件时,可以编辑 project.sct 文件,就是当前工程的链接配置文件。
在u-boot 中 u-boot.lds 就是链接配置文件,本例文件路径: cpu/arm920t/u-boot.lds
(2).ARM 启动文件:
如 keil 工程中的 ARM 启动 startup.s 文件。
在 u-boot 中 ARM 启动文件 start.s 文件,本例文件路径:cpu/arm920t/start.S
(3).板级硬件初始化:
ARM 裸程序初始化,程序员自己动手编写BSP程序,如:各种外设初始化。
u-boot 板级初始化 board.c 文件负责,本例此文件路径:lib_arm/board.c
通过如此对比认识u-boot的工作路线,会更容易让我们缩短与u-boot的之间的距离,就所实现功能看两者类同,接下来我们针对此三个部分内容逐步分析。
首先认识一下 u-boot 的文件结构:
$tree -L 1
├── api
├── board /* u-boot 支持的各种类型板子的配置文件(BSP), 板级设备初始化.
├ 本例程序路径:/board/samsung/smdk2410/
├ 文件夹内容如下:
├ ├── Makefile
├ ├── config.mk // 物理地址配置
├ ├── flash.c
├ ├── lowlevel_init.S
├ └── smdk2410.c // 板级初始化 BSP
├ */
├── build_uboot.sh
├── common // 实现u-boot行下支持的命令,每一个命令对应一个文件
├── config.mk
├── cpu /* u-boot 支持各种CPU,S3C2410处理器是Samsung公司
├ 基于ARM公司的ARM920T处理器核,文件路径:cpu/arm920t/
├ 此文件夹下是相同类型、不同厂家的处理器集合,
├ 本例 S3C2410 的启动文件 cpu/arm920t/start.S 。
├ 多看看文件内容编排的规则,利于对 u-boot 快速掌握 */
├── disk
├── doc // u-boot非常完善的文档
├── drivers // 是u-boot支持的各种设备的驱动程序
├── examples
├── fs // 支持的文件系统
├── include // u-boot使用的头文件,还有各种硬件平台支持的汇编文件,
├ //系统配置文件和文件系统支持的文件
├── lib_arm // bootm.c reset.c interrupts.c
├── lib_avr32
├── lib_blackfin
├── lib_generic
├── lib_i386
├── lib_m68k
├── lib_microblaze
├── lib_mips
├── lib_nios
├── lib_nios2
├── lib_ppc
├── lib_sh
├── lib_sparc
├── libfdt
├── mkbl2
├── mkconfig
├── mkuboot
├── nand_spl
├── net // 网络协议相关的代码,bootp协议、TFTP协议、NFS文件系统得实现
├── onenand_ipl
├── paddingaa
├── post
├── readme.txt
├── rules.mk
├── sdfuse // SD卡
├── sdfuse_q
├── tc4_cmm.cmm
├── tools // 生成U-boot的工具
└── uboot_readme.txt
第一部分 链接文件
cpu/arm920t/u-boot.lds 文件内容如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
/* 此处指定 cpu/arm920t/start.s 文件, 编译结果存储到0x00000000 地址 */
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 = .;
}
第二部分 ARM 启动文件
cpu/arm920t/start.S 文件内容如下:
#include
#include
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start // 向量地址
_start: b start_code /* 复位向量, u-boot 启动阶段第一个阶段 */
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 // 其它未指定向量
/*
*************************************************************************
*
* Startup Code (called from the ARM reset exception vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start // 程序入口
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
u-boot 启动第一阶段代码
/*
* the actual start code
*/
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
bl coloured_LED_init
bl red_LED_on
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
#ifdef CONFIG_S3C24X0
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# endif
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C24X0 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {
r3-r10} /* copy from source address [r0] */
stmia r1!, {
r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
/** u-boot 启动第二阶段,BSP板级初始化程序。 在 lib_arm/board.c 文件中, */
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
* CPU 内部总线和相关部件初始化.
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
/* 引用用户编写的初始化程序,文件 board/samsung/smdk2410/lowlevel_init.S */
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
u-boot 引导期间中断向量配置内容。
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {
r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE)
sub r2, r2, #(CONFIG_SYS_MALLOC_LEN)
/* set base 2 words into abort stack */
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8)
ldmia r2, {
r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {
r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {
r0 - r12} @ Calling r0-r12
add r7, sp, #S_PC
stmdb r7, {
sp, lr}^ @ Calling SP, LR
str lr, [r7, #0] @ Save calling PC
mrs r6, spsr
str r6, [r7, #4] @ Save CPSR
str r0, [r7, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {
r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
/* return & move spsr_svc into cpsr */
subs pc, lr, #4
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE)
sub r13, r13, #(CONFIG_SYS_MALLOC_LEN)
/* reserve a couple spots in abort stack */
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8)
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif
第三部分 板级硬件初始化
u-boot 启动第二阶段,BSP板级初始化程序。 在 lib_arm/board.c 文件中,内容如下:
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
int mmc_exist = 0;
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
// gd->flags |= GD_FLG_RELOC;
monitor_flash_len = _bss_start - _armboot_start;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
CONFIG_SYS_MALLOC_LEN);
#ifndef CONFIG_SYS_NO_FLASH
/* configure available FLASH banks */
display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
/* board init may have inited fb_base */
if (!gd->fb_base) {
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
lcd_setmem (addr);
gd->fb_base = addr;
}
#endif /* CONFIG_LCD */
#if defined(CONFIG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
#if defined(CONFIG_CMD_ONENAND)
onenand_init();
#endif
#ifdef CONFIG_GENERIC_MMC
puts ("MMC: ");
mmc_exist = mmc_initialize (gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* initialize environment */
env_relocate ();
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
#ifdef CONFIG_SERIAL_MULTI
serial_initialize();
#endif
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
stdio_init (); /* get the devices list going. */
jumptable_init ();
#if defined(CONFIG_API)
/* Initialize API */
api_init ();
#endif
console_init_r (); /* fully init console as a device */
#if defined(CONFIG_ARCH_MISC_INIT)
/* miscellaneous arch dependent initialisations */
arch_misc_init ();
#endif
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions */
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC
/* XXX: this needs to be moved to board init */
extern void davinci_eth_set_mac_addr (const u_int8_t *addr);
if (getenv ("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
davinci_eth_set_mac_addr(enetaddr);
}
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
/* XXX: this needs to be moved to board init */
if (getenv ("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if defined(CONFIG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#ifdef CONFIG_BITBANGMII
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif
/*
char tmp_cmd[100];
sprintf(tmp_cmd, "emmc open 0");
run_command(tmp_cmd, 0);
*/
/* main_loop() can return to retry autoboot, if so just run it again. */
#ifdef CONFIG_RECOVERY //mj for factory reset
recovery_preboot();
#endif
for (;;) {
/** 调用的 main_loop() 函数在文件 common/main.c 中。 */
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}
通过此三点能够理顺u-boot的流程、以及 u-boot 如何编译链接起来。
至此 u-boot 启动已经完成了,但是目前我们还没有看到 linux kernal 在什么地方启动的。=
距离成功看到 linux 内核启动还有两步之遥,加油,伙伴们。