Skip to content

Commit 1e37bf8

Browse files
Rafał Miłeckigregkh
authored andcommitted
nvmem: brcm_nvram: store a copy of NVRAM content
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: 3fef9ed ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM") Cc: <Stable@vger.kernel.org> Cc: Arınç ÜNAL <arinc.unal@arinc9.com> Cc: Florian Fainelli <florian.fainelli@broadcom.com> Cc: Scott Branden <scott.branden@broadcom.com> Signed-off-by: Rafał Miłecki <rafal@milecki.pl> Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Link: https://lore.kernel.org/r/20231215111358.316727-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent a2a8aef commit 1e37bf8

File tree

1 file changed

+94
-40
lines changed

1 file changed

+94
-40
lines changed

drivers/nvmem/brcm_nvram.c

Lines changed: 94 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,23 @@
1717

1818
#define NVRAM_MAGIC "FLSH"
1919

20+
/**
21+
* struct brcm_nvram - driver state internal struct
22+
*
23+
* @dev: NVMEM device pointer
24+
* @nvmem_size: Size of the whole space available for NVRAM
25+
* @data: NVRAM data copy stored to avoid poking underlaying flash controller
26+
* @data_len: NVRAM data size
27+
* @padding_byte: Padding value used to fill remaining space
28+
* @cells: Array of discovered NVMEM cells
29+
* @ncells: Number of elements in cells
30+
*/
2031
struct brcm_nvram {
2132
struct device *dev;
22-
void __iomem *base;
33+
size_t nvmem_size;
34+
uint8_t *data;
35+
size_t data_len;
36+
uint8_t padding_byte;
2337
struct nvmem_cell_info *cells;
2438
int ncells;
2539
};
@@ -36,10 +50,47 @@ static int brcm_nvram_read(void *context, unsigned int offset, void *val,
3650
size_t bytes)
3751
{
3852
struct brcm_nvram *priv = context;
39-
u8 *dst = val;
53+
size_t to_copy;
54+
55+
if (offset + bytes > priv->data_len)
56+
to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0);
57+
else
58+
to_copy = bytes;
59+
60+
memcpy(val, priv->data + offset, to_copy);
61+
62+
memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy);
63+
64+
return 0;
65+
}
66+
67+
static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev)
68+
{
69+
struct resource *res;
70+
void __iomem *base;
71+
72+
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
73+
if (IS_ERR(base))
74+
return PTR_ERR(base);
75+
76+
priv->nvmem_size = resource_size(res);
77+
78+
priv->padding_byte = readb(base + priv->nvmem_size - 1);
79+
for (priv->data_len = priv->nvmem_size;
80+
priv->data_len;
81+
priv->data_len--) {
82+
if (readb(base + priv->data_len - 1) != priv->padding_byte)
83+
break;
84+
}
85+
WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);
86+
87+
priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
88+
if (!priv->data)
89+
return -ENOMEM;
90+
91+
memcpy_fromio(priv->data, base, priv->data_len);
4092

41-
while (bytes--)
42-
*dst++ = readb(priv->base + offset++);
93+
bcm47xx_nvram_init_from_iomem(base, priv->data_len);
4394

4495
return 0;
4596
}
@@ -67,8 +118,13 @@ static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
67118
size_t len)
68119
{
69120
struct device *dev = priv->dev;
70-
char *var, *value, *eq;
121+
char *var, *value;
122+
uint8_t tmp;
71123
int idx;
124+
int err = 0;
125+
126+
tmp = priv->data[len - 1];
127+
priv->data[len - 1] = '\0';
72128

73129
priv->ncells = 0;
74130
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,
78134
}
79135

80136
priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
81-
if (!priv->cells)
82-
return -ENOMEM;
137+
if (!priv->cells) {
138+
err = -ENOMEM;
139+
goto out;
140+
}
83141

84142
for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
85143
var < (char *)data + len && *var;
86144
var = value + strlen(value) + 1, idx++) {
145+
char *eq, *name;
146+
87147
eq = strchr(var, '=');
88148
if (!eq)
89149
break;
90150
*eq = '\0';
151+
name = devm_kstrdup(dev, var, GFP_KERNEL);
152+
*eq = '=';
153+
if (!name) {
154+
err = -ENOMEM;
155+
goto out;
156+
}
91157
value = eq + 1;
92158

93-
priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
94-
if (!priv->cells[idx].name)
95-
return -ENOMEM;
159+
priv->cells[idx].name = name;
96160
priv->cells[idx].offset = value - (char *)data;
97161
priv->cells[idx].bytes = strlen(value);
98162
priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
99-
if (!strcmp(var, "et0macaddr") ||
100-
!strcmp(var, "et1macaddr") ||
101-
!strcmp(var, "et2macaddr")) {
163+
if (!strcmp(name, "et0macaddr") ||
164+
!strcmp(name, "et1macaddr") ||
165+
!strcmp(name, "et2macaddr")) {
102166
priv->cells[idx].raw_len = strlen(value);
103167
priv->cells[idx].bytes = ETH_ALEN;
104168
priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr;
105169
}
106170
}
107171

108-
return 0;
172+
out:
173+
priv->data[len - 1] = tmp;
174+
return err;
109175
}
110176

111177
static int brcm_nvram_parse(struct brcm_nvram *priv)
112178
{
179+
struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data;
113180
struct device *dev = priv->dev;
114-
struct brcm_nvram_header header;
115-
uint8_t *data;
116181
size_t len;
117182
int err;
118183

119-
memcpy_fromio(&header, priv->base, sizeof(header));
120-
121-
if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
184+
if (memcmp(header->magic, NVRAM_MAGIC, 4)) {
122185
dev_err(dev, "Invalid NVRAM magic\n");
123186
return -EINVAL;
124187
}
125188

126-
len = le32_to_cpu(header.len);
127-
128-
data = kzalloc(len, GFP_KERNEL);
129-
if (!data)
130-
return -ENOMEM;
131-
132-
memcpy_fromio(data, priv->base, len);
133-
data[len - 1] = '\0';
134-
135-
err = brcm_nvram_add_cells(priv, data, len);
136-
if (err) {
137-
dev_err(dev, "Failed to add cells: %d\n", err);
138-
return err;
189+
len = le32_to_cpu(header->len);
190+
if (len > priv->nvmem_size) {
191+
dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len,
192+
priv->nvmem_size);
193+
return -EINVAL;
139194
}
140195

141-
kfree(data);
196+
err = brcm_nvram_add_cells(priv, priv->data, len);
197+
if (err)
198+
dev_err(dev, "Failed to add cells: %d\n", err);
142199

143200
return 0;
144201
}
@@ -150,7 +207,6 @@ static int brcm_nvram_probe(struct platform_device *pdev)
150207
.reg_read = brcm_nvram_read,
151208
};
152209
struct device *dev = &pdev->dev;
153-
struct resource *res;
154210
struct brcm_nvram *priv;
155211
int err;
156212

@@ -159,21 +215,19 @@ static int brcm_nvram_probe(struct platform_device *pdev)
159215
return -ENOMEM;
160216
priv->dev = dev;
161217

162-
priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
163-
if (IS_ERR(priv->base))
164-
return PTR_ERR(priv->base);
218+
err = brcm_nvram_copy_data(priv, pdev);
219+
if (err)
220+
return err;
165221

166222
err = brcm_nvram_parse(priv);
167223
if (err)
168224
return err;
169225

170-
bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res));
171-
172226
config.dev = dev;
173227
config.cells = priv->cells;
174228
config.ncells = priv->ncells;
175229
config.priv = priv;
176-
config.size = resource_size(res);
230+
config.size = priv->nvmem_size;
177231

178232
return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
179233
}

0 commit comments

Comments
 (0)