nvram.c 11 KB

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