linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Vadym Kochan <vadym.kochan@plvision.eu>
To: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
	Rob Herring <robh+dt@kernel.org>,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
Cc: Robert Marko <robert.marko@sartura.hr>,
	Vadym Kochan <vadym.kochan@plvision.eu>
Subject: [PATCH v2 3/3] nvmem: add ONIE nvmem cells parser
Date: Tue,  8 Jun 2021 22:03:27 +0300	[thread overview]
Message-ID: <20210608190327.22071-4-vadym.kochan@plvision.eu> (raw)
In-Reply-To: <20210608190327.22071-1-vadym.kochan@plvision.eu>

ONIE is a small operating system, pre-installed on bare metal network
switches, that provides an environment for automated provisioning.

This system requires that NVMEM (EEPROM) device holds various system
information (mac address, platform name, etc) in a special TLV layout.

The driver registers ONIE TLV attributes as NVMEM cells which can be
accessed by other platform driver. Also it allows to use
of_get_mac_address() to retrieve mac address for the netdev.

Signed-off-by: Vadym Kochan <vadym.kochan@plvision.eu>
---
v2:
    1) onie-cells.c renamed to onie-tlv-cells.c

    2) Do not register onie cells parser via platform driver,
       but via module_init().

    3) Simplified cells table & lookups registration by just filling
       all this in nvmem_parser_data.

 drivers/nvmem/Kconfig          |   9 +
 drivers/nvmem/Makefile         |   2 +
 drivers/nvmem/onie-tlv-cells.c | 302 +++++++++++++++++++++++++++++++++
 3 files changed, 313 insertions(+)
 create mode 100644 drivers/nvmem/onie-tlv-cells.c

diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index dd2019006838..a08ff087361b 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -288,4 +288,13 @@ config NVMEM_BRCM_NVRAM
 	  This driver provides support for Broadcom's NVRAM that can be accessed
 	  using I/O mapping.
 
+config NVMEM_ONIE_TLV_CELLS
+	tristate "ONIE TLV cells support"
+	help
+	  This is a driver to provide cells from ONIE TLV structure stored
+	  on NVME device.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called nvmem-onie-tlv-cells.
+
 endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index bbea1410240a..f70d7b817377 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -59,3 +59,5 @@ obj-$(CONFIG_NVMEM_RMEM) 	+= nvmem-rmem.o
 nvmem-rmem-y			:= rmem.o
 obj-$(CONFIG_NVMEM_BRCM_NVRAM)	+= nvmem_brcm_nvram.o
 nvmem_brcm_nvram-y		:= brcm_nvram.o
+obj-$(CONFIG_NVMEM_ONIE_TLV_CELLS)	+= nvmem-onie-tlv-cells.o
+nvmem-onie-tlv-cells-y		:= onie-tlv-cells.o
diff --git a/drivers/nvmem/onie-tlv-cells.c b/drivers/nvmem/onie-tlv-cells.c
new file mode 100644
index 000000000000..85b1c92da0c5
--- /dev/null
+++ b/drivers/nvmem/onie-tlv-cells.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ONIE NVMEM cells provider
+ *
+ * Author: Vadym Kochan <vadym.kochan@plvision.eu>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/slab.h>
+
+#define ONIE_NVMEM_TLV_MAX_LEN	2048
+
+#define ONIE_NVMEM_HDR_ID	"TlvInfo"
+
+struct onie_tlv_hdr {
+	u8 id[8];
+	u8 version;
+	__be16 data_len;
+} __packed;
+
+struct onie_tlv {
+	u8 type;
+	u8 len;
+	u8 val[0];
+} __packed;
+
+struct onie_nvmem_attr {
+	struct list_head head;
+	const char *name;
+	unsigned int offset;
+	unsigned int len;
+};
+
+struct onie_tlv_parser {
+	unsigned int attr_count;
+	struct list_head attrs;
+	struct device *dev;
+
+	struct nvmem_cell_lookup *lookup;
+	int			nlookups;
+};
+
+static struct nvmem_parser *nvmem_parser;
+
+static bool onie_nvmem_hdr_is_valid(struct onie_tlv_hdr *hdr)
+{
+	if (memcmp(hdr->id, ONIE_NVMEM_HDR_ID, sizeof(hdr->id)) != 0)
+		return false;
+	if (hdr->version != 0x1)
+		return false;
+
+	return true;
+}
+
+static void onie_nvmem_attrs_free(struct onie_tlv_parser *parser)
+{
+	struct onie_nvmem_attr *attr, *tmp;
+
+	list_for_each_entry_safe(attr, tmp, &parser->attrs, head) {
+		list_del(&attr->head);
+		kfree(attr);
+	}
+}
+
+static const char *onie_nvmem_attr_name(u8 type)
+{
+	switch (type) {
+	case 0x21: return "product-name";
+	case 0x22: return "part-number";
+	case 0x23: return "serial-number";
+	case 0x24: return "mac-address";
+	case 0x25: return "manufacture-date";
+	case 0x26: return "device-version";
+	case 0x27: return "label-revision";
+	case 0x28: return "platforn-name";
+	case 0x29: return "onie-version";
+	case 0x2A: return "num-macs";
+	case 0x2B: return "manufacturer";
+	case 0x2C: return "country-code";
+	case 0x2D: return "vendor";
+	case 0x2E: return "diag-version";
+	case 0x2F: return "service-tag";
+	case 0xFD: return "vendor-extension";
+	case 0xFE: return "crc32";
+
+	default: return "unknown";
+	}
+}
+
+static int onie_nvmem_tlv_parse(struct onie_tlv_parser *parser, u8 *data, u16 len)
+{
+	unsigned int hlen = sizeof(struct onie_tlv_hdr);
+	unsigned int offset = 0;
+	int err;
+
+	parser->attr_count = 0;
+
+	while (offset < len) {
+		struct onie_nvmem_attr *attr;
+		struct onie_tlv *tlv;
+
+		tlv = (struct onie_tlv *)(data + offset);
+
+		if (offset + tlv->len >= len) {
+			pr_err("TLV len is too big(0x%x) at 0x%x\n",
+				tlv->len, hlen + offset);
+
+			/* return success in case something was parsed */
+			return 0;
+		}
+
+		attr = kmalloc(sizeof(*attr), GFP_KERNEL);
+		if (!attr) {
+			err = -ENOMEM;
+			goto err_attr_alloc;
+		}
+
+		attr->name = onie_nvmem_attr_name(tlv->type);
+		/* skip 'type' and 'len' */
+		attr->offset = hlen + offset + 2;
+		attr->len = tlv->len;
+
+		list_add(&attr->head, &parser->attrs);
+		parser->attr_count++;
+
+		offset += sizeof(*tlv) + tlv->len;
+	}
+
+	if (!parser->attr_count)
+		return -EINVAL;
+
+	return 0;
+
+err_attr_alloc:
+	onie_nvmem_attrs_free(parser);
+	return err;
+}
+
+static int onie_nvmem_decode(struct onie_tlv_parser *parser, struct nvmem_device *nvmem)
+{
+	struct onie_tlv_hdr hdr;
+	u8 *data;
+	u16 len;
+	int ret;
+
+	ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+	if (ret < 0)
+		return ret;
+
+	if (!onie_nvmem_hdr_is_valid(&hdr)) {
+		pr_err("invalid ONIE TLV header\n");
+		return -EINVAL;
+	}
+
+	len = be16_to_cpu(hdr.data_len);
+
+	if (len > ONIE_NVMEM_TLV_MAX_LEN)
+		len = ONIE_NVMEM_TLV_MAX_LEN;
+
+	data = kmalloc(len, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = nvmem_device_read(nvmem, sizeof(hdr), len, data);
+	if (ret < 0)
+		goto err_data_read;
+
+	ret = onie_nvmem_tlv_parse(parser, data, len);
+	if (ret)
+		goto err_info_parse;
+
+	kfree(data);
+
+	return 0;
+
+err_info_parse:
+err_data_read:
+	kfree(data);
+	return ret;
+}
+
+static int onie_nvmem_cells_parse(struct onie_tlv_parser *parser,
+				  struct nvmem_device *nvmem,
+				  struct nvmem_cell_table *table)
+{
+	struct nvmem_cell_info *cells;
+	struct onie_nvmem_attr *attr;
+	unsigned int ncells = 0;
+	int err;
+
+	INIT_LIST_HEAD(&parser->attrs);
+	parser->attr_count = 0;
+
+	err = onie_nvmem_decode(parser, nvmem);
+	if (err)
+		return err;
+
+	cells = kmalloc_array(parser->attr_count, sizeof(*cells), GFP_KERNEL);
+	if (!cells) {
+		err = -ENOMEM;
+		goto err_cells_alloc;
+	}
+
+	parser->lookup = kmalloc_array(parser->attr_count,
+				     sizeof(struct nvmem_cell_lookup),
+				     GFP_KERNEL);
+	if (!parser->lookup) {
+		err = -ENOMEM;
+		goto err_lookup_alloc;
+	}
+
+	list_for_each_entry(attr, &parser->attrs, head) {
+		struct nvmem_cell_lookup *lookup;
+		struct nvmem_cell_info *cell;
+
+		cell = &cells[ncells];
+
+		lookup = &parser->lookup[ncells];
+		lookup->con_id = NULL;
+
+		cell->offset = attr->offset;
+		cell->name = attr->name;
+		cell->bytes = attr->len;
+		cell->bit_offset = 0;
+		cell->nbits = 0;
+
+		lookup->cell_name = cell->name;
+		lookup->con_id = cell->name;
+
+		ncells++;
+	}
+
+	table->ncells = ncells;
+	table->cells = cells;
+
+	parser->nlookups = ncells;
+
+	onie_nvmem_attrs_free(parser);
+
+	return 0;
+
+err_lookup_alloc:
+	kfree(cells);
+err_cells_alloc:
+	onie_nvmem_attrs_free(parser);
+
+	return err;
+}
+
+static int onie_cells_parse(struct nvmem_device *nvmem,
+			    struct nvmem_parser_data *data)
+{
+	struct onie_tlv_parser parser;
+	int err;
+
+	err = onie_nvmem_cells_parse(&parser, nvmem, &data->table);
+	if (err) {
+		pr_err("failed to parse ONIE attributes\n");
+		return err;
+	}
+
+	data->nlookups = parser.nlookups;
+	data->lookup = parser.lookup;
+
+	return 0;
+}
+
+static int __init onie_tlv_init(void)
+{
+	struct nvmem_parser_config parser_config = { };
+
+	parser_config.cells_parse = onie_cells_parse;
+	parser_config.owner = THIS_MODULE;
+	parser_config.name = "onie-tlv-cells";
+
+	nvmem_parser = nvmem_parser_register(&parser_config);
+	if (IS_ERR(nvmem_parser)) {
+		pr_err("failed to register %s parser\n", parser_config.name);
+		return PTR_ERR(nvmem_parser);
+	}
+
+	pr_info("registered %s parser\n", parser_config.name);
+
+	return 0;
+}
+
+static void __exit onie_tlv_exit(void)
+{
+	nvmem_parser_unregister(nvmem_parser);
+}
+
+module_init(onie_tlv_init);
+module_exit(onie_tlv_exit);
+
+MODULE_AUTHOR("Vadym Kochan <vadym.kochan@plvision.eu>");
+MODULE_DESCRIPTION("ONIE TLV NVMEM cells parser");
+MODULE_LICENSE("GPL");
-- 
2.17.1


  parent reply	other threads:[~2021-06-08 19:16 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-08 19:03 [PATCH v2 0/3] nvmem: add ONIE NVMEM cells parser Vadym Kochan
2021-06-08 19:03 ` [PATCH v2 1/3] nvmem: core: introduce " Vadym Kochan
2021-06-08 22:49   ` kernel test robot
2021-06-09  3:05   ` kernel test robot
2021-06-14 10:44   ` Srinivas Kandagatla
2021-06-16 12:33     ` Vadym Kochan
2021-06-21 11:00       ` Srinivas Kandagatla
2021-09-08  9:38         ` Vadym Kochan
2021-09-13 14:19           ` Srinivas Kandagatla
2021-09-20 10:24             ` Vadym Kochan
2021-09-20 10:36               ` Srinivas Kandagatla
2021-09-20 11:25                 ` Vadym Kochan
2021-09-20 11:32                   ` Srinivas Kandagatla
2021-09-20 12:29                     ` Vadym Kochan
2021-09-20 12:34                       ` Srinivas Kandagatla
2021-09-20 13:29                         ` Vadym Kochan
2021-09-20 13:40                           ` Srinivas Kandagatla
2021-09-21  5:50                             ` John Thomson
2021-09-27  7:50                               ` Vadym Kochan
2021-09-27 10:12                                 ` Srinivas Kandagatla
2021-09-28 13:31                                   ` Vadym Kochan
2021-09-28 13:51                                     ` Srinivas Kandagatla
2021-09-28 14:11                                       ` Vadym Kochan
2021-09-28 14:39                                         ` Srinivas Kandagatla
2021-09-27 10:12                               ` Srinivas Kandagatla
2021-09-27 12:38                                 ` John Thomson
2021-09-08  9:44     ` Vadym Kochan
2021-06-08 19:03 ` [PATCH v2 2/3] dt-bindings: nvmem: document nvmem-cells-parser-name property Vadym Kochan
2021-06-18 20:59   ` Rob Herring
2021-06-08 19:03 ` Vadym Kochan [this message]
2021-08-06 15:39   ` [PATCH v2 3/3] nvmem: add ONIE nvmem cells parser Jan Lübbe
2021-09-08  9:56     ` Vadym Kochan
2021-09-12 21:06       ` John Thomson
2021-09-13 14:20         ` Srinivas Kandagatla

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210608190327.22071-4-vadym.kochan@plvision.eu \
    --to=vadym.kochan@plvision.eu \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robert.marko@sartura.hr \
    --cc=robh+dt@kernel.org \
    --cc=srinivas.kandagatla@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).