The state of Android bootloaders, a call to developers

images/header.jpg
Several phones on their fastboot mode screens

Android bootloaders have long been a frustration in the custom OS development scene… On many devices they’re unusably broken for developers!

Introduction

Lots devices ship with broken bootloader functionality where commands like “fastboot boot” are permanently broken, or worse, only broken some of the time.

There’s somewhat of a misnomer with regards to bootloader unlocking in the Android world. We commonly refer to an unlocked bootloader as a bootloader where you’re able to disable verification of the boot image, allowing you to boot non-stock kernels and ROMs. The problem I’m referring to here is that you aren’t able to replace the bootloader itself, doing so will lead to a bricked device in all cases as the bootloader (XBL on modern Qualcomm devices) is verified by the PBL (early boot code flashed into ROM) before it’s loaded. The only way to recover from this is to reflash the device from EDL mode.

Unfortunately most device vendors (at least every modern Qualcomm device) ship their devices with permanently locked bootloaders, that is to say it’s not possible to flash a third party bootloader to the device. This imposes some particularly strict limitations in the context of Linux on these devices due to the differences between the Android and Linux boot processes.

In this article I’ll primarily be focusing on modern (newer than msm8996) Qualcom devices, as that represents the majority of devices and is where my experience is from.

The current limitations

Android bootloaders share some common processes, during the boot process the bootloader will check if the user is holding some button combination and if they are it will enter fastboot mode, from this mode the user can perform actions like “unlocking” the device to disable boot verification, flash partitions like “boot”, “dtbo”, “system” and “vendor”, and, in cases where the device vendor hasn’t broken functionality, boot a new boot image directly from memory.

lk2nd

Some older (msm8916) Android devices can be supported by lk2nd, this project instead of replacing the stock bootloader, instead just jumps to a new one. lk2nd provides it’s own fastboot implementation and works by being small enough to take up ~500kb at the start of the boot partition, it then uses the rest of the boot partition for the regular Android boot image.

The main use of lk2nd is to overcome more severe limitations which are outside the scope of this article.

The Android boot image

The Android boot image is well documented, and many tools exist to interact with it. The image contains the Linux kernel, a DTB or devicetree blob, an initramfs and extra kernel cmdline parameters. Each object is packed at a device or SoC specific offset which is also hard coded in the bootloader. During boot the boot partition will be read, the objects unpacked and placed in memory, then the Linux kernel will be booted in much the same way as any other Linux platform.

Fundamentally, the Android boot image is simply a “convenient” way to pack up the same objects which are used to boot Linux on any platform, an alternative to image format used by u-boot, or the common EFI format supported by many devices.

So what’s wrong with this?

The main goals when it comes to mainlining Android devices are twofold:

Gain automony from vendors:

We don’t want to be held to the wrath of vendors update schedule, they have proven to be insufficient and often stop updates altogether after just a few years.

Gain ownership of the device

You paid money for a phone, the hardware is yours to do with as you please, so why shouldn’t you be able to run your own software too?

The first of these goals is the most obvious one, replace the outdated vendor kernel with mainline linux, remove the need for proprietary drivers and blobs which don’t work outside of an Android userspace. This gives us (the users) the ability to keep our devices up to date, and run Free and Open Source (FOSS) software instead of Android if we choose to. Along with faster security updates and (presuming the devices makes it upstream) long term support.

The second goal is the focus of this article, running real Linux on our phones means we have to work around Android quirks. One major example of this is the boot image format, where all Linux distributions support EFI / u-boot / grub formats, none properly support the Android boot image format.

Kernel updates on Mainline Android devices

There are several ways to go from here, we can create some distro-agnostic post install hooks to be run after the kernel package upgrades, these can generate and flash a new boot image. But there are a few caveats:

  1. Tools to generate an Android boot image must be installed
  2. Some device specific configuration is needed to specify the offsets and location of the boot partition
  3. On A/B devices, the current slot has to be parsed in order to make sure the correct boot partition is flashed

This solution will quickly get complicated and make the update process harder and more obscure for end users to deal with, debug etc. It also offers absolutely no recovery methods should the update process fail and leave the boot partition corrupted.

I propose a better alternative! On devices which already have mainline Linux, why should be beholden to these limitations? Whilst it’s true that we can’t currently replace the bootloader, leaking it’s limitations to userspace will only make our lives much harder. The logical conclusion then is to create a layer of abstraction between userspace and the boot process, compatible with for example Grub or u-boot configuration files.

Linux as a bootloader

The OnePlus 6 and other SDM845 devices are capable of booting EDK2, a UEFI compatible bootloader, however to do so requires extracting blobs from the stock XBL, it also lacks suppoort for the touchscreen or buttons, it doesn’t seem to be a very maintainable approach.

We already have a kernel with all of the drivers we need to boot Linux, that being Linux itself! With Linux as a bootloader we can deal with the abstraction outside of userspace as well as create an easy to use touch interface to allow multibooting, boot time configuration and all of the other utilities you’d expect from a bootloader. We could shrink the userdata partition by a small amount and create a new boot partition to be mounted by userspace, or even just reuse a partition like “vendor”.

Then, userspace simply updates the kernel and uses existing standards like u-boot configuration, thus saving the hassle of implementing complicated Android standards in userspace and offering fallbacks to prevent accidental soft bricks. In the case of a bad kernel update, you’d simply be able to boot back into your old kernel!

By crafting a minified Linux kernel (with only core hardware enabled) we can keep boot times extremely short. In conjunction with kexec we can create a user friendly bootloader for our mainlined Android phones, ensure that a failed update can’t make your device unbootable (until you can recover from a PC).

It is also possible to implement an equivalent to fastboot (or even fastboot itself with fastbootd) in our bootloader, similar projects lik jumpdrive already offer this functionality.

So lets do it

In my opinion, having a configurable user friendly bootloader is key to encouraging more users to experiment, get comfortable with and eventually begin to use Linux on their Android devices. So lets do it, if you have experience with embedded Linux, framebuffer graphics or fancy building some UI, get in touch with me and lets make it happen!

Join the Matrix channel

join us on Telegram

Or

Email me

Message me on Matrix