|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# SPDX-License-Identifier: GPL-2.0 |
| 4 | +# Copyright (c) 2026 Igor Velkov |
| 5 | +# This file is a part of the Armbian Build Framework https://github.com/armbian/build/ |
| 6 | +# |
| 7 | +# Enable cumulative kernel debug-information tiers for headless boards that |
| 8 | +# need to be debugged through their serial console. On its own this extension |
| 9 | +# does nothing visible at runtime — it just bakes enough information into the |
| 10 | +# kernel that the operator-console facilities (Magic SysRq, KGDB, pstore) and |
| 11 | +# any tracebacks they print are actually useful rather than streams of hex |
| 12 | +# addresses. |
| 13 | +# |
| 14 | +# Pair with the `sysrq-serial-trigger` extension (kconfig + sysctl + u-boot |
| 15 | +# autoboot delay) to actually have an operator-controllable surface; that |
| 16 | +# extension turns the facilities ON, this one makes them MEANINGFUL. |
| 17 | +# |
| 18 | +# Tiers are cumulative: KERNEL_DEBUG_TIER=N enables every tier ≤ N. |
| 19 | +# |
| 20 | +# 0 no-op (extension stays loaded, kernel side disabled — keeps the |
| 21 | +# extension declarable in a shared config without forcing the cost on |
| 22 | +# every board, e.g. when one board needs BTF=no for RAM reasons) |
| 23 | +# 1 printk timestamps + lockup/hung-task detection + stack guards |
| 24 | +# Cost: a handful of bytes per printk, a few cycles per scheduler tick. |
| 25 | +# No board prerequisites. Default tier. |
| 26 | +# 2 + pstore/ramoops (persistent dmesg through reboot) |
| 27 | +# Cost: same as tier 1 plus a reserved memory region. Without a DT |
| 28 | +# `/reserved-memory/ramoops` node or `ramoops.mem_address=…` bootargs, |
| 29 | +# the modules load but have nowhere to write — silent no-op. |
| 30 | +# 3 + KGDB / KDB over serial |
| 31 | +# Cost: same as tier 2 plus larger kernel image. Needs `kgdboc=<port>,<baud>` |
| 32 | +# in bootargs; without it the dispatcher is in-kernel but unreachable. |
| 33 | +# |
| 34 | +# BTF requirement: from tier 1 onwards this extension needs DEBUG_INFO_BTF=y |
| 35 | +# (Armbian's default unless KERNEL_BTF=no). Without BTF, hung-task tracebacks |
| 36 | +# and KGDB symbol output collapse to address-only — the whole point of the |
| 37 | +# extension is then defeated. So an explicit `KERNEL_BTF=no` (in board/family |
| 38 | +# conf or on the build CLI) combined with KERNEL_DEBUG_TIER>=1 is treated as |
| 39 | +# a hard error: pick one, not both. |
| 40 | +# |
| 41 | +# Usage — add to your userpatches config (e.g. `userpatches/config-my.conf`): |
| 42 | +# |
| 43 | +# enable_extension "kernel-debug-tiers" |
| 44 | +# KERNEL_DEBUG_TIER=2 # default 1 if unset |
| 45 | +# |
| 46 | +# Or pass on the build CLI for a one-off run: |
| 47 | +# |
| 48 | +# ./compile.sh build BOARD=helios64 BRANCH=edge \ |
| 49 | +# ENABLE_EXTENSIONS=kernel-debug-tiers KERNEL_DEBUG_TIER=3 |
| 50 | + |
| 51 | +function extension_prepare_config__kernel_debug_tiers() { |
| 52 | + declare tier="${KERNEL_DEBUG_TIER:-1}" |
| 53 | + case "${tier}" in |
| 54 | + 0 | 1 | 2 | 3) ;; |
| 55 | + *) exit_with_error "${EXTENSION}: KERNEL_DEBUG_TIER must be 0, 1, 2 or 3" "got '${tier}'" ;; |
| 56 | + esac |
| 57 | + |
| 58 | + # Tier 0 is a deliberate no-op — extension declared but kernel side off. |
| 59 | + # Skip the BTF check so a shared config can switch BTF on/off per board. |
| 60 | + if [[ "${tier}" == "0" ]]; then |
| 61 | + display_alert "${EXTENSION}: KERNEL_DEBUG_TIER=0" "extension loaded but kernel-side debug disabled" "info" |
| 62 | + return 0 |
| 63 | + fi |
| 64 | + |
| 65 | + # Hard-fail on conflicting BTF preference. Armbian disables BTF (and all |
| 66 | + # DEBUG_INFO) when KERNEL_BTF=no — that strips the very symbols this |
| 67 | + # extension's tracebacks rely on. |
| 68 | + if [[ "${KERNEL_BTF}" == "no" ]]; then |
| 69 | + exit_with_error \ |
| 70 | + "${EXTENSION}: KERNEL_BTF=no conflicts with KERNEL_DEBUG_TIER=${tier}" \ |
| 71 | + "BTF is required for hung-task tracebacks and KGDB symbol resolution; either set KERNEL_BTF=yes (or leave it unset for Armbian's default) or set KERNEL_DEBUG_TIER=0 to disable this extension's kernel side" |
| 72 | + fi |
| 73 | + |
| 74 | + # Make BTF explicit so Armbian's default-BTF-on path is unambiguous in logs |
| 75 | + # even when the user didn't set KERNEL_BTF themselves. |
| 76 | + declare -g KERNEL_BTF="${KERNEL_BTF:-yes}" |
| 77 | + display_alert "${EXTENSION}: KERNEL_DEBUG_TIER=${tier}" "BTF=${KERNEL_BTF}" "info" |
| 78 | +} |
| 79 | + |
| 80 | +# Tier 1: low-cost diagnostics. printk timestamps and PRINTK_CALLER add a few |
| 81 | +# bytes per line; stack-end check and lockup detectors cost a few cycles per |
| 82 | +# scheduler tick. Cheap enough to ship on a headless server with serial debug. |
| 83 | +function custom_kernel_config__kernel_debug_tier1() { |
| 84 | + if [[ "${KERNEL_DEBUG_TIER:-1}" -lt 1 ]]; then |
| 85 | + return 0 |
| 86 | + fi |
| 87 | + display_alert "${EXTENSION}: tier 1" "printk timestamps + lockup/hung-task detection" "info" |
| 88 | + opts_y+=( |
| 89 | + "PRINTK_TIME" |
| 90 | + "PRINTK_CALLER" |
| 91 | + "DETECT_HUNG_TASK" |
| 92 | + "SOFTLOCKUP_DETECTOR" |
| 93 | + "SCHED_STACK_END_CHECK" |
| 94 | + ) |
| 95 | + # Default is 120s upstream; explicit so the value shows up in .config. |
| 96 | + opts_val["DEFAULT_HUNG_TASK_TIMEOUT"]="120" |
| 97 | +} |
| 98 | + |
| 99 | +# Tier 2: pstore/ramoops — kernel writes its last printk before crash to a |
| 100 | +# reserved memory region; userspace reads /sys/fs/pstore/dmesg-ramoops-0 |
| 101 | +# after the reboot. Catch: needs a memory region reserved either via |
| 102 | +# `/reserved-memory/ramoops { ... }` in the device tree, or via bootargs |
| 103 | +# `ramoops.mem_address=<phys> ramoops.mem_size=0x100000 ramoops.console_size=0x40000`. |
| 104 | +# Without that the modules load but have nowhere to write — silent no-op. |
| 105 | +function custom_kernel_config__kernel_debug_tier2_pstore() { |
| 106 | + if [[ "${KERNEL_DEBUG_TIER:-1}" -lt 2 ]]; then |
| 107 | + return 0 |
| 108 | + fi |
| 109 | + display_alert "${EXTENSION}: tier 2 (pstore/ramoops)" "needs DT or bootarg reservation, otherwise no-op" "info" |
| 110 | + opts_y+=("PSTORE" "PSTORE_CONSOLE" "PSTORE_RAM" "PSTORE_DEFLATE_COMPRESS") |
| 111 | +} |
| 112 | + |
| 113 | +# Tier 3: KGDB/KDB over the serial console. SysRq+g drops into the KDB shell |
| 114 | +# on the same line as the kernel printk — `bt`, `dmesg`, `lsmod`, `dis`, |
| 115 | +# watchpoints. Needs `kgdboc=<console>,<baud>` in bootargs (e.g. |
| 116 | +# `kgdboc=ttyS2,1500000` on Helios64) for the dispatcher to attach to the |
| 117 | +# port; without that the kconfig is enabled but unreachable. |
| 118 | +function custom_kernel_config__kernel_debug_tier3_kgdb() { |
| 119 | + if [[ "${KERNEL_DEBUG_TIER:-1}" -lt 3 ]]; then |
| 120 | + return 0 |
| 121 | + fi |
| 122 | + display_alert "${EXTENSION}: tier 3 (KGDB)" "remember kgdboc=<port>,<baud> in bootargs" "info" |
| 123 | + opts_y+=( |
| 124 | + "KGDB" |
| 125 | + "KGDB_SERIAL_CONSOLE" |
| 126 | + "KGDB_KDB" |
| 127 | + "KGDB_LOW_LEVEL_TRAP" |
| 128 | + ) |
| 129 | + # 0xFF = enable every KDB command (memory peek, register inspect, single |
| 130 | + # step, etc.). On a locked-down production box this can be masked down — |
| 131 | + # bits are documented in Documentation/dev-tools/kgdb.rst. |
| 132 | + # shellcheck disable=SC2034 # opts_val is read by armbian_kernel_config_apply_opts_from_arrays |
| 133 | + opts_val["KDB_DEFAULT_ENABLE"]="0xFF" |
| 134 | +} |
0 commit comments