Skip to content

Commit cdf2521

Browse files
committed
feat(extensions): add sysrq-serial-trigger
Enable Magic SysRq through the serial console for headless ARM boards (Helios64, etc.). Mainline ships MAGIC_SYSRQ_SERIAL_SEQUENCE empty, disabling BREAK-triggered SysRq; this extension fills it with a deliberate sequence after BREAK so the operator on the serial console can sync/remount-RO/reboot from any kernel state where interrupts flow. Also overrides distro kernel.sysrq=176 to 1 (full command set) and extends u-boot autoboot delay so the prompt is actually catchable. Pair with the companion `kernel-debug-tiers` extension to make the tracebacks reachable via SysRq actually informative (BTF, hung-task, KGDB symbol resolution). Signed-off-by: Igor Velkov <325961+iav@users.noreply.github.com>
1 parent 7d91e37 commit cdf2521

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

extensions/sysrq-serial-trigger.sh

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 Magic SysRq through the serial console for headless boards. Without
8+
# this, kernel hangs (NFS deadlocks, ATA error-handler corner cases on Helios64,
9+
# etc.) leave you with no way out except the hardware-watchdog timeout or a
10+
# physical reset. With it, an operator on the serial console can sync,
11+
# remount-RO and reboot from any kernel state where interrupts still flow.
12+
#
13+
# This extension only enables the operator-console *surface*. To make the
14+
# tracebacks and KGDB sessions reachable through that surface actually
15+
# informative (rather than streams of hex addresses), pair with the
16+
# `kernel-debug-tiers` extension which adds BTF, hung-task detection,
17+
# pstore/ramoops, and KGDB symbol resolution.
18+
#
19+
# Mainline kernels ship CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE empty by default,
20+
# which disables BREAK-triggered SysRq entirely. From lib/Kconfig.debug:
21+
# "If unsure, leave an empty string and the option will not be enabled."
22+
# (https://elixir.bootlin.com/linux/latest/source/lib/Kconfig.debug#L698)
23+
# The serial driver requires a non-empty sequence after BREAK so that random
24+
# line noise cannot trigger a reboot. This extension fills that in with a
25+
# deliberate sequence that is vanishingly unlikely in normal serial traffic.
26+
#
27+
# Usage — add to your userpatches config (e.g. `userpatches/config-my.conf`):
28+
#
29+
# enable_extension "sysrq-serial-trigger"
30+
#
31+
# Or pass on the build CLI for a one-off run:
32+
#
33+
# ./compile.sh build BOARD=helios64 BRANCH=edge \
34+
# ENABLE_EXTENSIONS=sysrq-serial-trigger
35+
#
36+
# Operator workflow (picocom default escape Ctrl-A):
37+
# Ctrl-A \ (send BREAK)
38+
# sysrq (the magic sequence, default value of SYSRQ_SERIAL_SEQUENCE)
39+
# b (or any other SysRq command — see Documentation/admin-guide/sysrq.rst)
40+
#
41+
# To experiment with a different sequence, override SYSRQ_SERIAL_SEQUENCE
42+
# from the build CLI; pick something that does not occur in normal output of
43+
# anything you care about (logging, dialog, etc.).
44+
45+
function custom_kernel_config__sysrq_serial_trigger() {
46+
# Default "sysrq": printable, easy to type, and passes through Bash/sed without
47+
# escaping. Kernel matches bytes verbatim against serial input after BREAK.
48+
# `:-` substitutes only when SYSRQ_SERIAL_SEQUENCE is unset; an explicit
49+
# empty value would otherwise silently write MAGIC_SYSRQ_SERIAL_SEQUENCE=""
50+
# which lib/Kconfig.debug documents as disabling BREAK-triggered SysRq
51+
# entirely. Refuse empty loudly so the operator fixes the config rather
52+
# than getting a silently-disabled SysRq path.
53+
declare seq="${SYSRQ_SERIAL_SEQUENCE:-sysrq}"
54+
if [[ -z "${seq}" ]]; then
55+
exit_with_error "${EXTENSION}: SYSRQ_SERIAL_SEQUENCE must not be empty" \
56+
"empty would write MAGIC_SYSRQ_SERIAL_SEQUENCE=\"\" and disable BREAK-triggered SysRq; pick any non-empty sequence (default: SYSRQ_SERIAL_SEQUENCE=sysrq)"
57+
fi
58+
display_alert "${EXTENSION}: enabling SysRq over serial" "sequence='${seq}' after BREAK" "info"
59+
opts_y+=("MAGIC_SYSRQ" "MAGIC_SYSRQ_SERIAL")
60+
# 1 = SYSRQ_ENABLE_ALL (kernel special-case, not bit 1); enables all SysRq
61+
# commands regardless of the runtime kernel.sysrq sysctl value at boot.
62+
# shellcheck disable=SC2034 # opts_val is read by armbian_kernel_config_apply_opts_from_arrays
63+
opts_val["MAGIC_SYSRQ_DEFAULT_ENABLE"]="1"
64+
# Armbian has no opts_str[] equivalent for string kconfig options — opts_val[]
65+
# dispatches via --set-val which truncates strings to "" (it is designed for
66+
# numeric/hex values only). Use kernel_config_set_string directly; it calls
67+
# --set-str and registers the value in kernel_config_modifying_hashes.
68+
# Two-phase guard: kernel_config_set_string requires an unpacked source tree;
69+
# in the hash-only phase (.config absent) add to hashes manually instead.
70+
if [[ -f .config ]]; then
71+
kernel_config_set_string "MAGIC_SYSRQ_SERIAL_SEQUENCE" "${seq}"
72+
else
73+
kernel_config_modifying_hashes+=("MAGIC_SYSRQ_SERIAL_SEQUENCE=\"${seq}\"")
74+
fi
75+
}
76+
77+
# U-Boot autoboot timing — give the operator a real chance to interrupt.
78+
# Default Armbian u-boot prints "Hit any key to stop autoboot: 0" — i.e.
79+
# a 1-second window that's effectively unusable. BOOTDELAY=5 gives a 5-second
80+
# countdown; AUTOBOOT_NEVER_TIMEOUT means a single keypress freezes the
81+
# countdown entirely so the operator can take their time after that. The
82+
# trade-off is +5s on every cold boot — acceptable for headless servers,
83+
# probably annoying for kiosks. Comment out this whole function to revert.
84+
function post_config_uboot_target__sysrq_serial_uboot_autoboot() {
85+
display_alert "${EXTENSION}: u-boot BOOTDELAY=5 + AUTOBOOT_NEVER_TIMEOUT" "give the operator 5s to grab the prompt; pause forever after first keypress" "info"
86+
# `scripts/config` is u-boot's own kbuild helper (same script as Linux uses).
87+
# It handles "is not set" → enabled and value-overrides correctly without
88+
# depending on which form the line currently takes.
89+
run_host_command_logged ./scripts/config --set-val CONFIG_BOOTDELAY 5
90+
run_host_command_logged ./scripts/config --enable CONFIG_AUTOBOOT_NEVER_TIMEOUT
91+
}
92+
93+
# Distro defaults (Debian/Ubuntu /usr/lib/sysctl.d/55-magic-sysrq.conf) cap
94+
# kernel.sysrq at 176 — enough for `s`/`u`/`b` but not `t`/`m`/`f`/`w` which
95+
# are the actually useful debug commands when a kernel is misbehaving. A
96+
# headless box with serial-console SysRq is exactly the case where the full
97+
# set should be available, so override to 1 (all functions). The 60- prefix
98+
# beats the 55- distro file in sysctl.d lexical order.
99+
function post_customize_image__sysrq_serial_trigger_userland() {
100+
declare conf="${SDCARD}/etc/sysctl.d/60-armbian-sysrq.conf"
101+
display_alert "${EXTENSION}: enabling full kernel.sysrq" "${conf##*/} (kernel.sysrq=1)" "info"
102+
cat > "${conf}" <<- 'SYSCTL_CONF'
103+
# Installed by the Armbian sysrq-serial-trigger extension.
104+
# Overrides /usr/lib/sysctl.d/55-magic-sysrq.conf (default 176)
105+
# to enable the full SysRq command set — needed for serial-console
106+
# debugging (process dumps, blocked-task list, OOM kill, etc.).
107+
kernel.sysrq = 1
108+
SYSCTL_CONF
109+
}

0 commit comments

Comments
 (0)