2 * NVRAM variable manipulation (common)
4 * Copyright 2004, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
19 #include <linux/delay.h>
20 #include <bcmendian.h>
24 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
25 #define get_seconds() CURRENT_TIME
26 #define jiffy_time (jiffies)
28 #define jiffy_time (jiffies - INITIAL_JIFFIES)
32 extern int _nvram_init_read(void);
33 extern int nvram_space
; /* Size of the NVRAM. Generally 32kb */
34 extern char nvram_buf
[]; /* The buffer to hold values. */
35 extern char *nvram_commit_buf
; /* Buffer for the flash eraseblock(s). */
36 extern int oflow_area_present
;
38 char *_nvram_get(const char *name
);
39 int _nvram_set(const char *name
, const char *value
);
40 int _nvram_unset(const char *name
);
41 int _nvram_getall(char *buf
, int count
);
42 int _nvram_commit(struct nvram_header
*header
);
43 int _nvram_init(void *sb
);
44 void _nvram_exit(void);
45 uint8
nvram_calc_crc(struct nvram_header
* nvh
);
46 int walk_chain(int pr
);
47 struct nvram_dbitem
*_nvram_realloc(struct nvram_dbitem
*t
, const char *name
,
49 void _nvram_free(struct nvram_dbitem
*t
);
50 void _nvram_valbuf_compactify(void);
53 static struct nvram_dbitem
*BCMINITDATA(nvram_hash
)[NUM_HLH
];
54 static struct nvram_dbitem
*nvram_dead
;
55 static int it_siz
, it_cnt
;
56 static unsigned nvram_offset
= 0;
59 /* Prio == write priority (actually: category)
60 * * Entries that must be in the main (original) NVRAM area, visible
62 * * Entries that can go anywhere. PMON & CFE don't need to see these.
64 * Since there is no spec for what PMON & CFE need to see, and the list
65 * may change for different firmware versions:
66 * Assume that when "restore_defaults" != "0" then CFE cleared
67 * the NVRAM and set its defaults.
68 * Put a special flag item that is physically after the prio 1 items.
69 * When refreshing frm the flash, everything before this is defined as prio 1.
70 * Everything after it is prio 2.
71 * If it is not found, then everything is prio 1.
72 * When writing out (commit), write the prio 1 items, then the flag item,
73 * then everything else.
77 #define PRIO_MARK_ITEM "}Marker42"
79 #define PRIO_ANYWHERE 2
82 int prefer_ov
= 0; /* Prefer "any" go to oflow area? */
83 int oflow_ok
= 1; /* It is okay to go into oflow area? */
86 /* Free all tuples. Should be locked. */
88 BCMINITFN(nvram_free
)(void)
91 struct nvram_dbitem
*t
, *next
;
94 for (i
= 0; i
< NUM_HLH
; i
++) {
95 for (t
= nvram_hash
[i
]; t
; t
= next
) {
102 /* Free dead table */
103 for (t
= nvram_dead
; t
; t
= next
) {
109 /* Indicate to per-port code that all tuples have been freed */
113 /* String hash. FNV-1a algorithm */
117 unsigned hval
= 0x811c9dc5;
121 hval
+= (hval
<<1) + (hval
<<4) + (hval
<<7) + (hval
<<8) + (hval
<<24);
126 /* (Re)initialize the hash table. Should be locked. */
128 BCMINITFN(nvram_rehash
)(struct nvram_header
*header
)
130 char buf
[20], *name
, *value
, *end
, *eq
;
133 /* (Re)initialize hash table */
136 /* Parse and set "name=value\0 ... \0\0" */
137 name
= (char *) &header
[1];
138 end
= (char *) header
+ nvram_space
- 2;
139 end
[0] = end
[1] = '\0';
140 for (; *name
; name
= value
+ strlen(value
) + 1) {
141 if (!(eq
= strchr(name
, '=')))
146 _nvram_set(name
, value
);
150 /* Set special SDRAM parameters */
151 if (!_nvram_get("sdram_init")) {
152 sprintf(buf
, "0x%04X", (uint16
)(header
->crc_ver_init
>> 16));
153 _nvram_set("sdram_init", buf
);
155 if (!_nvram_get("sdram_config")) {
156 sprintf(buf
, "0x%04X", (uint16
)(header
->config_refresh
& 0xffff));
157 _nvram_set("sdram_config", buf
);
159 if (!_nvram_get("sdram_refresh")) {
160 sprintf(buf
, "0x%04X", (uint16
)((header
->config_refresh
>> 16) & 0xffff));
161 _nvram_set("sdram_refresh", buf
);
163 if (!_nvram_get("sdram_ncdl")) {
164 sprintf(buf
, "0x%08X", header
->config_ncdl
);
165 _nvram_set("sdram_ncdl", buf
);
167 printk("Item count: %d valsiz: %u\n", n
, nvram_offset
);
171 /* Get the value of an NVRAM variable. Should be locked. */
173 _nvram_get(const char *name
)
176 struct nvram_dbitem
*t
, **prev
;
181 if (name
[0] == '=') { /* Special dynamic size info */
182 if (strcmp(name
, "=nvram_used") == 0) {
183 sprintf(&nvram_buf
[nvram_space
- 128], "%d",
185 return (&nvram_buf
[nvram_space
- 128]);
187 if (strcmp(name
, "=nvram_space") == 0) {
188 i
= nvram_space
-1; /* Trailing null but no pad. */
189 if (oflow_area_present
)
191 sprintf(&nvram_buf
[nvram_space
- 128], "%d", i
);
192 return (&nvram_buf
[nvram_space
- 128]);
197 i
= hash(name
) % NUM_HLH
;
199 /* Find the item in the hash table */
200 for (prev
= &nvram_hash
[i
], t
= *prev
;
201 t
&& (strcmp(t
->name
, name
));
202 prev
= &t
->next
, t
= *prev
) {}
204 *prev
= t
->next
; /* Move it to the top of the chain - MRU. */
205 t
->next
= nvram_hash
[i
];
212 /* Set the value of an NVRAM variable. Should be locked. */
214 BCMINITFN(_nvram_set
)(const char *name
, const char *value
)
217 struct nvram_dbitem
*t
, *u
, **prev
;
219 /* Special control items. No name. Value is control command.
220 * prio_main, _any , oflow Says to flag subsequent sets accordingly.
221 * "=name" says to set prio on that name. */
223 printk("Special: '%s'\n", value
);
224 if (strcmp(value
, "prio_main") == 0)
226 else if (strcmp(value
, "prio_any") == 0)
227 prio
= PRIO_ANYWHERE
;
228 else if (strcmp(value
, "prio_oflow") == 0)
230 else if (*value
== '=') { /* Set prio on this. */
232 i
= hash(name
) % NUM_HLH
;
233 for (prev
= &nvram_hash
[i
], t
= *prev
;
234 t
&& (strcmp(t
->name
, name
));
235 prev
= &t
->next
, t
= *prev
) {}
239 else if (strcmp(value
, "prefer_oflow-n") == 0)
241 else if (strcmp(value
, "prefer_oflow-y") == 0)
243 else if (strcmp(value
, "prefer_oflow-n") == 0) alt
= 0; //temp
244 else if (strcmp(value
, "prefer_oflow-a") == 0) alt
= 1; //temp
245 else if (strcmp(value
, "oflow_ok-n") == 0)
247 else if (strcmp(value
, "oflow_ok-y") == 0)
249 else if (strcmp(value
, "reset_stat") == 0) {
250 _nvram_unset("z-commit");
251 for (i
= 0; ++i
< 50; ) {
252 sprintf(sbuf
, "z-commit_%02u", i
);
257 printk("nvram: Unknown special value '%s'\n", value
);
262 i
= hash(name
) % NUM_HLH
;
264 /* Find the item in the hash table */
265 for (prev
= &nvram_hash
[i
], t
= *prev
;
266 t
&& (strcmp(t
->name
, name
));
267 prev
= &t
->next
, t
= *prev
) {}
269 /* (Re)allocate tuple */
270 if (!(u
= _nvram_realloc(t
, name
, value
)))
271 return -12; /* -ENOMEM */
273 /* Value reallocated */
275 *prev
= u
->next
; /* Move it to the top of the chain. */
276 u
->next
= nvram_hash
[i
];
282 /* Move old tuple to the dead table */
283 // Can never get here!! It would mean that the node existed but
284 // nvram_realloc returned a different one. But it doesn't.
287 t
->next
= nvram_dead
;
291 /* Add new tuple to the hash table */
292 u
->next
= nvram_hash
[i
];
298 /* Unset the value of an NVRAM variable. Should be locked. */
300 BCMINITFN(_nvram_unset
)(const char *name
)
303 struct nvram_dbitem
*t
, **prev
;
309 i
= hash(name
) % NUM_HLH
;
311 /* Find the item in the hash table */
312 for (prev
= &nvram_hash
[i
], t
= *prev
;
313 t
&& (strcmp(t
->name
, name
));
314 prev
= &t
->next
, t
= *prev
) {}
316 /* Move it to the dead table */
319 t
->next
= nvram_dead
;
325 /* Get all NVRAM variables. Should be locked. */
327 _nvram_getall(char *buf
, int count
)
330 struct nvram_dbitem
*t
;
335 /* Write name=value\0 ... \0\0 */
336 for (i
= 0; i
< NUM_HLH
; i
++) {
337 for (t
= nvram_hash
[i
]; t
; t
= t
->next
) {
338 if ((count
- len
) > (strlen(t
->name
) + 1 + strlen(t
->value
) + 1))
339 len
+= sprintf(buf
+ len
, "%s=%s", t
->name
, t
->value
) + 1;
344 _nvram_valbuf_compactify(); //temp
348 /* Regenerate NVRAM. Should be locked. */
350 BCMINITFN(_nvram_commit
)(struct nvram_header
*header
)
352 char *init
, *config
, *refresh
, *ncdl
;
357 int cnt
= 0, n
= jiffies
; // temp
358 struct nvram_dbitem
*t
;
360 /* Regenerate header */
361 header
->magic
= NVRAM_MAGIC
;
362 header
->crc_ver_init
= (NVRAM_VERSION
<< 8);
363 if (!(init
= _nvram_get("sdram_init")) ||
364 !(config
= _nvram_get("sdram_config")) ||
365 !(refresh
= _nvram_get("sdram_refresh")) ||
366 !(ncdl
= _nvram_get("sdram_ncdl"))) {
367 header
->crc_ver_init
|= SDRAM_INIT
<< 16;
368 header
->config_refresh
= SDRAM_CONFIG
;
369 header
->config_refresh
|= SDRAM_REFRESH
<< 16;
370 header
->config_ncdl
= 0;
372 header
->crc_ver_init
|= (bcm_strtoul(init
, NULL
, 0) & 0xffff) << 16;
373 header
->config_refresh
= bcm_strtoul(config
, NULL
, 0) & 0xffff;
374 header
->config_refresh
|= (bcm_strtoul(refresh
, NULL
, 0) & 0xffff) << 16;
375 header
->config_ncdl
= bcm_strtoul(ncdl
, NULL
, 0);
378 /* Keep info on the commits, for development debugging. */
382 if ((ptr
= _nvram_get("z-commit")) != NULL
)
383 i
= simple_strtol(ptr
, NULL
, 10);
385 sprintf(sbuf
, "%02u it_cnt, it_siz, uptime (msec), time(sec)", i
);
386 _nvram_set("z-commit", sbuf
);
389 sprintf(sbuf
, "z-commit_%02u", i
);
390 sprintf(sbuf
+20, "%4d,%6d,%8u, %lu", it_cnt
, it_siz
, jiffies_to_msecs(jiffy_time
), get_seconds());
391 _nvram_set(sbuf
, sbuf
+20);
394 /* Clear data area */
395 /* Leave space for a closing NUL & roundup at the end */
396 rem1
= nvram_space
-1 -3 - sizeof(struct nvram_header
);
398 ptr
= (char *)header
+ sizeof(struct nvram_header
);
399 memset(ptr
, 0xff, nvram_space
- sizeof(struct nvram_header
));
401 /* Write out all tuples */
402 for (i
= 0; i
< NUM_HLH
; i
++) {
403 for (t
= nvram_hash
[i
]; t
; t
= t
->next
) {
405 siz
= strlen(t
->name
) + strlen(t
->value
) + 2;
407 printk("NVRAM overflow at %s=%-15.15s\n", t
->name
, t
->value
);
410 ptr
+= sprintf(ptr
, "%s=%s", t
->name
, t
->value
) + 1;
415 *ptr
++ = 0; /* Ends with an extra NUL */
416 header
->len
= ROUNDUP(ptr
- (char *)header
, 4);
417 header
->crc_ver_init
|= nvram_calc_crc(header
);
419 /* Reinitialize hash table. Why?? Just to check. And purge. */
420 nvram_rehash(header
);
424 /* Initialize hash table. Should be locked. */
426 BCMINITFN(_nvram_init
)(void *sb
)
429 struct nvram_header
*header
;
431 printk("jiffies: %lu msec: %u\n", jiffy_time
, jiffies_to_msecs(jiffy_time
)); //temp
432 ret
= _nvram_init_read();
434 header
= (struct nvram_header
*)(nvram_commit_buf
+ ret
);
435 nvram_rehash(header
);
438 oflow_area_present
= 1;
439 ov_hdr
= (struct nvram_header
*)(nvram_commit_buf
);
440 nvram_rehash_ov(ov_hdr
, header
);
447 /* Free hash table. Should be locked. */
449 BCMINITFN(_nvram_exit
)(void)
454 /* returns the CRC8 of the nvram */
456 BCMINITFN(nvram_calc_crc
)(struct nvram_header
* nvh
)
458 struct nvram_header tmp
;
461 /* Little-endian CRC8 over the last 11 bytes of the header */
462 tmp
.crc_ver_init
= htol32((nvh
->crc_ver_init
& NVRAM_CRC_VER_MASK
));
463 tmp
.config_refresh
= htol32(nvh
->config_refresh
);
464 tmp
.config_ncdl
= htol32(nvh
->config_ncdl
);
466 crc
= hndcrc8((uint8
*) &tmp
+ NVRAM_CRC_START_POSITION
,
467 sizeof(struct nvram_header
) - NVRAM_CRC_START_POSITION
,
470 /* Continue CRC8 over data bytes */
471 crc
= hndcrc8((uint8
*) &nvh
[1], nvh
->len
- sizeof(struct nvram_header
), crc
);
476 /* Purge un-used value items from the value buffer.
479 void _nvram_valbuf_compactify(void)
485 struct nvram_dbitem
*t
, *next
;
486 int sz
= nvram_offset
; //temp
488 wk_buf
= nvram_commit_buf
;
489 memcpy(wk_buf
, nvram_buf
, nvram_offset
);
490 /* Walk all tuples & copy & update value ptrs */
492 for (i
= 0; i
< NUM_HLH
; i
++) {
493 for (t
= nvram_hash
[i
]; t
; t
= t
->next
) {
495 siz
= strlen(t
->value
- nvram_buf
+ wk_buf
) +1;
496 if (siz
+ nxt
- nvram_buf
> NVRAM_VAL_SIZE
-4) {
497 printk("Oh crap! Over-ran valbuf during compatify. Can't happen!\n");
498 printk("We're gonna die!\n");
501 memcpy(nxt
, t
->value
- nvram_buf
+ wk_buf
, siz
);
503 nxt
+= ROUNDUP_P2(siz
, 4);
508 /* Free dead table */
509 for (t
= nvram_dead
; t
; t
= next
) {
514 nvram_offset
= nxt
- nvram_buf
;
515 printk("compactify: was: %d now: %d\n", sz
, nvram_offset
); //temp
519 * t = existing node to update its value.
520 * If t is NULL, alloc a new node, and set the name & prio.
522 * If value not NULL or new value is != existing value, then
523 * copy the value param to the nvram_buf.
526 struct nvram_dbitem
*_nvram_realloc(struct nvram_dbitem
*t
, const char *name
, const char *value
)
530 siz
= strlen(value
) +1;
531 if ((nvram_offset
+ siz
) >= NVRAM_VAL_SIZE
-256) {
532 _nvram_valbuf_compactify();
533 if ((nvram_offset
+ siz
) >= NVRAM_VAL_SIZE
-256)
538 if (!(t
= kmalloc(sizeof(struct nvram_tuple
) + strlen(name
) + 1, GFP_ATOMIC
)))
541 strcpy(t
->name
, name
);
546 /* Copy value. Always on a word boundary.*/
547 if (!t
->value
|| strcmp(t
->value
, value
)) {
548 t
->value
= &nvram_buf
[nvram_offset
];
549 memcpy(t
->value
, value
, siz
);
550 nvram_offset
+= ROUNDUP_P2(siz
, 4);
556 _nvram_free(struct nvram_dbitem
*t
)
565 /* Return the size the variables will take when written to NVRAM. */
566 int walk_chain(int z
)
569 struct nvram_dbitem
*t
;
572 for (i
= 0; i
< NUM_HLH
; i
++) {
573 for (t
= nvram_hash
[i
]; t
; t
= t
->next
) {
574 it_siz
+= strlen(t
->name
) + strlen(t
->value
) + 2;
577 printk("%3d: %s=%-15.15s\n", i
, t
->name
, t
->value
);
583 /* For the emacs code formatting