转载:http://blog.chinaunix.net/uid-28236237-id-3865045.html

u-boot第一阶段分析(一)

u-boot 第一阶段分析(二)

u-boot 第二阶段分析

U-boot第一阶段分析

u-boot是在单板刚启动的时候执行的一段程序。其作用是将内核从flash、硬盘等介质上拷贝到内存中,并传递给内核一些启动参数并跳转到内核去执行。

由于u-boot的代码非常的依赖于不 同的体系结构。对u-boot的分析也只能提炼出不同体系结构的共性,做相应的总结。总的说来,u-boot所做到工作分为两个阶段。第一个阶段是将u- boot第二阶段的代码拷贝到ram空间,跳转到第二阶段代码执行。第二阶段是将内核代码拷贝到ram并跳转到内核去执行。

具体执行过程中做到操作如下

· 第一阶段的功能

1.  硬件设备初始化

2. 加载U-Boot第二阶段代码到RAM空间

3. 设置好栈

4. 跳转到第二阶段代码入口

· 第二阶段的功能

5. 初始化本阶段使用的硬件设备

6. 检测系统内存映射

7. 将内核从Flash读取到RAM中

8. 为内核设置启动参数

9. 调用内核

下面以ARM9 u-boot执行过程做一个分析,之后如果有时间再继续分析mips架构的u-boot执行过程,由于u-boot中涉及到许多汇编和体系结构的知识,对我们学习不同的体系结构有很大的帮助。如果相关知识不熟悉,阅读代码的时候会遇到很多疑惑。

第一阶段--硬件设备初始

.globl _start

_start:b       reset

ldrpc, _undefined_instruction

ldrpc, _software_interrupt

ldrpc, _prefetch_abort

ldrpc, _data_abort

ldrpc, _not_used

ldrpc, _irq

ldrpc, _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

地址

异常

进入模式

描述

0x00000000

复位

管理模式

复位电平效时,产生复位异常,程序跳转到复位处理程序处执行

0x00000004

未定义指令

未定义模式

遇到不能处理的指令时,产生未定义指令异常

0x00000008

软件中断

管理模式

执行SWI指令产生,用于用户模式下的程序调用特权操作指令

0x0000000c

预存指令

中止模式

处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常

0x00000010

数据操作

中止模式

处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常

0x00000014

未使用

未使用

未使用

0x00000018

IRQ

IRQ

外部中断请求效,且CPSR中的I位为0时,产生IRQ异常

0x0000001c

FIQ

FIQ

快速中断请求引脚效,且CPSR中的F位为0时,产生FIQ异常

在之前的文章中曾经提及过arm处理器有8中运行方式,上电后,cpu首先跳转到0x00000000 地址开始执行

reset:

/*

* set the cpu to SVC32 mode

*/

mrsr0,cpsr

bicr0,r0,#0x1f

orrr0,r0,#0xd3

msrcpsr,r0

mrs r0,cpsr     //将当前cpsr的状态为保存到r0中。

bic r0,r0,#0x1f //bic,位清零指令。0x1f=00011111,相当于清除低5位。

刚好是模式位。

orr r0,r0,#0xd3 //或指令。置模式位。0xd3=11010011以及设置5,6,7位的

状态位。禁止FIQ,IRQ,处于arm状态。低5位为10011,则对应超级用户态。msr cpsr,r0     //在将r0中的值赋给状态寄存器cps

/* turn off the watchdog */

#if defined(CONFIG_S3C2400)

# define pWTCON0x15300000

# define INTMSK0x14400008/* Interupt-Controller base addresses */

# define CLKDIVN0x14800014/* clock divisor register */

#elif defined(CONFIG_S3C2410)

# define pWTCON0x53000000

# define INTMOD     0X4A000004

# define INTMSK0x4A000008/* Interupt-Controller base addresses */

# define INTSUBMSK0x4A00001C

# define CLKDIVN0x4C000014/* clock divisor register */

#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

ldr     r0, =pWTCON

mov     r1, #0x0

str     r1, [r0]

关闭看门狗定时器

/*

* mask all IRQs by setting all bits in the INTMR - default

*/

movr1, #0xffffffff

ldrr0, =INTMSK

strr1, [r0]

# if defined(CONFIG_S3C2410)

ldrr1, =0x3ff

ldrr0, =INTSUBMSK

strr1, [r0]

# endif

关闭禁止各个中断

#ifdef CONFIG_S3C2440

#define MPLLCON 0x4c000004

#define UPLLCON 0x4c000008

ldr r0, =CLKDIVN

mov r1, #5

str r1, [r0]

ldr r0, =MPLLCON

ldr r1, =0x7f021

str r1, [r0]

ldr r0, =UPLLCON

ldr r1, =0x38022

str r1, [r0]

#else

/* FCLK:HCLK:PCLK = 1:2:4 */

/* default FCLK is 120 MHz ! */

ldrr0, =CLKDIVN

movr1, #3

strr1, [r0]

#endif

设置cpu各个模块的主频大小

#endif/* CONFIG_S3C2400 || CONFIG_S3C2410 */

/*

* we do sys-critical inits only at reboot,

* not when booting from ram!

*/

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

adrr0, _start/* r0 <- current position of code   */

ldrr1, _TEXT_BASE/* test if we run from flash or RAM */

cmp     r0, r1                  /* don't reloc during debug         */

blnecpu_init_crit

#endif

如果cpu还是在flash或者step stone的内存中运行的话,跳转到cpu_init_crit运行

cpu_init_crit清空I cache和D cache并清空掉TLB表项,暂时关闭MMU,跳转到lowlevel_init对ram控制寄存器进行初始化

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

cpu_init_crit:

/*

* flush v4 I/D caches

*/

movr0, #0

mcrp15, 0, r0, c7, c7, 0/* flush v3/v4 cache */

mcrp15, 0, r0, c8, c7, 0/* flush v4 TLB */

/*

* disable MMU stuff and caches

*/

mrcp15, 0, r0, c1, c0, 0

bicr0, r0, #0x00002300@ clear bits 13, 9:8 (--V- --RS)

bicr0, r0, #0x00000087@ clear bits 7, 2:0 (B--- -CAM)

orrr0, r0, #0x00000002@ set bit 2 (A) Align

orrr0, r0, #0x00001000@ set bit 12 (I) I-Cache

mcrp15, 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.

*/

movip, lr

bllowlevel_init

movlr, ip

movpc, lr

#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

设置堆栈指针

stack_setup:

ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot   */

subr0, r0, #CFG_MALLOC_LEN

u-boot 第一阶段分析(二)

设置堆栈指针

stack_setup:

ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot   */

subr0, r0, #CFG_MALLOC_LEN/* malloc area                      */

subr0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */

#ifdef CONFIG_USE_IRQ

subr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

subsp, r0, #12/* leave 3 words for abort-stack    */

设置cpu的时钟

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl clock_init

#endif

#define S3C2410_MPLL_200MHZ     ((0x5c<<12)|(0x04<<4)|(0x00))

#define S3C2410_UPLL_48MHZ      ((0x28<<12)|(0x01<<4)|(0x02))

#define S3C2410_CLKDIV          0x03    /* FCLK:HCLK:PCLK = 1:2:4 */

void clock_init(void)

{

S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000;

/* support both of S3C2410 and S3C2440, by www.arm9.net */

if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))

{

/* FCLK:HCLK:PCLK = 1:2:4 */

clk_power->CLKDIVN = S3C2410_CLKDIV;

/* change to asynchronous bus mod */

__asm__(    "mrc    p15, 0, r1, c1, c0, 0\n"    /* read ctrl register   */

"orr    r1, r1, #0xc0000000\n"      /* Asynchronous         */

"mcr    p15, 0, r1, c1, c0, 0\n"    /* write ctrl register  */

:::"r1"

);

/* to reduce PLL lock time, adjust the LOCKTIME register */

clk_power->LOCKTIME = 0xFFFFFFFF;

/* configure UPLL */

clk_power->UPLLCON = S3C2410_UPLL_48MHZ;

/* some delay between MPLL and UPLL */

delay (4000);

/* configure MPLL */

clk_power->MPLLCON = S3C2410_MPLL_200MHZ;

/* some delay between MPLL and UPLL */

delay (8000);

}

else

{

/* FCLK:HCLK:PCLK = 1:4:8 */

clk_power->CLKDIVN = S3C2440_CLKDIV;

/* change to asynchronous bus mod */

__asm__(    "mrc    p15, 0, r1, c1, c0, 0\n"    /* read ctrl register   */

"orr    r1, r1, #0xc0000000\n"      /* Asynchronous         */

"mcr    p15, 0, r1, c1, c0, 0\n"    /* write ctrl register  */

:::"r1"

);

/* to reduce PLL lock time, adjust the LOCKTIME register */

clk_power->LOCKTIME = 0xFFFFFFFF;

/* configure UPLL */

clk_power->UPLLCON = S3C2440_UPLL_48MHZ;

/* some delay between MPLL and UPLL */

delay (4000);

/* configure MPLL */

clk_power->MPLLCON = S3C2440_MPLL_400MHZ;

/* some delay between MPLL and UPLL */

delay (8000);

}

}

硬件初始化完成,堆栈设置完成,之后将u-boot第二阶段的代码拷贝到内存

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:/* relocate U-Boot to RAM    */

adrr0, _start/* r0 <- current position of code   */

ldrr1, _TEXT_BASE/* test if we run from flash or RAM */

cmp     r0, r1                  /* don't reloc during debug         */

beq     clear_bss

ldrr2, _armboot_start

ldrr3, _bss_start

subr2, r3, r2/* r2 <- size of armboot            */

#if 1

bl  CopyCode2Ram/* r0: source, r1: dest, r2: size */

#else

addr2, r0, r2/* r2 <- source end address         */

copy_loop:

ldmiar0!, {r3-r10}/* copy from source address [r0]    */

stmiar1!, {r3-r10}/* copy to   target address [r1]    */

cmpr0, r2/* until source end addreee [r2]    */

blecopy_loop

#endif

#endif/* CONFIG_SKIP_RELOCATE_UBOOT */

上面的代码过程是,首先看uboot是不是已经在内存中运行了,如果是这样直接将bss区间置0就可以了,如果不是运行在内存中设置

r0 为flash起始位置

r1 为希望将u-boot拷贝到的ram位置

r2 u-boot代码段的长度

int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)

{

unsigned int *pdwDest;

unsigned int *pdwSrc;

int i;

if (bBootFrmNORFlash())

{

pdwDest = (unsigned int *)buf;

pdwSrc  = (unsigned int *)start_addr;

/* 从 NOR Flash启动 */

for (i = 0; i < size / 4; i++)

{

pdwDest[i] = pdwSrc[i];

}

return 0;

}

else

{

/* 初始化NAND Flash */

nand_init_ll();

/* 从 NAND Flash启动 */

nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));

return 0;

}

}

将bss区域清0

clear_bss:

ldrr0, _bss_start/* find start of bss segment        */

ldrr1, _bss_end/* stop here                        */

mov r2, #0x00000000/* clear                            */

clbss_l:strr2, [r0]/* clear loop...                    */

addr0, r0, #4

cmpr0, r1

bleclbss_l

跳转到内存中执行u-boot的第二阶段过程

ldrpc, _start_armboot

U-boot第二阶段分析

一、U-boot第二阶段概述

上面有两篇文关于u-boot第一阶段的介绍,这两篇文章是从网上找到的,由于分析的很详细,看完这后觉得对这两篇文章u-boot第一阶段的介绍已经比较完美了,所以分享出来。从这篇文章开始分析u-boot的第二阶段。

如果你只把u-boot理解成引导kernel的一段代码的话,u-boot完全没有必要设计成现在这样的一种软件框架,直接写几个文件就能完成kernel的引导和启动。U-boot的功能很大一部还有起到调试的作用,也就是u-boot命令行的部分。所以它才有了现在这种相对比较复杂的框架。U-boot的第二阶段可以认为是初始化u-boot的软件框架,并实现引导kernel启动和命令行调试环境的过程

U-boot第二阶段总结来说主要可以概括为下面几点

1、硬件的初始化

2、运行环境的初始化

3、载入内核并启动内核

4、运行u-boot调试的命令机制

其中1和2阶段可以看成是u-boot软件框架的初始化过程,只不过包括了硬件设备的初始化和软件相关结构体的初始化。而3就是引导kernel启动过程,而4是调试的命令行环境运行过程。

本文首先沿着第一阶段调用_start_armboot函数开始第二阶段的分析。首先完成一个概述,之后会对一些关键的问题做专题分析。

二、硬件和软件框架的初始化

首先初始化一个全局性的数据结构gd_t

/* Pointer is writable since we allocated a register for it */ 
gd = (gd_t *)(_armboot_start - CFG_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)); 
monitor_flash_len = _bss_start - _armboot_start;

我们看到这里u-boot动态的分配了gd_t和bd_t这两个数据结构,只是比较疑惑的是这两个数据结构完全可以定义成全局的数据,何必这样动态的分配呢?由于这个时候完全没有malloc这样的环境,u-boot只好跑马圈地,很野蛮的在_armboot_start前面画出一块位置作为上面结构体区域。这里我们就两个疑问?1、u-boot到底被第一阶段的代码拷贝到什么地方去执行了?第二个疑问就是u-boot在内存区域中相关代码和数据区域是怎么分布的?

Question1:

Answer:

查看一下下面的配置文件就可以知道,mini2440开发板sdram的地址范围是3000’0000到3400’0000,一共是64M。U-boot首先把自己载入了最后的512K的地址空间中,坐在墙角的位置。

u-boot-1.1.6\board\open24x0\config.mk

#

# SMDK2410 has 1 bank of 64 MB DRAM

#

# 3000'0000 to 3400'0000

#

# Linux-Kernel is expected to be at 3000'8000, entry 3000'8000

# optionally with a ramdisk at 3080'0000

#

# we load ourself to 33F8'0000

#

# download area is 3300'0000

#

TEXT_BASE = 0x33F80000

Question2:

Answer:

U-boot被完全载入到内存后,各部分在内存中的位置如下图所示:

需要注意的是图中TEXT_BASE的地址在整个SDRAM空间的最后512K位置了。

初始化函数调用

接着u-boot对单板的硬件和软件框架做相应的初始化

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) 

    if ((*init_fnc_ptr)() != 0) 
    { 
        hang (); 
    } 
}

U-boot软件框架定义了这样的一个初始化表,不同的单板具体实现下面的函数进行相应的初始化动作

init_fnc_t *init_sequence[] = 

    cpu_init,        /* basic cpu dependent setup */ 
    board_init,        /* basic board dependent setup */ 
    interrupt_init,        /* set up exceptions */ 
    env_init,        /* initialize environment */ 
    init_baudrate,        /* initialze baudrate settings */ 
    serial_init,        /* serial communications setup */ 
    console_init_f,        /* stage 1 init of console */ 
    display_banner,        /* say that we are here */ 
#if defined(CONFIG_DISPLAY_CPUINFO) 
    print_cpuinfo,        /* display cpu info (and speed) */ 
#endif 
#if defined(CONFIG_DISPLAY_BOARDINFO) 
    checkboard,        /* display board info */ 
#endif 
    dram_init,        /* configure available RAM banks */ 
    display_dram_config, 
    NULL, 
};

后续的文章会将上面的初始化函数做逐一的分析,但是本篇本意是想对第二阶段的处理过程做一个提纲挈领的描述,所以暂时举一个例子分析如下:

int cpu_init (void) 

    /* 
     * setup up stacks if necessary 
     */ 
#ifdef CONFIG_USE_IRQ 
    IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; 
    FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; 
    FREE_RAM_END = FIQ_STACK_START - CONFIG_STACKSIZE_FIQ - CONFIG_STACKSIZE; 
    FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; 
#else 
    FREE_RAM_END = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4 - CONFIG_STACKSIZE; 
    FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; 
#endif 
    return 0; 
}

Cpu_init函数基本上就是初始化了一下栈区空间和软件中断的栈空间

在完成了单板相应硬件初始化之后,u-boot在后继的代码里可以自由的使用u-boot框架提供的一些服务接口,比如从串口获取输入,从nandflash读写数据,网络接口通信等等。然后u-boot进入了一个大的循环中

for (;;) 

    main_loop (); 
}

在main_loop函数中,有两条比较关键的路径需要分析,一条路径就是引导内核启动,另外一条路径就是用户通过串口输入中断了u-boot启动内核过程而进入了u-boot的命令行调试运行环境中。

三、u-boot引导内核启动的过程。

首先我们来分析一下u-boot引导内核启动的过程

1、首先获取等待用户串口按键中断引导kernel的时间,一般为3秒

s = getenv ("bootdelay"); 
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

2、获取启动内核的命令,这个靠编译u-boot之前开发者的配置

s = getenv ("bootcmd");

而这条启动命令在mini2440的配置就是如下,其实是两条命令组成的

#define CONFIG_BOOTCOMMAND"nand read.jffs2 0x32000000 kernel; bootm 0x32000000"

3、调用abortboot函数检查在等待的3s中内是不是从串口有用户输入了空格,如果有就说明用户想进入命令行调试模式,如果没有就说明可以开始运行启动内核的命令。

if (bootdelay >= 0 && s && !abortboot (bootdelay)) 

    { 
        printf("Booting Linux ...\n"); 
        run_command (s, 0); 
    } 
}

4、abortboot中比较关键的代码就是在一个循环中不断检测是不是有用户的串口输入,使用了一个udelay函数,10000us等于10ms,循环100次就是1s

while ((bootdelay > 0) && (!abort)) 

    int i; 
    --bootdelay; 
    /* delay 100 * 10ms */ 
    for (i = 0; !abort && i < 100; ++i) 
    { 
        if (tstc())      /* we got a key press    */ 
        { 
# ifdef CONFIG_MENUKEY 
            abort  = 1;    /* don't auto boot    */ 
            bootdelay = 0;    /* no more delay    */ 
            menukey = getc(); 
            break; 
# else 
            /* consume input    */ 
            if (getc() == ' ') 
            { 
                abort  = 1; /* don't auto boot    */ 
                bootdelay = 0;    /* no more delay    */ 
                break; 
            } 
# endif 
        } 
        udelay (10000); 
    } 
    printf ("\b\b\b%2d ", bootdelay); 
}

5、用户不中断kernel引导过程的话,最后会调用到run_command函数,这个函数不需要怎么分析,它的作用就就是解析输入的命令,然后根据命令的名字查找相应的命令执行函数进行调用,上面我们看到,启动的时候需要调用两个命令,一个是nand命令一个bootm命令。

6、Nand命令完成将kernel载入到内存中指定的位置,而bootm则是最后调用固定位置的一个kernel启动函数进入kernel的代码中开始执行。

7、执行nand命令,u-boot在其软件框架中定义了一个命令nand如下所示,执行这个命令最后会调用到函数do_nand

U_BOOT_CMD(nand, 5, 1, do_nand, 
           "nand    - NAND sub-system\n", 
           "info                  - show available NAND devices\n" 
           "nand device [dev]     - show or set current device\n" 
           "nand read[.jffs2]     - addr off|partition size\n" 
           "nand write[.jffs2]    - addr off|partiton size - read/write `size' bytes starting\n" 
           "    at offset `off' to/from memory address `addr'\n" 
           "nand read.yaffs addr off size - read the `size' byte yaffs image starting\n" 
           "    at offset `off' to memory address `addr'\n" 
           "nand write.yaffs addr off size - write the `size' byte yaffs image starting\n" 
           "    at offset `off' from memory address `addr'\n" 
           "nand read.raw addr off size - read the `size' bytes starting\n" 
           "    at offset `off' to memory address `addr', without oob and ecc\n" 
           "nand write.raw addr off size - write the `size' bytes starting\n" 
           "    at offset `off' from memory address `addr', without oob and ecc\n" 
           "nand erase [clean] [off size] - erase `size' bytes from\n" 
           "    offset `off' (entire device if not specified)\n" 
           "nand bad - show bad blocks\n" 
           "nand dump[.oob] off - dump page\n" 
           "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" 
           "nand markbad off - mark bad block at offset (UNSAFE)\n" 
           "nand biterr off - make a bit error at offset (UNSAFE)\n" 
           "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" 
           "nand unlock [offset] [size] - unlock section\n");

这里也没有必要去深入探究do_nand的作用,它完成的工作就是将kernel从nandflash中准确的载入到内存指定的位置。

当nand命令将kernel读入到内存后,接着执行bootm命令,最后执行linux内核的code

U_BOOT_CMD( 
    bootm,    CFG_MAXARGS,    1,    do_bootm, 
    "bootm   - boot application image from memory\n", 
    "[addr [arg ...]]\n    - boot application image stored in memory\n" 
    "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n" 
    "\t'arg' can be the address of an initrd image\n" 
#ifdef CONFIG_OF_FLAT_TREE 
    "\tWhen booting a Linux kernel which requires a flat device-tree\n" 
    "\ta third argument is required which is the address of the of the\n" 
    "\tdevice-tree blob. To boot that kernel without an initrd image,\n" 
    "\tuse a '-' for the second argument. If you do not pass a third\n" 
    "\ta bd_info struct will be passed instead\n" 
#endif 
);

这个命令的执行最后会调用到do_bootm函数

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) 

    ulong    iflag; 
    ulong    addr; 
    ulong    data, len, checksum; 
    ulong  *len_ptr; 
    uint    unc_len = CFG_BOOTM_LEN; 
    int    i, verify; 
    char    *name, *s; 
    int    (*appl)(int, char *[]); 
    image_header_t *hdr = &header; 
 
    s = getenv ("verify"); 
    verify = (s && (*s == 'n')) ? 0 : 1; 
    /*argv[1]传入的是从nandflash中载入到内存的地址0x32000000是32M的位置*/ 
    if (argc < 2) 
    { 
        addr = load_addr; 
    } 
    else 
    { 
        addr = simple_strtoul(argv[1], NULL, 16); 
    } 
 
    SHOW_BOOT_PROGRESS (1); 
    printf ("## Booting image at %08lx ...\n", addr); 
 
    /*使用u-boot启动的内核在制作镜像的时候会增加一个64byte的头,这里要对这个头做相应的处理*/ 
    memmove (&header, (char *)addr, sizeof(image_header_t)); 
 
    /*检查这个header中的幻数对不对*/ 
    if (ntohl(hdr->ih_magic) != IH_MAGIC) 
    { 
        { 
            puts ("Bad Magic Number\n"); 
            SHOW_BOOT_PROGRESS (-1); 
            return 1; 
        } 
    } 
    SHOW_BOOT_PROGRESS (2); 
 
    data = (ulong)&header; 
    len  = sizeof(image_header_t); 
 
    /*对这个header进行crc校验,比较原来保存的crc看是不是一致*/ 
    checksum = ntohl(hdr->ih_hcrc); 
    hdr->ih_hcrc = 0; 
    if (crc32 (0, (uchar *)data, len) != checksum) 
    { 
        puts ("Bad Header Checksum\n"); 
        SHOW_BOOT_PROGRESS (-2); 
        return 1; 
    } 
    SHOW_BOOT_PROGRESS (3); 
 
    /* for multi-file images we need the data part, too */ 
    print_image_hdr ((image_header_t *)addr); 
 
    data = addr + sizeof(image_header_t); 
    len  = ntohl(hdr->ih_size); 
 
    if (verify) 
    { 
        puts ("   Verifying Checksum ... "); 
        if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) 
        { 
            printf ("Bad Data CRC\n"); 
            SHOW_BOOT_PROGRESS (-3); 
            return 1; 
        } 
        puts ("OK\n"); 
    } 
    SHOW_BOOT_PROGRESS (4); 
 
    len_ptr = (ulong *)data; 
    /*cpu体系结构玛是不是正确*/ 
#if defined(__PPC__) 
    if (hdr->ih_arch != IH_CPU_PPC) 
#elif defined(__ARM__) 
    if (hdr->ih_arch != IH_CPU_ARM) 
#elif defined(__I386__) 
    if (hdr->ih_arch != IH_CPU_I386) 
#elif defined(__mips__) 
    if (hdr->ih_arch != IH_CPU_MIPS) 
#elif defined(__nios__) 
    if (hdr->ih_arch != IH_CPU_NIOS) 
#elif defined(__M68K__) 
    if (hdr->ih_arch != IH_CPU_M68K) 
#elif defined(__microblaze__) 
    if (hdr->ih_arch != IH_CPU_MICROBLAZE) 
#elif defined(__nios2__) 
    if (hdr->ih_arch != IH_CPU_NIOS2) 
#elif defined(__blackfin__) 
    if (hdr->ih_arch != IH_CPU_BLACKFIN) 
#elif defined(__avr32__) 
    if (hdr->ih_arch != IH_CPU_AVR32) 
#else 
# error Unknown CPU type 
#endif 
    { 
        printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch); 
        SHOW_BOOT_PROGRESS (-4); 
        return 1; 
    } 
    SHOW_BOOT_PROGRESS (5); 
 
    switch (hdr->ih_type) 
    { 
    case IH_TYPE_STANDALONE: 
        name = "Standalone Application"; 
        /* A second argument overwrites the load address */ 
        if (argc > 2) 
        { 
            hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16)); 
        } 
        break; 
    case IH_TYPE_KERNEL: 
        name = "Kernel Image"; 
        break; 
    case IH_TYPE_MULTI: 
        name = "Multi-File Image"; 
        len  = ntohl(len_ptr[0]); 
        /* OS kernel is always the first image */ 
        data += 8; /* kernel_len + terminator */ 
        for (i = 1; len_ptr[i]; ++i) 
            data += 4; 
        break; 
    default: 
        printf ("Wrong Image Type for %s command\n", cmdtp->name); 
        SHOW_BOOT_PROGRESS (-5); 
        return 1; 
    } 
    SHOW_BOOT_PROGRESS (6); 
 
    /* 
     * We have reached the point of no return: we are going to 
     * overwrite all exception vector code, so we cannot easily 
     * recover from any failures any more... 
     */ 
 
    iflag = disable_interrupts(); 
 
#ifdef CONFIG_AMIGAONEG3SE 
    /* 
     * We've possible left the caches enabled during 
     * bios emulation, so turn them off again 
     */ 
    icache_disable(); 
    invalidate_l1_instruction_cache(); 
    flush_data_cache(); 
    dcache_disable(); 
#endif 
    /*这个位置需要注意,ih_load表示内核自己希望运行的位置,如果这个位置和 
      boot载入内核到内存的位置data不一样,叫进行拷贝*/ 
    switch (hdr->ih_comp) 
    { 
    case IH_COMP_NONE: 
        if(ntohl(hdr->ih_load) == data) 
        { 
            printf ("   XIP %s ... ", name); 
        } 
        else 
        { 
            memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); 
        } 
        break; 
    case IH_COMP_GZIP: 
        printf ("   Uncompressing %s ... ", name); 
        if (gunzip ((void *)ntohl(hdr->ih_load), unc_len, 
                    (uchar *)data, &len) != 0) 
        { 
            puts ("GUNZIP ERROR - must RESET board to recover\n"); 
            SHOW_BOOT_PROGRESS (-6); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
        break; 
#ifdef CONFIG_BZIP2 
    case IH_COMP_BZIP2: 
        printf ("   Uncompressing %s ... ", name); 
        /* 
         * If we've got less than 4 MB of malloc() space, 
         * use slower decompression algorithm which requires 
         * at most 2300 KB of memory. 
         */ 
        i = BZ2_bzBuffToBuffDecompress ((char *)ntohl(hdr->ih_load), 
                                        &unc_len, (char *)data, len, 
                                        CFG_MALLOC_LEN < (4096 * 1024), 0); 
        if (i != BZ_OK) 
        { 
            printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i); 
            SHOW_BOOT_PROGRESS (-6); 
            udelay(100000); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
        break; 
#endif /* CONFIG_BZIP2 */ 
    default: 
        if (iflag) 
            enable_interrupts(); 
        printf ("Unimplemented compression type %d\n", hdr->ih_comp); 
        SHOW_BOOT_PROGRESS (-7); 
        return 1; 
    } 
    puts ("OK\n"); 
    SHOW_BOOT_PROGRESS (7); 
    switch (hdr->ih_type) 
    { 
    case IH_TYPE_STANDALONE: 
        if (iflag) 
            enable_interrupts(); 
        /* load (and uncompress), but don't start if "autostart" 
         * is set to "no" 
         */ 
        if (((s = getenv("autostart")) != NULL) && (strcmp(s, "no") == 0)) 
        { 
            char buf[32]; 
            sprintf(buf, "%lX", len); 
            setenv("filesize", buf); 
            return 0; 
        } 
        appl = (int ( *)(int, char * []))ntohl(hdr->ih_ep); 
        (*appl)(argc - 1, &argv[1]); 
        return 0; 
    case IH_TYPE_KERNEL: 
    case IH_TYPE_MULTI: 
        /* handled below */ 
        break; 
    default: 
        if (iflag) 
            enable_interrupts(); 
        printf ("Can't boot image type %d\n", hdr->ih_type); 
        SHOW_BOOT_PROGRESS (-8); 
        return 1; 
    } 
    SHOW_BOOT_PROGRESS (8); 
 
    /*最后我们一般启动的linux内核,调用do_bootm_linux进行处理*/ 
    switch (hdr->ih_os) 
    { 
    default:            /* handled by (original) Linux case */ 
    case IH_OS_LINUX: 
#ifdef CONFIG_SILENT_CONSOLE 
        fixup_silent_linux(); 
#endif 
        do_bootm_linux  (cmdtp, flag, argc, argv, 
                         addr, len_ptr, verify); 
        break; 
    case IH_OS_NETBSD: 
        do_bootm_netbsd (cmdtp, flag, argc, argv, 
                         addr, len_ptr, verify); 
        break; 
#ifdef CONFIG_LYNXKDI 
    case IH_OS_LYNXOS: 
        do_bootm_lynxkdi (cmdtp, flag, argc, argv, 
                          addr, len_ptr, verify); 
        break; 
#endif 
    case IH_OS_RTEMS: 
        do_bootm_rtems (cmdtp, flag, argc, argv, 
                        addr, len_ptr, verify); 
        break; 
 
#if (CONFIG_COMMANDS & CFG_CMD_ELF) 
    case IH_OS_VXWORKS: 
        do_bootm_vxworks (cmdtp, flag, argc, argv, 
                          addr, len_ptr, verify); 
        break; 
    case IH_OS_QNX: 
        do_bootm_qnxelf (cmdtp, flag, argc, argv, 
                         addr, len_ptr, verify); 
        break; 
#endif /* CFG_CMD_ELF */ 
#ifdef CONFIG_ARTOS 
    case IH_OS_ARTOS: 
        do_bootm_artos  (cmdtp, flag, argc, argv, 
                         addr, len_ptr, verify); 
        break; 
#endif 
    } 
 
    SHOW_BOOT_PROGRESS (-9); 
#ifdef DEBUG 
    puts ("\n## Control returned to monitor - resetting...\n"); 
    do_reset (cmdtp, flag, argc, argv); 
#endif 
    return 1; 
}

最后调用do_linux_bootm完成内核启动,这个函数主要做三个工作,首先是拷贝inird到确定位置,然后初始化内核参数到确定的位置,最后调用内核的启动函数,并传递两个参数给内核一个是cpu number,一个是内核参数位置。

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], 
                     ulong addr, ulong *len_ptr, int verify) 

    ulong len = 0, checksum; 
    ulong initrd_start, initrd_end; 
    ulong data; 
    void (*theKernel)(int zero, int arch, uint params); 
    image_header_t *hdr = &header; 
    bd_t *bd = gd->bd; 
 
#ifdef CONFIG_CMDLINE_TAG 
    char *commandline = getenv ("bootargs"); 
#endif 
 
    /*ih_ep的值正是kernel启动的第一条code的位置*/ 
    theKernel = (void ( *)(int, int, uint))ntohl(hdr->ih_ep); 
 
    /* 
     * Check if there is an initrd image 
     */ 
    if (argc >= 3) 
    { 
        SHOW_BOOT_PROGRESS (9); 
 
        addr = simple_strtoul (argv[2], NULL, 16); 
 
        printf ("## Loading Ramdisk Image at %08lx ...\n", addr); 
 
        /* Copy header so we can blank CRC field for re-calculation */ 
#ifdef CONFIG_HAS_DATAFLASH 
        if (addr_dataflash (addr)) 
        { 
            read_dataflash (addr, sizeof (image_header_t), 
                            (char *) &header); 
        } 
        else 
#endif 
            memcpy (&header, (char *) addr, 
                    sizeof (image_header_t)); 
 
        if (ntohl (hdr->ih_magic) != IH_MAGIC) 
        { 
            printf ("Bad Magic Number\n"); 
            SHOW_BOOT_PROGRESS (-10); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
 
        data = (ulong) & header; 
        len = sizeof (image_header_t); 
 
        checksum = ntohl (hdr->ih_hcrc); 
        hdr->ih_hcrc = 0; 
 
        if (crc32 (0, (unsigned char *) data, len) != checksum) 
        { 
            printf ("Bad Header Checksum\n"); 
            SHOW_BOOT_PROGRESS (-11); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
 
        SHOW_BOOT_PROGRESS (10); 
 
        print_image_hdr (hdr); 
 
        data = addr + sizeof (image_header_t); 
        len = ntohl (hdr->ih_size); 
 
#ifdef CONFIG_HAS_DATAFLASH 
        if (addr_dataflash (addr)) 
        { 
            read_dataflash (data, len, (char *) CFG_LOAD_ADDR); 
            data = CFG_LOAD_ADDR; 
        } 
#endif 
 
        if (verify) 
        { 
            ulong csum = 0; 
 
            printf ("   Verifying Checksum ... "); 
            csum = crc32 (0, (unsigned char *) data, len); 
            if (csum != ntohl (hdr->ih_dcrc)) 
            { 
                printf ("Bad Data CRC\n"); 
                SHOW_BOOT_PROGRESS (-12); 
                do_reset (cmdtp, flag, argc, argv); 
            } 
            printf ("OK\n"); 
        } 
 
        SHOW_BOOT_PROGRESS (11); 
 
        if ((hdr->ih_os != IH_OS_LINUX) || 
                (hdr->ih_arch != IH_CPU_ARM) || 
                (hdr->ih_type != IH_TYPE_RAMDISK)) 
        { 
            printf ("No Linux ARM Ramdisk Image\n"); 
            SHOW_BOOT_PROGRESS (-13); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
 
#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO) 
        /* 
         *we need to copy the ramdisk to SRAM to let Linux boot 
         */ 
        memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); 
        data = ntohl(hdr->ih_load); 
#endif /* CONFIG_B2 || CONFIG_EVB4510 */ 
 
        /* 
         * Now check if we have a multifile image 
         */ 
    } 
    else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) 
    { 
        ulong tail = ntohl (len_ptr[0]) % 4; 
        int i; 
 
        SHOW_BOOT_PROGRESS (13); 
 
        /* skip kernel length and terminator */ 
        data = (ulong) (&len_ptr[2]); 
        /* skip any additional image length fields */ 
        for (i = 1; len_ptr[i]; ++i) 
            data += 4; 
        /* add kernel length, and align */ 
        data += ntohl (len_ptr[0]); 
        if (tail) 
        { 
            data += 4 - tail; 
        } 
 
        len = ntohl (len_ptr[1]); 
 
    } 
    else 
    { 
        /* 
         * no initrd image 
         */ 
        SHOW_BOOT_PROGRESS (14); 
 
        len = data = 0; 
    } 
 
#ifdef    DEBUG 
    if (!data) 
    { 
        printf ("No initrd\n"); 
    } 
#endif 
 
    if (data) 
    { 
        initrd_start = data; 
        initrd_end = initrd_start + len; 
    } 
    else 
    { 
        initrd_start = 0; 
        initrd_end = 0; 
    } 
    SHOW_BOOT_PROGRESS (15); 
    debug ("## Transferring control to Linux (at address %08lx) ...\n", 
           (ulong) theKernel); 
    /*设置要传递的内核参数到内存开始的512字节位置,然后启动linux内核,并 
    向linux 内核传递两个参数,一个参数是cpu类型,一个参数是内核参数位置*/ 
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \ 
    defined (CONFIG_CMDLINE_TAG) || \ 
    defined (CONFIG_INITRD_TAG) || \ 
    defined (CONFIG_SERIAL_TAG) || \ 
    defined (CONFIG_REVISION_TAG) || \ 
    defined (CONFIG_LCD) || \ 
    defined (CONFIG_VFD) 
    setup_start_tag (bd); 
#ifdef CONFIG_SERIAL_TAG 
    setup_serial_tag (?ms); 
#endif 
#ifdef CONFIG_REVISION_TAG 
    setup_revision_tag (?ms); 
#endif 
#ifdef CONFIG_SETUP_MEMORY_TAGS 
    setup_memory_tags (bd); 
#endif 
#ifdef CONFIG_CMDLINE_TAG 
    setup_commandline_tag (bd, commandline); 
#endif 
#ifdef CONFIG_INITRD_TAG 
    if (initrd_start && initrd_end) 
        setup_initrd_tag (bd, initrd_start, initrd_end); 
#endif 
#if defined (CONFIG_VFD) || defined (CONFIG_LCD) 
    setup_videolfb_tag ((gd_t *) gd); 
#endif 
    setup_end_tag (bd); 
#endif 
 
    /* we assume that the kernel is in place */ 
    printf ("\nStarting kernel ...\n\n"); 
 
#ifdef CONFIG_USB_DEVICE 
    { 
        extern void udc_disconnect (void); 
        //udc_disconnect (); // cancled by www.arm9.net 
    } 
#endif 
 
    cleanup_before_linux (); 
 
    theKernel (0, bd->bi_arch_number, bd->bi_boot_params); 
}

四、U-boot命令行调试状态

上面是对u-boot载入引导kernel的分析。如果用户通过串口打断引导过程,或者引导过程失败了,就会进入u-boot命令行调试状态。U-boot进入一个无限的for循环等待用户从串口输入命令,找到匹配的命令后执行相应的操作。

for (;;) 

    /*从串口读出命令*/ 
    len = readline (CFG_PROMPT); 
 
    flag = 0;    /* assume no special flags for now */ 
    if (len > 0) 
        strcpy (lastcommand, console_buffer); 
    else if (len == 0) 
        flag |= CMD_FLAG_REPEAT; 
#ifdef CONFIG_BOOT_RETRY_TIME 
    else if (len == -2) 
    { 
        /* -2 means timed out, retry autoboot 
         */ 
        puts ("\nTimed out waiting for command\n"); 
# ifdef CONFIG_RESET_TO_RETRY 
        /* Reinit board to run initialization code again */ 
        do_reset (NULL, 0, 0, NULL); 
# else 
        return;        /* retry autoboot */ 
# endif 
    } 
#endif 
    /*执行相应的命令*/ 
    if (len == -1) 
        puts ("<INTERRUPT>\n"); 
    else 
        rc = run_command (lastcommand, flag); 
 
    if (rc <= 0) 
    { 
        /* invalid command or not repeatable, forget it */ 
        lastcommand[0] = 0; 
    } 

#endif /*CFG_HUSH_PARSER*/

u-boot懂你并不难相关推荐

  1. 《读懂圣经并不难-李健》

    圣经是真理,是神给我们的话语,但有时候我们学习无法难懂,好好学习!这样才能懂神的心意,懂神对我们爱. 感谢主 http://www.fuyin.tv/content/view/movid/1134/

  2. 写给非技术人员的机器学习指南

    作者:Kannan Chandrasegaran 这里是另一家创业公司的聚会,你靠在吧台上,一边喝着小酒.这样的夜晚你只考虑自己,尽情享受社交活动. 偶然间,一个声音从你耳边传来. "他们绝 ...

  3. 双数组 实现 Trie

    NewSMTH zhjin (sweptAway): 在开发中文分词器的时候, 一个高效的词典结构尤其重要. 词典 的一种常见高效的实现方式就是使用 Trie 结构, 但是传统的 Trie 结构的实现 ...

  4. VLAN设置实例全程解读

    作者:IT168网络频道  2005-05-13 什么是三层交换和VLAN? 要回答这个问题我们还是先看看以太网的工作原理.以太网的工作原理是利用二进制位形成的一个个字节组合成一帧帧的数据(其实是一些 ...

  5. Cisco3550三层交换机vlan间路由配置实例

    下面以实例说明如何在一个典型的快速以太局域网中实现VLAN. 所谓典型局域网就是指由一台具备三层交换功能的核心交换机接几台分支交换机(不一定具备三层交换能力).我们假设核心交换机名称为:COM:分支交 ...

  6. 如何制作一个完美的全屏视频H5

    写在前面的话: 最近一波H5广告火爆整个互联网圈,身为圈内人,我们怎能     不! 知!道! :( 嘘!真不知道的也继续看下去,有收获 ↓ ) So,搞懂这个并不难. 这篇文章将带你从头到尾了解H5 ...

  7. cisco3560三层交换机vlan间路由配置实例

    所谓典型局域网就是指由一台具备三层交换功能的核心交换机接几台分支交换机(不一定具备三层交换能力).我们假设核心交换机名称为:COM;分支交换机分别为:PAR1.PAR2.PAR3,分别通过Port 1 ...

  8. 为什么学了模数电还是看不懂较复杂的电路图

    看懂电路并不难. (1) 首先要摆正心态,不要看到错综复杂的电路图就一脸懵逼,不知所错.你要明白,再复杂的电路也是由一个个的基本电路拼装出来的. (2) 基础知识当然是少不了的,常用的基本电路结构搞搞 ...

  9. 德国食品溯源安全体系浅析

    "假如你吃了个鸡蛋觉得不错,何必认识那下蛋的母鸡呢?"但是,这句话在德国就变成了"要吃到好鸡蛋,更要认识下蛋的母鸡". 从超市买回一盒鸡蛋,发现每枚鸡蛋上都印有 ...

最新文章

  1. 互联网主要安全威胁解读及应对方案大讨论 | 高可用架构系列
  2. Collections.sort()自定义排序方式
  3. 2021-07-29
  4. boost::math模块演示负二项分布使用的简单示例的测试程序
  5. 牛人的博客(机器学习,图像处理,计算机视觉)
  6. 向数组添加元素 java_java如何向数组里添加元素
  7. Project facet Dynamic Web Module version 3.1 is not supported.
  8. twisted系列教程七–小插曲,延迟对象
  9. 优化方法-模式搜索法
  10. 安装rocketmq-console插件
  11. [Oracle][Standby][PDB]在PDB中修改参数,设置范围为 SPFILE,报 ORA-65099错误
  12. 【计算机基础】 经常使用的排序算法的时间复杂度和空间复杂度
  13. 在Flex组件外观实施中使用Scale9
  14. 用计算机绘制v-t图像,2.用计算机绘制v-t图象. [预习导引]
  15. 烟囱加固技术不断完善
  16. 脚本入门(2)-perl脚本
  17. maven 本地仓库的配置
  18. vector详解以及一些问题(C++)
  19. java计算机毕业设计家用电器销售网站源码+mysql数据库+系统+lw文档+部署
  20. DeFCN debug记录(训练过程),以及对cvpods框架的分析

热门文章

  1. (转载)关于My97 datepicker与Angular ng-model绑定问题解决。
  2. MockupBuilder
  3. sql更新表的字段和主键
  4. 窗口结束后资源释放不掉问题解决办法
  5. vue npm: react运行时报错npm start — babel-eslint 版本兼容性问题
  6. 数据库mysql表怎么设置外键_如何设置数据库中的外键
  7. mysql into_MYSQL中replace into的用法
  8. win配置环境变量以及一些变量换行类的小技巧
  9. html弹窗超链接,点出超链接弹出一个小窗口
  10. Oracle sql如何把毫秒转为日期,sql – 将毫秒转换为时间戳