From bb629a630fb6bb89bb2e7a12c7407a28c046a6ac Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 23 Oct 2023 10:46:56 +0200 Subject: [PATCH] bcm53xx: update patch fixing NVMEM driver for NVRAM MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 1. Avoid corrupting (replacing '=' with '\0') NVRAM content 2. Check actual NVRAM data length to avoid allocating huge buffers Signed-off-by: Rafał Miłecki --- ..._nvram-store-a-copy-of-NVRAM-content.patch | 203 ++++++++++++++---- ..._nvram-store-a-copy-of-NVRAM-content.patch | 203 ++++++++++++++---- 2 files changed, 326 insertions(+), 80 deletions(-) diff --git a/target/linux/bcm53xx/patches-5.15/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/bcm53xx/patches-5.15/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch index 901bb6dd3c..2aae713b64 100644 --- a/target/linux/bcm53xx/patches-5.15/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch +++ b/target/linux/bcm53xx/patches-5.15/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch @@ -1,40 +1,176 @@ -From a18378409fee1cac0f0c58a4770ff557b498c778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= -Date: Fri, 1 Sep 2023 10:44:26 +0200 +Date: Thu, 14 Sep 2023 07:59:09 +0200 Subject: [PATCH] nvmem: brcm_nvram: store a copy of NVRAM content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +This driver uses MMIO access for reading NVRAM from a flash device. +Underneath there is a flash controller that reads data and provides +mapping window. + +Using MMIO interface affects controller configuration and may break real +controller driver. It was reported by multiple users of devices with +NVRAM stored on NAND. + +Modify driver to read & cache NVRAM content during init and use that +copy to provide NVMEM data when requested. On NAND flashes due to their +alignment NVRAM partitions can be quite big (1 MiB and more) while +actual NVRAM content stays quite small (usually 16 to 32 KiB). To avoid +allocating so much memory check for actual data length. + +Link: https://lore.kernel.org/linux-mtd/CACna6rwf3_9QVjYcM+847biTX=K0EoWXuXcSMkJO1Vy_5vmVqA@mail.gmail.com/ +Fixes: 3fef9ed0627a ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM") +Cc: Arınç ÜNAL +Cc: Florian Fainelli +Cc: Scott Branden Signed-off-by: Rafał Miłecki --- - drivers/nvmem/brcm_nvram.c | 54 +++++++++++++++++++------------------- - 1 file changed, 27 insertions(+), 27 deletions(-) + drivers/nvmem/brcm_nvram.c | 130 +++++++++++++++++++++++++------------ + 1 file changed, 90 insertions(+), 40 deletions(-) --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c -@@ -19,7 +19,7 @@ - +@@ -17,9 +17,20 @@ + + #define NVRAM_MAGIC "FLSH" + ++/** ++ * struct brcm_nvram - driver state internal struct ++ * ++ * @nvmem_size: Size of the whole space available for NVRAM ++ * @data: NVRAM data copy stored to avoid poking underlaying flash controller ++ * @data_len: NVRAM data size ++ * @padding_byte: Padding value used to fill remaining space ++ */ struct brcm_nvram { struct device *dev; - void __iomem *base; ++ size_t nvmem_size; + uint8_t *data; ++ size_t data_len; ++ uint8_t padding_byte; struct nvmem_cell_info *cells; int ncells; }; -@@ -36,10 +36,8 @@ static int brcm_nvram_read(void *context +@@ -36,10 +47,47 @@ static int brcm_nvram_read(void *context size_t bytes) { struct brcm_nvram *priv = context; - u8 *dst = val; ++ size_t to_copy; ++ ++ if (offset + bytes > priv->data_len) ++ to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0); ++ else ++ to_copy = bytes; ++ ++ memcpy(val, priv->data + offset, to_copy); ++ ++ memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy); ++ ++ return 0; ++} ++ ++static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev) ++{ ++ struct resource *res; ++ void __iomem *base; ++ ++ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ priv->nvmem_size = resource_size(res); ++ ++ priv->padding_byte = readb(base + priv->nvmem_size - 1); ++ for (priv->data_len = priv->nvmem_size; ++ priv->data_len; ++ priv->data_len--) { ++ if (readb(base + priv->data_len - 1) != priv->padding_byte) ++ break; ++ } ++ WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); - while (bytes--) - *dst++ = readb(priv->base + offset++); -+ memcpy(val, priv->data + offset, bytes); ++ priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; ++ ++ memcpy_fromio(priv->data, base, priv->data_len); ++ ++ bcm47xx_nvram_init_from_iomem(base, priv->data_len); return 0; } -@@ -110,35 +108,27 @@ static int brcm_nvram_add_cells(struct b +@@ -67,8 +115,13 @@ static int brcm_nvram_add_cells(struct b + size_t len) + { + struct device *dev = priv->dev; +- char *var, *value, *eq; ++ char *var, *value; ++ uint8_t tmp; + int idx; ++ int err = 0; ++ ++ tmp = priv->data[len - 1]; ++ priv->data[len - 1] = '\0'; + + priv->ncells = 0; + for (var = data + sizeof(struct brcm_nvram_header); +@@ -78,67 +131,67 @@ static int brcm_nvram_add_cells(struct b + } + + priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); +- if (!priv->cells) +- return -ENOMEM; ++ if (!priv->cells) { ++ err = -ENOMEM; ++ goto out; ++ } + + for (var = data + sizeof(struct brcm_nvram_header), idx = 0; + var < (char *)data + len && *var; + var = value + strlen(value) + 1, idx++) { ++ char *eq, *name; ++ + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; ++ name = devm_kstrdup(dev, var, GFP_KERNEL); ++ *eq = '='; ++ if (!name) { ++ err = -ENOMEM; ++ goto out; ++ } + value = eq + 1; + +- priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); +- if (!priv->cells[idx].name) +- return -ENOMEM; ++ priv->cells[idx].name = name; + priv->cells[idx].offset = value - (char *)data; + priv->cells[idx].bytes = strlen(value); + priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); +- if (!strcmp(var, "et0macaddr") || +- !strcmp(var, "et1macaddr") || +- !strcmp(var, "et2macaddr")) { ++ if (!strcmp(name, "et0macaddr") || ++ !strcmp(name, "et1macaddr") || ++ !strcmp(name, "et2macaddr")) { + priv->cells[idx].raw_len = strlen(value); + priv->cells[idx].bytes = ETH_ALEN; + priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr; + } + } + +- return 0; ++out: ++ priv->data[len - 1] = tmp; ++ return err; + } static int brcm_nvram_parse(struct brcm_nvram *priv) { @@ -42,7 +178,6 @@ Signed-off-by: Rafał Miłecki struct device *dev = priv->dev; - struct brcm_nvram_header header; - uint8_t *data; -+ uint8_t tmp; size_t len; int err; @@ -55,74 +190,62 @@ Signed-off-by: Rafał Miłecki } - len = le32_to_cpu(header.len); -+ len = le32_to_cpu(header->len); - +- - data = kzalloc(len, GFP_KERNEL); - if (!data) - return -ENOMEM; - - memcpy_fromio(data, priv->base, len); - data[len - 1] = '\0'; -+ tmp = priv->data[len - 1]; -+ priv->data[len - 1] = '\0'; - +- - err = brcm_nvram_add_cells(priv, data, len); - if (err) { -+ err = brcm_nvram_add_cells(priv, priv->data, len); -+ if (err) - dev_err(dev, "Failed to add cells: %d\n", err); +- dev_err(dev, "Failed to add cells: %d\n", err); - return err; -- } ++ len = le32_to_cpu(header->len); ++ if (len > priv->nvmem_size) { ++ dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, priv->nvmem_size); ++ return -EINVAL; + } - kfree(data); -+ priv->data[len - 1] = tmp; ++ err = brcm_nvram_add_cells(priv, priv->data, len); ++ if (err) ++ dev_err(dev, "Failed to add cells: %d\n", err); return 0; } -@@ -150,8 +140,10 @@ static int brcm_nvram_probe(struct platf +@@ -150,7 +203,6 @@ static int brcm_nvram_probe(struct platf .reg_read = brcm_nvram_read, }; struct device *dev = &pdev->dev; - struct resource *res; struct brcm_nvram *priv; -+ struct resource *res; -+ void __iomem *base; -+ size_t size; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -@@ -159,21 +151,29 @@ static int brcm_nvram_probe(struct platf +@@ -159,21 +211,19 @@ static int brcm_nvram_probe(struct platf return -ENOMEM; priv->dev = dev; - priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); -+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ size = resource_size(res); -+ -+ priv->data = kzalloc(size, GFP_KERNEL); -+ if (!priv->data) -+ return -ENOMEM; -+ -+ memcpy_fromio(priv->data, base, size); ++ err = brcm_nvram_copy_data(priv, pdev); ++ if (err) ++ return err; err = brcm_nvram_parse(priv); if (err) return err; - bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res)); -+ bcm47xx_nvram_init_from_iomem(base, size); - +- config.dev = dev; config.cells = priv->cells; config.ncells = priv->ncells; config.priv = priv; - config.size = resource_size(res); -+ config.size = size; ++ config.size = priv->nvmem_size; return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); } diff --git a/target/linux/bcm53xx/patches-6.1/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/bcm53xx/patches-6.1/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch index 901bb6dd3c..2aae713b64 100644 --- a/target/linux/bcm53xx/patches-6.1/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch +++ b/target/linux/bcm53xx/patches-6.1/800-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch @@ -1,40 +1,176 @@ -From a18378409fee1cac0f0c58a4770ff557b498c778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= -Date: Fri, 1 Sep 2023 10:44:26 +0200 +Date: Thu, 14 Sep 2023 07:59:09 +0200 Subject: [PATCH] nvmem: brcm_nvram: store a copy of NVRAM content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +This driver uses MMIO access for reading NVRAM from a flash device. +Underneath there is a flash controller that reads data and provides +mapping window. + +Using MMIO interface affects controller configuration and may break real +controller driver. It was reported by multiple users of devices with +NVRAM stored on NAND. + +Modify driver to read & cache NVRAM content during init and use that +copy to provide NVMEM data when requested. On NAND flashes due to their +alignment NVRAM partitions can be quite big (1 MiB and more) while +actual NVRAM content stays quite small (usually 16 to 32 KiB). To avoid +allocating so much memory check for actual data length. + +Link: https://lore.kernel.org/linux-mtd/CACna6rwf3_9QVjYcM+847biTX=K0EoWXuXcSMkJO1Vy_5vmVqA@mail.gmail.com/ +Fixes: 3fef9ed0627a ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM") +Cc: Arınç ÜNAL +Cc: Florian Fainelli +Cc: Scott Branden Signed-off-by: Rafał Miłecki --- - drivers/nvmem/brcm_nvram.c | 54 +++++++++++++++++++------------------- - 1 file changed, 27 insertions(+), 27 deletions(-) + drivers/nvmem/brcm_nvram.c | 130 +++++++++++++++++++++++++------------ + 1 file changed, 90 insertions(+), 40 deletions(-) --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c -@@ -19,7 +19,7 @@ - +@@ -17,9 +17,20 @@ + + #define NVRAM_MAGIC "FLSH" + ++/** ++ * struct brcm_nvram - driver state internal struct ++ * ++ * @nvmem_size: Size of the whole space available for NVRAM ++ * @data: NVRAM data copy stored to avoid poking underlaying flash controller ++ * @data_len: NVRAM data size ++ * @padding_byte: Padding value used to fill remaining space ++ */ struct brcm_nvram { struct device *dev; - void __iomem *base; ++ size_t nvmem_size; + uint8_t *data; ++ size_t data_len; ++ uint8_t padding_byte; struct nvmem_cell_info *cells; int ncells; }; -@@ -36,10 +36,8 @@ static int brcm_nvram_read(void *context +@@ -36,10 +47,47 @@ static int brcm_nvram_read(void *context size_t bytes) { struct brcm_nvram *priv = context; - u8 *dst = val; ++ size_t to_copy; ++ ++ if (offset + bytes > priv->data_len) ++ to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0); ++ else ++ to_copy = bytes; ++ ++ memcpy(val, priv->data + offset, to_copy); ++ ++ memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy); ++ ++ return 0; ++} ++ ++static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev) ++{ ++ struct resource *res; ++ void __iomem *base; ++ ++ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ priv->nvmem_size = resource_size(res); ++ ++ priv->padding_byte = readb(base + priv->nvmem_size - 1); ++ for (priv->data_len = priv->nvmem_size; ++ priv->data_len; ++ priv->data_len--) { ++ if (readb(base + priv->data_len - 1) != priv->padding_byte) ++ break; ++ } ++ WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); - while (bytes--) - *dst++ = readb(priv->base + offset++); -+ memcpy(val, priv->data + offset, bytes); ++ priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; ++ ++ memcpy_fromio(priv->data, base, priv->data_len); ++ ++ bcm47xx_nvram_init_from_iomem(base, priv->data_len); return 0; } -@@ -110,35 +108,27 @@ static int brcm_nvram_add_cells(struct b +@@ -67,8 +115,13 @@ static int brcm_nvram_add_cells(struct b + size_t len) + { + struct device *dev = priv->dev; +- char *var, *value, *eq; ++ char *var, *value; ++ uint8_t tmp; + int idx; ++ int err = 0; ++ ++ tmp = priv->data[len - 1]; ++ priv->data[len - 1] = '\0'; + + priv->ncells = 0; + for (var = data + sizeof(struct brcm_nvram_header); +@@ -78,67 +131,67 @@ static int brcm_nvram_add_cells(struct b + } + + priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); +- if (!priv->cells) +- return -ENOMEM; ++ if (!priv->cells) { ++ err = -ENOMEM; ++ goto out; ++ } + + for (var = data + sizeof(struct brcm_nvram_header), idx = 0; + var < (char *)data + len && *var; + var = value + strlen(value) + 1, idx++) { ++ char *eq, *name; ++ + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; ++ name = devm_kstrdup(dev, var, GFP_KERNEL); ++ *eq = '='; ++ if (!name) { ++ err = -ENOMEM; ++ goto out; ++ } + value = eq + 1; + +- priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); +- if (!priv->cells[idx].name) +- return -ENOMEM; ++ priv->cells[idx].name = name; + priv->cells[idx].offset = value - (char *)data; + priv->cells[idx].bytes = strlen(value); + priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); +- if (!strcmp(var, "et0macaddr") || +- !strcmp(var, "et1macaddr") || +- !strcmp(var, "et2macaddr")) { ++ if (!strcmp(name, "et0macaddr") || ++ !strcmp(name, "et1macaddr") || ++ !strcmp(name, "et2macaddr")) { + priv->cells[idx].raw_len = strlen(value); + priv->cells[idx].bytes = ETH_ALEN; + priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr; + } + } + +- return 0; ++out: ++ priv->data[len - 1] = tmp; ++ return err; + } static int brcm_nvram_parse(struct brcm_nvram *priv) { @@ -42,7 +178,6 @@ Signed-off-by: Rafał Miłecki struct device *dev = priv->dev; - struct brcm_nvram_header header; - uint8_t *data; -+ uint8_t tmp; size_t len; int err; @@ -55,74 +190,62 @@ Signed-off-by: Rafał Miłecki } - len = le32_to_cpu(header.len); -+ len = le32_to_cpu(header->len); - +- - data = kzalloc(len, GFP_KERNEL); - if (!data) - return -ENOMEM; - - memcpy_fromio(data, priv->base, len); - data[len - 1] = '\0'; -+ tmp = priv->data[len - 1]; -+ priv->data[len - 1] = '\0'; - +- - err = brcm_nvram_add_cells(priv, data, len); - if (err) { -+ err = brcm_nvram_add_cells(priv, priv->data, len); -+ if (err) - dev_err(dev, "Failed to add cells: %d\n", err); +- dev_err(dev, "Failed to add cells: %d\n", err); - return err; -- } ++ len = le32_to_cpu(header->len); ++ if (len > priv->nvmem_size) { ++ dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, priv->nvmem_size); ++ return -EINVAL; + } - kfree(data); -+ priv->data[len - 1] = tmp; ++ err = brcm_nvram_add_cells(priv, priv->data, len); ++ if (err) ++ dev_err(dev, "Failed to add cells: %d\n", err); return 0; } -@@ -150,8 +140,10 @@ static int brcm_nvram_probe(struct platf +@@ -150,7 +203,6 @@ static int brcm_nvram_probe(struct platf .reg_read = brcm_nvram_read, }; struct device *dev = &pdev->dev; - struct resource *res; struct brcm_nvram *priv; -+ struct resource *res; -+ void __iomem *base; -+ size_t size; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -@@ -159,21 +151,29 @@ static int brcm_nvram_probe(struct platf +@@ -159,21 +211,19 @@ static int brcm_nvram_probe(struct platf return -ENOMEM; priv->dev = dev; - priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); -+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ size = resource_size(res); -+ -+ priv->data = kzalloc(size, GFP_KERNEL); -+ if (!priv->data) -+ return -ENOMEM; -+ -+ memcpy_fromio(priv->data, base, size); ++ err = brcm_nvram_copy_data(priv, pdev); ++ if (err) ++ return err; err = brcm_nvram_parse(priv); if (err) return err; - bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res)); -+ bcm47xx_nvram_init_from_iomem(base, size); - +- config.dev = dev; config.cells = priv->cells; config.ncells = priv->ncells; config.priv = priv; - config.size = resource_size(res); -+ config.size = size; ++ config.size = priv->nvmem_size; return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); } -- 2.30.2