天天看点

U-BOOT 2010.03源码分析

U-boot第一阶段汇编代码分析

U-BOOT 2010.03源码分析

一个可执行的 image 必须有一个入口点,并且只能有一个全局入口点,所以要通知编译器这个入口在哪里,入口点是通过有链接脚本来实现的,由此我们可以找到程序的入口点是在cpu/arm_cortexa8/u-boot.lds 中指定的,其中ENTRY(_start) 说明程序从_start 开始运行,而它指向的是cpu/arm_cortexa8/start.o 文件。

因为我们用的是 cortex-a8 的 cpu 架构,在CPU复位后从iROM地址0x00000000取它的第一条指令,执行iROM代码的功能是把flash中的前16K的代码加载到iRAM中,系统上电后将首先执行 u-boot 程序。

1.stage1:cpu/arm_cortexa8/start.S

2.当系统启动时, ARM CPU 会跳到 0x00000000去执行,一般 BootLoader 包括如下几个部分:

                1. 建立异常向量表

                2. 显示的切换到 SVC 且 32 指令模式

                3. 设置异常向量表

                4. 关闭 TLB,MMU,cache,刷新指令 cache 数据 cache

                5. 关闭内部看门狗

                6. 禁止所有的中断

                7. 串口初始化

                8. tzpc(TrustZone Protection Controller)

                9. 配置系统时钟频率和总线频率

                10. 设置内存区的控制寄存器

                11. 设置堆栈

                12. 跳到 C 代码部分执行

具体代码分析如下:

.globl _start

        _start: b reset        

        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

        _pad:         .word 0x12345678

        .global _end_vect

        _end_vect:

        .balignl 16,0xdeadbeef

这句话的功能是申请一部分内存,具体的解释百度直接搜索.balignl 16,0xdeadbeef 这条指令可以得到详细的解释

_TEXT_BASE:

        .word TEXT_BASE

.globl _armboot_start

        _armboot_start:

                .word _start

.globl _bss_start

        _bss_start:

                .word __bss_start

        .globl _bss_end

        _bss_end:

                .word _end

        #ifdef CONFIG_USE_IRQ        // 这个宏没有定义,故不执行

        .globl IRQ_STACK_START

        IRQ_STACK_START:

                .word 0x0badc0de

        .globl FIQ_STACK_START

        FIQ_STACK_START:

                .word 0x0badc0de

        #endif

reset:

        mrs r0, cpsr

        bic r0, r0, #0x1f

        orr r0, r0, #0xd3

        msr cpsr,r0

#if (CONFIG_OMAP34XX)        // 这个宏没有定义,下面的代码不会预编译

        ...

        mov r3, #SRAM_OFFSET2

        add r1, r1, r3

        next:

        ldmia r0!, {r3 - r10}        @ copy from source address [r0]

        stmia r1!, {r3 - r10}        @ copy to target address [r1]

        cmp r0, r2        @ until source end address [r2]

        bne next        @ loop until equal */

        #if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT)

        bl cpy_clk_code        @ put dpll adjust code         behind vectors

        #endif        

        #endif        

        #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 装载_start的地址到r0中.

        ldr r1, _TEXT_BASE        @ 装载连接地址,这个地址是0x

        cmp        r0, r1        @ don't reloc during debug

        beq stack_setup

判断 当uboot在nand当中引导时,会把前16K的代码放到ram中,ram的地址和连接地址不一致, r0不等于r1的值,beq条件不成立. 当从usb引导是这个条件就成立.成立后后面的代码就不在执行了,后面的搬移代码就不在执行.

ldr r2, _armboot_start @功能是装载_start的地址

        / * .globl _armboot_start

        * _armboot_start:

        *         .word _start

        * /

        ldr r3, _bss_start @ 功能是装载

        / *        .globl _bss_start

        * _bss_start:

        *        .word __bss_start

        * __bss_start这个标号在cpu/arm_cortexa8/u-boot.lds 中定义,是bss段的开始也是bss段以前

        * 的一个结束标志 因此r3的值是uboot的除去bss的末尾地址,在搬移的时候是不搬移bss

        * 段的,bss段放的是未初始化的变量.

        * /

sub r2, r3, r2        @ r2 < - size of armboot uboot的大小的偏移量

        add r2, r0, r2        @ r2 < - source end address r0 是uboot的起始地址

        copy_loop:        @ copy 32 bytes at a time

        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] 等到搬移完成后,r0和r2的值相等,

        ble copy_loop        @ 条件不成立,就向下执行代码

        #endif        

Set up the stack(内存规划)

设置的堆栈,规划内存的使用的

        stack_setup:

        ldr        r0, _TEXT_BASE        @ upper 128 KiB: relocated uboot

        sub        r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area

        * 这句话的功能是r0 的值向低地址减去128K +1M的大小

        */

sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo

        / * 438 F        d        CONFIG_SYS_GBL_DATA_SIZE include/configs/smdkc100.h

        * #define CONFIG_SYS_GBL_DATA_SIZE 128

        * 这句话是把地址继续减去128 bytes

        * /

#ifdef CONFIG_USE_IRQ // 这个宏没有定义,下面的代码不会执行.

        sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)

        #endif

        sub sp, r0, #12        @ leave 3 words for abort-stack

        and sp, sp, #~7         @ 8 byte alinged for (ldr/str)d

        clear_bss:

        ldr r0, _bss_start        @ find start of bss segment

        ldr r1, _bss_end        @ stop here

        mov r2, #0x00000000        @ clear value

        clbss_l:

        str r2, [r0]        @ clear BSS location

        cmp r0, r1        @ are we at the end yet

        add r0, r0, #4        @ increment clear index pointer

        bne clbss_l        @ keep clearing till at end

        ldr pc, _start_armboot        @ 进入C代码

        _start_armboot: .word start_armboot

        / * 会进入lib_arm/board.c文件中的 void start_armboot (void) * /

bl cpu_init_crit

cpu_init_crit:

        mov        r0, #0        @ set up for MCR

        mcr        p15, 0, r0, c8, c7, 0        @ invalidate TLBs

        mcr        p15, 0, r0, c7, c5, 0        @ invalidate icache

        mrc        p15, 0, r0, c1, c0, 0

        bic        r0, r0, #0x00002000 @ clear bits 13 (--V-)

        bic        r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)

        orr        r0, r0, #0x00000002 @ set bit 1 (--A-) Align

        orr        r0, r0, #0x00000800 @ set bit 12 (Z---) BTB

        mcr        p15, 0, r0, c1, c0, 0

        mov ip, lr        @ persevere link reg across call

        bl        lowlevel_init        @ go setup pll,mux,memory

cpu_init_crit:

        mov ip, lr @ persevere link reg across call

        bl lowlevel_init @ go setup pll,mux,memory

/ *

        *lowlevel_init 这个函数在

U-BOOT 2010.03源码分析

的文件当中

        *定义

        * /

        mov lr, ip @ restore link

        mov pc, lr @ back to my caller

        #endif

lowlevel_init

lowlevel_init的功能如下:

/ *

        * 做一些层层硬件的初始化

        */

        .globl lowlevel_init

        lowlevel_init:

        mov        r9, lr

        mov        r5, #0

        ldr        r8, =S5PC100_GPIO_BASE

        ldr        r0, =S5PC100_WATCHDOG_BASE        @0xEA200000

        orr r0, r0, #0x0

        str r5, [r0]

#ifndef CONFIG_ONENAND_IPL

        ldr        r0, =S5PC100_SROMC_BASE

        ldr        r1, =0x9

        str        r1, [r0]

        #endif

        ldr r0, =S5PC100_VIC0_BASE        @0xE4000000

        ldr r1, =S5PC100_VIC1_BASE        @0xE4000000

        ldr r2, =S5PC100_VIC2_BASE        @0xE4000000

        mvn        r3, #0x0

        str        r3, [r0, #0x14]        @INTENCLEAR

        str        r3, [r1, #0x14]        @INTENCLEAR

        str        r3, [r2, #0x14]        @INTENCLEAR

        #ifndef CONFIG_ONENAND_IPL

        str        r5, [r0, #0xc]        @INTSELECT

        str        r5, [r1, #0xc]        @INTSELECT

        str        r5, [r2, #0xc]        @INTSELECT

        str        r5, [r0, #0xf00]        @INTADDRESS

        str        r5, [r1, #0xf00]        @INTADDRESS

        str        r5, [r2, #0xf00]        @INTADDRESS

        #endif

        #ifndef CONFIG_ONENAND_IPL

        bl uart_asm_init

        bl tzpc_asm_init

        #endif

        #ifdef CONFIG_ONENAND_IPL

        bl        system_clock_init

        bl        mem_ctrl_asm_init

        ldr        r0, =S5PC100_RST_STAT

        ldr        r1, [r0]

        bic        r1, r1, #0xfffffff7

        cmp        r1, #0x8

        beq        wakeup_reset

        #endif

        1:

        mov        lr, r9

        mov        pc, lr        // 这句话的功能从bl lowlevel_init返回

U-boot第二阶段C代码分析

void start_armboot (void)

start_armboot的定义在lib_arm/board.c中定义的这个C函数中定义的这个函数的功能是去执行一系列的函数进行底层硬件的初始化,最要中的初始化是进行内存的初始化。

u-boot 之 gd_t 和 bd_t 数据结构简介

        bd_t :这个结构体是board info 的缩写 用来保存板子的信息

        gd_t :这个结构体是global data的缩写,用来保存全局数据的信息

        bd_t 和 gd_t 是 u-boot 中两个重要的数据结构,在初始化操作很多都要靠这两个数据结构来保存或传递。

        bd_t在 include/asm-arm/u-boot.h 中定义

        gd_t 在 include/asm-arm/global_data.h中定义

        bd_t :board info 数据结构定义,主要是用来保存板子参数。

void start_armboot (void)

        {

                init_fnc_t **init_fnc_ptr;

                char *s;

                #if defined(CONFIG_VFD) || defined(CONFIG_LCD) // 这个条件不成立,下面的代码不执行

                unsigned long addr;

        #endif

                gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

        / * gd_t是一个包含了很多全局数据的结构体,gd是一个指向这个全局数据结构体的指针,该指针是通过DECLARE_GLOBAL_DATA_PTR来定义的。在文件在下面的文件中:         gd_t        include/asm-arm/global_data.h中的70行做了如下定义:

        #define DECLARE_GLOBAL_DATA_PTR        register volatile gd_t *gd asm ("r8")

        指针gd存放在指定的寄存器r8中。这个声明可避免编译器把r8分配给其它的变量。

        任何想要访问全局数据结构体的代码,代码开头加入DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。在使用gd指针前做了定义:lib_arm/board.c的64行做了如下宏定义:

U-BOOT 2010.03源码分析

        * /

        __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 ();

                }

        }

/ * 这是一个函数的初始化的循环,用循环函数去执行一系列函数*/

        mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,CONFIG_SYS_MALLOC_LEN);

        // 初始化malloc和env的内存

        #ifndef CONFIG_SYS_NO_FLASH // 条件不成立,接下来的代码不执行.

        display_flash_config (flash_init ());

        #endif

        #ifdef CONFIG_VFD        //宏没有定义,接下来的代码不执行

        # ifndef PAGE_SIZE

        # define PAGE_SIZE 4096

        # endif

        addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

        vfd_setmem (addr);

        gd->fb_base = addr;

        #endif

        #ifdef CONFIG_LCD        // 宏没有定义,下面的代码不执行

        if (!gd->fb_base) {

        #         ifndef PAGE_SIZE

        #         define PAGE_SIZE 4096

        #         endif

        addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

        lcd_setmem (addr);

        gd->fb_base = addr;

        }

        #endif

        #if defined(CONFIG_CMD_NAND)

        puts ("NAND: ");

        nand_init();        

        #endif

        #if defined(CONFIG_CMD_ONENAND)        // 这个宏定义了,下面的代码能够执行

        onenand_init();

        #endif

        #ifdef CONFIG_HAS_DATAFLASH        // 这个宏没有定义

        AT91F_DataflashInit();

        dataflash_print_info();

        #endif

        env_relocate ();

        #ifdef CONFIG_VFD // 宏没有定义,条件没有成立,下面的代码不能够执行

        drv_vfd_init();

        #endif

        #ifdef CONFIG_SERIAL_MULTI // 宏定义了,条件成立,下面的代码能够执行

                serial_initialize();

        #endif

        gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

        stdio_init ();        

        jumptable_init ();

        #if defined(CONFIG_API)        // 宏没有定义,条件不成立

        api_init ();

        #endif

        console_init_r ();        

        #if defined(CONFIG_ARCH_MISC_INIT)        // 条件不成立,下面的代码不执行

        arch_misc_init ();

        #endif

        #if defined(CONFIG_MISC_INIT_R)        // 条件不成立,下面的代码不成立

        misc_init_r ();

        #endif

        enable_interrupts ();

        #ifdef CONFIG_DRIVER_TI_EMAC        // 宏没有定义,不执行

        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)        // 宏没有定义,下面的代码不执行

        if (getenv ("ethaddr")) {

                uchar enetaddr[6];

                eth_getenv_enetaddr("ethaddr", enetaddr);

                smc_set_mac_addr(enetaddr);

        }

        #endif

        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_GENERIC_MMC        // 宏没有定义,代码不执行

        puts ("MMC: ");

        mmc_initialize (gd->bd);

        #endif

        #ifdef CONFIG_BITBANGMII        // 宏没有定义,代码不执行

        bb_miiphy_init();

        #endif

        #if defined(CONFIG_CMD_NET)        //#undef 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

        for (;;) {

                main_loop ();

                }

        }