@@ -23388,6 +23388,14 @@ W: http://sourceforge.net/projects/tlan/
F: Documentation/networking/device_drivers/ethernet/ti/tlan.rst
F: drivers/net/ethernet/ti/tlan.*
+TLV PARSER
+M: Roberto Sassu <roberto.sassu@huawei.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: include/linux/tlv_parser.h
+F: include/uapi/linux/tlv_parser.h
+F: lib/tlv_parser.*
+
TMIO/SDHI MMC DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
L: linux-mmc@vger.kernel.org
new file mode 100644
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Header file of TLV parser.
+ */
+
+#ifndef _LINUX_TLV_PARSER_H
+#define _LINUX_TLV_PARSER_H
+
+#include <uapi/linux/tlv_parser.h>
+
+/**
+ * typedef callback - Callback after parsing TLV entry
+ * @callback_data: Opaque data to supply to the callback function
+ * @field: Field identifier
+ * @field_data: Field data
+ * @field_len: Length of @field_data
+ *
+ * This callback is invoked after a TLV entry is parsed.
+ *
+ * Return: Zero on success, a negative value on error.
+ */
+typedef int (*callback)(void *callback_data, __u16 field,
+ const __u8 *field_data, __u32 field_len);
+
+int tlv_parse(callback callback, void *callback_data, const __u8 *data,
+ size_t data_len, const char **fields, __u32 num_fields);
+
+#endif /* _LINUX_TLV_PARSER_H */
new file mode 100644
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the user space interface for the TLV parser.
+ */
+
+#ifndef _UAPI_LINUX_TLV_PARSER_H
+#define _UAPI_LINUX_TLV_PARSER_H
+
+#include <linux/types.h>
+
+/*
+ * TLV format:
+ *
+ * +--------------+--+---------+--------+---------+
+ * | field1 (u16) | len1 (u32) | value1 (u8 len1) |
+ * +--------------+------------+------------------+
+ * | ... | ... | ... |
+ * +--------------+------------+------------------+
+ * | fieldN (u16) | lenN (u32) | valueN (u8 lenN) |
+ * +--------------+------------+------------------+
+ */
+
+/**
+ * struct tlv_entry - Entry of TLV format
+ * @field: Field identifier
+ * @length: Data length
+ * @data: Data
+ *
+ * This structure represents an entry of the TLV format.
+ */
+struct tlv_entry {
+ __u16 field;
+ __u32 length;
+ __u8 data[];
+} __attribute__((packed));
+
+#endif /* _UAPI_LINUX_TLV_PARSER_H */
@@ -777,3 +777,6 @@ config POLYNOMIAL
config FIRMWARE_TABLE
bool
+
+config TLV_PARSER
+ bool
@@ -393,5 +393,7 @@ obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
obj-$(CONFIG_FIRMWARE_TABLE) += fw_table.o
+obj-$(CONFIG_TLV_PARSER) += tlv_parser.o
+CFLAGS_tlv_parser.o += -I lib
subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify
new file mode 100644
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Implement the TLV parser.
+ */
+
+#define pr_fmt(fmt) "tlv_parser: "fmt
+#include <tlv_parser.h>
+
+/**
+ * tlv_parse - Parse TLV data
+ * @callback: Callback function to call to parse the entries
+ * @callback_data: Opaque data to supply to the callback function
+ * @data: Data to parse
+ * @data_len: Length of @data
+ * @fields: Array of field strings
+ * @num_fields: Number of elements of @fields
+ *
+ * Parse the TLV data format and call the supplied callback function for each
+ * entry, passing also the opaque data pointer.
+ *
+ * The callback function decides how to process data depending on the field.
+ *
+ * Return: Zero on success, a negative value on error.
+ */
+int tlv_parse(callback callback, void *callback_data, const __u8 *data,
+ size_t data_len, const char **fields, __u32 num_fields)
+{
+ const __u8 *data_ptr = data;
+ struct tlv_entry *entry;
+ __u16 parsed_field;
+ __u32 len;
+ int ret;
+
+ if (data_len > U32_MAX) {
+ pr_debug("Data too big, size: %zd\n", data_len);
+ return -E2BIG;
+ }
+
+ while (data_len) {
+ if (data_len < sizeof(*entry))
+ return -EBADMSG;
+
+ entry = (struct tlv_entry *)data_ptr;
+ data_ptr += sizeof(*entry);
+ data_len -= sizeof(*entry);
+
+ parsed_field = __be16_to_cpu(entry->field);
+ if (parsed_field >= num_fields) {
+ pr_debug("Invalid field %u, max: %u\n",
+ parsed_field, num_fields - 1);
+ return -EBADMSG;
+ }
+
+ len = __be32_to_cpu(entry->length);
+
+ if (data_len < len)
+ return -EBADMSG;
+
+ pr_debug("Data: field: %s, len: %u\n", fields[parsed_field],
+ len);
+
+ if (!len)
+ continue;
+
+ ret = callback(callback_data, parsed_field, data_ptr, len);
+ if (ret < 0) {
+ pr_debug("Parsing of field %s failed, ret: %d\n",
+ fields[parsed_field], ret);
+ return ret;
+ }
+
+ data_ptr += len;
+ data_len -= len;
+ }
+
+ if (data_len) {
+ pr_debug("Excess data: %zu bytes\n", data_len);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tlv_parse);
new file mode 100644
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Header file of TLV parser.
+ */
+
+#ifndef _LIB_TLV_PARSER_H
+#define _LIB_TLV_PARSER_H
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/tlv_parser.h>
+
+#endif /* _LIB_TLV_PARSER_H */