* Re: Call for Participation: Embedded & IoT micro-conference at Linux Plumbers 2025
From: Sebastian Reichel @ 2025-09-24 0:33 UTC (permalink / raw)
To: Brian Masney
Cc: Stefan Schmidt, Jan Lubbe, stefan.schmidt, linux-embedded,
linux-usb, linux-rockchip, Heikki Krogerus, Greg Kroah-Hartman,
RD Babiera, Amit Sunil Dhamne, kernel
In-Reply-To: <CABx5tq+U_hcmxXDCGpTawsu5C0bDknC=p1Rq3FrtVuJq9gj2Pw@mail.gmail.com>
Hi Brian,
On Tue, Sep 23, 2025 at 07:38:14PM -0400, Brian Masney wrote:
> Hi Sebastian,
>
> On Tue, Sep 23, 2025 at 7:03 PM Sebastian Reichel <sre@kernel.org> wrote:
> > I would like to present / discuss fusb302 (or other chips handled via
> > the TCPM framework) using boards that are mainly powered via USB-C and
> > not having any backup power source. This kind of setup is often found
> > on Rockchip boards (e.g. Libre Computer ROC-RK3399-PC, Radxa ROCK 5B
> > or ArmSoM Sige 5) and quite a pain, because a hard-reset effectively
> > kills the board power.
> >
> > I would present the problem(s), what I've done so far to get it working
> > to some degree with the upstream kernel and then discuss how to improve
> > the situation.
> >
> > I think to become a worthwhile discussion the session would need some
> > people that know the USB-PD specification and kernel subsystem, such as:
> >
> > * Heikki Krogerus (USB-C maintainer)
> > * Greg Kroah-Hartman (USB maintainer)
> > * RD Babiera or Amit Sunil Dhamne (Google is actively working on
> > ensuring TCPM code being compatible with the USB-PD specification
> > and they were unhappy about some of my changes :))
> >
> > P.S.: I'm not sure how the CfP for the LPC micro-conferences works.
> > Please tell me if this mail is not good enough and I need to insert
> > something into some system.
>
> You need to submit a proposal to
> https://lpc.events/event/19/abstracts/. You'll have to create an
> account, and there will be a "Submit new abstract" button at the
> bottom right of the page.
Ah, I was confused by the page not listing the embedded MC. I see
it appears in the drop down when trying to submit a new abstract.
Thanks for the hint - DONE.
> I went through the process for a talk that I submitted: "Fixing Clock
> Tree Propagation in the Common Clk Framework". It's related to this
> work:
> https://lore.kernel.org/linux-clk/20250923-clk-tests-docs-v4-0-9205cb3d3cba@redhat.com/T/
On most platforms I worked on we tried to have an exclusive PLL root
clock for DRM to avoid these issues. But sounds like a good topic!
Greetings,
-- Sebastian
^ permalink raw reply
* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
From: Dan Scally @ 2025-09-24 14:42 UTC (permalink / raw)
To: acampanella-thegoodpenguin, linux-embedded
In-Reply-To: <20250923-bootcache-v1-5-4f86fdc38b4e@thegoodpenguin.co.uk>
Hi Andrea
On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
> From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>
> bootcache_backend_memory provides a simple memory based backend that can
> inject data found stored in a reserved-memory block into the bootcache
> framework.
This looks really cool and I'd like to give it a go; how are you testing it currently? Do you have a
tool to create the blob that you're putting into the reserved memory that I could take a look at?
Thanks
Dan
>
> Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> ---
> drivers/base/Kconfig | 7 +
> drivers/base/Makefile | 1 +
> drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
> 3 files changed, 228 insertions(+)
>
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -95,6 +95,13 @@ config BOOTCACHE
> A simple backend for testing and development.
> It does not persist any data externally.
>
> + config BOOTCACHE_BACKEND_MEMORY
> + bool "Memory backend"
> + help
> + A backend that reads the cache data from reserved system memory.
> + The reserved memory block is defined in the device tree and is
> + assumed to be populated by the bootloader.
> +
> endchoice
> endif
>
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -10,6 +10,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
> obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
> obj-$(CONFIG_BOOTCACHE) += bootcache.o
> obj-$(CONFIG_BOOTCACHE_BACKEND_TEST) += bootcache_backend_test.o
> +obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY) += bootcache_backend_memory.o
> obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
> obj-y += power/
> obj-$(CONFIG_ISA_BUS_API) += isa.o
> diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
> --- /dev/null
> +++ b/drivers/base/bootcache_backend_memory.c
> @@ -0,0 +1,220 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#define DEBUG 1
> +#include <linux/unaligned.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/hashtable.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/kobject.h>
> +#include <linux/sysfs.h>
> +
> +#include <linux/bootcache.h>
> +
> +#define DRIVER_NAME "bootcache_memory_backend"
> +#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
> +#define BOOTCACHE_MINSIZE 4096
> +
> +/*
> + * This defines a cache entry as stored.
> + */
> +struct cache_memory_store_entry {
> + u32 key_length;
> + u32 data_length;
> + u8 data_type;
> + u8 data[];
> +} __packed;
> +
> +/*
> + * The in memory store of multiple cache entries.
> + */
> +struct cache_memory_store {
> + u32 magic;
> + u32 entry_count;
> + struct cache_memory_store_entry entries[];
> +} __packed;
> +
> +struct reserved_mem *rmem;
> +
> +/*
> + * This function processes the loaded data and adds each entry to the
> + * system cache via the callbck.
> + */
> +static int memory_backend_load_cache(void)
> +{
> + const struct cache_memory_store *store;
> + const u8 *current_ptr;
> + const void *max_address;
> + u32 entry_count;
> + int i;
> + int ret;
> +
> + if (!rmem) {
> + pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
> + return 0;
> + }
> +
> + store = ioremap(rmem->base, rmem->size);
> + if (!store) {
> + pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
> + return -ENOMEM;
> + }
> + max_address = store + rmem->size;
> + current_ptr = (const unsigned char *)store->entries;
> + entry_count = get_unaligned(&store->entry_count);
> +
> + for (i = 0; i < entry_count; i++) {
> + struct cache_memory_store_entry *entry;
> + struct bootcache_entry *new_entry = NULL;
> + size_t data_length, key_length;
> + u8 *src, *dest;
> + int j;
> +
> + entry = (struct cache_memory_store_entry *)current_ptr;
> + data_length = get_unaligned(&entry->data_length);
> + key_length = get_unaligned(&entry->key_length);
> +
> + /* Check if will go outside the bounds */
> + if ((current_ptr + sizeof(struct cache_memory_store_entry) +
> + data_length + key_length + 1) > max_address) {
> + ret = -ENOMEM;
> + goto error;
> + }
> +
> + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
> + if (!new_entry) {
> + ret = -ENOMEM;
> + goto error;
> + }
> +
> + new_entry->len = data_length;
> + new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
> + new_entry->data = kzalloc(data_length, GFP_KERNEL);
> +
> + if (!new_entry->key || !new_entry->data) {
> + pr_err("%s: Memory Allocation error creating new_entry data\n",
> + DRIVER_NAME);
> + kfree(new_entry->key);
> + kfree(new_entry->data);
> + kfree(new_entry);
> + ret = -ENOMEM;
> + goto error;
> + }
> + /*
> + * Source data is potentially unaligned, so we copy it with the correct
> + * access functions
> + */
> + src = &entry->data[0];
> + dest = new_entry->key;
> + for (j = 0; j < key_length; j++)
> + *dest++ = get_unaligned(src++);
> +
> + src = &entry->data[key_length+1];
> + dest = new_entry->data;
> + for (j = 0; j < data_length; j++)
> + *dest++ = get_unaligned(src++);
> +
> + pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
> + DRIVER_NAME, i, new_entry->key, new_entry->len);
> +
> + /* call the framework provided function */
> + ret = bootcache_add_entry(new_entry);
> + if (ret) {
> + kfree(new_entry->key);
> + kfree(new_entry->data);
> + kfree(new_entry);
> + ret = 0;
> + }
> +
> + /* Sanity check we've got space for the next extry */
> +
> + current_ptr += sizeof(struct cache_memory_store_entry) +
> + data_length + key_length + 1;
> + if (current_ptr + sizeof(struct cache_memory_store_entry)
> + > max_address) {
> + ret = ret = -ENOMEM;
> + goto error;
> + }
> + }
> +
> +error:
> + if (store)
> + iounmap((void *)store);
> +
> + return ret;
> +}
> +
> +static struct bootcache_info cache_info = {
> + .name = "memory",
> + .load_cache = memory_backend_load_cache,
> +};
> +
> +static int bootcache_backend_probe(struct platform_device *pdev)
> +{
> + int ret;
> + size_t table_size;
> + struct cache_memory_store *temp_store;
> + struct device_node *reserved_mem_node;
> +
> + /* Check for the front end being ready */
> +
> + pr_debug("%s: %s\n", DRIVER_NAME, __func__);
> +
> + reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
> + if (reserved_mem_node) {
> + rmem = of_reserved_mem_lookup(reserved_mem_node);
> + of_node_put(reserved_mem_node);
> + }
> +
> + if (!rmem) {
> + pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
> + return -ENOMEM;
> + }
> + pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
> + pr_debug("%s: Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
> + pr_debug("%s: Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
> + rmem->size);
> +
> + if (rmem->size < BOOTCACHE_MINSIZE) {
> + pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
> + return -ENOMEM;
> + }
> +
> + ret = bootcache_register_backend(&cache_info);
> +
> + if (ret < 0) {
> + pr_err("%s: bootcache_register_backend() failed with error %d\n",
> + DRIVER_NAME, ret);
> + return ret;
> + }
> + pr_info("%s: Backend loaded\n", DRIVER_NAME);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
> + { .compatible = "linux,backend-backend-memory", },
> + { }
> +};
> +
> +static struct platform_driver bootcache_memory_platform_driver = {
> + .probe = bootcache_backend_probe,
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
> + },
> +};
> +
> +static int __init bootcache_backend_init(void)
> +{
> + return platform_driver_register(&bootcache_memory_platform_driver);
> +}
> +
> +core_initcall(bootcache_backend_init);
>
^ permalink raw reply
* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
From: Andrea Campanella @ 2025-09-24 15:13 UTC (permalink / raw)
To: Dan Scally, linux-embedded
In-Reply-To: <362096ec-c5eb-4fe9-8b26-6b280306e0d7@ideasonboard.com>
On 9/24/25 15:42, Dan Scally wrote:
> Hi Andrea
>
> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
>> From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>>
>> bootcache_backend_memory provides a simple memory based backend that can
>> inject data found stored in a reserved-memory block into the bootcache
>> framework.
>
> This looks really cool and I'd like to give it a go; how are you
> testing it currently? Do you have a tool to create the blob that
> you're putting into the reserved memory that I could take a look at?
>
> Thanks
> Dan
>
>
Hello Dan,
I'm glad you are finding it interesting!
We are in the process of cleaning up the demo for public use, we are
using qemuarm64 to test it, we should be releasing the test setup soon!
^ permalink raw reply
* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
From: Marc Kelly @ 2025-09-26 17:34 UTC (permalink / raw)
To: linux-embedded; +Cc: Dan Scally
In-Reply-To: <362096ec-c5eb-4fe9-8b26-6b280306e0d7@ideasonboard.com>
Hi Dan,
There is a yocto build that targets qemuarm64 available at
https://github.com/The-Good-Penguin/TGP-kernel-cache-test which has
the latest patches and a script for generating the test cache data as
a binary for the bootloader to insert into reserved memory.
There are build instructions and it should be able to run with qemu
via the commands shown in the readme in the repo.
If you have any questions don't hesitate to get in touch. Our plan is
to keep the test distro up to date as we add more features etc as it's
a covenant way to test and demo things.
Best regards
Marc.
On Wed, 24 Sept 2025 at 15:42, Dan Scally <dan.scally@ideasonboard.com> wrote:
>
> Hi Andrea
>
> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
> > From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> >
> > bootcache_backend_memory provides a simple memory based backend that can
> > inject data found stored in a reserved-memory block into the bootcache
> > framework.
>
> This looks really cool and I'd like to give it a go; how are you testing it currently? Do you have a
> tool to create the blob that you're putting into the reserved memory that I could take a look at?
>
> Thanks
> Dan
>
> >
> > Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> > ---
> > drivers/base/Kconfig | 7 +
> > drivers/base/Makefile | 1 +
> > drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
> > 3 files changed, 228 insertions(+)
> >
> > diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> > index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
> > --- a/drivers/base/Kconfig
> > +++ b/drivers/base/Kconfig
> > @@ -95,6 +95,13 @@ config BOOTCACHE
> > A simple backend for testing and development.
> > It does not persist any data externally.
> >
> > + config BOOTCACHE_BACKEND_MEMORY
> > + bool "Memory backend"
> > + help
> > + A backend that reads the cache data from reserved system memory.
> > + The reserved memory block is defined in the device tree and is
> > + assumed to be populated by the bootloader.
> > +
> > endchoice
> > endif
> >
> > diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> > index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
> > --- a/drivers/base/Makefile
> > +++ b/drivers/base/Makefile
> > @@ -10,6 +10,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
> > obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
> > obj-$(CONFIG_BOOTCACHE) += bootcache.o
> > obj-$(CONFIG_BOOTCACHE_BACKEND_TEST) += bootcache_backend_test.o
> > +obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY) += bootcache_backend_memory.o
> > obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
> > obj-y += power/
> > obj-$(CONFIG_ISA_BUS_API) += isa.o
> > diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
> > --- /dev/null
> > +++ b/drivers/base/bootcache_backend_memory.c
> > @@ -0,0 +1,220 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#define DEBUG 1
> > +#include <linux/unaligned.h>
> > +#include <linux/init.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/slab.h>
> > +#include <linux/hashtable.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_reserved_mem.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <linux/types.h>
> > +#include <linux/errno.h>
> > +#include <linux/kobject.h>
> > +#include <linux/sysfs.h>
> > +
> > +#include <linux/bootcache.h>
> > +
> > +#define DRIVER_NAME "bootcache_memory_backend"
> > +#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
> > +#define BOOTCACHE_MINSIZE 4096
> > +
> > +/*
> > + * This defines a cache entry as stored.
> > + */
> > +struct cache_memory_store_entry {
> > + u32 key_length;
> > + u32 data_length;
> > + u8 data_type;
> > + u8 data[];
> > +} __packed;
> > +
> > +/*
> > + * The in memory store of multiple cache entries.
> > + */
> > +struct cache_memory_store {
> > + u32 magic;
> > + u32 entry_count;
> > + struct cache_memory_store_entry entries[];
> > +} __packed;
> > +
> > +struct reserved_mem *rmem;
> > +
> > +/*
> > + * This function processes the loaded data and adds each entry to the
> > + * system cache via the callbck.
> > + */
> > +static int memory_backend_load_cache(void)
> > +{
> > + const struct cache_memory_store *store;
> > + const u8 *current_ptr;
> > + const void *max_address;
> > + u32 entry_count;
> > + int i;
> > + int ret;
> > +
> > + if (!rmem) {
> > + pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
> > + return 0;
> > + }
> > +
> > + store = ioremap(rmem->base, rmem->size);
> > + if (!store) {
> > + pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
> > + return -ENOMEM;
> > + }
> > + max_address = store + rmem->size;
> > + current_ptr = (const unsigned char *)store->entries;
> > + entry_count = get_unaligned(&store->entry_count);
> > +
> > + for (i = 0; i < entry_count; i++) {
> > + struct cache_memory_store_entry *entry;
> > + struct bootcache_entry *new_entry = NULL;
> > + size_t data_length, key_length;
> > + u8 *src, *dest;
> > + int j;
> > +
> > + entry = (struct cache_memory_store_entry *)current_ptr;
> > + data_length = get_unaligned(&entry->data_length);
> > + key_length = get_unaligned(&entry->key_length);
> > +
> > + /* Check if will go outside the bounds */
> > + if ((current_ptr + sizeof(struct cache_memory_store_entry) +
> > + data_length + key_length + 1) > max_address) {
> > + ret = -ENOMEM;
> > + goto error;
> > + }
> > +
> > + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
> > + if (!new_entry) {
> > + ret = -ENOMEM;
> > + goto error;
> > + }
> > +
> > + new_entry->len = data_length;
> > + new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
> > + new_entry->data = kzalloc(data_length, GFP_KERNEL);
> > +
> > + if (!new_entry->key || !new_entry->data) {
> > + pr_err("%s: Memory Allocation error creating new_entry data\n",
> > + DRIVER_NAME);
> > + kfree(new_entry->key);
> > + kfree(new_entry->data);
> > + kfree(new_entry);
> > + ret = -ENOMEM;
> > + goto error;
> > + }
> > + /*
> > + * Source data is potentially unaligned, so we copy it with the correct
> > + * access functions
> > + */
> > + src = &entry->data[0];
> > + dest = new_entry->key;
> > + for (j = 0; j < key_length; j++)
> > + *dest++ = get_unaligned(src++);
> > +
> > + src = &entry->data[key_length+1];
> > + dest = new_entry->data;
> > + for (j = 0; j < data_length; j++)
> > + *dest++ = get_unaligned(src++);
> > +
> > + pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
> > + DRIVER_NAME, i, new_entry->key, new_entry->len);
> > +
> > + /* call the framework provided function */
> > + ret = bootcache_add_entry(new_entry);
> > + if (ret) {
> > + kfree(new_entry->key);
> > + kfree(new_entry->data);
> > + kfree(new_entry);
> > + ret = 0;
> > + }
> > +
> > + /* Sanity check we've got space for the next extry */
> > +
> > + current_ptr += sizeof(struct cache_memory_store_entry) +
> > + data_length + key_length + 1;
> > + if (current_ptr + sizeof(struct cache_memory_store_entry)
> > + > max_address) {
> > + ret = ret = -ENOMEM;
> > + goto error;
> > + }
> > + }
> > +
> > +error:
> > + if (store)
> > + iounmap((void *)store);
> > +
> > + return ret;
> > +}
> > +
> > +static struct bootcache_info cache_info = {
> > + .name = "memory",
> > + .load_cache = memory_backend_load_cache,
> > +};
> > +
> > +static int bootcache_backend_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + size_t table_size;
> > + struct cache_memory_store *temp_store;
> > + struct device_node *reserved_mem_node;
> > +
> > + /* Check for the front end being ready */
> > +
> > + pr_debug("%s: %s\n", DRIVER_NAME, __func__);
> > +
> > + reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
> > + if (reserved_mem_node) {
> > + rmem = of_reserved_mem_lookup(reserved_mem_node);
> > + of_node_put(reserved_mem_node);
> > + }
> > +
> > + if (!rmem) {
> > + pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
> > + return -ENOMEM;
> > + }
> > + pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
> > + pr_debug("%s: Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
> > + pr_debug("%s: Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
> > + rmem->size);
> > +
> > + if (rmem->size < BOOTCACHE_MINSIZE) {
> > + pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
> > + return -ENOMEM;
> > + }
> > +
> > + ret = bootcache_register_backend(&cache_info);
> > +
> > + if (ret < 0) {
> > + pr_err("%s: bootcache_register_backend() failed with error %d\n",
> > + DRIVER_NAME, ret);
> > + return ret;
> > + }
> > + pr_info("%s: Backend loaded\n", DRIVER_NAME);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
> > + { .compatible = "linux,backend-backend-memory", },
> > + { }
> > +};
> > +
> > +static struct platform_driver bootcache_memory_platform_driver = {
> > + .probe = bootcache_backend_probe,
> > + .driver = {
> > + .name = DRIVER_NAME,
> > + .of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
> > + },
> > +};
> > +
> > +static int __init bootcache_backend_init(void)
> > +{
> > + return platform_driver_register(&bootcache_memory_platform_driver);
> > +}
> > +
> > +core_initcall(bootcache_backend_init);
> >
>
>
--
Marc Kelly, Engineering Manager
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 PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
From: Dan Scally @ 2025-09-26 20:09 UTC (permalink / raw)
To: Marc Kelly, linux-embedded
In-Reply-To: <CAHkF4vQz5=1T4D=cF0zEdrHfgeTq7sfGxbUH_dohvYtG7ANGjw@mail.gmail.com>
Hi Marc
On 26/09/2025 18:34, Marc Kelly wrote:
> Hi Dan,
>
> There is a yocto build that targets qemuarm64 available at
> https://github.com/The-Good-Penguin/TGP-kernel-cache-test which has
> the latest patches and a script for generating the test cache data as
> a binary for the bootloader to insert into reserved memory.
>
> There are build instructions and it should be able to run with qemu
> via the commands shown in the readme in the repo.
>
> If you have any questions don't hesitate to get in touch. Our plan is
> to keep the test distro up to date as we add more features etc as it's
> a covenant way to test and demo things.
Thanks! That's great. I did actually give it a try on a board already and it seems to work well. I
knocked up a script that's pretty similar to your generate_test_cache.py but parses a yaml file to
create the .bin and load it and yeah - looks good to me.
I don't know if you're wanting reviews on the patches already yet, but if you let me know when you
do and CC me on the sets I'll make sure I review them
Thanks
Dan
>
> Best regards
> Marc.
>
> On Wed, 24 Sept 2025 at 15:42, Dan Scally <dan.scally@ideasonboard.com> wrote:
>>
>> Hi Andrea
>>
>> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
>>> From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>>>
>>> bootcache_backend_memory provides a simple memory based backend that can
>>> inject data found stored in a reserved-memory block into the bootcache
>>> framework.
>>
>> This looks really cool and I'd like to give it a go; how are you testing it currently? Do you have a
>> tool to create the blob that you're putting into the reserved memory that I could take a look at?
>>
>> Thanks
>> Dan
>>
>>>
>>> Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>>> ---
>>> drivers/base/Kconfig | 7 +
>>> drivers/base/Makefile | 1 +
>>> drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
>>> 3 files changed, 228 insertions(+)
>>>
>>> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
>>> index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
>>> --- a/drivers/base/Kconfig
>>> +++ b/drivers/base/Kconfig
>>> @@ -95,6 +95,13 @@ config BOOTCACHE
>>> A simple backend for testing and development.
>>> It does not persist any data externally.
>>>
>>> + config BOOTCACHE_BACKEND_MEMORY
>>> + bool "Memory backend"
>>> + help
>>> + A backend that reads the cache data from reserved system memory.
>>> + The reserved memory block is defined in the device tree and is
>>> + assumed to be populated by the bootloader.
>>> +
>>> endchoice
>>> endif
>>>
>>> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
>>> index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
>>> --- a/drivers/base/Makefile
>>> +++ b/drivers/base/Makefile
>>> @@ -10,6 +10,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
>>> obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
>>> obj-$(CONFIG_BOOTCACHE) += bootcache.o
>>> obj-$(CONFIG_BOOTCACHE_BACKEND_TEST) += bootcache_backend_test.o
>>> +obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY) += bootcache_backend_memory.o
>>> obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
>>> obj-y += power/
>>> obj-$(CONFIG_ISA_BUS_API) += isa.o
>>> diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
>>> new file mode 100644
>>> index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
>>> --- /dev/null
>>> +++ b/drivers/base/bootcache_backend_memory.c
>>> @@ -0,0 +1,220 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +#define DEBUG 1
>>> +#include <linux/unaligned.h>
>>> +#include <linux/init.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/hashtable.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/of_reserved_mem.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/string.h>
>>> +#include <linux/types.h>
>>> +#include <linux/errno.h>
>>> +#include <linux/kobject.h>
>>> +#include <linux/sysfs.h>
>>> +
>>> +#include <linux/bootcache.h>
>>> +
>>> +#define DRIVER_NAME "bootcache_memory_backend"
>>> +#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
>>> +#define BOOTCACHE_MINSIZE 4096
>>> +
>>> +/*
>>> + * This defines a cache entry as stored.
>>> + */
>>> +struct cache_memory_store_entry {
>>> + u32 key_length;
>>> + u32 data_length;
>>> + u8 data_type;
>>> + u8 data[];
>>> +} __packed;
>>> +
>>> +/*
>>> + * The in memory store of multiple cache entries.
>>> + */
>>> +struct cache_memory_store {
>>> + u32 magic;
>>> + u32 entry_count;
>>> + struct cache_memory_store_entry entries[];
>>> +} __packed;
>>> +
>>> +struct reserved_mem *rmem;
>>> +
>>> +/*
>>> + * This function processes the loaded data and adds each entry to the
>>> + * system cache via the callbck.
>>> + */
>>> +static int memory_backend_load_cache(void)
>>> +{
>>> + const struct cache_memory_store *store;
>>> + const u8 *current_ptr;
>>> + const void *max_address;
>>> + u32 entry_count;
>>> + int i;
>>> + int ret;
>>> +
>>> + if (!rmem) {
>>> + pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
>>> + return 0;
>>> + }
>>> +
>>> + store = ioremap(rmem->base, rmem->size);
>>> + if (!store) {
>>> + pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
>>> + return -ENOMEM;
>>> + }
>>> + max_address = store + rmem->size;
>>> + current_ptr = (const unsigned char *)store->entries;
>>> + entry_count = get_unaligned(&store->entry_count);
>>> +
>>> + for (i = 0; i < entry_count; i++) {
>>> + struct cache_memory_store_entry *entry;
>>> + struct bootcache_entry *new_entry = NULL;
>>> + size_t data_length, key_length;
>>> + u8 *src, *dest;
>>> + int j;
>>> +
>>> + entry = (struct cache_memory_store_entry *)current_ptr;
>>> + data_length = get_unaligned(&entry->data_length);
>>> + key_length = get_unaligned(&entry->key_length);
>>> +
>>> + /* Check if will go outside the bounds */
>>> + if ((current_ptr + sizeof(struct cache_memory_store_entry) +
>>> + data_length + key_length + 1) > max_address) {
>>> + ret = -ENOMEM;
>>> + goto error;
>>> + }
>>> +
>>> + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
>>> + if (!new_entry) {
>>> + ret = -ENOMEM;
>>> + goto error;
>>> + }
>>> +
>>> + new_entry->len = data_length;
>>> + new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
>>> + new_entry->data = kzalloc(data_length, GFP_KERNEL);
>>> +
>>> + if (!new_entry->key || !new_entry->data) {
>>> + pr_err("%s: Memory Allocation error creating new_entry data\n",
>>> + DRIVER_NAME);
>>> + kfree(new_entry->key);
>>> + kfree(new_entry->data);
>>> + kfree(new_entry);
>>> + ret = -ENOMEM;
>>> + goto error;
>>> + }
>>> + /*
>>> + * Source data is potentially unaligned, so we copy it with the correct
>>> + * access functions
>>> + */
>>> + src = &entry->data[0];
>>> + dest = new_entry->key;
>>> + for (j = 0; j < key_length; j++)
>>> + *dest++ = get_unaligned(src++);
>>> +
>>> + src = &entry->data[key_length+1];
>>> + dest = new_entry->data;
>>> + for (j = 0; j < data_length; j++)
>>> + *dest++ = get_unaligned(src++);
>>> +
>>> + pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
>>> + DRIVER_NAME, i, new_entry->key, new_entry->len);
>>> +
>>> + /* call the framework provided function */
>>> + ret = bootcache_add_entry(new_entry);
>>> + if (ret) {
>>> + kfree(new_entry->key);
>>> + kfree(new_entry->data);
>>> + kfree(new_entry);
>>> + ret = 0;
>>> + }
>>> +
>>> + /* Sanity check we've got space for the next extry */
>>> +
>>> + current_ptr += sizeof(struct cache_memory_store_entry) +
>>> + data_length + key_length + 1;
>>> + if (current_ptr + sizeof(struct cache_memory_store_entry)
>>> + > max_address) {
>>> + ret = ret = -ENOMEM;
>>> + goto error;
>>> + }
>>> + }
>>> +
>>> +error:
>>> + if (store)
>>> + iounmap((void *)store);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static struct bootcache_info cache_info = {
>>> + .name = "memory",
>>> + .load_cache = memory_backend_load_cache,
>>> +};
>>> +
>>> +static int bootcache_backend_probe(struct platform_device *pdev)
>>> +{
>>> + int ret;
>>> + size_t table_size;
>>> + struct cache_memory_store *temp_store;
>>> + struct device_node *reserved_mem_node;
>>> +
>>> + /* Check for the front end being ready */
>>> +
>>> + pr_debug("%s: %s\n", DRIVER_NAME, __func__);
>>> +
>>> + reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
>>> + if (reserved_mem_node) {
>>> + rmem = of_reserved_mem_lookup(reserved_mem_node);
>>> + of_node_put(reserved_mem_node);
>>> + }
>>> +
>>> + if (!rmem) {
>>> + pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
>>> + return -ENOMEM;
>>> + }
>>> + pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
>>> + pr_debug("%s: Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
>>> + pr_debug("%s: Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
>>> + rmem->size);
>>> +
>>> + if (rmem->size < BOOTCACHE_MINSIZE) {
>>> + pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + ret = bootcache_register_backend(&cache_info);
>>> +
>>> + if (ret < 0) {
>>> + pr_err("%s: bootcache_register_backend() failed with error %d\n",
>>> + DRIVER_NAME, ret);
>>> + return ret;
>>> + }
>>> + pr_info("%s: Backend loaded\n", DRIVER_NAME);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
>>> + { .compatible = "linux,backend-backend-memory", },
>>> + { }
>>> +};
>>> +
>>> +static struct platform_driver bootcache_memory_platform_driver = {
>>> + .probe = bootcache_backend_probe,
>>> + .driver = {
>>> + .name = DRIVER_NAME,
>>> + .of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
>>> + },
>>> +};
>>> +
>>> +static int __init bootcache_backend_init(void)
>>> +{
>>> + return platform_driver_register(&bootcache_memory_platform_driver);
>>> +}
>>> +
>>> +core_initcall(bootcache_backend_init);
>>>
>>
>>
>
>
^ permalink raw reply
* RE: [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit
From: Bird, Tim @ 2025-09-29 23:38 UTC (permalink / raw)
To: acampanella-thegoodpenguin, linux-embedded@vger.kernel.org
In-Reply-To: <20250923-bootcache-v1-1-4f86fdc38b4e@thegoodpenguin.co.uk>
> -----Original Message-----
> From: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
>
> bootcache provides boot-time key-value cache to help improve
> boot performance by allowing drivers to cache expensive computations.
>
> Simple API are provided:
> - bootcache_get_u16()/bootcache_set_u16() - retrieve/store u16 values
> - bootcache_get_u32()/bootcache_set_u32() - retrieve/store u32 values
> - bootcache_get_u64()/bootcache_set_u64() - retrieve/store u64 values
> - bootcache_get_string()/bootcache_set_string() - retrieve/store strings
> - bootcache_register_backend() - Backend registration
> - bootcache_add_entry() - Add cache entry into framework
>
> Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> Signed-off-by: Andrea Campanella <acampanella@thegoodpenguin.co.uk>
> ---
> MAINTAINERS | 6 ++
> drivers/base/Kconfig | 11 +++
> drivers/base/Makefile | 1 +
> drivers/base/bootcache.c | 179 +++++++++++++++++++++++++++++++++++++
> include/linux/bootcache.h | 219 ++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 416 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index daf520a13bdf6a991c0160a96620f40308c29ee0..4bf3766b30bbf75214911bd4ce5256a066f05726 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4418,6 +4418,12 @@ S: Maintained
> F: Documentation/devicetree/bindings/iio/imu/bosch,bmi323.yaml
> F: drivers/iio/imu/bmi323/
>
> +BOOT CACHE
> +M: Andrea Campanella <acampanella@thegoodpenguin.co.uk>
> +S: Maintained
> +F: drivers/base/bootcache.c
> +F: include/linux/bootcache.h
> +
> BPF JIT for ARC
> M: Shahab Vahedi <list+bpf@vahedi.org>
> L: bpf@vger.kernel.org
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index 064eb52ff7e2d4d8745e9c39882b41dc4cf02a89..da02f95948d880da83c3025addc1e111dbce339a 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -73,6 +73,17 @@ config DEVTMPFS_SAFE
> with the PROT_EXEC flag. This can break, for example, non-KMS
> video drivers.
>
> +config BOOTCACHE
> + bool "Boot-time cache for the kernel"
> + help
> + Enable a simple key-value cache subsystem for storing boot-time
> + configuration data. This allows drivers and kernel subsystems to
> + cache expensive computations during boot, potentially improving
> + boot performance on subsequent reboots by avoiding redundant
> + hardware detection and initialization work
> +
> + If unsure, say N.
> +
> config STANDALONE
> bool "Select only drivers that don't need compile-time external firmware"
> default y
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 8074a10183dcb720a6b820b8476b230716b37f01..10a16e6c2ea1ad778fb7793583b9ee54d2498b2b 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -8,6 +8,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
> topology.o container.o property.o cacheinfo.o \
> swnode.o faux.o
> obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
> +obj-$(CONFIG_BOOTCACHE) += bootcache.o
> obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
> obj-y += power/
> obj-$(CONFIG_ISA_BUS_API) += isa.o
> diff --git a/drivers/base/bootcache.c b/drivers/base/bootcache.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d74ead796b0f50ca9a90e84e7230b9ad6ca896d8
> --- /dev/null
> +++ b/drivers/base/bootcache.c
> @@ -0,0 +1,179 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/hashtable.h>
> +#include <linux/string.h>
> +#include <linux/stringhash.h>
> +#include <linux/types.h>
> +#include <linux/spinlock.h>
> +#include <linux/errno.h>
> +#include <linux/kobject.h>
> +#include <linux/sysfs.h>
> +#include <linux/bootcache.h>
> +
> +static DEFINE_HASHTABLE(bootcache_table, BOOTCACHE_HASH_BITS);
> +static DEFINE_SPINLOCK(bootcache_lock);
> +static struct kobject *bootcache_kobj;
> +static struct bootcache_info *bootcache_backend;
> +static bool bootcache_initilized;
> +
> +int bootcache_register_backend(struct bootcache_info *bci)
> +{
> + int ret;
> +
> + if (!bci)
> + return -EINVAL;
> + if (!bci->name)
> + return -EINVAL;
> +
> + /* If we're not ready, tell backend to try again later */
> + if (!bootcache_initilized)
> + return -EPROBE_DEFER;
> +
> + if (bootcache_backend) {
> + pr_warn("bootcache: Backend '%s' is already registered, cannot register '%s'\n",
> + bootcache_backend->name, bci->name);
> + return -EBUSY;
> + }
> + pr_info("bootcache: Registering backend '%s'\n",
> + bci->name);
> +
> + /* Have the backend load and populate the cache store */
> + ret = bci->load_cache();
> +
> + if (ret)
> + goto failed_initilize;
> +
> + bootcache_backend = bci;
> + return 0;
> +
> +failed_initilize:
> + return ret;
> +}
> +EXPORT_SYMBOL(bootcache_register_backend);
> +
> +int bootcache_get(const char *name, void *buf, size_t *len)
> +{
> + struct bootcache_entry *entry;
> + u32 hash;
> + int ret = -ENOENT;
> +
> + if (!name || !buf || !len)
> + return -EINVAL;
> +
> + hash = full_name_hash(NULL, name, strlen(name));
> +
> + spin_lock(&bootcache_lock);
> + hash_for_each_possible(bootcache_table, entry, node, hash) {
> + if (strcmp(entry->key, name) == 0) {
> + if (*len < entry->len) {
> + *len = entry->len;
> + ret = -ENOSPC;
> + goto unlock;
> + }
> + memcpy(buf, entry->data, entry->len);
> + *len = entry->len;
> + ret = 0;
> + goto unlock;
> + }
> + }
> +
> +unlock:
> + spin_unlock(&bootcache_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL(bootcache_get);
> +
> +int bootcache_add_entry(struct bootcache_entry *entry)
> +{
> + u32 hash;
> + struct bootcache_entry *existing_entry;
> + int ret = 0;
> +
> + hash = full_name_hash(NULL, entry->key, strlen(entry->key));
> +
> + spin_lock(&bootcache_lock);
> +
> + hash_for_each_possible(bootcache_table, existing_entry, node, hash) {
> + if (strcmp(existing_entry->key, entry->key) == 0) {
> + ret = -EEXIST; // Key already exists
> + goto unlock;
> + }
> + }
> +
> + hash_add(bootcache_table, &entry->node, hash);
> +
> +unlock:
> + spin_unlock(&bootcache_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL(bootcache_add_entry);
> +
> +int bootcache_set(const char *name, const void *data, size_t len)
> +{
> + struct bootcache_entry *new_entry;
> + u32 hash;
> + int ret = 0;
> +
> + if (!name || !data || !len)
> + return -EINVAL;
> +
> + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
> + if (!new_entry)
> + return -ENOMEM;
> +
> + new_entry->key = kstrdup(name, GFP_KERNEL);
> + if (!new_entry->key) {
> + ret = -ENOMEM;
> + goto free;
> + }
> +
> + new_entry->data = kmemdup(data, len, GFP_KERNEL);
> + if (!new_entry->data) {
> + ret = -ENOMEM;
> + goto free;
> + }
> +
> + new_entry->len = len;
> + ret = bootcache_add_entry(new_entry);
> + if (!ret)
> + return 0;
> +
> +free:
> + kfree(new_entry->data);
> + kfree(new_entry->key);
> + kfree(new_entry);
> + return ret;
> +}
> +EXPORT_SYMBOL(bootcache_set);
> +
> +static ssize_t writeout_store(struct kobject *kobj, struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + /*Implement persistent storage backend */
> + return count;
> +}
> +
> +static struct kobj_attribute writeout_attr = __ATTR_WO(writeout);
> +
> +static int __init bootcache_init(void)
> +{
> + int ret;
> +
> + pr_info("bootcache: backend loaded\n");
> +
> + /* Create /sys/kernel/bootcache/writeout */
> + bootcache_kobj = kobject_create_and_add("bootcache", kernel_kobj);
> + if (!bootcache_kobj)
> + return -ENOMEM;
> +
> + ret = sysfs_create_file(bootcache_kobj, &writeout_attr.attr);
> + if (ret) {
> + kobject_put(bootcache_kobj);
> + return ret;
> + }
> + bootcache_initilized = true;
> + return 0;
> +}
> +core_initcall(bootcache_init);
> diff --git a/include/linux/bootcache.h b/include/linux/bootcache.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..52c07cf09bb87fc6c305485e5409bd235ede4e6e
> --- /dev/null
> +++ b/include/linux/bootcache.h
> @@ -0,0 +1,219 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LINUX_BOOTCACHE_H
> +#define _LINUX_BOOTCACHE_H
> +
> +#include <linux/types.h>
> +
> +#ifdef CONFIG_BOOTCACHE
> +
> +#define BOOTCACHE_HASH_BITS 6 /* 64 buckets */
> +
> +struct bootcache_entry {
> + struct hlist_node node;
> + char *key;
> + void *data;
> + size_t len;
> +};
> +
> +/**
> + * struct bootcache_info - Structure for registering a boot cache backend.
> + *
> + * @name: The name of the backend.
> + *
> + * Callbacks:
> + * @load_cache: Callback function to read and populate the framework from the cache.
> + */
> +
> +struct bootcache_info {
> + const char *name;
> + /* Callback Function Pointers */
> + int (*load_cache)(void);
> +};
> +
> +/**
> + * bootcache_add_entry - Add an entry directly into the hash table
> + * @entry: bootcache_entry structure
> + *
> + * Returns: 0 on success, entry was added, do not free it.
> + * 1 on success but an existing entry was updated,
> + * free to deallocate entry.
> + */
> +int bootcache_add_entry(struct bootcache_entry *entry);
> +
> +/**
> + * bootcache_register_backend - Register a backend provider with the framework
> + * @bci: bootcache_info structure
> + *
> + * Returns: 0 on success, -EPROBE_DEFER if the frontend is not ready,
> + * -EBUSY if another backend is already registered,
> + * -EINVAL on invalid registration information.
> + */
> +int bootcache_register_backend(struct bootcache_info *bci);
> +
> +/**
> + * bootcache_get - Retrieve arbitrary data from the cache
> + * @name: Key to look up
> + * @buf: Buffer to store retrieved data
> + * @len: On input, size of buffer; on output, actual data size
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters,
> + * -ENOENT if not found, -ENOSPC if buffer too small
> + */
> +int bootcache_get(const char *name, void *buf, size_t *len);
> +
> +/**
> + * bootcache_set - Store arbitrary data in the cache
> + * @name: Key to store under
> + * @data: Data to store
> + * @len: Length of data
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +int bootcache_set(const char *name, const void *data, size_t len);
> +
> +#else /* !CONFIG_BOOTCACHE */
> +
> +static inline int bootcache_get(const char *name, void *buf, size_t *len)
> +{
> + return -ENOENT;
> +}
> +
> +static inline int bootcache_set(const char *name, const void *data, size_t len)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_u16 - Retrieve a u16 value from the cache
> + * @name: Key to look up
> + * @out_val: Pointer to store the retrieved value
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
> + */
> +static inline int bootcache_get_u16(const char *name, u16 *out_val)
> +{
> + size_t len = sizeof(u16);
> +
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
This seems redundant given we're in a #else /* !CONFIG_BOOTCACHE */ conditional section
How could IS_ENABLED() return true? (did I miss an #endif somewhere?)
> + return bootcache_get(name, out_val, &len);
> + else
> + return -ENOENT;
Come to think of it, why are these specialized calls in a !CONFIG_BOOTCACHE conditional section?
If the base call 'bootcache_get' is already conditional, and turns into a 'return -ENOENT', then
this call (bootcache_get_u16()), will turn into 'return -ENOENT' also, which will cause the whole
call chain to evaporate at the callsite (due to the conditional there on '==0')
I'm not sure any of these specialized calls need to be in conditional sections - just the base
ones.
> +}
> +
> +/**
> + * bootcache_set_u16 - Store a u16 value in the cache
> + * @name: Key to store under
> + * @val: Value to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_u16(const char *name, u16 val)
> +{
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
> + return bootcache_set(name, &val, sizeof(u16));
> + else
> + return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_u32 - Retrieve a u32 value from the cache
> + * @name: Key to look up
> + * @out_val: Pointer to store the retrieved value
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
> + */
> +static inline int bootcache_get_u32(const char *name, u32 *out_val)
> +{
> + size_t len = sizeof(u32);
> +
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
> + return bootcache_get(name, out_val, &len);
> + else
> + return -ENOENT;
> +}
> +
> +/**
> + * bootcache_set_u32 - Store a u32 value in the cache
> + * @name: Key to store under
> + * @val: Value to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_u32(const char *name, u32 val)
> +{
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
> + return bootcache_set(name, &val, sizeof(u32));
> + else
> + return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_u64 - Retrieve a u64 value from the cache
> + * @name: Key to look up
> + * @out_val: Pointer to store the retrieved value
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
> + */
> +static inline int bootcache_get_u64(const char *name, u64 *out_val)
> +{
> + size_t len = sizeof(u64);
> +
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
> + return bootcache_get(name, out_val, &len);
> + else
> + return -ENOENT;
> +}
> +
> +/**
> + * bootcache_set_u64 - Store a u64 value in the cache
> + * @name: Key to store under
> + * @val: Value to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_u64(const char *name, u64 val)
> +{
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
> + return bootcache_set(name, &val, sizeof(u64));
> + else
> + return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_string - Retrieve a string from the cache
> + * @name: Key to look up
> + * @buf: Buffer to store retrieved string
> + * @buflen: Size of buffer
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters,
> + * -ENOENT if not found, -ENOSPC if buffer too small
> + */
> +static inline int bootcache_get_string(const char *name, char *buf, size_t buflen)
> +{
> + size_t len = buflen;
> +
> + if (IS_ENABLED(CONFIG_BOOTCACHE))
> + return bootcache_get(name, buf, &len);
> + else
> + return -ENOENT;
> +}
> +
> +/**
> + * bootcache_set_string - Store a string in the cache
> + * @name: Key to store under
> + * @str: Null-terminated string to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_string(const char *name, const char *str)
> +{
> + if (IS_ENABLED(CONFIG_BOOTCACHE)) {
> + if (!str)
> + return -EINVAL;
> + return bootcache_set(name, str, strlen(str) + 1);
> + } else {
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +#endif /* _LINUX_BOOTCACHE_H */
>
> --
> 2.48.1
>
^ permalink raw reply
* RE: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
From: Bird, Tim @ 2025-09-29 23:48 UTC (permalink / raw)
To: acampanella-thegoodpenguin, linux-embedded@vger.kernel.org
In-Reply-To: <20250923-bootcache-v1-3-4f86fdc38b4e@thegoodpenguin.co.uk>
> -----Original Message-----
> From: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
> Sent: Tuesday, September 23, 2025 8:24 AM
> To: linux-embedded@vger.kernel.org
> Subject: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
>
> From: Andrew Murray <amurray@ thegoodpenguin. co. uk> During boot xor_blocks may determine the fastest xor algorithm by using
> do_xor_speed to perform a speed test on available algorithms. This process can increase the overall boot time. Let's
> From: Andrew Murray <amurray@thegoodpenguin.co.uk>
>
> During boot xor_blocks may determine the fastest xor algorithm
> by using do_xor_speed to perform a speed test on available
> algorithms. This process can increase the overall boot time.
It would be good to mention the amount of time we're talking about here.
It won't be the same value for all platforms, but you could mention the
amount of time this takes on the platform you're working on (or on a particularly
slow machine, if you want, just to highlight where it might be a problem
for some situations but not all.)
>
> Let's make use of bootcache to cache the result of the speed
> test for subsequent boots.
>
> Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
> ---
> crypto/xor.c | 29 ++++++++++++++++++++++++++++-
> 1 file changed, 28 insertions(+), 1 deletion(-)
>
> diff --git a/crypto/xor.c b/crypto/xor.c
> index f39621a57bb33c4015c06dff00e03a07716618f6..3457df0414064758a1923752e91642d2237af7b3 100644
> --- a/crypto/xor.c
> +++ b/crypto/xor.c
> @@ -14,6 +14,7 @@
> #include <linux/raid/xor.h>
> #include <linux/jiffies.h>
> #include <linux/preempt.h>
> +#include <linux/bootcache.h>
> #include <asm/xor.h>
>
> #ifndef XOR_SELECT_TEMPLATE
> @@ -54,13 +55,13 @@ EXPORT_SYMBOL(xor_blocks);
> /* Set of all registered templates. */
> static struct xor_block_template *__initdata template_list;
>
> -#ifndef MODULE
> static void __init do_xor_register(struct xor_block_template *tmpl)
> {
> tmpl->next = template_list;
> template_list = tmpl;
> }
>
> +#ifndef MODULE
> static int __init register_xor_blocks(void)
> {
> active_template = XOR_SELECT_TEMPLATE(NULL);
> @@ -79,6 +80,21 @@ static int __init register_xor_blocks(void)
> #define BENCH_SIZE 4096
> #define REPS 800U
>
> +static struct xor_block_template * __init
> +xor_get_template_by_name(char *fastest_name)
> +{
> + struct xor_block_template *f;
> +
> +#define xor_speed do_xor_register
> + // build a list of templates
> + XOR_TRY_TEMPLATES;
> +#undef xor_speed
> + for (f = template_list; f; f = f->next)
> + if (!strcmp(f->name, fastest_name))
> + return f;
> + return NULL;
> +}
> +
> static void __init
> do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
> {
> @@ -117,9 +133,18 @@ calibrate_xor_blocks(void)
> {
> void *b1, *b2;
> struct xor_block_template *f, *fastest;
> + char cached_name[32];
> + int ret;
>
> fastest = XOR_SELECT_TEMPLATE(NULL);
>
> + if (!fastest) {
> + ret = bootcache_get_string("xor_blocks_fastest",
> + cached_name, sizeof(cached_name));
> + if (!ret)
> + fastest = xor_get_template_by_name(cached_name);
I presume that if CONFIG_BOOTCACHE is not defined, then ret ends up being -ENOENT
always, which makes this whole block completely evaporate. Is that right?
In that case, do you end up with a warning about unused variable cached_name?
Or does the compiler know not to complain about that? Do you still end up with
space reserved on the stack?
> + }
> +
> if (fastest) {
> printk(KERN_INFO "xor: automatically using best "
> "checksumming function %-10s\n",
> @@ -149,6 +174,8 @@ calibrate_xor_blocks(void)
> if (f->speed > fastest->speed)
> fastest = f;
>
> + bootcache_set_string("xor_blocks_fastest", fastest->name);
> +
> pr_info("xor: using function: %s (%d MB/sec)\n",
> fastest->name, fastest->speed);
>
>
> --
> 2.48.1
>
^ permalink raw reply
* Re: [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit
From: Andrea Campanella @ 2025-09-30 8:24 UTC (permalink / raw)
To: Bird, Tim, linux-embedded@vger.kernel.org
In-Reply-To: <MW5PR13MB5632F23BDCA56EBFF25B5837FD1BA@MW5PR13MB5632.namprd13.prod.outlook.com>
On 9/30/25 00:38, Bird, Tim wrote:
>
>
> This seems redundant given we're in a #else /* !CONFIG_BOOTCACHE */ conditional section
> How could IS_ENABLED() return true? (did I miss an #endif somewhere?)
>
>> + return bootcache_get(name, out_val, &len);
>> + else
>> + return -ENOENT;
> Come to think of it, why are these specialized calls in a !CONFIG_BOOTCACHE conditional section?
> If the base call 'bootcache_get' is already conditional, and turns into a 'return -ENOENT', then
> this call (bootcache_get_u16()), will turn into 'return -ENOENT' also, which will cause the whole
> call chain to evaporate at the callsite (due to the conditional there on '==0')
>
> I'm not sure any of these specialized calls need to be in conditional sections - just the base
> ones.
>
Hi Tim,
Thanks for your feedback, good spot on that conditional, I will get it
fixed in the last patch!
^ permalink raw reply
* Re: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
From: Andrew Murray @ 2025-09-30 11:37 UTC (permalink / raw)
To: Bird, Tim; +Cc: acampanella-thegoodpenguin, linux-embedded@vger.kernel.org
In-Reply-To: <MW5PR13MB56323768BFB9EE9C5D18DB8DFD1BA@MW5PR13MB5632.namprd13.prod.outlook.com>
On Tue, 30 Sept 2025 at 00:48, Bird, Tim <Tim.Bird@sony.com> wrote:
>
> > -----Original Message-----
> > From: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
> > Sent: Tuesday, September 23, 2025 8:24 AM
> > To: linux-embedded@vger.kernel.org
> > Subject: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
> >
> > From: Andrew Murray <amurray@ thegoodpenguin. co. uk> During boot xor_blocks may determine the fastest xor algorithm by using
> > do_xor_speed to perform a speed test on available algorithms. This process can increase the overall boot time. Let's
> > From: Andrew Murray <amurray@thegoodpenguin.co.uk>
> >
> > During boot xor_blocks may determine the fastest xor algorithm
> > by using do_xor_speed to perform a speed test on available
> > algorithms. This process can increase the overall boot time.
>
> It would be good to mention the amount of time we're talking about here.
> It won't be the same value for all platforms, but you could mention the
> amount of time this takes on the platform you're working on (or on a particularly
> slow machine, if you want, just to highlight where it might be a problem
> for some situations but not all.)
Good idea, thanks!
Though, it may be that a whole bunch of patches like this are
accumulated, you boot the kernel with them enabled, blindly cache all
the results (for reuse in subsequent boots), and gain a quicker boot.
I.e. it may not be necessary for users to pick and choose.
> >
> > Let's make use of bootcache to cache the result of the speed
> > test for subsequent boots.
> >
> > Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
> > ---
> > crypto/xor.c | 29 ++++++++++++++++++++++++++++-
> > 1 file changed, 28 insertions(+), 1 deletion(-)
> >
> > diff --git a/crypto/xor.c b/crypto/xor.c
> > index f39621a57bb33c4015c06dff00e03a07716618f6..3457df0414064758a1923752e91642d2237af7b3 100644
> > --- a/crypto/xor.c
> > +++ b/crypto/xor.c
> > @@ -14,6 +14,7 @@
> > #include <linux/raid/xor.h>
> > #include <linux/jiffies.h>
> > #include <linux/preempt.h>
> > +#include <linux/bootcache.h>
> > #include <asm/xor.h>
> >
> > #ifndef XOR_SELECT_TEMPLATE
> > @@ -54,13 +55,13 @@ EXPORT_SYMBOL(xor_blocks);
> > /* Set of all registered templates. */
> > static struct xor_block_template *__initdata template_list;
> >
> > -#ifndef MODULE
> > static void __init do_xor_register(struct xor_block_template *tmpl)
> > {
> > tmpl->next = template_list;
> > template_list = tmpl;
> > }
> >
> > +#ifndef MODULE
> > static int __init register_xor_blocks(void)
> > {
> > active_template = XOR_SELECT_TEMPLATE(NULL);
> > @@ -79,6 +80,21 @@ static int __init register_xor_blocks(void)
> > #define BENCH_SIZE 4096
> > #define REPS 800U
> >
> > +static struct xor_block_template * __init
> > +xor_get_template_by_name(char *fastest_name)
> > +{
> > + struct xor_block_template *f;
> > +
> > +#define xor_speed do_xor_register
> > + // build a list of templates
> > + XOR_TRY_TEMPLATES;
> > +#undef xor_speed
> > + for (f = template_list; f; f = f->next)
> > + if (!strcmp(f->name, fastest_name))
> > + return f;
> > + return NULL;
> > +}
> > +
> > static void __init
> > do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
> > {
> > @@ -117,9 +133,18 @@ calibrate_xor_blocks(void)
> > {
> > void *b1, *b2;
> > struct xor_block_template *f, *fastest;
> > + char cached_name[32];
> > + int ret;
> >
> > fastest = XOR_SELECT_TEMPLATE(NULL);
> >
> > + if (!fastest) {
> > + ret = bootcache_get_string("xor_blocks_fastest",
> > + cached_name, sizeof(cached_name));
> > + if (!ret)
> > + fastest = xor_get_template_by_name(cached_name);
>
> I presume that if CONFIG_BOOTCACHE is not defined, then ret ends up being -ENOENT
> always, which makes this whole block completely evaporate. Is that right?
> In that case, do you end up with a warning about unused variable cached_name?
> Or does the compiler know not to complain about that? Do you still end up with
> space reserved on the stack?
Good spot, I'm not sure the answer but i'll find out.
Thanks,
Andrew Murray
>
> > + }
> > +
> > if (fastest) {
> > printk(KERN_INFO "xor: automatically using best "
> > "checksumming function %-10s\n",
> > @@ -149,6 +174,8 @@ calibrate_xor_blocks(void)
> > if (f->speed > fastest->speed)
> > fastest = f;
> >
> > + bootcache_set_string("xor_blocks_fastest", fastest->name);
> > +
> > pr_info("xor: using function: %s (%d MB/sec)\n",
> > fastest->name, fastest->speed);
> >
> >
> > --
> > 2.48.1
> >
>
^ permalink raw reply
* Re: [PATCH PREVIEW RFC 0/6] Add support for boot-time caching
From: Rob Landley @ 2025-09-30 12:49 UTC (permalink / raw)
To: acampanella-thegoodpenguin, linux-embedded
In-Reply-To: <20250923-bootcache-v1-0-4f86fdc38b4e@thegoodpenguin.co.uk>
On 9/23/25 09:23, acampanella-thegoodpenguin wrote:
> This patch series provides a simple key-value cache for storing boot-time
> configuration data. The goal is to allow kernel components and drivers to
> cache time-consuming-to-compute values during boot, enabling faster
> subsequent boots by avoiding redundant initialization work.
Does the vmlinux build use --gc-sections yet so this sort of thing can
drop out when it's not needed? (Marking it _init still eats early boot
and flash space.)
The kernel build is a giant complicated thing using linker scripts so I
don't want to blindly throw -ffunction-sections -fdata-sections in there
but this plumbing strikes me as something that will penalize systems
that _don't_ calculate expensive things and refer to them multiple times
(or would rather eat the CPU than eat the RAM to do it). And adding more
kconfig nonsense to have the build manually pull it in seems silly when
the tools have been able to do this automatically for over 20 years...
Rob
^ permalink raw reply
* [boot-time] Reminder of Boot-Time SIG meeting (Oct 28)
From: Bird, Tim @ 2025-10-24 16:38 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, October 28, at 9:00 am Mountain Daylight Time.
See this link for other time zones:
https://www.timeanddate.com/worldclock/meetingdetails.html?year=2025&month=10&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, 16:00 CET, 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 October 28 meeting will be available in the above document before the call.
Here are a few highlights:
- Saravana Kannan will be discussing fw_devlink, which can support driver ordering for device-tree related drivers,
reducing probe deferrals (and potentially increasing parallelization?) during the boot process.
Saravana gave a talk on this at LPC 2024 on this. If you want to review that talk, see https://lpc.events/event/18/contributions/1734/
- recent patches contributed or accepted into the upstream kernel
- ongoing work in progress (UBL, deferred probes, kernel boot-time regression test, printk 0-timestamps fix)
- information on the elinux wiki
- upcoming conferences and collaboration opportunities
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
* [PATCH RFC 0/3] Add splash DRM client
From: Francesco Valla @ 2025-10-26 23:03 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas
Cc: Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
Hello,
this patchset adds a new DRM client offering splash functionalities,
able to draw to screen:
- a colored background;
- a single-line text message, which can be set through sysfs or
directly from the kernel command line;
- a very simple progress bar, which can be driven through sysfs;
- a static image (optional).
Once compiled inside the kernel, the client can be enabled through the
command line specifying the drm_client_lib.active=splash parameter.
== Motivation ==
The motivation behind this work is to offer to embedded system
developers a new path for a simple activation of the display(s)
connected to their system, with the following usecases:
- bootsplash - possibly displaying even before init;
- early activation of the display pipeline, in particular whenever one
component of the pipeline (e.g.: a panel) takes a non-negligible
time to initialize;
- recovery systems, where the splash client can offer a simple feedback
for unattended recovery tasks;
- update systems, where the splash client can offer a simple feedback
for unattended update tasks.
While the first seems the most obvious one, it was the second that acted
as the driver, as in the past I had to implement a ugly workaround using
a systemd generator to kickstart the initialization of a display and
shave ~400ms of boot time.
The last 2 usecase, instead, are the reason I dropped the "boot" part
from bootsplash.
== Implementation details ==
The design is quite simple, with a kernel thread doing the heavylifting
for the rendering part and some locking to protect interactions with it.
The splash image is loaded using the firmware framework, with the client
expecting to find a binary dump having the right dimensions (width and
height) and FOURCC format for each modeset. Given a 1920x1080 RGB888
modeset, the client will for example search for a firmware named:
drm_splash_1920x1080_RG24.raw
If the firmware cannot be loaded directly, the NOUEVENT sysfs fallback
mechanism is used to let userspace load the appropriate image.
== Testing ==
Testing was done on qemu (both with vkms and bochs drivers), on a HDMI
display connected to a Beagleplay and on a ILI9341 SPI display connected
to a i.MX93 FRDM board. All these platforms revealed different
weaknesses that were hopefully removed.
== Open points / issues ==
The reason for this being an RFC is that there are several open points:
- Support for tiled connectors should be there, but has not been
tested. Any idea on how to test it?
- I'm not entirely convinced that using the firmware framework to load
the images is the right path. The idea behind it was to re-use the
compressed firmware support, but then I discovered it is not there
for built-in firmware.
- Again on the firmware loading: CONFIG_LOADPIN would interfere with
sysfs loading.
- And again: FW_ACTION_NOUEVENT only has one user inside the kernel,
leading me to think it is de-facto deprecated. And still, uevents
for firmware loading seem frowned upon these days...
- Generating binary dumps for... basically any format is not so
straightforward. I crafted a Python tool with AI help which seems
to work quite well, but I honestly did not yet understood which is
the policy for AI-generated code inside the kernel, so it is not
included in this patch set. All client code is genuine, though.
== Additional notes ==
A bootsplash client was one of the TODOs for the DRM subsystem, so patch
3 removes the relative section from the list.
Curious to hear your thoughts. Thank you in advance!
Best regards,
Francesco
Signed-off-by: Francesco Valla <francesco@valla.it>
---
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 | 46 +-
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 | 761 ++++++++++++++++++++++++++
7 files changed, 831 insertions(+), 18 deletions(-)
---
base-commit: 4bb1f7e19c4a1d6eeb52b80acff5ac63edd1b91d
change-id: 20251026-drm_client_splash-e10d7d663e7f
Best regards,
--
Francesco Valla <francesco@valla.it>
^ permalink raw reply
* [PATCH RFC 1/3] drm: client: add splash client
From: Francesco Valla @ 2025-10-26 23:03 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas
Cc: Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <20251027-drm_client_splash-v1-0-00698933b34a@valla.it>
Add a DRM client that draws a simple splash, with possibility to show:
- a colored background
- a text message
- a progress bar
- (optionally) a static splash image
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 text message and the progress bar can be set through sysfs
properties; a default value for the former can also be set from the
kernel command line using the drm_client_lib.message="<...>" parameter.
If enabled, the splash image is loaded as firmware for each DRM device.
The splash client expects this image to be a binary dump of the graphic
buffer to be shown on the screen connected to the modeset, with the
same dimensions (width and height) and color format.
Just like the existing DRM clients, the splash can be enable from the
kernel command line using drm_client_lib.active=splash.
Signed-off-by: Francesco Valla <francesco@valla.it>
---
drivers/gpu/drm/clients/Kconfig | 46 +-
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 | 761 ++++++++++++++++++++++++++
5 files changed, 824 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/clients/Kconfig b/drivers/gpu/drm/clients/Kconfig
index 6096c623d9d5b1a3d4a40d986c45aad2f8277767..0b23756f2453c9b815d9f242987d5f386b7a894f 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,46 @@ 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 graphical splash"
+ depends on DRM_CLIENT_SELECTION
+ select DRM_CLIENT
+ select DRM_CLIENT_SETUP
+ select DRM_DRAW
+ select FONT_SUPPORT
+ help
+ This enables a splash drm client, able to display a colored background,
+ a progress bar, some text and (optionally, if DRM_CLIENT_SPLASH_LOAD_AS_FW
+ is set) a graphical image until the userspace is ready to take over.
+
+ Message and progress can be set through sysfs; an initial message can also
+ be set by using the kernel command line 'drm_client_lib.message=' option.
+
+config DRM_CLIENT_SPLASH_FOREGROUND_COLOR
+ hex "Splash foreground color, in RGB"
+ depends on DRM_CLIENT_SPLASH
+ default 0xffffff
+
+config DRM_CLIENT_SPLASH_BACKGROUND_COLOR
+ hex "Splash background color, in RGB"
+ depends on DRM_CLIENT_SPLASH
+ default 0x000000
+
+config DRM_CLIENT_SPLASH_LOAD_AS_FW
+ bool "Load splash image as firmware"
+ depends on DRM_CLIENT_SPLASH
+ select FW_LOADER
+ help
+ Load image to be used as splash using the firmware loading facility the
+ kernel provides.
+
+ Even when this option is set, the image loading can be avoided by using
+ the kernel command line 'drm_client_lib.skip_image=true' option.
+
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 +148,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 +161,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 c16addbc327f09572aa3142cbf0d1d13f172a9e9..3df02d10cd18a47d7e8d7cee70163b0ef0129b51 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 6dc078bf6503b902cbb3267b64ea42d9f1c23375..48ee0c1c2529882b2bf5dc786788390823e25cd6 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 72480db1f00d0b9fcd1fe5aa72a3a31a074393b2..023a142319227c5f7ba887dcd55af4b04942f9ce 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 0000000000000000000000000000000000000000..308924e917e32d1e6d7686bf6f9b3a3ff2bafe68
--- /dev/null
+++ b/drivers/gpu/drm/clients/drm_splash.c
@@ -0,0 +1,761 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/*
+ * Copyright (c) 2025 Francesco Valla <francesco@valla.it>
+ *
+ */
+
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/font.h>
+#include <linux/init.h>
+#include <linux/iosys-map.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/types.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"
+
+// TODO: determine proper size for max message length
+#define DRM_SPLASH_MAX_MSG_LEN 128
+
+static char *message;
+module_param(message, charp, 0400);
+MODULE_PARM_DESC(message, "Initial message, up to " __stringify(DRM_SPLASH_MAX_MSG_LEN) " chars");
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+static bool skip_image;
+module_param(skip_image, bool, 0400);
+MODULE_PARM_DESC(skip_image, "Do not try to load splash image (default: false)");
+#endif
+
+/**
+ * DOC: overview
+ *
+ * This is a simple graphic bootsplash.
+ * Images to be shown are loaded as firmware.
+ */
+
+struct drm_splash_scanout {
+ int id;
+ u32 format;
+ unsigned int width;
+ unsigned int height;
+ struct drm_client_buffer *buffer;
+
+ struct mutex lock;
+ const struct font_desc *font;
+ bool bg_drawn;
+ bool message_drawn;
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+ const struct firmware *fw;
+#endif
+};
+
+struct drm_splash {
+ struct drm_client_dev client;
+ u32 preferred_format;
+ struct device dev;
+
+ struct mutex lock;
+ struct task_struct *thread;
+ atomic_t pending;
+ bool initialized;
+
+ char message[DRM_SPLASH_MAX_MSG_LEN];
+ u8 progress;
+
+ u32 n_scanout;
+ struct drm_splash_scanout *scanout;
+};
+
+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 u32 drm_splash_find_usable_format(struct drm_plane *plane,
+ u32 preferred_format)
+{
+ int i;
+
+ /* If preferred format is not set, use RGB888 (which offers full colors
+ * with minimal occupation).
+ */
+ if (preferred_format == 0)
+ preferred_format = DRM_FORMAT_RGB888;
+
+ /* 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_color_from_xrgb8888(0xffffffff, plane->format_types[i]) != 0)
+ return plane->format_types[i];
+
+ return DRM_FORMAT_INVALID;
+}
+
+static void drm_splash_blit(struct iosys_map *dst, unsigned int dst_pitch,
+ const u8 *src, unsigned int src_pitch,
+ u32 height, u32 width, u32 px_width, u32 color)
+{
+ switch (px_width) {
+ case 2:
+ drm_draw_blit16(dst, dst_pitch, src, src_pitch, height, width, 1, color);
+ break;
+ case 3:
+ drm_draw_blit24(dst, dst_pitch, src, src_pitch, height, width, 1, color);
+ break;
+ case 4:
+ drm_draw_blit32(dst, dst_pitch, src, src_pitch, height, width, 1, color);
+ break;
+ default:
+ WARN_ONCE(1, "Can't blit with pixel width %d\n", px_width);
+ }
+}
+
+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\n", 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_framebuffer_flush(buffer, &r);
+}
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+static int drm_splash_fill_from_data(struct drm_client_buffer *buffer,
+ const u8 *data, size_t data_len)
+{
+ 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);
+ struct iosys_map map;
+ size_t buffer_size;
+ int ret;
+
+ buffer_size = fb->width * fb->height * fb->format->cpp[0];
+ if (data_len != buffer_size) {
+ drm_err(client->dev,
+ "splash: data size mismatch (expected %zu, got %zu)",
+ data_len, buffer_size);
+ return -ENODATA;
+ }
+
+ ret = drm_client_buffer_vmap_local(buffer, &map);
+ if (ret) {
+ drm_err(client->dev, "splash: cannot vmap buffer: %d", ret);
+ return ret;
+ }
+
+ iosys_map_memcpy_to(&map, 0, data, data_len);
+
+ drm_client_buffer_vunmap_local(buffer);
+
+ return drm_client_framebuffer_flush(buffer, &r);
+}
+#endif
+
+static int drm_splash_draw_bar_message(struct drm_splash_scanout *scanout,
+ const char *msg,
+ unsigned int progress,
+ u32 bg_color,
+ u32 fg_color)
+{
+ struct drm_framebuffer *fb = scanout->buffer->fb;
+ const struct font_desc *font = scanout->font;
+ size_t font_pitch = DIV_ROUND_UP(font->width, 8);
+ u32 px_width = fb->format->cpp[0];
+ unsigned int y_padding = 2;
+ struct drm_rect r = DRM_RECT_INIT(0, fb->height * 3 / 4 - y_padding,
+ fb->width, font->height + y_padding);
+ unsigned int fill_width = drm_rect_width(&r) * progress / 100;
+ struct iosys_map map;
+ const u8 *src;
+ size_t i, len;
+
+ /* Clamp len if required */
+ len = min(strlen(msg), drm_rect_width(&r) / font->width);
+
+ if (drm_client_buffer_vmap_local(scanout->buffer, &map))
+ return -1;
+
+ /* Draw progress bar */
+ iosys_map_incr(&map, r.y1 * fb->pitches[0]);
+ drm_splash_fill(&map, fb->pitches[0], drm_rect_height(&r),
+ drm_rect_width(&r), px_width, bg_color);
+ drm_splash_fill(&map, fb->pitches[0], drm_rect_height(&r),
+ fill_width, px_width, fg_color);
+
+ /* Center the message horizontally */
+ iosys_map_incr(&map, y_padding * fb->pitches[0]);
+ iosys_map_incr(&map, (drm_rect_width(&r) - (font->width * len)) * px_width / 2);
+
+ /* Write message */
+ for (i = 0; i < len; i++) {
+ unsigned int ch_x;
+
+ src = drm_draw_get_char_bitmap(font, msg[i], font_pitch);
+
+ /* Use background color over fill bar, foreground otherwise */
+ ch_x = (drm_rect_width(&r) - font->width * len) / 2 + i * font->width;
+ drm_splash_blit(&map, fb->pitches[0], src, font_pitch,
+ font->height, font->width, px_width,
+ (fill_width > ch_x) ? bg_color : fg_color);
+ iosys_map_incr(&map, font->width * px_width);
+ }
+
+ drm_client_buffer_vunmap_local(scanout->buffer);
+ drm_client_framebuffer_flush(scanout->buffer, &r);
+
+ return 0;
+}
+
+static int drm_splash_draw_scanout(struct drm_splash_scanout *scanout,
+ const char *msg, unsigned int progress)
+{
+ u32 bg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_CLIENT_SPLASH_BACKGROUND_COLOR,
+ scanout->format);
+ u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_CLIENT_SPLASH_FOREGROUND_COLOR,
+ scanout->format);
+ int ret = -ENOENT;
+
+ if (!scanout->buffer)
+ return -ENODEV;
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+ if (!skip_image) {
+ const struct firmware *fw = NULL;
+
+ scoped_guard(mutex, &scanout->lock) {
+ fw = scanout->fw;
+ scanout->fw = NULL;
+ }
+
+ if (fw) {
+ ret = drm_splash_fill_from_data(scanout->buffer,
+ fw->data, fw->size);
+ release_firmware(fw);
+
+ if (ret == 0)
+ scanout->bg_drawn = true;
+ }
+ }
+#endif
+
+ /* If no firmware has been used to fill the screen (either by choice of
+ * because it's unavailable) fill the screen with the background color.
+ *
+ */
+ if (!scanout->bg_drawn) {
+ drm_splash_fill_solid_color(scanout->buffer, bg_color);
+ scanout->bg_drawn = true;
+ }
+
+ /* If message is empty and no previous message was shown, there is
+ * nothing to do
+ */
+ if (scanout->message_drawn || strlen(msg) != 0 || progress != 0) {
+ ret = drm_splash_draw_bar_message(scanout, msg, progress,
+ bg_color, fg_color);
+ if (ret)
+ return ret;
+
+ scanout->message_drawn = true;
+ }
+
+ return 0;
+}
+
+static int drm_splash_render_thread(void *data)
+{
+ struct drm_splash *splash = data;
+ struct drm_client_dev *client = &splash->client;
+ char buf[sizeof(splash->message)];
+ unsigned int progress;
+
+ while (!kthread_should_stop()) {
+ unsigned int draw_count = 0;
+ int j, ret;
+
+ /* Copy message and progress to be drawn, to avoid locking for
+ * too much time and/or showing different contents on different
+ * screens.
+ */
+ scoped_guard(mutex, &splash->lock) {
+ strscpy(buf, splash->message);
+ progress = splash->progress;
+ }
+
+ for (j = 0; j < splash->n_scanout; j++) {
+ ret = drm_splash_draw_scanout(&splash->scanout[j], buf,
+ progress);
+ if (ret) {
+ drm_err(client->dev,
+ "splash: failed to fill scanout %d: %d",
+ j, ret);
+ continue;
+ }
+
+ draw_count++;
+ }
+
+ 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 no changes arrived in the mean time, wait to be awaken by
+ * a sysfs write, a firmware callback or a stop command.
+ */
+ if (atomic_xchg(&splash->pending, 0) == 0)
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ schedule();
+ }
+
+ return 0;
+}
+
+static inline void drm_splash_wake_render_thread(struct drm_splash *splash)
+{
+ atomic_set(&splash->pending, 1);
+ wake_up_process(splash->thread);
+}
+
+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;
+ mutex_init(&scanout->lock);
+
+ 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_framebuffer_create(client,
+ scanout->width,
+ scanout->height,
+ scanout->format);
+ if (IS_ERR(scanout->buffer)) {
+ drm_warn(client->dev,
+ "splash: can't create framebuffer %d %d %p4cc",
+ scanout->width, scanout->height, &scanout->format);
+ continue;
+ }
+
+ drm_info(client->dev, "splash: created framebuffer %d %d %p4cc",
+ scanout->width, scanout->height, &scanout->format);
+
+ scanout->font = get_default_font(scanout->width, scanout->height,
+ NULL, NULL);
+ if (!scanout->font) {
+ drm_warn(client->dev,
+ "splash: failed to get default font");
+ }
+
+ 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_framebuffer_delete(splash->scanout[i].buffer);
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+ if (splash->scanout[i].fw)
+ release_firmware(splash->scanout[i].fw);
+#endif
+ mutex_destroy(&splash->scanout[i].lock);
+ }
+ splash->n_scanout = 0;
+ kfree(splash->scanout);
+ splash->scanout = NULL;
+ }
+}
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+static void drm_splash_fw_callback(const struct firmware *fw, void *context)
+{
+ struct drm_splash_scanout *scanout = context;
+ struct drm_client_dev *client = scanout->buffer->client;
+ struct drm_splash *splash = client_to_drm_splash(client);
+
+ if (!fw || !fw->data) {
+ drm_err(client->dev, "splash: no firmware");
+ return;
+ }
+
+ /* Assign new firmware to the scanout */
+ scoped_guard(mutex, &scanout->lock) {
+ if (scanout->fw)
+ release_firmware(scanout->fw);
+ scanout->fw = fw;
+ }
+
+ /* Wake the render thread */
+ drm_dbg(client->dev, "splash: firmware loaded, wake up drawing thread");
+ drm_splash_wake_render_thread(splash);
+}
+
+static int drm_splash_kick_fw_load(struct drm_splash *splash,
+ struct task_struct *thread)
+{
+ struct drm_client_dev *client = &splash->client;
+ int j;
+
+ for (j = 0; j < splash->n_scanout; j++) {
+ struct drm_splash_scanout *scanout = &splash->scanout[j];
+ char *fw_name = kasprintf(GFP_KERNEL,
+ "drm_splash_%ux%u_%.4s.raw",
+ scanout->width, scanout->height,
+ (const char *)&scanout->format);
+ if (!fw_name)
+ return -ENOMEM;
+
+ drm_dbg(client->dev, "splash: request firmware %s", fw_name);
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, fw_name,
+ &splash->dev, GFP_KERNEL,
+ scanout, drm_splash_fw_callback);
+ kfree(fw_name);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW */
+
+static int drm_splash_client_hotplug(struct drm_client_dev *client)
+{
+ struct drm_splash *splash = client_to_drm_splash(client);
+ int ret;
+
+ guard(mutex)(&splash->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;
+ }
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH_LOAD_AS_FW
+ if (!skip_image) {
+ ret = drm_splash_kick_fw_load(splash, splash->thread);
+ if (ret) {
+ drm_err(client->dev, "splash: failed to kick fw load: %d", ret);
+ kthread_stop(splash->thread);
+ drm_splash_free_scanout(client);
+ return ret;
+ }
+ }
+#endif
+
+ /* Wake the render thread to show initial contents */
+ drm_splash_wake_render_thread(splash);
+
+ splash->initialized = true;
+
+ return 0;
+}
+
+static void drm_splash_client_unregister(struct drm_client_dev *client)
+{
+ struct drm_splash *splash = client_to_drm_splash(client);
+ struct drm_device *dev = client->dev;
+
+ kthread_stop(splash->thread);
+ device_del(&splash->dev);
+ drm_splash_free_scanout(client);
+ drm_client_release(client);
+ put_device(&splash->dev);
+ 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,
+ .unregister = drm_splash_client_unregister,
+};
+
+static ssize_t progress_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct drm_splash *splash = dev_get_drvdata(device);
+ u8 progress;
+ int ret;
+
+ ret = kstrtou8(buf, 0, &progress);
+ if (ret)
+ return ret;
+
+ if (ret > 100)
+ return -ERANGE;
+
+ scoped_guard(mutex, &splash->lock)
+ splash->progress = progress;
+
+ drm_splash_wake_render_thread(splash);
+
+ return count;
+}
+DEVICE_ATTR_WO(progress);
+
+static ssize_t message_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct drm_splash *splash = dev_get_drvdata(device);
+ size_t len = min(count, sizeof(splash->message));
+
+ scoped_guard(mutex, &splash->lock)
+ strscpy(splash->message, buf, len);
+
+ drm_splash_wake_render_thread(splash);
+
+ return count;
+}
+DEVICE_ATTR_WO(message);
+
+static ssize_t stop_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct drm_splash *splash = dev_get_drvdata(device);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ if (val != 0)
+ kthread_stop(splash->thread);
+
+ return count;
+}
+DEVICE_ATTR_WO(stop);
+
+static struct attribute *drm_splash_attrs[] = {
+ &dev_attr_message.attr,
+ &dev_attr_progress.attr,
+ &dev_attr_stop.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(drm_splash);
+
+/**
+ * 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;
+ int ret;
+
+ splash = kzalloc(sizeof(*splash), GFP_KERNEL);
+ if (!splash)
+ goto err_warn;
+
+ mutex_init(&splash->lock);
+ if (format && format->num_planes == 1)
+ splash->preferred_format = format->format;
+
+ if (message)
+ strscpy(splash->message, message);
+
+ if (drm_client_init(dev, &splash->client, "drm_splash",
+ &drm_splash_client_funcs))
+ goto err_free;
+
+ device_initialize(&splash->dev);
+ splash->dev.parent = dev->dev;
+ splash->dev.groups = drm_splash_groups;
+ dev_set_name(&splash->dev, "drm_splash");
+ dev_set_drvdata(&splash->dev, splash);
+ ret = device_add(&splash->dev);
+ if (ret)
+ 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.51.0
^ permalink raw reply related
* [PATCH RFC 2/3] MAINTAINERS: add entry for DRM splash client
From: Francesco Valla @ 2025-10-26 23:03 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas
Cc: Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <20251027-drm_client_splash-v1-0-00698933b34a@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 3da2c26a796b82b9de4143c1591a9b1d44d42194..b365a6199dd427b0c66452cbc419d1fc0618c502 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8643,6 +8643,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.51.0
^ permalink raw reply related
* [PATCH RFC 3/3] drm: docs: remove bootsplash from TODO
From: Francesco Valla @ 2025-10-26 23:03 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas
Cc: Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <20251027-drm_client_splash-v1-0-00698933b34a@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 b5f58b4274b1d38e26b229b88a8b4f4ba3433179..b1a6d587c286f060d549a12cf8e771f753b712bc 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -717,23 +717,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.51.0
^ permalink raw reply related
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Maxime Ripard @ 2025-10-27 10:09 UTC (permalink / raw)
To: Francesco Valla
Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Jocelyn Falempe, Javier Martinez Canillas,
Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <20251027-drm_client_splash-v1-0-00698933b34a@valla.it>
[-- Attachment #1: Type: text/plain, Size: 4423 bytes --]
Hi,
On Mon, Oct 27, 2025 at 12:03:00AM +0100, Francesco Valla wrote:
> this patchset adds a new DRM client offering splash functionalities,
> able to draw to screen:
>
> - a colored background;
So, I like that part, and we were recently discussing about this.
> - a single-line text message, which can be set through sysfs or
> directly from the kernel command line;
> - a very simple progress bar, which can be driven through sysfs;
> - a static image (optional).
But there's no reason to have all that in the kernel, and we already
have userspace components to do so (plymouth being the main "mainstream"
one).
> Once compiled inside the kernel, the client can be enabled through the
> command line specifying the drm_client_lib.active=splash parameter.
>
> == Motivation ==
>
> The motivation behind this work is to offer to embedded system
> developers a new path for a simple activation of the display(s)
> connected to their system, with the following usecases:
>
> - bootsplash - possibly displaying even before init;
> - early activation of the display pipeline, in particular whenever one
> component of the pipeline (e.g.: a panel) takes a non-negligible
> time to initialize;
> - recovery systems, where the splash client can offer a simple feedback
> for unattended recovery tasks;
> - update systems, where the splash client can offer a simple feedback
> for unattended update tasks.
If plymouth cannot be used by embedded systems for some reason, then you
should work on a plymouth alternative.
> While the first seems the most obvious one, it was the second that acted
> as the driver, as in the past I had to implement a ugly workaround using
> a systemd generator to kickstart the initialization of a display and
> shave ~400ms of boot time.
>
> The last 2 usecase, instead, are the reason I dropped the "boot" part
> from bootsplash.
>
> == Implementation details ==
>
> The design is quite simple, with a kernel thread doing the heavylifting
> for the rendering part and some locking to protect interactions with it.
>
> The splash image is loaded using the firmware framework, with the client
> expecting to find a binary dump having the right dimensions (width and
> height) and FOURCC format for each modeset. Given a 1920x1080 RGB888
> modeset, the client will for example search for a firmware named:
>
> drm_splash_1920x1080_RG24.raw
>
> If the firmware cannot be loaded directly, the NOUEVENT sysfs fallback
> mechanism is used to let userspace load the appropriate image.
>
> == Testing ==
>
> Testing was done on qemu (both with vkms and bochs drivers), on a HDMI
> display connected to a Beagleplay and on a ILI9341 SPI display connected
> to a i.MX93 FRDM board. All these platforms revealed different
> weaknesses that were hopefully removed.
>
> == Open points / issues ==
>
> The reason for this being an RFC is that there are several open points:
>
> - Support for tiled connectors should be there, but has not been
> tested. Any idea on how to test it?
Did you mean tiled formats?
> - I'm not entirely convinced that using the firmware framework to load
> the images is the right path. The idea behind it was to re-use the
> compressed firmware support, but then I discovered it is not there
> for built-in firmware.
Yeah, firmware loading for this has a few issues (being tedious to setup
for when built-in being one). I think just going the fbdev penguin road
is a better choice: you provide the path, and it's embedded in the
kernel directly.
> - Again on the firmware loading: CONFIG_LOADPIN would interfere with
> sysfs loading.
> - And again: FW_ACTION_NOUEVENT only has one user inside the kernel,
> leading me to think it is de-facto deprecated. And still, uevents
> for firmware loading seem frowned upon these days...
> - Generating binary dumps for... basically any format is not so
> straightforward. I crafted a Python tool with AI help which seems
> to work quite well, but I honestly did not yet understood which is
> the policy for AI-generated code inside the kernel, so it is not
> included in this patch set. All client code is genuine, though.
BMP is simple enough to support so we should probably use that instead
of a custom format.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Thomas Zimmermann @ 2025-10-27 12:35 UTC (permalink / raw)
To: Maxime Ripard, Francesco Valla
Cc: Maarten Lankhorst, David Airlie, Simona Vetter, Jonathan Corbet,
Jocelyn Falempe, Javier Martinez Canillas, Sam Ravnborg,
linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <yq4btdc5qqukuqps7y53dratmu64ghyifgprlndnk5rbgml4of@rvca75sncvsm>
Hi Francenso, Maxime,
Am 27.10.25 um 11:09 schrieb Maxime Ripard:
> Hi,
>
> On Mon, Oct 27, 2025 at 12:03:00AM +0100, Francesco Valla wrote:
>> this patchset adds a new DRM client offering splash functionalities,
>> able to draw to screen:
>>
>> - a colored background;
> So, I like that part, and we were recently discussing about this.
The panic screen has configurable foreground/background colors. Maybe we
can harmonize these settings.
>
>> - a single-line text message, which can be set through sysfs or
>> directly from the kernel command line;
Put it into the kernel config.
>> - a very simple progress bar, which can be driven through sysfs;
Once you have options to control these settings from user space, you
should do it in user space entirely. As Maxime suggested, please improve
plymouth for anything with animation.
>> - a static image (optional).
Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
is a candidate for display, or the penguin or a custom image. Please
make it configurable by Kconfig. Again, if you need policy and
heuristics for deciding what to display, you better do this in user space.
> But there's no reason to have all that in the kernel, and we already
> have userspace components to do so (plymouth being the main "mainstream"
> one).
>
>> Once compiled inside the kernel, the client can be enabled through the
>> command line specifying the drm_client_lib.active=splash parameter.
>>
>> == Motivation ==
>>
>> The motivation behind this work is to offer to embedded system
>> developers a new path for a simple activation of the display(s)
>> connected to their system, with the following usecases:
>>
>> - bootsplash - possibly displaying even before init;
>> - early activation of the display pipeline, in particular whenever one
>> component of the pipeline (e.g.: a panel) takes a non-negligible
>> time to initialize;
>> - recovery systems, where the splash client can offer a simple feedback
>> for unattended recovery tasks;
>> - update systems, where the splash client can offer a simple feedback
>> for unattended update tasks.
> If plymouth cannot be used by embedded systems for some reason, then you
> should work on a plymouth alternative.
Agreed. With an updater running in user space, that process should also
manage the display update. No need for this in the kernel.
>
>> While the first seems the most obvious one, it was the second that acted
>> as the driver, as in the past I had to implement a ugly workaround using
>> a systemd generator to kickstart the initialization of a display and
>> shave ~400ms of boot time.
>>
>> The last 2 usecase, instead, are the reason I dropped the "boot" part
>> from bootsplash.
>>
>> == Implementation details ==
>>
>> The design is quite simple, with a kernel thread doing the heavylifting
>> for the rendering part and some locking to protect interactions with it.
>>
>> The splash image is loaded using the firmware framework, with the client
>> expecting to find a binary dump having the right dimensions (width and
>> height) and FOURCC format for each modeset. Given a 1920x1080 RGB888
>> modeset, the client will for example search for a firmware named:
>>
>> drm_splash_1920x1080_RG24.raw
>>
>> If the firmware cannot be loaded directly, the NOUEVENT sysfs fallback
>> mechanism is used to let userspace load the appropriate image.
>>
>> == Testing ==
>>
>> Testing was done on qemu (both with vkms and bochs drivers), on a HDMI
>> display connected to a Beagleplay and on a ILI9341 SPI display connected
>> to a i.MX93 FRDM board. All these platforms revealed different
>> weaknesses that were hopefully removed.
>>
>> == Open points / issues ==
>>
>> The reason for this being an RFC is that there are several open points:
>>
>> - Support for tiled connectors should be there, but has not been
>> tested. Any idea on how to test it?
> Did you mean tiled formats?
>
>> - I'm not entirely convinced that using the firmware framework to load
>> the images is the right path. The idea behind it was to re-use the
>> compressed firmware support, but then I discovered it is not there
>> for built-in firmware.
> Yeah, firmware loading for this has a few issues (being tedious to setup
> for when built-in being one). I think just going the fbdev penguin road
> is a better choice: you provide the path, and it's embedded in the
> kernel directly.
>
>> - Again on the firmware loading: CONFIG_LOADPIN would interfere with
>> sysfs loading.
>> - And again: FW_ACTION_NOUEVENT only has one user inside the kernel,
>> leading me to think it is de-facto deprecated. And still, uevents
>> for firmware loading seem frowned upon these days...
>> - Generating binary dumps for... basically any format is not so
>> straightforward. I crafted a Python tool with AI help which seems
>> to work quite well, but I honestly did not yet understood which is
>> the policy for AI-generated code inside the kernel, so it is not
>> included in this patch set. All client code is genuine, though.
> BMP is simple enough to support so we should probably use that instead
> of a custom format.
file /sys/firmware/acpi/bgrt/image
/sys/firmware/acpi/bgrt/image: PC bitmap, Windows 3.x format, 768 x 256
x 24, image size 589824, cbSize 589878, bits offset 54
That should probably be the format for now unless your firmware uses
something else natively. Code for reading a BMP file can be found in the
efifb driver. [1]
[1]
https://elixir.bootlin.com/linux/v6.17.5/source/drivers/video/fbdev/efifb.c#L24
Apart from the criticism for complexity, I do like the idea of having a
splash screen.
Best regards
Thomas
>
> Maxime
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Mario Limonciello @ 2025-10-27 16:01 UTC (permalink / raw)
To: Thomas Zimmermann, Maxime Ripard, Francesco Valla
Cc: Maarten Lankhorst, David Airlie, Simona Vetter, Jonathan Corbet,
Jocelyn Falempe, Javier Martinez Canillas, Sam Ravnborg,
linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <3edea192-6a3f-44f5-b570-7033776e2ce4@suse.de>
On 10/27/25 7:35 AM, Thomas Zimmermann wrote:
> Hi Francenso, Maxime,
>
> Am 27.10.25 um 11:09 schrieb Maxime Ripard:
>> Hi,
>>
>> On Mon, Oct 27, 2025 at 12:03:00AM +0100, Francesco Valla wrote:
>>> this patchset adds a new DRM client offering splash functionalities,
>>> able to draw to screen:
>>>
>>> - a colored background;
>> So, I like that part, and we were recently discussing about this.
>
> The panic screen has configurable foreground/background colors. Maybe we
> can harmonize these settings.
>
>>
>>> - a single-line text message, which can be set through sysfs or
>>> directly from the kernel command line;
>
> Put it into the kernel config.
>
>>> - a very simple progress bar, which can be driven through sysfs;
>
> Once you have options to control these settings from user space, you
> should do it in user space entirely. As Maxime suggested, please improve
> plymouth for anything with animation.
>
>>> - a static image (optional).
>
> Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
> is a candidate for display, or the penguin or a custom image. Please
> make it configurable by Kconfig. Again, if you need policy and
> heuristics for deciding what to display, you better do this in user space.
I'd actually argue that the static image from BGRT should be the
preferred priority. This can make for a nice hand off to Plymouth.
The (UEFI) BIOS already will show this image as soon as the GOP driver
is loaded. Bootloaders like GRUB by default will avoid showing anything
or will overwrite with the exact same image in the same location. This
can let the kernel do the same, and then the moment Plymouth takes over
it could do the same.
>
>> But there's no reason to have all that in the kernel, and we already
>> have userspace components to do so (plymouth being the main "mainstream"
>> one).
>>
>>> Once compiled inside the kernel, the client can be enabled through the
>>> command line specifying the drm_client_lib.active=splash parameter.
>>>
>>> == Motivation ==
>>>
>>> The motivation behind this work is to offer to embedded system
>>> developers a new path for a simple activation of the display(s)
>>> connected to their system, with the following usecases:
>>>
>>> - bootsplash - possibly displaying even before init;
>>> - early activation of the display pipeline, in particular whenever
>>> one
>>> component of the pipeline (e.g.: a panel) takes a non-negligible
>>> time to initialize;
>>> - recovery systems, where the splash client can offer a simple
>>> feedback
>>> for unattended recovery tasks;
>>> - update systems, where the splash client can offer a simple feedback
>>> for unattended update tasks.
>> If plymouth cannot be used by embedded systems for some reason, then you
>> should work on a plymouth alternative.
>
> Agreed. With an updater running in user space, that process should also
> manage the display update. No need for this in the kernel.
>
>>
>>> While the first seems the most obvious one, it was the second that acted
>>> as the driver, as in the past I had to implement a ugly workaround using
>>> a systemd generator to kickstart the initialization of a display and
>>> shave ~400ms of boot time.
>>>
>>> The last 2 usecase, instead, are the reason I dropped the "boot" part
>>> from bootsplash.
>>>
>>> == Implementation details ==
>>>
>>> The design is quite simple, with a kernel thread doing the heavylifting
>>> for the rendering part and some locking to protect interactions with it.
>>>
>>> The splash image is loaded using the firmware framework, with the client
>>> expecting to find a binary dump having the right dimensions (width and
>>> height) and FOURCC format for each modeset. Given a 1920x1080 RGB888
>>> modeset, the client will for example search for a firmware named:
>>>
>>> drm_splash_1920x1080_RG24.raw
>>>
>>> If the firmware cannot be loaded directly, the NOUEVENT sysfs fallback
>>> mechanism is used to let userspace load the appropriate image.
>>>
>>> == Testing ==
>>>
>>> Testing was done on qemu (both with vkms and bochs drivers), on a HDMI
>>> display connected to a Beagleplay and on a ILI9341 SPI display connected
>>> to a i.MX93 FRDM board. All these platforms revealed different
>>> weaknesses that were hopefully removed.
>>>
>>> == Open points / issues ==
>>>
>>> The reason for this being an RFC is that there are several open points:
>>>
>>> - Support for tiled connectors should be there, but has not been
>>> tested. Any idea on how to test it?
>> Did you mean tiled formats?
>>
>>> - I'm not entirely convinced that using the firmware framework to
>>> load
>>> the images is the right path. The idea behind it was to re-use the
>>> compressed firmware support, but then I discovered it is not there
>>> for built-in firmware.
>> Yeah, firmware loading for this has a few issues (being tedious to setup
>> for when built-in being one). I think just going the fbdev penguin road
>> is a better choice: you provide the path, and it's embedded in the
>> kernel directly.
>>
>>> - Again on the firmware loading: CONFIG_LOADPIN would interfere with
>>> sysfs loading.
>>> - And again: FW_ACTION_NOUEVENT only has one user inside the kernel,
>>> leading me to think it is de-facto deprecated. And still, uevents
>>> for firmware loading seem frowned upon these days...
>>> - Generating binary dumps for... basically any format is not so
>>> straightforward. I crafted a Python tool with AI help which seems
>>> to work quite well, but I honestly did not yet understood which is
>>> the policy for AI-generated code inside the kernel, so it is not
>>> included in this patch set. All client code is genuine, though.
>> BMP is simple enough to support so we should probably use that instead
>> of a custom format.
>
> file /sys/firmware/acpi/bgrt/image
> /sys/firmware/acpi/bgrt/image: PC bitmap, Windows 3.x format, 768 x 256
> x 24, image size 589824, cbSize 589878, bits offset 54
>
> That should probably be the format for now unless your firmware uses
> something else natively. Code for reading a BMP file can be found in the
> efifb driver. [1]
>
> [1] https://elixir.bootlin.com/linux/v6.17.5/source/drivers/video/fbdev/
> efifb.c#L24
>
> Apart from the criticism for complexity, I do like the idea of having a
> splash screen.
>
> Best regards
> Thomas
>
>>
>> Maxime
>
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Maxime Ripard @ 2025-10-27 16:28 UTC (permalink / raw)
To: Mario Limonciello
Cc: Thomas Zimmermann, Francesco Valla, Maarten Lankhorst,
David Airlie, Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas, Sam Ravnborg, linux-kernel, dri-devel,
linux-doc, linux-embedded
In-Reply-To: <5ff10f7d-e9d4-4d4d-ae82-8986dc28d14b@amd.com>
[-- Attachment #1: Type: text/plain, Size: 1391 bytes --]
On Mon, Oct 27, 2025 at 11:01:55AM -0500, Mario Limonciello wrote:
> On 10/27/25 7:35 AM, Thomas Zimmermann wrote:
> > > > - a very simple progress bar, which can be driven through sysfs;
> >
> > Once you have options to control these settings from user space, you
> > should do it in user space entirely. As Maxime suggested, please improve
> > plymouth for anything with animation.
> >
> > > > - a static image (optional).
> >
> > Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
> > is a candidate for display, or the penguin or a custom image. Please
> > make it configurable by Kconfig. Again, if you need policy and
> > heuristics for deciding what to display, you better do this in user
> > space.
>
> I'd actually argue that the static image from BGRT should be the preferred
> priority. This can make for a nice hand off to Plymouth.
>
> The (UEFI) BIOS already will show this image as soon as the GOP driver is
> loaded. Bootloaders like GRUB by default will avoid showing anything or
> will overwrite with the exact same image in the same location. This can let
> the kernel do the same, and then the moment Plymouth takes over it could do
> the same.
And BGRT isn't typically found on embedded systems at all, so I'm not
sure it's a sensible default, let alone a priority. At most a possible
fallback.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Mario Limonciello @ 2025-10-27 16:31 UTC (permalink / raw)
To: Maxime Ripard
Cc: Thomas Zimmermann, Francesco Valla, Maarten Lankhorst,
David Airlie, Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas, Sam Ravnborg, linux-kernel, dri-devel,
linux-doc, linux-embedded
In-Reply-To: <i7xxy33do4q4odvxxb77xv4ri5jgr6dup5kvfsjfs4h7mbmhrj@h3ke7h5whyvx>
On 10/27/25 11:28 AM, Maxime Ripard wrote:
> On Mon, Oct 27, 2025 at 11:01:55AM -0500, Mario Limonciello wrote:
>> On 10/27/25 7:35 AM, Thomas Zimmermann wrote:
>>>>> - a very simple progress bar, which can be driven through sysfs;
>>>
>>> Once you have options to control these settings from user space, you
>>> should do it in user space entirely. As Maxime suggested, please improve
>>> plymouth for anything with animation.
>>>
>>>>> - a static image (optional).
>>>
>>> Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
>>> is a candidate for display, or the penguin or a custom image. Please
>>> make it configurable by Kconfig. Again, if you need policy and
>>> heuristics for deciding what to display, you better do this in user
>>> space.
>>
>> I'd actually argue that the static image from BGRT should be the preferred
>> priority. This can make for a nice hand off to Plymouth.
>>
>> The (UEFI) BIOS already will show this image as soon as the GOP driver is
>> loaded. Bootloaders like GRUB by default will avoid showing anything or
>> will overwrite with the exact same image in the same location. This can let
>> the kernel do the same, and then the moment Plymouth takes over it could do
>> the same.
>
> And BGRT isn't typically found on embedded systems at all, so I'm not
> sure it's a sensible default, let alone a priority. At most a possible
There are certainly embedded machines using UEFI and that have a BGRT.
How about "Sensible default the top of the priority list if it exists"
Just like Plymouth will start out with graphical splash and fallback to
text if problems.
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Maxime Ripard @ 2025-10-27 17:19 UTC (permalink / raw)
To: Mario Limonciello
Cc: Thomas Zimmermann, Francesco Valla, Maarten Lankhorst,
David Airlie, Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
Javier Martinez Canillas, Sam Ravnborg, linux-kernel, dri-devel,
linux-doc, linux-embedded
In-Reply-To: <93cbbaef-918f-4300-aa5b-11f098e217b2@amd.com>
[-- Attachment #1: Type: text/plain, Size: 1868 bytes --]
On Mon, Oct 27, 2025 at 11:31:06AM -0500, Mario Limonciello wrote:
> On 10/27/25 11:28 AM, Maxime Ripard wrote:
> > On Mon, Oct 27, 2025 at 11:01:55AM -0500, Mario Limonciello wrote:
> > > On 10/27/25 7:35 AM, Thomas Zimmermann wrote:
> > > > > > - a very simple progress bar, which can be driven through sysfs;
> > > >
> > > > Once you have options to control these settings from user space, you
> > > > should do it in user space entirely. As Maxime suggested, please improve
> > > > plymouth for anything with animation.
> > > >
> > > > > > - a static image (optional).
> > > >
> > > > Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
> > > > is a candidate for display, or the penguin or a custom image. Please
> > > > make it configurable by Kconfig. Again, if you need policy and
> > > > heuristics for deciding what to display, you better do this in user
> > > > space.
> > >
> > > I'd actually argue that the static image from BGRT should be the preferred
> > > priority. This can make for a nice hand off to Plymouth.
> > >
> > > The (UEFI) BIOS already will show this image as soon as the GOP driver is
> > > loaded. Bootloaders like GRUB by default will avoid showing anything or
> > > will overwrite with the exact same image in the same location. This can let
> > > the kernel do the same, and then the moment Plymouth takes over it could do
> > > the same.
> >
> > And BGRT isn't typically found on embedded systems at all, so I'm not
> > sure it's a sensible default, let alone a priority. At most a possible
>
> There are certainly embedded machines using UEFI and that have a BGRT.
Yes, indeed, hence the "typically".
> How about "Sensible default the top of the priority list if it exists"
How about we don't tell contributors what their priorities must be?
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Francesco Valla @ 2025-10-28 8:03 UTC (permalink / raw)
To: Maxime Ripard, Thomas Zimmermann
Cc: Maarten Lankhorst, David Airlie, Simona Vetter, Jonathan Corbet,
Jocelyn Falempe, Javier Martinez Canillas, Sam Ravnborg,
linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <3edea192-6a3f-44f5-b570-7033776e2ce4@suse.de>
[-- Attachment #1: Type: text/plain, Size: 6618 bytes --]
Hi Thomas,
On Monday, 27 October 2025 at 13:35:31 Thomas Zimmermann <tzimmermann@suse.de> wrote:
> Hi Francenso, Maxime,
>
> Am 27.10.25 um 11:09 schrieb Maxime Ripard:
> > Hi,
> >
> > On Mon, Oct 27, 2025 at 12:03:00AM +0100, Francesco Valla wrote:
> >> this patchset adds a new DRM client offering splash functionalities,
> >> able to draw to screen:
> >>
> >> - a colored background;
> > So, I like that part, and we were recently discussing about this.
>
> The panic screen has configurable foreground/background colors. Maybe we
> can harmonize these settings.
>
Maybe, but probably the panic colors would typically be much more vibrant
than splash ones.
> >
> >> - a single-line text message, which can be set through sysfs or
> >> directly from the kernel command line;
>
> Put it into the kernel config.
>
> >> - a very simple progress bar, which can be driven through sysfs;
>
> Once you have options to control these settings from user space, you
> should do it in user space entirely. As Maxime suggested, please improve
> plymouth for anything with animation.
>
On this I can agree, see my reply to Maxime.
> >> - a static image (optional).
>
> Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
> is a candidate for display, or the penguin or a custom image. Please
> make it configurable by Kconfig. Again, if you need policy and
> heuristics for deciding what to display, you better do this in user space.
>
I'm not under ACPI/UEFI typically, and the concept for this patch was not
developed on such system. But I'll take a look!
> > But there's no reason to have all that in the kernel, and we already
> > have userspace components to do so (plymouth being the main "mainstream"
> > one).
> >
> >> Once compiled inside the kernel, the client can be enabled through the
> >> command line specifying the drm_client_lib.active=splash parameter.
> >>
> >> == Motivation ==
> >>
> >> The motivation behind this work is to offer to embedded system
> >> developers a new path for a simple activation of the display(s)
> >> connected to their system, with the following usecases:
> >>
> >> - bootsplash - possibly displaying even before init;
> >> - early activation of the display pipeline, in particular whenever one
> >> component of the pipeline (e.g.: a panel) takes a non-negligible
> >> time to initialize;
> >> - recovery systems, where the splash client can offer a simple feedback
> >> for unattended recovery tasks;
> >> - update systems, where the splash client can offer a simple feedback
> >> for unattended update tasks.
> > If plymouth cannot be used by embedded systems for some reason, then you
> > should work on a plymouth alternative.
>
> Agreed. With an updater running in user space, that process should also
> manage the display update. No need for this in the kernel.
>
> >
> >> While the first seems the most obvious one, it was the second that acted
> >> as the driver, as in the past I had to implement a ugly workaround using
> >> a systemd generator to kickstart the initialization of a display and
> >> shave ~400ms of boot time.
> >>
> >> The last 2 usecase, instead, are the reason I dropped the "boot" part
> >> from bootsplash.
> >>
> >> == Implementation details ==
> >>
> >> The design is quite simple, with a kernel thread doing the heavylifting
> >> for the rendering part and some locking to protect interactions with it.
> >>
> >> The splash image is loaded using the firmware framework, with the client
> >> expecting to find a binary dump having the right dimensions (width and
> >> height) and FOURCC format for each modeset. Given a 1920x1080 RGB888
> >> modeset, the client will for example search for a firmware named:
> >>
> >> drm_splash_1920x1080_RG24.raw
> >>
> >> If the firmware cannot be loaded directly, the NOUEVENT sysfs fallback
> >> mechanism is used to let userspace load the appropriate image.
> >>
> >> == Testing ==
> >>
> >> Testing was done on qemu (both with vkms and bochs drivers), on a HDMI
> >> display connected to a Beagleplay and on a ILI9341 SPI display connected
> >> to a i.MX93 FRDM board. All these platforms revealed different
> >> weaknesses that were hopefully removed.
> >>
> >> == Open points / issues ==
> >>
> >> The reason for this being an RFC is that there are several open points:
> >>
> >> - Support for tiled connectors should be there, but has not been
> >> tested. Any idea on how to test it?
> > Did you mean tiled formats?
> >
> >> - I'm not entirely convinced that using the firmware framework to load
> >> the images is the right path. The idea behind it was to re-use the
> >> compressed firmware support, but then I discovered it is not there
> >> for built-in firmware.
> > Yeah, firmware loading for this has a few issues (being tedious to setup
> > for when built-in being one). I think just going the fbdev penguin road
> > is a better choice: you provide the path, and it's embedded in the
> > kernel directly.
> >
> >> - Again on the firmware loading: CONFIG_LOADPIN would interfere with
> >> sysfs loading.
> >> - And again: FW_ACTION_NOUEVENT only has one user inside the kernel,
> >> leading me to think it is de-facto deprecated. And still, uevents
> >> for firmware loading seem frowned upon these days...
> >> - Generating binary dumps for... basically any format is not so
> >> straightforward. I crafted a Python tool with AI help which seems
> >> to work quite well, but I honestly did not yet understood which is
> >> the policy for AI-generated code inside the kernel, so it is not
> >> included in this patch set. All client code is genuine, though.
> > BMP is simple enough to support so we should probably use that instead
> > of a custom format.
>
> file /sys/firmware/acpi/bgrt/image
> /sys/firmware/acpi/bgrt/image: PC bitmap, Windows 3.x format, 768 x 256
> x 24, image size 589824, cbSize 589878, bits offset 54
>
> That should probably be the format for now unless your firmware uses
> something else natively. Code for reading a BMP file can be found in the
> efifb driver. [1]
>
> [1]
> https://elixir.bootlin.com/linux/v6.17.5/source/drivers/video/fbdev/efifb.c#L24
>
When I started working on the patch I was not able to find this BMP decoder,
I only found the PPM one from the bootup logo. I'll take a look here too.
> Apart from the criticism for complexity, I do like the idea of having a
> splash screen.
>
> Best regards
> Thomas
>
> >
> > Maxime
>
>
Thank you!
Best regards,
Francesco
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Francesco Valla @ 2025-10-28 8:09 UTC (permalink / raw)
To: Mario Limonciello, Maxime Ripard
Cc: Thomas Zimmermann, Maarten Lankhorst, David Airlie, Simona Vetter,
Jonathan Corbet, Jocelyn Falempe, Javier Martinez Canillas,
Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <23clcxtgbzbsji2knwp47xdc5udj7lnhbvzsgqi3vklvmhdgjd@26ycx2ed77l4>
[-- Attachment #1: Type: text/plain, Size: 2254 bytes --]
Hi,
On Monday, 27 October 2025 at 18:19:12 Maxime Ripard <mripard@kernel.org> wrote:
> On Mon, Oct 27, 2025 at 11:31:06AM -0500, Mario Limonciello wrote:
> > On 10/27/25 11:28 AM, Maxime Ripard wrote:
> > > On Mon, Oct 27, 2025 at 11:01:55AM -0500, Mario Limonciello wrote:
> > > > On 10/27/25 7:35 AM, Thomas Zimmermann wrote:
> > > > > > > - a very simple progress bar, which can be driven through sysfs;
> > > > >
> > > > > Once you have options to control these settings from user space, you
> > > > > should do it in user space entirely. As Maxime suggested, please improve
> > > > > plymouth for anything with animation.
> > > > >
> > > > > > > - a static image (optional).
> > > > >
> > > > > Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
> > > > > is a candidate for display, or the penguin or a custom image. Please
> > > > > make it configurable by Kconfig. Again, if you need policy and
> > > > > heuristics for deciding what to display, you better do this in user
> > > > > space.
> > > >
> > > > I'd actually argue that the static image from BGRT should be the preferred
> > > > priority. This can make for a nice hand off to Plymouth.
> > > >
> > > > The (UEFI) BIOS already will show this image as soon as the GOP driver is
> > > > loaded. Bootloaders like GRUB by default will avoid showing anything or
> > > > will overwrite with the exact same image in the same location. This can let
> > > > the kernel do the same, and then the moment Plymouth takes over it could do
> > > > the same.
> > >
> > > And BGRT isn't typically found on embedded systems at all, so I'm not
> > > sure it's a sensible default, let alone a priority. At most a possible
> >
> > There are certainly embedded machines using UEFI and that have a BGRT.
>
> Yes, indeed, hence the "typically".
>
> > How about "Sensible default the top of the priority list if it exists"
>
> How about we don't tell contributors what their priorities must be?
>
> Maxime
>
I'm not familiar at all with BGRT, I'll study a bit about it.
A build-time configuration could then let the user select:
- a plain solid color
- a custom static image
- the penguin logo (?)
- (on UEFI systems) BGRT source
Thank you
Regards,
Francesco
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Francesco Valla @ 2025-10-28 7:58 UTC (permalink / raw)
To: Maxime Ripard
Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Jocelyn Falempe, Javier Martinez Canillas,
Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <yq4btdc5qqukuqps7y53dratmu64ghyifgprlndnk5rbgml4of@rvca75sncvsm>
[-- Attachment #1: Type: text/plain, Size: 5706 bytes --]
Hi,
On Monday, 27 October 2025 at 11:09:56 Maxime Ripard <mripard@kernel.org> wrote:
> Hi,
>
> On Mon, Oct 27, 2025 at 12:03:00AM +0100, Francesco Valla wrote:
> > this patchset adds a new DRM client offering splash functionalities,
> > able to draw to screen:
> >
> > - a colored background;
>
> So, I like that part, and we were recently discussing about this.
>
> > - a single-line text message, which can be set through sysfs or
> > directly from the kernel command line;
> > - a very simple progress bar, which can be driven through sysfs;
> > - a static image (optional).
>
> But there's no reason to have all that in the kernel, and we already
> have userspace components to do so (plymouth being the main "mainstream"
> one).
>
I get that for the "text message" and "progress bar" parts. I still have
some uses for them, that however may not be adherent to upstream philosophy.
> > Once compiled inside the kernel, the client can be enabled through the
> > command line specifying the drm_client_lib.active=splash parameter.
> >
> > == Motivation ==
> >
> > The motivation behind this work is to offer to embedded system
> > developers a new path for a simple activation of the display(s)
> > connected to their system, with the following usecases:
> >
> > - bootsplash - possibly displaying even before init;
> > - early activation of the display pipeline, in particular whenever one
> > component of the pipeline (e.g.: a panel) takes a non-negligible
> > time to initialize;
> > - recovery systems, where the splash client can offer a simple feedback
> > for unattended recovery tasks;
> > - update systems, where the splash client can offer a simple feedback
> > for unattended update tasks.
>
> If plymouth cannot be used by embedded systems for some reason, then you
> should work on a plymouth alternative.
>
Thing is: any possible alternative would still start after userspace has
been loaded, checked (in case of secure boot, which is ubiquitous now)
and initialized. This means, at least in my usecases, several hundreds of
milliseconds after userspace start, to be summed to the panel initialization
time.
> > While the first seems the most obvious one, it was the second that acted
> > as the driver, as in the past I had to implement a ugly workaround using
> > a systemd generator to kickstart the initialization of a display and
> > shave ~400ms of boot time.
> >
> > The last 2 usecase, instead, are the reason I dropped the "boot" part
> > from bootsplash.
> >
> > == Implementation details ==
> >
> > The design is quite simple, with a kernel thread doing the heavylifting
> > for the rendering part and some locking to protect interactions with it.
> >
> > The splash image is loaded using the firmware framework, with the client
> > expecting to find a binary dump having the right dimensions (width and
> > height) and FOURCC format for each modeset. Given a 1920x1080 RGB888
> > modeset, the client will for example search for a firmware named:
> >
> > drm_splash_1920x1080_RG24.raw
> >
> > If the firmware cannot be loaded directly, the NOUEVENT sysfs fallback
> > mechanism is used to let userspace load the appropriate image.
> >
> > == Testing ==
> >
> > Testing was done on qemu (both with vkms and bochs drivers), on a HDMI
> > display connected to a Beagleplay and on a ILI9341 SPI display connected
> > to a i.MX93 FRDM board. All these platforms revealed different
> > weaknesses that were hopefully removed.
> >
> > == Open points / issues ==
> >
> > The reason for this being an RFC is that there are several open points:
> >
> > - Support for tiled connectors should be there, but has not been
> > tested. Any idea on how to test it?
>
> Did you mean tiled formats?
>
No, AFAIU the tiled connectors are different connectors that feed different
panels, which however are part of a single logical screen. Support for this
setup is present at drm level [1], but I'm not familiar with it.
I've only found this used inside the i915 Intel driver [2].
> > - I'm not entirely convinced that using the firmware framework to load
> > the images is the right path. The idea behind it was to re-use the
> > compressed firmware support, but then I discovered it is not there
> > for built-in firmware.
>
> Yeah, firmware loading for this has a few issues (being tedious to setup
> for when built-in being one). I think just going the fbdev penguin road
> is a better choice: you provide the path, and it's embedded in the
> kernel directly.
>
Yes, this is already working, but if large-ish images are used, the loading
time for them defeats the entire purpose of an in-kernel splash.
> > - Again on the firmware loading: CONFIG_LOADPIN would interfere with
> > sysfs loading.
> > - And again: FW_ACTION_NOUEVENT only has one user inside the kernel,
> > leading me to think it is de-facto deprecated. And still, uevents
> > for firmware loading seem frowned upon these days...
> > - Generating binary dumps for... basically any format is not so
> > straightforward. I crafted a Python tool with AI help which seems
> > to work quite well, but I honestly did not yet understood which is
> > the policy for AI-generated code inside the kernel, so it is not
> > included in this patch set. All client code is genuine, though.
>
> BMP is simple enough to support so we should probably use that instead
> of a custom format.
>
> Maxime
>
Thank you!
Regards,
Francesco
[1] https://elixir.bootlin.com/linux/v6.17.5/source/include/drm/drm_connector.h#L2253
[2] https://elixir.bootlin.com/linux/v6.17.5/source/drivers/gpu/drm/drm_connector.c#L2739
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 0/3] Add splash DRM client
From: Mario Limonciello @ 2025-10-28 13:39 UTC (permalink / raw)
To: Francesco Valla, Maxime Ripard
Cc: Thomas Zimmermann, Maarten Lankhorst, David Airlie, Simona Vetter,
Jonathan Corbet, Jocelyn Falempe, Javier Martinez Canillas,
Sam Ravnborg, linux-kernel, dri-devel, linux-doc, linux-embedded
In-Reply-To: <4497733.UPlyArG6xL@fedora.fritz.box>
On 10/28/25 3:09 AM, Francesco Valla wrote:
> Hi,
>
> On Monday, 27 October 2025 at 18:19:12 Maxime Ripard <mripard@kernel.org> wrote:
>> On Mon, Oct 27, 2025 at 11:31:06AM -0500, Mario Limonciello wrote:
>>> On 10/27/25 11:28 AM, Maxime Ripard wrote:
>>>> On Mon, Oct 27, 2025 at 11:01:55AM -0500, Mario Limonciello wrote:
>>>>> On 10/27/25 7:35 AM, Thomas Zimmermann wrote:
>>>>>>>> - a very simple progress bar, which can be driven through sysfs;
>>>>>>
>>>>>> Once you have options to control these settings from user space, you
>>>>>> should do it in user space entirely. As Maxime suggested, please improve
>>>>>> plymouth for anything with animation.
>>>>>>
>>>>>>>> - a static image (optional).
>>>>>>
>>>>>> Board vendors often provide an image, see /sys/firmware/acpi/bgrt/. This
>>>>>> is a candidate for display, or the penguin or a custom image. Please
>>>>>> make it configurable by Kconfig. Again, if you need policy and
>>>>>> heuristics for deciding what to display, you better do this in user
>>>>>> space.
>>>>>
>>>>> I'd actually argue that the static image from BGRT should be the preferred
>>>>> priority. This can make for a nice hand off to Plymouth.
>>>>>
>>>>> The (UEFI) BIOS already will show this image as soon as the GOP driver is
>>>>> loaded. Bootloaders like GRUB by default will avoid showing anything or
>>>>> will overwrite with the exact same image in the same location. This can let
>>>>> the kernel do the same, and then the moment Plymouth takes over it could do
>>>>> the same.
>>>>
>>>> And BGRT isn't typically found on embedded systems at all, so I'm not
>>>> sure it's a sensible default, let alone a priority. At most a possible
>>>
>>> There are certainly embedded machines using UEFI and that have a BGRT.
>>
>> Yes, indeed, hence the "typically".
>>
>>> How about "Sensible default the top of the priority list if it exists"
>>
>> How about we don't tell contributors what their priorities must be?
>>
>> Maxime
>>
>
> I'm not familiar at all with BGRT, I'll study a bit about it.
>
> A build-time configuration could then let the user select:
>
> - a plain solid color
> - a custom static image
> - the penguin logo (?)
> - (on UEFI systems) BGRT source
Thanks!
What I had in mind (which feel free to disagree - it's an RC afterall):
User can pick one of these:
CONFIG_SPLASH_IMAGE
CONFIG_SPLASH_COLOR
If user picks CONFIG_SPLASH_IMAGE then the default behavior would be
UEFI BGRT with a fallback to a Linux penguin.
Additionally there could be a
CONFIG_SPLASH_CUSTOM_IMAGE
This would populate the path to a custom image at build time.
If user picks this then it would override both UEFI BGRT and Linux penguin.
>
>
>
> Thank you
>
> Regards,
> Francesco
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox