{"id":2214,"date":"2011-09-14T09:23:47","date_gmt":"2011-09-14T07:23:47","guid":{"rendered":"http:\/\/glandium.org\/blog\/?p=2214"},"modified":"2011-09-14T09:23:47","modified_gmt":"2011-09-14T07:23:47","slug":"building-a-custom-kernel-for-the-nexus-s","status":"publish","type":"post","link":"https:\/\/glandium.org\/blog\/?p=2214","title":{"rendered":"Building a custom kernel for the Nexus S"},"content":{"rendered":"<p>There are several reasons why someone would want to build a custom kernel for their Android phone. In my case, this is because I wanted performance counters (those used by the <a href=\"http:\/\/perf.wiki.kernel.org\/\">perf tool<\/a> that comes with the kernel source). In <a href=\"http:\/\/blog.mozilla.com\/jseward\/\">Julian Seward<\/a>'s case, he wanted swap support to overcome the limited memory amount on these devices in order to run valgrind. In both cases, the usual suspects (AOSP, CyanogenMod) don't provide the wanted features in prebuilt ROMs.<\/p>\n<p>There are also several reasons why someone would NOT want to build a complete ROM for their Android phone. In my case, the Nexus S is what I use to work on Firefox Mobile, but it is also my actual mobile phone. It's a quite painful and long process to create a custom ROM, and another long (but arguably less painful thanks to ROM manager) process to backup the phone data, install the ROM, restore the phone data. And if you happen to like or use the proprietary Google Apps that don't come with the AOSP sources, you need to add more steps.<\/p>\n<p>There are however tricks that allow to build a custom kernel for the Nexus S and use it with the system already on the phone. Please note that the following procedure has only been tested on two Nexus S with a 2.6.35.7-something kernel (one with a stock ROM, but unlocked, and another one with an AOSP build). Also please note that there are various ways to achieve many of the steps in this procedure, but I'll only mention one (or two in a few cases). Finally, please note some steps rely on your device being rooted. There may be ways to do without, but I'm pretty sure it requires an unlocked device at the very least. This post doesn't cover neither rooting nor unlocking.<\/p>\n<h2>Preparing a build environment<\/h2>\n<p>To build an Android kernel, you need a cross-compiling toolchain. Theoretically, any will do, provided it targets ARM. I just used the one coming in the Android NDK:<\/p>\n<blockquote><p><code>$ wget http:\/\/dl.google.com\/android\/ndk\/android-ndk-r6b-linux-x86.tar.bz2<br \/>\n$ tar -jxf android-ndk-r6b-linux-x86.tar.bz2<br \/>\n$ export ARCH=arm<br \/>\n$ export CROSS_COMPILE=$(pwd)\/android-ndk-r6b\/toolchains\/arm-linux-androideabi-4.4.3\/prebuilt\/linux-x86\/bin\/arm-linux-androideabi-<\/code><\/p><\/blockquote>\n<p>For the latter, you need to use a directory path containing prefixed versions (such as <code>arm-eabi-gcc<\/code> or <code>arm-linux-androideabi-gcc<\/code>), and include the prefix, but not \"gcc\".<\/p>\n<p>You will also need the <code>adb<\/code> tool coming from the Android SDK. You can install it this way:<\/p>\n<blockquote><p><code>$ wget http:\/\/dl.google.com\/android\/android-sdk_r12-linux_x86.tgz<br \/>\n$ tar -zxf android-sdk_r12-linux_x86.tgz<br \/>\n$ android-sdk-linux_x86\/tools\/android update sdk -u -t platform-tool<br \/>\n$ export PATH=$PATH:$(pwd)\/android-sdk-linux_x86\/platform-tools<\/code><\/p><\/blockquote>\n<h2>Building the kernel<\/h2>\n<p>For the Nexus S, one needs to use the Samsung Android kernel tree, which happens to be unavailable at the moment of writing due to the kernel.org outage. Fortunately, there is a clone used for the <a href=\"https:\/\/wiki.mozilla.org\/B2G\">B2G<\/a> project, which also happens to contain the necessary cherry-picked patch to add support for the PMU registers on the Nexus S CPU that are needed for the performance counters.<\/p>\n<blockquote><p><code>$ git clone -b devrom-2.6.35 https:\/\/github.com\/cgjones\/samsung-android-kernel<br \/>\n$ cd samsung-android-kernel<\/code><\/p><\/blockquote>\n<p>You can then either start from the default kernel configuration:<\/p>\n<blockquote><p><code>$ make herring_defconfig<\/code><\/p><\/blockquote>\n<p>or use the one from the B2G project, which enables interesting features such as oprofile:<\/p>\n<blockquote><p><code>$ wget -O .config https:\/\/raw.github.com\/cgjones\/B2G\/master\/config\/kernel-nexuss4g<\/code><\/p><\/blockquote>\n<p>From then, you can use the <code>make menuconfig<\/code> or similar commands to further configure your kernel.<\/p>\n<p>One of the problems you'd first encounter when booting such a custom kernel image is that the bcm4329 driver module that is shipped in the system partition (and not in the boot image) won't match the kernel, and won't be loaded. The unfortunate consequence is the lack of WiFi support.<\/p>\n<p>One way to overcome this problem is to overwrite the kernel module in the system partition, but I didn't want to have to deal with switching modules when switching kernels.<\/p>\n<p>There is however a trick allowing the existing module to be loaded by the kernel: compile a kernel with the same version string as the one already on the phone. Please note this only really works if the kernel is really about the same. If there are differences in the binary interface between the kernel and the modules, it will fail in possibly dangerous ways.<\/p>\n<p>To use that trick, you first need to know what kernel version is running on your device. <em>Settings > About phone > Kernel version<\/em> will give you that information on the device itself. You can also retrieve that information with the following command:<\/p>\n<blockquote><p><code>$ adb shell cat \/proc\/version<\/code><\/p><\/blockquote>\n<p>With my stock ROM, this looks like the following:<\/p>\n<blockquote><p><code>Linux version 2.6.35.7-ge382d80 (android-build@apa28.mtv.corp.google.com) (gcc version 4.4.3 (GCC) ) #1 PREEMPT Thu Mar 31 21:11:55 PDT 2011<\/code><\/p><\/blockquote>\n<p>In the <em>About phone<\/em> information, it looks like:<\/p>\n<blockquote><p><code>2.6.35.7-ge382d80<br \/>\nandroid-build@apa28<\/code><\/p><\/blockquote>\n<p>The important part above is <code><b>-ge382d80<\/b><\/code>, and that is what we will be using in our kernel build. Make sure the part preceding <code>-ge382d80<\/code> does match the output of the following command:<\/p>\n<blockquote><p><code>$ make kernelversion<\/code><\/p><\/blockquote>\n<p>The trick is to write that <code>-ge382d80<\/code> in a <code>.scmversion<\/code> file in the kernel source tree (obviously, you need to replace <code>-ge382d80<\/code> with whatever your device has):<\/p>\n<blockquote><p><code>$ echo <em>-ge382d80<\/em> &gt; .scmversion<\/code><\/p><\/blockquote>\n<p>The kernel can now be built:<\/p>\n<blockquote><p><code>$ make -j$(($(grep -c processor \/proc\/cpuinfo) * 3 \/ 2))<\/code><\/p><\/blockquote>\n<p>The <code>-j<\/code>... part is the general rule I use when choosing the number of parallel processes <code>make<\/code> can use at the same time. You can pick whatever suits you better.<\/p>\n<p>Before going further, we need to get back to the main directory:<\/p>\n<blockquote><p><code>$ cd ..<\/code><\/p><\/blockquote>\n<h2>Getting the current boot image<\/h2>\n<p>The Android boot image living in the device doesn't contain only a kernel. It also contains a ramdisk containing a few scripts and binaries, that starts the system initialization. As we will be using the ramdisk coming with the existing kernel, we need to get that ramdisk from the device flash memory:<\/p>\n<blockquote><p><code>$ adb shell cat \/proc\/mtd | awk -F'[:\"]' '$3 == \"boot\" {print $1}'<\/code><\/p><\/blockquote>\n<p>The above command will print the mtd device name corresponding to the \"boot\" partition. On the Nexus S, this should be <code><b>mtd2<\/b><\/code>.<\/p>\n<blockquote><p><code>$ adb shell<br \/>\n$ su<br \/>\n# dd if=\/dev\/mtd\/<b>mtd2<\/b> of=\/sdcard\/boot.img bs=4096<br \/>\n2048+0 records in<br \/>\n2048+0 records out<br \/>\n8388608 bytes transferred in x.xxx secs (xxxxxxxx bytes\/sec)<br \/>\n# exit<br \/>\n$ exit<\/code><\/p><\/blockquote>\n<p>In the above command sequence, replace <em>mtd2<\/em> with whatever the previous command did output for you. Now, you can retrieve the boot image:<\/p>\n<blockquote><p><code>$ adb pull \/sdcard\/boot.img<\/code><\/p><\/blockquote>\n<h2>Creating the new boot image<\/h2>\n<p>We first want to extract the ramdisk from that boot image. There are various tools to do so, but for convenience, I took <a href=\"https:\/\/github.com\/szym\/unbootimg\">unbootimg<\/a>, on github, and modified it slightly to seemlessly support the page size on the Nexus S. For convenience as well, we'll use <code>mkbootimg<\/code> even if <code>fastboot<\/code> is able to create boot images.<\/p>\n<p>Building <code>unbootimg<\/code>, as well as the other tools rely on the Android build system, but since I didn't want to go through setting it up, I figured a minimalistic way to build the tools: <\/p>\n<blockquote><p><code>$ git clone https:\/\/github.com\/glandium\/unbootimg.git<br \/>\n$ git clone git:\/\/git.linaro.org\/android\/platform\/system\/core.git<\/code><\/p><\/blockquote>\n<p>The latter is a clone of <code>git:\/\/android.git.kernel.org\/platform\/system\/core.git<\/code>, which is down at the moment.<\/p>\n<blockquote><p><code>$ gcc -o unbootimg\/unbootimg unbootimg\/unbootimg.c core\/libmincrypt\/sha.c -Icore\/include -Icore\/mkbootimg<br \/>\n$ gcc -o mkbootimg core\/mkbootimg\/mkbootimg.c core\/libmincrypt\/sha.c -Icore\/include<br \/>\n$ gcc -o fastboot core\/fastboot\/{protocol,engine,bootimg,fastboot,usb_linux,util_linux}.c core\/libzipfile\/{centraldir,zipfile}.c -Icore\/mkbootimg -Icore\/include -lz<\/code><\/p><\/blockquote>\n<p>Once the tools are built, we can extract the various data from the boot image:<\/p>\n<blockquote><p><code>$ unbootimg\/unbootimg boot.img<br \/>\nsection sizes incorrect<br \/>\nkernel 1000 2b1b84<br \/>\nramdisk 2b3000 22d55<br \/>\nsecond 2d6000 0<br \/>\ntotal 2d6000 800000<br \/>\n...but we can still continue<\/code><\/p><\/blockquote>\n<p>Don't worry about the error messages about incorrect section sizes if it tells you \"we can still continue\". The <code>unbootimg<\/code> program creates three files:<\/p>\n<ul>\n<li><code>boot.img-mk<\/code>, containing the <code>mkbootimg<\/code> options required to produce a working boot image,<\/li>\n<li><code>boot.img-kernel, containing the kernel image,<\/code><\/li>\n<li><code>boot.img-ramdisk.cpio.gz<\/code>, containing the gzipped ramdisk, which we will reuse as-is.<\/li>\n<\/ul>\n<p>All that is left to do is to generate the new boot image:<\/p>\n<blockquote><p><code>$ eval .\/mkbootimg $(sed s,boot.img-kernel,samsung-android-kernel\/arch\/arm\/boot\/zImage, boot.img-mk)<\/code><\/p><\/blockquote>\n<h2>Booting the image<\/h2>\n<p>There are two ways you can use the resulting boot image: one-time boot or flash. If you want to go for the latter, it is best to actually do both, starting with the one-time boot, to be sure you won't be leaving your phone useless (though recovery is there to the rescue, but is not covered here).<\/p>\n<p>First, you need to get your device in the \"fastboot\" mode, a.k.a. boot-loader:<\/p>\n<blockquote><p><code>$ adb reboot bootloader<\/code><\/p><\/blockquote>\n<p>Alternatively, you can power it off, and power it back on while pressing the volume up button.<\/p>\n<p>Once you see the boot-loader screen, you can test the boot image with a one-time boot:<\/p>\n<blockquote><p><code>$ .\/fastboot boot boot.img<br \/>\ndownloading 'boot.img'...<br \/>\nOKAY [  0.xxxs]<br \/>\nbooting...<br \/>\nOKAY [  0.xxxs]<br \/>\nfinished. total time: 0.xxxs<\/code><\/p><\/blockquote>\n<p>As a side note, if <code>fastboot<\/code> sits \"waiting for device\", it either means your device is not in fastboot mode (or is not connected), or that you have permissions issues on the corresponding USB device in \/dev.<\/p>\n<p>Your device should now be starting up, and eventually be usable under your brand new kernel (and WiFi should be working, too). Congratulations.<\/p>\n<p>If you want to use that kernel permanently, you can now flash it after going back in the bootloader:<\/p>\n<blockquote><p><code>$ adb reboot bootloader<br \/>\n$ .\/fastboot flash boot boot.img<br \/>\nsending 'boot' (2904 KB)...<br \/>\nOKAY [  0.xxxs]<br \/>\nwriting 'boot'...<br \/>\nOKAY [  0.xxxs]<br \/>\nfinished. total time: 0.xxxs<br \/>\n$ .\/fastboot reboot<\/code><\/p><\/blockquote>\n<p>Voil\u00c3\u00a0.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are several reasons why someone would want to build a custom kernel for their Android phone. In my case, this is because I wanted performance counters (those used by the perf tool that comes with the kernel source). In Julian Seward&#8217;s case, he wanted swap support to overcome the limited memory amount on these [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,25],"tags":[23],"class_list":["post-2214","post","type-post","status-publish","format-standard","hentry","category-pdo","category-planet-mozilla","tag-en"],"_links":{"self":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2214","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2214"}],"version-history":[{"count":32,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2214\/revisions"}],"predecessor-version":[{"id":2257,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2214\/revisions\/2257"}],"wp:attachment":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2214"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2214"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2214"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}