Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend

Marc Kelly <[email protected]> Fri, 26 Sep 2025 18:34:06 +0100
Newsgroups org.kernel.vger.linux-embedded
Message-ID <CAHkF4vQz5=1T4D=cF0zEdrHfgeTq7sfGxbUH_dohvYtG7ANGjw@mail.gmail.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 <[email protected]> wrote:
>
> Hi Andrea
>
> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
> > From: Marc Kelly <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> >   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.