unoot的移植和第一阶段源码分析

    首先是对uboot源码的大致架构进行分析,在建立工程时加入有效的目录文件

board目录

    单板相关,由于不支持2440,所以只能使用 samsung/smdk2410 文件夹,加入smdk2410整个目录下所有的源码

 

Arch目录

    平台相关,arm的cpu类型存在于arch/arm/cpu下,2440属于arm920t, 将arch/arm/cpu/arm920t下的所有源码加入,然后将arch/arm/cpu/arm920t/s3c24x0目录下的所有源码加入

        头文件的加入, arch/arm/include/asm下就是对应各种不同类型cpu的头文件,找到arch\arm\include\asm\arch-s3c24x0目录,将所有源码加入

                                顶层的arch/arm/include/asm目录下也存在很多通用的头文件,全部加入

                                 arch\arm\include\asm\proc-armv目录可能也是通用的目录,将所有头文件加入

        库文件的加入    arch\arm\lib目录下所有的源码加入

 

Include目录

      头文件和开发板的配置文件, 顶层目录 include/下的所有文件加入

                                                    include目录下除了configs 目录下其他目录下的所有的文件都加入

                                                   include/configs 是单板的配置文件,我们只需要加入smdk2410.h

      其他目录下所有的源码都加入其中

 

uboot的配置编译

        首先安装交叉编译工具链 arm-linux-gcc-4.3.2.tar.bz2     解压命令    sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C  /    可以直接解压到/usr目录下,并且自动建立软链接arm-linux-gcc,

        紧接着配置环境变量

        配置: make smdk2410_config

        编译: make

 

uboot源码分析 

    首先了解生成的uboot是有哪些文件组成,将已经编译完成的uboot删除,重新执行make命令,这样以前的编译,汇编等命令不会再执行,主要会执行链接等命令,就可以看到 链接的文件情况

       arm-linux-ld  -pie -T u-boot.lds -Bstatic -Ttext 0x0 $UNDEF_SYM arch/arm/cpu/arm920t/start.o --start-group api/libapi.o arch/arm/cpu/arm920t/libarm920t.o arch/arm/cpu/arm920t/s3c24x0/libs3c24x0.o arch/arm/lib/libarm.o common/libcommon.o disk/libdisk.o drivers/bios_emulator/libatibiosemu.o drivers/block/libblock.o drivers/dma/libdma.o drivers/fpga/libfpga.o drivers/gpio/libgpio.o drivers/hwmon/libhwmon.o drivers/i2c/libi2c.o drivers/input/libinput.o drivers/misc/libmisc.o drivers/mmc/libmmc.o drivers/mtd/libmtd.o drivers/mtd/nand/libnand.o drivers/mtd/onenand/libonenand.o drivers/mtd/spi/libspi_flash.o drivers/mtd/ubi/libubi.o drivers/net/libnet.o drivers/net/phy/libphy.o drivers/pci/libpci.o drivers/pcmcia/libpcmcia.o drivers/power/libpower.o drivers/rtc/librtc.o drivers/serial/libserial.o drivers/spi/libspi.o drivers/twserial/libtws.o drivers/usb/eth/libusb_eth.o drivers/usb/gadget/libusb_gadget.o drivers/usb/host/libusb_host.o drivers/usb/musb/libusb_musb.o drivers/usb/phy/libusb_phy.o drivers/usb/ulpi/libusb_ulpi.o drivers/video/libvideo.o drivers/watchdog/libwatchdog.o fs/cramfs/libcramfs.o fs/ext2/libext2fs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o fs/reiserfs/libreiserfs.o fs/ubifs/libubifs.o fs/yaffs2/libyaffs2.o lib/libfdt/libfdt.o lib/libgeneric.o lib/lzma/liblzma.o lib/lzo/liblzo.o lib/zlib/libz.o net/libnet.o post/libpost.o board/samsung/smdk2410/libsmdk2410.o --end-group /home/book/Par_Uboot/mk_uboot/u-boot-2012.04.01/arch/arm/lib/eabi_compat.o  -L /usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t -lgcc -Map u-boot.map -o u-boot 

    首先从start.s入手

        里面的流程大致就是 设置管理员模式        关看门狗        设置时钟(这里只设置了分频系数,没有初始化时钟)        关mmu    初始化sdram

        在设置栈指针时,这里是个宏定义,里面的值很难推得到,可以采用直接反汇编的方法查看设置的栈大小

                    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)

         对源程序进行反汇编     arm-linux-objdump -D u-boot > uboot.dis      最后可以看到sp =0x30000f80

        紧接着对C函数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);        //gd是指向r8寄存器的    DECLARE_GLOBAL_DATA_PTR宏定义指定的
    /* compiler optimization barrier needed for GCC >= 3.4 */    //这里gd指向栈指针处    0x30000f80
    __asm__ __volatile__("": : :"memory");


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


    gd->mon_len = _bss_end_ofs;    //所有的代码的大小 从text到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) {    //初始化各种模块        IO,定时器,时钟等
        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;    //0x30000000 + 64M  -> 0x34000000


#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    预留16k给TLB */
    addr -= (4096 * 4);


    /* round down to next 64 kB limit 保持64kb对齐*/
    addr &= ~(0x10000 - 1);


    gd->tlb_addr = addr;    //保留16kb给tlb后向下64kb对齐,也就是消除0xffff,0x34000000-0x4000=0x33ffc0000
    debug("TLB table at: %08lx\n", addr);    //对齐后就是 0x33ff0000
#endif


    /* round down to next 4 kB limit */
    addr &= ~(4096 - 1);
    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;        //减去所有段的大小 从text到bss
    addr &= ~(4096 - 1);        //4k对齐,addr指针确定


    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;    //开始计算栈指针,首先减去自己设置的堆内存大小
    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_t结构体
    bd = (bd_t *) addr_sp;
    gd->bd = bd;
    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);    //减去gd结构体
    id = (gd_t *) addr_sp;        //重定位代码的第二个参数就在这里
    debug("Reserving %zu Bytes for Global Data at: %08lx\n",
            sizeof (gd_t), addr_sp);


    /* 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\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;    //8字节对齐后,sp指针确定
#else
    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


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


    relocate_code(addr_sp, id, addr);        //重定位代码,又进入start.s           传入的是栈指针,gd结构体,代码段的链接地址


    /* NOTREACHED - relocate_code() does not return */
}

    

    上面就是一系列的内存分布的设置,主要是确定addr指针和addr_sp的指针,整个内存分布大致如下所示:

            

 

    讲完内存分布就可以分析代码重定位的代码   

relocate_code:
    mov    r4, r0    /* save addr_sp 栈的地址*/
    mov    r5, r1    /* save addr of gd gd结构体的地址*/
    mov    r6, r2    /* save addr of destination 代码要链接的地址*/


    /* Set up the stack                            */
stack_setup:
    mov    sp, r4    //重新修改栈


    adr    r0, _start
    cmp    r0, r6
    beq    clear_bss        /* skip relocation 判断当前代码的起始地址和要链接的代码段地址是否相等,相等就直接清bss否则拷贝代码*/
    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]  从start读出代码,读完r0++  */
    stmia    r1!, {r9-r10}        /* copy to   target address [r1]  把读出的代码存到sdram上,拷贝完r1++   */
    cmp    r0, r2            /* until source end address [r2]  直到全部拷贝完成  */
    blo    copy_loop

    /*当前的拷贝方式只适合nor*/

 

        代码重定位完后还存在一个问题,因为程序的链接地址是0,访问全局变量、静态变量、调用函数时是使"基于0地址编译得到的地址" ,现在把程序复制到了SDRAM需要修改代码,需要把"基于0地址编译得到的地址"改为新地址

  /*
     * fix .rel.dyn relocations
     */
    ldr    r0, _TEXT_BASE        /* r0 <- Text base  r0=0*/
    sub    r9, r6, r0            /* r9 <- relocation offset     重定位的偏移地址(r9 = relocate_addr - 0 =0x33f41000)*/
    ldr    r10, _dynsym_start_ofs    /* r10 <- sym table ofs  dynsym段的偏移地址 r10 = 00073608*/
    add    r10, r10, r0        /* r10 <- sym table in FLASH   r10 = r10 + 0*/
    ldr    r2, _rel_dyn_start_ofs    /* r2 <- rel dyn start ofs  rel段的偏移地址 r2 = 0006b568*/
    add    r2, r2, r0        /* r2 <- rel dyn start in FLASH     r2 = r2 + 0*/
    ldr    r3, _rel_dyn_end_ofs    /* r3 <- rel dyn end ofs    rel段结束的偏移地址  r3 = 00073608*/
    add    r3, r3, r0        /* r3 <- rel dyn end in FLASH     r3 = r3+0*/
fixloop:
    ldr    r0, [r2]        /* r0 <- location to fix up, IN FLASH! r0 = [0006b568] = 0x00000020*/
    add    r0, r0, r9        /* r0 <- location to fix up in RAM        r0 = r0 + r9(链接地址) =0x00000020+0x33f41000= 0x33f41020 */
    ldr    r1, [r2, #4]    // r1 = [0006b568 + 4 ]  = 17
    and    r7, r1, #0xff    // r7 = 17
    cmp    r7, #23            /* relative fixup?  r7 = #23*/
    beq    fixrel            //
    cmp    r7, #2            /* absolute fixup? */
    beq    fixabs
    /* ignore unknown type of fixup */
    b    fixnext
fixabs:        //有些函数是运行时才确定地址的,所以刚开始给0地址, 等到要运行之前给个新地址,再去运行,下面这一段就是实现这个功能的
    /* 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]    // r1 = [0x00000020] = 000001e0  从rel段开始读取
    add    r1, r1, r9     // r1=r1+r9=000001e0 + 0x33f41000 = 33F411E0
fixnext:
    str    r1, [r0]    // [0x33f41020] = 33F411E0     在链接地址处修改对应的地址 例如在flash中0x00000020处的存的是000001e0到sdram中对应就是0x33f41020处变成了33F411E0
    add    r2, r2, #8    /* each rel.dyn entry is 8 bytes */
    cmp    r2, r3        //rel段是否全部拷贝完成
    blo    fixloop
#endif

   

  实现完变量等的地址拷贝之后然后清除bss段

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

 

    调用c函数,跳转到第二阶段(读取内核,启动内核)

    ldr    r0, _board_init_r_ofs
    adr    r1, _start
    add    lr, r0, r1
    add    lr, lr, r9    //找到sdram上board_init_r函数对应的地址
    /* setup parameters for board_init_r 设置函数形参 */
    mov    r0, r5        /* gd_t  gd_t    结构体的地址*/
    mov    r1, r6        /* dest_addr 重定位代码的地址*/
    /* jump to it ... */
    mov    pc, lr            //调用c函数 board_init_r 跳到第二阶段的程序


_board_init_r_ofs:
    .word board_init_r - _start  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值