现在的位置: 首页 > 综合 > 正文

MIPS u-boot 中code Relocate流程分析

2018年05月06日 ⁄ 综合 ⁄ 共 6548字 ⁄ 字号 评论关闭

u-boot的强大就在于它具有代码relocate功能,运行时代码在ram中跑,最明显的一个好处就是u-boot可以自己替换自己固化在flash中的代码。
        先看一下这套代码中和relocate有关的几个大名鼎鼎的参数:
board/ar7100/ap83/config.mk
# ROM version               
TEXT_BASE = 0xbf000000
# SDRAM version
#TEXT_BASE = 0x8020000
include/configs/ap83.h
#define CFG_MONITOR_BASE    TEXT_BASE
board/ar7100/ap83/u-boot.lds
OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradbigmips")
OUTPUT_ARCH(mips)
ENTRY(_start)
SECTIONS
{
    . = 0x00000000;

    . = ALIGN(4);
    .text       :
    {
      *(.text)
    }

    . = ALIGN(4);
    .rodata  : { *(.rodata) }

    . = ALIGN(4);
    .data  : { *(.data) }

    . = ALIGN(4);
    .sdata  : { *(.sdata) }

    _gp = ALIGN(16);

    __got_start = .;
    .got  : { *(.got) }
    __got_end = .;

    .sdata  : { *(.sdata) }

    __u_boot_cmd_start = .;
    .u_boot_cmd : { *(.u_boot_cmd) }
    __u_boot_cmd_end = .;

    uboot_end_data = .;
    num_got_entries = (__got_end - __got_start) >> 2;

    . = ALIGN(4);
    .sbss  : { *(.sbss) }
    .bss  : { *(.bss) }
    uboot_end = .;
}
注释:
0xbf000000 是mips 24kc Flash起始的位置,也是reset异常向量的入口。
0x80000000是ar9132平台DDR的起始位置。
链接脚本中的0x00000000会被这个TEXT_BASE替代,可以观察编译时候的输出,你会发现有一个-DTEXT_BASE=0xbf000000的选项。

Relocate的代码在start.S中。
启动的大致顺序如下,假设代码固化于flash起始位置。
       CPU 上电
       Reset异常(初始化cp0寄存器)           
       设置GOT
       lowlevel_init(初始化SDRAM)
       初始化cache
       Relocate代码

下面是代码的入口
       la  t9, board_init_f
          j   t9
          nop
这个board_init_f定义在lib_mips/board.c中。
这个程序为下列元素分配合适的内存空间:
[1] u-boot代码(.text .data .bss)
[2] malloc空间 (相当于heap)
[3] Board Info
[4] Global Data
[5] Stack
这里可以看出malloc的空间位于heap中,即内存的高端,而函数中的局部变量,都在stack中的,在内存低端。
                                          
void board_init_f(ulong bootflag)
{
       gd_t gd_data, *id;
       bd_t *bd;
       init_fnc_t **init_fnc_ptr;
       ulong addr, addr_sp, len = (ulong)&uboot_end - CFG_MONITOR_BASE;
       ulong *s;

       /* Pointer is writable since we allocated a register for it.
        */
       gd = &gd_data;
       /* compiler optimization barrier needed for GCC >= 3.4 */
       __asm__ __volatile__("": : :"memory");

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

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

       /*
        * Now that we have DRAM mapped and working, we can
        * relocate the code and continue running from DRAM.
        */
       addr = CFG_SDRAM_BASE + gd->ram_size;

       /* We can reserve some RAM "on top" here.
             */                          

       /* round down to next 4 kB limit.
        */
       addr &= ~(4096 - 1);
       debug ("Top of RAM usable for U-Boot at: %08lx/n", addr);

       /* Reserve memory for U-Boot code, data & bss
        * round down to next 16 kB limit
        */
       addr -= len;
       addr &= ~(16 * 1024 - 1);

       debug ("Reserving %ldk for U-Boot at: %08lx/n", len >> 10, addr);

        /* 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 = (bd_t *)addr_sp;
       gd->bd = bd;
       debug ("Reserving %d Bytes for Board Info at: %08lx/n",
                     sizeof(bd_t), addr_sp);

       addr_sp -= sizeof(gd_t);
       id = (gd_t *)addr_sp;
       debug ("Reserving %d Bytes for Global Data at: %08lx/n",
                     sizeof (gd_t), addr_sp);

      /* Reserve memory for boot params.
        */
       addr_sp -= CFG_BOOTPARAMS_LEN;
       bd->bi_boot_params = addr_sp;
       debug ("Reserving %dk for boot params() at: %08lx/n",
                     CFG_BOOTPARAMS_LEN >> 10, addr_sp);

       /*
        * Finally, we set up a new (bigger) stack.
        *
        * Leave some safety gap for SP, force alignment on 16 byte boundary
        * Clear initial stack frame
        */
       addr_sp -= 16;
       addr_sp &= ~0xF;
       s = (ulong *)addr_sp;
       *s-- = 0;
       *s-- = 0;
       addr_sp = (ulong)s;
       debug ("Stack Pointer at: %08lx/n", addr_sp);

      

       memcpy (id, (void *)gd, sizeof (gd_t));

       /* On the purple board we copy the code in a special way
        * in order to solve flash problems
        */
#ifdef CONFIG_PURPLE
       copy_code(addr);
#endif

       relocate_code (addr_sp, id, addr);

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

从代码中可以看出,U-boot被relocate到内存的最高端。Relocate的实际部分在start.S中,通过relocate_code (addr_sp, id, addr)进入汇编,mips体系中函数参数被保存在a0,a1,a2中,所以看start.S中的注释有:
/*
* void relocate_code (addr_sp, gd, addr_moni)
*                                       
* This "function" does not return, instead it continues in RAM
* after relocating the monitor code.
*
* a0 = addr_sp
* a1 = gd
* a2 = destination address
*/
Relocate的核心代码如下,分析一下能体会到mips汇编的delay branch
relocate_code:
    move    sp, a0      /* Set new stack pointer        */

    li  t0, CFG_MONITOR_BASE
    la  t3, in_ram        
    lw  t2, -12(t3) /* t2     */
move    t1, a2        

/*
     * t0 = source address
     * t1 = target address
     * t2 = source end address
     */
    /* On the purple board we copy the code earlier in a special way
     * in order to solve flash problems
     */
1:
    lw  t3, 0(t0)
    sw  t3, 0(t1)
    addu    t0, 4
    ble t0, t2, 1b
    addu    t1, 4           /* delay slot           */

    /* If caches were enabled, we would have to flush them here.
     */
    /* Jump to where we've relocated ourselves.
     */
    addi    t0, a2, in_ram - _start
    j   t0
    nop
上面计算u-boot end地址lw  t2, -12(t3)是因为有下面的定义,
    .word   uboot_end_data
    .word   uboot_end
    .word   num_got_entries
in_ram:                  
一个word四个字节,所以是in_ram的位置-12个字节。拷贝代码的时候,增加target指针位置的代码放在跳转代码之后,就是由于mips的流水线。等拷贝完代码之后,就跳转到ram中执行了。也就是in_ram的地方(此时的in_ram在relocate后的内存高端)。
注意到在u-boot的连接脚本里面专门有一个.u_boot_cmd段,专门设置这样一个段有什么好处呢?可以看一下find_cmd这个函数,就可以理解了,通过把所有cmd结构指针放在一个统一的段里面在查找起来非常方便,要添加新的命令,也不用改变原来的结构。看看它是怎么定义的吧:
原理:
每个命令都有一个命令结构体
struct cmd_tbl_s {
char*name;  /* Command Name*/
intmaxargs; /* maximum number of arguments*/
intrepeatable; /* autorepeat allowed?*/
int       (*cmd)(struct cmd_tbl_s *, int, int, char *[]);   /* Implementation function*/
char*usage;/* Usage message(short)*/
char*help;/* Help  message(long)*/
};
去定义它。Cmd为要调用的命令函数!name为该命令名字符串。
在u-boot里面有这样的宏
#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) /
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
宏U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)就是将
cmd_tbl_s{
name,
maxargs,
rep,
cmd,
usage,
help
}这样的一个命令结构体放入内存.u_boot_cmd这个区域,.u_boot_cmd这个域在board/smdk2410/u-boot.lds中定义!在U-boot中的shell中,根据用户输入的命令,就会在.u_boot_cmd这个内存区域中查找,当.u_boot_cmd中某一个cmd_tbl_s命令结构体的cmd_tbl_s.name和输入的命令字符串相符时,就调用该命令
结构体的cmd_tbl_s.cmd( ….)函数!
这里有一个要注意的是,cmd是一个函数指针,由于u-boot代码是要relocate的,所以在代码relocate之后,每个这样的指针也要加上相应的偏移才能正常动作,代码在board_init_r函数中。

  
最后是板子上电启动时的输出信息,看了很有帮助。

U-Boot 1.1.4-RAM VSC7395@MAC1 (Mar 21 2008 - 15:57:42)

AP83 (ar9100 with SPI flash)
DRAM:  16 MB
Top of RAM usable for U-Boot at: 81000000
Reserving 263k for U-Boot at: 80fbc000
Reserving 192k for malloc() at: 80f8c000
Reserving 56 Bytes for Board Info at: 80f8bfc8
Reserving 36 Bytes for Global Data at: 80f8bfa4
Reserving 128k for boot params() at: 80f6bfa4
Stack Pointer at: 80f6bf88
Now running in RAM - U-Boot at: 80fbc000
        Found MXIC Flash. ID c22018
Flash: 16 MB

Relocate代码中还包含PIC GOT的东西,学懂了也要分析一下。
从代码调试中的一个小地方可以看出GOT的作用,你只要把代码relocate前后的&__u_boot_cmd_start的值打印出来,你会发现它们是不一样的,这就是我们在start.S中修改GOT指针的作用。

抱歉!评论已关闭.