From: Dan Williams <djbw@kernel.org>
To: linux-coco@lists.linux.dev
Cc: linux-pci@vger.kernel.org, driver-core@lists.linux.dev,
ankita@nvidia.com, Alistair Francis <alistair.francis@wdc.com>,
Lukas Wunner <lukas@wunner.de>,
Donald Hunter <donald.hunter@gmail.com>,
Jakub Kicinski <kuba@kernel.org>
Subject: [PATCH 02/15] tools: ynl: Teach pyynl to handle blobs
Date: Sun, 5 Jul 2026 15:08:06 -0700 [thread overview]
Message-ID: <20260705220819.2472765-3-djbw@kernel.org> (raw)
In-Reply-To: <20260705220819.2472765-1-djbw@kernel.org>
The generic "device evidence" facility wants to convey large objects
(certificate chains, measurements) and support various security commands
relative to that evidence (like validate) [1].
Update pyynl to understand the "bloblen" property indicates:
* the associated attribute is a variable length byte array
* it is multi-attr where each message with the same attribute represents
chunks of the byte array
* the byte array's size is conveyed in a scalar attribute named by the
"bloblen" property.
* the attribute identified by "bloblen" always arrives in a message before
the first chunk of the associated blob
A "multi-attr" without "bloblen" retains the legacy behavior where repeated
reception of the same attribute across messages results in a new instance
of the object. The new behavior causes repeated reception of an
attribute to be treated as filling consecutive bytes of an array.
Assisted-by: Codex:GPT-5
[codex: drafted initial python changes]
Cc: Alistair Francis <alistair.francis@wdc.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Donald Hunter <donald.hunter@gmail.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Link: http://lore.kernel.org/20260318170014.6650d2bf@kernel.org [1]
Signed-off-by: Dan Williams <djbw@kernel.org>
---
tools/net/ynl/pyynl/lib/nlspec.py | 12 ++++++
tools/net/ynl/pyynl/lib/ynl.py | 62 ++++++++++++++++++++++++++++++-
2 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/tools/net/ynl/pyynl/lib/nlspec.py b/tools/net/ynl/pyynl/lib/nlspec.py
index 0469a0e270d0..4764263968ea 100644
--- a/tools/net/ynl/pyynl/lib/nlspec.py
+++ b/tools/net/ynl/pyynl/lib/nlspec.py
@@ -172,6 +172,7 @@ class SpecAttr(SpecElement):
selector string, name of attribute used to select
sub-message type
+ bloblen string, name of attr which reports the blob length
is_auto_scalar bool, attr is a variable-size scalar
"""
def __init__(self, family, attr_set, yaml, value):
@@ -181,6 +182,7 @@ class SpecAttr(SpecElement):
self.value = value
self.attr_set = attr_set
self.is_multi = yaml.get('multi-attr', False)
+ self.bloblen = yaml.get('bloblen')
self.struct_name = yaml.get('struct')
self.sub_type = yaml.get('sub-type')
self.byte_order = yaml.get('byte-order')
@@ -191,6 +193,10 @@ class SpecAttr(SpecElement):
self.is_auto_scalar = self.type in ("sint", "uint")
+ if self.bloblen and not self.is_multi:
+ raise SpecException(
+ f"Attribute '{attr_set.name}.{self.name}' has bloblen without multi-attr")
+
class SpecAttrSet(SpecElement):
""" Netlink Attribute Set class.
@@ -355,6 +361,8 @@ class SpecOperation(SpecElement):
attr_set attribute set name
fixed_header string, optional name of fixed header struct
+ dump_blob_attrs set, reply blob attribute names which may span messages
+
yaml raw spec as loaded from the spec file
"""
def __init__(self, family, yaml, req_value, rsp_value):
@@ -369,6 +377,7 @@ class SpecOperation(SpecElement):
self.is_async = 'notify' in yaml or 'event' in yaml
self.is_resv = not self.is_async and not self.is_call
self.fixed_header = self.yaml.get('fixed-header', family.fixed_header)
+ self.dump_blob_attrs = frozenset()
# Added by resolve:
self.attr_set = None
@@ -388,6 +397,9 @@ class SpecOperation(SpecElement):
raise SpecException(f"Can't resolve attribute set for op '{self.name}'")
if attr_set_name:
self.attr_set = self.family.attr_sets[attr_set_name]
+ reply_attrs = self.yaml.get('dump', {}).get('reply', {}).get('attributes', ())
+ self.dump_blob_attrs = frozenset(
+ name for name in reply_attrs if self.attr_set[name].bloblen)
class SpecMcastGroup(SpecElement):
diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py
index 092d132edec1..f21b68043bd3 100644
--- a/tools/net/ynl/pyynl/lib/ynl.py
+++ b/tools/net/ynl/pyynl/lib/ynl.py
@@ -998,6 +998,47 @@ class YnlFamily(SpecFamily):
else:
rsp[name] = [decoded]
+ def _blob_fill(self, buf, off, chunks, name):
+ for chunk in chunks:
+ end = off + len(chunk)
+ if end > len(buf):
+ raise YnlException(f"Blob '{name}' data exceeds reported length")
+ buf[off:end] = chunk
+ off = end
+ return off
+
+ def _blob_init(self, op, rsp, blob_attrs):
+ """
+ Start a new blob object. Preallocate each blob payload using the
+ length carried ahead of the data and copy in the first chunk(s).
+ Return the per-attribute write offsets for any following messages.
+ """
+ offsets = {}
+ for name in blob_attrs:
+ if name not in rsp:
+ continue
+ length = rsp.get(op.attr_set[name].bloblen, 0)
+ buf = bytearray(length)
+ offsets[name] = self._blob_fill(buf, 0, rsp[name], name)
+ rsp[name] = buf
+ return offsets
+
+ def _blob_extend(self, rsp, update, blob_attrs, offsets):
+ """
+ Continue filling the previous object's blob(s) when the current
+ message carries only blob continuation attributes.
+ """
+ if any(name in rsp and name not in blob_attrs for name in update):
+ return False
+
+ for name, value in update.items():
+ if name in blob_attrs and name in rsp:
+ offsets[name] = self._blob_fill(rsp[name], offsets.get(name, 0),
+ value, name)
+ else:
+ rsp[name] = value
+ return True
+
def _resolve_selector(self, attr_spec, search_attrs):
sub_msg = attr_spec.sub_message
if sub_msg not in self.sub_msgs:
@@ -1356,7 +1397,10 @@ class YnlFamily(SpecFamily):
for (method, vals, flags) in ops:
op = self.ops[method]
msg = self._encode_message(op, vals, flags, req_seq)
- reqs_by_seq[req_seq] = (op, vals, msg, flags)
+ blob_attrs = None
+ if Netlink.NLM_F_DUMP in flags:
+ blob_attrs = op.dump_blob_attrs or None
+ reqs_by_seq[req_seq] = (op, vals, msg, flags, blob_attrs)
payload += msg
req_seq += 1
@@ -1365,6 +1409,7 @@ class YnlFamily(SpecFamily):
done = False
rsp = []
op_rsp = []
+ blob_off = {}
while not done:
reply, ancdata = self._recvmsg()
nsid = self._decode_nsid(ancdata)
@@ -1372,13 +1417,14 @@ class YnlFamily(SpecFamily):
self._recv_dbg_print(reply, nms)
for nl_msg in nms:
if nl_msg.nl_seq in reqs_by_seq:
- (op, vals, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq]
+ (op, vals, req_msg, req_flags, blob_attrs) = reqs_by_seq[nl_msg.nl_seq]
if nl_msg.extack:
nl_msg.annotate_extack(op.attr_set)
self._decode_extack(req_msg, op, nl_msg.extack, vals)
else:
op = None
req_flags = []
+ blob_attrs = None
if nl_msg.error:
raise NlError(nl_msg)
@@ -1388,6 +1434,11 @@ class YnlFamily(SpecFamily):
print(nl_msg)
if Netlink.NLM_F_DUMP in req_flags:
+ if blob_attrs:
+ for obj in op_rsp:
+ for name in blob_attrs:
+ if isinstance(obj.get(name), bytearray):
+ obj[name] = bytes(obj[name])
rsp.append(op_rsp)
elif not op_rsp:
rsp.append(None)
@@ -1414,6 +1465,13 @@ class YnlFamily(SpecFamily):
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
if op.fixed_header:
rsp_msg.update(self._decode_struct(decoded.raw, op.fixed_header))
+
+ if blob_attrs:
+ if op_rsp and self._blob_extend(op_rsp[-1], rsp_msg,
+ blob_attrs, blob_off):
+ continue
+ blob_off = self._blob_init(op, rsp_msg, blob_attrs)
+
op_rsp.append(rsp_msg)
return rsp
--
2.54.0
next prev parent reply other threads:[~2026-07-05 22:08 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-05 22:08 [PATCH 00/15] Device Evidence and Trust for PCI Security Protocol (TDISP) Dan Williams
2026-07-05 22:08 ` [PATCH 01/15] netlink: specs: Introduce multi-message blobs for SPDM Dan Williams
2026-07-05 22:08 ` Dan Williams [this message]
2026-07-05 22:08 ` [PATCH 03/15] tools: ynl: Teach ynl_gen_c to validate and dump 'blob' attributes Dan Williams
2026-07-05 22:08 ` [PATCH 04/15] device core: Introduce "device evidence" over netlink Dan Williams
2026-07-05 22:08 ` [PATCH 05/15] device core: Add "device evidence" 'validate' command Dan Williams
2026-07-05 22:08 ` [PATCH 06/15] PCI/TSM: Add device evidence support Dan Williams
2026-07-05 22:08 ` [PATCH 07/15] modules: Document the global async_probe parameter Dan Williams
2026-07-05 22:08 ` [PATCH 08/15] device core: Initial device trust infrastructure Dan Williams
2026-07-06 13:45 ` Jason Gunthorpe
2026-07-05 22:08 ` [PATCH 09/15] PCI, device core: Move "untrusted" concept to DEVICE_TRUST_ADVERSARY Dan Williams
2026-07-06 13:49 ` Jason Gunthorpe
2026-07-05 22:08 ` [PATCH 10/15] PCI/TSM: Add device interface security LOCKED support Dan Williams
2026-07-05 22:08 ` [PATCH 11/15] PCI/TSM: Add device interface security RUN support Dan Williams
2026-07-05 22:08 ` [PATCH 12/15] PCI/TSM: Add device interface security DMA enable/disable Dan Williams
2026-07-05 22:08 ` [PATCH 13/15] PCI, device core: Add private memory access for DEVICE_TRUST_TCB Dan Williams
2026-07-06 12:42 ` Aneesh Kumar K.V
2026-07-05 22:08 ` [PATCH 14/15] PCI/TSM: Create MMIO descriptors via TDISP Report Dan Williams
2026-07-05 22:08 ` [PATCH 15/15] PCI/TSM: Add relative MMIO offset support? Dan Williams
2026-07-06 12:51 ` [PATCH 00/15] Device Evidence and Trust for PCI Security Protocol (TDISP) Jason Gunthorpe
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=20260705220819.2472765-3-djbw@kernel.org \
--to=djbw@kernel.org \
--cc=alistair.francis@wdc.com \
--cc=ankita@nvidia.com \
--cc=donald.hunter@gmail.com \
--cc=driver-core@lists.linux.dev \
--cc=kuba@kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-pci@vger.kernel.org \
--cc=lukas@wunner.de \
/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