1121 lines
26 KiB
NASM
1121 lines
26 KiB
NASM
title "Cmos Access Routines"
|
|
;++
|
|
;
|
|
; Module Name:
|
|
;
|
|
; ixcmos.asm
|
|
;
|
|
; 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.
|
|
;
|
|
;--
|
|
|
|
.386p
|
|
.xlist
|
|
include hal386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include mac386.inc
|
|
include i386\ix8259.inc
|
|
include i386\ixcmos.inc
|
|
.list
|
|
|
|
EXTRNP _DbgBreakPoint,0,IMPORT
|
|
extrn _HalpSystemHardwareLock:DWORD
|
|
extrn _HalpBusType:DWORD
|
|
extrn _HalpSerialLen:BYTE
|
|
extrn _HalpSerialNumber:BYTE
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
;
|
|
; HalpRebootNow is a reboot vector. Set in an MP system, to
|
|
; cause any processors which may be looping in HalpAcquireCmosSinLock
|
|
; to transfer control to the vector in HalpRebootNow
|
|
;
|
|
|
|
public _HalpRebootNow
|
|
_HalpRebootNow dd 0
|
|
|
|
;
|
|
; Holds the value of the eflags register before a cmos spinlock is
|
|
; acquired (used in HalpAcquire/ReleaseCmosSpinLock().
|
|
;
|
|
_HalpHardwareLockFlags dd 0
|
|
|
|
;
|
|
; Holds the offset to CMOS Century information.
|
|
;
|
|
|
|
public _HalpCmosCenturyOffset
|
|
_HalpCmosCenturyOffset dd 0
|
|
|
|
_DATA ends
|
|
|
|
subttl "HalpGetCmosData"
|
|
|
|
;++
|
|
;
|
|
; CMOS space read and write functions.
|
|
;
|
|
;--
|
|
|
|
CmosAddressPort equ 70H
|
|
CmosDataPort equ 71H
|
|
|
|
ECmosAddressLsbPort equ 74H
|
|
ECmosAddressMsbPort equ 75H
|
|
ECmosDataPort equ 76H
|
|
|
|
|
|
INIT SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
ifndef ACPI_HAL
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpInitializeCmos(
|
|
; VOID
|
|
; )
|
|
;
|
|
; This routine reads CMOS and initializes globals required for
|
|
; CMOS access, such as the location of the century byte.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalpInitializeCmos,0
|
|
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
|
|
;
|
|
; Assume default
|
|
;
|
|
|
|
mov eax, RTC_OFFSET_CENTURY
|
|
mov _HalpCmosCenturyOffset, eax
|
|
|
|
cmp _HalpBusType, MACHINE_TYPE_ISA
|
|
jne icm40
|
|
|
|
|
|
;
|
|
; If control comes here, this is ISA machine. We need to check if this is
|
|
; IBM PS/1 or Pc/ValuePoint machine and use RTC_CENTURY_OFFSET_MCA to get
|
|
; Century byte from CMOS.
|
|
;
|
|
|
|
;
|
|
; Check if the CMOS 2e and 2f contains memory checksum. On PS/1 machine
|
|
; the check should fail.
|
|
;
|
|
|
|
icm20: mov ecx, 2dh ; from 10h to 2dh
|
|
mov eax, 0 ; clear ax
|
|
mov edx, 0
|
|
|
|
icm30: mov al, cl
|
|
CMOS_READ
|
|
add edx, eax
|
|
dec ecx
|
|
cmp ecx, 0fh
|
|
jne short icm30
|
|
|
|
mov eax, 2eh
|
|
CMOS_READ
|
|
mov ah, al
|
|
mov al, 2fh
|
|
CMOS_READ
|
|
cmp eax, edx
|
|
je short icm50 ; NOT PS/1
|
|
|
|
mov eax, RTC_OFFSET_CENTURY_MCA
|
|
mov _HalpCmosCenturyOffset, eax
|
|
jmp icm90
|
|
|
|
icm40: cmp _HalpBusType, MACHINE_TYPE_MCA
|
|
jne short icm50
|
|
|
|
;
|
|
; See if this is a P700 MCA machine
|
|
;
|
|
|
|
in al, 07fh ; get PD700 ID byte
|
|
and al, 0F0h ; Mask high nibble
|
|
cmp al, 0A0h ; Is the ID Ax?
|
|
jz short icm50
|
|
cmp al, 090h ; Or an 9X?
|
|
jz short icm50 ; Yes, it's a 700
|
|
|
|
mov eax, RTC_OFFSET_CENTURY_MCA
|
|
mov _HalpCmosCenturyOffset, eax
|
|
|
|
icm50:
|
|
|
|
if 0
|
|
|
|
- Selecting BANK1 causes some devices to mess up their month value
|
|
- For now, I'm removing this code until this problem can be solved
|
|
|
|
;
|
|
; See if this is a Dallas Semiconductor DS17285 or later
|
|
; Switch to BANK 1
|
|
;
|
|
mov al, 0Ah
|
|
CMOS_READ
|
|
|
|
and al, 7fh ; Don't write UIP
|
|
mov ah, al
|
|
mov esi, eax ; save it for restore
|
|
or ah, 10h ; Set DV0 = 1
|
|
|
|
mov al, 0Ah ; Write register A
|
|
CMOS_WRITE
|
|
|
|
;
|
|
; Check for RTC serial # with matching crc
|
|
; (al) = current byte
|
|
; (ah) = scratch register
|
|
; (bl) = current crc
|
|
; (bh) = zero, non-zero, flag
|
|
; (ecx) = cmos offset
|
|
; (edx) = used by cmos_read macro
|
|
; (esi) = saved register 0A
|
|
;
|
|
mov ecx, 40h
|
|
xor ebx, ebx
|
|
|
|
icm60: mov al, cl
|
|
CMOS_READ
|
|
mov byte ptr _HalpSerialNumber+2+-40h[ecx], al
|
|
|
|
or bh, al ; or to check for all zeros
|
|
|
|
mov ch, 8 ; Bits per byte
|
|
|
|
icm65: mov ah, bl ; ah = crc
|
|
xor ah, al ; xor LSb
|
|
shr bl, 1 ; shift crc
|
|
shr ah, 1 ; mov LSb to carry
|
|
sbb ah, ah ; if carry set 1's else 0's
|
|
and ah, (118h shr 1) ; crc polynomial
|
|
xor bl, ah ; apply it
|
|
|
|
shr al, 1 ; next bit
|
|
dec ch ;
|
|
jnz short icm65 ; if ch non-zero, loop
|
|
|
|
inc cl ; next cmos location
|
|
cmp cl, 48h ; at end?
|
|
jne short icm60 ; no, loop
|
|
;
|
|
; (bh) = zero, non-zero flag
|
|
; (bl) = crc
|
|
;
|
|
|
|
mov eax, RTC_OFFSET_CENTURY_DS ; Read century byte
|
|
CMOS_READ
|
|
|
|
BCD_TO_BIN
|
|
movzx ecx, ax ; save it
|
|
|
|
;
|
|
; Switch back to BANK 0
|
|
;
|
|
|
|
mov eax, esi
|
|
mov al, 0Ah
|
|
CMOS_WRITE
|
|
|
|
;
|
|
; Check for valid DS data
|
|
;
|
|
cmp bh, 0 ; Was data all zeros?
|
|
je short icm90
|
|
|
|
cmp bl, 0 ; was CRC valid?
|
|
jnz short icm90
|
|
|
|
cmp ecx, 19 ; Is century before 19?
|
|
jb short icm90
|
|
|
|
cmp ecx, 20 ; Is century after 20?
|
|
ja short icm90
|
|
|
|
;
|
|
; Setup for DS century byte
|
|
;
|
|
mov byte ptr _HalpSerialNumber+0, 'D'
|
|
mov byte ptr _HalpSerialNumber+1, 'S'
|
|
mov _HalpSerialLen, 10
|
|
|
|
mov eax, RTC_OFFSET_CENTURY_DS
|
|
mov _HalpCmosCenturyOffset, eax
|
|
endif
|
|
|
|
icm90: pop edi
|
|
pop esi
|
|
pop ebx
|
|
stdRET _HalpInitializeCmos
|
|
|
|
stdENDP _HalpInitializeCmos
|
|
|
|
endif
|
|
|
|
INIT ends
|
|
|
|
_TEXT SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
|
|
;++
|
|
;
|
|
; ULONG
|
|
; HalpGetCmosData(
|
|
; IN ULONG SourceLocation
|
|
; IN ULONG SourceAddress
|
|
; IN ULONG ReturnBuffer
|
|
; IN PUCHAR ByteCount
|
|
; )
|
|
;
|
|
; 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 read from
|
|
;
|
|
; ReturnBuffer : address in system memory for return data
|
|
;
|
|
; ByteCount : number of bytes to be read
|
|
;
|
|
; Returns:
|
|
;
|
|
; Number of byte actually read.
|
|
;
|
|
;--
|
|
|
|
SourceLocation equ 2*4[ebp]
|
|
SourceAddress equ 3*4[ebp]
|
|
ReturnBuffer equ 4*4[ebp]
|
|
ByteCount equ 5*4[ebp]
|
|
|
|
cPublicProc _HalpGetCmosData ,4
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push ebx
|
|
push edi
|
|
|
|
;
|
|
; NOTE: The spinlock is needed even in the UP case, because
|
|
; the resource is also used in an interrupt handler (profiler).
|
|
; If we own the spinlock in this routine, and we service
|
|
; the profiler interrupt (which will wait for the spinlock forever),
|
|
; then we have a hosed system.
|
|
;
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
|
|
xor edx, edx ; initialize return data length
|
|
mov ecx, ByteCount
|
|
|
|
or ecx, ecx ; validate requested byte count
|
|
jz HalpGetCmosDataExit ; if no work to do, exit
|
|
|
|
mov edx, SourceAddress
|
|
mov edi, ReturnBuffer
|
|
|
|
mov eax, SourceLocation ; cmos or extended cmos?
|
|
cmp eax, 1
|
|
je ECmosReadByte
|
|
cmp eax, 0
|
|
jne HalpGetCmosDataExit
|
|
|
|
CmosReadByte:
|
|
cmp edx, 0ffH ; validate cmos source address
|
|
ja HalpGetCmosDataExit ; if out of range, exit
|
|
mov al, dl
|
|
out CmosAddressPort, al
|
|
in al, CmosDataPort
|
|
mov [edi], al
|
|
inc edx
|
|
inc edi
|
|
dec ecx
|
|
jnz CmosReadByte
|
|
jmp SHORT HalpGetCmosDataExit
|
|
|
|
ECmosReadByte:
|
|
cmp edx,0ffffH ; validate ecmos source address
|
|
ja HalpGetCmosDataExit ; if out of range, exit
|
|
mov al, dl
|
|
out ECmosAddressLsbPort, al
|
|
mov al, dh
|
|
out ECmosAddressMsbPort, al
|
|
in al, ECmosDataPort
|
|
mov [edi], al
|
|
inc edx
|
|
inc edi
|
|
dec ecx
|
|
jnz ECmosReadByte
|
|
|
|
HalpGetCmosDataExit:
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
mov eax, edx ; return bytes read
|
|
sub eax, SourceAddress ; subtract the initial offset
|
|
|
|
pop edi
|
|
pop ebx
|
|
pop ebp
|
|
|
|
stdRET _HalpGetCmosData
|
|
|
|
stdENDP _HalpGetCmosData
|
|
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpSetCmosData(
|
|
; IN ULONG SourceLocation
|
|
; IN ULONG SourceAddress
|
|
; IN ULONG ReturnBuffer
|
|
; IN PUCHAR ByteCount
|
|
; )
|
|
;
|
|
; This routine writes the requested number of bytes to CMOS/ECMOS
|
|
;
|
|
; Arguments:
|
|
;
|
|
; SourceLocation : where data is to be written to CMOS or ECMOS
|
|
; 0 - CMOS, 1 - ECMOS
|
|
;
|
|
; SourceAddress : address in CMOS/ECMOS where data is to write to.
|
|
;
|
|
; ReturnBuffer : address in system memory for data to write
|
|
;
|
|
; ByteCount : number of bytes to be write
|
|
;
|
|
; Returns:
|
|
;
|
|
; Number of byte actually written.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalpSetCmosData ,4
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push ebx
|
|
push edi
|
|
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
|
|
xor edx, edx ; initialize return data length
|
|
mov ecx, ByteCount
|
|
|
|
or ecx, ecx ; validate requested byte count
|
|
jz HalpSetCmosDataExit ; if no work to do, exit
|
|
|
|
mov edx, SourceAddress
|
|
mov edi, ReturnBuffer
|
|
|
|
mov eax, SourceLocation ; cmos or extended cmos?
|
|
cmp eax, 1
|
|
je ECmosWriteByte
|
|
cmp eax, 0
|
|
jne HalpSetCmosDataExit
|
|
|
|
CmosWriteByte:
|
|
cmp edx, 0ffH ; validate cmos source address
|
|
ja HalpSetCmosDataExit ; if out of range, exit
|
|
mov al, dl
|
|
out CmosAddressPort, al
|
|
mov al, [edi]
|
|
out CmosDataPort, al
|
|
inc edx
|
|
inc edi
|
|
dec ecx
|
|
jnz CmosWriteByte
|
|
jmp SHORT HalpSetCmosDataExit
|
|
|
|
ECmosWriteByte:
|
|
cmp edx,0ffffH ; validate ecmos source address
|
|
ja HalpSetCmosDataExit ; if out of range, exit
|
|
mov al, dl
|
|
out ECmosAddressLsbPort, al
|
|
mov al, dh
|
|
out ECmosAddressMsbPort, al
|
|
mov al, [edi]
|
|
out ECmosDataPort, al
|
|
inc edx
|
|
inc edi
|
|
dec ecx
|
|
jnz ECmosWriteByte
|
|
|
|
HalpSetCmosDataExit:
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
mov eax, edx ; return bytes written
|
|
sub eax, SourceAddress ; subtract the initial offset
|
|
|
|
pop edi
|
|
pop ebx
|
|
pop ebp
|
|
|
|
stdRET _HalpSetCmosData
|
|
|
|
stdENDP _HalpSetCmosData
|
|
|
|
|
|
page ,132
|
|
subttl "Read System Time"
|
|
;++
|
|
;
|
|
; 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.
|
|
;
|
|
;--
|
|
|
|
;
|
|
; Parameters:
|
|
;
|
|
|
|
KrctPTimeFields equ [esp+4]
|
|
|
|
cPublicProc _HalpReadCmosTime ,1
|
|
|
|
if DBG
|
|
krctwait0:
|
|
mov ecx, 100
|
|
krctwait:
|
|
push ecx
|
|
else
|
|
krctwait:
|
|
endif
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
mov ecx, 100
|
|
align 4
|
|
krct00: mov al, 0Ah ; Specify register A
|
|
CMOS_READ ; (al) = CMOS register A
|
|
test al, CMOS_STATUS_BUSY ; Is time update in progress?
|
|
jz short krct10 ; if z, no, go read CMOS time
|
|
loop short krct00 ; otherwise, try again.
|
|
|
|
;
|
|
; CMOS is still busy. Try again ...
|
|
;
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
if DBG
|
|
pop ecx
|
|
loop short krctwait
|
|
stdCall _DbgBreakPoint
|
|
jmp short krctwait0
|
|
else
|
|
jmp short krctwait
|
|
endif
|
|
align 4
|
|
if DBG
|
|
krct10:
|
|
pop ecx
|
|
else
|
|
krct10:
|
|
endif
|
|
mov edx, KrctPTimeFields ; (edx)-> TIME_FIELDS structure
|
|
xor eax, eax ; (eax) = 0
|
|
|
|
;
|
|
; The RTC is only accurate within one second. So
|
|
; add a half a second so that we are closer, on average,
|
|
; to the right answer.
|
|
;
|
|
mov word ptr [edx].TfMilliseconds, 500 ; add a half a second
|
|
|
|
mov al, RTC_OFFSET_SECOND
|
|
CMOS_READ ; (al) = second in BCD form
|
|
BCD_TO_BIN ; (ax) = second
|
|
mov [edx].TfSecond, ax ; set second in TIME_FIELDS
|
|
|
|
mov al, RTC_OFFSET_MINUTE
|
|
CMOS_READ ; (al) = minute in BCD form
|
|
BCD_TO_BIN ; (ax) = Minute
|
|
mov [edx].TfMinute, ax ; set minute in TIME_FIELDS
|
|
|
|
mov al, RTC_OFFSET_HOUR
|
|
CMOS_READ ; (al) = hour in BCD form
|
|
BCD_TO_BIN ; (ax) = Hour
|
|
mov [edx].TfHour, ax ; set hour in TIME_FIELDS
|
|
|
|
mov al, RTC_OFFSET_DAY_OF_WEEK
|
|
CMOS_READ ; (al) = day-of-week in BCD form
|
|
BCD_TO_BIN ; (ax) = day-of-week
|
|
mov [edx].TfWeekday, ax ; set Weekday in TIME_FIELDS
|
|
|
|
mov al, RTC_OFFSET_DATE_OF_MONTH
|
|
CMOS_READ ; (al) = date-of-month in BCD form
|
|
BCD_TO_BIN ; (ax) = date_of_month
|
|
mov [edx].TfDay, ax ; set day in TIME_FIELDS
|
|
|
|
mov al, RTC_OFFSET_MONTH
|
|
CMOS_READ ; (al) = month in BCD form
|
|
BCD_TO_BIN ; (ax) = month
|
|
mov [edx].TfMonth, ax ; set month in TIME_FIELDS
|
|
|
|
mov al, RTC_OFFSET_YEAR
|
|
CMOS_READ ; (al) = year in BCD form
|
|
BCD_TO_BIN ; (ax) = year
|
|
push eax ; save year in stack
|
|
|
|
push edx ; preserve edx
|
|
call _HalpGetCmosCenturyByte ; (al)= century byte in BCD form
|
|
BCD_TO_BIN ; (ax) = century
|
|
pop edx
|
|
|
|
mov ah, 100
|
|
mul ah ; (ax) = century * 100
|
|
pop ecx ; (cx) = year
|
|
add ax, cx ; (ax)= year
|
|
|
|
cmp ax, 1900 ; Is year > 1900
|
|
jb short krct40
|
|
cmp ax, 1920 ; and < 1920
|
|
jae short krct40
|
|
add ax, 100 ; Compensate for century field
|
|
|
|
krct40:
|
|
mov [edx].TfYear, ax ; set year in TIME_FIELDS
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
stdRET _HalpReadCmosTime
|
|
|
|
stdENDP _HalpReadCmosTime
|
|
|
|
page ,132
|
|
subttl "Write System Time"
|
|
;++
|
|
;
|
|
; 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.
|
|
;
|
|
;--
|
|
|
|
;
|
|
; Parameters:
|
|
;
|
|
|
|
KrctPTimeFields equ [esp+4]
|
|
|
|
cPublicProc _HalpWriteCmosTime ,1
|
|
|
|
if DBG
|
|
kwctwait0:
|
|
mov ecx, 100
|
|
kwctwait:
|
|
push ecx
|
|
else
|
|
kwctwait:
|
|
endif
|
|
stdCall _HalpAcquireCmosSpinLock
|
|
mov ecx, 100
|
|
align 4
|
|
kwct00: mov al, 0Ah ; Specify register A
|
|
CMOS_READ ; (al) = CMOS register A
|
|
test al, CMOS_STATUS_BUSY ; Is time update in progress?
|
|
jz short kwct10 ; if z, no, go write CMOS time
|
|
loop short kwct00 ; otherwise, try again.
|
|
|
|
;
|
|
; CMOS is still busy. Try again ...
|
|
;
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
if DBG
|
|
pop ecx
|
|
loop short kwctwait
|
|
stdCall _DbgBreakPoint
|
|
jmp short kwctwait0
|
|
else
|
|
jmp short kwctwait
|
|
endif
|
|
align 4
|
|
if DBG
|
|
kwct10:
|
|
pop ecx
|
|
else
|
|
kwct10:
|
|
endif
|
|
mov edx, KrctPTimeFields ; (edx)-> TIME_FIELDS structure
|
|
|
|
mov al, [edx].TfSecond ; Read second in TIME_FIELDS
|
|
BIN_TO_BCD
|
|
mov ah, al
|
|
mov al, RTC_OFFSET_SECOND
|
|
CMOS_WRITE
|
|
|
|
mov al, [edx].TfMinute ; Read minute in TIME_FIELDS
|
|
BIN_TO_BCD
|
|
mov ah, al
|
|
mov al, RTC_OFFSET_MINUTE
|
|
CMOS_WRITE
|
|
|
|
mov al, [edx].TfHour ; Read Hour in TIME_FIELDS
|
|
BIN_TO_BCD
|
|
mov ah, al
|
|
mov al, RTC_OFFSET_HOUR
|
|
CMOS_WRITE
|
|
|
|
mov al, [edx].TfWeekDay ; Read WeekDay in TIME_FIELDS
|
|
BIN_TO_BCD
|
|
mov ah, al
|
|
mov al, RTC_OFFSET_DAY_OF_WEEK
|
|
CMOS_WRITE
|
|
|
|
mov al, [edx].TfDay ; Read day in TIME_FIELDS
|
|
BIN_TO_BCD
|
|
mov ah, al
|
|
mov al, RTC_OFFSET_DATE_OF_MONTH
|
|
CMOS_WRITE
|
|
|
|
mov al, [edx].TfMonth ; Read month in TIME_FIELDS
|
|
BIN_TO_BCD
|
|
mov ah, al
|
|
mov al, RTC_OFFSET_MONTH
|
|
CMOS_WRITE
|
|
|
|
mov ax, [edx].TfYear ; Read Year in TIME_FIELDS
|
|
cmp ax, 9999
|
|
jbe short kwct15
|
|
mov ax, 9999
|
|
|
|
align 4
|
|
kwct15:
|
|
mov cl, 100
|
|
div cl ; [ax]/[cl]->al=quo, ah=rem
|
|
push eax
|
|
BIN_TO_BCD
|
|
|
|
push eax
|
|
call _HalpSetCmosCenturyByte
|
|
|
|
pop eax
|
|
mov al, ah ; [al] = Year
|
|
BIN_TO_BCD
|
|
mov ah, al ; [ah] = year in BCD
|
|
mov al, RTC_OFFSET_YEAR
|
|
CMOS_WRITE
|
|
|
|
stdCall _HalpReleaseCmosSpinLock
|
|
|
|
stdRET _HalpWriteCmosTime
|
|
|
|
stdENDP _HalpWriteCmosTime
|
|
|
|
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Acquires a spinlock to access the cmos chip. The cmos chip is
|
|
; accessed at different irql levels, so to be safe, we 'cli'.
|
|
; We could replace that to raise irql to PROFILE_LEVEL, but that's
|
|
; a lot of code.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Interrupt is disabled.
|
|
; Irql level not affected.
|
|
; Flags saved in _HalpHardwareLockFlags.
|
|
;--
|
|
|
|
cPublicProc _HalpAcquireCmosSpinLock ,0
|
|
public _HalpAcquireSystemHardwareSpinLock@0
|
|
_HalpAcquireSystemHardwareSpinLock@0:
|
|
push eax
|
|
|
|
Arsl10: pushfd
|
|
cli
|
|
lea eax, _HalpSystemHardwareLock
|
|
ACQUIRE_SPINLOCK eax, Arsl20
|
|
pop _HalpHardwareLockFlags ; save flags for release S.L.
|
|
pop eax
|
|
stdRET _HalpAcquireCmosSpinLock
|
|
|
|
Arsl20: popfd
|
|
|
|
Arsl30:
|
|
YIELD
|
|
ifndef NT_UP
|
|
cmp _HalpRebootNow, 0
|
|
jnz short Arsl50
|
|
endif
|
|
TEST_SPINLOCK eax, <short Arsl30>
|
|
jmp short ARsl10
|
|
|
|
Arsl50:
|
|
ifndef NT_UP
|
|
mov eax, _HalpRebootNow
|
|
call eax
|
|
int 3 ; should not return
|
|
endif
|
|
|
|
stdENDP _HalpAcquireCmosSpinLock
|
|
|
|
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Release spinlock, and restore flags to the state it was before
|
|
; acquiring the spinlock.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Interrupts restored to their state before acquiring spinlock.
|
|
; Irql level not affected.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalpReleaseCmosSpinLock ,0
|
|
public _HalpReleaseSystemHardwareSpinLock@0
|
|
_HalpReleaseSystemHardwareSpinLock@0:
|
|
push eax
|
|
;
|
|
; restore eflags as it was before acquiring spinlock. Put it on
|
|
; stack before releasing spinlock (so other cpus cannot overwrite
|
|
; it with their own eflags).
|
|
;
|
|
push _HalpHardwareLockFlags ; old eflags on stack.
|
|
lea eax, _HalpSystemHardwareLock
|
|
RELEASE_SPINLOCK eax
|
|
popfd ; restore eflags.
|
|
pop eax
|
|
stdRET _HalpReleaseCmosSpinLock
|
|
stdENDP _HalpReleaseCmosSpinLock
|
|
|
|
;++
|
|
;
|
|
; UCHAR
|
|
; HalpGetCmosCenturyByte (
|
|
; VOID
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine gets Century byte from CMOS.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; (al) = Century byte in BCD form.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalpGetCmosCenturyByte, 0
|
|
|
|
mov eax, _HalpCmosCenturyOffset
|
|
|
|
if DBG
|
|
|
|
;
|
|
; Make sure the HalpCmosCenturyOffset is initialized
|
|
;
|
|
|
|
cmp eax, 0
|
|
jne short @f
|
|
|
|
int 3
|
|
@@:
|
|
endif
|
|
test eax, BANK1
|
|
jnz short rcb50
|
|
|
|
CMOS_READ ; (al) = century in BCD form
|
|
stdRET _HalpGetCmosCenturyByte
|
|
|
|
rcb50: mov edx, eax
|
|
|
|
mov al, 0Ah
|
|
CMOS_READ
|
|
|
|
mov dh, al ; save it for restore
|
|
or al, 10h ; Set DV0 = 1
|
|
|
|
mov ah, al
|
|
mov al, 0Ah ; Write register A
|
|
CMOS_WRITE
|
|
|
|
mov al, dl ; century offset
|
|
CMOS_READ
|
|
mov dl, al ; save it
|
|
|
|
mov ah, dh ; Restore DV0
|
|
mov al, 0Ah ; Write register A
|
|
CMOS_WRITE
|
|
|
|
mov al, dl
|
|
stdRET _HalpGetCmosCenturyByte
|
|
|
|
stdENDP _HalpGetCmosCenturyByte
|
|
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpSetCmosCenturyByte (
|
|
; UCHAR Century
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine sets Century byte in CMOS.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Century - Supplies the value for CMOS century byte
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalpSetCmosCenturyByte, 1
|
|
|
|
mov eax, _HalpCmosCenturyOffset
|
|
if DBG
|
|
|
|
;
|
|
; Make sure the HalpCmosCenturyOffset is initialized
|
|
;
|
|
|
|
cmp eax, 0
|
|
jne short @f
|
|
|
|
int 3
|
|
@@:
|
|
endif
|
|
|
|
test eax, BANK1
|
|
jnz short scb50
|
|
|
|
mov ah, [esp+4] ; (ah) = Century in BCD form
|
|
CMOS_WRITE
|
|
stdRET _HalpSetCmosCenturyByte
|
|
|
|
|
|
scb50: mov edx, eax
|
|
|
|
mov al, 0Ah
|
|
CMOS_READ
|
|
|
|
mov dh, al ; save it for restore
|
|
or al, 10h ; Set DV0 = 1
|
|
|
|
mov ah, al
|
|
mov al, 0Ah ; Write register A
|
|
CMOS_WRITE
|
|
|
|
mov ah, [esp+4] ; (ah) = Century in BCD form
|
|
mov al, dl ; century offset
|
|
CMOS_WRITE
|
|
|
|
mov ah, dh ; Restore DV0
|
|
mov al, 0Ah ; Write register A
|
|
CMOS_WRITE
|
|
stdRET _HalpSetCmosCenturyByte
|
|
|
|
stdENDP _HalpSetCmosCenturyByte
|
|
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpCpuID (
|
|
; ULONG InEax,
|
|
; PULONG OutEax,
|
|
; PULONG OutEbx,
|
|
; PULONG OutEcx,
|
|
; PULONG OutEdx
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Executes the CPUID instruction and returns the registers from it
|
|
;
|
|
; Only available at INIT time
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Return Value:
|
|
;
|
|
;--
|
|
cPublicProc _HalpCpuID,5
|
|
|
|
push ebx
|
|
push esi
|
|
|
|
mov eax, [esp+12]
|
|
db 0fh, 0a2h ; CPUID
|
|
|
|
mov esi, [esp+16] ; return EAX
|
|
mov [esi], eax
|
|
|
|
mov esi, [esp+20] ; return EBX
|
|
mov [esi], ebx
|
|
|
|
mov esi, [esp+24] ; return ECX
|
|
mov [esi], ecx
|
|
|
|
mov esi, [esp+28] ; return EDX
|
|
mov [esi], edx
|
|
|
|
pop esi
|
|
pop ebx
|
|
|
|
stdRET _HalpCpuID
|
|
|
|
stdENDP _HalpCpuID
|
|
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpFlushTLB (
|
|
; VOID
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Flush the current TLB.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Return Value:
|
|
;
|
|
;--
|
|
cPublicProc _HalpFlushTLB, 0
|
|
.586p
|
|
pushfd
|
|
push ebx
|
|
push esi
|
|
|
|
cli
|
|
mov esi, cr3
|
|
|
|
mov ecx, PCR[PcPrcb]
|
|
cmp byte ptr [ecx].PbCpuID, 0
|
|
jz short ftb50
|
|
|
|
mov eax, 1 ; Get feature bits
|
|
cpuid ; (note "cpuid" between CR3 reload fixes
|
|
; P6 B step errata #11)
|
|
|
|
test edx, 2000h ; see if 'G' bit is supported
|
|
jz short ftb50
|
|
|
|
mov ecx, cr4 ; 'G' bit is supported, due global flush
|
|
mov edx, ecx ; Save orginal cr4
|
|
and ecx, not CR4_PGE ; Make sure global bit is disabled
|
|
mov cr4, ecx
|
|
mov cr3, esi ; flush TLB
|
|
mov cr4, edx ; restore cr4
|
|
jmp short ftp99
|
|
|
|
ftb50: mov cr3, esi
|
|
|
|
ftp99: pop esi
|
|
pop ebx
|
|
popfd
|
|
stdRET _HalpFlushTLB
|
|
|
|
.486p
|
|
stdENDP _HalpFlushTLB
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpYieldProcessor (
|
|
; VOID
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
cPublicProc _HalpYieldProcessor
|
|
YIELD
|
|
stdRET _HalpYieldProcessor
|
|
stdENDP _HalpYieldProcessor
|
|
|
|
|
|
_TEXT ends
|
|
|
|
end
|