OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * NVRAM variable manipulation (common) |
||
3 | * |
||
4 | * Copyright 2004, Broadcom Corporation |
||
5 | * Copyright 2009-2010, OpenWrt.org |
||
6 | * All Rights Reserved. |
||
7 | * |
||
8 | * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY |
||
9 | * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM |
||
10 | * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS |
||
11 | * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. |
||
12 | * |
||
13 | */ |
||
14 | |||
15 | #include "nvram.h" |
||
16 | |||
17 | #define TRACE(msg) \ |
||
18 | printf("%s(%i) in %s(): %s\n", \ |
||
19 | __FILE__, __LINE__, __FUNCTION__, msg ? msg : "?") |
||
20 | |||
21 | /* Size of "nvram" MTD partition */ |
||
22 | size_t nvram_part_size = 0; |
||
23 | |||
24 | |||
25 | /* |
||
26 | * -- Helper functions -- |
||
27 | */ |
||
28 | |||
29 | /* String hash */ |
||
30 | static uint32_t hash(const char *s) |
||
31 | { |
||
32 | uint32_t hash = 0; |
||
33 | |||
34 | while (*s) |
||
35 | hash = 31 * hash + *s++; |
||
36 | |||
37 | return hash; |
||
38 | } |
||
39 | |||
40 | /* Free all tuples. */ |
||
41 | static void _nvram_free(nvram_handle_t *h) |
||
42 | { |
||
43 | uint32_t i; |
||
44 | nvram_tuple_t *t, *next; |
||
45 | |||
46 | /* Free hash table */ |
||
47 | for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) { |
||
48 | for (t = h->nvram_hash[i]; t; t = next) { |
||
49 | next = t->next; |
||
50 | if (t->value) |
||
51 | free(t->value); |
||
52 | free(t); |
||
53 | } |
||
54 | h->nvram_hash[i] = NULL; |
||
55 | } |
||
56 | |||
57 | /* Free dead table */ |
||
58 | for (t = h->nvram_dead; t; t = next) { |
||
59 | next = t->next; |
||
60 | if (t->value) |
||
61 | free(t->value); |
||
62 | free(t); |
||
63 | } |
||
64 | |||
65 | h->nvram_dead = NULL; |
||
66 | } |
||
67 | |||
68 | /* (Re)allocate NVRAM tuples. */ |
||
69 | static nvram_tuple_t * _nvram_realloc( nvram_handle_t *h, nvram_tuple_t *t, |
||
70 | const char *name, const char *value ) |
||
71 | { |
||
72 | if ((strlen(value) + 1) > h->length - h->offset) |
||
73 | return NULL; |
||
74 | |||
75 | if (!t) { |
||
76 | if (!(t = malloc(sizeof(nvram_tuple_t) + strlen(name) + 1))) |
||
77 | return NULL; |
||
78 | |||
79 | /* Copy name */ |
||
80 | t->name = (char *) &t[1]; |
||
81 | strcpy(t->name, name); |
||
82 | |||
83 | t->value = NULL; |
||
84 | } |
||
85 | |||
86 | /* Copy value */ |
||
87 | if (!t->value || strcmp(t->value, value)) |
||
88 | { |
||
89 | if(!(t->value = (char *) realloc(t->value, strlen(value)+1))) |
||
90 | return NULL; |
||
91 | |||
92 | strcpy(t->value, value); |
||
93 | t->value[strlen(value)] = '\0'; |
||
94 | } |
||
95 | |||
96 | return t; |
||
97 | } |
||
98 | |||
99 | /* (Re)initialize the hash table. */ |
||
100 | static int _nvram_rehash(nvram_handle_t *h) |
||
101 | { |
||
102 | nvram_header_t *header = nvram_header(h); |
||
103 | char buf[] = "0xXXXXXXXX", *name, *value, *eq; |
||
104 | |||
105 | /* (Re)initialize hash table */ |
||
106 | _nvram_free(h); |
||
107 | |||
108 | /* Parse and set "name=value\0 ... \0\0" */ |
||
109 | name = (char *) &header[1]; |
||
110 | |||
111 | for (; *name; name = value + strlen(value) + 1) { |
||
112 | if (!(eq = strchr(name, '='))) |
||
113 | break; |
||
114 | *eq = '\0'; |
||
115 | value = eq + 1; |
||
116 | nvram_set(h, name, value); |
||
117 | *eq = '='; |
||
118 | } |
||
119 | |||
120 | /* Set special SDRAM parameters */ |
||
121 | if (!nvram_get(h, "sdram_init")) { |
||
122 | sprintf(buf, "0x%04X", (uint16_t)(header->crc_ver_init >> 16)); |
||
123 | nvram_set(h, "sdram_init", buf); |
||
124 | } |
||
125 | if (!nvram_get(h, "sdram_config")) { |
||
126 | sprintf(buf, "0x%04X", (uint16_t)(header->config_refresh & 0xffff)); |
||
127 | nvram_set(h, "sdram_config", buf); |
||
128 | } |
||
129 | if (!nvram_get(h, "sdram_refresh")) { |
||
130 | sprintf(buf, "0x%04X", |
||
131 | (uint16_t)((header->config_refresh >> 16) & 0xffff)); |
||
132 | nvram_set(h, "sdram_refresh", buf); |
||
133 | } |
||
134 | if (!nvram_get(h, "sdram_ncdl")) { |
||
135 | sprintf(buf, "0x%08X", header->config_ncdl); |
||
136 | nvram_set(h, "sdram_ncdl", buf); |
||
137 | } |
||
138 | |||
139 | return 0; |
||
140 | } |
||
141 | |||
142 | |||
143 | /* |
||
144 | * -- Public functions -- |
||
145 | */ |
||
146 | |||
147 | /* Get nvram header. */ |
||
148 | nvram_header_t * nvram_header(nvram_handle_t *h) |
||
149 | { |
||
150 | return (nvram_header_t *) &h->mmap[h->offset]; |
||
151 | } |
||
152 | |||
153 | /* Get the value of an NVRAM variable. */ |
||
154 | char * nvram_get(nvram_handle_t *h, const char *name) |
||
155 | { |
||
156 | uint32_t i; |
||
157 | nvram_tuple_t *t; |
||
158 | char *value; |
||
159 | |||
160 | if (!name) |
||
161 | return NULL; |
||
162 | |||
163 | /* Hash the name */ |
||
164 | i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash); |
||
165 | |||
166 | /* Find the associated tuple in the hash table */ |
||
167 | for (t = h->nvram_hash[i]; t && strcmp(t->name, name); t = t->next); |
||
168 | |||
169 | value = t ? t->value : NULL; |
||
170 | |||
171 | return value; |
||
172 | } |
||
173 | |||
174 | /* Set the value of an NVRAM variable. */ |
||
175 | int nvram_set(nvram_handle_t *h, const char *name, const char *value) |
||
176 | { |
||
177 | uint32_t i; |
||
178 | nvram_tuple_t *t, *u, **prev; |
||
179 | |||
180 | /* Hash the name */ |
||
181 | i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash); |
||
182 | |||
183 | /* Find the associated tuple in the hash table */ |
||
184 | for (prev = &h->nvram_hash[i], t = *prev; |
||
185 | t && strcmp(t->name, name); prev = &t->next, t = *prev); |
||
186 | |||
187 | /* (Re)allocate tuple */ |
||
188 | if (!(u = _nvram_realloc(h, t, name, value))) |
||
189 | return -12; /* -ENOMEM */ |
||
190 | |||
191 | /* Value reallocated */ |
||
192 | if (t && t == u) |
||
193 | return 0; |
||
194 | |||
195 | /* Move old tuple to the dead table */ |
||
196 | if (t) { |
||
197 | *prev = t->next; |
||
198 | t->next = h->nvram_dead; |
||
199 | h->nvram_dead = t; |
||
200 | } |
||
201 | |||
202 | /* Add new tuple to the hash table */ |
||
203 | u->next = h->nvram_hash[i]; |
||
204 | h->nvram_hash[i] = u; |
||
205 | |||
206 | return 0; |
||
207 | } |
||
208 | |||
209 | /* Unset the value of an NVRAM variable. */ |
||
210 | int nvram_unset(nvram_handle_t *h, const char *name) |
||
211 | { |
||
212 | uint32_t i; |
||
213 | nvram_tuple_t *t, **prev; |
||
214 | |||
215 | if (!name) |
||
216 | return 0; |
||
217 | |||
218 | /* Hash the name */ |
||
219 | i = hash(name) % NVRAM_ARRAYSIZE(h->nvram_hash); |
||
220 | |||
221 | /* Find the associated tuple in the hash table */ |
||
222 | for (prev = &h->nvram_hash[i], t = *prev; |
||
223 | t && strcmp(t->name, name); prev = &t->next, t = *prev); |
||
224 | |||
225 | /* Move it to the dead table */ |
||
226 | if (t) { |
||
227 | *prev = t->next; |
||
228 | t->next = h->nvram_dead; |
||
229 | h->nvram_dead = t; |
||
230 | } |
||
231 | |||
232 | return 0; |
||
233 | } |
||
234 | |||
235 | /* Get all NVRAM variables. */ |
||
236 | nvram_tuple_t * nvram_getall(nvram_handle_t *h) |
||
237 | { |
||
238 | int i; |
||
239 | nvram_tuple_t *t, *l, *x; |
||
240 | |||
241 | l = NULL; |
||
242 | |||
243 | for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) { |
||
244 | for (t = h->nvram_hash[i]; t; t = t->next) { |
||
245 | if( (x = (nvram_tuple_t *) malloc(sizeof(nvram_tuple_t))) != NULL ) |
||
246 | { |
||
247 | x->name = t->name; |
||
248 | x->value = t->value; |
||
249 | x->next = l; |
||
250 | l = x; |
||
251 | } |
||
252 | else |
||
253 | { |
||
254 | break; |
||
255 | } |
||
256 | } |
||
257 | } |
||
258 | |||
259 | return l; |
||
260 | } |
||
261 | |||
262 | /* Regenerate NVRAM. */ |
||
263 | int nvram_commit(nvram_handle_t *h) |
||
264 | { |
||
265 | nvram_header_t *header = nvram_header(h); |
||
266 | char *init, *config, *refresh, *ncdl; |
||
267 | char *ptr, *end; |
||
268 | int i; |
||
269 | nvram_tuple_t *t; |
||
270 | nvram_header_t tmp; |
||
271 | uint8_t crc; |
||
272 | |||
273 | /* Regenerate header */ |
||
274 | header->magic = NVRAM_MAGIC; |
||
275 | header->crc_ver_init = (NVRAM_VERSION << 8); |
||
276 | if (!(init = nvram_get(h, "sdram_init")) || |
||
277 | !(config = nvram_get(h, "sdram_config")) || |
||
278 | !(refresh = nvram_get(h, "sdram_refresh")) || |
||
279 | !(ncdl = nvram_get(h, "sdram_ncdl"))) { |
||
280 | header->crc_ver_init |= SDRAM_INIT << 16; |
||
281 | header->config_refresh = SDRAM_CONFIG; |
||
282 | header->config_refresh |= SDRAM_REFRESH << 16; |
||
283 | header->config_ncdl = 0; |
||
284 | } else { |
||
285 | header->crc_ver_init |= (strtoul(init, NULL, 0) & 0xffff) << 16; |
||
286 | header->config_refresh = strtoul(config, NULL, 0) & 0xffff; |
||
287 | header->config_refresh |= (strtoul(refresh, NULL, 0) & 0xffff) << 16; |
||
288 | header->config_ncdl = strtoul(ncdl, NULL, 0); |
||
289 | } |
||
290 | |||
291 | /* Clear data area */ |
||
292 | ptr = (char *) header + sizeof(nvram_header_t); |
||
293 | memset(ptr, 0xFF, nvram_part_size - h->offset - sizeof(nvram_header_t)); |
||
294 | memset(&tmp, 0, sizeof(nvram_header_t)); |
||
295 | |||
296 | /* Leave space for a double NUL at the end */ |
||
297 | end = (char *) header + nvram_part_size - h->offset - 2; |
||
298 | |||
299 | /* Write out all tuples */ |
||
300 | for (i = 0; i < NVRAM_ARRAYSIZE(h->nvram_hash); i++) { |
||
301 | for (t = h->nvram_hash[i]; t; t = t->next) { |
||
302 | if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end) |
||
303 | break; |
||
304 | ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1; |
||
305 | } |
||
306 | } |
||
307 | |||
308 | /* End with a double NULL and pad to 4 bytes */ |
||
309 | *ptr = '\0'; |
||
310 | ptr++; |
||
311 | |||
312 | if( (int)ptr % 4 ) |
||
313 | memset(ptr, 0, 4 - ((int)ptr % 4)); |
||
314 | |||
315 | ptr++; |
||
316 | |||
317 | /* Set new length */ |
||
318 | header->len = NVRAM_ROUNDUP(ptr - (char *) header, 4); |
||
319 | |||
320 | /* Little-endian CRC8 over the last 11 bytes of the header */ |
||
321 | tmp.crc_ver_init = header->crc_ver_init; |
||
322 | tmp.config_refresh = header->config_refresh; |
||
323 | tmp.config_ncdl = header->config_ncdl; |
||
324 | crc = hndcrc8((unsigned char *) &tmp + NVRAM_CRC_START_POSITION, |
||
325 | sizeof(nvram_header_t) - NVRAM_CRC_START_POSITION, 0xff); |
||
326 | |||
327 | /* Continue CRC8 over data bytes */ |
||
328 | crc = hndcrc8((unsigned char *) &header[0] + sizeof(nvram_header_t), |
||
329 | header->len - sizeof(nvram_header_t), crc); |
||
330 | |||
331 | /* Set new CRC8 */ |
||
332 | header->crc_ver_init |= crc; |
||
333 | |||
334 | /* Write out */ |
||
335 | msync(h->mmap, h->length, MS_SYNC); |
||
336 | fsync(h->fd); |
||
337 | |||
338 | /* Reinitialize hash table */ |
||
339 | return _nvram_rehash(h); |
||
340 | } |
||
341 | |||
342 | /* Open NVRAM and obtain a handle. */ |
||
343 | nvram_handle_t * nvram_open(const char *file, int rdonly) |
||
344 | { |
||
345 | int i; |
||
346 | int fd; |
||
347 | char *mtd = NULL; |
||
348 | nvram_handle_t *h; |
||
349 | nvram_header_t *header; |
||
350 | int offset = -1; |
||
351 | |||
352 | /* If erase size or file are undefined then try to define them */ |
||
353 | if( (nvram_part_size == 0) || (file == NULL) ) |
||
354 | { |
||
355 | /* Finding the mtd will set the appropriate erase size */ |
||
356 | if( (mtd = nvram_find_mtd()) == NULL || nvram_part_size == 0 ) |
||
357 | { |
||
358 | free(mtd); |
||
359 | return NULL; |
||
360 | } |
||
361 | } |
||
362 | |||
363 | if( (fd = open(file ? file : mtd, O_RDWR)) > -1 ) |
||
364 | { |
||
365 | char *mmap_area = (char *) mmap( |
||
366 | NULL, nvram_part_size, PROT_READ | PROT_WRITE, |
||
367 | (( rdonly == NVRAM_RO ) ? MAP_PRIVATE : MAP_SHARED) | MAP_LOCKED, fd, 0); |
||
368 | |||
369 | if( mmap_area != MAP_FAILED ) |
||
370 | { |
||
371 | /* |
||
372 | * Start looking for NVRAM_MAGIC at beginning of MTD |
||
373 | * partition. Stop if there is less than NVRAM_MIN_SPACE |
||
374 | * to check, that was the lowest used size. |
||
375 | */ |
||
376 | for( i = 0; i <= ((nvram_part_size - NVRAM_MIN_SPACE) / sizeof(uint32_t)); i++ ) |
||
377 | { |
||
378 | if( ((uint32_t *)mmap_area)[i] == NVRAM_MAGIC ) |
||
379 | { |
||
380 | offset = i * sizeof(uint32_t); |
||
381 | break; |
||
382 | } |
||
383 | } |
||
384 | |||
385 | if( offset < 0 ) |
||
386 | { |
||
387 | munmap(mmap_area, nvram_part_size); |
||
388 | free(mtd); |
||
389 | close(fd); |
||
390 | return NULL; |
||
391 | } |
||
392 | else if( (h = malloc(sizeof(nvram_handle_t))) != NULL ) |
||
393 | { |
||
394 | memset(h, 0, sizeof(nvram_handle_t)); |
||
395 | |||
396 | h->fd = fd; |
||
397 | h->mmap = mmap_area; |
||
398 | h->length = nvram_part_size; |
||
399 | h->offset = offset; |
||
400 | |||
401 | header = nvram_header(h); |
||
402 | |||
403 | if (header->magic == NVRAM_MAGIC && |
||
404 | (rdonly || header->len < h->length - h->offset)) { |
||
405 | _nvram_rehash(h); |
||
406 | free(mtd); |
||
407 | return h; |
||
408 | } |
||
409 | else |
||
410 | { |
||
411 | munmap(h->mmap, h->length); |
||
412 | free(h); |
||
413 | } |
||
414 | } |
||
415 | } |
||
416 | } |
||
417 | |||
418 | free(mtd); |
||
419 | close(fd); |
||
420 | return NULL; |
||
421 | } |
||
422 | |||
423 | /* Close NVRAM and free memory. */ |
||
424 | int nvram_close(nvram_handle_t *h) |
||
425 | { |
||
426 | _nvram_free(h); |
||
427 | munmap(h->mmap, h->length); |
||
428 | close(h->fd); |
||
429 | free(h); |
||
430 | |||
431 | return 0; |
||
432 | } |
||
433 | |||
434 | /* Determine NVRAM device node. */ |
||
435 | char * nvram_find_mtd(void) |
||
436 | { |
||
437 | FILE *fp; |
||
438 | int i, part_size; |
||
439 | char dev[PATH_MAX]; |
||
440 | char *path = NULL; |
||
441 | struct stat s; |
||
442 | |||
443 | if ((fp = fopen("/proc/mtd", "r"))) |
||
444 | { |
||
445 | while( fgets(dev, sizeof(dev), fp) ) |
||
446 | { |
||
447 | if( strstr(dev, "nvram") && sscanf(dev, "mtd%d: %08x", &i, &part_size) ) |
||
448 | { |
||
449 | nvram_part_size = part_size; |
||
450 | |||
451 | sprintf(dev, "/dev/mtdblock%d", i); |
||
452 | if( stat(dev, &s) > -1 && (s.st_mode & S_IFBLK) ) |
||
453 | { |
||
454 | if( (path = (char *) malloc(strlen(dev)+1)) != NULL ) |
||
455 | { |
||
456 | strncpy(path, dev, strlen(dev)+1); |
||
457 | break; |
||
458 | } |
||
459 | } |
||
460 | } |
||
461 | } |
||
462 | fclose(fp); |
||
463 | } |
||
464 | |||
465 | return path; |
||
466 | } |
||
467 | |||
468 | /* Check NVRAM staging file. */ |
||
469 | char * nvram_find_staging(void) |
||
470 | { |
||
471 | struct stat s; |
||
472 | |||
473 | if( (stat(NVRAM_STAGING, &s) > -1) && (s.st_mode & S_IFREG) ) |
||
474 | { |
||
475 | return NVRAM_STAGING; |
||
476 | } |
||
477 | |||
478 | return NULL; |
||
479 | } |
||
480 | |||
481 | /* Copy NVRAM contents to staging file. */ |
||
482 | int nvram_to_staging(void) |
||
483 | { |
||
484 | int fdmtd, fdstg, stat; |
||
485 | char *mtd = nvram_find_mtd(); |
||
486 | char buf[nvram_part_size]; |
||
487 | |||
488 | stat = -1; |
||
489 | |||
490 | if( (mtd != NULL) && (nvram_part_size > 0) ) |
||
491 | { |
||
492 | if( (fdmtd = open(mtd, O_RDONLY)) > -1 ) |
||
493 | { |
||
494 | if( read(fdmtd, buf, sizeof(buf)) == sizeof(buf) ) |
||
495 | { |
||
496 | if((fdstg = open(NVRAM_STAGING, O_WRONLY | O_CREAT, 0600)) > -1) |
||
497 | { |
||
498 | write(fdstg, buf, sizeof(buf)); |
||
499 | fsync(fdstg); |
||
500 | close(fdstg); |
||
501 | |||
502 | stat = 0; |
||
503 | } |
||
504 | } |
||
505 | |||
506 | close(fdmtd); |
||
507 | } |
||
508 | } |
||
509 | |||
510 | free(mtd); |
||
511 | return stat; |
||
512 | } |
||
513 | |||
514 | /* Copy staging file to NVRAM device. */ |
||
515 | int staging_to_nvram(void) |
||
516 | { |
||
517 | int fdmtd, fdstg, stat; |
||
518 | char *mtd = nvram_find_mtd(); |
||
519 | char buf[nvram_part_size]; |
||
520 | |||
521 | stat = -1; |
||
522 | |||
523 | if( (mtd != NULL) && (nvram_part_size > 0) ) |
||
524 | { |
||
525 | if( (fdstg = open(NVRAM_STAGING, O_RDONLY)) > -1 ) |
||
526 | { |
||
527 | if( read(fdstg, buf, sizeof(buf)) == sizeof(buf) ) |
||
528 | { |
||
529 | if( (fdmtd = open(mtd, O_WRONLY | O_SYNC)) > -1 ) |
||
530 | { |
||
531 | write(fdmtd, buf, sizeof(buf)); |
||
532 | fsync(fdmtd); |
||
533 | close(fdmtd); |
||
534 | stat = 0; |
||
535 | } |
||
536 | } |
||
537 | |||
538 | close(fdstg); |
||
539 | |||
540 | if( !stat ) |
||
541 | stat = unlink(NVRAM_STAGING) ? 1 : 0; |
||
542 | } |
||
543 | } |
||
544 | |||
545 | free(mtd); |
||
546 | return stat; |
||
547 | } |