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.

GCC version

The kernel gets compiled with GCC. Alpine Linux has been shipping version GCC 6 for a long time, but changed the default GCC version to GCC 8 in September 2018. It was discovered that at least one vendor kernel does not boot when compiled with GCC 8 (pmaports#103), so we ended up packaging GCC 6 side by side with GCC 8. All existing 3.x vendor kernels that were packaged have been adjusted to use GCC 6 at this point in time, just to make sure we don't introduce any breakage there. The default for new packages is using the current GCC version that is packaged in Alpine (at time of writing: GCC 8).

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 boot when compiled with newer versions)
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.

Use the newest GCC from Alpine

Let's say you are using a device that has already been packaged, and you want to figure out if it also boots when compiled with a newer compiler. Just removing HOSTCC vars and references to them should be enough:

Remove these lines:

# Compiler: this kernel was only tested with GCC6. Feel free to make a merge
# request if you find out that it is booting working with newer GCCs as
# well. See <https://postmarketos.org/vendorkernel> for instructions.
if [ "${CC:0:5}" != "gcc6-" ]; then
	CC="gcc6-$CC"
	HOSTCC="gcc6-gcc"
	CROSS_COMPILE="gcc6-$CROSS_COMPILE"
fi

And remove the mention of HOSTCC in invocation of downstreamkernel_prepare.

Then remove the gcc6 package from the makedepends line.

Afterwards, remove the compiler-gcc6.h file from the aport dir, and from the source line. Instead you will probably need other patches as linked here. First try compiling without patches, then add one patch at a time to fix a specific problem. As usually, with pmbootstrap checksum, you can regenerate the checksums at the bottom of the file, this needs to be done whenever you change source.

When you made it boot successfully, please increase the pkgrel and make a merge request.

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