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>
---
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
On Mon, Mar 09 2026, 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>
> ---
[...]
> 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;
> +
I noticed that the error handling here is a bit broken. We open the
subnode for the subtree, but then if we fail to add the "preserved-data"
property, we don't remove the subnode. So the next kernel gets an
invalid FDT (per KHO ABI) and might as well refuse to parse it.
Similarly here, the FDT might also be missing the size and then the next
kernel might reject the FDT.
Also, we directly return the FDT error code to the caller, which
wouldn't make sense since it probably expects -errno.
Not something this patchset has to fix, but I am pointing this out in
case someone (possibly also future me) is interested in fixing this up.
> 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;
If the size property is invalid, is it a good idea to ignore it? Should
we instead consider the subnode to be broken and reject it entirely with
an error message? Because if a caller expects a blob of 16 bytes but
gets one with 0 bytes, it will likely error out anyway.
> + }
> +
> return 0;
> }
> EXPORT_SYMBOL_GPL(kho_retrieve_subtree);
[...]
--
Regards,
Pratyush Yadav
On Fri, Mar 13, 2026 at 09:21:50AM +0000, Pratyush Yadav wrote:
> On Mon, Mar 09 2026, 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>
> > ---
> [...]
> > 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;
> > +
>
> I noticed that the error handling here is a bit broken. We open the
> subnode for the subtree, but then if we fail to add the "preserved-data"
> property, we don't remove the subnode. So the next kernel gets an
> invalid FDT (per KHO ABI) and might as well refuse to parse it.
>
> Similarly here, the FDT might also be missing the size and then the next
> kernel might reject the FDT.
>
> Also, we directly return the FDT error code to the caller, which
> wouldn't make sense since it probably expects -errno.
>
> Not something this patchset has to fix, but I am pointing this out in
> case someone (possibly also future me) is interested in fixing this up.
That is a good point, do you mean a fix like the following?
commit 633d0cb01ed959676b60de8b1851dad1757d8fe5
Author: Breno Leitao <leitao@debian.org>
Date: Mon Mar 16 04:03:51 2026 -0700
kho: fix error handling in kho_add_subtree()
Fix two error handling issues in kho_add_subtree():
1. If fdt_setprop() fails after the subnode has been created, the
subnode is not removed. This leaves an incomplete node in the FDT
(missing "preserved-data" or "blob-size" properties), which violates
the KHO ABI and may cause the next kernel to reject the FDT.
2. The fdt_setprop() return value (an FDT error code) is stored
directly in err and returned to the caller, which expects -errno.
Fix both by storing fdt_setprop() results in fdt_err, jumping to a new
out_del_node label that removes the subnode on failure, and only setting
err = 0 on the success path.
Signed-off-by: Breno Leitao <leitao@debian.org>
Suggested-by: Pratyush Yadav <pratyush@kernel.org>
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 62b1b8a9aa337..8d2d30119f6d4 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -787,19 +787,24 @@ int kho_add_subtree(const char *name, void *blob, size_t size)
goto out_pack;
}
- err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME,
- &phys, sizeof(phys));
- if (err < 0)
- goto out_pack;
+ fdt_err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME,
+ &phys, sizeof(phys));
+ if (fdt_err < 0)
+ goto out_del_node;
- err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_SIZE_PROP_NAME,
- &size_u64, sizeof(size_u64));
- if (err < 0)
- goto out_pack;
+ fdt_err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_SIZE_PROP_NAME,
+ &size_u64, sizeof(size_u64));
+ if (fdt_err < 0)
+ goto out_del_node;
WARN_ON_ONCE(kho_debugfs_blob_add(&kho_out.dbg, name, blob,
size, false));
+ err = 0;
+ goto out_pack;
+
+out_del_node:
+ fdt_del_node(root_fdt, off);
out_pack:
fdt_pack(root_fdt);
Given this is not strictly related to this patchset, I am planning to
send this fix separately.
> > 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;
>
> If the size property is invalid, is it a good idea to ignore it? Should
> we instead consider the subnode to be broken and reject it entirely with
> an error message? Because if a caller expects a blob of 16 bytes but
> gets one with 0 bytes, it will likely error out anyway.
Ack, let me update this, then.
Thanks for the review,
--breno
On Mon, Mar 16 2026, Breno Leitao wrote: > On Fri, Mar 13, 2026 at 09:21:50AM +0000, Pratyush Yadav wrote: >> On Mon, Mar 09 2026, Breno Leitao wrote: [...] >> I noticed that the error handling here is a bit broken. We open the >> subnode for the subtree, but then if we fail to add the "preserved-data" >> property, we don't remove the subnode. So the next kernel gets an >> invalid FDT (per KHO ABI) and might as well refuse to parse it. >> >> Similarly here, the FDT might also be missing the size and then the next >> kernel might reject the FDT. >> >> Also, we directly return the FDT error code to the caller, which >> wouldn't make sense since it probably expects -errno. >> >> Not something this patchset has to fix, but I am pointing this out in >> case someone (possibly also future me) is interested in fixing this up. > > That is a good point, do you mean a fix like the following? > > commit 633d0cb01ed959676b60de8b1851dad1757d8fe5 > Author: Breno Leitao <leitao@debian.org> > Date: Mon Mar 16 04:03:51 2026 -0700 > > kho: fix error handling in kho_add_subtree() > > Fix two error handling issues in kho_add_subtree(): > > 1. If fdt_setprop() fails after the subnode has been created, the > subnode is not removed. This leaves an incomplete node in the FDT > (missing "preserved-data" or "blob-size" properties), which violates > the KHO ABI and may cause the next kernel to reject the FDT. > > 2. The fdt_setprop() return value (an FDT error code) is stored > directly in err and returned to the caller, which expects -errno. > > Fix both by storing fdt_setprop() results in fdt_err, jumping to a new > out_del_node label that removes the subnode on failure, and only setting > err = 0 on the success path. > > Signed-off-by: Breno Leitao <leitao@debian.org> > Suggested-by: Pratyush Yadav <pratyush@kernel.org> > > diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c > index 62b1b8a9aa337..8d2d30119f6d4 100644 > --- a/kernel/liveupdate/kexec_handover.c > +++ b/kernel/liveupdate/kexec_handover.c > @@ -787,19 +787,24 @@ int kho_add_subtree(const char *name, void *blob, size_t size) > goto out_pack; > } > > - err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME, > - &phys, sizeof(phys)); > - if (err < 0) > - goto out_pack; > + fdt_err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME, > + &phys, sizeof(phys)); > + if (fdt_err < 0) > + goto out_del_node; > > - err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_SIZE_PROP_NAME, > - &size_u64, sizeof(size_u64)); > - if (err < 0) > - goto out_pack; > + fdt_err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_SIZE_PROP_NAME, > + &size_u64, sizeof(size_u64)); > + if (fdt_err < 0) > + goto out_del_node; > > WARN_ON_ONCE(kho_debugfs_blob_add(&kho_out.dbg, name, blob, > size, false)); > > + err = 0; > + goto out_pack; > + > +out_del_node: > + fdt_del_node(root_fdt, off); > out_pack: > fdt_pack(root_fdt); > > > Given this is not strictly related to this patchset, I am planning to > send this fix separately. Yep, looks good. Please send it out as an independent patch. [...] -- Regards, Pratyush Yadav
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.
© 2016 - 2026 Red Hat, Inc.