553 lines
13 KiB
NASM
553 lines
13 KiB
NASM
|
|
title "Processor Idle Handlers"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
;
|
|
; Module Name:
|
|
;
|
|
; cstate.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 _ReadGenAddr, 1
|
|
EXTRNP _KeStallExecutionProcessor, 1
|
|
EXTRNP _KeQueryPerformanceCounter, 1
|
|
extrn _HalpFixedAcpiDescTable:DWORD
|
|
extrn _HalpBroken440BX:byte
|
|
extrn _HalpOutstandingScatterGatherCount:DWORD
|
|
extrn _HalpPiix4:byte
|
|
extrn _PCntAddress:DWORD
|
|
extrn _C2Address:DWORD
|
|
extrn _C3Address:DWORD
|
|
extrn _AcpiC3Win2kCompatable:BYTE
|
|
extrn _Piix4ThrottleFix:DWORD
|
|
|
|
;
|
|
; 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 "Processor2 Idle Handlers"
|
|
|
|
;++
|
|
;typedef struct {
|
|
; ULONGLONG StartTime;
|
|
; ULONGLONG EndTime;
|
|
; ULONG IdleHandlerReserved[4];
|
|
;} PROCESSOR_IDLE_TIMES, *PPROCESSOR_IDLE_TIMES;
|
|
;
|
|
; BOOLEAN
|
|
; FASTCALL
|
|
; Acpi2C1Idle(
|
|
; 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 Acpi2C1Idle, 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 Acpi2C1Idle
|
|
|
|
fstENDP Acpi2C1Idle
|
|
|
|
|
|
;++
|
|
; BOOLEAN
|
|
; FASTCALL
|
|
; Acpi2C2Idle(
|
|
; 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 Acpi2C2Idle, 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:
|
|
|
|
mov edx, _Piix4ThrottleFix
|
|
test edx, edx ; if piix4 and currently throttled,
|
|
jnz short hc1_10 ; jump to C1 handler
|
|
|
|
|
|
;
|
|
; Enter C2 sleep
|
|
;
|
|
|
|
stdCall _ReadGenAddr, <offset _C2Address>
|
|
stdCall _ReadGenAddr, <offset _PCntAddress> ; close processor 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 Acpi2C2Idle
|
|
|
|
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 Acpi2C2Idle
|
|
|
|
;++
|
|
; 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 Halp2Setup440BXWorkaround, 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 Halp2Setup440BXWorkaround
|
|
|
|
fstENDP Halp2Setup440BXWorkaround
|
|
|
|
;++
|
|
; 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 Halp2Complete440BXWorkaround, 1
|
|
cPublicFpo 0,0
|
|
|
|
mov dx, 0cf8h
|
|
mov eax, 80000054h
|
|
out dx, eax
|
|
mov dx, 0cffh
|
|
mov al, cl
|
|
out dx, al
|
|
fstRET Halp2Complete440BXWorkaround
|
|
|
|
fstENDP Halp2Complete440BXWorkaround
|
|
|
|
|
|
;++
|
|
; BOOLEAN
|
|
; FASTCALL
|
|
; Acpi2C3ArbdisIdle(
|
|
; 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 Acpi2C3ArbdisIdle, 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 Acpi2C3ArbdisIdle
|
|
|
|
hac3_checkthrottle:
|
|
|
|
mov edx, _Piix4ThrottleFix
|
|
test edx, edx
|
|
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 Halp2Setup440BXWorkaround
|
|
mov esi, eax
|
|
.endif
|
|
|
|
;
|
|
; Enter C3 sleep
|
|
;
|
|
|
|
stdCall _ReadGenAddr, <offset _C3Address>
|
|
stdCall _ReadGenAddr, <offset _PCntAddress> ; close processor window on entering C3
|
|
|
|
|
|
.if (_HalpBroken440BX)
|
|
mov ecx, esi
|
|
fstCall Halp2Complete440BXWorkaround
|
|
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 Acpi2C3ArbdisIdle
|
|
|
|
hac3piix4:
|
|
sti
|
|
hlt
|
|
jmp short hac3_90
|
|
|
|
|
|
fstENDP Acpi2C3ArbdisIdle
|
|
|
|
_TEXT ends
|
|
|
|
end
|