Jump to content

Downstream kernel specific package

From postmarketOS Wiki

Unless a device has been mainlined, we need to package the downstream kernel (also called "vendor kernel"). This is the reference for the APKBUILD files of vendor kernels.

Generate a template

Usually you would generate both a new device specific package and a new vendor kernel package by running pmbootstrap init and typing in a new device name. In case you should only have a device package, but not a kernel package, you can regenerate the latter with pmbootstrap aportgen linux-changeme-changeme (you guessed it, changeme needs to be adjusted).

The template gets improved every now and then to make porting easier, so don't be surprised when you are looking at older ports and their APKBUILDs look a bit more complicated than what gets generated from the latest pmbootstrap version. Essentially they are doing the same thing.

Modernize your aport

To recreate your vendor kernel aport on top of the latest (and therefore most readable) template, do the following steps:

$ mv aports/device/testing/linux-wiki-example /tmp/linux-wiki-example-old
$ pmbootstrap aportgen linux-wiki-example
$ # (now adjust the new aport to use the sources and patches from the "-old" dir, rename $pkgname from linux-wiki-example to linux-wiki-example-old)
$ pmbootstrap checksum linux-wiki-example
$ pmbootstrap build --force linux-wiki-example
$ # (now test it on your device and make sure it boots, e.g. with 'pmbootstrap zap', 'pmbootstrap install', 'pmbootstrap flasher boot')
$ pmbootstrap pkgrel_bump linux-wiki-example
$ git add -A
$ git commit -m "device/linux-wiki-example: modernize aport"

downstreamkernel_prepare

Newly generated downstream kernel packages use the downstreamkernel_prepare script. That way we can share code between the APKBUILDs, so it is less redundant. To see what the function does, read the source over here.

It is supposed to be sourced, so the content of the script gets executed in the current shell. This is done with the dot in-front of the script name:

prepare() {
	default_prepare
	. downstreamkernel_prepare
}

Note that this behavior had changed in pmaports!1084. Out of tree device ports may not have been adjusted yet and still pass arguments to the script. This is not supported anymore, adjust them like written above or consider modernizing the whole pmaport as explained earlier in this article.

Compiler

postmarketOS tries using LLVM and Clang wherever it can with kernels. This means that all mainline kernels have to use LLVM. But, with downstream kernels, things get a bit murkier. Generally, downstream kernels are too old or too badly programmed to compile with such a new compiler and, even though Android is compiled with LLVM, may not compile without Google's fork of LLVM.

Historically, kernels have been compiled with the version of GCC in aports. This is not allowed anymore due to internal restructuring of how compilers work in pmaports. Therefore, kernels must either use LLVM, GCC 6, or GCC 4

Use LLVM

There are two ways to use LLVM with a downstream kernel depending on the version. If the version is 5.x.x or higher then the kernel can most likely be compiled with LLVM. If it is 4.x.x, proceed with caution, and if it is 3.x.x or lower, skip this section and read GCC6.

Method 1

Notice On kernel versions higher than 5.x.x, it may be possible to omit the LLVM_IAS line entirely.

Add clang lld llvm to makedepends in the kernel package APKBUILD.

Edit the build function to add LLVM=1 LLVM_IAS=1 HOSTLDFLAGS="-fuse-ld=lld". For example:

make O="$_outdir" ARCH="$_carch" \
		LLVM=1 \
        LLVM_IAS=1 \
        HOSTLDFLAGS="-fuse-ld=lld" \
		KBUILD_BUILD_VERSION="$((pkgrel + 1 ))-postmarketOS"

Method 2

If the kernel is too old or too broken to compile with the LLVM=1 environment variable, it may still be possible to force the build system to use it. This will most likely not work, but it is an option before resorting to ancient versions of GCC.

Remove anything that sets CC and above the prepare function, add:

CC=clang
LD=ld.lld
AR=llvm-ar
NM=llvm-nm
STRIP=llvm-strip
OBJCOPY=llvm-objcopy
OBJDUMP=llvm-objdump
READELF=llvm-readelf
HOSTCC=clang
HOSTCXX=clang++
HOSTAR=llvm-ar
HOSTLD=ld.lld
HOSTLDFLAGS="-fuse-ld=lld"

If the kernel does not build or does not boot, go to GCC 6.

Use GCC 4

Follow the GCC 6 instructions below, but replace 6 with 4 everywhere.

Use GCC 6

If your new kernel port does not boot at all, or it doesn't even compile, please try to compile it with GCC 6 and see if it works then.

To do that, open aports/device/linux-[your-device]/APKBUILD and add these lines:

# Compiler: GCC 6 (doesn't {FIXME: boot/compile} with LLVM)
if [ "${CC:0:5}" != "gcc6-" ]; then
	CC="gcc6-$CC"
	HOSTCC="gcc6-gcc"
	CROSS_COMPILE="gcc6-$CROSS_COMPILE"
fi

between makedepends= and # Source lines.

Then add gcc6 to the kernel's makedepends line.

Technical detail: the if... guard around setting the variables is necessary, because the CC and CROSS_COMPILE variables extend themselves. abuild will parse the APKBUILD a second time while running the package() step (inside fakeroot). So without the if statement, we will end up with CC="gcc6-gcc6-gcc" instead of CC="gcc6-gcc" which gets problematic if you are calling make to install kernel modules in the package section (as done in linux-asus-tf101 for example). Interestingly, only CC and CROSS_COMPILE gets passed through, hence we can't simply check the HOSTCC variable.

External dtc

Some downstream kernels does not compile device tree with dtc from kernel sources (scripts/dtc/dtc) and use prebuilt dtc defined via DTC_EXT environment variable. Add dtc package into makedepends and export DTC_EXT=dtc in build().

Using GNU Make 3.x

GNU Make sometimes introduce backward-incompatible changes. While recent versions of Linux kernel requires GNU Make 4.x, building older kernels fails with GNU Make 4.x but succeeds with GNU Make 3.x.

To use GNU Make 3.81, open aports/device/linux-[your-device]/APKBUILD and add these lines:

# GNU Make 3.81
PATH="/usr/make3.81/bin:$PATH"

between makedepends= and # Source lines.

Then add make3.81 to the kernel's makedepends line.

Using a specific kernel image

By default downstreamkernel_package.sh looks for a few kernel image files such as Image.gz-dtb and *zImage, but for various reasons you may want/need to look for something else; in these situations set KERNEL_IMAGE_NAME similar to the following:

package() {
	KERNEL_IMAGE_NAME="Image.gz" downstreamkernel_package \
		"$builddir" "$pkgdir" "$_carch" "$_flavor" "$_outdir"
}

heimdall-isorec

If you have a device that only boots with heimdall-isorec, you will need to adjust your linux APKBUILD as follows:

  • add busybox-static-armhf to makedepends
  • copy the "init" file from linux-samsung-i9100 and customize the partition
  • add "init" to source= in the APKBUILD
  • adjust your prepare() like it's done in linux-samsung-i9100 and adjust the paths in there

You can search for more examples of heimdall-isorec deviecs with grep:

$ grep -r 'deviceinfo_flash_method="heimdall-isorec"' aports
aports/device/device-samsung-i9070/deviceinfo:deviceinfo_flash_method="heimdall-isorec"
aports/device/device-samsung-i9100/deviceinfo:deviceinfo_flash_method="heimdall-isorec"

See also