windows-nt/Source/XPSP1/NT/base/hals/halacpi/amd64/ixcmos.c
2020-09-26 16:20:57 +08:00

907 lines
15 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ixcmos.c
Abstract:
Procedures necessary to access CMOS/ECMOS information.
Author:
David Risner (o-ncrdr) 20 Apr 1992
Revision History:
Landy Wang (corollary!landy) 04 Dec 1992
- Move much code from ixclock.asm to here so different HALs
can reuse the common functionality.
Forrest Foltz (forrestf) 24-Oct-2000
Ported ixcmos.asm to ixcmos.c
--*/
#include "halcmn.h"
ULONG HalpHardwareLockFlags;
//
// Module-specific types
//
typedef UCHAR (*READ_CMOS_CHAR)(ULONG Address);
typedef VOID (*WRITE_CMOS_CHAR)(ULONG Address, UCHAR Data);
typedef struct _CMOS_BUS_PARAMETERS {
ULONG MaximumAddress;
READ_CMOS_CHAR ReadFunction;
WRITE_CMOS_CHAR WriteFunction;
} CMOS_BUS_PARAMETERS, *PCMOS_BUS_PARAMETERS;
//
// External data
//
extern KSPIN_LOCK HalpSystemHardwareLock;
//
// Local prototypes
//
UCHAR
HalpCmosReadByte(
ULONG Address
);
VOID
HalpCmosWriteByte(
ULONG Address,
UCHAR Data
);
UCHAR
HalpECmosReadByte(
ULONG Address
);
VOID
HalpECmosWriteByte(
ULONG Address,
UCHAR Data
);
UCHAR
HalpGetCmosCenturyByte (
VOID
);
ULONG
HalpGetSetCmosData (
IN ULONG SourceLocation,
IN ULONG SourceAddress,
IN PVOID ReturnBuffer,
IN ULONG ByteCount,
IN BOOLEAN Write
);
VOID
HalpSetCmosCenturyByte (
UCHAR Century
);
//
// Local data
//
//
// Describes each of the CMOS types
//
CMOS_BUS_PARAMETERS HalpCmosBusParameterTable[] = {
{ 0xFF, HalpCmosReadByte, HalpCmosWriteByte },
{ 0xFFFF, HalpECmosReadByte, HalpECmosWriteByte }
};
//
// Contains the offset to the CMOS century information
//
ULONG HalpCmosCenturyOffset;
//
// HalpRebootNow is a reboot vector. Set in an MP system to cause any
// processors that might be looping in HalpAcquireCmosSpinLock to transfer
// control to the vector in HalpRebootNow.
//
VOID (*HalpRebootNow)(VOID);
ULONG
HalpGetCmosData (
IN ULONG SourceLocation,
IN ULONG SourceAddress,
IN PVOID ReturnBuffer,
IN ULONG ByteCount
)
/*++
Routine Description:
This routine reads the requested number of bytes from CMOS/ECMOS and
stores the data read into the supplied buffer in system memory. If
the requested data amount exceeds the allowable extent of the source
location, the return data is truncated.
Arguments:
SourceLocation - where data is to be read from CMOS or ECMOS
0 - CMOS, 1 - ECMOS
SourceAddress - address in CMOS/ECMOS where data is to be transferred
ReturnBuffer - address in system memory for data to transfer
ByteCount - number of bytes to be read
Returns:
Number of byte actually read.
--*/
{
return HalpGetSetCmosData(SourceLocation,
SourceAddress,
ReturnBuffer,
ByteCount,
FALSE);
}
ULONG
HalpSetCmosData (
IN ULONG SourceLocation,
IN ULONG SourceAddress,
IN PVOID ReturnBuffer,
IN ULONG ByteCount
)
/*++
Routine Description:
This routine writes the requested number of bytes to CMOS/ECMOS.
Arguments:
SourceLocation - where data is to be written from CMOS or ECMOS
0 - CMOS, 1 - ECMOS
SourceAddress - address in CMOS/ECMOS where data is to be transferred
ReturnBuffer - address in system memory for data to transfer
ByteCount - number of bytes to be written
Returns:
Number of byte actually read.
--*/
{
return HalpGetSetCmosData(SourceLocation,
SourceAddress,
ReturnBuffer,
ByteCount,
TRUE);
}
ULONG
HalpGetSetCmosData (
IN ULONG SourceLocation,
IN ULONG RangeStart,
IN PVOID Buffer,
IN ULONG ByteCount,
IN BOOLEAN Write
)
/*++
Routine Description:
This routine reads the requested number of bytes from CMOS/ECMOS and
stores the data read into the supplied buffer in system memory. If
the requested data amount exceeds the allowable extent of the source
location, the return data is truncated.
Arguments:
SourceLocation - where data is to be read from CMOS or ECMOS
0 - CMOS, 1 - ECMOS
RangeStart - address in CMOS/ECMOS where data is to be transferred
Buffer - address in system memory for data to transfer
ByteCount - number of bytes to be transferred
Write - Indicates whether the operation is a read or a write
Returns:
Number of byte actually transferred
--*/
{
ULONG address;
PCHAR buffer;
ULONG last;
PCMOS_BUS_PARAMETERS cmosParameters;
//
// Validate the "bus type" and get a pointer to the parameters
// for the corresponding CMOS "bus".
//
if (SourceLocation != 0 && SourceLocation != 1) {
return 0;
}
cmosParameters = &HalpCmosBusParameterTable[SourceLocation];
//
// Limit the range of bytes to that which the cmos bus can accomodate.
//
address = RangeStart;
buffer = Buffer;
last = address + ByteCount - 1;
if (last > cmosParameters->MaximumAddress) {
last = cmosParameters->MaximumAddress;
}
//
// Take the cmos spin lock, perform the transfer, and release the lock.
//
HalpAcquireCmosSpinLock();
while (address <= last) {
if (Write == FALSE) {
*buffer = cmosParameters->ReadFunction(address);
} else {
cmosParameters->WriteFunction(address,*buffer);
}
address += 1;
buffer += 1;
}
HalpReleaseCmosSpinLock();
//
// Calculate and return the number of bytes trasferred.
//
return last - RangeStart;
}
UCHAR
HalpCmosReadByte(
ULONG Address
)
/*++
Routine Description:
This routine reads a single byte from cmos.
Arguments:
Address - The CMOS address from which to retrieve the byte.
Returns:
The byte that was read.
--*/
{
return CMOS_READ((UCHAR)Address);
}
VOID
HalpCmosWriteByte(
ULONG Address,
UCHAR Data
)
/*++
Routine Description:
This routine writes a single byte to cmos.
Arguments:
Address - The CMOS address at which to write the byte
Data - The byte to write
Returns:
Nothing
--*/
{
CMOS_WRITE((UCHAR)Address,Data);
}
UCHAR
HalpECmosReadByte(
ULONG Address
)
/*++
Routine Description:
This routine reads a single byte from extended cmos (ECMOS).
Arguments:
Address - The CMOS address from which to retrieve the byte.
Returns:
The byte that was read.
--*/
{
UCHAR data;
WRITE_PORT_USHORT_PAIR (ECMOS_ADDRESS_PORT_LSB,
ECMOS_ADDRESS_PORT_MSB,
(USHORT)Address);
IO_DELAY();
data = READ_PORT_UCHAR(ECMOS_DATA_PORT);
IO_DELAY();
return data;
}
VOID
HalpECmosWriteByte(
ULONG Address,
UCHAR Data
)
/*++
Routine Description:
This routine writes a single byte to extended cmos (ECMOS).
Arguments:
Address - The CMOS address at which to write the byte
Data - The byte to write
Returns:
Nothing
--*/
{
WRITE_PORT_USHORT_PAIR (ECMOS_ADDRESS_PORT_LSB,
ECMOS_ADDRESS_PORT_MSB,
(USHORT)Address);
IO_DELAY();
WRITE_PORT_UCHAR(ECMOS_DATA_PORT,Data);
IO_DELAY();
}
VOID
HalpReadCmosTime(
PTIME_FIELDS TimeFields
)
/*++
Routine Description:
This routine reads current time from CMOS memory and stores it
in the TIME_FIELDS structure passed in by caller.
Arguments:
TimeFields - A pointer to the TIME_FIELDS structure.
Return Value:
None.
--*/
{
USHORT year;
HalpAcquireCmosSpinLockAndWait();
//
// The RTC is only accurate to within one second. So add a
// half a second so that we are closer, on average, to the right
// answer.
//
TimeFields->Milliseconds = 500;
TimeFields->Second = CMOS_READ_BCD(RTC_OFFSET_SECOND);
TimeFields->Minute = CMOS_READ_BCD(RTC_OFFSET_MINUTE);
TimeFields->Hour = CMOS_READ_BCD(RTC_OFFSET_HOUR);
TimeFields->Weekday = CMOS_READ_BCD(RTC_OFFSET_DAY_OF_WEEK);
TimeFields->Day = CMOS_READ_BCD(RTC_OFFSET_DATE_OF_MONTH);
year = BCD_TO_BIN(HalpGetCmosCenturyByte());
year = year * 100 + CMOS_READ_BCD(RTC_OFFSET_YEAR);
if (year >= 1900 && year < 1920) {
//
// Compensate for the century field
//
year += 100;
}
TimeFields->Year = year;
HalpReleaseCmosSpinLock();
}
VOID
HalpWriteCmosTime (
PTIME_FIELDS TimeFields
)
/*++
Routine Description:
This routine writes current time from TIME_FILEDS structure
to CMOS memory.
Arguments:
TimeFields - A pointer to the TIME_FIELDS structure.
Return Value:
None.
--*/
{
ULONG year;
HalpAcquireCmosSpinLockAndWait();
CMOS_WRITE_BCD(RTC_OFFSET_SECOND,(UCHAR)TimeFields->Second);
CMOS_WRITE_BCD(RTC_OFFSET_MINUTE,(UCHAR)TimeFields->Minute);
CMOS_WRITE_BCD(RTC_OFFSET_HOUR,(UCHAR)TimeFields->Hour);
CMOS_WRITE_BCD(RTC_OFFSET_DAY_OF_WEEK,(UCHAR)TimeFields->Weekday);
CMOS_WRITE_BCD(RTC_OFFSET_DATE_OF_MONTH,(UCHAR)TimeFields->Month);
year = TimeFields->Year;
if (year > 9999) {
year = 9999;
}
HalpSetCmosCenturyByte(BIN_TO_BCD((UCHAR)(year / 100)));
CMOS_WRITE_BCD(RTC_OFFSET_YEAR,(UCHAR)(year % 100));
HalpReleaseCmosSpinLock();
}
VOID
HalpAcquireCmosSpinLockAndWait (
VOID
)
/*++
Routine Description:
This routine acquires the CMOS spinlock, then waits for the CMOS
BUSY flag to be clear.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG count;
ULONG value;
//
// Acquire the cmos spinlock and wait until it is not busy. While
// waiting, periodically release and re-acquire the spinlock.
//
HalpAcquireCmosSpinLock();
count = 0;
while (TRUE) {
value = CMOS_READ(CMOS_STATUS_A);
if ((value & CMOS_STATUS_BUSY) == 0) {
return;
}
count += 1;
if (count == 100) {
count = 0;
HalpReleaseCmosSpinLock();
HalpAcquireCmosSpinLock();
}
}
}
VOID
HalpReleaseCmosSpinLock (
VOID
)
/*++
Routine Description:
This routine acquires the spin lock used to protect access to various
pieces of hardware.
Arguments:
None
Returns:
Nothing
--*/
{
ULONG flags;
flags = HalpHardwareLockFlags;
KeReleaseSpinLockFromDpcLevel(&HalpSystemHardwareLock);
HalpRestoreInterrupts(flags);
}
VOID
HalpAcquireCmosSpinLock (
VOID
)
/*++
Routine Description:
This routine acquires the spin lock used to protect access to various
pieces of hardware.
Arguments:
None
Returns:
Nothing
--*/
{
BOOLEAN acquired;
ULONG flags;
KIRQL oldIrql;
#if defined(NT_UP)
HalpHardwareLockFlags = HalpDisableInterrupts();
#else
while (TRUE) {
flags = HalpDisableInterrupts();
acquired = KeTryToAcquireSpinLockAtDpcLevel(&HalpSystemHardwareLock);
if (acquired != FALSE) {
break;
}
HalpRestoreInterrupts(flags);
while (KeTestSpinLock(&HalpSystemHardwareLock) == FALSE) {
if (HalpRebootNow != NULL) {
HalpRebootNow();
}
PAUSE_PROCESSOR;
}
}
HalpHardwareLockFlags = flags;
#endif
}
VOID
HalpAcquireSystemHardwareSpinLock (
VOID
)
/*++
Routine Description:
This routine acquires the spin lock used to protect access to various
pieces of hardware. It is a synonym of HalpAcquireCmosSpinLock().
Arguments:
None
Returns:
Nothing
--*/
{
HalpAcquireCmosSpinLock();
}
VOID
HalpReleaseSystemHardwareSpinLock (
VOID
)
/*++
Routine Description:
This routine releases the spin lock used to protect access to various
pieces of hardware. It is a synonym of HalpReleaseCmosSpinLock().
Arguments:
None
Returns:
Nothing
--*/
{
HalpReleaseCmosSpinLock();
}
UCHAR
HalpGetCmosCenturyByte (
VOID
)
/*++
Routine Description:
This routine retrieves the century byte from the CMOS.
N.B. The cmos spinlock must be acquired before calling this function.
Arguments:
None
Returns:
The century byte.
--*/
{
UCHAR value;
UCHAR oldStatus;
UCHAR centuryByte;
//
// Make sure the century offset is initialized
//
ASSERT(HalpCmosCenturyOffset != 0);
if ((HalpCmosCenturyOffset & CMOS_BANK_1) != 0) {
//
// Perform a bank 1 read
//
oldStatus = CMOS_READ(CMOS_STATUS_A);
value = oldStatus | CMOS_STATUS_BANK1;
CMOS_WRITE(CMOS_STATUS_A,value);
centuryByte = CMOS_READ((UCHAR)HalpCmosCenturyOffset);
CMOS_WRITE(CMOS_STATUS_A,oldStatus);
} else {
centuryByte = CMOS_READ((UCHAR)HalpCmosCenturyOffset);
}
return centuryByte;
}
VOID
HalpSetCmosCenturyByte (
UCHAR Century
)
/*++
Routine Description:
This routine sets the century byte in the CMOS.
N.B. The cmos spinlock must be acquired before calling this function.
Arguments:
Century - The century byte to set
Returns:
Nothing
--*/
{
UCHAR value;
UCHAR oldStatus;
//
// Make sure the century offset is initialized
//
ASSERT(HalpCmosCenturyOffset != 0);
if ((HalpCmosCenturyOffset & CMOS_BANK_1) != 0) {
//
// Perform a bank 1 write
//
oldStatus = CMOS_READ(CMOS_STATUS_A);
value = oldStatus | CMOS_STATUS_BANK1;
CMOS_WRITE(CMOS_STATUS_A,value);
CMOS_WRITE((UCHAR)HalpCmosCenturyOffset,Century);
CMOS_WRITE(CMOS_STATUS_A,oldStatus);
} else {
CMOS_WRITE((UCHAR)HalpCmosCenturyOffset,Century);
}
}
VOID
HalpFlushTLB (
VOID
)
/*++
Routine Description:
Flushes the current TLB.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG flags;
PKPCR pcr;
PKPRCB prcb;
ULONG64 cr3;
ULONG64 cr4;
ULONG64 oldCr4;
flags = HalpDisableInterrupts();
cr3 = ReadCR3();
pcr = KeGetPcr();
prcb = pcr->CurrentPrcb;
//
// Note: the original code (ixcmos.asm) had differing behavior based
// on whether this was CPU 0. That behavior is mimicked here.
// It would be good to find out why this is done.
//
if (prcb->Number == 0) {
WriteCR3(cr3);
} else {
cr4 = ReadCR4();
WriteCR4(cr4 & ~CR4_PGE);
WriteCR3(cr3);
WriteCR4(cr4);
}
HalpRestoreInterrupts(flags);
}
VOID
HalpCpuID (
IN ULONG Function,
OUT PULONG Eax,
OUT PULONG Ebx,
OUT PULONG Ecx,
OUT PULONG Edx
)
/*++
Routine Description:
This function executes a cpu id and returns the result as found in
registers eax, ebx, ecx and edx.
Arguments:
Function - supplies the CPUID function to execute.
Eax - supplies a pointer to the storage to contain the contents of eax.
Eax - supplies a pointer to the storage to contain the contents of ebx.
Eax - supplies a pointer to the storage to contain the contents of ecx.
Eax - supplies a pointer to the storage to contain the contents of edx.
Return Value:
None.
--*/
{
CPU_INFO cpuInfo;
KiCpuId (Function,&cpuInfo);
*Eax = cpuInfo.Eax;
*Ebx = cpuInfo.Ebx;
*Ecx = cpuInfo.Ecx;
*Edx = cpuInfo.Edx;
}