转自:http://blogs.arm.com/software-enablement/498-from-zero-to-boot-porting-android-to-your-arm-platform/
This article describes how to get Android running on your favourite ARM-based System on Chip (SoC) board. We run through the overall procedure and point out potential pitfalls and other things that you may encounter.
Since the Android software stack was primarily designed around the ARM Architecture, there are not many things that need amending to get it to work on another ARM platform.
We assume that your workstation has Ubuntu (10.10 or later) Operating System installed, and that you have already followed the instructions found at [1] to be ready to build Android sources. These instructions have been tested with Ubuntu 10.10, but they should be compatible with other GNU/Linux OSes.
For the purposes of this document, we use the following terms.
Mainline kernel | the kernel that you can get from the mainline (i.e. http://www.kernel.org); implies that no changes have been made to it. Also known as "vanilla kernel". |
Reference kernel | this is the kernel that you have for your board; that is most likely a vanilla kernel, plus any patches required to support your board. |
Merged kernel | the kernel that is the result of merging Android patches to the Reference kernel. A Merged kernel should support Android. |
Android patches | the patches that you extract from the Google Android kernel. These are the changes from the point that a Mainline Kernel version has been imported to the Android Kernel tree. See http://android.git.kernel.org |
Android kernel | the kernel that you can get from Google's Android Open Source Project repository. It contains Google's changes to the Mainline kernel, to support Android. You can get it from : http://android.git.kernel.org/kernel/common.git |
The procedure is split into two parts:
We must stress the importance of doing the "configure" step carefully in bothparts of the procedure. If it is not done correctly then it will not bepossible to complete the task successfully.
You might want to get Android working on a platform that already has an Androidport available. We provide pre-built kernel binaries, kernel sources and kernel.config
files, for certain ARM boards such as theARM Versatile Express.Please see [2] for more detail.
The following diagram shows the procedure of merging of the Reference with the Android kernel.
Diagram 1. Kernel Merging Procedure.
Make sure that you have a working kernel and a Linux root filesystem, functionalon your board, i.e. you can boot to a GNU/Linux graphical environment.
If this is not possible, there is no point attempting to complete thisexercise.
Note : The Linux root filesystem is only required to test the Merged Kernel, notfor the Android port to your SoC.
You need a kernel that boots and provides to the system all of the appropriatesupport required for a windowing system (e.g. graphics drivers for the framebufferor your graphics chip) as a reference point. This will be called your Referencekernel.
It should be noted here that every Android release after Cupcake requiresdouble buffering and page flipping support from the graphics system.If your Reference kernel does not support this, then Android releases afterCupcake will not work.
The .config file of the Reference kernel will be used as a base for the Mergedkernel. Keep a copy of this as a reference for later use.
For the purpose of this article, we assume that you have a 2.6.38-basedfunctional Reference kernel for your board. You may use any kernel revision youwish, but keep in mind that it may result in deviating from the information inthis article.
Find an Android kernel which has the same revision as your Reference kernel. Ifit is not possible, use a revision that is closest, as this will simplify theprocedure.
Revision mismatch of the two kernels will probably result in spending more timemerging and debugging. From experience, merging a Reference kernel and the Androidpatches of the same revision requires about a day; in the case of revision mismatch,more time may be required.
In this step you can either obtain the Android patches from the Android kernelsource, or use git to merge the two kernel trees using their common pointin time.
The first approach will produce a patch file that can be applied to theReference kernel, while using git-merge will produce a single tree that willkeep the patching history, and maintain all of the individual commits.
Please note that you will have to do either c.i or c.ii, not both!The expected size of the patch is about 2MB. To extract the patches, find thepoint in time (using "git log") where the Mainline kernel was imported to theAndroid kernel source tree.
Go to the top level of the Android kernel source tree and run:git log --pretty=oneline --format="%Cgreen%h %Creset%s" \ --grep="Linux 2.6." -n 20which will generate 20 one-line log entries that have the expression "Linux2.6." in the subject or in the commit message.The output should look like:
521cb40 Linux 2.6.38 a5abba9 Linux 2.6.38-rc8 dd9c154 Linux 2.6.38-rc7 f5412be Linux 2.6.38-rc6 85e2efb Linux 2.6.38-rc5 100b33c Linux 2.6.38-rc4 ebf5382 Linux 2.6.38-rc3 1bae4ce Linux 2.6.38-rc2 c56eb8f Linux 2.6.38-rc1 aa0adb1 batman-adv: Use "__attribute__" shortcut macros f5afcd3 drm/i915/crt: Check for a analog monitor in case of DVI-I 3c0eee3 Linux 2.6.37 387c31c Linux 2.6.37-rc8 90a8a73 Linux 2.6.37-rc7 b0c3844 Linux 2.6.37-rc6 cf7d7e5 Linux 2.6.37-rc5 e8a7e48 Linux 2.6.37-rc4 3561d43 Linux 2.6.37-rc3 5bd5a45 x86: Add NX protection for kernel data 64edc8e x86: Fix improper large page preservationSince you need the patches to be on top of the 2.6.38 Mainline kernel release,run the following command:
git diff 521cb40 HEAD > 2.6.38-to-Android.patchThis gives you a patch file (named
2.6.38-to-Android.patch
) containing thechanges to be merged.Check for merging conflicts, before attempting to perform the actual merge, byrunning:
git apply --test [path/to/2.6.38-to-Android.patch]which produces a list of merging conflicts (if any). Make sure that you resolvethese conflicts and that the patch applies cleanly to your tree before you continue.
git remote add [android tree] \ git://android.git.kernel.org/kernel/common.gitwhere
[android tree]
is the local name for the remote Android repository. Nowenter:
git fetch [android tree]to get all the git information for that tree (such as branch names, commits andtags).To identify the common point where the merge will be based, enter:
git merge-base armdroid-2.6.38 [android tree]/android-2.6.38where armdroid-2.6.38 is the local branch name for your Reference kernel that youwish to merge with the Android kernel tree, and
[android tree]/android-2.6.38
is the branch of the 2.6.38 Android Kernel at the remote repository.This returns the hash of the commit at which the two branches diverged. E.g.:
a5abba989deceb731047425812d268daf7536575You can use this to get an estimate of the number and size of changes madefrom that point in time, to the head of your Reference kernel tree.Then, attempt an automatic merge using:
git merge armdroid-2.6.38 [android tree]/android-2.6.38Any conflicts will be reported - review these using:
git statusFor conflict resolution
git mergetool
can be quite handy, and it allows youto use your favourite diff tool. For example:
git mergetool --tool=vimdiff kernel/printk.clets you review and resolve the merging conflict for
kernel/printk.c
using vimdiff.When you have resolved the conflicts, commit the changes using:
git commit -a
In order to get the Merged kernel configured correctly for Android, you musthave a known working configuration tested with a Linux root filesystem.
This should be tested by building a kernel image from the merged source tree,using the known working defconfig or .config
file saved in Part 1a, and checkingthat it boots your existing working Linux root filesystem.
Note: Be aware that due to the Android parts in the Merged kernel, you mightget asked about some options that were not available in the reference configurationof your Reference kernel.
If it does not boot, take a step back and make sure that what was merged fromthe Android kernel source tree did not break anything in your Reference kernel.Checking the files in which you have resolved any merge conflicts should be agood place to start.
If it does boot, you can then add options to your working configuration thatare required for Android.
These are:
CONFIG_PM=y
CONFIG_HAS_EARLYSUSPEND=y
CONFIG_SUSPEND=y
CONFIG_HAS_WAKELOCK=y
CONFIG_WAKELOCK=y
CONFIG_EARLYSUSPEND=y
CONFIG_USER_WAKELOCK=y
CONFIG_FB_EARLYSUSPEND=y
CONFIG_ASHMEM=y
CONFIG_ANDROID_PMEM=y
CONFIG_SWITCH=y
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_LOGGER=y
CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_USB_GADGET=y
CONFIG_USB_ANDROID=y
CONFIG_USB_ANDROID_ACM=y
CONFIG_USB_ANDROID_ADB=y
CONFIG_USB_ANDROID_MASS_STORAGE=y
CONFIG_USB_ANDROID_MTP=y
CONFIG_POWER_SUPPLY=y
CONFIG_PDA_POWER=y
Note that the names above are those which can be found in the 2.6.38 Android kernelconfiguration - be aware that these tend to change from version to version!
We have also found that in multiprocessor systems, CPU hotplugging support isrequired. You should enable this if your ARM based SoC has more than one core inorder to boot a kernel with multiprocessor support.
To get the Merged kernel to work correctly for Android, the above configurationoptions are the minimum options required.
Build the Merged kernel with:
make ARCH=arm CROSS_COMPILE=[path-to-arm-gcc] uImage
which will produce a kernel Image, uImage and zImage in arch/arm/boot directoryin the Merged kernel source tree. Replace the [path-to-arm-gcc]
with the path tothe latest version of Linaro or CodeSourcery ARM toolchain that you haveinstalled. For this exercise we used Sourcery G++ Lite 2010.09-50) 4.5.1.
Note that you need the uboot-mkimage package in order to generate a uImage.If you do not want a U-boot wrapped Linux kernel image, substitute uImage
with Image
above.
You now need the Android root filesystem in order to complete your port.
Each build target defines the configuration of the ARM based SoC/board and selectswhich sources should be built for Android.
The build target directory that the Android build system uses depends onthe Android version. In Froyo and Gingerbread the location is:
[android_root]/device
whereas in Eclair it is:
[android_root]/vendor
In this article, we assume that you are using Gingerbread.
Below is an example of adding a couple of ARM generic Android build targets,along with a mock target.
Create a directory in [android_root]/device, with the desired name. E.g.:
mkdir [android_root]/device/arm
Enter the new directory and create a products
folder:
mkdir [android_root]/device/arm/products
In the products
folder you need an AndroidProducts.mk
file that lists all ofthe products that you have under the arm
folder. This should be asfollows:
PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/armboards_v7a.mk \ $(LOCAL_DIR)/armboards_v7a_noneon.mk \ $(LOCAL_DIR)/another_product.mk \
As this implies, you also need to have one "Product_Makefile" per product in the[android_root]/device/arm/products/
folder. Therefore, for our example you need:
armboards_v7a.mk
armboards_v7a_noneon.mk
another_product.mk
Each of these files must contain the following, adapted to match the productand device naming :
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk) # # Overrides PRODUCT_NAME := [product_name] PRODUCT_DEVICE := [board_name]
where PRODUCT_NAME
is the build target name used by the Android build system,and PRODUCT_DEVICE
defines the name of the directory that contains thefiles which describe the device.
For our example the first product is :
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk) # # Overrides PRODUCT_NAME := armboard_v7a PRODUCT_DEVICE := armboard_v7a
Note that the PRODUCT_NAME
does not have to be the same as the PRODUCT_DEVICE
.For example, if you were to add support for ARM Versatile Express board, thecontents of the vexpress.mk
file could be :
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk) # # Overrides PRODUCT_NAME := vexpress PRODUCT_DEVICE := cortex-a9
and the files that define/configure that device would be in a "cortex-a9"directory. You are free to choose the name you wish for your product and device,limited to the characters allowed. For example, you cannot use minus "-"because this is used to separate the PRODUCT
prefix and the eng
,user
or test
, suffix from the product name in the build command line.
Following the example above, create the required directories:
mkdir [android_root]/device/arm/armboard_v7a/ mkdir [android_root]/device/arm/armboard_v7a_noneon/ mkdir [android_root]/device/arm/another_product/
and populate them accordingly.
Each one of these directories should contain at least the "Android.mk" and"BoardConfig.mk" files. The first one is the makefile for the product and thesecond, as the name implies, is the config file for the product. The Googleguidelines [3] for what the Android.mk file should contain are :
# make file for new hardware from # LOCAL_PATH := $(call my-dir) # # this is here to use the pre-built kernel ifeq ($(TARGET_PREBUILT_KERNEL),) TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel endif # file := $(INSTALLED_KERNEL_TARGET) ALL_PREBUILT += $(file) $(file): $(TARGET_PREBUILT_KERNEL) | $(ACP) $(transform-prebuilt-to-target) # # no boot loader, so we don't need any of that stuff.. # LOCAL_PATH := vendor/[company_name]/[board_name] # include $(CLEAR_VARS) # # include more board specific stuff here? Such as Audio parameters. #
which, when adapted to our example, becomes:
# make file for ARMv7-A based SoC # LOCAL_PATH := $(call my-dir) # # this is here to use the pre-built kernel ifeq ($(TARGET_PREBUILT_KERNEL),) TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel endif # file := $(INSTALLED_KERNEL_TARGET) ALL_PREBUILT += $(file) $(file): $(TARGET_PREBUILT_KERNEL) | $(ACP) $(transform-prebuilt-to-target) # # no boot loader, so we don't need any of that stuff.. # LOCAL_PATH := device/arm/armboard_v7a # include $(CLEAR_VARS) # # include more board specific stuff here? Such as Audio parameters. # PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/armboard_v7a.kl:system/usr/keylayout/armboard_v7a.kl
Note the added line in the PRODUCT_COPY_FILES
definition, that contains twolocations separated by a colon. The first is the location of the keyboardfile in the local Android source tree, and the second is where it will beinstalled on the target. This is added because we wanted to have our own,tweaked, keyboard layout.
The BoardConfig.mk
file for armboard_v7a
product is:
# These definitions override the defaults in config/config.make for armboard_v7a # # TARGET_NO_BOOTLOADER := false # TARGET_HARDWARE_3D := false # TARGET_CPU_ABI := armeabi-v7a TARGET_CPU_ABI2 := armeabi TARGET_NO_KERNEL := true TARGET_ARCH_VARIANT := armv7-a-neon BOARD_USES_GENERIC_AUDIO := true USE_CAMERA_STUB := true
where you define that you are using the armeabi-v7a
and that the targetarchitecture variant is ARMv7-A with NEON. *Note the support for 2 ABIs thatwas introduced in Froyo*. This allows the distribution to run native ARMapplications built for one specific architecture version (using Android NDK).
For any further system properties configuration, add a system.prop filecontaining the things you need, at the same location as the above files(device/arm/[DEVICE_NAME]
). Google's guidelines [3] provide a templatefor this :
# system.prop for # This overrides settings in the products/generic/system.prop file # # rild.libpath=/system/lib/libreference-ril.so # rild.libargs=-d /dev/ttyS0
For any other products/device you wish to add, follow the same procedure outlinedhere (2.a.i).
You should now be ready to build the Android file system for your SoC, with thedevice folder containing all the necessary configuration and build files.For our example, the device folder should look like this:
arm/
+-- armboard_v7a
| +-- Android.mk
| +-- armboard_v7a.kl
| +-- BoardConfig.mk
| \-- system.prop
+-- armboard_v7a_noneon
| +-- Android.mk
| +-- armboard_v7a_noneon.kl
| +-- BoardConfig.mk
| \-- system.prop
+-- another_product
| +-- Android.mk
| +-- another_product.kl
| +-- BoardConfig.mk
| \-- system.prop
\-- products
+-- AndroidProducts.mk
+-- armboard_v7a.mk
+-- armboard_v7a_noneon.mk
\-- another_product.mk
Build the Android filesystem for one of your added targets, by going to the topdirectory of the Android source tree, and entering:
make PRODUCT-[PRODUCT_NAME]-eng
e.g.:
make PRODUCT-armboard_v7a-eng
Note: You do not need to build Android as the root user.
After some time, and if everything was set up correctly, you will get theAndroid root filesystem and a compressed version of it at:
[android_root]/out/target/product/[product_name]
For our example :
[android_root]/out/target/product/armboard_v7a
We use the root filesystem over NFS. For that, make sure you have the NFSserver set up and serving the location you wish to place the Android rootfilesystem.
Note: To use a root filesystem on a remote NFS Server, your Reference Kernel(and therefore your Merged kernel) must support Rootfs over NFS (CONFIG_ROOT_NFS=y
).
The line in the /etc/exports
file should be like:
/srv/nfs4/android-fs *(rw,sync,no_root_squash,no_subtree_check)
where the path at the beginning is the Android root filesystem location, and thelast part is the NFS configuration string.
Now you need to copy the contents of the root
directory from theAndroid source tree to the NFS served location :
$ cp -a [android_root]/out/target/product/armboard_v7a/root/* \ /var/nfs4/android-fs
and the contents of the system
directory from the Android source tree inthe system
placeholder - now in the NFS served location :
$ cp -a [android_root]/out/target/product/armboard_v7a/system/* \ /var/nfs4/android-fs/system
Use "sudo" at the beginning of each command, if you need to have rootprivileges to copy things to the defined destination.
Ensure that the ownership and permissions of the files, as they come out of thebuild system, have not been modified. - cp -a
should have done this for you.
For this filesystem to work, you also need to make changes in the[android_root_filesystem]/init.rc
file. For Gingerbread you need to do thefollowing:
mount rootfs rootfs /ro remount
mount yaffs mtd@system /system
mount yaffs2 mtd@system /system ro remout
mount yaffs2 mtd@userdata /data nosuid nodev
mount yaffs2 mtd@cache /cache nosuid nodev
on fs
... service console /system/bin/sh console disabled user shell group log ...to:
... service console /system/bin/sh console disabled user root group log ...
# basic network init ifup lo hostname localhost domainname localdomain ---> setprop net.dns1 [DNS]and replace
[DNS]
with your local DNS server.Note : The instructions in this step can be also used for deploying theAndroid root filesystem on other media (such as USB sticks or MMC).
Load the kernel on your board, and set the appropriate boot argumentsin order boot successfully.
For our example and for the VExpress board that uses U-Boot, we use thefollowing bootargs:
root=/dev/nfs \ nfsroot=[NFS_IP]:/srv/nfs4/android-fs,nolock,wsize=1024,rsize=1024 rw \ ip=dhcp console=ttyAMA init=init noinitrd mem=1024M user_debug=31
Android should come up shortly. Note that the first boot will take longersince Android is optimizing the .dex files (this is known as the dexoptoperation). See [4] for more details on dexopt.
Should any minor issues appear, experiment a bit more with the kernel optionsand the init.rc
configuration, based on the information you get from Android'slogcat and the system's dmesg.
[1] http://source.android.com/source/initializing.html
[2] http://linux-arm.org/LinuxKernel/LinuxAndroidPlatform
[3] http://www.netmite.com/android/mydroid/development/pdk/docs/build_new_device.html
[4] http://www.netmite.com/android/mydroid/dalvik/docs/dexopt.html