XX教你玩 MiniPC (三)MK908之CPU RK3188启动 bootloader分析

上一篇我们已经弄明白了整个MiniPC的软件组成,但是并不是很清楚系统启动的细节,所以这一篇就仔细说明一下rk3188 从上电开始到启动内核为止的过程。

再说明一点,这篇有一部分是翻译的内容,但针对翻译的内容,我亲自做了一些实际的实验,所以我还是自己坦白,这篇算是“伪原创”吧。

先给大家发原创的链接:https://gist.github.com/sarg/5028505

这个是国外的一个大神做的工作,真是很佩服啊。

那么,有人会问,我们为什么这么费劲的去解析启动部分的内容,第一,我们没有RK3188的datasheet,第二,没有bootloader的源码。而我们的目的是能够自由的开发或移植bootloader到rk3188上,能够启动我们定制的操作系统。如果谁有RK3188的详细datasheet或者bootloader的源码,一定要给我一份啊!

首先,先翻译一下原创的内容吧。绿色的部分是原文没有提及,我自己根据自己的实验猜测的。其中的代码就不拷贝过来了。

如何获取RK3066的BootRom

 

以下是我的步骤。

首先,我在我的设备的升级包(update.img)中发现了一个RK30xxLoader(L)_V1.18.bin的文件。我用rk29Kitchen把它解包出来。

使用strings 命令(linux下)去查看其中的参考字符串,并没有发现有用的地方,所以我觉得这个文件是经过加密混淆过的。

下一个步骤是使用IDA pro来反汇编RKAndroidTool.exe ---rockchip公司提供的用于烧写升级包的工具。在分解过程中,我发现了loader文件使用了RC4的加密方法。这个文件被分隔成512字节的多个块,每个块都被加密。

所以,我用perl写了unpack_loader.pl 脚本,这样我得到了4个输出文件。

1           14a         321c        30_LPDDR2_300MHz_DD
2           3366        c914        rk30usbplug
4           fc7a        3400        FlashData
4           1307a       1ac00       FlashBoot

 

幸运的是,这些文件名都是有意义的名字。

30_LPDDR2_300MHz_DD  是一个dram 初始化处理过程。FlashData 是只是30_LPDDR2_300MHz_DD的拷贝,只不过是512字节对齐(flash的块大小)。

rk30usbplug 是负责MaskRom模式下的处理。FlashBoot是从NAND flash启动的第二阶段,里面也包含了DFU模式的处理。

我也反编译了上面所说的4个文件。在FlashBoot中,发现了用于DFU模式下从DDR Ram区域获取数据的指令。所以,我的想法是在FlashBoot中注入代码,把bootrom拷贝到DFU模式下能够访问到的内存地址。但是一个问题是,FlashBoot会覆盖掉从0地址开始的内存 --- 这个地址也是上电时bootrom被加载的地方。所以,我需要做的就是在Flashboot覆盖掉它以前,从0地址拷贝出0x2800个字节(boot rom 的大小)。这个就是我们想要的bootrom.bin。

然后,我写了一系列的工具:patch_loader.sh 帮助 注入代码;pack_loader.pl重新组包,并且用rkcrc签名。

我所有的命令就是以下几个:

# perl unpack_loader.pl RK30xxLoader\(L\)_V1.18.bin write
# sh patch_loader.sh FlashBoot.bin
# perl pack_loader.pl dram=30_LPDDR2_300MHz_DD.bin boot=FlashBoot.bin.patched data=FlashData.bin usbplug=rk30usbplug.bin out=patchedLoader.bin

用RKAndroidTool.exe把生成的patchedLoader.bin烧写到flash中,然后用以下的命令获取loader。

# rkflashtool m 0x64000000 0x2800 >bootrom.bin

我上传了 bootrom.hex。执行命令xxd -rp bootrom.hex >bootrom.bin 把bootrom.hex变换成二进制文件boot.bin。

现在,loader的所有部分都被破解并且反编译了,现在我们就可以把uboot移植到这个平台上,启动我们想要的任何操作系统。

 

 

RK3066 的启动过程

在上电时,rk30在bootrom的0地址开始启动。

Bootrom把自己拷贝到SRAM,然后到idbrom中搜索dram的初始化句柄。

Bootrom加载dram初始化句柄到内存(SRAM),然后执行它。

Bootrom到idbrom中搜索flashboot,加载到内存(DRAM)

如果没找到flashboot,bootrom继续搜索usbplug然后加载它。

如果找到,flashboot初始化NAND,搜索parameter文件,加载kernel到DRAM,然后启动kernel。


该翻译的已经翻译完了,不知道大家能不能理解,反正我是明白了一部分。对于作者的逆向工程的功底,我真是自叹不如啊。既熟悉Windows平台的可执行文件破解,又熟悉arm架构的机器码和汇编指令,还能开发工具进行代码注入。

下面就是我的实验阶段了。

我没有利用IDA pro进行反编译调试,这部分工作好长时间不做了,很生疏,费时。总之,把作者写的perl脚本下载下来运行,解我的Rk3188的bootloader,是完全行的通的。这说明rk3066和rk3188在启动过程以及架构上没有很大改变。运行结果如下图:

XX教你玩 MiniPC (三)MK908之CPU RK3188启动 bootloader分析_第1张图片

从中我们也能看到3188_LPDDR2_300MHz_ 和FlashData的大小很接近,FlashData是以512字节对齐的。这符合作者的阐述。

其实,以上的这些信息,我们使用winhex也是可以看到的,但不是很直观的。

  XX教你玩 MiniPC (三)MK908之CPU RK3188启动 bootloader分析_第2张图片

比较解压完的4个文件的大小,发现跟上面解包过程中提供的信息是一样的,这也能说明这个过程是正确的。

我没有分析3188_LPDDR2_300MHz_ 和rk30usbplug,因为DDR2的初始化是必需的过程,没有必要改动。rk30usbplug是通往MaskRom模式的(其实我是真不懂MaskRom模式是什么模式,做什么用,谁明白细节的告诉我啊),但是在我测试过程中,从没进入过这个模式,所以略过吧。

那么重点就是仔细分析FlashBoot这个模块了。

第一步,反汇编FlashBoot模块。FlashBoot模式是纯的二进制文件,不是我们通常的elf文件,在这里没有段的概念,没有架构的概念,有的只有二进制的代码和地址。先看看反汇编的指令吧。

arm-linux-gnueabi-objdump -b binary  -marmv5 -D FlashBoot.bin > flashboot.asm

每个标准的gcc交叉编译工具链,都很提供一个objdump程序,这是帮助我们做一些debug工作的。我用的arm gcc是从linaro社区下载的,版本是4.7。其中的参数是必须的,binary表示我们的输入是一个纯的二进制文件,marmv5是告诉objdump安装哪种架构来将机器码翻译成汇编语言。因为这个arm objdump不支持armv7的架构,所以先用armv5代替了,虽然有些小的差别,但不会错的天翻地覆。大家可以通过 --help来看看自己的objdump支持什么架构。

这样就得到反汇编得到的汇编语言。下面着重分析这个汇编文件。

XX教你玩 MiniPC (三)MK908之CPU RK3188启动 bootloader分析_第3张图片

从上面的分析得出,第一次拷贝时覆盖了0地址,所以需要在第一次拷贝之前进行代码注入。

那么以下,就分析作者的代码注入原理。

首先,先关注两个重要参数。offset和inject,分别代表注入代码的静态文件偏移和执行时的注入点。 这两个点的选择是有依据的。offset为A0,在这个位置数据全部为0,没有特殊意义,并且有很大的空间来存储注入代码。inject为224,通过上面的分析知道,这个是在第一次拷贝之前,比较早的执行位置。

再来看JUMP1的计算,实际上这里的目标地址是里面的2b0,因为2b0位置是memcpy的代码,从汇编代码看,就是在r0,r1,r2三个寄存器存入合适的数据(传参),然后把0地址的数据拷贝到0x64000000去,个数为0x2800,因为从仅有的手册上看bootrom只有10K。0x64000000是比较高的地址,不会被覆盖掉。但为什么计算过程这么麻烦呢,是因为流水线的缘故,这个大家自己去了解吧。

在执行我们注入代码的最后,是恢复原始环境的代码,就是把注入之前的环境恢复回来。224位置的代码被我们修改了,我们返回的时候,要把224位置的代码重新补回来,然后再跳转到228。224位置原来的意思是:把2D8中的数据存入r0,我们则在注入代码中直接写入10080000,效果是一样的。返回228之后,程序就正常执行了。

JUMP的计算,目的是获得offset位置相对PC的偏移,因为我们的跳转都是相对跳转。当执行到224的时候,要跳转到A0,就要计算向前跳转的偏移。

XX教你玩 MiniPC (三)MK908之CPU RK3188启动 bootloader分析_第4张图片

我们明白这些参数的含义之后,就可以用dd命令完成 flashboot的重新组装。

重组的过程是分解的逆过程,在此不多说了。


rkflashtool在前几篇的链接中有源码,在linux下编译通过,并且可以成功运行。在windows下,可执行文件rkflashtool.exe没法成功运行,不知道为什么,也没仔细调试,大家成功了的话,通知一下我吧。

到现在,我们就明白了注入过程和原理。根据我对kernel代码和上面地址的分析,以下就是我个人的猜测了。

bootrom是CPU内置的ROM,内容无法被修改,启动时会依据硬件设置来选择启动方式。

bootrom把自身拷贝带内部sram,因为内部sram无需初始化,所以非常适合bootrom操作。

我们所说的bootloader,实际上是被烧写在一块神秘的idbrom上。这个rom的特性应该是类似于Nor flash,可以随机的访问,并且能执行代码。这样bootrom在其中找到句柄时,就可以立刻跳转过去执行,而不用先拷贝后执行。idbrom的flash肯定不是NAND flash。这点从烧写工具上就可以看出:windows上的rkandroidtool,bootloader和paramter文件都在0地址,这是一组矛盾。linux上的rkflashtool ,有i选项和r选项,分别对应idb flash和nand flash。

在bootrom的高字节中,包含了CPU的标识ID,通过判断这些ID,就可以知道是什么CPU。

Flashboot中才是我们通常Bootloader的工作。所以获得flashboot的动作属性,是我们后续启动的关键。

动作属性主要包括:

对parameter的解析

对kernel分区的使用

对kernel启动时cmdline的作用


这篇的内容涉及很多底层的知识,搞明白这些真的非常有益。

下一篇讲述 Flashboot的动作属性。


有人知道IDB是什么的缩写吗?告诉我吧!

你可能感兴趣的:(汇编,kernel,Flash,嵌入式,bootloader,rk3188,MK908)