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