////////////////////////////////////////////////////////////////////////// ++ // // 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