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