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 EXTRNP _HalpWakeupTimeElapsed,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, 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, ; ; 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 ebx, SysHandler or ebx, ebx jz short has10 mov ecx, SysContext push ecx call ebx ; Call the system state handler mov Status, eax test eax, eax jnz has_s4_wake ; If not success, exit ; ; Check the RTC one last time in case it expired while we were writing ; out the Hiberfile. ; stdCall _HalpWakeupTimeElapsed or eax, eax jnz has_s4_wake ; If expired, 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 has_slept mov ax, SlpTypB out dx, ax has_slept: stdCall _HalpAcpiPostSleep, ; ; 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 ; ; 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 ; ; 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, snp_exit: mov esp, ebp pop ebp pop ebx pop edi pop esi stdRET _HalpSetupRealModeResume stdENDP _HalpSetupRealModeResume PAGELK ends end