|
|
title "Sleep Context" ;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; ixslpctx.asm ; ; Abstract: ; ; This module implements the code for saving processor ; context before putting the machine to sleep. It also ; contains the code for building a page that a processor ; in real mode can jump to in order to transition into ; p-mode and assume a thread context. ; ; Author: ; ; Jake Oshins (jakeo) March 13, 1998 ; ; Environment: ; ; Kernel mode only. ; ; Revision History: ; ; Much of this code has been moved from halmps\i386\mpsproca.asm. ; ;--
.386p .xlist include hal386.inc include callconv.inc ; calling convention macros include apic.inc include i386\ixslpctx.inc include mac386.inc .list
extrn _HalpLowStub:DWORD extrn _HalpIsNXEnabled@0:proc extrn _KeSaveStateForHibernate:proc ifdef ACPI_HAL EXTRNP HalpAcpiFlushCache, 0,,FASTCALL endif
PAGELK16 SEGMENT DWORD PUBLIC USE16 'CODE' ; start 16 bit code
;++ ; ; VOID ; _StartPx_RMStub ; ; Routine Description: ; ; When a new processor is started, it starts in real-mode and is ; sent to a copy of this function which has been copied into low memory. ; (below 1m and accessable from real-mode). ; ; Once CR0 has been set, this function jmp's to a StartPx_PMStub ; ; Arguments: ; none ; ; Return Value: ; does not return, jumps to StartPx_PMStub ; ;-- cPublicProc _StartPx_RMStub ,0 cli
db 066h ; load the GDT lgdt fword ptr cs:[SPx_PB.PsSpecialRegisters.SrGdtr]
db 066h ; load the IDT lidt fword ptr cs:[SPx_PB.PsSpecialRegisters.SrIdtr]
mov eax, cs:[SPx_TiledCR3]
nop ; Fill - Ensure 13 non-page split nop ; accesses before CR3 load nop ; (P6 errata #11 stepping B0) nop nop nop nop nop nop nop nop nop
mov cr3, eax
; ; Restore CR4 to enable Page Size Extensions ; before we got real CR3 which might use Large Page. ; If SrCr4 is non-zero, then CR4 exists ;
mov eax, dword ptr cs:[SPx_PB.PsSpecialRegisters.SrCr4] or eax, eax jz @f .586p mov cr4, eax .386p @@:
; ; Check whether NX should be enabled for this processor ;
test cs:[SPx_flag], SPX_FLAG_NX jz @f
; ; NX should be enabled. We can assume that this processor can ; support NX mode (and that it is in PAE mode) ;
.586p push edx mov ecx, 0c0000080h rdmsr or eax, 0800h mov ecx, 0c0000080h wrmsr .386p pop edx
@@:
mov ebp, dword ptr cs:[SPx_P0EBP] mov ecx, dword ptr cs:[SPx_PB.PsContextFrame.CsSegDs] mov ebx, dword ptr cs:[SPx_PB.PsSpecialRegisters.SrCr3] mov eax, dword ptr cs:[SPx_PB.PsSpecialRegisters.SrCr0] mov edi, dword ptr cs:[SPx_flat_addr]
mov cr0, eax ; into prot mode
db 066h db 0eah ; reload cs:eip SPrxPMStub dd 0 SPrxFlatCS dw 0
_StartPx_RMStub_Len equ $ - _StartPx_RMStub stdENDP _StartPx_RMStub
PAGELK16 ends ; End 16 bit code
PAGELK SEGMENT PARA PUBLIC 'CODE' ; Start 32 bit code ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
;++ ; ; VOID ; StartPx_PMStub ; ; Routine Description: ; ; This function completes the processor's state loading, and signals ; the requesting processor that the state has been loaded. ; ; Arguments: ; ebx - requested CR3 for this processors_state ; cx - requested ds for this processors_state ; ebp - EBP of P0 ; edi - p-mode address of startup block ; ; Return Value: ; does not return - completes the loading of the processors_state ; ;-- align dword ; to make sure we don't cross a page boundry ; before reloading CR3
cPublicProc _StartPx_PMStub ,0
; process is now in the load image copy of this function. ; (ie, it's not the low memory copy)
mov cr3, ebx ; get real CR3 mov ds, cx ; set real ds
lea esi, [edi].SPx_PB.PsSpecialRegisters
lldt word ptr ds:[esi].SrLdtr ; load ldtr
; ; Force the TSS descriptor into a non-busy state, so we don't fault ; when we load the TR. ; mov eax, ds:[esi].SrGdtr+2 ; (eax)->GDT base xor ecx, ecx mov cx, word ptr ds:[esi].SrTr add eax, 5 add eax, ecx ; (eax)->TSS Desc. Byte and byte ptr [eax],NOT 2
ltr word ptr ds:[esi].SrTr ; load tss
lea edx, [edi].SPx_PB.PsContextFrame mov es, word ptr ds:[edx].CsSegEs ; Set other selectors mov fs, word ptr ds:[edx].CsSegFs mov gs, word ptr ds:[edx].CsSegGs mov ss, word ptr ds:[edx].CsSegSs
cld ; make lodsd ascending (below) xor eax, eax ; disable debug registers while mov dr7, eax ; setting them.
add esi, SrKernelDr0
.errnz (SrKernelDr1 - SrKernelDr0 - 1 * 4) .errnz (SrKernelDr2 - SrKernelDr0 - 2 * 4) .errnz (SrKernelDr3 - SrKernelDr0 - 3 * 4) .errnz (SrKernelDr6 - SrKernelDr0 - 4 * 4) .errnz (SrKernelDr7 - SrKernelDr0 - 5 * 4)
lodsd mov dr0, eax ; load dr0-dr7 lodsd mov dr1, eax lodsd mov dr2, eax lodsd mov dr3, eax lodsd mov dr6, eax lodsd mov dr7, eax
mov esp, dword ptr ds:[edx].CsEsp mov ecx, dword ptr ds:[edx].CsEcx
push dword ptr ds:[edx].CsEflags popfd ; load eflags
push dword ptr ds:[edx].CsEip ; make a copy of remaining push dword ptr ds:[edx].CsEax ; registers which need push dword ptr ds:[edx].CsEbx ; loaded push dword ptr ds:[edx].CsEdx push dword ptr ds:[edx].CsEsi push dword ptr ds:[edx].CsEdi push dword ptr ds:[edx].CsEbp
or [edi.SPx_flag], SPX_FLAG_STARTED ; Signal p0 that we are ; done with it's data ; Set remaining registers pop ebp pop edi pop esi pop edx pop ebx pop eax stdRET _StartPx_PMStub
stdENDP _StartPx_PMStub
;++ ; ; VOID ; StartPx_BuildRealModeStart( ; IN PUCHAR ParamBlock ; ) ; ; Routine Description: ; ; This function sets up the real mode startup page ; ; Arguments: ; ; PxParamBlock -- address of the structure that should end up ; at the beginning of HalpLowStub ; ;--
ParamBlockAddress equ [ebp + 8] cPublicProc _StartPx_BuildRealModeStart ,1
push ebp mov ebp, esp push ebx push esi push edi
; ; Determine whether the new processor should have NX enabled ; ; N.B. The param block arrives here in an uninitialized ; state. ;
call _HalpIsNXEnabled@0 mov edx, ParamBlockAddress mov DWORD PTR [edx].SPx_flag, 0 or al, al jz spbrms_nonx or [edx].SPx_flag, SPX_FLAG_NX
spbrms_nonx:
; ; Build a jmp to the start of the Real mode startup code ; ; This is needed because the Local APIC implementations ; use a Startup IPI that must be Page aligned. The allocation ; code int MP_INIT ensures that this is page aligned. The ; original code was written to place the parameter block first. ; By adding a jump instruction to the start of the parameter block ; we can run either way. ;
mov eax, size PxParamBlock - 3 ; Jump destination relative to ; next instruction shl eax, 8 ; Need room for jmp instruction mov al,0e9h mov [edx].SPx_Jmp_Inst, eax
; ; Save the p-mode address of PxParamBlock ; mov eax, _HalpLowStub mov [edx].SPx_flat_addr, eax
; ; Copy RMStub to low memory ;
mov esi, OFFSET FLAT:_StartPx_RMStub mov ecx, _StartPx_RMStub_Len
mov edi, _HalpLowStub ; Destination was allocated by MpInit add edi, size PxParamBlock ; Parameter Block is placed first rep movsb
; ; Copy the parameter block to low memory ; mov ecx, size PxParamBlock ; Structure length mov esi, ParamBlockAddress ; Parameter Block is placed first mov edi, _HalpLowStub ; Destination Address rep movsb
; ; Now we need to create a pointer allowing the Real Mode code to ; Branch to the Protected mode code ; mov eax, _HalpLowStub ; low memory Address add eax, size PxParamBlock ; Move past the Parameter block
; ; In order to get to the label we need to compute the label offset relative ; to the start of the routine and then use this as a offset from the start of ; the routine ( HalpLowStub + (size PxParamBlock)) in low memory. ; ; The following code creates a pointer to (RMStub - StartPx_RMStub) ; which can then be used to access code locations via code labels directly. ; Since the [eax.Label] results in the address (eax + Label) loading eax ; with the pointer created above results in (RMStub - StartPx_RMStub + Label). ; mov ebx, OFFSET FLAT:_StartPx_RMStub sub eax, ebx ; (eax) = adjusted pointer
; ; Patch the real mode code with a valid long jump address, first CS then offset ; mov bx, word ptr [edx].SPx_PB.PsContextFrame.CsSegCs mov [eax.SPrxFlatCS], bx mov [eax.SPrxPMStub], offset _StartPx_PMStub
pop edi pop esi pop ebx pop ebp
stdRET _StartPx_BuildRealModeStart
stdENDP _StartPx_BuildRealModeStart
subttl "Save Processor State" ;++ ; ; VOID ; HalpSaveProcessorStateAndWait( ; IN PKPROCESSOR_STATE ProcessorState ; ) ; ; Routine Description: ; ; This function saves the volatile, non-volatile and special register ; state of the current processor. ; ; N.B. Floating point state is NOT captured. ; ; Arguments: ; ; ProcessorState (esp+4) - Address of processor state record to fill in. ; ; pBarrier - Address of a value to use as a lock. ; ; Return Value: ; ; None. This function does not return. ; ;--
ProcessorState equ [esp + 8] pBarrier equ dword ptr [esp + 12]
cPublicProc _HalpSaveProcessorStateAndWait,2
push ebx mov ebx, ProcessorState
cmp ebx, 0 ; if this isn't filled in, don't save context jz hspsaw_statesaved ; ; Fill in ProcessorState ;
push ebx call _KeSaveStateForHibernate ; _cdecl function add esp, 4
;; Save return address, not caller's return address mov eax,[esp+4] mov [ebx.PsContextFrame.CsEip],eax
;; Save caller's ebp, not caller's return ebp. mov [ebx.PsContextFrame.CsEbp],ebp
;; Set ESP to value just before this function call lea eax,[esp+16] mov [ebx.PsContextFrame.CsEsp],eax
hspsaw_statesaved: ifdef ACPI_HAL ; ; Flush the cache, as the processor may be about ; to power off. ; fstCall HalpAcpiFlushCache endif ; ; Signal that this processor has saved its state ;
mov ebx, pBarrier lock inc dword ptr [ebx]
; ; Wait for the hibernation file to be written. ; Processor 0 will zero Barrier when it is ; finished. ; ; N.B. We can't return from this function ; before the hibernation file is finished ; because we would be tearing down the very same ; stack that we will be jumping onto when the ; processor resumes. But after the hibernation ; file is written, it doesn't matter, because ; the stack will be restored from disk. ; hspsaw_spin:
YIELD cmp dword ptr [ebx], 0 jne hspsaw_spin
; ; 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
pop ebx
stdRET _HalpSaveProcessorStateAndWait
stdENDP _HalpSaveProcessorStateAndWait
PAGELK ends ; end 32 bit code
end
|