[Verizon HTC Droid Incredible 4G LTE] mdmower:
Build Guide

This guide is a historical account of how ClockworkMod was built for the fireball before a CyanogenMod device configuration existed for it. Obviously, all this work has been done for you now, and you can easily build ClockworkMod from the github source. If you're curious to know how configuration was done though, this post is for you! On the other hand, don't expect this to be your comprehensive guide to building recoveries. It's more an example to guide you. A brain and willingness to utilize said brain are still requirements. Furthermore, this guide ONLY focuses on the htc fireball. Don't expect direct translation to other devices. Finally, this thread is not a discussion zone for building recovery; please visit #koush on freenode if you need assistance.

Another more general resource that you should familiarize yourself with is XDA University: Porting ClockworkMod Recovery to a New Device. Some distinctions: the following uses cm-10.1 and mka instead of make -j4.

Prepare the build environment

ClockworkMod is distributed as part of the CyanogenMod source. Follow a guide for a device similar to fireball to set up a build environment. This HTC Evo 4G LTE guide should work well. Go all the way through Get pre-built apps, but stop before Prepare the device-specific code since we will be writing the device configuration from scratch.

Sun Java JDK 6 is necessary to build using Android sources. This Paranoid Android guide has a nice section on how to replace an existing Java installation on your computer. Do this.

Amend these directories as you see fit and set the variables:

export INC4G=~/inc4g
export CMHOME=~/android/system

$INC4G is our working directory and $CMHOME is the CyanogenMod source. If you close the terminal at any point, remember to re-set these (if you actually use them).

If you plan to upload your work to github at some point, give git some basic information:

git config --global user.name "Your Name"
git config --global user.email you@example.com

You should replace the name and e-mail with real(ish) values.

Some additional tools we will need throughout are abootimg and unpackbootimg. You can install abootimg through the package manager:

sudo apt-get install abootimg

unpackbootimg needs to be compiled from the CyanogenMod source. Go to $CMHOME and type

. build/envsetup.sh
mka otatools

Then copy unpackbootimg to a directory in your path, for example:

cp $CMHOME/out/host/linux-x86/bin/unpackbootimg ~/bin/

I put together a script that cleans up the output of unpackbootimg. Copy the below text into a file called unpack_image.sh, copy it to a directory in your path (~/bin/) and make it executable with chmod +x unpack_image.sh:

#!/bin/bash

strip() {
cp $@ out.img
unpackbootimg -i out.img > image_info.txt
BIT=`grep "Android magic found at:" image_info.txt | cut -d ':' -f 2 | cut -c 2-`

if [ $BIT -gt 0 ]; then
dd bs=$BIT skip=1 if=out.img of=unsigned.img
fi
}

extract() {
mkdir ramdisk
cd ramdisk
gunzip -c ../out.img-ramdisk.gz | cpio -i
cd ..
}

cleanup() {
rm out.img out.img-base out.img-cmdline out.img-pagesize
mv out.img-zImage zImage
mv out.img-ramdisk.gz ramdisk.gz
}

if [ -f $@ ]; then
strip $@
extract
cleanup
else
echo "No image specified"
fi

Start the device configuration

CyanogenMod has a few built-in tools to help setup a bare-bones device configuration based on a stock ROM's recovery image. Download recovery_signed.img from RUU 2.17.605.2 to $INC4G. Then, navigate to $CMHOME and type:

build/tools/device/mkvendor.sh htc fireball $INC4G/recovery_signed.img

The following files have been created at $CMHOME/device/htc/fireball:

AndroidBoard.mk
AndroidProducts.mk
BoardConfig.mk
cm.mk
device_fireball.mk
kernel
recovery.fstab
system.prop

File descriptions

AndroidBoard.mk - (no changes needed) is setup to pull in pre-built and proprietary files. Since there are so few proprietary files needed by recovery, we will just drop them in a sub-directory of the device tree instead of utilizing the vendor directory called by this file.

AndroidProducts.mk - (no changes needed) calls the config file specific to your device

BoardConfig.mk - kernel specifications, device specifications, and internal memory layout are defined here

cm.mk - (no changes needed) includes device name, and pulls in some CyanogenMod resources used by ClockworkMod

device_fireball.mk - this file pieces together all of the parts into a recovery

kernel - (binary file - do not modify) this is the linux kernel extracted from recovery_signed.img

recovery.fstab - defines the file system layout on the internal memory

system.prop - parameters used by the kernel at boot time to play nicely with the device

BoardConfig.mk - Link to final BoardConfig.mk

We can benefit from the work that has already been done on similar Qualcomm MSM8960 based HTC devices to configure settings in BoardConfig.mk. These devices share a common device configuration: CyanogenMod/android_device_htc_msm8960-common. Analyze BoardConfigCommon.mk and amend the following chipset capabilities:

TARGET_ARCH := arm
TARGET_NO_BOOTLOADER := true
TARGET_BOARD_PLATFORM := [B]msm8960[/B]
[B]TARGET_BOARD_PLATFORM_GPU := qcom-adreno200[/B]
TARGET_CPU_ABI := armeabi-v7a
TARGET_CPU_ABI2 := armeabi
TARGET_ARCH_VARIANT := armv7-a-neon
[B]TARGET_ARCH_VARIANT_CPU := cortex-a9[/B]
ARCH_ARM_HAVE_TLS_REGISTER := true
[B]TARGET_CPU_SMP := true[/B]
[B]TARGET_USE_KRAIT_BIONIC_OPTIMIZATION := true[/B]

Next, we can fill in the partition values under # fix this up by examining /proc/mtd on a running device. Connect your phone to the computer and type:

adb shell "cat /proc/emmc"

The output will look like:

dev: size erasesize name
mmcblk0p23: 000ffa00 00000200 "misc"
mmcblk0p22: 00fffe00 00000200 "recovery"
mmcblk0p21: 01000000 00000200 "boot"
mmcblk0p33: 47fffc00 00000200 "system"
mmcblk0p30: 00140200 00000200 "local"
mmcblk0p34: 13fffe00 00000200 "cache"
mmcblk0p35: 49fffe00 00000200 "userdata"
mmcblk0p26: 01400000 00000200 "devlog"
mmcblk0p28: 00040000 00000200 "pdata"
mmcblk0p36: 109c00000 00000200 "fat"
mmcblk0p31: 00010000 00000200 "extra"
mmcblk0p17: 02d00000 00000200 "radio"
mmcblk0p18: 00a00000 00000200 "adsp"
mmcblk0p16: 00100000 00000200 "dsps"
mmcblk0p19: 00500000 00000200 "wcnss"
mmcblk0p20: 007ffa00 00000200 "radio_config"
mmcblk0p24: 00400000 00000200 "modem_st1"
mmcblk0p25: 00400000 00000200 "modem_st2"

Convert the hex sizes to decimal for needed partitions:

boot: (hex) 01000000 --> (decimal) 16777216
recovery: (hex) 00fffe00 --> (decimal) 16776704
system: (hex) 47fffc00 --> (decimal) 1207958528
userdata: (hex) 49fffe00 --> (decimal) 1241513472

Then, fill in the values:

[B]TARGET_USERIMAGES_USE_EXT4 := true[/B]
BOARD_BOOTIMAGE_PARTITION_SIZE := [B]16777216[/B]
BOARD_RECOVERYIMAGE_PARTITION_SIZE := [B]16776704[/B]
BOARD_SYSTEMIMAGE_PARTITION_SIZE := [B]1207958528[/B]
BOARD_USERDATAIMAGE_PARTITION_SIZE := [B]1241513472[/B]
BOARD_FLASH_BLOCK_SIZE := 131072

TARGET_USERIMAGES_USE_EXT4 was added because the data partition is formatted as ext4. Discovery of partition types is discussed when we get to the recovery.fstab section.

We only have a recent apps button, not a select button, so the following is correct:

BOARD_HAS_NO_SELECT_BUTTON := true

These options allow the device to be mounted to a computer as a USB disk drive while in recovery:

[B]BOARD_VOLD_MAX_PARTITIONS := 36[/B]
[B]TARGET_USE_CUSTOM_LUN_FILE_PATH := /sys/devices/platform/msm_hsusb/gadget/lun%d/file[/B]

The LUN file path is general to msm-based ICS and Jellybean devices. The max partitions value comes from analyzing the output of

adb shell "ls /dev/block/mmcblk*"

The board name and kernel location are obviously correct:

TARGET_PREBUILT_KERNEL := device/htc/fireball/kernel
TARGET_BOOTLOADER_BOARD_NAME := fireball

We can verify the kernel options using unpackbootimg and abootimg. Navigate to $INC4G and execute the following command:

unpack_image.sh recovery.img

The script will unpack the recovery image, resulting in the following files:

image_info.txt - kernel information used in BoardConfig.mk

ramdisk.gz - compressed archive of the ramdisk

unsigned.img - the same recovery image with the 256bit signature stripped so it can be read by simpler programs like abootimg

zImage - the kernel

ramdisk/ - the unpacked ramdisk

The contents of image_info.txt can be compared to the following items in BoardConfig.mk:

BOARD_KERNEL_CMDLINE := console=ttyHSL0,115200,n8 [B]androidboot.hardware=qcom[/B]
BOARD_KERNEL_BASE := 0x80400000
BOARD_KERNEL_PAGESIZE := 2048
[B]BOARD_MKBOOTIMG_ARGS := --ramdisk_offset 0x01400000[/B]

A couple differences (in bold) are obvious. androidboot.hardware=qcom lets us identify the hardware through ${ro.hardware} in init scripts. The ramdisk_offset line is calculated from the difference (as in subtraction) between the hex addresses for the ramdisk and kernel. To find the ramdisk address, run

abootimg -i unsigned.img

and take note of the load address for ramdisk (ignore the kernel address which is different than KERNEL_BASE above). Then, subtract the two:

<ramdisk address> - <kernel base> = <ramdisk offset>
0x81800000 - 0x80400000 = 0x01400000

Often times, other devices define a custom init script using TARGET_RECOVERY_INITRC. By not specifying one, we will inherit the default init script which we can supplement rather than replace.

device_fireball.mk - Link to final device_fireball.mk

It's advisable to include charging support while in recovery. This amounts to copying a few charging specific binaries and calling them as services at startup. device_fireball.mk is the best place to put file copy commands. Find the lines for the kernel copy and piggy-back PRODUCT_COPY_FILES:

PRODUCT_COPY_FILES += \
$(LOCAL_KERNEL):kernel [B]\
$(LOCAL_PATH)/recovery/init.recovery.qcom.rc:root/init.recovery.qcom.rc \
$(LOCAL_PATH)/recovery/choice_fn:recovery/root/sbin/choice_fn \
$(LOCAL_PATH)/recovery/power_test:recovery/root/sbin/power_test \
$(LOCAL_PATH)/recovery/offmode_charging:recovery/root/sbin/offmode_charging \
$(LOCAL_PATH)/recovery/detect_key:recovery/root/sbin/detect_key[/B]

Of course, we need to supply these files. Notice I have specified a new recovery sub-directory which should be created under $CMHOME/device/htc/fireball. The needed binary files are in the ramdisk directory from our earlier unpacking of the recovery image. Navigate to $INC4G/ramdisk/sbin and copy choice_fn, power_test, offmode_charging, and detect_key to $CMHOME/device/htc/fireball/recovery.

init.recovery.qcom.rc is inherited by the default init script (notice the first line). Open $INC4G/ramdisk/init.rc and copy the service definitions related to the above charging binaries to a text file. Save it as $CMHOME/device/htc/fireball/recovery/init.recovery.qcom.rc:

service choice_fn /sbin/choice_fn
oneshot

service power_test /sbin/power_test
disabled
oneshot

service offmode_charging /sbin/offmode_charging
oneshot

service detect_key /sbin/detect_key
disabled
oneshot

Notice that disabled has been removed from the offmode_charging service. This ensures the service actually gets called at startup without relying on other processes to enable it.

system.prop - Link to final system.prop

We can directly copy/paste the contents of system.prop. You should still have the extracted files from recovery.img (see just above). Open $INC4G/ramdisk/default.prop and copy everything between

#
# system.prop for surf
#

[COLOR="Red"](Copy all of this)[/COLOR]

#
# ADDITIONAL_BUILD_PROPERTIES
#

This is your system.prop.

recovery.fstab - Link to final recovery.fstab

recovery.fstab is constructed by examining two sources. First, fix-up the device paths (column 3) by examining the output of

adb shell "cat /proc/emmc"

Then, fix-up the file-system types by examining the stock recovery.fstab in $INC4G/ramdisk/system/etc/recovery.fstab. Go ahead and add the missing /devlog line as well. The storage paths from stock recovery are not consistent with those used by ClockworkMod; rename

/sdcard --> /external_sd
/internal_sdcard --> /sdcard

Finally, we should leave 16kb of unformatted space at the end of the /data partition to allow for encryption in supported ROMs. This amounts to appending the following code to the end of the data partition line:

length=-16384

In the end you should have

# mount point fstype device [device2]

/boot emmc /dev/block/mmcblk0p21
/cache ext4 /dev/block/mmcblk0p34
/data ext4 /dev/block/mmcblk0p35 length=-16384
/misc emmc /dev/block/mmcblk0p23
/recovery emmc /dev/block/mmcblk0p22
/external_sd vfat /dev/block/mmcblk1p1 /dev/block/mmcblk1
/system ext4 /dev/block/mmcblk0p33
/devlog ext4 /dev/block/mmcblk0p26
/sdcard vfat /dev/block/mmcblk0p36

Compare your work

This is enough to compile a functional recovery. Compare your device tree with this recovery-only tree and tell me I did everything wrong!

It's time to build! Navigate to $CMHOME and type:

. build/envsetup.sh
croot
lunch cm_fireball-eng
. build/tools/device/makerecoveries.sh cm_fireball-eng

Ignore the warnings about using a pre-built kernel. This is standard practice for recoveries. After compilation, reboot your phone to the bootloader and flash the recovery.img:

adb reboot-bootloader
fastboot flash recovery $CMHOME/out/target/product/fireball/recovery.img
fastboot reboot-bootloader

To clean-up:

mka clobber

After clobber, you will need to run lunch before compiling again.