Difficulty level: Mindblowing
You have undoubtedly heard of famous devices like the HTC HD2 or HP TouchPad, running Windows Mobile or WebOS out of the box, where community managed to get the ARM-Linux kernel running with a fully operational Android platform on top of it.
This article will focus on the lower level aspects of getting such stuff done, namely bootloaders, the Linux kernel and native platform code. It will completely ignore high-level UI elements included in .apk files. Why? I will explain this at a later date.
Currently more than 90% of tablets and phones are built containing ARM CPUs. While this guide does predominantly cover ARM devices, most of the general points will apply to other computers (yes, a phone or tablet is still a computer, with many similarities to your Personal Computer), based on Intel Atom CPUs for instance.
Personally I work on an Android port for the Samsung Wave (running Samsung Bada OS), and I will use some of our code as examples.
What would one need?
To be completely honest, this isn’t a project anybody can take up. You definitely need some knowledge about the C programming language, assembler, electronics, and the architecture of computers. And, as you are reading this article, you will likely be new to all of this. You’ll need lots, loads, s**tloads of time. And
some a lot of help, possibly a team with at least one person having significant experience with embedded systems.
It is helpful to have at least one person in your team with some soldering skills and debugging hardware is a must – try to get a UART or JTAG interface for your PC. Most of the mobile mainboards do expose both of these.
As I will use some weird words here. Here’s a list of the words and abbreviations you will need to know:
CPU (Central Processing Unit)
AP (Application Processor) – this is where Android is executed
BP/CP (Baseband/Call Processor) aka Modem – this is what manages all cellular network activities
Bootloader – The very first program executed by CPU after bootup, the equivalent of BIOS you might know from x86 computers
RISC (Reduced Instruction Set Computer)
ARM (Advanced RISC Machine)
SoC (System on Chip)
IC (Integrated Circuit)
PMIC (Power Managament IC)
GPIO (General Purpose Input/Output)
SFR (Special Function register)
PC – Program Counter Register (R15), also known as Instruction Counter, Execution Pointer or Instruction Pointer (IP) in x86 assembler
OP-code (Single CPU instruction) – it’s good to know that all ARM32 OPcodes are 4 bytes in width, while in Thumb2 execution mode their length varies from 2 to 4 bytes
EP (Program Entry Point) – the address in memory where does the first Opcode of the program is placed
Secure Boot – Architecture where every stage of boot code (bootloaders, and ultimately OS kernel) checks the digital signature of the next stage that is going to be launched before actually launching it. This is supposed to prevent any code from being executed if it is not signed by the manufacturer and released through dedicated official update channels.
HAL (Hardware Abstraction Layer)
There are countless references that could fill up days and months of learning various aspects related to running ARM Linux on new devices and writing drivers for them. Here are the ones I found most useful during my process of learning about all of this.
www.google.com – Whatever is bothering you, Uncle Google does probably know the answer, or at least can point you the right direction. It’s only matter of asking him the right question.
www.arm.com – Great set of materials and reference datasheets about ARM CPUs.
www.heyrick.co.uk/assembler/ – Big reference about ARM ASM, I found “Instruction set quick finder” the most useful.
lxr.linux.no – Linux Cross Reference, can do better search for various kernel’s core functions or macros than ordinary grep.
What is Android?
Have you even been wondering what Android really is? How does tapping on the screen make your device react? How can some sequence of 2.4GHz microwaves generated by another machine cause your device to pop-up a Bluetooth connection notification? There are many explanations and probably all of them are valid. For the purposes of this article I will try to focus on, and cover, one of them.
Android is a platform based on an operating system called Linux. Heard of Linux before? Great. If you have ever used Android, it was ARMLinux with a specialised GUI and API.
Android consists of low level components (native C/C++ compiled to executables, including the Dalvik virtual machine) and high level interfaces (Java and Dalvik code compiled to APKs), Android has no direct influence on any hardware. The wonderful concept is that the same set of Android binaries can work on any Android-enabled, unified, Linux kernel, being completely separated and independant from the actual HW parts inside of the device.
Note: Since version 3.4 of the Linux kernel, some of low-level Android parts were merged into kernel mainline. This means that part of Android is also in kernel.
However, while good as a concept, in many cases the implementation of this is improving, but there are still many aspects where platform components have to be hardware-specific, compiled for a given IC, or, even worse, for a given HW configuration. We will discuss these later.
Here we’ll completely omit high level parts like APKs and focus on the low level components, starting on assembly and ending on lower-level platform components.
Where to start?
It doesn’t matter what OS your device normally runs – it’s am ARM-based device and your CPU is much more than a CPU understood in terms of x86 computer – It’s an SoC, meaning it contains roughly 75% of desktop PC’s mainboard in one, highly power efficent chip, around the size of a coin. The first task for you is to identify which SoC your device uses. Usually sites like http://pdadb.net/ are enough.
Great, you now know the name of your device’s heart. And what? Get all the data you can – datasheets, schematics, reference sources, binaries. Whatever you can find that is related to this particular SoC. Don’t forget about looking for Android devices that also contain this SoC. Android = Linux+platform, remember? Releasing an Android device does oblige the manufacturer, by the power of GPL license, to release complete sourcecode for the ARMLinux Kernel it’s based upon. If this is not the case, find out what the architecture of the SoC is. For example, the Samsung Exynos 4412 model name is S5PC220, that is very similiar to S5PC210, and its predecessor is S5PC110, the Exynos3, also known as Hummingbird, all use S5P architecture, and these are based on the S3C architecture, known from SoCs like S3C6400. The same also applies to the Qualcomm MSM SoCs series. If you can’t find any reference sources for a particular SoC, find reference sources for a few similiar SoCs.
As an example, for the Samsung Wave we’ve used Samsung Aries (Galaxy S) and Samsung Crespo (Nexus S) kernel sources, all of these being based on the S5PC110 SoC. The boards turned out to be very similiar.
Do not forget about the other ICs your device uses – high resolution pictures of the mainboard are very useful here – try to find out model numbers of sensor ICs used, what PMIC is used, and what BP is used. What screen model is used? Also look up any other chip you can identify. Look for other devices, their linux kernels and open-source bootloaders containing drivers for these – you will need them.
Cool, you’ve found some datasheets and hopefully, reference sources, the trouble is you most likely cannot make any practical use of the sources yet. Why? You have to tell the device to load your code, that would prepare some environment for ARMLinux, and then load and jump into the kernel image. Manufacturers tend not to make this task easy for you!
If you have no experience yet, you will make a lot of mistakes – this stage needs some information about what’s going inside the device. During the creation of device, manufacturer’s developers has to debug their mistakes too, so there are almost definitely AP debugging JTAG and UART interfaces exposed somewhere on the mainboard. Personally I think JTAG isn’t the best way to go, unless you have experience working with ARM, gathered on some development boards or resurrecting phones. UART might come in much more useful too.
Q: Why not USB? Android developers do use USB and ADB for debugging.
A: USB is a relatively complex system, so it is much more likely to break than UART. By way of comparison, the code to handle basic VCOM through USB, compiled for ARM, can weigh in at upto 4 kBytes, while it’s about 400 bytes for UART, with most of the code being just an initialization procedure.
While UART still needs some handling in the code executed on device, JTAG is always available, if only the CPU is powered on, not in suspend state, and no serious clocking issues are present. That is also why JTAG is so widely used for phone resurrection.
Anyway, assuming you’ve got hardware for either of the debugging interfaces, you have to hook up with them on the mainboard, probe for these and try to use them – you should get some debug info on UART from stock system too. If this is not the case, JTAG might be your last resort.
Alternatively – if your device has got one or more LEDs (key backlight, screen backlight) wired to GPIOs, this can be used as a very primitive signaling interface. However nothing is as good as the good old-fashioned, tried-and-tested, and widely used ASCII log.
Sometimes manufacturers does make the task much easier, making accessing UART possible without even opening the device. This is the case for Samsung Galaxy class phones.
If you’re probing for UART on your device, it’s very likely that Adam Outler is able to help you. But remember it’s not advise to spam his PM about it. Rather create thread with brief info and high resolution photos of the whole mainboard on the XDA Hardware Hacking Forum. There are also other people in that forum that can help you.
This can be considered as the most frustrating and difficult step – this is all about theoretical work with static analysis, and very little physical results in the end. Usually the end solution for this appears to be very trivial. This will be the part where ARM assembler knowledge comes to be most necessary and useful. Most likely your device does implement some kind of secure-boot process, as obviously the manufacturer doesn’t want you to launch anything custom-made and not approved by them. But you’re the master of your own device, right? So the task is to prove this!
Usually this process consists of analysing the boot-code contained by the system working on your device and finding a security hole in it, then creating an exploit for it to escalate your privileges, allowing you to do whatever you want. The bigger and more complex the boot code is, the higher the possibility that there is an easily exploitable security hole, although more code means much more time spent analysing it.
The primary target is to have the possibility of convincing the stock firmware to execute your custom code in supervisor mode (about ARM execution modes), and, on some modern systems, in the area considered by the ARM TrustZone as “secure”.
Have a look at other devices with the same SoC – they might have the same security keys used to sign bootloaders working with Android. As well, maybe other versions of the bootloaders for your device have lowered/dropped security checks. Think of the possibilities and check them all.
Useful links for the next step:
http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html – Coverage of bootloader tasks, before ARMLinux can be executed
http://www.arm.linux.org.uk/developer/booting.php – Another article about what the bootloader is expected to do before launching the kernel
Great – so you have got some way to load your custom code. What you really want now is for the bootloader to load the kernel.
The bootloader can be a really complex, OS-like program, capable of operations like executing shell scripts or using the LAN interface:
Cyanoboot – customized U-Boot, designed to boot Android on – in this case – Barnes & Nobles Nook Tablet, the device built on OMAP4430, also exploiting a security hole in the stock bootloader
LittleKernel – reference bootloader for Qualcomm SoCs
Alternatively, the bootloader can be a much simpler program:
Qi – bootloader designed for embedded systems, as replacement for complex U-Boot
JetQi – customized Qi, designed to boot Android on Samsung Jet, instead of stock Samsung Handset Platform, it’s replacing stock bootloader completely, however, keeping it in form of binary image, and launching it in case user wants to load SHP, allowing dual-boot
FBOOT – code making callbacks to the many functions of stock bootloader, designed to dualboot Android/Bada on Samsung Wave (ancestor of simple FOTA exploit presented in the Bootloader Hacking part)
There’s also new, WIP version of FBOOT, mostly rewritten to C
Depending on if you’ve got a method to replace stock bootloader code, or inject code into it – you will need to at least implement a complete bootloader (for the former case), or generate a table of callback pointers for the original code (see FBOOT for an example), for the latter case.
If you want to replace the stock bootloader, try to apply to the same rule you have used when doing kernel research – find a development board or end-user device with an open bootloader containing similiar SoC. You will get a pre-made implementation of SoC drivers, including simple peripherials like GPIO, UART, I2C, MMC-host and USB-slave.
So, assuming that you have your bootloader running, and are loading your kernel image from some external source like USB, SD-card or whatever else, you want it to boot.
Useful links for the next step:
Development shortcut – if you have got JTAG hooked up to the device, you can make a script for loading a zImage to the memory, preparing ATAGs and jumping into it. This of course isn’t useful for anybody without JTAG.
ARMLinux kernel bring-up
Once again, I’ll use the Wave’s kernel as the reference here.
Here is the link to the reference source tree
Pick the configuration of the device closest to yours (similiar SoC), and open it. It’s useful to create a repo for new kernel immediately – and it’s a must if you’re not working alone on it. One of the most useful functions is “git grep” – this works significantly faster than a regular grep, and when working with a kernel you need to grep a lot. :)
Defining our machine
Assuming you’ve read Jonathan Sevy’s note about boot sequence, the first thing I am going to look at closer is is __lookup_machine_type. This function compares the content of Register 1 (R1), passed to the kernel by bootloader, with the list of supported machine types. You have to specify your machine type and make sure that it is passed from bootloader correctly, and registered in the kernel during the build process correctly.
FBOOT passes the machine type here and it is equal to the decimal value of 8500 or 8530, depending on the model it’s built for.
- You won’t usually see or use multi-machine kernels. Often defining one machine is enough.
- On ARM systems names machine and board are usually considered to be equal.
- All wave-named files started to be copy-paste
kangsborrows of Aries files. While the whole kernel probably became unusable for Galaxy S devices, most of the sources and #ifdefs were left for future reference.
The config for Wave’s kernel is arch/arm/configs/wave_defconfig (config name needs to end with _defconfig)
The crucial point is making it all stick together, so proper Kconfig values had to be defined in arch/arm/mach-s5pv210/Kconfig, matching the previously defined machine type.
kanged borrowed files had to be included in the actual build process for MACH_WAVE, so there is a arch/arm/mach-s5pv210/Makefile modification.
In this case, the Wave had a different RAM configuration than the Aries, so kernel itself happened to be launched with different EP, so the modification of arch/arm/mach-s5pv210/Makefile.boot was necessary to tell the toolchain where the first kernel instruction should be placed, and where it can expect to have ATAG parameters list.
We want to debug early on during the boot process, so enabling DEBUG_LL and EARLY_PRINTK config flags is a good option. It does enable a very primitive set of print functions, mapped directly to UART interface SFRs and tells “printk” function to use them. All of the commonly used architectures support these switches.
You might know from the analysis of bootloader, and have it confirmed from HW probing, what UART port is used for the debug output – this might require proper patches on the kernel level. From grepping we know that printascii is a generic method from arch/arm/kernel/debug.Sthat does use platform-specific methods addruart, senduart, waituart and busyuart. In our case we are able to find first one in arch/arm/mach-s5pv210/include/mach/debug-macro.S and 3 in arch/arm/plat-samsung/include/plat/
debug-macro.S – it turns out that addruart is used for obtaining the base address to the SFR block of a certain UART port and that it depends on the CONFIG_DEBUG_S3C_UART value – bingo. Modifying this constant in defconfig would select any UART port we wish for debug output.
Assuming everything went okay, you can compile the kernel and launch the zImage on your device. It should produce some output and then hang or panic. Usually a panic is slighty better because it’s easier to debug (all registers and call-stack are dumped in the panic message output).
Example: This is one of the UART logs from Wave kernel booting – it was hanging early, during clock configuration – you can notice very verbose output from the S3C clocks driver, as I was trying to find out the exact reason for, and location of, the hang. It turned out that it hung when accessing the SoC sound module clock SFRs – after more research I found out that sound module power-gating was enabled by default, cutting off power to its SFRs completely. This made accessing any of the specified module’s SFRs cause the whole SoC to hang. The solution was simply writing 0xFFFFFFFF to the power gate SFR, that powered on all sub-blocks of the SoC and made the kernel boot normally. While this may appear easy, it took around 10 hours to figure out at the time.
Another good example is when the kernel did boot, and then was randomly throwing panics, sometimes after 1 minute, other time after 10, always having some random-looking complaints that looked like memory corruption. This took much longer to debug (in the order of weeks, in parallel with other development tasks). Finally it turned out that having a single bit HIGH in one of the DRAM controller SFRs, enabling “PHY DLL” funtionallity, that is meant to improve system stability is noted in the SoC reference manual that “PHY DLL should not be off for reliable operation”. However, apparently it didn’t play well with some part of the kernel or clocking driver for S5PV210, as turning PHY DLL off fixed the issue completely. I found out that it is not used either on the Galaxy S.
There are many, many more issues like this, and fixing them is usually quite difficult. But this just has to be done to proceed with the Android porting.
If your device has got a different amount of RAM than the reference device, you must adjust the memory setup. If your device has got the same amount of memory, it may also require adjustments anyway. This could look like this: original Aries memory setup -> modified Wave memory setup.
There were more things dependant on S5PV210_PA_SDRAM, that were also present on the Aries. And this constant has to be changed too. So there might be more than 1 place to change this.
One of the most difficult tasks – a typical SoC has several hundred GPIOs to communicate with other ICs on the board, provide PWM outputs to regulate the intensity of the vibrator motor or backlight of the screen or keys, provide keyboard support, the ability to serve external interrupts, and communicate with various memory types (RAM, MMC, NAND, NOR). Most of the GPIOs can serve as simple, code-controller digital I/Os – able to drive or detect a HIGH or LOW state on its port. These can also be configured as external outputs of various modules present in the SoC, such as SPI, I2C, USB, UART and memory controllers. These pins will probably have the same purpose on all boards built using this SoC. The most difficult are the pins used as simple I/Os. There are a few options for how to gather info about the GPIO mappings of the device:
- Reverse engineering of stock firmware, including OS kernel
- Obtaining detailed board schemas (not the PCB diagrams, but general schemas of connections), though usually you won’t find them in common service manuals
- Destroying the device, knowing the pinout of SoC and other ICs – desoldering them from board and tracing the signal lines between them
The device is not much use if it doesn’t display anything on the screen, doesn’t communicate with touchscreen, cannot connect to WiFi, doesn’t play a single sound, and can’t be controlled using HW keys. We’re not even talking yet about things like camera, sensors and other not-so-essential peripherials.
If you have already got GPIO mappings and know what’s wired to what, use previously gathered datasheets and info to create/adjust drivers for your board. This isn’t so difficult a task as it might look because there are many drivers you can use as a reference. Basically the most efficient approach is copy-paste, adjust, modify, copy-paste, fix-ups, more adjustments, then try to run it Then it won’t work, so debug it, do more copy-pasting, research, do more fixes, re-test.
If you have got a kernel with some basic functionallity, you are probably ready for the platform bring-up. Let’s not go for Android quite yet! Run Android Recovery first. This might be ClockWorkMod (CWM) or TeamWin Recovery Project (TWRP)- you choose.
You should create a new Android platform configuration (covered in other XDA-U articles), basing on some reference device with, again, preferably similiar HW inside.
What you definitely need is a lot of debug verbosity – so don’t be afraid to mess with system/core/init/log.h, maximally increasing verbosity.
Then what’s left is just compiling it, catching output on UART and debugging, debugging, debugging.
Don’t forget to configure the bootloader to load the ramdisk properly and pass info about it to the kernel.
After, most likely, a few days, you should be done with all of the bugs. Enjoy working ADB, Recovery and basically a Linux-box you can control over USB or that small menu available in recovery.
Where to put it?!
Your device does contain some amount of flash memory, and this memory is managed and used by the currently installed OS. While the Android platform /system partition can be in excess of 250MB, it does also need some living space for /data and /cache, requiring more than 500MBs of space reserved for Android usage. Again, there are a few possibilities, with advantages and disadvantages:
- Wipe current OS completely – does leave you with all the resources useful for Android, although also leaves you without the ability to dual-boot to your stock, fully working system.
- Install Android to SD card – this makes dual-booting possible, doesn’t touch the stock system, but usually only SD cards of class 8 or 10 are fast enough to serve at least an average end-user experience. That’s because internal memory of the device is the fastest memory it has.
- Share space with stock OS – gives you dual-boot, and doesn’t require the use of SD card. It’s often not possible for every device as there might be not enough embedded memory on it and another disadvantage is the reduced memory for both of installed systems.
So the choosing of any configuration is up to you. It’s also reasonable to make a few installation variations possible.
The Android platform does not communicate with kernel drivers directly. Such a topology would make whole platform extremally hardware-dependant. Anything the platform does, that has to have any influence on hardware, is done through interfaces known as HALs. There are two types of HALs:
- libraries, like libsensors.so, liblights.so, libvendor-ril.so, libaudio.so, gralloc.so, libGLES_android.so, wi-fi and graphics card drivers
- sysfs interfaces already compiled into kernel, like /sys/devices/system/cpu/cpu0/cpufreq/ used to pick a CPU governor or force the CPU to stay at desired frequency
Some of the HAL libraries can be easily coded, like libsensors (example) – in many of cases it’s just matter of using proper IOCTL requests on the device driver, and passing the received value to the platform. Other HALs are a completely different story, and the most complicated tend to be closed-source (example).
In the cases where your device does contain some system with no open-source drivers available normally the best path to take is just pulling various proprietary binaries and praying they work, if they don’t – you can try to analyse and patch them. If it turns out to be not possible, the last, most time consuming process is writing a HAL from scratch.
Radio Interface Layer
This is, as previously mentioned, the RIL – potentially the most complex and most crucial HAL in the Android for phones. Without a proper RIL there is no possibility of communication with the BP, rendering the device unable to make/receive calls and texts, use Location Based Services, access the internet over cellular data, and potentially a lot more.
In the case of the HTC HD2, the manufacturer had used a lot of similiar hardware and software on other phones of the same class, which allowed almost direct ports of libraries like the RIL from HTC Desire and similiar, modifying only small pieces of these where necessary.
A much more complicated case is where the manufacturer uses a non-standard design for BP, and a non-standard communications protocol with the AP, like Samsung did in the Jet and Wave. This implies a completely different approach, and consists of writing a custom RIL. For this task we’ve choosen an open-source replacement for the Samsung Crespo RIL, and an implementation for the Wave and Jet is still ongoing as, besides similiar topology (communication through packets transferred in shared-memory), this is where the similiarities with other Android phones end in this case.
In such cases, dynamic and static analysis of the stock OS code is necessary, pulling out all of the telephony layer info. This isn’t a trivial task, as in this case the whole OS is linked into huge binaries (kernel+drivers are about 40MBs).
Finally, after your recovery is working, using the previously created platform configuration files, you should put everything together and get a working, very buggy platform build. Usually adjusting all of the things does take a lot of time and brings a lot of frustration. But having fully working Android on completely non-Android device is worth all of it.
Final tips, common traps and more links
Don’t underestimate the power that a team brings to the project. Two one-man “teams” can do much less than one team consisting of two men. Team up with people and share your knowledge and code.
Sharing is an essence of open-source projects, and Git is all about sharing. Unfortunately, it isn’t the most user-friendly application, but don’t wait! Spend some time on learning it, let other people see your code, and let them improve it. You can’t do everything alone.
It’s all about the money. Right?
Don’t even count on money for maintaining such project. Donation are always great to receive, and do increase the motivation for work. But honestly, the money that a hired developer team get for launching Android on an upcoming device is unimaginably better than whatever you’re able to get from donations, while the well paid teams have also got all of the proprietary documentation they can ask for. So do not start thinking of your project as a money-making machine, because you’ll quickly realize that you’re doing something for no money, for bunch of whiny braindead kids, and you will get discouraged quickly. And besides, it’s about the fun of achieving this, not the fame or money!