2025-04-27 07:49:33 -04:00

636 lines
15 KiB
NASM

title "Processor Idle Handlers"
;++
;
; Copyright (c) 1989 Microsoft Corporation
;
; Module Name:
;
; ixcstate.asm
;
; Abstract:
;
; This module implements the code for idling the processor
; in low power modes.
;
; Author:
;
; Jake Oshins (jakeo) March 10, 1997
;
; Environment:
;
; Kernel mode only.
;
; Revision History:
;
;--
.386p
.xlist
include hal386.inc
include callconv.inc ; calling convention macros
include ntacpi.h
.list
EXTRNP _KeStallExecutionProcessor, 1
EXTRNP _KeQueryPerformanceCounter, 1
extrn _HalpFixedAcpiDescTable:DWORD
extrn _HalpBroken440BX:byte
extrn _HalpOutstandingScatterGatherCount:DWORD
extrn _HalpPiix4:byte
extrn _PBlkAddress:DWORD
extrn _AcpiC3Win2kCompatable:BYTE
PIIX4_THROTTLE_FIX EQU 10000h
;
; Defines for PROCESSOR_IDLE_TIMES structure
;
Idle struc
StartTimeLow dd ?
StartTimeHigh dd ?
EndTimeLow dd ?
EndTimeHigh dd ?
Pm1aState dw ?
Pm1bState dw ?
Idle ends
;
; Defines for GEN_ADDR
;
GenAddr struc
AddressSpaceID db ?
BitWidth db ?
BitOffset db ?
GenAddrReserved db ?
AddressLow dd ?
AddressHigh dd ?
GenAddr ends
_TEXT SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Processor Idle Handlers"
;++
;typedef struct {
; ULONGLONG StartTime;
; ULONGLONG EndTime;
; ULONG IdleHandlerReserved[4];
;} PROCESSOR_IDLE_TIMES, *PPROCESSOR_IDLE_TIMES;
;
; BOOLEAN
; FASTCALL
; AcpiC1Idle(
; OUT PPROCESSOR_IDLE_TIMES IdleTimes
; )
;
; Routine Description:
;
; This is the Idle Handler for processor state C1. It
; basically just stops the processor until an interrupt
; occurs.
;
; Arguments:
;
; (ecx) = IdleTimes - beginning and ending time stamps
;
; Return Value:
;
; TRUE if immediate demotion required
;
;--
cPublicFastCall AcpiC1Idle, 1
cPublicFpo 0, 2
push ecx
;
; record the StartTime of this idle
;
stdCall _KeQueryPerformanceCounter, <0>
mov ecx, [esp]
mov [ecx].StartTimeLow, eax
mov [ecx].StartTimeHigh, edx
;
hc1_10: ; Note the C2 handler will jump to this target in the C1 handler
; this is a piix4 and throttling is enabled (since on piix4 trying
; to enter C2 while throttling does not work)
;
sti
hlt
;
; record the EndTime of this idle
;
stdCall _KeQueryPerformanceCounter, <0>
pop ecx
mov [ecx].EndTimeLow, eax
mov [ecx].EndTimeHigh, edx
xor eax, eax
fstRET AcpiC1Idle
fstENDP AcpiC1Idle
;++
; BOOLEAN
; FASTCALL
; AcpiC2Idle(
; OUT PPROCESSOR_IDLE_TIMES IdleTimes
; )
;
; Routine Description:
;
; This is the Idle Handler for processor state C2. It
; shuts part of the processor off.
;
; Arguments:
;
; (ecx) = IdleTimes - beginning and ending time stamps
;
; Return Value:
;
; TRUE if immediate demotion required
;
;--
cPublicFastCall AcpiC2Idle, 1
cPublicFpo 0,2
push ecx
;
; record the StartTime of this idle
;
stdCall _KeQueryPerformanceCounter, <0>
mov ecx, [esp]
mov [ecx].StartTimeLow, eax
mov [ecx].StartTimeHigh, edx
;
; Check if BM_RLD has been set and needs clear
;
test byte ptr [ecx].Pm1aState, SCI_EN
jnz short hac2_piix4fix
hac2_10:
;
; Get the I/O port we need for C2. This codes
; assumes that nobody will ever do an ACPI 1.0 C2
; state on an MP machine.
;
mov edx, [_PBlkAddress].AddressLow
test edx, PIIX4_THROTTLE_FIX ; Piix4 throttling work-around
jnz short hc1_10 ; JUMP TO C1 handler
add edx, P_LVL2
;
; put the processor to bed
;
in al, dx
sub edx, P_LVL2-P_CNT ; Read P_CNT register to close processor
in eax, dx ; window on entering C2
;
; record the EndTime of this idle
;
stdCall _KeQueryPerformanceCounter, <0>
pop ecx
mov [ecx].EndTimeLow, eax
mov [ecx].EndTimeHigh, edx
xor eax, eax ; return FALSE
fstRET AcpiC2Idle
hac2_piix4fix:
;
; Clear the BM_RLD settings for piix4
;
mov edx, [PM1a_CNT]
mov ax, [ecx].Pm1aState
mov [ecx].Pm1aState, 0
out dx, ax
mov edx, [PM1b_CNT]
or edx, edx
jz short hac2_10
mov ax, [ecx].Pm1bState
out dx, ax
jmp short hac2_10
fstENDP AcpiC2Idle
;++
; UCHAR
; FASTCALL
; HalpSetup440BXWorkaround(
; )
;
; Routine Description:
;
; This function provides part of the workaround for
; broken 440BX chips.
;
; Arguments:
;
; none
;
; Return Value:
;
; the previous contents of 440BX DRAM Control Register (57h)
;
;--
cPublicFastCall HalpSetup440BXWorkaround, 0
cPublicFpo 0,0
mov dx, 0cf8h
mov eax, 80000054h
out dx, eax
mov dx, 0cffh
in al, dx
mov cl, al
or al, 7
out dx, al
push ecx
stdCall _KeStallExecutionProcessor <15>
pop ecx
mov dx, 0cf8h
mov eax, 80000054h
out dx, eax
mov dx, 0cffh
in al, dx
and al, 0f8h
out dx, al
movzx eax, cl
fstRET HalpSetup440BXWorkaround
fstENDP HalpSetup440BXWorkaround
;++
; VOID
; FASTCALL
; HalpComplete440BXWorkaround(
; UCHAR DramControl
; )
;
; Routine Description:
;
; This function provides the other part of the workaround for
; broken 440BX chips.
;
; Arguments:
;
; the previous contents of 440BX DRAM Control Register (57h)
;
; Return Value:
;
; none
;
;--
cPublicFastCall HalpComplete440BXWorkaround, 1
cPublicFpo 0,0
mov dx, 0cf8h
mov eax, 80000054h
out dx, eax
mov dx, 0cffh
mov al, cl
out dx, al
fstRET HalpComplete440BXWorkaround
fstENDP HalpComplete440BXWorkaround
;++
; BOOLEAN
; FASTCALL
; AcpiC3ArbdisIdle(
; OUT PPROCESSOR_IDLE_TIMES IdleTimes
; )
;
; Routine Description:
;
; This is the Idle Handler for processor state C3. It
; shuts part of the processor off.
;
; This routine assumes that the machine supports
; PM2_CNT.ARB_DIS.
;
; UNIPROCESSOR only. Due to a piix4 errata this function needs to
; mess with a global register (bm_rld)
;
; Arguments:
;
; IdleTimes - beginning and ending time stamps
;
; Return Value:
;
; none
;
;--
cPublicFastCall AcpiC3ArbdisIdle, 1
cPublicFpo 0, 3
push ebx
mov ebx, ecx
push esi
;
; Another PIIX4 bug. If the IDE controller might
; create any busmaster traffic, then we will hang
; the machine. (So would any F-type DMA traffic,
; but in NT, you can disable that by failing to
; add it to your ACPI BIOS. So the onus for worrying
; about it falls to the OEMs.) If the count
; of outstanding scatter/gather operations is
; non-zero, bail.
;
.if (_HalpPiix4)
mov eax, [_HalpOutstandingScatterGatherCount]
mov eax, [eax]
test eax, eax
jnz short hac3_abort2
.endif
;
; record the StartTime of this idle
;
stdCall _KeQueryPerformanceCounter, <0>
mov [ebx].StartTimeLow, eax
mov [ebx].StartTimeHigh, edx
;
; check to see if busmaster activity has occurred
; if so, clear it and if it stays set, just get out
;
mov edx, [PM1a_EVT]
mov ecx, [PM1b_EVT]
or ecx, ecx ; is there a 2nd bm_sts to check?
jz short hac3_10 ; no, skip it
in ax, dx ; read bm_sts
test al, BM_STS ; if not set, keep going
jz short hac3_9
mov al, _AcpiC3Win2kCompatable ; check to see if we should behave like win2k
test al, al
jnz short hac3_abort
mov eax, BM_STS ; clear the bit
out dx, ax
in ax, dx ; and reread it
test al, BM_STS ; if still set, abort c3
jnz short hac3_abort
hac3_9:
xchg edx, ecx ; read next bm_sts
hac3_10:
in ax, dx ; read bm_sts
test al, BM_STS
jz short hac3_checkthrottle ; if not set, keep going
mov al, _AcpiC3Win2kCompatable ; check to see if we should behave like win2k
test al, al
jnz short hac3_abort
mov eax, BM_STS
out dx, ax ; clear the bit
in ax, dx ; and reread it
test al, BM_STS
jz short hac3_checkthrottle ; if not set, keep going
hac3_abort:
;
; Bus master activity occured, abort C3 wait by returning
; non-zero to the OS. Clear bus master status for next
; attempt.
;
mov eax, BM_STS
out dx, ax
mov edx, ecx
or ecx, ecx
jz short hac3_abort2
out dx, ax
hac3_abort2:
; return non-zero
pop esi
pop ebx
fstRET AcpiC3ArbdisIdle
hac3_checkthrottle:
;
; Check to see if piix4 is in a state where it can't use C3
;
mov edx, [_PBlkAddress].AddressLow
test edx, PIIX4_THROTTLE_FIX
jnz hac3piix4
;
; If BM_RLD not set, then set it
;
test byte ptr [ebx].Pm1aState, SCI_EN
jnz short hac3_20
mov edx, [PM1a_CNT]
in ax, dx ; read first pm1a_cnt
mov [ebx].Pm1aState, ax ; save the original value
or ax, BM_RLD ; set BM_RLD
out dx, ax ; update the register
mov edx, [PM1b_CNT]
or edx, edx ; is there a 2nd pm1b_cnt register?
jz short hac3_20 ; no, done with this
in ax, dx ; read second pm1b_cnt
mov [ebx].Pm1bState, ax ; save the original value
or ax, BM_RLD ; set BM_RLD
out dx, ax ; update the register
hac3_20:
;
; disable bus masters
;
mov edx, [PM2_CNT_BLK]
in al, dx
mov cl, al ; save current PM2_CNT_BLK
or al, ARB_DIS ; set PM2_CNT.ARB_DIS
out dx, al ; write PM2_CNT_BLK
;
; Work around potentially broken 440BX
;
; N.B. This function will never be called on a machine with more
; than one processor
;
; N.B. We don't call the PCI configuration functions for several
; reasons. First, that would involve buying a bunch of stack.
; Second, we don't want the huge latency that would involve,
; as this workaround is about DRAM refresh timings.
;
.if (_HalpBroken440BX)
push ecx
pushfd
cli
fstCall HalpSetup440BXWorkaround
mov esi, eax
.endif
;
; put processor into C3 sleep
;
mov edx, [_PBlkAddress].AddressLow
add edx, P_LVL3
in al, dx ; Go to C3 sleep
sub edx, P_LVL3 ; Read P_CNT register to close processor
in eax, dx ; window on entering C3
.if (_HalpBroken440BX)
mov ecx, esi
fstCall HalpComplete440BXWorkaround
popfd
pop ecx
.endif
;
; Restore bus master access
;
mov edx, [PM2_CNT_BLK]
mov al, cl ; Get saved copy of pm2_cnt_blk
out dx, al
hac3_90:
;
; record the EndTime of this idle
;
stdCall _KeQueryPerformanceCounter, <0>
mov [ebx].EndTimeLow, eax
mov [ebx].EndTimeHigh, edx
xor eax, eax
pop esi
pop ebx
fstRET AcpiC3ArbdisIdle
hac3piix4:
sti
hlt
jmp short hac3_90
fstENDP AcpiC3ArbdisIdle
if 0
;++
; BOOLEAN
; FASTCALL
; HalAcpiC3WbinvdIdle(
; OUT PPROCESSOR_IDLE_TIMES IdleTimes
; )
;
; Routine Description:
;
; This is the Idle Handler for processor state C3. It
; shuts part of the processor off.
;
; This routine assumes that the machine supports
; WBINVD.
;
; Arguments:
;
; IdleTimes - beginning and ending time stamps
;
; Return Value:
;
; none
;
;--
cPublicFastCall HalAcpiC3WbinvdIdle, 1
cPublicFpo 0, 1
push ecx
;
; record the StartTime of this idle
;
fstCall HalpQueryPerformanceCounter ; move current counter into edx:eax
mov ecx, [esp]
mov [ecx].StartTimeLow, eax
mov [ecx].StartTimeHigh, edx
;
; get the I/O port we need for C3
;
mov edx, PCR[PcPrcb]
mov edx, [edx].PbHalReserved.PcrPblk
add edx, P_LVL3
.586p
wbinvd ; flush the processor cache
.386p
;
; put the processor to bed
;
mov edx, ecx
in al, dx
sub edx, P_LVL2-P_CNT ; Read P_CNT register to close processor
in eax, dx ; window on entering C3
;
; record the EndTime of this idle
;
fstCall HalpQueryPerformanceCounter ; move current counter into edx:eax
pop ecx
mov [ecx].EndTimeLow, eax
mov [ecx].EndTimeHigh, edx
xor eax, eax ; return FALSE
fstRET HalAcpiC3WbinvdIdle
fstENDP HalAcpiC3WbinvdIdle
endif
_TEXT ends
end