2170 lines
53 KiB
C
2170 lines
53 KiB
C
/**************************************************************************
|
|
* *
|
|
* Copyright (C) 1996, Silicon Graphics, Inc. *
|
|
* *
|
|
* These coded instructions, statements, and computer programs contain *
|
|
* unpublished proprietary information of Silicon Graphics, Inc., and *
|
|
* are protected by Federal copyright law. They may not be disclosed *
|
|
* to third parties or copied or duplicated in any form, in whole or *
|
|
* in part, without the prior written consent of Silicon Graphics, Inc. *
|
|
* *
|
|
**************************************************************************/
|
|
|
|
#if defined(IP30)
|
|
|
|
#include <arcs/errno.h>
|
|
#include <arcs/types.h>
|
|
#include <sys/cpu.h>
|
|
#include <sys/sbd.h>
|
|
#include <string.h>
|
|
#include <libsk.h>
|
|
#include <libsc.h>
|
|
#include <ksys/cacheops.h>
|
|
#include <sys/mips_addrspace.h>
|
|
#include <sys/RACER/sflash.h>
|
|
#include <sys/RACER/IP30nvram.h>
|
|
|
|
typedef struct pds_space_usage_s {
|
|
int space;
|
|
int inuse_env_ent;
|
|
int inuse_env_bytes;
|
|
int inuse_log_ent;
|
|
int inuse_log_bytes;
|
|
int inuse_dat_ent;
|
|
int inuse_dat_bytes;
|
|
int invalid_env_ent;
|
|
int invalid_env_bytes;
|
|
int invalid_log_ent;
|
|
int invalid_log_bytes;
|
|
int invalid_dat_ent;
|
|
int invalid_dat_bytes;
|
|
int free;
|
|
} pds_space_usage_t;
|
|
|
|
#define PDS_INUSE \
|
|
(pds_usage.inuse_log_bytes + \
|
|
pds_usage.inuse_env_bytes + \
|
|
pds_usage.inuse_dat_bytes )
|
|
|
|
static pds_space_usage_t pds_usage;
|
|
|
|
void flash_pds_space_usage(pds_space_usage_t *use);
|
|
|
|
static void flash_io_reloc(void);
|
|
typedef flash_err_t (flash_io_f)(int, vu_short *, __uint64_t, __uint64_t, __uint64_t);
|
|
typedef flash_err_t (flash_program_f)(flash_io_f *, int, uint16_t *, vu_short *, vu_short *);
|
|
|
|
#define BSS_FIO_SIZE 2048
|
|
char bss_flash_io[BSS_FIO_SIZE];
|
|
|
|
flash_program_f *flash_program_p;
|
|
flash_io_f *flash_io_p;
|
|
|
|
__psunsigned_t flash_mem_base;
|
|
static vu_short *flash_pds_freep;
|
|
static int pds_log_init;
|
|
static int power_disabled;
|
|
|
|
SETUP_TIME
|
|
|
|
#ifdef FDEBUG
|
|
#define FPR_FLG (FPR_FLG_MSG |FPR_FLG_ERR | FPR_FLG_PERF)
|
|
unsigned int fpr_flg = FPR_FLG | FPR_FLG_PDS; /* normal mode */
|
|
#endif /* FDEBUG */
|
|
|
|
struct reg_values csr7[] = {
|
|
{ 0, "Busy" },
|
|
{ 1, "Ready" },
|
|
};
|
|
|
|
static struct reg_desc csr_desc[] = {
|
|
/* mask shift name format values */
|
|
{ SFLASH_CSR_WSMS, 0, "Ready", NULL, NULL },
|
|
{ SFLASH_CSR_ESS, 0, "EraseSusp",NULL, NULL },
|
|
{ SFLASH_CSR_ES, 0, "EraseErr", NULL, NULL },
|
|
{ SFLASH_CSR_DWS, 0, "WriteErr", NULL, NULL },
|
|
{ SFLASH_CSR_VPPS, 0, "VppLow", NULL, NULL },
|
|
{ SFLASH_CSR_RSV2, 0, "rsvd2", NULL, NULL },
|
|
{ SFLASH_CSR_RSV1, 0, "rsvd1", NULL, NULL },
|
|
{ SFLASH_CSR_RSV0, 0, "rsvd0", NULL, NULL },
|
|
{ 0, 0, NULL, NULL, NULL },
|
|
};
|
|
|
|
static struct reg_desc gsr_desc[] = {
|
|
/* mask shift name format values */
|
|
{ SFLASH_GSR_WSMS, 0, "Ready", NULL, NULL },
|
|
{ SFLASH_GSR_OSS, 0, "OpSusp", NULL, NULL },
|
|
{ SFLASH_GSR_DOS, 0, "OpErr", NULL, NULL },
|
|
{ SFLASH_GSR_DSS, 0, "inSleep", NULL, NULL },
|
|
{ SFLASH_GSR_QS, 0, "Qfull", NULL, NULL },
|
|
{ SFLASH_GSR_PBAS, 0, "PgBAvail", NULL, NULL },
|
|
{ SFLASH_GSR_PBS, 0, "PgBReady", NULL, NULL },
|
|
{ SFLASH_GSR_PBSS, 0, "PgB1Sel", NULL, NULL },
|
|
{ 0, 0, NULL, NULL, NULL },
|
|
};
|
|
|
|
static struct reg_desc bsr_desc[] = {
|
|
/* mask shift name format values */
|
|
{ SFLASH_BSR_WSMS, 0, "Ready", NULL, NULL },
|
|
{ SFLASH_BSR_BLS, 0, "Unlocked", NULL, NULL },
|
|
{ SFLASH_BSR_BOS, 0, "OpErr", NULL, NULL },
|
|
{ SFLASH_BSR_BOAS, 0, "Aborted", NULL, NULL },
|
|
{ SFLASH_BSR_QS, 0, "Qfull", NULL, NULL },
|
|
{ SFLASH_BSR_VPPS, 0, "VppLow", NULL, NULL },
|
|
{ SFLASH_BSR_RSV1, 0, "rsvd1", NULL, NULL },
|
|
{ SFLASH_BSR_RSV0, 0, "rsvd0", NULL, NULL },
|
|
{ 0, 0, NULL, NULL, NULL },
|
|
};
|
|
|
|
#define HEART_MSEC_TICKS(ms) \
|
|
(((heartreg_t)(ms)*1000000)/(heartreg_t)HEART_COUNT_NSECS)
|
|
|
|
/* BEGIN BSS-RELOCATION Block */
|
|
/*
|
|
* CAUTION: Add functions for BSS relocation ONLY after this line
|
|
* The code inside this will be relocated to the bss_flash_io
|
|
* array and will be called by function pointers set to
|
|
* offsets within the bss array.
|
|
* You must make sure the code inside this block DOES NOT
|
|
* (A) make function calls (i.e. only LEAF fcts allowed) and
|
|
* (B) make references any data outside of the scope of the
|
|
* functions (rodata, data, bss-data). This means any global,
|
|
* local or static local data structures.
|
|
*/
|
|
|
|
static flash_err_t
|
|
_flash_io(int cmd, vu_short *fcmd, __uint64_t a1, __uint64_t a2, __uint64_t a3)
|
|
{
|
|
bridge_t *bp = BRIDGE_K1PTR;
|
|
ioc3_mem_t *ioc3 = (ioc3_mem_t *)IOC3_PCI_DEVIO_K1PTR;
|
|
bridgereg_t ctrl_sav;
|
|
flash_err_t rv = 0;
|
|
|
|
/* enable Bridge bit for prom write */
|
|
ctrl_sav = bp->b_wid_control;
|
|
bp->b_wid_control |= BRIDGE_CTRL_FLASH_WR_EN;
|
|
bp->b_wid_control; /* inval addr bug war */
|
|
|
|
switch (cmd) {
|
|
/*
|
|
* cmd - (I) flash command, SFLASH_CMD_ERASE
|
|
* fcmd - (I) command address, begin of block to be erased
|
|
*/
|
|
case SFLASH_CMD_ERASE: {
|
|
volatile heartreg_t ht_end, ht_blink, ht_count;
|
|
vu_short csr;
|
|
|
|
ioc3->gppr_0 = 0; ioc3->gppr_1 = 1; /* AMBER */
|
|
ht_blink = HEART_MSEC_TICKS(500); /* .5 sec */
|
|
ht_blink += HEART_PIU_K1PTR->h_count;
|
|
|
|
*fcmd = cmd;
|
|
*fcmd = SFLASH_CMD_ERASE_CONFIRM;
|
|
|
|
*fcmd = SFLASH_CMD_STATUS_READ;
|
|
ht_end = HEART_MSEC_TICKS(4000); /* 4 sec max wait */
|
|
ht_end += HEART_PIU_K1PTR->h_count;
|
|
do {
|
|
ht_count = HEART_PIU_K1PTR->h_count;
|
|
if (ht_count > ht_blink) {
|
|
ioc3->gppr_0 = 0; ioc3->gppr_1 = 0; /* OFF */
|
|
ht_blink = ht_count;
|
|
}
|
|
csr = *fcmd & 0xff;
|
|
} while (!(csr & SFLASH_STATUS_READY) && (ht_count < ht_end));
|
|
|
|
if (csr != SFLASH_STATUS_READY) {
|
|
/* clear flash status regs, abort current op */
|
|
*fcmd = SFLASH_CMD_STATUS_CLEAR;
|
|
HEART_PIU_K1PTR->h_count; /* flush bus */
|
|
rv = FLASH_ERR_ERASE_TO;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* cmd - (I) flash command, SFLASH_CMD_WRITE
|
|
* fcmd - (I) command address, any flash memory
|
|
* a1 - (I) source address for 16 bit word
|
|
* a2 - (I) destination address for 16 bit word
|
|
* a3 - (I) 16bit word write length (bytes/2)
|
|
* < 0 means to reverse copy for |a3|
|
|
* NOTE: a1, a2 must start on half wd boundary
|
|
*/
|
|
case SFLASH_CMD_WRITE: {
|
|
vu_short *src = (vu_short *)a1;
|
|
vu_short *dst = (vu_short *)a2;
|
|
int len = (int)a3;
|
|
__uint64_t *lsp = (__uint64_t *)a1;
|
|
__uint64_t *ldp = (__uint64_t *)a2;
|
|
int not_dw_aligned = (a1 & 0x7 || a2 & 0x7);
|
|
|
|
volatile heartreg_t ht_end;
|
|
vu_short csr;
|
|
|
|
ioc3->gppr_0 = 0; ioc3->gppr_1 = 0; /* OFF */
|
|
if (len < 0) {
|
|
dst -= len;
|
|
src -= len;
|
|
}
|
|
|
|
while (len) {
|
|
*fcmd = cmd;
|
|
if (len > 0)
|
|
*dst++ = *src++;
|
|
else
|
|
*--dst = *--src;
|
|
|
|
/*
|
|
* spec sez max 8 usecs for write
|
|
* but we will wait 1 msec max for
|
|
* each write to complete
|
|
*/
|
|
*fcmd = SFLASH_CMD_STATUS_READ;
|
|
ht_end = HEART_MSEC_TICKS(1);
|
|
ht_end += HEART_PIU_K1PTR->h_count;
|
|
do {
|
|
if ((int)a3 > SFLASH_SEG_SIZE ||
|
|
(int)a3 < -SFLASH_SEG_SIZE) {
|
|
if (((__psint_t)dst & 0xffff) == 0x8000)
|
|
ioc3->gppr_0 = 0; ioc3->gppr_1 = 0; /* OFF */
|
|
if (((__psint_t)dst & 0xffff) == 0)
|
|
ioc3->gppr_0 = 1; ioc3->gppr_1 = 0; /* GREEN */
|
|
}
|
|
csr = *fcmd & 0xff; /* read flash status */
|
|
} while (!(csr & SFLASH_STATUS_READY) &&
|
|
HEART_PIU_K1PTR->h_count < ht_end);
|
|
|
|
if (csr != SFLASH_STATUS_READY) {
|
|
/* clear flash status regs, abort current op */
|
|
*fcmd = SFLASH_CMD_STATUS_CLEAR;
|
|
HEART_PIU_K1PTR->h_count; /* flush bus */
|
|
rv = FLASH_ERR_WRITE_TO;
|
|
break;
|
|
}
|
|
(len > 0) ? --len : ++len;
|
|
}
|
|
if (rv)
|
|
break;
|
|
/*
|
|
* verify the copy, reload src, dst, len
|
|
*/
|
|
ioc3->gppr_0 = 0; ioc3->gppr_1 = 0; /* OFF */
|
|
*fcmd = SFLASH_CMD_READ; /* return to read mode */
|
|
len = (int)a3;
|
|
if (len < 0)
|
|
len *= -1;
|
|
if ( !not_dw_aligned) {
|
|
while (len > 3) {
|
|
len -= 4;
|
|
if (*lsp != *ldp) {
|
|
rv = (flash_err_t)ldp;
|
|
break;
|
|
}
|
|
lsp++, ldp++;
|
|
}
|
|
}
|
|
if (len && !rv) {
|
|
src = (vu_short *)lsp;
|
|
dst = (vu_short *)ldp;
|
|
|
|
while (len) {
|
|
if (*src != *dst) {
|
|
rv = (flash_err_t)dst;
|
|
break;
|
|
}
|
|
src++, dst++, len--;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* cmd - (I) flash command, SFLASH_CMD_STATUS_READ
|
|
* fcmd - (I) command address, any flash memory
|
|
* a1 - (O) Common Status Register, 8 bits
|
|
* a2 - (O) Global Status Register, 8 bits
|
|
* a3 - (O) Block Status Register, 8 bits
|
|
*/
|
|
case SFLASH_CMD_STATUS_READ: {
|
|
vu_short *csrp = (vu_short *)a1;
|
|
vu_short *gsrp = (vu_short *)a2;
|
|
vu_short *bsrp = (vu_short *)a3;
|
|
|
|
*fcmd = cmd;
|
|
*csrp = 0xff & *fcmd;
|
|
|
|
*fcmd = SFLASH_CMD_ESR_READ;
|
|
*gsrp = 0xff & fcmd[2];
|
|
*fcmd = SFLASH_CMD_ESR_READ;
|
|
*bsrp = 0xff & fcmd[1];
|
|
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* cmd - (I) flash command, SFLASH_CMD_ID_READ
|
|
* fcmd - (I) command address, any flash memory
|
|
* a1 - (O) Manufactureer ID, 8 bits
|
|
* a2 - (O) Device ID, 8 bits
|
|
*/
|
|
case SFLASH_CMD_ID_READ: {
|
|
vu_short *mfgid = (vu_short *)a1;
|
|
vu_short *devid = (vu_short *)a2;
|
|
|
|
*fcmd = cmd;
|
|
/*
|
|
* mfg and dev id's are read off of different
|
|
* offsets from the base of flash memory
|
|
*/
|
|
*mfgid = 0xffff & fcmd[0];
|
|
*devid = 0xffff & fcmd[1];
|
|
break;
|
|
}
|
|
|
|
default:
|
|
rv = FLASH_ERR_UNKOWN_CMD;
|
|
break;
|
|
}
|
|
|
|
*fcmd = SFLASH_CMD_READ; /* return to read mode */
|
|
/* disable Bridge bit for prom write */
|
|
bp->b_wid_control = ctrl_sav;
|
|
bp->b_wid_control; /* inval addr bug war */
|
|
|
|
if (rv) {
|
|
ioc3->gppr_0 = 0; ioc3->gppr_1 = 1; /* AMBER */
|
|
} else {
|
|
ioc3->gppr_0 = 1; ioc3->gppr_1 = 0; /* GREEN */
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
#define POST_FLASH_RESET 0x1
|
|
#define REVERSE_PROGRAM 0x2
|
|
|
|
static flash_err_t
|
|
_flash_program(flash_io_f *fiop, int flag,
|
|
uint16_t *src, vu_short *beg_segp, vu_short *end_segp)
|
|
{
|
|
vu_short *segp;
|
|
__int64_t hlen;
|
|
flash_err_t frv;
|
|
|
|
for (segp = beg_segp; segp <= end_segp;
|
|
segp += SFLASH_SEG_SIZE/sizeof(uint16_t)) {
|
|
|
|
(*fiop)(SFLASH_CMD_ERASE, segp, 0, 0, 0);
|
|
}
|
|
hlen = (__int64_t)(segp - beg_segp);
|
|
if (flag & REVERSE_PROGRAM)
|
|
hlen *= -1;
|
|
|
|
frv = (*fiop)(SFLASH_CMD_WRITE, beg_segp,
|
|
(__uint64_t)src,
|
|
(__uint64_t)beg_segp,
|
|
(__uint64_t)hlen);
|
|
|
|
if (flag & POST_FLASH_RESET) {
|
|
/*
|
|
* the contents of this if block must mirror
|
|
* the reset procedures in cpu_hardreset()
|
|
* we'd like to call cpu_hardreset() but
|
|
* it had just been erased and re-flashed
|
|
*/
|
|
HEART_PIU_K1PTR->h_mode |= HM_SW_RST;
|
|
HEART_PIU_K1PTR->h_mode; /* flush bus */
|
|
}
|
|
return(frv);
|
|
}
|
|
|
|
/*
|
|
* CAUTION: Add functions for BSS relocation before this line
|
|
* and flash_io() must follow immediately after because
|
|
* its used as the END marker when calculating the
|
|
* relocation size.
|
|
*/
|
|
/* END BSS-RELOCATION Block */
|
|
|
|
static flash_err_t
|
|
flash_io(int cmd, vu_short *fcmd, __uint64_t a1, __uint64_t a2, __uint64_t a3)
|
|
{
|
|
int reenable = 0;
|
|
flash_err_t frv;
|
|
|
|
if (!flash_io_p)
|
|
flash_io_reloc();
|
|
|
|
if (!power_disabled) {
|
|
ip30_disable_power(); /* disable power button */
|
|
power_disabled = 1;
|
|
reenable = 1;
|
|
}
|
|
|
|
frv = (*flash_io_p)(cmd, fcmd, a1, a2, a3);
|
|
|
|
if (reenable) {
|
|
ip30_setup_power(); /* re-enable power button */
|
|
power_disabled = 0;
|
|
}
|
|
return(frv);
|
|
}
|
|
|
|
/* CAUTION: Add functions NON-BSS relocation (ie. regular) code after this line */
|
|
|
|
/*
|
|
* The sections headers for rprom, fprom and PDS must be invalidated before we
|
|
* reprogram them. Since fprom and PDS have the headers at beginning of the addr
|
|
* space the copy must be reverse (so the header magic will be written last).
|
|
*/
|
|
flash_err_t
|
|
flash_program(uint16_t *src, int reset, unsigned beg_seg, unsigned end_seg)
|
|
{
|
|
vu_short *begp, *endp;
|
|
int cnt;
|
|
int reenable = 0;
|
|
flash_err_t frv;
|
|
|
|
if (!flash_program_p)
|
|
flash_io_reloc();
|
|
|
|
if (reset)
|
|
reset = POST_FLASH_RESET;
|
|
|
|
begp = SFLASH_SEG_ADDR(beg_seg);
|
|
endp = SFLASH_SEG_ADDR(end_seg);
|
|
|
|
if (!power_disabled) {
|
|
ip30_disable_power(); /* disable power button */
|
|
power_disabled = 1;
|
|
reenable = 1;
|
|
}
|
|
|
|
if (SFLASH_FPROM_SEG >= beg_seg && SFLASH_FPROM_SEG <= end_seg) {
|
|
cnt = flash_get_nv_cnt(SFLASH_FPROM_SEG) + 1;
|
|
flash_set_nv_cnt(SFLASH_FPROM_SEG, &cnt);
|
|
flash_header_invalidate((void *)SFLASH_FPROM_HDR_ADDR);
|
|
|
|
flash_set_nv_rpromflg(flash_get_nv_rpromflg() | RPROM_FLG_FVP);
|
|
reset |= REVERSE_PROGRAM;
|
|
}
|
|
if (SFLASH_RPROM_SEG >= beg_seg && SFLASH_RPROM_SEG <= (int)end_seg) {
|
|
cnt = flash_get_nv_cnt(SFLASH_RPROM_SEG) + 1;
|
|
flash_set_nv_cnt(SFLASH_RPROM_SEG, &cnt);
|
|
flash_header_invalidate((void *)SFLASH_RPROM_HDR_ADDR);
|
|
}
|
|
if (SFLASH_PDS_SEG >= beg_seg && SFLASH_PDS_SEG <= end_seg) {
|
|
cnt = flash_get_nv_cnt(SFLASH_PDS_SEG) + 1;
|
|
flash_set_nv_cnt(SFLASH_PDS_SEG, &cnt);
|
|
flash_header_invalidate((void *)SFLASH_PDS_ADDR);
|
|
reset |= REVERSE_PROGRAM;
|
|
}
|
|
|
|
frv = (*flash_program_p)(flash_io_p, reset, src, begp, endp);
|
|
|
|
if (reenable) {
|
|
ip30_setup_power(); /* re-enable power button */
|
|
power_disabled = 0;
|
|
}
|
|
return(frv);
|
|
}
|
|
|
|
static int
|
|
running_cached(void)
|
|
{
|
|
ulong pc, ppc;
|
|
extern ulong get_pc(void);
|
|
|
|
pc = get_pc();
|
|
|
|
if (IS_KSEG0(pc) || IS_COMPATK0(pc))
|
|
return(1);
|
|
else
|
|
return(0);
|
|
|
|
}
|
|
|
|
static void
|
|
flash_io_reloc(void)
|
|
{
|
|
__psunsigned_t reloc_size;
|
|
__psunsigned_t bss_size;
|
|
__psunsigned_t dst = (__psunsigned_t)bss_flash_io;
|
|
|
|
reloc_size = (__psunsigned_t)flash_io - (__psunsigned_t)_flash_io;
|
|
|
|
/*
|
|
* Align to scache at both ends to avoid sharing the cache line
|
|
* with other data both at the beginning and at the end of the
|
|
* relocation array.
|
|
*/
|
|
dst = (dst + CACHE_SLINE_SIZE - 1) & CACHE_SLINE_MASK;
|
|
bss_size = (__psunsigned_t)&bss_flash_io[BSS_FIO_SIZE] - dst;
|
|
bss_size &= CACHE_SLINE_MASK;
|
|
if (running_cached())
|
|
__dcache_wb_inval((void *)dst, reloc_size);
|
|
/*
|
|
* BUG: XXX we can only run from bss as uncached.
|
|
* we currently cannot run from K0
|
|
*/
|
|
dst = PHYS_TO_K1(KDM_TO_PHYS(dst));
|
|
|
|
if (reloc_size > bss_size) {
|
|
FPR_ERR(("flash_io is too big for bss_flash_io, reloc_size\n",
|
|
reloc_size));
|
|
return;
|
|
}
|
|
bcopy((void *)_flash_io, (void *)dst, reloc_size);
|
|
FPR_HI(("bss_flash_io %#x dst %#x reloc_size %#x(%d) \n",
|
|
bss_flash_io, dst, reloc_size, reloc_size));
|
|
|
|
flash_io_p = (flash_io_f *)dst;
|
|
flash_program_p = (flash_program_f *)(dst +
|
|
((__psunsigned_t)_flash_program - (__psunsigned_t)_flash_io));
|
|
FPR_HI(("flash_io_p %#x flash_program_p %#x, bss_size %#x(%d)\n",
|
|
flash_io_p, flash_program_p, bss_size, bss_size));
|
|
}
|
|
|
|
void
|
|
flash_print_status(int seg)
|
|
{
|
|
vu_short *fcmd, *regp, gsr, bsr, csr;
|
|
flash_err_t frv;
|
|
|
|
if (seg < 0 && seg > SFLASH_MAX_SEGS)
|
|
seg = 0;
|
|
frv = flash_io(SFLASH_CMD_STATUS_READ, SFLASH_SEG_ADDR(seg),
|
|
(__uint64_t)&csr, (__uint64_t)&gsr, (__uint64_t)&bsr);
|
|
|
|
if (frv) {
|
|
flash_print_err(frv);
|
|
} else {
|
|
FPR_MSG((" CSR: %R\n", csr, csr_desc));
|
|
FPR_MSG((" GSR: %R\n", gsr, gsr_desc));
|
|
FPR_MSG((" BSR[seg=%d]: %R\n", seg, bsr, bsr_desc));
|
|
}
|
|
}
|
|
|
|
flash_err_t
|
|
flash_cp(uint16_t *src, vu_short *dst, int hlen)
|
|
{
|
|
return flash_io(SFLASH_CMD_WRITE, dst,
|
|
(__uint64_t)src, (__uint64_t)dst, (__uint64_t)hlen);
|
|
}
|
|
|
|
|
|
void
|
|
flash_id(unsigned short *mfg, unsigned short *dev)
|
|
{
|
|
flash_io(SFLASH_CMD_ID_READ, SFLASH_FCMD_ADDR,
|
|
(__uint64_t)mfg, (__uint64_t)dev, 0);
|
|
}
|
|
|
|
int
|
|
flash_ok(void)
|
|
{
|
|
vu_short * p;
|
|
unsigned short mfgid, devid;
|
|
|
|
mfgid = devid = 0;
|
|
flash_id(&mfgid, &devid);
|
|
return(mfgid == SFLASH_MFG_ID && devid == SFLASH_DEV_ID);
|
|
}
|
|
|
|
flash_err_t
|
|
flash_erase(int segment)
|
|
{
|
|
flash_err_t rv;
|
|
|
|
START_TIME;
|
|
rv = flash_io(SFLASH_CMD_ERASE, SFLASH_SEG_ADDR(segment), 0, 0, 0);
|
|
STOP_TIME("flash erase ");
|
|
return(rv);
|
|
}
|
|
|
|
void
|
|
flash_print_err(flash_err_t err)
|
|
{
|
|
switch (err) {
|
|
case 0:
|
|
FPR_MSG(("flash operation successful\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_ERASE_TO:
|
|
FPR_ERR(("Erase timeout Failure, Operation Aborted\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_WRITE_TO:
|
|
FPR_ERR(("Write timeout Failure, Operation Aborted\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_PDS_ADDR:
|
|
FPR_ERR(("Bad PDS Address\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_PARANOID:
|
|
FPR_ERR(("PARANOID check failed bogus data abound!\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_PDS_NEW:
|
|
FPR_ERR(("Cannot allocate new pds entry\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_STR_TOO_LONG:
|
|
FPR_ERR(("pds string too long\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_ENT_TOO_LONG:
|
|
FPR_ERR(("pds data entry too long\n"));
|
|
break;
|
|
|
|
case FLASH_ERR_OUT_OF_MEM:
|
|
FPR_ERR(("out of memory\n"));
|
|
break;
|
|
|
|
default:
|
|
FPR_ERR(("verify failure at flash address 0x%x\n", err));
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
flash_pds_hdr_ok(flash_pds_hdr_t *h)
|
|
{
|
|
return(h->magic == FPDS_HDR_MAGIC &&
|
|
(0xffff & ~_cksum1((void *)h, sizeof(*h), 0)) == 0);
|
|
}
|
|
|
|
/*
|
|
* will be called in very early RPROM, do *NOT* use printfs nor
|
|
* should you assume you're running cached
|
|
*/
|
|
int
|
|
flash_header_ok(flash_header_t *h)
|
|
{
|
|
return(h->magic == FLASH_PROM_MAGIC &&
|
|
(0xffff & ~_cksum1((void *)h, sizeof(*h), 0)) == 0);
|
|
}
|
|
|
|
/*
|
|
* will be called in very early RPROM, do *NOT* use printfs nor
|
|
* should you assume you're running cached
|
|
*/
|
|
int
|
|
flash_prom_ok(char *buf, flash_header_t *h)
|
|
{
|
|
if (flash_header_ok(h)) {
|
|
if (flash_cksum_r((char *)buf, h->datalen, 0) == h->dcksum)
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Called by RPROM for decision on whether to jump to FPROM
|
|
* or to continue in RPROM and do the FPROM recovery.
|
|
*
|
|
* rv 0 do not jump to fprom, continue RPROM start recovery
|
|
* 1 jump to fprom continue PROM boot
|
|
*
|
|
* this fct called in very early RPROM, do *NOT* use printfs nor
|
|
* should you assume you're running cached
|
|
*/
|
|
int
|
|
flash_fprom_reloc_chk(void)
|
|
{
|
|
#if XXX
|
|
currently having problems accessing the nvram this early
|
|
|
|
ushort rpromflg = flash_get_nv_rpromflg();
|
|
pon_puts("flash_fprom_reloc_chk ");
|
|
pon_puthex(rpromflg);
|
|
pon_puts("\r\n");
|
|
/*
|
|
* RPROM test flag on, unconditionally continue
|
|
* RPROM, do NOT jump to FPROM
|
|
*/
|
|
if (rpromflg & RPROM_FLG_TEST) {
|
|
pon_puts("RPROM_FLG_TEST set\r\n");
|
|
/*
|
|
* unset the flag so we can return to fprom
|
|
* on next boot, if all goes well
|
|
*/
|
|
flash_set_nv_rpromflg(rpromflg & ~RPROM_FLG_TEST);
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
if (jumper_off()) {
|
|
|
|
#if XXX
|
|
pon_puts("jumper off\r\n");
|
|
/*
|
|
* jumper off, and FPROM Verify Pending bit on
|
|
* FPROM failed to clear the FVP bit, FPROM is in
|
|
* trouble, reprogram with known good fprom image
|
|
*/
|
|
if (rpromflg & RPROM_FLG_FVP)
|
|
return(0);
|
|
|
|
pon_puts("full fprom chk\r\n");
|
|
#endif
|
|
/*
|
|
* jumper off, and FPROM Verify Pending bit off
|
|
* do full prom check sum
|
|
*/
|
|
return(flash_prom_ok((char *)SFLASH_FIXED_FPROM_ADDR,
|
|
(flash_header_t *)SFLASH_FIXED_FPROM_HDR));
|
|
} else {
|
|
/*
|
|
* jumper in on, Normal operating condition
|
|
* do the abridged FPROM check
|
|
*/
|
|
return(flash_header_ok((flash_header_t *)SFLASH_FIXED_FPROM_HDR));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* invalidate the headers
|
|
* zero out the 1st 32bit wd of header
|
|
* which must be the non-zero magic word
|
|
*/
|
|
int
|
|
flash_header_invalidate(void *hdr)
|
|
{
|
|
vu_short zero = 0;
|
|
vu_short *dst = hdr;
|
|
|
|
return(flash_io(SFLASH_CMD_WRITE, dst,
|
|
(__uint64_t)&zero,
|
|
(__uint64_t)dst, sizeof(uint32_t)/sizeof(uint16_t)));
|
|
}
|
|
|
|
/* initialized to 0 */
|
|
static int bad_nv_cnt;
|
|
|
|
/*
|
|
* only rprom should be calling this fct and ONLY if the
|
|
* dallas part has bad checksum
|
|
*/
|
|
void
|
|
flash_nv_cntr_bad(void)
|
|
{
|
|
bad_nv_cnt = 1;
|
|
}
|
|
|
|
|
|
int
|
|
flash_get_nflashed(int seg)
|
|
{
|
|
flash_header_t *h;
|
|
|
|
switch (seg) {
|
|
case SFLASH_RPROM_SEG: {
|
|
h = SFLASH_RPROM_HDR_ADDR;
|
|
if (flash_header_ok(h))
|
|
return(h->nflashed);
|
|
break;
|
|
}
|
|
|
|
case SFLASH_FPROM_SEG: {
|
|
h = SFLASH_FPROM_HDR_ADDR;
|
|
if (flash_header_ok(h))
|
|
return(h->nflashed);
|
|
break;
|
|
}
|
|
|
|
case SFLASH_PDS_SEG: {
|
|
flash_pds_hdr_t *pds = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
|
|
if (flash_pds_hdr_ok(pds))
|
|
return(pds->erases);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
FPR_MSG(("flash_get_nflashed: Bogus seg! %d\n", seg));
|
|
break;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
unsigned short
|
|
flash_get_nv_rpromflg(void)
|
|
{
|
|
char *cp;
|
|
|
|
cp = cpu_get_nvram_offset(NVOFF_RPROMFLAGS, NVLEN_RPROMFLAGS);
|
|
return((ushort)((cp[0] << 8) | cp[1]));
|
|
}
|
|
|
|
void
|
|
flash_set_nv_rpromflg(ushort flg)
|
|
{
|
|
cpu_set_nvram_offset(NVOFF_RPROMFLAGS, NVLEN_RPROMFLAGS, (char *)&flg);
|
|
}
|
|
|
|
int
|
|
flash_get_nv_cnt(int seg)
|
|
{
|
|
int cnt;
|
|
char *cp;
|
|
|
|
/*
|
|
* if dallas nvram is inaccessible try to
|
|
* use the backups in flash headers
|
|
*/
|
|
if (bad_nv_cnt)
|
|
return(flash_get_nflashed(seg));
|
|
|
|
switch (seg) {
|
|
case SFLASH_RPROM_SEG:
|
|
cp = cpu_get_nvram_offset(NVOFF_NFLASHEDRP, NVLEN_NFLASHEDRP);
|
|
break;
|
|
|
|
case SFLASH_FPROM_SEG:
|
|
cp = cpu_get_nvram_offset(NVOFF_NFLASHEDFP, NVLEN_NFLASHEDFP);
|
|
break;
|
|
|
|
case SFLASH_PDS_SEG:
|
|
cp = cpu_get_nvram_offset(NVOFF_NFLASHEDPDS, NVLEN_NFLASHEDPDS);
|
|
break;
|
|
|
|
default:
|
|
FPR_MSG(("flash_get_nv_cnt: Bogus seg! %d\n", seg));
|
|
return(0);
|
|
}
|
|
bcopy(cp, (char *)&cnt, sizeof(cnt));
|
|
|
|
return(cnt);
|
|
}
|
|
|
|
void
|
|
flash_set_nv_cnt(int seg, int *val)
|
|
{
|
|
char *cp = (char *)val;
|
|
|
|
if (bad_nv_cnt)
|
|
return;
|
|
|
|
switch (seg) {
|
|
case SFLASH_RPROM_SEG:
|
|
cpu_set_nvram_offset(NVOFF_NFLASHEDRP, NVLEN_NFLASHEDRP, cp);
|
|
break;
|
|
|
|
case SFLASH_FPROM_SEG:
|
|
cpu_set_nvram_offset(NVOFF_NFLASHEDFP, NVLEN_NFLASHEDFP, cp);
|
|
break;
|
|
|
|
case SFLASH_PDS_SEG:
|
|
cpu_set_nvram_offset(NVOFF_NFLASHEDPDS, NVLEN_NFLASHEDPDS, cp);
|
|
break;
|
|
|
|
default:
|
|
FPR_MSG(("flash_set_nv_cnt: Bogus seg! %d\n", seg));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* update nu_hdr for rprom/fprom based on current hdr in flash memory
|
|
*/
|
|
void
|
|
flash_upd_prom_hdr(int seg, __psunsigned_t nu_hdr)
|
|
{
|
|
flash_header_t *nh = (flash_header_t *)nu_hdr;
|
|
TIMEINFO *t;
|
|
|
|
/*
|
|
* use the dallas nvram to keep track of number of flashes
|
|
*/
|
|
nh->nflashed = flash_get_nv_cnt(seg) + 1;
|
|
|
|
cpu_get_tod((TIMEINFO *)&nh->timestamp);
|
|
nh->hcksum = 0;
|
|
nh->hcksum = (uint16_t)~_cksum1((void *)nh, sizeof(*nh), 0);
|
|
}
|
|
|
|
void
|
|
flash_print_prom_info(flash_header_t *h, u_short *prom)
|
|
{
|
|
u_short cksum;
|
|
int dallas_nflashed;
|
|
|
|
if (h == SFLASH_RPROM_HDR_ADDR)
|
|
dallas_nflashed = flash_get_nv_cnt(SFLASH_RPROM_SEG);
|
|
else
|
|
dallas_nflashed = flash_get_nv_cnt(SFLASH_FPROM_SEG);
|
|
|
|
FPR_PR(("Header @ 0x%x: ", h));
|
|
if ( !flash_header_ok(h)) {
|
|
FPR_PR(("BAD\n"));
|
|
FPR_PR(("num erases: (dallas %d)\n", dallas_nflashed));
|
|
dump_bytes((void *)h, sizeof(*h), 1);
|
|
return;
|
|
}
|
|
FPR_PR(("OK\n"));
|
|
cksum = flash_cksum_r((char *)prom, h->datalen, 0);
|
|
FPR_PR(("magic: \"%s\" (0x%x)\n", &h->magic, h->magic));
|
|
FPR_PR(("num erases: %d (dallas %d)\n", h->nflashed, dallas_nflashed));
|
|
FPR_PR(("last flashed at: %d/%d/%d %02d:%02d:%02d.%03d GMT\n",
|
|
h->timestamp.Month, h->timestamp.Day,
|
|
h->timestamp.Year,
|
|
h->timestamp.Hour, h->timestamp.Minutes,
|
|
h->timestamp.Seconds, h->timestamp.Milliseconds));
|
|
FPR_PR(("version: %d.%d\n", h->version>>8, h->version&0xff));
|
|
FPR_PR(("image cksum 0x%x (%d bytes) in header is %s (actual 0x%x)\n",
|
|
h->dcksum, h->datalen,
|
|
(cksum!=h->dcksum)?"BAD":"OK", cksum));
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Flash Persistant Data Storage Section
|
|
**********************************************************************/
|
|
|
|
/*
|
|
* PDS Segment Initialization and Maintenance Functions
|
|
*/
|
|
|
|
#define ERASED_UINT64 0xffffffffffffffff
|
|
|
|
static flash_err_t
|
|
flash_setup_pds_segment(flash_pds_hdr_t *hdr, int len)
|
|
{
|
|
uint64_t *fdp = (uint64_t *)SFLASH_PDS_ADDR;
|
|
uint64_t *fdp_end = (uint64_t *)SFLASH_PDS_END_ADDR;
|
|
TIMEINFO *t;
|
|
int i;
|
|
flash_err_t rv;
|
|
|
|
FPR_PDS(("flash_setup_pds_segment\n"));
|
|
/*
|
|
* check if erased already and if not then erase segment(s)
|
|
*/
|
|
while (fdp < fdp_end) {
|
|
|
|
if (*fdp++ == ERASED_UINT64)
|
|
continue;
|
|
|
|
for (i = 0; i < SFLASH_PDS_NSEGS; i++) {
|
|
rv = flash_erase(SFLASH_PDS_SEG + i);
|
|
if (rv) {
|
|
FPR_ERR(("Flash PDS(%d) Erase failed\n", i));
|
|
return(rv);
|
|
}
|
|
} /* for */
|
|
} /* while */
|
|
|
|
hdr->magic = FPDS_HDR_MAGIC;
|
|
hdr->erases = flash_get_nv_cnt(SFLASH_PDS_SEG) + 1;
|
|
/* increment the counter in dallas part */
|
|
flash_set_nv_cnt(SFLASH_PDS_SEG, (int *)&hdr->erases);
|
|
cpu_get_tod((TIMEINFO *)&hdr->last_flashed);
|
|
hdr->cksum = 0;
|
|
|
|
hdr->cksum = ~_cksum1((void *)hdr, sizeof(*hdr), 0);
|
|
us_delay(100);
|
|
rv = flash_io(SFLASH_CMD_WRITE, SFLASH_PDS_ADDR,
|
|
(__uint64_t)hdr,
|
|
(__uint64_t)SFLASH_PDS_ADDR,
|
|
len/sizeof(uint16_t));
|
|
us_delay(100);
|
|
return(rv);
|
|
}
|
|
|
|
/*
|
|
* reset the PDS flash memory area (erase and set header)
|
|
* we will loose all entries
|
|
*/
|
|
static flash_err_t
|
|
flash_pds_reset(void)
|
|
{
|
|
flash_pds_hdr_t new_hdr;
|
|
|
|
new_hdr.erases = flash_get_nv_cnt(SFLASH_PDS_SEG);
|
|
return(flash_setup_pds_segment(&new_hdr, sizeof(new_hdr)));
|
|
}
|
|
|
|
/*
|
|
* Flash PDS being accessed for the first time since reset/compress or
|
|
* bootup, check the header and set free ptr
|
|
*/
|
|
vu_short *
|
|
flash_pds_init(int resetpds)
|
|
{
|
|
flash_pds_hdr_t *hdr = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
flash_pds_ent_t *p_end = (flash_pds_ent_t *)SFLASH_PDS_END_ADDR;
|
|
flash_pds_ent_t *p;
|
|
u_short cksum = 1;
|
|
|
|
FPR_PDS(("flash_pds_init:\n"));
|
|
|
|
if (!resetpds) {
|
|
if (hdr->magic == FPDS_HDR_MAGIC)
|
|
cksum = 0xffff & ~_cksum1((void *)hdr, sizeof(*hdr), 0);
|
|
if (cksum) {
|
|
flash_err_t frv;
|
|
|
|
FPR_ERR(("WARNING: Bad/Uninitialized "
|
|
"Flash Persistant Data Storage Header\n"));
|
|
FPR_ERR((" Re-Initializing PDS\n"));
|
|
}
|
|
}
|
|
|
|
if (cksum || resetpds) {
|
|
flash_err_t frv;
|
|
|
|
pds_log_init = 0;
|
|
frv = flash_pds_reset();
|
|
if (frv) {
|
|
flash_print_err(frv);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
p = (flash_pds_ent_t *)(hdr + 1);
|
|
while (p->pds_type_len != FPDS_TYPELEN_FREE && p < p_end)
|
|
FPDS_NEXT_ENT(p);
|
|
if (p >= p_end)
|
|
p = 0;
|
|
flash_pds_freep = (vu_short *)p;
|
|
/*
|
|
* re-calcuate the current usage
|
|
*/
|
|
flash_pds_space_usage(&pds_usage);
|
|
|
|
FPR_PDS(("flash_pds_init: flash_pds_freep 0x%x\n", p));
|
|
return(flash_pds_freep);
|
|
}
|
|
|
|
void
|
|
flash_pds_init_free(void)
|
|
{
|
|
/*REFERENCED*/
|
|
vu_short *rv;
|
|
|
|
if ( !flash_pds_freep)
|
|
rv = flash_pds_init(0);
|
|
FPR_LO(("flash_pds_init_free rv = 0x%x\n", rv));
|
|
}
|
|
|
|
/*
|
|
* compress the pds block
|
|
*/
|
|
vu_short *
|
|
flash_pds_compress(int flags)
|
|
{
|
|
flash_pds_hdr_t *hdr = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
flash_pds_hdr_t *nu_hdr;
|
|
flash_pds_ent_t *p = 0;
|
|
u_short *nu_p;
|
|
uint16_t *buf;
|
|
int hlen, len;
|
|
int env_flg;
|
|
pds_space_usage_t use;
|
|
flash_err_t frv;
|
|
|
|
FPR_PDS(("flash_pds_compress\n"));
|
|
|
|
buf = (uint16_t *)align_malloc(SFLASH_PDS_SIZE, sizeof(uint64_t));
|
|
if (!buf) {
|
|
FPR_ERR(("flash_pds_compress: out of malloc\n"));
|
|
return(0);
|
|
}
|
|
nu_hdr = (flash_pds_hdr_t *)buf;
|
|
nu_p = (u_short *)(nu_hdr + 1);
|
|
|
|
/* only info needed for new from previous hdr */
|
|
nu_hdr->erases = hdr->erases;
|
|
|
|
/*
|
|
* if recoverable space is < 10 % of flash
|
|
* save only the ENV types
|
|
*/
|
|
env_flg = FPDS_ENT_ENV_TYPE | FPDS_ENT_DAT_TYPE;
|
|
if (PDS_INUSE < FPDS_HI_WATER_MARK)
|
|
env_flg |= FPDS_ENT_LOG_TYPE;
|
|
else
|
|
pds_log_init = 0; /* we are scrubbing logs re-init */
|
|
|
|
while (p = flash_pds_ent_next(p, env_flg)) {
|
|
hlen = p->pds_hlen + FPDS_ENT_DATA_OFFS;
|
|
bcopy((void *)p, (void *)nu_p, hlen*sizeof(uint16_t));
|
|
nu_p += hlen;
|
|
}
|
|
|
|
len = (__psunsigned_t)nu_p - (__psunsigned_t)nu_hdr;
|
|
frv = flash_setup_pds_segment(nu_hdr, len);
|
|
if (frv) {
|
|
flash_print_err(frv);
|
|
free(buf);
|
|
return(0);
|
|
}
|
|
free(buf);
|
|
return(flash_pds_init(0));
|
|
}
|
|
|
|
/*
|
|
* PDS entry Service functions
|
|
*/
|
|
|
|
static flash_err_t
|
|
flash_pds_write(void *pds_src, vu_short *dst, int hlen)
|
|
{
|
|
flash_err_t rv;
|
|
|
|
/* bounds check before flash write */
|
|
if (dst < SFLASH_PDS_ADDR || (dst+hlen) >= SFLASH_PDS_END_ADDR) {
|
|
FPR_ERR(("pds_write: bogus pds addr 0x%x hlen %d\n",
|
|
dst, hlen));
|
|
return(FLASH_ERR_PDS_ADDR);
|
|
}
|
|
rv = flash_io(SFLASH_CMD_WRITE, dst,
|
|
(__uint64_t)pds_src, (__uint64_t)dst, hlen);
|
|
return(rv);
|
|
}
|
|
|
|
/*
|
|
* get the next pds entry base on the filter starting from
|
|
* entry at p
|
|
* filter bits:
|
|
* FPDS_ENT_ENV_TYPE valid env entry
|
|
* FPDS_ENT_LOG_TYPE valid log entry
|
|
* FPDS_FILTER_ALL any valid or invalid entry
|
|
*
|
|
* stop at end of pds seg space or at pds free space and return 0
|
|
*/
|
|
flash_pds_ent_t *
|
|
flash_pds_ent_next(flash_pds_ent_t *p, u_short filter)
|
|
{
|
|
flash_pds_hdr_t *hdr = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
flash_pds_ent_t *p_end = (flash_pds_ent_t *)SFLASH_PDS_END_ADDR;
|
|
u_short typelen, valid;
|
|
|
|
/*
|
|
* If caller wants to start from beginning p==0 and we set out ptr
|
|
* otherwise we start at the NEXT entry of p unless p points to
|
|
* free space.
|
|
*/
|
|
if (!p) {
|
|
/*
|
|
* start from the beginning
|
|
*/
|
|
p = (flash_pds_ent_t *)(hdr + 1);
|
|
} else {
|
|
/*
|
|
* check if we are at PDS freespace
|
|
*/
|
|
typelen = p->pds_type_len;
|
|
if (typelen == FPDS_TYPELEN_FREE)
|
|
return(0);
|
|
|
|
FPDS_NEXT_ENT(p);
|
|
|
|
/* PARANOID */
|
|
if (p >= p_end) {
|
|
FPR_ERR(("Flash: Ptr 0x%x out of PDS bounds 0x%x\n",
|
|
p, p_end));
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
/*
|
|
* we are either the beginning or next entry after given p
|
|
* and this is were we start looking for entries matching filter
|
|
* Before we go into a loop check to see if we point to
|
|
* free space, and if the use wants any entry
|
|
*/
|
|
typelen = p->pds_type_len;
|
|
if (typelen == FPDS_TYPELEN_FREE)
|
|
return(0);
|
|
|
|
if (filter == FPDS_FILTER_ALL)
|
|
return(p);
|
|
|
|
do {
|
|
valid = p->valid;
|
|
if ((valid == FPDS_ENT_VALID &&
|
|
(typelen & FPDS_ENT_TYPE_MASK) & filter)) {
|
|
return(p);
|
|
}
|
|
FPDS_NEXT_ENT(p);
|
|
|
|
/* PARANOID */
|
|
if (p >= p_end) {
|
|
FPR_ERR(("Flash: Ptr 0x%x out of PDS bounds 0x%x\n",
|
|
p, SFLASH_PDS_END_ADDR));
|
|
return(0);
|
|
}
|
|
|
|
typelen = p->pds_type_len;
|
|
} while (typelen != FPDS_TYPELEN_FREE);
|
|
return(0);
|
|
}
|
|
|
|
flash_err_t
|
|
flash_pds_ent_inval(flash_pds_ent_t *p)
|
|
{
|
|
vu_short zero = 0;
|
|
u_short type = FPDS_ENT_TYPE_MASK & p->pds_type_len;
|
|
u_short len =(FPDS_ENT_HLEN_MASK & p->pds_type_len)*sizeof(uint16_t);
|
|
|
|
FPR_LO(("flash_pds_ent_inval: 0x%x\n", p));
|
|
/* PARNOID error case check for valid or free entry */
|
|
|
|
if (p->valid != FPDS_ENT_VALID ||
|
|
p->pds_type_len == FPDS_TYPELEN_FREE) {
|
|
FPR_ERR(("flash_pds_ent_inval: invalid entry @x0%x\n", p));
|
|
return FLASH_ERR_PARANOID;
|
|
}
|
|
|
|
if (type == FPDS_ENT_ENV_TYPE) {
|
|
pds_usage.invalid_env_ent++;
|
|
pds_usage.invalid_env_bytes += len;
|
|
pds_usage.inuse_env_ent--;
|
|
pds_usage.inuse_env_bytes -= len;
|
|
} else if (type == FPDS_ENT_LOG_TYPE) {
|
|
pds_usage.invalid_log_ent++;
|
|
pds_usage.invalid_log_bytes += len;
|
|
pds_usage.inuse_log_ent--;
|
|
pds_usage.inuse_log_bytes -= len;
|
|
} else {
|
|
pds_usage.invalid_dat_ent++;
|
|
pds_usage.invalid_dat_bytes += len;
|
|
pds_usage.inuse_dat_ent--;
|
|
pds_usage.inuse_dat_bytes -= len;
|
|
}
|
|
/* invalidate the entry */
|
|
return(flash_pds_write((void *)&zero, (void *)&p->valid, 1));
|
|
}
|
|
|
|
/*
|
|
* reserve space in the free flash memory section
|
|
* check for segment intialization and that we have enough space
|
|
*/
|
|
static vu_short *
|
|
flash_pds_ent_new(int hlen)
|
|
{
|
|
flash_pds_hdr_t *hdr = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
vu_short *p_end = SFLASH_PDS_END_ADDR;
|
|
vu_short *p;
|
|
|
|
FPR_LO(("flash_pds_ent_new: p 0x%x, hlen %d\n", flash_pds_freep, hlen));
|
|
|
|
/* make sure pds is initialized */
|
|
flash_pds_init_free();
|
|
p = flash_pds_freep;
|
|
|
|
if ((p + hlen) >= p_end) {
|
|
/*
|
|
* XXX compression level/mode flag
|
|
* what should we toss ?
|
|
*/
|
|
flash_pds_compress(0/*flags*/);
|
|
/*
|
|
* flash_pds_freep has been reset, refresh p
|
|
*/
|
|
p = flash_pds_freep;
|
|
}
|
|
|
|
flash_pds_freep += hlen;
|
|
return(p);
|
|
}
|
|
|
|
/*
|
|
* take data of len bytes, build a pds entry of type
|
|
* allocate pds space
|
|
* and finally commit to flash pds seg in 2 writes, skipping over the
|
|
* half word flag for valid
|
|
*/
|
|
flash_err_t
|
|
flash_pds_ent_write(u_short *data, int type, int len)
|
|
{
|
|
flash_pds_ent_t new;
|
|
vu_short *p;
|
|
int ent_hlen;
|
|
flash_err_t frv;
|
|
|
|
FPR_PDS(("flash_pds_ent_write: p 0x%x, type 0x%x, len %d\n",
|
|
data, type, len));
|
|
ent_hlen = (len + 1)/sizeof(uint16_t); /* round up to even bytes */
|
|
new.pds_type_len = ent_hlen & FPDS_ENT_HLEN_MASK;
|
|
new.pds_type_len |= type & FPDS_ENT_TYPE_MASK;
|
|
|
|
ent_hlen += FPDS_ENT_DATA_OFFS; /* add hdr len */
|
|
p = flash_pds_ent_new(ent_hlen);
|
|
if (!p)
|
|
return(FLASH_ERR_PDS_NEW);
|
|
|
|
frv = flash_pds_write((void *)&new, p, FPDS_ENT_VALID_OFFS);
|
|
if (frv)
|
|
goto bail;
|
|
|
|
/* skip over the valid field */
|
|
p += FPDS_ENT_DATA_OFFS;
|
|
frv = flash_pds_write((void *)data, p, new.pds_hlen);
|
|
if (frv)
|
|
flash_print_err(frv);
|
|
|
|
bail:
|
|
len = ent_hlen * sizeof(uint16_t);
|
|
if (type == FPDS_ENT_ENV_TYPE) {
|
|
pds_usage.inuse_env_bytes += len;
|
|
pds_usage.inuse_env_ent++;
|
|
} else if (type == FPDS_ENT_LOG_TYPE) {
|
|
pds_usage.inuse_log_bytes += len;
|
|
pds_usage.inuse_log_ent++;
|
|
} else {
|
|
pds_usage.inuse_dat_bytes += len;
|
|
pds_usage.inuse_dat_ent++;
|
|
}
|
|
pds_usage.free -= len;
|
|
return(frv);
|
|
}
|
|
|
|
/**********
|
|
* PDS ENV
|
|
**********/
|
|
flash_pds_ent_t *
|
|
flash_findenv(flash_pds_ent_t *ent, char *var)
|
|
{
|
|
int len = strlen(var);
|
|
char *cp;
|
|
|
|
FPR_PDS(("flash_findenv:\n"));
|
|
while(ent = flash_pds_ent_next(ent, FPDS_ENT_ENV_TYPE)) {
|
|
if (strncasecmp(var, (char *)ent->data, len) == 0) {
|
|
cp = (char *)ent->data;
|
|
if (cp[len] == '=')
|
|
break;
|
|
}
|
|
}
|
|
return(ent);
|
|
}
|
|
|
|
/*
|
|
* Setup access to flash memory
|
|
*/
|
|
int
|
|
flash_init(void)
|
|
{
|
|
flash_mem_base = FLASH_MEM_BASE;
|
|
if (!flash_ok()) {
|
|
flash_mem_base = FLASH_MEM_ALT_BASE;
|
|
if (!flash_ok()) {
|
|
FPR_ERR(("flash part not found at 0x%x or 0x%x\n",
|
|
FLASH_MEM_BASE, FLASH_MEM_ALT_BASE));
|
|
return(0);
|
|
}
|
|
}
|
|
FPR_HI(("flash_init: flash @ 0x%x = 0x%x\n", flash_mem_base));
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* go through valid pds env entries and call the putstr
|
|
* fct to build the string list for the current enviroment.
|
|
* putstr should be new_str2()
|
|
*/
|
|
int
|
|
flash_loadenv(int (*putstr)(char *, char *, struct string_list *),
|
|
struct string_list *env)
|
|
{
|
|
flash_pds_ent_t *ent = 0;
|
|
char buf[FPDS_ENT_MAX_DATA_LEN];
|
|
char *cp;
|
|
int rv;
|
|
|
|
FPR_PDS(("flash_loadenv:\n"));
|
|
|
|
/* make sure pds is initialized */
|
|
flash_pds_init_free();
|
|
|
|
/* make sure there is a putstr */
|
|
if (!putstr)
|
|
return(0);
|
|
|
|
while(ent = flash_pds_ent_next(ent, FPDS_ENT_ENV_TYPE)) {
|
|
int i;
|
|
|
|
FPR_PDS(("env ent hlen %d (%s):\n", ent->pds_hlen, ent->data));
|
|
/*
|
|
* call the env stringlist call back routine with
|
|
* var-name and var-str parts as separate strings.
|
|
* This is kinda stupid since the fct (new_str2) puts
|
|
* back together just like we have it here as a single
|
|
* string. new_str1 would be best but
|
|
* the generic i/f uses new_str2 ..sigh
|
|
*/
|
|
strcpy(buf, (char *)ent->data);
|
|
i = 0;
|
|
cp = buf;
|
|
while(i<FPDS_ENT_MAX_DATA_LEN && *cp != '=')
|
|
cp++, i++;
|
|
if (*cp != '=') {
|
|
FPR_ERR(("Bad PDS env entry at 0x%x\n", ent));
|
|
continue;
|
|
}
|
|
*cp++ = 0; /* terminate the var name part of string */
|
|
|
|
/* resetting env code calls w/o env set-up to initialize */
|
|
rv = (*putstr)(buf, cp, env);
|
|
if (rv)
|
|
return(rv);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* resetenv cmd, invalidate all pds env vars
|
|
*/
|
|
void
|
|
flash_resetenv(void)
|
|
{
|
|
flash_err_t frv;
|
|
flash_pds_ent_t *ent = 0;
|
|
|
|
FPR_PDS(("flash_resetenv:\n"));
|
|
while(ent = flash_pds_ent_next(ent, FPDS_ENT_ENV_TYPE)) {
|
|
frv = flash_pds_ent_inval(ent);
|
|
if (frv)
|
|
flash_print_err(frv);
|
|
}
|
|
}
|
|
|
|
int
|
|
flash_setenv(char *var, char *str)
|
|
{
|
|
flash_pds_ent_t *p;
|
|
u_short pds_data[FPDS_ENT_MAX_DATA_HLEN];
|
|
int type;
|
|
int rv = EINVAL;
|
|
int len;
|
|
int unset = (str == 0) || (*str == '\0');
|
|
|
|
FPR_PDS(("flash_setenv: var=%s str=%s\n", var, (str)?str:"NULL"));
|
|
if ( !unset) {
|
|
len = sprintf((char *)pds_data, "%s=%s", var, str);
|
|
len++; /* for the null byte string terminator */
|
|
if (len > FPDS_ENT_MAX_DATA_LEN) {
|
|
FPR_ERR(("string too long (%d), > %d\n",
|
|
len, FPDS_ENT_MAX_DATA_LEN));
|
|
return(rv);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* look for valid pds env/penv entry matching var
|
|
* If there is a valid entry and str is different
|
|
* invalidate it.
|
|
*/
|
|
if (p = flash_findenv(0, var)) {
|
|
flash_err_t frv;
|
|
|
|
if (!unset) {
|
|
char *cp = (char *)p->data;
|
|
|
|
while(*cp && *cp != '=')
|
|
cp++;
|
|
if (!strcmp(str, ++cp))
|
|
return 0;
|
|
}
|
|
frv = flash_pds_ent_inval(p);
|
|
if (frv)
|
|
return(EIO);
|
|
rv = 0;
|
|
}
|
|
#ifdef FDEBUG
|
|
if (rv == 0) {
|
|
/* PARANOID make sure there is only 1 valid var */
|
|
p = 0;
|
|
while (p = flash_findenv(p, var)) {
|
|
FPR_PDS(("BOGUS: multiple env var %s @0x%x invalidating ...\n",
|
|
var, p));
|
|
flash_pds_ent_inval(p);
|
|
}
|
|
}
|
|
#endif
|
|
if (unset)
|
|
return(0);
|
|
|
|
if (flash_pds_ent_write(pds_data, FPDS_ENT_ENV_TYPE, len))
|
|
rv = EIO;
|
|
return(rv);
|
|
}
|
|
|
|
/**********
|
|
* PDS LOG
|
|
**********/
|
|
/*
|
|
* log some data to the flash pds
|
|
* log types:
|
|
* PDS_LOG_TYPE0:
|
|
* special entry for the first log. This entry will
|
|
* enter the current system tick, timeofday timestamp,
|
|
* and tick to usecs multiplier-divider
|
|
*/
|
|
flash_err_t
|
|
flash_pds_write_log(uint64_t log_type,
|
|
uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4)
|
|
{
|
|
flash_pds_log_data_t log_data;
|
|
int len;
|
|
|
|
if (pds_log_init == 0) {
|
|
flash_err_t frv;
|
|
|
|
pds_log_init = 1;
|
|
frv = flash_pds_write_log(PDS_LOG_TYPE0, 0, 0, 0, 0);
|
|
if (frv)
|
|
return(frv);
|
|
}
|
|
log_data.log_hdr = PDS_LOG_TICK_MASK & HEART_PIU_K1PTR->h_count |
|
|
PDS_LOG_TYPE_MASK & (log_type << PDS_LOG_TYPE_SHFT);
|
|
|
|
switch (log_type) {
|
|
case PDS_LOG_TYPE0: {
|
|
/*
|
|
* current tod
|
|
*/
|
|
cpu_get_tod(&log_data.u.init.tod);
|
|
/*
|
|
* tick to usecs multiplier/divider
|
|
*/
|
|
log_data.u.init.tick_mult = HEART_COUNT_NSECS;
|
|
log_data.u.init.tick_div = 1000;
|
|
len = sizeof(TIMEINFO) + sizeof(log_data.u.w[0]) * 2;
|
|
break;
|
|
}
|
|
case PDS_LOG_TYPE1: {
|
|
char *s = (char *)a1;
|
|
|
|
len = strlen(s);
|
|
if (len >= FPDS_ENT_MAX_DATA_LEN) {
|
|
len = FPDS_ENT_MAX_DATA_LEN;
|
|
s[FPDS_ENT_MAX_DATA_LEN-1] = 0;
|
|
} else
|
|
len++; /* NULL byte str terminator */
|
|
bcopy(s, log_data.u.c, len);
|
|
break;
|
|
}
|
|
case PDS_LOG_TYPE2:
|
|
default: {
|
|
log_data.u.d[0] = a1;
|
|
log_data.u.d[1] = a2;
|
|
log_data.u.d[2] = a3;
|
|
log_data.u.d[3] = a4;
|
|
|
|
len = sizeof(uint64_t)*4;
|
|
break;
|
|
}
|
|
}
|
|
len += sizeof(log_data.log_hdr);
|
|
return(flash_pds_ent_write((u_short *)&log_data,
|
|
FPDS_ENT_LOG_TYPE, len));
|
|
}
|
|
|
|
#include <stdarg.h>
|
|
void
|
|
flash_pds_prf(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char buf[256];
|
|
|
|
/*CONSTCOND*/
|
|
va_start(ap, fmt);
|
|
vsprintf(buf, fmt, ap);
|
|
va_end(ap);
|
|
|
|
flash_pds_log(buf);
|
|
}
|
|
|
|
void
|
|
flash_pds_log(char *str)
|
|
{
|
|
flash_err_t frv;
|
|
|
|
frv = flash_pds_write_log(PDS_LOG_TYPE1, (uint64_t)str, 0, 0, 0);
|
|
if (frv) {
|
|
FPR_ERR(("flash_pds_log: flash_pds_write_log err\n"));
|
|
flash_print_err(frv);
|
|
}
|
|
}
|
|
|
|
flash_pds_ent_t *
|
|
flash_pds_get_log(flash_pds_ent_t *p, char **data, int *len, int inval)
|
|
{
|
|
flash_pds_hdr_t *hdr = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
flash_pds_ent_t *p_end = (flash_pds_ent_t *)SFLASH_PDS_END_ADDR;
|
|
flash_pds_ent_t *cur;
|
|
|
|
FPR_LO(("flash_pds_get_log:\n"));
|
|
if (p) {
|
|
u_short type;
|
|
|
|
/*
|
|
* do the best we can to ensure a good entry ptr
|
|
* addr range, valid & type check
|
|
*/
|
|
if (p < (flash_pds_ent_t *)(hdr + 1) || p >= p_end) {
|
|
FPR_ERR(("pds_get_log: bogus pds addr 0x%x\n", p));
|
|
return(0);
|
|
}
|
|
if (p->pds_type_len == FPDS_TYPELEN_FREE)
|
|
return(0);
|
|
|
|
type = p->pds_type_len & FPDS_ENT_TYPE_MASK;
|
|
if (p->valid != FPDS_ENT_VALID ||
|
|
(type != FPDS_ENT_ENV_TYPE && type != FPDS_ENT_LOG_TYPE)) {
|
|
FPR_ERR(("pds_get_log: bad entry - valid=0x%x type=0x%x\n",
|
|
p->valid, type));
|
|
return(0);
|
|
}
|
|
} else {
|
|
p = flash_pds_ent_next(p, FPDS_ENT_LOG_TYPE);
|
|
if (!p)
|
|
return(0);
|
|
}
|
|
*data = (char *)p->data;
|
|
*len = p->pds_hlen*sizeof(uint16_t);
|
|
if (inval)
|
|
flash_pds_ent_inval(p);
|
|
return(flash_pds_ent_next(p, FPDS_ENT_LOG_TYPE));
|
|
}
|
|
|
|
/*
|
|
* take a ptr to PDS log entry, and print it in a reasonable format.
|
|
* NOTE: this is the simplest print, it certainly can be made much nicer
|
|
*/
|
|
void
|
|
flash_print_log(void *data, int len)
|
|
{
|
|
flash_pds_log_data_t log_data;
|
|
uint64_t us, us_mod;
|
|
|
|
bcopy(data, (void *)&log_data, len);
|
|
|
|
/*
|
|
* we can use the tick_mult and tick_div in PDS_LOG_TYPE0
|
|
* but since we know what H/W take the short cut.
|
|
*/
|
|
us = (log_data.log_hdr & PDS_LOG_TICK_MASK) * HEART_COUNT_NSECS;
|
|
us += 500; /* rounding */
|
|
us /= 1000; /* in rounded usecs */
|
|
us_mod = us%1000000; /* usec in modulo seconds */
|
|
FPR_PR(("%7d.%03d,%03d: ", us/1000000,us_mod/1000,us_mod%1000));
|
|
|
|
len -= sizeof(log_data.log_hdr);
|
|
switch (log_data.log_hdr >> PDS_LOG_TYPE_SHFT) {
|
|
case PDS_LOG_TYPE0: {
|
|
TIMEINFO *t = (TIMEINFO *)&log_data.u.init.tod;
|
|
int mult = log_data.u.init.tick_mult;
|
|
int div = log_data.u.init.tick_div;
|
|
|
|
FPR_PR(("Log Init: %d/%d/%d %02d:%02d:%02d.%03d GMT\n",
|
|
t->Month, t->Day, t->Year,
|
|
t->Hour, t->Minutes, t->Seconds, t->Milliseconds));
|
|
FPR_PR((" log tick multiplier(%d)/divider(%d)\n",
|
|
mult, div));
|
|
break;
|
|
}
|
|
case PDS_LOG_TYPE1: {
|
|
FPR_PR(("T1: %s\n", log_data.u.c));
|
|
break;
|
|
}
|
|
case PDS_LOG_TYPE2:
|
|
default: {
|
|
FPR_PR(("T2: 0x%x 0x%x 0x%x 0x%x\n",
|
|
log_data.u.d[0], log_data.u.d[1],
|
|
log_data.u.d[2], log_data.u.d[3]));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/**********
|
|
* PDS DATA
|
|
**********/
|
|
/*
|
|
* Each PDS entry can hold max 512 bytes not counting
|
|
* the 4 byte overhead (typelen and valid fields).
|
|
* The max size of the PDS DATA type entry is 16 Kbytes.
|
|
* To accomodate the large sized PDS DATA entries,
|
|
* there PDS DATA entries will be one of 2 types, DA0 or DA1
|
|
* these entries must be layed out in the PDS segment in
|
|
* DA0 [ DA1 [ DA1 [ ... ]]] manner.
|
|
*/
|
|
/*
|
|
* find the PDS entry containing the first entry (DA0) of
|
|
* the <key> data entry(s) (DA0 & DA1s) starting from ent,
|
|
* return the pds ent ptr.
|
|
* if key==0, then first DA0 starting from ent will be returned
|
|
* if ent==0, then the search will start from beginning of PDS
|
|
* ent MUST point to a PDS entry.
|
|
*
|
|
* If there are addition DA1 type entries in use to
|
|
* hold this data then they should be located
|
|
* right after this entry in sequence.
|
|
*/
|
|
flash_pds_ent_t *
|
|
flash_pds_find_data(char *key, flash_pds_ent_t *ent)
|
|
{
|
|
flash_pds_da0_t *da0;
|
|
char *ekey;
|
|
|
|
while(ent = flash_pds_ent_next(ent, FPDS_ENT_DA0_TYPE)) {
|
|
da0 = (flash_pds_da0_t *)ent->data;
|
|
ekey = (char *)(da0 + 1);
|
|
FPR_PDS(("flash_pds_find_data: find %s, entry %s(%d)\n",
|
|
key, ekey, da0->datalen));
|
|
if (key == 0)
|
|
break;
|
|
if (strcmp(key, ekey) == 0)
|
|
break;
|
|
}
|
|
return(ent);
|
|
}
|
|
|
|
/*
|
|
* invalidate the pds data type entry by finding the first
|
|
* (DA0) type entry and then all following sequential DA1 type entry(s).
|
|
*/
|
|
void
|
|
flash_pds_inval_data(char *key)
|
|
{
|
|
flash_pds_ent_t *old;
|
|
flash_pds_ent_t *ent = flash_pds_find_data(key, 0);
|
|
flash_pds_ent_t *p_end = (flash_pds_ent_t *)SFLASH_PDS_END_ADDR;
|
|
|
|
if (!ent)
|
|
return;
|
|
|
|
do {
|
|
old = ent;
|
|
FPDS_NEXT_ENT(ent);
|
|
flash_pds_ent_inval(old);
|
|
} while(ent < p_end && FPDS_ENT_TYPE(ent) == FPDS_ENT_DA1_TYPE);
|
|
FPR_PDS(("PDS DATA: invalidated %s\n", key));
|
|
}
|
|
|
|
/*
|
|
* copy the data from pds data entry matching the key string
|
|
* if cksum on verify the cksum and return w/o copying the data
|
|
* The caller MUST have buf space large enough to accomdate
|
|
* the datalen bytes.
|
|
*/
|
|
int
|
|
flash_pds_copy_data(char *key, char *buf, int cksum)
|
|
{
|
|
char *datap = buf;
|
|
char *cptr;
|
|
int len, datalen, cplen;
|
|
flash_pds_da0_t *da0;
|
|
flash_pds_ent_t *ent0 = flash_pds_find_data(key, 0);
|
|
flash_pds_ent_t *ent = ent0;
|
|
flash_pds_ent_t *p_end = (flash_pds_ent_t *)SFLASH_PDS_END_ADDR;
|
|
|
|
if (ent0 == 0)
|
|
return(0);
|
|
|
|
/*
|
|
* prepare to copy the data out of DA0, skipping the
|
|
* da0 hdr and key
|
|
*/
|
|
da0 = (flash_pds_da0_t *)ent0->data;
|
|
if (da0->datalen > FPDS_ENT_DAT_MAX_LEN) {
|
|
FPR_ERR(("Bad PDS data size, %d > %d",
|
|
da0->datalen, FPDS_ENT_DAT_MAX_LEN));
|
|
return(0);
|
|
}
|
|
cptr = (char *)(da0 + 1);
|
|
/*
|
|
* skip the key str and possible byte pad
|
|
*/
|
|
cptr += strlen(key);
|
|
if ((__psunsigned_t)cptr & 1)
|
|
cptr += 1;
|
|
else
|
|
cptr += 2;
|
|
cplen = FPDS_ENT_LEN(ent);
|
|
cplen -= (int)(cptr - (char *)da0);
|
|
if (cplen - 1 == da0->datalen)
|
|
cplen -= 1;
|
|
|
|
datalen = 0;
|
|
|
|
do {
|
|
if (datalen >= da0->datalen) /* PARANOIA */
|
|
break;
|
|
/*
|
|
* the PDS entries are always stored in even bytes, 16bit
|
|
* boundaries. So with odd datalen there is 1 extra byte of pad
|
|
* which we want to subtract when the actual data is copied to
|
|
* user buffer (key is already padded out to even bytes).
|
|
*/
|
|
if (cplen - 1 == da0->datalen - datalen)
|
|
cplen -= 1;
|
|
FPR_PDS(("copy: 0x%x -> 0x%x %d bytes\n", cptr, datap, cplen));
|
|
bcopy(cptr, datap, cplen);
|
|
datap += cplen;
|
|
datalen += cplen;
|
|
|
|
FPDS_NEXT_ENT(ent);
|
|
cplen = FPDS_ENT_LEN(ent);
|
|
cptr = (char *)ent->data;
|
|
|
|
} while(ent < p_end && FPDS_ENT_TYPE(ent) == FPDS_ENT_DA1_TYPE);
|
|
|
|
if (datalen != da0->datalen) {
|
|
FPR_ERR(("PDS datalen error %d, actual %d\n",
|
|
da0->datalen, datalen));
|
|
}
|
|
|
|
if (cksum) {
|
|
if (da0->datasum != flash_cksum_r(buf, datalen, 0)) {
|
|
FPR_ERR(("PDS data cksum error, for %s (%d bytes)\n",
|
|
key, datalen));
|
|
datalen = 0;
|
|
}
|
|
}
|
|
return(datalen);
|
|
}
|
|
|
|
/*
|
|
* create PDS data entry(s) for arbitaray data of datalen bytes
|
|
* each PDS data entry can be 512 bytes max, and these
|
|
* can be used for a max datalen of 16K bytes.
|
|
*/
|
|
flash_err_t
|
|
flash_pds_set_data(char *key, char *data, int datalen)
|
|
{
|
|
int entlen, keylen, len, actual_use;
|
|
flash_pds_ent_t *ent;
|
|
char *datap;
|
|
u_short *buf, sum;
|
|
flash_pds_da0_t *da0;
|
|
flash_err_t ferr;
|
|
|
|
/*
|
|
* check lengths
|
|
*/
|
|
if (datalen > FPDS_ENT_DAT_MAX_LEN) {
|
|
return(FLASH_ERR_ENT_TOO_LONG);
|
|
}
|
|
keylen = strlen(key);
|
|
if (keylen > FPDS_ENT_MAX_DATA_LEN) {
|
|
return(FLASH_ERR_STR_TOO_LONG);
|
|
}
|
|
/*
|
|
* ensure total space for key is even bytes
|
|
*/
|
|
if (keylen & 1)
|
|
keylen += 1; /* null terminator only */
|
|
else
|
|
keylen += 2; /* null terminator and byte pad */
|
|
len = keylen + datalen;
|
|
entlen = sizeof(flash_pds_da0_t); /* da0 datalen and cksum */
|
|
len += entlen; /* add da0 header */
|
|
/*
|
|
* check if there is space, for total and pds entry overhead
|
|
* minus the amount we will recover if we need to invalidate an
|
|
* exiting entry with the same key
|
|
*/
|
|
actual_use = (len/FPDS_ENT_MAX_LEN) + 1; /* num entries needed */
|
|
actual_use *= FPDS_ENT_DATA_OFFS * sizeof(uint16_t); /* overhead */
|
|
|
|
actual_use += PDS_INUSE + len;
|
|
/*
|
|
* if we are to recover any space subtract that
|
|
*/
|
|
if (ent = flash_pds_find_data(key, 0)) {
|
|
actual_use -= ((flash_pds_da0_t *)ent->data)->datalen;
|
|
if (actual_use > FPDS_HI_WATER_MARK) {
|
|
return(FLASH_ERR_OUT_OF_MEM);
|
|
}
|
|
}
|
|
|
|
buf = (u_short *)malloc(FPDS_ENT_MAX_LEN);
|
|
if (!buf) {
|
|
return(FLASH_ERR_OUT_OF_MEM);
|
|
}
|
|
/*
|
|
* if an entry exist for key, invalidate it
|
|
*/
|
|
flash_pds_inval_data(key);
|
|
/*
|
|
* the first PDS entry DA0 is special, extra header
|
|
* and the key str need to be prepended to the data
|
|
*/
|
|
da0 = (flash_pds_da0_t *)buf;
|
|
da0->datalen = datalen;
|
|
da0->datasum = flash_cksum_r(data, datalen, 0); /* cksum */
|
|
datap = (char *)(da0 + 1);
|
|
|
|
strcpy(datap, key);
|
|
datap += keylen;
|
|
entlen += keylen;
|
|
|
|
if (len <= FPDS_ENT_MAX_LEN) {
|
|
bcopy(data, datap, datalen);
|
|
entlen = len;
|
|
len = 0;
|
|
} else {
|
|
bcopy(data, datap, FPDS_ENT_MAX_LEN-entlen);
|
|
datap = data + (FPDS_ENT_MAX_LEN-entlen);
|
|
entlen = FPDS_ENT_MAX_LEN;
|
|
len -= FPDS_ENT_MAX_LEN;
|
|
}
|
|
ferr = flash_pds_ent_write(buf, FPDS_ENT_DA0_TYPE, entlen);
|
|
free(buf);
|
|
if (ferr) {
|
|
return(ferr);
|
|
}
|
|
|
|
while (len) {
|
|
if (len > FPDS_ENT_MAX_LEN) {
|
|
entlen = FPDS_ENT_MAX_LEN;
|
|
len -= FPDS_ENT_MAX_LEN;
|
|
} else {
|
|
entlen = len;
|
|
len = 0;
|
|
}
|
|
ferr = flash_pds_ent_write((void *)datap,FPDS_ENT_DA1_TYPE,entlen);
|
|
if (ferr) {
|
|
return(ferr);
|
|
}
|
|
datap += entlen;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Flash Misc fcts
|
|
**********************************************************************/
|
|
/*
|
|
* caculate current pds space usage
|
|
*/
|
|
void
|
|
flash_pds_space_usage(pds_space_usage_t *use)
|
|
{
|
|
flash_pds_ent_t *ent;
|
|
__psunsigned_t pds_end = (__psunsigned_t)SFLASH_PDS_END_ADDR;
|
|
|
|
bzero((void *)use, sizeof(*use));
|
|
|
|
use->free = (__psunsigned_t)pds_end - (__psunsigned_t)flash_pds_freep;
|
|
ent = 0;
|
|
while (ent = flash_pds_ent_next(ent, FPDS_FILTER_ALL)) {
|
|
|
|
switch (ent->pds_type_len & FPDS_ENT_TYPE_MASK) {
|
|
case FPDS_ENT_ENV_TYPE:
|
|
if (ent->valid) {
|
|
use->inuse_env_ent++;
|
|
use->inuse_env_bytes += (ent->pds_hlen +
|
|
FPDS_ENT_DATA_OFFS) * sizeof(uint16_t);
|
|
} else {
|
|
use->invalid_env_ent++;
|
|
use->invalid_env_bytes += (ent->pds_hlen +
|
|
FPDS_ENT_DATA_OFFS) * sizeof(uint16_t);
|
|
}
|
|
break;
|
|
|
|
case FPDS_ENT_LOG_TYPE:
|
|
if (ent->valid) {
|
|
use->inuse_log_ent++;
|
|
use->inuse_log_bytes += (ent->pds_hlen +
|
|
FPDS_ENT_DATA_OFFS) * sizeof(uint16_t);
|
|
} else {
|
|
use->invalid_log_ent++;
|
|
use->invalid_log_bytes += (ent->pds_hlen +
|
|
FPDS_ENT_DATA_OFFS) * sizeof(uint16_t);
|
|
}
|
|
break;
|
|
|
|
case FPDS_ENT_DA0_TYPE:
|
|
case FPDS_ENT_DA1_TYPE:
|
|
if (ent->valid) {
|
|
use->inuse_dat_ent++;
|
|
use->inuse_dat_bytes += (ent->pds_hlen +
|
|
FPDS_ENT_DATA_OFFS) * sizeof(uint16_t);
|
|
} else {
|
|
use->invalid_dat_ent++;
|
|
use->invalid_dat_bytes += (ent->pds_hlen +
|
|
FPDS_ENT_DATA_OFFS) * sizeof(uint16_t);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
flash_pds_print_usage(pds_space_usage_t *use)
|
|
{
|
|
int unfree;
|
|
|
|
FPR_PR(("\nPDS usage stats: PDS SIZE :%d\n", SFLASH_PDS_SIZE));
|
|
FPR_PR((" env entries inuse :%3d (%d bytes)\n",
|
|
use->inuse_env_ent, use->inuse_env_bytes));
|
|
FPR_PR((" log entries inuse :%3d (%d bytes)\n",
|
|
use->inuse_log_ent, use->inuse_log_bytes));
|
|
FPR_PR((" dat entries inuse :%3d (%d bytes)\n",
|
|
use->inuse_dat_ent, use->inuse_dat_bytes));
|
|
FPR_PR((" env entries retired :%3d (%d bytes)\n",
|
|
use->invalid_env_ent, use->invalid_env_bytes));
|
|
FPR_PR((" log entries retired :%3d (%d bytes)\n",
|
|
use->invalid_log_ent, use->invalid_log_bytes));
|
|
FPR_PR((" dat entries retired :%3d (%d bytes)\n",
|
|
use->invalid_dat_ent, use->invalid_dat_bytes));
|
|
|
|
FPR_PR((" total pds entries inuse :%3d (%d bytes)\n",
|
|
use->inuse_env_ent +
|
|
use->inuse_log_ent + use->inuse_dat_ent,
|
|
use->inuse_env_bytes +
|
|
use->inuse_log_bytes + use->inuse_dat_bytes));
|
|
|
|
unfree = use->inuse_env_bytes +
|
|
use->inuse_log_bytes +
|
|
use->inuse_dat_bytes;
|
|
|
|
FPR_PR((" total pds entries retired :%3d (%d bytes)\n",
|
|
use->invalid_env_ent +
|
|
use->invalid_log_ent + use->invalid_dat_ent,
|
|
use->invalid_env_bytes +
|
|
use->invalid_log_bytes + use->invalid_dat_bytes));
|
|
|
|
unfree += use->invalid_env_bytes +
|
|
use->invalid_log_bytes +
|
|
use->invalid_dat_bytes;
|
|
|
|
FPR_PR(("free space %d, used space %d, hdr %d, total %d\n",
|
|
use->free, unfree, sizeof(flash_pds_hdr_t),
|
|
use->free + unfree + sizeof(flash_pds_hdr_t) ));
|
|
}
|
|
|
|
void
|
|
flash_print_pds_status(int check)
|
|
{
|
|
flash_pds_hdr_t *hdr = (flash_pds_hdr_t *)SFLASH_PDS_ADDR;
|
|
pds_space_usage_t usage;
|
|
int unfree;
|
|
int hdr_ok;
|
|
int dallas_nflashed;
|
|
char *cp;
|
|
|
|
dallas_nflashed = flash_get_nv_cnt(SFLASH_PDS_SEG);
|
|
|
|
FPR_PR(("PDS Header: is "));
|
|
if (!flash_pds_hdr_ok(hdr)) {
|
|
FPR_PR(("BAD\n"));
|
|
FPR_PR(("num erases: (dallas: %d)\n", dallas_nflashed));
|
|
dump_bytes((char *)hdr, sizeof(*hdr), 1);
|
|
return;
|
|
}
|
|
|
|
FPR_PR(("OK\n"));
|
|
FPR_PR(("num erases: %d (dallas: %d)\n",
|
|
hdr->erases, dallas_nflashed));
|
|
FPR_PR(("magic: \"%s\" (0x%x)\n", &hdr->magic, hdr->magic));
|
|
FPR_PR(("last flashed at: %d/%d/%d %02d:%02d:%02d.%03d GMT\n",
|
|
hdr->last_flashed.Month, hdr->last_flashed.Day,
|
|
hdr->last_flashed.Year,
|
|
hdr->last_flashed.Hour, hdr->last_flashed.Minutes,
|
|
hdr->last_flashed.Seconds, hdr->last_flashed.Milliseconds));
|
|
|
|
flash_pds_print_usage(&pds_usage);
|
|
|
|
if (check) {
|
|
FPR_PR(("\nUsage stats check\n"));
|
|
flash_pds_space_usage(&usage);
|
|
flash_pds_print_usage(&usage);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* cksum the bytes in sum -r style
|
|
*/
|
|
unsigned short
|
|
flash_cksum_r(char *cp, int len, unsigned short sum)
|
|
{
|
|
while (len--) {
|
|
if (sum & 0x0001)
|
|
sum = (sum >> 1) | 0x8000;
|
|
else
|
|
sum >>= 1;
|
|
sum += *cp++;
|
|
}
|
|
return(sum);
|
|
}
|
|
|
|
/*
|
|
* dump data in hex/ascii format
|
|
*/
|
|
void
|
|
dump_bytes(char *ptr, int count, int flag /*show addr*/)
|
|
{
|
|
#ifdef FDEBUG
|
|
#define DOT 0x2e /* "." */
|
|
#define SP 0x20 /* " " */
|
|
|
|
u_int n = 0, hx;
|
|
char *end, nlx=0;
|
|
char aline[32], hexline[80];
|
|
|
|
aline[16] = 0;
|
|
|
|
end = ptr + count;
|
|
|
|
if ((__psunsigned_t)ptr & 0xf) {
|
|
if(flag)
|
|
hx = sprintf(hexline , "%08x:", (__psunsigned_t)ptr & ~0xf);
|
|
else
|
|
hx = 0;
|
|
while (n != ((__psunsigned_t)ptr & 0xf)) {
|
|
if ((n & 3) == 0)
|
|
sprintf(&hexline[hx], ": ");
|
|
else
|
|
sprintf(&hexline[hx], " ");
|
|
hx += 3;
|
|
aline[(n & 0xf)] = SP;
|
|
n++;
|
|
}
|
|
nlx = '\n';
|
|
}
|
|
while (ptr != end) {
|
|
char c;
|
|
|
|
if ((n & 0xf) == 0) {
|
|
if(flag)
|
|
hx = sprintf(hexline, "%c%08x:", nlx, (__psunsigned_t)ptr);
|
|
else
|
|
hx = sprintf(hexline, "%c", nlx);
|
|
nlx = '\n';
|
|
}
|
|
c = *ptr++;
|
|
if (c >= 0x20 && c <= 0x7e)
|
|
aline[(n & 0xf)] = c;
|
|
else
|
|
aline[(n & 0xf)] = DOT;
|
|
if ((n & 3) == 0)
|
|
sprintf(&hexline[hx], ":%02x", c);
|
|
else
|
|
sprintf(&hexline[hx], " %02x", c);
|
|
hx += 3;
|
|
if ((n & 0xf) == 0xf)
|
|
printf("%s %s", hexline, aline);
|
|
n++;
|
|
}
|
|
if (n & 0xf) {
|
|
while (n & 0xf) {
|
|
sprintf(&hexline[hx], " ");
|
|
hx += 3;
|
|
aline[(n & 0xf)] = SP;
|
|
n++;
|
|
}
|
|
printf("%s %s", hexline, aline);
|
|
}
|
|
printf("\n");
|
|
#endif /* FDEBUG */
|
|
}
|
|
#endif /* defined(IP30) */
|