uboot在加载kernel,ramdisk,dtb时,要注意内存布局。
否则,会出现各种问题,例如
machine ID问题,calibrating delay 问题等等。
这些都是因为kernel在启动时,找不到dtb而产生的。
又例如
ramdisk could not found 问题。
这是因为kernel启动时,不能在initrd指定的问题找到有效格式的RAMDISK导致的。
这些问题,都涉及到uboot加载时的内存布局。
我们加载时,会用到
load_uimage或者load_zimage命令行。
load_uimage=fatload mmc 0 ${kernel_load_address} ${kernel_image} && fatload mmc 0 ${devicetree_load_address} ${devicetree_image} && fatload mmc 0 ${ramdisk_load_address} ${ramdisk_image}
load_zimage=fatload mmc 0 ${kernel_zimage_load_address} ${kernel_zimage} && fatload mmc 0 ${devicetree_load_address} ${devicetree_image} && fatload mmc 0 ${ramdisk_zimage_load_address} ${ramdisk_zimage}
kernel_load_address=0x2080000
ramdisk_load_address=0x7fffc0
devicetree_load_address=0x1000000
kernel_zimage_load_address=0x8000
ramdisk_zimage_load_address=0x800000
kernel_zimage=zImage
ramdisk_zimage=zramdisk.image.gz
ramdisk_image=uramdisk.image.gz
可以看到,他们都是用到fatload命令,从SD卡中读取文件到指定的内存地址中。
如果需要用uImage启动,那么uenvcmd如下:
uenvcmd=run mmc_loadbit_fat && echo Copying Linux from SD to RAM… && run load_uimage && echo run kernel_boot_uimage… && run kernel_bootm
如果需要从zImage启动,那么uenvcmd如下:
uenvcmd=run mmc_loadbit_fat && echo Copying Linux from SD to RAM… && run load_zimage && echo run kernel_boot_zimage… && run kernel_bootz
两者的区别在于:
uImage用的是bootm命令启动的。
kernel_bootm=bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}
而zImage用的是bootz命令启动的。
kernel_bootz=bootz ${kernel_zimage_load_address}
在新版uboot中,我们使用bootz命令,只是希望它取代go命令。并不希望它完成搬移RAMDISK和DTB的功能。
所以bootz的操作,就是在指定的地址上,解压zImage,然后跳转到start address,启动内核。
这里就涉及到bootm对uImage的处理方式的理解了。
bootm识别UBOOT格式的镜像,uImage相比zImage,多了0x40个字节的U_HEADER。
(1)当bootm识别出加载的是uImage时,且TYPE为kernel,那么会去比较source address 和load address。
如果两者相等,那么不执行move操作。
然后会记下entry address,之后start kernel时,会跳转到entry address。后续操作和bootz一样。
所以,此时,entry addr = 0x40 + load addr。
如果两者不相等,那么执行move操作。
注意,这个时候,bootm的特殊地方就来了。
为了避免跑飞,bootm在搬移kernel时,去掉了U_HEADER,然后把净剩载荷move到load address上。也就是说,zImage被move。
所以,此时,entry addr = load addr。
(2)当bootm识别出加载的是uImage时,且TYPE为RAMDISK。
由于不是kernel,所以不记下load address 和 entry address。
然后move到RAM的高段地址区。并返回一个地址区间start和end,供内核进行虚拟地址映射使用。
(3)当bootm识别出加载的是DTB时,
由于不是UIMAGE格式,所以不存在load address和entry address。
然后move到RAM的高段地址区。并返回一个地址区间start和end,供内核进行虚拟地址映射使用。
由于TYPE的区别,我们在mkimage时,使用的选项是不同的。
生成kernel的命令如下:
mkimage -A arm -O linux -T kernel -C none -a 00008000 -e 00008000 -n linux-kernel -d ./zImage ./uImage
生成kernel的命令如下:
mkimage -A arm -O linux -T ramdisk -C gzip -n uboot ramdisk -d ./zramdisk.image.gz ./uramdisk.image.gz