From: Jiri Pirko <jiri@resnulli.us>
To: linux-coco@lists.linux.dev
Cc: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org,
x86@kernel.org, kvm@vger.kernel.org, linux-doc@vger.kernel.org,
jgg@ziepe.ca, david.quigley@intel.com, lukas@wunner.de,
ksinyuk@kernel.org, rodrigo.vivi@intel.com,
cedric.xing@intel.com, sameo@meta.com, drdeeglaze@gmail.com,
James.Bottomley@HansenPartnership.com, aik@amd.com,
jpiotrowski@nvidia.com, qinkun@google.com, pgonda@google.com,
jic23@kernel.org, alistair.francis@wdc.com, ankita@nvidia.com,
suzuki.poulose@arm.com, kas@kernel.org,
rick.p.edgecombe@intel.com, elena.reshetova@intel.com,
dave.hansen@linux.intel.com,
sathyanarayanan.kuppuswamy@linux.intel.com, peter.fang@intel.com
Subject: [PATCH RFC 05/20] ctlv: Prepare message input handling
Date: Thu, 15 Oct 2026 16:50:38 +0200 [thread overview]
Message-ID: <20261015145053.3664509-6-jiri@resnulli.us> (raw)
In-Reply-To: <20261015145053.3664509-1-jiri@resnulli.us>
From: Jiri Pirko <jiri@nvidia.com>
Add wire walking, request indexing and validated input accessors for
generated family readers.
Keep CONFIG_CTLV unwired until dispatch completes the module.
Assisted-by: Cursor: GPT-5.6 Sol
Assisted-by: Cursor: Claude Opus 5.5
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
MAINTAINERS | 1 +
drivers/ctlv/Kconfig | 12 ++
drivers/ctlv/Makefile | 4 +
drivers/ctlv/internal.h | 66 ++++++
drivers/ctlv/message.c | 403 ++++++++++++++++++++++++++++++++++++
drivers/ctlv/message.h | 92 ++++++++
include/linux/ctlv_family.h | 138 ++++++++++++
7 files changed, 716 insertions(+)
create mode 100644 drivers/ctlv/Kconfig
create mode 100644 drivers/ctlv/Makefile
create mode 100644 drivers/ctlv/internal.h
create mode 100644 drivers/ctlv/message.c
create mode 100644 drivers/ctlv/message.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 882836d85125..b308e3a5f0b6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7007,6 +7007,7 @@ L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/ctlv/
F: Documentation/userspace-api/ctlv.rst
+F: drivers/ctlv/
F: include/linux/ctlv*.h
F: include/uapi/linux/ctlv*.h
F: tools/ctlv/
diff --git a/drivers/ctlv/Kconfig b/drivers/ctlv/Kconfig
new file mode 100644
index 000000000000..aed3243c55fd
--- /dev/null
+++ b/drivers/ctlv/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig CTLV
+ tristate "CTLV character device TLV UAPI"
+ help
+ CTLV provides character device UAPIs described by a checked-in YAML
+ specification. It owns the character device lifetime, the strict TLV
+ message format, request validation, reply serialization, the large
+ payloads a request describes rather than carries, per-open event
+ queues and runtime schema introspection, so families only implement
+ semantics.
+
+ If you don't know what to do here, say N.
diff --git a/drivers/ctlv/Makefile b/drivers/ctlv/Makefile
new file mode 100644
index 000000000000..4f0fe41bc9e1
--- /dev/null
+++ b/drivers/ctlv/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CTLV) += ctlv.o
+
+ctlv-y += message.o
diff --git a/drivers/ctlv/internal.h b/drivers/ctlv/internal.h
new file mode 100644
index 000000000000..7d2b43857299
--- /dev/null
+++ b/drivers/ctlv/internal.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES
+ *
+ * CTLV core internals.
+ */
+#ifndef _CTLV_INTERNAL_H
+#define _CTLV_INTERNAL_H
+
+#include <linux/ctlv_family.h>
+#include <linux/types.h>
+
+#include <uapi/linux/ctlv.h>
+
+#include "message.h"
+
+#define CTLV_ERROR_MESSAGE_LEN 256
+
+/* An op that is always enabled has no per-device availability state. */
+#define CTLV_OP_STATE_NONE U32_MAX
+
+struct ctlv_op_entry {
+ const struct ctlv_op_schema *schema;
+ const struct ctlv_op *binding;
+ void *priv;
+ struct ctlv_op_sizes sizes;
+ u32 state_index;
+ bool absent;
+};
+
+struct ctlv_error {
+ u32 reason;
+ char message[CTLV_ERROR_MESSAGE_LEN];
+ u32 bad_attr_offset;
+ u32 nest_offset;
+ u64 missing_attr_id;
+ u64 expected_generation;
+ u64 current_generation;
+};
+
+struct ctlv_query_fragment {
+ u64 generation;
+ u64 cursor;
+ u64 reported;
+ u64 next_cursor;
+ bool progress;
+ bool answered;
+ int result;
+};
+
+struct ctlv_op_ctx {
+ const struct ctlv_family *family;
+ const struct ctlv_op_entry *op;
+
+ struct ctlv_msg request;
+ struct ctlv_cursor request_root;
+
+ struct ctlv_msg output;
+ struct ctlv_cursor output_root;
+ struct ctlv_cursor *deepest;
+
+ struct ctlv_query_fragment frag;
+ struct ctlv_error error;
+};
+
+#endif /* _CTLV_INTERNAL_H */
diff --git a/drivers/ctlv/message.c b/drivers/ctlv/message.c
new file mode 100644
index 000000000000..7e5742ba8f70
--- /dev/null
+++ b/drivers/ctlv/message.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Message walking, indexing, and generated-accessor primitives.
+ */
+#include <linux/align.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/ctlv.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <uapi/linux/ctlv.h>
+#include <uapi/linux/ctlv_gen.h>
+
+#include "internal.h"
+#include "message.h"
+
+enum ctlv_message_step ctlv_message_next(const struct ctlv_attr *nest,
+ const struct ctlv_attr **child)
+{
+ const u8 *end = (const u8 *)nest + nest->len;
+ const u8 *next;
+ size_t room;
+
+ if (nest->len < sizeof(*nest))
+ return CTLV_MESSAGE_STEP_BAD;
+ if (*child)
+ next = (const u8 *)*child + ALIGN((size_t)(*child)->len, CTLV_ALIGNTO);
+ else
+ next = (const u8 *)(nest + 1);
+ if (next >= end)
+ return next == end ? CTLV_MESSAGE_STEP_END : CTLV_MESSAGE_STEP_BAD;
+
+ room = end - next;
+ if (room < sizeof(**child))
+ return CTLV_MESSAGE_STEP_BAD;
+ *child = (const struct ctlv_attr *)next;
+ /*
+ * Length before alignment, so that aligning a length this nest could
+ * never hold is not what overflows.
+ */
+ if ((*child)->len < sizeof(**child) || (*child)->len > room)
+ return CTLV_MESSAGE_STEP_BAD;
+ if (ALIGN((size_t)(*child)->len, CTLV_ALIGNTO) > room)
+ return CTLV_MESSAGE_STEP_BAD;
+ return CTLV_MESSAGE_STEP_ATTR;
+}
+
+u32 ctlv_message_offset(const struct ctlv_msg *msg, const struct ctlv_attr *attr)
+{
+ return (const u8 *)attr - (const u8 *)msg->root;
+}
+
+void ctlv_message_root(struct ctlv_cursor *cursor, struct ctlv_msg *msg)
+{
+ *cursor = (struct ctlv_cursor){
+ .msg = msg,
+ .attr = msg->root,
+ .tree = msg->tree,
+ .index_nest_scope = CTLV_MESSAGE_INPUT_INDEX_NONE,
+ .index_scope = CTLV_MESSAGE_INPUT_INDEX_NONE,
+ };
+ if (msg->request && msg->index)
+ cursor->index_nest_scope = msg->index->nests[0].first_scope;
+}
+
+static bool ctlv_message_nest_index_ok(const struct ctlv_cursor *nest)
+{
+ const struct ctlv_message_input_index *index = nest->msg->index;
+ const struct ctlv_message_input_nest *indexed;
+ const struct ctlv_message_input_scope *scope;
+ const struct ctlv_attr *attr;
+
+ if (WARN_ON_ONCE(!nest->msg->request || !index ||
+ nest->index_nest_scope >= index->scopes_count))
+ return false;
+ scope = &index->scopes[nest->index_nest_scope];
+ if (WARN_ON_ONCE(scope->nest >= index->nests_count))
+ return false;
+ indexed = &index->nests[scope->nest];
+ attr = (const void *)((const u8 *)nest->msg->root + indexed->offset);
+ if (WARN_ON_ONCE(attr != nest->attr))
+ return false;
+ return !WARN_ON_ONCE(scope->tree != nest->tree || scope->node != nest->node);
+}
+
+static bool ctlv_message_input_scope_ok(const struct ctlv_message_input_index *index,
+ const struct ctlv_message_input_scope *scope)
+{
+ return !WARN_ON_ONCE(scope->first_slot > index->slots_count ||
+ scope->slots_count > index->slots_count - scope->first_slot);
+}
+
+static const struct ctlv_node_schema *ctlv_message_input_ref_node(struct ctlv_input_ref ref)
+{
+ const struct ctlv_node_schema *parent;
+ const struct ctlv_node_schema *node;
+
+ if (WARN_ON_ONCE(!ref.tree->node_schemas || ref.node >= ref.tree->node_schemas_count))
+ return NULL;
+ parent = &ref.tree->node_schemas[ref.node];
+ if (WARN_ON_ONCE(ref.slot >= parent->children_count ||
+ parent->first_child + ref.slot >= ref.tree->node_schemas_count))
+ return NULL;
+ node = &ref.tree->node_schemas[parent->first_child + ref.slot];
+ if (WARN_ON_ONCE(node->attr_id != ref.attr_id || node->kind != ref.kind))
+ return NULL;
+ return node;
+}
+
+static const struct ctlv_message_input_slot *
+ctlv_message_cursor_index(const struct ctlv_cursor *cursor)
+{
+ const struct ctlv_message_input_index *index;
+ const struct ctlv_message_input_scope *scope;
+ const struct ctlv_node_schema *parent;
+ const struct ctlv_node_schema *node;
+ const struct ctlv_message_input_slot *slot;
+ const struct ctlv_message_input_nest *nest;
+ u32 local_slot;
+ u32 offset;
+
+ if (WARN_ON_ONCE(!cursor->msg || !cursor->tree || !cursor->msg->request ||
+ !cursor->msg->index))
+ return NULL;
+ index = cursor->msg->index;
+ if (WARN_ON_ONCE(cursor->index_scope >= index->scopes_count))
+ return NULL;
+ scope = &index->scopes[cursor->index_scope];
+ if (!ctlv_message_input_scope_ok(index, scope))
+ return NULL;
+ parent = &scope->tree->node_schemas[scope->node];
+ if (WARN_ON_ONCE(scope->tree != cursor->tree || cursor->node < parent->first_child ||
+ cursor->node >= parent->first_child + scope->slots_count))
+ return NULL;
+ local_slot = cursor->node - parent->first_child;
+ node = &cursor->tree->node_schemas[cursor->node];
+ slot = &index->slots[scope->first_slot + local_slot];
+ if (!cursor->attr)
+ return slot;
+ if (WARN_ON_ONCE(cursor->attr->attr_id != node->attr_id))
+ return NULL;
+ if (WARN_ON_ONCE(scope->nest >= index->nests_count))
+ return NULL;
+ nest = &index->nests[scope->nest];
+ if (WARN_ON_ONCE(cursor->parent !=
+ (const void *)((const u8 *)cursor->msg->root + nest->offset)))
+ return NULL;
+ offset = ctlv_message_offset(cursor->msg, cursor->attr);
+ if (WARN_ON_ONCE(offset < slot->first_offset || offset >= slot->end_offset ||
+ offset + ALIGN((size_t)cursor->attr->len, CTLV_ALIGNTO) >
+ slot->end_offset))
+ return NULL;
+ if (node->kind == CTLV_SCHEMA_NEST) {
+ const struct ctlv_message_input_scope *nest_scope;
+
+ if (WARN_ON_ONCE(cursor->index_nest_scope >= index->scopes_count))
+ return NULL;
+ nest_scope = &index->scopes[cursor->index_nest_scope];
+ if (WARN_ON_ONCE(nest_scope->nest >= index->nests_count ||
+ index->nests[nest_scope->nest].offset != offset ||
+ nest_scope->tree != cursor->tree ||
+ nest_scope->node != cursor->node))
+ return NULL;
+ } else if (WARN_ON_ONCE(cursor->index_nest_scope != CTLV_MESSAGE_INPUT_INDEX_NONE)) {
+ return NULL;
+ }
+ return slot;
+}
+
+bool ctlv_message_input_rebind(struct ctlv_cursor *nest, const struct ctlv_schema_tree *tree,
+ u32 node)
+{
+ const struct ctlv_message_input_scope *bound_scope;
+ const struct ctlv_message_input_nest *indexed;
+ u32 i;
+
+ if (WARN_ON_ONCE(!nest->attr || !nest->msg || !nest->msg->index))
+ return false;
+ if (!ctlv_message_nest_index_ok(nest))
+ return false;
+ bound_scope = &nest->msg->index->scopes[nest->index_nest_scope];
+ indexed = &nest->msg->index->nests[bound_scope->nest];
+ for (i = 0; i < indexed->scopes_count; i++) {
+ u32 scope_index = indexed->first_scope + i;
+ const struct ctlv_message_input_scope *scope =
+ &nest->msg->index->scopes[scope_index];
+
+ if (scope->tree != tree || scope->node != node)
+ continue;
+ nest->tree = tree;
+ nest->node = node;
+ nest->index_nest_scope = scope_index;
+ return true;
+ }
+ return false;
+}
+
+struct ctlv_cursor *ctlv_input_attr(struct ctlv_cursor *cursor, const struct ctlv_cursor *nest,
+ struct ctlv_input_ref ref)
+{
+ const struct ctlv_message_input_index *index;
+ const struct ctlv_message_input_scope *scope;
+ const struct ctlv_message_input_slot *indexed;
+ const struct ctlv_node_schema *schema;
+
+ *cursor = (struct ctlv_cursor){
+ .index_nest_scope = CTLV_MESSAGE_INPUT_INDEX_NONE,
+ .index_scope = CTLV_MESSAGE_INPUT_INDEX_NONE,
+ };
+ if (WARN_ON_ONCE(!nest->attr || !nest->msg))
+ return cursor;
+ if (!ctlv_message_nest_index_ok(nest))
+ return cursor;
+ index = nest->msg->index;
+ if (WARN_ON_ONCE(nest->index_nest_scope >= index->scopes_count))
+ return cursor;
+ scope = &index->scopes[nest->index_nest_scope];
+ if (!ctlv_message_input_scope_ok(index, scope))
+ return cursor;
+ if (WARN_ON_ONCE(scope->tree != ref.tree || scope->node != ref.node ||
+ nest->tree != ref.tree || nest->node != ref.node ||
+ ref.slot >= scope->slots_count))
+ return cursor;
+ schema = ctlv_message_input_ref_node(ref);
+ if (!schema)
+ return cursor;
+ indexed = &index->slots[scope->first_slot + ref.slot];
+ *cursor = (struct ctlv_cursor){
+ .msg = nest->msg,
+ .parent = nest->attr,
+ .attr = indexed->count ? (const void *)((const u8 *)nest->msg->root +
+ indexed->first_offset) :
+ NULL,
+ .tree = ref.tree,
+ .node = ref.tree->node_schemas[ref.node].first_child + ref.slot,
+ .depth = nest->depth + 1,
+ .index_nest_scope = CTLV_MESSAGE_INPUT_INDEX_NONE,
+ .index_scope = nest->index_nest_scope,
+ };
+ if (indexed->count && schema->kind == CTLV_SCHEMA_NEST) {
+ if (WARN_ON_ONCE(indexed->first_nest >= index->nests_count))
+ return cursor;
+ cursor->index_nest_scope = index->nests[indexed->first_nest].first_scope;
+ }
+ return cursor;
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_input_attr, "CTLV");
+
+bool ctlv_input_cursor(const struct ctlv_cursor *cursor, struct ctlv_input_ref ref)
+{
+ const struct ctlv_message_input_scope *scope;
+ const struct ctlv_node_schema *schema;
+
+ if (!ctlv_message_cursor_index(cursor))
+ return false;
+ schema = ctlv_message_input_ref_node(ref);
+ if (!schema)
+ return false;
+ scope = &cursor->msg->index->scopes[cursor->index_scope];
+ return !WARN_ON_ONCE(scope->tree != ref.tree ||
+ scope->node != ref.node ||
+ cursor->tree != ref.tree ||
+ cursor->node !=
+ ref.tree->node_schemas[ref.node].first_child +
+ ref.slot);
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_input_cursor, "CTLV");
+
+void ctlv_attr_next(struct ctlv_cursor *cursor)
+{
+ const struct ctlv_message_input_slot *slot;
+ const struct ctlv_message_input_scope *scope;
+ const struct ctlv_node_schema *node;
+ const struct ctlv_attr *next;
+ u32 next_nest;
+ u32 next_offset;
+
+ slot = ctlv_message_cursor_index(cursor);
+ if (!slot || WARN_ON_ONCE(!cursor->attr))
+ return;
+ next = (const void *)((const u8 *)cursor->attr +
+ ALIGN((size_t)cursor->attr->len, CTLV_ALIGNTO));
+ next_offset = ctlv_message_offset(cursor->msg, next);
+ if (next_offset == slot->end_offset) {
+ cursor->attr = NULL;
+ cursor->index_nest_scope = CTLV_MESSAGE_INPUT_INDEX_NONE;
+ return;
+ }
+ if (WARN_ON_ONCE(next_offset > slot->end_offset))
+ return;
+ cursor->attr = next;
+ node = &cursor->tree->node_schemas[cursor->node];
+ if (node->kind == CTLV_SCHEMA_NEST) {
+ if (WARN_ON_ONCE(cursor->index_nest_scope >= cursor->msg->index->scopes_count))
+ return;
+ scope = &cursor->msg->index->scopes[cursor->index_nest_scope];
+ if (WARN_ON_ONCE(scope->nest >= cursor->msg->index->nests_count))
+ return;
+ next_nest = cursor->msg->index->nests[scope->nest].next;
+ if (WARN_ON_ONCE(next_nest == CTLV_MESSAGE_INPUT_INDEX_NONE ||
+ next_nest >= cursor->msg->index->nests_count))
+ return;
+ cursor->index_nest_scope = cursor->msg->index->nests[next_nest].first_scope;
+ }
+ WARN_ON_ONCE(!ctlv_message_cursor_index(cursor));
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_next, "CTLV");
+
+bool ctlv_attr_ok(const struct ctlv_cursor *cursor)
+{
+ return cursor && cursor->attr && ctlv_message_cursor_index(cursor);
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_ok, "CTLV");
+
+/* The payload, checked against the kinds a reader of this C type accepts. */
+static const void *ctlv_message_cursor_payload(const struct ctlv_cursor *cursor, u32 kinds,
+ u32 *len)
+{
+ const struct ctlv_node_schema *node;
+
+ if (!ctlv_message_cursor_index(cursor) || WARN_ON_ONCE(!cursor->attr))
+ return NULL;
+ node = &cursor->tree->node_schemas[cursor->node];
+ if (WARN_ON_ONCE(!(kinds & BIT(node->kind))))
+ return NULL;
+ *len = ctlv_message_attr_payload_len(cursor->attr);
+ return ctlv_message_attr_payload(cursor->attr);
+}
+
+/* A length disagreeing with a validated kind is this core's mistake. */
+static const void *ctlv_message_cursor_fixed(const struct ctlv_cursor *cursor, u32 kinds,
+ u32 expected)
+{
+ const void *payload;
+ u32 len;
+
+ payload = ctlv_message_cursor_payload(cursor, kinds, &len);
+ if (!payload || WARN_ON_ONCE(len != expected))
+ return NULL;
+ return payload;
+}
+
+u64 ctlv_attr_u64(const struct ctlv_cursor *cursor)
+{
+ const u64 *value = ctlv_message_cursor_fixed(cursor,
+ BIT(CTLV_SCHEMA_UINT) | BIT(CTLV_SCHEMA_ENUM) |
+ BIT(CTLV_SCHEMA_FLAGS),
+ sizeof(u64));
+
+ return value ? *value : 0;
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_u64, "CTLV");
+
+s64 ctlv_attr_s64(const struct ctlv_cursor *cursor)
+{
+ const s64 *value = ctlv_message_cursor_fixed(cursor, BIT(CTLV_SCHEMA_SINT), sizeof(s64));
+
+ return value ? *value : 0;
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_s64, "CTLV");
+
+bool ctlv_attr_bool(const struct ctlv_cursor *cursor)
+{
+ const u8 *value = ctlv_message_cursor_fixed(cursor, BIT(CTLV_SCHEMA_BOOL), sizeof(u8));
+
+ return value ? *value : false;
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_bool, "CTLV");
+
+const char *ctlv_attr_string(const struct ctlv_cursor *cursor)
+{
+ const char *value;
+ u32 len;
+
+ value = ctlv_message_cursor_payload(cursor, BIT(CTLV_SCHEMA_STRING), &len);
+ if (!value || WARN_ON_ONCE(!len || value[len - 1]))
+ return NULL;
+ return value;
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_string, "CTLV");
+
+const void *ctlv_attr_binary(const struct ctlv_cursor *cursor, u32 *len)
+{
+ const void *value;
+
+ *len = 0;
+ value = ctlv_message_cursor_payload(cursor, BIT(CTLV_SCHEMA_BINARY), len);
+ if (!value)
+ *len = 0;
+ return value;
+}
+EXPORT_SYMBOL_NS_GPL(ctlv_attr_binary, "CTLV");
+
+/*
+ * The room is measured before anything is written, which is what leaves a
+ * refused attr's message exactly as it was, padding included.
+ */
diff --git a/drivers/ctlv/message.h b/drivers/ctlv/message.h
new file mode 100644
index 000000000000..efd781af9b37
--- /dev/null
+++ b/drivers/ctlv/message.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Private message representation and nest walking.
+ */
+#ifndef _CTLV_MESSAGE_H
+#define _CTLV_MESSAGE_H
+
+#include <linux/ctlv_family.h>
+#include <linux/types.h>
+
+#include <uapi/linux/ctlv.h>
+
+#define CTLV_MESSAGE_INPUT_INDEX_NONE U32_MAX
+
+/* Attr identity remains in the generated schema, not in this request index. */
+struct ctlv_message_input_slot {
+ u32 first_offset;
+ u32 end_offset;
+ u32 count;
+ u32 excess_offset;
+ u32 first_nest;
+ u32 last_nest;
+};
+
+struct ctlv_message_input_scope {
+ const struct ctlv_schema_tree *tree;
+ u32 nest;
+ u32 node;
+ u32 first_slot;
+ u32 slots_count;
+ bool optional;
+};
+
+struct ctlv_message_input_nest {
+ u32 offset;
+ u32 first_scope;
+ u32 scopes_count;
+ u32 next;
+};
+
+struct ctlv_message_input_index {
+ struct ctlv_message_input_nest *nests;
+ struct ctlv_message_input_scope *scopes;
+ struct ctlv_message_input_slot *slots;
+ u32 nests_count;
+ u32 nests_capacity;
+ u32 scopes_count;
+ u32 scopes_capacity;
+ u32 slots_count;
+ u32 slots_capacity;
+};
+
+struct ctlv_msg {
+ struct ctlv_attr *root;
+ size_t len;
+ const struct ctlv_schema_tree *tree;
+ const struct ctlv_family *family;
+ struct ctlv_op_ctx *ctx;
+ struct ctlv_message_input_index *index;
+ bool request;
+ int poison;
+};
+
+enum ctlv_message_step {
+ CTLV_MESSAGE_STEP_ATTR,
+ CTLV_MESSAGE_STEP_END,
+ CTLV_MESSAGE_STEP_BAD,
+};
+
+enum ctlv_message_step ctlv_message_next(const struct ctlv_attr *nest,
+ const struct ctlv_attr **child);
+u32 ctlv_message_offset(const struct ctlv_msg *msg,
+ const struct ctlv_attr *attr);
+void ctlv_message_root(struct ctlv_cursor *cursor, struct ctlv_msg *msg);
+void ctlv_message_index_free(struct ctlv_msg *msg);
+bool ctlv_message_input_rebind(struct ctlv_cursor *nest,
+ const struct ctlv_schema_tree *tree, u32 node);
+
+static inline u32 ctlv_message_attr_payload_len(const struct ctlv_attr *attr)
+{
+ return attr->len - sizeof(*attr);
+}
+
+static inline const void *
+ctlv_message_attr_payload(const struct ctlv_attr *attr)
+{
+ return attr + 1;
+}
+
+#endif /* _CTLV_MESSAGE_H */
diff --git a/include/linux/ctlv_family.h b/include/linux/ctlv_family.h
index 1ae4d3a536f7..eae690b9c1d5 100644
--- a/include/linux/ctlv_family.h
+++ b/include/linux/ctlv_family.h
@@ -313,4 +313,142 @@ struct ctlv_family {
extern const struct ctlv_family ctlv_family;
+struct ctlv_cursor *
+ctlv_input_attr(struct ctlv_cursor *cursor, const struct ctlv_cursor *nest,
+ struct ctlv_input_ref ref);
+bool ctlv_input_cursor(const struct ctlv_cursor *cursor,
+ struct ctlv_input_ref ref);
+void ctlv_attr_next(struct ctlv_cursor *cursor);
+bool ctlv_attr_ok(const struct ctlv_cursor *cursor);
+u64 ctlv_attr_u64(const struct ctlv_cursor *cursor);
+s64 ctlv_attr_s64(const struct ctlv_cursor *cursor);
+bool ctlv_attr_bool(const struct ctlv_cursor *cursor);
+const char *ctlv_attr_string(const struct ctlv_cursor *cursor);
+const void *ctlv_attr_binary(const struct ctlv_cursor *cursor, u32 *len);
+
+#define CTLV_IN_REF(_name, _tree, _attr_id, _node, _slot, _kind) \
+static inline struct ctlv_input_ref _name##_ref(void) \
+{ \
+ return (struct ctlv_input_ref){ \
+ .tree = (_tree), \
+ .attr_id = (_attr_id), \
+ .node = (_node), \
+ .slot = (_slot), \
+ .kind = (_kind), \
+ }; \
+}
+
+#define CTLV_IN_FOR_EACH(_name, _attr, _nest) \
+ for (struct ctlv_cursor *__ctlv_attr = \
+ ctlv_input_attr(_attr, _nest, _name##_ref()); \
+ ctlv_attr_ok(__ctlv_attr); \
+ ctlv_attr_next(__ctlv_attr))
+
+#define CTLV_IN_HAS(_name) \
+static inline bool _name##_present(const struct ctlv_cursor *nest) \
+{ \
+ struct ctlv_cursor attr; \
+ \
+ ctlv_input_attr(&attr, nest, _name##_ref()); \
+ return ctlv_attr_ok(&attr); \
+}
+
+#define CTLV_IN_U64(_name) \
+static inline u64 _name##_get(const struct ctlv_cursor *nest) \
+{ \
+ struct ctlv_cursor attr; \
+ \
+ ctlv_input_attr(&attr, nest, _name##_ref()); \
+ return ctlv_attr_u64(&attr); \
+}
+
+#define CTLV_IN_S64(_name) \
+static inline s64 _name##_get(const struct ctlv_cursor *nest) \
+{ \
+ struct ctlv_cursor attr; \
+ \
+ ctlv_input_attr(&attr, nest, _name##_ref()); \
+ return ctlv_attr_s64(&attr); \
+}
+
+#define CTLV_IN_BOOL(_name) \
+static inline bool _name##_get(const struct ctlv_cursor *nest) \
+{ \
+ struct ctlv_cursor attr; \
+ \
+ ctlv_input_attr(&attr, nest, _name##_ref()); \
+ return ctlv_attr_bool(&attr); \
+}
+
+#define CTLV_IN_STRING(_name) \
+static inline const char * \
+_name##_get(const struct ctlv_cursor *nest) \
+{ \
+ struct ctlv_cursor attr; \
+ \
+ ctlv_input_attr(&attr, nest, _name##_ref()); \
+ return ctlv_attr_string(&attr); \
+}
+
+#define CTLV_IN_BINARY(_name) \
+static inline const void * \
+_name##_get(const struct ctlv_cursor *nest, u32 *len) \
+{ \
+ struct ctlv_cursor attr; \
+ \
+ ctlv_input_attr(&attr, nest, _name##_ref()); \
+ return ctlv_attr_binary(&attr, len); \
+}
+
+#define CTLV_IN_NEST(_name) \
+static inline void _name##_get(struct ctlv_cursor *nested, \
+ const struct ctlv_cursor *nest) \
+{ \
+ ctlv_input_attr(nested, nest, _name##_ref()); \
+}
+
+#define CTLV_IN_EACH_U64(_name) \
+static inline u64 _name##_get(const struct ctlv_cursor *attr) \
+{ \
+ if (!ctlv_input_cursor(attr, _name##_ref())) \
+ return 0; \
+ return ctlv_attr_u64(attr); \
+}
+
+#define CTLV_IN_EACH_S64(_name) \
+static inline s64 _name##_get(const struct ctlv_cursor *attr) \
+{ \
+ if (!ctlv_input_cursor(attr, _name##_ref())) \
+ return 0; \
+ return ctlv_attr_s64(attr); \
+}
+
+#define CTLV_IN_EACH_BOOL(_name) \
+static inline bool _name##_get(const struct ctlv_cursor *attr) \
+{ \
+ if (!ctlv_input_cursor(attr, _name##_ref())) \
+ return false; \
+ return ctlv_attr_bool(attr); \
+}
+
+#define CTLV_IN_EACH_STRING(_name) \
+static inline const char * \
+_name##_get(const struct ctlv_cursor *attr) \
+{ \
+ if (!ctlv_input_cursor(attr, _name##_ref())) \
+ return NULL; \
+ return ctlv_attr_string(attr); \
+}
+
+#define CTLV_IN_EACH_BINARY(_name) \
+static inline const void * \
+_name##_get(const struct ctlv_cursor *attr, u32 *len) \
+{ \
+ if (!ctlv_input_cursor(attr, _name##_ref())) { \
+ *len = 0; \
+ return NULL; \
+ } \
+ return ctlv_attr_binary(attr, len); \
+}
+
#endif /* _LINUX_CTLV_FAMILY_H */
--
2.56.0
next prev parent reply other threads:[~2026-10-15 14:51 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 14:50 [PATCH RFC 00/20] attestation: Add a common subsystem and TDX support Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 01/20] ctlv: Add the schema language and generator Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 02/20] ctlv: Add the UAPI wire format and family API Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 03/20] ctlv: Add the root family specification and generated files Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 04/20] Documentation: ctlv: Add userspace ABI Jiri Pirko
2026-10-15 14:50 ` Jiri Pirko [this message]
2026-10-15 14:50 ` [PATCH RFC 06/20] ctlv: Prepare blob descriptor validation Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 07/20] ctlv: Prepare schema validation Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 08/20] ctlv: Prepare message output handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 09/20] ctlv: Prepare VA blob handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 10/20] ctlv: Prepare event queue support Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 11/20] ctlv: Prepare runtime introspection Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 12/20] ctlv: Add device and operation dispatch Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 13/20] Documentation: ctlv: Describe how to write a family Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 14/20] attestation: Add the base family specification and generated files Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 15/20] attestation: Prepare provider and instance core Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 16/20] attestation: Prepare measurement register journal Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 17/20] attestation: Add CTLV operation handling Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 18/20] tdx-guest: Expose the native attestation service API Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 19/20] attestation: Add the TDX family specification and generated files Jiri Pirko
2026-10-15 14:50 ` [PATCH RFC 20/20] attestation: Add the TDX family implementation Jiri Pirko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261015145053.3664509-6-jiri@resnulli.us \
--to=jiri@resnulli.us \
--cc=James.Bottomley@HansenPartnership.com \
--cc=aik@amd.com \
--cc=alistair.francis@wdc.com \
--cc=ankita@nvidia.com \
--cc=cedric.xing@intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=david.quigley@intel.com \
--cc=drdeeglaze@gmail.com \
--cc=elena.reshetova@intel.com \
--cc=jgg@ziepe.ca \
--cc=jic23@kernel.org \
--cc=jpiotrowski@nvidia.com \
--cc=kas@kernel.org \
--cc=ksinyuk@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-api@vger.kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=peter.fang@intel.com \
--cc=pgonda@google.com \
--cc=qinkun@google.com \
--cc=rick.p.edgecombe@intel.com \
--cc=rodrigo.vivi@intel.com \
--cc=sameo@meta.com \
--cc=sathyanarayanan.kuppuswamy@linux.intel.com \
--cc=suzuki.poulose@arm.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox