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