在上一篇中,我已经给KPW2焊上了新的8GB eMMC,并且验证了通过MfgTools直接把U-Boot加载到内存中运行是可行的,这次就完成主板的修复。

重新配置参数

其实在上一步完成时,U-Boot的启动记录是这样的:

Board: Unknown
Boot Reason: [ POR ]
Boot Device: NAND
Board Id:
S/N:
I2C:   ready
Invaild board id!  Can't determine system type for RAM init.. bailing!
DRAM:   0 kB
Using default environment

这是因为换上全新的eMMC,KPW2也就像失忆了一样,忘记了一些比如自己的型号之类的重要参数以至于无法顺利初始化RAM……这里是我的失误,实际上在拆除原来的eMMC前,应该在U-Boot中输入idme查看NVRAM变量并记录下来(注:虽然名为NVRAM变量,但是实际上是存储在eMMC当中的)。但是我没有做过,所以现在只能自己恢复。在U-Boot中输入idme可以看见一共有5个变量,分别为serial、mac、sec、pcbsn、bootmode和postmode。只要恢复serial mac pcbsn并把其它清空即可。serial是机器的序列号,在“Kindle需要维修的界面”或者包装盒上有写,但是据说会不一样,我是按照需要维修界面上的DSN填写的(使用idme serial XXXXX命令设置)。MAC地址在包装盒上是没有写的,如果要找回可能需要登陆路由器管理界面看下DHCP分配,如果Kindle连接过WiFi应该有记录。如果没有,那么就随便编一个吧,比如把自己电脑的MAC地址+1然后写上去。pcbsn可以在主板的贴纸上找到,如下图:

这个就是PCBSN,也使用idme指令写入。写入完成后记得检查下是否正确,顺便清空一下其它的值(比如idme bootmode就可以清空bootmode),输入reset重启,等待识别usb后再次进入U-Boot,此时应该显示

Board: Pinot WFO
...
DRAM:  256MB

说明参数已经配置完成。

刷入新的系统

由于Kindle的Fastboot有一些问题(无法下载过大的文件),没有办法直接刷入完整的rootfs,我使用的方法是先刷入DIAGS,进入DIAGS进行分区+格式化,再进入正常模式的Recovery Mode,拷入系统升级镜像,进行升级。以下是具体步骤:

首先需要在电脑上安装一下LibUSB Win32,在SourceForge上下载devel_filter然后安装。此时开始菜单中应该有了Inf Wizard这个工具。

下面是个稍微需要一点速度的活,在U-Boot交互界面下输入fastboot回车,此时电脑应该发现新硬件,随后在电脑上打开Inf Wizard,选择下一步,里面能看见VID/PID分别为0x1949 0xD0E0的Kindle,继续点击Next生成Inf,保存在一个容易找到的地方(比如我的文档)。注意这一步是没有驱动的,所以直接用Filter Driver Installer是不会成功的。生成完成下方会有一个安装驱动的选项,点击安装。如果失败请确认自己电脑的系统是不是Windows 7及以上的64位版本,如果是,请在开机时按F8(BIOS)或者在电脑设置里面选择重启至高级启动选项(UEFI)选择禁用驱动签名强制进入系统,然后再手动在设备管理器里面安装刚刚生成的Inf。再未安装驱动的情况下,fastboot模式会在大约5秒内自动退出,重新输入fastboot回车即可。安装完成后应该在设备管理器内会看见如下设备:

在下载http://ixtab.tk/kindle-touch-images/PW2/中的diags文件系统、diags镜像和主内核镜像。在http://wiki.mobileread.com/wiki/Kindle_Tools_Index下载fastboot for Win32。放到同一个文件夹当中,把上一节编译的U-Boot也复制过来,目录结构应该如下:

在这个目录下打开命令提示符,依次执行以下命令:

fastboot flash kernel main_kernel.img
fastboot flash diags_kernel diags_kernel.img
fastboot flash diags mmcblk0p2.bin
fastboot flash prod u-boot-prod.bin
fastboot flash bist u-boot-bist.bin

现在需要通过fastboot烧录的内容都完成了,继续执行以下命令设置进入diags并重启

fastboot setvar bootmode diags
fastboot reboot

注意重启后还是需要使用MfgTools引导启动的,一会再修复它,先装系统。启动过程可能会出错,如果出错,就长按电源键重新启动,直到进入Diags菜单:

   PINOT-WFO - System Diags
   ~~~~  1.12.536.206610  ~~~~ 
     pcbId:027220115105020M
~ S ~ Device Setting  
P) Touch Sensor Plate Test     
O) Operator test suite     
E) 511     
N) Misc individual diagnostics     
Y) nART factory test     
U) USB device mode     
H) Touch Hold Test     
D) Exit, Reboot or Disable Diags     
L) Lock diags screen     
X) Exit

选择D进入下一级菜单

   PINOT-WFO - Exit, Reboot or Disable Diags
   ~~~~  1.12.536.206610  ~~~~ 
     pcbId:027220115105020M
~ U ~ USB Bundle Install  
C) Check Pass/Fail Status     
R) Reboot System     
D) Disable Diagnostics     
L) Exit to login prompt     
M) ReportToXml     
S) Dump System Logs     
X) Exit

选择L返回登录界面,随后Q确定,过一会会提示login,使用root/mario登录。登录后输入fdisk -l检查分区情况是否正确无误(载入diags内核的过程中应该会自动进行分区)

Disk /dev/mmcblk0: 7818 MB, 7818182656 bytes
4 heads, 16 sectors/track, 238592 cylinders
Units = cylinders of 64 * 512 = 32768 bytes

        Device Boot      Start         End      Blocks  Id System
/dev/mmcblk0p1   *        1025       12224      358400  83 Linux
/dev/mmcblk0p2           12225       14272       65536  83 Linux
/dev/mmcblk0p3           14273       16320       65536  83 Linux
/dev/mmcblk0p4           16321      238592     7112704   b Win95 FAT32

继续执行mkfs -t ext3 /dev/mmcblk0p1对主分区进行格式化。格式化完成后执行reboot重启。重启后记得中断uboot的正常启动,运行idme bootmode清除启动模式,随后执行reset再进行一次重启。重启后应该会加载正常的内核,在开机提示“Press [ENTER] for recovery menu…”时按下回车,进入恢复菜单,选择E连接电脑。此时电脑上会出现Kindle的分区,把升级文件复制进去,之后安全弹出设备,在串口终端按X退出,随后在恢复菜单选择U安装系统更新。还是和之前一样,可能会出错,出错后长按电源键强制重启即可。现在应该可以顺利开机了:

至此,除了开机需要用MfgTools引导,已经和一台正常的Kindle一样了。但是解决开机问题,真的就不那么简单了,可以说相当蛋疼……

开机引导修复

首先先讲一个疑问或者说结论:KPW2使用的i.MX 6 SoloLite所表现的行为和数据手册当中的描述不太一致,个人猜测可能是采用了定制的bootROM而非公版默认的bootROM,因此SRC寄存器还有OTP设定和数据手册上描述的有出入,我之前一直对着数据手册来绕了一个大弯子。因此以下对于数据手册,只看流程,不去在意具体的数据。

根据现有情况已知KPW2被配置成了eMMC启动模式(OTP中存储的数据按照数据手册为NAND,实际上是eMMC),因此直接看数据手册中对于eMMC启动的描述(404页 Expansion Device):

MMC/SD/eSD/SDXC/eMMC can be connected to any of the USDHC blocks and can be booted by copying 4Kbyte of data from MMC/SD/eSD/eMMC device to internal RAM. After checking the Image Vector Table header value (0xD1) from Program Image, the ROM code performs a DCD check. After successful DCD extraction, the ROM code extracts from Boot Data Structure the destination pointer and length of image to be copied to RAM device from where code execution occurs.

也就是讲程序镜像的开头应该为0xD1,也就是中断向量表的头,检查通过会继续启动。对照一下编译出来的u-boot-prod.bin发现0xD1位于0x400位置,继续看数据手册(415页 Expansion Device Structures Layout):

也就是程序镜像从0x400位置开始,和bin中看见的情况一致。检查fastboot中对于prod的定义,是下载到0地址,那么0xD1确实应该在0x400位置,那么为什么无法启动呢?还是得看数据手册(405页 MMC and eMMC Boot Details):

The boot partition can be selected for an MMC4.x card after the card initialization is complete. The ROM code reads the BOOT_PARTITION_ENABLE field in the Ext_CSD[179] to get the boot partition to be set. If there is no boot partition mentioned in BOOT_PARTITION_ENABLE field or the user partition has been mentioned, ROM boots from the user partition.

也就是说对于MMC4.x的卡还有启动分区一说,会在开机读取Ext_CSD[179]来确定。等等,这是啥?了解不知道的东西最好的办法就是看规范文档。eMMC的规范文档可以在www.jedec.org上下载到,我看的是4.4版本。要搞清楚启动分区,得先弄明白分区是怎么一回事。JEDEC的协议文档因为是禁止以一切形式再生的,所以我就不贴原文了,有兴趣的自己下载文档去看,注册一下就可以了。

首先是看文档7.2的Partition Management。对于eMMC设备,默认来说被分为四个区域,分别是用户数据区域(User Data Area)、两个启动区域分区和一个RPMB(Replay Protected Memory Block)分区。它们虽然位于同一个die上但是逻辑上是保持独立的,也就是每个分区都各自从0地址开始寻址。这个和基于MBR或者GPT的分区表类似,比如MBR分区表就是位于存储设备第一个扇区(512字节)的一段内容,里面定义了四个主分区的起始柱面和终止柱面。在Linux系统上,比如硬盘是/dev/sda,MBR分区表就位于/dev/sda的0地址位置,随后Linux系统会根据分区表的设定,从/dev/sda抽象出/dev/sda0、/dev/sda1、/dev/sda2和/dev/sda3这三个分区,这三个分区各自从0地址开始寻址,但是实际上都是/dev/sda的一部分。MMC则是从控制器层面进行了抽象,得到四个分区。但是值得注意的就是,真正抽象前的分配对于Linux是不可见的,Linux里面看见的/dev/mmcblk0只是User分区,所有从mmcblk0抽象出来的分区都只是User分区下的小分区!这也是为什么之前我常识备份的时候连U-Boot的影子都没见到,因为我一直在User区操作,而U-Boot根本不在那里!想要访问Boot分区还需要使用MMC Switch(0x06)指令进行分区选择。

不知道上面讲了这么多大家有没有看明白,简单讲就是有两套分区就是了。讲回之前的启动分区。首先了解Ext_CSD是eMMC 4.0规范新加入的一个eMMC寄存器,长度为256字节,具体作用慢慢讲。其中,Ext_CSD[179]就是用于存储启动配置的,iMX6在启动时就会读取它来确定要从哪个分区启动(启动分区同样可以设置为禁用,但是根据描述此时iMX6依然会尝试从用户区启动)。随后,iMX6设定Ext_CSD[179]中的PARTITION_ACCESS位,启用对启动分区的访问。这时可以使用正常的MMC操作指令访问启动分区,就像整个eMMC就只有启动区一样。

那么根据之前mmcinfo的结果,得知当前配置下启动分区为boot1,继续检查fastboot相关的分区定义(省略了一部分内容):

  {
.name = "bootloader",
.address = 0,
.size = (376*1024), /* 376 KiB */
.partition = CONFIG_BOOT_FROM_PARTITION,
  },
  {
.name = "prod",
.address = 0x0, /* overlap with bootloader */
.size = (120*1024), /* 120 KiB */
.partition = CONFIG_BOOT_FROM_PARTITION,
  },
  {
.name = "bist",
.address = CONFIG_MMC_BIST_ADDR, /* overlap with bootloader */
.size = CONFIG_MMC_BIST_SIZE, /* 256 KiB */
.partition = CONFIG_BOOT_FROM_PARTITION,
  },
  ...
  {
.name = "kernel",
.address = CONFIG_MMC_BOOTFLASH_ADDR,
.size = CONFIG_MMC_BOOTFLASH_SIZE,  /* 14 MiB */
.partition = 0,
  },
  {
.name = "diags_kernel",
.address = CONFIG_MMC_BOOTDIAGS_ADDR,
.size = CONFIG_MMC_BOOTFLASH_SIZE,  /* 14 MiB */
.partition = 0,
  },

不难发现prod bist之类的都在CONFIG_BOOT_FROM_PARTITION分区当中,而内核之类的都在0分区(用户区)当中,而CONFIG_BOOT_FROM_PARTITION在imx60_wario.h中被定义为1,根据代码注释为启动分区1。现在看来之前u-boot也确实通过fastboot烧录到了正确的位置。但是为啥不能启动呢?还是得看手册……下面一部分的内容在iMX6的手册上没有怎么讲,但是在eMMC标准中可以注意到有启动时总线宽度这么一说:

在eMMC进入启动过程的时候(boot operation),总线宽度和DDR模式由Ext_CSD[177]确定。

仔细看U-Boot代码中drivers/mmc/mmc.c,可以看到这么一条注释:

/* LAB126 - always stay in 1-bit boot for now */

Lab126是亚马逊的研发部门,这条注释就是他们写的,目前一直使用1-bit的启动模式。然而想想之前mmcinfo读取出来的启动位宽设定是4-bit SDR!这就是问题所在!定位到问题后,解决就不难了。就在找到这句注释的函数mmc_switch_partition,本身就具有配置启动位宽的功能,只是被禁用了。而且这个函数会在每次U-Boot载入的时候被调用(用于切换分区),那么就对这个函数动手让它在启动时重新配置一下启动位宽就可以了。

首先把函数内所有用#if 0 … #endif禁用的部分恢复,然后把if ((ext_csd[EXT_CSD_CARD_TYPE] & 0xC) && enable_boot != 0)这个判断给去掉,下面的选择最佳模式的代码也去掉,就留下一句boot_bus_width=0。编译U-Boot后用MfgTools加载运行,因为函数会在启动时被自动调用,因此只要手动输入mmc dev 1和mmcinfo看信息,应该能看到如下提示说明修改成功:

Current boot width: 1-bit SDR

现在输入reset回车,应该马上就能看到U-Boot又运行起来了!这次U-Boot再也不是通过MfgTools引导的了,而是直接从eMMC中加载的!主板维修,至此告一段落。

参考资料