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,
	jarkko@kernel.org, zohar@linux.ibm.com, yeoreum.yun@arm.com,
	aneesh.kumar@kernel.org, dave.jiang@intel.com,
	nik.borisov@suse.com, sergii.dmytruk@3mdeb.com
Subject: [PATCH RFC v2 03/15] ctlv: Add the root family specification and generated files
Date: Fri, 13 Nov 2026 16:55:02 +0100	[thread overview]
Message-ID: <20261113155515.2510194-4-jiri@resnulli.us> (raw)
In-Reply-To: <20261113155515.2510194-1-jiri@resnulli.us>

From: Jiri Pirko <jiri@nvidia.com>

Define the root family used by every CTLV device. Add its ABI snapshot,
registry and manifest entries, and generated outputs.

The generated kernel descriptor is preparatory and is linked after its
handlers arrive with operation dispatch.

Assisted-by: Cursor: GPT-5.6 Sol
Assisted-by: Cursor: Claude Opus 5.5
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
RFC->RFCv2:
- reported the error information length of actions and queries
- dropped the root family's priv and its hooks
- dropped the event ops, the events and the sets, groups and attrs only
  they use
- warned against adding a family before agreeing its position
- wrote the spec as op trees and records
- added required-length to the error information
- said node instead of placement
- said record instead of nest, the root being one
- numbered every enum member
- moved the generated UAPI headers to include/uapi/ctlv/
- documented every item of the spec
- put each field's doc above its generated helpers
- renamed families-dump and ops-dump to family-get and op-get
- put ctlv_gen.c in lib/ctlv/
- said message instead of wire
---
 Documentation/ctlv/abi/ctlv.yaml              | 111 +++++++
 Documentation/ctlv/abi/families.registry.yaml |   7 +
 Documentation/ctlv/families.yaml              |  17 ++
 Documentation/ctlv/generated-files.yaml       |  17 ++
 Documentation/ctlv/specs/ctlv.yaml            | 211 +++++++++++++
 include/linux/ctlv_gen.h                      | 154 ++++++++++
 include/uapi/ctlv/ctlv_families_gen.h         |  17 ++
 include/uapi/ctlv/ctlv_gen.h                  | 278 ++++++++++++++++++
 lib/ctlv/ctlv_gen.c                           | 161 ++++++++++
 9 files changed, 973 insertions(+)
 create mode 100644 Documentation/ctlv/abi/ctlv.yaml
 create mode 100644 Documentation/ctlv/abi/families.registry.yaml
 create mode 100644 Documentation/ctlv/families.yaml
 create mode 100644 Documentation/ctlv/generated-files.yaml
 create mode 100644 Documentation/ctlv/specs/ctlv.yaml
 create mode 100644 include/linux/ctlv_gen.h
 create mode 100644 include/uapi/ctlv/ctlv_families_gen.h
 create mode 100644 include/uapi/ctlv/ctlv_gen.h
 create mode 100644 lib/ctlv/ctlv_gen.c

diff --git a/Documentation/ctlv/abi/ctlv.yaml b/Documentation/ctlv/abi/ctlv.yaml
new file mode 100644
index 000000000000..dc26ba575a68
--- /dev/null
+++ b/Documentation/ctlv/abi/ctlv.yaml
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+%YAML 1.2
+---
+# Checked-in ABI snapshot of the ctlv family, from
+# Documentation/ctlv/specs/ctlv.yaml, written by
+# "make -C tools/ctlv regen".
+#
+# Updating the file is how a UAPI change gets reviewed; an update that
+# removes or renumbers anything, narrows a bound, or makes a member required
+# is an ABI break, whatever the file says afterwards.
+family: {name: ctlv, id: 0}
+enums:
+  op-type: {action: 1, query: 2}
+  op-availability: {fixed: 1, dynamic: 2}
+  blob-type: {va: 1}
+  blob-direction: {input: 1, output: 2, inout: 3}
+  validation-reason: {malformed-message: 1, unknown-type: 2, unknown-flag: 3,
+                      record-too-deep: 4, missing-field: 5,
+                      too-many-occurrences: 6, value-out-of-range: 7,
+                      bad-length: 8, bad-string: 9, bad-blob-descriptor: 10,
+                      stale-generation: 11, noncontiguous-type: 12}
+  op-fd-access: {read: 1, read-write: 2}
+records:
+  blob:
+    id: 0x00010000
+    fields:
+      type: {number: 1, enum: blob-type}
+      address: {number: 2, type: uint, optional: true}
+      fd: {number: 3, type: uint, max: s32-max, optional: true}
+      offset: {number: 4, type: uint, optional: true}
+      length: {number: 5, type: uint, min: 1}
+  error-info:
+    id: 0x00020000
+    fields:
+      reason: {number: 1, enum: validation-reason, optional: true}
+      message: {number: 2, type: string, max-length: 255, optional: true}
+      bad-tlv-offset: {number: 3, type: uint, optional: true}
+      record-offset: {number: 4, type: uint, optional: true}
+      missing-type: {number: 5, type: uint, optional: true}
+      expected-generation: {number: 6, type: uint, optional: true}
+      current-generation: {number: 7, type: uint, optional: true}
+      required-length: {number: 8, type: uint, optional: true}
+  schema-node:
+    id: 0x00030000
+    fields:
+      type: {number: 1, type: uint}
+      kind: {number: 2, type: uint}
+      max-occurrences: {number: 3, type: uint}
+      min: {number: 4, type: uint, optional: true}
+      max: {number: 5, type: uint, optional: true}
+      min-length: {number: 6, type: uint, optional: true}
+      max-length: {number: 7, type: uint, optional: true}
+      flags-mask: {number: 8, type: uint, optional: true}
+      unit: {number: 9, type: uint, optional: true}
+      scale: {number: 10, type: sint, optional: true}
+      blob-direction: {number: 11, enum: blob-direction, optional: true}
+      blob-backings: {number: 12, type: uint, optional: true}
+      blob-max-length: {number: 13, type: uint, optional: true}
+      blob-alignment: {number: 14, type: uint, optional: true}
+      reenters: {number: 15, type: uint, min: 1, optional: true}
+      fields: {number: 16, record: schema-node, max-occurrences: unlimited,
+               optional: true}
+      optional-fields: {number: 17, record: schema-node,
+                        max-occurrences: unlimited, optional: true}
+  family-get request:
+    id: 0x80010000
+  family-get reply:
+    id: 0xc0010000
+    fields:
+      families: {number: 1, record: family-get families,
+                 max-occurrences: unlimited}
+  family-get families:
+    id: 0xc0011000
+    fields:
+      id: {number: 1, type: uint}
+      name: {number: 2, type: string, max-length: 63}
+  op-get request:
+    id: 0x80020000
+  op-get reply:
+    id: 0xc0020000
+    fields:
+      ops: {number: 1, record: op-get ops, max-occurrences: unlimited}
+  op-get ops:
+    id: 0xc0021000
+    fields:
+      id: {number: 1, type: uint, min: 1}
+      type: {number: 2, enum: op-type}
+      availability: {number: 3, enum: op-availability, optional: true}
+      disabled: {number: 4, type: bool, optional: true}
+      request-max-len: {number: 5, type: uint, optional: true}
+      action-reply-max-len: {number: 6, type: uint, optional: true}
+      query-reply-min-len: {number: 7, type: uint, optional: true}
+      query-reply-suggested-len: {number: 8, type: uint, optional: true}
+      fd-access: {number: 9, enum: op-fd-access, optional: true}
+      error-info-max-len: {number: 10, type: uint, optional: true}
+  op-schema-get request:
+    id: 0x80030000
+    fields:
+      id: {number: 1, type: uint, min: 1}
+  op-schema-get reply:
+    id: 0xc0030000
+    fields:
+      request: {number: 1, record: schema-node, optional: true}
+      reply: {number: 2, record: schema-node, optional: true}
+ops:
+  family-get: {id: 1, type: query, fd-access: read, request: family-get request,
+               reply: family-get reply}
+  op-get: {id: 2, type: query, fd-access: read, request: op-get request,
+           reply: op-get reply}
+  op-schema-get: {id: 3, type: query, fd-access: read,
+                  request: op-schema-get request, reply: op-schema-get reply}
diff --git a/Documentation/ctlv/abi/families.registry.yaml b/Documentation/ctlv/abi/families.registry.yaml
new file mode 100644
index 000000000000..5845e7457771
--- /dev/null
+++ b/Documentation/ctlv/abi/families.registry.yaml
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+%YAML 1.2
+---
+# Checked-in ABI baseline of the ordered CTLV family registry.
+# Written by "make -C tools/ctlv regen".
+families:
+  - ctlv
diff --git a/Documentation/ctlv/families.yaml b/Documentation/ctlv/families.yaml
new file mode 100644
index 000000000000..4d219d7af74a
--- /dev/null
+++ b/Documentation/ctlv/families.yaml
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+%YAML 1.2
+---
+# Do not add a family without agreeing its position with the CTLV
+# maintainers (CTLV SUBSYSTEM in MAINTAINERS) first. The position is the
+# family ID, so families added in parallel trees end up with the same ID.
+#
+# Append-only registry of CTLV family names, indexed by family ID: the entry at
+# index N has family ID N, up to 65535. The root family owns ID 0 and heads the
+# list, and every other entry comes after the entry of the family it inherits.
+# An entry may be marked deprecated, but never reordered, removed, renamed or
+# reused, its position being its family ID. Every entry must have a
+# specification in specs/<name>.yaml, and is added by the change that adds the
+# implementation. Fixtures under tools/ctlv/tests/ are not registered.
+
+families:
+  - ctlv
diff --git a/Documentation/ctlv/generated-files.yaml b/Documentation/ctlv/generated-files.yaml
new file mode 100644
index 000000000000..5488ddf40e17
--- /dev/null
+++ b/Documentation/ctlv/generated-files.yaml
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+%YAML 1.2
+---
+# Generated files for registered families.
+version: 1
+registry:
+  source: Documentation/ctlv/families.yaml
+  outputs:
+    uapi-header: include/uapi/ctlv/ctlv_families_gen.h
+
+families:
+  - name: ctlv
+    source: Documentation/ctlv/specs/ctlv.yaml
+    outputs:
+      uapi-header: include/uapi/ctlv/ctlv_gen.h
+      kernel-header: include/linux/ctlv_gen.h
+      kernel-source: lib/ctlv/ctlv_gen.c
diff --git a/Documentation/ctlv/specs/ctlv.yaml b/Documentation/ctlv/specs/ctlv.yaml
new file mode 100644
index 000000000000..a7108f59c267
--- /dev/null
+++ b/Documentation/ctlv/specs/ctlv.yaml
@@ -0,0 +1,211 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+%YAML 1.2
+---
+# The root CTLV family, family ID 0 and the first entry in families.yaml. It
+# inherits nothing, and every other family name begins with "ctlv-". Every
+# CTLV device answers these ops in addition to its own family's. Every blob
+# field of every family carries the blob record, and every failure may write
+# error-info. Nothing here may be reordered or removed.
+family: ctlv
+
+enums:
+  # What an op is, which decides the messages it has.
+  op-type:
+    # Executed and acknowledged.
+    - action
+    # Executed and answered, in as many fragments as the answer needs.
+    - query
+  # A permission, not a state: what a device currently allows is disabled.
+  op-availability:
+    # A device that publishes the op always answers it.
+    - fixed
+    # A device may disable and enable the op at any time.
+    - dynamic
+  # What holds the bytes of a blob.
+  blob-type:
+    # Anonymous memory of the calling process, at address.
+    - va
+  # Which way the bytes of a blob go.
+  blob-direction:
+    # From the caller to the device, which only reads the blob.
+    - input
+    # From the device to the caller.
+    - output
+    # Both ways: the device reads the blob and writes it.
+    - inout
+  # The first rule a refused message broke.
+  validation-reason:
+    # The TLVs do not frame: the root is not one, a length runs past its
+    # record, or a TLV is a record where its schema has a scalar or the
+    # reverse.
+    - malformed-message
+    # A TLV of a type its record does not place there, not marked
+    # IGNORE_IF_UNKNOWN or of a type the schema places elsewhere.
+    - unknown-type
+    # A header flag the message may not carry, or a flags payload with a bit
+    # its field does not declare.
+    - unknown-flag
+    # Records nested deeper than the core follows.
+    - record-too-deep
+    # A required field is absent, and missing-type says which.
+    - missing-field
+    # A field occurs more often than its schema allows.
+    - too-many-occurrences
+    # An integer, an enum value or a bool outside what its field takes.
+    - value-out-of-range
+    # A payload of the wrong length for its kind, or outside its field's
+    # length bounds.
+    - bad-length
+    # A string payload that is not exactly one NUL-terminated string.
+    - bad-string
+    # A blob descriptor its field does not take, or one whose memory could
+    # not be acquired or is too short for what the op writes there.
+    - bad-blob-descriptor
+    # A query continuation of a generation the query no longer has.
+    - stale-generation
+    # The TLVs of one type are not contiguous in their record.
+    - noncontiguous-type
+  # What an op asks of the file description that executes it.
+  op-fd-access:
+    # Read access, which an observation fd has as well as a full-control one.
+    - read
+    # Read-write access, which only a full-control fd has.
+    - read-write
+
+records:
+  # va takes address; fd and offset are reserved for later fd-backed
+  # types.
+  blob:
+    # What holds the bytes, which says which of the fields below are present.
+    type: blob-type
+    # Where a va blob starts in the calling process.
+    address?: uint
+    # Reserved for a later fd-backed type.
+    fd?: uint(..s32-max)
+    # Reserved for a later fd-backed type.
+    offset?: uint
+    # The length of the blob in bytes, at most what its field takes.
+    length: uint(1..)
+
+  # Only what describes the first failure is present.
+  error-info:
+    # The rule the message broke.
+    reason?: validation-reason
+    # What went wrong, for a person to read.
+    message?: string(..255)
+    # The offset of the offending TLV from the start of the message, or of
+    # its record when what is wrong is something the record lacks.
+    bad-tlv-offset?: uint
+    # The offset of the record holding it, from the start of the message.
+    record-offset?: uint
+    # The type of the required field that is absent.
+    missing-type?: uint
+    # The generation the query continuation carried.
+    expected-generation?: uint
+    # The generation the query has now.
+    current-generation?: uint
+    # How long the output blob at bad-tlv-offset has to be for the op.
+    required-length?: uint
+
+  # One node of an op's request or reply tree. Only the members the node's
+  # schema kind has are present. Nothing here is a name: a caller matches the
+  # family chain from family-get to find the generated UAPI.
+  schema-node:
+    # Zero in a message root node, as in the root TLV.
+    type: uint
+    # enum ctlv_schema_kind.
+    kind: uint
+    # Zero where the node may repeat as often as the message has room.
+    max-occurrences: uint
+    # The inclusive lower bound of an integer or enum payload, a raw 64-bit
+    # value read according to kind. An enum's values are numbered without
+    # gaps.
+    min?: uint
+    # The inclusive upper bound, read the same way.
+    max?: uint
+    # The shortest payload of a string or binary field, a string's terminator
+    # excluded.
+    min-length?: uint
+    # The longest payload, likewise.
+    max-length?: uint
+    # The bits a flags payload may carry.
+    flags-mask?: uint
+    # enum ctlv_unit, absent when the payload is dimensionless.
+    unit?: uint
+    # The decimal exponent of the unit, a payload being value * 10^scale of
+    # it, present only with a unit.
+    scale?: sint
+    # Which way a blob field's bytes go.
+    blob-direction?: blob-direction
+    # Mask of BIT(enum ctlv_blob_type): the backings this node takes.
+    blob-backings?: uint
+    # Maximum external payload length, unrelated to the inline message limit.
+    blob-max-length?: uint
+    # The power of two a blob's address or offset is a multiple of.
+    blob-alignment?: uint
+    # Levels to climb to the node whose members are this one's as well, one
+    # meaning the node this one is a member of. A node that carries it lists
+    # no members itself.
+    reenters?: uint(1..)
+    # The required nodes one level in.
+    fields[]?: record schema-node
+    # The optional nodes one level in.
+    optional-fields[]?: record schema-node
+
+ops:
+  # The families this device answers, this one first and the device family
+  # last. The only names any reply of this family carries.
+  family-get:
+    type: query
+    fd-access: read
+    reply:
+      # One entry per family of the chain.
+      families[]:
+        # The family ID, which bits 63..48 of the family's types carry.
+        id: uint
+        # The registry name, also what a uevent carries. Bounded here, this
+        # being the only place a message carries a family name.
+        name: string(..63)
+
+  # One entry per effective op. Length fields that do not apply to an
+  # entry's op type are absent.
+  op-get:
+    type: query
+    fd-access: read
+    reply:
+      # One entry per op the device publishes.
+      ops[]:
+        # The op ID, which CTLV_IOC_OP_EXEC carries.
+        id: uint(1..)
+        # Action or query.
+        type: op-type
+        # Whether the device may disable and enable the op.
+        availability?: op-availability
+        # Whether the device has the op disabled right now.
+        disabled?: bool
+        # The longest request composed from the schema.
+        request-max-len?: uint
+        # The reply buffer an action needs, zero for one that answers nothing.
+        action-reply-max-len?: uint
+        # The shortest reply buffer a query takes.
+        query-reply-min-len?: uint
+        # The reply buffer length the family suggests for a query.
+        query-reply-suggested-len?: uint
+        # The access a file description needs to execute the op.
+        fd-access?: op-fd-access
+        # The longest error information the op can return.
+        error-info-max-len?: uint
+
+  # The request and reply ABI of one op, whether or not it can be called right
+  # now. A removed op answers as one that was never declared.
+  op-schema-get:
+    type: query
+    fd-access: read
+    request:
+      # The ID of the op to describe.
+      id: uint(1..)
+    reply:
+      # The root of the op's request tree, absent when it has no request.
+      request?: record schema-node
+      # The root of its reply tree, absent when it has no reply.
+      reply?: record schema-node
diff --git a/include/linux/ctlv_gen.h b/include/linux/ctlv_gen.h
new file mode 100644
index 000000000000..117e5714c3fe
--- /dev/null
+++ b/include/linux/ctlv_gen.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Generated from Documentation/ctlv/specs/ctlv.yaml by
+ * tools/ctlv/ctlv-regen.sh. Do not edit.
+ */
+/* CTLV-GEN kernel header */
+
+/* Kernel metadata and family API of the ctlv CTLV family. */
+
+#ifndef _LINUX_CTLV_GEN_H
+#define _LINUX_CTLV_GEN_H
+
+#include <linux/ctlv_family.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+
+#include <uapi/ctlv/ctlv_gen.h>
+
+/* Ops this family declares, in declaration order. */
+extern const struct ctlv_op_schema ctlv_op_schemas[];
+
+extern const struct ctlv_family_schema ctlv_schema;
+
+/* The error-info message, built by the core. */
+#define CTLV_ERROR_INFO_MAX_LEN 456
+
+extern const struct ctlv_schema_tree ctlv_error_info_schema;
+
+/* Implemented by the CTLV core, one per action and query it declares. */
+int ctlv_family_get_query(struct ctlv *ctlv, struct ctlv_op_ctx *ctx,
+			  const struct ctlv_cursor *request, struct ctlv_cursor *reply);
+int ctlv_op_get_query(struct ctlv *ctlv, struct ctlv_op_ctx *ctx, const struct ctlv_cursor *request,
+		      struct ctlv_cursor *reply);
+int ctlv_op_schema_get_query(struct ctlv *ctlv, struct ctlv_op_ctx *ctx,
+			     const struct ctlv_cursor *request, struct ctlv_cursor *reply);
+
+extern const struct ctlv_family ctlv_family;
+
+#ifndef _CTLV_GENERATED_METADATA_ONLY
+
+/* Reading a validated message. A blob has a resolver instead of a getter. */
+/* The ID of the op to describe. */
+CTLV_IN_REF(ctlv_op_schema_get_id, CTLV_T_OP_SCHEMA_GET_ID, CTLV_SCHEMA_UINT)
+CTLV_IN_HAS(ctlv_op_schema_get_id)
+CTLV_IN_U64(ctlv_op_schema_get_id)
+
+/* Writing a reply. An append is atomic; a record ends with ctlv_record_end(). */
+/* One entry per family of the chain. */
+CTLV_RECORD_START(ctlv_family_get_families, CTLV_T_FAMILY_GET_FAMILIES, CTLV_SCHEMA_RECORD)
+/* The family ID, which bits 63..48 of the family's types carry. */
+CTLV_PUT_U64(ctlv_family_get_families_id, CTLV_T_FAMILY_GET_FAMILIES_ID, CTLV_SCHEMA_UINT)
+/*
+ * The registry name, also what a uevent carries. Bounded here, this being the only place a message
+ * carries a family name.
+ */
+CTLV_PUT_STRING(ctlv_family_get_families_name, CTLV_T_FAMILY_GET_FAMILIES_NAME, CTLV_SCHEMA_STRING)
+/* One entry per op the device publishes. */
+CTLV_RECORD_START(ctlv_op_get_ops, CTLV_T_OP_GET_OPS, CTLV_SCHEMA_RECORD)
+/* The op ID, which CTLV_IOC_OP_EXEC carries. */
+CTLV_PUT_U64(ctlv_op_get_ops_id, CTLV_T_OP_GET_OPS_ID, CTLV_SCHEMA_UINT)
+/* Action or query. */
+CTLV_PUT_U64(ctlv_op_get_ops_type, CTLV_T_OP_GET_OPS_TYPE, CTLV_SCHEMA_ENUM)
+/* Whether the device may disable and enable the op. */
+CTLV_PUT_U64(ctlv_op_get_ops_availability, CTLV_T_OP_GET_OPS_AVAILABILITY, CTLV_SCHEMA_ENUM)
+/* Whether the device has the op disabled right now. */
+CTLV_PUT_BOOL(ctlv_op_get_ops_disabled, CTLV_T_OP_GET_OPS_DISABLED, CTLV_SCHEMA_BOOL)
+/* The longest request composed from the schema. */
+CTLV_PUT_U64(ctlv_op_get_ops_request_max_len, CTLV_T_OP_GET_OPS_REQUEST_MAX_LEN, CTLV_SCHEMA_UINT)
+/* The reply buffer an action needs, zero for one that answers nothing. */
+CTLV_PUT_U64(ctlv_op_get_ops_action_reply_max_len, CTLV_T_OP_GET_OPS_ACTION_REPLY_MAX_LEN,
+	     CTLV_SCHEMA_UINT)
+/* The shortest reply buffer a query takes. */
+CTLV_PUT_U64(ctlv_op_get_ops_query_reply_min_len, CTLV_T_OP_GET_OPS_QUERY_REPLY_MIN_LEN,
+	     CTLV_SCHEMA_UINT)
+/* The reply buffer length the family suggests for a query. */
+CTLV_PUT_U64(ctlv_op_get_ops_query_reply_suggested_len, CTLV_T_OP_GET_OPS_QUERY_REPLY_SUGGESTED_LEN,
+	     CTLV_SCHEMA_UINT)
+/* The access a file description needs to execute the op. */
+CTLV_PUT_U64(ctlv_op_get_ops_fd_access, CTLV_T_OP_GET_OPS_FD_ACCESS, CTLV_SCHEMA_ENUM)
+/* The longest error information the op can return. */
+CTLV_PUT_U64(ctlv_op_get_ops_error_info_max_len, CTLV_T_OP_GET_OPS_ERROR_INFO_MAX_LEN,
+	     CTLV_SCHEMA_UINT)
+/* The root of the op's request tree, absent when it has no request. */
+CTLV_RECORD_START(ctlv_op_schema_get_request, CTLV_T_OP_SCHEMA_GET_REQUEST, CTLV_SCHEMA_RECORD)
+/* Zero in a message root node, as in the root TLV. */
+CTLV_PUT_U64(ctlv_schema_node_type, CTLV_T_SCHEMA_NODE_TYPE, CTLV_SCHEMA_UINT)
+/* enum ctlv_schema_kind. */
+CTLV_PUT_U64(ctlv_schema_node_kind, CTLV_T_SCHEMA_NODE_KIND, CTLV_SCHEMA_UINT)
+/* Zero where the node may repeat as often as the message has room. */
+CTLV_PUT_U64(ctlv_schema_node_max_occurrences, CTLV_T_SCHEMA_NODE_MAX_OCCURRENCES, CTLV_SCHEMA_UINT)
+/*
+ * The inclusive lower bound of an integer or enum payload, a raw 64-bit value read according to
+ * kind. An enum's values are numbered without gaps.
+ */
+CTLV_PUT_U64(ctlv_schema_node_min, CTLV_T_SCHEMA_NODE_MIN, CTLV_SCHEMA_UINT)
+/* The inclusive upper bound, read the same way. */
+CTLV_PUT_U64(ctlv_schema_node_max, CTLV_T_SCHEMA_NODE_MAX, CTLV_SCHEMA_UINT)
+/* The shortest payload of a string or binary field, a string's terminator excluded. */
+CTLV_PUT_U64(ctlv_schema_node_min_length, CTLV_T_SCHEMA_NODE_MIN_LENGTH, CTLV_SCHEMA_UINT)
+/* The longest payload, likewise. */
+CTLV_PUT_U64(ctlv_schema_node_max_length, CTLV_T_SCHEMA_NODE_MAX_LENGTH, CTLV_SCHEMA_UINT)
+/* The bits a flags payload may carry. */
+CTLV_PUT_U64(ctlv_schema_node_flags_mask, CTLV_T_SCHEMA_NODE_FLAGS_MASK, CTLV_SCHEMA_UINT)
+/* enum ctlv_unit, absent when the payload is dimensionless. */
+CTLV_PUT_U64(ctlv_schema_node_unit, CTLV_T_SCHEMA_NODE_UNIT, CTLV_SCHEMA_UINT)
+/*
+ * The decimal exponent of the unit, a payload being value * 10^scale of it, present only with a
+ * unit.
+ */
+CTLV_PUT_S64(ctlv_schema_node_scale, CTLV_T_SCHEMA_NODE_SCALE, CTLV_SCHEMA_SINT)
+/* Which way a blob field's bytes go. */
+CTLV_PUT_U64(ctlv_schema_node_blob_direction, CTLV_T_SCHEMA_NODE_BLOB_DIRECTION, CTLV_SCHEMA_ENUM)
+/* Mask of BIT(enum ctlv_blob_type): the backings this node takes. */
+CTLV_PUT_U64(ctlv_schema_node_blob_backings, CTLV_T_SCHEMA_NODE_BLOB_BACKINGS, CTLV_SCHEMA_UINT)
+/* Maximum external payload length, unrelated to the inline message limit. */
+CTLV_PUT_U64(ctlv_schema_node_blob_max_length, CTLV_T_SCHEMA_NODE_BLOB_MAX_LENGTH, CTLV_SCHEMA_UINT)
+/* The power of two a blob's address or offset is a multiple of. */
+CTLV_PUT_U64(ctlv_schema_node_blob_alignment, CTLV_T_SCHEMA_NODE_BLOB_ALIGNMENT, CTLV_SCHEMA_UINT)
+/*
+ * Levels to climb to the node whose members are this one's as well, one meaning the node this one
+ * is a member of. A node that carries it lists no members itself.
+ */
+CTLV_PUT_U64(ctlv_schema_node_reenters, CTLV_T_SCHEMA_NODE_REENTERS, CTLV_SCHEMA_UINT)
+/* The required nodes one level in. */
+CTLV_RECORD_START(ctlv_schema_node_fields, CTLV_T_SCHEMA_NODE_FIELDS, CTLV_SCHEMA_RECORD)
+/* The optional nodes one level in. */
+CTLV_RECORD_START(ctlv_schema_node_optional_fields, CTLV_T_SCHEMA_NODE_OPTIONAL_FIELDS,
+		  CTLV_SCHEMA_RECORD)
+/* The root of its reply tree, absent when it has no reply. */
+CTLV_RECORD_START(ctlv_op_schema_get_reply, CTLV_T_OP_SCHEMA_GET_REPLY, CTLV_SCHEMA_RECORD)
+/* The rule the message broke. */
+CTLV_PUT_U64(ctlv_error_info_reason, CTLV_T_ERROR_INFO_REASON, CTLV_SCHEMA_ENUM)
+/* What went wrong, for a person to read. */
+CTLV_PUT_STRING(ctlv_error_info_message, CTLV_T_ERROR_INFO_MESSAGE, CTLV_SCHEMA_STRING)
+/*
+ * The offset of the offending TLV from the start of the message, or of its record when what is
+ * wrong is something the record lacks.
+ */
+CTLV_PUT_U64(ctlv_error_info_bad_tlv_offset, CTLV_T_ERROR_INFO_BAD_TLV_OFFSET, CTLV_SCHEMA_UINT)
+/* The offset of the record holding it, from the start of the message. */
+CTLV_PUT_U64(ctlv_error_info_record_offset, CTLV_T_ERROR_INFO_RECORD_OFFSET, CTLV_SCHEMA_UINT)
+/* The type of the required field that is absent. */
+CTLV_PUT_U64(ctlv_error_info_missing_type, CTLV_T_ERROR_INFO_MISSING_TYPE, CTLV_SCHEMA_UINT)
+/* The generation the query continuation carried. */
+CTLV_PUT_U64(ctlv_error_info_expected_generation, CTLV_T_ERROR_INFO_EXPECTED_GENERATION,
+	     CTLV_SCHEMA_UINT)
+/* The generation the query has now. */
+CTLV_PUT_U64(ctlv_error_info_current_generation, CTLV_T_ERROR_INFO_CURRENT_GENERATION,
+	     CTLV_SCHEMA_UINT)
+/* How long the output blob at bad-tlv-offset has to be for the op. */
+CTLV_PUT_U64(ctlv_error_info_required_length, CTLV_T_ERROR_INFO_REQUIRED_LENGTH, CTLV_SCHEMA_UINT)
+#endif /* _CTLV_GENERATED_METADATA_ONLY */
+
+#endif /* _LINUX_CTLV_GEN_H */
diff --git a/include/uapi/ctlv/ctlv_families_gen.h b/include/uapi/ctlv/ctlv_families_gen.h
new file mode 100644
index 000000000000..83f1719c89aa
--- /dev/null
+++ b/include/uapi/ctlv/ctlv_families_gen.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Generated from Documentation/ctlv/families.yaml by
+ * tools/ctlv/ctlv-regen.sh. Do not edit.
+ */
+/* CTLV-GEN uapi header */
+
+/* CTLV family numbers. */
+
+#ifndef _UAPI_CTLV_CTLV_FAMILIES_GEN_H
+#define _UAPI_CTLV_CTLV_FAMILIES_GEN_H
+
+enum _ctlv_family {
+	CTLV_FAMILY_ROOT = 0,
+};
+
+#endif /* _UAPI_CTLV_CTLV_FAMILIES_GEN_H */
diff --git a/include/uapi/ctlv/ctlv_gen.h b/include/uapi/ctlv/ctlv_gen.h
new file mode 100644
index 000000000000..1eff51c0dd58
--- /dev/null
+++ b/include/uapi/ctlv/ctlv_gen.h
@@ -0,0 +1,278 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Generated from Documentation/ctlv/specs/ctlv.yaml by
+ * tools/ctlv/ctlv-regen.sh. Do not edit.
+ */
+/* CTLV-GEN uapi header */
+
+/*
+ * Constants of the ctlv CTLV family. Types and op IDs are complete; the message format carrying
+ * them is in <ctlv/ctlv.h>.
+ */
+
+#ifndef _UAPI_CTLV_CTLV_GEN_H
+#define _UAPI_CTLV_CTLV_GEN_H
+
+#include <ctlv/ctlv.h>
+#include <ctlv/ctlv_families_gen.h>
+
+/* enum op-type */
+enum _ctlv_op_type {
+	/* Executed and acknowledged. */
+	CTLV_OP_TYPE_ACTION = 1,
+	/* Executed and answered, in as many fragments as the answer needs. */
+	CTLV_OP_TYPE_QUERY = 2,
+};
+
+/* enum op-availability */
+enum _ctlv_op_availability {
+	/* A device that publishes the op always answers it. */
+	CTLV_OP_AVAILABILITY_FIXED = 1,
+	/* A device may disable and enable the op at any time. */
+	CTLV_OP_AVAILABILITY_DYNAMIC = 2,
+};
+
+/* enum blob-type */
+enum _ctlv_blob_type {
+	/* Anonymous memory of the calling process, at address. */
+	CTLV_BLOB_TYPE_VA = 1,
+};
+
+/* enum blob-direction */
+enum _ctlv_blob_direction {
+	/* From the caller to the device, which only reads the blob. */
+	CTLV_BLOB_DIRECTION_INPUT = 1,
+	/* From the device to the caller. */
+	CTLV_BLOB_DIRECTION_OUTPUT = 2,
+	/* Both ways: the device reads the blob and writes it. */
+	CTLV_BLOB_DIRECTION_INOUT = 3,
+};
+
+/* enum validation-reason */
+enum _ctlv_validation_reason {
+	/*
+	 * The TLVs do not frame: the root is not one, a length runs past its record, or a TLV is a
+	 * record where its schema has a scalar or the reverse.
+	 */
+	CTLV_VALIDATION_REASON_MALFORMED_MESSAGE = 1,
+	/*
+	 * A TLV of a type its record does not place there, not marked IGNORE_IF_UNKNOWN or of a
+	 * type the schema places elsewhere.
+	 */
+	CTLV_VALIDATION_REASON_UNKNOWN_TYPE = 2,
+	/*
+	 * A header flag the message may not carry, or a flags payload with a bit its field does not
+	 * declare.
+	 */
+	CTLV_VALIDATION_REASON_UNKNOWN_FLAG = 3,
+	/* Records nested deeper than the core follows. */
+	CTLV_VALIDATION_REASON_RECORD_TOO_DEEP = 4,
+	/* A required field is absent, and missing-type says which. */
+	CTLV_VALIDATION_REASON_MISSING_FIELD = 5,
+	/* A field occurs more often than its schema allows. */
+	CTLV_VALIDATION_REASON_TOO_MANY_OCCURRENCES = 6,
+	/* An integer, an enum value or a bool outside what its field takes. */
+	CTLV_VALIDATION_REASON_VALUE_OUT_OF_RANGE = 7,
+	/* A payload of the wrong length for its kind, or outside its field's length bounds. */
+	CTLV_VALIDATION_REASON_BAD_LENGTH = 8,
+	/* A string payload that is not exactly one NUL-terminated string. */
+	CTLV_VALIDATION_REASON_BAD_STRING = 9,
+	/*
+	 * A blob descriptor its field does not take, or one whose memory could not be acquired or
+	 * is too short for what the op writes there.
+	 */
+	CTLV_VALIDATION_REASON_BAD_BLOB_DESCRIPTOR = 10,
+	/* A query continuation of a generation the query no longer has. */
+	CTLV_VALIDATION_REASON_STALE_GENERATION = 11,
+	/* The TLVs of one type are not contiguous in their record. */
+	CTLV_VALIDATION_REASON_NONCONTIGUOUS_TYPE = 12,
+};
+
+/* enum op-fd-access */
+enum _ctlv_op_fd_access {
+	/* Read access, which an observation fd has as well as a full-control one. */
+	CTLV_OP_FD_ACCESS_READ = 1,
+	/* Read-write access, which only a full-control fd has. */
+	CTLV_OP_FD_ACCESS_READ_WRITE = 2,
+};
+
+/* blob */
+/* What holds the bytes, which says which of the fields below are present. */
+#define CTLV_T_BLOB_TYPE \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00010000, 1)
+/* Where a va blob starts in the calling process. */
+#define CTLV_T_BLOB_ADDRESS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00010000, 2)
+/* Reserved for a later fd-backed type. */
+#define CTLV_T_BLOB_FD \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00010000, 3)
+/* Reserved for a later fd-backed type. */
+#define CTLV_T_BLOB_OFFSET \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00010000, 4)
+/* The length of the blob in bytes, at most what its field takes. */
+#define CTLV_T_BLOB_LENGTH \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00010000, 5)
+
+/* error-info */
+/* The rule the message broke. */
+#define CTLV_T_ERROR_INFO_REASON \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 1)
+/* What went wrong, for a person to read. */
+#define CTLV_T_ERROR_INFO_MESSAGE \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 2)
+/*
+ * The offset of the offending TLV from the start of the message, or of its record when what is
+ * wrong is something the record lacks.
+ */
+#define CTLV_T_ERROR_INFO_BAD_TLV_OFFSET \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 3)
+/* The offset of the record holding it, from the start of the message. */
+#define CTLV_T_ERROR_INFO_RECORD_OFFSET \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 4)
+/* The type of the required field that is absent. */
+#define CTLV_T_ERROR_INFO_MISSING_TYPE \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 5)
+/* The generation the query continuation carried. */
+#define CTLV_T_ERROR_INFO_EXPECTED_GENERATION \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 6)
+/* The generation the query has now. */
+#define CTLV_T_ERROR_INFO_CURRENT_GENERATION \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 7)
+/* How long the output blob at bad-tlv-offset has to be for the op. */
+#define CTLV_T_ERROR_INFO_REQUIRED_LENGTH \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00020000, 8)
+
+/* schema-node */
+/* Zero in a message root node, as in the root TLV. */
+#define CTLV_T_SCHEMA_NODE_TYPE \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 1)
+/* enum ctlv_schema_kind. */
+#define CTLV_T_SCHEMA_NODE_KIND \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 2)
+/* Zero where the node may repeat as often as the message has room. */
+#define CTLV_T_SCHEMA_NODE_MAX_OCCURRENCES \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 3)
+/*
+ * The inclusive lower bound of an integer or enum payload, a raw 64-bit value read according to
+ * kind. An enum's values are numbered without gaps.
+ */
+#define CTLV_T_SCHEMA_NODE_MIN \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 4)
+/* The inclusive upper bound, read the same way. */
+#define CTLV_T_SCHEMA_NODE_MAX \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 5)
+/* The shortest payload of a string or binary field, a string's terminator excluded. */
+#define CTLV_T_SCHEMA_NODE_MIN_LENGTH \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 6)
+/* The longest payload, likewise. */
+#define CTLV_T_SCHEMA_NODE_MAX_LENGTH \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 7)
+/* The bits a flags payload may carry. */
+#define CTLV_T_SCHEMA_NODE_FLAGS_MASK \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 8)
+/* enum ctlv_unit, absent when the payload is dimensionless. */
+#define CTLV_T_SCHEMA_NODE_UNIT \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 9)
+/*
+ * The decimal exponent of the unit, a payload being value * 10^scale of it, present only with a
+ * unit.
+ */
+#define CTLV_T_SCHEMA_NODE_SCALE \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 10)
+/* Which way a blob field's bytes go. */
+#define CTLV_T_SCHEMA_NODE_BLOB_DIRECTION \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 11)
+/* Mask of BIT(enum ctlv_blob_type): the backings this node takes. */
+#define CTLV_T_SCHEMA_NODE_BLOB_BACKINGS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 12)
+/* Maximum external payload length, unrelated to the inline message limit. */
+#define CTLV_T_SCHEMA_NODE_BLOB_MAX_LENGTH \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 13)
+/* The power of two a blob's address or offset is a multiple of. */
+#define CTLV_T_SCHEMA_NODE_BLOB_ALIGNMENT \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 14)
+/*
+ * Levels to climb to the node whose members are this one's as well, one meaning the node this one
+ * is a member of. A node that carries it lists no members itself.
+ */
+#define CTLV_T_SCHEMA_NODE_REENTERS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 15)
+/* The required nodes one level in. */
+#define CTLV_T_SCHEMA_NODE_FIELDS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 16)
+/* The optional nodes one level in. */
+#define CTLV_T_SCHEMA_NODE_OPTIONAL_FIELDS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x00030000, 17)
+
+/* family-get reply */
+/* One entry per family of the chain. */
+#define CTLV_T_FAMILY_GET_FAMILIES \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0010000, 1)
+
+/* family-get families */
+/* The family ID, which bits 63..48 of the family's types carry. */
+#define CTLV_T_FAMILY_GET_FAMILIES_ID \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0011000, 1)
+/*
+ * The registry name, also what a uevent carries. Bounded here, this being the only place a message
+ * carries a family name.
+ */
+#define CTLV_T_FAMILY_GET_FAMILIES_NAME \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0011000, 2)
+
+/* op-get reply */
+/* One entry per op the device publishes. */
+#define CTLV_T_OP_GET_OPS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0020000, 1)
+
+/* op-get ops */
+/* The op ID, which CTLV_IOC_OP_EXEC carries. */
+#define CTLV_T_OP_GET_OPS_ID \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 1)
+/* Action or query. */
+#define CTLV_T_OP_GET_OPS_TYPE \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 2)
+/* Whether the device may disable and enable the op. */
+#define CTLV_T_OP_GET_OPS_AVAILABILITY \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 3)
+/* Whether the device has the op disabled right now. */
+#define CTLV_T_OP_GET_OPS_DISABLED \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 4)
+/* The longest request composed from the schema. */
+#define CTLV_T_OP_GET_OPS_REQUEST_MAX_LEN \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 5)
+/* The reply buffer an action needs, zero for one that answers nothing. */
+#define CTLV_T_OP_GET_OPS_ACTION_REPLY_MAX_LEN \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 6)
+/* The shortest reply buffer a query takes. */
+#define CTLV_T_OP_GET_OPS_QUERY_REPLY_MIN_LEN \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 7)
+/* The reply buffer length the family suggests for a query. */
+#define CTLV_T_OP_GET_OPS_QUERY_REPLY_SUGGESTED_LEN \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 8)
+/* The access a file description needs to execute the op. */
+#define CTLV_T_OP_GET_OPS_FD_ACCESS \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 9)
+/* The longest error information the op can return. */
+#define CTLV_T_OP_GET_OPS_ERROR_INFO_MAX_LEN \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0021000, 10)
+
+/* op-schema-get request */
+/* The ID of the op to describe. */
+#define CTLV_T_OP_SCHEMA_GET_ID \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0x80030000, 1)
+
+/* op-schema-get reply */
+/* The root of the op's request tree, absent when it has no request. */
+#define CTLV_T_OP_SCHEMA_GET_REQUEST \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0030000, 1)
+/* The root of its reply tree, absent when it has no reply. */
+#define CTLV_T_OP_SCHEMA_GET_REPLY \
+	CTLV_TYPE(CTLV_FAMILY_ROOT, 0xc0030000, 2)
+
+/* ops */
+#define CTLV_OP_FAMILY_GET CTLV_OP(CTLV_FAMILY_ROOT, 1)
+#define CTLV_OP_OP_GET CTLV_OP(CTLV_FAMILY_ROOT, 2)
+#define CTLV_OP_OP_SCHEMA_GET CTLV_OP(CTLV_FAMILY_ROOT, 3)
+
+#endif /* _UAPI_CTLV_CTLV_GEN_H */
diff --git a/lib/ctlv/ctlv_gen.c b/lib/ctlv/ctlv_gen.c
new file mode 100644
index 000000000000..78b322507711
--- /dev/null
+++ b/lib/ctlv/ctlv_gen.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/*
+ * Generated from Documentation/ctlv/specs/ctlv.yaml by
+ * tools/ctlv/ctlv-regen.sh. Do not edit.
+ */
+/* CTLV-GEN kernel source */
+
+#define _CTLV_GENERATED_METADATA_ONLY
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/ctlv_family.h>
+#include <linux/export.h>
+#include <linux/limits.h>
+
+#include <uapi/ctlv/ctlv_gen.h>
+
+#include <linux/ctlv_gen.h>
+
+static const struct ctlv_node_schema family_get_request_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(0, 0, 0),
+};
+
+static const struct ctlv_node_schema family_get_reply_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(1, 1, 1),
+	CTLV_NODE_SCHEMA_RECORD(CTLV_T_FAMILY_GET_FAMILIES, CTLV_NODE_SCHEMA_UNLIMITED, 2, 2, 2),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_FAMILY_GET_FAMILIES_ID, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_STRING(CTLV_T_FAMILY_GET_FAMILIES_NAME, 1, 0, 63),
+};
+
+static const struct ctlv_node_schema op_get_request_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(0, 0, 0),
+};
+
+static const struct ctlv_node_schema op_get_reply_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(1, 1, 1),
+	CTLV_NODE_SCHEMA_RECORD(CTLV_T_OP_GET_OPS, CTLV_NODE_SCHEMA_UNLIMITED, 2, 10, 2),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_GET_OPS_ID, 1, 1, U64_MAX),
+	CTLV_NODE_SCHEMA_ENUM(CTLV_T_OP_GET_OPS_TYPE, 1, 2),
+	CTLV_NODE_SCHEMA_ENUM(CTLV_T_OP_GET_OPS_AVAILABILITY, 1, 2),
+	CTLV_NODE_SCHEMA_BOOL(CTLV_T_OP_GET_OPS_DISABLED, 1),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_GET_OPS_REQUEST_MAX_LEN, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_GET_OPS_ACTION_REPLY_MAX_LEN, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_GET_OPS_QUERY_REPLY_MIN_LEN, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_GET_OPS_QUERY_REPLY_SUGGESTED_LEN, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_ENUM(CTLV_T_OP_GET_OPS_FD_ACCESS, 1, 2),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_GET_OPS_ERROR_INFO_MAX_LEN, 1, 0, U64_MAX),
+};
+
+static const struct ctlv_node_schema op_schema_get_request_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(1, 1, 1),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_OP_SCHEMA_GET_ID, 1, 1, U64_MAX),
+};
+
+static const struct ctlv_node_schema op_schema_get_reply_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(1, 2, 0),
+	CTLV_NODE_SCHEMA_RECORD(CTLV_T_OP_SCHEMA_GET_REQUEST, 1, 3, 17, 3),
+	CTLV_NODE_SCHEMA_RECORD(CTLV_T_OP_SCHEMA_GET_REPLY, 1, 20, 17, 3),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_TYPE, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_KIND, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MAX_OCCURRENCES, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MIN, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MAX, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MIN_LENGTH, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MAX_LENGTH, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_FLAGS_MASK, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_UNIT, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_SINT(CTLV_T_SCHEMA_NODE_SCALE, 1, S64_MIN, S64_MAX),
+	CTLV_NODE_SCHEMA_ENUM(CTLV_T_SCHEMA_NODE_BLOB_DIRECTION, 1, 3),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_BLOB_BACKINGS, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_BLOB_MAX_LENGTH, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_BLOB_ALIGNMENT, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_REENTERS, 1, 1, U64_MAX),
+	CTLV_NODE_SCHEMA_RECORD_REENTERS(CTLV_T_SCHEMA_NODE_FIELDS, CTLV_NODE_SCHEMA_UNLIMITED, 3,
+					 17, 3),
+	CTLV_NODE_SCHEMA_RECORD_REENTERS(CTLV_T_SCHEMA_NODE_OPTIONAL_FIELDS,
+					 CTLV_NODE_SCHEMA_UNLIMITED, 3, 17, 3),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_TYPE, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_KIND, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MAX_OCCURRENCES, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MIN, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MAX, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MIN_LENGTH, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_MAX_LENGTH, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_FLAGS_MASK, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_UNIT, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_SINT(CTLV_T_SCHEMA_NODE_SCALE, 1, S64_MIN, S64_MAX),
+	CTLV_NODE_SCHEMA_ENUM(CTLV_T_SCHEMA_NODE_BLOB_DIRECTION, 1, 3),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_BLOB_BACKINGS, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_BLOB_MAX_LENGTH, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_BLOB_ALIGNMENT, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_SCHEMA_NODE_REENTERS, 1, 1, U64_MAX),
+	CTLV_NODE_SCHEMA_RECORD_REENTERS(CTLV_T_SCHEMA_NODE_FIELDS, CTLV_NODE_SCHEMA_UNLIMITED, 20,
+					 17, 3),
+	CTLV_NODE_SCHEMA_RECORD_REENTERS(CTLV_T_SCHEMA_NODE_OPTIONAL_FIELDS,
+					 CTLV_NODE_SCHEMA_UNLIMITED, 20, 17, 3),
+};
+
+static const struct ctlv_node_schema error_info_node_schemas[] = {
+	CTLV_NODE_SCHEMA_ROOT(1, 8, 0),
+	CTLV_NODE_SCHEMA_ENUM(CTLV_T_ERROR_INFO_REASON, 1, 12),
+	CTLV_NODE_SCHEMA_STRING(CTLV_T_ERROR_INFO_MESSAGE, 1, 0, 255),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_ERROR_INFO_BAD_TLV_OFFSET, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_ERROR_INFO_RECORD_OFFSET, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_ERROR_INFO_MISSING_TYPE, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_ERROR_INFO_EXPECTED_GENERATION, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_ERROR_INFO_CURRENT_GENERATION, 1, 0, U64_MAX),
+	CTLV_NODE_SCHEMA_UINT(CTLV_T_ERROR_INFO_REQUIRED_LENGTH, 1, 0, U64_MAX),
+};
+
+const struct ctlv_schema_tree ctlv_error_info_schema = {
+	.node_schemas = error_info_node_schemas,
+	.node_schemas_count =
+		ARRAY_SIZE(error_info_node_schemas),
+};
+
+static_assert(CTLV_ERROR_INFO_MAX_LEN <= CTLV_MAX_ERROR_LEN);
+
+/* The ops this family declares, in declaration order. */
+const struct ctlv_op_schema ctlv_op_schemas[] = {
+	CTLV_OP_SCHEMA_QUERY(CTLV_OP_FAMILY_GET, CTLV_OP_FD_ACCESS_READ, CTLV_OP_AVAILABILITY_FIXED,
+			     16, 112, 65536, CTLV_SCHEMA_TREE(family_get_request),
+			     CTLV_SCHEMA_TREE(family_get_reply)),
+	CTLV_OP_SCHEMA_QUERY(CTLV_OP_OP_GET, CTLV_OP_FD_ACCESS_READ, CTLV_OP_AVAILABILITY_FIXED, 16,
+			     56, 65536, CTLV_SCHEMA_TREE(op_get_request),
+			     CTLV_SCHEMA_TREE(op_get_reply)),
+	CTLV_OP_SCHEMA_QUERY(CTLV_OP_OP_SCHEMA_GET, CTLV_OP_FD_ACCESS_READ,
+			     CTLV_OP_AVAILABILITY_FIXED, 40, 168, 65536,
+			     CTLV_SCHEMA_TREE(op_schema_get_request),
+			     CTLV_SCHEMA_TREE(op_schema_get_reply)),
+};
+
+/* The family ID is its position in the central registry. */
+const struct ctlv_family_schema ctlv_schema = {
+	.name = "ctlv",
+	.family_id = 0,
+	.ops = ctlv_op_schemas,
+	.ops_count = ARRAY_SIZE(ctlv_op_schemas),
+};
+
+/* Maximum uevent cost for this family chain. */
+static_assert(60 <= CTLV_UEVENT_MAX_LEN);
+static_assert(3 <= CTLV_UEVENT_MAX_VARS);
+
+/* Guards for generated maxima. */
+static_assert(65536 <= CTLV_MAX_INLINE_MESSAGE_LEN);
+static_assert(2 <= CTLV_MAX_DECLARED_RECORD_DEPTH);
+
+/* Local action and query bindings in declaration order. */
+static const struct ctlv_op ctlv_ops[] = {
+	CTLV_OP_QUERY(ctlv_family_get),
+	CTLV_OP_QUERY(ctlv_op_get),
+	CTLV_OP_QUERY(ctlv_op_schema_get),
+};
+
+/* Root-family descriptor used by the CTLV core. */
+const struct ctlv_family ctlv_family = {
+	.schema = &ctlv_schema,
+	.ops = ctlv_ops,
+	.ops_count = ARRAY_SIZE(ctlv_ops),
+};
+EXPORT_SYMBOL_NS_GPL(ctlv_family, "CTLV");
-- 
2.56.0


  parent reply	other threads:[~2026-11-13 15:55 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 15:54 [PATCH RFC v2 00/15] ctlv: Add a schema-driven TLV uAPI for character devices Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 01/15] ctlv: Add the schema language and generator Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 02/15] ctlv: Add the UAPI message format and family API Jiri Pirko
2026-11-13 15:55 ` Jiri Pirko [this message]
2026-11-13 15:55 ` [PATCH RFC v2 04/15] Documentation: ctlv: Add userspace ABI Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 05/15] ctlv: Prepare message input handling Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 06/15] ctlv: Prepare blob descriptor validation Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 07/15] ctlv: Prepare schema validation Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 08/15] ctlv: Prepare message output handling Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 09/15] ctlv: Prepare VA blob handling Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 10/15] ctlv: Prepare runtime introspection Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 11/15] ctlv: Add operation dispatch Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 12/15] ctlv: Add character device nodes Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 13/15] Documentation: ctlv: Describe how to write a family Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 14/15] tools/ctlv: Add the userspace library Jiri Pirko
2026-11-13 15:55 ` [PATCH RFC v2 15/15] tools/ctlv: Add ctlvctl 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=20261113155515.2510194-4-jiri@resnulli.us \
    --to=jiri@resnulli.us \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=aik@amd.com \
    --cc=alistair.francis@wdc.com \
    --cc=aneesh.kumar@kernel.org \
    --cc=ankita@nvidia.com \
    --cc=cedric.xing@intel.com \
    --cc=dave.hansen@linux.intel.com \
    --cc=dave.jiang@intel.com \
    --cc=david.quigley@intel.com \
    --cc=drdeeglaze@gmail.com \
    --cc=elena.reshetova@intel.com \
    --cc=jarkko@kernel.org \
    --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=nik.borisov@suse.com \
    --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=sergii.dmytruk@3mdeb.com \
    --cc=suzuki.poulose@arm.com \
    --cc=x86@kernel.org \
    --cc=yeoreum.yun@arm.com \
    --cc=zohar@linux.ibm.com \
    /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