注:本记录并非教程!操作前请确定你清楚自己在做什么!

起因

起因呢,大致就是@ntzyz有天发现他的Kindle Paperwhite 2(下文简称KPW2)复制不进东西了(复制到一半出错),重刷系统也是因为一样的原因失败(没法完整复制刷机包),考虑到网上的救砖教程大多要求拆机飞线使用TTL串口操作,怕自己手残搞坏就寄给了我。随后折腾就开始了……

到手

到手的时候发现完全开不开机,原来是没电了,先接上USB充电。大约半小时后尝试开机,卡死在大树界面,进度条走到30%或者70%卡住,接上电脑只会提示需要进行格式化,然而如果尝试格式化,则是格式化完成后复制不进东西,再次连接则是又提示需要格式化。其实这里已经基本猜到是eMMC坏了,但是还是先祈祷下不是那样,接上TTL看bootlog才能下定论。首先是拆机,Kindle的拆机不复杂,前面板是一片比较软的塑料片,可以撕下来。接着拧掉螺丝,角度合适就可以直接把后盖拿下来了。主板上不难发现在三个未焊接的按键焊盘上方有一个3脚的连接器焊盘:

这个连接器焊盘就是TTL串口的所在位置了。中间是RXD,有大片覆铜的是GND,剩下一个则是TXD。或者也可以把GND接好,剩下两个碰运气随便接,反正烧不了,没反应就对调一下。

接上TTL,我看到的,是如下的提示……

至此,问题已经很明显了,eMMC坏了。芯片坏了自然也是没有什么办法可以直接让它复生,只能更换。于是问题来了,Kindle启动后需要加载的U-Boot、系统内核都是存储在eMMC当中的,我要是直接焊接一块全新的eMMC上去,缺少这些东西是无法开机的,而依靠系统内核才能完成的刷机更是无从谈起。所以参考一般手机维修的思路,备份正常eMMC的镜像(俗称资料,eMMC俗称字库),随后烧录到另外一片要代换的eMMC上面,再把烧录完成的eMMC焊接上去。其实也就是需要一台正常的模板机从来复制镜像,单有一台坏机是不行的。于是这里便遇到了几个问题:我没有正常主板的eMMC,我没有全新的eMMC,我不会焊接BGA……后面两个问题好解决,买就可以了,焊接也可以练习一下,唯独第一个……我总不能为了救这个再去拆一台正常的Kindle,毕竟我不是专业维修,一个没有那么大的量,另外一个也没有把握能把拆下来的再焊回去,要是一台没修好又报废一台那就亏大了。

尝试备份

不过,其实也不一定要备份完整的镜像。讲道理的话,我只要可以有U-Boot,我就可以用U-Boot刷入系统内核,再通过系统内核完成分区并且刷入文件系统。现在这台Kindle,虽然eMMC坏了,但是似乎其中U-Boot和系统内核都还是可以用的,只要能备份出来即可。备份也不一定要拆机备份,如果能够进入Shell或者能把整个eMMC挂载成USB存储设备,就可以使用dd进行备份。而且幸运的是,Kindle的恢复模式(系统内核提供)似乎有挂载整个eMMC的选项!但是……

Menu
====
3. Load MMC0 over USB storage
4. Erase MMC0
I. Initialize Partition Table (fdisk) and format FAT
O. Format and overwrite FAT partition
E. Export FAT partition
U. Update using update*.bin file on FAT partition
M. Update using update*.bin file on FAT partition of second MMC port
D. dmesg / kernel printk ring buffer.
Q. quit
Choose:

Unknown option '3'

未知选项是什么鬼啊!强行不让我用!于是我也没有太多办法了,拆吧。Kindle的屏蔽罩是焊死的,而且用烙铁根本加热不动,最好的办法可能就是暴力拆解。(我本来是不想这么做的,但是看了iFixit的拆机图也是明显的暴力拆机,想想如果他们都没有办法,我也基本上是找不到什么更好的办法了)暴力拆解就没啥好多说的,用很小的一字螺丝刀或者其它能塞进去的东西撬吧,尽量别损坏PCB。我这里其实是损坏了一点,不过因为都是焊接在GND覆铜上的,也就是扯掉也只是扯掉GND覆铜,一般不会有什么大问题。

来自SanDisk的4GB eMMC,型号SDIN7DP2-4G。风枪吹个几分钟就可以取下来。取下来后就要开始考虑备份eMMC了。不过,不需要复杂的编程器,很多情况下,只需要一个TF读卡器就可以了。原因是这样的,eMMC是MMC的嵌入式版本,协议上基本兼容MMC,而SDIO和MMC协议类似,引脚定义上也只是数据总线宽度不同,一般的主控制器都会同时支持两种协议,所以一般来说用TF读卡器就可以读写eMMC了。连接也很简单,CMD CLK DAT0-3这些信号线直连,VCC和VCCQ需要自己并联然后连接到读卡器的VCC上,GND和GNDQ自己并联连接到读卡器的GND上。VDDI建议连接一个104电容到地,有些芯片不接也可以工作。这里VCC、VCCQ需要并联的原因是这样的,VCCQ是IO电压而VCC是供电电压,供电电压必须为3.3V左右,而IO电压通常根据实际主控的需要可以降低到1.8V 2.5V这样,于是就成为了分开的两组电压。TF卡的电压只有单组3.3V,也就是IO电压必须为3.3V,这里在连接的时候就让eMMC的IO电压等于3.3V了。TF读卡器自然是焊不上eMMC芯片的,于是,万能的漆包线就出场了:

(注:上图为我第一次尝试,没有接VCCQ和GNDQ,结果不识别,接上就ok了)

连接上电脑,如果没有问题的话磁盘管理里面应该可以看到一个新的磁盘:

如图所示就是Kindle的分区结构了,其中350MB的分区为根文件系统,第一个64M分区为恢复模式的文件系统,第二个64M分区则是存储用户配置,最后一个大分区是用户可见分区。把读卡器接到一台Linux PC上,运行dd进行备份,结果不出意外地我看到了如下的错误提示:

~$ sudo dd if=/dev/sdb of=/home/backup.bin bs=1M count=32
dd: error reading '/dev/sdb': Input/Output error
0+1 records in
0+1 records out
4096 bytes (4.1 kB) copied, 0.00154611 s, 2.6 MB/s

这才4KB就爆了啊……不过也不要紧,有多少是多少,使用dd if=/dev/sdb of=/file/name/of/backup conv=noerror,sync就可以强行忽略错误进行备份。不过事实证明,我除了分区表就没备份下来什么有价值的东西……

重新编译U-Boot

然而如果到这里我就放弃了,我也不会写这个博文,显然故事还没有结束。要知道U-Boot和Linux可都是通过GNU GPL协议开源的,也就是如果亚马逊改动了它的代码,也就必须按照GPL协议发布改动后的代码。(如果没有改动的话,也就是我直接下载mainline的U-Boot就可以支持的意思吧,当然这可能性不大)只要有了代码,就可以自行编译出需要的二进制文件,而有了需要的二进制文件,应该就可以自己重新构建出eMMC的镜像。虽然这个协议拦不住国产厂家保密他们的代码(全志啊、MTK啊都是劣迹斑斑,想知道的可以自行Google搜索MTK GPL Violation),但是亚马逊这种厂家我还是相信他们会遵守的。果然,在亚马逊的官网上有他们所有设备的开源: http://www.amazon.com/gp/help/customer/display.html?nodeId=200203720

那么下载KPW2的源码包,解压里面果然有u-boot的源码。查看config文件夹基本就能看到支持的配置文件。已知KPW2使用的是i.MX6 SoloLite处理器,于是就找和imx6有关的配置文件,果然有个imx60_wario的配置,而wario就是KPW2的开发代号。输入以下命令进行配置和编译:

make imx60_wario_config
TYPE=prod make -j4

第一句是写入配置,第二句是进行编译,-j4表示使用四个线程。前面的TYPE可以取prod、bist和mfgtool,主要是用于定义不同的TEXT_BASE,这个取值可以在board/imx60_wario/config.mk里面找到,我也是第一次没有定义结果编译报错没有定义TEXT_BASE然后找了半天发现要这样的。值得注意的是,我这里是使用了TegraK1单板机+Ubuntu 14.04 LTS进行编译,也就是宿主机就是ARM,不需要进行交叉编译。如果各位是在树莓派上进行操作则可以像我一样直接这样编译,但是如果是使用一般的x86 Linux PC,则需要自行安装交叉编译工具链,并且在两条命令中都加上ARCH=arm CROSS_COMPILE=/path/to/your/compile/toolchain。编译完成后可以得到一个u-boot.bin,这就是需要的目标文件。

KPW2中使用了两个U-Boot,开机默认加载的第一个就是上面编译出来的U-Boot,而第二个被称为bist,其实就是一个功能更全面的U-Boot,可以通过第一级U-Boot的bist命令调用。救转同样需要重新编译这个bist。输入以下命令备份当前的uboot并继续编译bist uboot:

mv u-boot.bin u-boot-prod.bin
make distclean
make imx60_wario_bist_config
TYPE=bist make -j4
mv u-boot.bin u-boot-bist.bin

现在就得到了u-boot-prod.bin和u-boot-bist.bin,保存好一会用fastboot烧入。

下一步需要折腾出一个用来在RAM中运行的U-Boot。其实这一步很关键,在没有eMMC编程器的情况下,刷写系统完全得依靠Kindle本身。好在KPW2所使用的iMX6处理器的bootROM内置了一个USB下载模式,在配置的启动设备中没有发现有效的启动镜像的时候就会进入USB下载模式。在USB下载模式下,根据官方手册,可以使用MfgTools下载U-Boot镜像到RAM运行,并进一步刷入其它内容。不如先来试试上面编译得到的U-Boot是否可以配合MfgTools使用。

访问NXP官网上的iMX6 SoloLite页面,点击上方的Software&Tools,在Programmers (Flash, etc.) 一栏中下载Tool and documentation for downloading OS images to the i.MX 6SoloLite (REV L3.0.35_4.1.0)。完成后解压到硬盘。这个包说是i.MX6 SoloLite,实际上默认配置是给i.MX6 Dual用的,下面做一些小配置。

打开Profiles\MX6DL Linux Update\OS Firmware文件夹,找到ucl2.xml,把开头部分的pid=“0061”修改为pid=“0063”,如下所示:

<STATE name="BootStrap" dev="MX6D" vid="15A2" pid="0063"/>

这是为了让工具可以识别到MX6SL,MX6SL和MX6D的PID不同,但是下载协议是一样的。接下来在里面建立自己的配置,内容很简单,如下所示:

<LIST name="i.MX6SL-Kindle" desc="Kindle Paperwhite 2">  
    <CMD state="BootStrap" type="boot" body="BootStrap" file ="u-boot.bin" >Loading U-boot</CMD>
    <CMD state="BootStrap" type="jump" > Jumping to OS image. </CMD>
</LIST>

随后在cfg.ini当中,把LIST下的name修改为i.MX6SL-Kindle。现在把之前编译出来的u-boot-prod.bin复制过来重命名为u-boot.bin,在电脑识别到Kindle的时候双击MfgTools.exe,点击Start,一会串口终端应该能看见以下文字:

U-Boot 2009.08-lab126 (Feb 29 2016 - 13:03:16)

CPU: Freescale i.MX6 family TO0.0 at 996MHz
Temperature:   35 C, calibration data 0x5a15215f
mx6sl pll1: 996MHz
mx6sl pll2: 528MHz
mx6sl pll3: 480MHz
mx6sl pll8: 50MHz
ipg clock     : 66000000Hz
ipg per clock : 66000000Hz
uart clock    : 80000000Hz
cspi clock    : 60000000Hz
ahb clock     : 132000000Hz
axi clock   : 198000000Hz
emi_slow clock: 22000000Hz
ddr clock     : 396000000Hz
usdhc1 clock  : 198000000Hz
usdhc2 clock  : 198000000Hz
usdhc3 clock  : 198000000Hz
usdhc4 clock  : 198000000Hz
MMC:  FSL_ESDHC: 0,FSL_ESDHC: 1,FSL_ESDHC: 2
Card did not respond to voltage select!
### ERROR ### Please RESET the board ###

现在说明U-Boot已经顺利在内存当中跑起来了。其实我是先编译了TYPE=mfgtool的U-Boot,一直都是卡死在Loading U-boot,研究了好久才发现prod的就直接可以用……至于error,我已经把eMMC拆了所以这个很正常。既然可以在内存中跑U-Boot,自然也就可以把东西不通过编程器直接用USB或者串口烧入到eMMC里面,于是焊上一片新的eMMC。顺便测试下是否可以扩容,我使用了一片8GB的eMMC。因为是小芯片,不用预热台,直接一个风枪就可以搞定。

再次启动用USB引导,如果焊接正常,应该没有报错了。但是默认的U-Boot太弱,连查看eMMC配置的功能都没有。于是修改配置文件,加上一些缺失的功能,可以参考bist的配置来做。打开include/configs/imx60_wario.h,分别在合适的位置添加以下内容来启用一些额外的功能(主要是eMMC相关的命令和fastboot)

/* MMC drivers */
#define CONFIG_MMC
#define CONFIG_GENERIC_MMC
#define CONFIG_MMC_SDIO
#define CONFIG_IMX_MMC
#define CONFIG_SYS_FSL_ESDHC_NUM        3
#define CONFIG_SYS_FSL_ESDHC_DMA	1
/* crc32 Buffer*/
#define CRC32_BUFFER 	        0x02000000 + PHYS_SDRAM_1
#define CRC32_BUFFER_SIZE 	0x00400000
#define CRC32_CHECK_UBOOT	0x00000010	/* check crc32 on u-boot.bin */
#define CRC32_CHECK_UIMAGE	0x00000020	/* check crc32 on uImage */
#define CRC32_CHECK_ROOTFS	0x00000040	/* check crc32 on rootfs.img */
#define CRC32_CHECK_MBR	0x00000080	/* check crc32 on mbr-xxx-bin */
/*
 * USB Configs
 */
#define CONFIG_USB_DEVICE		1
/* #define CONFIG_IMX_UDC		1 */
#define CONFIG_DRIVER_FSLUSB		1
#define CONFIG_GADGET_FASTBOOT	1
/* #define CONFIG_GADGET_FILE_STORAGE	1 */
#define USB_BASE_ADDR			OTG_BASE_ADDR

#define CONFIG_USBD_MANUFACTURER "Amazon"
#define CONFIG_USBD_PRODUCT_NAME "Kindle"

#define CONFIG_USBD_VENDORID			0x1949
#define CONFIG_USBD_PRODUCTID_FASTBOOT	0xd0e0
#define CONFIG_USBD_PRODUCTID_FILE_STORAGE	0x0003
#define CONFIG_FASTBOOT_MAX_DOWNLOAD_LEN	((get_dram_size()) - (2*1024*1024) - (CONFIG_FASTBOOT_TEMP_BUFFER - CONFIG_SYS_SDRAM_BASE))
#define CONFIG_FASTBOOT_TEMP_BUFFER		CONFIG_LOADADDR
/* Standard commands */
#define CONFIG_CMD_BOOTD	/* bootd			*/
#define CONFIG_CMD_CONSOLE	/* coninfo			*/
#define CONFIG_CMD_ECHO	/* echo arguments		*/
#define CONFIG_CMD_IMI	/* iminfo			*/
#define CONFIG_CMD_ITEST	/* Integer (and string) test	*/
#define CONFIG_CMD_LOADB	/* loadb			*/
#define CONFIG_CMD_LOADS	/* loads			*/
#define CONFIG_CMD_MEMORY	/* md mm nm mw cp cmp crc base loop mtest */
#define CONFIG_CMD_MISC	/* Misc functions like sleep etc*/
#define CONFIG_CMD_RUN	/* run command in env variable	*/
#define CONFIG_CMD_SOURCE	/* "source" command support	*/
#define CONFIG_CMD_ENV
#define CONFIG_CMD_MMC
#define CONFIG_CMD_PANIC
#define CONFIG_XYZMODEM
#define CONFIG_SRECORD
#define CONFIG_LOOPW
#define CONFIG_CMD_HALT
#define CONFIG_CMD_CRC 	1
#define CONFIG_CMD_GADGET	1
#define CONFIG_CMD_IDME	1
#define CONFIG_CMD_LPM	1
#define CONFIG_CMD_VNI	1
#define CONFIG_CMD_HAPTIC     1
#define CONFIG_CMD_FSR	1

再次编译,使用MfgTools载入内存,启动执行mmc dev 1选中eMMC,然后mmcinfo应该可以看见容量为8GB。

uboot > mmc dev 1
mmc1(part 0) is current device
uboot > mmcinfo
Device: FSL_ESDHC
Manufacturer ID: 45
OEM: 100
Name: SEM08G 
Revision: 0x28
Tran Speed: 25000000
Rd Block Len: 512
MMC version 4.0
High Capacity: Yes
Capacity: 7818182656
Bus Width: 8-bit 
Current Partition for boot: Boot partition 1
Current boot width: 4-bit SDR