|
|
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
|