nvram.c 11 KB

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