Android系统移植(二)


一、启动镜像制作

上一讲当中我们总结了,Android系统运行所需要的三大部分:bootlader,kernelramdisk.那我们怎么才能获得它们呢?接下来我们来看看怎么制作bootlader,kernelramdisk.

Build bootloader

bootloder我们选用友善之臂提供的uboot_tiny4412
a)
安装好toolchain (arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz)并设置好
环境变量PATH,保证可以正常使用。
b)
解压 uboot_tiny4412-20130729.tgz并进入相应的目录
tar xzf uboot_tiny4412-20130729.tgz
c)
配置 uboot并编译
cd uboot_tiny4412
make tiny4412_config
make
d)
编译用于生成bl2的工具
make -C sd_fuse
或者
cd sd_fuse; make

这样bootloader就制作好了。

Build kernel

a) 进入linux-3.5的解压目录:cp tiny4412_android_defconfig .config

b) make

Android的支持做出的修改,见另一篇文档。

Build Android4.2.2

a) .setenv             中间有个空格,且必须执行这条

b)  make

中间的具体错误及处理方法,见另一篇文档。

Build filesystem

大家都知道Android移植需要的三大部分:bootloader,kernelramdisk,前两个部分制作较为简单,我就不再赘述了。现在我来说说第三个部分:ramdisk,也就是根文件系统。要制作根文件系统当然离不开Android源码,那我先来分析Android源码编译完成后生成的三个比较重要的镜像文件:ramdisk.img,system.img,userdata.img,它们和我们要制作的根文件系统息息相关,因此我们需要知道它们都有什么用?拿来做什么?

a)  emulator: emulator运行时会自动的加载这三个镜像文件,ramdisk.img是根文件系统,system.img包括了主要的包、库等文件,userdata.img包括了一些用户数据,在加载完这三个镜像文件后,会把systemuserdata分别加载到ramdisk文件系统中的systemuserdata目录下。

那既然这样,emulator仿真器已经可以运行了,是不是就意味着我们的根文件系统以及Android运行所需要的已经准备好了么?答案是:NO

这三个镜像文件只是emulator运行时加载的,而Android运行所需要的并不是它们.为什么这么说呢?我们先来看看Android源码编译完成后,在<android_home>/out/target/product/generic目录下生成ramdisk.img,system.imguserdata.img的这三个镜像文件,首先查看这写镜像文件的格式:

file *.img

ramdisk.img:       gzip compressed data, from Unix

system.img:        VMS Alpha executable

userdata.img:      VMS Alpha executable

userdata-qemu.img:VMS Alpha executable

从这里就能看出ramdisk.img映像文件是采用cpio打包,gzip压缩的,而其它几个镜像文件都是yaffs2文件系统。那这又跟我们Android移植有什么关联呢?当然有。我们再来看看友善之臂给我们提供的能让Android运行的这几个镜像又是什么格式:

fileramdisk-u.img

ramdisk-u.img:    u-boot legacy uImage, ramdisk, Linux/ARM,RAMDisk Image (Not compressed), 675532 bytes, Wed Jun  3 12:13:14 2015, Load Address: 0x40800000,Entry Point: 0x40800000, Header CRC: 0x2660B05F, Data CRC: 0xD045562D

filesystem.img

system.img:       data

从这里就可以看出来它们是不一样的,那又怎么能让Android正常运行(源码编译后生成的镜像文件)呢?方法自然有。

b) android:

接下来我们来讨论如何制作能让Android正常运行的镜像文件,首先,自然是我们的重点:ramdisk.img.我尝试了几种不同的方式去制作可用的镜像文件,即:

1. 将源码编译后生成的ramdisk.img制作成ramdisk-u.img

mkdir ram_dir

mv ramdisk.imgramdisk.img.gz

gunzipramdisk.img.gz

fileramdisk.img     //cpio

ramdisk.img:ASCII cpio archive (SVR4 with no CRC)

1.1  cd ram_dir

cpio解压

cpio -idmv<../ramdisk.img

将解压后的文件压缩重新打包为emulator运行需要的镜像文件:

find . | cpio-ov -H newc | gzip > ./ramdisk.img

fileramdisk.img

ramdisk.img:gzip compressed data, from Unix, last modified: Tue Jan 19 11:59:47 2016

1.2  .gz格式的ramdisk.img.gz转化为安卓运行需要的文件系统ramdisk-u.img----->zImage转化为uImage,mkimage工具是u-boot格式uImage内核映像制作工具,uImage就是在zImage的前面加上64字节的头信息

mkimage -A arm-O linux -T ramdisk -C none -a 0x40800000 -n "ramdisk" -dramdisk.img.gz ramdisk-u.img

mkimage  

     -A ---->  用于指定CPU类型,比如ARM

     -O ---->  用于指定操作系统,比如linux

-T---->  用于指定image类型,比如kernel

-C---->  指定压缩类型

-a---->  指定image的载入地址

-e---->  内核的入口地址,一般是:image的载入地址+0x40(信息头的大小)  

-n---->  image在头结构中的命名

-d---->  无头信息的image文件名

-x---->  设置执行位置

 

-T 指定映像类型,可以取下列值:

standalone,kernel,ramdisk,multi,firmware,script,filesystem

      -C 指定映像压缩方式,可以取下列值:

none   不压缩

gzip   gzip的压缩方式

bzips  bzip2的压缩方式

-a 指定映像在内存中的加载地址,映像下载到内存中时,要按照用mkimage制作映像时,这个参数所指定的地址值来下载

-e 指定映像运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)

-n 指定映像名

-d 指定制作映像的源文件

 

2. 直接将root目录制作成ramdisk.img

(1).mkbootfsroot/ | minigzip > src_system/ramdisk.img

(2).mkimage -Aarm -O linux -T ramdisk -C none -a 0x40800000 -n "ramdisk" -dramdisk.img ramdisk-u.img

制作好了根文件系统,接下来就要准备system.img和userdata.img,也是两种方式:

1.源码编译后的system.img制作成system.img

因为这两种镜像文件是不同的格式,只需要进行格式转换就可以了。Android系统运行所需要的system.img是data格式,也就是ext4文件系统。源码编译完成后生成的system.img是yaffs2文件系统,所以首先需要对yaffs2进行解压,需要用到unyaffs,该软件可以从网上下载。

mkdir system_dir

mkdir src_system

cd system_dir

unyaffs ../system.img    //解压源码生成的system.img文件

生成ext4--data文件

make_ext4fs -s -l 330000000 -a systemsrc_system/system.img system_dir

 

  1. 直接将system目录制作成system.img

make_ext4fs -s -l 330000000 -a systemsrc_system/system.img system_dir

但是注意:该过程之前需要把/vendor/friendly-arm/exynos4412目录下的busybox-bin.tgz和gapps-jb-20121212.tgz解压到system/目录下

  1. userdata.img制作和system.img一样,就不再赘述了。

至此,Android系统运行所需要的镜像已经准备好了。可以开始我们的Android系统移植了。

二、烧写Android

两种烧写方式:SD卡脱机烧写,fastboot

1.SD卡脱机烧写

按照友善之臂提供的烧写步骤进行即可。但需注意:友善之臂的superboot开启了三星推荐的TrustZone安全模式,注意内核必须也要启动 TrustZone 模式,才能配合此版本的 Superboot使用,否则将无法启动。

而烧写安卓到SD卡时uboot不支持trustzone,内核必须禁止trustzone后编译才能启动;如果是superboot,此日期(2014-03-17)前的应该能启动,此日期后的则必须是启用trustzone后编译的内核才能启动。

 

2014-03-17 更新说明:

================================

Superboot4412更新如下:

1)修正了eMMC大小显示不正确的问题;

2)实现了根据eMMC大小进行智能分区,以使Android下的Data分区得到更大的可用空间;

3)开启了三星推荐的TrustZone安全模式,注意内核必须也要启动 TrustZone 模式,才能配合此版本的 Superboot使用,否则将无法启动;

 

总结:用uboot必须禁止TrustZone安全模式,用superboot必须开启TrustZone安全模式

2.fastboot烧写

uboot界面输入fastboot,PC端输入以下命令进行烧写:

fastboot flash kernel zImage (烧写kernel)
fastboot -w (格式化userdata和cache)
fastboot flash ramdisk ramdisk-u.img (烧写ramdisk)
fastboot flash system system.img (烧写system)

这样,Android系统移植完成。EMMC方式启动Android可能会遇到问题,下面进行总结:

VFS: Cannot open root device"(null)" or unknown-block(0,0): error -6

 

 Kernel panic - not syncing: VFS: Unable tomount root fs on unknown-block(0,0)

该错误是由于内核找不到根文件系统的分区位置。但是一直找不到解决方法,根文件系统无法挂载就不会执行安卓的第一个进程init,因此安卓不能启动。

 

最初uboot的bootcmd环境变量为:

bootcmd=movi read kernel 0 40008000;bootm40008000

但是内核启动时一直找不到根文件系统,通过设置bootargs参数的root=/dev/mmcblock0p1或者root=/dev/ram等等都不行都会出现类似上面的错误或者下面的错误(卡死在这):

 Nosoundcards found.

[   3.675000] Waiting for root device /dev/mmcblock0p1...

 

参考EMMC分区方案,又一无所获。后来参照资料更改bootcmd参数结果出现严重错误:找不到内核image

由此得出bootcmd参数应该比较重要。

参照别人的uboot的环境变量参数为下面值后,根文件系统正常挂载,安卓系统正常启动:

bootargs=console=ttySAC0,115200n8   androidboot.console=ttySAC0 uhost0=n ctp=2 skipcali=y vmalloc=512m lcd=S70init=/init root=/dev/mmcblock0p1 rootwait rw

bootcmd=movi read kernel 0 40008000;movi  read  rootfs 0 41000000 100000;bootm 40008000 41000000

 但是还是有点疑问。

安卓系统启动流程:

uboot----->kernel------->ramdisk

(1).UBOOT启动后是根据bootargs参数来启动内核的么?会怎么选择?

         bootargs会传给内核,优先使用bootargs

(2).bootcmd参数的作用以及它需要传递给内核还是...?

         作为自动执行命令

(3).bootargs和bootcmd在安卓系统启动过程中担任什么角色?它们的功能以及设置好后的执行现象由此决定?

         在安卓系统启动前期的uboot的启动和内核启动指定了参数.如果这两个参数设置错误,安卓不能正常启动。

 

查资料获取bootcmd的作用:

uboot提供两种工作模式:一是启动加载模式,一是下载模式。工作在启动加载模式时,uboot会自动执行bootcmd命令,如bootcmd=movi read kernel 0 40008000;bootm 40008000   uboot首先把内核镜像拷贝到内存地址0x40008000的地方,然后执行bootm0x40008000命令

bootm完成的功能有:

.CRC校验image head struct 的64字节的正确性

.根据镜像压缩类型,把kernel解压到指定的位置(0x40800000)

.调用函数do_bootm_linux(...),启动内核

而在启动内核的时候(do_bootm_linux时),会从参数中获取环境变量bootargs的值,然后再从image head struct中获取kernel的入口地址(0x40800000),最后会把commandline拷贝到指定的COMMAND_LINE地址,最后执行kernel的入口函数,把控制权交给kernel.

既然kernel加上了64K字节头信息,那加载地址和真正的入口地址应该不一样啊?为什么两个地址都设置成0X40800000,还是说UBOOT找到该地址时做了相应的处理?

mkimage时需要加上64K字节头信息,这样UBOOT才能识别这个映像是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置,入口点在内存的哪个位置以及映像名。

在UBOOT中每一个命令都对应一个其实现的函数,在启动linux过程中,主要是执行环境变量bootcmd和bootargs所定义的命令。因此,对这两个变量的定义很重要,如果定义不对就不能正确的加载内核和文件系统。尤其是必须确保文件系统所在分区。为什么找不到文件系统,因为没有告诉uboot--->bootcmd。那为什么要告诉uboot而不是直接告诉kernel呢?因为uboot的命令行参数会传递给内核。

文件系统和根文件系统:

根文件系统镜像直接烧写到EMMC中的第一个块的第一个分区,将根文件系统镜像从EMMC中读到指定地址0x41000000处,然后bootm解压缩到指定位置。

那文件系统是安卓的第一个进程init启动之后建立的么?

既然告诉了uboot那uboot是怎么告诉内核这个文件系统的位置呢?

bootm后调用do_bootm_linux么?

可以不可以不定义bootcmd中文件系统的位置而直接告诉内核让内核自己去找根文件系统?

bootm rootfs那条命令又是什么作用,是像内核那样启动根文件系统么?文件系统的位置和内核一样是固定的么?

bootcmd:是系统在上电自动执行时所执行的命令

bootcmd=movi read kernel 0 40008000;movi  read  rootfs 0 41000000 100000;bootm 40008000 41000000

这是两条命令:具体功能看上面bootm分析(NOTE:bootm会根据镜像压缩类型将文件解压到指定的地址).

 

 

至此,Android系统移植启动镜像的制作已经完成,Android可以正常启动。

未完待续。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值