uboot20120401简单分析与移植

最近想自己做个摄像头采集图像,然后调用opencv库处理图像的小项目,由于担心uImage太大,超过之前uboot定义的内核烧录大小,所以想着修改下uboot,之后就索性从0开始把uboot从新移植一遍,然后自己写个ov7670的驱动,再写个应用代码,先从uboot移植开始;

 

uboot的运行环境是:编译系统是Ubuntu12.04版本,uboot是20120401版本,交叉编译工具链是4.3.2,linux内核是3.4.2版本的源码,开发板是买的jz2440;

 

分析官方提供的uboot源码:官方提供的是支持s3c2410的uboot,而且是只支持norflash启动的,为了支持nandflash启动,需要修改部分代码;

 

由于官方的uboot是在norflash内启动,所以链接脚本起始地址为0x00,但是实际代码是拷入sdram中运行,sdram起始地址为0x30000000,另外为了适应不同的移植情况编制的代码,所以有些变量的链接地址要再实际运行中才能确定链接地址,所以代码需要重定向才可以正确运行(在链接的时候加入-pie选项);

 

总结下官方uboot的启动过程:设置cpu为超级用户模式、关闭看门狗、屏蔽所有中断、设置时钟分频比例、初始化sdram、调用第一阶段初始化代码、然后重定位、再清除bss代码段、再调用第二阶段初始化代码.........

 

修改uboot新运行流畅如下:设置cpu为超级用户模式、关闭看门狗、屏蔽所有中断、设置系统主时钟、初始化存储管理器、简单初始化nandflash、转移代码到sdram、清除bss段、调用第一阶段初始化代码、调用第二阶段初始化代码.....

 

新代码简单分析:

1.start.s代码:

/* 2. 设置时钟 */
    ldr r0, =0x4c000014
    //    mov r1, #0x03;              // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
    mov r1, #0x05;              // FCLK:HCLK:PCLK=1:4:8
    str r1, [r0]

    /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
    mrc    p15, 0, r1, c1, c0, 0        /* 读出控制寄存器 */ 
    orr    r1, r1, #0xc0000000            /* 设置为“asynchronous bus mode” */
    mcr    p15, 0, r1, c1, c0, 0        /* 写入控制寄存器 */

#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))

    /* MPLLCON = S3C2440_MPLL_200MHZ */
    ldr r0, =0x4c000004
    ldr r1, =S3C2440_MPLL_400MHZ
    str r1, [r0]

    /* 启动ICACHE */
    mrc p15, 0, r0, c1, c0, 0    @ read control reg
    orr r0, r0, #(1<<12)
    mcr    p15, 0, r0, c1, c0, 0   @ write it back


#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
#endif

    ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)    /* sp = 30000f80 */
    bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

    bl nand_init_ll
    mov r0, #0                    //
    ldr r1, _TEXT_BASE            // 目的    
    ldr r2, _bss_start_ofs        // 长度
    
    bl copy_code_to_sdram
    bl clear_bss_ll

    ldr pc, =call_board_init_f
call_board_init_f:
    ldr    r0,=0x00000000
    bl    board_init_f

    /* unsigned int的值存在r0里, 正好给board_init_r */
    ldr r1, _TEXT_BASE
    ldr sp, BASE_SP             /* 重新设置栈 */

    /* 调用第2阶段的代码 */
    bl board_init_r

 

2.第一阶段初始化代码

bd_t *bd;
    init_fnc_t **init_fnc_ptr;
    gd_t *id;
    ulong addr, addr_sp;
#ifdef CONFIG_PRAM
    ulong reg;
#endif
    extern ulong BASE_SP;
    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 );                    // 初始化gd_t结构体存储起始地址,之后需要将这个位置的数据转移到新的gd_t结构体空间
    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__ ( "": : :"memory" );

    memset ( ( void * ) gd, 0, sizeof ( gd_t ) );

    gd->mon_len = _bss_end_ofs;                                                    // 整个bin文件运行需要的内存空间(包括bss段的地址空间)
#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\n", gd->mon_len );
    /*
     * Ram is setup, size stored in gd !!
     */
    debug ( "ramsize: %08lX\n", 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初始值为sdram的最顶端。从最顶端开始分配内存空间 0x3400 0000

#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
    /* reserve kernel log buffer */
    addr -= ( LOGBUFF_RESERVE );
    debug ( "Reserving %dk for kernel logbuffer at %08lx\n", 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\n", reg, addr );
#endif /* CONFIG_PRAM */

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
    /* reserve TLB table */
    addr -= ( 4096 * 4 );                                            // MMU管理模块 16K 存储页表信息 ,addr = 0x33ff c000

    /* round down to next 64 kB limit */
    addr &= ~ ( 0x10000 - 1 );                                       //再减去64K地址(包括前面的16K地址) addr = 0x33ff 0000

    gd->tlb_addr = addr;
    debug ( "TLB table at: %08lx\n", addr );
#endif

    /* round down to next 4 kB limit */
    addr &= ~ ( 4096 - 1 );                                         // 4K对齐
    debug ( "Top of RAM usable for U-Boot at: %08lx\n", 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;                                            // 减去uboot运行需要的空间(text data bss段)
//    addr &= ~ ( 4096 - 1 );                                         // 4K对齐
    addr = 0x33f80000;                                                // 根据链接地址,设置uboot在内存中的地址
    debug ( "Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr );

#ifndef CONFIG_SPL_BUILD
    /*
     * reserve memory for malloc() arena
     */
    addr_sp = addr - TOTAL_MALLOC_LEN;                                // 再减去动态申请内存需要的空间 4*1024*1024 ,设置sp初始值,之后再计算sp的最终值
    debug ( "Reserving %dk for malloc() at: %08lx\n",
            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;                                         // bd指向存储单板信息结构体的起始地址
    gd->bd = bd;                                                     // gb_t成员指向该地址
    debug ( "Reserving %zu Bytes for Board Info at: %08lx\n",
            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;                                          // id指向环境变了结构体起始地址
    debug ( "Reserving %zu Bytes for Global Data at: %08lx\n",
            sizeof ( gd_t ), addr_sp );

    /* setup stackpointer for exeptions */
    gd->irq_sp = addr_sp;                                             // 设置中断向量起始地址(中断向量处理堆栈向下生长,不会影响上面的gd_t结构体空间)
#ifdef CONFIG_USE_IRQ
    addr_sp -= ( CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ );
    debug ( "Reserving %zu Bytes for IRQ stack at: %08lx\n",
            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 /* 处理#ifndef CONFIG_SPL_BUILD */
    addr_sp += 128;    /* leave 32 words for abort-stack   */
    gd->irq_sp = addr_sp;
#endif

    debug ( "New Stack Pointer is: %08lx\n", addr_sp );

#ifdef CONFIG_POST
    post_bootmode_init();
    post_run ( NULL, POST_ROM | post_bootmode_get ( 0 ) );
#endif

    BASE_SP = addr_sp;                                               // 读出最终堆栈指针作为uboot的堆栈指针

    /* 设置环境变量 */
    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\n", gd->reloc_off );
    memcpy ( id, ( void * ) gd, sizeof ( gd_t ) );                     // 转移gd_t结构体到新的存储位置

    return ( unsigned int ) id;                                        // 返回gd_t结构体的起始地址

观察以上代码的中文解释,可以知道第一阶段主要是对sdram划分了不同的区域而已,然后把环境变量、设置好的uboot运行堆栈指针传递出来而已;

 

第二阶段就是针对单板的配置进行一些初始化:比如需要norflash启动、Nandflash启动、网络支持等等,均在第二阶段进行初始化;

 

总结:移植uboot只要清楚处理的流程,按照流程进行各模块的初始化,主要还是硬件的初始化工作。

 

posted @ 2017-08-17 14:18  迷途小菜鸟  阅读(208)  评论(0编辑  收藏  举报