zoukankan      html  css  js  c++  java
  • 从单片机到系统之--uboot启动arm linux

    UBOOT官网下载地址:http://ftp.denx.de/pub/u-boot/

    很详细的UBOOT解释: https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/html/uboot_starts_analysis.html

    U-BOOT主要作用和执行流程

    ①一句话描述

      U-BOOT对硬件进行前期的初始化并准备堆栈,之后载入内核并向内核传递必要的参数,便于内核启动。

    ②执行流程概况

      u-boot载入芯片后nor flash从0地址开始执行,nand flash会将前4K考入芯片的内存然后从0地址开始执行,程序最还是从_start标签开始(lds文件定义,start.s有具体实现)。

      u-boot一般启动分为两阶段:1.硬件相关的用汇编实现,初始化及重定位代码完成后交给第二阶段 2.第二阶段较为通用,使用c代码编写。

    1. UBoot内存划分, lds文件

      内存划分存在于对应芯片架构中的u-boot.lds文件中,这个文件是用于连接器连接时对程序各个段空间进行划分之用。

    
    

    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    OUTPUT_ARCH(arm)
    ENTRY(_start)      //程序开始标号,既程序从标号为_start的位置开始执行,这个标号在对应CPU(如arm920t)的start.s文件中,接下来程序分析将分析这个文件

    SECTIONS
    {
        . = 0x00000000;   //程序最开始的地址为0
    
        . = ALIGN(4);    //4字节地址对齐
        .text :        //文本段开始位置
        {
            __image_copy_start = .;  //拷贝image开始的地方(其实也是0)
            CPUDIR/start.o (.text)    //保证start.s的text段放在最前面
            *(.text)            //u-boot中所有程序的文本段都会放到这个位置
        }
    
        . = ALIGN(4);      //4字节对齐
        .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }    //只读数据从这里开始存放
    
        . = ALIGN(4);
        .data : {
            *(.data)        //所有文件的数据段
        }
    
        . = ALIGN(4);
    
        . = .;    //不i知道,可能重定位一下定位器的位置比较好玩儿吧
        __u_boot_cmd_start = .;  //同上,定义了一个宏,保存当前位置信息
        .u_boot_cmd : { *(.u_boot_cmd) }    //存储u_boot_cmd
        __u_boot_cmd_end = .;
    
        . = ALIGN(4);
    
        __image_copy_end = .;    
    
        .rel.dyn : {
            __rel_dyn_start = .;
            *(.rel*)
            __rel_dyn_end = .;
        }
    
        .dynsym : {
            __dynsym_start = .;
            *(.dynsym)
        }
    
        _end = .;
    
        /*
         * Deprecated: this MMU section is used by pxa at present but
         * should not be used by new boards/CPUs.
         */
        . = ALIGN(4096);
        .mmutable : {
            *(.mmutable)
        }
    
        .bss __rel_dyn_start (OVERLAY) : {
            __bss_start = .;
            *(.bss)
             . = ALIGN(4);
            __bss_end__ = .;
        }
    
        /DISCARD/ : { *(.dynstr*) }
        /DISCARD/ : { *(.dynamic*) }
        /DISCARD/ : { *(.plt*) }
        /DISCARD/ : { *(.interp*) }
        /DISCARD/ : { *(.gnu*) }
    }

      

    2. 按代码执行流程分析各个函数

      2.1 主要功能

    1. 设置CPU模式
    2. 关闭看门狗
    3. 关闭中断
    4. 设置堆栈sp指针
    5. 清除bss段
    6. 异常中断处理

      2.2 主要函数

    函数入口:

    .globl _start    //全局声明,在lds中定义为entry
    _start:    b    start_code      //主要执行函数,进入代码后直接跳转到start_code
        ldr    pc, _undefined_instruction  //ldr,将地址导入寄存器,pc为程序指针。导入pc后会进行调用跳转
        ldr    pc, _software_interrupt
        ldr    pc, _prefetch_abort
        ldr    pc, _data_abort
        ldr    pc, _not_used
        ldr    pc, _irq
        ldr    pc, _fiq

     初始化函数:start_code

    start_code:
        /*
         * set the cpu to SVC32 mode
         */
        mrs    r0, cpsr      //cspr值存入寄存器,方便修改
        bic    r0, r0, #0x1f    //将低5位【4:0】清零
        orr    r0, r0, #0xd3    //或操作,进行置位
        msr    cpsr, r0        //修改后的cspr放入寄存器
    /*以上为设置cspr值的代码段,使系统进入SVC方式*/
    #if    defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
        /*
         * relocate exception table
         */
        ldr    r0, =_start  //标号地址放入r0,重载时确定起始位置
        ldr    r1, =0x0    //地址0x00放入r1, 重载后为0地址
        mov    r2, #16     //循环次数计数, 16*4  存前64字节?
    copyex:
        subs    r2, r2, #1  //减数做循环
        ldr    r3, [r0], #4  //r0地址存入r3,并且r9加4
        str    r3, [r1], #4  //r3地址数据(start开始的地方),放入r1的地址(0地址),r1加4
        bne    copyex    //r2不为0则循环写数据,因为r2做了减法,cspr状态值改变只会因为r2的操作产生
    #endif
    
    #ifdef CONFIG_S3C24X0
        /* turn off the watchdog */
    
    # if defined(CONFIG_S3C2400)
    #  define pWTCON    0x15300000
    #  define INTMSK    0x14400008    /* Interrupt-Controller base addresses */
    #  define CLKDIVN    0x14800014    /* clock divisor register */
    #else
    #  define pWTCON    0x53000000
    #  define INTMSK    0x4A000008    /* Interrupt-Controller base addresses */
    #  define INTSUBMSK    0x4A00001C
    #  define CLKDIVN    0x4C000014    /* clock divisor register */
    # endif
    
        ldr    r0, =pWTCON    //看门狗地址存入r0,查datasheet
        mov    r1, #0x0      //0存入r1
        str    r1, [r0]      //看门狗地址存入0,既关看门狗
    
        /*
         * mask all IRQs by setting all bits in the INTMR - default
         */
        mov    r1, #0xffffffff
        ldr    r0, =INTMSK
        str    r1, [r0]    //中断地址存入全1,禁止中断
    # if defined(CONFIG_S3C2410)
        ldr    r1, =0x3ff
        ldr    r0, =INTSUBMSK
        str    r1, [r0]
    # endif
    
        /* FCLK:HCLK:PCLK = 1:2:4 */
        /* default FCLK is 120 MHz ! */
        ldr    r0, =CLKDIVN
        mov    r1, #3
        str    r1, [r0]  //修改时钟参数
    #endif    /* CONFIG_S3C24X0 */
    
        /*
         * we do sys-critical inits only at reboot,
         * not when booting from ram!
         */
    #ifndef CONFIG_SKIP_LOWLEVEL_INIT
        bl    cpu_init_crit    //非从ram启动时会调用这个函数
    #endif
    
    /* Set stackpointer in internal RAM to call board_init_f */
    call_board_init_f:    //设置了lowlevel初始化从这里初始化板子
        ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)    //设置栈顶指针,宏在config.mk
        bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */ //清除后四位,8字节对齐
        ldr    r0,=0x00000000
        bl    board_init_f    //调用函数,bl相对于b调用区别:会填充R4(lr)寄存器,调用结束会返回,函数在board.c中。返回后重新执行下面的函数,正好对应c中的relocate_code
                     //
    cpu_init_crit最终也会调用这个函数  
     

     

    非从RAM启动的情况会执行这个函数:cpu_init_crit

    cpu_init_crit:
        /*
         * flush v4 I/D caches
         */
        mov    r0, #0
        mcr    p15, 0, r0, c7, c7, 0    //关闭ICaches(指令缓存,关闭是为了降低MMU查表带来的开销)和DCaches(数据缓存,DCaches使用的是虚拟地址,开启MMU之前必须关闭)
        mcr    p15, 0, r0, c8, c7, 0    //使无效整个数据TLB和指令TLB(TLB就是负责将虚拟内存地址翻译成实际的物理内存地址
        /*
         * disable MMU stuff and caches
         */
      mrc    p15, 0, r0, c1, c0, 0
      bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS) //bit8:系统不保护,bit9:ROM不保护,bit13:设置中断向量表的位置为0x0~0x1c,即异常模式基地址为0X0
      bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM) //bit0~2:禁止MMU,禁止地址对齐检查,禁止数据Cache.bit7:设为小端模式
      orr    r0, r0, #0x00000002    @ set bit 2 (A) Align //bit2:开启数据Cache
      orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache //bit12:开启指令Cache
      mcr    p15, 0, r0, c1, c0, 0
        /*
         * before relocating, we have to setup RAM timing
         * because memory timing is board-dependend, you will
         * find a lowlevel_init.S in your board directory.
         */
        mov    ip, lr    //保存lr的值,用于返回调用cpu_init_crit的函数处,用于调用board_init_f
    bl lowlevel_init //函数在对应的board文件夹中,第二阶段启动代码,主要作用是初始化两个重要数据结构,对SDRAM的内存分配设置,对各种需要用到的外设进行初始化,最后循环跳入main_loop()函数
    mov lr, ip
    mov pc, lr

     board_init_f函数

    涉及到两个重要的数据结构:1)bd_t结构体,关于开发板信息(波特率,ip, 平台号,启动参数)。2)gd_t结构体成员主要是一些全局的系统初始化参数。需要用到时,用宏定义DECLARD_GLOBAL_DATA_PTT,指定占用寄存器r8,具体定义如下:
    
    typedef struct bd_info {
                     int                  bi_baudrate; /* serial console baudrate串口波特率 */
                     unsigned long                  bi_ip_addr; /* IP Address IP 地址*/
                     ulong                  bi_arch_number; /* unique id for this board 板子的id */
                     ulong                  bi_boot_params; /* where this board expects params 启动参数*/
                     struct                  /* RAM configuration RAM 配置*/
                     { 
                     ulong start;
                     ulong size;
                     }                  bi_dram[CONFIG_NR_DRAM_BANKS];
             } bd_t;
    Gd_t结构体定义,如下:
            typedef struct global_data {
                    bd_t *bd;
                    unsigned long flags; //指示标志,如设备已经初始化标志等
                    unsigned long baudrate; //串行口通信速率
                    unsigned long have_console; /* serial_init() was called */
              #ifdef CONFIG_PRE_CONSOLE_BUFFER
                    unsigned long precon_buf_idx; /* Pre-Console buffer index */
              #endif
                    unsigned long env_addr; /* Address of Environment struct 环境参数地址 */
                         unsigned long env_valid; /* Checksum of Environment valid? 环境参数CRC检验有效标志*/
                    unsigned long fb_base; /* base address of frame buffer 帧缓冲区基地址*/
             ……
             } gd_t;
    

     

    c函数,计算重定位后地址分配,填充bd和gd数据:board_init_f
    void board_init_f(ulong bootflag)
    {
        bd_t *bd;
        init_fnc_t **init_fnc_ptr;
        gd_t *id;
        ulong addr, addr_sp;
    #ifdef CONFIG_PRAM
        ulong reg;
    #endif
    
        bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");
    
        /* Pointer is writable since we allocated a register for it */
        gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
        /* compiler optimization barrier needed for GCC >= 3.4 */
        __asm__ __volatile__("": : :"memory");   /*内存屏障,防止编译器优化赋值顺序*/
    
        memset((void *)gd, 0, sizeof(gd_t));
    
        gd->mon_len = _bss_end_ofs;   /*_bss_end_ofs的定义在start.S中: .globl _bss_end_ofs,这里计算出来了U-BOOT的大小,用于后面做减法计算重定位的起始地址*/
    #ifdef CONFIG_OF_EMBED
        /* Get a pointer to the FDT */
        gd->fdt_blob = _binary_dt_dtb_start;
    #elif defined CONFIG_OF_SEPARATE
        /* FDT is at end of image */
        gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
    #endif
        /* Allow the early environment to override the fdt address */
        gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
                            (uintptr_t)gd->fdt_blob);
    
        for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {  /*初始化函数的数组,会逐个调用数组中的初始化函数*/
            if ((*init_fnc_ptr)() != 0) {
                hang ();
            }
        }
    
    #ifdef CONFIG_OF_CONTROL
        /* For now, put this check after the console is ready */
        if (fdtdec_prepare_fdt()) {
            panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
                "doc/README.fdt-control");
        }
    #endif
    
        debug("monitor len: %08lX
    ", gd->mon_len);
        /*
         * Ram is setup, size stored in gd !!
         */
        debug("ramsize: %08lX
    ", gd->ram_size);
    #if defined(CONFIG_SYS_MEM_TOP_HIDE)
        /*
         * Subtract specified amount of memory to hide so that it won't
         * get "touched" at all by U-Boot. By fixing up gd->ram_size
         * the Linux kernel should now get passed the now "corrected"
         * memory size and won't touch it either. This should work
         * for arch/ppc and arch/powerpc. Only Linux board ports in
         * arch/powerpc with bootwrapper support, that recalculate the
         * memory size from the SDRAM controller setup will have to
         * get fixed.
         */
        gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
    #endif
    
        addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;   /*这样addr是内存的高地址,0x30000000+256M, 关于gd->ram_size,在初始化函数dram_init中,已经给过值了*/
    
    #ifdef CONFIG_LOGBUFFER
    #ifndef CONFIG_ALT_LB_ADDR
        /* reserve kernel log buffer */
        addr -= (LOGBUFF_RESERVE);
        debug("Reserving %dk for kernel logbuffer at %08lx
    ", LOGBUFF_LEN,
            addr);
    #endif
    #endif
    
    #ifdef CONFIG_PRAM
        /*
         * reserve protected RAM
         */
        reg = getenv_ulong("pram", 10, CONFIG_PRAM);    /*从环境变量获取代码地址,pram*/
        addr -= (reg << 10);        /* size is in kB */
        debug("Reserving %ldk for protected RAM at %08lx
    ", reg, addr);
    #endif /* CONFIG_PRAM */
    
    #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
        /* reserve TLB table */
        addr -= (4096 * 4);
    
        /* round down to next 64 kB limit */
        addr &= ~(0x10000 - 1);   //64K对齐
    
        gd->tlb_addr = addr;
        debug("TLB table at: %08lx
    ", addr);
    #endif
    
        /* round down to next 4 kB limit */
        addr &= ~(4096 - 1);  // K对齐,此处前面已经64K对齐了,就不需改动
        debug("Top of RAM usable for U-Boot at: %08lx
    ", addr);
    
    #ifdef CONFIG_LCD
    #ifdef CONFIG_FB_ADDR
        gd->fb_base = CONFIG_FB_ADDR;
    #else
        /* reserve memory for LCD display (always full pages) */
        addr = lcd_setmem(addr);
        gd->fb_base = addr;
    #endif /* CONFIG_FB_ADDR */
    #endif /* CONFIG_LCD */
    
        /*
         * reserve memory for U-Boot code, data & bss
         * round down to next 4 kB limit
         */
        addr -= gd->mon_len;  /*重定位地址再减去U-BOOT的大小,此时就是重定位时代码要拷贝到的目的位置*/
        addr &= ~(4096 - 1);
    
        debug("Reserving %ldk for U-Boot at: %08lx
    ", gd->mon_len >> 10, addr);
    
    #ifndef CONFIG_SPL_BUILD
        /*
         * reserve memory for malloc() arena
         */
        addr_sp = addr - TOTAL_MALLOC_LEN;   /*当前地址减去预留给堆的空间后,作为栈的初始地址*/
        debug("Reserving %dk for malloc() at: %08lx
    ",
                TOTAL_MALLOC_LEN >> 10, addr_sp);
        /*
         * (permanently) allocate a Board Info struct
         * and a permanent copy of the "global" data
         */
        addr_sp -= sizeof (bd_t);  /*栈中用于放bd_t的数据,预留出足够的空间*/
        bd = (bd_t *) addr_sp;
        gd->bd = bd;   /*bd指向新分配的地址,代码重定位把数据copy过去后可以直接使用地址指针*/
        debug("Reserving %zu Bytes for Board Info at: %08lx
    ",
                sizeof (bd_t), addr_sp);
    
    #ifdef CONFIG_MACH_TYPE
        gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
    #endif
    
        addr_sp -= sizeof (gd_t);  /*栈顶指针减法,预留出gd_t的空间,参考bd_t*/
        id = (gd_t *) addr_sp;
        debug("Reserving %zu Bytes for Global Data at: %08lx
    ",
                sizeof (gd_t), addr_sp);
    
        /* setup stackpointer for exeptions */
        gd->irq_sp = addr_sp;  /*此时栈顶的地址放入gd结构体,作为ird中断使用的栈顶地址*/
    #ifdef CONFIG_USE_IRQ
        addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
        debug("Reserving %zu Bytes for IRQ stack at: %08lx
    ",
            CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
    #endif
        /* leave 3 words for abort-stack    */
        addr_sp -= 12;  /*为中断预留的栈空间*/
    
        /* 8-byte alignment for ABI compliance */
        addr_sp &= ~0x07;
    #else
        addr_sp += 128;    /* leave 32 words for abort-stack   */
        gd->irq_sp = addr_sp;
    #endif
    
        debug("New Stack Pointer is: %08lx
    ", addr_sp);
    
    #ifdef CONFIG_POST
        post_bootmode_init();
        post_run(NULL, POST_ROM | post_bootmode_get(0));
    #endif
    
        gd->bd->bi_baudrate = gd->baudrate;
        /* Ram ist board specific, so move it to board code ... */
        dram_init_banksize();
        display_dram_config();    /* and display it */
    
        gd->relocaddr = addr;    /*u-boot重新搬运后的起始地址。*/
        gd->start_addr_sp = addr_sp;
        gd->reloc_off = addr - _TEXT_BASE;
        debug("relocation Offset is: %08lx
    ", gd->reloc_off);
        memcpy(id, (void *)gd, sizeof(gd_t));
    
        relocate_code(addr_sp, id, addr);  /*relocate_code(addr_sp, id, addr);在start.S中定义,C又回到了汇编*/
    
        /* NOTREACHED - relocate_code() does not return */
    }

     

    被c函数调用,用于重定位代码,存在于start.s中:relocate_code
        .globl    relocate_code
    relocate_code:
        mov    r4, r0    /* save addr_sp */
        mov    r5, r1    /* save addr of gd */
        mov    r6, r2    /* save addr of destination */
      /*以上为保存传入的3个参数*/
        /* Set up the stack                            */
    stack_setup:
        mov    sp, r4
    
        adr    r0, _start
        cmp    r0, r6
        beq    clear_bss        /* skip relocation */
        mov    r1, r6            /* r1 <- scratch for copy_loop */
        ldr    r3, _bss_start_ofs
        add    r2, r0, r3        /* r2 <- source end address        */
    
    copy_loop:
        ldmia    r0!, {r9-r10}        /* copy from source address [r0]    */
        stmia    r1!, {r9-r10}        /* copy to   target address [r1]    */
        cmp    r0, r2            /* until source end address [r2]    */
        blo    copy_loop
    /*上述代码将U-BOOT拷贝到了新的重定位后的位置,即上面片段计算出来的addr的地址*/
    #ifndef CONFIG_SPL_BUILD
        /*
         * fix .rel.dyn relocations
         */
    /*使用 .rel.dyn字段进行重定位*/
    ldr r0, _TEXT_BASE /* r0 <- Text base */ sub r9, r6, r0 /* r9 <- relocation offset */ ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ add r10, r10, r0 /* r10 <- sym table in FLASH */ ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ fixloop: ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ add r0, r0, r9 /* r0 <- location to fix up in RAM */ ldr r1, [r2, #4] and r7, r1, #0xff cmp r7, #23 /* relative fixup? */ beq fixrel cmp r7, #2 /* absolute fixup? */ beq fixabs /* ignore unknown type of fixup */ b fixnext fixabs: /* absolute fix: set location to (offset) symbol value */ mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ add r1, r10, r1 /* r1 <- address of symbol in table */ ldr r1, [r1, #4] /* r1 <- symbol value */ add r1, r1, r9 /* r1 <- relocated sym addr */ b fixnext fixrel: /* relative fix: increase location by offset */ ldr r1, [r0] add r1, r1, r9 fixnext: str r1, [r0] add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ cmp r2, r3 blo fixloop #endif clear_bss: #ifndef CONFIG_SPL_BUILD ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs mov r4, r6 /* reloc addr */ add r0, r0, r4 add r1, r1, r4 mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l bl coloured_LED_init bl red_led_on #endif /* * We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */ #ifdef CONFIG_NAND_SPL ldr r0, _nand_boot_ofs mov pc, r0 _nand_boot_ofs: .word nand_boot      //nand启动 #else ldr r0, _board_init_r_ofs adr r1, _start add lr, r0, r1 add lr, lr, r9 /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ mov r1, r6 /* dest_addr */ /* jump to it ... */ mov pc, lr    //这里lr为board_init_r地址 _board_init_r_ofs: .word board_init_r - _start #endif _rel_dyn_start_ofs: .word __rel_dyn_start - _start _rel_dyn_end_ofs: .word __rel_dyn_end - _start _dynsym_start_ofs: .word __dynsym_start - _start
    第二阶段:board_init_r最终调用main_loop解析参数启动内核
    void board_init_r(gd_t *id, ulong dest_addr)
    {
        ulong malloc_start;
    #if !defined(CONFIG_SYS_NO_FLASH)
        ulong flash_size;
    #endif
    
        gd = id;
    
        gd->flags |= GD_FLG_RELOC;    /* tell others: relocation done */
        bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");
    
        monitor_flash_len = _end_ofs;
    
        /* Enable caches */
        enable_caches();
    
        debug("monitor flash len: %08lX
    ", monitor_flash_len);
        board_init();    /* Setup chipselects */  /*设置板级的初始化*/
        /*
         * TODO: printing of the clock inforamtion of the board is now
         * implemented as part of bdinfo command. Currently only support for
         * davinci SOC's is added. Remove this check once all the board
         * implement this.
         */
    #ifdef CONFIG_CLOCKS
        set_cpu_clk_info(); /* Setup clock information */
    #endif
    #ifdef CONFIG_SERIAL_MULTI
        serial_initialize();
    #endif
    
        debug("Now running in RAM - U-Boot at: %08lx
    ", dest_addr);
    
    #ifdef CONFIG_LOGBUFFER
        logbuff_init_ptrs();
    #endif
    #ifdef CONFIG_POST
        post_output_backlog();
    #endif
    
        /* The Malloc area is immediately below the monitor copy in DRAM */
        malloc_start = dest_addr - TOTAL_MALLOC_LEN;  /*剩余出堆内存的空间*/
        mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
    
    #if !defined(CONFIG_SYS_NO_FLASH)
        puts("Flash: ");
    
        flash_size = flash_init();
        if (flash_size > 0) {
    # ifdef CONFIG_SYS_FLASH_CHECKSUM
            char *s = getenv("flashchecksum");
    
            print_size(flash_size, "");
            /*
             * Compute and print flash CRC if flashchecksum is set to 'y'
             *
             * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
             */
            if (s && (*s == 'y')) {
                printf("  CRC: %08X", crc32(0,
                    (const unsigned char *) CONFIG_SYS_FLASH_BASE,
                    flash_size));
            }
            putc('
    ');
    # else    /* !CONFIG_SYS_FLASH_CHECKSUM */
            print_size(flash_size, "
    ");
    # endif /* CONFIG_SYS_FLASH_CHECKSUM */
        } else {
            puts(failed);
            hang();
        }
    #endif
    
    #if defined(CONFIG_CMD_NAND)
        puts("NAND:  ");
        nand_init();        /* go init the NAND */
    #endif
    
    #if defined(CONFIG_CMD_ONENAND)
        onenand_init();
    #endif
    
    #ifdef CONFIG_GENERIC_MMC
           puts("MMC:   ");
           mmc_initialize(gd->bd);
    #endif
    
    #ifdef CONFIG_HAS_DATAFLASH
        AT91F_DataflashInit();
        dataflash_print_info();
    #endif
    
        /* initialize environment */
        env_relocate();
    
    #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
        arm_pci_init();
    #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
    
         /* set up exceptions */
        interrupt_init();
        /* enable exceptions */
        enable_interrupts();
    
        /* Perform network card initialisation if necessary */
    #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 */
        load_addr = getenv_ulong("loadaddr", 16, load_addr);
    #if defined(CONFIG_CMD_NET)
        {
            char *s = getenv("bootfile");
    
            if (s != NULL)
                copy_filename(BootFile, s, sizeof(BootFile));
        }
    #endif
    
    #ifdef CONFIG_BOARD_LATE_INIT
        board_late_init();
    #endif
    
    #ifdef CONFIG_BITBANGMII
        bb_miiphy_init();
    #endif
    #if defined(CONFIG_CMD_NET)
        puts("Net:   ");
        eth_initialize(gd->bd);
    #if defined(CONFIG_RESET_PHY_R)
        debug("Reset Ethernet PHY
    ");
        reset_phy();
    #endif
    #endif
    
    #ifdef CONFIG_POST
        post_run(NULL, POST_RAM | post_bootmode_get(0));
    #endif
    
    #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
        /*
         * Export available size of memory for Linux,
         * taking into account the protected RAM at top of memory
         */
        {
            ulong pram = 0;
            uchar memsz[32];
    
    #ifdef CONFIG_PRAM
            pram = getenv_ulong("pram", 10, CONFIG_PRAM);
    #endif
    #ifdef CONFIG_LOGBUFFER
    #ifndef CONFIG_ALT_LB_ADDR
            /* Also take the logbuffer into account (pram is in kB) */
            pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;
    #endif
    #endif
            sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);
            setenv("mem", (char *)memsz);
        }
    #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 */
    }

     main.c中的main_loop函数

    main_loop函数分析参考这里: https://blog.csdn.net/andy_wsj/article/details/8614905

    执行到启动内核的调用流程:parse_file_outer->parse_stream_outer->run_list->run_list_real->run_pipe_real->cmd_process->(通过注册到u_boot_cmd结构的的do_bootm函数后向下继续调用)->do_bootm->do_bootm_linux->【boot_prep_linux(填充a_tag),boot_jump_linux(控制权移交内核,内核入口从image的laodaddr获取)】,到此u_boot寿终正寝,权限交给内核。

    void main_loop (void)
    {
    #ifndef CONFIG_SYS_HUSH_PARSER
        static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
        int len;
        int rc = 1;
        int flag;
    #endif
    
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
        char *s;
        int bootdelay;
    #endif
    #ifdef CONFIG_PREBOOT
        char *p;
    #endif
    #ifdef CONFIG_BOOTCOUNT_LIMIT
        unsigned long bootcount = 0;
        unsigned long bootlimit = 0;
        char *bcs;
        char bcs_set[16];
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    
    #ifdef CONFIG_BOOTCOUNT_LIMIT
        bootcount = bootcount_load();
        bootcount++;
        bootcount_store (bootcount);
        sprintf (bcs_set, "%lu", bootcount);
        setenv ("bootcount", bcs_set);
        bcs = getenv ("bootlimit");
        bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    
    #ifdef CONFIG_MODEM_SUPPORT
        debug ("DEBUG: main_loop:   do_mdm_init=%d
    ", do_mdm_init);
        if (do_mdm_init) {
            char *str = strdup(getenv("mdm_cmd"));
            setenv ("preboot", str);  /* set or delete definition */
            if (str != NULL)
                free (str);
            mdm_init(); /* wait for modem connection */
        }
    #endif  /* CONFIG_MODEM_SUPPORT */
    
    #ifdef CONFIG_VERSION_VARIABLE
        {
            setenv ("ver", version_string);  /* set version variable */
        }
    #endif /* CONFIG_VERSION_VARIABLE */
    
    #ifdef CONFIG_SYS_HUSH_PARSER
        u_boot_hush_start ();
    #endif
    
    #if defined(CONFIG_HUSH_INIT_VAR)
        hush_init_var ();
    #endif
    
    #ifdef CONFIG_PREBOOT
        if ((p = getenv ("preboot")) != NULL) {
    # ifdef CONFIG_AUTOBOOT_KEYED
            int prev = disable_ctrlc(1);    /* disable Control C checking */
    # endif
    
            run_command(p, 0);
    
    # ifdef CONFIG_AUTOBOOT_KEYED
            disable_ctrlc(prev);    /* restore Control C checking */
    # endif
        }
    #endif /* CONFIG_PREBOOT */
    
    #if defined(CONFIG_UPDATE_TFTP)
        update_tftp (0UL);
    #endif /* CONFIG_UPDATE_TFTP */
    
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
        s = getenv ("bootdelay");
        bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    
        debug ("### main_loop entered: bootdelay=%d
    
    ", bootdelay);
    
    #if defined(CONFIG_MENU_SHOW)
        bootdelay = menu_show(bootdelay);
    #endif
    # ifdef CONFIG_BOOT_RETRY_TIME
        init_cmd_timeout ();
    # endif    /* CONFIG_BOOT_RETRY_TIME */
    
    #ifdef CONFIG_POST
        if (gd->flags & GD_FLG_POSTFAIL) {
            s = getenv("failbootcmd");
        }
        else
    #endif /* CONFIG_POST */
    #ifdef CONFIG_BOOTCOUNT_LIMIT
        if (bootlimit && (bootcount > bootlimit)) {
            printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.
    ",
                    (unsigned)bootlimit);
            s = getenv ("altbootcmd");
        }
        else
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
            s = getenv ("bootcmd");
    
        debug ("### main_loop: bootcmd="%s"
    ", s ? s : "<UNDEFINED>");
    
        if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
    # ifdef CONFIG_AUTOBOOT_KEYED
            int prev = disable_ctrlc(1);    /* disable Control C checking */
    # endif
    
            run_command(s, 0);
    
    # ifdef CONFIG_AUTOBOOT_KEYED
            disable_ctrlc(prev);    /* restore Control C checking */
    # endif
        }
    
    # ifdef CONFIG_MENUKEY
        if (menukey == CONFIG_MENUKEY) {
            s = getenv("menucmd");
            if (s)
                run_command(s, 0);
        }
    #endif /* CONFIG_MENUKEY */
    #endif /* CONFIG_BOOTDELAY */
    
        /*
         * Main Loop for Monitor Command Processing
         */
    #ifdef CONFIG_SYS_HUSH_PARSER
        parse_file_outer();    /*2410走这里,最终通过cmd_process根据命令行的入参调用内核启动,启动函数定义在U_BOOT_CMD结构体定义的结构中*/
        /* This point is never reached */
        for (;;);
    #else
        for (;;) {
    #ifdef CONFIG_BOOT_RETRY_TIME
            if (rc >= 0) {
                /* Saw enough of a valid command to
                 * restart the timeout.
                 */
                reset_cmd_timeout();
            }
    #endif
            len = readline (CONFIG_SYS_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 ("
    Timed out waiting for command
    ");
    # 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>
    ");
            else
                rc = run_command(lastcommand, flag);
    
            if (rc <= 0) {
                /* invalid command or not repeatable, forget it */
                lastcommand[0] = 0;
            }
        }
    #endif /*CONFIG_SYS_HUSH_PARSER*/
    }






  • 相关阅读:
    算法-排序(二)-快速排序
    算法- 排序(一)
    python(十四)新式类和旧式类
    Python(十三)python的函数重载
    django(二)中间件与面向切面编程
    MySQL(二)MySQL的启动或链接失败
    django(一)验证码
    python(七) Python中单下划线和双下划线
    Python(十) Python 中的 *args 和 **kwargs
    python(六)列表推导式、字典推导式、集合推导式
  • 原文地址:https://www.cnblogs.com/edver/p/11229238.html
Copyright © 2011-2022 走看看