首先介绍原理,最后介绍具体怎么实现。
eloader,uboot存储在SPI Flash上面。SPI FLash在开机时会被SoC映射到某地址,然后依次启动eloader,uboot。
uboot启动后,将会启动kernel。
boot.img是Android搞出来的一种文件布局格式。boot.img包括三大部分,kernel、ramdisk和cmdline。
ramdisk中包括了Android需要的根目录,核心包括init,init.rc等。
cmdline是传递给内核的参数。uboot中有环境变量bootargs也是传递给内核的参数。谁优先呢?需要根据不同平台的uboot来看。不同厂家实现不一样。
各Android平台下的uboot中增加了对boot.img的解析功能。可以从boot.img的布局中解析出内核、ramdisk及cmdline来。
文件系统必须布局在某个分区。分区可以存在于sd卡,emmc上,甚至是u盘。也就是kernel存在于某种存储设备的分区上。uboot需要支持分区上的文件系统,能够从文件系统中将kernel读到内存里。
以上两种方式的kernel,文件封装格式方式不一样。
对于boot.img中的kernel,就是普通的zImage格式,而对于在文件系统里存储的,kernel是uImage方式,此uImage通过mkimage方式产生,请参考后文内容。
zImage就是经过普通压缩的vmlinux。uImage是把zImage再次封装后的形式。u的意思是uboot再次解析的意思。
uboot通过各种努力,从不同地方找到了kernel了,这各种努力,根据在kernel存储在哪里有不同的行为。
根据kernel存储在哪里来分别描述:
boot.img如何被uboot找到呢?
又分为两种情况:
一般来说,无论是emmc,还是nand,都会有一个boot分区存在。boot.img会被同linux 命令dd一样的方式写入到这个分区里。
uboot可以找到这个分区,然后读出boot.img里的内容。所以uboot需要支持emmc的读写,sd卡的读写,nand的读写,甚至u盘的读写。这些读写都是按扇区方式读写的。
这种启动方式,uboot的启动参数是booti boot。
booti命令首先根据分区名称找到boot分区位置,然后读入到内存中,内存中的内容,就是boot.img。
解析boot.img,得到内核,并得到ramdisk,cmdline。
uboot中有一个重要的环境变量bootargs,不同的uboot版本,会让cmdline和bootargs较量来决定什么值传递给kernel。
最重要的boot args是指定了根分区及init。
kernel会以此为依据找到根分区及init。
boot.img不是直接按二进制方式写入到某个分区,不像dd一样。而是存在分区的文件系统中。比如存在SD卡第一个fat32文件系统分区中,或者emmc第15个ext4文件系统中。
这种启动方式,首先需要将boot.img加载到内存,然后uboot从这个地址启动。boot.img就可以存在sd,emmc,nand,u盘等分区的文件系统中,需要首先被ext4load,fatload等加载到内存中。
加载到内存后,uboot的booti命令可以从这个内存地址启动。
booti address
这时候,kernel需要是uImage封装方式。并且需要被uboot提供的ext4load,fatload等方式将uImage加载到内存,然后被uboot的bootm来启动。
kernel需要找到根分区,这个是通过uboot传递给kernel的cmdline指定的。VT6080一般都是指定一个实际分区。也可以是ramdisk。
kernel启动了,也找到了根分区,如何启动Android系统呢?
在根分区有一个init进程及Init.rc,kernel从根分区启动init,init解析init.rc。
在init.rc及其include进来的rc文件里,我们需要建立system目录及data目录。
init.rc会通过mount_all命令,挂载system分区到/system,data分区到/data.
system里存储的,就是android系统,主要包括framework。
找到system和data目录,基本上Android就能够启动了。
system目录是system分区的挂载点。一般是在刷机的时候,将system.img刷在system分区。所以system.img刷到system分区的时候,是带着文件系统刷过去的,也就是system.img一般是ext4的img,和boot.img不一样的格式。
让Android运行在SD卡上,就是从SD卡启动的意思了。根据前面介绍,我们知道必须具备以下条件:
1.uboot能够支持sd卡读写。(mmcread/write)
2.uboot可以读取sd卡分区上的文件。(ext4load,fatload mmc0:1)
3.uImage在SD卡分区上。对于VT6080,还需要一个dtb文件。
4.kernel启动后,能够驱动sd卡(mmc支持)及sd卡上的文件系统。
5.kernel启动后,根据内核参数,将SD卡分区当作系统的根分区。
6.SD卡分区中有init,有system,data目录。
0.sd卡格式化成ext4
1.编译内核,找到uImage及dtb文件(arch/arm/boot/)
2.编译Android。
3.拷贝android输出文件到sd根目录:
3.1 out../root/*
3.2 out../system
注意一个是root下的所有文件,一个是整个system目录。
4.拷贝内核uImage,dtb文件到sd卡根目录。
5.修改init.rc,init.elite1000.rc,fstab.elite1000等文件中对system,userdata分区的挂载动作,都去掉。因为sd卡根目录下已经有了system目录了。
6.将s3g GFX driver安装到sd根目录。
7.修改uboot参数
setenv bootcmd 'ext4load mmc 0:1 0x0 scriptcmd; ifiminfo 0x0; then source 0; else run bootcmd_mmc; fi'
--uboot启动后,首先运行bootcmd命令。这行意思是找sd卡上有没有scriptcmd文件,有的话就执行它。没有就运行bootcmd_mmc
--所以sd卡上不能存在scriptcmd文件。这个文件是安装bootloader及android的时候用的。
setenv bootcmd_mmc '
run bootargs_mmc&&
ext4load mmc 0:1 0x6000000elite1000-emmc.dtb;
ext4load mmc 0:1 0x2800000uImage;
if iminfo 0x2800000; then bootm0x2800000 - 0x6000000;
else run bootcmd_emmc;
fi'
--首先设置mmc启动的参数。下文解释
--加载dtb和内核到内存。
--如果内存中内核image符合uImage的封装格式,运行之,否则,从emmc启动。
setenv bootargs_mmc '
setenv bootargs
${kernelargs}
androidboot.serialno=${androidno}
root=/dev/mmcblk0p1 rootwait
rootflags=errors=remount-ro,commit=0
rootfstype=ext4 rw
${mtdparts}
init=/initrw'
bootargs_mmc命令就是设置bootargs参数,这个参数被uboot解释并传递给kernel。
kernelargs是传递给kernel的参数。
root=,这是指示根分区在哪里。
mmcblk0
vt6080上有3个mmc控制器。sd卡是mmc 0,emmc是mmc2,mmc1没有使用。这里的0,1,2编号是uboot中使用的。
linux里如何编号还不清楚。
这里的mmcblk0是在linux内核中使用的,这里指的是sd卡
mmcblk0p1,p1指的是sd卡上的第一个分区。分区编号从1数起。
setenv kernelargs 'console=ttyS1,115200n8rootdelay=1'
setenv androidno '030a3d8408e49475'
setenv mtdparts 'mtdparts=nand:4M(secureboot),4M(secureos),4M(audiofirmware),2M(uboot_env),4M(uboot),4M(boot_logo),4M(nvram),12M(devicetree),32M(otaloader),32M(iploader),16M(device_info),16M(misc),16M(boot),16M(recovery),384M(system),32M(package),384M(cache),-(data)'
setenv bootargs_mmc 'setenv bootargs ${kernelargs} androidboot.serialno=${androidno} root=/dev/mmcblk0p1 rootwaitrootflags=errors=remount-ro,commit=0 rootfstype=ext4 rw ${mtdparts} init=/initrw'
setenv bootcmd_mmc 'run bootargs_mmc &&ext4load mmc 0:1 0x6000000 elite1000-emmc.dtb; ext4load mmc 0:1 0x2800000uImage; if iminfo 0x2800000; then bootm 0x2800000 - 0x6000000; else runbootcmd_emmc; fi'
setenv bootcmd 'ext4load mmc 0:1 0x0 scriptcmd; ifiminfo 0x0; then source 0; else run bootcmd_mmc; fi'
以上就是uboot参数。可以在uboot命令行环境输入执行。注意最后要saveenv。
8.重启。android将会从sd卡启动了。
从zImage创建uImage:
mkimage -A arm -O linux -T kernel -C none -a02008000 -e 02008000 -n Linux-3.4.6 -d zImage uImage
打印信息:
Image Name: Linux-3.4.6
Created: Mon Dec 29 17:21:36 2014
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 3857160 Bytes = 3766.76 kB = 3.68 MB
Load Address: 0x02008000
Entry Point: 0x02008000
如何从image安装方式改变成从SD卡启动
1.使用工具splite_bootimg.pl,分解boot.img,得到kernel和Ramdisk
2.使用mkimage工具将kenrel转换成uImage
3.将ramdisk解压到目录ramdisk:
# mkdir ramdisk
# cd ramdisk
# gzip -dc ../*-ramdisk.gz | cpio -i
4.修改fstab.elite1000或者init.rc,移除system分区的挂载,因为system也会存在于SD卡。
5.将system.img挂载到某目录,如挂载到/home/brilly/system:
使用Android编译后得到的host工具simg2img工具将system.img转换成ext4文件系统的image
#simg2img system.img system.ext4
#mount -t ext4 -o loop system.ext4/home/brilly/system
6.ext4格式化sd卡。
7.拷贝ramdisk目录所有文件到SD根目录
8.拷贝system目录所有文件到SD根目录下的system目录。
9.拷贝uImage到SD根目录。