Practice in Droidian Adaptation and Syncing LineageOS 23.0 updates

A guide for Droidian device porters on resolving kernel config fragmentation and bootloop issues when upgrading to LineageOS 23.0.

Background

Recently, I noticed that the upstream LineageOS kernel source for the Sony sm8250 platform was preparing to update to version 23.0 (i.e., Android 16, the successor to the current lineage-22.2 branch). Consequently, the Droidian device kernel also needed to be updated. After merging the changes, I discovered that the upstream had switched to defconfig fragments. This means it no longer relies on a single pdx206_defconfig file for configuration.

Initially, I tried to manually merge the multiple fragment files using the old single-configuration-file approach, but this led to a series of compilation issues, such as the device tree blob (dtb) not being generated and missing BPF function definitions. Subsequent investigation revealed conflicts in my manually merged configuration. For instance, one fragment contained # CONFIG_MACH_SONY_PDX206 is not set, while the vendor/pdx206.config file that defines this config was included earlier. Unexpectedly, this overrode the required CONFIG_MACH_SONY_PDX206=y setting (similar to the SYSVIPC configuration issue encountered by Lindroid: https://t.me/linux_on_droid/1263).

Due to these issues, I had to completely rewrite the configuration and revisit the Droidian linux-packaging-snippets configuration scripts. Since these details might not be explicitly covered in the official documentation, I decided to systematically document this troubleshooting experience.

Kernel Config Merging Logic

The logic for this is defined in /usr/share/linux-packaging-snippets/kernel-snippet.mk, which can be examined in the official Droidian Docker build image (quay.io/droidian/build-essential). The final kernel .config file is generated by concatenating multiple configuration fragments defined in debian/kernel-info.mk. The order of the configuration files determines their priority; later fragments can override settings from earlier ones. Ultimately, the final value of a configuration option is determined by the last fragment that sets it. For example, if Fragment A sets CONFIG_X=y and Fragment B sets # CONFIG_X is not set, the final result will be CONFIG_X not being enabled.

The main configuration parameters used are (listed in the actual concatenation order):

  • KERNEL_DEFCONFIG: The default defconfig to use. This practically uses $(KERNEL_SOURCES)/arch/$(KERNEL_ARCH)/configs/$(KERNEL_DEFCONFIG).
  • KERNEL_CONFIG_COMMON_FRAGMENTS: Common kernel configuration fragments typically used for Droidian kernels. Usually doesn’t require manual configuration; it defaults to include $(KERNEL_SOURCES)/droidian/common_fragments/halium.config and $(KERNEL_SOURCES)/droidian/common_fragments/droidian.config.
  • KERNEL_CONFIG_DEVICE_FRAGMENTS: Device-specific configuration fragments. Files are included in the order specified by the following variables:
    • DEVICE_PLATFORM: Use $(KERNEL_SOURCES)/droidian/$(DEVICE_PLATFORM).config
    • DEVICE_MODEL: Use $(KERNEL_SOURCES)/droidian/$(DEVICE_MODEL).config
    • KERNEL_CONFIG_EXTRA_FRAGMENTS: Additional fragment configuration files to be added, which must be placed under $(KERNEL_SOURCES)/droidian/. Multiple-files is supported.

How to Find the Kernel Configuration Files for Your Device

This explanation is based on the current upstream LineageOS. For other Android system upstreams, please use this as a reference only.

You typically need to look at two projects: android_device_{VENDOR}_{SOC}-common and android_device_{VENDOR}_{MODEL}.

For the Sony Xperia 5 II, the VENDOR is Sony, and the device codename is pdx206. Therefore, the first project to examine is android_device_sony_pdx206. Then, look for TARGET_KERNEL_CONFIG in BoardConfig.mk to find the device’s kernel configuration file.

1
TARGET_KERNEL_CONFIG += vendor/pdx206.config

However, it usually also requires additional SoC platform kernel configuration files. This requires checking the corresponding android_device_{VENDOR}_{SOC}-common project. You can typically find this in the device project’s lineage.dependencies file. For example: https://github.com/LineageOS/android_device_sony_pdx206/blob/lineage-23.0/lineage.dependencies

1
2
3
4
5
6
[
  {
    "repository": "android_device_sony_sm8250-common",
    "target_path": "device/sony/sm8250-common"
  }
]

The corresponding SoC platform project is android_device_sony_sm8250-common. Next, you need to find the SoC kernel configuration files in this project.

The configuration file for the SoC platform project is BoardConfigCommon.mk. Again, search for TARGET_KERNEL_CONFIG within it to find the remaining configuration files.

1
2
3
TARGET_KERNEL_CONFIG := \
    vendor/kona-perf_defconfig \
    vendor/edo.config

Extra: How to Debug Bootloop Issues

This section addresses the bootloop situation caused by using an incorrect kernel config previously. In the past, I was helpless in such scenarios. However, I recently stumbled upon the correct debugging method and am no longer working blindly.

The problem was this: I flashed the kernel merged with the upstream updates. After rebooting, it bootlooped a few times before eventually booting into the system normally. Checking uname showed the old kernel version. journalctl logs contained no information from the bootloop period, only logs after the successful boot. I realized I needed to debug and collect logs from the device’s early boot stage. I subsequently found the following two methods:

ADB on Boot

One method is to enable all ADB debug connections by default at boot. Details can be found here: https://johannes.truschnigg.info/writing/2022-05_android_bootloop_debugging/. However, I couldn’t use it due to two problems:

  1. It requires modifying the build.prop configuration on the system partition. This is impossible under normal operation in Droidian because the system partition is mounted as a read-only loopback. Furthermore, the system directory isn’t visible in recovery mode, making this approach unusable.
  2. If the problem occurs during the kernel bootup phase, the system crashes before even reaching the point where adbd starts. In my case, the bootloop was ultimately caused by a kernel panic, making this method unsuitable.

pstore

Another method is to use pstore to preserve the kernel crash logs from the bootup phase in a persistent storage area (often RAM). Upon the next boot, you can find the kernel panic information and the crash logs in /sys/fs/pstore. For details, refer to: https://docs.kernel.org/admin-guide/pstore-blk.html

When I tried it, possibly due to system design, the pstore area might be cleared during a normal boot, or the logs from the previous boot might not be retained in /sys/fs/pstore by default, so the directory appeared empty. Later, I read that pstore contents might be accessible in recovery mode. I tried forcing a shutdown after a bootloop, directly entering fastboot mode, and then booting into recovery. Sure enough, I found the logs there. Finally, I saw the reason for the kernel panic during boot:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
[    1.341874] qpnp-pdphy c440000.qcom,spmi:qcom,pm8150b@2:qcom,usb-pdphy@1700: Linked as a consumer to regulator.10
[    1.342419] list_add corruption. next->prev should be prev (ffffff9528ef5210), but was 0000000000000000. (next=fffffff44eb62710).
[    1.342487] ------------[ cut here ]------------
[    1.342521] kernel BUG at /build/sources/lib/list_debug.c:29!
[    1.342583] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[    1.342617] Modules linked in:
[    1.342651] Process kworker/6:0 (pid: 70, stack limit = 0x00000000307301e5)
[    1.342713] CPU: 6 PID: 70 Comm: kworker/6:0 Tainted: G S                4.19-325-sony-pdx206 #1
[    1.342773] Hardware name: Sony Mobile Communications. PDX-206(KONA) (DT)
[    1.342813] Workqueue: events deferred_probe_work_func
[    1.342873] pstate: 60800085 (nZCv daIf -PAN +UAO)
[    1.342910] pc : __list_add_valid+0x94/0xb0
[    1.342971] lr : __list_add_valid+0x94/0xb0
[    1.343003] sp : ffffff8010403a10
[    1.343035] x29: ffffff8010403a10 x28: 0000000000000402
[    1.343095] x27: fffffff47fb65160 x26: 000000007fb6970d
[    1.343128] x25: 0000000000000000 x24: 000000000000002a
[    1.343188] x23: fffffff44eb62110 x22: ffffff9528ef5210
[    1.343220] x21: fffffff44eb62710 x20: 0000000000000000
[    1.343280] x19: fffffff44eb62100 x18: 0000000000000098
[    1.343313] x17: 0000000000000000 x16: ffffff95273a8a98
[    1.343374] x15: ffffff9527a70d4f x14: 3020736177207475
[    1.343406] x13: ffffff80105d982b x12: 0000000000000000
[    1.343467] x11: 0000000000000000 x10: ffffffffffffffff
[    1.343501] x9 : 934cf3dcfcf6cd00 x8 : 934cf3dcfcf6cd00
[    1.343534] x7 : 00f300be002000f3 x6 : fffffff475a4441e
[    1.343567] x5 : 0000000000000000 x4 : 000000000000000e
[    1.343599] x3 : 0000000000000010 x2 : 0000000000000001
[    1.343659] x1 : 0000000000000000 x0 : 0000000000000075
[    1.343693] Call trace:
[    1.343755]  __list_add_valid+0x94/0xb0
[    1.343790]  wakeup_source_register+0x120/0x158
[    1.343822]  device_wakeup_enable+0x58/0x100
[    1.343883]  device_init_wakeup+0x70/0xe8
[    1.343918]  usbpd_create+0xa8/0x7e8
[    1.343951]  pdphy_probe+0x220/0x2e8
[    1.344010]  platform_drv_probe+0x80/0xb8
[    1.344043]  really_probe+0x468/0x6a4
[    1.344075]  driver_probe_device+0xb0/0x138
[    1.344136]  __device_attach_driver+0x88/0x1ac
[    1.344170]  bus_for_each_drv+0x8c/0xd4
[    1.344203]  __device_attach+0xbc/0x168
[    1.344264]  device_initial_probe+0x20/0x2c
[    1.344297]  bus_probe_device+0x34/0x9c
[    1.344330]  deferred_probe_work_func+0x5c/0xf0
[    1.344393]  process_one_work+0x270/0x43c
[    1.344427]  worker_thread+0x314/0x4b0
[    1.344488]  kthread+0x140/0x150
[    1.344521]  ret_from_fork+0x10/0x18
[    1.344555] Code: f000fc20 9113b000 aa0803e1 97ec8a43 (d4210000)
[    1.344617] ---[ end trace 6cd728b9c400b2c8 ]---
[    1.346506] Kernel panic - not syncing: Fatal exception
[    1.346569] SMP: stopping secondary CPUs
[    1.986572] cnss: Crash shutdown with driver_state 0x0
[    1.986611] cnss: cnss_pci_collect_dump_info: PCIe link is in suspend state
[    2.199896] ipa ipa3_active_clients_panic_notifier:305
[    2.199896] ---- Active Clients Table ----
[    2.199896]
[    2.199896] Total active clients count: 0
[    2.199896]
[    2.199959] Kernel Offset: 0x1515800000 from 0xffffff8010000000
[    2.200021] CPU features: 0x00000254,a2002238
[    2.200054] Memory Limit: none
[    2.200115] NPU_INFO: npu_panic_handler: 733 Apps crashed
[    2.202015] SMP: stopping secondary CPUs
[    2.202051]
[    2.202111] Restarting Linux version 4.19-325-sony-pdx206 (root@a5849d50bdf6) (Android (6051079 based on r370808) clang version 10.0.1 (https://android.googlesource.com/toolchain/llvm-project b9738d6d99f614c8bf7a3e7c769659b313b88244), GNU ld (binutils-2.27-41d8fcb) 2.27.0.20170315) #1 SMP PREEMPT Sun Oct 5 17:06:50 UTC 2025
[    2.202111]
[    2.202201] Going down for restart now

After examining the kernel code, the issue was likely related to a USB PD (Power Delivery) device. During wakeup initialization, a device information node encountered a problem, specifically a prev null pointer during registration. Such linked list errors are typically related to kernel configuration conflicts or incorrect driver initialization. It’s essential to verify whether related configurations (like CONFIG_USB_PDPHY) are enabled. However, I later realized the root cause was the kernel configuration approach itself. After modifying how the kernel configuration files were handled, this problem disappeared. Nonetheless, if similar issues arise in the future, this debugging approach should still be applicable.

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy