| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
- 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 <[email protected]>
- Cc: Florian Fainelli <[email protected]>
- Cc: Scott Branden <[email protected]>
- Signed-off-by: Rafał Miłecki <[email protected]>
- Acked-by: Arınç ÜNAL <[email protected]>
- ---
- drivers/nvmem/brcm_nvram.c | 134 ++++++++++++++++++++++++++-----------
- 1 file changed, 94 insertions(+), 40 deletions(-)
- --- 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
- 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++);
- + 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;
- }
- @@ -67,8 +118,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 +134,68 @@ 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)
- {
- + 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 platf
- .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 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);
- + 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));
- }
|