/** @file
|
CmosAccessLib implementation.
|
|
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include "CmosAccessLibInternal.h"
|
|
/**
|
Return the entry for the specified address in entries returned
|
from platform.
|
|
@param [in] Address The CMOS address to find.
|
|
@return A pointer to CMOS_ENTRY for the specified address,
|
or NULL if not found.
|
**/
|
CMOS_ENTRY *
|
CmosAccessLibLocateEntry (
|
IN UINT8 Address
|
)
|
{
|
UINTN Index;
|
UINTN Count;
|
CMOS_ENTRY *Entries;
|
|
Entries = PlatformCmosGetEntry (&Count);
|
for (Index = 0; Index < Count; Index++) {
|
if (Entries[Index].Address == Address) {
|
return &Entries[Index];
|
}
|
}
|
|
return NULL;
|
}
|
|
/**
|
Test the attributes of the Entry and return ValueIfSet if test passes
|
or !ValueIfSet if test fails. It returns DefaultValue if the Entry is
|
NULL.
|
|
@param [in] Address The CMOS address.
|
@param [in] Attributes The attributes to test.
|
@param [in] ValueIfSet The value to return if test passes.
|
@param [in] DefaultValue The value to return if Entry is NULL.
|
@param [in] Entry Pointer to entry.
|
|
@retval ValueIfSet If test passes.
|
@retval !ValueIfSet If test fails.
|
@retval DefaultValue If the Entry is NULL.
|
**/
|
BOOLEAN
|
CmosAccessLibCheckAttribute (
|
IN UINT8 Address,
|
IN UINT8 Attributes,
|
IN BOOLEAN ValueIfSet,
|
IN BOOLEAN DefaultValue,
|
IN CMOS_ENTRY *Entry OPTIONAL
|
)
|
{
|
if (Entry != NULL) {
|
ASSERT (Entry->Address == Address);
|
if ((Entry->Attributes & Attributes) == Attributes) {
|
return ValueIfSet;
|
} else {
|
return !ValueIfSet;
|
}
|
}
|
|
return DefaultValue;
|
}
|
|
/**
|
Check if the CMOS address needs Checksum calculation.
|
|
@param [in] Address CMOS address to be checked
|
@param [in] Entry Pointer to entry.
|
|
@retval TRUE CMOS address needs Checksum calculation.
|
@retval FALSE CMOS address doesn't need Checksum calculation.
|
**/
|
BOOLEAN
|
CmosAccessLibNeedChecksum (
|
IN UINT8 Address,
|
IN CMOS_ENTRY *Entry OPTIONAL
|
)
|
{
|
return CmosAccessLibCheckAttribute (Address, CMOS_EXCLUDE_FROM_CHECKSUM, FALSE, FALSE, Entry);
|
}
|
|
|
/**
|
Check if the CMOS address needs to fill default data.
|
|
@param [in] Address CMOS address to be checked
|
@param [in] Entry Pointer to entry.
|
|
@retval TRUE CMOS address need to fill default data.
|
@retval FALSE CMOS address doesn't need to fill default data.
|
**/
|
BOOLEAN
|
CmosAccessLibNeedFillDefault (
|
IN UINT8 Address,
|
IN CMOS_ENTRY *Entry OPTIONAL
|
)
|
{
|
return CmosAccessLibCheckAttribute (Address, CMOS_EXCLUDE_FROM_INIT_DATA, FALSE, TRUE, Entry);
|
}
|
|
/**
|
Check if the CMOS address is accessible.
|
|
@param [in] Address CMOS address to be checked.
|
@param [in] Entry Pointer to entry.
|
|
@retval TRUE CMOS address is accessible.
|
@retval FALSE CMOS address isn't accessible.
|
**/
|
BOOLEAN
|
CmosAccessLibIsAccessible (
|
IN UINT8 Address,
|
IN CMOS_ENTRY *Entry OPTIONAL
|
)
|
{
|
//
|
// CMOS 0-9, A, B, C, D are for RTC.
|
//
|
if (Address <= 0xD) {
|
return FALSE;
|
}
|
return CmosAccessLibCheckAttribute (Address, CMOS_EXCLUDE_FROM_ACCESS, FALSE, TRUE, Entry);
|
}
|
|
/**
|
Return the CMOS location to store checksum.
|
|
@param [out] Location Return the CMOS location to store the checksum.
|
**/
|
VOID
|
CmosAccessLibGetChecksumLocation (
|
OUT CMOS_CHECKSUM_LOCATION_INFO *Location
|
)
|
{
|
UINTN Index;
|
UINTN Count;
|
CMOS_ENTRY *Entries;
|
|
Location->Length = 0;
|
|
Entries = PlatformCmosGetEntry (&Count);
|
for (Index = 0; Index < Count; Index++) {
|
if ((Entries[Index].Attributes & CMOS_CHECKSUM_LOCATION) == CMOS_CHECKSUM_LOCATION) {
|
Location->Length++;
|
if (Location->Length == 1) {
|
Location->LowByteAddress = Entries[Index].Address;
|
} else if (Location->Length == 2) {
|
Location->HighByteAddress = Entries[Index].Address;
|
break;
|
}
|
}
|
}
|
|
ASSERT (Location->Length <= 2);
|
}
|
|
/**
|
Calculate the sum of CMOS values who need checksum calculation.
|
|
@param [in] Location The CMOS location to store the checksum.
|
|
@return The sum.
|
**/
|
UINT16
|
CmosAccessLibCalculateSum (
|
IN CMOS_CHECKSUM_LOCATION_INFO *Location
|
)
|
{
|
UINT16 Sum;
|
UINTN Index;
|
UINTN Count;
|
CMOS_ENTRY *Entries;
|
|
if (Location->Length == 0) {
|
return 0;
|
}
|
|
Sum = 0;
|
Entries = PlatformCmosGetEntry (&Count);
|
for (Index = 0; Index < Count; Index++) {
|
if (CmosAccessLibNeedChecksum (Entries[Index].Address, &Entries[Index])) {
|
Sum += CmosRead8 (Entries[Index].Address);
|
}
|
}
|
|
if (Location->Length == 1) {
|
return (UINT8) Sum;
|
} else {
|
return Sum;
|
}
|
}
|
|
/**
|
Return the checksum value stored in CMOS.
|
|
@param [in] Location The CMOS location to store the checksum.
|
|
@return The checksum value.
|
**/
|
UINT16
|
CmosAccessLibReadChecksum (
|
IN CMOS_CHECKSUM_LOCATION_INFO *Location
|
)
|
{
|
UINT16 Checksum;
|
|
Checksum = 0;
|
|
switch (Location->Length) {
|
case 2:
|
Checksum = (CmosRead8 (Location->HighByteAddress) << 8);
|
//
|
// Fall to case 1 to get the low byte value
|
//
|
case 1:
|
Checksum += CmosRead8 (Location->LowByteAddress);
|
break;
|
|
default:
|
break;
|
}
|
return Checksum;
|
}
|
|
|
/**
|
Write the Checksum to appropriate address.
|
|
@param [in] Location The CMOS location to store the checksum.
|
@param [in] Checksum The checksum value.
|
**/
|
VOID
|
CmosAccessLibWriteChecksum (
|
CMOS_CHECKSUM_LOCATION_INFO *Location,
|
IN UINT16 Checksum
|
)
|
{
|
|
switch (Location->Length) {
|
case 0:
|
break;
|
case 2:
|
CmosWrite8 (Location->HighByteAddress, Checksum >> 8);
|
//
|
// Fall to case 1 to update low byte value
|
//
|
case 1:
|
CmosWrite8 (Location->LowByteAddress, (UINT8) Checksum);
|
break;
|
}
|
}
|
|
/**
|
Read a byte value from a CMOS address.
|
|
@param [in] Address Location to read from CMOS
|
|
@return The byte value read from the CMOS address.
|
**/
|
UINT8
|
EFIAPI
|
CmosRead8 (
|
IN UINT8 Address
|
)
|
{
|
if (!CmosAccessLibIsAccessible (Address, CmosAccessLibLocateEntry (Address))) {
|
return 0xFF;
|
}
|
|
if (Address <= CMOS_BANK0_LIMIT) {
|
if (PlatformCmosGetNmiState ()) {
|
Address |= BIT7;
|
}
|
IoWrite8 (PORT_70, Address);
|
return IoRead8 (PORT_71);
|
} else {
|
IoWrite8 (PORT_72, Address);
|
return IoRead8 (PORT_73);
|
}
|
}
|
|
/**
|
Write a byte value to a CMOS address.
|
|
It's an internal function that doesn't update the checksum.
|
|
@param [in] Address Location to write to CMOS.
|
@param [in] Data The byte value write to the CMOS address.
|
**/
|
VOID
|
CmosAccessLibICmosWrite8 (
|
IN UINT8 Address,
|
IN UINT8 Data
|
)
|
{
|
if (Address <= CMOS_BANK0_LIMIT) {
|
if (PlatformCmosGetNmiState ()) {
|
Address |= BIT7;
|
}
|
IoWrite8 (PORT_70, Address);
|
IoWrite8 (PORT_71, Data);
|
} else {
|
IoWrite8 (PORT_72, Address);
|
IoWrite8 (PORT_73, Data);
|
}
|
}
|
|
/**
|
Write a byte value to a CMOS address.
|
|
@param [in] Address Location to write to CMOS.
|
@param [in] Data The byte value write to the CMOS address.
|
**/
|
VOID
|
EFIAPI
|
CmosWrite8 (
|
IN UINT8 Address,
|
IN UINT8 Data
|
)
|
{
|
UINT8 OriginalData;
|
CMOS_ENTRY *Entry;
|
CMOS_CHECKSUM_LOCATION_INFO ChecksumLocation;
|
|
Entry = CmosAccessLibLocateEntry (Address);
|
|
if (!CmosAccessLibIsAccessible (Address, Entry)) {
|
return;
|
}
|
|
OriginalData = CmosRead8 (Address);
|
|
CmosAccessLibICmosWrite8 (Address, Data);
|
|
if (CmosAccessLibNeedChecksum (Address, Entry)) {
|
//
|
// Sum of Data + Checksum = New Sum of Data + New Checksum = 0
|
// New Sum of Data - Sum of Data = Checksum - New Checksum
|
// New Checksum = Checksum - (New Sum of Data - Sum of Data)
|
//
|
CmosAccessLibGetChecksumLocation (&ChecksumLocation);
|
CmosAccessLibWriteChecksum (
|
&ChecksumLocation,
|
CmosAccessLibReadChecksum (&ChecksumLocation) - (Data - OriginalData)
|
);
|
}
|
}
|
|
/**
|
Read a word value from a CMOS address.
|
|
@param [in] Address Location to read from CMOS
|
|
@return The word value read from the CMOS address.
|
**/
|
UINT16
|
EFIAPI
|
CmosRead16 (
|
IN UINT8 Address
|
)
|
{
|
return CmosRead8 (Address) + (CmosRead8 (Address + 1) << 8);
|
}
|
|
/**
|
Write a word value to a CMOS address.
|
|
@param [in] Address Location to write to CMOS.
|
@param [in] Data The word value write to the CMOS address.
|
**/
|
VOID
|
EFIAPI
|
CmosWrite16 (
|
IN UINT8 Address,
|
IN UINT16 Data
|
)
|
{
|
CmosWrite8 (Address, (UINT8) Data);
|
CmosWrite8 (Address + 1, (UINT8) (Data >> 8));
|
}
|
|
/**
|
Read a dword value from a CMOS address.
|
|
@param [in] Address Location to read from CMOS
|
|
@return The dword value read from the CMOS address.
|
**/
|
UINT32
|
EFIAPI
|
CmosRead32 (
|
IN UINT8 Address
|
)
|
{
|
return CmosRead16 (Address) + (CmosRead16 (Address + 2) << 16);
|
}
|
|
/**
|
Write a dword value to a CMOS address.
|
|
@param [in] Address Location to write to CMOS.
|
@param [in] Data The dword value write to the CMOS address.
|
**/
|
VOID
|
EFIAPI
|
CmosWrite32 (
|
IN UINT8 Address,
|
IN UINT32 Data
|
)
|
{
|
CmosWrite16 (Address, (UINT16) Data);
|
CmosWrite16 (Address + 2, (UINT16) (Data >> 16));
|
}
|
|
|
/**
|
Initialize the CMOS.
|
|
It initialize the CMOS area when Force is TRUE or the checksum is incorrect.
|
|
@param[in] Force TRUE indicating initializing the CMOS area without checking the checksum.
|
|
@retval TRUE The CMOS is initialized to default value.
|
@retval FALSE The CMOS isn't initialized to default value.
|
**/
|
BOOLEAN
|
EFIAPI
|
CmosInit (
|
IN BOOLEAN Force
|
)
|
{
|
UINTN Address;
|
CMOS_ENTRY *Entry;
|
CMOS_CHECKSUM_LOCATION_INFO ChecksumLocation;
|
UINT16 Checksum;
|
|
CmosAccessLibGetChecksumLocation (&ChecksumLocation);
|
|
if (!Force) {
|
//
|
// Initialize the CMOS area when checksum is incorrect.
|
//
|
Checksum = CmosAccessLibCalculateSum (&ChecksumLocation) + CmosAccessLibReadChecksum (&ChecksumLocation);
|
if (ChecksumLocation.Length == 1) {
|
Checksum = (UINT8) Checksum;
|
}
|
|
if (Checksum != 0) {
|
Force = TRUE;
|
}
|
}
|
|
if (Force) {
|
//
|
// Traverse through entire CMOS location and fill it with zero
|
//
|
for (Address = 0; Address <= CMOS_BANK1_LIMIT; Address++) {
|
Entry = CmosAccessLibLocateEntry ((UINT8) Address);
|
if (CmosAccessLibNeedFillDefault ((UINT8) Address, Entry)) {
|
CmosAccessLibICmosWrite8 ((UINT8) Address, (Entry == NULL) ? CMOS_DEFAULT_VALUE : Entry->DefaultValue);
|
}
|
}
|
|
//
|
// Write the New checksum to the Checksum field
|
//
|
CmosAccessLibWriteChecksum (
|
&ChecksumLocation,
|
(UINT16) (0x10000 - CmosAccessLibCalculateSum (&ChecksumLocation))
|
);
|
return TRUE;
|
}
|
|
return FALSE;
|
}
|