        title  "mpipia"
;++
;
; Copyright (c) Microsoft Corporation. All rights reserved. 
;
; You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
; If you do not agree to the terms, do not use the code.
;
;
; Module Name:
;
;    mpipia.asm
;
; Abstract:
;
;    This module implements the x86 specific functions required to
;    support multiprocessor systems.
;
;--

.586p
        .xlist
include ks386.inc
include mac386.inc
include callconv.inc
        .list

        EXTRNP  HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
        EXTRNP  HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
        EXTRNP  _HalRequestIpi,1,IMPORT
        EXTRNP  _KiFreezeTargetExecution, 2
ifdef DBGMP
        EXTRNP  _KiPollDebugger
endif
        extrn   _KiProcessorBlock:DWORD

DELAYCOUNT  equ    2000h

_DATA   SEGMENT DWORD PUBLIC 'DATA'

public  _KiSynchPacket
_KiSynchPacket dd  0

_DATA   ENDS



_TEXT  SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;++
;
; BOOLEAN
; KiIpiServiceRoutine (
;     IN PKTRAP_FRAME TrapFrame,
;     IN PKEXCEPTION_FRAME ExceptionFrame
;     )
;
; Routine Description:
;
;     This routine is called at IPI level to process any outstanding
;     interporcessor requests for the current processor.
;
; Arguments:
;
;     TrapFrame - Supplies a pointer to a trap frame.
;
;     ExceptionFrame - Not used.
;
; Return Value:
;
;     A value of TRUE is returned, if one of more requests were service.
;     Otherwise, FALSE is returned.
;
;--

cPublicProc _KiIpiServiceRoutine, 2

ifndef NT_UP

cPublicFpo 2, 3
        push    ebx                     ; save nonvolatile registers
        push    esi                     ;
        push    edi                     ;

        xor     ebx, ebx                ; set exchange value
        xor     edi, edi
        mov     esi, PCR[PcPrcb]        ; get current processor block address

        xchg    dword ptr [esi].PbRequestSummary, ebx
        xchg    dword ptr [esi].PbSignalDone, edi
;
; Check for freeze request or synchronous request.
;

        test    bl, IPI_FREEZE + IPI_SYNCH_REQUEST ; test for freeze or packet
        jnz     short isr50             ; if nz, freeze or synch request

;
; For RequestSummary's other then IPI_FREEZE set return to TRUE
;

        mov     bh, 1                   ; set return value

;
; Check for Packet ready.
;
; If a packet is ready, then get the address of the requested function
; and call the function passing the address of the packet address as a
; parameter.
;

isr10:  mov     edx, edi                ; copy request pack address
        and     edx, NOT 1              ; Clear point to point bit
        jz      short isr20             ; if z set, no packet ready
        push    [edx].PbCurrentPacket + 8 ; push parameters on stack
        push    [edx].PbCurrentPacket + 4 ;
        push    [edx].PbCurrentPacket + 0 ;
        push    edi                     ; push source processor block address
        mov     eax, [edx].PbWorkerRoutine ; get worker routine address
        mov     edx, [esp + 16 + 4*4]   ; get current trap frame address
        mov     [esi].PbIpiFrame, edx   ; save current trap frame address
        call    eax                     ; call worker routine
        mov     bh, 1                   ; set return value

;
; Check for APC interrupt request.
;

isr20:  test    bl, IPI_APC             ; check if APC interrupt requested
        jz      short isr30             ; if z, APC interrupt not requested

        mov     ecx, APC_LEVEL          ; request APC interrupt
        fstCall HalRequestSoftwareInterrupt ;

;
; Check for DPC interrupt request.
;

isr30:  test    bl, IPI_DPC             ; check if DPC interrupt requested
        jz      short isr40             ; if z, DPC interrupt not requested

        mov     ecx, DISPATCH_LEVEL     ; request DPC interrupt
        fstCall HalRequestSoftwareInterrupt ;

isr40:  mov     al, bh                  ; return status
        pop     edi                     ; restore nonvolatile registers
        pop     esi                     ;
        pop     ebx                     ;

        stdRET  _KiIpiServiceRoutine

;
; Freeze or synchronous request
;

isr50:  test    bl, IPI_FREEZE          ; test if freeze request
        jz      short isr60             ; if z, no freeze request

;
; Freeze request is requested
;

        mov     ecx, [esp] + 20         ; get exception frame address
        mov     edx, [esp] + 16         ; get trap frame address
        stdCall _KiFreezeTargetExecution, <edx, ecx> ; freeze execution
        test    bl, not IPI_FREEZE      ; Any other IPI RequestSummary?
        setnz   bh                      ; Set return code accordingly
        test    bl, IPI_SYNCH_REQUEST   ; test if synch request
        jz      isr10                   ; if z, no sync request

;
; Synchronous packet request.   Pointer to requesting PRCB in KiSynchPacket.
;

isr60:  mov     eax, _KiSynchPacket     ; get PRCB of requesting processor
        mov     edx, eax                ; clear low bit in packet address
        btr     edx, 0                  ;
        push    [edx].PbCurrentPacket+8 ; push parameters on stack
        push    [edx].PbCurrentPacket+4 ;
        push    [edx].PbCurrentPacket+0 ;
        push    eax                     ; push source processor block address
        mov     eax, [edx].PbWorkerRoutine ; get worker routine address
        mov     edx, [esp + 16 + 4*4]   ; get current trap frame address
        mov     [esi].PbIpiFrame, edx   ; save current trap frame address
        call    eax                     ; call worker routine
        mov     bh, 1                   ; set return value
        jmp     isr10                   ; join common code

else

        xor     eax, eax                ; return FALSE

        stdRET  _KiIpiServiceRoutine

endif

stdENDP _KiIpiServiceRoutine


;++
;
; VOID
; FASTCALL
; KiIpiSend (
;    IN KAFFINITY TargetProcessors,
;    IN KIPI_REQUEST Request
;    )
;
; Routine Description:
;
;    This function requests the specified operation on the targt set of
;    processors.
;
; Arguments:
;
;    TargetProcessors (ecx) - Supplies the set of processors on which the
;        specified operation is to be executed.
;
;    IpiRequest (edx) - Supplies the request operation code.
;
; Return Value:
;
;     None.
;
;--

cPublicFastCall KiIpiSend, 2

ifndef NT_UP

cPublicFpo 0, 2
        push    esi                     ; save registers
        push    edi                     ;
        mov     esi, ecx                ; save target processor set

        shr     ecx, 1                  ; shift out first bit
        lea     edi, _KiProcessorBlock  ; get processor block array address
        jnc     short is20              ; if nc, not in target set

is10:   mov     eax, [edi]              ; get processor block address
   lock or      [eax].PbRequestSummary, edx ; set request summary bit

is20:   shr     ecx, 1                  ; shift out next bit
        lea     edi, [edi+4]            ; advance to next processor
        jc      short is10              ; if target, go set summary bit
        jnz     short is20              ; if more, check next

        stdCall _HalRequestIpi, <esi>   ; request IPI interrupts on targets

        pop     edi                     ; restore registers
        pop     esi                     ;
endif
        fstRet  KiIpiSend

fstENDP KiIpiSend

;++
;
; VOID
; KiIpiSendPacket (
;     IN KAFFINITY TargetProcessors,
;     IN PKIPI_WORKER WorkerFunction,
;     IN PVOID Parameter1,
;     IN PVOID Parameter2,
;     IN PVOID Parameter3
;     )
;
; Routine Description:
;
;    This routine executes the specified worker function on the specified
;    set of processors.
;
; Arguments:
;
;   TargetProcessors [esp + 4] - Supplies the set of processors on which the
;       specfied operation is to be executed.
;
;   WorkerFunction [esp + 8] - Supplies the address of the worker function.
;
;   Parameter1 - Parameter3 [esp + 12] - Supplies worker function specific
;       parameters.
;
; Return Value:
;
;     None.
;
;--*/

cPublicProc _KiIpiSendPacket, 5

ifndef NT_UP

cPublicFpo 5, 2
        push    esi                     ; save registers
        push    edi                     ;

;
; Store function address and parameters in the packet area of the PRCB on
; the current processor.
;

        mov     edx, PCR[PcPrcb]        ; get current processor block address
        mov     ecx, [esp] + 12         ; get target processor set
        mov     eax, [esp] + 16         ; get worker function address
        mov     edi, [esp] + 20         ; get worker function parameter 1
        mov     esi, [esp] + 24         ; get worker function parameter 2

        mov     [edx].PbTargetSet, ecx  ; set target processor set
        mov     [edx].PbWorkerRoutine, eax ; set worker function address

        mov     eax, [esp] + 28         ; get worker function parameter 3
        mov     [edx].PbCurrentPacket, edi ; set work function parameters
        mov     [edx].PbCurrentPacket + 4, esi ;
        mov     [edx].PbCurrentPacket + 8, eax ;

;
; Determine whether one and only one bit is set in the target set.
;

        mov     edi, ecx                ; copy recipient target set
        lea     esi, dword ptr [ecx-1]  ; compute target set - 1
        and     edi, esi                ; and target set with target set - 1
        neg     edi                     ; negate result (CF = 0 if zero)
        sbb     edi, edi                ; compute result as one if the
        inc     edi                     ; target set has one bit set
        jnz     short isp5              ; if nz, target set has one bit
        mov     [edx].PbPacketBarrier, ecx ; set packet barrier
isp5:   add     edx, edi                ; set low order bit if appropriate

;
; Loop through the target processors and send the packet to the specified
; recipients.
;

        shr     ecx, 1                  ; shift out first bit
        lea     edi, _KiProcessorBlock  ; get processor block array address
        jnc     short isp30             ; if nc, not in target set
isp10:  mov     esi, [edi]              ; get processor block address
isp20:  mov     eax, [esi].PbSignalDone ; check if packet being processed
        or      eax, eax                ;
        jne     isp40                   ; if ne, packet being processed

   lock cmpxchg [esi].PbSignalDone, edx ; compare and exchange

        jnz     short isp20             ; if nz, exchange failed

isp30:  shr     ecx, 1                  ; shift out next bit
        lea     edi, [edi+4]            ; advance to next processor
        jc      short isp10             ; if c, in target set
        jnz     short isp30             ; if nz, more target processors

        mov     ecx, [esp] + 12         ; set target processor set
        stdCall _HalRequestIpi, <ecx>   ; send IPI to targets

        pop     edi                     ; restore register
        pop     esi                     ;
endif

        stdRet  _KiIpiSendPacket

ifndef NT_UP
isp40:
        YIELD
        jmp     short isp20             ; retry packet test
endif

stdENDP _KiIpiSendPacket

;++
;
; VOID
; FASTCALL
; KiIpiSignalPacketDone (
;     IN PKIPI_CONTEXT Signaldone
;     )
;
; Routine Description:
;
;     This routine signals that a processor has completed a packet by
;     clearing the calling processor's set member of the requesting
;     processor's packet.
;
; Arguments:
;
;     SignalDone (ecx) - Supplies a pointer to the processor block of the
;         sending processor.
;
;         N.B. The low order bit of signal done is set if the target set
;              has one and only one bit set.
;
; Return Value:
;
;     None.
;
;--

cPublicFastCall KiIpiSignalPacketDone, 1

ifndef NT_UP

        btr     ecx, 0                          ; test and clear bit 0
        jc      short spd20                     ; if c set, only one bit set
        mov     edx, PCR[PcPrcb]                ; get current processor block address
        mov     eax, [edx].PbSetMember          ; get processor bit
if DBG
        test    [ecx].PbTargetSet, eax
        jne     @f
        int     3
@@:
endif

   lock xor     [ecx].PbTargetSet, eax          ; clear processor set member
        jnz     short spd10                     ; if nz, more targets to go
        xor     eax, eax                        ; clear packet barrier
if DBG
        cmp     [ecx].PbPacketBarrier, eax
        jne     @f
        int     3
@@:
endif
        mov     [ecx].PbPacketBarrier, eax      ;

spd10:  fstRET  KiIpiSignalPacketDone

;
; One and only one bit is set in the target set. Since this is the only
; processor that can clear any bits in the target set, the target set can
; be cleared with a simple write.
;

spd20:  xor     eax, eax                        ; clear target set
if DBG
        cmp     [ecx].PbTargetSet, eax
        jne     @f
        int     3
@@:
endif
        mov     [ecx].PbTargetSet, eax          ;

endif

        fstRET  KiIpiSignalPacketDone

fstENDP KiIpiSignalPacketDone


;++
;
; VOID
; FASTCALL
; KiIpiSignalPacketDoneAndStall (
;     IN PKIPI_CONTEXT Signaldone
;     IN PULONG ReverseStall
;     )
;
; Routine Description:
;
;     This routine signals that a processor has completed a packet by
;     clearing the calling processor's set member of the requesting
;     processor's packet, and then stalls on the reverse stall value
;
; Arguments:
;
;     SignalDone (ecx) - Supplies a pointer to the processor block of the
;         sending processor.
;
;         N.B. The low order bit of signal done is set if the target set
;              has one and only one bit set.
;
;     ReverseStall (edx) - Supplies a pointer to the reverse stall barrier
;
; Return Value:
;
;     None.
;
;--

cPublicFastCall KiIpiSignalPacketDoneAndStall, 2
cPublicFpo 0, 2

ifndef NT_UP

        push    ebx                             ; save register
        mov     ebx, dword ptr [edx]            ; get current value of barrier
        btr     ecx, 0                          ; test and clear bit 0
        jc      short sps10                     ; if c set, only one bit set
        mov     eax, PCR[PcPrcb]                ; get processor block address
        mov     eax, [eax].PbSetMember          ; get processor bit

if DBG
        test    [ecx].PbTargetSet, eax          ; Make sure the bit is set in the mask
        jne     @f
        int     3
@@:
endif

   lock xor     [ecx].PbTargetSet, eax          ; clear processor set member
        jnz     short sps20                     ; if nz, more targets to go
        xor     eax, eax                        ; clear packet barrier
if DBG
        cmp     [ecx].PbPacketBarrier, eax      ; Make sure the barrier is still set
        jne     @f
        int     3
@@:
endif
        mov     [ecx].PbPacketBarrier, eax      ;
        jmp     short sps20                     ;

;
; One and only one bit is set in the target set. Since this is the only
; processor that can clear any bits in the target set, the target set can
; be cleared with a simple write.
;

sps10:  xor     eax, eax                        ; clear target set

if DBG
        cmp    [ecx].PbTargetSet, eax          ; Make sure the bit is set in the mask
        jne     @f
        int     3
@@:
endif
        mov     [ecx].PbTargetSet, eax          ;

;
; Wait for barrier value to change.
;

sps20:  mov     eax, DELAYCOUNT
sps30:  cmp     ebx, dword ptr [edx]            ; barrier set?
        jne     short sps90                     ; yes, all done

        YIELD
        dec     eax                             ; P54C pre C2 workaround
        jnz     short sps30                     ; if eax = 0, generate bus cycle

ifdef DBGMP
        stdCall _KiPollDebugger                 ; Check for debugger ^C
endif

;
; There could be a freeze execution outstanding.  Check and clear
; freeze flag.
;

.errnz IPI_FREEZE - 4
        mov     eax, PCR[PcPrcb]                ; get processor block address
   lock btr     [eax].PbRequestSummary, 2       ; Generate bus cycle
        jnc     short sps20                     ; Freeze pending?

cPublicFpo 0,4
        push    ecx                             ; save target processor block
        push    edx                             ; save barrier address
        stdCall _KiFreezeTargetExecution, <[eax].PbIpiFrame, 0> ;
        pop     edx                             ; restore barrier address
        pop     ecx                             ; restore target processor block
        jmp     short sps20                     ;

sps90:  pop     ebx                             ; restore register

endif
        fstRET  KiIpiSignalPacketDoneAndStall

fstENDP KiIpiSignalPacketDoneAndStall

_TEXT   ends
        end

