windows-nt/Source/XPSP1/NT/base/hals/halacpi/i386/ixsstate.asm
2020-09-26 16:20:57 +08:00

721 lines
17 KiB
NASM

title "Sleep Handlers"
;++
;
; Copyright (c) 1989 Microsoft Corporation
;
; Module Name:
;
; ixsstate.asm
;
; Abstract:
;
; This module implements the code for putting the machine to
; sleep.
;
; Author:
;
; Jake Oshins (jakeo) March 13, 1997
;
; Environment:
;
; Kernel mode only.
;
; Revision History:
;
;--
.386p
.xlist
include hal386.inc
include callconv.inc ; calling convention macros
include i386\ix8259.inc
include i386\kimacro.inc
include mac386.inc
include i386\ixcmos.inc
include xxacpi.h
include i386\ixslpctx.inc
.list
EXTRNP _HalpAcpiPreSleep ,1
EXTRNP _HalpAcpiPostSleep ,1
EXTRNP _HalpPostSleepMP, 2
EXTRNP _HalpReenableAcpi, 0
EXTRNP _StartPx_BuildRealModeStart,1
EXTRNP KfLowerIrql, 1,,FASTCALL
EXTRNP _KeGetCurrentIrql,0
EXTRNP _HalpSaveProcessorStateAndWait,2
EXTRNP _KeStallExecutionProcessor, 1
EXTRNP _HalpClearSlpSmiStsInICH,0
extrn _HalpLowStubPhysicalAddress:DWORD
extrn _KeSaveStateForHibernate:proc
extrn _HalpFixedAcpiDescTable:DWORD
extrn _HalpWakeVector:DWORD
extrn _HalpTiledCr3Addresses:DWORD
extrn _HalpVirtAddrForFlush:DWORD
extrn _HalpPteForFlush:DWORD
extrn _HalpHiberProcState:DWORD
extrn _HalpBroken440BX:byte
_DATA SEGMENT DWORD PUBLIC 'DATA'
ALIGN dword
public HalpSleepSync
HalpSleepSync dd 0
public HalpBarrier
HalpBarrier dd 0
_DATA ends
PAGELK SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Sleep Handlers"
; VOID
; FASTCALL
; HalpAcpiFlushCache(
; VOID
; )
; /*++
;
; Routine Description:
;
; This is called to flush everything from the caches.
;
; Arguments:
;
; none
;
; Return Value:
;
; none
;
; --*/
;
; eax - offset within a page
; ebx - stride size
; ecx - address of PTE we are manipulating
; edi - Physical Address of page
; esi - Virtual Address of page used for flush
; ebp - Flush Limit
;
FlushBase equ 100000h
PageLength equ 4096
PteValid equ 1
PteWrite equ 2
PteAccessed equ 20h
PteDirty equ 40h
PteBits equ (PteValid or PteWrite)
cPublicFastCall HalpAcpiFlushCache, 0
cPublicFpo 0, 0
mov eax, [FADT_FLAGS]
test eax, WBINVD_SUPPORTED or WBINVD_FLUSH
jz short hafc10
.586p
wbinvd
fstRET HalpAcpiFlushCache
hafc10:
push ebp
push ebx
push esi
push edi
movzx eax, word ptr [FLUSH_STRIDE]
mov ebx, eax ; save the stride size
movzx ecx, word ptr [FLUSH_SIZE]
mul ecx
add eax, FlushBase
mov ebp, eax ; ebp <- ending physical address
;
; Iterate across all cache lines
;
mov edi, FlushBase ; start at 1MB, physical
mov esi, [_HalpVirtAddrForFlush]
mov ecx, [_HalpPteForFlush]
hafc20:
; put the right physical page into the PTE
mov edx, PteBits ; mask off the page
or edx, edi
mov [ecx], edx
invlpg [esi]
add edi, PageLength ; next physical page
xor eax, eax
; flush a cache line
hafc30: mov edx, [esi][eax]
add eax, ebx
cmp eax, PageLength
jl short hafc30
cmp edi, ebp
jl short hafc20
pop edi
pop esi
pop ebx
pop ebp
.386p
fstRET HalpAcpiFlushCache
fstENDP HalpAcpiFlushCache
;++
; 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
; NTSTATUS
; HaliAcpiSleep(
; IN PVOID Context,
; IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL,
; IN PVOID SystemContext,
; IN LONG NumberProcessors,
; IN volatile PLONG Number
; )
; /*++
;
; Routine Description:
;
; This is called by the Policy Manager to enter Sx.
;
; Arguments:
;
; Context - unused
;
; NumberProcessors - currently unused
;
; Number - currently unused
;
; Return Value:
;
; none
;
; --*/
Context equ [ebp+8]
SysHandler equ [ebp+12]
SysContext equ [ebp+16]
NumberProc equ [ebp+20]
Barrier equ [ebp+24]
Pm1aEvt equ [ebp-4]
Pm1bEvt equ [ebp-8]
Status equ [ebp-12]
SlpTypA equ [ebp-14]
SlpTypB equ [ebp-16]
ThisProc equ [ebp-20]
OldIrql equ [ebp-24]
PrevDRAM equ [ebp-28]
FrameSize equ 28
cPublicProc _HaliAcpiSleep, 5
cPublicFpo 5, 4
push ebp
mov ebp, esp
sub esp, FrameSize
push ebx
push esi
push edi
pushfd
cli
mov edi, HalpSleepSync ; Get current sleep sync value
xor eax, eax
mov Status, eax
;
; Get current IRQL
;
stdCall _KeGetCurrentIrql
mov OldIrql, eax
;
; Send all
;
mov al, PCR[PcNumber]
or al, al ; Is this processor 0?
jnz has_wait ; if not, set it waiting
mov HalpBarrier, 0 ; init Barrier, processor 0 does it
;
; Make sure the other processors have saved their
; state and begun to spin.
;
lock inc [HalpSleepSync] ; account for this proc
has1: YIELD
mov eax, [HalpSleepSync]
cmp eax, NumberProc
jnz short has1
;
; Take care of chores (RTC, interrupt controller, etc.)
stdCall _HalpAcpiPreSleep, <Context>
or al, al ; check for failure
jz has_slept ; if FALSE, then don't sleep at all
;
; If we will be losing processor state, save it
;
mov eax, Context
test eax, SLEEP_STATE_FIRMWARE_RESTART shl CONTEXT_FLAG_SHIFT
jz short has2
lea eax, haswake
stdCall _HalpSetupRealModeResume, <eax>
;
; Record the values in the SLP_TYP registers
;
has2:
mov edx, [PM1a_CNT]
in ax, dx
mov SlpTypA, ax
mov edx, [PM1b_CNT]
or edx, edx
jz short has5
in ax, dx
mov SlpTypB, ax
has5:
;
; The hal has all of it's state saved into ram and is ready
; for the power down. If there's a system state handler give
; it a shot
;
mov eax, SysHandler
or eax, eax
jz short has10
mov ecx, SysContext
push ecx
call eax ; Call the system state handler
mov Status, eax
test eax, eax
jnz has_s4_wake ; If not success, exit
has10:
mov esi, Context
mov edx, [PM1a_EVT]
mov ecx, [PM1b_EVT]
or ecx, ecx
jnz short has20
mov ecx, edx
has20:
mov Pm1aEvt, ecx
mov Pm1bEvt, edx ; save PM1a_EVT & PM1b_EVT address
;
; Reset WAK_STS
;
mov eax, WAK_STS
out dx, ax ; clear PM1a WAK_STS
mov edx, ecx
out dx, ax ; clear PM1b WAK_STS
;
; Flush the caches if necessary
;
mov eax, SLEEP_STATE_FLUSH_CACHE shl CONTEXT_FLAG_SHIFT
test eax, esi
jz short @f
fstCall HalpAcpiFlushCache
@@:
;
; Work around 440BX bug. Criteria is that we have one of
; the broken BX parts and we are not hibernating, which
; we know because the SysHandler is 0.
;
mov eax, SysHandler
.if (_HalpBroken440BX && (eax == 0))
fstCall HalpSetup440BXWorkaround
movzx eax, al
mov PrevDRAM, eax
.endif
;
; Issue SLP commands to PM1a_CNT and PM1b_CNT
;
mov edx, [PM1a_CNT]
mov ecx, esi
and ecx, 0fh ; nibble 0 is 1a sleep type
shl ecx, SLP_TYP_SHIFT ; put it in position
or ecx, SLP_EN ; enable sleep ********
in ax, dx
and ax, CTL_PRESERVE ; preserve some bits
or ax, cx
out dx, ax
mov edx, [PM1b_CNT]
or edx, edx
jz short has30
mov ecx, esi
and ecx, 0f0h ; nibble 1 is 1b sleep type
shl ecx, (SLP_TYP_SHIFT-4) ; put it in position
or ecx, SLP_EN ; enable sleep *********
in ax, dx
and ax, CTL_PRESERVE ; preserve some bits
or ax, cx
out dx, ax
has30:
;
; Wait for sleep to be over
;
mov ecx, Pm1bEvt
mov edx, Pm1aEvt ; retrieve PM1_EVT & PM1b_EVT
has40: in ax, dx
test ax, WAK_STS
xchg edx, ecx
jz short has40
;
; Finish 440BX workaround
;
mov eax, SysHandler
.if (_HalpBroken440BX && (eax == 0))
mov ecx, PrevDRAM
fstCall HalpComplete440BXWorkaround
.endif
;
; Invalidate the processor cache so that any stray gamma
; rays (I'm serious) that may have flipped cache bits
; while in S1 will be ignored.
;
; Honestly. Intel asked for this. I'm serious.
;
;.586
; invd
;.386
haswake:
;
; Hack around ICH2/ASUS BIOS.
;
stdCall _HalpClearSlpSmiStsInICH
;
; Restore the SLP_TYP registers. (So that embedded controllers
; and BIOSes can be sure that we think the machine is awake.)
;
mov edx, [PM1a_CNT]
mov ax, SlpTypA
out dx, ax
mov edx, [PM1b_CNT]
or edx, edx
jz short has60
mov ax, SlpTypB
out dx, ax
has60:
stdCall _HalpAcpiPostSleep, <Context>
has_slept:
;
; Notify other processor of completion
;
mov HalpSleepSync, 0
jmp short has_90
has_wait:
xor eax, eax
mov edx, Context
test edx, SLEEP_STATE_OFF shl CONTEXT_FLAG_SHIFT
jnz has_wait2 ; if going to S5, don't save context
mov al, PCR[PcNumber] ; get processor number
mov edx, ProcessorStateLength ; get size of proc state
mul dx ; generate an index into HalpHiberProcState
add eax, _HalpHiberProcState ; add the index to base
has_wait2:
lea edx, HalpSleepSync
stdCall _HalpSaveProcessorStateAndWait <eax, edx>
;
; Wait for next phase
;
hasw10: YIELD
cmp HalpSleepSync, 0 ; wait for barrier to move
jne short hasw10
;
; All phases complete, exit
;
has_90:
;
; Restore each processor's APIC state.
;
lea eax, HalpBarrier
stdCall _HalpPostSleepMP <NumberProc, eax>
;
; Restore caller's IRQL
;
mov ecx, OldIrql
fstCall KfLowerIrql
;
; Exit
;
mov HalpSleepSync, 0
mov eax, Status
popfd
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
stdRET _HaliAcpiSleep
has_s4_wake:
stdCall _HalpReenableAcpi
jmp haswake
stdENDP _HaliAcpiSleep
;++
;
; BOOLEAN
; HalpSetupRealModeResume (
; )
;
; Routine Description:
;
; This routine is called by the kernel durning kernel initialization
; to obtain more processors. It is called until no more processors
; are available.
;
; If another processor exists this function is to initialize it to
; the passed in processorstate structure, and return TRUE.
;
; If another processor does not exists or if the processor fails to
; start, then a FALSE is returned.
;
; Also note that the loader block has been setup for the next processor.
; The new processor logical thread number can be obtained from it, if
; required.
;
; In order to use the Startup IPI the real mode startup code must be
; page aligned. The MpLowStubPhysicalAddress has always been page
; aligned but because the PxParamBlock was placed first in this
; segment the real mode code has been something other than page aligned.
; This has been changed by making the first entry in the PxParamBlock
; a jump instruction to the real mode startup code.
;
; Arguments:
;
; WakeupReturnAddress - address that processor should return to
; after it has been asleep
;
; Return Value:
;
;
;--
WakeupReturnAddress equ dword ptr [ebp + 20]
;
; Local variables
;
PxFrame equ [ebp - size PxParamBlock]
LWarmResetVector equ [ebp - size PxParamBlock - 4]
LStatusCode equ [ebp - size PxParamBlock - 8]
LCmosValue equ [ebp - size PxParamBlock - 12]
CallingEbp equ [ebp]
CallingEsi equ [ebp + 12]
CallingEdi equ [ebp + 8]
CallingEbx equ [ebp + 4]
CallingEsp equ 24 ; relative to current ebp
cPublicProc _HalpSetupRealModeResume ,1
cPublicFpo 4, 80
push esi ; Save required registers
push edi
push ebx
push ebp ; save ebp
mov ebp, esp ; Save Frame
sub esp, size PxParamBlock + 12 ; Make room for local vars
xor eax, eax
mov LStatusCode, eax
;
; Fill in the firmware wakeup vector
;
mov eax, _HalpLowStubPhysicalAddress
mov ecx, [_HalpWakeVector]
mov [ecx], eax
;
; Save the processor context
;
lea edi, PxFrame.SPx_PB
push edi
call _KeSaveStateForHibernate ; _cdecl function
add esp, 4
;
; Get a CR3 for the starting processor
;
mov eax, [_HalpTiledCr3Addresses] ; the low 32-bits of processor 0's CR3
mov eax, [eax] ; physical address will be here
mov PxFrame.SPx_TiledCR3, eax ; Newly contructed CR3
mov PxFrame.SPx_P0EBP, ebp ; Stack pointer
lea edi, PxFrame.SPx_PB.PsContextFrame
mov eax, WakeupReturnAddress
mov dword ptr [edi].CsEip, eax ; make a copy of remaining
mov eax, CallingEbx ; registers which need
mov dword ptr [edi].CsEbx, eax ; loaded
mov eax, CallingEsi
mov dword ptr [edi].CsEsi, eax
mov eax, CallingEdi
mov dword ptr [edi].CsEdi, eax
mov eax, CallingEbp
mov dword ptr [edi].CsEbp, eax
mov eax, ebp
add eax, CallingEsp
mov dword ptr [edi].CsEsp, eax
lea eax, PxFrame
stdCall _StartPx_BuildRealModeStart, <eax>
snp_exit:
mov esp, ebp
pop ebp
pop ebx
pop edi
pop esi
stdRET _HalpSetupRealModeResume
stdENDP _HalpSetupRealModeResume
PAGELK ends
end