| /* |
| * Creation Date: <2003/12/01 00:26:13 samuel> |
| * Time-stamp: <2004/01/07 19:59:53 samuel> |
| * |
| * <nvram.c> |
| * |
| * medium-level NVRAM handling |
| * |
| * Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se) |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * version 2 |
| * |
| */ |
| |
| #include "config.h" |
| #include "libopenbios/bindings.h" |
| #include "arch/common/nvram.h" |
| #include "packages/nvram.h" |
| |
| //#define CONFIG_DEBUG_NVRAM 1 |
| |
| #ifdef CONFIG_DEBUG_NVRAM |
| #define DPRINTF(fmt, args...) \ |
| do { printk("NVRAM: " fmt , ##args); } while (0) |
| #else |
| #define DPRINTF(fmt, args...) do {} while(0) |
| #endif |
| |
| #define DEF_SYSTEM_SIZE 0xc10 |
| |
| #define NV_SIG_SYSTEM 0x70 |
| #define NV_SIG_FREE 0x7f |
| |
| |
| typedef struct { |
| unsigned char signature; |
| unsigned char checksum; |
| unsigned char len_hi; |
| unsigned char len_lo; |
| char name[12]; |
| char data[0]; |
| } nvpart_t; |
| |
| static struct { |
| char *data; |
| int size; |
| |
| nvpart_t *config; |
| int config_size; |
| } nvram; |
| |
| |
| /************************************************************************/ |
| /* generic */ |
| /************************************************************************/ |
| |
| static unsigned int |
| nvpart_checksum( nvpart_t* hdr ) |
| { |
| unsigned char *p = (unsigned char*)hdr; |
| int i, val = p[0]; |
| |
| for( i=2; i<16; i++ ) { |
| val += p[i]; |
| if( val > 255 ) |
| val = (val - 256 + 1) & 0xff; |
| } |
| return val; |
| } |
| |
| static inline int |
| nvpart_size( nvpart_t *p ) |
| { |
| return (p->len_lo | ((int)p->len_hi<<8)) * 16; |
| } |
| |
| static int |
| next_nvpart( nvpart_t **p ) |
| { |
| nvpart_t *end = (nvpart_t*)(nvram.data + nvram.size); |
| int len; |
| |
| if( !*p ) { |
| *p = (nvpart_t*)nvram.data; |
| return 1; |
| } |
| |
| if( !(len=nvpart_size(*p)) ) { |
| printk("invalid nvram partition length\n"); |
| return -1; |
| } |
| *p = (nvpart_t*)((char*)*p + len); |
| if( *p < end ) |
| return 1; |
| if( *p == end ) |
| return 0; |
| return -1; |
| } |
| |
| static void |
| create_free_part( char *ptr, int size ) |
| { |
| nvpart_t *nvp = (nvpart_t*)ptr; |
| memset( nvp, 0, size ); |
| |
| strncpy( nvp->name, "777777777777", sizeof(nvp->name) ); |
| nvp->signature = NV_SIG_FREE; |
| nvp->len_hi = (size /16) >> 8; |
| nvp->len_lo = size /16; |
| nvp->checksum = nvpart_checksum(nvp); |
| } |
| |
| static int |
| create_nv_part( int signature, const char *name, int size ) |
| { |
| nvpart_t *p = NULL; |
| int fs; |
| |
| while( next_nvpart(&p) > 0 ) { |
| if( p->signature != NV_SIG_FREE ) |
| continue; |
| |
| fs = nvpart_size( p ); |
| if( fs < size ) |
| size = fs; |
| p->signature = signature; |
| memset( p->name, 0, sizeof(p->name) ); |
| strncpy( p->name, name, sizeof(p->name) ); |
| p->len_hi = (size>>8)/16; |
| p->len_lo = size/16; |
| p->checksum = nvpart_checksum(p); |
| if( fs > size ) { |
| char *fp = (char*)p + size; |
| create_free_part( fp, fs-size ); |
| } |
| return size; |
| } |
| printk("create-failed\n"); |
| return -1; |
| } |
| |
| static void |
| zap_nvram( void ) |
| { |
| create_free_part( nvram.data, nvram.size ); |
| create_nv_part( NV_SIG_SYSTEM, "common", DEF_SYSTEM_SIZE ); |
| } |
| |
| #if 0 |
| static void |
| show_partitions( void ) |
| { |
| nvpart_t *p = NULL; |
| char buf[13]; |
| |
| while( next_nvpart(&p) > 0 ) { |
| memcpy( buf, p->name, sizeof(p->name) ); |
| buf[12] = 0; |
| printk("[%02x] %-13s: %03x\n", |
| p->signature, buf, nvpart_size(p)); |
| } |
| } |
| #endif |
| |
| void |
| update_nvram( void ) |
| { |
| PUSH( pointer2cell(nvram.config->data) ); |
| PUSH( nvram.config_size ); |
| fword("nvram-store-configs"); |
| arch_nvram_put( nvram.data ); |
| } |
| |
| void |
| nvconf_init( void ) |
| { |
| int once=0; |
| |
| /* initialize nvram structure completely */ |
| nvram.config = NULL; |
| nvram.config_size = 0; |
| |
| nvram.size = arch_nvram_size(); |
| nvram.data = malloc( nvram.size ); |
| arch_nvram_get( nvram.data ); |
| |
| bind_func( "update-nvram", update_nvram ); |
| |
| for( ;; ) { |
| nvpart_t *p = NULL; |
| int err; |
| |
| while( (err=next_nvpart(&p)) > 0 ) { |
| if( nvpart_checksum(p) != p->checksum ) { |
| err = -1; |
| break; |
| } |
| if( p->signature == NV_SIG_SYSTEM ) { |
| nvram.config = p; |
| nvram.config_size = nvpart_size(p) - 0x10; |
| |
| if( !once++ ) { |
| PUSH( pointer2cell(p->data) ); |
| PUSH( nvram.config_size ); |
| fword("nvram-load-configs"); |
| } |
| } |
| } |
| if( err || !nvram.config ) { |
| printk("nvram error detected, zapping pram\n"); |
| zap_nvram(); |
| if( !once++ ) |
| fword("set-defaults"); |
| continue; |
| } |
| break; |
| } |
| } |
| |
| |
| /************************************************************************/ |
| /* nvram */ |
| /************************************************************************/ |
| |
| typedef struct { |
| unsigned int mark_hi; |
| unsigned int mark_lo; |
| } nvram_ibuf_t; |
| |
| DECLARE_UNNAMED_NODE( nvram, INSTALL_OPEN, sizeof(nvram_ibuf_t )); |
| |
| /* ( pos_lo pos_hi -- status ) */ |
| static void |
| nvram_seek( nvram_ibuf_t *nd ) |
| { |
| int pos_hi = POP(); |
| int pos_lo = POP(); |
| |
| DPRINTF("seek %08x %08x\n", pos_hi, pos_lo ); |
| nd->mark_lo = pos_lo; |
| nd->mark_hi = pos_hi; |
| |
| if( nd->mark_lo >= nvram.size ) { |
| PUSH(-1); |
| return; |
| } |
| |
| /* 0=success, -1=failure (1=legacy success) */ |
| PUSH(0); |
| } |
| |
| /* ( addr len -- actual ) */ |
| static void |
| nvram_read( nvram_ibuf_t *nd ) |
| { |
| int len = POP(); |
| char *p = (char*)cell2pointer(POP()); |
| int n=0; |
| |
| while( nd->mark_lo < nvram.size && n < len ) { |
| *p++ = nvram.data[nd->mark_lo++]; |
| n++; |
| } |
| PUSH(n); |
| DPRINTF("read %p %x -- %x\n", p, len, n); |
| } |
| |
| /* ( addr len -- actual ) */ |
| static void |
| nvram_write( nvram_ibuf_t *nd ) |
| { |
| int len = POP(); |
| char *p = (char*)cell2pointer(POP()); |
| int n=0; |
| |
| while( nd->mark_lo < nvram.size && n < len ) { |
| nvram.data[nd->mark_lo++] = *p++; |
| n++; |
| } |
| PUSH(n); |
| DPRINTF("write %p %x -- %x\n", p, len, n ); |
| } |
| |
| /* ( -- size ) */ |
| static void |
| nvram_size( __attribute__((unused)) nvram_ibuf_t *nd ) |
| { |
| DPRINTF("nvram_size %d\n", nvram.size); |
| PUSH( nvram.size ); |
| } |
| |
| NODE_METHODS( nvram ) = { |
| { "size", (void*)nvram_size }, |
| { "read", (void*)nvram_read }, |
| { "write", (void*)nvram_write }, |
| { "seek", (void*)nvram_seek }, |
| }; |
| |
| |
| void |
| nvram_init( const char *path ) |
| { |
| nvconf_init(); |
| |
| REGISTER_NAMED_NODE( nvram, path ); |
| } |