U-boot移植 (v2012.04.1 S3C2440平台) (二) Nand flash 启动支持

5  NAND Flash 启动支持

对于老版的u-boot, 由于Nor flash支持读取代码执行,所以u-boot 默认是烧写进Nor flash启动的。想要在Nand flash中启动u-boot,需要在启动阶段将u-boot拷贝到内存中执行才行。

而最新版本的u-boot在启动第一阶段时,会将u-boot代码重定向到sdram里运行,具体过程如下:

①  在start.S中设置完CPU后,接着调用arch/arm/lib/board.c中的board_init_f做第一阶段平台的初始化工作(timer_init, env_init, ram_init... 计算堆栈addr_sp,设置gd结构体,计算出重定向u-boot的位置addr)。

② 执行完board_init_f后,会调用relocate_code(addr_sp, id, addr)返回start.S, 执行relocate_code的代码,拷贝u-boot代码到内存addr位置。

对于这个版本的u-boot,我实现同时支持Nor flash和Nand flash启动的方法是:在启动的开始阶段,初始化SDRAM后,通过CopyCode2Ram函数(CopyCode2Ram能够分辨代码是在Nor flash中还是在Nand flash中,从而采用不同的拷贝方法,具体实现在board/samsung/micro2440/nand.c中),将u-boot代码拷贝到TEXT_BASE指定的内存区域。然后再执行board_init_f和relocate_code。此时relocate_code不再是从flash中拷贝u-boot代码到内存了,而是从内存中TEXT_BASE处将代码拷贝到board_init_f中计算的addr位置。

这样,不管是从Nor flash还是Nand flash启动,一开始时就通过Copycode2Ram将u-boot代码复制到内存中,然后就可以顺利的执行后面的board_init_f和relocate_code了,简单方便的实现了同时支持Nor flash和Nand flash启动的功能。


u-boot第一阶段启动时,重定向代码示意图

下面是具体实现过程:

1)  修改include/configs/micro2440.h,设置TEXT_BASE

#define CONFIG_SYS_TEXT_BASE  0x33D80000

这里要注意,board_init_f计算出来的relocate addr一般是内存的末尾,所以此处的TEXT_BASE定义得不能太靠后,不然会出现代码拷贝过程中的overlap。(以前老版本的u-boot在内存中运行时,习惯将u-boot拷贝到0x33F80000处,这次我刚开始也设定的是这个值,结果第二次relocate_code拷贝代码时,就覆盖了先前的代码,造成系统错误。)

2)  修改arch/arm/cpu/u-boot.lds,将Copycode2Ram的代码放在.text段的最前面:

由于micro2440在通过Nand flash启动时,只会将flash中前4k的代码复制到内部ram中执行,所以我们在第一阶段拷贝代码时,必须保证执行拷贝操作的代码在.text段的前4k。这个可以通过修改u-boot.lds文件实现。

.text :
{
  __image_copy_start = .;
  CPUDIR/start.o (.text)
  board/samsung/micro2440/libmicro2440.o (.text)

  *(.text)

}

3)  修改arch/arm/cpu/arm920t/start.S:

这里,我们可以把设置系统时钟,初始化MPLL的操作提到前面来做,设置完时钟后再执行代码复制的操作,这样可以加快代码复制的速度。

在start.S的call_board_init_f前添加下面一段代码:

#ifdef CONFIG_MICRO2440

ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)

bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

bl board_early_init_f /* configure MPLL UPLL */


ldr r0, =0x0

ldr r1, =(CONFIG_SYS_TEXT_BASE)

ldr r2, _end_ofs

bl CopyCode2Ram    /* r0: source, r1: dest, r2: size */

ldr pc, =call_board_init_f /* jump to SDRAM */

#endif


6  NAND Flash 启动支持 ⇒ 改进

按照上节描述的方法实现同时支持从Nor flash 和Nand flash启动后,在后面做usbslave download时,发现这种实现方法有缺陷(这个缺陷应该是u-boot官方code自带的?)。问题出现在中断处理过程里,我们先来看一下start.S中的中断向量和中断处理例程(假设链接地址_TEXT_BASE=0x0):

.globl _start

_start:bstart_code

ldr pc, _undefined_instruction  /* 当产生中断或异常时,Arm内核会跳转到0x0地址

ldr pc, _software_interrupt         *  开始处按照中断(或异常)的类型偏移一定地址执行

ldr pc, _prefetch_abort               *  跳转指令。

ldr pc, _data_abort                      */

ldr pc, _not_used

ldr pc, _irq

ldr pc, _fiq


_undefined_instruction: .word undefined_instruction

_software_interrupt: .word software_interrupt

_prefetch_abort: .word prefetch_abort

_data_abort: .word data_abort

_not_used:. word not_used

_irq: .word irq     /*此处,编译时确定_irq的值:irq = _TEXT_BASE +irq_ofs*/

_fiq: .word fiq


需要注意的是,此处_irq的值是编译时确定的,为_TEXT_BASE加上irq函数相对于_TEXT_BASE的偏移。当u-boot执行relocate函数,把u-boot代码重定向到内存中后,若有中断发生,会执行0x0开始的中断跳转指令。由于重定向时,并未更改0x0开始处的_irq, _fiq等label的值,此处的跳转仍然会跳转到重定向前的地址执行。(执行relocate时,在relocate .rel.dyn段时,会重新计算.rel.dyn段内汇编代码中Label的值。参见http://blog.csdn.net/caiyuqing2001/article/details/7377925)

所以在这种情况下执行中断处理例程时,执行的是_TXET_BASE为0x0,重定向前的代码,引用的汇编代码内的Label和C语言的全局变更也是重定向前的。而我们在初始化中断时,初始化的Label和全局变量是重写向后的变量(中断初始化时,代码已经重定向了),所以这种情况下执行的中断处理例程用到的变量全是未初始化的,会导致系统出错。

解决这个问题有两个方法:

1)  运行时根据重定向后中断处理例程的实际地址,更改0x0开始处的中断跳转地址。

若u-boot从Nand flash启动,0x0开始的4K的地址是在内部ram里的,此处的中断向量地址可以直接修改;但若是从Nor flash启动时,0x0开始处的地址是在Nor flash中的,此处的中断向量不能直接修改,得按照Nor flash的写时序来操作。所以想要同时支持Nor flash和Nand flash 启动,这种方法不可行。

2)  在运行过程中指定中断向量跳转地址。

中断向量跳转可以写成这种形式:ldr pc, =CONFIG_VECTOR_TABLE+0x08*irq_no。其中CONFIG_VECTORY_TABLE是设定的内存中的中断向量基地址,这样,relocate代码后在初始化中断过程中,可以根据中断处理函数的最终地址来设置中断向量表:

#define CONFIG_VECTOR_TABLE0x33FFFFC0

#define INSTRUCTION_LDR_PC0xE51FF004 /*ldr pc, [pc, #4]的指令代码*/

unsigned long int p_vect_table;

p_vect_table = (unsigned long int *) (CONFIG_VECTOR_TABLE + irq_no<<3);

p_vect_table++ = INSTRUCTION_LDR_PC;

p_vect_table = irq_handle;

3)  移除掉u-boot的重定向代码的功能,初始化SDRAM后,直接把u-boot代码拷贝至_TEXT_BASE处(假设_TEXT_BASE=0x33F40000),不再进行代码的重定向。这样发生中断时,跳转到的就是代码在内存中的最终地址,便没有上述问题了。

我采用的是第3种方法,这样在启动开始时,只需要复制一次代码就行了,实现也较简单。

①  修改arch/arm/config.mk,去除掉-pie链接选项,这样就不会生成.rel.dyn段了

#needed for relocation

#ifndef CONFIG_NAND_SPL

#LDFLAGS_u-boot += -pie

#endif

②  移除掉arch/arm/cpu/arm920t/start.S中的relocate代码和clear_bss代码:

#ifndef CONFIG_MICRO2440

adrr0, _start

cmpr0, r6

beqclear_bss/* skip relocation */

movr1, r6 /* r1 <- scratch for copy_loop */

ldrr3, _bss_start_ofs

addr2, r0, r3 /* r2 <- source end address   */


copy_loop:

ldmiar0!, {r9-r10} /* copy from source address [r0]    */

stmiar1!, {r9-r10} /* copy to   target address [r1]    */

cmpr0, r2 /* until source end address [r2]    */

blocopy_loop

fixnext:

strr1, [r0]

addr2, r2, #8 /* each rel.dyn entry is 8 bytes */

cmpr2, r3

blofixloop

#endif

#endif /* !CONFIG_MICRO2440 */

clear_bss:

#ifndef CONFIG_SPL_BUILD

ldrr0, _bss_start_ofs

ldrr1, _bss_end_ofs

ldrr4, =_TEXT_BASE /* reloc addr */

addr0, r0, r4

addr1, r1, r4

ldrr0, _board_init_r_ofs

adrr1, _start

addlr, r0, r1

#ifndef CONFIG_MICRO2440

addlr, lr, r9

#endif

/* setup parameters for board_init_r */

movr0, r5 /* gd_t */

③  修改arch/arm/lib/board.c的board_init_f函数:

gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;

#endif


addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;


#ifdef CONFIG_MICRO2440

addr -= CONFIG_SYS_U_BOOT_SIZE;

#endif


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


#ifndef CONFIG_MICRO2440

/*

* 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\n", gd->mon_len >> 10, addr);

#else

addr &= ~(4096 - 1);

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


#ifdef CONFIG_MICRO2440

gd->relocaddr = CONFIG_SYS_TEXT_BASE;

gd->start_addr_sp = addr_sp;

gd->reloc_off = 0;

debug("relocation Offset is: %08lx\n", gd->reloc_off);

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


relocate_code(addr_sp, id, addr);

#else

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

#endif

你可能感兴趣的:(嵌入式系统,flash,平台,c,table,vector,alignment)