Linux userland API discussions
 help / color / mirror / Atom feed
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


  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