Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2033 lines
48 KiB

////////////////////////////////////////////////////////////////////////// ++
//
// FWNVR.C
//
// Copyright 1991-94 IBM, Motorola, Microsoft
//
// Functions to access Non-Volatile Ram on PowerPC systems.
//
// This module is used exactly as-is by both ARC and HAL. By convention,
// it originates in the ARC environment and is copied to the HAL tree.
//
////////////////////////////////////////////////////////////////////////// --
/*
YET TO DO:
Fix and use NVR size-sensing
Use nvr_set_variable() function.
Allocate memory even in the ARC environment, since later the size of
the buffers required may vary wildly. This won't be a
problem (for either HAL or ARC) IF we go to NVR-direct use.
NOTE: must handle pool-clearing problems FIRST (ARC only)
Q: handle buffer-size changes when nvr_read_nvram() gets actual
NVR size? Bufferless fixes this problem too.
Handle 24-bit access
*/
#ifdef _HALNVR_ /////// BUILDING FOR USE IN HAL ///////
#include "halp.h"
#else /////// BUILDING FOR USE IN ARC ///////
#include "fwp.h"
#define ExAllocatePool(type,size) FwAllocatePool(size)
#endif // _HALNVR_ /////////////////////////////////////////
#include "prepnvr.h"
#include "fwstatus.h"
typedef struct _nvr_object
{
struct _nvr_object *self ;
HEADER * bhead ;
HEADER * lhead ;
UCHAR bend [NVSIZE*2] ;
UCHAR lend [NVSIZE*2] ;
} NVR_OBJECT, *PNVR_OBJECT ;
typedef NVRAM_MAP *PNVRAM_MAP ;
extern PVOID HalpIoControlBase ;
#define ENDSWAP_SHORT(_x) (((_x<<8)&0xff00)|((_x>>8)&0x00ff))
#define ENDSWAP_LONG(_x)\
(((_x<<24)&0xff000000)|((_x<<8)&0x00ff0000)|\
((_x>>8)&0x0000ff00)|((_x>>24)&0x000000ff))
#define _toupr_(_c) (((_c >= 'a') && (_c <= 'z')) ? (_c - 'a' + 'A') : _c)
// Define the maximum size required for fetching any item from NVRAM, which
// is defined to be the maximum size OF the NVRAM.
// NOTE this is a poor assumption and needs to be sensed at run-time !!
#define MAXNVRFETCH NVSIZE*2
#define MAXIMUM_ENVIRONMENT_VALUE 1024
/////////////////////////////////////////////////////////////////////////////
// LOCAL DATA, NOT VISIBLE TO OTHER MODULES
/////////////////////////////////////////////////////////////////////////////
static UCHAR _currentstring[MAXIMUM_ENVIRONMENT_VALUE] ;
static UCHAR _currentfetch[MAXNVRFETCH] ;
#ifndef _HALNVR_
NVR_OBJECT nvrobj ;
#endif // _HALNVR_
PNVR_OBJECT pnvrobj = NULL ;
// The access method varies with planar construction, and is switched
// based on the Planar ID Register.
#define NVR_ACCESS_UNKNOWN 0
#define NVR_ACCESS_SANDALFOOT 1
#define NVR_ACCESS_STANDARD 2
LONG NvrAccessMethod = NVR_ACCESS_UNKNOWN ;
// The use of 24-bit NVRAM addressing is enable with this flag:
LONG NvrAccess24bit = FALSE ;
// These are the ISA port addresses for NVRAM Registers
#define NVRREG_AD0 0x74 // LS byte of address (bits 0-7)
#define NVRREG_AD1 0x75 // next byte of address (bits 8-15)
// Below to be added when register definition finalized
// #define NVRREG_AD2 0x?? // MS byte of address (bits 16-23)
#define NVRREG_STDDATA 0x76 // Data Port (R/W), standard
#define NVRREG_SFTDATA 0x77 // Data Port (R/W), Sandalfoot only
// The size below is used for error recovery, to either set NVRAM to
// a default state (a dubious proposition) or to clear it entirely.
ULONG NvrFillSize = 4096 ; // "safe" default value
USHORT NvrVersion = 0 ; // MSB=version, LSB=revision
//////// Bring in prototypes automatically generated by CPROTO. These
//////// should be placed AFTER any other headers and any module data
//////// and data definitions, and BEFORE module code begins.
#define _CPROTO_FW_SCOPE_
#define _CPROTO_FWNVR_STATICS_
#include "fwdebug.h"
#include "fwnvr.h"
/////////////////////////////////////////////////////////////////////////////
// !=
PNVR_OBJECT
nvr_alloc (
ULONG size
)
{
PNVR_OBJECT p ;
#ifdef _HALNVR_
// HAL allocates memory for it so it doesn't sit around all the time
p = ExAllocatePool (NonPagedPool, size) ;
#else
// FW uses static allocation (probably change later...)
p = &nvrobj ;
#endif // _HALNVR_
return (p) ;
}
// !=
VOID
nvr_free (
PVOID p
)
{
if ( !p )
return ;
NvrAccessMethod = NVR_ACCESS_UNKNOWN ;
NvrAccess24bit = 0 ;
#ifdef _HALNVR_
ExFreePool (p) ; // de-allocate HAL memory
#endif // _HALNVR_
}
/////////////////////////////////////////////////////////////////////////////
// ==
USHORT
NvrGetVersion (
VOID
)
{
return ( NvrVersion ) ;
}
/////////////////////////////////////////////////////////////////////////////
// ==
ULONG
NvrGetSize (
VOID
)
// Performs a hardware scan of NVRAM and returns it's size in bytes.
// The algorithm accepts the fact that the bridge will return the last
// byte written, even if it did NOT come from a memory location (it's
// just a latch which is left with a stale value). So in order to get
// a new value in the latch, we write to NVRAM location 0 between other
// accesses.
{
ULONG size = 1 ;
UCHAR b0, b1, b2, b3, b4 ;
// Since we'll be writing to location 0 for each byte tested, we
// can't test byte 0 without some special gyrations. Instead, we
// just start the test at byte 1. Recall that this is not intended
// so much as a memory TEST, but more a SIZE check.
size = 1 ;
b4 = nvr_read (0) ; // save byte 0 so the test is non-destructive
while ( size < 250000 )
{
b0 = nvr_read (size) ;
nvr_write (size,(UCHAR)(size&0xFF)) ;
nvr_write (0,(UCHAR)~(size&0xFF)) ;
b1 = nvr_read (size) ;
nvr_write (size,(UCHAR)~(size&0xFF)) ;
nvr_write (0,(UCHAR)(size&0xFF)) ;
b2 = nvr_read (size) ;
nvr_write (size,b0) ;
b3 = nvr_read (size) ;
if ( b3!=b0 || b1 != (UCHAR)(size&0xFF) || b2 != (UCHAR)~(size&0xFF) )
break ;
size++ ;
}
nvr_write (0,b4) ; // set first byte back again
if ( size == 1 )
size = 0 ;
return ( size ) ;
}
// ==
BOOLEAN
NvrSetSize (
LONG NvramSize // size of NVRAM in bytes if non-zero
)
{
ULONG size = 1 ;
UCHAR b0, b1, b2, b3, b4 ;
if ( NvramSize )
{
// Caller has specified what fill size is required. Just set
// the global variable and return.
NvrFillSize = NvramSize ;
DEBUG_PRINT (1,"NvrSetSize: fill size caller-set to %d bytes\n",NvrFillSize) ;
return TRUE ;
}
else
{
// Caller didn't know how big NVRAM is. We try to find out
// with a hardware test, and if successful we fill in the
// value. If we can't find out either, we disable clearing
// of NVRAM by setting NvrFillSize to zero.
size = NvrGetSize() & 0xFFFFF000 ; // size modulo-4k
DEBUG_PRINT (1,"NvrSetSize: fill size measured at %d bytes\n",size) ;
// Overridden temporarily until we decide how to handle caller issues as to
// whether clearing NVRAM is even legitimate to do. Code above works OK.
NvrFillSize = 0 ;
DEBUG_PRINT (1,"NvrSetSize: fill disabled by setting size to 0 bytes\n") ;
}
return TRUE ;
}
/////////////////////////////////////////////////////////////////////////////
// ==
LONG
NvrGetMethod (
VOID
)
// Returns the access method in use. If it has not yet been set, a
// check of the hardware is made in an attempt to set it properly.
{
if ( NvrAccessMethod == NVR_ACCESS_UNKNOWN )
NvrSetMethod (0) ;
return ( NvrAccessMethod ) ;
}
// ==
BOOLEAN
NvrSetMethod (
LONG ForcedValue // if Non-zero, set to this value regardless
)
// Decide how to access NVRAM, so that the nvr_read() and nvr_write()
// functions can work properly. If a non-zero parameter is passed in,
// the method is simply set to this value. If ZERO is passed in, then
// local code tries to determine the proper method. Either way, the
// operation is checked afterwards, and FALSE is returned if the access
// method does recognize values inside NVRAM. If OK, TRUE returned.
{
UCHAR PlanarID = 0;
UCHAR endian = '*' ;
HEADER *hp = (HEADER *)0;
// If caller sets it explicitly, just check the results. Otherwise,
// use the Planar ID Register to decide what method to use.
if ( ForcedValue )
NvrAccessMethod = ForcedValue ;
else
{
PlanarID = READ_REGISTER_UCHAR ((ULONG)HalpIoControlBase+0x852) ;
// NOTE that while the 0xDE value is documented as a
// Sandalfoot, it was never used as one. Instead, it is used
// on Woodfield. Unclear if 0xDD used, but it's defined in
// DAKOARCH as a Sandalfoot.
if ( (PlanarID >= 0xFC && PlanarID < 0xFF) // Sandalfoot
|| (PlanarID >= 0xDC && PlanarID < 0xDE) // more Sandalfoot
|| (PlanarID == 0x95) // Victory
|| (PlanarID == 0x0C) // Harley
|| (PlanarID == 0x5a) // Zapatos
)
NvrAccessMethod = NVR_ACCESS_SANDALFOOT ; // Carolina
else
NvrAccessMethod = NVR_ACCESS_STANDARD ;
}
endian = nvr_read((ULONG)((ULONG)(&hp->Endian)-(ULONG)hp)) ;
DEBUG_PRINT (1,"NvrSetMethod: PlanarID=0x%02X, Method set to %d (Endian shown as '%c')\n",
PlanarID,NvrAccessMethod,endian) ;
if ( endian != 'B' && endian != 'L' )
{
DEBUG_PRINT (0,"NvrSetMethod FAILED: Endian value was '%c'\n",
endian) ;
return FALSE ;
}
return TRUE ;
}
// ==
UCHAR
nvr_read (
ULONG addr
)
{
UCHAR uc = 0 ;
if ( !NvrAccessMethod )
NvrSetMethod (0) ;
switch ( NvrAccessMethod )
{
case 0:
DEBUG_PRINT (1,"nvr_read: NO NvrAccessMethod\n") ;
return 0 ;
case 1:
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD0,
(UCHAR)(addr&0xFF)) ;
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD1,
(UCHAR)((addr>>8)&0x1F)) ;
uc = READ_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_SFTDATA) ;
break ;
case 2:
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD0,
(UCHAR)(addr&0xFF)) ;
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD1,
(UCHAR)((addr>>8)&0x1F)) ;
uc = READ_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_STDDATA) ;
break ;
}
return ( uc ) ;
}
// ==
VOID
nvr_write (
ULONG addr,
UCHAR data
)
{
if ( !NvrAccessMethod )
NvrSetMethod (0) ;
switch ( NvrAccessMethod )
{
case 0:
DEBUG_PRINT (1,"nvr_write: NO NvrAccessMethod\n") ;
return ;
case 1:
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD0,
(UCHAR)(addr&0xFF)) ;
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD1,
(UCHAR)((addr>>8)&0x1F)) ;
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_SFTDATA,
data) ;
break ;
case 2:
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD0,
(UCHAR)(addr&0xFF)) ;
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_AD1,
(UCHAR)((addr>>8)&0x1F)) ;
WRITE_REGISTER_UCHAR((ULONG)HalpIoControlBase+NVRREG_STDDATA,
data) ;
break ;
}
}
/////////////////////////////////////////////////////////////////////////////
// !=
VOID
nvr_swap_Header (
HEADER* dest,
HEADER* src
)
{
ULONG i ;
PUCHAR cp ;
if ( !dest || !src )
return ; // invalid pointer
dest->Size = ENDSWAP_SHORT(src->Size) ;
dest->Version = src->Version ;
dest->Revision = src->Revision ;
dest->Crc1 = ENDSWAP_SHORT(src->Crc1) ;
dest->Crc2 = ENDSWAP_SHORT(src->Crc2) ;
dest->LastOS = src->LastOS ;
dest->Endian = src->Endian ;
dest->OSAreaUsage = src->OSAreaUsage ;
dest->PMMode = src->PMMode ;
// NOTE THIS CHANGES WITH UPDATED PPCNVR01.H
/* convert NVRRESTART_BLOCK structure of Header */
dest->ResumeBlock.CheckSum = ENDSWAP_LONG(src->ResumeBlock.CheckSum) ;
dest->ResumeBlock.BootStatus = ENDSWAP_LONG(src->ResumeBlock.BootStatus) ;
dest->ResumeBlock.ResumeAddr =
(PVOID) ENDSWAP_LONG((ULONG)src->ResumeBlock.ResumeAddr) ;
dest->ResumeBlock.SaveAreaAddr =
(PVOID) ENDSWAP_LONG((ULONG)src->ResumeBlock.SaveAreaAddr) ;
dest->ResumeBlock.SaveAreaLength =
ENDSWAP_LONG((ULONG)src->ResumeBlock.SaveAreaLength) ;
dest->ResumeBlock.HibResumeImageRBA =
ENDSWAP_LONG((ULONG)src->ResumeBlock.HibResumeImageRBA) ;
dest->ResumeBlock.HibResumeImageRBACount =
ENDSWAP_LONG((ULONG)src->ResumeBlock.HibResumeImageRBACount) ;
dest->ResumeBlock.Reserved =
ENDSWAP_LONG((ULONG)src->ResumeBlock.Reserved) ;
/* convert SECURITY structure */
dest->Security.BootErrCnt =
ENDSWAP_LONG(src->Security.BootErrCnt) ;
dest->Security.ConfigErrCnt =
ENDSWAP_LONG(src->Security.ConfigErrCnt) ;
dest->Security.BootErrorDT[0] =
ENDSWAP_LONG(src->Security.BootErrorDT[0]) ;
dest->Security.BootErrorDT[1] =
ENDSWAP_LONG(src->Security.BootErrorDT[1]) ;
dest->Security.ConfigErrorDT[0] =
ENDSWAP_LONG(src->Security.ConfigErrorDT[0]) ;
dest->Security.ConfigErrorDT[1] =
ENDSWAP_LONG(src->Security.ConfigErrorDT[1]) ;
dest->Security.BootCorrectDT[0] =
ENDSWAP_LONG(src->Security.BootCorrectDT[0]) ;
dest->Security.BootCorrectDT[1] =
ENDSWAP_LONG(src->Security.BootCorrectDT[1]) ;
dest->Security.ConfigCorrectDT[0] =
ENDSWAP_LONG(src->Security.ConfigCorrectDT[0]) ;
dest->Security.ConfigCorrectDT[1] =
ENDSWAP_LONG(src->Security.ConfigCorrectDT[1]) ;
dest->Security.BootSetDT[0] =
ENDSWAP_LONG(src->Security.BootSetDT[0]) ;
dest->Security.BootSetDT[1] =
ENDSWAP_LONG(src->Security.BootSetDT[1]) ;
dest->Security.ConfigSetDT[0] =
ENDSWAP_LONG(src->Security.ConfigSetDT[0]) ;
dest->Security.ConfigSetDT[1] =
ENDSWAP_LONG(src->Security.ConfigSetDT[1]) ;
for (i = 0 ; i < 16 ; i++)
dest->Security.Serial[i] = src->Security.Serial[i] ;
/* convert ERROR_LOG 0 and ERROR_LOG 1 structure */
// ASAP: use sizeof() instead of 40 below...
for (i = 0 ; i < 40 ; i++)
{
dest->ErrorLog[0].ErrorLogEntry[i] = src->ErrorLog[0].ErrorLogEntry[i] ;
dest->ErrorLog[1].ErrorLogEntry[i] = src->ErrorLog[1].ErrorLogEntry[i] ;
}
/* convert remainder of Header */
dest->GEAddress = (PVOID) ENDSWAP_LONG((ULONG)src->GEAddress) ;
dest->GELength = ENDSWAP_LONG((ULONG)src->GELength) ;
dest->GELastWriteDT[0] = ENDSWAP_LONG(src->GELastWriteDT[0]) ;
dest->GELastWriteDT[1] = ENDSWAP_LONG(src->GELastWriteDT[1]) ;
dest->ConfigAddress =
(PVOID) ENDSWAP_LONG((ULONG)src->ConfigAddress) ;
dest->ConfigLength = ENDSWAP_LONG(src->ConfigLength) ;
dest->ConfigLastWriteDT[0] =
ENDSWAP_LONG(src->ConfigLastWriteDT[0]) ;
dest->ConfigLastWriteDT[1] =
ENDSWAP_LONG(src->ConfigLastWriteDT[1]) ;
dest->ConfigCount = ENDSWAP_LONG(src->ConfigCount) ;
dest->OSAreaAddress =
(PVOID) ENDSWAP_LONG((ULONG)src->OSAreaAddress) ;
dest->OSAreaLength = ENDSWAP_LONG(src->OSAreaLength) ;
dest->OSAreaLastWriteDT[0] =
ENDSWAP_LONG(src->OSAreaLastWriteDT[0]) ;
dest->OSAreaLastWriteDT[1] =
ENDSWAP_LONG(src->OSAreaLastWriteDT[1]) ;
}
// !=
VOID
nvr_headb2l (
PNVR_OBJECT p
)
{
if ( !p || p != p->self )
return ; // invalid pointer
nvr_swap_Header (p->lhead,p->bhead) ;
}
// !=
VOID
nvr_headl2b (
PNVR_OBJECT p
)
{
if ( !p || p != p->self )
return ; // invalid pointer
nvr_swap_Header (p->bhead,p->lhead) ;
}
/////////////////////////////////////////////////////////////////////////////
// !=
VOID
nvr_default_nvram (
PNVR_OBJECT p
)
// Attempts to protect operation from faulty intitialization of NVRAM
// by early versions of ROS. Called only from nvr_read_nvram() when a
// bad 'endian' indicator or CRC is found.
{
ULONG i ;
PUCHAR cp ;
HEADER * bethp ;
if ( !p || p != p->self )
return ; // invalid pointer
DEBUG_PRINT (1,"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n") ;
DEBUG_PRINT (1,">>>>> CAUTION: Continuing from here will attempt to CLEAR NVRAM ! >>>>>>\n") ;
DEBUG_PRINT (1,"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n") ;
DEBUG_BREAK (1) ;
if ( NvrFillSize == 0 )
{
DEBUG_PRINT (1,">>>>>>>>>> nvr_default_nvram: CAN'T SET NVRAM TO DEFAULT (NO SIZE INFO)!\n") ;
DEBUG_BREAK (1) ;
return ;
}
DEBUG_PRINT (1,">>>>>>>>>> nvr_default_nvram: SETTING NVRAM TO DEFAULT VALUES!\n") ;
nvr_clear_nvram () ; // empty the physical NVRAM
bethp = (HEADER *)p->bend ;
cp = (PUCHAR)p->bend ;
/* clear internal header */
for (i = 0 ; i < sizeof(HEADER) ; i++)
*cp++ = 0 ;
// ASAP: interlock with the (allocated) memory space available. We have to
// never clear more memory than we have allocated!
// ASAP: save size of volatile buffer in struct itself.
/* clear internal data areas */
for (i = 0 ; i < NvrFillSize ; i++)
p->bend[i] = p->lend[i] = 0 ;
bethp->Endian = 'B' ;
bethp->Size = ENDSWAP_SHORT((USHORT)NvrFillSize/1024) ;
// Watch it -- these could come back and byte us !!
bethp->Version = 1 ;
bethp->Revision = 4 ;
// Global Environment starts right after header, and uses all the
// space not reserved for the OS and CONFIG areas below.
bethp->GEAddress = (PVOID) ENDSWAP_LONG((ULONG)sizeof(HEADER)) ;
bethp->GELength =
ENDSWAP_LONG((ULONG)NvrFillSize-CONFSIZE-OSAREASIZE-sizeof(HEADER)) ;
// OS Area follows, taking up a default amount of space
bethp->OSAreaAddress =
(PVOID) ENDSWAP_LONG((ULONG)(NvrFillSize-CONFSIZE-OSAREASIZE)) ;
bethp->OSAreaLength = ENDSWAP_LONG((ULONG)OSAREASIZE) ;
// Set the config area such that it isn't used. This leaves some
// free space (CONFSIZE bytes) for it to grow downward into. This is
// counterintuitive, but matches what ROS uses for initialization.
bethp->ConfigAddress = (PVOID) ENDSWAP_LONG(NvrFillSize) ;
bethp->ConfigLength = ENDSWAP_LONG((ULONG)0) ;
nvr_headb2l (p) ; // copy data to Little-Endian (internal) side
nvr_write_Header (p) ; // write header to the hardware
}
static BOOLEAN crc2_short = FALSE;
USHORT
nvr_calc2crc_read (
PNVR_OBJECT p
)
// Checksum the CONFIGURATION AREA ONLY.
{
ULONG ul ;
PUCHAR cp ;
PUCHAR end ;
if ( !p || p != p->self )
return 0xFFFF ; // invalid pointer
// Original version returned indeterminate value here !!
// ASAP: check with ESW for proper resolution!
// return ;
ul = 0xFFFF ;
// ASAP: revisit the calculation size. The first Wiltwick had a "gap"
// between the OS and CFG areas, and it may NOT have been checksummed.
cp = (PUCHAR)((ULONG)p->bhead + (ULONG)p->lhead->ConfigAddress) ;
#if 0
// end = (PUCHAR)((ULONG)p->bhead +
// (ULONG)(((ULONG)p->lhead->Size << 10) - 1)) ;
end = (PUCHAR)((ULONG)p->bhead +
(ULONG)(((ULONG)p->lhead->Size << 10) )) ;
#endif
//
// PLJ reverted to original code to avoid blowing checksum in config
// area on sandalfoot.
//
end = (PUCHAR)((ULONG)p->bhead +
(ULONG)(((ULONG)p->lhead->Size << 10) - 1)) ;
// end PLJ change
for ( ; cp < end ; cp++)
ul = nvr_computecrc(ul, *cp) ;
//
// Note that we checksummed 1 byte too few, this is to
// allow for OLD versions of the firmware. If the checksum
// now == expected value, set the boolean crc2_short to TRUE
// so we calculate the right value when writing and return
// the current value. If it is not currently correct,
// checksum 1 more byte and return that value.
//
if ((USHORT)(ul & 0xFFFF) == p->lhead->Crc2) {
crc2_short = TRUE;
} else {
ul = nvr_computecrc(ul, *cp) ;
}
return ( (USHORT)(ul & 0xFFFF) ) ;
}
USHORT
nvr_calc2crc_write (
PNVR_OBJECT p
)
// Checksum the CONFIGURATION AREA ONLY.
{
ULONG ul ;
PUCHAR cp ;
PUCHAR end ;
if ( !p || p != p->self )
return 0xFFFF ; // invalid pointer
// Original version returned indeterminate value here !!
// ASAP: check with ESW for proper resolution!
// return ;
ul = 0xFFFF ;
// ASAP: revisit the calculation size. The first Wiltwick had a "gap"
// between the OS and CFG areas, and it may NOT have been checksummed.
cp = (PUCHAR)((ULONG)p->bhead + (ULONG)p->lhead->ConfigAddress) ;
// end = (PUCHAR)((ULONG)p->bhead +
// (ULONG)(((ULONG)p->lhead->Size << 10) - 1)) ;
end = (PUCHAR)((ULONG)p->bhead +
(ULONG)(((ULONG)p->lhead->Size << 10) )) ;
//
// PLJ if we were short 1 byte on the read, calculate the new checksum
// 1 byte short also. This is to support old firmware.
//
if ( crc2_short ) {
end--;
}
// end PLJ change
for ( ; cp < end ; cp++)
ul = nvr_computecrc(ul, *cp) ;
return ( (USHORT)(ul & 0xFFFF) ) ;
}
/////////////////////////////////////////////////////////////////////////////
// !=
BOOLEAN
nvr_read_nvram (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR cp ;
HEADER * bhp ; // ptr to BE header
USHORT crc1a = 0 , crc1c = 0 ; // actual and calculated
USHORT crc2a = 0 , crc2c = 0 ; // values for each CRC
ULONG nvr_selfsize = 0 ;
DEBUG_PRINT (2,"nvr_read_nvram: entry\n") ;
if ( !p || p != p->self )
return FALSE ; // invalid pointer
/* read the HEADER from the NVRAM chip */
bhp = p->bhead ;
cp = (PUCHAR)p->bend ;
for (i = 0 ; i < sizeof(HEADER) ; i++)
*cp++ = nvr_read(i) ;
if ((bhp->Endian != 'B') && (bhp->Endian != 'L'))
goto error ;
// convert big endian header to little endian
nvr_headb2l (p) ;
// read the data areas. We have to do this before calculating the
// checksum, since these areas are checked.
nvr_read_GEArea(p) ;
nvr_read_OSArea(p) ;
nvr_read_CFArea(p) ;
// validate checksum 1
crc1a = ENDSWAP_SHORT(bhp->Crc1) ;
crc1c = nvr_calc1crc(p) ;
if ( crc1a != crc1c )
goto error ;
// validate checksum 2
crc2a = ENDSWAP_SHORT(bhp->Crc2) ;
crc2c = nvr_calc2crc_read(p) ;
if ( crc2a != crc2c )
goto error ;
// At this point the checksum matches, and we have good confidence of
// having valid data. We use the data within the NVRAM to firm up
// our notion of how big NVRAM is, in case we later have to clear it.
nvr_selfsize = ((ULONG)(ENDSWAP_SHORT(bhp->Size))) * 1024 ;
NvrSetSize (nvr_selfsize) ;
// Save the NVR version for later query if required
NvrVersion = (bhp->Version<<8) | bhp->Revision ;
// ASAP: cross-check NvrFillSize with all three addresses and sizes stored
// in NVRAM to see if the values make sense.
return TRUE ;
// We get below only if there was an error reading NVRAM. If so,
// show whatever we can for debugging and then go set NVRAM to the
// "default" state.
error:
DEBUG_PRINT (1,"NVRAM READ ERROR ! Endian byte = '%c'\n",bhp->Endian) ;
DEBUG_PRINT (1," CRC1 as read: 0x%04X Calculated: 0x%04X\n",crc1a,crc1c) ;
DEBUG_PRINT (1," CRC2 as read: 0x%04X Calculated: 0x%04X\n",crc2a,crc2c) ;
nvr_print_object () ; // if debugging, show before deleting
nvr_default_nvram (p) ;
return FALSE ;
}
// !=
VOID
nvr_read_GEArea (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR lp ;
PUCHAR bp ;
ULONG offset ;
if ( !p || p != p->self )
return ; // invalid pointer
// Read Global Environment data into both BE and LE areas
offset = (ULONG)p->lhead->GEAddress ;
lp = (PUCHAR)((ULONG)p->lhead + offset) ;
bp = (PUCHAR)((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->GELength ; i++, bp++, lp++)
*bp = *lp = nvr_read(offset + i) ;
}
// !=
VOID
nvr_read_OSArea (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR lp ;
PUCHAR bp ;
ULONG offset ;
if ( !p || p != p->self )
return ; // invalid pointer
// Read OS-Specific Environment data into both BE and LE areas
offset = (ULONG)p->lhead->OSAreaAddress ;
lp = (PUCHAR)((ULONG)p->lhead + offset) ;
bp = (PUCHAR)((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->OSAreaLength ; i++, bp++, lp++)
*bp = *lp = nvr_read(offset + i) ;
}
// !=
VOID
nvr_read_CFArea (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR lp ;
PUCHAR bp ;
ULONG offset ;
if ( !p || p != p->self )
return ; // invalid pointer
// Read Configuration data into both BE and LE areas
offset = (ULONG) p->lhead->ConfigAddress ;
lp = (PUCHAR) ((ULONG)p->lhead + offset) ;
bp = (PUCHAR) ((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->ConfigLength ; i++)
bp[i] = lp[i] = nvr_read(offset + i) ;
}
/////////////////////////////////////////////////////////////////////////////
// !=
VOID
nvr_write_Header (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR cp ;
USHORT us ;
if ( !p || p != p->self )
return ; // invalid pointer
// We treat the LE header as the 'master', so first convert it's
// contents into BE format to be written ti NVRAM
nvr_headl2b(p) ;
// Fill in the CRC values. NOTE that changes are made to the LE
// header WITHOUT updating the CRC, so we have to do it before
// writing the header. It's the BE data that's being checksummed.
us = nvr_calc1crc(p) ;
p->bhead->Crc1 = ENDSWAP_SHORT(us) ;
us = nvr_calc2crc_write(p) ;
p->bhead->Crc2 = ENDSWAP_SHORT(us) ;
// spit out data
cp = (PUCHAR)p->bend ;
for ( i = 0 ; i < sizeof(HEADER) ; i++ )
nvr_write (i, *cp++) ;
}
// !=
VOID
nvr_write_GEArea (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR dest ;
PUCHAR src ;
ULONG offset ;
if ( !p || p != p->self )
return ; // invalid pointer
/* copy from little endian to big endian staging area */
offset = (ULONG)p->lhead->GEAddress ;
src = (PUCHAR)((ULONG)p->lhead + offset) ;
dest = (PUCHAR)((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->GELength ; i++, dest++, src++)
*dest = *src ;
/* convert to big endian, compute crc, and write header */
nvr_write_Header(p) ;
/* spit out global environment data */
src = (PUCHAR)((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->GELength ; i++, src++)
nvr_write (i+offset, *src) ;
}
// !=
VOID
nvr_write_OSArea (
PNVR_OBJECT p
)
{
ULONG i ;
ULONG offset ;
PUCHAR src ;
PUCHAR dest ;
if ( !p || p != p->self )
return ; // invalid pointer
/* copy from little endian to big endian staging area */
offset = (ULONG) p->lhead->OSAreaAddress ;
src = (PUCHAR) ((ULONG)p->lhead + offset) ;
dest = (PUCHAR) ((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->OSAreaLength ; i++, dest++, src++)
*dest = *src ;
/* spit out OS specific data */
/* header not needed - no crc for OS Area in Header */
src = (PUCHAR)((ULONG)p->bhead + offset) ;
for (i = 0 ; i < p->lhead->OSAreaLength ; i++, src++)
nvr_write (i+offset, *src) ;
}
// !=
VOID
nvr_write_CFArea (
PNVR_OBJECT p
)
{
ULONG i ;
PUCHAR dest ;
PUCHAR src ;
ULONG offset ;
if ( !p || p != p->self )
return ; // invalid pointer
/* copy from little endian to big endian staging area */
offset = (ULONG)p->lhead->ConfigAddress ;
dest = (PUCHAR) ((ULONG)p->bhead + offset - 1) ;
src = (PUCHAR) ((ULONG)p->lhead + offset - 1) ;
for (i = 0 ; i < p->lhead->ConfigLength ; i++, dest--, src--)
*dest = *src ;
/* convert to big endian, compute crc, and write header */
nvr_write_Header(p) ;
/* spit out configuration data */
src = (PUCHAR)((ULONG)p->bhead + offset - 1) ;
for (i = 1 ; i <= p->lhead->ConfigLength ; i++, src--)
nvr_write (i+offset, *src) ;
}
/////////////////////////////////////////////////////////////////////////////
// USED_BY_HAL:
// ==
VOID
nvr_delete_object (
VOID
)
{
if ( !pnvrobj || pnvrobj != pnvrobj->self )
return ;
pnvrobj->self = NULL ;
nvr_free (pnvrobj) ;
pnvrobj = NULL ;
}
/////////////////////////////////////////////////////////////////////////////
// !=
PNVR_OBJECT
nvr_create_object (
VOID
)
{
ULONG i ;
PUCHAR cp ;
UCHAR pid ;
// Allocate (or just find) memory for the local NVR Object
pnvrobj = nvr_alloc (sizeof(NVR_OBJECT)) ;
if ( !pnvrobj )
return NULL ; // ERROR: couldn't get memory
// Zero out the object
for (i = 0, cp = (PUCHAR)pnvrobj ; i < sizeof(NVR_OBJECT) ; i++, cp++)
*cp = 0 ;
// initialize internal elements
pnvrobj->self = pnvrobj ;
pnvrobj->bhead = (HEADER *) pnvrobj->bend ;
pnvrobj->lhead = (HEADER *) pnvrobj->lend ;
return (pnvrobj) ;
}
// USED_BY_HAL:
// ==
STATUS_TYPE
nvr_initialize_object (
LONG AccessMethod,
ULONG Size // NVR size in bytes
)
// Set up everything required to be able to access and modify NVRAM.
// The parameters are "optional" (may be zero).
//
// If AccessMethod == 0, the actual value will be derived from the
// hardware (at this point the hardware test is reliable).
//
// If Size == 0, the program will attempt to determine the actual NVRAM
// size by a hardware test (at this point this is in-op). After the
// NVRAM is read successfully and the CRC checks, the value will be
// updated based on the value found inside NVRAM itself. If the caller
// furnishes no value AND a valid value is NOT found, clearing of NVRAM
// will be disabled.
//
// NOTE that this module must somehow know the Method in order to
// perform ANY accesses, but that Size is only used in error cases to
// destroy the entire NVRAM.
{
DEBUG_PRINT (1,"enter nvr_initialize_object\n") ;
if ( pnvrobj )
return stat_exist ; // object ALREADY initialized!
// create object or get static address
if ( !(pnvrobj = nvr_create_object()) )
return stat_error ;
NvrFillSize = Size ;
// Decide HOW to access NVRAM and set global variable
NvrSetMethod (AccessMethod) ;
// ASAP: figure how to handle an error properly. If zero returned from
// above, the endian indicator didn't show up and we probably can't
// read NVRAM at all. What to do, what to do??
NvrSetSize (Size) ;
// read the header from NVRAM and convert to little endian
nvr_read_nvram (pnvrobj) ;
nvr_print_object() ; // if debugging, print the values
return stat_ok ;
}
/////////////////////////////////////////////////////////////////////////////
// !=
VOID
nvr_clear_nvram (
VOID
)
// Set ALL of NVRAM to zeros: header, checksum, EVERYTHING! This is
// used to set default values in case a corrupted system is found, OR
// from the (user-initiated) nvr_destory() function.
{
ULONG i ;
for ( i = 0 ; i < NvrFillSize ; i++ )
nvr_write (i,0) ;
}
// ==
VOID
nvr_destroy (
LONG AccessMethod,
ULONG Size // size of NVRAM
)
// This is used as a debugging and recovery feature only. It is accessed
// via a secret back door in the FWBOOT.C module, and is never executed,
// other than manually by the user.
{
if ( !pnvrobj || pnvrobj != pnvrobj->self )
return ; // invalid pointer
nvr_delete_object () ; // delete in case corrupt
nvr_initialize_object (AccessMethod,Size) ; // get new copy
nvr_clear_nvram () ; // EMPTY THE PHYSICAL NVRAM
nvr_delete_object() ; // delete local copy
// re-read so local copy matches
nvr_initialize_object (AccessMethod,Size) ;
}
/////////////////////////////////////////////////////////////////////////////
// The CRC computation algorithm must match that used by the resident
// firmware (ROS). The algorithms below were obtained from the ESW Group
// that develops Dakota Firmware. Whenever there are changes in their
// algorithms, they must be changed here also.
/////////////////////////////////////////////////////////////////////////////
#define rol(x,y) ( ( ((x)<<(y)) | ((x)>>(16 - (y))) ) & 0x0FFFF)
#define ror(x,y) ( ( ((x)>>(y)) | ((x)<<(16 - (y))) ) & 0x0FFFF)
// !=
ULONG
nvr_computecrc (
ULONG oldcrc,
UCHAR data
)
{
ULONG pd, crc ;
pd = ((oldcrc>>8) ^ data) << 8 ;
crc = 0xFF00 & (oldcrc << 8) ;
crc |= pd >> 8 ;
crc ^= rol(pd,4) & 0xF00F ;
crc ^= ror(pd,3) & 0x1FE0 ;
crc ^= pd & 0xF000 ;
crc ^= ror(pd,7) & 0x01E0 ;
return crc ;
}
// !=
USHORT
nvr_calc1crc (
PNVR_OBJECT p
)
{
ULONG ul ;
ULONG i ;
PUCHAR cp ;
ULONG len1 ;
ULONG len2 ;
USHORT us ;
if ( !p || p != p->self )
return 0 ; // invalid pointer
ul = 0x0ffff ;
// do not include current Crc1/Crc2 in checksum
len1 = (sizeof(p->bhead->Size) +
sizeof(p->bhead->Version) +
sizeof(p->bhead->Revision)) ;
len2 = (ULONG) p->lhead->OSAreaAddress ;
// calculate the area before Crc1/Crc2 in the header
for (cp = (PUCHAR)p->bhead, i = 0 ; i < len1 ; i++)
ul = nvr_computecrc(ul, cp[i]) ;
// NOTE: this switch was required starting with NVR 1.4 as shipped on Delmar.
// It's unclear whether the change is really related to 1.4 format or
// to some other ROS change, but we're switching on 1.4 anyway. If a
// problem develops where the CRC of a new Machine or ROS goes bad,
// check this out early. Originally this switch was done at compile-
// time, and was labelled FIX_D852. Defining this name enabled the
// (now) post-1.3 code.
if ( p->bhead->Version <= 1 && p->bhead->Revision < 4 )
i += (sizeof(p->bhead->Crc1) + sizeof(p->bhead->Crc2)) + 1 ;
else
i += (sizeof(p->bhead->Crc1) + sizeof(p->bhead->Crc2)) ;
for (i = i ; i < len2 ; i++)
ul = nvr_computecrc(ul, cp[i]) ;
us = (USHORT)(ul & 0x0ffff) ;
return (us) ;
}
// !=
USHORT
nvr_calc2crc (
PNVR_OBJECT p
)
// Checksum the CONFIGURATION AREA ONLY.
{
ULONG ul ;
PUCHAR cp ;
PUCHAR end ;
if ( !p || p != p->self )
return 0xFFFF ; // invalid pointer
// Original version returned indeterminate value here !!
// ASAP: check with ESW for proper resolution!
// return ;
ul = 0xFFFF ;
// ASAP: revisit the calculation size. The first Wiltwick had a "gap"
// between the OS and CFG areas, and it may NOT have been checksummed.
cp = (PUCHAR)((ULONG)p->bhead + (ULONG)p->lhead->ConfigAddress) ;
#if 0
// end = (PUCHAR)((ULONG)p->bhead +
// (ULONG)(((ULONG)p->lhead->Size << 10) - 1)) ;
end = (PUCHAR)((ULONG)p->bhead +
(ULONG)(((ULONG)p->lhead->Size << 10) )) ;
#endif
//
// PLJ reverted to original code to avoid blowing checksum in config
// area on sandalfoot.
//
end = (PUCHAR)((ULONG)p->bhead +
(ULONG)(((ULONG)p->lhead->Size << 10) - 1)) ;
// end PLJ change
for ( ; cp < end ; cp++)
ul = nvr_computecrc(ul, *cp) ;
return ( (USHORT)(ul & 0xFFFF) ) ;
}
/////////////////////////////////////////////////////////////////////////////
// FUNCTIONS PUBLIC TO THE HIGHER LAYERS OF SOFTWARE
// The functions below operate on the little endian section of the
// data structure internal to this file. Little endian is the internal
// (volatile RAM) representation of the NVRAM contents. All access to
// NVRAM data (variables, etc) are performed on this internal
// representation. When necessary, the internal representation is
// loaded back into NVRAM.
/////////////////////////////////////////////////////////////////////////////
// ==
VOID
nvr_print_object (
VOID
)
// Called by SFENVIR.C after nvr_initialize()
{
PUCHAR cp ;
PUCHAR max ;
UCHAR tmp ;
PNVRAM_MAP mp ;
HEADER* hp ;
LONG i ;
CHAR buf[100] ; // buffer for string creation
if ( !pnvrobj || pnvrobj != pnvrobj->self )
return ; // invalid pointer
if ( DEBUG_GETLEVEL() < 1 )
return ;
mp = (PNVRAM_MAP) pnvrobj->lend ;
hp = pnvrobj->lhead ;
DEBUG_PRINT (1,"================= INTERNAL NVRAM DISPLAY ==================\n") ;
DEBUG_PRINT (1," Object Addr: 0x%08lx\n", (ULONG)pnvrobj->self) ;
DEBUG_PRINT (1," BE Header addr: 0x%08lx\n", (ULONG)pnvrobj->bhead) ;
DEBUG_PRINT (1," LE Header addr: 0x%08lx\n", (ULONG)pnvrobj->lhead) ;
DEBUG_PRINT (1,"=============== NVRAM LITTLE-ENDIAN DISPLAY ===============\n") ;
DEBUG_PRINT (1," Size: %dK, Version %d.%d CRC1=0x%04X CRC2=0x%04X Endian: '%c'\n",
(int)hp->Size,
(int)hp->Version, (int)hp->Revision,
(int)hp->Crc1,
(int)hp->Crc2,
hp->Endian
) ;
// Show the serial number
for ( i=0 ;
i < sizeof(hp->Security.Serial)
&& i < (sizeof(buf)-2)
&& (tmp=hp->Security.Serial[i]) ;
i++
)
buf[i] = tmp ;
buf[i] = '\0' ; // terminate the string
DEBUG_PRINT (1," Serial: '%s'\n",buf) ;
DEBUG_PRINT (1," ---- GEAddress: 0x%08lx GELength: 0x%08lx\n",
hp->GEAddress, hp->GELength) ;
cp = (PUCHAR)((ULONG)hp + (ULONG)hp->GEAddress) ;
max = (PUCHAR)((ULONG)cp + hp->GELength) ;
while ((*cp) && (cp < max))
{
DEBUG_PRINT (1," '%s'\n", cp) ;
cp += (strlen(cp) + 1) ;
}
DEBUG_PRINT (1," ---- OSAreaAddress: 0x%08lx OSAreaLength: 0x%08lx\n",
hp->OSAreaAddress, hp->OSAreaLength) ;
cp = (PUCHAR)((ULONG)hp + (ULONG)hp->OSAreaAddress) ;
max = (PUCHAR)((ULONG)cp + hp->OSAreaLength) ;
while ((*cp) && (cp < max))
{
DEBUG_PRINT (1," '%s'\n", cp) ;
cp += (strlen(cp) + 1) ;
}
DEBUG_PRINT (1," ---- ConfigAddress: 0x%08lx ConfigLength: 0x%08lx Count: 0x%08lx\n",
hp->ConfigAddress, hp->ConfigLength, hp->ConfigCount) ;
}
/////////////////////////////////////////////////////////////////////////////
// NOT YET USED; other finds will be written in terms of this one ASAP
// ==
STATUS_TYPE
nvr_find_variable (
PUCHAR VarName, // name of variable to find
PUCHAR ArrayAddr, // address of variable array
ULONG ArraySize, // max size of variable array
PULONG ni,
PULONG vi
)
{
PUCHAR lvar ;
PUCHAR cp ;
ULONG i ;
if ( !VarName || !(*VarName) || !ArrayAddr || !ArraySize )
return stat_error ; // bad input
i = 0 ;
while ( TRUE )
{
lvar = VarName ;
*ni = i ; // RETURN Name Index
cp = ArrayAddr ;
// does the variable we want start at this index?
while ( i < ArraySize )
{
/* break if mismatch */
if (_toupr_(cp[i]) != _toupr_(*lvar))
break ; // mismatch
lvar++, i++ ;
}
// if var name matches
if ( *lvar == 0 && cp[i] == '=' )
{
*vi = ++i ; // RETURN Value Index
return stat_ok ; // indicate FOUND
}
// no match - set index to start of the next variable
if ( i >= ArraySize )
return stat_error ;
while ( cp[i++] != 0 )
{
if ( i >= ArraySize )
return stat_error ;
}
}
}
// ==
STATUS_TYPE
nvr_set_variable (
PUCHAR VarName, // name of variable to add/change
PUCHAR VarValue, // value to be set into variable
PUCHAR ArrayAddr, // address of variable array
ULONG ArraySize // max size of variable array
)
{
PUCHAR lvar ;
PUCHAR cp ;
ULONG i ;
ULONG ni ;
ULONG vi ;
ULONG eos ;
PUCHAR str ;
ULONG count ;
CHAR c ;
if ( !VarName || !(*VarName) || !ArrayAddr || !ArraySize )
return stat_error ; // bad input
// MORE; NOT QUITE READY FOR PRIME TIME
// Convert to use pointers throughout instead of indexes (including
// calling convention). At some future time this function will be one
// of the basic blocks for dealing with NVRAM directly (no buffers),
// IF the hardware folk say it's OK.
// find the end of the used space by looking for
// the first non-null character from the top
eos = ArraySize - 1 ;
while ( ArrayAddr[--eos] == 0 )
{
if ( eos == 0 )
break ;
}
// position eos to the first new character, unless
// environment space is empty
if ( eos != 0 )
eos += 2 ;
count = ArraySize - eos ;
// find out if the variable already has a value
if ( nvr_find_variable(VarName,ArrayAddr,ArraySize,&ni,&vi) == stat_ok )
{
// The VarName already exists. See if there is room to
// substitute it with the new one.
// count free space
// start with the free area at the top and add
// the old ni value
for ( str = &(ArrayAddr[vi]) ; *str != 0 ; str++ )
count++ ;
// if free area is not large enough to handle new value,
// return an error
for ( str = VarValue ; *str != 0 ; str++ )
{
if ( count-- == 0 )
return stat_error ;
}
// pack strings
// first move vi to the end of the value
while ( ArrayAddr[vi++] != 0 )
;
// now move everything to where the variable starts
// covering up the old name/value pair
while ( vi < eos )
{
c = ArrayAddr[vi++] ;
ArrayAddr[ni++] = c ;
}
// adjust new top of environment
eos = ni ;
// zero to the end of OS area
while ( ni < ArraySize )
ArrayAddr[ni++] = 0 ;
}
else
{
// variable is new
// if free area is not large enough to handle new value return error
for ( str = VarValue ; *str != 0 ; str++ )
{
if ( count-- == 0 )
return stat_error ;
}
}
// At this point any existing variable by the name specified has been
// removed. If there is no new value to be added, we're done
if ( *VarValue )
{
// insert new name, converting to upper case.
while ( *VarName )
{
ArrayAddr[eos++] = *VarName++ ;
}
ArrayAddr[eos++] = '=' ;
// insert new value, leaving case alone
while ( *VarValue )
ArrayAddr[eos++] = *VarValue++ ;
}
return stat_ok;
}
/////////////////////////////////////////////////////////////////////////////
// ==
STATUS_TYPE
nvr_find_OS_variable (
PUCHAR var,
PULONG ni,
PULONG vi
)
{
PUCHAR cp ;
HEADER * lhp ;
if ( !pnvrobj || !var || !(*var) )
return stat_error ;
lhp = (HEADER*)pnvrobj->lhead ;
cp = (PUCHAR)((ULONG)lhp + (ULONG)(lhp->OSAreaAddress)) ;
return ( nvr_find_variable(var,cp,lhp->OSAreaLength,ni,vi) ) ;
}
// ==
STATUS_TYPE
nvr_find_GE_variable (
PUCHAR var,
PULONG ni,
PULONG vi
)
{
PUCHAR cp ;
HEADER * lhp ;
if ( !pnvrobj || !var || !(*var) )
return stat_error ;
lhp = (HEADER*)pnvrobj->lhead ;
cp = (PUCHAR)((ULONG)lhp + (ULONG)(lhp->GEAddress)) ;
return ( nvr_find_variable(var,cp,lhp->GELength,ni,vi) ) ;
}
// ==
PUCHAR
nvr_get_OS_variable (
PUCHAR vname
)
{
ULONG ni ;
ULONG vi ;
ULONG i ;
PNVRAM_MAP lep ;
PUCHAR array ;
if ( !pnvrobj || !vname || !(*vname) )
return NULL ;
if (nvr_find_OS_variable(vname, &ni, &vi) != stat_ok)
return NULL ;
lep = (PNVRAM_MAP)pnvrobj->lend ;
array = (PUCHAR)((ULONG)lep + (ULONG)(lep->Header.OSAreaAddress)) ;
for ( i = 0 ; i < MAXIMUM_ENVIRONMENT_VALUE - 1 ; i++ )
{
if ( array[vi] == 0 )
break ;
_currentstring[i] = array[vi++] ;
}
_currentstring[i] = 0 ;
return ( _currentstring ) ;
}
// USED_BY_HAL: also SFENVIR.C
// ==
PUCHAR
nvr_get_GE_variable (
PUCHAR vname
)
{
ULONG ni ;
ULONG vi ;
ULONG i ;
PUCHAR cp ;
HEADER* lhp ;
if ( !pnvrobj || !vname || !(*vname) )
return NULL ;
if (nvr_find_GE_variable(vname, &ni, &vi) != stat_ok)
return NULL ;
lhp = (HEADER*)pnvrobj->lhead ;
cp = (PUCHAR)((ULONG)lhp + (ULONG)lhp->GEAddress) ;
for (i = 0 ; i < MAXIMUM_ENVIRONMENT_VALUE - 1 ; i++)
{
if (cp[vi] == 0)
{
break ;
}
_currentstring[i] = cp[vi++] ;
}
_currentstring[i] = 0 ;
// DEBUG_PRINT (1,"get_GE vname: '%s' value: '%s'\n", vname, _currentstring);
return (_currentstring) ;
}
// ==
STATUS_TYPE
nvr_set_OS_variable (
PUCHAR vname,
PUCHAR value
)
{
ULONG nameindex ;
ULONG valueindex ;
ULONG eos ;
PUCHAR str ;
ULONG count ;
CHAR c ;
PUCHAR aptr ;
HEADER* lhp ;
if ( !pnvrobj || !vname || !value || !(*vname) )
return stat_error ;
lhp = (HEADER*)pnvrobj->lhead ;
// DEBUG_PRINT (1,"OS vname: '%s' value: '%s'\n", vname, value);
/* initialize pointer to OS area */
aptr = (PUCHAR)((ULONG)lhp + (ULONG)lhp->OSAreaAddress) ;
// find the end of the used OS space by looking for
// the first non-null character from the top
eos = lhp->OSAreaLength - 1 ;
while (aptr[--eos] == 0)
{
if (eos == 0)
break ;
}
// position eos to the first new character, unless
// environment space is empty
if (eos != 0)
eos += 2 ;
// find out if the variable already has a value
count = lhp->OSAreaLength - eos ;
if (nvr_find_OS_variable(vname, &nameindex, &valueindex) == stat_ok)
{
// count free space
// start with the free area at the top and add
// the old nameindex value
for (str = &(aptr[valueindex]) ; *str != 0 ; str++)
count++ ;
// if free area is not large enough to handle new value return error
for (str = value ; *str != 0 ; str++)
{
if ( count-- == 0 )
return stat_error ;
}
// pack strings
// first move valueindex to the end of the value
while (aptr[valueindex++] != 0)
;
// now move everything to where the variable starts
// covering up the old name/value pair
while (valueindex < eos)
{
c = aptr[valueindex++] ;
aptr[nameindex++] = c ;
}
// adjust new top of environment
eos = nameindex ;
// zero to the end of OS area
while (nameindex < lhp->OSAreaLength)
aptr[nameindex++] = 0 ;
}
else
{
// variable is new
// if free area is not large enough to handle new value return error
for (str = value ; *str != 0 ; str++)
{
if (count-- == 0)
return stat_error ;
}
}
/* if value is null, we have removed the variable */
if (*value)
{
// insert new name, converting to upper case.
while ( *vname )
{
aptr[eos++] = *vname++ ;
}
aptr[eos++] = '=' ;
// insert new value
while ( *value )
{
aptr[eos++] = *value ;
value++ ;
}
}
nvr_write_OSArea(pnvrobj) ;
return stat_ok ;
}
// USED_BY_HAL:
// ==
STATUS_TYPE
nvr_set_GE_variable (
PUCHAR vname,
PUCHAR value
)
{
ULONG nameindex ;
ULONG valueindex ;
ULONG toe ;
PUCHAR str ;
ULONG count ;
CHAR c ;
PUCHAR aptr ;
HEADER* lhp ;
if ( !pnvrobj || !vname || !(*vname) )
return stat_error ; // invalid input
lhp = (HEADER*)pnvrobj->lhead ;
DEBUG_PRINT (3,"set_GE vname: '%s' value: '%s'\n", vname, value) ;
/* initialize pointer to GE area */
aptr = (PUCHAR)((ULONG)lhp + (ULONG)lhp->GEAddress) ;
/* find the top of the used environment space by looking for */
/* the first non-null character from the top */
toe = lhp->GELength - 1 ;
aptr = (PUCHAR)((ULONG)lhp + (ULONG)lhp->GEAddress) ;
while (aptr[--toe] == 0)
{
if (toe == 0)
break ;
}
/* adjust toe to the first new character, unless */
/* environment space is empty */
if (toe != 0)
toe += 2 ;
/* find out if the variable already has a value */
count = lhp->GELength - toe ;
if (nvr_find_GE_variable(vname, &nameindex, &valueindex) == stat_ok)
{
/* count free space */
/* start with the free area at the top and add */
/* the old nameindex value */
for (str = &(aptr[valueindex]) ; *str != 0 ; str++)
count++ ;
/* if free area is not large enough to handle new value return error */
if (value)
{
for (str = value ; *str != 0 ; str++)
{
if (count-- == 0)
return stat_error ;
}
}
/* pack strings */
/* first move valueindex to the end of the value */
while (aptr[valueindex++] != 0)
;
/* now move everything to where the variable starts */
/* covering up the old name/value pair */
while (valueindex < toe)
{
c = aptr[valueindex++] ;
aptr[nameindex++] = c ;
}
/* adjust new top of environment */
toe = nameindex ;
/* zero to the end of GE area */
while (nameindex < lhp->GELength)
aptr[nameindex++] = 0 ;
}
else
{
/* variable is new */
/* if free area is not large enough to handle new value return error */
if (value)
{
for (str = value ; *str != 0 ; str++)
{
if (count-- == 0)
return stat_error ;
}
}
}
/* if value is null or is a pointer to a 0 */
/* the variable has been removed */
if ( value && *value )
{
/* insert new name, converting to upper case */
while ( *vname )
{
aptr[toe] = *vname++ ;
toe++ ;
}
aptr[toe++] = '=' ;
/* insert new value */
while ( *value )
{
aptr[toe] = *value ;
value++ ;
toe++ ;
}
}
nvr_write_GEArea(pnvrobj) ;
return stat_ok ;
}
// ==
PUCHAR
nvr_fetch_GE (
VOID
)
{
ULONG i ;
ULONG toe ;
PUCHAR aptr ;
HEADER* lhp ;
PNVRAM_MAP lep ;
if (!pnvrobj)
return NULL ;
lep = (PNVRAM_MAP) pnvrobj->lend ;
NvrCopyFill (pnvrobj->lend + (ULONG)lep->Header.GEAddress,
lep->Header.GELength) ;
return (_currentfetch) ;
}
// ==
ULONG
nvr_stat_GE (
PULONG size
)
{
ULONG i ;
ULONG toe ;
PUCHAR aptr ;
HEADER* lhp ;
ULONG free ;
if ( !pnvrobj )
return 0 ;
/* initialize pointers to GE area */
lhp = (HEADER*) pnvrobj->lhead ;
aptr = (PUCHAR) ((ULONG)lhp + (ULONG)lhp->GEAddress) ;
/* return original size to caller */
if (size)
*size = lhp->GELength ;
/* find the top of the used environment space by looking for */
/* the first non-null character from the top */
toe = lhp->GELength - 1 ;
free = 0 ;
while ((aptr[--toe]) == 0)
{
free++ ;
if (toe == 0)
break ;
}
return ( free ) ;
}
// ==
PUCHAR
nvr_fetch_OS (
VOID
)
{
ULONG i ;
ULONG toe ;
PNVRAM_MAP lep ;
if ( !pnvrobj )
return NULL ;
lep = (PNVRAM_MAP) pnvrobj->lend ;
NvrCopyFill (pnvrobj->lend + (ULONG)lep->Header.OSAreaAddress,
lep->Header.OSAreaLength) ;
return ( _currentfetch ) ;
}
// ==
PUCHAR
nvr_fetch_CF (
VOID
)
{
ULONG i ;
PNVRAM_MAP lep ; // LE ptr to NVRAM volatile image
if ( !pnvrobj )
return NULL ;
lep = (PNVRAM_MAP) pnvrobj->lend ;
NvrCopyFill (pnvrobj->lend + (ULONG)lep->Header.ConfigAddress,
lep->Header.ConfigLength) ;
return ( _currentfetch ) ;
}
// ==
VOID
NvrCopyFill (
PVOID src,
ULONG srclen
)
// Fill the _currentfetch area from the source described, then fill the
// remainder of the buffer with zeros. The same buffer is used to pass
// several areas, and it is the callers responsibility to copy each one
// if required before fetching another area.
{
PUCHAR srcp = (PUCHAR)src ;
PUCHAR dstp = _currentfetch ;
ULONG dstlen = MAXNVRFETCH ;
while ( dstlen-- )
{
if ( srclen )
*dstp++ = *srcp++, srclen-- ;
else
*dstp++ = 0 ;
}
}
#ifdef _HALNVR_
/////////////////////////////////////////////////////////////////////////////
// Resolve references to debug functions provided by ARC that are not part
// of the HAL environment. Currently these merely disable the debugging
// features when used in the HAL, but could be expanded later to provide
// the same features available in ARC.
/////////////////////////////////////////////////////////////////////////////
VOID
DEBUG_PRINT (
LONG DebugLevelReqd,
PCHAR DebugMessage,
...
)
{
}
VOID
DEBUG_BREAK (
LONG DebugLevelReqd
)
{
}
LONG
DEBUG_GETLEVEL ()
{
return 0 ;
}
LONG
DEBUG_GETREGION ()
{
return 0 ;
}
#endif