new file mode 100644
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * This header defines the XDR data type primitives, specified in
+ * Section 4 of RFC 4506, that are used by RPC programs implemented
+ * in the Linux kernel.
+ */
+
+#ifndef _SUNRPC_XDRGEN_BUILTINS_H_
+#define _SUNRPC_XDRGEN_BUILTINS_H_
+
+#include <linux/sunrpc/xdr.h>
+
+static inline bool
+xdrgen_decode_void(struct xdr_stream *xdr)
+{
+ return true;
+}
+
+static inline bool
+xdrgen_encode_void(struct xdr_stream *xdr)
+{
+ return true;
+}
+
+static inline bool
+xdrgen_decode_bool(struct xdr_stream *xdr, bool *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = (*p != xdr_zero);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_bool(struct xdr_stream *xdr, bool val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *p = val ? xdr_one : xdr_zero;
+ return true;
+}
+
+static inline bool
+xdrgen_decode_int(struct xdr_stream *xdr, s32 *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = be32_to_cpup(p);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_int(struct xdr_stream *xdr, s32 val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *p = cpu_to_be32(val);
+ return true;
+}
+
+static inline bool
+xdrgen_decode_unsigned_int(struct xdr_stream *xdr, u32 *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = be32_to_cpup(p);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_unsigned_int(struct xdr_stream *xdr, u32 val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *p = cpu_to_be32(val);
+ return true;
+}
+
+static inline bool
+xdrgen_decode_long(struct xdr_stream *xdr, s32 *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = be32_to_cpup(p);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_long(struct xdr_stream *xdr, s32 val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *p = cpu_to_be32(val);
+ return true;
+}
+
+static inline bool
+xdrgen_decode_unsigned_long(struct xdr_stream *xdr, u32 *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = be32_to_cpup(p);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_unsigned_long(struct xdr_stream *xdr, u32 val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
+
+ if (unlikely(!p))
+ return false;
+ *p = cpu_to_be32(val);
+ return true;
+}
+
+static inline bool
+xdrgen_decode_hyper(struct xdr_stream *xdr, s64 *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = get_unaligned_be64(p);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_hyper(struct xdr_stream *xdr, s64 val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
+
+ if (unlikely(!p))
+ return false;
+ put_unaligned_be64(val, p);
+ return true;
+}
+
+static inline bool
+xdrgen_decode_unsigned_hyper(struct xdr_stream *xdr, u64 *ptr)
+{
+ __be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+
+ if (unlikely(!p))
+ return false;
+ *ptr = get_unaligned_be64(p);
+ return true;
+}
+
+static inline bool
+xdrgen_encode_unsigned_hyper(struct xdr_stream *xdr, u64 val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
+
+ if (unlikely(!p))
+ return false;
+ put_unaligned_be64(val, p);
+ return true;
+}
+
+typedef struct {
+ u32 len;
+ unsigned char *data;
+} string;
+
+static inline bool
+xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen)
+{
+ __be32 *p;
+ u32 len;
+
+ if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT))
+ return false;
+ if (unlikely(maxlen && len > maxlen))
+ return false;
+ if (len != 0) {
+ p = xdr_inline_decode(xdr, len);
+ if (unlikely(!p))
+ return false;
+ ptr->data = (unsigned char *)p;
+ }
+ ptr->len = len;
+ return true;
+}
+
+static inline bool
+xdrgen_encode_string(struct xdr_stream *xdr, string val, u32 maxlen)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
+
+ if (unlikely(!p))
+ return false;
+ xdr_encode_opaque(p, val.data, val.len);
+ return true;
+}
+
+typedef struct {
+ u32 len;
+ u8 *data;
+} opaque;
+
+static inline bool
+xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen)
+{
+ __be32 *p;
+ u32 len;
+
+ if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT))
+ return false;
+ if (unlikely(maxlen && len > maxlen))
+ return false;
+ if (len != 0) {
+ p = xdr_inline_decode(xdr, len);
+ if (unlikely(!p))
+ return false;
+ ptr->data = (u8 *)p;
+ }
+ ptr->len = len;
+ return true;
+}
+
+static inline bool
+xdrgen_encode_opaque(struct xdr_stream *xdr, opaque val)
+{
+ __be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
+
+ if (unlikely(!p))
+ return false;
+ xdr_encode_opaque(p, val.data, val.len);
+ return true;
+}
+
+#endif /* _SUNRPC_XDRGEN_BUILTINS_H_ */
new file mode 100644
@@ -0,0 +1,2 @@
+__pycache__
+generators/__pycache__
new file mode 100644
@@ -0,0 +1,231 @@
+xdrgen - Linux Kernel XDR code generator
+
+Introduction
+------------
+
+SunRPC programs are typically specified using a language defined by
+RFC 4506. In fact, all IETF-published NFS specifications provide a
+description of the specified protocol using this language.
+
+Since the 1990's, user space consumers of SunRPC have had access to
+a tool that could read such XDR specifications and then generate C
+code that implements the RPC portions of that protocol. This tool is
+called rpcgen.
+
+This RPC-level code is code that handles input directly from the
+network, and thus a high degree of memory safety and sanity checking
+is needed to help ensure proper levels of security. Bugs in this
+code can have significant impact on security and performance.
+
+However, it is code that is repetitive and tedious to write by hand.
+
+The C code generated by rpcgen makes extensive use of the facilities
+of the user space TI-RPC library and libc. Furthermore, the dialect
+of the generated code is very traditional K&R C.
+
+The Linux kernel's implementation of SunRPC-based protocols hand-roll
+their XDR implementation. There are two main reasons for this:
+
+1. libtirpc (and its predecessors) operate only in user space. The
+ kernel's RPC implementation and its API are significantly
+ different than libtirpc.
+
+2. rpcgen-generated code is believed to be less efficient than code
+ that is hand-written.
+
+These days, gcc and its kin are capable of optimizing code better
+than human authors. There are only a few instances where writing
+XDR code by hand will make a measurable performance different.
+
+In addition, the current hand-written code in the Linux kernel is
+difficult to audit and prove that it implements exactly what is in
+the protocol specification.
+
+In order to accrue the benefits of machine-generated XDR code in the
+kernel, a tool is needed that will output C code that works against
+the kernel's SunRPC implementation rather than libtirpc.
+
+Enter xdrgen.
+
+
+Dependencies
+------------
+
+These dependencies are typically packaged by Linux distributions:
+
+- python3
+- python3-lark
+- python3-jinja2
+
+These dependencies are available via PyPi:
+
+- pip install 'lark[interegular]'
+
+
+XDR Specifications
+------------------
+
+XDR specifications for SunRPC-based protocols implemented in the
+Linux kernel are located here:
+
+ Documentation/sunrpc/xdr/
+
+A symlink has been added to the xdrgen directory for easy access to
+those files.
+
+When adding a new protocol implementation to the kernel, the XDR
+specification can be derived by feeding a .txt copy of the RFC to
+the script located in tools/net/sunrpc/extract.sh.
+
+ $ extract.sh < rfc0001.txt > xdr/new2.x
+
+
+Operation
+---------
+
+Once a .x file is available, use xdrgen to generate a C source and
+header file containing an implementation of XDR encoding and
+decoding functions for the specified protocol.
+
+ $ ./xdrgen header xdr/new.x > new2xdr_gen.h
+
+and
+
+ $ ./xdrgen source xdr/new.x > new2xdr_gen.c
+
+The files are ready to use for a server-side protocol implementation,
+or may be used as a guide for implementing these routines by hand.
+
+By default, the only comments added to this code are kdoc comments
+that appear directly in front of the public per-procedure APIs. For
+deeper introspection, specifying the "--annotate" flag will insert
+additional comments in the generated code to help readers match the
+generated code to specific parts of the XDR specification.
+
+Because the generated code is targeted for the Linux kernel, it
+is tagged with a GPLv2-only license.
+
+The xdrgen tool can also provide lexical and syntax checking of
+an XDR specification:
+
+ $ ./xdrgen lint xdr/new.x
+
+
+How It Works
+------------
+
+xdrgen does not use machine learning to generate source code. The
+translation is entirely deterministic.
+
+RFC 4506 Section 6 contains a BNF grammar of the XDR specification
+language. The grammar has been adapted for use by the Python Lark
+module.
+
+The xdr.ebnf file in this directory contains the grammar used to
+parse XDR specifications. xdrgen configures Lark using the grammar
+in xdr.ebnf. Lark parses the target XDR specification using this
+grammar, creating a parse tree.
+
+xdrgen then transforms the parse tree into an abstract syntax tree.
+This tree is passed to a series of code generators.
+
+The generators are implemented as Python classes residing in the
+generators/ directory. Each generator emits code created from Jinja2
+templates stored in the templates/ directory.
+
+The source code is generated in the same order in which they appear
+in the specification to ensure the generated code compiles. This
+conforms with the behavior of rpcgen.
+
+xdrgen assumes that the generated source code is further compiled by
+a compiler that can optimize in a number of ways, including:
+
+ - Unused functions are discarded (ie, not added to the executable)
+
+ - Aggressive function inlining removes unnecessary stack frames
+
+ - Single-arm switch statements are replaced by a single conditional
+ branch
+
+And so on.
+
+
+Pragmas
+-------
+
+Pragma directives specify exceptions to the normal generation of
+encoding and decoding functions. Currently one directive is
+implemented: "public".
+
+Pragma header
+------ ------
+
+ pragma header <string> ;
+
+Provide a name to use for the header file and header guards.
+
+For example:
+
+ pragma header NLM4;
+
+Adds header guards in the generated header file called
+
+ NLM4_XDR_GEN_H
+
+And adds
+
+ #include "nlm4xdr_gen.h"
+
+to the generated C source file.
+
+Pragma public
+------ ------
+
+ pragma public <XDR data item> ;
+
+Normally XDR encoder and decoder functions are "static". In case an
+implementer wants to call these functions from other source code,
+s/he can add a public pragma in the input .x file to indicate a set
+of functions that should get a prototype in the generated header,
+and the function definitions will not be declared static.
+
+For example:
+
+ pragma public nfsstat3;
+
+Adds these prototypes in the generated header:
+
+ bool xdrgen_decode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 *ptr);
+ bool xdrgen_encode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 value);
+
+And, in the generated source code, both of these functions appear
+without the "static __maybe_unused" declaration.
+
+
+Future Work
+-----------
+
+Enable xdrgen to be run in any directory rather than only in
+tools/net/sunrpc/xdrgen.
+
+Add more pragma directives:
+
+ * @exclude -- don't emit procedure argument or result encoders;
+ the specified procedure will be hand-rolled
+ * @pages -- use xdr_read/write_pages() for the specified opaque
+ field
+ * @skip -- do not decode, but rather skip, the specified argument
+ field
+
+Enable something like a #include to dynamically insert the content
+of other specification files
+
+Properly support line-by-line pass-through via the "%" decorator
+
+Build a unit test suite for verifying translation of XDR language
+into C code
+
+Generate kernel Rust code as well as C code
+
+Add a command-line option to insert trace_printk call sites in the
+generated source code, for improved (temporary) observability
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Utility functions and global variables for xdrgen."""
+
+global annotate
+annotate = False
+
+
+def set_xdr_annotate(set_it):
+ """Set 'annotate' if --annotate was specified on the command line"""
+ global annotate
+
+ annotate = set_it
+
+
+def get_xdr_annotate():
+ """Return True if --annotate was specified on the command line"""
+ global annotate
+
+ return annotate
new file mode 100644
new file mode 100644
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate boilerplate items"""
+
+import os.path
+import time
+from jinja2 import Environment, FileSystemLoader
+
+from xdr_ast import _RpcProgram, get_header_name
+
+
+def find_xdr_program_name(ast):
+ """Retrieve the RPC program name from an abstract syntax tree"""
+ raw_name = get_header_name()
+ if raw_name != "none":
+ return raw_name.lower()
+ for definition in ast.definitions:
+ if isinstance(definition.value, _RpcProgram):
+ raw_name = definition.value.name
+ return raw_name.lower().removesuffix("_program").removesuffix("_prog")
+ return "noprog"
+
+
+class SourceGenerator:
+ """Generate source code boilerplate"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/boilerplate/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_header_bottom(self, ast):
+ """Emit the bottom header guard"""
+ name = find_xdr_program_name(ast)
+ template = self.environment.get_template("header_bottom.j2")
+ print(template.render(program=name))
+
+ def emit_header_top(self, filename, ast):
+ """Emit the top header guard"""
+ name = find_xdr_program_name(ast)
+ template = self.environment.get_template("header_top.j2")
+ print(
+ template.render(
+ program=name,
+ mtime=time.ctime(os.path.getmtime(filename)),
+ )
+ )
+
+ def emit_source_top(self, filename, ast):
+ """Emit the top source boilerplate"""
+ name = find_xdr_program_name(ast)
+ template = self.environment.get_template("source_top.j2")
+ print(
+ template.render(
+ program=name,
+ mtime=time.ctime(os.path.getmtime(filename)),
+ )
+ )
new file mode 100644
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR constants"""
+
+from jinja2 import Environment, FileSystemLoader
+
+
+class SourceGenerator:
+ """Generate source code for XDR constants"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/constants/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_declaration(self, ast_node):
+ """Emit one declaration for a constant"""
+ template = self.environment.get_template("declaration.j2")
+ print(
+ template.render(
+ name=ast_node.name,
+ value=ast_node.value,
+ )
+ )
+
+ def emit_decoder(self, ast_node):
+ """Emit one decoder for a constant"""
+
+ def emit_encoder(self, ast_node):
+ """Emit one encoder for a constant"""
new file mode 100644
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR enum types"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from xdr_ast import public_apis
+from common import get_xdr_annotate
+
+
+class SourceGenerator:
+ """Generate source code for XDR enum types"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/enum/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_declaration(self, ast_node):
+ """Emit one declaration for an enum type"""
+
+ template = self.environment.get_template("declaration/open.j2")
+ print(template.render(name=ast_node.name))
+
+ template = self.environment.get_template("declaration/enumerator.j2")
+ for enumerator in ast_node.enumerators:
+ print(
+ template.render(
+ name=enumerator.name,
+ value=enumerator.value,
+ )
+ )
+
+ template = self.environment.get_template("declaration/close.j2")
+ print(template.render(public_apis=public_apis, name=ast_node.name))
+
+ def emit_decoder(self, ast_node):
+ """Emit one decoder function for an enum type"""
+
+ template = self.environment.get_template("decoder/enum.j2")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ def emit_encoder(self, ast_node):
+ """Emit one encoder function for an enum type"""
+
+ template = self.environment.get_template("encoder/enum.j2")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
new file mode 100644
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code for an RPC program's procedures"""
+
+from jinja2 import Environment, FileSystemLoader
+
+
+def emit_version_declarations(environment, program, version):
+ """Emit C declarations for each RPC version's procedures"""
+ print("")
+ template = environment.get_template("declaration/argument.j2")
+ for procedure in version.procedures:
+ print(
+ template.render(
+ program=program,
+ argument=procedure.argument.type_name,
+ )
+ )
+
+ print("")
+ template = environment.get_template("declaration/result.j2")
+ for procedure in version.procedures:
+ print(template.render(program=program, result=procedure.result.type_name))
+
+
+def emit_version_argument_decoders(environment, program, version):
+ """Emit C decoders for each RPC version's procedures"""
+ arguments = set()
+ for procedure in version.procedures:
+ arguments.add(procedure.argument.type_name)
+
+ template = environment.get_template("decoder/argument.j2")
+ for argument in arguments:
+ print(
+ template.render(
+ program=program,
+ argument=argument,
+ )
+ )
+
+
+def emit_version_result_encoders(environment, program, version):
+ """Emit C encoders for each RPC version's procedures"""
+ results = set()
+ for procedure in version.procedures:
+ results.add(procedure.result.type_name)
+
+ template = environment.get_template("encoder/result.j2")
+ for result in results:
+ print(
+ template.render(
+ program=program,
+ result=result,
+ )
+ )
+
+
+class SourceGenerator:
+ """Generate source code for an RPC program's procedures"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/program/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_declaration(self, ast_node):
+ """Emit a declarations pair for each of an RPC programs's procedures"""
+ raw_name = ast_node.name
+ program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
+
+ for version in ast_node.versions:
+ emit_version_declarations(self.environment, program, version)
+
+ def emit_decoder(self, ast_node):
+ """Emit all decoder functions for an RPC program's procedures"""
+ raw_name = ast_node.name
+ program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
+
+ for version in ast_node.versions:
+ emit_version_argument_decoders(self.environment, program, version)
+
+ def emit_encoder(self, ast_node):
+ """Emit all encoder functions for an RPC program's procedures"""
+ raw_name = ast_node.name
+ program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
+
+ for version in ast_node.versions:
+ emit_version_result_encoders(self.environment, program, version)
new file mode 100644
@@ -0,0 +1,435 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR struct types"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from common import get_xdr_annotate
+
+from xdr_ast import _BasicDeclaration, _VariableLengthString
+from xdr_ast import _FixedLengthOpaque, _VariableLengthOpaque
+from xdr_ast import _FixedLengthArray, _VariableLengthArray
+from xdr_ast import _XdrOptionalData, _BuiltInType, public_apis
+
+
+def get_kernel_c_type(type_spec):
+ """Return C type to be used for an XDR built-in type"""
+ xdr_type_to_c_type = {
+ "unsigned_hyper": "u64",
+ "hyper": "s64",
+ "unsigned_long": "u32",
+ "long": "s32",
+ "unsigned_int": "u32",
+ "int": "s32",
+ "bool": "bool",
+ }
+ if isinstance(type_spec, _BuiltInType):
+ return xdr_type_to_c_type[type_spec.type_name]
+ return type_spec.type_name
+
+
+def get_jinja_template(environment, template_type, template_name):
+ """Retrieve a Jinja2 template for emitting source code"""
+ return environment.get_template(template_type + "/" + template_name + ".j2")
+
+
+def emit_struct_member_definition(environment, field):
+ """Emit C definition for one field in one XDR struct"""
+ if isinstance(field, _BasicDeclaration):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=get_kernel_c_type(field.spec),
+ ctype=field.spec.type_decorator,
+ )
+ )
+ elif isinstance(field, _FixedLengthOpaque):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ size=field.size,
+ )
+ )
+ elif isinstance(field, _VariableLengthOpaque):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ )
+ )
+ elif isinstance(field, _VariableLengthString):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ )
+ )
+ elif isinstance(field, _FixedLengthArray):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=get_kernel_c_type(field.spec),
+ size=field.size,
+ )
+ )
+ elif isinstance(field, _VariableLengthArray):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=get_kernel_c_type(field.spec),
+ ctype=field.spec.type_decorator,
+ )
+ )
+ elif isinstance(field, _XdrOptionalData):
+ template = get_jinja_template(environment, "declaration", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=get_kernel_c_type(field.spec),
+ ctype=field.spec.type_decorator,
+ )
+ )
+
+
+def emit_struct_definition(environment, ast_node):
+ """Emit one C declaration for a struct type"""
+ template = get_jinja_template(environment, "declaration", "open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.name,
+ )
+ )
+
+ for field in ast_node.fields:
+ emit_struct_member_definition(environment, field)
+
+ template = get_jinja_template(environment, "declaration", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+
+def emit_pointer_definition(environment, ast_node):
+ """Emit one C definition for a pointer type"""
+ template = get_jinja_template(environment, "declaration", "pointer-open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.name,
+ )
+ )
+
+ for field in ast_node.fields[0:-1]:
+ emit_struct_member_definition(environment, field)
+
+ template = get_jinja_template(environment, "declaration", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+
+def emit_struct_member_decoder(environment, field):
+ """Emit C decoder for one field in one XDR struct"""
+ if isinstance(field, _BasicDeclaration):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ ctype=field.spec.type_decorator,
+ )
+ )
+ elif isinstance(field, _FixedLengthOpaque):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ size=field.size,
+ )
+ )
+ elif isinstance(field, _VariableLengthOpaque):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ maxsize=field.maxsize,
+ )
+ )
+ elif isinstance(field, _VariableLengthString):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ maxsize=field.maxsize,
+ )
+ )
+ elif isinstance(field, _FixedLengthArray):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ size=field.size,
+ ctype=field.spec.type_decorator,
+ )
+ )
+ elif isinstance(field, _VariableLengthArray):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ maxsize=field.maxsize,
+ ctype=field.spec.type_decorator,
+ )
+ )
+ elif isinstance(field, _XdrOptionalData):
+ template = get_jinja_template(environment, "decoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ ctype=field.spec.type_decorator,
+ )
+ )
+
+
+def emit_struct_decoder(environment, ast_node):
+ """Emit one decoder function for a struct type"""
+ template = get_jinja_template(environment, "decoder", "open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ for field in ast_node.fields:
+ emit_struct_member_decoder(environment, field)
+
+ template = get_jinja_template(environment, "decoder", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ )
+ )
+
+
+# cel: Would be better to just create a new dataclass for pointer types,
+# and copy all of this to generators/pointer.py
+def emit_pointer_decoder(environment, ast_node):
+ """Emit one decoder function for a struct type"""
+ template = get_jinja_template(environment, "decoder", "pointer-open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ for field in ast_node.fields[0:-1]:
+ emit_struct_member_decoder(environment, field)
+
+ template = get_jinja_template(environment, "decoder", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ )
+ )
+
+
+def emit_struct_member_encoder(environment, field):
+ """Emit C encoder for one field in one XDR struct"""
+ if isinstance(field, _BasicDeclaration):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ encode_needs_addressof=field.spec.pass_by_reference,
+ )
+ )
+ elif isinstance(field, _FixedLengthOpaque):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ size=field.size,
+ )
+ )
+ elif isinstance(field, _VariableLengthOpaque):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ maxsize=field.maxsize,
+ )
+ )
+ elif isinstance(field, _VariableLengthString):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ maxsize=field.maxsize,
+ )
+ )
+ elif isinstance(field, _FixedLengthArray):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ size=field.size,
+ encode_needs_addressof=field.spec.pass_by_reference,
+ )
+ )
+ elif isinstance(field, _VariableLengthArray):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ maxsize=field.maxsize,
+ encode_needs_addressof=field.spec.pass_by_reference,
+ )
+ )
+ elif isinstance(field, _XdrOptionalData):
+ template = get_jinja_template(environment, "encoder", field.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=field.name,
+ type=field.spec.type_name,
+ ctype=field.spec.type_decorator,
+ )
+ )
+
+
+def emit_struct_encoder(environment, ast_node):
+ """Emit one encoder function for a struct type"""
+ template = get_jinja_template(environment, "encoder", "open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ for field in ast_node.fields:
+ emit_struct_member_encoder(environment, field)
+
+ template = get_jinja_template(environment, "encoder", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ )
+ )
+
+
+# cel: Would be better to just create a new dataclass for pointer types,
+# and copy all of this to generators/pointer.py
+def emit_pointer_encoder(environment, ast_node):
+ """Emit one encoder function for a struct type"""
+ template = get_jinja_template(environment, "encoder", "pointer-open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ for field in ast_node.fields[0:-1]:
+ emit_struct_member_encoder(environment, field)
+
+ template = get_jinja_template(environment, "encoder", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ )
+ )
+
+
+def is_pointer_type(ast_node):
+ """Predicate: returns true if @ast_node should be a pointer type"""
+ last_field = ast_node.fields[-1]
+ if (
+ isinstance(last_field, _XdrOptionalData)
+ and ast_node.name == last_field.spec.type_name
+ ):
+ return True
+ return False
+
+
+class SourceGenerator:
+ """Generate source code for XDR structs"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/struct/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_declaration(self, ast_node):
+ """Emit one declaration for a struct type"""
+ if is_pointer_type(ast_node):
+ emit_pointer_definition(self.environment, ast_node)
+ else:
+ emit_struct_definition(self.environment, ast_node)
+
+ def emit_decoder(self, ast_node):
+ """Emit one decoder function for a struct type"""
+ if is_pointer_type(ast_node):
+ emit_pointer_decoder(self.environment, ast_node)
+ else:
+ emit_struct_decoder(self.environment, ast_node)
+
+ def emit_encoder(self, ast_node):
+ """Emit one encoder function for a struct type"""
+ if is_pointer_type(ast_node):
+ emit_pointer_encoder(self.environment, ast_node)
+ else:
+ emit_struct_encoder(self.environment, ast_node)
new file mode 100644
@@ -0,0 +1,282 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR typedefs"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from common import get_xdr_annotate
+
+from xdr_ast import _BasicDeclaration, _VariableLengthString
+from xdr_ast import _FixedLengthOpaque, _VariableLengthOpaque
+from xdr_ast import _FixedLengthArray, _VariableLengthArray
+from xdr_ast import _XdrOptionalData, _XdrVoid, _BuiltInType
+from xdr_ast import public_apis
+
+
+def get_kernel_c_type(type_spec):
+ """Return C type to be used for an XDR built-in type"""
+ xdr_type_to_c_type = {
+ "unsigned_hyper": "u64",
+ "hyper": "s64",
+ "unsigned_long": "u32",
+ "long": "s32",
+ "unsigned_int": "u32",
+ "int": "s32",
+ "bool": "bool",
+ }
+ if isinstance(type_spec, _BuiltInType):
+ return xdr_type_to_c_type[type_spec.type_name]
+ return type_spec.type_name
+
+
+def get_jinja_template(environment, template_type, template_name):
+ """Retrieve a Jinja2 template for emitting source code"""
+ return environment.get_template(template_type + "/" + template_name + ".j2")
+
+
+def emit_type_definition(environment, ast_node):
+ """Emit C declaration for one XDR typedef"""
+ if isinstance(ast_node, _BasicDeclaration):
+ template = get_jinja_template(environment, "declaration", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=get_kernel_c_type(ast_node.spec),
+ ctype=ast_node.spec.type_decorator,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthString):
+ template = get_jinja_template(environment, "declaration", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+ elif isinstance(ast_node, _FixedLengthOpaque):
+ template = get_jinja_template(environment, "declaration", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ size=ast_node.size,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthOpaque):
+ template = get_jinja_template(environment, "declaration", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+ elif isinstance(ast_node, _FixedLengthArray):
+ template = get_jinja_template(environment, "declaration", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ size=ast_node.size,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthArray):
+ template = get_jinja_template(environment, "declaration", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ ctype=ast_node.spec.type_decorator,
+ )
+ )
+ elif isinstance(ast_node, _XdrOptionalData):
+ raise NotImplementedError("<optional_data> typedef not yet implemented")
+ elif isinstance(ast_node, _XdrVoid):
+ raise NotImplementedError("<void> typedef not yet implemented")
+ else:
+ raise NotImplementedError("typedef: type not recognized")
+
+
+def emit_typedef_decoder(environment, ast_node):
+ """Emit C decoder function for one typedef"""
+ if isinstance(ast_node, _BasicDeclaration):
+ template = get_jinja_template(environment, "decoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthString):
+ template = get_jinja_template(environment, "decoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ maxsize=ast_node.maxsize,
+ )
+ )
+ elif isinstance(ast_node, _FixedLengthOpaque):
+ template = get_jinja_template(environment, "decoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ size=ast_node.size,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthOpaque):
+ template = get_jinja_template(environment, "decoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ maxsize=ast_node.maxsize,
+ )
+ )
+ elif isinstance(ast_node, _FixedLengthArray):
+ template = get_jinja_template(environment, "decoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ size=ast_node.size,
+ ctype=ast_node.spec.type_decorator,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthArray):
+ template = get_jinja_template(environment, "decoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ maxsize=ast_node.maxsize,
+ )
+ )
+ elif isinstance(ast_node, _XdrOptionalData):
+ raise NotImplementedError("<optional_data> typedef not yet implemented")
+ elif isinstance(ast_node, _XdrVoid):
+ raise NotImplementedError("<void> typedef not yet implemented")
+ else:
+ raise NotImplementedError("typedef: type not recognized")
+
+
+def emit_typedef_encoder(environment, ast_node):
+ """Emit one encoder function for one typedef"""
+ if isinstance(ast_node, _BasicDeclaration):
+ template = get_jinja_template(environment, "encoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ encode_needs_addressof=ast_node.spec.pass_by_reference,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthString):
+ template = get_jinja_template(environment, "encoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ maxsize=ast_node.maxsize,
+ )
+ )
+ elif isinstance(ast_node, _FixedLengthOpaque):
+ template = get_jinja_template(environment, "encoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ size=ast_node.size,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthOpaque):
+ template = get_jinja_template(environment, "encoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ maxsize=ast_node.maxsize,
+ )
+ )
+ elif isinstance(ast_node, _FixedLengthArray):
+ template = get_jinja_template(environment, "encoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ size=ast_node.size,
+ encode_needs_addressof=ast_node.spec.pass_by_reference,
+ )
+ )
+ elif isinstance(ast_node, _VariableLengthArray):
+ template = get_jinja_template(environment, "encoder", ast_node.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ maxsize=ast_node.maxsize,
+ encode_needs_addressof=ast_node.spec.pass_by_reference,
+ )
+ )
+ elif isinstance(ast_node, _XdrOptionalData):
+ raise NotImplementedError("<optional_data> typedef not yet implemented")
+ elif isinstance(ast_node, _XdrVoid):
+ raise NotImplementedError("<void> typedef not yet implemented")
+ else:
+ raise NotImplementedError("typedef: type not recognized")
+
+
+class SourceGenerator:
+ """Generate source code for XDR typedefs"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/typedef/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_declaration(self, ast_node):
+ """Emit one declaration for an XDR typedef"""
+ emit_type_definition(self.environment, ast_node.declaration)
+
+ def emit_decoder(self, ast_node):
+ """Emit one decoder function for an XDR typedef"""
+ emit_typedef_decoder(self.environment, ast_node.declaration)
+
+ def emit_encoder(self, ast_node):
+ """Emit one encoder function for an XDR typedef"""
+ emit_typedef_encoder(self.environment, ast_node.declaration)
new file mode 100644
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code to handle XDR unions"""
+
+from jinja2 import Environment, FileSystemLoader
+
+from common import get_xdr_annotate
+
+from xdr_ast import _BasicDeclaration, _XdrVoid, public_apis
+
+
+def get_jinja_template(environment, template_type, template_name):
+ """Retrieve a Jinja2 template for emitting source code"""
+ return environment.get_template(template_type + "/" + template_name + ".j2")
+
+
+def emit_union_switch_spec_definition(environment, ast_node):
+ """Emit C declaration for an XDR union's discriminant"""
+ assert isinstance(ast_node, _BasicDeclaration)
+ template = get_jinja_template(environment, "declaration", "switch_spec")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ ctype=ast_node.spec.type_decorator,
+ )
+ )
+
+
+def emit_union_case_spec_definition(environment, ast_node):
+ """Emit C declaration for an XDR union's case arm"""
+ if isinstance(ast_node.arm, _XdrVoid):
+ return
+ assert isinstance(ast_node.arm, _BasicDeclaration)
+ template = get_jinja_template(environment, "declaration", "case_spec")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.arm.name,
+ type=ast_node.arm.spec.type_name,
+ ctype=ast_node.arm.spec.type_decorator,
+ )
+ )
+
+
+def emit_union_declaration(environment, ast_node):
+ """Emit one C union definition"""
+ template = get_jinja_template(environment, "declaration", "open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.name,
+ )
+ )
+
+ emit_union_switch_spec_definition(environment, ast_node.discriminant)
+
+ for case in ast_node.cases:
+ emit_union_case_spec_definition(environment, case)
+
+ if ast_node.default is not None:
+ emit_union_case_spec_definition(environment, ast_node.default)
+
+ template = get_jinja_template(environment, "declaration", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+
+def emit_union_switch_spec_decoder(environment, ast_node):
+ """Emit C decoder for an XDR union's discriminant"""
+ assert isinstance(ast_node, _BasicDeclaration)
+ template = get_jinja_template(environment, "decoder", "switch_spec")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ )
+ )
+
+
+def emit_union_case_spec_decoder(environment, ast_node):
+ """Emit C decoder functions for an XDR union's case arm"""
+
+ if isinstance(ast_node.arm, _XdrVoid):
+ return
+
+ template = get_jinja_template(environment, "decoder", "case_spec")
+ for case in ast_node.values:
+ match case:
+ case "TRUE":
+ print(template.render(case="true"))
+ case "FALSE":
+ print(template.render(case="false"))
+ case _:
+ print(template.render(case=case))
+
+ assert isinstance(ast_node.arm, _BasicDeclaration)
+ template = get_jinja_template(environment, "decoder", ast_node.arm.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.arm.name,
+ type=ast_node.arm.spec.type_name,
+ ctype=ast_node.arm.spec.type_decorator,
+ )
+ )
+
+ template = get_jinja_template(environment, "decoder", "break")
+ print(template.render())
+
+
+def emit_union_default_spec_decoder(environment, ast_node):
+ """Emit C decoder function for an XDR union's default arm"""
+ default_case = ast_node.default
+
+ # Avoid a gcc warning about a default case with boolean discriminant
+ if default_case is None and ast_node.discriminant.spec.type_name == "bool":
+ return
+
+ template = get_jinja_template(environment, "decoder", "default_spec")
+ print(template.render())
+
+ if default_case is None or isinstance(default_case.arm, _XdrVoid):
+ template = get_jinja_template(environment, "decoder", "break")
+ print(template.render())
+ return
+
+ assert isinstance(default_case.arm, _BasicDeclaration)
+ template = get_jinja_template(environment, "decoder", default_case.arm.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=default_case.arm.name,
+ type=default_case.arm.spec.type_name,
+ ctype=default_case.arm.spec.type_decorator,
+ )
+ )
+
+
+def emit_union_decoder(environment, ast_node):
+ """Emit one C union decoder"""
+ template = get_jinja_template(environment, "decoder", "open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ emit_union_switch_spec_decoder(environment, ast_node.discriminant)
+
+ for case in ast_node.cases:
+ emit_union_case_spec_decoder(environment, case)
+
+ emit_union_default_spec_decoder(environment, ast_node)
+
+ template = get_jinja_template(environment, "decoder", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ )
+ )
+
+
+def emit_union_switch_spec_encoder(environment, ast_node):
+ """Emit C encoder for an XDR union's discriminant"""
+ assert isinstance(ast_node, _BasicDeclaration)
+ template = get_jinja_template(environment, "encoder", "switch_spec")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.name,
+ type=ast_node.spec.type_name,
+ )
+ )
+
+
+def emit_union_case_spec_encoder(environment, ast_node):
+ """Emit C encoder functions for an XDR union's case arm"""
+
+ if isinstance(ast_node.arm, _XdrVoid):
+ return
+
+ template = get_jinja_template(environment, "encoder", "case_spec")
+ for case in ast_node.values:
+ match case:
+ case "TRUE":
+ print(template.render(case="true"))
+ case "FALSE":
+ print(template.render(case="false"))
+ case _:
+ print(template.render(case=case))
+
+ assert isinstance(ast_node.arm, _BasicDeclaration)
+ template = get_jinja_template(environment, "encoder", ast_node.arm.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=ast_node.arm.name,
+ type=ast_node.arm.spec.type_name,
+ encode_needs_addressof=ast_node.arm.spec.pass_by_reference,
+ )
+ )
+
+ template = get_jinja_template(environment, "encoder", "break")
+ print(template.render())
+
+
+def emit_union_default_spec_encoder(environment, ast_node):
+ """Emit C encoder function for an XDR union's default arm"""
+ default_case = ast_node.default
+
+ # Avoid a gcc warning about a default case with boolean discriminant
+ if default_case is None and ast_node.discriminant.spec.type_name == "bool":
+ return
+
+ template = get_jinja_template(environment, "encoder", "default_spec")
+ print(template.render())
+
+ if default_case is None or isinstance(default_case.arm, _XdrVoid):
+ template = get_jinja_template(environment, "encoder", "break")
+ print(template.render())
+ return
+
+ assert isinstance(default_case.arm, _BasicDeclaration)
+ template = get_jinja_template(environment, "encoder", default_case.arm.template)
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ name=default_case.arm.name,
+ type=default_case.arm.spec.type_name,
+ encode_needs_addressof=default_case.arm.spec.pass_by_reference,
+ )
+ )
+
+
+def emit_union_encoder(environment, ast_node):
+ """Emit one C union encoder"""
+ template = get_jinja_template(environment, "encoder", "open")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ public_apis=public_apis,
+ name=ast_node.name,
+ )
+ )
+
+ emit_union_switch_spec_encoder(environment, ast_node.discriminant)
+
+ for case in ast_node.cases:
+ emit_union_case_spec_encoder(environment, case)
+
+ emit_union_default_spec_encoder(environment, ast_node)
+
+ template = get_jinja_template(environment, "encoder", "close")
+ print(
+ template.render(
+ annotate=get_xdr_annotate(),
+ )
+ )
+
+
+class SourceGenerator:
+ """Generate source code for XDR unions"""
+
+ def __init__(self, language):
+ """Set the output language"""
+ match language:
+ case "C":
+ self.environment = Environment(
+ loader=FileSystemLoader("templates/C/union/"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ case _:
+ raise NotImplementedError("Language not supported")
+
+ def emit_declaration(self, ast_node):
+ """Emit one declaration for an XDR union"""
+ emit_union_declaration(self.environment, ast_node)
+
+ def emit_decoder(self, ast_node):
+ """Emit one decoder function for an XDR union"""
+ emit_union_decoder(self.environment, ast_node)
+
+ def emit_encoder(self, ast_node):
+ """Emit one encoder function for an XDR union"""
+ emit_union_encoder(self.environment, ast_node)
new file mode 100644
new file mode 100755
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+import logging
+
+from lark import logger
+
+from generators import boilerplate, constant, enum
+from generators import program, typedef, struct, union
+from xdr_ast import transform_parse_tree, _RpcProgram, _XdrEnum
+from xdr_ast import _XdrConstant, _XdrTypedef, _XdrStruct, _XdrUnion
+from xdr_parse import xdr_parser
+
+logger.setLevel(logging.INFO)
+
+
+def generate_header(filename, ast, language):
+ """Generate and emit a header file"""
+
+ sg = boilerplate.SourceGenerator(language)
+ sg.emit_header_top(filename, ast)
+
+ for definition in ast.definitions:
+ if isinstance(definition.value, _XdrConstant):
+ sg = constant.SourceGenerator(language)
+ elif isinstance(definition.value, _XdrEnum):
+ sg = enum.SourceGenerator(language)
+ elif isinstance(definition.value, _XdrTypedef):
+ sg = typedef.SourceGenerator(language)
+ elif isinstance(definition.value, _XdrStruct):
+ sg = struct.SourceGenerator(language)
+ elif isinstance(definition.value, _XdrUnion):
+ sg = union.SourceGenerator(language)
+ elif isinstance(definition.value, _RpcProgram):
+ sg = program.SourceGenerator(language)
+ else:
+ continue
+ sg.emit_declaration(definition.value)
+
+ generator = boilerplate.SourceGenerator(language)
+ generator.emit_header_bottom(ast)
+
+
+def handle_parse_error(e):
+ """Simple parse error reporting, no recovery attempted"""
+ print(e)
+ return True
+
+
+def subcmd(args):
+ """Generate definitions and declarations"""
+
+ parser = xdr_parser()
+ with open(args.filename, encoding="utf-8") as f:
+ parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+ ast = transform_parse_tree(parse_tree)
+ generate_header(args.filename, ast, args.language)
+
+ return 0
new file mode 100755
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+import logging
+
+from lark import logger
+from xdr_parse import xdr_parser
+
+logger.setLevel(logging.DEBUG)
+
+
+def handle_parse_error(e):
+ """Simple parse error reporting, no recovery attempted"""
+ print(e)
+ return True
+
+
+def subcmd(args):
+ """Lexical and syntax check of an XDR specification"""
+
+ parser = xdr_parser()
+ with open(args.filename, encoding="utf-8") as f:
+ parser.parse(f.read(), on_error=handle_parse_error)
+
+ return 0
new file mode 100755
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+import logging
+
+from lark import logger
+
+from generators import boilerplate, enum, program
+from generators import typedef, struct, union
+from xdr_ast import transform_parse_tree, _XdrEnum, _XdrTypedef
+from xdr_ast import _XdrStruct, _XdrUnion, _RpcProgram
+from xdr_parse import xdr_parser
+
+logger.setLevel(logging.INFO)
+
+
+def emit_source_decoder(ast_node, language):
+ """Emit one XDR decoder function for a source file"""
+ if isinstance(ast_node, _XdrEnum):
+ sg = enum.SourceGenerator(language)
+ elif isinstance(ast_node, _XdrTypedef):
+ sg = typedef.SourceGenerator(language)
+ elif isinstance(ast_node, _XdrStruct):
+ sg = struct.SourceGenerator(language)
+ elif isinstance(ast_node, _XdrUnion):
+ sg = union.SourceGenerator(language)
+ elif isinstance(ast_node, _RpcProgram):
+ sg = program.SourceGenerator(language)
+ else:
+ return
+ sg.emit_decoder(ast_node)
+
+
+def emit_source_encoder(ast_node, language):
+ """Emit one XDR encoder function for a source file"""
+ if isinstance(ast_node, _XdrEnum):
+ sg = enum.SourceGenerator(language)
+ elif isinstance(ast_node, _XdrTypedef):
+ sg = typedef.SourceGenerator(language)
+ elif isinstance(ast_node, _XdrStruct):
+ sg = struct.SourceGenerator(language)
+ elif isinstance(ast_node, _XdrUnion):
+ sg = union.SourceGenerator(language)
+ elif isinstance(ast_node, _RpcProgram):
+ sg = program.SourceGenerator(language)
+ else:
+ return
+ sg.emit_encoder(ast_node)
+
+
+def generate_source(filename, ast, language):
+ """Generate and emit a C source file"""
+
+ sg = boilerplate.SourceGenerator(language)
+ sg.emit_source_top(filename, ast)
+
+ for definition in ast.definitions:
+ emit_source_decoder(definition.value, language)
+
+ for definition in ast.definitions:
+ emit_source_encoder(definition.value, language)
+
+
+def handle_parse_error(e):
+ """Simple parse error reporting, no recovery attempted"""
+ print(e)
+ return True
+
+
+def subcmd(args):
+ """Generate encoder and decoder functions"""
+
+ parser = xdr_parser()
+ with open(args.filename, encoding="utf-8") as f:
+ parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+ ast = transform_parse_tree(parse_tree)
+ generate_source(args.filename, ast, args.language)
+
+ return 0
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+#endif /* _LINUX_{{ program.upper() }}_XDRGEN_H */
new file mode 100644
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Generated by xdrgen. Manual edits will be lost. */
+/* XDR specification modification time: {{ mtime }} */
+
+#ifndef _LINUX_{{ program.upper() }}_XDRGEN_H
+#define _LINUX_{{ program.upper() }}_XDRGEN_H
+
+#include <linux/types.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/sunrpc/xdrgen-builtins.h>
new file mode 100644
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+// Generated by xdrgen. Manual edits will be lost.
+// XDR specification modification time: {{ mtime }}
+
+#include "{{ program }}xdr_gen.h"
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+enum {
+ {{ name }} = {{ value }}
+};
new file mode 100644
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ {{ name }} = {{ value }},
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+enum {{ name }} {
new file mode 100644
@@ -0,0 +1,19 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* enum {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr)
+{
+ u32 val;
+
+ if (xdr_stream_decode_u32(xdr, &val) < 0)
+ return false;
+ *ptr = val;
+ return true;
+}
new file mode 100644
@@ -0,0 +1,14 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* enum {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr);
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr);
new file mode 100644
@@ -0,0 +1,21 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+/**
+ * {{ program }}_svc_decode_{{ argument }} - Decode a {{ argument }} argument
+ * @rqstp: RPC transaction context
+ * @xdr: source XDR data stream
+ *
+ * Return values:
+ * %true: procedure arguments decoded successfully
+ * %false: decode failed
+ */
+bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+{% if argument == 'void' %}
+ return xdrgen_decode_void(xdr);
+{% else %}
+ struct {{ argument }} *argp = rqstp->rq_argp;
+
+ return xdrgen_decode_{{ argument }}(xdr, argp);
+{% endif %}
+}
new file mode 100644
@@ -0,0 +1,21 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+/**
+ * {{ program }}_svc_encode_{{ result }} - Encode a {{ result }} result
+ * @rqstp: RPC transaction context
+ * @xdr: target XDR data stream
+ *
+ * Return values:
+ * %true: procedure results encoded successfully
+ * %false: encode failed
+ */
+bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr)
+{
+{% if result == 'void' %}
+ return xdrgen_encode_void(xdr);
+{% else %}
+ struct {{ result }} *resp = rqstp->rq_resp;
+
+ return xdrgen_encode_{{ result }}(xdr, resp);
+{% endif %}
+}
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (basic) */
+{% endif %}
+ {{ ctype }}{{ type }} {{ name }};
new file mode 100644
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (fixed-length array) */
+{% endif %}
+ {{ type }} {{ name }}[{{ size }}];
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (fixed-length opaque) */
+{% endif %}
+ u8 {{ name }}[{{ size }}];
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* struct {{ name }} */
+{% endif %}
+struct {{ name }} {
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (optional data) */
+{% endif %}
+ {{ ctype }}{{ type }} *{{ name }};
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* pointer {{ name }} */
+{% endif %}
+struct {{ name }} {
new file mode 100644
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (variable-length array) */
+{% endif %}
+ struct {
+ u32 count;
+ {{ ctype }}{{ type }} *element;
+ } {{ name }};
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (variable-length opaque) */
+{% endif %}
+ opaque {{ name }};
new file mode 100644
@@ -0,0 +1,5 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* (variable-length string) */
+{% endif %}
+ string {{ name }};
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (basic) */
+{% endif %}
+ if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
+ return false;
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ return true;
+};
new file mode 100644
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (fixed-length array) */
+{% endif %}
+ for (u32 i = 0; i < {{ size }}; i++) {
+ if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
+ return false;
+ }
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (fixed-length opaque) */
+{% endif %}
+ if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0)
+ return false;
new file mode 100644
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* struct {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
+{
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (optional data) */
+{% endif %}
+ if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }}))
+ return false;
new file mode 100644
@@ -0,0 +1,22 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* pointer {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
+{
+ bool opted;
+
+{% if annotate %}
+ /* opted */
+{% endif %}
+ if (!xdrgen_decode_bool(xdr, &opted))
+ return false;
+ if (!opted)
+ return true;
+
new file mode 100644
@@ -0,0 +1,13 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length array) */
+{% endif %}
+ if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT)
+ return false;
+{% if maxsize != 0 %}
+ if (ptr->{{ name }}.count > {{ maxsize }})
+ return false;
+{% endif %}
+ for (u32 i = 0; i < ptr->{{ name }}.count; i++)
+ if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i]))
+ return false;
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length opaque) */
+{% endif %}
+ if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }}))
+ return false;
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length string) */
+{% endif %}
+ if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }}))
+ return false;
new file mode 100644
@@ -0,0 +1,10 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (basic) */
+{% endif %}
+{% if encode_needs_addressof %}
+ if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}))
+{% else %}
+ if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
+{% endif %}
+ return false;
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ return true;
+};
new file mode 100644
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (fixed-length array) */
+{% endif %}
+ for (u32 i = 0; i < {{ size }}; i++) {
+{% if encode_needs_addressof %}
+ if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0)
+{% else %}
+ if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0)
+{% endif %}
+ return false;
+ }
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (fixed-length opaque) */
+{% endif %}
+ if (xdr_stream_encode_opaque_fixed(xdr, value->{{ name }}, {{ size }}) < 0)
+ return false;
new file mode 100644
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* struct {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value)
+{
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (optional data) */
+{% endif %}
+ if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
+ return false;
new file mode 100644
@@ -0,0 +1,20 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* pointer {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value)
+{
+{% if annotate %}
+ /* opted */
+{% endif %}
+ if (!xdrgen_encode_bool(xdr, value != NULL))
+ return false;
+ if (!value)
+ return true;
+
new file mode 100644
@@ -0,0 +1,15 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length array) */
+{% endif %}
+ if (value->{{ name }}.count > {{ maxsize }})
+ return false;
+ if (xdr_stream_encode_u32(xdr, value->{{ name }}.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value->{{ name }}.count; i++)
+{% if encode_needs_addressof %}
+ if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}.element[i]))
+{% else %}
+ if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}.element[i]))
+{% endif %}
+ return false;
new file mode 100644
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length opaque) */
+{% endif %}
+ if (value->{{ name }}.len > {{ maxsize }})
+ return false;
+ if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
+ return false;
new file mode 100644
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length string) */
+{% endif %}
+ if (value->{{ name }}.len > {{ maxsize }})
+ return false;
+ if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
+ return false;
new file mode 100644
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (basic) */
+{% endif %}
+typedef {{ ctype }}{{ type }} {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (fixed-length array) */
+{% endif %}
+typedef {{ type }}{{ name }}[{{ size }}];
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (fixed-length opaque) */
+{% endif %}
+typedef u8 {{ name }}[{{ size }}];
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,14 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (variable-length array) */
+{% endif %}
+typedef struct {
+ u32 count;
+ {{ ctype }}{{ type }} *element;
+} {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (variable-length opaque) */
+{% endif %}
+typedef opaque {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,11 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} (variable-length string) */
+{% endif %}
+typedef string {{ name }};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr)
+{
+{% if annotate %}
+ /* (basic) */
+{% endif %}
+ return xdrgen_decode_{{ type }}(xdr, ptr);
+};
new file mode 100644
@@ -0,0 +1,25 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+ /* (fixed-length array) */
+{% endif %}
+ for (u32 i = 0; i < {{ size }}; i++) {
+{%- if ctype == '' %}
+ if (xdrgen_decode_{{ type }}(xdr, ptr->items[i]) < 0)
+{% else %}
+ if (xdrgen_decode_{{ type }}(xdr, &ptr->items[i]) < 0)
+{% endif %}
+ return false;
+ }
+ return true;
+};
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+ /* (fixed-length opaque) */
+{% endif %}
+ return xdr_stream_decode_opaque_fixed(xdr, ptr, {{ size }}) >= 0;
+};
new file mode 100644
@@ -0,0 +1,26 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+ /* (variable-length array) */
+{% endif %}
+ if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
+ return false;
+{% if maxsize != 0 %}
+ if (ptr->count > {{ maxsize }})
+ return false;
+{% endif %}
+ for (u32 i = 0; i < ptr->count; i++)
+ if (!xdrgen_decode_{{ type }}(xdr, &ptr->element[i]))
+ return false;
+ return true;
+};
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+ /* (variable-length opaque) */
+{% endif %}
+ return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0;
+};
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ ctype }}{{ name }} *ptr)
+{
+{% if annotate %}
+ /* (variable-length string) */
+{% endif %}
+ return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0;
+};
new file mode 100644
@@ -0,0 +1,21 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+ /* (basic) */
+{% endif %}
+{% if encode_needs_addressof %}
+ return xdrgen_encode_{{ type }}(xdr, &value);
+{% else %}
+ return xdrgen_encode_{{ type }}(xdr, value);
+{% endif %}
+};
new file mode 100644
@@ -0,0 +1,25 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+ /* (fixed-length array) */
+{% endif %}
+ for (u32 i = 0; i < {{ size }}; i++) {
+{% elif encode_needs_deref %}
+ if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0)
+{% else %}
+ if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0)
+{% endif %}
+ return false;
+ }
+ return true;
+};
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+ /* (fixed-length opaque) */
+{% endif %}
+ return xdr_stream_encode_opaque_fixed(xdr, value, {{ size }}) >= 0;
+};
new file mode 100644
@@ -0,0 +1,30 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+ /* (variable-length array) */
+{% endif %}
+{% if maxsize != 0 %}
+ if (unlikely(value.count > {{ maxsize }}))
+ return false;
+{% endif %}
+ if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value.count; i++)
+{% if encode_needs_addressof %}
+ if (!xdrgen_encode_{{ type }}(xdr, &value.element[i]))
+{% else %}
+ if (!xdrgen_encode_{{ type }}(xdr, value.element[i]))
+{% endif %}
+ return false;
+ return true;
+};
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+ /* (variable-length opaque) */
+{% endif %}
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
new file mode 100644
@@ -0,0 +1,17 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* typedef {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ ctype }}{{ name }} value)
+{
+{% if annotate %}
+ /* (variable-length string) */
+{% endif %}
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ {{ ctype }}{{ type }} {{ name }};
new file mode 100644
@@ -0,0 +1,8 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ } u;
+};
+{%- if name in public_apis %}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr);
+{%- endif -%}
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ {{ ctype }}{{ type }} {{ name }};
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* union {{ name }} */
+{% endif %}
+struct {{ name }} {
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ {{ ctype }}{{ type }} {{ name }};
+ union {
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (basic) */
+{% endif %}
+ if (!xdrgen_decode_{{ type }}(xdr, &ptr->u.{{ name }}))
+ return false;
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ break;
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ case {{ case }}:
new file mode 100644
@@ -0,0 +1,4 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ }
+ return true;
+};
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ default:
new file mode 100644
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* union {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
+{
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (optional data) */
+{% endif %}
+ if (!xdrgen_decode_{{ type }}(xdr, &ptr->u.{{ name }}))
+ return false;
new file mode 100644
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* discriminant {{ name }} */
+{% endif %}
+ if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
+ return false;
+ switch (ptr->{{ name }}) {
new file mode 100644
@@ -0,0 +1,13 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length array) */
+{% endif %}
+ if (xdr_stream_decode_u32(xdr, &count) != XDR_UNIT)
+ return false;
+ if (count > {{ maxsize }})
+ return false;
+ for (u32 i = 0; i < count; i++) {
+ if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
+ return false;
+ }
+ ptr->{{ name }}.len = count;
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length opaque) */
+{% endif %}
+ if (!xdrgen_decode_opaque(xdr, (struct opaque *)ptr->u.{{ name }}, {{ maxsize }}))
+ return false;
new file mode 100644
@@ -0,0 +1,6 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (variable-length string) */
+{% endif %}
+ if (!xdrgen_decode_string(xdr, (struct string *)ptr->u.{{ name }}, {{ maxsize }}))
+ return false;
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ if (!xdrgen_decode_void(xdr))
+ return false;
new file mode 100644
@@ -0,0 +1,10 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* member {{ name }} (basic) */
+{% endif %}
+{% if encode_needs_addressof %}
+ if (!xdrgen_encode_{{ type }}(xdr, &ptr->u.{{ name }}))
+{% else %}
+ if (!xdrgen_encode_{{ type }}(xdr, ptr->u.{{ name }}))
+{% endif %}
+ return false;
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ break;
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ case {{ case }}:
new file mode 100644
@@ -0,0 +1,4 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ }
+ return true;
+};
new file mode 100644
@@ -0,0 +1,2 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ default:
new file mode 100644
@@ -0,0 +1,12 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{% if annotate %}
+/* union {{ name }} */
+{% endif %}
+{% if name in public_apis %}
+bool
+{% else %}
+static bool __maybe_unused
+{% endif %}
+xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr)
+{
new file mode 100644
@@ -0,0 +1,7 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+{% if annotate %}
+ /* discriminant {{ name }} */
+{% endif %}
+ if (!xdrgen_encode_{{ type }}(xdr, ptr->{{ name }}))
+ return false;
+ switch (ptr->{{ name }}) {
new file mode 100644
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+ if (!xdrgen_encode_void(xdr))
+ return false;
new file mode 100644
@@ -0,0 +1,36 @@
+/* Sample XDR specification from RFC 1832 Section 5.5 */
+
+const MAXUSERNAME = 32; /* max length of a user name */
+const MAXFILELEN = 65535; /* max length of a file */
+const MAXNAMELEN = 255; /* max length of a file name */
+
+/*
+ * Types of files:
+ */
+enum filekind {
+ TEXT = 0, /* ascii data */
+ DATA = 1, /* raw data */
+ EXEC = 2 /* executable */
+};
+
+/*
+ * File information, per kind of file:
+ */
+union filetype switch (filekind kind) {
+case TEXT:
+ void; /* no extra information */
+case DATA:
+ string creator<MAXNAMELEN>; /* data creator */
+case EXEC:
+ string interpretor<MAXNAMELEN>; /* program interpretor */
+};
+
+/*
+ * A complete file:
+ */
+struct file {
+ string filename<MAXNAMELEN>; /* name of file */
+ filetype type; /* info about file */
+ string owner<MAXUSERNAME>; /* owner of file */
+ opaque data<MAXFILELEN>; /* file data */
+};
new file mode 120000
@@ -0,0 +1 @@
+../../../../Documentation/sunrpc/xdr
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,117 @@
+// A Lark grammar for the XDR specification language based on
+// https://tools.ietf.org/html/rfc4506 Section 6.3
+
+declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque
+ | "opaque" identifier "<" [ value ] ">" -> variable_length_opaque
+ | "string" identifier "<" [ value ] ">" -> variable_length_string
+ | type_specifier identifier "[" value "]" -> fixed_length_array
+ | type_specifier identifier "<" [ value ] ">" -> variable_length_array
+ | type_specifier "*" identifier -> optional_data
+ | type_specifier identifier -> basic
+ | "void" -> void
+
+value : decimal_constant
+ | hexadecimal_constant
+ | octal_constant
+ | identifier
+
+constant : decimal_constant | hexadecimal_constant | octal_constant
+
+type_specifier : unsigned_hyper
+ | unsigned_long
+ | unsigned_int
+ | hyper
+ | long
+ | int
+ | float
+ | double
+ | quadruple
+ | bool
+ | enum_type_spec
+ | struct_type_spec
+ | union_type_spec
+ | identifier
+
+unsigned_hyper : "unsigned" "hyper"
+unsigned_long : "unsigned" "long"
+unsigned_int : "unsigned" "int"
+hyper : "hyper"
+long : "long"
+int : "int"
+float : "float"
+double : "double"
+quadruple : "quadruple"
+bool : "bool"
+
+enum_type_spec : "enum" enum_body
+
+enum_body : "{" ( identifier "=" value ) ( "," identifier "=" value )* "}"
+
+struct_type_spec : "struct" struct_body
+
+struct_body : "{" ( declaration ";" )+ "}"
+
+union_type_spec : "union" union_body
+
+union_body : switch_spec "{" case_spec+ [ default_spec ] "}"
+
+switch_spec : "switch" "(" declaration ")"
+
+case_spec : ( "case" value ":" )+ declaration ";"
+
+default_spec : "default" ":" declaration ";"
+
+constant_def : "const" identifier "=" value ";"
+
+type_def : "typedef" declaration ";" -> typedef
+ | "enum" identifier enum_body ";" -> enum
+ | "struct" identifier struct_body ";" -> struct
+ | "union" identifier union_body ";" -> union
+
+specification : definition*
+
+definition : constant_def
+ | type_def
+ | program_def
+ | pragma_def
+
+//
+// RPC program definitions not specified in RFC 4506
+//
+
+program_def : "program" identifier "{" version_def+ "}" "=" constant ";"
+
+version_def : "version" identifier "{" procedure_def+ "}" "=" constant ";"
+
+procedure_def : type_specifier identifier "(" type_specifier ")" "=" constant ";"
+
+pragma_def : "pragma" directive identifier [ identifier ] ";"
+
+directive : header_directive
+ | pages_directive
+ | public_directive
+ | skip_directive
+
+header_directive : "header"
+pages_directive : "pages"
+public_directive : "public"
+skip_directive : "skip"
+
+//
+// XDR language primitives
+//
+
+identifier : /([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*/
+
+decimal_constant : /[\+-]?(0|[1-9][0-9]*)/
+hexadecimal_constant : /0x([a-f]|[A-F]|[0-9])+/
+octal_constant : /0[0-7]+/
+
+COMMENT : "/*" /(.|\n)*?/ "*/"
+%ignore COMMENT
+
+PASSTHRU : "%" | "%" /.+/
+%ignore PASSTHRU
+
+WS : /[ \t\f\r\n]/+
+%ignore WS
new file mode 100644
@@ -0,0 +1,437 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Define and implement the Abstract Syntax Tree for the XDR language."""
+
+import sys
+from typing import List
+from dataclasses import dataclass
+
+from lark import ast_utils, Transformer
+from lark.tree import Meta
+
+this_module = sys.modules[__name__]
+
+header_name = "none"
+public_apis = []
+enums = set()
+structs = set()
+
+
+@dataclass
+class _Ast(ast_utils.Ast):
+ """Parent class for the XDR abstract syntax tree"""
+
+
+@dataclass
+class _XdrConstant(_Ast):
+ """Corresponds to 'constant_def' in the grammar"""
+
+ name: str
+ value: str
+
+
+@dataclass
+class _XdrEnumerator(_Ast):
+ """An 'identifier = value' enumerator"""
+
+ name: str
+ value: str
+
+
+@dataclass
+class _XdrEnum(_Ast):
+ """An XDR enum definition"""
+
+ name: str
+ minimum: int
+ maximum: int
+ enumerators: List[_XdrEnumerator]
+
+
+@dataclass
+class _TypeSpecifier(_Ast):
+ """Corresponds to 'type_specifier' in the grammar"""
+
+ type_name: str
+
+
+@dataclass
+class _DefinedType(_TypeSpecifier):
+ type_decorator: str
+ pass_by_reference: bool
+
+
+@dataclass
+class _BuiltInType(_TypeSpecifier):
+ display_name: str
+ type_decorator: str = ""
+ pass_by_reference: bool = False
+
+
+@dataclass
+class _Declaration(_Ast):
+ """Parent class of type declarations"""
+
+
+@dataclass
+class _FixedLengthOpaque(_Declaration):
+ """A fixed-length opaque declaration"""
+
+ name: str
+ size: str
+ template: str = "fixed_length_opaque"
+
+
+@dataclass
+class _VariableLengthOpaque(_Declaration):
+ """A variable-length opaque declaration"""
+
+ name: str
+ maxsize: str
+ template: str = "variable_length_opaque"
+
+
+@dataclass
+class _VariableLengthString(_Declaration):
+ """A variable-length string declaration"""
+
+ name: str
+ maxsize: str
+ template: str = "variable_length_string"
+
+
+@dataclass
+class _FixedLengthArray(_Declaration):
+ """A fixed-length array declaration"""
+
+ name: str
+ spec: _TypeSpecifier
+ size: str
+ template: str = "fixed_length_array"
+
+
+@dataclass
+class _VariableLengthArray(_Declaration):
+ """A variable-length array declaration"""
+
+ name: str
+ spec: _TypeSpecifier
+ maxsize: str
+ template: str = "variable_length_array"
+
+
+@dataclass
+class _XdrOptionalData(_Declaration):
+ """An 'optional_data' declaration"""
+
+ name: str
+ spec: _TypeSpecifier
+ template: str = "optional_data"
+
+
+@dataclass
+class _BasicDeclaration(_Declaration):
+ """A 'basic' declaration"""
+
+ name: str
+ spec: _TypeSpecifier
+ template: str = "basic"
+
+
+@dataclass
+class _XdrVoid(_Declaration):
+ """A void declaration"""
+
+
+@dataclass
+class _XdrStruct(_Ast):
+ """An XDR struct definition"""
+
+ name: str
+ fields: List[_Declaration]
+
+
+@dataclass
+class _XdrTypedef(_Ast):
+ """An XDR typedef"""
+
+ declaration: _Declaration
+
+
+@dataclass
+class _CaseSpec(_Ast):
+ """One case in an XDR union"""
+
+ values: List[str]
+ arm: _Declaration
+ template: str = "case_spec"
+
+
+@dataclass
+class _DefaultSpec(_Ast):
+ """Default case in an XDR union"""
+
+ arm: _Declaration
+ template: str = "default_spec"
+
+
+@dataclass
+class _XdrUnion(_Ast):
+ """An XDR union"""
+
+ name: str
+ discriminant: _Declaration
+ cases: List[_CaseSpec]
+ default: _Declaration
+
+
+@dataclass
+class _RpcProcedure(_Ast):
+ """RPC procedure definition"""
+
+ name: str
+ number: str
+ argument: _TypeSpecifier
+ result: _TypeSpecifier
+
+
+@dataclass
+class _RpcVersion(_Ast):
+ """RPC version definition"""
+
+ name: str
+ number: str
+ procedures: List[_RpcProcedure]
+
+
+@dataclass
+class _RpcProgram(_Ast):
+ """RPC program definition"""
+
+ name: str
+ number: str
+ versions: List[_RpcVersion]
+
+
+@dataclass
+class _Pragma(_Ast):
+ """Empty class for pragma directives"""
+
+
+@dataclass
+class Definition(_Ast, ast_utils.WithMeta):
+ """Corresponds to 'definition' in the grammar"""
+
+ meta: Meta
+ value: object
+
+
+@dataclass
+class Specification(_Ast, ast_utils.AsList):
+ """Corresponds to 'specification' in the grammar"""
+
+ definitions: List[Definition]
+
+
+class ParseToAst(Transformer):
+ """Functions that transform productions into AST nodes"""
+
+ def constant_def(self, children):
+ """Instantiate one _XdrConstant object"""
+ name = children[0].children[0].value
+ value = children[1].children[0].children[0].value
+ return _XdrConstant(name, value)
+
+ # cel: Python can compute a min() and max() for the enumerator values
+ # so that the generated code can perform proper range checking.
+ def enum(self, children):
+ """Instantiate one _XdrEnum object"""
+ enum_name = children[0].children[0].value
+ enums.add(enum_name)
+
+ i = 0
+ enumerators = []
+ body = children[1]
+ while i < len(body.children):
+ name = body.children[i].children[0].value
+ value = body.children[i + 1].children[0].children[0].value
+ enumerators.append(_XdrEnumerator(name, value))
+ i = i + 2
+
+ return _XdrEnum(enum_name, 0, 0, enumerators)
+
+ def type_specifier(self, children):
+ """Instantiate one type_specifier object"""
+ if children[0].data == "identifier":
+ name = children[0].children[0].value
+ decorator = ""
+ pbr = False
+ if name in enums:
+ decorator = "enum "
+ if name in structs:
+ decorator = "struct "
+ pbr = True
+ return _DefinedType(name, decorator, pbr)
+
+ token = children[0].data
+ return _BuiltInType(
+ type_name=token.value, display_name=token.value.replace("_", " ")
+ )
+
+ def fixed_length_opaque(self, children):
+ """Instantiate one _FixedLengthOpaque declaration object"""
+ name = children[0].children[0].value
+ size = children[1].children[0].children[0].value
+
+ return _FixedLengthOpaque(name, size)
+
+ def variable_length_opaque(self, children):
+ """Instantiate one _VariableLengthOpaque declaration object"""
+ name = children[0].children[0].value
+ if children[1] is not None:
+ maxsize = children[1].children[0].children[0].value
+ else:
+ maxsize = 0
+
+ return _VariableLengthOpaque(name, maxsize)
+
+ def variable_length_string(self, children):
+ """Instantiate one _VariableLengthString declaration object"""
+ name = children[0].children[0].value
+ if children[1] is not None:
+ maxsize = children[1].children[0].children[0].value
+ else:
+ maxsize = 0
+
+ return _VariableLengthString(name, maxsize)
+
+ def fixed_length_array(self, children):
+ """Instantiate one _FixedLengthArray declaration object"""
+ spec = children[0]
+ name = children[1].children[0].value
+ size = children[2].children[0].children[0].value
+
+ return _FixedLengthArray(name, spec, size)
+
+ def variable_length_array(self, children):
+ """Instantiate one _VariableLengthArray declaration object"""
+ spec = children[0]
+ name = children[1].children[0].value
+ if children[2] is not None:
+ maxsize = children[2].children[0].children[0].value
+ else:
+ maxsize = 0
+
+ return _VariableLengthArray(name, spec, maxsize)
+
+ def optional_data(self, children):
+ """Instantiate one _XdrOptionalData declaration object"""
+ spec = children[0]
+ name = children[1].children[0].value
+ structs.add(name)
+
+ return _XdrOptionalData(name, spec)
+
+ def basic(self, children):
+ """Instantiate one _BasicDeclaration object"""
+ spec = children[0]
+ name = children[1].children[0].value
+
+ return _BasicDeclaration(name, spec)
+
+ def void(self, children):
+ """Instantiate one _XdrVoid declaration object"""
+ return _XdrVoid()
+
+ def struct(self, children):
+ """Instantiate one _XdrStruct object"""
+ name = children[0].children[0].value
+ structs.add(name)
+ fields = children[1].children
+
+ return _XdrStruct(name, fields)
+
+ def typedef(self, children):
+ """Instantiate one _XdrTypedef object"""
+
+ return _XdrTypedef(children[0])
+
+ def case_spec(self, children):
+ """Instantiate one _CaseSpec object"""
+ values = []
+ for item in children[0:-1]:
+ value = item.children[0].children[0].value
+ values.append(value)
+ arm = children[-1]
+
+ return _CaseSpec(values, arm)
+
+ def default_spec(self, children):
+ """Instantiate one _DefaultSpec object"""
+ arm = children[0]
+
+ return _DefaultSpec(arm)
+
+ def union(self, children):
+ """Instantiate one _XdrUnion object"""
+ name = children[0].children[0].value
+ structs.add(name)
+ body = children[1]
+ discriminant = body.children[0].children[0]
+ cases = body.children[1:-1]
+ default = body.children[-1]
+
+ return _XdrUnion(name, discriminant, cases, default)
+
+ def procedure_def(self, children):
+ """Instantiate one _RpcProcedure object"""
+ result = children[0]
+ name = children[1].children[0].value
+ argument = children[2]
+ number = children[3].children[0].children[0].value
+
+ return _RpcProcedure(name, number, argument, result)
+
+ def version_def(self, children):
+ """Instantiate one _RpcVersion object"""
+ name = children[0].children[0].value
+ number = children[-1].children[0].children[0].value
+ procedures = children[1:-1]
+
+ return _RpcVersion(name, number, procedures)
+
+ def program_def(self, children):
+ """Instantiate one _RpcProgram object"""
+ name = children[0].children[0].value
+ number = children[-1].children[0].children[0].value
+ versions = children[1:-1]
+
+ return _RpcProgram(name, number, versions)
+
+ def pragma_def(self, children):
+ """Instantiate one _Pragma object"""
+ directive = children[0].children[0].data
+ match directive:
+ case "public_directive":
+ public_apis.append(children[1].children[0].value)
+ case "header_directive":
+ global header_name
+ header_name = children[1].children[0].value
+ case _:
+ raise NotImplementedError("Directive not supported")
+ return _Pragma()
+
+
+transformer = ast_utils.create_transformer(this_module, ParseToAst())
+
+
+def transform_parse_tree(parse_tree):
+ """Transform productions into an abstract syntax tree"""
+
+ return transformer.transform(parse_tree)
+
+
+def get_header_name():
+ """Return header name set by pragma header directive"""
+ return header_name
new file mode 100644
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Common parsing code for xdrgen"""
+
+from lark import Lark, Discard
+
+
+def xdr_parser():
+ """Return a Lark parser instance configured with the XDR language grammar"""
+
+ return Lark.open(
+ "xdr.ebnf",
+ start="specification",
+ debug=True,
+ strict=True,
+ propagate_positions=True,
+ parser="lalr",
+ lexer="contextual",
+ )
new file mode 100755
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Translate an XDR specification into executable C code that
+can be compiled for the Linux kernel."""
+
+__author__ = "Chuck Lever"
+__copyright__ = "Copyright (c) 2024 Oracle and/or its affiliates."
+__license__ = "GPL-2.0 only"
+__version__ = "0.2"
+
+import sys
+import argparse
+
+from subcmds import source
+from subcmds import header
+from subcmds import lint
+
+from common import set_xdr_annotate
+
+
+sys.path.insert(1, "@pythondir@")
+
+
+def main():
+ """Parse command-line options"""
+ parser = argparse.ArgumentParser(
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description="Convert an XDR specification to Linux kernel source code",
+ epilog="""\
+Copyright (c) 2024 Oracle and/or its affiliates.
+
+License GPLv2: <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
+This is free software. You are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.""",
+ )
+ parser.add_argument(
+ "--version",
+ help="Display the version of this tool",
+ action="version",
+ version=__version__,
+ )
+
+ subcommands = parser.add_subparsers(title="Subcommands", required=True)
+
+ linter = subcommands.add_parser("lint", help="Check an XDR specification")
+ linter.add_argument("filename", help="File containing an XDR specification")
+ linter.set_defaults(func=lint.subcmd)
+
+ source_output = subcommands.add_parser(
+ "source", help="Generate XDR encoder and decoder source code"
+ )
+ source_output.add_argument(
+ "--annotate",
+ action="store_true",
+ default=False,
+ help="Add annotation comments",
+ )
+ source_output.add_argument(
+ "--language",
+ action="store_true",
+ default="C",
+ help="Output language",
+ )
+ source_output.add_argument("filename", help="File containing an XDR specification")
+ source_output.set_defaults(func=source.subcmd)
+
+ header_output = subcommands.add_parser(
+ "header", help="Generate XDR definitions and declarations"
+ )
+ header_output.add_argument(
+ "--annotate",
+ action="store_true",
+ default=False,
+ help="Add annotation comments",
+ )
+ header_output.add_argument(
+ "--language",
+ action="store_true",
+ default="C",
+ help="Output language",
+ )
+ header_output.add_argument("filename", help="File containing an XDR specification")
+ header_output.set_defaults(func=header.subcmd)
+
+ args = parser.parse_args()
+ set_xdr_annotate(args.annotate)
+ return args.func(args)
+
+
+try:
+ if __name__ == "__main__":
+ sys.exit(main())
+except (SystemExit, KeyboardInterrupt, RuntimeError):
+ sys.exit(1)