source: trunk/package/unvram/src/nvram.c @ 15429

Last change on this file since 15429 was 15429, checked in by jow, 8 years ago

[package] unvram: make it work on bcm47xx, also look for /dev/mtdblockX

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