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

459 lines
12 KiB
NASM

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 _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
@@:
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
inc [edi.SPx_flag] ; 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
mov edx, ParamBlockAddress
;
; 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