Firefly-RK3128采用Cortex-A7架构四核1.3GHz处理器,集成Mali-400MP2GPU,拥有优秀的运算与图形处理能力;板载千兆以太网口、2.4GHzWi-Fi和蓝牙4.0,展现出不俗的网络扩展和传输性能;同时可支持Android与Ubuntu双系统,并拥有丰富的硬件资源与扩展接口,所以它是一台扩展性特强的卡片电脑,但我们相信你的创意与灵感能给予它更完美的定义
本文讨论如何利用安卓系统自身的启动特性,加进Linux系统的启动支持,并实现双系统的切换。要达到这一点,有必要先了解一下安卓系统的启动流程。
安卓系统的启动模式有两种:正常模式和急救(recovery)模式。急救模式,其内核和根文件系统均独立于正常模式,功能简单,一般很少更新,用作系统修复和维护。也就是说,安卓系统本身就是支持双启动的。安卓系统的启动流程是:
1U-Boot初始化1.1U-Boot读取CPU寄存器,如果有recovery标志,则跳转到31.2U-Boot读取misc分区,如果含有recovery命令,则跳转到31.3正常启动模式,跳转到22正常启动模式2.1加载boot分区2.1.1如果boot分区含有内核和initramfs,则分别加载到内存特定位置,跳转到 2.3(略过kernel分区处理)2.1.2如果boot分区仅含有initramfs,则加载到内存特定位置。2.2加载kernel分区到内存特定位置。2.3跳转到43急救模式3.1读出recovery分区内含的内核和initramfs,分别加载到内存特定位置,跳转到44初始化内核启动参数,将执行权移交内核。注意,initramfs是固化了的小型根文件系统,内核启动后会将其解压至内存中,并执行其中的init程序进行初始化。也就是说,initramfs是第一个获得执行权的根文件系统,负责挂载真正的根文件系统(可以在各种各样的存储设备中,如U盘、TF卡、USB硬盘、NAND或eMMC闪存等)。
分析安卓系统的启动流程,一个比较简单的双启动方案就是:
加入Linux系统的根文件系统分区。
替换recovery分区成Linux系统的内核和initramfs。
改变misc分区的内容,就可设定开机启动的操作系统。
在Linux系统内实现安卓急救系统的部分功能。
如何进入Linux呢?因为我们将Linux放在recovery分区,因此,问题等价于如何进入安卓的急救模式。以下有几种方式:
拔掉USB线,按住开发板的recovery键开机(无论是初次上电、重启或按reset键开机都可以)。这是临时性的切换,下次开机不按,还是会进入Linux。
在安卓系统的设置里选择恢复出厂设置。实际上,恢复出厂设备这个功能已被阉割了,重启后会进入Linux。
在安卓系统的关机菜单(点底部工具栏的关机按钮进入)增加了一项切换系统的选择。当然,它是检测到linuxroot分区才会出现,也就是说单系统是不会出现的。
将SDK里的rkst/Image/misc.img刷进到misc分区。
2~4项都是通过写misc分区,达到切换到recovery,这里也即是Linux的目的。
我们先来看看纯安卓的存储分区情况。分区信息在parameter文件里的CMDLINE行:
FIRMWARE_VER:4.4.2MACHINE_MODEL:rk30sdkMACHINE_ID:007MANUFACTURER:RK30SDKMAGIC:0x5041524BATAG:0x60000800MACHINE:3066CHECK_MASK:0x80PWR_HLD:0,0,A,0,1#KERNEL_IMG:0x62008000#FDT_NAME:rk-kernel.dtb#RECOVER_KEY:1,1,0,20,0CMDLINE:console=ttyFIQ0androidboot.hardware=rk30boardandroidboot.console=ttyFIQ0board.ap_has_alsa=0init=/initinitrd=0x62000000,0x00800000mtdparts=rk29xxnand:0x00002000@0x00002000(uboot),0x00002000@0x00004000(misc),0x00008000@0x00006000(resource),0x00008000@0x0000e000(kernel),0x00010000@0x00016000(boot),0x00010000@0x00026000(recovery),0x0001a000@0x00036000(backup),0x00040000@0x00050000(cache),0x00002000@0x00090000(kpanic),0x00180000@0x00092000(system),0x00002000@0x00212000(metadata),0x00200000@0x00214000(userdata),0x00020000@0x00414000(radical_update),-@0x00434000(user)CMDLINE是传递到内核的命令行,参数mtdparts就含有分区信息,其格式是:
0x00002000@0x00002000(uboot)大小偏移分区名称单位是512字节(即传统磁盘的扇区大小)。转换成表格比较直观些:
uboot:是用来存放第二阶段(stagetwo)U-Boot,如果开发板用的是eMMC分区,其U-Boot就不需要分阶段。
misc:非常有用的一个分区,下面会介绍到,用来控制启动模式的。
resource:存放内核的开机图片和设备树(DeviceTree)信息。
kernel:存放安卓的内核
boot:存放安卓的正常系统启动的初始内存文件系统(initramfs)。注意,如果在OTA方式下,boot分区跟recovery分区一样,含有内核和初始内存文件系统,此时kernel分区不作使用。
recovery:存放安卓急救模式所使用到的内核和初始内存文件系统。
backup:RK设计的用来存放备份固件的分区,FireNow系统开发板没有用到。
cache:安卓的缓存分区
kpanic:安卓的kernelpanic分区(?)
system:安卓的系统分区(挂载于/system)
metadata:RK的元数据分区,使用情况不详
userdata:安卓的数据分区(挂载于/data)
radical_update:RK的升级分区,使用情况不详
user:安卓的内部存储分区(挂载于/mnt/sdcard)
我们需要增加一个名为‘linuxroot’的新分区,用来存放Linux的根文件系统。为了使分区保持兼容,我们选择了替换radical_update分区,容量给够3G:
这样,修改后的parameter文件,其CMDLINE更改为:
$hexdump-Crkst/Image/misc.img0000000000000000000000000000000000000000|................|*00004000626f6f742d7265636f76657279000000|boot-recovery...|0000401000000000000000000000000000000000|................|*000040407265636f766572790a2d2d776970655f|recovery.--wipe_|00004050616c6c00000000000000000000000000|all.............|0000406000000000000000000000000000000000|................|*0000c000可见,前16K(0x4000)字节都是0,然后是一个“boot-recovery”命令,后面又跟着“recovery”,“–wipe_all”这些动作和参数,因此初次升级固件,系统会进入recovery模式,格式化所需的分区,之后才重启进入安卓系统。将misc分区清空,系统启动时就会加载boot和kernel分区,从而进入Android;而往misc分区写入“boot-recovery”命令,系统启动时就会加载recovery分区,从而进入Linux系统。开发时,可以用烧写工具烧写misc分区,从而控制进入哪个系统。
1.判断misc分区是否有特殊的标志内容“firefly-linux”,如果没有,则转6。2.判断backup分区是否含有安卓急救系统的initramfs,如果没有,则转6。3.提取backup分区的initramfs,解压至/root目录中。4.将/proc,/sys,/dev等重要的系统目录移到/root中(mount–n–omove)。5.执行execchroot/root/init命令,将/root目录切换成新的根目录,并执行里面的init程序,从而引导安卓系统本身的急救系统。操作完成。6.走原有流程,正常加载Linux系统。操作完成。采用这样的修改,用Linux系统的initramfs有选择地去加载安卓的急救系统,便可以达到要求。Linux系统的initramfs的init程序是shell脚本,修改和调试起来非常方便安卓系统的急救程序无需任何修改。剩下要做的,就要修改安卓系统的切换系统菜单项,将入切换到Linux系统的特殊标志内容“firefly-linux”写到misc分区即可。如此修改,可以最大程序上兼容原有系统:
很简单,写个脚本/usr/local/bin/b2android.sh将misc分区清空,然后重启即可: