Advanced OS Customizations & Bootloaders

Mastering Android UKI: A Step-by-Step Guide to Systemd-boot Unified Kernel Image Creation

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: The Dawn of Unified Kernel Images in Android

The Android ecosystem has historically relied on the

boot.img

format to package the kernel, ramdisk, and device tree blob (DTB) for bootloaders. While effective, this approach can introduce complexity in multi-boot scenarios and doesn’t align with modern boot strategies like the Unified Kernel Image (UKI) concept. A UKI bundles the kernel, initial ramdisk, and kernel command line (and potentially other assets like DTB or splash images) into a single, self-executable EFI binary. This article delves into creating an Android UKI, leveraging the power and simplicity of

systemd-boot

as the EFI boot manager.

Adopting UKIs for Android devices offers several advantages:

  • Simplified Boot Process: A single EFI executable streamlines the boot flow, reducing the number of components the bootloader needs to parse.
  • Enhanced Security: When combined with Secure Boot, UKIs provide a more robust chain of trust by verifying a single, immutable binary.
  • Flexibility: Easier integration into heterogeneous boot environments, especially on devices that also run desktop Linux distributions.
  • Modern Alignment: Aligns Android’s boot mechanism with contemporary Linux distributions that increasingly adopt UKIs.

This guide provides a comprehensive, expert-level walkthrough for developers and enthusiasts looking to customize their Android booting experience.

Prerequisites for Android UKI Creation

Before diving into the technical steps, ensure you have the following:

  • AOSP Build Environment: A working Android Open Source Project (AOSP) build environment. You’ll need access to the Android kernel source and various build tools.
  • Linux Kernel Compilation Knowledge: Familiarity with configuring and compiling the Linux kernel.
  • EFI System Partition (ESP): Access to an ESP on your target device, formatted as FAT32, where the UKI and
    systemd-boot

    configuration will reside.

  • systemd-boot

    Installation:

    systemd-boot

    must be installed and configured on your target device’s ESP.

  • Toolchain: An appropriate cross-compilation toolchain for your Android device’s architecture (e.g., AArch64).

Step 1: Preparing the Android Kernel Source for UKI

The core of a UKI is an EFI-stub enabled kernel. This means the kernel itself can act as an EFI executable. You need to enable specific configurations in your Android kernel source.

Kernel Configuration

Navigate to your Android kernel source directory and launch the kernel configuration menu:

cd path/to/android/kernel/source make menuconfig 

Within the configuration interface, ensure the following options are enabled:

  • Processor type and features —> EFI stub support:
     CONFIG_EFI_STUB=y 
  • General setup —> Initial RAM filesystem and RAM disk (initramfs/initrd) support:
     CONFIG_BLK_DEV_INITRD=y 
  • (Optional, but recommended for debugging) Kernel hacking —> Kernel debugging: Ensure necessary debugging features are enabled for easier troubleshooting.

Save your configuration and exit

menuconfig

.

Step 2: Compiling the Android Kernel with EFI Stub

Now, compile your modified Android kernel. For AArch64 (commonly used in modern Android devices), the target image is typically

Image.efi

or

Image

which can be processed. The EFI stub creates an

Image.efi

or makes

Image

directly executable. Let’s assume you’re building for an AArch64 target:

export ARCH=arm64 export CROSS_COMPILE=aarch64-linux-gnu- make Image.efi 

If

Image.efi

is not generated,

Image

typically contains the EFI stub when

CONFIG_EFI_STUB=y

. You’ll find the compiled kernel at

arch/arm64/boot/Image.efi

(or

Image

).

Step 3: Crafting the Android Ramdisk for UKI

Unlike traditional Android

boot.img

, where the ramdisk is a separate blob, for a UKI, the ramdisk needs to be embedded directly into the kernel as an

initramfs

. The Android ramdisk is a gzipped CPIO archive.

Extracting and Preparing the Android Ramdisk

First, you need a working Android ramdisk. You can extract it from an existing

boot.img

or build it from your AOSP tree. Assuming you have a

ramdisk.img

file (e.g., from

out/target/product/[device_name]/ramdisk.img

):

# Decompress the ramdisk.img gzip -d ramdisk.img # Extract the contents (optional, for modification) mkdir ramdisk_contents cd ramdisk_contents cpio -id < ../ramdisk # If modifications needed, do them here. # Re-create the cpio archive cd .. find ramdisk_contents | cpio -H newc -o > new_ramdisk.cpio # Compress it again gzip new_ramdisk.cpio 

You should now have

new_ramdisk.cpio.gz

which will serve as your

initramfs

.

Step 4: Assembling the Unified Kernel Image (UKI)

This is the crucial step where all components are combined into a single EFI executable. We’ll use

objcopy

to achieve this, adding sections for the initial ramdisk and the kernel command line.

# Define paths KERNEL_IMAGE=arch/arm64/boot/Image.efi RAMDISK=new_ramdisk.cpio.gz UKI_OUTPUT=android-uki.efi # Create a temporary file for the kernel command line echo "console=ttyS0,115200 root=/dev/dm-0 dm_verity.verity_mode=EIO androidboot.selinux=permissive androidboot.fstab_suffix=default" > cmdline.txt # Example kernel command line. Adjust as per your device requirements. # Use llvm-objcopy if available, otherwise objcopy llvm-objcopy  --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x20000  --add-section .initrd=${RAMDISK} --change-section-vma .initrd=0x3000000  --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x10000  ${KERNEL_IMAGE} ${UKI_OUTPUT} # Cleanup rm cmdline.txt 

A note on virtual memory addresses (

--change-section-vma

): These addresses are arbitrary but must be distinct and outside the kernel’s own text/data sections to avoid collisions. The values `0x10000`, `0x20000`, and `0x3000000` are commonly used and safe for these small sections.

You now have

android-uki.efi

, your complete Android Unified Kernel Image.

Step 5: Configuring Systemd-boot

With the UKI created, the next step is to place it on the EFI System Partition (ESP) and configure

systemd-boot

to recognize and boot it.

Placing the UKI on the ESP

Mount your ESP (e.g.,

/boot

or

/efi

) and copy the UKI to an appropriate location:

sudo mount /dev/sdXN /efi # Replace sdXN with your ESP partition sudo mkdir -p /efi/EFI/Android/ sudo cp android-uki.efi /efi/EFI/Android/ 

Creating the Systemd-boot Entry

Create a new boot entry file within the

loader/entries/

directory on your ESP. For example,

/efi/loader/entries/android.conf

:

# /efi/loader/entries/android.conf title   Android UKI linux   /EFI/Android/android-uki.efi # No initrd or options needed here, as they are embedded in the UKI 

Because we’ve embedded the ramdisk and command line directly into the UKI, the

initrd

and

options

lines are not strictly necessary in the

systemd-boot

configuration file for a basic setup. The kernel automatically finds them. This simplifies the boot loader configuration.

Step 6: Booting and Troubleshooting

Reboot your device. When the

systemd-boot

menu appears, you should see

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner