让Android运行在SD卡上


首先介绍原理,最后介绍具体怎么实现。

 

1.  Android启动流程

eloader,uboot存储在SPI Flash上面。SPI FLash在开机时会被SoC映射到某地址,然后依次启动eloader,uboot。

uboot启动后,将会启动kernel。

1.1.kernel在哪里呢?

1.1.1.    kernel可以在boot.img中

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来。

1.1.2.    kernel可以在某个文件系统里

文件系统必须布局在某个分区。分区可以存在于sd卡,emmc上,甚至是u盘。也就是kernel存在于某种存储设备的分区上。uboot需要支持分区上的文件系统,能够从文件系统中将kernel读到内存里。

 

以上两种方式的kernel,文件封装格式方式不一样。

1.2.kernel的两种文件封装格式

对于boot.img中的kernel,就是普通的zImage格式,而对于在文件系统里存储的,kernel是uImage方式,此uImage通过mkimage方式产生,请参考后文内容。

zImage就是经过普通压缩的vmlinux。uImage是把zImage再次封装后的形式。u的意思是uboot再次解析的意思。

 

uboot通过各种努力,从不同地方找到了kernel了,这各种努力,根据在kernel存储在哪里有不同的行为。

1.3.kernel如何被执行起来并找到根分区

根据kernel存储在哪里来分别描述:

1.3.1.   kernel在boot.img中– booti

boot.img如何被uboot找到呢?

又分为两种情况:

1.3.1.1. boot.img dd 到boot分区。

一般来说,无论是emmc,还是nand,都会有一个boot分区存在。boot.img会被同linux 命令dd一样的方式写入到这个分区里。

uboot可以找到这个分区,然后读出boot.img里的内容。所以uboot需要支持emmc的读写,sd卡的读写,nand的读写,甚至u盘的读写。这些读写都是按扇区方式读写的。

1.3.1.2. 从分区直接读--booti boot

这种启动方式,uboot的启动参数是booti boot。

booti命令首先根据分区名称找到boot分区位置,然后读入到内存中,内存中的内容,就是boot.img。

解析boot.img,得到内核,并得到ramdisk,cmdline。

uboot中有一个重要的环境变量bootargs,不同的uboot版本,会让cmdline和bootargs较量来决定什么值传递给kernel。

最重要的boot args是指定了根分区及init。

kernel会以此为依据找到根分区及init。

1.3.1.3. boot.img存在某文件系统中--booti address

boot.img不是直接按二进制方式写入到某个分区,不像dd一样。而是存在分区的文件系统中。比如存在SD卡第一个fat32文件系统分区中,或者emmc第15个ext4文件系统中。

这种启动方式,首先需要将boot.img加载到内存,然后uboot从这个地址启动。boot.img就可以存在sd,emmc,nand,u盘等分区的文件系统中,需要首先被ext4load,fatload等加载到内存中。

加载到内存后,uboot的booti命令可以从这个内存地址启动。

booti address

1.3.2.   kernel在分区文件系统中-bootm

这时候,kernel需要是uImage封装方式。并且需要被uboot提供的ext4load,fatload等方式将uImage加载到内存,然后被uboot的bootm来启动。

1.3.3.   kernel如何找到根分区

kernel需要找到根分区,这个是通过uboot传递给kernel的cmdline指定的。VT6080一般都是指定一个实际分区。也可以是ramdisk。

kernel启动了,也找到了根分区,如何启动Android系统呢?

1.4.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不一样的格式。

2.  如何让Android运行在SD卡上

让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目录。

 

3.  VT6080实做从SD卡启动

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根目录。

 

你可能感兴趣的:(工作记录)