Skip to contentSkip to conversation

Read MAC address from SST vendor specific SFDP region

PATCH Superseded Manikandan Muralidharan 4 participants 18 messages Oct 14 – Oct 15

Subsystems (8)

v8 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v8 cover Read MAC address from SST vendor specific SFDP region

Some Microchip/SST QSPI flashes (e.g. the SST26VF064BEUI) are factory programmed with globally unique, write-protected EUI-48 and EUI-64 identifiers stored in a vendor-specific SFDP parameter table. On boards that have no on-board EEPROM (sama5d27_wlsom1, sama5d29 curiosity, sam9x75 curiosity) this is a reliable source for an Ethernet MAC address, instead of relying on a U-Boot-provided or random address.

The series exposes the SFDP as a generic NVMEM framework with no vendor code in the SPI NOR core:
- The SPI NOR core exposes the entire SFDP as a generic read-only NVMEM device, rooted at a new "sfdp" child node of the flash.
- A new NVMEM layout driver (drivers/nvmem/layouts/) locates the Microchip vendor parameter table at runtime and presents the EUI-48 as a "mac-address" cell.
- Arbitrary parameters can be read with a standard fixed-layout (known offset) or with an nvmem-layout parser (location discovered at runtime).

Changes in v8:
- New 3/8 - nvmem: core: take a reference on dev.of_node in

             nvmem_register(). v7's 3/8 (now 4/8) called of_node_put() right
			 after devm_nvmem_register(), which left nvmem->dev.of_node backed by
			 no reference at all once the caller's own reference was dropped.
			 Fixing it at the nvmem_register() by taking  its own of_node_get()
			 when assigning nvmem->dev.of_node and released in nvmem_release().

- 6/8 - drop the #address-cells/#size-cells from the qspi1_flash node.

Changes in v7:
- 1/7 - Add blank lines in the "sfdp" schema per review;
Add Reviewed-by: Rob Herring.
- 2/7 - Add Reviewed-by: Rob Herring.
- 3/7 - Drop the spi_nor_sfdp_nvmem_put_np() helper and
call of_node_put() right after devm_nvmem_register().
- 6/7 - Note in the commit message that the sama5d27_wlsom1 SoM ships the factory-programmed SST26VF064BEUI, so the EUI-48 "mac-address" cell is always present.

Changes in v6:
- 3/7 - Move the SFDP NVMEM registration from sfdp.c into core.c, since sfdp.c is only for parsing the tables;use of_get_compatible_child() instead of the open-coded child-node lookup.
- 2/7, 6/7, 7/7 - Add Reviewed-by: Linus Walleij.

Changes in v5:
- 1/7 and 5/7 - Add R-b tags
- 3/7- Rework the comments, function name and commit message.Check all the nodes (for_each_available_child) for the right compatible "jedec,sfdp" instead of node name.

Changes in v4:
- Rework per v3 review: remove the vendor-specific SFDP handling from the SPI NOR core; expose the whole SFDP as a generic read-only NVMEM device and move the EUI extraction into an nvmem-layout driver.
- Introduce a new nvmem-layout driver to discover the vendor-table location at runtime; no offset hardcoded in the device tree.
- Describe the SFDP via a dedicated "sfdp" subnode (compatible "jedec,sfdp"), which also resolves the v3 dtbs_check "Unevaluated properties ('nvmem-layout')" warning.
- Reverse the stored EUI bytes into canonical MAC order.
- Enable the layout in sama5_defconfig.

Changes in v3:
- 2/3 - add support to update the QSPI partition into 'fixed-partition' binding in sama5d27_wlsom1
- 3/3 - add nvmem-layout in qspi node for EUI48 MAC Address and nvmem cell properties for macb node in sama5d27_wlsom1

Changes in v2:
- 1/3 - parse the SST vendor table, read and store the addresses into a resource - managed space. Register the addresses into NVMEM framework
- 2/3 - add support to update the QSPI partition into 'fixed-partition' binding

v7: https://lore.kernel.org/all/[email protected]/

Manikandan Muralidharan (8):
dt-bindings: mtd: jedec,spi-nor: allow the SFDP to be exposed via
NVMEM
dt-bindings: nvmem: layouts: add Microchip/SST SFDP EUI layout
nvmem: core: take a reference on dev.of_node in nvmem_register()
mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device
nvmem: layouts: add Microchip/SST SFDP EUI layout driver
ARM: dts: microchip: sama5d27_wlsom1: use fixed-partitions for QSPI
flash
ARM: dts: microchip: sama5d27_wlsom1: read MAC address from QSPI SFDP
ARM: configs: sama5: enable Microchip/SST SFDP EUI NVMEM layout

 .../bindings/mtd/jedec,spi-nor.yaml           |  20 ++
 .../layouts/microchip,sst26vf-sfdp-eui.yaml   |  60 ++++++
 .../bindings/nvmem/layouts/nvmem-layout.yaml  |   1 +
 MAINTAINERS                                   |   6 +
 .../dts/microchip/at91-sama5d27_wlsom1.dtsi   |  63 +++---
 .../dts/microchip/at91-sama5d27_wlsom1_ek.dts |   2 +
 arch/arm/configs/sama5_defconfig              |   1 +
 drivers/mtd/spi-nor/core.c                    |  78 ++++++++
 drivers/nvmem/core.c                          |   5 +-
 drivers/nvmem/layouts/Kconfig                 |  10 +
 drivers/nvmem/layouts/Makefile                |   1 +
 drivers/nvmem/layouts/sst26vf-sfdp-eui.c      | 182 ++++++++++++++++++
 12 files changed, 403 insertions(+), 26 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/nvmem/layouts/microchip,sst26vf-sfdp-eui.yaml
 create mode 100644 drivers/nvmem/layouts/sst26vf-sfdp-eui.c

base-commit: 551c722f40809618230001baccf219193e22fc5a --
2.43.0

#1 lore raw

PATCH v8 1/8 dt-bindings: mtd: jedec,spi-nor: allow the SFDP to be exposed via NVMEM

Add an optional "sfdp" child node (compatible "jedec,sfdp") that describes the SFDP as a read-only NVMEM provider via nvmem.yaml, so its contents (e.g. a vendor EUI-48/EUI-64) can be read through NVMEM cells.

---
 .../bindings/mtd/jedec,spi-nor.yaml           | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml +20 -0
index 587af4968255..d7e659912b4a 100644
@@ -103,6 +103,22 @@ properties:
   spi-cpol: true
   spi-cpha: true
 
+  sfdp:
+    $ref: /schemas/nvmem/nvmem.yaml#
+    unevaluatedProperties: false
+    description:
+      The Serial Flash Discoverable Parameters (SFDP) tables exposed as a
+      read-only NVMEM device. This allows standard or vendor-specific SFDP
+      data (for example a factory-programmed EUI-48/EUI-64 identifier) to be
+      consumed through NVMEM cells.
+
+    properties:
+      compatible:
+        const: jedec,sfdp
+
+    required:
+      - compatible
+
 dependencies:
   spi-cpol: [ spi-cpha ]
   spi-cpha: [ spi-cpol ]
@@ -122,6 +138,10 @@ examples:
             spi-max-frequency = <40000000>;
             m25p,fast-read;
             reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
+
+            sfdp {
+                compatible = "jedec,sfdp";
+            };
         };
     };
 ...
Signature · 1 line
2.43.0
#2 lore raw

PATCH v8 2/8 dt-bindings: nvmem: layouts: add Microchip/SST SFDP EUI layout

In reply to Manikandan Muralidharan

Add a binding for the NVMEM layout that exposes the factory-programmed EUI-48 identifier from the Microchip/SST vendor-specific SFDP parameter table (e.g. SST26VF064BEUI) as a "mac-address" NVMEM cell, and reference it from nvmem-layout.yaml.

---
 .../layouts/microchip,sst26vf-sfdp-eui.yaml   | 60 +++++++++++++++++++
 .../bindings/nvmem/layouts/nvmem-layout.yaml  |  1 +
 2 files changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/nvmem/layouts/microchip,sst26vf-sfdp-eui.yaml
Documentation/devicetree/bindings/nvmem/layouts/microchip,sst26vf-sfdp-eui.yaml new file +60 -0
new file mode 100644
index 000000000000..37357efb7840
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/nvmem/layouts/microchip,sst26vf-sfdp-eui.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVMEM layout of the Microchip/SST SFDP EUI-48 identifier
+
+maintainers:
+  - Manikandan Muralidharan <[email protected]>
+
+description:
+  Some Microchip/SST serial flashes (for example the SST26VF064BEUI) are
+  factory programmed with a globally unique EUI-48 identifier stored in a
+  vendor-specific SFDP parameter table and permanently write-protected. This
+  layout locates that table and exposes the EUI-48 as an NVMEM cell so that,
+  for example, a network driver can use it as a MAC address. The location of
+  the data is discovered at runtime from the SFDP; no offset is encoded in the
+  device tree.
+
+select: false
+
+properties:
+  compatible:
+    const: microchip,sst26vf-sfdp-eui
+
+  mac-address:
+    type: object
+    description:
+      The factory-programmed EUI-48 identifier, usable as a MAC address.
+    additionalProperties: false
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        flash@0 {
+            compatible = "jedec,spi-nor";
+            reg = <0>;
+
+            sfdp {
+                compatible = "jedec,sfdp";
+
+                nvmem-layout {
+                    compatible = "microchip,sst26vf-sfdp-eui";
+
+                    mac-address {
+                    };
+                };
+            };
+        };
+    };
+...
Documentation/devicetree/bindings/nvmem/layouts/nvmem-layout.yaml +1 -0
index 382507060651..e63b93083821 100644
@@ -20,6 +20,7 @@ description: |
 oneOf:
   - $ref: fixed-layout.yaml
   - $ref: kontron,sl28-vpd.yaml
+  - $ref: microchip,sst26vf-sfdp-eui.yaml
   - $ref: onie,tlv-layout.yaml
   - $ref: u-boot,env.yaml
 
Signature · 1 line
2.43.0
#3 lore raw

PATCH v8 3/8 nvmem: core: take a reference on dev.of_node in nvmem_register()

In reply to Manikandan Muralidharan

nvmem_register() stores config->of_node (or config->dev->of_node) into nvmem->dev.of_node without taking its own reference. Callers that get the node via of_get_compatible_child() etc. and drop their reference right after registering leave nvmem->dev.of_node backed by no reference at all.

Take an of_node_get() when assigning nvmem->dev.of_node and release it in nvmem_release(). Existing callers that hold their own get/put pair around the registration call need no changes, since get/put on a device_node is per-holder and independent of core now also holding one.

---
 drivers/nvmem/core.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/nvmem/core.c +3 -2
index 0556d140170a..98192597b935 100644
@@ -541,6 +541,7 @@ static void nvmem_release(struct device *dev)
 
 	ida_free(&nvmem_ida, nvmem->id);
 	gpiod_put(nvmem->wp_gpio);
+	of_node_put(nvmem->dev.of_node);
 	kfree(nvmem->ops);
 	kfree(nvmem);
 }
@@ -950,9 +951,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->keepout = config->keepout;
 	nvmem->nkeepout = config->nkeepout;
 	if (config->of_node)
-		nvmem->dev.of_node = config->of_node;
+		nvmem->dev.of_node = of_node_get(config->of_node);
 	else
-		nvmem->dev.of_node = config->dev->of_node;
+		nvmem->dev.of_node = of_node_get(config->dev->of_node);
 
 	switch (config->id) {
 	case NVMEM_DEVID_NONE:
Signature · 1 line
2.43.0
maintainer Reviewed-by on v8 3/8
#4 lore raw

Re: [PATCH v8 3/8] nvmem: core: take a reference on dev.of_node in nvmem_register()

Quoted 6 lines · Manikandan Muralidharan

nvmem_register() stores config->of_node (or config->dev->of_node) into nvmem->dev.of_node without taking its own reference. Callers that get the node via of_get_compatible_child() etc. and drop their reference right after registering leave nvmem->dev.of_node backed by no reference at all.

Quoted 8 lines

Take an of_node_get() when assigning nvmem->dev.of_node and release it in nvmem_release(). Existing callers that hold their own get/put pair around the registration call need no changes, since get/put on a device_node is per-holder and independent of core now also holding one.

Reviewed-by on v8 3/8
#5 lore raw

Re: [PATCH v8 3/8] nvmem: core: take a reference on dev.of_node in nvmem_register()

In reply to Manikandan Muralidharan
Quoted 6 lines · Manikandan Muralidharan

nvmem_register() stores config->of_node (or config->dev->of_node) into nvmem->dev.of_node without taking its own reference. Callers that get the node via of_get_compatible_child() etc. and drop their reference right after registering leave nvmem->dev.of_node backed by no reference at all.

Quoted 8 lines

Take an of_node_get() when assigning nvmem->dev.of_node and release it in nvmem_release(). Existing callers that hold their own get/put pair around the registration call need no changes, since get/put on a device_node is per-holder and independent of core now also holding one.

Nice!

Did you check if any callers are also taking a reference, which might not be needed anymore now.

-michael

Quoted 18 lines
---
 drivers/nvmem/core.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/nvmem/core.c +1 -0
index 0556d140170a..98192597b935 100644
@@ -541,6 +541,7 @@ static void nvmem_release(struct device *dev)
 
 	ida_free(&nvmem_ida, nvmem->id);
 	gpiod_put(nvmem->wp_gpio);
+	of_node_put(nvmem->dev.of_node);
 	kfree(nvmem->ops);
 	kfree(nvmem);
 }
@@ -950,9 +951,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->keepout = config->keepout;

drivers/nvmem/core.c struct nvmem_device *nvmem_register(const struct nvmem_config *config) · line 959

Quoted 10 lines
drivers/nvmem/core.c +2 -2
 	nvmem->nkeepout = config->nkeepout;
 	if (config->of_node)
-		nvmem->dev.of_node = config->of_node;
+		nvmem->dev.of_node = of_node_get(config->of_node);
 	else
-		nvmem->dev.of_node = config->dev->of_node;
+		nvmem->dev.of_node = of_node_get(config->dev->of_node);
 
 	switch (config->id) {
 	case NVMEM_DEVID_NONE:

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

#6 lore raw

Re: [PATCH v8 3/8] nvmem: core: take a reference on dev.of_node in nvmem_register()

Hi Michael,

Quoted 13 lines · Michael Walle
Quoted 12 lines · Manikandan Muralidharan

nvmem_register() stores config->of_node (or config->dev->of_node) into nvmem->dev.of_node without taking its own reference. Callers that get the node via of_get_compatible_child() etc. and drop their reference right after registering leave nvmem->dev.of_node backed by no reference at all.

Take an of_node_get() when assigning nvmem->dev.of_node and release it in nvmem_release(). Existing callers that hold their own get/put pair around the registration call need no changes, since get/put on a device_node is per-holder and independent of core now also holding one.

Quoted 8 lines
Quoted 2 lines

Nice!

Did you check if any callers are also taking a reference, which might not be needed anymore now.

Yes, I checked — none of the callers hold a reference beyond the registration call itself. The pattern I verified in each drivers is short-lived, the reference is dropped right after nvmem_register()/devm_nvmem_register() call.

Quoted 33 lines

-michael

Quoted 28 lines
---
  drivers/nvmem/core.c | 5 +++--
  1 file changed, 3 insertions(+), 2 deletions(-)
drivers/nvmem/core.c +3 -2
index 0556d140170a..98192597b935 100644
@@ -541,6 +541,7 @@ static void nvmem_release(struct device *dev)
 
  	ida_free(&nvmem_ida, nvmem->id);
  	gpiod_put(nvmem->wp_gpio);
+	of_node_put(nvmem->dev.of_node);
  	kfree(nvmem->ops);
  	kfree(nvmem);
  }
@@ -950,9 +951,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
  	nvmem->keepout = config->keepout;
  	nvmem->nkeepout = config->nkeepout;
  	if (config->of_node)
-		nvmem->dev.of_node = config->of_node;
+		nvmem->dev.of_node = of_node_get(config->of_node);
  	else
-		nvmem->dev.of_node = config->dev->of_node;
+		nvmem->dev.of_node = of_node_get(config->dev->of_node);
 
  	switch (config->id) {
  	case NVMEM_DEVID_NONE:
Signature · 2 lines
Thanks and Regards,
Manikandan M.
#7 lore raw

PATCH v8 4/8 mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device

In reply to Manikandan Muralidharan

The SPI NOR core already reads the SFDP tables during enumeration and caches them in nor->sfdp->dwords (see spi_nor_parse_sfdp()). Re-expose that cached data as a read-only NVMEM device, in on-flash byte order, rooted at the flash's SFDP child node (compatible "jedec,sfdp").

This lets NVMEM cells reference any SFDP data: a fixed-layout for parameters at a known offset, or an nvmem-layout parser for vendor data whose location must be discovered at runtime. The device is only registered when an "sfdp" node is present in the device tree.

---
 drivers/mtd/spi-nor/core.c | 78 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)
drivers/mtd/spi-nor/core.c +78 -0
index 8bc117b46e02..4e47459b1679 100644
@@ -17,6 +17,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/spi-nor.h>
 #include <linux/mutex.h>
+#include <linux/nvmem-provider.h>
 #include <linux/of.h>
 #include <linux/regulator/consumer.h>
 #include <linux/sched/task_stack.h>
@@ -3001,6 +3002,75 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
 		nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
 }
 
+static int spi_nor_sfdp_reg_read(void *priv, unsigned int offset,
+				 void *val, size_t bytes)
+{
+	struct spi_nor *nor = priv;
+	struct sfdp *sfdp = nor->sfdp;
+	size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords);
+
+	if (offset >= sfdp_size || bytes > sfdp_size - offset)
+		return -EINVAL;
+
+	/* The cached SFDP is kept in on-flash (little-endian) byte order. */
+	memcpy(val, (u8 *)sfdp->dwords + offset, bytes);
+
+	return 0;
+}
+
+/**
+ * spi_nor_register_sfdp_nvmem() - expose the SFDP as a read-only NVMEM device
+ * @nor:	pointer to a 'struct spi_nor'
+ *
+ * Expose the whole SFDP, in on-flash byte order, as a read-only NVMEM device
+ * rooted at the flash's SFDP child node (compatible "jedec,sfdp"). This lets
+ * generic (fixed-layout) or vendor (nvmem-layout) cells reference any SFDP
+ * data. The device is only registered when a child node with the "jedec,sfdp"
+ * compatible is described in the device tree.
+ *
+ * Return: 0 on success or if there is nothing to do, -errno otherwise.
+ */
+static int spi_nor_register_sfdp_nvmem(struct spi_nor *nor)
+{
+	struct device *dev = nor->dev;
+	struct nvmem_config config = { };
+	struct nvmem_device *nvmem;
+	struct device_node *np;
+
+	if (!nor->sfdp)
+		return 0;
+
+	np = of_get_compatible_child(dev_of_node(dev), "jedec,sfdp");
+	if (!np)
+		return 0;
+
+	config.dev = dev;
+	config.of_node = np;
+	config.name = "sfdp";
+	config.id = NVMEM_DEVID_AUTO;
+	config.owner = THIS_MODULE;
+	config.read_only = true;
+	config.word_size = 1;
+	config.stride = 1;
+	config.size = (int)(nor->sfdp->num_dwords * sizeof(*nor->sfdp->dwords));
+	config.reg_read = spi_nor_sfdp_reg_read;
+	config.priv = nor;
+
+	nvmem = devm_nvmem_register(dev, &config);
+	of_node_put(np);
+	if (IS_ERR(nvmem)) {
+		/* NVMEM support is optional. */
+		if (PTR_ERR(nvmem) == -EOPNOTSUPP)
+			return 0;
+		return dev_err_probe(dev, PTR_ERR(nvmem),
+				     "failed to register SFDP NVMEM device\n");
+	}
+
+	dev_dbg(dev, "exposed %d-byte SFDP as an NVMEM device\n", config.size);
+
+	return 0;
+}
+
 /**
  * spi_nor_late_init_params() - Late initialization of default flash parameters.
  * @nor:	pointer to a 'struct spi_nor'
@@ -3204,6 +3274,14 @@ static int spi_nor_init_params(struct spi_nor *nor)
 		spi_nor_init_params_deprecated(nor);
 	}
 
+	/*
+	 * Expose the SFDP table as an NVMEM device only when
+	 * the flash actually provides one
+	 */
+	ret = spi_nor_register_sfdp_nvmem(nor);
+	if (ret)
+		return ret;
+
 	ret = spi_nor_late_init_params(nor);
 	if (ret)
 		return ret;
Signature · 1 line
2.43.0
maintainer Reviewed-by on v8 4/8
#8 lore raw

Re: [PATCH v8 4/8] mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device

Quoted 11 lines · Manikandan Muralidharan

The SPI NOR core already reads the SFDP tables during enumeration and caches them in nor->sfdp->dwords (see spi_nor_parse_sfdp()). Re-expose that cached data as a read-only NVMEM device, in on-flash byte order, rooted at the flash's SFDP child node (compatible "jedec,sfdp").

This lets NVMEM cells reference any SFDP data: a fixed-layout for parameters at a known offset, or an nvmem-layout parser for vendor data whose location must be discovered at runtime. The device is only registered when an "sfdp" node is present in the device tree.

#9 lore raw

Re: [PATCH v8 4/8] mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device

In reply to Manikandan Muralidharan
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] mtd: spi-nor: UAF of SFDP cached data via NVMEM cell consumer after unbind --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] mtd: spi-nor: UAF of SFDP cached data via NVMEM cell consumer after unbind --

commit 27ebe83329cdf6f2e062cc50041bbf3a877fcaba
Author: Manikandan Muralidharan <[email protected]>

mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device

This commit exposes the SFDP tables read from the SPI NOR flash as a read-only NVMEM device. The device is conditionally registered when a jedec,sfdp child node is present in the device tree, allowing consumers to reference any SFDP data.

Quoted 3 lines
drivers/mtd/spi-nor/core.c +0 -0

[ ... ]

drivers/mtd/spi-nor/core.c static void spi_nor_init_fixup_flags(struct spi_nor *nor) · line 3016

Quoted 12 lines
drivers/mtd/spi-nor/core.c +12 -0
+static int spi_nor_sfdp_reg_read(void *priv, unsigned int offset,
+				 void *val, size_t bytes)
+{
+	struct spi_nor *nor = priv;
+	struct sfdp *sfdp = nor->sfdp;
+	size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords);
+
+	if (offset >= sfdp_size || bytes > sfdp_size - offset)
+		return -EINVAL;
+
+	/* The cached SFDP is kept in on-flash (little-endian) byte order. */
+	memcpy(val, (u8 *)sfdp->dwords + offset, bytes);

[Severity: High]
Does this code introduce a potential use-after-free if the SPI NOR device is unbound while an NVMEM cell consumer still holds a reference?

When an in-kernel consumer driver obtains an NVMEM cell reference to the SFDP device, it increments the nvmem_device reference count. Later, if the SPI NOR device is unbound via sysfs, the devres framework runs devm_nvmem_unregister.

Looking at nvmem_unregister in drivers/nvmem/core.c, it only drops the provider's reference count. It does not explicitly block future reads, clear the operations pointer, or disconnect active consumers. Because the consumer still holds a cell reference, the nvmem_device object and its reg_read ops remain active.

Devres then proceeds to free the nor struct and nor->sfdp->dwords buffers which were allocated earlier in the probe sequence.

If the consumer subsequently calls nvmem_cell_read, the read is forwarded to spi_nor_sfdp_reg_read, which dereferences the already freed nor and sfdp memory.

Quoted 3 lines
drivers/mtd/spi-nor/core.c +3 -0
+
+	return 0;
+}

[ ... ]

Quoted 17 lines
drivers/mtd/spi-nor/core.c +17 -0
+static int spi_nor_register_sfdp_nvmem(struct spi_nor *nor)
+{
+	struct device *dev = nor->dev;
+	struct nvmem_config config = { };
+	struct nvmem_device *nvmem;
+	struct device_node *np;
+
+	if (!nor->sfdp)
+		return 0;
+
+	np = of_get_compatible_child(dev_of_node(dev), "jedec,sfdp");
+	if (!np)
+		return 0;
+
+	config.dev = dev;
+	config.of_node = np;
+	config.name = "sfdp";

drivers/mtd/spi-nor/core.c static void spi_nor_init_fixup_flags(struct spi_nor *nor) · line 3059

Quoted 10 lines
drivers/mtd/spi-nor/core.c +10 -0
+	config.id = NVMEM_DEVID_AUTO;
+	config.owner = THIS_MODULE;
+	config.read_only = true;
+	config.word_size = 1;
+	config.stride = 1;
+	config.size = (int)(nor->sfdp->num_dwords * sizeof(*nor->sfdp->dwords));
+	config.reg_read = spi_nor_sfdp_reg_read;
+	config.priv = nor;
+
+	nvmem = devm_nvmem_register(dev, &config);

[Severity: High]
Is there a risk to binding the NVMEM registration to the devres lifecycle here?

Since devres tears down in reverse order, devm_nvmem_unregister will run before the SFDP memory is freed. But as noted above, this does not safely destroy the NVMEM device if a consumer is holding a cell reference, allowing the operations pointer to outlive the memory it accesses.

Quoted 2 lines
drivers/mtd/spi-nor/core.c +2 -0
+	of_node_put(np);
+	if (IS_ERR(nvmem)) {
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Reviewed-by on v8 4/8
#10 lore raw

Re: [PATCH v8 4/8] mtd: spi-nor: sfdp: expose the SFDP as a read-only NVMEM device

In reply to Manikandan Muralidharan
Quoted 62 lines · Manikandan Muralidharan

The SPI NOR core already reads the SFDP tables during enumeration and caches them in nor->sfdp->dwords (see spi_nor_parse_sfdp()). Re-expose that cached data as a read-only NVMEM device, in on-flash byte order, rooted at the flash's SFDP child node (compatible "jedec,sfdp").

This lets NVMEM cells reference any SFDP data: a fixed-layout for parameters at a known offset, or an nvmem-layout parser for vendor data whose location must be discovered at runtime. The device is only registered when an "sfdp" node is present in the device tree.

---
 drivers/mtd/spi-nor/core.c | 78 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)
drivers/mtd/spi-nor/core.c +32 -0
index 8bc117b46e02..4e47459b1679 100644
@@ -17,6 +17,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/spi-nor.h>
 #include <linux/mutex.h>
+#include <linux/nvmem-provider.h>
 #include <linux/of.h>
 #include <linux/regulator/consumer.h>
 #include <linux/sched/task_stack.h>
@@ -3001,6 +3002,75 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
 		nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
 }
 
+static int spi_nor_sfdp_reg_read(void *priv, unsigned int offset,
+				 void *val, size_t bytes)
+{
+	struct spi_nor *nor = priv;
+	struct sfdp *sfdp = nor->sfdp;
+	size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords);
+
+	if (offset >= sfdp_size || bytes > sfdp_size - offset)
+		return -EINVAL;
+
+	/* The cached SFDP is kept in on-flash (little-endian) byte order. */
+	memcpy(val, (u8 *)sfdp->dwords + offset, bytes);
+
+	return 0;
+}
+
+/**
+ * spi_nor_register_sfdp_nvmem() - expose the SFDP as a read-only NVMEM device
+ * @nor:	pointer to a 'struct spi_nor'
+ *
+ * Expose the whole SFDP, in on-flash byte order, as a read-only NVMEM device
+ * rooted at the flash's SFDP child node (compatible "jedec,sfdp"). This lets
+ * generic (fixed-layout) or vendor (nvmem-layout) cells reference any SFDP
+ * data. The device is only registered when a child node with the "jedec,sfdp"
+ * compatible is described in the device tree.
+ *
+ * Return: 0 on success or if there is nothing to do, -errno otherwise.
+ */
+static int spi_nor_register_sfdp_nvmem(struct spi_nor *nor)
+{
+	struct device *dev = nor->dev;

drivers/mtd/spi-nor/core.c static void spi_nor_init_fixup_flags(struct spi_nor *nor) · line 3045

Quoted 10 lines
drivers/mtd/spi-nor/core.c +10 -0
+	struct nvmem_config config = { };
+	struct nvmem_device *nvmem;
+	struct device_node *np;
+
+	if (!nor->sfdp)
+		return 0;
+
+	np = of_get_compatible_child(dev_of_node(dev), "jedec,sfdp");
+	if (!np)
+		return 0;

I've missed replying to your former question about this, sorry. Yeah I was implying to always add the nvmem device regardless of the device tree. I don't see a reason why it should only be added if dt node is there. Userspace might still do something useful with it.

So if you'll respin a new version you can remove the check, otherwise it could be a follow-up patch.

-michael

Quoted 46 lines
drivers/mtd/spi-nor/core.c +36 -0
+
+	config.dev = dev;
+	config.of_node = np;
+	config.name = "sfdp";
+	config.id = NVMEM_DEVID_AUTO;
+	config.owner = THIS_MODULE;
+	config.read_only = true;
+	config.word_size = 1;
+	config.stride = 1;
+	config.size = (int)(nor->sfdp->num_dwords * sizeof(*nor->sfdp->dwords));
+	config.reg_read = spi_nor_sfdp_reg_read;
+	config.priv = nor;
+
+	nvmem = devm_nvmem_register(dev, &config);
+	of_node_put(np);
+	if (IS_ERR(nvmem)) {
+		/* NVMEM support is optional. */
+		if (PTR_ERR(nvmem) == -EOPNOTSUPP)
+			return 0;
+		return dev_err_probe(dev, PTR_ERR(nvmem),
+				     "failed to register SFDP NVMEM device\n");
+	}
+
+	dev_dbg(dev, "exposed %d-byte SFDP as an NVMEM device\n", config.size);
+
+	return 0;
+}
+
 /**
  * spi_nor_late_init_params() - Late initialization of default flash parameters.
  * @nor:	pointer to a 'struct spi_nor'
@@ -3204,6 +3274,14 @@ static int spi_nor_init_params(struct spi_nor *nor)
 		spi_nor_init_params_deprecated(nor);
 	}
 
+	/*
+	 * Expose the SFDP table as an NVMEM device only when
+	 * the flash actually provides one
+	 */
+	ret = spi_nor_register_sfdp_nvmem(nor);
+	if (ret)
+		return ret;
+
 	ret = spi_nor_late_init_params(nor);
 	if (ret)
 		return ret;
#11 lore raw

PATCH v8 5/8 nvmem: layouts: add Microchip/SST SFDP EUI layout driver

In reply to Manikandan Muralidharan

Add an NVMEM layout that exposes the factory-programmed EUI-48 identifier from the Microchip/SST vendor SFDP parameter table (e.g. SST26VF064BEUI) as a "mac-address" cell, for use as a network MAC address. The vendor table is located at runtime via the SFDP NVMEM device (no offset in DT), and a read_post_process callback reverses the LSB-first bytes into canonical MAC order. Binds to an "nvmem-layout" node with compatible "microchip,sst26vf-sfdp-eui".

---
 MAINTAINERS                              |   6 +
 drivers/nvmem/layouts/Kconfig            |  10 ++
 drivers/nvmem/layouts/Makefile           |   1 +
 drivers/nvmem/layouts/sst26vf-sfdp-eui.c | 182 +++++++++++++++++++++++
 4 files changed, 199 insertions(+)
 create mode 100644 drivers/nvmem/layouts/sst26vf-sfdp-eui.c
MAINTAINERS +6 -0
index 360977678f70..d39b30a7a09c 100644
@@ -18001,6 +18001,12 @@ F:	Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml
 F:	drivers/misc/atmel-ssc.c
 F:	include/linux/atmel-ssc.h
 
+MICROCHIP SST SFDP EUI NVMEM LAYOUT DRIVER
+M:	Manikandan Muralidharan <[email protected]>
+S:	Maintained
+F:	Documentation/devicetree/bindings/nvmem/layouts/microchip,sst26vf-sfdp-eui.yaml
+F:	drivers/nvmem/layouts/sst26vf-sfdp-eui.c
+
 Microchip Timer Counter Block (TCB) Capture Driver
 M:	Kamel Bouhara <[email protected]>
 L:	[email protected] (moderated for non-subscribers)
drivers/nvmem/layouts/Kconfig +10 -0
index 5e586dfebe47..855c7db530da 100644
@@ -26,6 +26,16 @@ config NVMEM_LAYOUT_ONIE_TLV
 
 	  If unsure, say N.
 
+config NVMEM_LAYOUT_SST26VF_SFDP_EUI
+	tristate "Microchip/SST SFDP EUI-48 layout support"
+	help
+	  Say Y here if you want to expose the factory-programmed EUI-48
+	  identifier stored in the Microchip/SST vendor-specific SFDP parameter
+	  table (e.g. SST26VF064BEUI) as NVMEM cells, so that network drivers
+	  can use them as a MAC address.
+
+	  If unsure, say N.
+
 config NVMEM_LAYOUT_U_BOOT_ENV
 	tristate "U-Boot environment variables layout"
 	select CRC32
drivers/nvmem/layouts/Makefile +1 -0
index dd6c6c70b1a9..ab4e996075b2 100644
@@ -6,4 +6,5 @@
 obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o
 obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
 obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
+obj-$(CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI) += sst26vf-sfdp-eui.o
 obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
drivers/nvmem/layouts/sst26vf-sfdp-eui.c new file +182 -0

182 lines not on this page · raw message

Signature · 1 line
2.43.0
maintainer
#12 lore raw

Re: [PATCH v8 5/8] nvmem: layouts: add Microchip/SST SFDP EUI layout driver

Hi,

Quoted 9 lines · Manikandan Muralidharan

Add an NVMEM layout that exposes the factory-programmed EUI-48 identifier from the Microchip/SST vendor SFDP parameter table (e.g. SST26VF064BEUI) as a "mac-address" cell, for use as a network MAC address. The vendor table is located at runtime via the SFDP NVMEM device (no offset in DT), and a read_post_process callback reverses the LSB-first bytes into canonical MAC order. Binds to an "nvmem-layout" node with compatible "microchip,sst26vf-sfdp-eui".

Mostly minor nits, see below.

drivers/nvmem/layouts/Makefile line 10

Quoted 8 lines
drivers/nvmem/layouts/Makefile +1 -0
@@ -6,4 +6,5 @@
 obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o
 obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
 obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
+obj-$(CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI) += sst26vf-sfdp-eui.o
 obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o

Not sure this is ordered alphabetically, so in general add it at the end.

Quoted 14 lines
drivers/nvmem/layouts/Makefile +14 -0
+/* SFDP header and parameter header, as laid out on the flash. */
+struct sfdp_header {
+	u8 signature[4];
+	u8 minor;
+	u8 major;
+	u8 nph;
+	u8 unused;
+};
+
+struct sfdp_parameter_header {
+	u8 id_lsb;
+	u8 minor;
+	u8 major;
+	u8 length;

drivers/nvmem/layouts/Makefile

Quoted 10 lines
drivers/nvmem/layouts/Makefile +10 -0
+	u8 parameter_table_pointer[3];
+	u8 id_msb;
+};
+
+#define SFDP_SIGNATURE			0x50444653U
+
+#define SFDP_PARAM_HEADER_ID(h)		(((h)->id_msb << 8) | (h)->id_lsb)
+#define SFDP_PARAM_HEADER_PTP(h)	get_unaligned_le24((h)->parameter_table_pointer)
+
+/* Microchip (vendor) parameter table identifier: id_msb << 8 | id_lsb. */

This is not relevant, we saw it just above ^^^^^^^^^^^^^^^^^^^^^^^

drivers/nvmem/layouts/Makefile

Quoted 13 lines
drivers/nvmem/layouts/Makefile +13 -0
+#define SFDP_MCHP_VENDOR_ID		0x01bf
+
+#define SFDP_MCHP_EUI48_MARKER_OFFSET	0x60
+#define SFDP_MCHP_EUI48_MARKER		0x30
+#define SFDP_MCHP_EUI48_OFFSET		0x61
+
+static int sfdp_eui_read_post_process(void *priv, const char *id, int index,
+				      unsigned int offset, void *buf,
+				      size_t bytes)
+{
+	u8 *data = buf;
+	int i;
+

I believe the if against the length is wrong and should be inverted and placed here:

        if (bytes != ETH_ALEN)
                return -EINVAL;

Since lenght does not change, prefer to bail out early.

drivers/nvmem/layouts/Makefile

Quoted 5 lines
drivers/nvmem/layouts/Makefile +5 -0
+	/* SFDP stores the address least-significant octet first; reverse it. */
+	for (i = 0; i < bytes / 2; i++)
+		swap(data[i], data[bytes - 1 - i]);
+
+	if (bytes == ETH_ALEN && !is_valid_ether_addr(buf))

Is bytes != ETH_ALEN a valid use case?

Quoted 10 lines
drivers/nvmem/layouts/Makefile +10 -0
+		return -EINVAL;
+
+	return 0;
+}
+
+static int sfdp_eui_find_vendor_table(struct nvmem_device *nvmem, u32 *ptp)
+{
+	struct sfdp_parameter_header ph;
+	struct sfdp_header hdr;
+	int nph, i, ret;

drivers/nvmem/layouts/Makefile

Quoted 10 lines
drivers/nvmem/layouts/Makefile +10 -0
+
+	ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+	if (ret < 0)
+		return ret;
+
+	if (get_unaligned_le32(hdr.signature) != SFDP_SIGNATURE)
+		return -EINVAL;
+
+	/* The number of parameter headers (NPH) field is zero-based. */
+	nph = hdr.nph;

nph = hdr.nph + 1 ?

drivers/nvmem/layouts/Makefile

Quoted 2 lines
drivers/nvmem/layouts/Makefile +2 -0
+
+	for (i = 0; i <= nph; i++) {

and here the usual i < nph?

Quoted 14 lines
drivers/nvmem/layouts/Makefile +14 -0
+		ret = nvmem_device_read(nvmem, sizeof(hdr) + i * sizeof(ph),
+					sizeof(ph), &ph);
+		if (ret < 0)
+			return ret;
+
+		if (SFDP_PARAM_HEADER_ID(&ph) != SFDP_MCHP_VENDOR_ID)
+			continue;
+
+		*ptp = SFDP_PARAM_HEADER_PTP(&ph);
+		return 0;
+	}
+
+	return -ENOENT;
+}

...

drivers/nvmem/layouts/Makefile

Quoted 11 lines
drivers/nvmem/layouts/Makefile +11 -0
+	info.name = "mac-address";
+	info.offset = base + SFDP_MCHP_EUI48_OFFSET;
+	info.bytes = ETH_ALEN;
+	info.np = of_get_child_by_name(layout_np, "mac-address");
+	info.read_post_process = sfdp_eui_read_post_process;
+
+	ret = nvmem_add_one_cell(nvmem, &info);
+	if (ret)
+		of_node_put(info.np);
+	else
+		dev_dbg(dev, "exposed EUI-48 at SFDP offset 0x%x\n", info.offset);

This one is not really useful anymore.

drivers/nvmem/layouts/Makefile

Quoted 6 lines
drivers/nvmem/layouts/Makefile +6 -0
+
+	of_node_put(layout_np);
+
+	return ret;
+}
+

Thanks,
Miquèl

#13 lore raw

Re: [PATCH v8 5/8] nvmem: layouts: add Microchip/SST SFDP EUI layout driver

Hi Miquel,

Thanks for the review — all valid, I will fix in v9.

Quoted 153 lines · Miquel Raynal

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

Hi,

Quoted 9 lines · Manikandan Muralidharan

Add an NVMEM layout that exposes the factory-programmed EUI-48 identifier from the Microchip/SST vendor SFDP parameter table (e.g. SST26VF064BEUI) as a "mac-address" cell, for use as a network MAC address. The vendor table is located at runtime via the SFDP NVMEM device (no offset in DT), and a read_post_process callback reverses the LSB-first bytes into canonical MAC order. Binds to an "nvmem-layout" node with compatible "microchip,sst26vf-sfdp-eui".

Mostly minor nits, see below.

drivers/nvmem/layouts/Makefile line 10

Quoted 8 lines
drivers/nvmem/layouts/Makefile +1 -0
@@ -6,4 +6,5 @@
  obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o
  obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
  obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
+obj-$(CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI) += sst26vf-sfdp-eui.o
  obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o

Not sure this is ordered alphabetically, so in general add it at the end.

Quoted 14 lines
drivers/nvmem/layouts/Makefile +14 -0
+/* SFDP header and parameter header, as laid out on the flash. */
+struct sfdp_header {
+     u8 signature[4];
+     u8 minor;
+     u8 major;
+     u8 nph;
+     u8 unused;
+};
+
+struct sfdp_parameter_header {
+     u8 id_lsb;
+     u8 minor;
+     u8 major;
+     u8 length;

drivers/nvmem/layouts/Makefile

Quoted 10 lines
drivers/nvmem/layouts/Makefile +10 -0
+     u8 parameter_table_pointer[3];
+     u8 id_msb;
+};
+
+#define SFDP_SIGNATURE                       0x50444653U
+
+#define SFDP_PARAM_HEADER_ID(h)              (((h)->id_msb << 8) | (h)->id_lsb)
+#define SFDP_PARAM_HEADER_PTP(h)     get_unaligned_le24((h)->parameter_table_pointer)
+
+/* Microchip (vendor) parameter table identifier: id_msb << 8 | id_lsb. */

This is not relevant, we saw it just above ^^^^^^^^^^^^^^^^^^^^^^^

drivers/nvmem/layouts/Makefile

Quoted 13 lines
drivers/nvmem/layouts/Makefile +13 -0
+#define SFDP_MCHP_VENDOR_ID          0x01bf
+
+#define SFDP_MCHP_EUI48_MARKER_OFFSET        0x60
+#define SFDP_MCHP_EUI48_MARKER               0x30
+#define SFDP_MCHP_EUI48_OFFSET               0x61
+
+static int sfdp_eui_read_post_process(void *priv, const char *id, int index,
+                                   unsigned int offset, void *buf,
+                                   size_t bytes)
+{
+     u8 *data = buf;
+     int i;
+

I believe the if against the length is wrong and should be inverted and placed here:

         if (bytes != ETH_ALEN)
                 return -EINVAL;

Since lenght does not change, prefer to bail out early.

drivers/nvmem/layouts/Makefile

Quoted 5 lines
drivers/nvmem/layouts/Makefile +5 -0
+     /* SFDP stores the address least-significant octet first; reverse it. */
+     for (i = 0; i < bytes / 2; i++)
+             swap(data[i], data[bytes - 1 - i]);
+
+     if (bytes == ETH_ALEN && !is_valid_ether_addr(buf))

Is bytes != ETH_ALEN a valid use case?

Quoted 10 lines
drivers/nvmem/layouts/Makefile +10 -0
+             return -EINVAL;
+
+     return 0;
+}
+
+static int sfdp_eui_find_vendor_table(struct nvmem_device *nvmem, u32 *ptp)
+{
+     struct sfdp_parameter_header ph;
+     struct sfdp_header hdr;
+     int nph, i, ret;

drivers/nvmem/layouts/Makefile

Quoted 10 lines
drivers/nvmem/layouts/Makefile +10 -0
+
+     ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+     if (ret < 0)
+             return ret;
+
+     if (get_unaligned_le32(hdr.signature) != SFDP_SIGNATURE)
+             return -EINVAL;
+
+     /* The number of parameter headers (NPH) field is zero-based. */
+     nph = hdr.nph;

nph = hdr.nph + 1 ?

drivers/nvmem/layouts/Makefile

Quoted 2 lines
drivers/nvmem/layouts/Makefile +2 -0
+
+     for (i = 0; i <= nph; i++) {

and here the usual i < nph?

Quoted 14 lines
drivers/nvmem/layouts/Makefile +14 -0
+             ret = nvmem_device_read(nvmem, sizeof(hdr) + i * sizeof(ph),
+                                     sizeof(ph), &ph);
+             if (ret < 0)
+                     return ret;
+
+             if (SFDP_PARAM_HEADER_ID(&ph) != SFDP_MCHP_VENDOR_ID)
+                     continue;
+
+             *ptp = SFDP_PARAM_HEADER_PTP(&ph);
+             return 0;
+     }
+
+     return -ENOENT;
+}

...

drivers/nvmem/layouts/Makefile

Quoted 11 lines
drivers/nvmem/layouts/Makefile +11 -0
+     info.name = "mac-address";
+     info.offset = base + SFDP_MCHP_EUI48_OFFSET;
+     info.bytes = ETH_ALEN;
+     info.np = of_get_child_by_name(layout_np, "mac-address");
+     info.read_post_process = sfdp_eui_read_post_process;
+
+     ret = nvmem_add_one_cell(nvmem, &info);
+     if (ret)
+             of_node_put(info.np);
+     else
+             dev_dbg(dev, "exposed EUI-48 at SFDP offset 0x%x\n", info.offset);

This one is not really useful anymore.

drivers/nvmem/layouts/Makefile

Quoted 6 lines
drivers/nvmem/layouts/Makefile +6 -0
+
+     of_node_put(layout_np);
+
+     return ret;
+}
+

Thanks,
Miquèl

Signature · 2 lines
Thanks and Regards,
Manikandan M.
#14 lore raw

Re: [PATCH v8 5/8] nvmem: layouts: add Microchip/SST SFDP EUI layout driver

In reply to Manikandan Muralidharan
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] nvmem: use-after-free on layout driver module unload --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] nvmem: use-after-free on layout driver module unload
--

commit 4e7e82e84f19ca0cc7f7eae9477c171b2bdab685
Author: Manikandan Muralidharan <[email protected]>

nvmem: layouts: add Microchip/SST SFDP EUI layout driver

This commit adds an NVMEM layout that exposes the factory-programmed EUI-48 identifier from the Microchip/SST vendor SFDP parameter table as a "mac-address" cell.

Quoted 5 lines
drivers/nvmem/layouts/sst26vf-sfdp-eui.c new file +0 -0
new file mode 100644
index 0000000000000..641318d6f0af6

[ ... ]

Quoted 7 lines
drivers/nvmem/layouts/sst26vf-sfdp-eui.c new file +7 -0
+	info.name = "mac-address";
+	info.offset = base + SFDP_MCHP_EUI48_OFFSET;
+	info.bytes = ETH_ALEN;
+	info.np = of_get_child_by_name(layout_np, "mac-address");
+	info.read_post_process = sfdp_eui_read_post_process;
+
+	ret = nvmem_add_one_cell(nvmem, &info);

[ ... ]

drivers/nvmem/layouts/sst26vf-sfdp-eui.c line 162

Quoted 4 lines
drivers/nvmem/layouts/sst26vf-sfdp-eui.c new file +4 -0
+static void sfdp_eui_remove(struct nvmem_layout *layout)
+{
+	nvmem_layout_unregister(layout);
+}

[Severity: High]
Does this leave a dangling pointer if the layout module is unloaded?

In sfdp_eui_add_cells(), the driver registers sfdp_eui_read_post_process() as a callback via nvmem_add_one_cell(). However, the NVMEM core tracks the provider device's module, not the layout driver module.

Furthermore, nvmem_layout_unregister() is an empty stub in the NVMEM core. Because the core API does not remove the cells, the cell remains in the NVMEM device's cell list with a dangling read_post_process function pointer.

If the layout module is unloaded and the cell is subsequently read (e.g., via sysfs), could this execute unmapped module memory and cause a use-after-free?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
#15 lore raw

PATCH v8 6/8 ARM: dts: microchip: sama5d27_wlsom1: use fixed-partitions for QSPI flash

In reply to Manikandan Muralidharan

Move the QSPI flash partitions under a "partitions" node with the "fixed-partitions" compatible, as required by the current MTD partition binding, instead of declaring them as direct children of the flash node. No functional change.

---
 .../dts/microchip/at91-sama5d27_wlsom1.dtsi   | 54 ++++++++++---------
 1 file changed, 29 insertions(+), 25 deletions(-)
arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi +29 -25
index 0417f53b3e96..6ce42a42a1bf 100644
@@ -229,8 +229,6 @@ &qspi1 {
 	status = "disabled";
 
 	qspi1_flash: flash@0 {
-		#address-cells = <1>;
-		#size-cells = <1>;
 		compatible = "jedec,spi-nor";
 		reg = <0>;
 		spi-max-frequency = <104000000>;
@@ -240,34 +238,40 @@ qspi1_flash: flash@0 {
 		m25p,fast-read;
 		status = "disabled";
 
-		at91bootstrap@0 {
-			label = "at91bootstrap";
-			reg = <0x0 0x40000>;
-		};
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
 
-		bootloader@40000 {
-			label = "bootloader";
-			reg = <0x40000 0xc0000>;
-		};
+			at91bootstrap@0 {
+				label = "at91bootstrap";
+				reg = <0x0 0x40000>;
+			};
 
-		bootloaderenvred@100000 {
-			label = "bootloader env redundant";
-			reg = <0x100000 0x40000>;
-		};
+			bootloader@40000 {
+				label = "bootloader";
+				reg = <0x40000 0xc0000>;
+			};
 
-		bootloaderenv@140000 {
-			label = "bootloader env";
-			reg = <0x140000 0x40000>;
-		};
+			bootloaderenvred@100000 {
+				label = "bootloader env redundant";
+				reg = <0x100000 0x40000>;
+			};
 
-		dtb@180000 {
-			label = "device tree";
-			reg = <0x180000 0x80000>;
-		};
+			bootloaderenv@140000 {
+				label = "bootloader env";
+				reg = <0x140000 0x40000>;
+			};
 
-		kernel@200000 {
-			label = "kernel";
-			reg = <0x200000 0x600000>;
+			dtb@180000 {
+				label = "device tree";
+				reg = <0x180000 0x80000>;
+			};
+
+			kernel@200000 {
+				label = "kernel";
+				reg = <0x200000 0x600000>;
+			};
 		};
 	};
 };
Signature · 1 line
2.43.0
#16 lore raw

PATCH v8 7/8 ARM: dts: microchip: sama5d27_wlsom1: read MAC address from QSPI SFDP

In reply to Manikandan Muralidharan

Describe the QSPI flash SFDP as an NVMEM provider with the microchip,sst26vf-sfdp-eui layout, which exposes the factory-programmed EUI-48 as a "mac-address" cell, and point macb0 at it through nvmem-cells. This yields a stable MAC address on boards where U-Boot does not program one, instead of falling back to a random address.

The sama5d27_wlsom1 SoM is fitted with the factory-programmed SST26VF064BEUI, and the QSPI flash node is only enabled on boards carrying that part, so the EUI-48 marker and its "mac-address" cell are always present. Boards without an EUI-programmed part simply do not reference the cell, so macb0 never defers waiting for it.

---
 arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi | 11 +++++++++++
 .../boot/dts/microchip/at91-sama5d27_wlsom1_ek.dts    |  2 ++
 2 files changed, 13 insertions(+)
arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi +11 -0
index 6ce42a42a1bf..a4f55b2aa232 100644
@@ -238,6 +238,17 @@ qspi1_flash: flash@0 {
 		m25p,fast-read;
 		status = "disabled";
 
+		sfdp {
+			compatible = "jedec,sfdp";
+
+			nvmem-layout {
+				compatible = "microchip,sst26vf-sfdp-eui";
+
+				mac_address_eui48: mac-address {
+				};
+			};
+		};
+
 		partitions {
 			compatible = "fixed-partitions";
 			#address-cells = <1>;
arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1_ek.dts +2 -0
index 35a933eec573..5e87bf04bc47 100644
@@ -97,6 +97,8 @@ uart6: serial@200 {
 
 &macb0 {
 	status = "okay";
+	nvmem-cells = <&mac_address_eui48>;
+	nvmem-cell-names = "mac-address";
 };
 
 &pioA {
Signature · 1 line
2.43.0
#17 lore raw

PATCH v8 8/8 ARM: configs: sama5: enable Microchip/SST SFDP EUI NVMEM layout

In reply to Manikandan Muralidharan

Enable CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI so the factory EUI-48 stored in the SST26VF QSPI flash SFDP can be used as a MAC address on boards such as the sama5d27_wlsom1.

---
 arch/arm/configs/sama5_defconfig | 1 +
 1 file changed, 1 insertion(+)
arch/arm/configs/sama5_defconfig +1 -0
index 1eafad9139f1..f8cc94106c54 100644
@@ -219,6 +219,7 @@ CONFIG_PWM=y
 CONFIG_PWM_ATMEL=y
 CONFIG_PWM_ATMEL_HLCDC_PWM=y
 CONFIG_PWM_ATMEL_TCB=y
+CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI=y
 CONFIG_EXT4_FS=y
 CONFIG_FANOTIFY=y
 CONFIG_AUTOFS_FS=m
Signature · 1 line
2.43.0