In this part of the series I will describe how I backed up the kernel of the Kindle 4 non-touch and how you can use the kernel to extract the initramfs. History has proven that if things can go wrong, they will probably also go wrong. So before you start working on the device, you should create a full back up of everything that you may or may not modify.
Extracting the kernel is not that difficult after all. You have two options for this: If you want, you can use the boot loader and transfer the kernel using a serial connection. Alternatively the kernel can also be obtained by running “dd if=/dev/mmcblk0″ with the right offsets and lengths. This is generally the less painful method, but it only works if you still have a Linux that boots. Note that downloading kernel images via fastboot does not work. Amazon only provided a way to download kernels to the device, but has no options for uploading a kernel image to the host PC.
General stuff
There are two kernel images on the Kindle 4:
- The “normal” kernel image that is used to boot the device
- The diagnostics kernel image that is used e.g. when an ENABLE_DIAGS file has been created on the USB partition
Looking at the bootloader reveals the memory addresses of the images:
uboot > printenv
[...]
bootcmd=bootm 0x41000
bootcmd_diags=run bootargs_diags ; bootm 0xE41000 |
So the address of the standard kernel image is at 0×41000 on the flash storage, whereas the recovery image is at 0xE41000.These numbers are also defined in include/configs/imx50_yoshi.h in Amazon’s u-boot source code:
98
99
100
| #define CONFIG_MMC_BOOTFLASH_ADDR 0x41000
#define CONFIG_MMC_BOOTFLASH_SIZE (14*1024*1024) /* 14 MiB */
#define CONFIG_MMC_BOOTDIAGS_ADDR 0xE41000 |
Getting the kernel image – the easy way
If you still have a working Linux system on the Kindle, you can use the following process to get the kernel image:
- The kernel image can be obtained by running a simple dd command. The parameters for dd are:
- block size=1024 (just my convention)
- skip=260(normal kernel) or 14596 for recovery image. This can be calculated by dividing 0×41000/0xE41000 through the block size
- count=14*1024. This was obtained by dividing 14Mb through the block size
The dd command then goes like this:
dd if=/dev/mmcblk0 bs=1024 skip=260 count=$((14*1024)) of=/mnt/us/uImage
dd if=/dev/mmcblk0 bs=1024 skip=14596 count=$((14*1024)) of=/mnt/us/uImage-diags |
We now have two U-Boot images that contain the kernel images for normal operation and recovery, respectively.
- Now we need to strip the unused bytes from the uImage. We do this by looking at the U-Boot header of the downloaded files with a hex editor like hexedit. The size of the image can be obtained by looking at bytes 12-15. This offset can be explained by looking at “struct image_header” in file include/image.h in the Amazon U-Boot source code:
174
175
176
177
178
179
180
| typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
[...]
} image_header_t; |
In my example file, the image data size is 0x0048A540, which amounts to something like 4.5 Mb. However, the 64 bytes of the uImage header are not included in this size, so we need to add these 64 bytes in order to get the final size of the uImage. The file can then be truncated as follows:
truncate -s $(( 0x0048A540+64 )) ./uImage |
Replace the number with the size of your kernel image.
- Test your image using mkimage:
$ mkimage -l uImage
Image Name: Linux-2.6.31-rt11-lab126
Created: Fri Sep 23 06:34:55 2011
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4760896 Bytes = 4649.31 kB = 4.54 MB
Load Address: 70008000
Entry Point: 70008000 |
- Now we can extract the kernel image file from the uImage by stripping the 64 bytes of U-Boot header information:
dd if=uImage bs=64 skip=1 of=Image |
Extract the initramfs
The initramfs can be extracted using the unpack.sh script from here:
git clone "https://github.com/choff/galaxys2_kernel_repack.git" kindle_kernel_repack
cd kindle_kernel_repack
sudo ./unpack.sh /path/to/Image ./initramfs |
The extracted initramfs will be placed in the “initramfs” directory.
Remember to always run the “unpack” command as root. This is critical because you need root priviliges in order to extract the device nodes in the “/dev” directory of your initramfs. If you are not root, you get a full initramfs, but without any device nodes. But usually one wants to have the device nodes as well, especially when one plans to reuse the initramfs for a new kernel.
Getting the kernel image – the painful way
This information is here just for completeness. This is probably not what you want. Only use this method if you cannot boot your kindle any more and need to extract the initramfs from the device. The other method above is much simpler and in most cases preferable. You will also need a serial console for this experiment since we need to access the boot loader directly.
To be able to do this, we first have to enter the “built-in self-test”(bist) mode of the bootloader. We this this by typing “bist” on the U-Boot command line(accessible only via serial console):
bist > mmcinfo 0
Device: FSL_ESDHC
[...]
Rd Block Len: 512
[...]
This gave us the information that mmc #0 uses a block size of 512 bytes. Now, the whole space for both the recovery and “normal” kernel is 14 Mb, so we will have to read a total of 14Mb/512B = 28672 = 0×7000 blocks from the internal MMC into RAM:
uboot > mmc read 0 $(loadaddr) 0x41000 7000 0 |
This loads the uImage from position 0×41000 to the RAM address “loadaddr” of the device. “loadaddr” is a defined position in the RAM where the kernel images are written to. The number of blocks(7000) is interpreted as hex number and was calculated as above.
Let’s see if we did everything correct by displaying some details about our image:
bist > imi $(loadaddr)
## Checking Image at 7a000000 ...
Legacy image found
Image Name: Linux-2.6.31-rt11-chris
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4890016 Bytes = 4.7 MB
Load Address: 70008000
Entry Point: 70008000
Verifying Checksum ... OK |
We can also obtain this header information in hexadecimal by issuing
bist > md.b $(loadaddr) 40
7a000000: 27 05 19 56 3e 26 3f 1a 4f 78 57 62 00 4a 9d a0 '..V>&?.OxWb.J..
7a000010: 70 00 80 00 70 00 80 00 1e f0 e6 b4 05 02 02 00 p...p...........
7a000020: 4c 69 6e 75 78 2d 32 2e 36 2e 33 31 2d 72 74 31 Linux-2.6.31-rt1
7a000030: 31 2d 63 68 72 69 73 00 00 00 00 00 00 00 00 00 1-chris......... |
Which displays us the first 64 bytes(the uImage header). By looking at bytes 12-15 again, we find the data size of the kernel image which is 0x004a9da0 in our case(decimal 4890016, just like in the output of the “imi”(image info) command.
Now comes the tricky part: We are going to dump the whole kernel using md.b. I will not go into the details here since they have already been discussed this post. The rest of the instructions are identical with the “easy” method, so you can proceed with step 2.