diff options
author | raywu <raywu0301@gmail.com> | 2018-06-15 00:00:50 +0800 |
---|---|---|
committer | raywu <raywu0301@gmail.com> | 2018-06-15 00:00:50 +0800 |
commit | b7c51c9cf4864df6aabb99a1ae843becd577237c (patch) | |
tree | eebe9b0d0ca03062955223097e57da84dd618b9a /Core/EM/CmosManager/CmosAccess.c | |
download | zprj-master.tar.xz |
Diffstat (limited to 'Core/EM/CmosManager/CmosAccess.c')
-rw-r--r-- | Core/EM/CmosManager/CmosAccess.c | 2252 |
1 files changed, 2252 insertions, 0 deletions
diff --git a/Core/EM/CmosManager/CmosAccess.c b/Core/EM/CmosManager/CmosAccess.c new file mode 100644 index 0000000..d3dea31 --- /dev/null +++ b/Core/EM/CmosManager/CmosAccess.c @@ -0,0 +1,2252 @@ +//************************************************************************* +//************************************************************************* +//** ** +//** (C)Copyright 1985-2009, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 ** +//** ** +//** Phone: (770)-246-8600 ** +//** ** +//************************************************************************* +//************************************************************************* + +//********************************************************************** +// $Header: /Alaska/SOURCE/Modules/CMOS Manager/CMOS Core/CMOS Source/CmosAccess.c 25 6/15/10 2:24p Michaela $ +// +// $Revision: 25 $ +// +// $Date: 6/15/10 2:24p $ +//********************************************************************** +// Revision History +// ---------------- +// $Log: /Alaska/SOURCE/Modules/CMOS Manager/CMOS Core/CMOS Source/CmosAccess.c $ +// +// 25 6/15/10 2:24p Michaela +// +// 24 3/10/10 4:39p Michaela +// +// 23 3/08/10 5:39p Michaela +// +// 22 3/08/10 1:31p Michaela +// +// 21 3/08/10 1:09p Michaela +// +// 20 3/05/10 4:55p Michaela +// +// 19 12/04/09 7:32p Michaela +// +// 18 12/03/09 6:42p Michaela +// 1. Updated CmosReadWrite() to use physical access to +// non-checksummed locations within the Managed Region +// only if CMOS_RECOVER_ONLY_CHECKSUMMED is TRUE. +// (controlled via CMOS_MGR_RECOVER_ONLY_CHECKUMMED +// SDL token) +// +// 2. Added CanClearLegacyStatusBits() to determine +// whether or not the legacy status bits can be +// clear to a non-failure status. +// +// 3. SynchronizeLegacyStatusRegisters() is updated to +// only clear the bad checksum failure status in +// the legacy status registers in the DXE phase +// or on the first boot after flashing the firmware. +// +// 17 11/10/09 9:14p Michaela +// +// 16 7/29/09 9:59a Michaela +// updates Aptio Enhancement EIP 22205 +// (no code changes) +// +// 15 7/23/09 1:30p Michaela +// Rename ReadRtcIndex and WriteRtcIndex +// +// 14 6/15/09 5:11p Michaela +// +// 13 6/02/09 3:27p Michaela +// For label: 4.6.3_CMOSMGR_11 +// +// 12 2/23/09 6:03p Michaela +// --code clean-up in CmosBankReadWrite() +// --added test code to support test module it it is present +// +// 11 11/25/08 3:20p Michaela +// Updates for Label 4.6.3_CMOSMGR_08 +// - Assembly macro fixes +// - Added assembly macros +// - Moved loading defaults into DXE phase +// - Updated help file example +// +// 10 11/17/08 4:40p Michaela +// --Removed Token Name strings in debug development code +// +// 9 11/17/08 3:39p Michaela +// --Removed debug development code +// --CMOS Buffer feature is depreciated +// --ReadCmosStatusBytes and UpdateBatteryStatus now directly +// calls CmosPhysicalReadWrite to avoid error when the +// Optimal Defaults table is being used for reads & writes +// +// 8 11/14/08 9:12a Michaela +// **CMOS register variables changed from UINT8 to UINT16 +// **added global CMOS_PORT_MAP array (gCmosBank) for +// supporting board-specific access functions. The array is defined +// using the CMOS_PORT_MAPPING Elink. The array starts at +// index 1. +// **Added CmosBankReadWrite() for calling the board-specific +// access functions. +// **Modified CmosPhysicalReadWrite() to call CmosBankReadWrite() +// for physical CMOS access +// +// 7 11/07/08 5:13p Michaela +// Updated to make CMOS manager available in all phases +// of the boot process: +// +// A CMOS API Pointer is maintained in CMOS and accessible +// via provided macros in C and assembly source. +// +// 6 3/25/08 3:04p Michaela +// --deleted UpdateLegacyChecksumStatus +// --Modified SynchronizeLegacyStatusRegisters to +// update both battery and checksum status bits +// +// 5 3/07/08 4:07p Michaela +// Label 4.6.3_CMOSMGR_05 fixes: +// -- write errors to Optimal Defaults buffer before memory detection +// -- CMOS Token table corruption when name strings are disabled +// +// 4 2/29/08 9:35p Michaela +// - Added recovery path policy +// - fixed other minor bugs +// +// 3 2/26/08 12:49p Michaela +// Added/modified Helpbuilder headers +// +// 2 2/22/08 3:20p Olegi +// Renamed some of the equates to avoid the naming collisions. +// +// 1 2/22/08 2:29p Michaela +// +// 1 2/04/08 6:00p MichaelA +// Created +// +//********************************************************************** + +//<AMI_FHDR_START> +//--------------------------------------------------------------------------- +// +// Name: CmosAccess.c +// +// Description: Contains the routines that constitute the CMOS access +// implementation. This file contains source code that is used +// for both PEI and DXE phases. +// +//--------------------------------------------------------------------------- +//<AMI_FHDR_END> + +#include <Efi.h> +#ifdef PEI_COMPILE + #include <Pei.h> + #include <AmiPeiLib.h> +#else + #include <AmiDxeLib.h> +#endif +#include <CmosAccess.h> +#include <SspTokens.h> +#include "CmosManager.h" +#include "CmosManagerHob.h" +#include "CmosBoard.h" + +//--------------------------------------------------------------------------- +// CmosBank is initialized using the CMOS_PORT_MAPPING SDL Elink. +// This array is used to determine which index/data port pair to use or +// which board-specific access function to call for a specific CMOS register +// address. The first element is not used and contains all zeros. +//--------------------------------------------------------------------------- + +CMOS_PORT_MAP gCmosBank[] = { {0,0,0,0,0}, CMOS_PORT_MAPPING }; +UINT16 gCmosBankCount = \ + sizeof(gCmosBank) / sizeof(CMOS_PORT_MAP); + +EFI_CMOS_BATTERY_TEST gCmosBatteryIsGood = CMOS_BATTERY_TEST_MAPPING; + +//--------------------------------------------------------------------------- +// Function(s) originally defined in CmosManager.c +//--------------------------------------------------------------------------- +extern EFI_CMOS_MANAGER_INTERFACE *GetCmosMangerInterface( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos ); + + +//--------------------------------------------------------------------------- +// Function(s) originally defined in CmosBuffer.c or CmosPhysical.c +// depending on the sdl token BUFFERED_CMOS_SUPPORT +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Function declarations for this file +//--------------------------------------------------------------------------- +EFI_STATUS CalculateUpdatedCheckSum( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN UINT16 CmosAddress, + IN UINT8 OldCmosValue, + IN UINT8 NewCmosValue, + OUT UINT16 *NewChecksum ); + +EFI_STATUS CalculateChecksum( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + OUT UINT16 *ChecksumValue ); + +EFI_STATUS WriteChecksum( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + OUT UINT16 ChecksumValue ); + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EncodeParameter +// +// Description: +// Value is shifted left by the bit field size specified in Token. +// +// Input: +// OUT UINT8 *Value +// -- Address of value to be encoded +// IN CMOS_TOKEN *Token +// -- Token describing the bit field size +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID EncodeParameter( + OUT UINT8 *Value, + IN CMOS_TOKEN *Token ) +{ + *Value <<= Token->Value.Field.BitOffset; + +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: DecodeParameter +// +// Description: +// Value is shifted right and masked by the bit field's size and offset +// as specified in Token. +// +// Input: +// OUT UINT8 *Value +// -- Address of value to be decoded +// IN CMOS_TOKEN *Token +// -- Token describing the bit field's size and offset +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID DecodeParameter( + OUT UINT8 *Value, + IN CMOS_TOKEN *Token ) +{ + *Value >>= Token->Value.Field.BitOffset; + *Value &= (0xff >> (8 - Token->Value.Field.Size)); + +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: BitFieldOverflow +// +// Description: +// For testing whether or not a Value overflows the field size +// as specified in Token. +// +// Input: +// IN UINT8 *Value +// -- Value to be tested +// IN CMOS_TOKEN *Token +// -- Token describing the bit field's size +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +BOOLEAN BitFieldOverflow( + IN UINT8 *Value, + IN CMOS_TOKEN *Token ) +{ + if ( *Value & ~(0xff >> (8 - Token->Value.Field.Size)) ) + return TRUE; + else + return FALSE; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: SetClobberBits +// +// Description: +// Creates a mask with bits set corresponding to the size and offset +// of the bit field as specified by Token. +// +// Input: +// OUT UINT8 *Mask +// -- Mask to be created +// IN CMOS_TOKEN *Token +// -- Token describing the bit field's size and offset +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID SetClobberBits( + OUT UINT8 *Mask, + IN CMOS_TOKEN *Token ) +{ + *Mask = 0xff >> (8 - Token->Value.Field.Size); + *Mask <<= Token->Value.Field.BitOffset; +} + + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosFindToken +// +// Description: +// Returns index of CMOS token in token table or 0 if not found. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- This is the CMOS Manager interface pointer. +// IN UINT16 CmosToken +// -- Encoded CMOS token for which to locate an index into +// the CMOS token table +// +// Output: +// UINT16 (Return Value) +// = Index of the CmosToken in the token table, or +// = 0, if the CmosToken was not found +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 CmosFindToken( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN UINT16 CmosToken ) +{ + UINT16 i = 0; // First valid token table index is 1 + + while (++i < Manager->TokenCount) { + if (Manager->TokenTable[i].Value.AllBits == CmosToken) { + break; + } + } + + if (i < Manager->TokenCount) + return i; + + return 0; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosGetTokenFromRegister +// +// Description: +// Returns encoded token for the specified register. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- This is the CMOS Manager interface pointer. +// IN UINT8 CmosRegister +// -- CMOS register to for which to search +// +// Output: +// UINT16 (Return Value) +// = Token found in the token table, or +// = 0, if the Register was not found +// +// Notes: +// +// Caller must ensure the register has been reserved properly +// in SSP. +// +// This should be considered a risky function call and extra care +// should be taken to ensure the proper CmosRegister is specified. +// +// This function has the potential for problems, as it is possible that +// a specified CmosRegister has not been defined in SSP and/or may +// be used by another CmosToken. +// +// All verification of parameters must be done by the caller. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 CmosGetTokenFromRegister( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos, + IN UINT16 CmosRegister ) +{ + UINT16 i = 0; + EFI_CMOS_MANAGER_INTERFACE *Manager = GetCmosMangerInterface(Cmos); + + // Manager->TokenCount is the total number of entries in + // Manager->TokenTable where the first entry is zero. + + while (++i < Manager->TokenCount) { + if (Manager->TokenTable[i].Value.Field.CmosAddress == CmosRegister){ + break; + } + } + + // Minimal error checking is to ensure it is an 8-bit CMOS Token + if (i < Manager->TokenCount ){ + if (Manager->TokenTable[i].Value.Field.Size == 8) + return Manager->TokenTable[i].Value.AllBits; + } + + // a valid token is greater than 0x1000 + return 0; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosBankReadWrite +// +// Description: +// This function determines which structure in the global CMOS_PORT_MAP +// array corresponds to the provided CmosAddress and either calls the +// associated function or uses the associated IO ports to read/write the +// CMOS register value to/from the CmosParameterValue. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Pointer to the CMOS Manager internal interface +// IN CMOS_ACCESS_TYPE AccessType +// -- WriteType or ReadType +// IN UINT16 CmosAddress +// -- CMOS register to for which the access is to be made +// IN/OUT UINT8 *CmosParameterValue +// -- This is the value to write for write access or the +// value that was read for read access. +// +// +// Output: +// EFI_STATUS (Return Value) +// EFI_SUCCESS or valid EFI error code +// +// Notes: +// N/A +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosBankReadWrite( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN CMOS_ACCESS_TYPE AccessType, + IN UINT16 CmosAddress, + IN OUT UINT8 *CmosParameterValue ) +{ + UINT8 i = 1; + EFI_STATUS Status = EFI_NOT_FOUND; + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); // defines NULL if DXE + + // scan the CMOS_PORT_MAP array to determine how to access this address + for (; i < gCmosBankCount; i++) + { + if ( (CmosAddress >= gCmosBank[i].Low) + && (CmosAddress <= gCmosBank[i].High) ) + { + + // First, attempt to call the board-specific function if available + if (gCmosBank[i].BoardReadWrite != NULL){ + Status = gCmosBank[i].BoardReadWrite( PeiServices, + AccessType, + CmosAddress, + CmosParameterValue ); + break; + } + + // Otherwise, attempt to use the provided index/data ports + // if available (assuming 8 bit port access) + else if ( (gCmosBank[i].Index > 0) && (gCmosBank[i].Data > 0) ){ + IoWrite8( gCmosBank[i].Index, (UINT8)CmosAddress ); + if (AccessType == ReadType){ + *CmosParameterValue = IoRead8( gCmosBank[i].Data ); + } + else { + IoWrite8( gCmosBank[i].Data, *CmosParameterValue ); + } + Status = EFI_SUCCESS; + break; + } + + } + } + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosPhysicalReadWrite +// +// Description: +// This function provides support for performing the actual +// read or write from/to a physical CMOS location. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manager interface pointer +// IN CMOS_ACCESS_TYPE AccessType +// -- Specifies whether to perform a read or a write +// IN UINT8 CmosAddress +// -- Actual CMOS address/offset +// IN UINT8 BitsToWrite +// -- Mask specifying the bits to be written (these +// bits will be cleared first) +// IN OUT UINT8 *CmosParameterValue +// -- CMOS value to be written or, on successful exit, will +// contain the value that was read +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if successful +// = or other valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosPhysicalReadWrite( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN CMOS_ACCESS_TYPE AccessType, + IN UINT16 CmosAddress, + IN UINT8 BitsToWrite, + IN OUT UINT8 *CmosParameterValue ) +{ + UINT8 CmosRegisterValue; + UINT8 NewValue; + UINT16 NewChecksum = 0; + EFI_STATUS Status = EFI_SUCCESS; + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); + + // Always read the value + CmosBankReadWrite( Manager, + ReadType, + CmosAddress, + &CmosRegisterValue ); + + // If read access, simply return the unmodified data + if ( AccessType == ReadType ) { + CMOS_TRACE_FULL(( CMOS_TRACE_ALWAYS, + " Physical Read: Register 0x%02X = 0x%02X\n", + CmosAddress, CmosRegisterValue)); + *CmosParameterValue = CmosRegisterValue; + } + + // If writing, then don't clobber unused bits + else { + NewValue = (CmosRegisterValue & ~BitsToWrite) | *CmosParameterValue; + + // get the new checksum before writing + if (!Manager->CheckStatus(Manager, CMOS_FORCE_NO_CHECKSUM)) + Status = CalculateUpdatedCheckSum( Manager, CmosAddress, + CmosRegisterValue, NewValue, &NewChecksum); + + // write the value + CMOS_TRACE_FULL(( CMOS_TRACE_ALWAYS, + " Physical Write: Register 0x%02X = 0x%02X\n", + CmosAddress, NewValue)); + CmosBankReadWrite( Manager, + WriteType, + CmosAddress, + &NewValue ); + + // write the checksum, if necessary + // (this write will cause an infinite loop without a flag) + if (!Manager->CheckStatus(Manager, CMOS_FORCE_NO_CHECKSUM)){ + if (Manager->CheckStatus(Manager, CMOS_ADDRESS_IS_CHECKSUMMED)){ + Manager->SetStatus(Manager, CMOS_FORCE_NO_CHECKSUM); + WriteChecksum( Manager, NewChecksum ); + Manager->ClearStatus(Manager, CMOS_FORCE_NO_CHECKSUM); + } + } + } + + return EFI_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: NotChecksummed +// +// Description: +// This function returns TRUE if the CmosAddress is NOT included in the +// checksum and returns FALSE otherwise. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manager interface pointer +// IN UINT8 CmosAddress +// -- Actual CMOS address/offset +// +// Output: +// BOOLEAN (Return Value) +// = TRUE if CmosAddress is NOT included in the checksum +// = FALSE if CmosAddress IS included in the checksum +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +BOOLEAN NotChecksummed( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + IN UINT16 CmosAddress ) +{ + UINT16 NoChecksumIndex = 1; // NoChecksumTable starts at 1 + + while ( (NoChecksumIndex < Mgr->NoChecksumCount) + && (CmosAddress > Mgr->NoChecksumTable[NoChecksumIndex].Index) ) + { + ++NoChecksumIndex; + } + + if ( CmosAddress == Mgr->NoChecksumTable[NoChecksumIndex].Index ) + return TRUE; + else + return FALSE; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosBufferReadWrite +// +// Description: +// This function provides support for performing the actual +// read or write from/to a CMOS buffer location (using the +// Optimal Defaults Table Buffer). +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manager interface pointer +// IN CMOS_ACCESS_TYPE AccessType +// -- Specifies whether to perform a read or a write +// IN UINT8 CmosAddress +// -- Actual CMOS address/offset +// IN UINT8 BitsToWrite +// -- Mask specifying the bits to be written (these +// bits will be cleared first) +// IN OUT UINT8 *CmosParameterValue +// -- CMOS value to be written or, on successful exit, will +// contain the value that was read +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if successful +// = or other valid EFI error code +// +// Notes: +// +// Control comes here because of one of two reasons: +// +// 1) The checksum is bad +// +// All writes/read are to/from the +// Manager->OptimalDefaultTable only for locations not found +// in the Manager->NoChecksumTable until DXE phase. +// +// 2) The battery is bad +// +// All writes/read are to/from the +// Manager->OptimalDefaultTable for all locations. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosBufferReadWrite( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN CMOS_ACCESS_TYPE AccessType, + IN UINT16 CmosAddress, + IN UINT8 BitsToWrite, + IN OUT UINT8 *CmosParameterValue ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINT8 NewValue; + UINT16 NewChecksum = 0; + UINT8 CmosRegisterValue; + // First CMOS register @ buffer index 1 + UINT16 BufferIndex = CmosAddress - FIRST_CMOS_REGISTER + 1; + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); + + if (CmosAddress < FIRST_CMOS_REGISTER || CmosAddress > LAST_CMOS_REGISTER){ + CMOS_TRACE((CMOS_TRACE_ALWAYS, + " CmosBufferReadWrite: Invalid CmosAddress (0x%X)\n", + CmosAddress )); + return EFI_INVALID_PARAMETER; + } + + // get the default value from the table + CmosRegisterValue = Manager->OptimalDefaultTable[BufferIndex].Value; + + // If read access, simply return the unmodified data + if ( AccessType == ReadType ) { + *CmosParameterValue = CmosRegisterValue; + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " Buffer Read: 0x%X = OptimalDefaultTable[%d].Value \n", + CmosRegisterValue, BufferIndex )); + } + + // If writing, mark the location as dirty and don't clobber unused bits + else { + NewValue = (CmosRegisterValue & ~BitsToWrite) | *CmosParameterValue; + + // get the new checksum before writing (this sets or clears + // the CMOS_ADDRESS_IS_CHECKSUMMED Manager->ManagerStatus flag) + if (!Manager->CheckStatus(Manager, CMOS_FORCE_NO_CHECKSUM)) { + Status = CalculateUpdatedCheckSum( Manager, CmosAddress, + CmosRegisterValue, NewValue, &NewChecksum); + } + + // Optimal default table writes take precedence + Manager->OptimalDefaultTable[BufferIndex].Value = NewValue; + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " Buffer Write: OptimalDefaultTable[%d].Value = 0x%X\n", + BufferIndex, NewValue )); + + // write the checksum, if necessary + // (this write will cause an infinite loop without a flag) + if (!Manager->CheckStatus(Manager, CMOS_FORCE_NO_CHECKSUM)){ + if (Manager->CheckStatus(Manager, CMOS_ADDRESS_IS_CHECKSUMMED)){ + Manager->SetStatus(Manager, CMOS_FORCE_NO_CHECKSUM); + WriteChecksum( Manager, NewChecksum ); + Manager->ClearStatus(Manager, CMOS_FORCE_NO_CHECKSUM); + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " NewChecksum = 0x%X\n", + NewChecksum )); + } + } + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosReadWrite +// +// Description: +// Decides which version of xxxReadWrite to call based upon the +// current Access Routing Logic. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manager interface pointer +// IN CMOS_ACCESS_TYPE AccessType +// -- Specifies whether to perform a read or a write +// IN UINT8 CmosAddress +// -- Actual CMOS address/offset +// IN UINT8 BitsToWrite +// -- Mask specifying the bits to be written (these +// bits will be cleared first) +// IN OUT UINT8 *CmosParameter +// -- CMOS value to be written or, on successful exit, will +// contain the value that was read +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosReadWrite( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + IN CMOS_ACCESS_TYPE AccessType, + IN UINT16 CmosAddress, + IN UINT8 BitsToWrite, + IN OUT UINT8 *CmosParameter ) +{ + BOOLEAN UseBufferAccess = FALSE; + + // If the checksum is bad or the battery is bad, accesses will be + // routed to the optimal default table by default. + + if ( Mgr->CheckStatus( Mgr, CMOS_OPTIMAL_DEFAULTS_ENABLED )){ + UseBufferAccess = TRUE; + + // Force physical access if these conditions are applicable: + // + // 1. the CMOS is usable, + // 2. recovery is limited to only checksummed locations + // 3. the current location is not checksummed + + if ( Mgr->CheckStatus( Mgr, CMOS_IS_USABLE ) + && Mgr->CheckStatus( Mgr, CMOS_RECOVER_ONLY_CHECKSUMMED ) + && NotChecksummed( Mgr, CmosAddress ) ) + { + UseBufferAccess = FALSE; + } + } + + if (UseBufferAccess) + return CmosBufferReadWrite( Mgr, + AccessType, + CmosAddress, + BitsToWrite, + CmosParameter); + else + return CmosPhysicalReadWrite( Mgr, + AccessType, + CmosAddress, + BitsToWrite, + CmosParameter); +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosReadWriteEncodedToken +// +// Description: +// This is the main worker function which verifies and either +// reads/writes a value from/to the CMOS location as specified by the +// encoded token. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- This is the CMOS Manager interface pointer. +// IN CMOS_ACCESS_TYPE AccessType +// -- Specifies whether to perform a read or a write +// IN UINT16 TokenTableIndex +// -- Index into the token table +// IN OUT UINT8 *CmosParameter +// -- CMOS value to be written or, on successful exit, will +// contain the value that was read +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if successful +// = EFI_INVALID_PARAMETER, if the CmosParameter does +// not comply with the expected size +// = or other valid EFI error code +// +// Notes: +// 1) Gets the CMOS token from the global table +// 2) If writing, the value is error-checked for size and +// up-shifted to the correct bit position. Also, the +// BitsToWrite mask is used to specify +// which bits to clear in the destination before +// writing. +// 3) This token is decoded to get the CMOS index, +// 4) The read/write is performed +// 5) If reading, the value is down-shifted and masked to +// return the expected value. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosReadWriteEncodedToken( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN CMOS_ACCESS_TYPE AccessType, + IN UINT16 TokenTableIndex, + IN OUT UINT8 *CmosParameter ) +{ + CMOS_TOKEN *CmosToken = &Manager->TokenTable[TokenTableIndex]; + UINT8 BitsToWrite = 0; + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); + + // If writing, encode the CmosParameter + + if (AccessType == WriteType) { + + // Check to make sure the data is the correct size for the bit field + + if ( BitFieldOverflow(CmosParameter, CmosToken) ){ + return EFI_INVALID_PARAMETER; + } + + // Shift up to the correct offset within the byte + + EncodeParameter(CmosParameter, CmosToken); + + // Set the bits in BitsToWrite mask that are + // consumed by this token + + SetClobberBits(&BitsToWrite, CmosToken); + } + + // Read (or write) the CmosParameter to actual (or CMOS buffer) location + + CmosReadWrite(Manager, + AccessType, + (UINT16)CmosToken->Value.Field.CmosAddress, // byte address + BitsToWrite, + CmosParameter); + + // If reading, shift down to bit-position zero and mask off upper bits + + if (AccessType == ReadType) + DecodeParameter(CmosParameter, CmosToken); + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosRead +// +// Description: +// Reads to CmosValue from the CMOS location specified by CmosToken. +// +// Input: +// IN EFI_CMOS_ACCESS_INTERFACE *Cmos +// -- This is the access interface pointer. +// IN UINT16 CmosToken +// -- This is the encoded CMOS location. +// OUT UINT8 *CmosValue +// -- On success, this will contain the value at +// the specified CMOS location. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if successful +// = EFI_NOT_FOUND, if the token is not in the token table +// = or other valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosRead( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos, + IN UINT16 CmosToken, + OUT UINT8 *CmosValue ) +{ + EFI_STATUS Status = EFI_NOT_FOUND; + UINT16 TokenTableIndex = 0; + EFI_CMOS_MANAGER_INTERFACE *Mgr = GetCmosMangerInterface(Cmos); + DEFINE_PEI_SERVICES(Cmos->PeiServices); + + // if CmosToken <= 0xe, then always read the register. These registers + // will be readable regardless of the state of the battery. + + if (CmosToken <= 0xe){ + Status = CmosBankReadWrite( Mgr, ReadType, CmosToken, CmosValue ); + } + + // Otherwise, if CmosToken < MIN_TOKEN_VALUE and the battery is good, + // then directly read the register. (For a bad battery, return + // EFI_DEVICE_ERROR.) + + else if (CmosToken < MIN_TOKEN_VALUE) { + if ( !Mgr->CheckStatus( Mgr, CMOS_IS_USABLE ) ) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = CmosBankReadWrite( Mgr, ReadType, CmosToken, CmosValue ); + } + } + + // Otherwise, after validating the CmosToken, decode it to read the + // associated register bit(s). + + else { + TokenTableIndex = CmosFindToken(Mgr, CmosToken); + if (TokenTableIndex > 0) { + Status = CmosReadWriteEncodedToken( Mgr, ReadType, + TokenTableIndex, CmosValue); + } + else + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: ReadCmosBytes +// +// Description: +// Reads Bytes from the specified CMOS register(s). +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manger interface pointer. +// IN UINT8 Count +// -- Count of CMOS_BYTEs to read +// OUT CMOS_BYTE *Bytes +// -- An array of CMOS_BYTE structures +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS ReadCmosBytes( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN UINT16 Count, + OUT CMOS_BYTE *CmosBytes ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINT16 i = 0; + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); + + + for (; i < Count; i++){ + Status = CmosReadWrite(Manager, ReadType, CmosBytes[i].Register, 0, + &CmosBytes[i].Value); + } + + return EFI_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: ReadCmosStatusBytes +// +// Description: +// Initializes a CMOS_STATUS_BYTES structure and reads the associated +// CMOS registers. +// +// Input: +// IN EFI_CMOS_ACCESS_INTERFACE *Cmos +// -- Manger interface pointer. +// OUT CMOS_STATUS_BYTES *StatusBytes +// -- Address of a valid, uninitialized, CMOS_STATUS_BYTES +// structure. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS ReadCmosStatusBytes( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos, + OUT CMOS_STATUS_BYTES *StatusBytes ) +{ + EFI_STATUS Status = EFI_SUCCESS; + EFI_CMOS_MANAGER_INTERFACE *Mgr = GetCmosMangerInterface(Cmos); + UINT8 CmosValue; + DEFINE_PEI_SERVICES(Mgr->Access.PeiServices); + + if (StatusBytes == NULL) + return Status = EFI_INVALID_PARAMETER; + + StatusBytes->DiagnosticStatusRegister = CMOS_DIAGNOSTIC_STATUS_REGISTER; + StatusBytes->BatteryStatusRegister = CMOS_RTC_STATUS_REGISTER; + + CmosPhysicalReadWrite( Mgr, + ReadType, + StatusBytes->BatteryStatusRegister, + 0, + &StatusBytes->Battery.AllBits ); + + CmosPhysicalReadWrite( Mgr, + ReadType, + StatusBytes->DiagnosticStatusRegister, + 0, + &StatusBytes->Diagnostic.AllBits ); + + if ( Mgr->CheckStatus(Mgr, CMOS_OPTIMAL_DEFAULTS_ENABLED) ) + StatusBytes->ConfigurationStatus.IsVirtualized = TRUE; + else + StatusBytes->ConfigurationStatus.IsVirtualized = FALSE; + + // If the CMOS hardware is usable, update from cmos-based + // status bits, which are more reliable. Otherwise, use the + // the memory-based status. + // + // Note, some information could be lost on reset in DXE + // when using the memory-based status. + // + // Also, register 0xe is cleared in DXE, if the CMOS + // is usable and the checksum has been recalculated. + + StatusBytes->ConfigurationStatus.BadBattery = FALSE; + StatusBytes->ConfigurationStatus.DefaultsLoaded = FALSE; + StatusBytes->ConfigurationStatus.IsFirstBoot = FALSE; + StatusBytes->ConfigurationStatus.BadChecksum = FALSE; + + if ( Mgr->CheckStatus(Mgr, CMOS_IS_USABLE) ) { + StatusBytes->ConfigurationStatus.NotUsable = FALSE; + + Cmos->Read( Cmos, CMOS_MGR_BATTERY_BAD, &CmosValue ); + StatusBytes->ConfigurationStatus.BadBattery |= CmosValue; + + Cmos->Read( Cmos, CMOS_MGR_DEFAULTS_LOADED, &CmosValue ); + StatusBytes->ConfigurationStatus.DefaultsLoaded |= CmosValue; + + Cmos->Read( Cmos, CMOS_MGR_FIRST_BOOT_DETECTED, &CmosValue ); + StatusBytes->ConfigurationStatus.IsFirstBoot |= CmosValue; + + Cmos->Read( Cmos, CMOS_MGR_CHECKSUM_BAD,&CmosValue ); + StatusBytes->ConfigurationStatus.BadChecksum |= CmosValue; + } + else { + StatusBytes->ConfigurationStatus.NotUsable = TRUE; + if ( Mgr->CheckStatus(Mgr, CMOS_BAD_BATTERY) ) + StatusBytes->ConfigurationStatus.BadBattery = TRUE; + if ( Mgr->CheckStatus(Mgr, CMOS_DEFAULTS_LOADED) ) + StatusBytes->ConfigurationStatus.DefaultsLoaded = TRUE; + if ( Mgr->CheckStatus(Mgr, CMOS_FIRST_BOOT_DETECTED) ) + StatusBytes->ConfigurationStatus.IsFirstBoot = TRUE; + if ( Mgr->CheckStatus(Mgr, CMOS_BAD_CHECKSUM) ) + StatusBytes->ConfigurationStatus.BadChecksum = TRUE; + } + + + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: ReadChecksum +// +// Description: +// Reads the checksum from the CMOS register(s). +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manger interface pointer. +// OUT UINT16 *ChecksumValue +// -- The returned checksum value +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS ReadChecksum( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + OUT UINT16 *ChecksumValue ) +{ + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); + + Manager->Access.Read( &Manager->Access, CMOS_CHECKSUM_HIGH, + (UINT8*)ChecksumValue ); + *ChecksumValue <<= 8; + Manager->Access.Read( &Manager->Access, CMOS_CHECKSUM_LOW, + (UINT8*)ChecksumValue ); + + return EFI_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: WriteChecksum +// +// Description: +// Write the checksum to the CMOS register(s). +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manger interface pointer. +// IN UINT16 ChecksumValue +// -- Value to write to the checksum location. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS WriteChecksum( + IN EFI_CMOS_MANAGER_INTERFACE *Manager, + IN UINT16 ChecksumValue ) +{ + DEFINE_PEI_SERVICES(Manager->Access.PeiServices); + + // update the HOB if available + + if (Manager->ManagerHob != NULL) + Manager->ManagerHob->Checksum = ChecksumValue; + + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, "WriteChecksum: Value = 0x%X\n", + ChecksumValue )); + + // update the CMOS checksum + + Manager->SetStatus(Manager, CMOS_FORCE_NO_CHECKSUM); + Manager->Access.Write( &Manager->Access, CMOS_CHECKSUM_LOW, + (UINT8)ChecksumValue ); + ChecksumValue >>= 8; + Manager->Access.Write( &Manager->Access, CMOS_CHECKSUM_HIGH, + (UINT8)ChecksumValue ); + Manager->ClearStatus(Manager, CMOS_FORCE_NO_CHECKSUM); + + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: UpdateBatteryStatus +// +// Description: +// This function is used to update the CMOS battery status. It is up +// to the user of the Manager interface to determine whether or ot +// the platform supports this feature. (If the platform does not +// support this feature, the battery status is indeterminate.) +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Pointer to the Manager's interface +// +// Output: +// EFI_STATUS (Return value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + + +EFI_STATUS UpdateBatteryStatus ( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr ) +{ + EFI_STATUS Status = EFI_SUCCESS; + BOOLEAN IsGood; + DEFINE_PEI_SERVICES(Mgr->Access.PeiServices); + + CMOS_TRACE_FULL(( CMOS_TRACE_ALWAYS, "\n\nUpdateBatteryStatus Entry\n")); + + // Update the battery status bit in the Manager's CMOS_MANAGER_STATUS + // field and update the legacy CMOS status register if legacy register + // updates are enabled. + +#ifdef PEI_COMPILE + IsGood = gCmosBatteryIsGood( PeiServices ); +#else + IsGood = gCmosBatteryIsGood( NULL ); +#endif + + if ( IsGood ) { + Mgr->ClearStatus(Mgr, CMOS_BAD_BATTERY); + CMOS_TRACE_FULL(( CMOS_TRACE_ALWAYS, " ...Battery is good.\n" )); + } + else { + Mgr->SetStatus(Mgr, CMOS_BAD_BATTERY); + CMOS_TRACE_FULL(( CMOS_TRACE_ALWAYS, " ...Battery is BAD!!\n" )); + } + + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CanClearLegacyStatusBits +// +// Description: +// Determines whether or not the legacy status bits in +// CMOS_DIAGNOSTIC_STATUS_REGISTER can be cleared to a non-failure +// status. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Mgr +// -- Manger interface pointer +// +// Output: +// BOOLEAN (Return Value) +// = TRUE - bits can be cleared +// = FALSE - bits can be cleared +// +// Notes: +// +// EFI_CMOS_ACCESS_INTERFACE.PeiServices == NULL in DXE phase. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +BOOLEAN CanClearLegacyStatusBits ( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr ) +{ + BOOLEAN IsDxePhase = (Mgr->Access.PeiServices == NULL) ? TRUE : FALSE; + BOOLEAN IsFirstBoot = Mgr->CheckStatus(Mgr, CMOS_FIRST_BOOT_DETECTED); + + if ( IsDxePhase ){ + return TRUE; + } + else { + return FALSE; + } +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: SynchronizeLegacyStatusRegisters +// +// Description: +// Synchronizes legacy CMOS status registers with status changes. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- Manger interface pointer +// IN CMOS_MANAGER_STATUS BitMap +// -- The status bits to be modified +// IN CMOS_BIT_ACCESS_TYPE AccessType +// -- Specifies whether to set or clear the bits +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Notes: +// The BitMap contains all the bits to be modified in the legacy +// status registers. +// +// If CMOS_LEGACY_STATUS_ENABLED is off, or is being turned off, this +// function will not modify CMOS registers. +// +// Failure status bits in CMOS_DIAGNOSTIC_STATUS_REGISTER can only be +// cleared if this is the first boot or if executing in the DXE phase. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS SynchronizeLegacyStatusRegisters( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + IN CMOS_MANAGER_STATUS BitMap, + IN CMOS_BIT_ACCESS_TYPE AccessType ) +{ + DIAGNOSTIC_STATUS_BYTE StatusByte; + DEFINE_PEI_SERVICES(Mgr->Access.PeiServices); + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + "\n\nSynchronizeLegacyStatusRegisters: Entry\n" )); + + // If the checksum status is being modified, + // then update the ChecksumIsBad bit + + if ( (BitMap & CMOS_BAD_CHECKSUM) != 0 ) + { + // Read the legacy diagnostic status register + + CmosBankReadWrite( Mgr, ReadType, CMOS_DIAGNOSTIC_STATUS_REGISTER, + &StatusByte.AllBits ); + + if (AccessType == SetType){ + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.ChecksumIsBad = 1\n" )); + StatusByte.Field.ChecksumIsBad = 1; + } + else if ( CanClearLegacyStatusBits(Mgr) ) { + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.ChecksumIsBad = 0\n" )); + StatusByte.Field.ChecksumIsBad = 0; + } + else { + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.ChecksumIsBad = No Change\n" )); + } + + // write back the changes + + CmosBankReadWrite( Mgr, WriteType, CMOS_DIAGNOSTIC_STATUS_REGISTER, + &StatusByte.AllBits ); + } + else { + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.ChecksumIsBad = No Change\n" )); + } + + + + // If the battery status is being modified, + // then update the RtcPowerIsBad bit + + if ( (BitMap & CMOS_BAD_BATTERY) != 0 ) + { + // Read the legacy diagnostic status register + + CmosBankReadWrite( Mgr, ReadType, CMOS_DIAGNOSTIC_STATUS_REGISTER, + &StatusByte.AllBits ); + + if (AccessType == SetType){ + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.RtcPowerIsBad = 1\n" )); + StatusByte.Field.RtcPowerIsBad = 1; + } + else if ( CanClearLegacyStatusBits(Mgr) ) { + StatusByte.Field.RtcPowerIsBad = 0; + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.RtcPowerIsBad = 0\n" )); + } + + // Write the legacy diagnostic status register + + CmosBankReadWrite( Mgr, WriteType, CMOS_DIAGNOSTIC_STATUS_REGISTER, + &StatusByte.AllBits ); + } + else { + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " StatusByte.Field.RtcPowerIsBad = No Change\n" )); + } + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + "SynchronizeLegacyStatusRegisters: Exit\n\n" )); + + return EFI_SUCCESS; + +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CalculateUpdatedCheckSum +// +// Description: +// Computes a new "updated" CMOS checksum based on the change of value +// at a single address. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- The Manger interface pointer. +// IN UINT16 CmosAddress +// -- CMOS location to be changed +// IN UINT8 OldCmosValue +// -- Current value at the CMOS location +// IN UINT8 NewCmosValue +// -- New value to be written to the CMOS location +// OUT UINT16 *NewChecksum +// -- Updated checksum +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or a valid EFI error code +// +// Notes: +// After this call, Manager->CheckStatus(Manager, +// CMOS_ADDRESS_IS_CHECKSUMMED) returns TRUE if the address "is" +// included in the checksum. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CalculateUpdatedCheckSum( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + IN UINT16 CmosAddress, + IN UINT8 OldCmosValue, + IN UINT8 NewCmosValue, + OUT UINT16 *NewChecksum ) +{ + UINT16 UnmanagedIndex = 1; // UnmanagedTable starts at 1 + UINT16 NoChecksumIndex = 1; // NoChecksumTable starts at 1 + DEFINE_PEI_SERVICES(Mgr->Access.PeiServices); + + // Check if the address is included in the NoChecksumTable + // or the UnmanagedTable + + while ( NoChecksumIndex < Mgr->NoChecksumCount ) + { + if ( CmosAddress <= Mgr->NoChecksumTable[NoChecksumIndex].Index ) + break; + ++NoChecksumIndex; + } + + while ( UnmanagedIndex < Mgr->UnmanagedTableCount ) + { + if ( CmosAddress <= Mgr->UnmanagedTable[UnmanagedIndex].Index ) + break; + ++UnmanagedIndex; + } + + + // Set ManagerStatus and compute NewChecksum if necessary + + if ( CmosAddress == Mgr->NoChecksumTable[NoChecksumIndex].Index + || CmosAddress == Mgr->UnmanagedTable[UnmanagedIndex].Index ) + { +#if (FULL_CMOS_MANAGER_DEBUG) + if ( CmosAddress == Mgr->NoChecksumTable[NoChecksumIndex].Index ) { + CMOS_TRACE((CMOS_TRACE_ALWAYS, " ->Not Checksummed (0x%X)\n", CmosAddress )); + } + else { + CMOS_TRACE((CMOS_TRACE_ALWAYS, " ->Not Managed (0x%X)\n", CmosAddress )); + } +#endif + Mgr->ClearStatus(Mgr, CMOS_ADDRESS_IS_CHECKSUMMED); + } + else { + ReadChecksum( Mgr, NewChecksum ); + *NewChecksum = *NewChecksum - OldCmosValue + NewCmosValue; + Mgr->SetStatus(Mgr, CMOS_ADDRESS_IS_CHECKSUMMED); + } + + return EFI_SUCCESS; +} + + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CalculateChecksum +// +// Description: +// Calculate the checksum over the entire range of managed CMOS +// (standard and/or extended CMOS). +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- The Manger interface pointer. +// OUT UINT16 *ChecksumValue +// -- The calculated checksum. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Modified: +// Manager->ManagerStatus +// CMOS_BAD_CHECKSUM - set/clear after comparing with +// the current checksum in CMOS. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CalculateChecksum( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + OUT UINT16 *ChecksumValue ) +{ + //----------------------------------------------------------------------- + // Algorithm Notes: + // + // The NoChecksumTable is in numeric order and contains all CMOS + // registers starting at FIRST_CMOS_REGISTER and ending with + // LAST_CMOS_REGISTER unless the register has been explicitly specified + // with Checksum = YES in the NvramField declaration. + // + // ...the NoChecksumTable need traversed only once. + //----------------------------------------------------------------------- + + UINT16 CurRegister; + UINT8 CurValue; + UINT16 UnmanagedIndex = 1; // UnmanagedTable starts at 1 + UINT16 NoChecksumIndex = 1; // NoChecksumTable starts at 1 + UINT16 CurrentChecksum; + DEFINE_PEI_SERVICES(Mgr->Access.PeiServices); + + *ChecksumValue = 0; // ...just in case + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, "\n\nCalculateChecksum: Entry\n" )); + + // Calculate it + for ( CurRegister = Mgr->FirstManagedRegister; + CurRegister <= Mgr->LastManagedRegister; // inclusive + ++CurRegister ) + { + //--------------------------------------------- + // Skip the location if the location is not managed + // + // Note: the UnmanagedTable is in numeric order, + // so it only needs traversed one time. + //--------------------------------------------- + + while ( (UnmanagedIndex < Mgr->UnmanagedTableCount) + && (CurRegister + > Mgr->UnmanagedTable[UnmanagedIndex].Index) ) + { + ++UnmanagedIndex; + } + if ( CurRegister == Mgr->UnmanagedTable[UnmanagedIndex].Index ){ + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " -->0x%X (un-managed)\n", CurRegister)); + continue; + } + + + while ( (NoChecksumIndex < Mgr->NoChecksumCount) + && (CurRegister + > Mgr->NoChecksumTable[NoChecksumIndex].Index) ) + { + ++NoChecksumIndex; + } + + + if ( (CurRegister != + Mgr->NoChecksumTable[NoChecksumIndex].Index ) ) + { + CmosReadWrite(Mgr, ReadType, CurRegister, 0, + &CurValue); + *ChecksumValue += CurValue; // update the output parameter + } + } + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + "\n --------------------------------\n")); + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " Computed *ChecksumValue = 0x%04X\n\n", + *ChecksumValue )); + + // Compare with physical CMOS checksum value and set/clear status bit + + ReadChecksum( Mgr, &CurrentChecksum); + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + "\n -----------------------\n")); + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " Saved Checksum = 0x%04X\n\n", + CurrentChecksum )); + + if (*ChecksumValue != CurrentChecksum) { + Mgr->SetStatus(Mgr, CMOS_BAD_CHECKSUM); + CMOS_TRACE((CMOS_TRACE_ALWAYS, " ...CMOS_BAD_CHECKSUM\n")); + } + else { + Mgr->ClearStatus(Mgr, CMOS_BAD_CHECKSUM); + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, " ...Checksum is OK\n" )); + } + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, "CalculateChecksum: Done\n" )); + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: CmosWrite +// +// Description: +// Writes from CmosValue to CMOS location encoded into CmosToken. +// +// Input: +// IN EFI_CMOS_ACCESS_INTERFACE *Cmos +// -- This is the access interface pointer. +// IN UINT16 CmosToken +// -- This is the encoded CMOS location. +// IN UINT8 CmosValue +// -- On success, this value will be written to +// the specified CMOS location. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if successful +// = EFI_NOT_FOUND, if the token is not in the token table +// = or other valid EFI error code +// +// Notes: +// None +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosWrite( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos, + IN UINT16 CmosToken, + IN UINT8 CmosValue ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINT16 TokenTableIndex = 0; + EFI_CMOS_MANAGER_INTERFACE *Mgr = GetCmosMangerInterface(Cmos); + DEFINE_PEI_SERVICES(Cmos->PeiServices); + + + // if CmosToken <= 0xe, then always write the register. These registers + // will be writable regardless of the state of the battery. + + if (CmosToken <= 0xe){ + Status = CmosBankReadWrite( Mgr, WriteType, CmosToken, &CmosValue ); + } + + // Otherwise, if CmosToken is less than MIN_TOKEN_VALUE and the battery + // is good, then an additional test is made to disallow writing of actual + // registers if they are within the managed region. Otherwise, the + // actual physical register is written. (For a bad battery, return + // EFI_DEVICE_ERROR when trying to write the physical register.) + + else if ( CmosToken < MIN_TOKEN_VALUE ){ + if ( !Mgr->CheckStatus( Mgr, CMOS_IS_USABLE ) ) { + Status = EFI_DEVICE_ERROR; + } + else if ( (CmosToken >= FIRST_MANAGED_CMOS_ADDRESS) + && (CmosToken < MAX_MANAGED_CMOS_ADDRESS) ) + Status = EFI_INVALID_PARAMETER; + else + Status = CmosBankReadWrite( Mgr, WriteType, + CmosToken, &CmosValue ); + } + + // Otherwise, after validating the CmosToken, decode it to write the + // associated register bit(s). + + else if ((TokenTableIndex = CmosFindToken( Mgr, CmosToken )) > 0) { + Status = CmosReadWriteEncodedToken( Mgr, WriteType, TokenTableIndex, + &CmosValue ); + } + else + Status = EFI_INVALID_PARAMETER; + + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: LoadOptimalDefaults +// +// Description: +// Initialize all physical CMOS registers with the default values +// specified in NvramField's Default value. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- The Manger interface pointer. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Modified: +// Manager->ManagerStatus bits: +// CMOS_BAD_CHECKSUM - cleared on success +// CMOS_ERROR_LOADING_DEFAULTS - set on error +// +// Notes: +// LoadOptimalDefaults will be called for one of two reasons: +// +// 1) On the first boot after flashing the ROM, the default +// values will be written to all CMOS registers, regardless +// of whether or not they are listed in the NoChecksumTable. +// +// 2) If a bad checksum is reported in PEI, the +// OptimalDefaultTable will be used as a read/write buffer +// until DXE, where (by default) the CMOS_BAD_CHECKSUM status bit will +// signal to write the buffer to physical CMOS. +// +// * The optimal defaults buffer may be modified from the original +// version if CMOS_OPTIMAL_DEFAULTS_ENABLED status bit is set. +// +// * If the CMOS_BAD_CHECKSUM status is set, then only those values +// included in the checksum are written to physical CMOS. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS LoadOptimalDefaults( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINT16 CurRegister; + UINT8 CurValue; + UINT16 NewChecksum = 0; + UINT16 UnmanagedIndex = 1; // UnmanagedTable starts at 1 + UINT16 DefaultIndex = 1; // OptimalDefaultTable starts at 1 + UINT16 NoChecksumIndex = 1; // NoChecksumTable starts at 1 + CMOS_MANAGER_STATUS SavedStatus = Mgr->ManagerStatus & + (CMOS_OPTIMAL_DEFAULTS_ENABLED ); + EFI_CMOS_ACCESS_INTERFACE *Cmos = Mgr->GetAccessInterface(Mgr); + DEFINE_PEI_SERVICES(Mgr->Access.PeiServices); + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, "LoadOptimalDefaults Entry\n")); + + // Calculate it + //----------------------------------------------------------------------- + // Configure to ensure reads/writes are to physical registers. + //----------------------------------------------------------------------- + + Mgr->ClearStatus(Mgr, CMOS_OPTIMAL_DEFAULTS_ENABLED ); + + // temporarily disallow checksum updating + + Mgr->SetStatus(Mgr, CMOS_FORCE_NO_CHECKSUM); + + + //--------------------------------------------- + // For the first boot, ignore the bad checksum + // so that defaults are loaded for all + // registers. + //--------------------------------------------- + + if ( Mgr->CheckStatus( Mgr, CMOS_FIRST_BOOT_DETECTED ) ){ + Mgr->ClearStatus(Mgr, CMOS_BAD_CHECKSUM); + } + + //----------------------------------------------------------------------- + // Write the physical registers: + // + // There "should" be exactly one default entry for each register in the + // managed region. (This is not assumed to be true.) + //----------------------------------------------------------------------- + + for ( CurRegister = Mgr->FirstManagedRegister; + CurRegister <= Mgr->LastManagedRegister; // inclusive + ++CurRegister, ++DefaultIndex ) + { + if ( DefaultIndex >= Mgr->OptimalDefaultCount){ // bad news! + Mgr->SetStatus(Mgr, CMOS_ERROR_LOADING_DEFAULTS); + break; + } + + //--------------------------------------------- + // Skip the location if the location is not managed + // + // Note: the UnmanagedTable is in numeric order, + // so it only needs traversed one time. + //--------------------------------------------- + + while ( (UnmanagedIndex < Mgr->UnmanagedTableCount) + && (CurRegister + > Mgr->UnmanagedTable[UnmanagedIndex].Index) ) + { + ++UnmanagedIndex; + } + if ( CurRegister == Mgr->UnmanagedTable[UnmanagedIndex].Index ){ + + // Ensure DefaultIndex is not incremented at the top of the + // loop, as un-managed locations do not have a default value. + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " -->Skip 0x%X (un-managed)\n", + CurRegister)); + --DefaultIndex; + continue; + } + + + //--------------------------------------------- + // Skip location if: + // + // 1. there is a bad checksum, + // 2. recovery is enabled only for checksummed locations + // + // (Note, the NoChecksumTable is in numeric order) + //--------------------------------------------- + + if ( Mgr->CheckStatus( Mgr, + CMOS_BAD_CHECKSUM + CMOS_RECOVER_ONLY_CHECKSUMMED ) ) + { + while ( (NoChecksumIndex < Mgr->NoChecksumCount) + && (CurRegister + > Mgr->NoChecksumTable[NoChecksumIndex].Index) ) + { + ++NoChecksumIndex; + } + if ( (CurRegister + == Mgr->NoChecksumTable[NoChecksumIndex].Index ) ){ + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, + " -->Skip 0x%X (not checksummed)\n", + CurRegister)); + continue; + } + } + + // Update the physical register + + CurValue = Mgr->OptimalDefaultTable[DefaultIndex].Value; + Status = CmosReadWrite(Mgr, WriteType, CurRegister, ALL_BITS, + &CurValue); + + if (EFI_ERROR(Status)){ + CMOS_TRACE((CMOS_TRACE_ALWAYS, + " Unable to load default for register: 0x%X\n", + CurRegister )); + break; + } + + + } + + // if no error, calculate checksum and reset the + // CMOS_BAD_CHECKSUM status bit. + + if ( EFI_ERROR(Status) + || Mgr->CheckStatus(Mgr, CMOS_ERROR_LOADING_DEFAULTS)) + { + CMOS_TRACE((CMOS_TRACE_ALWAYS, + " Error: Could not load Optimal Defaults\n")); + Status = EFI_UNSUPPORTED; + } + else + { + CalculateChecksum(Mgr, &NewChecksum); + WriteChecksum(Mgr, NewChecksum); + Mgr->ClearStatus(Mgr, CMOS_BAD_CHECKSUM); + } + + //----------------------------------------------------------------------- + // Restore previous access routing settings. + //----------------------------------------------------------------------- + + Mgr->ManagerStatus = Mgr->ManagerStatus | SavedStatus; + + // Allow checksums to be computed now + + Mgr->ClearStatus(Mgr, CMOS_FORCE_NO_CHECKSUM); + + // Set status bit to indicate that defaults have been loaded + + Mgr->SetStatus(Mgr, CMOS_DEFAULTS_LOADED); + + CMOS_TRACE_FULL((CMOS_TRACE_ALWAYS, "LoadOptimalDefaults Exit\n")); + + return Status; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: StopTime +// +// Description: Stops the time on RTC clock. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Mgr +// -- The Manger interface pointer. +// +// Output: None +// +// Notes: Here is the control flow of this function: +// 1. Stop the time by setting bit 7 on RTC register 0xb. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID StopTime( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr ) +{ + UINT8 Value; + + CmosBankReadWrite( Mgr, ReadType, 0xb, &Value ); + Value |= 0x80; + CmosBankReadWrite( Mgr, WriteType, 0xb, &Value ); +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: StartTime +// +// Description: Start the time on RTC clock. This is used when changing the +// Date and Time +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Mgr +// -- The Manger interface pointer. +// +// Output: None +// +// Notes: Here is the control flow of this function: +// 1. Start the time by clearing bit 7 on RTC register 0xb. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID StartTime( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr ) +{ + UINT8 Value; + + CmosBankReadWrite( Mgr, ReadType, 0xb, &Value ); + Value &= 0x7f; + CmosBankReadWrite( Mgr, WriteType, 0xb, &Value ); +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: BCDToDec +// +// Description: Converts a Decimal value to a BCD value. +// +// Input: +// IN UINT8 Dec - Decimal value +// +// Output: +// UINT8 (return value) - BCD value +// +// Notes: +// Only for 2 digit decimal. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 DecToBCD(IN UINT8 Dec) +{ + UINT8 FirstDigit = Dec % 10; + UINT8 SecondDigit = Dec / 10; + + return (SecondDigit << 4) + FirstDigit; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: BCDToDec +// +// Description: Converts a BCD value to a Decimal value. +// +// Input: +// IN UINT8 BCD -- BCD value +// +// Output: +// UINT8 - decimal value +// +// Notes: +// Only for 2 digit BCD. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 BCDToDec(IN UINT8 BCD) +{ + UINT8 FirstDigit = BCD & 0xf; + UINT8 SecondDigit = BCD >> 4;; + + return SecondDigit * 10 + FirstDigit; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: CmosMgrReadRtcIndex +// +// Description: Read the RTC value at the given Index. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Mgr +// -- The Manger interface pointer. +// IN UINT8 Index +// +// Output: +// UINT8 (return value) -- +// = RTC Value read from the provided Index +// +// Notes: +// Here is the control flow of this function: +// 1. Read port 0x70 (RTC Index Register) to get bit 7. +// Bit 7 is the NMI bit-it should not be changed. +// 2. Output 0x70 with the Index and NMI bit setting. +// 3. Read 0x71 for Data. Getting Dec when appropriate. +// 4. Return the Data. +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +static UINT8 CmosMgrReadRtcIndex( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + IN UINT8 Index ) +{ + UINT8 Value; + + CmosBankReadWrite( Mgr, ReadType, Index, &Value ); + Value = BCDToDec(Value); + + return Value; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: CmosMgrWriteRtcIndex +// +// Description: Write the RTC value at the given Index. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Mgr +// -- The Manger interface pointer. +// IN UINT8 Index +// -- Index to write +// IN UINT8 Value +// -- Value to write +// +// Output: None +// +// Notes: Here is the control flow of this function: +// 1. Read port 0x70 (RTC Index Register) to get bit 7. +// Bit 7 is the NMI bit-it should not be changed. +// 2. Output 0x70 with the Index. Switch to BCD when needed. +// 3. Write the data to 0x71. +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +static void CmosMgrWriteRtcIndex( + IN EFI_CMOS_MANAGER_INTERFACE *Mgr, + IN UINT8 Index, + IN UINT8 Value ) +{ + Value = DecToBCD(Value); + CmosBankReadWrite( Mgr, WriteType, Index, &Value ); +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: CmosSetDateTime +// +// Description: +// This routine simply writes the CMOS RTC registers from information +// provided by the EFI_TIME structure, assuming the information is +// correct. +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- The Manger interface pointer. +// IN EFI_TIME *Time +// -- EFI Time structure +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// Modified: +// N/A +// +// Notes: +// Data is assumed to be valid. Only a simple check for NULL pointer +// is done. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosSetDateTime ( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos, + IN EFI_TIME *Time ) +{ + EFI_CMOS_MANAGER_INTERFACE *Mgr = GetCmosMangerInterface(Cmos); + DEFINE_PEI_SERVICES(Cmos->PeiServices); + + if (Time == NULL || Mgr == NULL) + return EFI_INVALID_PARAMETER; + + StopTime(Mgr); + + CmosMgrWriteRtcIndex( Mgr, ACPI_CENTURY_CMOS, Time->Year / 100 ); + CmosMgrWriteRtcIndex( Mgr, 9, Time->Year % 100 ); + CmosMgrWriteRtcIndex( Mgr, 8, Time->Month ); + CmosMgrWriteRtcIndex( Mgr, 7, Time->Day ); + CmosMgrWriteRtcIndex( Mgr, 4, Time->Hour ); + CmosMgrWriteRtcIndex( Mgr, 2, Time->Minute ); + CmosMgrWriteRtcIndex( Mgr, 0, Time->Second ); + + StartTime(Mgr); + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// Procedure: CmosGetDateTime +// +// Description: Return the current date and time +// +// Input: +// IN EFI_CMOS_MANAGER_INTERFACE *Manager +// -- The Manger interface pointer. +// OUT EFI_TIME *Time +// -- EFI Time structure +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS or valid EFI error code +// +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS CmosGetDateTime ( + IN EFI_CMOS_ACCESS_INTERFACE *Cmos, + OUT EFI_TIME *Time ) +{ + EFI_CMOS_MANAGER_INTERFACE *Mgr = GetCmosMangerInterface(Cmos); + DEFINE_PEI_SERVICES(Cmos->PeiServices); + + if (Time == NULL || Cmos == NULL) + return EFI_INVALID_PARAMETER; + + //If RTC Year only 1 digit, EFI spec says years range is 1998 - 2097 + Time->Year = CmosMgrReadRtcIndex(Mgr, ACPI_CENTURY_CMOS) * 100 + + CmosMgrReadRtcIndex(Mgr, 9); + Time->Month = CmosMgrReadRtcIndex(Mgr, 8); + Time->Day = CmosMgrReadRtcIndex(Mgr, 7); + Time->Hour = CmosMgrReadRtcIndex(Mgr, 4); + Time->Minute = CmosMgrReadRtcIndex(Mgr, 2); + Time->Second = CmosMgrReadRtcIndex(Mgr, 0); + Time->Nanosecond= 0; + + return EFI_SUCCESS; +} + +//************************************************************************* +//************************************************************************* +//** ** +//** (C)Copyright 1985-2009, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 ** +//** ** +//** Phone: (770)-246-8600 ** +//** ** +//************************************************************************* +//************************************************************************* |