From fe82e8f7d5bf7c33da39c73af9d34db3afc1eecd Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 11 Apr 2026 11:35:01 +0200 Subject: [PATCH 1/6] grub: stop emitting splash=verbose, always pass quiet splash plymouth.ignore-serial-consoles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extensions/grub.sh and extensions/grub-riscv64.sh both had this conditional in configure_grub(): [[ "$BOOT_LOGO" == "yes" || "$BOOT_LOGO" == "desktop" && "$BUILD_DESKTOP" == "yes" ]] && GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles ..." || GRUB_CMDLINE_LINUX_DEFAULT+=" splash=verbose ..." Two problems with the false branch: 1. 'splash=verbose' is not a valid kernel argument. The kernel logs it explicitly: Unknown kernel command line parameters "splash=verbose", will be passed to user space. It is also not a documented Plymouth flag (Plymouth uses bare 'splash' as a boolean), and Plymouth interprets the literal value 'verbose' as a request to render the verbose/text theme. So a CLI image gets baked with this useless string. 2. The condition only fires the graphical branch when the image is being built as a desktop image AT IMAGE BUILD TIME. Users routinely build a CLI image, then add a desktop later via armbian-config. /etc/default/grub.d/98-armbian.cfg was already baked with the bad cmdline at image-build time, so no amount of desktop-install postinst work could ever make Plymouth render its graphical theme — the user always saw either text/blank because the cmdline still said 'splash=verbose'. Drop the conditional. Always emit quiet splash plymouth.ignore-serial-consoles on UEFI x86 and riscv64. Plymouth handles "no theme installed" and "no DRM" gracefully — these flags are harmless on a CLI install — and they are correct for any subsequent desktop install whether done at image-build time or later. Also drop 'i915.force_probe=*' from the riscv64 cmdline. i915 is the x86 Intel graphics driver and the knob is meaningless on riscv64 hardware. Reproducer: build any uefi-x86 image with BUILD_DESKTOP=no, install gnome via armbian-config later, reboot. /proc/cmdline shows 'splash=verbose', dmesg complains about the unknown arg, and Plymouth renders its text/details theme instead of the Armbian splash on every subsequent boot. --- extensions/grub-riscv64.sh | 12 +++++++++--- extensions/grub.sh | 20 +++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/extensions/grub-riscv64.sh b/extensions/grub-riscv64.sh index b665291eb0ed..4ede924be6fe 100644 --- a/extensions/grub-riscv64.sh +++ b/extensions/grub-riscv64.sh @@ -112,9 +112,15 @@ configure_grub() { [[ -n "$SERIALCON" ]] && GRUB_CMDLINE_LINUX_DEFAULT+=" console=${SERIALCON}" - [[ "$BOOT_LOGO" == "yes" || "$BOOT_LOGO" == "desktop" && "$BUILD_DESKTOP" == "yes" ]] && - GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles i915.force_probe=* loglevel=3" || - GRUB_CMDLINE_LINUX_DEFAULT+=" splash=verbose i915.force_probe=*" + # Kernel cmdline. Always pass the graphical-Plymouth flags + # regardless of whether this image is being built as CLI or + # desktop — same reasoning as in extensions/grub.sh: users + # add a desktop later via armbian-config and we can't + # regenerate grub.cfg from there. Plymouth handles the + # "no theme installed" / "no DRM" cases gracefully. + # (No i915.force_probe here — that's an x86 Intel-graphics + # driver knob and is meaningless on riscv64.) + GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles loglevel=3" # Enable Armbian Wallpaper on GRUB if [[ "${VENDOR}" == Armbian ]]; then diff --git a/extensions/grub.sh b/extensions/grub.sh index 8e835f0e5a5f..b4235070dbc6 100644 --- a/extensions/grub.sh +++ b/extensions/grub.sh @@ -265,9 +265,23 @@ configure_grub() { [[ -n "$SERIALCON" ]] && GRUB_CMDLINE_LINUX_DEFAULT+=" console=${SERIALCON}" - [[ "$BOOT_LOGO" == "yes" || "$BOOT_LOGO" == "desktop" && "$BUILD_DESKTOP" == "yes" ]] && - GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles i915.force_probe=* loglevel=3" || - GRUB_CMDLINE_LINUX_DEFAULT+=" splash=verbose i915.force_probe=*" + # Kernel cmdline. We always pass the graphical-Plymouth flags + # (quiet splash plymouth.ignore-serial-consoles) on UEFI x86, + # regardless of whether this image is being built as CLI or + # desktop. Two reasons: + # 1. Users routinely add a desktop later via armbian-config + # and we don't want that to require regenerating grub.cfg. + # The .cfg is baked once at image-build time and stays + # put across desktop installs. + # 2. Plymouth handles the "no theme installed" / "no DRM" + # cases gracefully — the flags are harmless on a CLI + # install. They are NOT harmless when wrong: the previous + # 'splash=verbose' value was rejected by the kernel + # ("Unknown kernel command line parameters splash=verbose" + # in dmesg) AND interpreted by Plymouth as "render the + # verbose/text theme", so a desktop installed later still + # booted to a black/text screen. + GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles i915.force_probe=* loglevel=3" # Enable Armbian Wallpaper on GRUB if [[ "${VENDOR}" == Armbian ]]; then From e24fc85a118502e6fa389c30f4b0ba004dfb0090 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 11 Apr 2026 11:39:37 +0200 Subject: [PATCH 2/6] bootscripts: stop emitting splash=verbose for non-UEFI boards Companion to the previous commit ('grub: stop emitting splash=verbose'), which fixed the same bug in extensions/grub.sh and extensions/grub-riscv64.sh for UEFI x86/riscv64 images. The exact same broken pattern lived in 24 U-Boot boot script templates and one .tvb board file: if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else setenv consoleargs "splash=verbose ${consoleargs}" # <-- broken fi The else branch fires whenever /boot/armbianEnv.txt does not set bootlogo=true, which is the default on every supported SBC. The resulting kernel cmdline contains 'splash=verbose', which the kernel rejects with Unknown kernel command line parameters "splash=verbose" and Plymouth interprets as "render the verbose/text theme", so even after a user installs a desktop, Plymouth never shows the graphical Armbian splash on these boards. Strip 'splash=verbose' from the else branch in every boot script. The else branch becomes a no-op self-assignment of ${consoleargs} (or empty plymouthargs in the boot-meson-s4t7 variant), which is what bootlogo=false should mean: no splash flag in the cmdline at all. Plymouth then runs in text mode when bootlogo=false and switches to the configured graphical theme when the user flips bootlogo=true in armbianEnv.txt. Affected files (24 boot scripts + 1 board file): config/bootscripts/boot-{cubox,generic.cmd.template,genio, meson-gx,meson-s4t7,meson64,mvebu,nuvoton-ma35d1, odroid-xu4,onecloud,qemu,qrb2210,rk322x,rk3506,rk3576, rk35xx,rockchip,rockchip64,rockchip64-ttyS0,sun50i-next, sun50iw9,sunxi,udoo,xpressreal-t3}.{cmd,template,ini} config/boards/aml-t95z-plus.tvb (SRC_CMDLINE) Two files containing 'splash=verbose' are intentionally left alone: - aml-c400-plus/_packages/bsp-cli/boot/{s905,emmc}_autoscript: these are *binary* compiled U-Boot scripts; the source .cmd is fixed above and the binaries will be regenerated on the next image build. - patch/kernel/archive/rockchip64-6.6/dt/rk3566-core3566.dts: a vendor-image device tree dump, not active boot config. --- config/boards/aml-t95z-plus.tvb | 2 +- config/bootscripts/boot-cubox.cmd | 2 +- config/bootscripts/boot-generic.cmd.template | 2 +- config/bootscripts/boot-genio.cmd | 2 +- config/bootscripts/boot-meson-gx.cmd | 2 +- config/bootscripts/boot-meson-s4t7.cmd | 2 +- config/bootscripts/boot-meson64.cmd | 4 ++-- config/bootscripts/boot-mvebu.cmd | 2 +- config/bootscripts/boot-nuvoton-ma35d1.cmd | 2 +- config/bootscripts/boot-odroid-xu4.ini | 2 +- config/bootscripts/boot-onecloud.cmd | 2 +- config/bootscripts/boot-qemu.cmd | 2 +- config/bootscripts/boot-qrb2210.cmd | 2 +- config/bootscripts/boot-rk322x.cmd | 2 +- config/bootscripts/boot-rk3506.cmd | 2 +- config/bootscripts/boot-rk3576.cmd | 2 +- config/bootscripts/boot-rk35xx.cmd | 2 +- config/bootscripts/boot-rockchip.cmd | 2 +- config/bootscripts/boot-rockchip64-ttyS0.cmd | 2 +- config/bootscripts/boot-rockchip64.cmd | 2 +- config/bootscripts/boot-sun50i-next.cmd | 2 +- config/bootscripts/boot-sun50iw9.cmd | 2 +- config/bootscripts/boot-sunxi.cmd | 2 +- config/bootscripts/boot-udoo.cmd | 2 +- config/bootscripts/boot-xpressreal-t3.cmd | 2 +- 25 files changed, 26 insertions(+), 26 deletions(-) diff --git a/config/boards/aml-t95z-plus.tvb b/config/boards/aml-t95z-plus.tvb index cf60b4f1719b..566ccf138b8e 100644 --- a/config/boards/aml-t95z-plus.tvb +++ b/config/boards/aml-t95z-plus.tvb @@ -18,7 +18,7 @@ BOOTPATCHDIR="v2024.04/board_t95z" # This has a 000.patching_config.yaml inside, # Use extlinux and u-boot-menu extension declare -g SRC_EXTLINUX="yes" -declare -g SRC_CMDLINE="loglevel=7 splash=verbose console=ttyAML0,115200" # Those boards are serial-debug only; 'root=LABEL=armbi_root' ? +declare -g SRC_CMDLINE="loglevel=7 console=ttyAML0,115200" # Those boards are serial-debug only; 'root=LABEL=armbi_root' ? enable_extension "u-boot-menu" # this generates the extlinux.conf # Use the blobs for not-exactly-matching SBCs which are also S912. Note: the VIM2 blobs expect DDR4 and fails with "DDR4 chl: Rank0+1 @ 1008MHz" last message. diff --git a/config/bootscripts/boot-cubox.cmd b/config/bootscripts/boot-cubox.cmd index 14ba2339e79a..75add4f63770 100644 --- a/config/bootscripts/boot-cubox.cmd +++ b/config/bootscripts/boot-cubox.cmd @@ -28,7 +28,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "root=${rootdev} rootfstype=${rootfstype} rootwait ${consoleargs} consoleblank=0 video=mxcfb0:dev=hdmi,${disp_mode},if=RGB24,bpp=32 coherent_pool=2M cma=256M@2G rd.dm=0 rd.luks=0 rd.lvm=0 raid=noautodetect pci=nomsi vt.global_cursor_default=0 loglevel=${verbosity} usb-storage.quirks=${usbstoragequirks} ${extraargs}" diff --git a/config/bootscripts/boot-generic.cmd.template b/config/bootscripts/boot-generic.cmd.template index 6c7dc181a854..76030beb5978 100644 --- a/config/bootscripts/boot-generic.cmd.template +++ b/config/bootscripts/boot-generic.cmd.template @@ -113,7 +113,7 @@ fi if test "${bootlogo}" = "true" ; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi part uuid ${devtype} ${devnum}:${distro_bootpart} l_ubootpart diff --git a/config/bootscripts/boot-genio.cmd b/config/bootscripts/boot-genio.cmd index 0296e3b84a23..b5065d806586 100644 --- a/config/bootscripts/boot-genio.cmd +++ b/config/bootscripts/boot-genio.cmd @@ -84,7 +84,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}" diff --git a/config/bootscripts/boot-meson-gx.cmd b/config/bootscripts/boot-meson-gx.cmd index 67e90e4a0824..2d99b902b6db 100644 --- a/config/bootscripts/boot-meson-gx.cmd +++ b/config/bootscripts/boot-meson-gx.cmd @@ -25,7 +25,7 @@ if test "${console}" = "serial"; then setenv consoleargs "console=ttyAML0,115200 if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi if test "${disable_vu7}" = "false"; then setenv usbhidquirks "usbhid.quirks=0x0eef:0x0005:0x0004"; fi diff --git a/config/bootscripts/boot-meson-s4t7.cmd b/config/bootscripts/boot-meson-s4t7.cmd index b5d64394d29c..e0141ec415e7 100644 --- a/config/bootscripts/boot-meson-s4t7.cmd +++ b/config/bootscripts/boot-meson-s4t7.cmd @@ -33,7 +33,7 @@ fi if test "${bootlogo}" = "true"; then setenv plymouthargs "splash plymouth.ignore-serial-consoles" else - setenv plymouthargs "splash=verbose" + setenv plymouthargs "" fi setenv bootargs "${bootargs} root=${rootdev} rootfstype=${rootfstype} rw fsck.repair=yes rootwait ${consoleargs} partition_type=generic loglevel=${verbosity} ${plymouthargs} ${extraargs} ${extraboardargs}" diff --git a/config/bootscripts/boot-meson64.cmd b/config/bootscripts/boot-meson64.cmd index 7f6066eebba4..fe4a9559c54d 100644 --- a/config/bootscripts/boot-meson64.cmd +++ b/config/bootscripts/boot-meson64.cmd @@ -114,7 +114,7 @@ if test -e ${devtype} ${devnum} ${prefix}zImage; then if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 coherent_pool=2M loglevel=${verbosity} ${amlogic} no_console_suspend fsck.repair=yes net.ifnames=0 elevator=noop hdmimode=${hdmimode} cvbsmode=576cvbs max_freq_a55=${max_freq_a55} maxcpus=${maxcpus} voutmode=${voutmode} ${cmode} disablehpd=${disablehpd} cvbscable=${cvbscable} overscan=${overscan} ${hid_quirks} monitor_onoff=${monitor_onoff} ${cec_enable} sdrmode=${sdrmode}" @@ -135,7 +135,7 @@ else if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi if test "${disable_vu7}" = "false"; then setenv usbhidquirks "usbhid.quirks=0x0eef:0x0005:0x0004"; fi diff --git a/config/bootscripts/boot-mvebu.cmd b/config/bootscripts/boot-mvebu.cmd index c7661b353910..8e40962d4d5a 100644 --- a/config/bootscripts/boot-mvebu.cmd +++ b/config/bootscripts/boot-mvebu.cmd @@ -101,7 +101,7 @@ fi if test "${bootlogo}" = "true" ; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi part uuid ${devtype} ${devnum}:${distro_bootpart} l_ubootpart diff --git a/config/bootscripts/boot-nuvoton-ma35d1.cmd b/config/bootscripts/boot-nuvoton-ma35d1.cmd index 7fe7d29580bb..e066f9b78525 100644 --- a/config/bootscripts/boot-nuvoton-ma35d1.cmd +++ b/config/bootscripts/boot-nuvoton-ma35d1.cmd @@ -75,7 +75,7 @@ fi if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # MA35D1 specific: limit kernel memory if needed (for OP-TEE reserved memory) diff --git a/config/bootscripts/boot-odroid-xu4.ini b/config/bootscripts/boot-odroid-xu4.ini index 42a95c35d8d3..3255e63b6a7d 100644 --- a/config/bootscripts/boot-odroid-xu4.ini +++ b/config/bootscripts/boot-odroid-xu4.ini @@ -27,7 +27,7 @@ if test "${console}" = "serial" || test "${console}" = "both"; then setenv conso if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootrootfs "${consoleargs} consoleblank=0 loglevel=${verbosity} root=${rootdev} rootfstype=${rootfstype} rootwait rw" diff --git a/config/bootscripts/boot-onecloud.cmd b/config/bootscripts/boot-onecloud.cmd index b8e44338ef56..422c9772d8bb 100644 --- a/config/bootscripts/boot-onecloud.cmd +++ b/config/bootscripts/boot-onecloud.cmd @@ -46,7 +46,7 @@ if test -n "${consoleargs}"; test $? != 0; then if test "${bootlogo}" = "true"; then setenv consoleargs "${consoleargs} splash plymouth.ignore-serial-consoles" else - setenv consoleargs "${consoleargs} splash=verbose" + setenv consoleargs "${consoleargs}" fi fi diff --git a/config/bootscripts/boot-qemu.cmd b/config/bootscripts/boot-qemu.cmd index 825f01b76d0c..a011258ce9b9 100644 --- a/config/bootscripts/boot-qemu.cmd +++ b/config/bootscripts/boot-qemu.cmd @@ -30,7 +30,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "root=${rootdev} rw rootdelay=5 rootwait rootfstype=${rootfstype} ${consoleargs} loglevel=7 apparmor=0 nousb selinux=0 ${extraargs} ${extraboardargs}" diff --git a/config/bootscripts/boot-qrb2210.cmd b/config/bootscripts/boot-qrb2210.cmd index cae718e90dd0..140c6b12ebed 100644 --- a/config/bootscripts/boot-qrb2210.cmd +++ b/config/bootscripts/boot-qrb2210.cmd @@ -36,7 +36,7 @@ if test "${console}" = "serial" || test "${console}" = "both"; then setenv conso if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of partition the boot script was loaded from diff --git a/config/bootscripts/boot-rk322x.cmd b/config/bootscripts/boot-rk322x.cmd index 6a17f144ad4b..688a950189df 100644 --- a/config/bootscripts/boot-rk322x.cmd +++ b/config/bootscripts/boot-rk322x.cmd @@ -37,7 +37,7 @@ if test "${devtype}" = "mmc"; then part uuid mmc ${devnum}:1 partuuid; fi if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "earlyprintk root=${rootdev} console=ttyS2,115200n8 console=tty1 rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}" diff --git a/config/bootscripts/boot-rk3506.cmd b/config/bootscripts/boot-rk3506.cmd index d3bdbfaff3fc..622b426d7b43 100644 --- a/config/bootscripts/boot-rk3506.cmd +++ b/config/bootscripts/boot-rk3506.cmd @@ -32,7 +32,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from diff --git a/config/bootscripts/boot-rk3576.cmd b/config/bootscripts/boot-rk3576.cmd index cf1157a0e173..ed31a5361722 100644 --- a/config/bootscripts/boot-rk3576.cmd +++ b/config/bootscripts/boot-rk3576.cmd @@ -31,7 +31,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from diff --git a/config/bootscripts/boot-rk35xx.cmd b/config/bootscripts/boot-rk35xx.cmd index d65181e815b7..569d7510eaaf 100644 --- a/config/bootscripts/boot-rk35xx.cmd +++ b/config/bootscripts/boot-rk35xx.cmd @@ -31,7 +31,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from diff --git a/config/bootscripts/boot-rockchip.cmd b/config/bootscripts/boot-rockchip.cmd index 33c5b456bb1e..a828198de303 100644 --- a/config/bootscripts/boot-rockchip.cmd +++ b/config/bootscripts/boot-rockchip.cmd @@ -32,7 +32,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from diff --git a/config/bootscripts/boot-rockchip64-ttyS0.cmd b/config/bootscripts/boot-rockchip64-ttyS0.cmd index 905f56d5572b..01f2c54d2251 100644 --- a/config/bootscripts/boot-rockchip64-ttyS0.cmd +++ b/config/bootscripts/boot-rockchip64-ttyS0.cmd @@ -31,7 +31,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from diff --git a/config/bootscripts/boot-rockchip64.cmd b/config/bootscripts/boot-rockchip64.cmd index c215824a31af..824c0e940eab 100644 --- a/config/bootscripts/boot-rockchip64.cmd +++ b/config/bootscripts/boot-rockchip64.cmd @@ -31,7 +31,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from diff --git a/config/bootscripts/boot-sun50i-next.cmd b/config/bootscripts/boot-sun50i-next.cmd index 0bd8b1b425c2..b3d53ac42c3f 100644 --- a/config/bootscripts/boot-sun50i-next.cmd +++ b/config/bootscripts/boot-sun50i-next.cmd @@ -72,7 +72,7 @@ if test "${console}" = "serial"; then setenv consoleargs "console=ttyS0,115200"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC it was loaded from diff --git a/config/bootscripts/boot-sun50iw9.cmd b/config/bootscripts/boot-sun50iw9.cmd index c55a656f940c..06f5e0f0b8c4 100644 --- a/config/bootscripts/boot-sun50iw9.cmd +++ b/config/bootscripts/boot-sun50iw9.cmd @@ -29,7 +29,7 @@ if test "${console}" = "serial"; then setenv consoleargs "console=ttyS0,115200"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC it was loaded from diff --git a/config/bootscripts/boot-sunxi.cmd b/config/bootscripts/boot-sunxi.cmd index 86f5f7052979..dfbae52c4c17 100644 --- a/config/bootscripts/boot-sunxi.cmd +++ b/config/bootscripts/boot-sunxi.cmd @@ -83,7 +83,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} hdmi.audio=EDID:0 disp.screen0_output_mode=${disp_mode} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} ubootsource=${devtype} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}" diff --git a/config/bootscripts/boot-udoo.cmd b/config/bootscripts/boot-udoo.cmd index f8e1b2758dd3..65d1e7990483 100644 --- a/config/bootscripts/boot-udoo.cmd +++ b/config/bootscripts/boot-udoo.cmd @@ -28,7 +28,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi setenv bootargs "root=${rootdev} rootfstype=${rootfstype} rootwait ${consoleargs} video=mxcfb0:dev=hdmi,${disp_mode},if=RGB24,bpp=32 rd.dm=0 rd.luks=0 rd.lvm=0 raid=noautodetect pci=nomsi ahci_imx.hotplug=1 vt.global_cursor_default=0 loglevel=${verbosity} usb-storage.quirks=${usbstoragequirks} ${extraargs}" diff --git a/config/bootscripts/boot-xpressreal-t3.cmd b/config/bootscripts/boot-xpressreal-t3.cmd index 39e9ea438daf..79cd16f5dbe6 100644 --- a/config/bootscripts/boot-xpressreal-t3.cmd +++ b/config/bootscripts/boot-xpressreal-t3.cmd @@ -28,7 +28,7 @@ if test "${earlycon}" = "on"; then setenv consoleargs "earlycon=uart8250,mmio32, if test "${bootlogo}" = "true"; then setenv consoleargs "splash plymouth.ignore-serial-consoles ${consoleargs}" else - setenv consoleargs "splash=verbose ${consoleargs}" + setenv consoleargs "${consoleargs}" fi # get PARTUUID of first partition on SD/eMMC the boot script was loaded from From 30c43a30ec72c79641e3392c72ff4dad34f0a418 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 11 Apr 2026 11:52:49 +0200 Subject: [PATCH 3/6] grub: drop 'quiet' / 'loglevel=3' so kernel boot messages stay visible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the previous two commits in this PR. They left the graphical Plymouth flags in place but also kept 'quiet' and 'loglevel=3', which suppress kernel boot messages entirely. The result is a clean boot splash but a user complaint: "I don't see anything during boot anymore." Drop both. Plymouth still renders the splash on top of the kernel boot messages — the messages just remain visible underneath so you can see what the system is doing. The user can still press Esc during boot to drop the splash and read the messages directly, which is the upstream-standard interaction. Applies to extensions/grub.sh (UEFI x86) and extensions/grub-riscv64.sh (UEFI riscv64). U-Boot SBC scripts are unaffected — they already have no 'quiet' or 'loglevel=3' in the bootlogo=true branch and the bootlogo=false branch (the default) is now empty after the previous commit, so SBC users already see kernel messages. --- extensions/grub-riscv64.sh | 6 ++++-- extensions/grub.sh | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/extensions/grub-riscv64.sh b/extensions/grub-riscv64.sh index 4ede924be6fe..9d8f60c9e3be 100644 --- a/extensions/grub-riscv64.sh +++ b/extensions/grub-riscv64.sh @@ -119,8 +119,10 @@ configure_grub() { # regenerate grub.cfg from there. Plymouth handles the # "no theme installed" / "no DRM" cases gracefully. # (No i915.force_probe here — that's an x86 Intel-graphics - # driver knob and is meaningless on riscv64.) - GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles loglevel=3" + # driver knob and is meaningless on riscv64. No 'quiet' / + # 'loglevel=3' either: kernel boot messages stay visible + # underneath the splash so users can see what's happening.) + GRUB_CMDLINE_LINUX_DEFAULT+=" splash plymouth.ignore-serial-consoles" # Enable Armbian Wallpaper on GRUB if [[ "${VENDOR}" == Armbian ]]; then diff --git a/extensions/grub.sh b/extensions/grub.sh index b4235070dbc6..2e2dd9802d5c 100644 --- a/extensions/grub.sh +++ b/extensions/grub.sh @@ -266,7 +266,7 @@ configure_grub() { GRUB_CMDLINE_LINUX_DEFAULT+=" console=${SERIALCON}" # Kernel cmdline. We always pass the graphical-Plymouth flags - # (quiet splash plymouth.ignore-serial-consoles) on UEFI x86, + # (splash plymouth.ignore-serial-consoles) on UEFI x86, # regardless of whether this image is being built as CLI or # desktop. Two reasons: # 1. Users routinely add a desktop later via armbian-config @@ -281,7 +281,13 @@ configure_grub() { # in dmesg) AND interpreted by Plymouth as "render the # verbose/text theme", so a desktop installed later still # booted to a black/text screen. - GRUB_CMDLINE_LINUX_DEFAULT+=" quiet splash plymouth.ignore-serial-consoles i915.force_probe=* loglevel=3" + # + # Deliberately NO 'quiet' and NO 'loglevel=3' here. Plymouth + # still draws the splash on top of the kernel boot messages, + # but the messages remain visible underneath so users can see + # what their system is doing. Press Esc during boot to drop + # the splash and read the messages directly. + GRUB_CMDLINE_LINUX_DEFAULT+=" splash plymouth.ignore-serial-consoles i915.force_probe=*" # Enable Armbian Wallpaper on GRUB if [[ "${VENDOR}" == Armbian ]]; then From 882e84734482a5b65cc00171f82fa481d7a3f40b Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 11 Apr 2026 12:15:26 +0200 Subject: [PATCH 4/6] grub: disable Ubuntu's vt.handoff=7 auto-injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ubuntu ships a /etc/grub.d/10_linux snippet that, whenever 'splash' appears in GRUB_CMDLINE_LINUX_DEFAULT, automatically appends 'vt.handoff=7' to the generated kernel cmdline: if [ x$GRUB_DISABLE_VT_HANDOFF != xtrue ]; then case "$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT" in *splash*) vt_handoff=vt.handoff=7 ;; esac fi vt.handoff=7 tells the kernel to hand the framebuffer console over to whatever userspace eventually claims VT7 — i.e. the X server started by the display manager. On a stock Ubuntu desktop install that's fine: gdm starts, claims VT7, the screen is graphical from boot to login. On Armbian: - CLI installs have no display manager, nothing ever claims VT7, and after Plymouth quits the kernel never restores the framebuffer to fbcon. Result: a blank local console even though getty@tty1 is alive and accepting writes — direct 'echo TEST > /dev/tty1' silently succeeds but renders no pixels. - Desktop installs that the user later removes via armbian-config end up in the same state: gdm/lightdm gets purged, VT7 becomes orphaned, and even after we 'systemctl set-default multi-user.target' and isolate, the framebuffer stays blank. (This is what motivated finding this bug — the symptom on a virtio-gpu QEMU test box was "screen never lights up after desktop remove".) The fix is GRUB_DISABLE_VT_HANDOFF=true, which is the upstream-supported escape hatch that the same 10_linux snippet honours: it skips the vt_handoff injection entirely. The framebuffer console then stays bound to fbcon across the entire boot, regardless of whether a DM is installed, and tty1 remains visible and writable for the entire userspace lifetime. Apply to both extensions/grub.sh (UEFI x86) and extensions/grub-riscv64.sh. Together with the previous commits in this PR, the UEFI cmdline on a freshly built Armbian image is now: splash plymouth.ignore-serial-consoles i915.force_probe=* (no quiet, no loglevel=3, no vt.handoff=7) — Plymouth shows the Armbian theme on top of visible kernel boot messages, and the local console keeps working through every install/remove cycle. --- extensions/grub-riscv64.sh | 1 + extensions/grub.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/extensions/grub-riscv64.sh b/extensions/grub-riscv64.sh index 9d8f60c9e3be..2b2ae92a1d43 100644 --- a/extensions/grub-riscv64.sh +++ b/extensions/grub-riscv64.sh @@ -146,6 +146,7 @@ configure_grub() { GRUB_GFXPAYLOAD=keep GRUB_DISABLE_UUID=false # Be explicit about wanting UUID GRUB_DISABLE_LINUX_UUID=false # Be explicit about wanting UUID + GRUB_DISABLE_VT_HANDOFF=true # See extensions/grub.sh — Ubuntu's 10_linux generator auto-appends 'vt.handoff=7' whenever 'splash' is in the cmdline, which leaves the framebuffer console blank on CLI installs and after desktop uninstalls. grubCfgFrag if [[ "a${UEFI_GRUB_DISABLE_OS_PROBER}" != "a" ]]; then diff --git a/extensions/grub.sh b/extensions/grub.sh index 2e2dd9802d5c..00900c29f3a3 100644 --- a/extensions/grub.sh +++ b/extensions/grub.sh @@ -313,6 +313,7 @@ configure_grub() { GRUB_GFXPAYLOAD=keep GRUB_DISABLE_UUID=false # Be explicit about wanting UUID GRUB_DISABLE_LINUX_UUID=false # Be explicit about wanting UUID + GRUB_DISABLE_VT_HANDOFF=true # Ubuntu's 10_linux grub generator auto-appends 'vt.handoff=7' whenever 'splash' is in the cmdline. That arg tells the kernel to hand the framebuffer to whatever userspace owns VT7 (the X server / display manager). On a CLI install, or after the user uninstalls the desktop, nothing claims VT7, fbcon never gets the framebuffer back, and the local console stays blank even though getty@tty1 is happily running. Disable the auto-injection so the framebuffer console keeps working regardless of whether a DM is installed. grubCfgFrag if [[ "a${UEFI_GRUB_DISABLE_OS_PROBER}" != "a" ]]; then From f82463b72d99eacd457e01a2516803ce20cc6093 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 11 Apr 2026 12:19:11 +0200 Subject: [PATCH 5/6] =?UTF-8?q?grub:=20GRUB=5FGFXPAYLOAD=5FLINUX=3Dtext=20?= =?UTF-8?q?=E2=80=94=20actually=20disable=20vt.handoff=20injection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit set GRUB_DISABLE_VT_HANDOFF=true expecting Ubuntu's grub2 to honour it. It does not — that variable is upstream grub2 only. Ubuntu carries a build-time patch (debian/patches/vt-handoff.patch) that hardwires the behaviour behind ./configure --enable-vt-handoff and gives no /etc/default/grub knob to disable it. Verified against ubuntu/noble grub2 2.12-1ubuntu7 source. The runtime gate that DOES exist is in the gfxmode function emitted into /boot/grub/grub.cfg: if [ "${1}" = "keep" ]; then set vt_handoff=vt.handoff=7 else set vt_handoff= fi So vt.handoff=7 only expands when the gfxpayload arg is exactly 'keep'. Setting GRUB_GFXPAYLOAD_LINUX=text makes the runtime check fail and the framebuffer console stays bound to fbcon for the entire userspace lifetime. While I was here I also discovered the previous line 'GRUB_GFXPAYLOAD=keep' was silently ignored — the correct variable name is GRUB_GFXPAYLOAD_LINUX, not GRUB_GFXPAYLOAD. That bug has been in extensions/grub.sh and grub-riscv64.sh since the file was written. The previous typo did no harm because grub2's default for the missing variable happens to be 'keep' — but it does mean we never had the gfxpayload knob under our control, which is part of why this bug took so long to track down. Drop the dead GRUB_DISABLE_VT_HANDOFF=true line that the previous commit added, and replace the old GRUB_GFXPAYLOAD=keep typo with GRUB_GFXPAYLOAD_LINUX=text. After this, /proc/cmdline on a freshly built UEFI image will be: splash plymouth.ignore-serial-consoles i915.force_probe=* with no vt.handoff=7, regardless of whether a desktop is installed. The framebuffer console remains visible across install/remove cycles, Plymouth still draws its splash on top. References: https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/grub2/2.12-1ubuntu7/grub2_2.12-1ubuntu7.debian.tar.xz debian/patches/vt-handoff.patch in that source tree --- extensions/grub-riscv64.sh | 3 +-- extensions/grub.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/extensions/grub-riscv64.sh b/extensions/grub-riscv64.sh index 2b2ae92a1d43..161c2cd3dc74 100644 --- a/extensions/grub-riscv64.sh +++ b/extensions/grub-riscv64.sh @@ -143,10 +143,9 @@ configure_grub() { GRUB_DISTRIBUTOR="${UEFI_GRUB_DISTRO_NAME}" # On GRUB menu will show up as "Armbian GNU/Linux" (will show up in some UEFI BIOS boot menu (F8?) as "armbian", not on others) GRUB_DISABLE_OS_PROBER=false # Have to be explicit about enabling os-prober GRUB_GFXMODE=1024x768 - GRUB_GFXPAYLOAD=keep + GRUB_GFXPAYLOAD_LINUX=text # See extensions/grub.sh — correct var name is GRUB_GFXPAYLOAD_LINUX, not GRUB_GFXPAYLOAD, and 'text' disables Ubuntu's vt.handoff=7 injection. GRUB_DISABLE_UUID=false # Be explicit about wanting UUID GRUB_DISABLE_LINUX_UUID=false # Be explicit about wanting UUID - GRUB_DISABLE_VT_HANDOFF=true # See extensions/grub.sh — Ubuntu's 10_linux generator auto-appends 'vt.handoff=7' whenever 'splash' is in the cmdline, which leaves the framebuffer console blank on CLI installs and after desktop uninstalls. grubCfgFrag if [[ "a${UEFI_GRUB_DISABLE_OS_PROBER}" != "a" ]]; then diff --git a/extensions/grub.sh b/extensions/grub.sh index 00900c29f3a3..84061ae39d9b 100644 --- a/extensions/grub.sh +++ b/extensions/grub.sh @@ -310,10 +310,9 @@ configure_grub() { GRUB_DISABLE_SUBMENU=y # Do not put all kernel options into a submenu, instead, list them all on the main menu. GRUB_DISABLE_OS_PROBER=false # Have to be explicit about enabling os-prober GRUB_FONT="/usr/share/grub/unicode.pf2" # Be explicit about the font to use so Ubuntu does not freak out and mess gfxterm - GRUB_GFXPAYLOAD=keep + GRUB_GFXPAYLOAD_LINUX=text # Note the correct var name is GRUB_GFXPAYLOAD_LINUX, not GRUB_GFXPAYLOAD (the latter is silently ignored). The 'text' value disables Ubuntu's vt.handoff=7 injection: Ubuntu's grub2 10_linux only expands 'vt.handoff=7' inside grub.cfg's gfxmode function when the gfxpayload arg is exactly 'keep'. Setting it to 'text' makes the runtime check fail and the framebuffer console stays bound to fbcon for the entire userspace lifetime — which is what we want, otherwise after Plymouth quits on a CLI install (or after the user uninstalls the desktop), the kernel hands the framebuffer to VT7 waiting for an X server, nothing ever claims it, and the local console goes black even though getty@tty1 is running. GRUB_DISABLE_UUID=false # Be explicit about wanting UUID GRUB_DISABLE_LINUX_UUID=false # Be explicit about wanting UUID - GRUB_DISABLE_VT_HANDOFF=true # Ubuntu's 10_linux grub generator auto-appends 'vt.handoff=7' whenever 'splash' is in the cmdline. That arg tells the kernel to hand the framebuffer to whatever userspace owns VT7 (the X server / display manager). On a CLI install, or after the user uninstalls the desktop, nothing claims VT7, fbcon never gets the framebuffer back, and the local console stays blank even though getty@tty1 is happily running. Disable the auto-injection so the framebuffer console keeps working regardless of whether a DM is installed. grubCfgFrag if [[ "a${UEFI_GRUB_DISABLE_OS_PROBER}" != "a" ]]; then From 3c133b4dc32783b8e47829801ec8b7a032eb7057 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 11 Apr 2026 12:41:57 +0200 Subject: [PATCH 6/6] bsp: clear screen before agetty banner on tty1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The getty@tty1.service drop-in at packages/bsp/common/lib/systemd/system/getty@tty1.service.d/10-noclear.conf only contained 'TTYVTDisallocate=no'. It did NOT actually drop the upstream '--noclear' from agetty's ExecStart, so the inherited template ran: /sbin/agetty -o '-p -- \\u' --noclear - $TERM Result on every Armbian boot: kernel boot messages and initramfs output remain stacked above the login prompt on tty1, even though Plymouth ran and we expected a clean screen. Override ExecStart in the same drop-in to drop --noclear, so agetty wipes the screen with \\033[H\\033[2J before painting its issue/login banner. After this, tty1 shows just: Armbian X.Y.Z release tty1 hostname login: The empty 'ExecStart=' line is required by systemd to clear the inherited template value before we set the new one — that's the documented pattern for ExecStart overrides in service drop-ins. The drop-in filename ("10-noclear.conf") is now a slight misnomer since the file does the opposite of "no clear", but renaming the file would break package upgrades (the old file would stick around alongside the new one). The original name was always referring to TTYVTDisallocate, not agetty's --noclear, so the filename was never accurate to begin with. Add a comment in the file explaining what it actually does. --- .../system/getty@tty1.service.d/10-noclear.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/bsp/common/lib/systemd/system/getty@tty1.service.d/10-noclear.conf b/packages/bsp/common/lib/systemd/system/getty@tty1.service.d/10-noclear.conf index 52671c758d83..6224c8583cbe 100644 --- a/packages/bsp/common/lib/systemd/system/getty@tty1.service.d/10-noclear.conf +++ b/packages/bsp/common/lib/systemd/system/getty@tty1.service.d/10-noclear.conf @@ -1,2 +1,12 @@ [Service] TTYVTDisallocate=no +# Drop the upstream --noclear from ExecStart so agetty wipes the +# screen before painting its issue/login banner. Without this +# override, kernel boot messages stay visible above the login +# prompt on tty1 — Plymouth ran (so we expected a clean screen) +# but agetty's default --noclear preserves whatever was on the +# console at handoff time. The empty ExecStart= line is required +# by systemd to clear the inherited value before we set the new +# one. +ExecStart= +ExecStart=-/sbin/agetty -o '-p -- \\u' - $TERM