The stock Android kernel is unfortunately not enough to be able to run Droidian.
The good news is that, on GSI-capable devices, often only kernel changes are necessary.
- Summary
- Prerequisites
- Package bring-up
- Kernel info options
- Kernel adaptation
- Compiling
- Obtaining the boot image
- Committing changes
Droidian runs on halium. If your device is launched with Android 9 or above it is possible to port Droidian to it. If it already has a halium-compliant kernel of halium-9.0 and above chances are that Droidian will work without much modification.
If your device has shipped with Android 8.1 or 9, it probably is GSI (Generic System Image) capable, and such it's possible to use an already available, generic Android System Image with Halium patches applied. This will reduce the amount of work the porter has to do significantly.
If your device doesn't support GSI, you'll also need to compile a patched system image. The documentation for image creation can be found in this guide
On Halium, the Android kernel is built via the standard Android toolchain. While this makes sense, on GSI-capable devices this can be a waste of time since often only kernel changes are required.
Thus, Droidian uses a different approach to compile kernels - the upside is that you get packaging for free so that kernels can be upgraded over-the-air via APT, if you wish so.
Note that this guide assumes that you're going to cross-compile an arm64 Android kernel on an x86_64 (amd64) machine using the Android-supplied precompiled toolchain that's available in the Droidian repositories. It's trivial to disable cross-compiling and compiling using the standard Debian toolchain.
Using this method you can also compile and package mainline kernels.
An example kernel packaged using this guide is the Android Kernel for the F(x)tec Pro1.
- Device kernel sources
- A Halium-compliant kernel defconfig
- Docker
If you do not have a Halium-compliant kernel yet you should modify your kernel's configuration as suggested in Kernel adaptation later in the guide. Before doing this make sure your kernel sources can be built and can boot Android.
Assuming your kernel sources are located in ~/droidian/kernel/vendor/device
,
you should create a new branch to house the Debian packaging.
We're also assuming that you want the resulting packages in ~/droidian/packages
.
Droidian tooling expects the kernel source to have a working git directory structure (be a kernel cloned from a git repository).
(host)$ KERNEL_DIR="$HOME/droidian/kernel/vendor/device"
(host)$ PACKAGES_DIR="$HOME/droidian/packages"
(host)$ mkdir -p $PACKAGES_DIR
(host)$ cd $KERNEL_DIR
(host)$ git checkout -b droidian
Now it's time to fire up the Docker container.
(host)$ docker run --rm -v $PACKAGES_DIR:/buildd -v $KERNEL_DIR:/buildd/sources -it quay.io/droidian/build-essential:current-amd64 bash
Inside the Docker container, install the linux-packaging-snippets
, that
provides the example kernel-info.mk
file.
(docker)# apt-get install linux-packaging-snippets
And create the skeleton packaging:
(docker)# cd /buildd/sources
(docker)# mkdir -p debian/source
(docker)# cp -v /usr/share/linux-packaging-snippets/kernel-info.mk.example debian/kernel-info.mk
(docker)# echo 13 > debian/compat
(docker)# echo "3.0 (native)" > debian/source/format
(docker)# cat > debian/rules <<EOF
#!/usr/bin/make -f
include /usr/share/linux-packaging-snippets/kernel-snippet.mk
%:
dh \$@
EOF
(docker)# chmod +x debian/rules
Now edit debian/kernel-info.mk
to match your kernel settings.
Most of the defaults are enough for building Android kernels with the Pie toolchain, so you'll probably need to change device-specific settings (such as vendor, name, cmdline, defconfig and the various offsets).
by unpacking an already built boot.img
using unpackbootimg all the offsets can be found.
unpackbootimg syntax goes as follows
(docker)# unpackbootimg --boot_img boot.img
or for the AOSP version of unpackbootimg
(docker)# unpackbootimg -i boot.img
KERNEL_BASE_VERSION
is the kernel version which can be viewed in Makefile at the root of your kernel source.
As an example
VERSION = 4
PATCHLEVEL = 14
SUBLEVEL = 221
will be 4.14.221
-
KERNEL_DEFCONFIG
is the defconfig filename found at arch/YOURARCH/configs -
KERNEL_IMAGE_WITH_DTB
determines whether or not to include a dtb file in the kernel. if this option is setKERNEL_IMAGE_DTB
also needs to be set. if not an attempt to find it will occur. -
KERNEL_IMAGE_DTB
is the path to the dtb file which can be found in arch/YOURARCH/boot/dts/SOC/ -
KERNEL_IMAGE_WITH_DTB_OVERLAY
determines whether or not to build a dtbo file. if this option is setKERNEL_IMAGE_DTB_OVERLAY
also needs to be set. if not an attempt to find it will occur. -
KERNEL_IMAGE_DTB_OVERLAY
is the path to the dtbo file which can be found in arch/YOURARCH/boot/dts/SOC/ -
KERNEL_PREBUILT_DT
is available for devices with a prebuilt DT image (such as samsungs) and takes a path in the kernel tree.
All these values can be viewed by extracting a boot image with unpackbootimg
-
KERNEL_BOOTIMAGE_CMDLINE
corresponds to "command line args" or "BOARD_KERNEL_CMDLINE"console=tty0
anddroidian.lvm.prefer
should be appended to the cmdline. Make sure to remove anysystempart
entry from cmdline. -
KERNEL_BOOTIMAGE_PAGE_SIZE
corresponds to "page size" or "BOARD_PAGE_SIZE" -
KERNEL_BOOTIMAGE_BASE_OFFSET
corresponds to "base" or "BOARD_KERNEL_BASE" -
KERNEL_BOOTIMAGE_KERNEL_OFFSET
corresponds to "kernel load address" or "BOARD_KERNEL_OFFSET" -
KERNEL_BOOTIMAGE_INITRAMFS_OFFSET
corresponds to "ramdisk load address" or "BOARD_RAMDISK_OFFSET" -
KERNEL_BOOTIMAGE_SECONDIMAGE_OFFSET
corresponds to "second bootloader load address" or "BOARD_SECOND_OFFSET" -
KERNEL_BOOTIMAGE_TAGS_OFFSET
corresponds to "kernel tags load address" or "BOARD_TAGS_OFFSET" -
KERNEL_BOOTIMAGE_DTB_OFFSET
corresponds to "dtb address" or "BOARD_DTB_OFFSET"
Although this option is only required for kernel header version 2. it can be commented otherwise.
-
KERNEL_BOOTIMAGE_VERSION
correlates to the kernel header version. Devices launched with Android 8 and lower are 0, Android 9 is 1, Android 10 is 2 and Android 11 is 2 and GKI devices are 3. -
For Samsung devices
DEVICE_VBMETA_IS_SAMSUNG
must be set to 1. -
BUILD_CC
for most devices launched with Android 9 and above is clang but if your kernel fails to build withclang
you might try changing the value toaarch64-linux-android-gcc-4.9
to build with gcc. -
If your device requires a toolchain which is not included in the buildsystem, you can manually download the toolchain and add the path to
BUILD_PATH
. -
DEB_BUILD_FOR
andKERNEL_ARCH
should be changed according to device architecture.
Installing the built packages means that the kernel and its modules are put in place - but the user should still flash it to their boot partition.
If you wish to enable automatic flashing (via flash-bootimage),
you can do so by setting FLASH_ENABLED
to 1 (which is the default).
If your device doesn't support A/B updates, be sure to set FLASH_IS_LEGACY_DEVICE
to 1.
Note that you need to specify some device info so that flash-bootimage
can cross-check when running on-device:
-
FLASH_INFO_MANUFACTURER
: the value of thero.product.vendor.manufacturer
Android property. On a running Droidian system, you can obtain it with(device)$ sudo android_getprop ro.product.vendor.manufacturer
-
FLASH_INFO_MODEL
: the value of thero.product.vendor.model
Android property. On a running Droidian system, you can obtain it with(device)$ sudo android_getprop ro.product.vendor.model
-
FLASH_INFO_CPU
: a relevant bit of info from/proc/cpuinfo
.
If FLASH_INFO_MANUFACTURER
or FLASH_INFO_MODEL
are not defined (they both
are required for checking against the Android properties), flash-bootimage
will check for FLASH_INFO_CPU
.
If no device-specific information has been specified, the kernel upgrade will fail.
An example for the F(x)tec Pro1:
FLASH_ENABLED = 1
FLASH_INFO_MANUFACTURER = Fxtec
FLASH_INFO_MODEL = QX1000
FLASH_INFO_CPU = Qualcomm Technologies, Inc MSM8998
Droidian ships
- gcc 4.9 (legacy kernels)
- clang-android-6.0-4691093 (recommended toolchain for android9)
- clang-android-9.0-r353983c (recommended toolchain for android10)
- clang-android-10.0-r370808 (recommended toolchain for android11)
- clang-android-12.0-r416183b (recommended toolchain for android12-12.1)
- clang-android-14.0-r450784d (recommended toolchain for android13)
To use clang-android-6.0-4691093
add it to DEB_TOOLCHAIN
and set BUILD_PATH
to the following value
/usr/lib/llvm-android-6.0-4691093/bin
To use clang-android-9.0-r353983c
add it to DEB_TOOLCHAIN
and set BUILD_PATH
to the following value
/usr/lib/llvm-android-9.0-r353983c/bin
To use clang-android-10.0-r370808
add it to DEB_TOOLCHAIN
and set BUILD_PATH
to the following value
/usr/lib/llvm-android-10.0-r370808/bin
To use clang-android-12.0-r416183b
add it to DEB_TOOLCHAIN
and set BUILD_PATH
to the following value
/usr/lib/llvm-android-12.0-r416183b/bin
To use clang-android-14.0-r450784d
add it to DEB_TOOLCHAIN
and set BUILD_PATH
to the following value
/usr/lib/llvm-android-14.0-r450784d/bin
In case you're on an older device and your kernel does not compile with any of the clang toolchains you can fallback to GCC
To use GCC change BUILD_CC
to from clang
to aarch64-linux-android-gcc-4.9
Keep in mind that any kernel newer than 4.4 should should compile fine with clang, there are always exceptions.
Build target is passed to make
which tells the make command what we expect at the end of the build.
Default build target is Image.gz
which generates an image and adds the dtb image after making the gzip archive.
Image.gz-dtb
can also be used for older devices that require the DTB appended to the end of the kernel.
Image
is also available if your device requires a prebuilt dt
You can find which build target is required for your device by looking at the device tree of your device.
In case of any issues with initramfs (as an example unl0kr showing a black screen while encryption is enabled), scripts can be added to the packaging which will be included in the ramdisk.
This packaging can be taken as a reference.
The following adds a function which will run in the initramfs on boot.
Any command that will aid the system in booting can be added to the script.
On builds failing after the kernel compilation stage (during packaging), errors can be ignored.
build sequence can be altered with override_dh_sequence
(make sure to replace sequence with your own sequence)
As as example
override_dh_dwz:
override_dh_strip:
override_dh_makeshlibs:
can be added to the end of debian/rules
.
It is recommended to come back and fix the build errors later on instead of ignoring them as they might cause various issues in the future.
For various android common kernels, Droidian provides ready-to-use config fragments.
KERNEL_CONFIG_USE_FRAGMENTS = 1
can be set in kernel-info.mk
to include defconfig fragments inside your defconfig on build time.
defconfig fragments should be placed in the root of the kernel source in a directory called droidian. this source can be taken as a reference.
KERNEL_CONFIG_USE_DIFFCONFIG
can be enabled to use the python script diffconfig
to compare fragments and the main defconfig.
to use diffconfig a diff file should be provided like so
KERNEL_PRODUCT_DIFFCONFIG = diff_file
If LXC fails to start you can use lxc-checkconfig
after device first booted to check for other options that might be needed.
Now that kernel-info.mk
has been modified, the only thing that remains
is to actually compile the kernel.
First of all, (re)create the debian/control
file:
(docker)# rm -f debian/control
(docker)# debian/rules debian/control
Now that everything is in place, you can start a build with releng-build-package
:
(docker)# RELENG_HOST_ARCH="arm64" releng-build-package
The RELENG_HOST_ARCH
variable is required when cross-building.
If everything goes well, you'll find the resulting packages in $PACKAGES_DIR
.
In case of DTB/DTBO compilation failure it is a good idea to try using an external dtc compiler instead of the one presend in the kernel tree.
To do so after building the control file, add device-tree-compiler
to Build-Depends then and in debian/rules
after the include line add
BUILD_COMMAND := $(BUILD_COMMAND) DTC_EXT=/usr/bin/dtc
The boot image is shipped into the linux-bootimage-VERSION-VENDOR-DEVICE
package.
You can pick up the boot.img by extracting the package with dpkg-deb
or
by picking up directly from the compiled artifacts (out/KERNEL_OBJ/boot.img
).
The kernel image already embeds the Droidian initramfs.
Make sure to save all your linux-*.deb
packages as you'll need those further in the guide.
When you're happy with the kernel, be sure to commit your changes as well as the debian packaging to a git repository:
- debian/source/
- debian/control
- debian/rules
- debian/compat
- debian/kernel-info.mk
...and then push your droidian
branch for others to enjoy.