Embedded Linux development
 help / color / mirror / Atom feed
* Re: Reminder of Boot-Time SIG meeting (June 30, 2026)
From: Roberto A. Foglietta @ 2026-06-18 19:08 UTC (permalink / raw)
  To: Bird, Tim; +Cc: Embedded Linux mailing list
In-Reply-To: <MW5PR13MB563294337A346C20F42DFC7DFDE32@MW5PR13MB5632.namprd13.prod.outlook.com>

[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]

On Thu, 18 Jun 2026 at 20:18, Bird, Tim <Tim.Bird@sony.com> wrote:
>
> Hey Linux Boot-Time SIG interested parties (and other interested Linux kernel developers),
>

Just for the record, I read

 items in flight:
      - early_times - fix zero-valued printks in kernel "blind spot"
          - original patch thread here:
            - https://lore.kernel.org/all/39b09edb-8998-4ebd-a564-7d594434a981@bird.org/
          - see V4 submission here:
             - https://lore.kernel.org/linux-embedded/20260410203741.997410-1-tim.bird@sony.com/
          - patch is saved here:
https://github.com/tbird20d/boot-time-wizard/blob/main/patches/early-times.patch
          - next action: make v5 version of patch?

However, a V6 is already available since 2026-04-16, now also in more
LKML format alike

- https://raw.githubusercontent.com/robang74/uchaosys/b88e3c6c493108ca9dad8474ec41ec95c74db01c/cnfg/printk-early-boot-timestamps-hack-v6.patch

Also in attachment for reference but wget (or lynx) is your friend
since the link points to the raw patch.

Best regards,
-- 
Roberto A. Foglietta
+49.176.274.75.661
+39.349.33.30.697

[-- Attachment #2: 0001-printk-fix-zero-valued-printk-timestamps-in-early-bo.patch --]
[-- Type: text/x-patch, Size: 8382 bytes --]

From 68b9eef9f37f991ea33ec134ea67bd6b8c95e97e Mon Sep 17 00:00:00 2001
From: "Roberto A. Foglietta" <roberto.foglietta@gmail.com>
Date: Thu Apr 16 09:34:35 2026 +0200
Subject: [PATCH] printk: fix zero-valued printk timestamps in early boot V6

This is a heavily "refactored by hands" patch for Linux Kernel 5.15.202 LTS
trying to solve the source tree janitoring vs 1-single place hack for hackers
in need for a simple-bare profiling tool and based on the patch V4 submitted by

    Pre-Kernel boot time (qemu, KVM, bios) is 0.251063 / 1.896 = 0.132 seconds

    [    0.250814] Linux version 5.15.202 (roberto@x280) (x86_64-linux-musl-gcc
                   (GCC) 14.3.0, GNU ld (GNU Binutils) 2.44) #1 Thu A6
    [    0.250820] Command line: HOST=x86_64 root=/dev/ram0 init=/init
                   console=ttyS0,115200n8 net.ifnames=0 nokaslr
    [    0.250823] KERNEL supported cpus:
    [    0.250824]   Intel GenuineIntel
    [    0.250826]   AMD AuthenticAMD
    [    0.250972] BIOS-provided physical RAM map:
    [    0.250973] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
    [    0.250978] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
    [    0.250981] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
    [    0.250983] BIOS-e820: [mem 0x0000000000100000-0x0000000001fdefff] usable
    [    0.250986] BIOS-e820: [mem 0x0000000001fdf000-0x0000000001ffffff] reserved
    [    0.250989] BIOS-e820: [mem 0x00000000b0000000-0x00000000bfffffff] reserved
    [    0.250991] BIOS-e820: [mem 0x00000000fed1c000-0x00000000fed1ffff] reserved
    [    0.250994] BIOS-e820: [mem 0x00000000feffc000-0x00000000feffffff] reserved
    [    0.250996] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
    [    0.251043] NX (Execute Disable) protection: active
    [    0.251050] Hypervisor detected: KVM
    [    0.251053] kvm-clock: Using msrs 4b564d01 and 4b564d00
    [    0.251063] kvm-clock: cpu 0, msr bac001, primary cpu clock
    [    0.000001] kvm-clock: using sched offset of 56130848 cycles
    [    0.000003] clocksource: kvm-clock: mask: 0xffffffffffffffff
                   max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns
    [    0.000005] tsc: Detected 1896.002 MHz processor

From: Tim Bird <tim.bird@sony.com>
Date: Fri, 10 Apr 2026 14:37:41 -0600
Message-ID: <20260410203741.997410-2-tim.bird@sony.com>

During early boot, printk timestamps are reported as zero before
kernel timekeeping starts (i.e. before time_init()). This hinders
boot-time optimization efforts. This period ranges from 17 to 1700
milliseconds on different embedded machines running Linux.

Add support for early timestamps based on processor cycle-generators
that need no kernel initialization.This feature isn't intended for a
generic distro kernels but for temporary use during kernel development
and boot-time research and optimization by kernel hackers only.

This yields non-zero timestamps for printks from the very start
of kernel execution. The timestamps are relative to the start of
an architecture-specific counter (e.g. tsc on x86_64 and cntvct_el0
on arm64). Affected timestamps reflect cycle counter related values
since init (usually machine power-on or virtual machine start) instead
of time from the kernel's timekeeping initialization. This results in
a discontinuity in the printk timestamp values, one time, when
kernel timekeeping starts.

Suggested-by: Tim Bird <tim.bird@sony.com>
Signed-off-by: Roberto A. Foglietta <roberto.foglietta@gmail.com>

V5 -> V6 // RAF
  Feature moved in the proper menu place, immediately after:
  Kernel hacking -> printk and dmesg options -> show timitng information printks
  Comments and descriptions more clearly indicate the aim and the limits of this
  patch and the related feature introduced by this patch.
V4 -> V5 // RAF
  Rationale: single point of change hack for printk. Code rewritten, entirely.
  It provides a feature for kernel hackers willing to profiling the early boot
  with a basic printk approach (rather than using HW monitors which might not
  be available or immediately available). Thus, it is a first-look or a last
  resort hack profiling feature. Something that isn't good to spread around
  the kernel sources tree and it is fine to have into a single header file.
V3 -> V4 // Tim
  Replace config vars with single one: CONFIG_EARLY_CYCLES_KHZ
  Replace runtime calibration with static config variable
  Remove reference to get_cycles()
  Add support for RISCV platforms
V2 -> V3 // Tim
  Default CONFIG option to 'n'
  Move more code into early_times.h (reduce ifdefs in init/main.c)
  Use match64 helper routines
  Use cycles_t instead of u64 type
  Add #defines for EARLY_CYCLES_BIT and EARLY_CYCLES_MASK
  Invert if logic in adjust_early_ts()
V1 -> V2 // Tim
  Remove calibration CONFIG vars
  Add 'depends on' to restrict arches (to handle ppc bug)
  Add early_ts_offset to avoid discontinuity
  Save cycles in ts_nsec, and convert on output
  Move conditional code to include file early_times.h

Source:
- https://raw.githubusercontent.com/robang74/uchaosys/refs/heads/v074/
		/cnfg/printk-early-boot-timestamps-hack-v6.patch
- commit: 2026-04-16, #2bd0bb1

---
 kernel/printk/early_times.h | 26 ++++++++++++++++++++++++++
 kernel/printk/printk.c      | 12 ++++++++++++
 lib/Kconfig.debug           | 23 +++++++++++++++++++++++
 3 files changed, 61 insertions(+)
 create mode 100644 kernel/printk/early_times.h

diff --git a/kernel/printk/early_times.h b/kernel/printk/early_times.h
new file mode 100644
index 000000000000..af0ef42bd1f1
--- /dev/null
+++ b/kernel/printk/early_times.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _EARLY_TIMES_H
+#define _EARLY_TIMES_H
+
+#include <linux/timekeeping.h>
+
+/*
+ * Fencing isn't optional here, otherwise unreliable values displaying
+ */
+#if defined(CONFIG_ARM64)
+	#include <asm/sysreg.h>
+	#define __early_raw_cycles ({ u64 val; \
+		asm volatile("isb; mrs %0, cntvct_el0" : "=r"(val)); val; })
+#elif defined(CONFIG_X86_64)
+	#define __early_raw_cycles ({ u64 val; \
+		asm volatile("lfence; rdtsc; shl $32, %%rdx; or %%rdx, %%rax" \
+			: "=a"(val) : : "rdx"); val; })
+#elif defined(CONFIG_RISCV_TIMER)
+	#define __early_raw_cycles ({ u64 val; \
+		asm volatile("fence; rdtime %0" : "=r"(val)); val; })
+#else
+	#define __early_raw_cycles 0
+#endif
+
+#endif /* _EARLY_TIMES_H */
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 113990f38436..0405a988c96f 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2151,6 +2151,18 @@ int vprintk_store(int facility, int level,
 	 */
 	ts_nsec = local_clock();
 
+#if CONFIG_PRINTK_EARLY_BOOT_TIMINGS
+#include "early_times.h"
+	/*
+	 * Very few developers are going to use this feature and it is
+	 * expected they're able to deal with it as a single entry-point of
+	 * changes. An hack to be further customised by them for porting the
+	 * kernel on not-yet-supported silicon or bug fixing / optimisations.
+	 */
+	if (unlikely(!ts_nsec))
+		ts_nsec = __early_raw_cycles;
+#endif
+
 	if (!printk_enter_irqsave(recursion_ptr, irqflags))
 		return 0;
 
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index db4f8ac489d4..714427089cb7 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -18,6 +18,29 @@ config PRINTK_TIME
 	  The behavior is also controlled by the kernel command line
 	  parameter printk.time=1. See Documentation/admin-guide/kernel-parameters.rst
 
+config PRINTK_EARLY_BOOT_TIMINGS
+	bool "Early boot printk shows times in raw cycle counter style"
+	default 0
+	depends on PRINTK
+	depends on ARM64 || X86_64 || RISCV_TIMER
+	select PRINTK_TIME
+	help
+	  Boolean value, disabled by default.
+
+	  Set this option to provide cycles information for printks at early
+	  boot times (before the start of kernel timekeeping), that would
+	  otherwise show as 0.
+
+	  Useful for profiling by relative monotonic values, not time.
+
+	  Note that this causes the kernel to show, for some early printks,
+	  cycles that are relative to processor power on, instead of
+	  relative to the start of kernel timekeeping. When kernel
+	  timekeeping starts, the timestamps values reset, causing
+	  a discontinuity in the timestamp values.
+
+	  If unsure, say N.
+
 config PRINTK_CALLER
 	bool "Show caller information on printks"
 	depends on PRINTK
-- 
2.34.1


^ permalink raw reply related

* grab-boot-data.sh fails on mkroot (zombies abound)
From: Bird, Tim @ 2026-06-18 18:40 UTC (permalink / raw)
  To: Rob Landley, linux-embedded@vger.kernel.org
In-Reply-To: <064b8c64-ebd7-4210-ac28-8be27773c8bf@landley.net>

Hey Rob,

I tried grab-boot-data.sh on mkroot, using 'toybox sh' as interpreter.
I got some weird behavior that I'd like to report.  (I was planning on experimenting
more to see if I could determine the issue, but I'm going on vacation next week.)
Maybe you have an idea off the top of you head what might be the problem...

When I use init=/bin/sh, and then try to run grab-boot-data.sh, it always
goes zombie after execution.

$ grab-boot-data.sh -h
Usage: ...
...

$ ps -l
F S   UID   PID  PPID C PRI  NI BIT    SZ WCHAN  TTY          TIME CMD          
0 S     0    56     1 0  19   0  32   244 do_wait ttyS0   00:00:00 sh
1 Z     0    58     1 0  19   0   -     0 0      4:64     00:00:00 grab-boot-da.
5 R     0    65    56 1  19   0  32   246 0      ttyS0    00:00:00 sh

Also, the program does not execute fully, and produces no output
when run in operational mode:
$ grab-boot-data.sh -l timslab -m mkroot-qemu-x86-64
$

(and it leaves another zombie)

I added 'vi' to toybox, and tried to edit grab-boot-data.sh to add
some echos to see where it's exiting, but when I try to save
the file, the qemu session hangs.  (I think this is a separate problem,
with toybox vi).

When running toybox sh as the 'init' process, does it need to 
do a 'wait' on it's children at some point, in order to reap them?

Regards,
 -- Tim


^ permalink raw reply

* Reminder of Boot-Time SIG meeting (June 30, 2026)
From: Bird, Tim @ 2026-06-18 17:56 UTC (permalink / raw)
  To: Embedded Linux mailing list

Hey Linux Boot-Time SIG interested parties (and other interested Linux kernel developers),

Here is the information for the next Linux Boot-Time SIG conference call.

The meeting will be held via the Jitsi online meeting platform.
To Join the meeting via web, click on:
https://meet.jit.si/LinuxBootTimeSIG

----
Our next meeting is Tuesday, June 30, at 9:00 am Mountain Daylight Time.
See this link for other time zones:
https://www.timeanddate.com/worldclock/meetingdetails.html?year=2026&month=06&day=30&hour=15&min=0&sec=0&p1=220&p2=137&p3=195&p4=771

(That makes it 8:00 am Pacific, 15:00 UTC, 17:00 CEST, and 20:30 IST)

I'm planning on 1 hour for this meeting.

The agenda for the meeting (and where we'll keep the minutes) is here:
https://docs.google.com/document/d/1XAufoTT6VVJOTMzKMoz8SyOss-JA9H4J1_yVXQq5mN0/edit?usp=sharing

The agenda for the June meeting will be available in the above document before the call.
.
As usual, we'll do a review of patches in progress, as well as discuss the following topics:
 - Review of talks at Embedded Linux Conference and Embedded Recipes
 - bootph work by TI
 - mkroot experimentation (grab-boot-data.sh does not run)
 - next steps for boot-time-wizard

If you would like to add something to the agenda, please respond to this email
or contact me directly.

I look forward to talking to you in the meeting.

Thanks,
 -- Tim

P.S. This announcement is earlier than usual because I'm on vacation next week.

^ permalink raw reply

* Re: mkroot quickstart (was RE: Testing on lots of architecture targets with mkroot.)
From: Rob Landley @ 2026-06-02 14:44 UTC (permalink / raw)
  To: Bird, Tim, linux-embedded@vger.kernel.org
In-Reply-To: <MW5PR13MB563232D40A099D798E698272FD152@MW5PR13MB5632.namprd13.prod.outlook.com>

On 6/1/26 15:54, Bird, Tim wrote:
> Next up is testing it with grab-boot-data.sh.

I _think_ I fixed the bug you sent me that was screwing up that script 
(sequencing issue using variable expansion results as a shell function 
to call), but my todo list runneth over and started composting some 
years ago. Lemme know if you hit a bug. (I'm redoing the \ line 
appending in HERE documents right now.

I have a busybox package in mkroot/packages which in theory lets you do

$ mkroot/mkroot.sh busybox CROSS=i686 LINUX=~/linux

And the result should have busybox instead of toybox providing the $PATH.

In practice, I never finished implementing bzip2 compression in toybox 
(because it was obsoleted by xz, so at the design level I went "I'll 
provide gzip as a compressor, and just provide extractors for everything 
else") but busybox decided that if you have bzip2 decompression enabled 
(which is in defconfig) the build will compress its help text with bzip2 
and there's NO WAY TO TELL IT NOT TO. You cannot have the extractor 
enabled WITHOUT the build calling the "bzip2" compressor on the host.

So building busybox with the toybox airlock enabled required a hack to 
the busybox source/build which disabled the help text entirely, and I 
went "there has to be a less terrible way to do this" but Denys has been 
a bit distracted recently and I haven't come up with a less ugly busybox 
config patch myself yet...

> I'll need to turn on PRINTK_TIME,
> and set initcall_debug on the kernel command line, and I'll likely want to add
> my latest early-times patch.  I'll poke around the scripts and see if I can figure
> out how best to do each of these.

The kernel is an external repo so you just patch it yourself. The patch 
stack I applied to 7.0 is at:

https://landley.net/bin/mkroot/0.8.14/linux-patches/

$ for i in ../patches; do echo $i; patch -p1 -i $i || break; done

To enable extra kernel config symbols from the command line, set KEXTRA 
in CSV format. (The plumbing circa line 361 literally sources 
docs/linux-microconfig and that gets written in there on line 348.) 
Something like:

$ mkroot/mkroot.sh CROSS=allnonstop LINUX=~/linux \
   KEXTRA=PRINTK_TIME,COREDUMP,JFS_FS

(There's also a MODULES= if you want to go there, and yes it enables 
module support when it has any modules to build. Eventually 
modules/plumbing/tests is intended to run the toybox test suite under 
mkroot, including a bunch of "requires root and a known kernel 
environment" tests I've only partially written so far. Really hard to 
test "mount" or "ps" outside a virtual environment like that, let alone 
modprobe. But I need to get the shell finished first...)

Rob

(Did you know the kernel's initrd loader will accept appended cpio.gz 
archives? Just cat them together, you don't even have to decompress and 
recompress. That makes module support in initramfs MUCH easier. Last I 
checked mkroot.sh was doing that right, but I haven't regression tested 
it in forever...)

^ permalink raw reply

* Re: mkroot quickstart (was RE: Testing on lots of architecture targets with mkroot.)
From: Roberto A. Foglietta @ 2026-06-02  5:52 UTC (permalink / raw)
  To: Rob Landley; +Cc: Bird, Tim, linux-embedded@vger.kernel.org
In-Reply-To: <9e3f48db-c827-4892-8e41-656e9c60ff89@landley.net>

On Sat, 30 May 2026 at 23:58, Rob Landley <rob@landley.net> wrote:
>

>
> In theory the toybox list, although that's in the process of migrating
> from dreamhost to osuosl because

osuosl is the same host busybox is using (not a question, a statement)


> Your absolute minimum is rdinit=/bin/sh or similar, but the mkroot init
> is trying to do the minimum work to bring up a usable environment with
> mount points and networking before running the $HANDOFF program.

from a PoV of the kernel it matters the /init timestamp

from a PoV of the kernel what precedes t=0 is unknown / uncertain

from a PoV of the real-world observer what precede kernel t=0 matters
and /init timestamp depends by the hardware and the kernel
version/config

IMHO, the minimum boot time is a question as well-defined as: what
"boot" means and where?


Best regards, R-

^ permalink raw reply

* RE: mkroot quickstart (was RE: Testing on lots of architecture targets with mkroot.)
From: Bird, Tim @ 2026-06-01 20:54 UTC (permalink / raw)
  To: Rob Landley, linux-embedded@vger.kernel.org
In-Reply-To: <9e3f48db-c827-4892-8e41-656e9c60ff89@landley.net>



> -----Original Message-----
> From: Rob Landley <rob@landley.net>
> On 5/29/26 10:54, Bird, Tim wrote:
> > Hey Rob,
> >
> > I just tried the mkroot quickstart instructions in toybox/mkroot/README, and the image failed
> > to start, due to missing init program.
> 
> Commit d7cef2f4a7d6 should have fixed that?
> 
> https://codeberg.org/landley/toybox/commit/d7cef2f4a7d6
> 
> The problem is that while /init is always there, /bin/sh isn't unless
> your .config pulls in the shell, which is still in toys/pending and thus
> not in defconfig.
> 
> In an eventual 1.0 release that should all be tidied away, but right now
> mkroot.sh has a workaround for it that wasn't always triggering because
> I tried to be clever about letting it use your existing .config if you
> had one...
> 
> Did you pull from microsoft github instead of codeberg? I haven't
> updated the microsoft github version in weeks...
Yes.  I had an old toybox repo lying around with an upgream of github,
and I just 'git pull'ed it.

> 
> > Maybe I missed something.  What is the appropriate mailing list to use for mkroot
> > discussion?  It is on the toybox list or somewhere else?
> 
> In theory the toybox list, although that's in the process of migrating
> from dreamhost to osuosl because
> https://www.reddit.com/r/dreamhost/comments/1tjunvv/ugh_dreamhost_getting_rid_of_mailman_email_lists/
> 
> I'm happy to stay on this list if you don't mind the noise?
It doesn't bother me, and sometimes the discussion is relevant for other
embedded Linux issues outside of toybox and mkroot.  If anyone else objects,
please let Rob or me know.

> 
> > I'm thinking a mkroot environment might make a good baseline test system for short
> > boot-times (kind of an extreme case to establish a minimum boot time value,
> > at least for the kernel).
> 
> I hope so. :)
> 
...
> > Also, the build rebuilt the entire kernel, even though I had just built it a few minutes earlier.
> > Not sure what's going on there.
> 
> I didn't bother to implement rebuilds for packages, it always does "make
> clean/all".

OK.  Got it.
 
... [useful info snipped, see previous email for details] ...

> 
> > Anyways, thanks for any tips you can provide to help me get this working...
> 
> I mean to push one last update to microsoft github as part of the next
> release, but the release notes say the project moved to codeberg (the
> website links were updated circa
> https://landley.net/toybox/git/commit/ea6c172dadd0 ) and I'll probably
> take the microsoft github version down in the following release.
> 
> If you don't like codeberg you can also pull/clone from
> landley.net/toybox/git but microsoft github has gone VERY STRANGE and
> I'm slowly backing away from it. (Microsoft github still being there is
> ALSO a sharp edge, but I'm unsure how to wean people off of it, or if I
> should just have it suddenly vanish one day...?)

I set my git remote to the codeberg repo, pulled, and rebuilt, and it works.

Thanks!

Next up is testing it with grab-boot-data.sh.  I'll need to turn on PRINTK_TIME,
and set initcall_debug on the kernel command line, and I'll likely want to add
my latest early-times patch.  I'll poke around the scripts and see if I can figure
out how best to do each of these.
 -- Tim



^ permalink raw reply

* Re: mkroot quickstart (was RE: Testing on lots of architecture targets with mkroot.)
From: Rob Landley @ 2026-05-30 20:41 UTC (permalink / raw)
  To: Bird, Tim, linux-embedded@vger.kernel.org
In-Reply-To: <MW5PR13MB5632FDD1833ABA105437D02AFD162@MW5PR13MB5632.namprd13.prod.outlook.com>

On 5/29/26 10:54, Bird, Tim wrote:
> Hey Rob,
> 
> I just tried the mkroot quickstart instructions in toybox/mkroot/README, and the image failed
> to start, due to missing init program.

Commit d7cef2f4a7d6 should have fixed that?

https://codeberg.org/landley/toybox/commit/d7cef2f4a7d6

The problem is that while /init is always there, /bin/sh isn't unless 
your .config pulls in the shell, which is still in toys/pending and thus 
not in defconfig.

In an eventual 1.0 release that should all be tidied away, but right now 
mkroot.sh has a workaround for it that wasn't always triggering because 
I tried to be clever about letting it use your existing .config if you 
had one...

Did you pull from microsoft github instead of codeberg? I haven't 
updated the microsoft github version in weeks...

> Maybe I missed something.  What is the appropriate mailing list to use for mkroot
> discussion?  It is on the toybox list or somewhere else?

In theory the toybox list, although that's in the process of migrating 
from dreamhost to osuosl because 
https://www.reddit.com/r/dreamhost/comments/1tjunvv/ugh_dreamhost_getting_rid_of_mailman_email_lists/

I'm happy to stay on this list if you don't mind the noise?

> I'm thinking a mkroot environment might make a good baseline test system for short
> boot-times (kind of an extreme case to establish a minimum boot time value,
> at least for the kernel).

I hope so. :)

Your absolute minimum is rdinit=/bin/sh or similar, but the mkroot init 
is trying to do the minimum work to bring up a usable environment with 
mount points and networking before running the $HANDOFF program. 
Generally takes a fraction of a second, and the /init/mnt automation 
lets you record a timestamp as soon as your provided application code 
takes control. (Which if it's wget reaching out to a host cgi or 
something can be recorded on the host clock, although qemu's BIOS boot 
on platforms like s390 or powerpc can take ages. There's a design pass 
to figure out what success looks like here.)

The only actually delays where the script is waiting for anything are in 
the time setting error path when the virtual board doesn't emulate a 
battery backed up clock:

   [ "$(date +%s)" -lt 1000 ] && timeout 2 sntp -sq 10.0.2.2 # Ask host
   [ "$(date +%s)" -lt 10000000 ] && sntp -sq time.google.com

Which has a 2 second timeout for the first one and I believe a 3 second 
default timeout built into sntp? So 5 seconds total for "clock could not 
be set", but only in the case where the network card came up but (so no 
immediate "no route to host" failure) but isn't connected to anything 
(neither loopback NTP server nor network), AND the board's emulation of 
a persistent clock failed to trigger the attempts in the first place.

(You need to set the clock before calling "make" on a source package, 
otherwise all your file timestamps are in the future and make DOES NOT 
LIKE THAT. Part of my use case is "automated native build environment 
under emulation", ideally with distcc calling out to the cross compiler 
on the host.)

> Below was the output.
> 
> I checked the root filesystem in toybox/root/i686/fs/bin and there's no 'init' or 'sh' link or program
> present.

The /init isn't in bin, it's in the root directory (which is the first 
place the kernel looks for it):

$ grep '"/init"' linux/init/main.c
static char *ramdisk_execute_command = "/init";

> Does toybox use the same configuration in mkroot as the base toybox build?

See commit above. That was the failure I hit during the demo, which is 
AFTER I did https://mstdn.jp/@landley/116580239742755479 to try to make 
the previous "check and see if it's already got it" code reliable...

Potentially re-using the existing .config by default was just a sharp 
edge, I gave up trying to make it reliable so took out the "by default" 
part. Now it has to be specified explicitly as an override to be able to 
screw up like that.

> I modified the configuration for toybox to include the 'sh' toy, rebuilt it, and verified that the
> x86_64 toybox had a working 'sh' toy.
> 
> Then I rebuilt the mkroot environment with:
> $ mkroot/mkroot.sh CROSS=i686 LINUX=linux
> and ran it with:
> $ root/i686/run-qemu.sh
> 
> I got the same error (missing 'init') on running qemu.
> bin/init and bin/sh are still not in root/i686/fs.

It's not /bin/init, just /init. And the failure code path I was 
wrestling on mastodon back before the talk was the script replacing a 
good .config with a bad .config. I also have modified the .config to be 
right and the have the build fail.

(Possibly I had a test reversed or something, but I just ripped it out 
and stopped trying to do it that way because once /bin/sh is out of 
pending "defconfig is broken" stops being an issue, so I was debugging a 
workaround I intend to eliminate anyway. It also wouldn't apply to using 
busybox instead of toybox, where "make defconfig" should also just work 
unless Denys did something to it since I last checked. Busybox hasn't 
got "pending": they had a 10 year headstart on me, greater willingness 
to suck in external code because gplv2 vs 0BSD license, and a lot more 
developers...)

> Also, the build rebuilt the entire kernel, even though I had just built it a few minutes earlier.
> Not sure what's going on there.

I didn't bother to implement rebuilds for packages, it always does "make 
clean/all".

It's not hard to add, but I didn't want the complexity: incremental 
rebuilds are a RICH source of bugs. That whole "try to reuse the 
existing .config" bit is conceptually adjacent. :P

(That said, if you really want incremental builds, they're not hard to 
add. I'd probably make it another NOREBUILD=1 explicit override though: 
you get to keep the pieces, NOT the first timer's experience.)

> I note that the mkroot.sh script has 'make clean defconfig toybox install_airlock ...'
> which I'm not sure includes 'sh'.  Note that root/build/airlock has 'sh', but
> root/i686/fs/bin does not have 'sh'.

Toybox has a "pending" directory for unfinished commands. Both "sh" and 
"route" are in there, because each still has significant missing 
features, although both are good enough to use in mkroot. They work but 
they're not finished.

The "make defconfig" target does not include any pending commands by 
default.

The current code on codeberg looks like this:

> # Build static toybox with existing .config if there is one, else defconfig+sh
> if [ -z "$NOTOYBOX" ]; then
>   announce toybox
>   [ -z "$TOYCFG" ] && { rm -f "${TOYCFG:=.config}";} || CONF=silentoldconfig
>   [ -e "$ROOT"/lib/libc.so ] || export LDFLAGS=--static
>   PREFIX="$ROOT" KCONFIG_CONFIG="$TOYCFG" make clean \
>     ${CONF:-defconfig KCONFIG_ALLCONFIG=<(csv2cfg $(be2csv $PENDING SH ROUTE) y)} \
>     toybox install || exit 1
>   unset LDFLAGS
> fi

(Which now that I look at it again is slightly broken because 
${TOYCFG:=.config} can never assign a default value under a [ -z 
"$TOYCFG" ] guard... didn't clean it up enough from the previous code.)

Anyway, what that TRIES to do is add "sh" and "route" to defconfig. Ugh, 
I need to do another pass to simplify that again, I was clearly rushing 
before the talk. Maybe...

PENDING="$(csv2cfg $(be2csv $PENDING SH ROUTE) y)"
PREFIX="$ROOT" KCONFIG_CONFIG="$TOYCFG" make clean \
   ${CONF:-defconfig KCONFIG_ALLCONFIG=<(echo "$PENDING")} \
   toybox install || exit 1

Sigh, I really wanna split the whole CONF= assignment out instead of 
PENDING= but the problem is the ${X:-} naturally makes evaluating the 
<(subshell) part conditional (bash!) so the subprocess only gets 
launched when it'll be used, AND the lifetime is right when called 
inline. If I moved the <() into that separate PENDING= assignment it'll 
exit again before "make" gets called, and thus the /dev/fd/63 or similar 
it expands to will no longer be valid (it's the filename of a process's 
output pipe). I can't(?) use a DIFFERENT redirection style without 
creating a temp file, which I then need to delete, and there's a ctrl-c 
error path race with making SURE it got deleted so I'd need a trap and 
NOT GOING THERE. This is all because KCONFIG_ALLCONFIG= wants a filename 
to read from. (When I do this stuff right it looks like I didn't do 
anything, but getting there is gordian knot unraveling...)

Anyway, in the above code you can set PENDING= or TOYCFG= on the 
mkroot.sh command line:

A) set PENDING=diff,awk,getty,bootchartd,dhcp to tell it to use various 
commands out of toys/pending/*.c that mostly work but haven't been fully 
reviewed yet or which have major known missing features that should 
really be added before promoting t hem out of pending. (Note: some 
commands in there like git.c are really just stubs.) It will always add 
"sh" and "route" to this list. Oops yes I need to flatten the provided 
names to all upper case, I just changed how it works last week...

B) instead set TOYCFG=filename and it will use the provided .config file 
as-is, although it will run it through "make silentoldconfig" first.

But PENDING= at least is just a short-term hack because an actual 1.0 
release wouldn't still have a "pending" directory so defconfig SHOULD 
"just work". I just need to review and fix up all the unifinished code 
and external submissions in toybox and get out a 1.0 release...

I'm working my way towards fewer fine-tuning knobs. It should "just 
work". All that's transient, which is why I haven't polished it enough.

> Anyways, thanks for any tips you can provide to help me get this working...

I mean to push one last update to microsoft github as part of the next 
release, but the release notes say the project moved to codeberg (the 
website links were updated circa 
https://landley.net/toybox/git/commit/ea6c172dadd0 ) and I'll probably 
take the microsoft github version down in the following release.

If you don't like codeberg you can also pull/clone from 
landley.net/toybox/git but microsoft github has gone VERY STRANGE and 
I'm slowly backing away from it. (Microsoft github still being there is 
ALSO a sharp edge, but I'm unsure how to wean people off of it, or if I 
should just have it suddenly vanish one day...?)

> Write protecting kernel text and read-only data: 5764k
> Run /init as init process
> Failed to execute /init (error -2)

$ grep -w 2 /usr/include/asm-generic/errno-base.h
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#define	ENOENT		 2	/* No such file or directory */

Because /init starts with #!/bin/sh and if .config doesn't have 
CONFIG_SH in it (which defconfig doesn't because pending, I'm working on 
it ala https://codeberg.org/landley/toybox/commit/0b6e231b52ae) it will 
build a toybox that doesn't include "sh". Which the commit I linked 
above tried to make stop happening unless you EXPLICITLY asked it to 
re-use an existing .config, in which case you get to keep the pieces.

(It SEEMED like "re-use the existing toybox .config if it's good enough" 
was a good UI, but in practice overwriting .config sometimes and using 
it as-is other times was just too brittle and fiddly to be robust, and 
the resulting error message is far too eldrich to impose upon new users. 
So I made it explicit. The kernel could probably use a similar one, but 
splicing initramfs into that is even worse than pending. I could do a 
kernel patch that lets you say INITRAMFS=/path on the make command line, 
but my https://landley.net/bin/mkroot/0.8.14/linux-patches/ never go 
upstream, Greg KH hates me personally...)

Rob

P.S. Making things simple is _REALLY_HARD_. It needs to just work, a 
thousand things can go wrong, I have maybe 500 lines before their eyes 
glaze over trying to understand it and a third of that is whitespace and 
comments... I'm doing my best.

^ permalink raw reply

* mkroot quickstart (was RE: Testing on lots of architecture targets with mkroot.)
From: Bird, Tim @ 2026-05-29 15:54 UTC (permalink / raw)
  To: Rob Landley, linux-embedded@vger.kernel.org
In-Reply-To: <81e4aefd-f052-4a52-94b5-36030b26c358@landley.net>

Hey Rob,

I just tried the mkroot quickstart instructions in toybox/mkroot/README, and the image failed
to start, due to missing init program.

Maybe I missed something.  What is the appropriate mailing list to use for mkroot
discussion?  It is on the toybox list or somewhere else?

I'm thinking a mkroot environment might make a good baseline test system for short
boot-times (kind of an extreme case to establish a minimum boot time value,
at least for the kernel).

Below was the output.

I checked the root filesystem in toybox/root/i686/fs/bin and there's no 'init' or 'sh' link or program
present.  Does toybox use the same configuration in mkroot as the base toybox build?
I modified the configuration for toybox to include the 'sh' toy, rebuilt it, and verified that the
x86_64 toybox had a working 'sh' toy.

Then I rebuilt the mkroot environment with:
$ mkroot/mkroot.sh CROSS=i686 LINUX=linux
and ran it with:
$ root/i686/run-qemu.sh

I got the same error (missing 'init') on running qemu.
bin/init and bin/sh are still not in root/i686/fs.

Also, the build rebuilt the entire kernel, even though I had just built it a few minutes earlier.
Not sure what's going on there.

I note that the mkroot.sh script has 'make clean defconfig toybox install_airlock ...'
which I'm not sure includes 'sh'.  Note that root/build/airlock has 'sh', but
root/i686/fs/bin does not have 'sh'.

Anyways, thanks for any tips you can provide to help me get this working...
 -- Tim

--------- output from run-qemu.sh
Linux version 7.1.0-rc5 (tbird@timdesk) (i686-linux-musl-gcc (GCC) 11.2.0, GNU 6
x86/CPU: Model not found in latest microcode list
BIOS-provided physical RAM map:
BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff]  System RAM
BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff]  device reserved
BIOS-e820: [gap 0x00000000000a0000-0x00000000000effff]
BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff]  device reserved
BIOS-e820: [mem 0x0000000000100000-0x000000000ffdffff]  System RAM
BIOS-e820: [mem 0x000000000ffe0000-0x000000000fffffff]  device reserved
BIOS-e820: [gap 0x0000000010000000-0x00000000fffbffff]
BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff]  device reserved
Notice: NX (Execute Disable) protection missing in CPU!
DMI: SMBIOS 3.0.0 present.
DMI: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-4
DMI: Memory slots populated: 1/1
tsc: Fast TSC calibration using PIT
tsc: Detected 2095.043 MHz processor
last_pfn = 0xffe0 max_arch_pfn = 0x100000
MTRR map: 4 entries (3 fixed + 1 variable; max 19), built from 8 variable MTRRs
x86/PAT: PAT not supported by the CPU.
x86/PAT: Configuration [0-7]: WB  WT  UC- UC  WB  WT  UC- UC  
RAMDISK: [mem 0x0ff6b000-0x0ffdffff]
255MB LOWMEM available.
  mapped low ram: 0 - 0ffe0000
  low ram: 0 - 0ffe0000
[gap 0x10000000-0xfffbffff] available for PCI devices
clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idles
Zone ranges:
  DMA      [mem 0x0000000000001000-0x0000000000ffffff]
  Normal   [mem 0x0000000001000000-0x000000000ffdffff]
Movable zone start for each node
Early memory node ranges
  node   0: [mem 0x0000000000001000-0x000000000009efff]
  node   0: [mem 0x0000000000100000-0x000000000ffdffff]
Initmem setup node 0 [mem 0x0000000000001000-0x000000000ffdffff]
On node 0, zone DMA: 1 pages in unavailable ranges
On node 0, zone DMA: 97 pages in unavailable ranges
On node 0, zone Normal: 32 pages in unavailable ranges
Kernel command line: HOST=i686 console=ttyS0 
Unknown kernel command line parameters "HOST=i686", will be passed to user spac.
printk: log buffer data + meta data: 131072 + 409600 = 540672 bytes
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes, linear)
Inode-cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
Built 1 zonelists, mobility grouping on.  Total pages: 65406
mem auto-init: stack:off, heap alloc:off, heap free:off
Checking if this processor honours the WP bit even in supervisor mode...Ok.
SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
Console: colour VGA+ 80x25
printk: legacy console [ttyS0] enabled
clocksource: tsc-early: mask: 0xffffffffffffffff max_cycles: 0x1e32e5fed37, maxs
Calibrating delay loop (skipped), value calculated using timer frequency.. 4190)
Last level iTLB entries: 4KB 0, 2MB 0, 4MB 0
Last level dTLB entries: 4KB 0, 2MB 0, 4MB 0, 1GB 0
CPU: Intel Pentium III (Katmai) (family: 0x6, model: 0x7, stepping: 0x3)
mitigations: Enabled attack vectors: SMT mitigations: off
Speculative Store Bypass: Vulnerable
Spectre V2 : Vulnerable
ITS: Vulnerable
MDS: Vulnerable
Spectre V1 : Vulnerable: __user pointer sanitization and usercopy barriers onlys
L1TF: Kernel not compiled for PAE. No mitigation for L1TF
x86/fpu: x87 FPU will use FXSAVE
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
VFS: Finished mounting rootfs on nullfs
Performance Events: PMU not available due to virtualization, using software eve.
signal: max sigframe size: 1440
Memory: 250396K/261624K available (4793K kernel code, 608K rwdata, 968K rodata,)
devtmpfs: initialized
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 764s
posixtimers hash table entries: 512 (order: 0, 2048 bytes, linear)
futex hash table entries: 256 (4096 bytes on 1 NUMA nodes, total 4 KiB, linear).
NET: Registered PF_NETLINK/PF_ROUTE protocol family
clocksource: pit: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1601818s
PCI: PCI BIOS revision 2.10 entry at 0xfd1d9, last bus=0
PCI: Using configuration type 1 for base access
SCSI subsystem initialized
PCI: Probing PCI hardware
PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [io  0x0000-0xffff]
pci_bus 0000:00: root bus resource [mem 0x00000000-0xffffffff]
pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff]
pci 0000:00:00.0: [8086:1237] type 00 class 0x060000 conventional PCI endpoint
pci 0000:00:01.0: [8086:7000] type 00 class 0x060100 conventional PCI endpoint
pci 0000:00:01.1: [8086:7010] type 00 class 0x010180 conventional PCI endpoint
pci 0000:00:01.1: BAR 4 [io  0xc040-0xc04f]
pci 0000:00:01.1: BAR 0 [io  0x01f0-0x01f7]: legacy IDE quirk
pci 0000:00:01.1: BAR 1 [io  0x03f6]: legacy IDE quirk
pci 0000:00:01.1: BAR 2 [io  0x0170-0x0177]: legacy IDE quirk
pci 0000:00:01.1: BAR 3 [io  0x0376]: legacy IDE quirk
pci 0000:00:01.3: [8086:7113] type 00 class 0x068000 conventional PCI endpoint
pci 0000:00:01.3: quirk: [io  0x0600-0x063f] claimed by PIIX4 ACPI
pci 0000:00:01.3: quirk: [io  0x0700-0x070f] claimed by PIIX4 SMB
pci 0000:00:02.0: [1234:1111] type 00 class 0x030000 conventional PCI endpoint
pci 0000:00:02.0: BAR 0 [mem 0xfd000000-0xfdffffff pref]
pci 0000:00:02.0: BAR 2 [mem 0xfebb0000-0xfebb0fff]
pci 0000:00:02.0: ROM [mem 0xfeba0000-0xfebaffff pref]
pci 0000:00:02.0: Video device with shadowed ROM at [mem 0x000c0000-0x000dffff]
pci 0000:00:03.0: [8086:100e] type 00 class 0x020000 conventional PCI endpoint
pci 0000:00:03.0: BAR 0 [mem 0xfeb80000-0xfeb9ffff]
pci 0000:00:03.0: BAR 1 [io  0xc000-0xc03f]
pci 0000:00:03.0: ROM [mem 0xfeb00000-0xfeb7ffff pref]
pci_bus 0000:00: busn_res: [bus 00-ff] end is updated to 00
pci 0000:00:01.0: PIIX/ICH IRQ router [8086:7000]
e820: register RAM buffer resource [mem 0x0009fc00-0x0009ffff]
e820: register RAM buffer resource [mem 0x0ffe0000-0x0fffffff]
pci 0000:00:02.0: vgaarb: setting as boot VGA device
pci 0000:00:02.0: vgaarb: bridge control possible
pci 0000:00:02.0: vgaarb: VGA device added: decodes=io+mem,owns=io+mem,locks=noe
vgaarb: loaded
clocksource: Switched to clocksource tsc-early
NET: Registered PF_INET protocol family
IP idents hash table entries: 4096 (order: 3, 32768 bytes, linear)
tcp_listen_portaddr_hash hash table entries: 1024 (order: 0, 4096 bytes, linear)
Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
TCP established hash table entries: 2048 (order: 1, 8192 bytes, linear)
TCP bind hash table entries: 2048 (order: 2, 16384 bytes, linear)
TCP: Hash tables configured (established 2048 bind 2048)
UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
NET: Registered PF_UNIX/PF_LOCAL protocol family
pci_bus 0000:00: resource 4 [io  0x0000-0xffff]
pci_bus 0000:00: resource 5 [mem 0x00000000-0xffffffff]
pci 0000:00:01.0: PIIX3: Enabling Passive Release
pci 0000:00:00.0: Limiting direct PCI/PCI transfers
pci 0000:00:01.0: Activating ISA DMA hang workarounds
PCI: CLS 0 bytes, default 32
platform rtc_cmos: registered fallback platform RTC device
workingset: timestamp_bits=30 (anon: 26) max_order=16 bucket_order=0 (anon: 0)
squashfs: version 4.0 (2009/01/31) Phillip Lougher
Unpacking initramfs...
Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
serial8250: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
loop: module loaded
scsi host0: ata_piix
scsi host1: ata_piix
ata1: PATA max MWDMA2 cmd 0x1f0 ctl 0x3f6 bmdma 0xc040 irq 14 lpm-pol 0
ata2: PATA max MWDMA2 cmd 0x170 ctl 0x376 bmdma 0xc048 irq 15 lpm-pol 0
e1000: Intel(R) PRO/1000 Network Driver
e1000: Copyright (c) 1999-2006 Intel Corporation.
e1000 0000:00:03.0: found PCI INT A -> IRQ 11
Freeing initrd memory: 468K
ata2: found unknown device (class 0)
ata2.00: ATAPI: QEMU DVD-ROM, 2.5+, max UDMA/100
scsi 1:0:0:0: CD-ROM            QEMU     QEMU DVD-ROM     2.5+ PQ: 0 ANSI: 5
e1000 0000:00:03.0 eth0: (PCI:33MHz:32-bit) 52:54:00:12:34:56
e1000 0000:00:03.0 eth0: Intel(R) PRO/1000 Network Connection
NET: Registered PF_INET6 protocol family
Segment Routing with IPv6
In-situ OAM (IOAM) with IPv6
NET: Registered PF_PACKET protocol family
sched_clock: Marking stable (958101015, 36815557)->(1006698485, -11781913)
netconsole: network logging started
Freeing unused kernel image (initmem) memory: 412K
Write protecting kernel text and read-only data: 5764k
Run /init as init process
Failed to execute /init (error -2)
Run /sbin/init as init process
Run /etc/init as init process
Run /bin/init as init process
Run /bin/sh as init process
Kernel panic - not syncing: No working init found.  Try passing init= option to.
CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.1.0-rc5 #1 PREEMPTLAZY 
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BI4
Call Trace:
 dump_stack_lvl+0x52/0x5c
 dump_stack+0x12/0x18
 vpanic+0x284/0x28c
 ? rest_init+0xa8/0xa8
 panic+0xe/0x10
 kernel_init+0x15b/0x1b8
 ret_from_fork+0x1c1/0x1fc
 ? rest_init+0xa8/0xa8
 ret_from_fork_asm+0x12/0x18
 entry_INT80_32+0xef/0xef
Kernel Offset: disabled
Rebooting in 1 seconds..

> -----Original Message-----
> From: Rob Landley <rob@landley.net>
> Sent: Tuesday, May 26, 2026 12:52 PM
> To: linux-embedded@vger.kernel.org
> Subject: Testing on lots of architecture targets with mkroot.
> 
> I promised Tim I'd explain to him how I regression test on s390x and sh4 and mips and powerpc and so on in parallel, and he was in the
> audience for my wednesday talk at ELC, but I'm not sure that gave him everything he needed to do proper automated
> 
> I promised Tim I'd explain to him how I regression test on s390x and sh4
> and mips and powerpc and so on in parallel, and he was in the audience
> for my wednesday talk at ELC, but I'm not sure that gave him everything
> he needed to do proper automated regression testing under qemu on lots
> of architectures in parallel.
> 
> The source repo I used was: https://codeberg.org/landley/toybox
> 
> Prebuilt binaries of all the outputs are at https://landley.net/bin/
> with several releases worth of history. (Although it'll probably go down
> at the end of the year to avoid Gavin Newsom's $7500/download fines.)
> 
> There's a README in the toybox/mkroot directory, but also instructions
> at https://landley.net/toybox/faq.html#mkroot and
> https://landley.net/talks/mkroot-2023.txt (which was the previous time I
> gave a mkroot talk).
> 
> You'll need cross compilers: my musl+gcc cross (and native) compilers
> are built with Rich Felker's
> https://github.com/richfelker/musl-cross-make using a wrapper script in
> scripts/mcm-buildall.sh in the toybox source. The binary tarballs are in
> https://landley.net/bin/toolchains but you'll probably want to build
> them from source.
> 
> If you run that mcm-buildall.sh script in a fresh musl-cross-make
> checkout directory with no arguments it builds toolchains for all the
> targets I've managed to get working so far.
> 
> If you want to rebuild just one, you can feed it any of the strings from
> https://codeberg.org/landley/toybox/src/branch/master/scripts/mcm-buildall.sh#L18
> as an argument, ala:
> 
> $ ~/toybox/mcm-buildall.sh m68k::
> 
> Note that these toolchains are statically linked with a host compiler to
> be portable, so the script will first build a musl host compiler (see
> line 38 in the buildall script) because glibc is incompetent at static
> linking.
> 
> In theory this means you can just grab a tarball and extract it
> somewhere and add it to your $PATH and it should just work. There's also
> a https://landley.net/bin/toolchains/25-03-2024/musl-cross-make.tar.xz
> with everything (all the source and all the targets it built) to shut
> Bradley Stallman up.
> 
> The test harness script is mkroot/testroot.sh again in the toybox
> source. Run it from the top level (toybox source) directory and it'll
> iterate through all the images under root/ and try to run them under
> qemu (you need qemu-system-* and mksquashfs in your $PATH, it'll
> complain if either is missing). It provides a squashfs filesystem with
> controls scripts and test data, so instead of booting to a shell prompt
> the qemu instance runs a test script and then exits on its own. It
> prints out various "=== thingy" lines for successful tests, and the
> pass/fail thing at the end says whether it got enough === lines of
> output. If not, look at root/build/log/*-test.txt to see what output it
> DID get.
> 
> I did the start of a busybox-based version at
> https://codeberg.org/landley/toybox/src/branch/master/mkroot/packages/busybox
> a while back but unfortunately building busybox defconfig is INSANE
> these days. You'll notice rather extensive patching with sed to remove
> build dependencies toybox doesn't have, and even then it was mad about
> something (I forget what).
> 
> That said, you can also just do:
> 
> mkroot/mkroot.sh NOAIRLOCK=1 LINUX=~/linux CROSS=allnonstop mybusybox
> 
> To skip the airlock step and run your own much simpler busybox build
> script (just stick it in mkroot/packages), and as long as your script
> sets NOTOYBOX=1 before it exits you should get the directory layout, the
> init script, the kernel build and packaging, and the run-qemu.sh for the
> target, with whatever additional files YOU installed (under $ROOT/). So
> a busybox based filesystem instead of toybox shouldn't be a big lift,
> just wasn't a priority for me.
> 
> But since you're not testing _toybox_, and toybox isn't 1.0 yet, that
> would probably give you an environment much closer to what you want. :)
> 
> Note that mkroot.sh is almost entirely self-contained, merging it into
> toybox and having LINUX= be specified on the command line was just so I
> didn't have to download any packages in the default build. (There's
> download plumbing in scripts/plumbing, I think I went over that in the
> talk. The "dropbear" package is another one to read to see how that
> stuff works.)
> 
> Rob


^ permalink raw reply

* Testing on lots of architecture targets with mkroot.
From: Rob Landley @ 2026-05-26 18:51 UTC (permalink / raw)
  To: linux-embedded@vger.kernel.org
In-Reply-To: <MW5PR13MB5632F0D9C8E8A12F2C24B64AFD012@MW5PR13MB5632.namprd13.prod.outlook.com>

I promised Tim I'd explain to him how I regression test on s390x and sh4 
and mips and powerpc and so on in parallel, and he was in the audience 
for my wednesday talk at ELC, but I'm not sure that gave him everything 
he needed to do proper automated regression testing under qemu on lots 
of architectures in parallel.

The source repo I used was: https://codeberg.org/landley/toybox

Prebuilt binaries of all the outputs are at https://landley.net/bin/ 
with several releases worth of history. (Although it'll probably go down 
at the end of the year to avoid Gavin Newsom's $7500/download fines.)

There's a README in the toybox/mkroot directory, but also instructions 
at https://landley.net/toybox/faq.html#mkroot and 
https://landley.net/talks/mkroot-2023.txt (which was the previous time I 
gave a mkroot talk).

You'll need cross compilers: my musl+gcc cross (and native) compilers 
are built with Rich Felker's 
https://github.com/richfelker/musl-cross-make using a wrapper script in 
scripts/mcm-buildall.sh in the toybox source. The binary tarballs are in 
https://landley.net/bin/toolchains but you'll probably want to build 
them from source.

If you run that mcm-buildall.sh script in a fresh musl-cross-make 
checkout directory with no arguments it builds toolchains for all the 
targets I've managed to get working so far.

If you want to rebuild just one, you can feed it any of the strings from 
https://codeberg.org/landley/toybox/src/branch/master/scripts/mcm-buildall.sh#L18 
as an argument, ala:

$ ~/toybox/mcm-buildall.sh m68k::

Note that these toolchains are statically linked with a host compiler to 
be portable, so the script will first build a musl host compiler (see 
line 38 in the buildall script) because glibc is incompetent at static 
linking.

In theory this means you can just grab a tarball and extract it 
somewhere and add it to your $PATH and it should just work. There's also 
a https://landley.net/bin/toolchains/25-03-2024/musl-cross-make.tar.xz 
with everything (all the source and all the targets it built) to shut 
Bradley Stallman up.

The test harness script is mkroot/testroot.sh again in the toybox 
source. Run it from the top level (toybox source) directory and it'll 
iterate through all the images under root/ and try to run them under 
qemu (you need qemu-system-* and mksquashfs in your $PATH, it'll 
complain if either is missing). It provides a squashfs filesystem with 
controls scripts and test data, so instead of booting to a shell prompt 
the qemu instance runs a test script and then exits on its own. It 
prints out various "=== thingy" lines for successful tests, and the 
pass/fail thing at the end says whether it got enough === lines of 
output. If not, look at root/build/log/*-test.txt to see what output it 
DID get.

I did the start of a busybox-based version at 
https://codeberg.org/landley/toybox/src/branch/master/mkroot/packages/busybox 
a while back but unfortunately building busybox defconfig is INSANE 
these days. You'll notice rather extensive patching with sed to remove 
build dependencies toybox doesn't have, and even then it was mad about 
something (I forget what).

That said, you can also just do:

mkroot/mkroot.sh NOAIRLOCK=1 LINUX=~/linux CROSS=allnonstop mybusybox

To skip the airlock step and run your own much simpler busybox build 
script (just stick it in mkroot/packages), and as long as your script 
sets NOTOYBOX=1 before it exits you should get the directory layout, the 
init script, the kernel build and packaging, and the run-qemu.sh for the 
target, with whatever additional files YOU installed (under $ROOT/). So 
a busybox based filesystem instead of toybox shouldn't be a big lift, 
just wasn't a priority for me.

But since you're not testing _toybox_, and toybox isn't 1.0 yet, that 
would probably give you an environment much closer to what you want. :)

Note that mkroot.sh is almost entirely self-contained, merging it into 
toybox and having LINUX= be specified on the command line was just so I 
didn't have to download any packages in the default build. (There's 
download plumbing in scripts/plumbing, I think I went over that in the 
talk. The "dropbear" package is another one to read to see how that 
stuff works.)

Rob

^ permalink raw reply

* No Boot-Time SIG meeting in May
From: Bird, Tim @ 2026-05-22 16:55 UTC (permalink / raw)
  To: Embedded Linux mailing list

Hello everyone,

Given that ELC just happened and I haven't had time to prepare the agenda, and
Memorial Day and Embedded Recipes are next week, I have decided to cancel
our monthly Boot-Time Special Interest Group meeting for May.
It would have been May 26, next Tuesday.

There was some good material (IMHO) presented at Embedded Linux Conference
on the subject of Linux boot time, including my Boot-Time status overview talk and
a talk by Gokul Praveen and Beleswar Prasad Padhi of TI on 'Bootph' markers for device
tree for optimizing u-boot device handling during bootup.  The slides for these
talks are available now, and hopefully videos will be available shortly.

Let's plan to regroup for a SIG call in June.  I'm currently looking at June 30 (the last Tuesday
in June) for the next SIG call.  See the Boot-Time page on the elinux wiki for details:
https://elinux.org/Boot_Time#Monthly_Meeting

   -- Tim

^ permalink raw reply

* Re: [PATCH RFC v3 0/3] Add splash DRM client
From: Francesco Valla @ 2026-05-12 17:41 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan, Sam Ravnborg, linux-kernel,
	dri-devel, linux-doc, linux-embedded
In-Reply-To: <5d7067de-97b7-4232-9cf6-e4b978696482@amd.com>

Hello Mario,

Thank you for taking a stab at this.

On Mon, May 11, 2026 at 08:59:14PM -0500, Mario Limonciello wrote:
> 
> 
> On 5/10/26 16:29, Francesco Valla wrote:
> > Hello,
> > 
> > this is the third (and hopefully last) RFC version for the DRM-based
> > splash screen.
> > 
> > Motivation behind the work can be found in v1 [0]; in a nutshell, the
> > splash DRM client can draw a splashscreen using:
> > 
> >    - the BMP image supplied by the EFI BGRT;
> >    - a BMP image loaded as firmware (either built-in or loaded from the
> >      filesystem);
> >    - a colored background.
> > 
> > This revision greatly simplifies the image seletion logic; now the EFI
> > BGRT is always used as first source if enabled, with a fallback to BMP
> > image loaded as firmware and then to a plain color.
> > 
> > Sanity checks on the EFI BGRT image have been borrowed from the efifb
> > driver. More complete splash providers (e.g.: Plymouth) have an
> > extensive management of platform-specific quirks, but I don't think it
> > would be reasonable to introduce such complexity here.
> > 
> > Additional notes:
> >    - Rotation is still not managed (and probably won't?).
> >    - Support for tiled screens is untested.
> >    - Plain color and BMP sources were tested on QEMU, Beagleplay and
> >      i.MX93 FRDM.
> >    - EFI BGRT support was tested using QEMU+OVMF.
> > 
> > Thank you in advance for any feedback.
> 
> Unfortunately I found that I couldn't compile with my normal Kconfig.
> 
> ERROR: modpost: "bgrt_tab" [drivers/gpu/drm/clients/drm_client_lib.ko]
> undefined!
> ERROR: modpost: "bgrt_image_size"
> [drivers/gpu/drm/clients/drm_client_lib.ko] undefined!
> make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1
> make[1]: *** [/home/supermario/src/linux/Makefile:2091: modpost] Error 2
> make: *** [Makefile:248: __sub-make] Error 2
> 
> ❮ grep ^CONFIG_DRM .config
> CONFIG_DRM=y
> CONFIG_DRM_KMS_HELPER=m
> CONFIG_DRM_DRAW=y
> CONFIG_DRM_CLIENT=y
> CONFIG_DRM_CLIENT_LIB=m

Here lies the source of the issue, since I forgot to export the BGRT
table symbols. In my test setup I had the clients built-in and didn't
catch this. A simple patch (which will be included in v4) is attached.

> CONFIG_DRM_CLIENT_SELECTION=m
> CONFIG_DRM_CLIENT_SETUP=y
> CONFIG_DRM_FBDEV_EMULATION=y
> CONFIG_DRM_FBDEV_OVERALLOC=100
> CONFIG_DRM_CLIENT_SPLASH=y
> CONFIG_DRM_CLIENT_SPLASH_BACKGROUND_COLOR=0x000000
> CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT=y
> CONFIG_DRM_CLIENT_SPLASH_BMP_SUPPORT=y
> CONFIG_DRM_CLIENT_DEFAULT_SPLASH=y
> CONFIG_DRM_CLIENT_DEFAULT="splash"
> CONFIG_DRM_LOAD_EDID_FIRMWARE=y
> CONFIG_DRM_DISPLAY_HELPER=m
> CONFIG_DRM_DISPLAY_DP_AUX_CHARDEV=y
> CONFIG_DRM_DISPLAY_DP_HELPER=y
> CONFIG_DRM_DISPLAY_DSC_HELPER=y
> CONFIG_DRM_DISPLAY_HDCP_HELPER=y
> CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER=y
> CONFIG_DRM_DISPLAY_HDMI_HELPER=y
> CONFIG_DRM_TTM=m
> CONFIG_DRM_EXEC=m
> CONFIG_DRM_BUDDY=m
> CONFIG_DRM_TTM_HELPER=m
> CONFIG_DRM_GEM_SHMEM_HELPER=m
> CONFIG_DRM_SUBALLOC_HELPER=m
> CONFIG_DRM_SCHED=m
> CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS=m
> CONFIG_DRM_PRIVACY_SCREEN=y
> CONFIG_DRM_AMDGPU=m
> CONFIG_DRM_AMDGPU_CIK=y
> CONFIG_DRM_AMDGPU_USERPTR=y
> CONFIG_DRM_AMD_ISP=y
> CONFIG_DRM_AMD_ACP=y
> CONFIG_DRM_AMD_DC=y
> CONFIG_DRM_AMD_DC_FP=y
> CONFIG_DRM_AMD_SECURE_DISPLAY=y
> CONFIG_DRM_BRIDGE=y
> CONFIG_DRM_PANEL_BRIDGE=y
> CONFIG_DRM_PANEL=y
> CONFIG_DRM_SYSFB_HELPER=m
> CONFIG_DRM_SIMPLEDRM=m
> CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
> CONFIG_DRM_ACCEL=y
> CONFIG_DRM_ACCEL_AMDXDNA=m
> CONFIG_DRM_ACCEL_HABANALABS=m
> CONFIG_DRM_ACCEL_IVPU=m
> CONFIG_DRM_ACCEL_QAIC=m
> ❮ grep BGRT .config
> CONFIG_ACPI_BGRT=y
> CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT=y
>

Regards,
Francesco

---
 drivers/firmware/efi/efi-bgrt.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/firmware/efi/efi-bgrt.c b/drivers/firmware/efi/efi-bgrt.c
index 1da451582812..4ca06ed5d6f5 100644
--- a/drivers/firmware/efi/efi-bgrt.c
+++ b/drivers/firmware/efi/efi-bgrt.c
@@ -17,7 +17,10 @@
 #include <linux/efi-bgrt.h>
 
 struct acpi_table_bgrt bgrt_tab;
+EXPORT_SYMBOL(bgrt_tab);
+
 size_t bgrt_image_size;
+EXPORT_SYMBOL(bgrt_image_size);
 
 struct bmp_header {
        u16 id;
--


^ permalink raw reply related

* [PATCH] unseeded cnrg warning flooding dmesg fix, v2
From: Roberto A. Foglietta @ 2026-05-12  4:11 UTC (permalink / raw)
  To: Embedded Linux mailing list
  Cc: Sasha Levin, Hardik Garg, Vijayendra Suman, Jon Hunter,
	Ron Economos, Brett A C Sheffield, Florian Fainelli, Mark Brown

[-- Attachment #1: Type: text/plain, Size: 2338 bytes --]

Hi,

A patch based on 5.15.202, in-line for comments + attachment as patch

Public patch by url / commit for future reference:

https://raw.githubusercontent.com/robang74/uchaosys/
4ddd9859582441afe0faf43a227124d8cf579c5c/
cnfg/bothering-warn_unseeded_randomness-fix.patch

Best regards,
Roberto A. Foglietta

From df61514c6e07ae722c87b2727999bdc49a08bcaa Mon Sep 17 00:00:00 2001
From: "Roberto A. Foglietta" <roberto.foglietta@gmail.com>
Date: Tue, 12 May 2026 05:39:41 +0200
Subject: [PATCH] unseeded cnrg warning flooding dmesg fix, v2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Better than hiding the warning, there is a third approach compared
print_ratelimited() and printk_once(), printk one over N=256 which
is fitting this specific case: at boot time too much warnings for
a few seconds but still printing something in case the problem
persists without clogging the log.

On a SMP system the counter might face a race condition due to non
atomic increment and this can lead to skipping a scheduled print or
print two of them when just one was expected. No harm in both cases,
anyway.

To be honest, complaining that a suppression counter might be subject
to a race condition on an SMP system - when, if it weren’t there, an
alarm would be triggered stating that the system is initialising
cryptographic directives without adequate entropy support - seems
a bit surreal to me.

Patch based on Linux kernel 5.15.202 LTS master branch.

v1 -> v2: a bit better English and "git am" format

Signed-off-by: Roberto A. Foglietta <roberto.foglietta@gmail.com>
---
 drivers/char/random.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 8642326de6e1..eeec336b10fa 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -182,7 +182,8 @@ static void __cold process_random_ready_list(void)
 }

 #define warn_unseeded_randomness() \
- if (IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) && !crng_ready()) \
+ if (IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) && !crng_ready() \
+ && !({ static u8 x=-1; ++x; })) \
  printk_deferred(KERN_NOTICE "random: %s called from %pS with
crng_init=%d\n", \
  __func__, (void *)_RET_IP_, crng_init)

-- 
2.34.1

[-- Attachment #2: 0001-unseeded-cnrg-warning-flooding-dmesg-fix-v2.patch --]
[-- Type: text/x-patch, Size: 1971 bytes --]

From df61514c6e07ae722c87b2727999bdc49a08bcaa Mon Sep 17 00:00:00 2001
From: "Roberto A. Foglietta" <roberto.foglietta@gmail.com>
Date: Tue, 12 May 2026 05:39:41 +0200
Subject: [PATCH] unseeded cnrg warning flooding dmesg fix, v2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Better than hiding the warning, there is a third approach compared
print_ratelimited() and printk_once(), printk one over N=256 which
is fitting this specific case: at boot time too much warnings for
a few seconds but still printing something in case the problem
persists without clogging the log.

On a SMP system the counter might face a race condition due to non
atomic increment and this can lead to skipping a scheduled print or
print two of them when just one was expected. No harm in both cases,
anyway.

To be honest, complaining that a suppression counter might be subject
to a race condition on an SMP system - when, if it weren’t there, an
alarm would be triggered stating that the system is initialising
cryptographic directives without adequate entropy support - seems
a bit surreal to me.

Patch based on Linux kernel 5.15.202 LTS master branch.

v1 -> v2: a bit better English and "git am" format

Signed-off-by: Roberto A. Foglietta <roberto.foglietta@gmail.com>
---
 drivers/char/random.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 8642326de6e1..eeec336b10fa 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -182,7 +182,8 @@ static void __cold process_random_ready_list(void)
 }
 
 #define warn_unseeded_randomness() \
-	if (IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) && !crng_ready()) \
+	if (IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) && !crng_ready() \
+	&& !({ static u8 x=-1; ++x; })) \
 		printk_deferred(KERN_NOTICE "random: %s called from %pS with crng_init=%d\n", \
 				__func__, (void *)_RET_IP_, crng_init)
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH RFC v3 0/3] Add splash DRM client
From: Mario Limonciello @ 2026-05-12  1:59 UTC (permalink / raw)
  To: Francesco Valla, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet,
	Jocelyn Falempe, Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>



On 5/10/26 16:29, Francesco Valla wrote:
> Hello,
> 
> this is the third (and hopefully last) RFC version for the DRM-based
> splash screen.
> 
> Motivation behind the work can be found in v1 [0]; in a nutshell, the
> splash DRM client can draw a splashscreen using:
> 
>    - the BMP image supplied by the EFI BGRT;
>    - a BMP image loaded as firmware (either built-in or loaded from the
>      filesystem);
>    - a colored background.
> 
> This revision greatly simplifies the image seletion logic; now the EFI
> BGRT is always used as first source if enabled, with a fallback to BMP
> image loaded as firmware and then to a plain color.
> 
> Sanity checks on the EFI BGRT image have been borrowed from the efifb
> driver. More complete splash providers (e.g.: Plymouth) have an
> extensive management of platform-specific quirks, but I don't think it
> would be reasonable to introduce such complexity here.
> 
> Additional notes:
>    - Rotation is still not managed (and probably won't?).
>    - Support for tiled screens is untested.
>    - Plain color and BMP sources were tested on QEMU, Beagleplay and
>      i.MX93 FRDM.
>    - EFI BGRT support was tested using QEMU+OVMF.
> 
> Thank you in advance for any feedback.

Unfortunately I found that I couldn't compile with my normal Kconfig.

ERROR: modpost: "bgrt_tab" [drivers/gpu/drm/clients/drm_client_lib.ko] 
undefined!
ERROR: modpost: "bgrt_image_size" 
[drivers/gpu/drm/clients/drm_client_lib.ko] undefined!
make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1
make[1]: *** [/home/supermario/src/linux/Makefile:2091: modpost] Error 2
make: *** [Makefile:248: __sub-make] Error 2

❮ grep ^CONFIG_DRM .config
CONFIG_DRM=y
CONFIG_DRM_KMS_HELPER=m
CONFIG_DRM_DRAW=y
CONFIG_DRM_CLIENT=y
CONFIG_DRM_CLIENT_LIB=m
CONFIG_DRM_CLIENT_SELECTION=m
CONFIG_DRM_CLIENT_SETUP=y
CONFIG_DRM_FBDEV_EMULATION=y
CONFIG_DRM_FBDEV_OVERALLOC=100
CONFIG_DRM_CLIENT_SPLASH=y
CONFIG_DRM_CLIENT_SPLASH_BACKGROUND_COLOR=0x000000
CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT=y
CONFIG_DRM_CLIENT_SPLASH_BMP_SUPPORT=y
CONFIG_DRM_CLIENT_DEFAULT_SPLASH=y
CONFIG_DRM_CLIENT_DEFAULT="splash"
CONFIG_DRM_LOAD_EDID_FIRMWARE=y
CONFIG_DRM_DISPLAY_HELPER=m
CONFIG_DRM_DISPLAY_DP_AUX_CHARDEV=y
CONFIG_DRM_DISPLAY_DP_HELPER=y
CONFIG_DRM_DISPLAY_DSC_HELPER=y
CONFIG_DRM_DISPLAY_HDCP_HELPER=y
CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER=y
CONFIG_DRM_DISPLAY_HDMI_HELPER=y
CONFIG_DRM_TTM=m
CONFIG_DRM_EXEC=m
CONFIG_DRM_BUDDY=m
CONFIG_DRM_TTM_HELPER=m
CONFIG_DRM_GEM_SHMEM_HELPER=m
CONFIG_DRM_SUBALLOC_HELPER=m
CONFIG_DRM_SCHED=m
CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS=m
CONFIG_DRM_PRIVACY_SCREEN=y
CONFIG_DRM_AMDGPU=m
CONFIG_DRM_AMDGPU_CIK=y
CONFIG_DRM_AMDGPU_USERPTR=y
CONFIG_DRM_AMD_ISP=y
CONFIG_DRM_AMD_ACP=y
CONFIG_DRM_AMD_DC=y
CONFIG_DRM_AMD_DC_FP=y
CONFIG_DRM_AMD_SECURE_DISPLAY=y
CONFIG_DRM_BRIDGE=y
CONFIG_DRM_PANEL_BRIDGE=y
CONFIG_DRM_PANEL=y
CONFIG_DRM_SYSFB_HELPER=m
CONFIG_DRM_SIMPLEDRM=m
CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
CONFIG_DRM_ACCEL=y
CONFIG_DRM_ACCEL_AMDXDNA=m
CONFIG_DRM_ACCEL_HABANALABS=m
CONFIG_DRM_ACCEL_IVPU=m
CONFIG_DRM_ACCEL_QAIC=m
❮ grep BGRT .config
CONFIG_ACPI_BGRT=y
CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT=y

> 
> Best regards,
> Francesco
> 
> [0] https://lore.kernel.org/all/20251027-drm_client_splash-v1-0-00698933b34a@valla.it
> 
> Signed-off-by: Francesco Valla <francesco@valla.it>
> ---
> Changes in v3:
>    - Simplified the image selection and management logic, with direct
>      fallback from EFI BGRT to MP as firmware
>    - Used new drm_draw_can_convert_from_xrgb8888() API
>    - Added proper get_unaligned_ calls for EFI BGRT access
>    - Fixed Kconfig dependencies
>    - Link to v2: https://lore.kernel.org/r/20260106-drm_client_splash-v2-0-6e86a7434b59@valla.it
> 
> Changes in v2:
>    - Moved from raw dump to BMP format for static image source
>    - Removed support for configurable message
>    - Removed support for progress bar
>    - Added EFI BGRT as image source
> Link to v1: https://lore.kernel.org/r/20251027-drm_client_splash-v1-0-00698933b34a@valla.it
> 
> To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> To: Maxime Ripard <mripard@kernel.org>
> To: Thomas Zimmermann <tzimmermann@suse.de>
> To: David Airlie <airlied@gmail.com>
> To: Simona Vetter <simona@ffwll.ch>
> To: Francesco Valla <francesco@valla.it>
> To: Jonathan Corbet <corbet@lwn.net>
> To: Shuah Khan <skhan@linuxfoundation.org>
> Cc: linux-kernel@vger.kernel.org
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-doc@vger.kernel.org
> 
> ---
> Francesco Valla (3):
>        drm: client: add splash client
>        MAINTAINERS: add entry for DRM splash client
>        drm: docs: remove bootsplash from TODO
> 
>   Documentation/gpu/todo.rst                    |  17 -
>   MAINTAINERS                                   |   7 +
>   drivers/gpu/drm/clients/Kconfig               |  63 +-
>   drivers/gpu/drm/clients/Makefile              |   1 +
>   drivers/gpu/drm/clients/drm_client_internal.h |   9 +
>   drivers/gpu/drm/clients/drm_client_setup.c    |   8 +
>   drivers/gpu/drm/clients/drm_splash.c          | 932 ++++++++++++++++++++++++++
>   7 files changed, 1019 insertions(+), 18 deletions(-)
> ---
> base-commit: afaa0a477099cb7256e26fe11289c753a225ac97
> change-id: 20251026-drm_client_splash-e10d7d663e7f
> 
> Best regards,
> --
> Francesco Valla <francesco@valla.it>
> 


^ permalink raw reply

* [PATCH RFC v3 0/3] Add splash DRM client
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla

Hello,

this is the third (and hopefully last) RFC version for the DRM-based
splash screen.

Motivation behind the work can be found in v1 [0]; in a nutshell, the
splash DRM client can draw a splashscreen using:

  - the BMP image supplied by the EFI BGRT;
  - a BMP image loaded as firmware (either built-in or loaded from the
    filesystem);
  - a colored background.

This revision greatly simplifies the image seletion logic; now the EFI
BGRT is always used as first source if enabled, with a fallback to BMP
image loaded as firmware and then to a plain color.

Sanity checks on the EFI BGRT image have been borrowed from the efifb
driver. More complete splash providers (e.g.: Plymouth) have an
extensive management of platform-specific quirks, but I don't think it
would be reasonable to introduce such complexity here.

Additional notes:
  - Rotation is still not managed (and probably won't?).
  - Support for tiled screens is untested.
  - Plain color and BMP sources were tested on QEMU, Beagleplay and
    i.MX93 FRDM.
  - EFI BGRT support was tested using QEMU+OVMF.

Thank you in advance for any feedback.

Best regards,
Francesco

[0] https://lore.kernel.org/all/20251027-drm_client_splash-v1-0-00698933b34a@valla.it

Signed-off-by: Francesco Valla <francesco@valla.it>
---
Changes in v3:
  - Simplified the image selection and management logic, with direct
    fallback from EFI BGRT to MP as firmware
  - Used new drm_draw_can_convert_from_xrgb8888() API
  - Added proper get_unaligned_ calls for EFI BGRT access
  - Fixed Kconfig dependencies
  - Link to v2: https://lore.kernel.org/r/20260106-drm_client_splash-v2-0-6e86a7434b59@valla.it

Changes in v2:
  - Moved from raw dump to BMP format for static image source
  - Removed support for configurable message
  - Removed support for progress bar
  - Added EFI BGRT as image source
Link to v1: https://lore.kernel.org/r/20251027-drm_client_splash-v1-0-00698933b34a@valla.it

To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
To: Maxime Ripard <mripard@kernel.org>
To: Thomas Zimmermann <tzimmermann@suse.de>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
To: Francesco Valla <francesco@valla.it>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-kernel@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-doc@vger.kernel.org

---
Francesco Valla (3):
      drm: client: add splash client
      MAINTAINERS: add entry for DRM splash client
      drm: docs: remove bootsplash from TODO

 Documentation/gpu/todo.rst                    |  17 -
 MAINTAINERS                                   |   7 +
 drivers/gpu/drm/clients/Kconfig               |  63 +-
 drivers/gpu/drm/clients/Makefile              |   1 +
 drivers/gpu/drm/clients/drm_client_internal.h |   9 +
 drivers/gpu/drm/clients/drm_client_setup.c    |   8 +
 drivers/gpu/drm/clients/drm_splash.c          | 932 ++++++++++++++++++++++++++
 7 files changed, 1019 insertions(+), 18 deletions(-)
---
base-commit: afaa0a477099cb7256e26fe11289c753a225ac97
change-id: 20251026-drm_client_splash-e10d7d663e7f

Best regards,
--  
Francesco Valla <francesco@valla.it>


^ permalink raw reply

* [PATCH RFC v3 3/3] drm: docs: remove bootsplash from TODO
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>

Now that a splash client exists, remove the bootsplash task from the
TODO list for the DRM subsystem.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 Documentation/gpu/todo.rst | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index bc9f14c8a2ec..f367d8980a87 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -802,23 +802,6 @@ See drivers/gpu/drm/amd/display/TODO for tasks.
 
 Contact: Harry Wentland, Alex Deucher
 
-Bootsplash
-==========
-
-There is support in place now for writing internal DRM clients making it
-possible to pick up the bootsplash work that was rejected because it was written
-for fbdev.
-
-- [v6,8/8] drm/client: Hack: Add bootsplash example
-  https://patchwork.freedesktop.org/patch/306579/
-
-- [RFC PATCH v2 00/13] Kernel based bootsplash
-  https://lore.kernel.org/r/20171213194755.3409-1-mstaudt@suse.de
-
-Contact: Sam Ravnborg
-
-Level: Advanced
-
 Brightness handling on devices with multiple internal panels
 ============================================================
 

-- 
2.54.0


^ permalink raw reply related

* [PATCH RFC v3 1/3] drm: client: add splash client
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>

Add a DRM client that draws a simple splash, with possibility to show:

  - the logo provided by EFI BGRT;
  - a static BMP image (loaded as firmware);
  - a colored background.

The client is not meant to replace a full-featured bootsplash, but
rather to remove some complexity (and hopefully boot time) on small
embedded platforms or on systems with a limited scope (e.g: recovery
or manufacturing images).

The background color can be set either at build time from a dedicated
config option or at runtime through the drm_client_lib.splash_color
command line parameter. Any color in RGB888 format can be used.

If enabled, the static BMP image is loaded using the kernel firmware
infrastructure; a valid BMP image with 24bpp color and no compression
is expected. The name of the image can be set through the
drm_client_lib.splash_bmp kernel command line parameter, with the
default being 'drm_splash.bmp'.

Just like the existing DRM clients, the splash can be enabled from the
kernel command line using drm_client_lib.active=splash.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 drivers/gpu/drm/clients/Kconfig               |  63 +-
 drivers/gpu/drm/clients/Makefile              |   1 +
 drivers/gpu/drm/clients/drm_client_internal.h |   9 +
 drivers/gpu/drm/clients/drm_client_setup.c    |   8 +
 drivers/gpu/drm/clients/drm_splash.c          | 932 ++++++++++++++++++++++++++
 5 files changed, 1012 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/clients/Kconfig b/drivers/gpu/drm/clients/Kconfig
index 6096c623d9d5..dc114429c1f1 100644
--- a/drivers/gpu/drm/clients/Kconfig
+++ b/drivers/gpu/drm/clients/Kconfig
@@ -12,6 +12,7 @@ config DRM_CLIENT_LIB
 config DRM_CLIENT_SELECTION
 	tristate
 	depends on DRM
+	select DRM_CLIENT_LIB if DRM_CLIENT_SPLASH
 	select DRM_CLIENT_LIB if DRM_CLIENT_LOG
 	select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION
 	help
@@ -85,10 +86,63 @@ config DRM_CLIENT_LOG
 	  If you only need logs, but no terminal, or if you prefer userspace
 	  terminal, say "Y".
 
+config DRM_CLIENT_SPLASH
+	bool "Display graphic splash"
+	depends on DRM_CLIENT_SELECTION
+	select DRM_CLIENT
+	select DRM_CLIENT_SETUP
+	select DRM_DRAW
+	help
+	  This enables a splash drm client, able to display either a plain
+	  color or a static image until the userspace is ready to take over.
+	  The splash will be displayed on all screens available at boot, if
+	  any, or on the ones part of the first hotplug event.
+
+config DRM_CLIENT_SPLASH_BACKGROUND_COLOR
+	hex "Splash background color"
+	depends on DRM_CLIENT_SPLASH
+	default 0x000000
+	help
+	  The default splash background color, in RGB888 format.
+
+	  The color can be overridden through the drm_client_lib.splash_color
+	  kernel command line parameter.
+
+config DRM_CLIENT_SPLASH_SRC_BGRT
+	bool "EFI BGRT as splash source"
+	depends on DRM_CLIENT_SPLASH
+	depends on ACPI_BGRT
+	select DRM_CLIENT_SPLASH_BMP_SUPPORT
+	help
+	  Use the BGRT image provided by the EFI bootloader. If the image is
+	  smaller than the display(s), it will be centered and the color
+	  specified through the DRM_CLIENT_SPLASH_BACKGROUND_COLOR config
+	  option will be used as background.
+
+config DRM_CLIENT_SPLASH_SRC_BMP
+	bool "BMP image as splash source"
+	depends on DRM_CLIENT_SPLASH
+	select DRM_CLIENT_SPLASH_BMP_SUPPORT
+	select FW_LOADER
+	help
+	  Use a BMP (bitmap) image as splash. If the image is smaller than the
+	  display(s), it will be centered and the color specified through the
+	  DRM_CLIENT_SPLASH_BACKGROUND_COLOR config option will be used as
+	  background.
+
+	  The image will be loaded using the firmware loading facility the
+	  kernel provides; it shall use 24 bits per pixel and shall not be
+	  compressed. The name of the file can be set through the
+	  drm_client_lib.splash_bmp command line parameter, with the default
+	  being 'drm_splash.bmp'.
+
+config DRM_CLIENT_SPLASH_BMP_SUPPORT
+	bool
+
 choice
 	prompt "Default DRM Client"
 	depends on DRM_CLIENT_SELECTION
-	depends on DRM_FBDEV_EMULATION || DRM_CLIENT_LOG
+	depends on DRM_FBDEV_EMULATION || DRM_CLIENT_LOG || DRM_CLIENT_SPLASH
 	default DRM_CLIENT_DEFAULT_FBDEV
 	help
 	  Selects the default drm client.
@@ -111,6 +165,12 @@ config DRM_CLIENT_DEFAULT_LOG
 	  screen, but doesn't implement a full terminal. For that you will need
 	  a userspace terminal using drm/kms.
 
+config DRM_CLIENT_DEFAULT_SPLASH
+	bool "splash"
+	depends on DRM_CLIENT_SPLASH
+	help
+	  Use splash as default drm client.
+
 endchoice
 
 config DRM_CLIENT_DEFAULT
@@ -118,6 +178,7 @@ config DRM_CLIENT_DEFAULT
        depends on DRM_CLIENT
        default "fbdev" if DRM_CLIENT_DEFAULT_FBDEV
        default "log" if DRM_CLIENT_DEFAULT_LOG
+       default "splash" if DRM_CLIENT_DEFAULT_SPLASH
        default ""
 
 endmenu
diff --git a/drivers/gpu/drm/clients/Makefile b/drivers/gpu/drm/clients/Makefile
index c16addbc327f..3df02d10cd18 100644
--- a/drivers/gpu/drm/clients/Makefile
+++ b/drivers/gpu/drm/clients/Makefile
@@ -5,4 +5,5 @@ subdir-ccflags-y += -I$(src)/..
 drm_client_lib-y := drm_client_setup.o
 drm_client_lib-$(CONFIG_DRM_CLIENT_LOG) += drm_log.o
 drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o
+drm_client_lib-$(CONFIG_DRM_CLIENT_SPLASH) += drm_splash.o
 obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o
diff --git a/drivers/gpu/drm/clients/drm_client_internal.h b/drivers/gpu/drm/clients/drm_client_internal.h
index 6dc078bf6503..48ee0c1c2529 100644
--- a/drivers/gpu/drm/clients/drm_client_internal.h
+++ b/drivers/gpu/drm/clients/drm_client_internal.h
@@ -22,4 +22,13 @@ void drm_log_register(struct drm_device *dev);
 static inline void drm_log_register(struct drm_device *dev) {}
 #endif
 
+#ifdef CONFIG_DRM_CLIENT_SPLASH
+void drm_splash_register(struct drm_device *dev,
+			 const struct drm_format_info *format);
+#else
+static inline void drm_splash_register(struct drm_device *dev,
+				       const struct drm_format_info *format)
+{}
+#endif
+
 #endif
diff --git a/drivers/gpu/drm/clients/drm_client_setup.c b/drivers/gpu/drm/clients/drm_client_setup.c
index 515aceac22b1..c19498938ee3 100644
--- a/drivers/gpu/drm/clients/drm_client_setup.c
+++ b/drivers/gpu/drm/clients/drm_client_setup.c
@@ -56,6 +56,14 @@ void drm_client_setup(struct drm_device *dev, const struct drm_format_info *form
 		return;
 	}
 #endif
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH
+	if (!strcmp(drm_client_default, "splash")) {
+		drm_splash_register(dev, format);
+		return;
+	}
+#endif
+
 	if (strcmp(drm_client_default, ""))
 		drm_warn(dev, "Unknown DRM client %s\n", drm_client_default);
 }
diff --git a/drivers/gpu/drm/clients/drm_splash.c b/drivers/gpu/drm/clients/drm_splash.c
new file mode 100644
index 000000000000..a038807019c0
--- /dev/null
+++ b/drivers/gpu/drm/clients/drm_splash.c
@@ -0,0 +1,932 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/*
+ * Copyright (c) 2025-2026 Francesco Valla <francesco@valla.it>
+ */
+
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/efi-bgrt.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/iosys-map.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include <acpi/actbl1.h>
+
+#include <drm/drm_client.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "drm_client_internal.h"
+#include "drm_draw_internal.h"
+#include "drm_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * This is a simple graphic bootsplash, able to display either a plain color or
+ * a static image.
+ */
+
+static unsigned int splash_color = CONFIG_DRM_CLIENT_SPLASH_BACKGROUND_COLOR;
+module_param(splash_color, uint, 0400);
+MODULE_PARM_DESC(splash_color, "Splash background color (RGB888)");
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BMP)
+#define DEFAULT_SPLASH_BMP "drm_splash.bmp"
+static char *splash_bmp = DEFAULT_SPLASH_BMP;
+module_param(splash_bmp, charp, 0400);
+MODULE_PARM_DESC(splash_bmp, "Name of splash image (default: \"" DEFAULT_SPLASH_BMP "\")");
+#endif // CONFIG_DRM_CLIENT_SPLASH_SRC_BMP
+
+#define BMP_FILE_MAGIC_ID 0x4d42
+
+/* BMP header structures copied from drivers/video/fbdev/efifb.c */
+struct bmp_file_header {
+	__le16 id;
+	__le32 file_size;
+	__le32 reserved;
+	__le32 bitmap_offset;
+} __packed;
+
+struct bmp_dib_header {
+	__le32 dib_header_size;
+	__le32 width;
+	__le32 height;
+	__le16 planes;
+	__le16 bpp;
+	__le32 compression;
+	__le32 bitmap_size;
+	__le32 horz_resolution;
+	__le32 vert_resolution;
+	__le32 colors_used;
+	__le32 colors_important;
+} __packed;
+
+struct drm_splash_scanout {
+	int id;
+	u32 format;
+	unsigned int width;
+	unsigned int height;
+	struct drm_client_buffer *buffer;
+	bool bg_drawn;
+	bool img_drawn;
+};
+
+struct drm_splash {
+	struct drm_client_dev client;
+	u32 preferred_format;
+	struct device dev;
+
+	struct task_struct *thread;
+	atomic_t pending;
+
+	struct mutex hotplug_lock;
+	bool initialized;
+
+	u32 n_scanout;
+	struct drm_splash_scanout *scanout;
+
+	spinlock_t fw_lock;
+	const struct firmware *fw;
+	void *map_data;
+
+	bool use_bgrt;
+};
+
+static struct drm_splash *client_to_drm_splash(struct drm_client_dev *client)
+{
+	return container_of_const(client, struct drm_splash, client);
+}
+
+static struct drm_splash_scanout *
+get_scanout_from_tile_group(struct drm_splash *splash, int id)
+{
+	int j;
+
+	for (j = 0; j < splash->n_scanout; j++)
+		if (splash->scanout[j].id == id)
+			return &splash->scanout[j];
+
+	return NULL;
+}
+
+static inline void drm_splash_wake_render_thread(struct drm_splash *splash)
+{
+	wake_up_process(splash->thread);
+}
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BMP)
+static int drm_splash_fw_load(struct drm_splash *splash, const u8 **data,
+			      size_t *size)
+{
+	const struct firmware *fw;
+
+	scoped_guard(spinlock, &splash->fw_lock)
+		fw = splash->fw;
+
+	if (!fw)
+		return -ENOENT;
+
+	*data = fw->data;
+	*size = fw->size;
+
+	return 0;
+}
+
+static void drm_splash_fw_callback(const struct firmware *fw, void *context)
+{
+	struct drm_splash *splash = context;
+	struct drm_client_dev *client = &splash->client;
+
+	if (!fw || !fw->data) {
+		drm_err(client->dev, "splash: no firmware");
+		return;
+	}
+
+	scoped_guard(spinlock, &splash->fw_lock)
+		splash->fw = fw;
+
+	/* Wake the render thread */
+	drm_dbg(client->dev, "splash: firmware loaded, wake up drawing thread");
+	atomic_set(&splash->pending, 1);
+	drm_splash_wake_render_thread(splash);
+}
+
+static int drm_splash_fw_request_bmp(struct drm_splash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+
+	drm_info(client->dev, "splash: request %s as firmware", splash_bmp);
+
+	return request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+				       splash_bmp, client->dev->dev, GFP_KERNEL,
+				       splash, drm_splash_fw_callback);
+}
+#else
+static inline int drm_splash_fw_load(struct drm_splash *splash, const u8 **data,
+				     size_t *size)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int drm_splash_fw_request_bmp(struct drm_splash *splash)
+{
+	return -EOPNOTSUPP;
+}
+#endif // CONFIG_DRM_CLIENT_SPLASH_SRC_BMP
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT)
+static bool drm_splash_bgrt_available(struct drm_splash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+
+	if (!bgrt_tab.image_address) {
+		drm_info(client->dev, "splash: no BGRT found");
+		return false;
+	}
+
+	if (bgrt_tab.status & ACPI_BGRT_ORIENTATION_OFFSET) {
+		drm_info(client->dev, "splash: BGRT rotation bits set, skipping");
+		return false;
+	}
+
+	return true;
+}
+
+static inline unsigned int drm_splash_bgrt_get_xoffset(void)
+{
+	return bgrt_tab.image_offset_x;
+}
+
+static inline unsigned int drm_splash_bgrt_get_yoffset(void)
+{
+	return bgrt_tab.image_offset_y;
+}
+
+static int drm_splash_bgrt_load(struct drm_splash *splash, const u8 **data,
+				size_t *size)
+{
+	struct drm_client_dev *client = &splash->client;
+
+	if (!drm_splash_bgrt_available(splash))
+		return -ENOENT;
+
+	drm_dbg(client->dev, "splash: BGRT image is at 0x%016llx, size=%zX",
+		bgrt_tab.image_address, bgrt_image_size);
+
+	splash->map_data = memremap(bgrt_tab.image_address, bgrt_image_size,
+				    MEMREMAP_WB);
+	if (!splash->map_data) {
+		drm_warn(client->dev, "splash: failed to map BGRT image memory");
+		return -ENOMEM;
+	}
+
+	*data = splash->map_data;
+	*size = bgrt_image_size;
+
+	return 0;
+}
+#else
+static inline bool drm_splash_bgrt_available(struct drm_splash *splash)
+{
+	return false;
+}
+
+static inline unsigned int drm_splash_bgrt_get_xoffset(void)
+{
+	return 0;
+}
+
+static inline unsigned int drm_splash_bgrt_get_yoffset(void)
+{
+	return 0;
+}
+
+static inline int drm_splash_bgrt_load(struct drm_splash *splash,
+				       const u8 **data, size_t *size)
+{
+	return -EOPNOTSUPP;
+}
+#endif // CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT
+
+static u32 drm_splash_find_usable_format(struct drm_plane *plane,
+					 u32 preferred_format)
+{
+	int i;
+
+	/* Check if the preferred format can be used */
+	for (i = 0; i < plane->format_count; i++)
+		if (plane->format_types[i] == preferred_format)
+			return preferred_format;
+
+	/* Otherwise, find the first format that can be converted from XRGB8888 */
+	for (i = 0; i < plane->format_count; i++)
+		if (drm_draw_can_convert_from_xrgb8888(plane->format_types[i]))
+			return plane->format_types[i];
+
+	return DRM_FORMAT_INVALID;
+}
+
+static void drm_splash_fill(struct iosys_map *map, unsigned int dst_pitch,
+			    unsigned int height, unsigned int width,
+			    u32 px_width, u32 color)
+{
+	switch (px_width) {
+	case 2:
+		drm_draw_fill16(map, dst_pitch, height, width, color);
+		break;
+	case 3:
+		drm_draw_fill24(map, dst_pitch, height, width, color);
+		break;
+	case 4:
+		drm_draw_fill32(map, dst_pitch, height, width, color);
+		break;
+	default:
+		WARN_ONCE(1, "Can't fill with pixel width %d", px_width);
+	}
+}
+
+static int drm_splash_fill_solid_color(struct drm_client_buffer *buffer,
+				       u32 color)
+{
+	struct drm_client_dev *client = buffer->client;
+	struct drm_framebuffer *fb = buffer->fb;
+	struct drm_rect r = DRM_RECT_INIT(0, 0, fb->width, fb->height);
+	u32 px_width = fb->format->cpp[0];
+	struct iosys_map map;
+	int ret;
+
+	ret = drm_client_buffer_vmap_local(buffer, &map);
+	if (ret) {
+		drm_err(client->dev, "splash: cannot vmap buffer: %d", ret);
+		return ret;
+	}
+
+	drm_splash_fill(&map, fb->pitches[0], drm_rect_height(&r),
+			drm_rect_width(&r), px_width, color);
+
+	drm_client_buffer_vunmap_local(buffer);
+
+	return drm_client_buffer_flush(buffer, &r);
+}
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_BMP_SUPPORT)
+static void drm_splash_blit_pix16(struct iosys_map *map, unsigned int dpitch,
+				  unsigned int x_pad, unsigned int y_pad,
+				  const u8 *sbuf8, unsigned int spitch,
+				  unsigned int width, unsigned int height,
+				  bool invert_y, u32 format)
+{
+	unsigned int x, y, src_offset, dst_offset;
+	u32 scolor, dcolor, wr_off;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch;
+
+		for (x = 0; x < width; x++) {
+			scolor = *(const u32 *)(&sbuf8[src_offset + 3 * x]);
+			dcolor = drm_draw_color_from_xrgb8888(scolor, format);
+			wr_off = dst_offset + (x_pad + x) * sizeof(u16);
+
+			iosys_map_wr(map, wr_off, u16, dcolor);
+		}
+	}
+}
+
+static void drm_splash_blit_pix24(struct iosys_map *map, unsigned int dpitch,
+				  unsigned int x_pad, unsigned int y_pad,
+				  const u8 *sbuf8, unsigned int spitch,
+				  unsigned int width, unsigned int height,
+				  bool invert_y, u32 format)
+{
+	unsigned int x, y, src_offset, dst_offset;
+	u32 scolor, dcolor, wr_off;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch;
+
+		for (x = 0; x < width; x++) {
+			scolor = *(const u32 *)(&sbuf8[src_offset + 3 * x]);
+			dcolor = drm_draw_color_from_xrgb8888(scolor, format);
+			wr_off = dst_offset + (x_pad + x) * 3;
+
+			iosys_map_wr(map, wr_off, u8, (dcolor & 0x000000FF) >> 0);
+			iosys_map_wr(map, wr_off + 1, u8, (dcolor & 0x0000FF00) >> 8);
+			iosys_map_wr(map, wr_off + 2, u8, (dcolor & 0x00FF0000) >> 16);
+		}
+	}
+}
+
+static void drm_splash_blit_pix32(struct iosys_map *map, unsigned int dpitch,
+				  unsigned int x_pad, unsigned int y_pad,
+				  const u8 *sbuf8, unsigned int spitch,
+				  unsigned int width, unsigned int height,
+				  bool invert_y, u32 format)
+{
+	unsigned int x, y, src_offset, dst_offset;
+	u32 scolor, dcolor, wr_off;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch;
+
+		for (x = 0; x < width; x++) {
+			scolor = *(const u32 *)(&sbuf8[src_offset + 3 * x]);
+			dcolor = drm_draw_color_from_xrgb8888(scolor, format);
+			wr_off = dst_offset + (x_pad + x) * sizeof(u32);
+
+			iosys_map_wr(map, wr_off, u32, dcolor);
+		}
+	}
+}
+
+static void drm_splash_blit_rgb888(struct iosys_map *map, unsigned int dpitch,
+				   unsigned int x_pad, unsigned int y_pad,
+				   const u8 *sbuf8, unsigned int spitch,
+				   unsigned int width, unsigned int height,
+				   bool invert_y)
+{
+	unsigned int y, src_offset, dst_offset;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch + x_pad * 3;
+
+		iosys_map_memcpy_to(map, dst_offset, &sbuf8[src_offset], width * 3);
+	}
+}
+
+static int drm_splash_bmp_to_scanout(struct drm_splash *splash,
+				     struct drm_splash_scanout *scanout,
+				     const u8 *data, size_t data_len)
+
+{
+	struct drm_client_buffer *buffer = scanout->buffer;
+	struct drm_client_dev *client = buffer->client;
+	struct drm_framebuffer *fb = buffer->fb;
+	u32 px_width = fb->format->cpp[0];
+	const struct bmp_file_header *file_header;
+	const struct bmp_dib_header *dib_header;
+	u32 dib_header_size;
+	u16 bmp_id, bmp_bpp, bmp_planes;
+	u32 bmp_compression, bmp_pitch;
+	s32 bmp_width, bmp_height;
+	bool bmp_invert_y;
+	u32 bitmap_offset;
+	unsigned int x_pad, y_pad;
+	const u8 *image_data;
+	struct iosys_map map;
+	struct drm_rect r;
+	int ret;
+
+	if (data_len < (sizeof(*file_header) + sizeof(*dib_header))) {
+		drm_err(client->dev, "splash: BMP file too short");
+		return -EINVAL;
+	}
+
+	file_header = (const struct bmp_file_header *)data;
+
+	bmp_id = get_unaligned_le16(&file_header->id);
+	if (bmp_id != BMP_FILE_MAGIC_ID) {
+		drm_err(client->dev, "splash: invalid BMP magic 0x%04X", bmp_id);
+		return -EINVAL;
+	}
+
+	bitmap_offset = get_unaligned_le32(&file_header->bitmap_offset);
+
+	dib_header = (const struct bmp_dib_header *)(data + sizeof(*file_header));
+
+	dib_header_size = get_unaligned_le32(&dib_header->dib_header_size);
+
+	bmp_width = (s32)get_unaligned_le32(&dib_header->width);
+	bmp_height = (s32)get_unaligned_le32(&dib_header->height);
+	bmp_planes = get_unaligned_le16(&dib_header->planes);
+	bmp_bpp = get_unaligned_le16(&dib_header->bpp);
+	bmp_compression = get_unaligned_le32(&dib_header->compression);
+
+	/* Restrict supported format to uncompressed, 24bit RGB888 */
+	if (dib_header_size != 40 || bmp_width < 0 || bmp_planes != 1 ||
+	    bmp_compression != 0 || bmp_bpp != 24) {
+		drm_err(client->dev, "splash: invalid BMP format");
+		return -EINVAL;
+	}
+
+	bmp_pitch = round_up(3 * bmp_width, 4);
+
+	/* A positive height means bottom-to-top scan direction */
+	bmp_invert_y = (bmp_height > 0);
+	bmp_height = abs(bmp_height);
+
+	if ((bitmap_offset + bmp_pitch * bmp_height) > data_len) {
+		drm_err(client->dev, "splash: invalid BMP size");
+		return -EINVAL;
+	}
+
+	if (bmp_width > scanout->width || bmp_height > scanout->height) {
+		drm_err(client->dev, "splash: BMP image is too big for the screen");
+		return -EINVAL;
+	}
+
+	if (splash->use_bgrt) {
+		x_pad = drm_splash_bgrt_get_xoffset();
+		y_pad = drm_splash_bgrt_get_yoffset();
+
+		if ((x_pad + bmp_width) > scanout->width ||
+		    (y_pad + bmp_height) > scanout->height) {
+			drm_err(client->dev, "splash: BGRT image would overflow");
+			return -EINVAL;
+		}
+
+#ifdef CONFIG_X86
+		/*
+		 * BGRT sanity check, taken from efifb.c:
+		 *
+		 * On x86 some firmwares use a low non native resolution for
+		 * the display when they have shown some text messages. While
+		 * keeping the bgrt filled with info for the native resolution.
+		 * If the bgrt image intended for the native resolution still
+		 * fits, it will be displayed very close to the right edge of
+		 * the display looking quite bad.
+		 */
+
+		if (x_pad != (scanout->width - bmp_width) / 2) {
+			drm_err(client->dev, "splash: BGRT sanity check failed");
+			return -EINVAL;
+		}
+#endif
+	} else {
+		/* Center X and Y */
+		x_pad = (scanout->width - bmp_width) / 2;
+		y_pad = (scanout->height - bmp_height) / 2;
+	}
+
+	image_data = data + bitmap_offset;
+
+	ret = drm_client_buffer_vmap_local(buffer, &map);
+	if (ret) {
+		drm_err(client->dev, "splash: cannot vmap buffer: %d", ret);
+		return ret;
+	}
+
+	r = DRM_RECT_INIT(x_pad, y_pad, bmp_width, bmp_height);
+
+	/* In case the target format is RGB888, source data can be copied to
+	 * the video buffer line by line, avoiding some overhead.
+	 */
+	if (scanout->format == DRM_FORMAT_RGB888) {
+		drm_splash_blit_rgb888(&map, fb->pitches[0], x_pad, y_pad,
+				       image_data, bmp_pitch, bmp_width,
+				       bmp_height, bmp_invert_y);
+	} else {
+		switch (px_width) {
+		case 2:
+			drm_splash_blit_pix16(&map, fb->pitches[0], x_pad,
+					      y_pad, image_data, bmp_pitch,
+					      bmp_width, bmp_height,
+					      bmp_invert_y, scanout->format);
+			break;
+		case 3:
+			drm_splash_blit_pix24(&map, fb->pitches[0], x_pad,
+					      y_pad, image_data, bmp_pitch,
+					      bmp_width, bmp_height,
+					      bmp_invert_y, scanout->format);
+			break;
+		case 4:
+			drm_splash_blit_pix32(&map, fb->pitches[0], x_pad,
+					      y_pad, image_data, bmp_pitch,
+					      bmp_width, bmp_height,
+					      bmp_invert_y, scanout->format);
+			break;
+		default:
+			drm_warn_once(client->dev,
+				      "splash: can't blit with pixel width %d",
+				      px_width);
+		}
+	}
+
+	drm_client_buffer_vunmap_local(buffer);
+
+	return drm_client_buffer_flush(buffer, &r);
+}
+#else
+static inline int drm_splash_bmp_to_scanout(struct drm_splash *splash,
+					    struct drm_splash_scanout *scanout,
+					    const u8 *data, size_t data_len)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+static int drm_splash_image_load(struct drm_splash *splash, const u8 **img_data,
+				 size_t *img_data_len)
+{
+	int ret = 0;
+
+	if (splash->use_bgrt) {
+		ret = drm_splash_bgrt_load(splash, img_data, img_data_len);
+		if (ret)
+			splash->use_bgrt = false;
+	}
+
+	/* BGRT failed to load */
+	if (!splash->use_bgrt)
+		ret = drm_splash_fw_load(splash, img_data, img_data_len);
+
+	return ret;
+}
+
+static void drm_splash_image_cleanup(struct drm_splash *splash)
+{
+	const struct firmware *fw = NULL;
+
+	memunmap(splash->map_data);
+
+	scoped_guard(spinlock, &splash->fw_lock) {
+		fw = splash->fw;
+		splash->fw = NULL;
+	}
+
+	release_firmware(fw);
+}
+
+static int drm_splash_render_thread(void *data)
+{
+	struct drm_splash *splash = data;
+	struct drm_client_dev *client = &splash->client;
+
+	while (!kthread_should_stop()) {
+		unsigned int draw_count = 0;
+		const u8 *img_data = NULL;
+		size_t img_data_len = 0;
+		bool img_loaded;
+		int i, ret;
+
+		drm_dbg(client->dev, "splash: run render thread...");
+
+		ret = drm_splash_image_load(splash, &img_data, &img_data_len);
+		img_loaded = (ret == 0);
+
+		for (i = 0; i < splash->n_scanout; i++) {
+			struct drm_splash_scanout *scanout = &splash->scanout[i];
+
+			if (!scanout->buffer) {
+				drm_err(client->dev,
+					"splash: no buffer for scanout %d", i);
+				continue;
+			}
+
+			if (!scanout->bg_drawn) {
+				drm_dbg(client->dev, "draw background for scanout %d", i);
+				u32 color = drm_draw_color_from_xrgb8888(splash_color,
+									 scanout->format);
+				drm_splash_fill_solid_color(scanout->buffer, color);
+				scanout->bg_drawn = true;
+			}
+
+			if (img_loaded && !scanout->img_drawn) {
+				drm_dbg(client->dev, "draw image for scanout %d", i);
+				/* Ignore the return value, since the solid
+				 * color has already been drawn to screen.
+				 */
+				ret = drm_splash_bmp_to_scanout(splash, scanout,
+								img_data,
+								img_data_len);
+				scanout->img_drawn = (ret == 0);
+			}
+
+			draw_count++;
+		}
+
+		if (img_loaded)
+			drm_splash_image_cleanup(splash);
+
+		if (draw_count > 0) {
+			ret = drm_client_modeset_commit(client);
+			/* If commit returns EBUSY, another master showed up.
+			 * This means that the splash is no more required.
+			 */
+			if (ret == -EBUSY) {
+				drm_info(client->dev,
+					"splash: not master anymore, exiting");
+				break;
+			}
+		}
+
+		if (img_loaded)
+			break;
+
+		/* If no changes arrived in the mean time, wait to be awaken,
+		 * e.g.by a firmware callback.
+		 */
+		if (atomic_xchg(&splash->pending, 0) == 0)
+			set_current_state(TASK_UNINTERRUPTIBLE);
+
+		schedule();
+	}
+
+	return 0;
+}
+
+static int drm_splash_init_client(struct drm_splash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+	struct drm_mode_set *modeset;
+	unsigned int modeset_mask = 0;
+	unsigned int fb_count = 0;
+	int j;
+
+	if (drm_client_modeset_probe(client, 0, 0))
+		return -1;
+
+	j = 0;
+	drm_client_for_each_modeset(modeset, client) {
+		struct drm_splash_scanout *tmp;
+		struct drm_splash_scanout *scanout;
+		u32 format;
+		int id = -1;
+
+		/* Skip modesets without a mode */
+		if (!modeset->mode)
+			continue;
+
+		if (modeset->connectors[0]->has_tile) {
+			struct drm_splash_scanout *tiled;
+			int new_id = modeset->connectors[0]->tile_group->id;
+
+			/* Tiled modesets contribute to a single framebuffer,
+			 * check if this tiled group has already been seen.
+			 */
+			tiled = get_scanout_from_tile_group(splash, new_id);
+			if (tiled != NULL) {
+				if (!modeset->x)
+					tiled->width += modeset->mode->vdisplay;
+				if (!modeset->y)
+					tiled->height += modeset->mode->hdisplay;
+				modeset->fb = tiled->buffer->fb;
+				continue;
+			}
+
+			/* New tile group, save its ID for later */
+			id = new_id;
+		}
+
+		format = drm_splash_find_usable_format(modeset->crtc->primary,
+						       splash->preferred_format);
+		if (format == DRM_FORMAT_INVALID) {
+			drm_warn(client->dev,
+				 "splash: can't find a usable format for modeset");
+			continue;
+		}
+
+		tmp = krealloc(splash->scanout,
+			       (splash->n_scanout + 1) * sizeof(*splash->scanout),
+			       GFP_KERNEL);
+		if (!tmp) {
+			drm_warn(client->dev,
+				 "splash: can't reallocate the scanout array");
+			break;
+		}
+
+		splash->scanout = tmp;
+		scanout = &splash->scanout[splash->n_scanout];
+		splash->n_scanout++;
+
+		memset(scanout, 0, sizeof(*scanout));
+		scanout->id = id;
+		scanout->format = format;
+		scanout->width = modeset->mode->hdisplay;
+		scanout->height = modeset->mode->vdisplay;
+
+		modeset_mask |= BIT(j);
+		j++;
+	}
+
+	/* Now that all sensible modesets have been collected, allocate buffers */
+	j = 0;
+	drm_client_for_each_modeset(modeset, client) {
+		struct drm_splash_scanout *scanout;
+
+		if (!(modeset_mask & BIT(j)))
+			continue;
+
+		scanout = &splash->scanout[j];
+		j++;
+
+		scanout->buffer = drm_client_buffer_create_dumb(client,
+								scanout->width,
+								scanout->height,
+								scanout->format);
+		if (IS_ERR(scanout->buffer)) {
+			drm_warn(client->dev,
+				 "splash: can't create dumb buffer %d %d %p4cc",
+				 scanout->width, scanout->height, &scanout->format);
+			continue;
+		}
+
+		drm_info(client->dev, "splash: created dumb buffer %d %d %p4cc",
+			 scanout->width, scanout->height, &scanout->format);
+
+		modeset->fb = scanout->buffer->fb;
+		fb_count++;
+	}
+
+	return (fb_count == 0) ? -ENODEV : 0;
+}
+
+static void drm_splash_free_scanout(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+	int i;
+
+	if (splash->n_scanout) {
+		for (i = 0; i < splash->n_scanout; i++)
+			drm_client_buffer_delete(splash->scanout[i].buffer);
+
+		splash->n_scanout = 0;
+		kfree(splash->scanout);
+		splash->scanout = NULL;
+	}
+}
+
+static int drm_splash_client_hotplug(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+	int ret = 0;
+
+	guard(mutex)(&splash->hotplug_lock);
+
+	/* The modesets that get a splash are defined at first hotplug event */
+	if (splash->initialized)
+		return 0;
+
+	ret = drm_splash_init_client(splash);
+	if (ret == -ENODEV) {
+		drm_info(client->dev, "splash: no modeset found");
+		return 0;
+	} else if (ret) {
+		drm_err(client->dev,
+			"splash: failed to init client: %d", ret);
+		return ret;
+	}
+
+	/* Create the render thread, waken later */
+	splash->thread = kthread_create(drm_splash_render_thread,
+					splash, "drm_splash_%s",
+					client->dev->unique);
+	if (IS_ERR(splash->thread)) {
+		ret = PTR_ERR(splash->thread);
+		drm_err(client->dev,
+			"splash: failed to create render thread: %d", ret);
+		drm_splash_free_scanout(client);
+		return ret;
+	}
+
+	splash->use_bgrt = drm_splash_bgrt_available(splash);
+
+	/* If no other image has been loaded, try to load a BMP as firmware */
+	if (IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BMP) && !splash->use_bgrt) {
+		ret = drm_splash_fw_request_bmp(splash);
+		if (ret) {
+			drm_err(client->dev,
+				"splash: failed to kick image load: %d", ret);
+			kthread_stop(splash->thread);
+			drm_splash_free_scanout(client);
+			return ret;
+		}
+	}
+
+	/* Wake the render thread to show initial contents */
+	drm_splash_wake_render_thread(splash);
+
+	splash->initialized = true;
+
+	return 0;
+}
+
+static int drm_splash_client_restore(struct drm_client_dev *client, bool force)
+{
+	int ret;
+
+	if (force)
+		ret = drm_client_modeset_commit_locked(client);
+	else
+		ret = drm_client_modeset_commit(client);
+
+	return ret;
+}
+
+static void drm_splash_client_unregister(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+
+	kthread_stop(splash->thread);
+	drm_splash_free_scanout(client);
+	drm_client_release(client);
+
+	drm_splash_image_cleanup(splash);
+}
+
+static void drm_splash_client_free(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+	struct drm_device *dev = client->dev;
+
+	mutex_destroy(&splash->hotplug_lock);
+	kfree(splash);
+
+	drm_dbg(dev, "Unregistered with drm splash");
+}
+
+static const struct drm_client_funcs drm_splash_client_funcs = {
+	.owner		= THIS_MODULE,
+	.hotplug	= drm_splash_client_hotplug,
+	.restore	= drm_splash_client_restore,
+	.unregister	= drm_splash_client_unregister,
+	.free		= drm_splash_client_free,
+};
+
+/**
+ * drm_splash_register() - Register a drm device to drm_splash
+ * @dev: the drm device to register.
+ * @format: drm device preferred format.
+ */
+void drm_splash_register(struct drm_device *dev,
+			 const struct drm_format_info *format)
+{
+	struct drm_splash *splash;
+
+	splash = kzalloc_obj(*splash);
+	if (!splash)
+		goto err_warn;
+
+	mutex_init(&splash->hotplug_lock);
+	spin_lock_init(&splash->fw_lock);
+
+	if (format && format->num_planes == 1)
+		splash->preferred_format = format->format;
+	else
+		splash->preferred_format = DRM_FORMAT_RGB888;
+
+	if (drm_client_init(dev, &splash->client, "drm_splash",
+			    &drm_splash_client_funcs))
+		goto err_free;
+
+	drm_client_register(&splash->client);
+	drm_dbg(dev, "Registered with drm splash");
+
+	return;
+
+err_free:
+	kfree(splash);
+err_warn:
+	drm_warn(dev, "Failed to register with drm splash");
+}

-- 
2.54.0


^ permalink raw reply related

* [PATCH RFC v3 2/3] MAINTAINERS: add entry for DRM splash client
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>

Add myself as maintainer for the DRM splash client.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b2040011a386..6c132139e87c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8933,6 +8933,13 @@ T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:	drivers/gpu/drm/drm_privacy_screen*
 F:	include/drm/drm_privacy_screen*
 
+DRM SPLASH
+M:	Francesco Valla <francesco@valla.it>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F:	drivers/gpu/drm/clients/drm_splash.c
+
 DRM TTM SUBSYSTEM
 M:	Christian Koenig <christian.koenig@amd.com>
 M:	Huang Rui <ray.huang@amd.com>

-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Geert Uytterhoeven @ 2026-04-28 13:27 UTC (permalink / raw)
  To: Andrew Murray
  Cc: Bird, Tim, pmladek@suse.com, rostedt@goodmis.org,
	john.ogness@linutronix.de, senozhatsky@chromium.org,
	francesco@valla.it, tglx@kernel.org, shashankbalaji02@gmail.com,
	linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CALqELGx+8=honboz+_dRP8tX2ngnquQhizxNg5J+yVwa_YgUhQ@mail.gmail.com>

Hi Andrew,

On Tue, 28 Apr 2026 at 13:17, Andrew Murray
<amurray@thegoodpenguin.co.uk> wrote:
> I've tested this on my PocketBeagle2...

> That shows nearly a second of boot time prior to timestamps. In this
> case it's due to a slow console as it's using earlycon
> ("earlycon=ns16550a,mmio32,0x02860000"), without that I get:

"earlycon" (no parameters) should be sufficient, once you have
added the missing serial console speed to chosen/stdout-path in
arch/arm64/boot/dts/ti/k3-am62-pocketbeagle2.dts.

BeaglePlay already has it:
https://elixir.bootlin.com/linux/v7.0.1/source/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts#L44

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Andrew Murray @ 2026-04-28 11:17 UTC (permalink / raw)
  To: Bird, Tim
  Cc: pmladek@suse.com, rostedt@goodmis.org, john.ogness@linutronix.de,
	senozhatsky@chromium.org, francesco@valla.it,
	geert@linux-m68k.org, tglx@kernel.org, shashankbalaji02@gmail.com,
	linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <MW5PR13MB56327ABDE42B1CB0D02B8D5BFD372@MW5PR13MB5632.namprd13.prod.outlook.com>

Hi Tim,

On Tue, 28 Apr 2026 at 06:30, Bird, Tim <Tim.Bird@sony.com> wrote:
>
> > -----Original Message-----
> > From: Andrew Murray <amurray@thegoodpenguin.co.uk>
> > Hi Tim,
> >
> > On Fri, 10 Apr 2026 at 21:41, Tim Bird <tim.bird@sony.com> wrote:
> > >
> > > During early boot, printk timestamps are reported as zero before
> > > kernel timekeeping starts (i.e. before time_init()).  This
> > > hinders boot-time optimization efforts.  This period ranges from
> > > 17 to 1700 milliseconds on different embedded machines running Linux.
> > >
> > > Add support for early timestamps based on processor cycle-generators
> > > that need no kernel initialization. Use a statically-configured
> > > frequency value for converting cycles to nanoseconds. This means
> > > that this feature cannot be turned on in generic distro kernels.
> > > It is intended for temporary use during kernel development and
> > > boot-time research and optimization only.
> > >
> > > This yields non-zero timestamps for printks from the very start
> > > of kernel execution.  The timestamps are relative to the start of
> > > an architecture-specific counter (e.g. tsc on x86_64 and cntvct_el0
> > > on arm64).  Affected timestamps reflect time from cycle counter
> > > init (usually machine power-on or virtual machine start) instead of
> > > time from the kernel's timekeeping initialization. This results in a
> > > discontinuity in the printk timestamp values, one time, when
> > > kernel timekeeping starts.
> > >
> > > Signed-off-by: Tim Bird <tim.bird@sony.com>
> > > ---
> >
> > The limitation of this series is that while it narrows the window
> > where delays may be hiding, it doesn't remove the window completely.
> > For example, there may be a delay prior to the first printk (perhaps
> > unlikely). It would instead be useful to understand how much time has
> > passed since execution was handed over to the kernel.
>
> Hey Andrew,
>
> My patch allows one to add a printk as the very first line in start_kernel(), which
> I believe is the earliest you can do so in C code in the kernel proper.
>
> You are correct that this patch doesn’t measure time before
> that first line of C code accessible to instrument with printks.  So there's
> more blind spots in the total system boot than this patch addresses.
>
> >
> > Also, in my view, the goal of this work is really to identify
> > regressions in boot time, though this can be achieved via other means.
> > Replacing zero timestamps in printk messages with real timestamps is
> > only necessary to make it easier to bisect where those delays are
> > hiding, i.e. between two printk messages. But that benefit it limited
> > given how small the window is. (And if a regression is spotted,
> > someone would likely add debug to find it anyway).
>
> In my experience, although the kernel's blind spot is relatively small on x86, it
> is quite a bit larger on other platforms: 183 milliseconds on a raspberry pi
> and 1700 milliseconds on a FPGA-based riscv board.  When I've used the
> patch in its current form, along with sprinkling printks and measurements
> in other place in init/main.c and other relevant early functions in the kernel,
> it has yielded valuable data about the timings in this blind spot, that I can
> easily read using the console and dmesg.

I've tested this on my PocketBeagle2...

Before (abbreviated):

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
...
[    0.000000] GICv3: CPU0: using allocated LPI pending table
@0x0000000080660000
[    0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    0.000000] arch_timer: cp15 timer running at 200.00MHz (phys).
[    0.000000] clocksource: arch_sys_counter: mask: 0x3ffffffffffffff
max_cycles: 0x2e2049d3e8, max_idle_ns: 440795210634 ns
[    0.000001] sched_clock: 58 bits at 200MHz, resolution 5ns, wraps
every 4398046511102ns
[    0.009241] Console: colour dummy device 80x25
[    0.013890] Lock dependency validator: Copyright (c) 2006 Red Hat,
Inc., Ingo Molnar
[    0.021836] ... MAX_LOCKDEP_SUBCLASSES:  8
[    0.026049] ... MAX_LOCK_DEPTH:          48


After (abbreviated):

[    2.658895] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
...
[    3.619549] GICv3: CPU0: using allocated LPI pending table
@0x0000000080660000
[    3.627177] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    3.635146] arch_timer: cp15 timer running at 200.00MHz (phys).
[    3.641227] clocksource: arch_sys_counter: mask: 0x3ffffffffffffff
max_cycles: 0x2e2049d3e8, max_idle_ns: 440795210634 ns
[    0.000001] sched_clock: 58 bits at 200MHz, resolution 5ns, wraps
every 4398046511102ns
[    0.009249] Console: colour dummy device 80x25
[    0.013897] Lock dependency validator: Copyright (c) 2006 Red Hat,
Inc., Ingo Molnar
[    0.021843] ... MAX_LOCKDEP_SUBCLASSES:  8
[    0.026055] ... MAX_LOCK_DEPTH:          48

That shows nearly a second of boot time prior to timestamps. In this
case it's due to a slow console as it's using earlycon
("earlycon=ns16550a,mmio32,0x02860000"), without that I get:

[   36.979738] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
...
[   37.399295] GICv3: CPU0: using allocated LPI pending table
@0x0000000080660000
[   37.399531] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[   37.400446] arch_timer: cp15 timer running at 200.00MHz (phys).
[   37.400463] clocksource: arch_sys_counter: mask: 0x3ffffffffffffff
max_cycles: 0x2e2049d3e8, max_idle_ns: 440795210634 ns
[    0.000001] sched_clock: 58 bits at 200MHz, resolution 5ns, wraps
every 4398046511102ns
[    0.000962] Console: colour dummy device 80x25
[    0.001032] Lock dependency validator: Copyright (c) 2006 Red Hat,
Inc., Ingo Molnar
[    0.001037] ... MAX_LOCKDEP_SUBCLASSES:  8
[    0.001041] ... MAX_LOCK_DEPTH:          48

Showing approximately 0.42 seconds, for the curious that comes from
the following, but I've not investigated:

[   37.073687] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=2, Nodes=1
[   37.335840] ftrace: allocating 66138 entries in 260 pages

In its current form this is useful to me, though it would be helpful
if it were clearer when the timestamps switch.

Feel free to add:

Tested-by: Andrew Murray <amurray@thegoodpenguin.co.uk>

Though I'll test again in later revisions of this series.

>
> >
> > Given the risks identified by others with this series and the above, I
> > thought I'd suggest an alternative approach.
> >
> > Perhaps we can rely on a prior boot stage (e.g. U-Boot) to capture a
> > timestamp (perhaps cntvct on arm) immediately prior to kernel
> > handover, this could be stored in the device tree and it could include
> > architecture information on where that timestamp came from. During
> > boot, once timekeeping is initialised, the architecture specific
> > timekeeping initialisation code can print relevant timestamp values to
> > the console, alongside the data captured in the device tree.
> >
> > Userspace tools (systemd-analyze) or log parsing tools, can then make
> > sense of the values. The time between kernel handover and the end of
> > timekeeping initalisation printk can be calculated. You could also
> > subtract the earliest non-zero printk timestamp from this figure to
> > measure the time between kernel handover and first non-zero timestamp.
> >
> > Besides logging values from hardware registers to the console, this
> > approach pushes out all the maths to userspace or log parsing tools.
> > It also doesn't impact existing printk timestamps and can be
> > structured to not introduce new get_cycles types of functions.
>
> It is certainly of value to get a measure of the time between
> the bootloader and the kernel start (ie, the kernel self-decompressor).
> But that's a different problem than the one I originally intended
> to solve with this patch, which was the zero-valued printks in the kernel,
> before kernel timekeeping start.

Yes, I appreciate that, and of course it's not either problem, both
can be solved :)


>
> Because my code is using the same cycle counter as U-boot, it will operate
> with the same time-base, making it easier to correlate the times that U-boot
> records with the times reported in the early printks, using an external tool.
> One could certainly output cycles in a few places and set up a correspondence
> using external tooling.
> That's what this patch was striving for:
>  https://lore.kernel.org/all/20250823044034.189939-1-v-singh1@ti.com/
>
> That was posted as part of the effort described in [1].

Thanks, I was trying to find more information on this.

Andrew Murray

>
> That would be a valuable thing to do, and possibly there is some coordination
> we could do to harmonize these efforts or have them use each other's facilities.
> But again, that's solving a different problem than the one my patch solves.
>
> >
> > If this type of approach is taken, it may be worth investigating other
> > initiatives in this area, for example [1], [2], [3] which may be
> > relevant.
> >
> > [1] https://static.sched.com/hosted_files/osseu2025/a2/EOSS_2025_Unified%20Boot%20Time%20log%20based%20measurement.pdf
> > [2] https://issues.chromium.org/issues/40986147
> > [3] https://github.com/FirmwareHandoff/firmware_handoff
>
> I worry that if I change the problem I'm trying to solve, or the scope
> of the required external tools, it will be harder to get a patch accepted
> to solve the thing I'm interested in.
>  -- Tim
>
>
> > Thanks,
> >
> > Andrew Murray
> >
> > > V3 -> V4
> > >   Replace config vars with single one: CONFIG_EARLY_CYCLES_KHZ
> > >   Replace runtime calibration with static config variable
> > >   Remove reference to get_cycles()
> > >   Add support for RISCV platforms
> > > V2 -> V3
> > >   Default CONFIG option to 'n'
> > >   Move more code into early_times.h (reduce ifdefs in init/main.c)
> > >   Use match64 helper routines
> > >   Use cycles_t instead of u64 type
> > >   Add #defines for EARLY_CYCLES_BIT and EARLY_CYCLES_MASK
> > >   Invert if logic in adjust_early_ts()
> > > V1 -> V2
> > >   Remove calibration CONFIG vars
> > >   Add 'depends on' to restrict arches (to handle ppc bug)
> > >   Add early_ts_offset to avoid discontinuity
> > >   Save cycles in ts_nsec, and convert on output
> > >   Move conditional code to include file (early_times.h>
> > > ---
> > >  include/linux/early_times.h | 55 +++++++++++++++++++++++++++++++++++++
> > >  init/Kconfig                | 30 ++++++++++++++++++++
> > >  kernel/printk/printk.c      |  3 ++
> > >  3 files changed, 88 insertions(+)
> > >  create mode 100644 include/linux/early_times.h
> > >
> > > diff --git a/include/linux/early_times.h b/include/linux/early_times.h
> > > new file mode 100644
> > > index 000000000000..82bacfd0e26b
> > > --- /dev/null
> > > +++ b/include/linux/early_times.h
> > > @@ -0,0 +1,55 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +
> > > +#ifndef _EARLY_TIMES_H
> > > +#define _EARLY_TIMES_H
> > > +
> > > +#include <linux/timekeeping.h>
> > > +#ifdef CONFIG_ARM64
> > > +#include <asm/sysreg.h>
> > > +#endif
> > > +
> > > +#ifdef CONFIG_EARLY_CYCLES_KHZ
> > > +static inline u64 early_unsafe_cycles(void)
> > > +{
> > > +#if defined(CONFIG_X86_64)
> > > +       /*
> > > +        * This rdtsc may happen before secure TSC is initialized, and
> > > +        * it is unordered. So please don't use this value for cryptography
> > > +        * or after SMP is initialized.
> > > +        */
> > > +       return rdtsc();
> > > +#elif defined(CONFIG_ARM64)
> > > +       return read_sysreg(cntvct_el0);
> > > +#elif defined(CONFIG_RISCV_TIMER)
> > > +       u64 val;
> > > +
> > > +       asm volatile("rdtime %0" : "=r"(val));
> > > +       return val;
> > > +#else
> > > +       return 0;
> > > +#endif
> > > +}
> > > +
> > > +#define NS_PER_KHZ     1000000UL
> > > +
> > > +/* returns a nanosecond value based on early cycles */
> > > +static inline u64 early_times_ns(void)
> > > +{
> > > +       if (CONFIG_EARLY_CYCLES_KHZ)
> > > +               /*
> > > +                * Note: the multiply must precede the division to avoid
> > > +                * truncation and loss of resolution
> > > +                * Don't use fancier MULT/SHIFT math here.  Since this is
> > > +                * static, the compiler can optimize the math operations.
> > > +                */
> > > +               return (early_unsafe_cycles() * NS_PER_KHZ) / CONFIG_EARLY_CYCLES_KHZ;
> > > +       return 0;
> > > +}
> > > +#else
> > > +static inline u64 early_times_ns(void)
> > > +{
> > > +       return 0;
> > > +}
> > > +#endif
> > > +
> > > +#endif /* _EARLY_TIMES_H */
> > > diff --git a/init/Kconfig b/init/Kconfig
> > > index 7484cd703bc1..40c3123c2c27 100644
> > > --- a/init/Kconfig
> > > +++ b/init/Kconfig
> > > @@ -792,6 +792,36 @@ config IKHEADERS
> > >           or similar programs.  If you build the headers as a module, a module called
> > >           kheaders.ko is built which can be loaded on-demand to get access to headers.
> > >
> > > +config EARLY_CYCLES_KHZ
> > > +       int "Frequency of the early times cycle counter, in KHz"
> > > +       default 0
> > > +       depends on PRINTK
> > > +       depends on ARM64 || X86_64 || RISCV_TIMER
> > > +       help
> > > +         Specifies the frequency value (in KHz) of the cycle counter
> > > +         used for early times information.
> > > +
> > > +         Set this to provide timing information for printks in early boot
> > > +         (before the start of kernel timekeeping), that would otherwise
> > > +         show as 0.
> > > +
> > > +         To calculate the value for your system, boot your system, and
> > > +         use the value expressed on the tsc or cntvct_el0 calibration line
> > > +         in your kernel message log:
> > > +         ex x86_64: tsc: Refined TSC clocksource calibration: 2095.057 MHz
> > > +          In that case, use the value 2095057.
> > > +         ex arm64: arch_timer: cp15 timer running at 54.00MHz (phys).
> > > +         In that case, use the value of 54000.
> > > +
> > > +         Note that this causes the kernel to show, for some early printks,
> > > +         times that are relative to processor power on, instead of
> > > +         relative to the start of kernel timekeeping.  When kernel
> > > +         timekeeping starts, the timestamps values reset, causing
> > > +         a discontinuity in the timestamp values.  If you see
> > > +         non-monotonic printk timestamps, please don't freak out.
> > > +
> > > +         Set to 0 to disable this feature.
> > > +
> > >  config LOG_BUF_SHIFT
> > >         int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
> > >         range 12 25
> > > diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> > > index 0323149548f6..ddeab81f04ef 100644
> > > --- a/kernel/printk/printk.c
> > > +++ b/kernel/printk/printk.c
> > > @@ -46,6 +46,7 @@
> > >  #include <linux/ctype.h>
> > >  #include <linux/uio.h>
> > >  #include <linux/sched/clock.h>
> > > +#include <linux/early_times.h>
> > >  #include <linux/sched/debug.h>
> > >  #include <linux/sched/task_stack.h>
> > >  #include <linux/panic.h>
> > > @@ -2294,6 +2295,8 @@ int vprintk_store(int facility, int level,
> > >          * timestamp with respect to the caller.
> > >          */
> > >         ts_nsec = local_clock();
> > > +       if (unlikely(!ts_nsec))
> > > +               ts_nsec = early_times_ns();
> > >
> > >         caller_id = printk_caller_id();
> > >
> > > --
> > > 2.43.0
> > >
> > >



-- 
Andrew Murray, Director
https://www.thegoodpenguin.co.uk

The Good Penguin Ltd is a company registered in England and Wales with
company number 12374667 and VAT number 341687879. Registered office:
The Good Penguin Ltd, Merlin House, Priory Drive, Langstone, Newport,
Wales, NP18 2HJ, UK.

^ permalink raw reply

* Re: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Roberto A. Foglietta @ 2026-04-28  5:56 UTC (permalink / raw)
  To: Bird, Tim
  Cc: Andrew Murray, pmladek@suse.com, rostedt@goodmis.org,
	john.ogness@linutronix.de, senozhatsky@chromium.org,
	francesco@valla.it, geert@linux-m68k.org, tglx@kernel.org,
	shashankbalaji02@gmail.com, linux-embedded@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <MW5PR13MB56327ABDE42B1CB0D02B8D5BFD372@MW5PR13MB5632.namprd13.prod.outlook.com>

On Tue, 28 Apr 2026 at 07:42, Bird, Tim <Tim.Bird@sony.com> wrote:
>

> In my experience, although the kernel's blind spot is relatively small on x86, it
> is quite a bit larger on other platforms: 183 milliseconds on a raspberry pi
> and 1700 milliseconds on a FPGA-based riscv board.

Human time is relative, it is 1700ms but what is going on during that time?

The same thing that happens in 0.183 seconds in a x86 powerful silicon?

The blind spot is the same. Just the clock of the CPU is different.

Considering that 200ms is the time in which an entire Linux embedded
system can boot by a Qemu/KVM

~> https://github.com/robang74/uchaosys#quick-start (contains boot example data)

I would suggest that it is better to cut the boot time by an early
CNRG initialisation (which also brings in security) rather than worry
about a short (in terms of operations, whatever is the human time)
blind spot.

Wonderful that U-boot can provide reliable early timings but U-boot
isn't granted. From this point of view considering early timings as a
bounded case for debug/hacking makes sense

~> https://github.com/robang74/uchaosys/blob/v070/cnfg/printk-early-boot-timestamps-hack-v6.patch

as well as cutting down the boot time by providing early CNRG
initialisation. Solving practical problems and creating easy to access
bounded areas of experimentation for what we cannot control. Code or
patch aren't perfect? Humans aren't perfect, but improvable,
hopefully.

Best regards, R-

^ permalink raw reply

* RE: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Bird, Tim @ 2026-04-28  5:29 UTC (permalink / raw)
  To: Andrew Murray
  Cc: pmladek@suse.com, rostedt@goodmis.org, john.ogness@linutronix.de,
	senozhatsky@chromium.org, francesco@valla.it,
	geert@linux-m68k.org, tglx@kernel.org, shashankbalaji02@gmail.com,
	linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CALqELGz3qJ=KtDmBHMRP4NMSmmeZGKxk6SpTCQGcvGru5Dn0SA@mail.gmail.com>

> -----Original Message-----
> From: Andrew Murray <amurray@thegoodpenguin.co.uk>
> Hi Tim,
> 
> On Fri, 10 Apr 2026 at 21:41, Tim Bird <tim.bird@sony.com> wrote:
> >
> > During early boot, printk timestamps are reported as zero before
> > kernel timekeeping starts (i.e. before time_init()).  This
> > hinders boot-time optimization efforts.  This period ranges from
> > 17 to 1700 milliseconds on different embedded machines running Linux.
> >
> > Add support for early timestamps based on processor cycle-generators
> > that need no kernel initialization. Use a statically-configured
> > frequency value for converting cycles to nanoseconds. This means
> > that this feature cannot be turned on in generic distro kernels.
> > It is intended for temporary use during kernel development and
> > boot-time research and optimization only.
> >
> > This yields non-zero timestamps for printks from the very start
> > of kernel execution.  The timestamps are relative to the start of
> > an architecture-specific counter (e.g. tsc on x86_64 and cntvct_el0
> > on arm64).  Affected timestamps reflect time from cycle counter
> > init (usually machine power-on or virtual machine start) instead of
> > time from the kernel's timekeeping initialization. This results in a
> > discontinuity in the printk timestamp values, one time, when
> > kernel timekeeping starts.
> >
> > Signed-off-by: Tim Bird <tim.bird@sony.com>
> > ---
> 
> The limitation of this series is that while it narrows the window
> where delays may be hiding, it doesn't remove the window completely.
> For example, there may be a delay prior to the first printk (perhaps
> unlikely). It would instead be useful to understand how much time has
> passed since execution was handed over to the kernel.

Hey Andrew,

My patch allows one to add a printk as the very first line in start_kernel(), which
I believe is the earliest you can do so in C code in the kernel proper.

You are correct that this patch doesn’t measure time before
that first line of C code accessible to instrument with printks.  So there's
more blind spots in the total system boot than this patch addresses.

> 
> Also, in my view, the goal of this work is really to identify
> regressions in boot time, though this can be achieved via other means.
> Replacing zero timestamps in printk messages with real timestamps is
> only necessary to make it easier to bisect where those delays are
> hiding, i.e. between two printk messages. But that benefit it limited
> given how small the window is. (And if a regression is spotted,
> someone would likely add debug to find it anyway).

In my experience, although the kernel's blind spot is relatively small on x86, it
is quite a bit larger on other platforms: 183 milliseconds on a raspberry pi
and 1700 milliseconds on a FPGA-based riscv board.  When I've used the
patch in its current form, along with sprinkling printks and measurements
in other place in init/main.c and other relevant early functions in the kernel,
it has yielded valuable data about the timings in this blind spot, that I can
easily read using the console and dmesg.

> 
> Given the risks identified by others with this series and the above, I
> thought I'd suggest an alternative approach.
> 
> Perhaps we can rely on a prior boot stage (e.g. U-Boot) to capture a
> timestamp (perhaps cntvct on arm) immediately prior to kernel
> handover, this could be stored in the device tree and it could include
> architecture information on where that timestamp came from. During
> boot, once timekeeping is initialised, the architecture specific
> timekeeping initialisation code can print relevant timestamp values to
> the console, alongside the data captured in the device tree.
> 
> Userspace tools (systemd-analyze) or log parsing tools, can then make
> sense of the values. The time between kernel handover and the end of
> timekeeping initalisation printk can be calculated. You could also
> subtract the earliest non-zero printk timestamp from this figure to
> measure the time between kernel handover and first non-zero timestamp.
> 
> Besides logging values from hardware registers to the console, this
> approach pushes out all the maths to userspace or log parsing tools.
> It also doesn't impact existing printk timestamps and can be
> structured to not introduce new get_cycles types of functions.

It is certainly of value to get a measure of the time between
the bootloader and the kernel start (ie, the kernel self-decompressor).
But that's a different problem than the one I originally intended
to solve with this patch, which was the zero-valued printks in the kernel,
before kernel timekeeping start.

Because my code is using the same cycle counter as U-boot, it will operate
with the same time-base, making it easier to correlate the times that U-boot
records with the times reported in the early printks, using an external tool.
One could certainly output cycles in a few places and set up a correspondence
using external tooling.
That's what this patch was striving for:
 https://lore.kernel.org/all/20250823044034.189939-1-v-singh1@ti.com/

That was posted as part of the effort described in [1].

That would be a valuable thing to do, and possibly there is some coordination
we could do to harmonize these efforts or have them use each other's facilities.
But again, that's solving a different problem than the one my patch solves.

> 
> If this type of approach is taken, it may be worth investigating other
> initiatives in this area, for example [1], [2], [3] which may be
> relevant.
> 
> [1] https://static.sched.com/hosted_files/osseu2025/a2/EOSS_2025_Unified%20Boot%20Time%20log%20based%20measurement.pdf
> [2] https://issues.chromium.org/issues/40986147
> [3] https://github.com/FirmwareHandoff/firmware_handoff

I worry that if I change the problem I'm trying to solve, or the scope
of the required external tools, it will be harder to get a patch accepted
to solve the thing I'm interested in.
 -- Tim


> Thanks,
> 
> Andrew Murray
> 
> > V3 -> V4
> >   Replace config vars with single one: CONFIG_EARLY_CYCLES_KHZ
> >   Replace runtime calibration with static config variable
> >   Remove reference to get_cycles()
> >   Add support for RISCV platforms
> > V2 -> V3
> >   Default CONFIG option to 'n'
> >   Move more code into early_times.h (reduce ifdefs in init/main.c)
> >   Use match64 helper routines
> >   Use cycles_t instead of u64 type
> >   Add #defines for EARLY_CYCLES_BIT and EARLY_CYCLES_MASK
> >   Invert if logic in adjust_early_ts()
> > V1 -> V2
> >   Remove calibration CONFIG vars
> >   Add 'depends on' to restrict arches (to handle ppc bug)
> >   Add early_ts_offset to avoid discontinuity
> >   Save cycles in ts_nsec, and convert on output
> >   Move conditional code to include file (early_times.h>
> > ---
> >  include/linux/early_times.h | 55 +++++++++++++++++++++++++++++++++++++
> >  init/Kconfig                | 30 ++++++++++++++++++++
> >  kernel/printk/printk.c      |  3 ++
> >  3 files changed, 88 insertions(+)
> >  create mode 100644 include/linux/early_times.h
> >
> > diff --git a/include/linux/early_times.h b/include/linux/early_times.h
> > new file mode 100644
> > index 000000000000..82bacfd0e26b
> > --- /dev/null
> > +++ b/include/linux/early_times.h
> > @@ -0,0 +1,55 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> > +#ifndef _EARLY_TIMES_H
> > +#define _EARLY_TIMES_H
> > +
> > +#include <linux/timekeeping.h>
> > +#ifdef CONFIG_ARM64
> > +#include <asm/sysreg.h>
> > +#endif
> > +
> > +#ifdef CONFIG_EARLY_CYCLES_KHZ
> > +static inline u64 early_unsafe_cycles(void)
> > +{
> > +#if defined(CONFIG_X86_64)
> > +       /*
> > +        * This rdtsc may happen before secure TSC is initialized, and
> > +        * it is unordered. So please don't use this value for cryptography
> > +        * or after SMP is initialized.
> > +        */
> > +       return rdtsc();
> > +#elif defined(CONFIG_ARM64)
> > +       return read_sysreg(cntvct_el0);
> > +#elif defined(CONFIG_RISCV_TIMER)
> > +       u64 val;
> > +
> > +       asm volatile("rdtime %0" : "=r"(val));
> > +       return val;
> > +#else
> > +       return 0;
> > +#endif
> > +}
> > +
> > +#define NS_PER_KHZ     1000000UL
> > +
> > +/* returns a nanosecond value based on early cycles */
> > +static inline u64 early_times_ns(void)
> > +{
> > +       if (CONFIG_EARLY_CYCLES_KHZ)
> > +               /*
> > +                * Note: the multiply must precede the division to avoid
> > +                * truncation and loss of resolution
> > +                * Don't use fancier MULT/SHIFT math here.  Since this is
> > +                * static, the compiler can optimize the math operations.
> > +                */
> > +               return (early_unsafe_cycles() * NS_PER_KHZ) / CONFIG_EARLY_CYCLES_KHZ;
> > +       return 0;
> > +}
> > +#else
> > +static inline u64 early_times_ns(void)
> > +{
> > +       return 0;
> > +}
> > +#endif
> > +
> > +#endif /* _EARLY_TIMES_H */
> > diff --git a/init/Kconfig b/init/Kconfig
> > index 7484cd703bc1..40c3123c2c27 100644
> > --- a/init/Kconfig
> > +++ b/init/Kconfig
> > @@ -792,6 +792,36 @@ config IKHEADERS
> >           or similar programs.  If you build the headers as a module, a module called
> >           kheaders.ko is built which can be loaded on-demand to get access to headers.
> >
> > +config EARLY_CYCLES_KHZ
> > +       int "Frequency of the early times cycle counter, in KHz"
> > +       default 0
> > +       depends on PRINTK
> > +       depends on ARM64 || X86_64 || RISCV_TIMER
> > +       help
> > +         Specifies the frequency value (in KHz) of the cycle counter
> > +         used for early times information.
> > +
> > +         Set this to provide timing information for printks in early boot
> > +         (before the start of kernel timekeeping), that would otherwise
> > +         show as 0.
> > +
> > +         To calculate the value for your system, boot your system, and
> > +         use the value expressed on the tsc or cntvct_el0 calibration line
> > +         in your kernel message log:
> > +         ex x86_64: tsc: Refined TSC clocksource calibration: 2095.057 MHz
> > +          In that case, use the value 2095057.
> > +         ex arm64: arch_timer: cp15 timer running at 54.00MHz (phys).
> > +         In that case, use the value of 54000.
> > +
> > +         Note that this causes the kernel to show, for some early printks,
> > +         times that are relative to processor power on, instead of
> > +         relative to the start of kernel timekeeping.  When kernel
> > +         timekeeping starts, the timestamps values reset, causing
> > +         a discontinuity in the timestamp values.  If you see
> > +         non-monotonic printk timestamps, please don't freak out.
> > +
> > +         Set to 0 to disable this feature.
> > +
> >  config LOG_BUF_SHIFT
> >         int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
> >         range 12 25
> > diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> > index 0323149548f6..ddeab81f04ef 100644
> > --- a/kernel/printk/printk.c
> > +++ b/kernel/printk/printk.c
> > @@ -46,6 +46,7 @@
> >  #include <linux/ctype.h>
> >  #include <linux/uio.h>
> >  #include <linux/sched/clock.h>
> > +#include <linux/early_times.h>
> >  #include <linux/sched/debug.h>
> >  #include <linux/sched/task_stack.h>
> >  #include <linux/panic.h>
> > @@ -2294,6 +2295,8 @@ int vprintk_store(int facility, int level,
> >          * timestamp with respect to the caller.
> >          */
> >         ts_nsec = local_clock();
> > +       if (unlikely(!ts_nsec))
> > +               ts_nsec = early_times_ns();
> >
> >         caller_id = printk_caller_id();
> >
> > --
> > 2.43.0
> >
> >

^ permalink raw reply

* Re: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Andrew Murray @ 2026-04-27 23:33 UTC (permalink / raw)
  To: Tim Bird
  Cc: pmladek, rostedt, john.ogness, senozhatsky, francesco, geert,
	tglx, shashankbalaji02, linux-embedded, linux-kernel
In-Reply-To: <20260410203741.997410-2-tim.bird@sony.com>

Hi Tim,

On Fri, 10 Apr 2026 at 21:41, Tim Bird <tim.bird@sony.com> wrote:
>
> During early boot, printk timestamps are reported as zero before
> kernel timekeeping starts (i.e. before time_init()).  This
> hinders boot-time optimization efforts.  This period ranges from
> 17 to 1700 milliseconds on different embedded machines running Linux.
>
> Add support for early timestamps based on processor cycle-generators
> that need no kernel initialization. Use a statically-configured
> frequency value for converting cycles to nanoseconds. This means
> that this feature cannot be turned on in generic distro kernels.
> It is intended for temporary use during kernel development and
> boot-time research and optimization only.
>
> This yields non-zero timestamps for printks from the very start
> of kernel execution.  The timestamps are relative to the start of
> an architecture-specific counter (e.g. tsc on x86_64 and cntvct_el0
> on arm64).  Affected timestamps reflect time from cycle counter
> init (usually machine power-on or virtual machine start) instead of
> time from the kernel's timekeeping initialization. This results in a
> discontinuity in the printk timestamp values, one time, when
> kernel timekeeping starts.
>
> Signed-off-by: Tim Bird <tim.bird@sony.com>
> ---

The limitation of this series is that while it narrows the window
where delays may be hiding, it doesn't remove the window completely.
For example, there may be a delay prior to the first printk (perhaps
unlikely). It would instead be useful to understand how much time has
passed since execution was handed over to the kernel.

Also, in my view, the goal of this work is really to identify
regressions in boot time, though this can be achieved via other means.
Replacing zero timestamps in printk messages with real timestamps is
only necessary to make it easier to bisect where those delays are
hiding, i.e. between two printk messages. But that benefit it limited
given how small the window is. (And if a regression is spotted,
someone would likely add debug to find it anyway).

Given the risks identified by others with this series and the above, I
thought I'd suggest an alternative approach.

Perhaps we can rely on a prior boot stage (e.g. U-Boot) to capture a
timestamp (perhaps cntvct on arm) immediately prior to kernel
handover, this could be stored in the device tree and it could include
architecture information on where that timestamp came from. During
boot, once timekeeping is initialised, the architecture specific
timekeeping initialisation code can print relevant timestamp values to
the console, alongside the data captured in the device tree.

Userspace tools (systemd-analyze) or log parsing tools, can then make
sense of the values. The time between kernel handover and the end of
timekeeping initalisation printk can be calculated. You could also
subtract the earliest non-zero printk timestamp from this figure to
measure the time between kernel handover and first non-zero timestamp.

Besides logging values from hardware registers to the console, this
approach pushes out all the maths to userspace or log parsing tools.
It also doesn't impact existing printk timestamps and can be
structured to not introduce new get_cycles types of functions.

If this type of approach is taken, it may be worth investigating other
initiatives in this area, for example [1], [2], [3] which may be
relevant.

[1] https://static.sched.com/hosted_files/osseu2025/a2/EOSS_2025_Unified%20Boot%20Time%20log%20based%20measurement.pdf
[2] https://issues.chromium.org/issues/40986147
[3] https://github.com/FirmwareHandoff/firmware_handoff

Thanks,

Andrew Murray

> V3 -> V4
>   Replace config vars with single one: CONFIG_EARLY_CYCLES_KHZ
>   Replace runtime calibration with static config variable
>   Remove reference to get_cycles()
>   Add support for RISCV platforms
> V2 -> V3
>   Default CONFIG option to 'n'
>   Move more code into early_times.h (reduce ifdefs in init/main.c)
>   Use match64 helper routines
>   Use cycles_t instead of u64 type
>   Add #defines for EARLY_CYCLES_BIT and EARLY_CYCLES_MASK
>   Invert if logic in adjust_early_ts()
> V1 -> V2
>   Remove calibration CONFIG vars
>   Add 'depends on' to restrict arches (to handle ppc bug)
>   Add early_ts_offset to avoid discontinuity
>   Save cycles in ts_nsec, and convert on output
>   Move conditional code to include file (early_times.h>
> ---
>  include/linux/early_times.h | 55 +++++++++++++++++++++++++++++++++++++
>  init/Kconfig                | 30 ++++++++++++++++++++
>  kernel/printk/printk.c      |  3 ++
>  3 files changed, 88 insertions(+)
>  create mode 100644 include/linux/early_times.h
>
> diff --git a/include/linux/early_times.h b/include/linux/early_times.h
> new file mode 100644
> index 000000000000..82bacfd0e26b
> --- /dev/null
> +++ b/include/linux/early_times.h
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _EARLY_TIMES_H
> +#define _EARLY_TIMES_H
> +
> +#include <linux/timekeeping.h>
> +#ifdef CONFIG_ARM64
> +#include <asm/sysreg.h>
> +#endif
> +
> +#ifdef CONFIG_EARLY_CYCLES_KHZ
> +static inline u64 early_unsafe_cycles(void)
> +{
> +#if defined(CONFIG_X86_64)
> +       /*
> +        * This rdtsc may happen before secure TSC is initialized, and
> +        * it is unordered. So please don't use this value for cryptography
> +        * or after SMP is initialized.
> +        */
> +       return rdtsc();
> +#elif defined(CONFIG_ARM64)
> +       return read_sysreg(cntvct_el0);
> +#elif defined(CONFIG_RISCV_TIMER)
> +       u64 val;
> +
> +       asm volatile("rdtime %0" : "=r"(val));
> +       return val;
> +#else
> +       return 0;
> +#endif
> +}
> +
> +#define NS_PER_KHZ     1000000UL
> +
> +/* returns a nanosecond value based on early cycles */
> +static inline u64 early_times_ns(void)
> +{
> +       if (CONFIG_EARLY_CYCLES_KHZ)
> +               /*
> +                * Note: the multiply must precede the division to avoid
> +                * truncation and loss of resolution
> +                * Don't use fancier MULT/SHIFT math here.  Since this is
> +                * static, the compiler can optimize the math operations.
> +                */
> +               return (early_unsafe_cycles() * NS_PER_KHZ) / CONFIG_EARLY_CYCLES_KHZ;
> +       return 0;
> +}
> +#else
> +static inline u64 early_times_ns(void)
> +{
> +       return 0;
> +}
> +#endif
> +
> +#endif /* _EARLY_TIMES_H */
> diff --git a/init/Kconfig b/init/Kconfig
> index 7484cd703bc1..40c3123c2c27 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -792,6 +792,36 @@ config IKHEADERS
>           or similar programs.  If you build the headers as a module, a module called
>           kheaders.ko is built which can be loaded on-demand to get access to headers.
>
> +config EARLY_CYCLES_KHZ
> +       int "Frequency of the early times cycle counter, in KHz"
> +       default 0
> +       depends on PRINTK
> +       depends on ARM64 || X86_64 || RISCV_TIMER
> +       help
> +         Specifies the frequency value (in KHz) of the cycle counter
> +         used for early times information.
> +
> +         Set this to provide timing information for printks in early boot
> +         (before the start of kernel timekeeping), that would otherwise
> +         show as 0.
> +
> +         To calculate the value for your system, boot your system, and
> +         use the value expressed on the tsc or cntvct_el0 calibration line
> +         in your kernel message log:
> +         ex x86_64: tsc: Refined TSC clocksource calibration: 2095.057 MHz
> +          In that case, use the value 2095057.
> +         ex arm64: arch_timer: cp15 timer running at 54.00MHz (phys).
> +         In that case, use the value of 54000.
> +
> +         Note that this causes the kernel to show, for some early printks,
> +         times that are relative to processor power on, instead of
> +         relative to the start of kernel timekeeping.  When kernel
> +         timekeeping starts, the timestamps values reset, causing
> +         a discontinuity in the timestamp values.  If you see
> +         non-monotonic printk timestamps, please don't freak out.
> +
> +         Set to 0 to disable this feature.
> +
>  config LOG_BUF_SHIFT
>         int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
>         range 12 25
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> index 0323149548f6..ddeab81f04ef 100644
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -46,6 +46,7 @@
>  #include <linux/ctype.h>
>  #include <linux/uio.h>
>  #include <linux/sched/clock.h>
> +#include <linux/early_times.h>
>  #include <linux/sched/debug.h>
>  #include <linux/sched/task_stack.h>
>  #include <linux/panic.h>
> @@ -2294,6 +2295,8 @@ int vprintk_store(int facility, int level,
>          * timestamp with respect to the caller.
>          */
>         ts_nsec = local_clock();
> +       if (unlikely(!ts_nsec))
> +               ts_nsec = early_times_ns();
>
>         caller_id = printk_caller_id();
>
> --
> 2.43.0
>
>

^ permalink raw reply

* Reminder of Boot-Time SIG meeting (April 28, 2026)
From: Bird, Tim @ 2026-04-25 20:07 UTC (permalink / raw)
  To: Linux Embedded

Hey Linux Boot-Time SIG interested parties (and other interested Linux kernel developers),

Here is the information for the next Linux Boot-Time SIG conference call.

The meeting will be held via the Jitsi online meeting platform.
To Join the meeting via web, click on:
https://meet.jit.si/LinuxBootTimeSIG

----
Our next meeting is Tuesday, April 28, at 9:00 am Mountain Daylight Time.
See this link for other time zones:
https://www.timeanddate.com/worldclock/meetingdetails.html?year=2026&month=04&day=28&hour=15&min=0&sec=0&p1=220&p2=137&p3=195&p4=771
(That makes it 8:00 am Pacific, 15:00 UTC, 17:00 CEST, and 20:30 IST)

I'm planning on 1 hour for this meeting.

The agenda for the meeting (and where we'll keep the minutes) is here:
https://docs.google.com/document/d/1XAufoTT6VVJOTMzKMoz8SyOss-JA9H4J1_yVXQq5mN0/edit?usp=sharing

The agenda for the April meeting will be available in the above document before the call.

As usual, we'll do a review of patches in progress, as well as discuss the following topics:
 - U-Boot Falcon Mode
 - Boot-time tuning wizard

If you have items you'd like to add to the agenda, please let me know.
(Please e-mail me and/or put them in the document.)

I look forward to talking to you then.

Thanks,
 -- Tim

^ permalink raw reply

* Re: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Roberto A. Foglietta @ 2026-04-21 16:55 UTC (permalink / raw)
  To: Bird, Tim
  Cc: Petr Mladek, Thomas Gleixner, rostedt@goodmis.org,
	john.ogness@linutronix.de, senozhatsky@chromium.org,
	francesco@valla.it, geert@linux-m68k.org,
	shashankbalaji02@gmail.com, linux-embedded@vger.kernel.org,
	linux-kernel@vger.kernel.org, Brian Masney
In-Reply-To: <MW5PR13MB5632E5E9176349336E9AD376FD2C2@MW5PR13MB5632.namprd13.prod.outlook.com>

On Tue, 21 Apr 2026 at 16:13, Bird, Tim <Tim.Bird@sony.com> wrote:
>
> > -----Original Message-----
> > From: Petr Mladek <pmladek@suse.com>
> > On Mon 2026-04-20 22:18:29, Bird, Tim wrote:

> > I think that we could print both notices from the printk() code:
> >
> >  + 1st one before the very first message
> >  + 2nd one before the first message with a proper timestamp from
> >    the local_clock().
>
> Yes.  That's a good solution.  I feel a bit dumb for not having thought of it. :-)

The very first message is when the kernel receive the hand off:

[    0.183957] Linux version 5.15.202 (roberto@x280) (x86_64-linux-musl-gcc
               (GCC) 14.3.0, GNU ld (GNU Binutils) 2.44) #1 Wed A6

The second one is when the skew happens

[    0.184241] kvm-clock: cpu 0, msr bac001, primary cpu clock
--------- time skew here ---------
[    0.000001] kvm-clock: using sched offset of 55985760 cycles

and also the second is already there: printed by kvm-clock about the
offset (3x the legacy rdtsc reading on a KVM machine, in my specific
case, at least).

[    0.000005] tsc: Detected 1896.002 MHz processor

Soon later another printk explains how to convert the early printk
timestamps into time, using the CPU clock. An information provided by
the tsc subsystem.

Everything was already there. Adding an how-to read data into Kconfig
description would impose the need to update that how-to and
differentiate it among arch/cases plus it might be wrong/useless when
working on an unsupported platforms or not a yet delivered products.
Moreover, it would put a burden on code janitors to explain stuff to
expert kernel hackers which is a non-sense role-inversion!

Therefore patch V6, again.

Best regards, R-

^ permalink raw reply

* RE: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot
From: Bird, Tim @ 2026-04-21 14:07 UTC (permalink / raw)
  To: Petr Mladek
  Cc: Thomas Gleixner, rostedt@goodmis.org, john.ogness@linutronix.de,
	senozhatsky@chromium.org, francesco@valla.it,
	geert@linux-m68k.org, shashankbalaji02@gmail.com,
	linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org,
	Brian Masney
In-Reply-To: <aec2a2RPO9rYQa0D@pathway.suse.cz>



> -----Original Message-----
> From: Petr Mladek <pmladek@suse.com>
> On Mon 2026-04-20 22:18:29, Bird, Tim wrote:
> > > From: Petr Mladek <pmladek@suse.com>
> > >    2. The early timestamps provided by the bogo cycles
> > >       are not synchronized with timestamps from
> > >       the proper time keeping.
> > >
> > >       Would it help to print a disclaimer, similar to,
> > >       for example, trace_printk() first use?
> > >       Something like:
> > >
> > > [    0.002912] **********************************************************
> > > [    0.002917] ****   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
> > > [    0.002921] **
> > > [    0.002935] ** Using BOGO early timestamps
> > > [    0.002939] **
> > > [    0.002943] ** They are not properly calibrated and might use a source
> > > [    0.002949] ** with an unstable frequency.
> > > [    0.002953] **
> > > [    0.002957] ** They are not comparable with timestamps after
> > > [    0.002961] ** the timekeeping is initialized.
> > > [    0.002966] **
> > > [    0.002968] ****   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
> > > [    0.002971] *******************************************************
> >
> > That's a bit long, but I think a warning like this is useful.
> 
> Yeah, feel free to make it shorter. I just wanted to explain what is
> going on and what is the effect.
> 
> > > [    0.002975] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
> > > [    0.002998] Linux version 7.0.0-rc6-v8+ (tbird@timdesk) (aarch64-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU
> > > Binutils for Ubuntu) 2.42) #20 SMP PREEMPT Fri Apr 10 11:57:48 MDT 2026
> > > [    0.003002] KASLR enabled
> > > [    0.003338] random: crng init done
> > > [    0.003866] Machine model: Raspberry Pi 4 Model B Rev 1.5
> > > [    0.004495] efi: UEFI not found.
> > > ...
> > > [    0.183552] Root IRQ handler: gic_handle_irq
> > > [    0.183561] GIC: Using split EOI/Deactivate mode
> > > [    0.183699] rcu: srcu_init: Setting srcu_struct sizes based on contention.
> > > [    0.183958] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0xc743ce346, max_idle_ns: 440795203123 ns
> > > [    0.183952] arch_timer: cp15 timer running at 54.00MHz (phys).
> > > [    0.183957] **********************************************************
> > > [    0.183962] ****   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
> > > [    0.183967] **
> > > [    0.183971] ** End of BOGO early timestamps
> > > [    0.183976] **
> > > [    0.183982] ****   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
> > > [    0.183989] **********************************************************
> >
> > I actually worked on a notice like this, to bridge/explain the transition (for humans).  But it turned
> > out that different platforms initialized their clocks differently enough that it would have
> > required putting the notice in different places, which would have required architecture-specific
> > #ifdefs in generic code (so I didn't do it).
> 
> I think that we could print both notices from the printk() code:
> 
>  + 1st one before the very first message
>  + 2nd one before the first message with a proper timestamp from
>    the local_clock().

Yes.  That's a good solution.  I feel a bit dumb for not having thought of it. :-)

I'll plan to add this to a V5 patch.  I'd like to wait a bit longer to submit
a V5 patch, to let the dust settle on discussion from your post.
If Thomas still objects, then I'll focus on adjusting the patch for out-of-tree
maintenance.

As an aside, having a couple of printks submitted in quick succession early on
is actually helpful to get a sense of time that each printk takes.

Thanks,
 -- Tim



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox