Re: [PATCH v8 3/6] kho: persist blob size in KHO FDT

Mike Rapoport
2026-03-10T10:36:07Z

On Mon, Mar 09, 2026 at 06:41:46AM -0700, Breno Leitao wrote:
> kho_add_subtree() accepts a size parameter but only forwards it to
> debugfs. The size is not persisted in the KHO FDT, so it is lost across
> kexec. This makes it impossible for the incoming kernel to determine the
> blob size without understanding the blob format.
>
> Store the blob size as a "blob-size" property in the KHO FDT alongside
> the "preserved-data" physical address. This allows the receiving kernel
> to recover the size for any blob regardless of format.
>
> Also extend kho_retrieve_subtree() with an optional size output
> parameter so callers can learn the blob size without needing to
> understand the blob format. Update all callers to pass NULL for the
> new parameter.
>
> Signed-off-by: Breno Leitao <leitao@debian.org>

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

> ---
> include/linux/kexec_handover.h | 5 +++--
> include/linux/kho/abi/kexec_handover.h | 20 ++++++++++++++++----
> kernel/liveupdate/kexec_handover.c | 26 +++++++++++++++++++++-----
> kernel/liveupdate/luo_core.c | 2 +-
> lib/test_kho.c | 2 +-
> mm/memblock.c | 2 +-
> 6 files changed, 43 insertions(+), 14 deletions(-)
>
> diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
> index 0666cf298c7f4..8968c56d2d73e 100644
> --- a/include/linux/kexec_handover.h
> +++ b/include/linux/kexec_handover.h
> @@ -34,7 +34,7 @@ struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages);
> void *kho_restore_vmalloc(const struct kho_vmalloc *preservation);
> int kho_add_subtree(const char *name, void *blob, size_t size);
> void kho_remove_subtree(void *blob);
> -int kho_retrieve_subtree(const char *name, phys_addr_t *phys);
> +int kho_retrieve_subtree(const char *name, phys_addr_t *phys, size_t *size);
>
> void kho_memory_init(void);
>
> @@ -104,7 +104,8 @@ static inline int kho_add_subtree(const char *name, void *blob, size_t size)
>
> static inline void kho_remove_subtree(void *blob) { }
>
> -static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys)
> +static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys,
> + size_t *size)
> {
> return -EOPNOTSUPP;
> }
> diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h
> index 6b7d8ef550f98..7e847a2339b09 100644
> --- a/include/linux/kho/abi/kexec_handover.h
> +++ b/include/linux/kho/abi/kexec_handover.h
> @@ -41,25 +41,28 @@
> * restore the preserved data.::
> *
> * / {
> - * compatible = "kho-v2";
> + * compatible = "kho-v3";
> *
> * preserved-memory-map = <0x...>;
> *
> * <subnode-name-1> {
> * preserved-data = <0x...>;
> + * blob-size = <0x...>;
> * };
> *
> * <subnode-name-2> {
> * preserved-data = <0x...>;
> + * blob-size = <0x...>;
> * };
> * ... ...
> * <subnode-name-N> {
> * preserved-data = <0x...>;
> + * blob-size = <0x...>;
> * };
> * };
> *
> * Root KHO Node (/):
> - * - compatible: "kho-v2"
> + * - compatible: "kho-v3"
> *
> * Indentifies the overall KHO ABI version.
> *
> @@ -78,16 +81,25 @@
> *
> * Physical address pointing to a subnode data blob that is also
> * being preserved.
> + *
> + * - blob-size: u64
> + *
> + * Size in bytes of the preserved data blob. This is needed because
> + * blobs may use arbitrary formats (not just FDT), so the size
> + * cannot be determined from the blob content alone.
> */
>
> /* The compatible string for the KHO FDT root node. */
> -#define KHO_FDT_COMPATIBLE "kho-v2"
> +#define KHO_FDT_COMPATIBLE "kho-v3"
>
> /* The FDT property for the preserved memory map. */
> #define KHO_FDT_MEMORY_MAP_PROP_NAME "preserved-memory-map"
>
> /* The FDT property for preserved data blobs. */
> -#define KHO_FDT_SUB_TREE_PROP_NAME "preserved-data"
> +#define KHO_SUB_TREE_PROP_NAME "preserved-data"
> +
> +/* The FDT property for the size of preserved data blobs. */
> +#define KHO_SUB_TREE_SIZE_PROP_NAME "blob-size"
>
> /**
> * DOC: Kexec Handover ABI for vmalloc Preservation
> diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
> index 54fe59fe43acd..1f22705d5d246 100644
> --- a/kernel/liveupdate/kexec_handover.c
> +++ b/kernel/liveupdate/kexec_handover.c
> @@ -768,6 +768,7 @@ int kho_add_subtree(const char *name, void *blob, size_t size)
> {
> phys_addr_t phys = virt_to_phys(blob);
> void *root_fdt = kho_out.fdt;
> + u64 size_u64 = size;
> int err = -ENOMEM;
> int off, fdt_err;
>
> @@ -784,11 +785,16 @@ int kho_add_subtree(const char *name, void *blob, size_t size)
> goto out_pack;
> }
>
> - err = fdt_setprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME,
> + err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME,
> &phys, sizeof(phys));
> if (err < 0)
> goto out_pack;
>
> + err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_SIZE_PROP_NAME,
> + &size_u64, sizeof(size_u64));
> + if (err < 0)
> + goto out_pack;
> +
> WARN_ON_ONCE(kho_debugfs_blob_add(&kho_out.dbg, name, blob,
> size, false));
>
> @@ -817,7 +823,7 @@ void kho_remove_subtree(void *blob)
> const u64 *val;
> int len;
>
> - val = fdt_getprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, &len);
> + val = fdt_getprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME, &len);
> if (!val || len != sizeof(phys_addr_t))
> continue;
>
> @@ -1314,13 +1320,14 @@ EXPORT_SYMBOL_GPL(is_kho_boot);
> * kho_retrieve_subtree - retrieve a preserved sub blob by its name.
> * @name: the name of the sub blob passed to kho_add_subtree().
> * @phys: if found, the physical address of the sub blob is stored in @phys.
> + * @size: if not NULL and found, the size of the sub blob is stored in @size.
> *
> * Retrieve a preserved sub blob named @name and store its physical
> - * address in @phys.
> + * address in @phys and optionally its size in @size.
> *
> * Return: 0 on success, error code on failure
> */
> -int kho_retrieve_subtree(const char *name, phys_addr_t *phys)
> +int kho_retrieve_subtree(const char *name, phys_addr_t *phys, size_t *size)
> {
> const void *fdt = kho_get_fdt();
> const u64 *val;
> @@ -1336,12 +1343,21 @@ int kho_retrieve_subtree(const char *name, phys_addr_t *phys)
> if (offset < 0)
> return -ENOENT;
>
> - val = fdt_getprop(fdt, offset, KHO_FDT_SUB_TREE_PROP_NAME, &len);
> + val = fdt_getprop(fdt, offset, KHO_SUB_TREE_PROP_NAME, &len);
> if (!val || len != sizeof(*val))
> return -EINVAL;
>
> *phys = (phys_addr_t)*val;
>
> + if (size) {
> + val = fdt_getprop(fdt, offset, KHO_SUB_TREE_SIZE_PROP_NAME,
> + &len);
> + if (val && len == sizeof(*val))
> + *size = (size_t)*val;
> + else
> + *size = 0;
> + }
> +
> return 0;
> }
> EXPORT_SYMBOL_GPL(kho_retrieve_subtree);
> diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
> index 04d06a0906c0e..48b25c9abeda3 100644
> --- a/kernel/liveupdate/luo_core.c
> +++ b/kernel/liveupdate/luo_core.c
> @@ -88,7 +88,7 @@ static int __init luo_early_startup(void)
> }
>
> /* Retrieve LUO subtree, and verify its format. */
> - err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys);
> + err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys, NULL);
> if (err) {
> if (err != -ENOENT) {
> pr_err("failed to retrieve FDT '%s' from KHO: %pe\n",
> diff --git a/lib/test_kho.c b/lib/test_kho.c
> index 2631824373152..aa6a0956bb8b7 100644
> --- a/lib/test_kho.c
> +++ b/lib/test_kho.c
> @@ -319,7 +319,7 @@ static int __init kho_test_init(void)
> if (!kho_is_enabled())
> return 0;
>
> - err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys);
> + err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys, NULL);
> if (!err) {
> err = kho_test_restore(fdt_phys);
> if (err)
> diff --git a/mm/memblock.c b/mm/memblock.c
> index 29e12ea2a854c..4f4bf1a9d7900 100644
> --- a/mm/memblock.c
> +++ b/mm/memblock.c
> @@ -2533,7 +2533,7 @@ static void *__init reserve_mem_kho_retrieve_fdt(void)
> if (fdt)
> return fdt;
>
> - err = kho_retrieve_subtree(MEMBLOCK_KHO_FDT, &fdt_phys);
> + err = kho_retrieve_subtree(MEMBLOCK_KHO_FDT, &fdt_phys, NULL);
> if (err) {
> if (err != -ENOENT)
> pr_warn("failed to retrieve FDT '%s' from KHO: %d\n",
>
> --
> 2.47.3
>

--
Sincerely yours,
Mike.