参考

http://www.cnblogs.com/sywtt/archive/2012/01/25/2329284.html

按各地址起作用的顺序,uboot引导linux内核启动涉及到以下地址:

  1. load address:
  2. entry point: 这两个地址是mkimage时指定的
  3. bootm address:bootm为uboot的一个命令,以此从address启动kernel
  4. kernel运行地址:在具体mach目录中的Makefile.boot中指定,为kernel启动后实际运行的物理地址   
  5.  

#./mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x50008000 -e 0x50008040 -d zImage uImage
以上命令是为了生成uImage

理论上因为mkimage要为zImage加上0x40字节的header,所以entry point = load address + 0x40

但由于uboot 的bootm对uImage处理不是简单的go操作,其对前三个地址都有比较判断,所以在实际的操作中,就分为两种不同的情况:

1.烧录uImage
bootm地址和load address一样 也就是uboot 里set bootcmd里bootm的地址和做成uImage时的 -a参数的地址相同
#./mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x50008000 -e 0x50008040 -d zImage uImage
UBOOT中设置参数(uboot要先烧录uImage到40000 nandflash)

#tftp 50003000 uImage                        //uboot烧录uImage到内存50003000
#nand erase  40000 400000
#nand write 50003000 40000 400000  //将内存50003000开始的东西烧录到40000 nandflash地址
#set bootcmd "nand read 50008000 40000 400000;bootm 50008000"

 此种情况下,bootm不会对uImage header后的zImage进行memory move的动作,而会直接go到entrypoint开始执行。因此此时的entry point必须设置为load address + 0x40。如果kernelboot过程没有到uncompressing the kernel,就可能是这里设置不对。

boom address == load address == entry point - 0x40

具体细节可参看uboot代码common/cmd_bootm.c 中do_bootm函数

2,烧录zImage
由于不是uImage ,所以没有头部64字节。镜像文件头部就没有entry point 和 load address

zImage是可可运行的二进制内核镜像,zImage是由vmlinux处理、压缩得到的; vmlinux是内核文件,zImage是一般情况下默认的压缩内核映像文件,压缩vmlinux,加上一段解压启动代码得到;
 
    #tftp 50003000 zImage                        //uboot烧录zimage到内存50003000
    #nand erase  40000 400000
    #nand write 50003000 40000 400000  //将内存50003000开始的东西烧录到40000 nandflash地址
设置uboot参数
#set bootcmd "nand read 50008000 40000 400000;bootm 50008000"


common/cmd_bootm.c

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) 函数里
//#define LINUX_ZIMAGE_MAGIC    0x016f2818
//(1)这是一个魔数,0x016f2818表示这个镜像是zImage,
//也就是说zImage格式的镜像中,在头部的一个固定位置存放了这个数,作为格式标记,
//如果我们拿到了一个image,去他的那个位置去取4个字节,判断它是否等于这个魔数LINUX_ZIMAGE_MAGIC。则可以知道这个镜像是不是zImage
#ifdef CONFIG_ZIMAGE_BOOT
#define LINUX_ZIMAGE_MAGIC    0x016f2818
    if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
                ////zImage从头部开始的第37到40个字节,存的是zImage的标志的魔数
        printf("Boot with zImage\n");
            //addr == 50008000
        addr = virt_to_phys(addr);
            //virt_to_phys之后还是50008000
        hdr->ih_os = IH_OS_LINUX;
        hdr->ih_ep = ntohl(addr);
        goto after_header_check;//一下子跳了过去,中间一大段memmove的东东都不执行了。如果烧录的是zImage
    }
#endif

此时bootm里的地址是50008000        
   //打印出来发现theKernel == 0x50008000  (theKernel是跳转函数)
//打印出来发现 hdr->ih_ep = 0x800050  也就是必须ntohl之后才是50008000     (入口地址entry point)

3.烧录uImage
bootm地址(bootm 51000000)和load address(50008000)不一样
(但需要避免出现memory move时出现覆盖导致uImage被破坏的情况)
uboot参数#set bootcmd "nand read 51000000 40000 400000;bootm 51000000"
此种情况51000000不能是50008000,否则内核起不来。

#print 可以看到uboot里
bootcmd=nand read 51000000 40000 400000;bootm 51000000
 

  此种情况下,bootm会把uImage header后的zImage move到load address,然后go到entry point开始执行。 由此知道此时的load address必须等于entry point。

boom address != load address == entry point

此种情况是在生成uImage时  先在/root/.bash_profile里指明mkimage工具的目录路径(由uboot生成在tools目录里)
PATH=$PATH:$HOME/bin:/4.2.2-eabi/usr/bin:/home/smdk6410_resource/src/u-boot-1.1.6_smdk6410/tools
然后
#make ARCH=arm UIMAGE_LOADADDR=0X50008000  uImage
或者
#./mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x50008000 -e 0x50008000 -d zImage uImage

 
UIMAGE_LOADADDR会把入口地址和加载地址都设置位为50008000


至于以上原因,清查看博客

里的
common/cmd_bootm.c  里的do_bootm函数的详解

因此,在mkimage以及设置uboot boot command的时候需要注意到以上两种情况。

 

至于kernel的运行地址,其与前3个地址没有关系,除了要避免内存覆盖导致解压后kernel不完整的情况。

zImage的头部有地址无关的自解压程序,因此刚开始执行的时候,zImage所在的内存地址(entrypoint)不需要同编译kernel的地址相同。自解压程序会把kernel解压到编译时指定的物理地址,然后开始地址相关代码的执行。在开启MMU之前,kernel都是直接使用物理地址(可参看System.map)。






12-16 23:13