UBoot支持双启动(Nor and Nand)及环境变量保存的实现

来源:互联网 发布:杭州城市骑行大数据 编辑:程序博客网 时间:2024/06/07 18:43
本文所要讲述的是如何使UBoot只编译一次就能支持从Nor Flash和Nand Flash启动,并且在保存环境变量时能够保存在其所在的Flash中.


注意,本文针对的为S3C2410芯片,其他芯片原理相同,可在适当修改代码后进行移植.


我们知道UBoot源码本身直接支持从Nor Flash启动,这是Nor Flash的可执行特性决定的.而针对Nand Flash,则涉及到前4K数据的问题.S3C2410的Nand Flash控制器有一个特殊的功能,就是能在上电后自动将Nand Flash中的前4K数据搬移到4K内部的RAM中,并把0x00000000设置为内部RAM的起始地址,然后CPU从内部RAM的0x000000位置启动,该过程由硬件自动完成.
所以,要使UBoot支持从Nand Flash启动,必须在其前4K代码执行过程中完成将自身复制到RAM中的工作.

但是,我们现在需要让UBoot支持双启动,现在的问题就是该如何判断UBoot是在Nor Flash还是在Nand Flash中呢?呵呵,在S3C2410(S3C2440)中,这个可以通过BWSCON(BUS WIDTH & WAIT CONTROL REGISTER)控制寄存器[2:1]的值进行判断,如果为00则表示是从Nand Flash启动的.因此,我们可以直接通过如下代码进行判断并在BWSCON[2:1]为00时跳转到UBoot复制自身(copy_myself)的代码处:

#defineBWSCON0x48000000ldrr0, =BWSCONldr r0, [r0]ands r0, r0, #6@ 判断BWSCON[2:1]是否为00,如果是,则跳转到copy_myselfbeqcopy_myself
好了,双启动问题算是解决了,但是新的问题又来了,这个环境变量又应该如何保存呢?是都保存到Nor Flash还是Nand Flash呢?当然,最好的方式还是保存到各自所在的Flash中.这个可以设置一个标志,在进入Nand Flash启动代码时设置该标志,表示从Nand Flash启动,然后,在保存环境变量时通过该标志的值判断是调用Nor Flash的保存方法还是Nand Flash的保存方法.


下面讲解具体的修改过程(以u-boot-2010.09为例).
首先,修改include/configs/smdk2410.h文件,添加如下宏定义:

#define CONFIG_CMD_ENV// 开启环境变量操作命令#define CONFIG_CMD_NAND// 开启Nand Flash操作命令#ifdef CONFIG_CMD_NAND#define CONFIG_SYS_NAND_BASE            0x4E000000 //Nand配置寄存器基地址#define CONFIG_SYS_MAX_NAND_DEVICE      1#define CONFIG_MTD_NAND_VERIFY_WRITE    1#define CONFIG_NAND_S3C2410#define STACK_BASE 0x33f00000   /* 堆栈基址 */#define STACK_SIZE 0x8000       /* 堆栈大小 */#endif/*--------------------------------------------------------------------- * Boot From Nor or Nand Flash or both */#define CONFIG_NOR_BOOT#define CONFIG_NAND_BOOT#ifdef CONFIG_NOR_BOOT#define PHYS_FLASH_10x00000000 /* Flash Bank #1 */#define CONFIG_SYS_FLASH_BASEPHYS_FLASH_1#define CONFIG_AMD_LV160B/* 使用AM29LV160DB Nor Flash芯片 */#define CONFIG_SYS_MAX_FLASH_BANKS1/* max number of memory banks *//* timeout values are in ticks */#define CONFIG_SYS_FLASH_ERASE_TOUT(5*CONFIG_SYS_HZ) /* Timeout for Flash Erase */#define CONFIG_SYS_FLASH_WRITE_TOUT(5*CONFIG_SYS_HZ) /* Timeout for Flash Write */#defineCONFIG_ENV_IS_IN_FLASH/* 将环境变量保存到Nor Flash中*/#endif#ifdef CONFIG_NAND_BOOT#define UBOOT_RAM_BASE0x33f80000#define NAND_CTL_BASE0x4E000000#define bINT_CTL(Nb)__REG(INT_CTL_BASK + (Nb))#define oNFCONF0x00#define oNFCMD0x04#define oNFADDR0x08#define oNFDATA0x0c#define oNFSTAT0x10#define oNFECC0x14#define CONFIG_UBOOT_SIZE0x30000#define CONFIG_ENV_IS_IN_NAND/* common/env_nand.c */#ifndef CONFIG_NOR_BOOT#define CONFIG_SYS_NO_FLASH#undef CONFIG_CMD_FLASH#undef CONFIG_CMD_IMLS#endif#endif/*--------------------------------------------------------------------------- * Nor Flash Device */#ifdef CONFIG_AMD_LV160B#define PHYS_FLASH_SIZE0x00200000/* 2MB */#define CONFIG_SYS_MAX_FLASH_SECT(35)/* max number of sectors on one chip */#define CONFIG_ENV_ADDR(CONFIG_SYS_FLASH_BASE + 0x030000)/* addr of environment */#endif/*-------------------------------------------------------------------------- * Environment Setting */// 环境变量的大小必须为sector的整数倍,此处直接设置为0x1000,// 能够同时满足Nor Flash和Nand Flash的要求#defineCONFIG_ENV_SIZE0x10000#ifdefCONFIG_ENV_IS_IN_FLASH//#defineCONFIG_ENV_ADDR  (CONFIG_SYS_FLASH_BASE + 0x30000)#endif#ifdefCONFIG_ENV_IS_IN_NAND#defineCONFIG_ENV_OFFSETCONFIG_UBOOT_SIZE /* 环境变量紧接在uboot之后 */#endif
然后,修改启动文件arch/arm/cpu/arm920t/start.S,增加Nand Flash启动代码和启动标志的设置(直接修改relocate代码段):

/* 双启动判断 */#defineBWSCON0x48000000ldrr0, =BWSCONldr r0, [r0]ands r0, r0, #6@ 判断BWSCON[2:1]是否为00,如果是,则跳转到copy_myselfbeqcopy_myself#ifdef CONFIG_NOR_BOOT#ifndef CONFIG_SKIP_RELOCATE_UBOOTrelocate:/* relocate U-Boot to RAM    */adrr0, _start/* r0 <- current position of code   */ldrr1, _TEXT_BASE/* test if we run from flash or RAM */cmpr0, r1/* don't reloc during debug         */beqstack_setupldrr2, _armboot_startldrr3, _bss_startsubr2, r3, r2/* r2 <- size of armboot            */addr2, r0, r2/* r2 <- source end address         */copy_loop:ldmiar0!, {r3-r10}/* copy from source address [r0]    */stmiar1!, {r3-r10}/* copy to   target address [r1]    */cmpr0, r2/* until source end addreee [r2]    */blecopy_loopb stack_setup/* 跳过nand启动代码 */#endif/* CONFIG_SKIP_RELOCATE_UBOOT */#endif  /* CONFIG_NOR_BOOT */#ifdefCONFIG_NAND_BOOTcopy_myself:       @ reset NANDmov r1, #NAND_CTL_BASEldr r2, =0xf830           @ initial valuestr r2, [r1, #oNFCONF]ldr r2, [r1, #oNFCONF]bic r2, r2, #0x800          @ enable chipstr r2, [r1, #oNFCONF]mov r2, #0xff    @ RESET commandstrb r2, [r1, #oNFCMD]mov r3, #0                  @ waitnand1: addr3, r3, #0x1cmpr3, #0xablt nand1nand2:ldr r2, [r1, #oNFSTAT]      @ wait readytst r2, #0x1beq nand2ldr r2, [r1, #oNFCONF]orr r2, r2, #0x800          @ disable chipstr r2, [r1, #oNFCONF]@ get read to call C functions (for nand_read())ldr sp, DW_STACK_START      @ setup stack pointermov fp, #0                  @ no previous frame, so fp=0@ copy U-Boot to RAMldr r0, =UBOOT_RAM_BASE@ buf : first parameter of nand_read_ll()mov r1, #0x0@ start_addr : second parameter of nand_read_ll()mov r2, #CONFIG_UBOOT_SIZE@ size : third parameter of nand_read_ll()bl  nand_read_lltst r0, #0x0beq ok_nand_readbad_nand_read:loop2:  b     loop2          @ infinite loopok_nand_read:@ set flag : 0->boot from nand;1->boot from nor@ set flag after finishing to copy U-Boot to memoryldrr0, =boot_flash@ defined in common/cmd_nvedit.c    movr1, #0x0    strr1, [r0]@ verifymov r0, #0ldr r1, =UBOOT_RAM_BASE@ NOTE: DON'T USE '_TEXT_BASE'mov r2, #0x400     @ 4 bytes * 1024 = 4K-bytesgo_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne notmatchsubs r2, r2, #4beq  stack_setupbne  go_nextnotmatch:loop3:  b     loop3         @ infinite loop#endif/* CONFIG_NAND_BOOT */.align 2DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
其中,boot_flash为1表示从Nor Flash启动,为0则表示从Nand Flash启动,为使该值能够正确被修改,所以将其在UBoot复制完毕后进行修改.代码中的UBOOT_RAM_BASE不要直接替换为_TEXT_BASE,虽然两者值相同,但是在运行中却有问题,我就因为使用了_TEXT_BASE,导致费了很多时间,前车之荐,请勿效仿!
nand_read_ll为新增加的Nand Flash读取方法,在board/samsung/smdk2410目录下新建文件nand_read.c,并添加如下代码:

#include <config.h>#define __REGb(x) (*(volatile unsigned char *)(x))#define __REGi(x) (*(volatile unsigned int *)(x))#define NF_BASE   0x4e000000#define NFCONF    __REGi(NF_BASE + 0x0)#define NFCMD     __REGb(NF_BASE + 0x4)#define NFADDR    __REGb(NF_BASE + 0x8)#define NFDATA    __REGb(NF_BASE + 0xc)#define NFSTAT    __REGb(NF_BASE + 0x10)#define BUSY 1inline void wait_idle(void) {    int i;    while(!(NFSTAT & BUSY))        for(i=0; i<10; i++);}#define NAND_SECTOR_SIZE 512#define NAND_BLOCK_MASK   (NAND_SECTOR_SIZE - 1)/* low level nand read function */int nand_read_ll(unsigned char *buf,                 unsigned long start_addr,                 int            size){    int i, j;    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {        return -1; /* invalid alignment */    }    /* chip Enable */    NFCONF &= ~0x800;    for(i=0; i<10; i++);    for(i=start_addr; i < (start_addr + size);) {        /* READ0 */        NFCMD = 0;        /* Write Address */        NFADDR = i & 0xff;        NFADDR = (i >> 9) & 0xff;        NFADDR = (i >> 17) & 0xff;        NFADDR = (i >> 25) & 0xff;        wait_idle();        for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {            *buf = (NFDATA & 0xff);            buf++;        }    }    /* chip Disable */    NFCONF |= 0x800; /* chip disable */    return 0;}
修改Makefile,使nand_read.c能够被编译进UBoot:
COBJS := smdk2410.o flash.o nand_read.o
为了保证nand_read_ll方法能够被编译到UBoot代码的前4K中,以确保代码的正确执行,还需修改链接文件arch/arm/cpu/arm920t/u-boot.lds,如下:

.text :{arch/arm/cpu/arm920t/start.o(.text)board/samsung/smdk2410/lowlevel_init.o(.text)board/samsung/smdk2410/nand_read.o(.text)*(.text)}
最后,该修改代码以支持环境变量的保存了.
在UBoot中,环境变量操作的命令在文件common/cmd_nvedit.c中定义和实现,而针对不同Flash的操作,如env_init,saveenv和env_relocate_spec,则在common/env_flash.c和common/env_nand.c中进行定义和实现.
为了双启动的支持,并且能够支持单启动(Nor或Nand启动),我们将上面三个方法整合到了cmd_nvedit.c中,并将env_flash.c和env_nand.c中的这三个方法添加上nor和nand前缀,用于区分调用,并防止同时编译时的重定义.
然后,将env_flash.c和env_nand.c中定义的env_name_spec和env_ptr两个变量放到cmd_nvedit.c中进行定义,这两个文件直接使用extern关键字引用这两个变量.由于函数env_get_char_spec在env_flash.c和env_nand.c中都进行了定义,所以也和env_init一样转到cmd_nvedit.c中进行定义.
接着,就是定义启动标志boot_flash,并设置初值为1.


具体修改如下:
在common/cmd_nvedit.c中增加如下代码:

// 1:NOR FLASH启动,0:NAND FLASH启动// boot_flash将在start.S中被修改int boot_flash = 1;char * env_name_spec;env_t * env_ptr;extern nor_env_get_char_spec(int index);extern nand_env_get_char_spec(int index);extern int nor_env_init(void);extern int nand_env_init(void);extern int nor_saveenv(void);extern int nand_saveenv(void);extern void nor_env_relocate_spec(void);extern void nand_env_relocate_spec(void);#ifdef ENV_IS_EMBEDDEDextern uchar environment[];#endifuchar env_get_char_spec(int index){if (boot_flash) {return nor_env_get_char_spec(index);} else {return nand_env_get_char_spec(index);}} int env_init(void){if (boot_flash) {#ifdef ENV_IS_EMBEDDEDenv_ptr = (env_t *)(&environment[0]);#elseenv_ptr = (env_t *)CONFIG_ENV_ADDR;#endifenv_name_spec = "Flash";return nor_env_init();} else {#if defined(ENV_IS_EMBEDDED)env_ptr = (env_t *)(&environment[0]);#elif defined(CONFIG_NAND_ENV_DST)env_ptr = (env_t *)CONFIG_NAND_ENV_DST;#elseenv_ptr = NULL;#endifenv_name_spec = "NAND";return nand_env_init();}}int saveenv(void){if (boot_flash) {return nor_saveenv();} else {return nand_saveenv();}}void env_relocate_spec(void){if (boot_flash) {return nor_env_relocate_spec();} else {return nand_env_relocate_spec();}}
在common/env_flash.c和common/env_nand.c中,去掉对env_name_spec和env_ptr的定义,改为外部引用:

extern char * env_name_spec;extern env_t *env_ptr;
最后,在env_get_char_spec,env_init,saveenv和env_relocate_spec方法前分别添加前缀"nor_"和"nand_".


好了,现在编译UBoot:
make smdk2410_config && make all
下载到开发板上试试吧!


参考资料:
1. <<如何让U-boot实现Nand/Nor 双启动>>
2. <<Nand Flash 和 Nor Flash 双启动方法探究>>  关于S3C2440的
3. <<成功在skyeye 上实现U-Boot 的Nand命令并从Nand中启动Linux>>
4. <<从Nand Flash启动U-BOOT的基本原理>>
5. <<关于u-boot同时支持nand启动和Nor启动环境变量的保存位置>> 标志修改的代码放置位置好像不行,我这没有成功


原创粉丝点击