Bootloader
The Kindle uses
Das U-Boot bootloader. To break into the interactive shell, just press any key right afer reset.
"help" gives a list of available commands
KINDLE> help
? - alias for 'help'
badblocks - print OneNAND bad block info
base - print or set address offset
bbm - BBM sub-system
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootf - boot from various options
bootm - boot application image from memory
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
dcache - enable or disable data cache
diags - execute the User Diagnostics from OneNAND
dsleep - sleep USB device controller
dwake - wake USB device controller
echo - echo args to console
erase - erase FLASH memory
exit - exit script
factory - string [lock] [LLL_RR_PP]
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fb - framebuffer subsystem
flinfo - print FLASH memory information
gain - displays/sets the gain value
go - start application at address 'addr'
help - print online help
hsuspend - suspend the 1761 USB host controller
hwake - wake USB host controller
icache - enable or disable instruction cache
iminfo - print header information for application image
itest - return true/false on integer compare
keys - prints out hex values from device keyboard until console key is pressed
kindle - print info about Kindle's revision
load - load OneNAND page into DataRAM
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loop - infinite loop on address range
loopw - infinite write loop on address range
mbboot - boot bootloader from MMC/SC card
md - memory display
mdc - memory display cyclic
mkboot - boot kernel & initrd from MMC/SD card
mm - memory modify (auto-incrementing)
mmcinit - init mmc card
mtest - simple RAM test
mw - memory write (fill)
mwc - memory write cyclic
nand - NAND sub-system
nboot - boot from NAND device
nm - memory modify (constant address)
ohms - calculates board resistance
onenand - print OneNAND register info
opamp - displays/sets the op-amp offset value
otp - dump/read/write OneNAND OTP
printenv- print environment variables
protect - enable or disable FLASH write protection
reboot - alias of reset to match kernel
reset - perform RESET of the CPU
run - run commands in an environment variable
rve - displays/sets the rve (reference voltage error) value
saveenv - save environment variables to persistent storage
serial - set/display board serial number in OTP
setenv - set environment variables
sleep - delay execution for some time
snuz - put PXA to sleep
test - minimal test like /bin/sh
update - update sub-system (updates images from MMC/SD card to flash)
usb_init - init USB host controller
version - print monitor version
write - write DataRAM buffer to OneNAND page
Let's see what we can do with bbm command
KINDLE> ? bbm
bbm format
- format device
bbm open
- open device
bbm eraseall
- erase all blocks
bbm erase 'start' 'end'
- erase blocks from 'start' to 'end'
bbm load image 'id' ['start']
- load image from partition 'id' into RAM;
image is loaded into RAM at location 0xA2000000
or into 'start' if 'start' is specified (in hex)
bbm save image 'id' ['start'] 'size'
- save image of 'size' to partition 'id';
image should be loaded into RAM at 0xA2000000
or into 'start' if 'start' is specified (in hex)
- Partition Info -
bbm show partition
- show partition information
bbm save partition
- save partition information
bbm del partition
- delete last partition
bbm add partition 'id 'attr' 'blocks'
- add partition 'id' of type 'attr' and of size 'blocks'
KINDLE> bbm show partition
<< PARTITION INFORMATION >>
id : Bootloaders, Diagnostics (3)
attr : RW (1)
first_blk : 0 (0x00000000)
no_blks : 12 (1.5 MB)
---------------------
id : Standard Kernel (17)
attr : RW (1)
first_blk : 12 (0x00180000)
no_blks : 12 (1.5 MB)
---------------------
id : Recovery Kernel (16)
attr : RW (1)
first_blk : 24 (0x00300000)
no_blks : 12 (1.5 MB)
---------------------
id : Standard Initrd (15)
attr : RW (1)
first_blk : 36 (0x00480000)
no_blks : 10 (1.3 MB)
---------------------
id : Recovery Initrd (14)
attr : RW (1)
first_blk : 46 (0x005C0000)
no_blks : 10 (1.3 MB)
---------------------
id : Read-only Root Filesystem (8)
attr : RW (1)
first_blk : 56 (0x00700000)
no_blks : 96 (12 MB)
---------------------
id : Default Content (9)
attr : RW (1)
first_blk : 152 (0x01300000)
no_blks : 120 (15 MB)
---------------------
id : Read/Write Root Filesystem (10)
attr : RW (1)
first_blk : 272 (0x02200000)
no_blks : 144 (18 MB)
---------------------
id : Userstore (11)
attr : RW (1)
first_blk : 416 (0x03400000)
no_blks : 1584 (198 MB)
---------------------
id : Environment Variables (4)
attr : RW (1)
first_blk : 2000 (0x0FA00000)
no_blks : 2 (256 KB)
---------------------
The diagnostics U-Boot image is stored in the first partition.
There are two kernels, standard and recovery with corresponding ramdisks. Recovery kernel is used for firmware update.
There is a read-only root filesystem and read-write part. There is a partition with default content used for factory reset.
Then there is a userstore partition, which is visible as mass storage drive over USB.
There were no commands to copy data from flash to SD/MMC, but I could load flash partitions into memory and dump that.
KINDLE> bbm load image 3
Loading partition "Bootloaders, Diagnostics" into 0xA2000000... Success
KINDLE> base a2000000
KINDLE> md.b 0 100
a2000000: 4e 69 63 6b ff ff ff ff ff ff ff ff ff ff ff ff Nick............
a2000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a2000020: 0e 00 00 ea 18 f0 9f e5 2c f0 9f e5 18 f0 9f e5 ........,.......
a2000030: 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 ................
a2000040: 60 00 00 a2 20 01 00 a2 80 01 00 a2 e0 01 00 a2 `... ...........
a2000050: 40 02 00 a2 a0 02 00 a2 00 03 00 a2 40 03 00 a2 @...........@...
a2000060: 00 00 0f e1 80 00 c0 e3 00 f0 29 e1 7c 00 9f e5 ..........).|...
a2000070: 21 0a 40 e2 02 0c 40 e2 02 0a 40 e2 0c d0 40 e2 !.@...@...@...@.
a2000080: 70 00 9f e5 70 10 9f e5 00 20 a0 e3 00 20 80 e5 p...p.... ... ..
a2000090: 04 00 80 e2 01 00 50 e1 fb ff ff 1a e6 80 00 ea ......P.........
a20000a0: 00 00 a0 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............
a20000b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a20000c0: 28 00 1f e5 18 10 90 e5 01 10 81 e3 18 10 80 e5 (...............
a20000d0: 10 10 90 e5 02 1b 81 e2 02 1b 81 e2 0c 10 80 e5 ................
a20000e0: fe ff ff ea ef be ad de ef be ad de ef be ad de ................
a20000f0: 00 00 00 a2 20 00 00 a2 1c 56 06 a2 98 52 4a a3 .... ....V...RJ.
(Nick probaly refers to Nick Vaccaro of Lab126, who apparently was one of the main Kindle software developers. Hi Nick!)
Due to either my cable or inconsistent terminal settings, any dump of over 256 bytes was returning only some bytes in the beginning and some in the end. So I had to conjure up a little Python script to send dump commands in chunks of 256 bytes, parse the output and write it to a file. It took a few hours per partition, but in the end I was able to dump what I needed. In the initrd image of the recovery kernel I found scripts that performed firmware update and so I could reverse the update file format.
Firmware updates
Firmware update can be performed both over-the-air and from the SD card or mass storage partition.
The firmware update file should match the mask "update*.bin" and reside in the root of the userstore partition (Kindle's USB drive) or SD/MMC card. There should be only one such file present. The file consists of a header and scrambled .tar.gz file with update files.
offset size value
0 4 signature (OTA: "FC02", manual: "FB01")
4 4 fromVersion (minimal version to update)
8 4 toVersion (maximal version to update)
0C 2 deviceCode (number in 3rd and 4th characters of the serial, i.e. 01)
0E 1 updateOptional (seems unused)
0F 1
10 32 scrambled md5 hash string of the tgz
20000 ? scrambled tgz with update files
Version value is made from the kindle version string. In my unit, it's 292-Kindle-012138 and the version value is 121380292 (12138*10000 + 292).
The manual update doesn't check any fields except signature and md5.
Scramble algorithm:
byte = rol(byte,4)^0x7A;
Descramble:
byte = rol(byte^0x7A,4);
The tgz should contain a text file matching the mask "update*.dat". Its lines have the following format:
id md5 filename block_count display_name
id is the ID of the flash partition to write to. I know the following numbers:
6 base RO fs (squashfs image)
7 default content (squashfs)
block_count is the number of 128K blocks to flash (see bbm show partition output above).
If
id is 129, the file is considered a shell script and is executed.
If
id is 128, the md5 is checked but nothing is done with the file.
I made a small Python script to assemble an update file with all checksums calculated automagically. It will be included in the next post.
To do a manual update, first put the update bin in the root of Kindle's flash drive or SD card. Then hold Home button while resetting the Kindle. In a while you will see the following menu:
2 "Firmware Reset" clears all user-specific data and settings and returns the Kindle to factory state. I'm not sure if it will be usable without extra initialization by Amazon technicians.
3 "Exit" starts normal boot process.
0 (not shown) starts the diagnostics bootloader. Interaction is done mostly over the console, you won't see much on the Kindle screen.
1 "Firmware update" starts the manual update process.