Uboot25之重定位

    bl    lowlevel_init    /* go setup pll,mux,memory */

    /* To hold max8698 output before releasing power on switch,

     * set PS_HOLD signal to high

     */

    ldr    r0, =0xE010E81C /* PS_HOLD_CONTROL register */

    ldr    r1, =0x00005301     /* PS_HOLD output high    */

    str    r1, [r0]

再次开发板电源置锁。再次开发板供电锁存。第一,做2次是不会错的;第二,做2次则第2次无意义;做代码移植时有一个古怪谨慎保守策略就是尽量添加代码而不要删除代码。

    /* get ready to call C functions */

    ldr    sp, _TEXT_PHY_BASE    /* setup temp stack pointer */

Uboot25之重定位

Uboot25之重定位

Uboot25之重定位

    sub    sp, sp, #12

    mov    fp, #0            /* no previous frame, so fp=0 */

1)之前在调用lowlevel_init程序前设置过1次栈(start.S 284-287行),那时候因为DDR尚未初始化,因此程序执行都是在SRAM中,所以在SRAM中分配了一部分内存作为栈。本次因为DDR已经被初始化了,因此要把栈挪移到DDR中,所以要重新设置栈,这是第二次(start.S 297-299行);这里实际设置的栈的地址是33E00000,刚好在uboot的代码段的下面紧挨着。

2)为什么要再次设置栈?DDR已经初始化了,已经有大片内存可以用了,没必要再把栈放在SRAM中可怜兮兮的了;原来SRAM中内存大小空间有限,栈放在那里要注意不能使用过多的栈否则栈会溢出,我们及时将栈迁移到DDR中也是为了尽可能避免栈使用时候的小心翼翼。

感慨:uboot的启动阶段主要技巧就在于小范围内有限条件下的辗转腾挪。

    /* when we already run in ram, we don't need to relocate U-Boot.

     * and actually, memory controller must be configured before U-Boot

     * is running in ram.

     */

    ldr    r0, =0xff000fff

    bic    r1, pc, r0        /* r0 <- current base addr of code */

    ldr    r2, _TEXT_BASE        /* r1 <- original base addr in ram */

    bic    r2, r2, r0        /* r0 <- current base addr of code */

    cmp r1, r2 /* compare r0, r1 */

    beq after_copy        /* r0 == r1 then skip flash copy */

1)再次用相同的代码判断运行地址是在SRAM中还是DDR中,不过本次判断的目的不同(上次判断是为了决定是否要执行初始化时钟和DDR的代码)这次判断是为了决定是否进行uboot的relocate。

2)冷启动时当前情况是uboot的前一部分(16kb或者8kb)开机自动从SD卡加载到SRAM中正在运行,uboot的第二部分(其实第二部分是整个uboot)还躺在SD卡的某个扇区开头的N个扇区中。此时uboot的第一阶段已经即将结束了(第一阶段该做的事基本做完了),结束之前要把第二部分加载到DDR中链接地址处(33e00000),这个加载过程就叫重定位。

这里我们是冷启动。不跳转,继续看。

#if defined(CONFIG_EVT1)

我们在x210_sd.h中定义了。

    /* If BL1 was copied from SD/MMC CH2 */

    ldr    r0, =0xD0037488

1)D0037488这个内存地址在SRAM中,这个地址中的值是被硬件自动设置的。硬件根据我们实际电路中SD卡在哪个通道中,会将这个地址中的值设置为相应的数字。譬如我们从SD0通道启动时,这个值为EB000000;从SD2通道启动时,这个值为EB200000

    ldr    r1, [r0]

    ldr    r2, =0xEB200000

    cmp    r1, r2

    beq mmcsd_boot

#endif

Uboot25之重定位

2)我们在start.S的260行确定了从MMCSD启动,然后又在278行将#BOOT_MMCSD写入了INF_REG3寄存器中存储着。然后又在322行读出来,再和#BOOT_MMCSD去比较,确定是从MMCSD启动。最终跳转到mmcsd_boot函数中去执行重定位动作。

3)真正的重定位是通过调用movi_bl2_copy函数完成的,在uboot/cpu/s5pc11x/movi.c中。是一个C语言的函数

4)copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,

            CFG_PHY_UBOOT_BASE, 0);

分析参数:2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000).

    ldr    r0, =INF_REG_BASE

    ldr    r1, [r0, #INF_REG3_OFFSET]

    cmp    r1, #BOOT_NAND        /* 0x0 => boot device is nand */

    beq    nand_boot

    cmp    r1, #BOOT_ONENAND    /* 0x1 => boot device is onenand */

    beq    onenand_boot

    cmp r1, #BOOT_MMCSD

    beq mmcsd_boot

    cmp r1, #BOOT_NOR

    beq nor_boot

    cmp r1, #BOOT_SEC_DEV

    beq mmcsd_boot

nand_boot:

    mov    r0, #0x1000

    bl    copy_from_nand

    b    after_copy

 

onenand_boot:

    bl    onenand_bl2_copy

    b    after_copy

 

mmcsd_boot:

#if DELETE

    ldr sp, _TEXT_PHY_BASE

    sub sp, sp, #12

    mov fp, #0

#endif

    bl movi_bl2_copy

1)210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin来决定从哪个设备启动,如果启动设备是SD卡,则BL0会从SD卡读取前16KB(不一定是16,反正16是工作的)到SRAM中去启动执行(这部分就是BL1,这就是steppingstone技术)

2)BL1执行之后剩下的就是软件的事情了,SoC就不用再去操心了。

SD卡启动流程(bin文件小于16KB时和大于16KB时)

1)启动的第一种情况是整个镜像大小小于16KB。这时候相当于我的整个镜像作为BL1被steppingstone直接硬件加载执行了而已。

2)启动的第二种情况就是整个镜像大小大于16KB。(只要大于16KB,哪怕是17KB,或者是700MB都是一样的)这时候就要把整个镜像分为2部分:第一部分16KB大小,第二部分是剩下的大小。然后第一部分作为BL1启动,负责去初始化DRAM并且将第二部分加载到DRAM中去执行(uboot就是这样做的)。

最重要的但是却隐含未讲的东西

1)问题:iROM究竟是怎样读取SD卡/NandFlash的?

2)三星在iROM中事先内置了一些代码去初始化外部SD卡/NandFlash,并且内置了读取各种SD卡/NandFlash的代码在iROM中。BL0执行时就是通过调用这些device copy function来读取外部SD卡/NandFlash中的BL1的。

SoC支持SD卡启动的秘密(iROM代码)

三星系列SoC支持SD卡/NandFlash启动,主要是依靠SteppingStone技术,具体在S5PV210中支持steppingstone技术的是内部的iROM代码。

扇区和块的概念

1)早期的块设备就是软盘硬盘这类磁存储设备,这种设备的存储单元不是以字节为单位,而是以扇区为单位。磁存储设备读写的最小单元就是扇区,不能只读取或写部分扇区。这个限制是磁存储设备本身物理方面的原因造成的,也成为了我们编程时必须遵守的规律。

2)一个扇区有好多个字节(一般是512个字节)。早期的磁盘扇区是512字节,实际上后来的磁盘扇区可以做的比较大(譬如1024字节,譬如2048字节,譬如4096字节),但是因为原来最早是512字节,很多的软件(包括操作系统和文件系统)已经默认了512这个数字,因此后来的硬件虽然物理上可能支持更大的扇区,但是实际上一般还是兼容512字节扇区这种操作方法。

3)一个扇区可以看成是一个块block(块的概念就是:不是一个字节,是多个字节组成一个共同的操作单元块),所以就把这一类的设备称为块设备。常见的块设备有:磁存储设备硬盘、软盘、DVD和Flash设备(U盘、SSD、SD卡、NandFlash、Norflash、eMMC、iNand)

4)linux里有个mtd驱动,就是用来管理这类块设备的。

5)磁盘和Flash以块为单位来读写,就决定了我们启动时device copy function只能以整块为单位来读取SD卡。

用函数指针方式调用device copy function

1)第一种方法:宏定义方式来调用。好处是简单方便,坏处是编译器不能帮我们做参数的静态类型检查。

#define CopySDMMCtoMem(z,a,b,c,e)(((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned

int *)0xD0037F98)))(z,a,b,c,e))

2)第二种方法:用函数指针方式来调用。

---------------------------------------- movi_bl2_copy函数讲解 ----------------------------------------------------------

typedef u32(*copy_sd_mmc_to_mem) (u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);

使用typedef定义了一个函数指针类型的数据类型,数据类型的名字为指针:copy_sd_mmc_to_mem,函数的参数列表为:u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init。函数返回一个整型。具体的函数是三星官网提供的。

Uboot25之重定位

void movi_bl2_copy(void)

{

    ulong ch;

#if defined(CONFIG_EVT1)

我们在x210_sd.h中定义了。

    ch = *(volatile u32 *)(0xD0037488);

将0xD0037488强制类型转换成地址,然后取出此地址中的内容,赋值给ch。取出的结果是EB200000赋值给ch。

    copy_sd_mmc_to_mem copy_bl2 =

     (copy_sd_mmc_to_mem) (*(u32 *) (0xD0037F98));

定义一个函数指针变量copy_bl2,变量的值为(*(u32 *) (0xD0037F98)),为什么要写成(*(u32 *) (0xD0037F98))。

我们来分析一下,首先将0Xd0037F98强制类型转换成u32类型的指针。既然已经是指针为什么还要加*呢?我们在C语言的函数指针一节中讲过,函数名是一个指向自己的指针,函数名就是函数指针,它指向自己。我们定义了函数copy_bl2的地址进行强制赋值,我们对函数指针的解引用是(* copy_bl2)(参数列表)。但是我们如果想使用(opy_bl2)(参数列表),把这个*去掉,那就要在定义的时候,放在右值中,这是一种经典的做法。

    #if defined(CONFIG_SECURE_BOOT)

    ulong rv;

    #endif

#else

    ch = *(volatile u32 *)(0xD003A508);

    copy_sd_mmc_to_mem copy_bl2 =

     (copy_sd_mmc_to_mem) (*(u32 *) (0xD003E008));

#endif

上面主要是对copy_bl2这个函数指针进行赋值,也就是函数地址。

    u32 ret;

    if (ch == 0xEB000000) {

        ret = copy_bl2(0, MOVI_BL2_POS, MOVI_BL2_BLKCNT,

            CFG_PHY_UBOOT_BASE, 0);

Uboot25之重定位

0xEB000000对应的是0通道。

#define MOVI_BL2_POS        ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT)

#define eFUSE_SIZE            (1 * 1024)    // 0.5k eFuse, 0.5k reserved

#define MOVI_BLKSIZE            (1<<9) /* 512 bytes */

#define MOVI_BL1_BLKCNT        (SS_SIZE / MOVI_BLKSIZE)    /* 8KB */

#define SS_SIZE            (8 * 1024)

#define MOVI_ENV_BLKCNT        (CFG_ENV_SIZE / MOVI_BLKSIZE)    /* 16KB */

#define CFG_ENV_SIZE            0x4000    /* Total Size of Environment Sector */

计算一下:MOVI_BL2_POS :2+16+32=50

#define MOVI_BL2_BLKCNT        (PART_SIZE_BL / MOVI_BLKSIZE)    /* 512KB */

#define PART_SIZE_BL            (512 * 1024)

MOVI_BL2_BLKCNT:1024
CFG_PHY_UBOOT_BASE:0x33e00000

#if defined(CONFIG_SECURE_BOOT)

没定义,这个是安全签名,主要用在安全启动中。就像windows里面的数字签名一样。

        /* do security check */

        rv = Check_Signature( (SecureBoot_CTX *)SECURE_BOOT_CONTEXT_ADDR,

                 (unsigned char *)CFG_PHY_UBOOT_BASE, (1024*512-128),

             (unsigned char *)(CFG_PHY_UBOOT_BASE+(1024*512-128)), 128 );

        if (rv != 0){

                while(1);

            }

#endif

    }

    else if (ch == 0xEB200000) {

        ret = copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,

            CFG_PHY_UBOOT_BASE, 0);

我们使用的2通道,SD卡启动。

2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000).

#if defined(CONFIG_SECURE_BOOT)

        /* do security check */

        rv = Check_Signature( (SecureBoot_CTX *)SECURE_BOOT_CONTEXT_ADDR,

                 (unsigned char *)CFG_PHY_UBOOT_BASE, (1024*512-128),

             (unsigned char *)(CFG_PHY_UBOOT_BASE+(1024*512-128)), 128 );

        if (rv != 0) {

            while(1);

        }

#endif

    }

    else

        return;

 

    if (ret == 0)

        while (1)

判断下是否搬移完成,如果==0就等待,直至ret = 1;

            ;

    else

        return;

}

----------------------------------------------------------------------------------------------------------------------------------

函数执行完成之后,因为使用的是bl,所以返回地址还是这个copy函数,继续执行后面的。

b after_copy

使用的是b,这个after_copy函数是空函数,不用管。

nor_boot:

    bl read_hword

    b after_copy

after_copy: