* .. code-block:: none
*
* / {
- * compatible = "luo-v1";
- * liveupdate-number = <...>;
- *
- * luo-session {
- * compatible = "luo-session-v1";
- * luo-session-header = <phys_addr_of_session_header_ser>;
- * };
- *
- * luo-flb {
- * compatible = "luo-flb-v1";
- * luo-flb-header = <phys_addr_of_flb_header_ser>;
- * };
+ * compatible = "luo-v2";
+ * luo-abi-header = <phys_addr_of_luo_ser>;
* };
*
* Main LUO Node (/):
*
- * - compatible: "luo-v1"
+ * - compatible: "luo-v2"
* Identifies the overall LUO ABI version.
- * - liveupdate-number: u64
- * A counter tracking the number of successful live updates performed.
- *
- * Session Node (luo-session):
- * This node describes all preserved user-space sessions.
- *
- * - compatible: "luo-session-v1"
- * Identifies the session ABI version.
- * - luo-session-header: u64
- * The physical address of a `struct luo_session_header_ser`. This structure
- * is the header for a contiguous block of memory containing an array of
- * `struct luo_session_ser`, one for each preserved session.
- *
- * File-Lifecycle-Bound Node (luo-flb):
- * This node describes all preserved global objects whose lifecycle is bound
- * to that of the preserved files (e.g., shared IOMMU state).
- *
- * - compatible: "luo-flb-v1"
- * Identifies the FLB ABI version.
- * - luo-flb-header: u64
- * The physical address of a `struct luo_flb_header_ser`. This structure is
- * the header for a contiguous block of memory containing an array of
- * `struct luo_flb_ser`, one for each preserved global object.
+ * - luo-abi-header: u64
+ * The physical address of `struct luo_ser`.
*
* Serialization Structures:
* The FDT properties point to memory regions containing arrays of simple,
* `__packed` structures. These structures contain the actual preserved state.
*
+ * - struct luo_ser:
+ * The central ABI structure that contains the overall state of the LUO.
+ * It includes the liveupdate-number and pointers to sessions and FLBs.
+ *
* - struct luo_session_header_ser:
* Header for the session array. Contains the total page count of the
* preserved memory block and the number of `struct luo_session_ser`
/*
* The LUO FDT hooks all LUO state for sessions, fds, etc.
- * In the root it also carries "liveupdate-number" 64-bit property that
- * corresponds to the number of live-updates performed on this machine.
*/
#define LUO_FDT_SIZE PAGE_SIZE
#define LUO_FDT_KHO_ENTRY_NAME "LUO"
-#define LUO_FDT_COMPATIBLE "luo-v1"
-#define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number"
+#define LUO_FDT_COMPATIBLE "luo-v2"
+#define LUO_FDT_ABI_HEADER "luo-abi-header"
+
+/**
+ * struct luo_ser - Centralized LUO ABI header.
+ * @liveupdate_num: A counter tracking the number of successful live updates.
+ * @sessions_pa: Physical address of the first session block header.
+ * @flbs_pa: Physical address of the FLB header.
+ *
+ * This structure is the root of all preserved LUO state. It is pointed to by
+ * the "luo-abi-header" property in the LUO FDT.
+ */
+struct luo_ser {
+ u64 liveupdate_num;
+ u64 sessions_pa;
+ u64 flbs_pa;
+} __packed;
#define LIVEUPDATE_HNDL_COMPAT_LENGTH 48
u64 count;
} __packed;
-/*
- * LUO FDT session node
- * LUO_FDT_SESSION_HEADER: is a u64 physical address of struct
- * luo_session_header_ser
- */
-#define LUO_FDT_SESSION_NODE_NAME "luo-session"
-#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v2"
-#define LUO_FDT_SESSION_HEADER "luo-session-header"
-
/**
* struct luo_session_header_ser - Header for the serialized session data block.
* @count: The number of `struct luo_session_ser` entries that immediately
* physical memory preserved across the kexec. It provides the necessary
* metadata to interpret the array of session entries that follow.
*
- * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_session_header_ser {
u64 count;
* session) is created and passed to the new kernel, allowing it to reconstruct
* the session context.
*
- * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_session_ser {
char name[LIVEUPDATE_SESSION_NAME_LENGTH];
/* The max size is set so it can be reliably used during in serialization */
#define LIVEUPDATE_FLB_COMPAT_LENGTH 48
-#define LUO_FDT_FLB_NODE_NAME "luo-flb"
-#define LUO_FDT_FLB_COMPATIBLE "luo-flb-v1"
-#define LUO_FDT_FLB_HEADER "luo-flb-header"
-
/**
* struct luo_flb_header_ser - Header for the serialized FLB data block.
* @pgcnt: The total number of pages occupied by the entire preserved memory
* in the memory block.
*
* This structure is located at the physical address specified by the
- * `LUO_FDT_FLB_HEADER` FDT property. It provides the new kernel with the
- * necessary information to find and iterate over the array of preserved
- * File-Lifecycle-Bound objects and to manage the underlying memory.
+ * flbs_pa in luo_ser.
*
- * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_flb_header_ser {
u64 pgcnt;
* passed to the new kernel. Each entry allows the LUO core to restore one
* global, shared object.
*
- * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_flb_ser {
char name[LIVEUPDATE_FLB_COMPAT_LENGTH];
#include <linux/rwsem.h>
#include <linux/sizes.h>
#include <linux/string.h>
-#include <linux/unaligned.h>
#include "kexec_handover_internal.h"
#include "luo_internal.h"
static int __init luo_early_startup(void)
{
+ struct luo_ser *luo_ser;
+ int err, header_size;
phys_addr_t fdt_phys;
- int err, ln_size;
const void *ptr;
+ u64 luo_ser_pa;
if (!kho_is_enabled()) {
if (liveupdate_enabled())
return -EINVAL;
}
- ln_size = 0;
- ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_LIVEUPDATE_NUM,
- &ln_size);
- if (!ptr || ln_size != sizeof(luo_global.liveupdate_num)) {
- pr_err("Unable to get live update number '%s' [%d]\n",
- LUO_FDT_LIVEUPDATE_NUM, ln_size);
+ header_size = 0;
+ ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_ABI_HEADER, &header_size);
+ if (!ptr || header_size != sizeof(u64)) {
+ pr_err("Unable to get ABI header '%s' [%d]\n",
+ LUO_FDT_ABI_HEADER, header_size);
return -EINVAL;
}
- luo_global.liveupdate_num = get_unaligned((u64 *)ptr);
+ luo_ser_pa = get_unaligned((u64 *)ptr);
+ luo_ser = phys_to_virt(luo_ser_pa);
+
+ luo_global.liveupdate_num = luo_ser->liveupdate_num;
pr_info("Retrieved live update data, liveupdate number: %lld\n",
luo_global.liveupdate_num);
- err = luo_session_setup_incoming(luo_global.fdt_in);
+ err = luo_session_setup_incoming(luo_ser->sessions_pa);
if (err)
- return err;
+ goto out_free_ser;
+
+ luo_flb_setup_incoming(luo_ser->flbs_pa);
- err = luo_flb_setup_incoming(luo_global.fdt_in);
+ err = 0;
+out_free_ser:
+ kho_restore_free(luo_ser);
return err;
}
/* Called during boot to create outgoing LUO fdt tree */
static int __init luo_fdt_setup(void)
{
- const u64 ln = luo_global.liveupdate_num + 1;
+ struct luo_ser *luo_ser;
+ u64 luo_ser_pa;
void *fdt_out;
int err;
return PTR_ERR(fdt_out);
}
+ luo_ser = kho_alloc_preserve(sizeof(*luo_ser));
+ if (IS_ERR(luo_ser)) {
+ err = PTR_ERR(luo_ser);
+ goto exit_free_fdt;
+ }
+ luo_ser_pa = virt_to_phys(luo_ser);
+
err = fdt_create(fdt_out, LUO_FDT_SIZE);
err |= fdt_finish_reservemap(fdt_out);
err |= fdt_begin_node(fdt_out, "");
err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln));
- err |= luo_session_setup_outgoing(fdt_out);
- err |= luo_flb_setup_outgoing(fdt_out);
+ err |= fdt_property(fdt_out, LUO_FDT_ABI_HEADER, &luo_ser_pa,
+ sizeof(luo_ser_pa));
err |= fdt_end_node(fdt_out);
err |= fdt_finish(fdt_out);
if (err)
- goto exit_free;
+ goto exit_free_luo_ser;
+
+ err = luo_session_setup_outgoing(&luo_ser->sessions_pa);
+ if (err)
+ goto exit_free_luo_ser;
+
+ err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
+ if (err)
+ goto exit_free_luo_ser;
+
+ luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out,
fdt_totalsize(fdt_out));
if (err)
- goto exit_free;
+ goto exit_free_luo_ser;
luo_global.fdt_out = fdt_out;
return 0;
-exit_free:
+exit_free_luo_ser:
+ kho_unpreserve_free(luo_ser);
+exit_free_fdt:
kho_unpreserve_free(fdt_out);
pr_err("failed to prepare LUO FDT: %d\n", err);
#include <linux/io.h>
#include <linux/kexec_handover.h>
#include <linux/kho/abi/luo.h>
-#include <linux/libfdt.h>
#include <linux/list_private.h>
#include <linux/liveupdate.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/unaligned.h>
#include "luo_internal.h"
#define LUO_FLB_PGCNT 1ul
return 0;
}
-int __init luo_flb_setup_outgoing(void *fdt_out)
+int __init luo_flb_setup_outgoing(u64 *flbs_pa)
{
struct luo_flb_header_ser *header_ser;
- u64 header_ser_pa;
- int err;
header_ser = kho_alloc_preserve(LUO_FLB_PGCNT << PAGE_SHIFT);
if (IS_ERR(header_ser))
return PTR_ERR(header_ser);
- header_ser_pa = virt_to_phys(header_ser);
-
- err = fdt_begin_node(fdt_out, LUO_FDT_FLB_NODE_NAME);
- err |= fdt_property_string(fdt_out, "compatible",
- LUO_FDT_FLB_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_FLB_HEADER, &header_ser_pa,
- sizeof(header_ser_pa));
- err |= fdt_end_node(fdt_out);
-
- if (err)
- goto err_unpreserve;
+ *flbs_pa = virt_to_phys(header_ser);
header_ser->pgcnt = LUO_FLB_PGCNT;
luo_flb_global.outgoing.header_ser = header_ser;
luo_flb_global.outgoing.active = true;
return 0;
-
-err_unpreserve:
- kho_unpreserve_free(header_ser);
-
- return err;
}
-int __init luo_flb_setup_incoming(void *fdt_in)
+void __init luo_flb_setup_incoming(u64 flbs_pa)
{
struct luo_flb_header_ser *header_ser;
- int err, header_size, offset;
- const void *ptr;
- u64 header_ser_pa;
-
- offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_FLB_NODE_NAME);
- if (offset < 0) {
- pr_err("Unable to get FLB node [%s]\n", LUO_FDT_FLB_NODE_NAME);
-
- return -ENOENT;
- }
-
- err = fdt_node_check_compatible(fdt_in, offset,
- LUO_FDT_FLB_COMPATIBLE);
- if (err) {
- pr_err("FLB node is incompatible with '%s' [%d]\n",
- LUO_FDT_FLB_COMPATIBLE, err);
-
- return -EINVAL;
- }
-
- header_size = 0;
- ptr = fdt_getprop(fdt_in, offset, LUO_FDT_FLB_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get FLB header property '%s' [%d]\n",
- LUO_FDT_FLB_HEADER, header_size);
- return -EINVAL;
- }
-
- header_ser_pa = get_unaligned((u64 *)ptr);
- header_ser = phys_to_virt(header_ser_pa);
+ if (!flbs_pa)
+ return;
+ header_ser = phys_to_virt(flbs_pa);
luo_flb_global.incoming.header_ser = header_ser;
luo_flb_global.incoming.ser = (void *)(header_ser + 1);
luo_flb_global.incoming.active = true;
-
- return 0;
}
/**
int luo_session_create(const char *name, struct file **filep);
int luo_session_retrieve(const char *name, struct file **filep);
-int __init luo_session_setup_outgoing(void *fdt);
-int __init luo_session_setup_incoming(void *fdt);
+int __init luo_session_setup_outgoing(u64 *sessions_pa);
+int __init luo_session_setup_incoming(u64 sessions_pa);
int luo_session_serialize(void);
int luo_session_deserialize(void);
void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh);
void luo_flb_file_finish(struct liveupdate_file_handler *fh);
void luo_flb_unregister_all(struct liveupdate_file_handler *fh);
-int __init luo_flb_setup_outgoing(void *fdt);
-int __init luo_flb_setup_incoming(void *fdt);
+int __init luo_flb_setup_outgoing(u64 *flbs_pa);
+void __init luo_flb_setup_incoming(u64 flbs_pa);
void luo_flb_serialize(void);
#ifdef CONFIG_LIVEUPDATE_TEST
*
* - Serialization: Session metadata is preserved using the KHO framework. When
* a live update is triggered via kexec, an array of `struct luo_session_ser`
- * is populated and placed in a preserved memory region. An FDT node is also
- * created, containing the count of sessions and the physical address of this
- * array.
+ * is populated and placed in a preserved memory region. The physical address
+ * of this array is stored in the centralized `struct luo_ser` structure.
*
* Session Lifecycle:
*
#include <linux/io.h>
#include <linux/kexec_handover.h>
#include <linux/kho/abi/luo.h>
-#include <linux/libfdt.h>
#include <linux/list.h>
#include <linux/liveupdate.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
-#include <linux/unaligned.h>
#include <uapi/linux/liveupdate.h>
#include "luo_internal.h"
return err;
}
-int __init luo_session_setup_outgoing(void *fdt_out)
+int __init luo_session_setup_outgoing(u64 *sessions_pa)
{
struct luo_session_header_ser *header_ser;
- u64 header_ser_pa;
- int err;
header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
if (IS_ERR(header_ser))
return PTR_ERR(header_ser);
- header_ser_pa = virt_to_phys(header_ser);
-
- err = fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME);
- err |= fdt_property_string(fdt_out, "compatible",
- LUO_FDT_SESSION_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_SESSION_HEADER, &header_ser_pa,
- sizeof(header_ser_pa));
- err |= fdt_end_node(fdt_out);
- if (err)
- goto err_unpreserve;
+ *sessions_pa = virt_to_phys(header_ser);
luo_session_global.outgoing.header_ser = header_ser;
luo_session_global.outgoing.ser = (void *)(header_ser + 1);
luo_session_global.outgoing.active = true;
return 0;
-
-err_unpreserve:
- kho_unpreserve_free(header_ser);
- return err;
}
-int __init luo_session_setup_incoming(void *fdt_in)
+int __init luo_session_setup_incoming(u64 sessions_pa)
{
struct luo_session_header_ser *header_ser;
- int err, header_size, offset;
- u64 header_ser_pa;
- const void *ptr;
-
- offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME);
- if (offset < 0) {
- pr_err("Unable to get session node: [%s]\n",
- LUO_FDT_SESSION_NODE_NAME);
- return -EINVAL;
- }
- err = fdt_node_check_compatible(fdt_in, offset,
- LUO_FDT_SESSION_COMPATIBLE);
- if (err) {
- pr_err("Session node incompatible [%s]\n",
- LUO_FDT_SESSION_COMPATIBLE);
- return -EINVAL;
- }
-
- header_size = 0;
- ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get session header '%s' [%d]\n",
- LUO_FDT_SESSION_HEADER, header_size);
- return -EINVAL;
+ if (sessions_pa) {
+ header_ser = phys_to_virt(sessions_pa);
+ luo_session_global.incoming.header_ser = header_ser;
+ luo_session_global.incoming.ser = (void *)(header_ser + 1);
+ luo_session_global.incoming.active = true;
}
- header_ser_pa = get_unaligned((u64 *)ptr);
- header_ser = phys_to_virt(header_ser_pa);
-
- luo_session_global.incoming.header_ser = header_ser;
- luo_session_global.incoming.ser = (void *)(header_ser + 1);
- luo_session_global.incoming.active = true;
-
return 0;
}