zoukankan      html  css  js  c++  java
  • 文章标题

    声明:

    本文转载自《U-BOOT移植过程详解: u-boot.bin》
    

    u-boot.bin

    这里的u-boot.bin指的是不包含SPL的stage2部分的代码. 它会被SPL搬移到RAM的某个地址处开始运行. 本篇下面提到的u-boot.bin时, 也是指的这个概念.

    代码分析

    u-boot.lds: 它的位置在上文中我们分析了
    根据u-boot.lds中的规则, 与SPL阶段一样, CPUDIR/start.o被放在了最前面. 它与SPL阶段对应的是同一个文件arch/arm/cpu/armv7/start.S
    start.S
    与SPL中start.S的执行流程基本一致, 不同的地方是, 这里的start.S会负责处理异常中断.

    ctr0.S

    _main

    ENTRY(_main)  
    
    /*  
     * Set up initial C runtime environment and call board_init_f(0).  
     */  
    
    #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)  
        ldr sp, =(CONFIG_SPL_STACK)  
    #else  
        ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)  
    #endif  
        bic sp, sp, #7  /* 8-byte alignment for ABI compliance */  
        sub sp, #GD_SIZE    /* allocate one GD above SP */  
        bic sp, sp, #7  /* 8-byte alignment for ABI compliance */  
        mov r9, sp      /* GD is above SP */  
        mov r0, #0  
        bl  board_init_f  

    sp, gd相关, 参考SPL中的_main
    bl board_init_f: 跳转到board_init_f
    SPL最后也是跳转到board_init_f, 它们有什么不一样吗? 是的, 完全不一样. 由于SPL阶段与u-boot.bin阶段编译选项的不同, 会导致不同的c文件被编译. 这里的board_init_f实现函数与SPL阶段board_init_f的实现函数不是同一个.

    #if ! defined(CONFIG_SPL_BUILD)  
    
    /*  
     * Set up intermediate environment (new sp and gd) and call  
     * relocate_code(addr_moni). Trick here is that we'll return  
     * 'here' but relocated.  
     */  
    
        ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */  
        bic sp, sp, #7  /* 8-byte alignment for ABI compliance */  
        ldr r9, [r9, #GD_BD]        /* r9 = gd->bd */  
        sub r9, r9, #GD_SIZE        /* new GD is below bd */  
    
        adr lr, here  
        ldr r0, [r9, #GD_RELOC_OFF]     /* r0 = gd->reloc_off */  
        add lr, lr, r0  
        ldr r0, [r9, #GD_RELOCADDR]     /* r0 = gd->relocaddr */  
        b   relocate_code  
    here:  
    
    /* Set up final (full) environment */  
    
        bl  c_runtime_cpu_setup /* we still call old routine here */  
    
        ldr r0, =__bss_start    /* this is auto-relocated! */  
        ldr r1, =__bss_end      /* this is auto-relocated! */  
    
        mov r2, #0x00000000     /* prepare zero to clear BSS */  
    
    clbss_l:cmp r0, r1          /* while not at end of BSS */  
        strlo   r2, [r0]        /* clear 32-bit BSS word */  
        addlo   r0, r0, #4      /* move to next */  
        blo clbss_l  
    
        bl coloured_LED_init  
        bl red_led_on  
    
        /* call board_init_r(gd_t *id, ulong dest_addr) */  
        mov     r0, r9                  /* gd_t */  
        ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */  
        /* call board_init_r */  
        ldr pc, =board_init_r   /* this is auto-relocated! */  
    
        /* we should not return here. */  
    
    #endif  
    #if ! defined(CONFIG_SPL_BUILD) : 说明只有在u-boot.bin阶段, 才会执行这段代码. 这里也与SPL阶段不一样
    b relocate_code : 把代码relocate到编译地址处
    ldr pc, =board_init_r: 调用board_init_r, 同样, 该函数实现的地方与SPL阶段的也不一样.
    arch/arm/lib/board.c
    

    board_init_f

    gd的定义在board.c里面, 有一行: DECLARE_GLOBAL_DATA_PTR;
    memset清零, 说明从这里开始, 重新对gd进行初始化.
    那我们在前面的代码很看到很多对gd的操作啊, 有什么用呢? 主要是在一些汇编代码里面引用了gd的相关变量. 哪些变量可能被引用到呢? 在这里 lib/asm-offsets.c

    #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); 

    fdt相关, 类似于linux内核里面的dtb. 暂不分析

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

    依次调用init_sequence里面的函数, 如果某个函数执行失败, 即返回值不为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  

    fdt相关, 暂不分析

    #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  

    隐藏一块MEM, uboot不会touch它. 同样也可以做到让linux kernel不touch它.
    一般ppc或者powerpc体系架构才会用到这个

     addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;  
    
    #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);  
        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 */  
        gd->arch.tlb_size = 4096 * 4;  
        addr -= gd->arch.tlb_size;  
    
        /* round down to next 64 kB limit */  
        addr &= ~(0x10000 - 1);  
    
        gd->arch.tlb_addr = addr;  
        debug("TLB table from %08lx to %08lx
    ", addr, addr + gd->arch.tlb_size);  
    #endif  
    
        /* round down to next 4 kB limit */  
        addr &= ~(4096 - 1);  
        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;  
        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 = (bd_t *) addr_sp;  
        gd->bd = bd;  
        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);  
        id = (gd_t *) addr_sp;  
        debug("Reserving %zu Bytes for Global Data at: %08lx
    ",  
                sizeof (gd_t), addr_sp);  
    
    #if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)  
        /*  
         * If the device tree is sitting immediate above our image then we  
         * must relocate it. If it is embedded in the data section, then it  
         * will be relocated with other data.  
         */  
        if (gd->fdt_blob) {  
            fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);  
    
            addr_sp -= fdt_size;  
            new_fdt = (void *)addr_sp;  
            debug("Reserving %zu Bytes for FDT at: %08lx
    ",  
                  fdt_size, addr_sp);  
        }  
    #endif  
    
        /* setup stackpointer for exeptions */  
        gd->irq_sp = addr_sp;  
    #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  
        interrupt_init();  
    
        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;  
        gd->start_addr_sp = addr_sp;  
        gd->reloc_off = addr - _TEXT_BASE;  
        debug("relocation Offset is: %08lx
    ", gd->reloc_off);  
        if (new_fdt) {  
            memcpy(new_fdt, gd->fdt_blob, fdt_size);  
            gd->fdt_blob = new_fdt;  
        }  
        memcpy(id, (void *)gd, sizeof(gd_t));  

    这一段主要是在做内存分配动作.
    addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 从RAM的顶部地址开始
    LOGBUFF_RESERVE : Reserving %dk for kernel logbuffer
    pram : Reserving %ldk for protected RAM
    tlb_addr: reserve TLB table(16K), 然后会把地址对齐到64K处.
    addr &= ~(4096 - 1): 做完上面的内存分配动作之后, 在保留4KB的空间. 干嘛用还不清楚.
    relocaddr: 在接来下的RAM就是U-boot可以用的了 : Top of RAM usable for U-Boot
    LCD
    如果用户定义了CONFIG_FB_ADDR, 那这里就不为LCD保留显存了
    如果用户定义了CONFIG_LCD, 又没有定义CONFIG_FB_ADDR, 则为LCD保留一块显存
    Code, data, bss : reserve memory for U-Boot code, data & bss
    malloc : reserve memory for malloc() arena, 突然想起了堆和栈的区别, 网上查了一下, 用户自己分配和释放的属于堆区. 貌似这个地方就是堆区哦.
    bd : Board Info struct
    gd: Global Data
    fdt
    irq_sp : IRQ & FIQ 栈指针. 向下生长
    start_addr_sp : 系统栈指针. 主要是在C函数调用过程中, 函数参数, 返回地址的入栈出栈操作.
    reloc_off : gd->reloc_off = addr - _TEXT_BASE;
    relocate offset, 这里的意思暂时没弄懂
    gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ 这个赋值动作, 记得老版本的uboot是在BOARDDIR下面做的.
    dram_init_banksize : 需要在BOARDDIR下面实现, 告诉系统有几块RAM, 每一块的大小是多少.

    init_sequence

    init_fnc_t *init_sequence[] = {  
        arch_cpu_init,      /* basic arch cpu dependent setup */  
        mark_bootstage,  
    #ifdef CONFIG_OF_CONTROL  
        fdtdec_check_fdt,  
    #endif  
    #if defined(CONFIG_BOARD_EARLY_INIT_F)  
        board_early_init_f,  
    #endif  
        timer_init,     /* initialize timer */  
    #ifdef CONFIG_BOARD_POSTCLK_INIT  
        board_postclk_init,  
    #endif  
    #ifdef CONFIG_FSL_ESDHC  
        get_clocks,  
    #endif  
        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  
    #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)  
        init_func_i2c,  
    #endif  
        dram_init,      /* configure available RAM banks */  
        NULL,  
    };  

    arch_cpu_init
    一般厂商会实现, 根据实际需要初始化CPU相关的一些东西
    如果没有其他实现, 就会用board.c中的weak实现.
    mark_bootstage
    Record the board_init_f() bootstage (after arch_cpu_init())
    fdtdec_check_fdt : fdt相关, 暂不分析
    board_early_init_f
    一般在BOARDDIR下面实现, 初始化必要的硬件
    timer_init
    时钟初始化
    board_postclk_init
    get_clocks
    env_init
    以nandflash为例, 此时还没有对nandflash进行初始化, 所以这个函数的实现不会从nand中去读取实际保存的变量. 只是简单标记env_valid为1
    env_common.c中的env_relocate() will do the real validation
    init_baudrate
    初始化gd->baudrate
    gd->baudrate = getenv_ulong(“baudrate”, 10, CONFIG_BAUDRATE);
    serial_init
    uboot的serial子系统相关
    console_init_f
    stage 1 init of console
    Called before relocation - use serial functions
    print_cpuinfo
    display cpu info (and speed)
    checkboard
    display board info
    dram_init
    初始化gd->ram_size
    接下来的code会做内存分配, 需要用到这个值. 注意, 如果有多块DRAM, 起始地址不一样, 那这里的ram_size应该只是其中1块的大小.
    比如BANK1: 0x20000000 64M; BANK2: 0x40000000 64M; 那这个ram_size应该是64M, 而不是128M. 具体原因, 可以看上文中的内存分配动作

    board_init_r

    gd->flags |= GD_FLG_RELOC;   /* tell others: relocation done */  

    做过relocate之后, 才调用的board_init_r

    board_init();   /* Setup chipselects */  

    board_init需要在BOARDDIR中实现.

    serial_initialize();  

    uboot的serial子系统相关. 完成实际的串口初始化工作

    /* 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);  

    dest_addr是_main的汇编代码传递进来的参数 GD_RELOCADDR.
    GD_RELOCADDR是在board_init_f阶段被赋值的.

    power_init_board();  

    可以选择性的在BOARDDIR下实现.
    如果没有实现, 则会使用board.c中的weak实现

    nand_init(); 

    nandflash初始化, 会调用mtd/nand/nand.c中的nand_init

    mmc_initialize  

    sd卡初始化, 会调用drivers/mmc/mmc.c中的mmc_initialize

    env_relocate 

    board_init_f->init_sequence中的env_init其实没有做什么实质性动作, 因为当时nand还没有初始化
    所以这里的env_relocate会调用common/env_common.c中的env_relocate, 继而开始做实质性的env初始动作. 也就是从nandflash中读取我们自己设置的环境变量

    stdio_init 

    调用common/stdio.c中的stdio_init, 做一些IO相关的初始化
    lcd, video, keyboard, nc, jtag等等

    jumptable_init

    调用include/_exports.h中定义的各种通用的操作函数
    get_version, getc, malloc, udelay等等

    console_init_r();   /* fully init console as a device */ 

    控制台最终初始化

    #ifdef CONFIG_BOARD_LATE_INIT  
        board_late_init();  
    #endif  

    可以选择性的在BOARDDIR中定义board_late_init, 做一些最后的初始化动作

    #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 

    在BOARDDIR中实现eth_initialize, 初始化网络
    在BOARDDIR中实现reset_phy, 复位phy.

    for (;;) {  
        main_loop();  
    }  

    进入common/main.c中的main_loop
    如果没有按下空格键, uboot会执行bootcmd中的命令
    如果按下空格键, 则会进入控制台, 与用户交互, 执行命令

    总结

    stage1 vs stage2
    stage1也就是SPL, stage2也就是本篇所指的u-boot.bin, 主要有以下不同点
    u-boot.bin在start.S中会对异常中断进行响应
    u-boot.bin中的board_init_f实现与SPL阶段不一样
    u-boot.bin中的board_init_r实现与SPL阶段不一样
    global_data
    u-boot.bin会对全局数据gd先清理, 然后在重新初始化.

    需要实现的函数
    dram_init_banksize: BOARDDIR下实现
    告诉系统有几块RAM, 每一块的大小是多少.
    board_early_init_f : BOARDDIR下实现
    board_init : BOARDDIR下实现
    eth_initialize : BOARDDIR下实现

  • 相关阅读:
    flutter Sliver滑动视图组件
    Ionic4.x、Cordova Android 检测应用版本号、服务器下载文件以及实现App自动升级、安装
    flutter SnackBar 底部消息提示
    Flutter ExpansionPanel 可展开的收缩控件
    Ionic4 Cordova 调用原生硬件 Api 实现扫码功能
    Flutter BottomSheet底部弹窗效果
    Flutter 中AlertDialog确认提示弹窗
    Ionic Cordova 调用原生 Api 实现拍照上传 图片到服务器功能
    Flutter 中SimpleDialog简单弹窗使用
    Springboot项目mysql日期存储不匹配问题和在idea本地可以运行起来,但打包jar后运行报找不到mysql驱动的解决方案
  • 原文地址:https://www.cnblogs.com/xxg1992/p/6636380.html
Copyright © 2011-2022 走看看