U-BOOT 2010.03源码分析

来源:互联网 发布:tomcat连接mysql测试 编辑:程序博客网 时间:2024/05/17 01:43

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

一个可执行的 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        /*0x0,正常情况下,系统 reset 后进入的入口*/
        ldr        pc, _undefined_instruction        /*0x4,未定义指令,系统出错处理的入口*/
        ldr        pc, _software_interrupt        /* 0x8,软中断,monitor 程序的入口*/
        ldr        pc, _prefetch_abort        /*0x0c,预取失败错误*/
        ldr        pc, _data_abort        /*0x10,取数据失败错误(通常是保护现场,然后 do nothing)*/ ldr        pc, _not_used        /*0x14 保留*/
        ldr        pc, _irq        /*0x18,快速中断请求 */
        ldr        pc, _fiq        /*0x1c 快速中断 */

_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 /* now 16*4=64 */
        .global _end_vect
        _end_vect:
        .balignl 16,0xdeadbeef

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

/*************************************************************************
        *
        * Startup Code (reset vector)
        *
        * do important init only if we don't start from memory!
        * setup Memory and board specific bits prior to relocation.
        * relocate armboot to ram
        * setup stack
        *
        *************************************************************************/

_TEXT_BASE:
        .word TEXT_BASE

/*TEXT_BASE这个标号的定义在如下文件中定义:
        *390 F m TEXT_BASE board/samsung/smdkc100/config.mk
        *在文件中的定义是:TEXT_BASE = 0x34800000
        */

.globl _armboot_start
        _armboot_start:
                .word _start

/* _start 是uboot的第一行代码的标号,代表的是第一行代码的地址*/

/*
        These are defined in the board-specific linker script.在cpu/arm_cortexa8/u-boot.lds中定义
         */

.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

/*
        * the actual reset code 系统复位后会执行这部分代码
        */

reset:
        /*
        * set the cpu to SVC32 mode
        */
        mrs r0, cpsr
        bic r0, r0, #0x1f
        orr r0, r0, #0xd3
        msr cpsr,r0

#if (CONFIG_OMAP34XX)        // 这个宏没有定义,下面的代码不会预编译
        /* Copy vectors to mask ROM indirect addr */
        ...
        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)
        /* No need to copy/exec the clock code - DPLL adjust already done
        * in NAND/oneNAND Boot.
        */
        bl cpy_clk_code        @ put dpll adjust code         behind vectors
        #endif        /* NAND Boot */
        #endif        /* #if (CONFIG_OMAP34XX) * /

/* the mask ROM code should have PLL and others stable */
        #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
        /* _TEXT_BASE:
        * .word        TEXT_BASE
        * TEXT_BASE这个标号的定义在如下文件中定义:
        * 390 F        m        TEXT_BASE        board/samsung/smdkc100/config.mk
        * 在文件中的定义是:TEXT_BASE = 0x34800000
        * 最后得出r1的值是0x34800000
        */
        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        /* CONFIG_SKIP_RELOCATE_UBOOT */

Set up the stack(内存规划)

/* Set up the stack */ 设置的堆栈,规划内存的使用的
        stack_setup:
        ldr        r0, _TEXT_BASE        @ upper 128 KiB: relocated uboot
        sub        r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
        /*

*462 F d CONFIG_SYS_MALLOC_LEN include/configs/smdkc100.h
        *中的的65行 #define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (1 << 20))
        *环境变量大小在228行 #define CONFIG_ENV_SIZE (128 << 10) /* 128KiB, *0x20000 */
        * 这句话的功能是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 /* size in bytes for */
        * 这句话是把地址继续减去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 (if any). 对bss段进行初始化 */
        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_critical registers 初始化关键的寄存器
        *
        * setup important registers
        * setup memory timing
        *
        *************************************************************************/

cpu_init_crit:
        /*
        * Invalidate L1 I/D
        */
        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
        /*
        * disable MMU stuff and caches
        */
        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
        /*
        * Jump to board specific initialization...
        * The Mask ROM will have already initialized
        * basic memory. Go here to bump up clock rate and handle
        * wake up conditions.
        */
        mov ip, lr        @ persevere link reg across call
        bl        lowlevel_init        @ go setup pll,mux,memory
        /* lowlevel_init是底层初始化的代码,在下面的文件中定义
        * 60 F        l        lowlevel_init        board/samsung/smdkc100/lowlevel_init.S
        * /
        mov        lr, ip        @ restore link
        mov        pc, lr        @ back to my caller

/*************************************************************************
        *
        * CPU_init_critical registers
        *
        * setup important registers
        * setup memory timing
        *
        *************************************************************************/

cpu_init_crit:
        /*
        * Jump to board specific initialization...
        * The Mask ROM will have already initialized
        * basic memory. Go here to bump up clock rate and handle
        * wake up conditions.
        */
        mov ip, lr @ persevere link reg across call
        bl lowlevel_init @ go setup pll,mux,memory

/ *
        *lowlevel_init 这个函数在的文件当中
        *定义
        * /
        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
        /* r5 has always zero */
        mov        r5, #0
        ldr        r8, =S5PC100_GPIO_BASE
        /* Disable Watchdog */
        ldr        r0, =S5PC100_WATCHDOG_BASE        @0xEA200000
        orr r0, r0, #0x0
        str r5, [r0]

#ifndef CONFIG_ONENAND_IPL
        /* setting SRAM */
        ldr        r0, =S5PC100_SROMC_BASE
        ldr        r1, =0x9
        str        r1, [r0]
        #endif

/* S5PC100 has 3 groups of interrupt sources */
        ldr r0, =S5PC100_VIC0_BASE        @0xE4000000
        ldr r1, =S5PC100_VIC1_BASE        @0xE4000000
        ldr r2, =S5PC100_VIC2_BASE        @0xE4000000

/* Disable all interrupts (VIC0, VIC1 and VIC2) */
        mvn        r3, #0x0
        str        r3, [r0, #0x14]        @INTENCLEAR
        str        r3, [r1, #0x14]        @INTENCLEAR
        str        r3, [r2, #0x14]        @INTENCLEAR
        #ifndef CONFIG_ONENAND_IPL
        /* Set all interrupts as IRQ */
        str        r5, [r0, #0xc]        @INTSELECT
        str        r5, [r1, #0xc]        @INTSELECT
        str        r5, [r2, #0xc]        @INTSELECT
        /* Pending Interrupt Clear */
        str        r5, [r0, #0xf00]        @INTADDRESS
        str        r5, [r1, #0xf00]        @INTADDRESS
        str        r5, [r2, #0xf00]        @INTADDRESS
        #endif
        #ifndef CONFIG_ONENAND_IPL
        /* for UART */
        bl uart_asm_init
        /* for TZPC */
        bl tzpc_asm_init
        #endif
        #ifdef CONFIG_ONENAND_IPL
        /* init system clock */
        bl        system_clock_init
        bl        mem_ctrl_asm_init
        /* Wakeup support. Don't know if it's going to be used, untested. */
        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
                /* Pointer is writable since we allocated a register for it */
                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行做了如下宏定义:
        * /
        /* 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);
        // 初始化malloc和env的内存
        #ifndef CONFIG_SYS_NO_FLASH // 条件不成立,接下来的代码不执行.
        /* 在include/configs/smdkc100.h中定义的宏为1 #define CONFIG_SYS_NO_FLASH         1
        /* 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)
        /* #undef CONFIG_CMD_NAND 这个宏没有定义,条件不成立,后面的代码不执行 */
        puts ("NAND: ");
        nand_init();        /* go init the NAND */
        #endif
        #if defined(CONFIG_CMD_ONENAND)        // 这个宏定义了,下面的代码能够执行
        onenand_init();
        #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_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
        /* main_loop() can return to retry autoboot, if so just run it again. */
        for (;;) {
                main_loop ();
                }
        /* NOTREACHED - no way out of command loop except booting */
        }
0 0
原创粉丝点击