[PATCH V3] nvmem: brcm_nvram: store a copy of NVRAM content
Srinivas Kandagatla
srinivas.kandagatla at linaro.org
Fri Dec 8 03:15:24 PST 2023
Thank you for the patch,
On 02/11/2023 06:28, Rafał Miłecki wrote:
> From: Rafał Miłecki <rafal at milecki.pl>
>
> 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")
Any reason not to add
Cc: <Stable at vger.kernel.org>
?
--srini
> Cc: Arınç ÜNAL <arinc.unal at arinc9.com>
> Cc: Florian Fainelli <florian.fainelli at broadcom.com>
> Cc: Scott Branden <scott.branden at broadcom.com>
> Signed-off-by: Rafał Miłecki <rafal at milecki.pl>
> Acked-by: Arınç ÜNAL <arinc.unal at arinc9.com>
> ---
> V2: Minialize amount of allocated memory (check for actual data length)
> V3: Document missed fields in struct brcm_nvram
>
> drivers/nvmem/brcm_nvram.c | 134 ++++++++++++++++++++++++++-----------
> 1 file changed, 94 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
> index 9737104f3b76..5cdf339cfbec 100644
> --- a/drivers/nvmem/brcm_nvram.c
> +++ b/drivers/nvmem/brcm_nvram.c
> @@ -17,9 +17,23 @@
>
> #define NVRAM_MAGIC "FLSH"
>
> +/**
> + * struct brcm_nvram - driver state internal struct
> + *
> + * @dev: NVMEM device pointer
> + * @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
> + * @cells: Array of discovered NVMEM cells
> + * @ncells: Number of elements in cells
> + */
> 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 +50,47 @@ static int brcm_nvram_read(void *context, unsigned int offset, void *val,
> 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);
> +
> + priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
> + if (!priv->data)
> + return -ENOMEM;
> +
> + memcpy_fromio(priv->data, base, priv->data_len);
>
> - while (bytes--)
> - *dst++ = readb(priv->base + offset++);
> + bcm47xx_nvram_init_from_iomem(base, priv->data_len);
>
> return 0;
> }
> @@ -67,8 +118,13 @@ static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
> 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 +134,68 @@ static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
> }
>
> 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)
> {
> + struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data;
> struct device *dev = priv->dev;
> - struct brcm_nvram_header header;
> - uint8_t *data;
> size_t len;
> int err;
>
> - memcpy_fromio(&header, priv->base, sizeof(header));
> -
> - if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
> + if (memcmp(header->magic, NVRAM_MAGIC, 4)) {
> dev_err(dev, "Invalid NVRAM magic\n");
> return -EINVAL;
> }
>
> - 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';
> -
> - err = brcm_nvram_add_cells(priv, data, len);
> - if (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);
> + err = brcm_nvram_add_cells(priv, priv->data, len);
> + if (err)
> + dev_err(dev, "Failed to add cells: %d\n", err);
>
> return 0;
> }
> @@ -150,7 +207,6 @@ static int brcm_nvram_probe(struct platform_device *pdev)
> .reg_read = brcm_nvram_read,
> };
> struct device *dev = &pdev->dev;
> - struct resource *res;
> struct brcm_nvram *priv;
> int err;
>
> @@ -159,21 +215,19 @@ static int brcm_nvram_probe(struct platform_device *pdev)
> 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);
> + 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));
> -
> config.dev = dev;
> config.cells = priv->cells;
> config.ncells = priv->ncells;
> config.priv = priv;
> - config.size = resource_size(res);
> + config.size = priv->nvmem_size;
>
> return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
> }
More information about the linux-mtd
mailing list