转载: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. c#跨线程访问控件帮助类

    1.背景 对于winform程序来说,当我们点击按钮,需要消耗一定时长才能拿到数据后才能显示在界面上某个控件上的情况,我们通常会专门开一个线程去拿数据,这样不会造成界面处于假死状态 2.常规做法 // ...

  2. HEVC码率控制浅析——HM代码阅读之四

    继续分析第一篇提到的compressSlice中对LCU的RC参数初始化: #if RATE_CONTROL_LAMBDA_DOMAIN Double oldLambda = m_pcRdCost-& ...

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

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

  4. 微服务架构实践 - 你只懂docker与spring boot就够了吗?

    微服务架构实践 - 你只懂docker与spring boot就够了吗? 作者 浮云发发 已关注 2017.02.27 02:50* 字数 2613 阅读 2583评论 6喜欢 35赞赏 2 微服务并 ...

  5. 一文读懂 Spring Boot、微服务架构和大数据治理三者之间的故事

    微服务架构 微服务的诞生并非偶然,它是在互联网高速发展,技术日新月异的变化以及传统架构无法适应快速变化等多重因素的推动下诞生的产物.互联网时代的产品通常有两类特点:需求变化快和用户群体庞大,在这种情况 ...

  6. 一文读懂spring boot 和微服务的关系

    欢迎访问网易云社区,了解更多网易技术产品运营经验. Spring Boot 和微服务没关系, Java 微服务治理框架普遍用的是 Spring Cloud. Spring Boot 产生的背景,是开发 ...

  7. 读懂这些spring boot的核心注解,快速配置完成项目搭建

    在spring boot中,摒弃了spring以往项目中大量繁琐的配置,遵循约定大于配置的原则,通过自身默认配置,极大的降低了项目搭建的复杂度.同样在spring boot中,大量注解的使用,使得代码 ...

  8. 搞懂分布式技术14:Spring Boot使用注解集成Redis缓存

    本文内容参考网络,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutor ...

  9. 一图读懂Spring Core,Spring MVC, Spring Boot,Spring Cloud 的关系与区别

    Spring框架自诞生到现在,历经多次革新,形成了多种不同的产品,分别应用于不同的项目中,为了帮助自己理解这些产品之间的关系,特此整理此图,以便自己记忆和复习.

随机推荐

  1. wxPython 画图板

    终于开始Python学习之旅了,姑且以一个“画图板”小项目开始吧.放慢脚步,一点一点地学习. 1月28日更新 第一次遇到的麻烦便是“重绘”,查了好多资料,终于重绘成功了. #-*- encoding: ...

  2. Jenkins+Ant+SVN+Jmeter实现持续集成

     一.什么是持续集成? 待补充 二.说明: 本次框架介绍中不涉及到介绍框架的构建过程,介绍如何构建环境详细的构建见前篇文章: jmeter+Jenkins持续集成(邮件通知) Jmeter+Jenki ...

  3. TEC-2几条微指令的微码说明 & TEC-2微程序运行测试步骤

    个人理解,不保证完全正确…… 给正在被何朝东虐的,以及将来会被何朝东虐的同胞们………… 祈祷软院赶快更新课程让下一代逃脱TEC-2魔爪,monitor里那1994的年份真是看得人一口老血…… 微码说明 ...

  4. web_reg_save_param_regexp函数的用法

    关联从服务器返回的所有的内容: 本例通过一个使用HTTP/HTML协议发送.获取服务器数据的vuser脚本,分析LoadRunner如何进行HTTP关联. 下面这个例子包括两个事务:上传数据到服务器. ...

  5. LoadRunner中Vugen-Recording Options选项卡介绍:

    LoadRunner中Vugen-Recording Options选项卡介绍:

  6. perl相关知识

    转:http://www.runoob.com/perl/perl-cgi-programming.html Perl 是 Practical Extraction and Report Langua ...

  7. 洛谷P2571 [SCOI2010]传送带 [三分]

    题目传送门 传送带 题目描述 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移 ...

  8. Python之路【第二篇】: 列表、元组、字符串、字典、集合

    本文内容: -------------------------------------- 列表.元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码 1. 列表(list) 序列是Pyt ...

  9. React Native升级方法——升级到最新版本0.59

    React Native最近有大动作,于2019年3月12日发布新版本0.59.主要有两点值得升级:支持React Hooks:升级了JavaScriptCore,使Android性能有大幅提升.据用 ...

  10. js基本数据类型 BigInt 和 Number 的区别

    今天在做LeetCode的一到 “加一” 的题,题目如下 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一. 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字. 你可以假设除 ...