Jump to content

Mainlining Guide

From postmarketOS Wiki
Revision as of 02:00, 4 May 2021 by Uclydde (talk | contribs) (fixed file path in kernel patching guide)
Note Parts of this page with useful information are slowly integrated into the Mainlining page instead.

Introduction

Note Please have realistic expectations: Booting mainline even if nothing else is working is already a great achievement!

This guide will walk you through mainlining your device. Starting by collecting all required information, setting up your build environment and picking a mentor that will guide you through the tricky parts. For the devices where this will work, you should be fine with basic shell knowledge, and you will be rewarded with learning a lot of new stuff and possibly booting the mainline kernel on your device!

Requirements

postmarketOS port

To make the instructions easier, this guide assumes that postmarketOS was already ported with a downstream kernel to your device, and that you can enter a shell after it booted via USB (SSH or telnet). Downstream kernels are the ones from the vendor, that ship with the device, and all derivatives from that (e.g. your typical Android / LineageOS ROM). You can check if it was ported already on the devices page, and start a new port if necessary.

Note An existing port with downstream kernel is not a hard dependency, it may take less effort overall to skip that depending on the state of the codebase.

Mentor

The mainlining process varies greatly for each device, which means we can't just write down straight forward instructions here. But luckily there are skilled people who have a bigger picture of a specific SoC and they know how to help you out efficiently. Head over to your device's wiki page (as linked in the device overview) and look at the chipset the device has (infobox on the top right).

SoC Where to ask for help/mentors
Exynos #postmarketOS-mainline, #replicant on Freenode (ask forkbomb, Putti)
OMAP (3, 4) #postmarketOS-mainline, linux-omap mailing list, last resort #maemo-leste on Freenode
Qualcomm (msm...) #postmarketOS-mainline, ##linux-msm on Freenode
Tegra #postmarketOS-mainline, ask @okias, ##tegra on Freenode

Make sure that your SoC is in the list (if it isn't then we don't have an easy way for mainlining right now, sorry). Let's do some more preparation now, so everything is in place before you kindly ask your mentor to help you with the process.

Cooperative firmware

Some firmware, especially bootloader, trustzone etc., doesn't play nice with mainline kernel. That is because it may require ugly hacks in the kernel, courtesy of being disgustingly broken. Those ugly hacks will not be accepted into mainline, because the proper solution would be to fix the firmware, regardless of the fact that the freedom to do that was taken away from you thanks to signature verification.

For example, a device shipping with 32bit kernel or even 64bit kernel with aforementioned ugly hacks, is likely to not support PSCI, the only mainline-approved way of CPU power management (including SMP) on aarch64. Considering this relies on trustzone cooperation, which is as far from being under your control as it possibly can, you're probably out of luck unless you a) implement said ugly hacks or b) manage to exploit your device and supply a proper trustzone.

Only if we campaign for the freedom to run our own software on the devices we own will we be able to replace the firmware with an open one, shall one emerge.

Preparation

Serial cable

It is recommended to have a working serial cable for your device. In case you can not make one, it might be possible to use a workaround for retrieving log messages but that isn't straight forward.

pmbootstrap setup

If you have not done this already, please download and initialize pmbootstrap. You can stay with the defaults, just make sure to select the right device, and (to save you some time) none as user interface. Replace ~/code with the path where you would like to store the source code.

$ cd ~/code
$ git clone https://gitlab.com/postmarketOS/pmbootstrap.git
$ cd pmbootstrap
$ alias pmbootstrap=$PWD/pmbootstrap.py
$ pmbootstrap init

Generate an initramfs with the debug-shell hook:

$ pmbootstrap initfs hook_add debug-shell
$ pmbootstrap export

Kernel

Note The kernel source code is several gigabytes in size, this will take a while!

Download a copy of linux-next:

$ cd ~/code
$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git linux
$ cd linux

If you have already checked out the regular linux repository, you can add the linux-next branch as follows:

$ cd ~/code/linux
$ git remote add linux-next https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
$ git fetch --tags linux-next
$ git checkout -b development linux-next/master

After running the commands of one of the two blocks above, add a new branch for your device (replace lg-mako with your device's name):

$ cd ~/code/linux
$ git checkout -b "device-lg-mako"

envkernel.sh

Usually when compiling a kernel, you would install all dependencies and a cross compiler in your host system. To make it easier, we have the envkernel.sh script (for fish shell: envkernel.fish). It sets up an Alpine Linux chroot with all the dependencies by using pmbootstrap internally, exports the environment variables to use the right cross compiler and creates an alias to make. This means, whenever you type make after sourcing that script, it will actually run make in the chroot. Please try the script out once to make sure everything is working as expected:

(enter the directory where you have cloned the linux kernel source)
$ cd ~/code/linux
$ source ~/code/pmbootstrap/helpers/envkernel.sh
Initializing Alpine chroot (details: 'pmbootstrap log')
pmbootstrap envkernel.sh activated successfully.
 * kernel source:  /home/user/code/linux
 * output folder:  /home/user/code/linux/.output
 * architecture:   arm (samsung-i9100 is armhf)
 * aliases: make, kernelroot, pmbootstrap, pmbroot (see 'type make' etc.)
$ make defconfig
*** pmbootstrap envkernel.sh active for /home/user/code/linux! ***
make: Entering directory '/mnt/linux'
make[1]: Entering directory '/mnt/linux/.output'
getconf: LFS_CFLAGS: unknown variable
getconf: LFS_LDFLAGS: unknown variable
getconf: LFS_LIBS: unknown variable
  HOSTCC  scripts/basic/fixdep
  GEN     ./Makefile
  HOSTCC  scripts/kconfig/conf.o
  YACC    scripts/kconfig/zconf.tab.c
  LEX     scripts/kconfig/zconf.lex.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
*** Default configuration is based on 'multi_v7_defconfig'
#
# configuration written to .config
#
make[1]: Leaving directory '/mnt/linux/.output'
make: Leaving directory '/mnt/linux'

envkernel.sh has grown to be quite powerful, read more about it at the compiling kernels with envkernel.sh wiki page.

Got everything?

Please double check with the following checklist that you meet all the requirements before proceeding further. If you need any help with the above, just ask in the channel or on GitHub as usually.

Contact your mentor

Hop on Matrix or IRC and ask the mentor if they have time to walk you through the further steps. This should be obvious, but remember to be nice and understanding if the mentor does not have time right away, they are supporting you for free so you'll need to be grateful and patient.

The following steps are meant to be taken together with the mentor.

Device Tree Source

The DTS file describes how the peripherals of the device are connected to the SoC. Here is a short description of the process you are guided through here.

Start with an existing file

Your mentor probably knows which file you should use to start your work on. This may either be a file in the existing Linux source tree, or a file your mentor uploaded somewhere.

Example for copying an existing file:

$ cd ~/code/linux/arch/arm/boot/dts
$ cp qcom-apq8064-asus-nexus7-flo.dts qcom-apq8064-lge-nexus4-mako.dts

Let's commit that directly, so it will be easy to see what you have changed later on by running git diff (adjust the description of the commit accordingly):

$ git add -A
$ git commit -m "Starting with: qcom-apq8064-asus-nexus7-flo.dts"

Adjust model and compatible

Open the DTS file in an editor and replace the model and compatible strings at the top of the file:

// SPDX-License-Identifier: GPL-2.0
#include "qcom-apq8064-v2.0.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
/ {
	model = "Asus Nexus7(flo)";
	compatible = "asus,nexus7-flo", "qcom,apq8064";

// ...

In the example, we replace model with "LGE Nexus4 (mako)" and the first string after compatible with "lge,nexus4-mako (we leave the second string in place, as this is the name of the SoC, which is the same).

And again, let's commit this change. It's recommended to commit after every change you make to the DTS files so you can revert easily to the last known working state if necessary:

$ git add -A
$ git commit -m "Adjusted model and compatible"

! caveat: The right compatible values can be found in downstream dts. You may however find something unexpected.

In the following example (sm-a300fu), msm8916-mtp is used, which will result in the wrong dts being selected unless you copy qcom,board-id = <0xCE08FF01 1>; as well. Unfortunately, this board-id is shared between several Samsung phones, but considering they only bundle the one dtb a particular model uses, we can be grateful there even is a board-id. A solution has not been decided upon yet. (18. 3. 2019)

// SPDX-License-Identifier: GPL-2.0
/dts-v1/;

#include "msm8916-samsung-a300fu.dtsi"

/ {
	model = "Samsung Galaxy A3 sm-a300fu";
	compatible = "qcom,msm8916-mtp", "qcom,msm8916", "qcom,mtp";
	qcom,board-id = <0xCE08FF01 1>;
};

Minimal booting version

Your mentor will instruct you how to modify the file further, so you have a minimal configuration that allows you to boot into the mainline kernel if everything went right. Almost nothing will work at this point, except for initializing RAM and serial output. We will work our way to enabling one feature after another throughout the process.

Adjust DTS Makefile

Open arch/arm/boot/dts/Makefile in an editor and add one line for your new device tree. First find the right location by searching for the prefix in the file (e.g. all qcom- lines are in the dtb-$(CONFIG_ARCH_QCOM) += \ section). Then insert the name of your dts file there, but use dtb as extenssion and don't forget the backslash \ at the end of the line. Save, check the diff (it should be similar to the one below) and commit your change:

$ git diff
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 3b4cc1b64a1e..af74d95b63fa 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -756,6 +756,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
        qcom-apq8064-ifc6410.dtb \
        qcom-apq8064-sony-xperia-yuga.dtb \
        qcom-apq8064-asus-nexus7-flo.dtb \
+       qcom-apq8064-lge-nexus4-mako.dtb \
        qcom-apq8074-dragonboard.dtb \
        qcom-apq8084-ifc6540.dtb \
        qcom-apq8084-mtp.dtb \
$ git add arch/arm/boot/dts/Makefile
$ git commit -m "Enable my new DTS in the Makefile"

Testing

Defconfig and compilation

Ask your mentor for the right defconfig to use. Then configure the kernel to use the defconfig and finally build it. Example:

$ cd ~/code/linux
$ source ~/code/pmbootstrap/helpers/envkernel.sh
$ make qcom_defconfig
$ make -j5


Execute on the device

This greatly depends on your device, and you can figure out the correct method by looking at your deviceinfo and at the postmarketos-mkinitfs code if necessary. But typically for fastboot based Android devices, you can patch the boot.img and boot it as follows.

Patch boot.img

Prepare your current shell session once:

$ pmbootstrap chroot -- apk add abootimg android-tools mkbootimg dtbtool
$ export DEVICE="$(pmbootstrap config device)"
$ export WORK="$(pmbootstrap config work)"
$ export TEMP="$WORK/chroot_native/tmp/mainline/"
$ mkdir -p "$TEMP"

Then run one of the following every time you would like to replace the kernel inside the boot.img file. The non-QCDT version is easier, so check if you have a QCDT or not in the deviceinfo first.

non-QCDT devices

Now append the DTB (device tree blob), which has been generated from your DTS file, to the kernel, then create a bootimage from it. Replace qcom-apq8064-lge-nexus4-mako accordingly, and mind the dtb at the end instead of dts:

$ cd ~/code/linux/.output/source/arch/arm/boot
$ cat zImage dts/qcom-apq8064-lge-nexus4-mako.dtb > ~/code/linux/.zImage-dtb
$ cp ~/code/linux/.zImage-dtb "$TEMP"/zImage-dtb
$ cp "/tmp/postmarketOS-export/boot.img-$DEVICE" "$TEMP/boot.img"
$ pmbootstrap chroot
# abootimg -u /tmp/mainline/boot.img  -k /tmp/mainline/zImage-dtb
reading kernel from /tmp/mainline/zImage-dtb
Writing Boot Image /tmp/mainline/boot.img
# exit
[20:00:00] Done

If you are getting

/tmp/mainline/boot.img: updated is too big for the Boot Image (7323648 vs 5853184 bytes)

error, and it can fit on boot partition, add -c "bootsize=<boot partition size>" to abootimg command:

# abootimg -u /tmp/mainline/boot.img  -k /tmp/mainline/zImage-dtb -c "bootsize=12582912"
QCDT devices
Note On Ubuntu, make sure to install libfdt-dev

All flasher offsets must be replaced with the values from the deviceinfo (you might want to save the command as a shell script for quick usage).

$ cp ~/code/linux/.output/arch/arm/boot/zImage  "$TEMP"/zImage
$ cp "/tmp/postmarketOS-export/boot.img-$DEVICE" "$TEMP/boot.img"
$ cp "/tmp/postmarketOS-export/initramfs-$DEVICE" "$TEMP/initramfs"
$ $WORK/chroot_native/usr/bin/dtbTool -s 2048 -p ~/code/linux/scripts/dtc/ -o "$TEMP/dt.img" ~/code/linux/.output/arch/arm/boot/dts
$ pmbootstrap chroot -- mkbootimg-osm0sis \
--kernel "/tmp/mainline/zImage" \
--ramdisk "/tmp/mainline/initramfs" \
--dt "/tmp/mainline/dt.img" \
--base "0x00000000" \
--second_offset "0x00f00000" \
--cmdline "console=ttyMSM0,115200,n8 PMOS_NO_OUTPUT_REDIRECT" \
--kernel_offset "0x00008000" \
--ramdisk_offset "0x02000000" \
--tags_offset "0x01e00000" \
--pagesize "2048" \
-o "/tmp/mainline/boot.img"

Boot

First start dmesg -w (see below). Then bring your device into fastboot mode, connect it via USB and boot (or flash) the kernel:

$ pmbootstrap flasher list_devices
$ pmbootstrap chroot -- fastboot boot /tmp/mainline/boot.img

The list_devices line instructs pmbootstrap to mount the USB device nodes inside the chroot, so without this line being executed at least once in the session, fastboot will not find any devices (see #1345).

dmesg on your PC

Before booting the first time, run dmesg -w on your PC. If everything went right, you should see something like the following as soon as the device boots:

[27166.665566] usb 3-9: new high-speed USB device number 16 using xhci_hcd
[27166.793111] usb 3-9: New USB device found, idVendor=0525, idProduct=a4a7
[27166.793117] usb 3-9: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[27166.793121] usb 3-9: Product: Gadget Serial v2.4
[27166.793125] usb 3-9: Manufacturer: Linux 4.16.0-rc5-next-20180314-dirty with ci_hdrc_msm
[27166.809593] cdc_acm 3-9:2.0: ttyACM0: USB ACM device
[27166.809777] usbcore: registered new interface driver cdc_acm
[27166.809778] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters

TODO

  • Mention this is ARM only
  • Explain how to get dmesg from the device via telnet (dhcpcd interface --nohook ipv6 might be necessary etc.)
  • Add steps to upstream the changes made into the kernel (there are already good tutorials out there)
  • Add step to integrate the changes with the pmbootstrap source tree (might need changes in pmbootstrap architecture for that)
  • Describe how to add a "Mainline section" to each device specific page (with a template!) - something like this overview would be great to have for every device. This can be done while doing the first kernel compilation when following this guide, because that takes a while anyway. Get the components from:
    • downstream dts or teardown results like from ifixit
    • decipher from downstream dmesg
    • grep compatible in downstream DTS decompiled also works

See also

  • pmaports#175 Google Galaxy Nexus (samsung-maguro): Mainlining progress (initial tricks with RAM and some sleep calls before the reboots in the code to figure out where it was failing will also be useful for other devices)
  • pmaports#153 Google Pixel 3 XL (google-crosshatch): mainlining attempt: progress and questions (useful for newer SoCs, useful info about force-reboot code, stub dtbo partition)