From dc02849c75a5a53c64064a8e0632b39bc4db22bf Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Jan 2011 14:08:21 -0500 Subject: [PATCH] nvram [K26 part]: auto-detect the nvram size This patch continues the patchset for phase 1 of expanding the NVRAM size to be more than 32KB. It also reverts commit d9cc31058157541b9e464761ae0456e53e8c76f8 --- release/src-rt/include/bcmnvram.h | 46 +- release/src-rt/linux/linux-2.6/arch/mips/Kconfig | 4 - .../arch/mips/brcm-boards/bcm947xx/nvram_linux.c | 625 +++++++++++++++------ release/src-rt/shared/nvram/nvram.c | 430 +++++++++++--- 4 files changed, 839 insertions(+), 266 deletions(-) diff --git a/release/src-rt/include/bcmnvram.h b/release/src-rt/include/bcmnvram.h index a88b480d58..d4514bcf42 100644 --- a/release/src-rt/include/bcmnvram.h +++ b/release/src-rt/include/bcmnvram.h @@ -20,20 +20,33 @@ #include #include +#define ROUNDUP_P2(n, a) ((n + (a - 1)) & ~(a - 1)) + struct nvram_header { uint32 magic; uint32 len; uint32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ + /* as in: IIIIVVCC */ uint32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ uint32 config_ncdl; /* ncdl values for memc */ }; +/* Used by app, CFE, etc. */ struct nvram_tuple { char *name; char *value; struct nvram_tuple *next; }; +/* Used by kernel NVRAM internals. */ +struct nvram_dbitem { + struct nvram_dbitem *next; + char *value; + uint16 hsh; + uint16 prio; + char name[0]; +}; + /* * Get default value for an NVRAM variable */ @@ -43,23 +56,23 @@ extern char *nvram_default_get(const char *name); * Initialize NVRAM access. May be unnecessary or undefined on certain * platforms. */ -extern int nvram_init(void *sih); +extern int nvram_init(void *sbh); /* * Append a chunk of nvram variables to the global list */ -extern int nvram_append(void *si, char *vars, uint varsz); +extern int nvram_append(void *sb, char *vars, uint varsz); /* * Check for reset button press for restoring factory defaults. */ -extern int nvram_reset(void *sih); +extern bool nvram_reset(void *sbh); /* * Disable NVRAM access. May be unnecessary or undefined on certain * platforms. */ -extern void nvram_exit(void *sih); +extern void nvram_exit(void *sbh); /* * Get the value of an NVRAM variable. The pointer returned may be @@ -73,7 +86,7 @@ extern char * nvram_get(const char *name); * Read the reset GPIO value from the nvram and set the GPIO * as input */ -extern int BCMINITFN(nvram_resetgpio_init)(void *sih); +extern int BCMINITFN(nvram_resetgpio_init)(void *sbh); /* * Get the value of an NVRAM variable. @@ -156,18 +169,29 @@ uint8 nvram_calc_crc(struct nvram_header * nvh); #define NVRAM_SOFTWARE_VERSION "1" #define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ +#define NVRAM_OFLOW_MAGIC 0x464c5348 /* 'HSLF' */ #define NVRAM_CLEAR_MAGIC 0x0 #define NVRAM_INVALID_MAGIC 0xFFFFFFFF #define NVRAM_VERSION 1 #define NVRAM_HEADER_SIZE 20 -#if CONFIG_NVRAM_SIZE -#define NVRAM_SPACE CONFIG_NVRAM_SIZE * 0x0400 -#else #define NVRAM_SPACE 0x8000 -#endif -#define NVRAM_MAX_VALUE_LEN 255 -#define NVRAM_MAX_PARAM_LEN 64 +/* The size of the buffer for storing item values. + * This can be smaller than the nvram area, because it has no names. + * But each value is on a 4-byte boundary, so average of 2 wasted + * bytes per item. Replaced values also occupy space. We have to + * garbage collect when it gets full. + * + * This is for "nvram_buf", which is statically allocated. + * early_nvram_*() copies the nvram data to this buffer. So it *must* + * be at least 60KB for E3000 routers that have 60KB nvram. + */ +#define NVRAM_VAL_SIZE (64 * 1024) + +#define NVRAM_32K 0x8000 /* Sorry, 32kB is pretty much baked in. */ + +#define NVRAM_MAX_VALUE_LEN 255 /* Not true! */ +#define NVRAM_MAX_PARAM_LEN 64 /* Not true! */ #define NVRAM_CRC_START_POSITION 9 /* magic, len, crc8 to be skipped */ #define NVRAM_CRC_VER_MASK 0xffffff00 /* for crc_ver_init */ diff --git a/release/src-rt/linux/linux-2.6/arch/mips/Kconfig b/release/src-rt/linux/linux-2.6/arch/mips/Kconfig index cf901e5479..208050abd8 100644 --- a/release/src-rt/linux/linux-2.6/arch/mips/Kconfig +++ b/release/src-rt/linux/linux-2.6/arch/mips/Kconfig @@ -704,10 +704,6 @@ config RAM_SIZE int "Memory size (in megabytes)" depends on BCM947XX -config NVRAM_SIZE - int "NVRAM size (in kilobytes, 0 = default)" - depends on BCM947XX - config SHRINKMEM bool "Support for shrink memory" depends on BCM947XX diff --git a/release/src-rt/linux/linux-2.6/arch/mips/brcm-boards/bcm947xx/nvram_linux.c b/release/src-rt/linux/linux-2.6/arch/mips/brcm-boards/bcm947xx/nvram_linux.c index ed4477a70b..3d22929aa8 100644 --- a/release/src-rt/linux/linux-2.6/arch/mips/brcm-boards/bcm947xx/nvram_linux.c +++ b/release/src-rt/linux/linux-2.6/arch/mips/brcm-boards/bcm947xx/nvram_linux.c @@ -1,6 +1,7 @@ /* * NVRAM variable manipulation (Linux kernel half) * + * Copyright 2006, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY @@ -8,7 +9,6 @@ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * - * $Id: nvram_linux.c,v 1.8 2008/07/04 01:15:09 Exp $ */ #include @@ -26,117 +26,248 @@ #include #include #include -#include #include +#include #include #include #include -#include -#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) #include +#else // K24 +#include +#include +#endif #include #include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include +#include +#include +#include +#ifdef MODULE +/* This isn't right, but I can't figure out how to make the link error go away. */ +#define flush_cache_all() do { hndcrc8(nvram_buf, sizeof(nvram_buf), 0); hndcrc8(nvram_buf, sizeof(nvram_buf), 0); } while (0) +#endif +#endif // K26 + +MODULE_LICENSE("GPL"); + +#define KB * 1024 + +static int hdr_valid(struct nvram_header *header, int max); /* In BSS to minimize text size and page aligned so it can be mmap()-ed */ -static char nvram_buf[NVRAM_SPACE] __attribute__((aligned(PAGE_SIZE))); -static char *nvram_commit_buf = NULL; +/* Used for: + * In early... as nvram (read) staging buffer. + * In normal.. to hold the values of items. + */ +char nvram_buf[NVRAM_VAL_SIZE] __attribute__((aligned(PAGE_SIZE))); + +/* This is the staging buffer for data going to/from the flash. + * Also as work buffer for compactify. + * It is large enough to hold all the NVRAM data and is 1 or more EBs is size. + * The first chunk (before the nvram areaa) in the flash eb is preserved. */ +unsigned char *nvram_commit_buf = NULL; +static int erasesize; /* The size of flash eraseblock & commit_buf. + * 32KB rounded up to mtd->erasesise. (64KB or 128KB) */ + +int oflow_area_present = 0; + +/* The nvram area is the last 32KB (or 60kb for E3000) of the last eraseblock of the flash chip. + * Normally this is the mtdN partition named "nvram". + * Normally this paritition is the entire last eraseblock. Do "cat /proc/mtd" to see this. + * The first part of the last EB, from the start up to the NVRAM area, is + * not used by pmon/cfe. + * We use the next-to-last 32kb for overflow (extended) nvram area. On a + * smallish flash chip these 2 areas are the entire EB. Some larger routers + * have 128KB EB size, and on these the 1st 64KB of the last EB is unused. + * + * The implementation of this code is one main area of 32KB and one oflow area + * of 32KB, for a total available nvram of 64KB. + * Period. + * + * Some routers have pmon/cfe that uses 60KB for nvram. On these, there is no + * overflow area. The total nvram area of these is 60kb. + * + */ -#ifdef MODULE +#if NVRAM_SPACE != (32 * 1024) +#error Attempt to redefine NVRAM_SPACE to something other than 32K. +#endif -#define early_nvram_get(name) nvram_get(name) +/* This is the size of pmon/cfe (and for us: "main") nvram area. + * Normally 32kb, but a few routers it is 60kb. + */ +int nvram_space = NVRAM_32K; /* Determined at probe time. */ -#else /* !MODULE */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) +#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args) +#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args) -/* Global SB handle */ +#define mem_map_reserve(a) SetPageReserved(a) +#define mem_map_unreserve(a) ClearPageReserved(a) + +#define bcm947xx_sbh bcm947xx_sih extern void *bcm947xx_sih; -extern spinlock_t bcm947xx_sih_lock; -/* Convenience */ +#define sb_setcore si_setcore +#define SB_CC CC_CORE_ID +#define SB_FLASH2_SZ SI_FLASH2_SZ +#define SB_FLASH1_SZ SI_FLASH1_SZ +#define SB_FLASH1 SI_FLASH1 +#define SB_FLASH2 SI_FLASH2 +#define SB_BUS SI_BUS +#define sb_setosh si_setosh +#define sb_memc_get_ncdl si_memc_get_ncdl + #define sih bcm947xx_sih -#define sih_lock bcm947xx_sih_lock -#define KB * 1024 -#define MB * 1024 * 1024 +#endif // KERNEL 2.6 +#if 0 static int nvram_valid(struct nvram_header *header) { - return (header->magic == NVRAM_MAGIC) && - (header->len >= sizeof(struct nvram_header)) && (header->len <= NVRAM_SPACE) -#if 0 - && (nvram_calc_crc(header) == (uint8) header->crc_ver_init)) + return + header->magic == NVRAM_MAGIC && + header->len >= sizeof(struct nvram_header) && + header->len <= NVRAM_SPACE && +#ifdef MIPSEB + 1; /* oleg -- no crc check for now */ +#else + (header->crc_ver_init & 255) == + hndcrc8((char *) header + NVRAM_CRC_START_POSITION, + header->len - NVRAM_CRC_START_POSITION, CRC8_INIT_VALUE); #endif - ; } +#endif + +#ifdef MODULE + +#define early_nvram_get(name) nvram_get(name) +#define early_nvram_getall(name,c) _nvram_getall(name,c) +extern void *bcm947xx_sbh; +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock +#define NVR_DEVNAME "nvram2" +#define NVR_DEVNUM 1 + +#else /* !MODULE */ +#define NVR_DEVNAME "nvram" +#define NVR_DEVNUM 0 + +/* Global SB handle */ +extern void *bcm947xx_sbh; +extern spinlock_t bcm947xx_sbh_lock; + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock + +/* Early (before mm or mtd) read-only access to NVRAM */ /* Probe for NVRAM header */ -static int +static void __init early_nvram_init(void) { struct nvram_header *header; chipcregs_t *cc; struct sflash *info = NULL; int i; + int j; uint32 base, off, lim; u32 *src, *dst; - if ((cc = si_setcore(sih, CC_CORE_ID, 0)) != NULL) { - base = KSEG1ADDR(SI_FLASH2); + if ((cc = sb_setcore(sbh, SB_CC, 0)) != NULL) { + base = KSEG1ADDR(SB_FLASH2); switch (readl(&cc->capabilities) & CC_CAP_FLASH_MASK) { case PFLASH: - lim = SI_FLASH2_SZ; + lim = SB_FLASH2_SZ; break; case SFLASH_ST: case SFLASH_AT: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) if ((info = sflash_init(sih, cc)) == NULL) - return -1; + return; +#else + if ((info = sflash_init(cc)) == NULL) + return; +#endif lim = info->size; break; case FLASH_NONE: default: - return -1; + return; } } else { /* extif assumed, Stop at 4 MB */ - base = KSEG1ADDR(SI_FLASH1); - lim = SI_FLASH1_SZ; + base = KSEG1ADDR(SB_FLASH1); + lim = SB_FLASH1_SZ; + } + + /* XXX: hack for supporting the CFE environment stuff on WGT634U */ + src = (u32 *) KSEG1ADDR(base + 8 * 1024 * 1024 - 0x2000); + dst = (u32 *) nvram_buf; + if ((lim == 0x02000000) && ((*src & 0xff00ff) == 0x000001)) { + printk("early_nvram_init: WGT634U NVRAM found.\n"); + + for (i = 0; i < 0x1ff0; i++) { + if (*src == 0xFFFFFFFF) + break; + *dst++ = *src++; + } + return; } off = FLASH_MIN; while (off <= lim) { /* Windowed flash access */ - header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_SPACE); - if (nvram_valid(header)) + j = 32 KB; + header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_32K); + if (hdr_valid(header, NVRAM_32K)) + goto found; + j = 4 KB; + header = (struct nvram_header *) KSEG1ADDR(base + off - (NVRAM_32K + 28 KB)); + if (hdr_valid(header, NVRAM_32K + 28 KB)) goto found; off <<= 1; } + printk("Probing didn't find nvram, assuming 32K.\n"); + j = 32 KB; /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ header = (struct nvram_header *) KSEG1ADDR(base + 4 KB); - if (nvram_valid(header)) + if (header->magic == NVRAM_MAGIC) goto found; - + header = (struct nvram_header *) KSEG1ADDR(base + 1 KB); - if (nvram_valid(header)) + if (header->magic == NVRAM_MAGIC) goto found; - - return -1; + + printk("early_nvram_init: NVRAM not found\n"); + return; found: src = (u32 *) header; dst = (u32 *) nvram_buf; + nvram_space = 64 KB - j; + printk("early_nvram_init detected %d KB NVRAM area\n", nvram_space/1024); + bzero(nvram_buf, sizeof(nvram_buf)); for (i = 0; i < sizeof(struct nvram_header); i += 4) *dst++ = *src++; - for (; i < header->len && i < NVRAM_SPACE; i += 4) - *dst++ = ltoh32(*src++); - - return 0; + for (; i < header->len && i < nvram_space; i += 4) + *dst++ = *src++; } /* Early (before mm or mtd) read-only access to NVRAM */ -static char * +static char * __init early_nvram_get(const char *name) { char *var, *value, *end, *eq; @@ -145,14 +276,11 @@ early_nvram_get(const char *name) return NULL; /* Too early? */ - if (sih == NULL) + if (sbh == NULL) return NULL; if (!nvram_buf[0]) - if (early_nvram_init() != 0) { - printk("early_nvram_get: Failed reading nvram var %s\n", name); - return NULL; - } + early_nvram_init(); /* Look for name=value and return value */ var = &nvram_buf[sizeof(struct nvram_header)]; @@ -169,21 +297,18 @@ early_nvram_get(const char *name) return NULL; } -static int +static int __init early_nvram_getall(char *buf, int count) { char *var, *end; int len = 0; - + /* Too early? */ - if (sih == NULL) + if (sbh == NULL) return -1; if (!nvram_buf[0]) - if (early_nvram_init() != 0) { - printk("early_nvram_getall: Failed reading nvram var\n"); - return -1; - } + early_nvram_init(); bzero(buf, count); @@ -206,17 +331,36 @@ extern int _nvram_set(const char *name, const char *value); extern int _nvram_unset(const char *name); extern int _nvram_getall(char *buf, int count); extern int _nvram_commit(struct nvram_header *header); -extern int _nvram_init(void *sih); +extern int _nvram_init(void *sbh); extern void _nvram_exit(void); /* Globals */ static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED; static struct semaphore nvram_sem; -static unsigned long nvram_offset = 0; static int nvram_major = -1; -static struct class *nvram_class = NULL; static struct mtd_info *nvram_mtd = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static struct class *nvram_class = NULL; +#else +static devfs_handle_t nvram_handle = NULL; +#endif + +static int +hdr_valid(struct nvram_header *header, int max) +{ + return (header->magic == NVRAM_MAGIC && + header->len >= sizeof(struct nvram_header) && + header->len <= max && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + (nvram_calc_crc(header) == (uint8) header->crc_ver_init)); +#else + (header->crc_ver_init & 255) == + hndcrc8((char *) header + NVRAM_CRC_START_POSITION, + header->len - NVRAM_CRC_START_POSITION, CRC8_INIT_VALUE); +#endif +} +#if 0 int _nvram_read(char *buf) { @@ -224,54 +368,73 @@ _nvram_read(char *buf) size_t len; if (!nvram_mtd || - nvram_mtd->read(nvram_mtd, nvram_mtd->size - NVRAM_SPACE, NVRAM_SPACE, &len, buf) || - len != NVRAM_SPACE || + MTD_READ(nvram_mtd, nvram_mtd->size - nvram_space, nvram_space, &len, buf) || + len != nvram_space || !nvram_valid(header)) { + printk("_nvram_read: invalid nvram image\n"); /* Maybe we can recover some data from early initialization */ - memcpy(buf, nvram_buf, NVRAM_SPACE); + memcpy(buf, nvram_buf, nvram_space); } return 0; } +#endif -struct nvram_tuple * -_nvram_realloc(struct nvram_tuple *t, const char *name, const char *value) +/* Read the entire /dev/nvram block. Works only if EB is >= 32KB. + * Uses the beginning of commit_buf. + * Returns: < 0 for error. + * >= 0 -- offset into nvram_commit_buf of the NVRAM header. + * N.B., nvram_commit_buf[] has the last 64KB of the flash nvram partition. + */ +int +_nvram_init_read(void) { - if ((nvram_offset + strlen(value) + 1) > NVRAM_SPACE) - return NULL; + size_t len; + int j; + int ret; + struct nvram_header *header; + u_int32_t offset; /* fseek position of the last EB in the /mtd/nvram partition. */ + unsigned int i; - if (!t) { - if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC))) - return NULL; + if (!nvram_mtd) { + printk("nvram_init: NVRAM not found\n"); + return -ENODEV; + } - /* Copy name */ - t->name = (char *) &t[1]; - strcpy(t->name, name); + oflow_area_present = 0; + if (erasesize < 2 * NVRAM_32K) + return -ENODEV; - t->value = NULL; + i = 2 * NVRAM_32K; + /* seek offset to the last 64KB. Normally 0. 64k on 128k EB size. */ + offset = nvram_mtd->size - i; + len = 0; + /* Read the last 64kb of flash */ + ret = MTD_READ(nvram_mtd, offset, i, &len, nvram_commit_buf); + if (ret || len != i) { + printk("nvram_init: read error ret = %d, len = %d/%d\n", ret, len, i); + return -EIO; } - - /* Copy value */ - if (!t->value || strcmp(t->value, value)) { - t->value = &nvram_buf[nvram_offset]; - strcpy(t->value, value); - nvram_offset += strlen(value) + 1; + /* Probe various spots to find the header. Every 4K down from 32K from the end.*/ + for (j = 32 KB; j >= 0 ; j -= 4 KB) { + header = (struct nvram_header *)(nvram_commit_buf + j); + if (hdr_valid(header, 64 KB - j)) + break; } - - return t; + if (j >= 0) + printk("Probing found nvram header at %dK, size %dK\n", (j)/1024, (64 KB - j)/1024); + else { + printk("Probing didn't find nvram header. Assuming 32K\n"); + j = 32 KB; + } + nvram_space = 64 KB - j; + return j; } -void -_nvram_free(struct nvram_tuple *t) -{ - if (!t) - nvram_offset = 0; - else - kfree(t); -} +/* Called in early initialization. */ int -nvram_init(void *sih) +nvram_init(void *sbh) { return 0; } @@ -281,17 +444,9 @@ nvram_set(const char *name, const char *value) { unsigned long flags; int ret; - struct nvram_header *header; spin_lock_irqsave(&nvram_lock, flags); - if ((ret = _nvram_set(name, value))) { - /* Consolidate space and try again */ - if ((header = kmalloc(NVRAM_SPACE, GFP_ATOMIC))) { - if (_nvram_commit(header) == 0) - ret = _nvram_set(name, value); - kfree(header); - } - } + ret = _nvram_set(name, value); spin_unlock_irqrestore(&nvram_lock, flags); return ret; @@ -342,9 +497,6 @@ erase_callback(struct erase_info *done) int nvram_commit(void) { -#if 0 - char *buf; -#endif size_t erasesize, len, magic_len; unsigned int i; int ret; @@ -367,40 +519,39 @@ nvram_commit(void) } /* Backup sector blocks to be erased */ - erasesize = ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize); -#if 0 - if (!(buf = kmalloc(erasesize, GFP_KERNEL))) { - printk("nvram_commit: out of memory\n"); - return -ENOMEM; - } -#endif + erasesize = ROUNDUP(nvram_space, nvram_mtd->erasesize); down(&nvram_sem); - if ((i = erasesize - NVRAM_SPACE) > 0) { + //#warning no commit + //_nvram_commit(nvram_commit_buf); ret = -ENODEV; goto done; //temp!!! + + if ((i = erasesize - nvram_space) > 0) { offset = nvram_mtd->size - erasesize; len = 0; - ret = nvram_mtd->read(nvram_mtd, offset, i, &len, nvram_commit_buf); + ret = MTD_READ(nvram_mtd, offset, i, &len, nvram_commit_buf); if (ret || len != i) { printk("nvram_commit: read error ret = %d, len = %d/%d\n", ret, len, i); ret = -EIO; goto done; } - header = (struct nvram_header *)(nvram_commit_buf + i); - magic_offset = i + ((void *)&header->magic - (void *)header); - } else { - offset = nvram_mtd->size - NVRAM_SPACE; - magic_offset = ((void *)&header->magic - (void *)header); - header = (struct nvram_header *)nvram_commit_buf; } + else { + offset = nvram_mtd->size - nvram_space; + i = 0; + } + header = (struct nvram_header *)(nvram_commit_buf + i); + magic_offset = i + offsetof(struct nvram_header, magic); /* clear the existing magic # to mark the NVRAM as unusable * we can pull MAGIC bits low without erase */ header->magic = NVRAM_CLEAR_MAGIC; /* All zeros magic */ - /* Unlock sector blocks */ + + /* Unlock sector blocks (for Intel 28F320C3B flash) , 20060309 */ if (nvram_mtd->unlock) nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize); - ret = nvram_mtd->write(nvram_mtd, offset + magic_offset, sizeof(header->magic), + + ret = MTD_WRITE(nvram_mtd, offset + magic_offset, sizeof(header->magic), &magic_len, (char *)&header->magic); if (ret || magic_len != sizeof(header->magic)) { printk("nvram_commit: clear MAGIC error\n"); @@ -408,10 +559,10 @@ nvram_commit(void) goto done; } - header->magic = NVRAM_MAGIC; /* reset MAGIC before we regenerate the NVRAM, * otherwise we'll have an incorrect CRC */ + header->magic = NVRAM_MAGIC; /* Regenerate NVRAM */ spin_lock_irqsave(&nvram_lock, flags); ret = _nvram_commit(header); @@ -421,9 +572,7 @@ nvram_commit(void) /* Erase sector blocks */ init_waitqueue_head(&wait_q); - for (; offset < nvram_mtd->size - NVRAM_SPACE + header->len; - offset += nvram_mtd->erasesize) { - + for (; offset < nvram_mtd->size - nvram_space + header->len; offset += nvram_mtd->erasesize) { erase.mtd = nvram_mtd; erase.addr = offset; erase.len = nvram_mtd->erasesize; @@ -437,7 +586,7 @@ nvram_commit(void) if (nvram_mtd->unlock) nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize); - if ((ret = nvram_mtd->erase(nvram_mtd, &erase))) { + if ((ret = MTD_ERASE(nvram_mtd, &erase))) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); printk("nvram_commit: erase error\n"); @@ -452,19 +601,17 @@ nvram_commit(void) /* Write partition up to end of data area */ header->magic = NVRAM_INVALID_MAGIC; /* All ones magic */ offset = nvram_mtd->size - erasesize; - i = erasesize - NVRAM_SPACE + header->len; - ret = nvram_mtd->write(nvram_mtd, offset, i, &len, nvram_commit_buf); + i = erasesize - nvram_space + header->len; + ret = MTD_WRITE(nvram_mtd, offset, i, &len, nvram_commit_buf); if (ret || len != i) { printk("nvram_commit: write error\n"); ret = -EIO; goto done; } - /* Now mark the NVRAM in flash as "valid" by setting the correct - * MAGIC # - */ + /* Now mark the NVRAM in flash as "valid" by setting the correct MAGIC # */ header->magic = NVRAM_MAGIC; - ret = nvram_mtd->write(nvram_mtd, offset + magic_offset, sizeof(header->magic), + ret = MTD_WRITE(nvram_mtd, offset + magic_offset, sizeof(header->magic), &magic_len, (char *)&header->magic); if (ret || magic_len != sizeof(header->magic)) { printk("nvram_commit: write MAGIC error\n"); @@ -472,14 +619,15 @@ nvram_commit(void) goto done; } + /* + * Reading a few bytes back here will put the device + * back to the correct mode on certain flashes + */ offset = nvram_mtd->size - erasesize; - ret = nvram_mtd->read(nvram_mtd, offset, 4, &len, nvram_commit_buf); + ret = MTD_READ(nvram_mtd, offset, 4, &len, nvram_commit_buf); done: up(&nvram_sem); -#if 0 - kfree(buf); -#endif return ret; } @@ -499,12 +647,13 @@ nvram_getall(char *buf, int count) return ret; } -EXPORT_SYMBOL(nvram_init); +#ifndef MODULE EXPORT_SYMBOL(nvram_get); EXPORT_SYMBOL(nvram_getall); EXPORT_SYMBOL(nvram_set); EXPORT_SYMBOL(nvram_unset); EXPORT_SYMBOL(nvram_commit); +#endif /* User mode interface below */ @@ -549,7 +698,7 @@ dev_nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos) goto done; } - ret = sizeof(unsigned long); + ret = sizeof(char *); } flush_cache_all(); @@ -589,7 +738,7 @@ done: kfree(name); return ret; -} +} static int dev_nvram_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) @@ -600,28 +749,96 @@ dev_nvram_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsign return nvram_commit(); } +#ifdef MODULE +/* This maps the vmalloced module buffer to user space. */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static int do_vm_mmap(struct vm_area_struct *vma, char *adr, unsigned long siz) +{ + unsigned int start = vma->vm_start; + int pfn; + int ret; + + while (siz > 0) { + pfn = vmalloc_to_pfn(adr); + if ((ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_READONLY)) < 0) { + return ret; + } + start += PAGE_SIZE; + adr += PAGE_SIZE; + siz -= PAGE_SIZE; + } + + return 0; +} + +#else // K24 +/* From bttv-driver.c + * Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva; + + kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + return __pa(kva); +} + +static int do_vm_mmap(struct vm_area_struct *vma, char *adr, unsigned long siz) +{ + unsigned int start = vma->vm_start; + unsigned long page; + + while (siz > 0) { + page = kvirt_to_pa((unsigned long)adr); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_READONLY)) + return -EAGAIN; + start += PAGE_SIZE; + adr += PAGE_SIZE; + siz -= PAGE_SIZE; + } + return 0; +} +#endif +#endif // MODULE + static int dev_nvram_mmap(struct file *file, struct vm_area_struct *vma) { - unsigned long offset = __pa(nvram_buf) >> PAGE_SHIFT; + unsigned long siz = vma->vm_end - vma->vm_start; - if (remap_pfn_range(vma, vma->vm_start, offset, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) + if (siz > NVRAM_VAL_SIZE) siz = NVRAM_VAL_SIZE; +#ifdef MODULE + return (do_vm_mmap(vma, nvram_buf, siz)); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (remap_pfn_range(vma, vma->vm_start, + __pa(nvram_buf) >> PAGE_SHIFT, + siz, vma->vm_page_prot)) return -EAGAIN; - +#else + if (remap_page_range(vma->vm_start, virt_to_phys(nvram_buf), + siz, vma->vm_page_prot)) + return -EAGAIN; +#endif return 0; +#endif } static int dev_nvram_open(struct inode *inode, struct file * file) { + MOD_INC_USE_COUNT; return 0; } static int dev_nvram_release(struct inode *inode, struct file * file) { + MOD_DEC_USE_COUNT; return 0; } @@ -638,52 +855,129 @@ static struct file_operations dev_nvram_fops = { static void dev_nvram_exit(void) { +#ifndef MODULE int order = 0; struct page *page, *end; +#else + char *adr = nvram_buf; + int size = NVRAM_VAL_SIZE; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if (nvram_handle) + devfs_unregister(nvram_handle); + if (nvram_major >= 0) + devfs_unregister_chrdev(nvram_major, NVR_DEVNAME); +#else // K26 if (nvram_class) { - class_device_destroy(nvram_class, MKDEV(nvram_major, 0)); + class_device_destroy(nvram_class, MKDEV(nvram_major, NVR_DEVNUM)); class_destroy(nvram_class); } if (nvram_major >= 0) - unregister_chrdev(nvram_major, "nvram"); - + unregister_chrdev(nvram_major, NVR_DEVNAME); +#endif if (nvram_mtd) put_mtd_device(nvram_mtd); - while ((PAGE_SIZE << order) < NVRAM_SPACE) +#ifndef MODULE + while ((PAGE_SIZE << order) < NVRAM_VAL_SIZE) order++; end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(nvram_buf); page <= end; page++) - ClearPageReserved(page); - + mem_map_unreserve(page); +#else + while (size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } +#endif _nvram_exit(); + vfree(nvram_commit_buf); } -static int +static int __init dev_nvram_init(void) { - int order = 0, ret = 0; - struct page *page, *end; + int ret = 0; unsigned int i; osl_t *osh; +#ifndef MODULE + int order = 0; + struct page *page, *end; +#else + char *adr = nvram_buf; + int size = NVRAM_VAL_SIZE; +#endif + //printk("---------------------------------------------------------\n"); + printk("----nvram loading -----" __DATE__ " " __TIME__ " --------\n"); +#ifndef MODULE /* Allocate and reserve memory to mmap() */ - while ((PAGE_SIZE << order) < NVRAM_SPACE) + while ((PAGE_SIZE << order) < NVRAM_VAL_SIZE) order++; end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); - for (page = virt_to_page(nvram_buf); page <= end; page++) { - SetPageReserved(page); + for (page = virt_to_page(nvram_buf); page <= end; page++) + mem_map_reserve(page); +#else + while (size > 0) { + mem_map_reserve(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#ifdef CONFIG_MTD + /* Find associated MTD device */ + for (i = 0; i < MAX_MTD_DEVICES; i++) { + nvram_mtd = get_mtd_device(NULL, i); + if (nvram_mtd) { + if (!strcmp(nvram_mtd->name, "nvram") && + nvram_mtd->size >= NVRAM_32K) + break; + put_mtd_device(nvram_mtd); + } } + if (i >= MAX_MTD_DEVICES) + nvram_mtd = NULL; +#endif + /* Initialize hash table lock */ + spin_lock_init(&nvram_lock); + + /* Initialize commit semaphore */ + init_MUTEX(&nvram_sem); + + /* Register char device */ + if ((nvram_major = devfs_register_chrdev(0, NVR_DEVNAME, &dev_nvram_fops)) < 0) { + ret = nvram_major; + goto err; + } + + if (sb_osh(sbh) == NULL) { + osh = osl_attach(NULL, SB_BUS, FALSE); + if (osh == NULL) { + printk("Error allocating osh\n"); + goto err; + } + sb_setosh(sbh, osh); + } + + /* Create /dev/nvram handle */ + nvram_handle = devfs_register(NULL, NVR_DEVNAME, DEVFS_FL_NONE, nvram_major, NVR_DEVNUM, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, &dev_nvram_fops, NULL); + +#else // KERNEL 2.6 #if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE) /* Find associated MTD device */ for (i = 0; i < MAX_MTD_DEVICES; i++) { nvram_mtd = get_mtd_device(NULL, i); if (!IS_ERR(nvram_mtd)) { if (!strcmp(nvram_mtd->name, "nvram") && - nvram_mtd->size >= NVRAM_SPACE) { + nvram_mtd->size >= NVRAM_32K) { break; } put_mtd_device(nvram_mtd); @@ -700,7 +994,7 @@ dev_nvram_init(void) init_MUTEX(&nvram_sem); /* Register char device */ - if ((nvram_major = register_chrdev(0, "nvram", &dev_nvram_fops)) < 0) { + if ((nvram_major = register_chrdev(0, NVR_DEVNAME, &dev_nvram_fops)) < 0) { ret = nvram_major; goto err; } @@ -709,38 +1003,40 @@ dev_nvram_init(void) osh = osl_attach(NULL, SI_BUS, FALSE); if (osh == NULL) { printk("Error allocating osh\n"); - unregister_chrdev(nvram_major, "nvram"); + unregister_chrdev(nvram_major, NVR_DEVNAME); goto err; } si_setosh(sih, osh); } - /* Initialize hash table */ - _nvram_init(sih); - /* Create /dev/nvram handle */ - nvram_class = class_create(THIS_MODULE, "nvram"); + nvram_class = class_create(THIS_MODULE, NVR_DEVNAME); if (IS_ERR(nvram_class)) { printk("Error creating nvram class\n"); goto err; } /* Add the device nvram0 */ - class_device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram"); + class_device_create(nvram_class, NULL, MKDEV(nvram_major, NVR_DEVNUM), NULL, NVR_DEVNAME); +#endif /* reserve commit read buffer */ /* Backup sector blocks to be erased */ - if (!(nvram_commit_buf = kmalloc(ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize), GFP_KERNEL))) { + erasesize = ROUNDUP(NVRAM_VAL_SIZE, nvram_mtd->erasesize); + if (!(nvram_commit_buf = vmalloc(erasesize))) { printk("dev_nvram_init: nvram_commit_buf out of memory\n"); goto err; } + /* Initialize the in-memory database */ + _nvram_init(sbh); + /* Set the SDRAM NCDL value into NVRAM if not already done */ if (getintvar(NULL, "sdram_ncdl") == 0) { unsigned int ncdl; - char buf[] = "0x00000000"; + char buf[16]; - if ((ncdl = si_memc_get_ncdl(sih))) { + if ((ncdl = sb_memc_get_ncdl(sbh))) { sprintf(buf, "0x%08x", ncdl); nvram_set("sdram_ncdl", buf); nvram_commit(); @@ -748,7 +1044,6 @@ dev_nvram_init(void) } return 0; - err: dev_nvram_exit(); return ret; @@ -756,3 +1051,9 @@ err: module_init(dev_nvram_init); module_exit(dev_nvram_exit); + +/* For the emacs code formatting +Local Variables: + c-basic-offset: 8 +End: +*/ diff --git a/release/src-rt/shared/nvram/nvram.c b/release/src-rt/shared/nvram/nvram.c index 45f47d55f5..1273b92d2d 100644 --- a/release/src-rt/shared/nvram/nvram.c +++ b/release/src-rt/shared/nvram/nvram.c @@ -1,7 +1,7 @@ /* * NVRAM variable manipulation (common) * - * Copyright (C) 2009, Broadcom Corporation + * Copyright 2004, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY @@ -9,44 +9,89 @@ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * - * $Id: nvram.c,v 1.57 2008/03/28 19:25:55 Exp $ + * $Id$ */ #include #include #include #include -#include +#include #include #include #include -extern struct nvram_tuple *_nvram_realloc(struct nvram_tuple *t, const char *name, - const char *value); -extern void _nvram_free(struct nvram_tuple *t); -extern int _nvram_read(void *buf); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#define get_seconds() CURRENT_TIME +#define jiffy_time (jiffies) +#else +#define jiffy_time (jiffies - INITIAL_JIFFIES) +#endif + + +extern int _nvram_init_read(void); +extern int nvram_space; /* Size of the NVRAM. Generally 32kb */ +extern char nvram_buf[]; /* The buffer to hold values. */ +extern char *nvram_commit_buf; /* Buffer for the flash eraseblock(s). */ +extern int oflow_area_present; char *_nvram_get(const char *name); int _nvram_set(const char *name, const char *value); int _nvram_unset(const char *name); int _nvram_getall(char *buf, int count); int _nvram_commit(struct nvram_header *header); -int _nvram_init(void *sih); +int _nvram_init(void *sb); void _nvram_exit(void); -uint8 nvram_calc_crc(struct nvram_header *nvh); +uint8 nvram_calc_crc(struct nvram_header * nvh); +int walk_chain(int pr); +struct nvram_dbitem *_nvram_realloc(struct nvram_dbitem *t, const char *name, + const char *value); +void _nvram_free(struct nvram_dbitem *t); +void _nvram_valbuf_compactify(void); + +#define NUM_HLH 257 +static struct nvram_dbitem *BCMINITDATA(nvram_hash)[NUM_HLH]; +static struct nvram_dbitem *nvram_dead; +static int it_siz, it_cnt; +static unsigned nvram_offset = 0; +char sbuf[128]; + +/* Prio == write priority (actually: category) + * * Entries that must be in the main (original) NVRAM area, visible + * to PMON & CFE. + * * Entries that can go anywhere. PMON & CFE don't need to see these. + * + * Since there is no spec for what PMON & CFE need to see, and the list + * may change for different firmware versions: + * Assume that when "restore_defaults" != "0" then CFE cleared + * the NVRAM and set its defaults. + * Put a special flag item that is physically after the prio 1 items. + * When refreshing frm the flash, everything before this is defined as prio 1. + * Everything after it is prio 2. + * If it is not found, then everything is prio 1. + * When writing out (commit), write the prio 1 items, then the flag item, + * then everything else. + */ + +static uint16 prio; +#define PRIO_MARK_ITEM "}Marker42" +#define PRIO_MAIN 1 +#define PRIO_ANYWHERE 2 +#define PRIO_OFLOW 3 -static struct nvram_tuple *BCMINITDATA(nvram_hash)[257]; -static struct nvram_tuple *nvram_dead; +int prefer_ov = 0; /* Prefer "any" go to oflow area? */ +int oflow_ok = 1; /* It is okay to go into oflow area? */ +int alt = 0; // temp /* Free all tuples. Should be locked. */ -static void +static void BCMINITFN(nvram_free)(void) { uint i; - struct nvram_tuple *t, *next; + struct nvram_dbitem *t, *next; /* Free hash table */ - for (i = 0; i < ARRAYSIZE(nvram_hash); i++) { + for (i = 0; i < NUM_HLH; i++) { for (t = nvram_hash[i]; t; t = next) { next = t->next; _nvram_free(t); @@ -65,34 +110,37 @@ BCMINITFN(nvram_free)(void) _nvram_free(NULL); } -/* String hash */ +/* String hash. FNV-1a algorithm */ static INLINE uint hash(const char *s) { - uint hash = 0; - - while (*s) - hash = 31 *hash + *s++; + unsigned hval = 0x811c9dc5; - return hash; + while (*s) { + hval ^= *s++; + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); + } + return hval; } /* (Re)initialize the hash table. Should be locked. */ -static int +static int BCMINITFN(nvram_rehash)(struct nvram_header *header) { - char buf[] = "0xXXXXXXXX", *name, *value, *end, *eq; + char buf[20], *name, *value, *end, *eq; + int n = 0; //temp /* (Re)initialize hash table */ nvram_free(); /* Parse and set "name=value\0 ... \0\0" */ name = (char *) &header[1]; - end = (char *) header + NVRAM_SPACE - 2; + end = (char *) header + nvram_space - 2; end[0] = end[1] = '\0'; for (; *name; name = value + strlen(value) + 1) { if (!(eq = strchr(name, '='))) break; + ++n; *eq = '\0'; value = eq + 1; _nvram_set(name, value); @@ -116,61 +164,130 @@ BCMINITFN(nvram_rehash)(struct nvram_header *header) sprintf(buf, "0x%08X", header->config_ncdl); _nvram_set("sdram_ncdl", buf); } - + printk("Item count: %d valsiz: %u\n", n, nvram_offset); return 0; } /* Get the value of an NVRAM variable. Should be locked. */ -char * +char * _nvram_get(const char *name) { uint i; - struct nvram_tuple *t; - char *value; + struct nvram_dbitem *t, **prev; if (!name) return NULL; - /* Hash the name */ - i = hash(name) % ARRAYSIZE(nvram_hash); - - /* Find the associated tuple in the hash table */ - for (t = nvram_hash[i]; t && strcmp(t->name, name); t = t->next); + if (name[0] == '=') { /* Special dynamic size info */ + if (strcmp(name, "=nvram_used") == 0) { + sprintf(&nvram_buf[nvram_space - 128], "%d", + walk_chain(0)); + return (&nvram_buf[nvram_space - 128]); + } + if (strcmp(name, "=nvram_space") == 0) { + i = nvram_space -1; /* Trailing null but no pad. */ + if (oflow_area_present) + i += NVRAM_32K -1; + sprintf(&nvram_buf[nvram_space - 128], "%d", i); + return (&nvram_buf[nvram_space - 128]); + } + } - value = t ? t->value : NULL; + /* Hash the name */ + i = hash(name) % NUM_HLH; - return value; + /* Find the item in the hash table */ + for (prev = &nvram_hash[i], t = *prev; + t && (strcmp(t->name, name)); + prev = &t->next, t = *prev) {} + if (t) { + *prev = t->next; /* Move it to the top of the chain - MRU. */ + t->next = nvram_hash[i]; + nvram_hash[i] = t; + return(t->value); + } + return NULL; } /* Set the value of an NVRAM variable. Should be locked. */ -int +int BCMINITFN(_nvram_set)(const char *name, const char *value) { uint i; - struct nvram_tuple *t, *u, **prev; + struct nvram_dbitem *t, *u, **prev; + + /* Special control items. No name. Value is control command. + * prio_main, _any , oflow Says to flag subsequent sets accordingly. + * "=name" says to set prio on that name. */ + if (*name == '\0') { + printk("Special: '%s'\n", value); + if (strcmp(value, "prio_main") == 0) + prio = PRIO_MAIN; + else if (strcmp(value, "prio_any") == 0) + prio = PRIO_ANYWHERE; + else if (strcmp(value, "prio_oflow") == 0) + prio = PRIO_OFLOW; + else if (*value == '=') { /* Set prio on this. */ + name = value +1; + i = hash(name) % NUM_HLH; + for (prev = &nvram_hash[i], t = *prev; + t && (strcmp(t->name, name)); + prev = &t->next, t = *prev) {} + if (t) + t->prio = prio; + } + else if (strcmp(value, "prefer_oflow-n") == 0) + prefer_ov = 0; + else if (strcmp(value, "prefer_oflow-y") == 0) + prefer_ov = 1; + else if (strcmp(value, "prefer_oflow-n") == 0) alt = 0; //temp + else if (strcmp(value, "prefer_oflow-a") == 0) alt = 1; //temp + else if (strcmp(value, "oflow_ok-n") == 0) + oflow_ok = 0; + else if (strcmp(value, "oflow_ok-y") == 0) + oflow_ok = 1; + else if (strcmp(value, "reset_stat") == 0) { + _nvram_unset("z-commit"); + for (i = 0; ++i < 50; ) { + sprintf(sbuf, "z-commit_%02u", i); + _nvram_unset(sbuf); + } + } + else + printk("nvram: Unknown special value '%s'\n", value); + return 0; + } /* Hash the name */ - i = hash(name) % ARRAYSIZE(nvram_hash); + i = hash(name) % NUM_HLH; - /* Find the associated tuple in the hash table */ - for (prev = &nvram_hash[i], t = *prev; t && strcmp(t->name, name); - prev = &t->next, t = *prev); + /* Find the item in the hash table */ + for (prev = &nvram_hash[i], t = *prev; + t && (strcmp(t->name, name)); + prev = &t->next, t = *prev) {} /* (Re)allocate tuple */ if (!(u = _nvram_realloc(t, name, value))) return -12; /* -ENOMEM */ /* Value reallocated */ - if (t && t == u) + if (t && t == u) { + *prev = u->next; /* Move it to the top of the chain. */ + u->next = nvram_hash[i]; + nvram_hash[i] = u; return 0; + } +#if 0 /* Move old tuple to the dead table */ + // Can never get here!! It would mean that the node existed but + // nvram_realloc returned a different one. But it doesn't. if (t) { *prev = t->next; t->next = nvram_dead; nvram_dead = t; } - +#endif /* Add new tuple to the hash table */ u->next = nvram_hash[i]; nvram_hash[i] = u; @@ -179,21 +296,22 @@ BCMINITFN(_nvram_set)(const char *name, const char *value) } /* Unset the value of an NVRAM variable. Should be locked. */ -int +int BCMINITFN(_nvram_unset)(const char *name) { uint i; - struct nvram_tuple *t, **prev; + struct nvram_dbitem *t, **prev; if (!name) return 0; /* Hash the name */ - i = hash(name) % ARRAYSIZE(nvram_hash); + i = hash(name) % NUM_HLH; - /* Find the associated tuple in the hash table */ - for (prev = &nvram_hash[i], t = *prev; t && strcmp(t->name, name); - prev = &t->next, t = *prev); + /* Find the item in the hash table */ + for (prev = &nvram_hash[i], t = *prev; + t && (strcmp(t->name, name)); + prev = &t->next, t = *prev) {} /* Move it to the dead table */ if (t) { @@ -201,22 +319,21 @@ BCMINITFN(_nvram_unset)(const char *name) t->next = nvram_dead; nvram_dead = t; } - return 0; } /* Get all NVRAM variables. Should be locked. */ -int +int _nvram_getall(char *buf, int count) { uint i; - struct nvram_tuple *t; + struct nvram_dbitem *t; int len = 0; bzero(buf, count); /* Write name=value\0 ... \0\0 */ - for (i = 0; i < ARRAYSIZE(nvram_hash); i++) { + for (i = 0; i < NUM_HLH; i++) { for (t = nvram_hash[i]; t; t = t->next) { if ((count - len) > (strlen(t->name) + 1 + strlen(t->value) + 1)) len += sprintf(buf + len, "%s=%s", t->name, t->value) + 1; @@ -224,7 +341,7 @@ _nvram_getall(char *buf, int count) break; } } - + _nvram_valbuf_compactify(); //temp return 0; } @@ -233,9 +350,12 @@ int BCMINITFN(_nvram_commit)(struct nvram_header *header) { char *init, *config, *refresh, *ncdl; - char *ptr, *end; - int i; - struct nvram_tuple *t; + char *ptr; + unsigned int i; + int rem1, rem2; + int siz; + int cnt = 0, n = jiffies; // temp + struct nvram_dbitem *t; /* Regenerate header */ header->magic = NVRAM_MAGIC; @@ -254,59 +374,78 @@ BCMINITFN(_nvram_commit)(struct nvram_header *header) header->config_refresh |= (bcm_strtoul(refresh, NULL, 0) & 0xffff) << 16; header->config_ncdl = bcm_strtoul(ncdl, NULL, 0); } - +#if 1 + /* Keep info on the commits, for development debugging. */ + i = n; //temp + i = 0; + + if ((ptr = _nvram_get("z-commit")) != NULL) + i = simple_strtol(ptr, NULL, 10); + ++i; + sprintf(sbuf, "%02u it_cnt, it_siz, uptime (msec), time(sec)", i); + _nvram_set("z-commit", sbuf); + if (i < 50) { + walk_chain(0); + sprintf(sbuf, "z-commit_%02u", i); + sprintf(sbuf +20, "%4d,%6d,%8u, %lu", it_cnt, it_siz, jiffies_to_msecs(jiffy_time), get_seconds()); + _nvram_set(sbuf, sbuf +20); + } +#endif /* Clear data area */ - ptr = (char *) header + sizeof(struct nvram_header); - bzero(ptr, NVRAM_SPACE - sizeof(struct nvram_header)); - - /* Leave space for a double NUL at the end */ - end = (char *) header + NVRAM_SPACE - 2; + /* Leave space for a closing NUL & roundup at the end */ + rem1 = nvram_space -1 -3 - sizeof(struct nvram_header); + rem2 = 0; + ptr = (char *)header + sizeof(struct nvram_header); + memset(ptr, 0xff, nvram_space - sizeof(struct nvram_header)); /* Write out all tuples */ - for (i = 0; i < ARRAYSIZE(nvram_hash); i++) { + for (i = 0; i < NUM_HLH; i++) { for (t = nvram_hash[i]; t; t = t->next) { - if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end) + ++cnt; + siz = strlen(t->name) + strlen(t->value) + 2; + if (siz > rem1) { + printk("NVRAM overflow at %s=%-15.15s\n", t->name, t->value); break; + } ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1; + rem1 -= siz; } } - /* End with a double NUL */ - ptr += 2; - - /* Set new length */ - header->len = ROUNDUP(ptr - (char *) header, 4); - - /* Set new CRC8 */ + *ptr++ = 0; /* Ends with an extra NUL */ + header->len = ROUNDUP(ptr - (char *)header, 4); header->crc_ver_init |= nvram_calc_crc(header); - /* Reinitialize hash table */ - return nvram_rehash(header); + /* Reinitialize hash table. Why?? Just to check. And purge. */ + nvram_rehash(header); + return 0; } /* Initialize hash table. Should be locked. */ -int -BCMINITFN(_nvram_init)(void *sih) +int +BCMINITFN(_nvram_init)(void *sb) { - struct nvram_header *header; int ret; + struct nvram_header *header; - - if (!(header = (struct nvram_header *) MALLOC(si_osh(sih), NVRAM_SPACE))) { - printf("nvram_init: out of memory\n"); - return -12; /* -ENOMEM */ - } - - if ((ret = _nvram_read(header)) == 0 && - header->magic == NVRAM_MAGIC) + printk("jiffies: %lu msec: %u\n", jiffy_time, jiffies_to_msecs(jiffy_time)); //temp + ret = _nvram_init_read(); + if (ret >= 0) { + header = (struct nvram_header *)(nvram_commit_buf + ret); nvram_rehash(header); - - MFREE(si_osh(sih), header, NVRAM_SPACE); - return ret; +#if 0 + if (j >= 32 KB) { + oflow_area_present = 1; + ov_hdr = (struct nvram_header *)(nvram_commit_buf); + nvram_rehash_ov(ov_hdr, header); + } +#endif + } + return(ret); } /* Free hash table. Should be locked. */ -void +void BCMINITFN(_nvram_exit)(void) { nvram_free(); @@ -314,7 +453,7 @@ BCMINITFN(_nvram_exit)(void) /* returns the CRC8 of the nvram */ uint8 -BCMINITFN(nvram_calc_crc)(struct nvram_header *nvh) +BCMINITFN(nvram_calc_crc)(struct nvram_header * nvh) { struct nvram_header tmp; uint8 crc; @@ -333,3 +472,116 @@ BCMINITFN(nvram_calc_crc)(struct nvram_header *nvh) return crc; } + +/* Purge un-used value items from the value buffer. + * Should be locked. +*/ +void _nvram_valbuf_compactify(void) +{ + char *wk_buf; + char *nxt; + int i; + int siz; + struct nvram_dbitem *t, *next; + int sz = nvram_offset; //temp + + wk_buf = nvram_commit_buf; + memcpy(wk_buf, nvram_buf, nvram_offset); + /* Walk all tuples & copy & update value ptrs */ + nxt = nvram_buf; + for (i = 0; i < NUM_HLH; i++) { + for (t = nvram_hash[i]; t; t = t->next) { + if (t->value) { + siz = strlen(t->value - nvram_buf + wk_buf) +1; + if (siz + nxt - nvram_buf > NVRAM_VAL_SIZE -4) { + printk("Oh crap! Over-ran valbuf during compatify. Can't happen!\n"); + printk("We're gonna die!\n"); + break; + } + memcpy(nxt, t->value - nvram_buf + wk_buf, siz); + t->value = nxt; + nxt += ROUNDUP_P2(siz, 4); + } + } + } + + /* Free dead table */ + for (t = nvram_dead; t; t = next) { + next = t->next; + kfree(t); + } + nvram_dead = NULL; + nvram_offset = nxt - nvram_buf; + printk("compactify: was: %d now: %d\n", sz, nvram_offset); //temp +} + +/* In: + * t = existing node to update its value. + * If t is NULL, alloc a new node, and set the name & prio. + * In either case, + * If value not NULL or new value is != existing value, then + * copy the value param to the nvram_buf. + * Returns the node. + */ +struct nvram_dbitem *_nvram_realloc(struct nvram_dbitem *t, const char *name, const char *value) +{ + int siz; + + siz = strlen(value) +1; + if ((nvram_offset + siz) >= NVRAM_VAL_SIZE -256) { + _nvram_valbuf_compactify(); + if ((nvram_offset + siz) >= NVRAM_VAL_SIZE -256) + return NULL; + } + + if (!t) { + if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC))) + return NULL; + + strcpy(t->name, name); + t->prio = prio; + t->value = NULL; + } + + /* Copy value. Always on a word boundary.*/ + if (!t->value || strcmp(t->value, value)) { + t->value = &nvram_buf[nvram_offset]; + memcpy(t->value, value, siz); + nvram_offset += ROUNDUP_P2(siz, 4); + } + return t; +} + +void +_nvram_free(struct nvram_dbitem *t) +{ + if (!t) + nvram_offset = 0; + else + kfree(t); +} + + +/* Return the size the variables will take when written to NVRAM. */ +int walk_chain(int z) +{ + int i; + struct nvram_dbitem *t; + + it_siz = it_cnt = 0; + for (i = 0; i < NUM_HLH; i++) { + for (t = nvram_hash[i]; t; t = t->next) { + it_siz += strlen(t->name) + strlen(t->value) + 2; + ++it_cnt; + if (z) + printk("%3d: %s=%-15.15s\n", i, t->name, t->value); + } + } + return (it_siz); +} + +/* For the emacs code formatting +Local Variables: + c-basic-offset: 8 +End: +*/ -- 2.11.4.GIT