[RFC PATCH v2 3/4] liveupdate: Replace compatibility string with a 2-bit pair feature header

Pasha Tatashin pasha.tatashin at soleen.com
Wed Oct 21 10:27:22 PDT 2026


On Wed, Oct 21, 2026 at 11:25 AM Tarun Sahu <tarunsahu at google.com> wrote:
>
> LUO and its file handlers validate handed-over state with monolithic
> compatibility strings ("luo-v5", "memfd-v2"). Any change to a serialized
> structure, even an optional additive one, must bump the string, which
> breaks live update between adjacent kernels. Replace the string in
> struct luo_ser with a feature header, so that the target kernel can
> accept or reject the source kernel state per feature.
>
> struct luo_feature_hdr is a fixed 128-byte header: the serialized

This patch is all wrong; 128 bytes is far too large. For every _ser
struct, we only need 8 bytes instead.

The first 4 bytes are the size, and the next 4 bytes are flags (i.e.,
30 feature flags + 2 reserved bits for flags extension). The last two
bits of the flags indicate whether an extended flags field is present.
Each extended flags field is 8 bytes, and its last two bits indicate
that yet another extended flags field follows.

> structure size from the source kernel followed by 15 chained u64 feature
> flags entries. The fixed size keeps payload field offsets identical in
> every kernel, regardless of how many flags entries a source kernel uses.
>
> Each feature has a 2-bit pair (req, active) state (enum luo_feat_state:
> LUO_FEAT_DISABLED, LUO_FEAT_OPTIONAL, LUO_FEAT_REQUIRED). A flags entry
> holds 31 features, and bits [63:62] = 0b11 chain to the next entry (465
> features in total). Each subsystem defines its feature states as a
> static const u8 array indexed by its feature enum, which
> luo_feature_hdr_init() packs into 64-bit flags entries with
> LUO_FEAT_EXT_MASK chain bits. The target kernel validates the header from
> the source kernel against its own feature array via
> luo_feature_hdr_validate() according to the per-feature (req, active)
> truth table:
>
>   Source Target Action
>   (1,0)  (x,x)  Reject -EINVAL: invalid source kernel (req without active)
>   (x,x)  (1,0)  Reject -EINVAL + WARN_ON_ONCE: invalid target kernel
>   (0,0)  (0,0)  Accept: feature absent
>   (0,0)  (0,1)  Accept: feature absent
>   (0,0)  (1,1)  Reject -EOPNOTSUPP: target kernel requires the feature
>   (0,1)  (0,0)  Accept: ignore payload data
>   (1,1)  (0,0)  Reject -EOPNOTSUPP: source kernel requires the feature
>   (x,1)  (x,1)  Accept: use payload data
>
> The relative order and offsets of fields in serialized structures must
> remain unchanged as features are added or retired. New fields are always
> appended at the end of the structure under a new feature flag, and
> retired fields are kept in place so that the offsets of subsequent
> fields never shift. If the type or format of a field needs to change, the
> existing field and feature are kept intact (and retired when no longer
> needed) and a new feature flag with a new appended field of the new type
> is added.
>
> In the target kernel, fields of a LUO_FEAT_REQUIRED feature are
> guaranteed to be present once luo_feature_hdr_validate() succeeds,
> whereas fields of a LUO_FEAT_OPTIONAL feature should be accessed only
> after verifying that the feature is active in the source kernel header:
>
>   if (luo_feature_is_active(&ser->features, FEAT_X))
>           val = ser->x_field;
>
> All current struct luo_ser fields (liveupdate_num, sessions_pa, flbs_pa)
> belong to LUO_CORE_FEAT_CORE (LUO_FEAT_REQUIRED in luo_core_features).
> Future changes add new entries to enum luo_core_feature before
> LUO_CORE_FEAT_MAX.
>
> Signed-off-by: Logan Odell <loganodell at google.com>
> Signed-off-by: Tarun Sahu <tarunsahu at google.com>
> ---
>  include/linux/kho/abi/luo.h  | 345 +++++++++++++++++++++++++++++++----
>  kernel/liveupdate/luo_core.c |  27 ++-
>  2 files changed, 332 insertions(+), 40 deletions(-)
>
> diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
> index 5fb2f46425d7..be35cfbfdb2b 100644
> --- a/include/linux/kho/abi/luo.h
> +++ b/include/linux/kho/abi/luo.h
> @@ -9,19 +9,53 @@
>   * DOC: Live Update Orchestrator ABI
>   *
>   * Live Update Orchestrator uses the stable Application Binary Interface
> - * defined below to pass state from a pre-update kernel to a post-update
> - * kernel. The ABI is built upon the Kexec HandOver framework and registers
> - * the central `struct luo_ser` via the KHO raw subtree API.
> + * defined below to pass state from a source kernel to a target kernel.
> + * The ABI is built upon the Kexec HandOver framework and registers the
> + * central `struct luo_ser` via the KHO raw subtree API.
>   *
> - * This interface is a contract. Any modification to the structure fields,
> - * compatible strings, or the layout of the `__packed` serialization
> - * structures defined here constitutes a breaking change. Such changes require
> - * incrementing the version number in the relevant `_COMPATIBLE` string to
> - * prevent a new kernel from misinterpreting data from an old kernel.
> + * This interface is a contract. Serialized structures start with a fixed
> + * 128-byte feature header (`struct luo_feature_hdr`) holding the source
> + * kernel's structure `size` and 15 chained 64-bit feature flags entries
> + * (`flags[LUO_FEAT_NR_FLAGS]`). Each entry encodes up to 31 features as
> + * 2-bit `0b<req><active>` pairs (`enum luo_feat_state`), with
> + * `LUO_FEAT_EXT_MASK` in bits `[63:62]` chaining to the next entry (up to
> + * 465 features total). Because the header size is fixed at 128 bytes,
> + * payload field offsets do not depend on how many flags entries a source
> + * kernel uses.
>   *
> - * Changes are allowed provided the compatibility version is incremented;
> - * however, backward/forward compatibility is only guaranteed for kernels
> - * supporting the same ABI version.
> + * Truth table (`(req,active)` pairs per feature)::
> + *
> + *   Source Target Action
> + *   (1,0)  (x,x)  Reject -EINVAL: invalid source kernel (req without active)
> + *   (x,x)  (1,0)  Reject -EINVAL: invalid target kernel (req without active)
> + *   (0,0)  (0,0)  Accept: feature absent
> + *   (0,0)  (0,1)  Accept: feature absent
> + *   (0,0)  (1,1)  Reject -EOPNOTSUPP: target kernel requires the feature
> + *   (0,1)  (0,0)  Accept: ignore payload data
> + *   (1,1)  (0,0)  Reject -EOPNOTSUPP: source kernel requires the feature
> + *   (x,1)  (x,1)  Accept: use payload data
> + *
> + * The relative order and offsets of fields in serialized structures must
> + * remain unchanged as features are added or retired. New fields are always
> + * appended at the end of the structure under a new feature flag, and
> + * retired fields are kept in place so that the offsets of subsequent
> + * fields never shift. If a field's type or format needs to change, the
> + * existing field and feature are kept intact (and retired when no longer
> + * needed) and a new feature flag with a new appended field of the new type
> + * is added.
> + *
> + * In the target kernel, fields of a `LUO_FEAT_REQUIRED` feature are
> + * guaranteed to be present once `luo_feature_hdr_validate()` succeeds,
> + * whereas fields of a `LUO_FEAT_OPTIONAL` feature should be accessed only
> + * after verifying that the feature is active in the source kernel's header::
> + *
> + *   if (luo_feature_is_active(&ser->features, FEAT_X))
> + *           val = ser->x_field;
> + *
> + * All current `struct luo_ser` fields (`liveupdate_num`, `sessions_pa`,
> + * `flbs_pa`) belong to `LUO_CORE_FEAT_CORE` (`LUO_FEAT_REQUIRED` in
> + * `luo_core_features`). Future changes add new entries to
> + * `enum luo_core_feature` before `LUO_CORE_FEAT_MAX`.
>   *
>   * KHO Structure Overview:
>   *   The entire LUO state is encapsulated within a single KHO entry named "LUO".
> @@ -30,8 +64,9 @@
>   * Serialization Structures:
>   *   - struct luo_ser:
>   *     The central ABI structure that contains the overall state of the LUO.
> - *     It includes the compatibility string, the liveupdate-number, and pointers
> - *     to sessions and FLBs.
> + *     It includes the feature header (with struct size and 2-bit feature flag
> + *     pairs), and the core feature (`LUO_CORE_FEAT_CORE`) fields: the
> + *     liveupdate-number and pointers to sessions and FLBs.
>   *
>   *   - struct luo_session_ser:
>   *     Metadata for a single session, including its name and a physical pointer
> @@ -40,8 +75,9 @@
>   *
>   *   - struct luo_file_ser:
>   *     Metadata for a single preserved file. Contains the handler `name` string
> - *     to find the correct handler in the new kernel, a user-provided `token`
> - *     for identification, and an opaque `data` handle for the handler to use.
> + *     to find the correct handler in the target kernel, a user-provided
> + *     `token` for identification, and an opaque `data` handle for the handler
> + *     to use.
>   *
>   *   - struct luo_flb_header_ser:
>   *     Header for the FLB array. Contains the total page count of the
> @@ -57,20 +93,265 @@
>  #ifndef _LINUX_KHO_ABI_LUO_H
>  #define _LINUX_KHO_ABI_LUO_H
>
> -#include <linux/align.h>
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/bug.h>
> +#include <linux/build_bug.h>
> +#include <linux/errno.h>
>  #include <linux/kho/abi/block.h>
> +#include <linux/types.h>
>  #include <uapi/linux/liveupdate.h>
>
>  /*
>   * The LUO state is registered under this KHO entry name.
>   */
>  #define LUO_KHO_ENTRY_NAME     "LUO"
> -#define LUO_ABI_COMPATIBLE     "luo-v5"
> -#define LUO_ABI_COMPAT_LEN     ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8)
> +
> +#define LUO_FEAT_PER_FLAGS     31
> +#define LUO_FEAT_NR_FLAGS      15
> +#define LUO_FEAT_MAX           (LUO_FEAT_NR_FLAGS * LUO_FEAT_PER_FLAGS)
> +#define LUO_FEAT_EXT_MASK      GENMASK_ULL(63, 62)
> +#define LUO_FEAT_ACTIVE_MASK   (0x5555555555555555ULL & ~LUO_FEAT_EXT_MASK)
> +#define LUO_FEAT_REQ_MASK      (0xaaaaaaaaaaaaaaaaULL & ~LUO_FEAT_EXT_MASK)
> +
> +#define LUO_FEAT_FLAGS_IDX(f)  ((unsigned int)(f) / LUO_FEAT_PER_FLAGS)
> +#define LUO_FEAT_SLOT(f)       ((unsigned int)(f) % LUO_FEAT_PER_FLAGS)
> +
> +#define LUO_FEAT_ACTIVE(f)     BIT_ULL(2 * LUO_FEAT_SLOT(f))
> +#define LUO_FEAT_REQ(f)                BIT_ULL(2 * LUO_FEAT_SLOT(f) + 1)
> +
> +/**
> + * enum luo_feat_state - 2-bit state of a LUO feature (`0b<req><active>`).
> + * @LUO_FEAT_DISABLED: Feature is inactive (`req=0, active=0`).
> + * @LUO_FEAT_OPTIONAL: Feature is active and optional (`req=0, active=1`).
> + * @LUO_FEAT_REQUIRED: Feature is active and required (`req=1, active=1`).
> + */
> +enum luo_feat_state {
> +       LUO_FEAT_DISABLED = 0b00,
> +       LUO_FEAT_OPTIONAL = 0b01,
> +       LUO_FEAT_REQUIRED = 0b11,
> +};
> +
> +/**
> + * struct luo_feature_hdr - Fixed 128-byte header for serialized LUO ABI structures.
> + * @size:  Size in bytes of the source kernel's enclosing serialized structure.
> + * @flags: Chained 64-bit feature flags entries (`0 .. LUO_FEAT_NR_FLAGS - 1`).
> + *         Each entry holds 31 2-bit feature pairs (`[2*slot + 1 : 2*slot]`)
> + *         plus %LUO_FEAT_EXT_MASK in bits `[63:62]` when the next entry is
> + *         active.
> + */
> +struct luo_feature_hdr {
> +       u64 size;
> +       u64 flags[LUO_FEAT_NR_FLAGS];
> +} __packed;


> +
> +static_assert(sizeof(struct luo_feature_hdr) == 128);
> +
> +/**
> + * luo_feature_hdr_for_each_flags - Iterate over chained flags entries in @hdr.
> + * @hdr: Pointer to the &struct luo_feature_hdr to iterate.
> + * @idx: Loop cursor of type `unsigned int` (`0 .. LUO_FEAT_NR_FLAGS - 1`).
> + */
> +#define luo_feature_hdr_for_each_flags(hdr, idx)                       \
> +       for ((idx) = 0; (idx) < LUO_FEAT_NR_FLAGS;                      \
> +            (idx) = (((hdr)->flags[idx] & LUO_FEAT_EXT_MASK) ==        \
> +                     LUO_FEAT_EXT_MASK) ? (idx) + 1 : LUO_FEAT_NR_FLAGS)
> +
> +/**
> + * luo_feature_hdr_get_flags - Get the flags entry containing feature @feat.
> + * @hdr:  Pointer to the source kernel's feature header.
> + * @feat: Feature index (`0 .. LUO_FEAT_MAX - 1`).
> + *
> + * Return: The u64 flags entry containing @feat if the extension chain reaches
> + *         it, or 0 otherwise.
> + */
> +static inline u64
> +luo_feature_hdr_get_flags(const struct luo_feature_hdr *hdr,
> +                         unsigned int feat)
> +{
> +       unsigned int target_idx = LUO_FEAT_FLAGS_IDX(feat);
> +       unsigned int idx;
> +
> +       if (target_idx >= LUO_FEAT_NR_FLAGS)
> +               return 0;
> +
> +       luo_feature_hdr_for_each_flags(hdr, idx) {
> +               if (idx == target_idx)
> +                       return hdr->flags[idx];
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * luo_feature_is_active - Test whether feature @feat is active in @hdr.
> + * @hdr:  Pointer to the source kernel's feature header.
> + * @feat: Feature index (`0 .. LUO_FEAT_MAX - 1`).
> + *
> + * Return: true if the source kernel marked @feat active, false otherwise.
> + */
> +static inline bool luo_feature_is_active(const struct luo_feature_hdr *hdr,
> +                                        unsigned int feat)
> +{
> +       return luo_feature_hdr_get_flags(hdr, feat) & LUO_FEAT_ACTIVE(feat);
> +}
> +
> +/**
> + * luo_feature_is_req - Test whether feature @feat is required in @hdr.
> + * @hdr:  Pointer to the source kernel's feature header.
> + * @feat: Feature index (`0 .. LUO_FEAT_MAX - 1`).
> + *
> + * Return: true if the source kernel marked @feat required, false otherwise.
> + */
> +static inline bool luo_feature_is_req(const struct luo_feature_hdr *hdr,
> +                                     unsigned int feat)
> +{
> +       return luo_feature_hdr_get_flags(hdr, feat) & LUO_FEAT_REQ(feat);
> +}
> +
> +/**
> + * luo_feature_pack_entry - Pack up to 31 feature states into a flags entry.
> + * @features:    Array of &enum luo_feat_state values indexed by feature ID.
> + * @nr_features: Number of elements in @features.
> + * @idx:         Flags entry index (`0 .. LUO_FEAT_NR_FLAGS - 1`).
> + *
> + * Return: The packed 64-bit flags entry for @idx (without %LUO_FEAT_EXT_MASK).
> + */
> +static inline u64
> +luo_feature_pack_entry(const u8 *features, size_t nr_features,
> +                      unsigned int idx)
> +{
> +       unsigned int base = idx * LUO_FEAT_PER_FLAGS;
> +       unsigned int slot;
> +       u64 flags = 0;
> +
> +       for (slot = 0; slot < LUO_FEAT_PER_FLAGS && base + slot < nr_features;
> +            slot++)
> +               flags |= (u64)(features[base + slot] & 0b11) << (2 * slot);
> +
> +       return flags;
> +}
> +
> +/**
> + * __luo_feature_hdr_init - Initialize a serialized LUO feature header.
> + * @hdr:         Pointer to the feature header to initialize.
> + * @size:        Byte size of the enclosing serialized structure.
> + * @features:    Array of &enum luo_feat_state values for the source kernel.
> + * @nr_features: Number of elements in @features (`<= LUO_FEAT_MAX`).
> + */
> +static inline void
> +__luo_feature_hdr_init(struct luo_feature_hdr *hdr, u64 size,
> +                      const u8 *features, size_t nr_features)
> +{
> +       bool ext = false;
> +       int idx;
> +
> +       hdr->size = size;
> +       for (idx = LUO_FEAT_NR_FLAGS - 1; idx >= 0; idx--) {
> +               u64 flags = luo_feature_pack_entry(features, nr_features, idx);
> +
> +               WARN_ON_ONCE(((flags & LUO_FEAT_REQ_MASK) >> 1) &
> +                            ~(flags & LUO_FEAT_ACTIVE_MASK));
> +               if (ext)
> +                       flags |= LUO_FEAT_EXT_MASK;
> +               else if (flags)
> +                       ext = true;
> +               hdr->flags[idx] = flags;
> +       }
> +}
> +
> +/**
> + * luo_feature_hdr_init - Initialize a serialized LUO feature header.
> + * @hdr:      Pointer to the feature header to initialize.
> + * @size:     Byte size of the enclosing serialized structure.
> + * @features: Fixed-size `u8` array of &enum luo_feat_state entries.
> + */
> +#define luo_feature_hdr_init(hdr, size, features)                      \
> +({                                                                     \
> +       BUILD_BUG_ON(ARRAY_SIZE(features) > LUO_FEAT_MAX);              \
> +       __luo_feature_hdr_init(hdr, size, features,                     \
> +                              ARRAY_SIZE(features));                   \
> +})
> +
> +/**
> + * __luo_feature_hdr_validate - Validate a source kernel's LUO feature header
> + *                              against the target kernel's feature array.
> + * @hdr:         Pointer to the feature header from the source kernel.
> + * @features:    Array of &enum luo_feat_state values for the target kernel.
> + * @nr_features: Number of elements in @features (`<= LUO_FEAT_MAX`).
> + *
> + * Return: 0 if compatible, -EINVAL on malformed header or `(req=1, active=0)`
> + *         feature state, or -EOPNOTSUPP on required feature mismatch.
> + */
> +static inline int
> +__luo_feature_hdr_validate(const struct luo_feature_hdr *hdr,
> +                          const u8 *features, size_t nr_features)
> +{
> +       bool src_ext = true, target_ext = (nr_features > 0);
> +       unsigned int idx;
> +
> +       if (hdr->size < sizeof(*hdr))
> +               return -EINVAL;
> +
> +       for (idx = 0; idx < LUO_FEAT_NR_FLAGS && (src_ext || target_ext); idx++) {
> +               u64 src = src_ext ? hdr->flags[idx] : 0;
> +               u64 target = target_ext ?
> +                       luo_feature_pack_entry(features, nr_features, idx) : 0;
> +               u64 ext = src & LUO_FEAT_EXT_MASK;
> +               u64 src_act = src & LUO_FEAT_ACTIVE_MASK;
> +               u64 src_req = (src & LUO_FEAT_REQ_MASK) >> 1;
> +               u64 target_act = target & LUO_FEAT_ACTIVE_MASK;
> +               u64 target_req = (target & LUO_FEAT_REQ_MASK) >> 1;
> +
> +               if (ext && (ext != LUO_FEAT_EXT_MASK ||
> +                           idx == LUO_FEAT_NR_FLAGS - 1))
> +                       return -EINVAL;
> +               if (src_req & ~src_act)
> +                       return -EINVAL;
> +               if (WARN_ON_ONCE(target_req & ~target_act))
> +                       return -EINVAL;
> +               if ((src_req & ~target_act) || (target_req & ~src_act))
> +                       return -EOPNOTSUPP;
> +
> +               src_ext = (ext == LUO_FEAT_EXT_MASK);
> +               target_ext = ((idx + 1) * LUO_FEAT_PER_FLAGS < nr_features);
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * luo_feature_hdr_validate - Validate a source kernel's LUO feature header
> + *                            against the target kernel's feature array.
> + * @hdr:      Pointer to the feature header from the source kernel.
> + * @features: Fixed-size `u8` array of &enum luo_feat_state entries.
> + *
> + * Return: 0 if compatible, -EINVAL on malformed header or feature state, or
> + *         -EOPNOTSUPP on required feature mismatch.
> + */
> +#define luo_feature_hdr_validate(hdr, features)                                \
> +({                                                                     \
> +       BUILD_BUG_ON(ARRAY_SIZE(features) > LUO_FEAT_MAX);              \
> +       __luo_feature_hdr_validate(hdr, features,                       \
> +                                  ARRAY_SIZE(features));               \
> +})
> +
> +/**
> + * enum luo_core_feature - Feature indices for &struct luo_ser.
> + * @LUO_CORE_FEAT_CORE: Core feature covering `liveupdate_num`, `sessions_pa`,
> + *                      and `flbs_pa`.
> + * @LUO_CORE_FEAT_MAX:  Number of defined &enum luo_core_feature features.
> + */
> +enum luo_core_feature {
> +       LUO_CORE_FEAT_CORE = 0,
> +       LUO_CORE_FEAT_MAX,
> +};
> +
> +static_assert((int)LUO_CORE_FEAT_MAX <= LUO_FEAT_MAX);
>
>  /**
>   * struct luo_ser - Centralized LUO ABI header.
> - * @compatible:     Compatibility string identifying the LUO ABI version.
> + * @features:       ABI feature and size header for luo_ser.
>   * @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.
> @@ -78,7 +359,7 @@
>   * This structure is the root of all preserved LUO state.
>   */
>  struct luo_ser {
> -       char compatible[LUO_ABI_COMPAT_LEN];
> +       struct luo_feature_hdr features;
>         u64 liveupdate_num;
>         u64 sessions_pa;
>         u64 flbs_pa;
> @@ -90,7 +371,7 @@ struct luo_ser {
>   * @data:        Private data
>   * @token:       User provided token for this file
>   *
> - * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
> + * Layout changes must be gated by a required feature in &enum luo_core_feature.
>   */
>  struct luo_file_ser {
>         char name[LIVEUPDATE_HNDL_NAME_LENGTH];
> @@ -121,10 +402,10 @@ struct luo_file_set_ser {
>   *
>   * This structure is used to package session-specific metadata for transfer
>   * between kernels via Kexec Handover. An array of these structures (one per
> - * session) is created and passed to the new kernel, allowing it to reconstruct
> - * the session context.
> + * session) is created and passed to the target kernel, allowing it to
> + * reconstruct the session context.
>   *
> - * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
> + * Layout changes must be gated by a required feature in &enum luo_core_feature.
>   */
>  struct luo_session_ser {
>         char name[LIVEUPDATE_SESSION_NAME_LENGTH];
> @@ -145,7 +426,7 @@ struct luo_session_ser {
>   * This structure is located at the physical address specified by the
>   * flbs_pa in luo_ser.
>   *
> - * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
> + * Layout changes must be gated by a required feature in &enum luo_core_feature.
>   */
>  struct luo_flb_header_ser {
>         u64 pgcnt;
> @@ -155,19 +436,19 @@ struct luo_flb_header_ser {
>  /**
>   * struct luo_flb_ser - Represents the serialized state of a single FLB object.
>   * @name:    The unique name string of the FLB object, used to find the
> - *           corresponding &struct liveupdate_flb handler in the new kernel.
> + *           corresponding &struct liveupdate_flb in the target kernel.
>   * @data:    The opaque u64 handle returned by the FLB's .preserve() operation
> - *           in the old kernel. This handle encapsulates the entire state needed
> - *           for restoration.
> + *           in the source kernel. This handle encapsulates the entire state
> + *           needed for restoration.
>   * @count:   The reference count at the time of serialization; i.e., the number
>   *           of preserved files that depended on this FLB. This is used by the
> - *           new kernel to correctly manage the FLB's lifecycle.
> + *           target kernel to correctly manage the FLB's lifecycle.
>   *
>   * An array of these structures is created in a preserved memory region and
> - * passed to the new kernel. Each entry allows the LUO core to restore one
> - * global, shared object.
> + * passed to the target kernel. Each entry allows the LUO core to restore
> + * one global, shared object.
>   *
> - * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
> + * Layout changes must be gated by a required feature in &enum luo_core_feature.
>   */
>  struct luo_flb_ser {
>         char name[LIVEUPDATE_FLB_NAME_LENGTH];
> diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
> index 1b2bda22902d..3627a206a79c 100644
> --- a/kernel/liveupdate/luo_core.c
> +++ b/kernel/liveupdate/luo_core.c
> @@ -70,6 +70,10 @@ static struct {
>         u64 liveupdate_num;
>  } luo_global;
>
> +static const u8 luo_core_features[LUO_CORE_FEAT_MAX] = {
> +       [LUO_CORE_FEAT_CORE] = LUO_FEAT_REQUIRED,
> +};
> +
>  /*
>   * luo_register_rwlock - Protects registration of file handlers and FLBs.
>   */
> @@ -107,15 +111,21 @@ static int __init luo_early_startup(void)
>                 return 0;
>         }
>
> -       if (len < sizeof(*luo_ser)) {
> -               pr_err("LUO state is too small (%zu < %zu)\n", len, sizeof(*luo_ser));
> -               return -EINVAL;
> +       luo_ser = phys_to_virt(luo_ser_phys);
> +       if (len < sizeof(luo_ser->features) || luo_ser->features.size > len) {
> +               pr_err("LUO state size invalid (source kernel header size=%llu, KHO len=%zu)\n",
> +                      len >= sizeof(luo_ser->features) ? luo_ser->features.size : 0,
> +                      len);
> +               err = -EINVAL;
> +               goto out_free_ser;
>         }
>
> -       luo_ser = phys_to_virt(luo_ser_phys);
> -       if (strncmp(luo_ser->compatible, LUO_ABI_COMPATIBLE, LUO_ABI_COMPAT_LEN)) {
> -               pr_err("LUO state is incompatible with '%s'\n", LUO_ABI_COMPATIBLE);
> -               return -EINVAL;
> +       err = luo_feature_hdr_validate(&luo_ser->features, luo_core_features);
> +       if (err) {
> +               pr_err("LUO state feature header validation failed (size=%llu, flags[0]=%#llx): %pe\n",
> +                      luo_ser->features.size, luo_ser->features.flags[0],
> +                      ERR_PTR(err));
> +               goto out_free_ser;
>         }
>
>         luo_global.liveupdate_num = luo_ser->liveupdate_num;
> @@ -162,7 +172,8 @@ static int __init luo_state_setup(void)
>                 return PTR_ERR(luo_ser);
>         }
>
> -       strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible));
> +       luo_feature_hdr_init(&luo_ser->features, sizeof(*luo_ser),
> +                            luo_core_features);
>         luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
>
>         luo_session_setup_outgoing(&luo_ser->sessions_pa);
> --
> 2.56.0.212.g129ede20d9-goog
>



More information about the kexec mailing list