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[h->offset];
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 i;
341    int fd;
342    char *mtd = NULL;
343    nvram_handle_t *h;
344    nvram_header_t *header;
345    int offset = -1;
346
347    /* If erase size or file are undefined then try to define them */
348    if( (nvram_erase_size == 0) || (file == NULL) )
349    {
350        /* Finding the mtd will set the appropriate erase size */
351        if( (mtd = nvram_find_mtd()) == NULL || nvram_erase_size == 0 )
352        {
353            free(mtd);
354            return NULL;
355        }
356    }
357
358    if( (fd = open(file ? file : mtd, O_RDWR)) > -1 )
359    {
360        char *mmap_area = (char *) mmap(
361            NULL, nvram_erase_size, PROT_READ | PROT_WRITE,
362            (( rdonly == NVRAM_RO ) ? MAP_PRIVATE : MAP_SHARED) | MAP_LOCKED, fd, 0);
363
364        if( mmap_area != MAP_FAILED )
365        {
366            for( i = 0; i <= ((nvram_erase_size - NVRAM_SPACE) / sizeof(uint32_t)); i++ )
367            {
368                if( ((uint32_t *)mmap_area)[i] == NVRAM_MAGIC )
369                {
370                    offset = i * sizeof(uint32_t);
371                    break;
372                }
373            }
374
375            if( offset < 0 )
376            {
377                free(mtd);
378                return NULL;
379            }
380            else if( (h = malloc(sizeof(nvram_handle_t))) != NULL )
381            {
382                memset(h, 0, sizeof(nvram_handle_t));
383
384                h->fd = fd;
385                h->mmap = mmap_area;
386                h->length = nvram_erase_size;
387                h->offset = offset;
388
389                header = nvram_header(h);
390
391                if( header->magic == NVRAM_MAGIC )
392                {
393                    _nvram_rehash(h);
394                    free(mtd);
395                    return h;
396                }
397                else
398                {
399                    munmap(h->mmap, h->length);
400                    free(h);
401                }
402            }
403        }
404    }
405
406    free(mtd);
407    return NULL;
408}
409
410/* Close NVRAM and free memory. */
411int nvram_close(nvram_handle_t *h)
412{
413    _nvram_free(h);
414    munmap(h->mmap, h->length);
415    close(h->fd);
416    free(h);
417
418    return 0;
419}
420
421/* Determine NVRAM device node. */
422char * nvram_find_mtd(void)
423{
424    FILE *fp;
425    int i, esz;
426    char dev[PATH_MAX];
427    char *path = NULL;
428    struct stat s;
429    int supported = 1;
430
431    /* Refuse any operation on the WGT634U */
432    if( (fp = fopen("/proc/diag/model", "r")) )
433    {
434        if( fgets(dev, sizeof(dev), fp) && !strncmp(dev, "Netgear WGT634U", 15) )
435            supported = 0;
436
437        fclose(fp);
438    }
439
440    if( supported && (fp = fopen("/proc/mtd", "r")) )
441    {
442        while( fgets(dev, sizeof(dev), fp) )
443        {
444            if( strstr(dev, "nvram") && sscanf(dev, "mtd%d: %08x", &i, &esz) )
445            {
446                nvram_erase_size = esz;
447
448                sprintf(dev, "/dev/mtdblock/%d", i);
449                if( stat(dev, &s) > -1 && (s.st_mode & S_IFBLK) )
450                {
451                    if( (path = (char *) malloc(strlen(dev)+1)) != NULL )
452                    {
453                        strncpy(path, dev, strlen(dev)+1);
454                        break;
455                    }
456                }
457                else
458                {
459                    sprintf(dev, "/dev/mtdblock%d", i);
460                    if( stat(dev, &s) > -1 && (s.st_mode & S_IFBLK) )
461                    {
462                        if( (path = (char *) malloc(strlen(dev)+1)) != NULL )
463                        {
464                            strncpy(path, dev, strlen(dev)+1);
465                            break;
466                        }
467                    }
468                }
469            }
470        }
471        fclose(fp);
472    }
473
474    return path;
475}
476
477/* Check NVRAM staging file. */
478char * nvram_find_staging(void)
479{
480    struct stat s;
481
482    if( (stat(NVRAM_STAGING, &s) > -1) && (s.st_mode & S_IFREG) )
483    {
484        return NVRAM_STAGING;
485    }
486
487    return NULL;
488}
489
490/* Copy NVRAM contents to staging file. */
491int nvram_to_staging(void)
492{
493    int fdmtd, fdstg, stat;
494    char *mtd = nvram_find_mtd();
495    char buf[nvram_erase_size];
496
497    stat = -1;
498
499    if( (mtd != NULL) && (nvram_erase_size > 0) )
500    {
501        if( (fdmtd = open(mtd, O_RDONLY)) > -1 )
502        {
503            if( read(fdmtd, buf, sizeof(buf)) == sizeof(buf) )
504            {
505                if((fdstg = open(NVRAM_STAGING, O_WRONLY | O_CREAT, 0600)) > -1)
506                {
507                    write(fdstg, buf, sizeof(buf));
508                    fsync(fdstg);
509                    close(fdstg);
510
511                    stat = 0;
512                }
513            }
514
515            close(fdmtd);
516        }
517    }
518
519    free(mtd);
520    return stat;
521}
522
523/* Copy staging file to NVRAM device. */
524int staging_to_nvram(void)
525{
526    int fdmtd, fdstg, stat;
527    char *mtd = nvram_find_mtd();
528    char buf[nvram_erase_size];
529
530    stat = -1;
531
532    if( (mtd != NULL) && (nvram_erase_size > 0) )
533    {
534        if( (fdstg = open(NVRAM_STAGING, O_RDONLY)) > -1 )
535        {
536            if( read(fdstg, buf, sizeof(buf)) == sizeof(buf) )
537            {
538                if( (fdmtd = open(mtd, O_WRONLY | O_SYNC)) > -1 )
539                {
540                    write(fdmtd, buf, sizeof(buf));
541                    fsync(fdmtd);
542                    close(fdmtd);
543                    stat = 0;
544                }
545            }
546
547            close(fdstg);
548
549            if( !stat )
550                stat = unlink(NVRAM_STAGING) ? 1 : 0;
551        }
552    }
553
554    free(mtd);
555    return stat;
556}
557

Archive Download this file



interactive