Root/package/nvram/src/nvram.c

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

Archive Download this file



interactive