/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    allproc.c

Abstract:

    This module allocates and intializes kernel resources required
    to start a new processor, and passes a complete processor state
    structure to the HAL to obtain a new processor.

Author:

    Bernard Lint 31-Jul-96

Environment:

    Kernel mode only.

Revision History:

    Based on MIPS original (David N. Cutler 29-Apr-1993)

--*/


#include "ki.h"

#if defined(KE_MULTINODE)

NTSTATUS
KiNotNumaQueryProcessorNode(
    IN ULONG ProcessorNumber,
    OUT PUSHORT Identifier,
    OUT PUCHAR Node
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, KiNotNumaQueryProcessorNode)
#endif

#endif

#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, KeStartAllProcessors)
#pragma alloc_text(INIT, KiAllProcessorsStarted)

#endif

//
// Define macro to round up to 64-byte boundary and define block sizes.
//

#define ROUND_UP(x) ((sizeof(x) + 63) & (~63))
#define BLOCK1_SIZE (2 * (KERNEL_BSTORE_SIZE + KERNEL_STACK_SIZE) + PAGE_SIZE)
#define BLOCK2_SIZE (ROUND_UP(KPRCB) + ROUND_UP(KNODE) + ROUND_UP(ETHREAD) + 64)

#if !defined(NT_UP)

//
// Define barrier wait static data.
//

ULONG KiBarrierWait = 0;

#endif

#if defined(KE_MULTINODE)

PHALNUMAQUERYPROCESSORNODE KiQueryProcessorNode = KiNotNumaQueryProcessorNode;

//
// Statically preallocate enough KNODE structures to allow MM
// to allocate pages by node during system initialization.  As
// processors are brought online, real KNODE structures are
// allocated in the appropriate memory for the node.
//
// This statically allocated set will be deallocated once the
// system is initialized.
//

#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("INITDATA")
#endif

KNODE KiNodeInit[MAXIMUM_PROCESSORS];

#endif

extern ULONG_PTR KiUserSharedDataPage;
extern ULONG_PTR KiKernelPcrPage;

//
// Define forward referenced prototypes.
//

VOID
KiCalibratePerformanceCounter(
    VOID
    );

VOID
KiCalibratePerformanceCounterTarget (
    IN PULONG SignalDone,
    IN PVOID Count,
    IN PVOID Parameter2,
    IN PVOID Parameter3
    );

VOID
KiOSRendezvous (
    VOID
    );

VOID
KeStartAllProcessors(
    VOID
    )

/*++

Routine Description:

    This function is called during phase 1 initialize on the master boot
    processor to start all of the other registered processors.

Arguments:

    None.

Return Value:

    None.

--*/

{

#if !defined(NT_UP)

    ULONG_PTR MemoryBlock1;
    ULONG_PTR MemoryBlock2;
    ULONG_PTR MemoryBlock3;
    ULONG_PTR PcrAddress;
    ULONG Number;
    ULONG Count;
    PHYSICAL_ADDRESS PcrPage;
    PKPRCB Prcb;
    BOOLEAN Started;
    KPROCESSOR_STATE ProcessorState;
    UCHAR NodeNumber = 0;
    USHORT ProcessorId;
    SIZE_T ProcessorDataSize;
    PKNODE Node;
    NTSTATUS Status;
    PKPCR NewPcr;

#if defined(KE_MULTINODE)

    //
    // In the unlikely event that processor 0 is not on node
    // 0, fix it.
    //


    if (KeNumberNodes > 1) {
        Status = KiQueryProcessorNode(0,
                                      &ProcessorId,
                                      &NodeNumber);

        if (NT_SUCCESS(Status)) {

            //
            // This should never fail.
            //

            if (NodeNumber != 0) {
                KeNodeBlock[0]->ProcessorMask &= ~1I64;
                KeNodeBlock[NodeNumber]->ProcessorMask |= 1;
                KeGetCurrentPrcb()->ParentNode = KeNodeBlock[NodeNumber];
            }
            KeGetCurrentPrcb()->ProcessorId = ProcessorId;
        }
    }

#endif

    //
    // If the registered number of processors is greater than the maximum
    // number of processors supported, then only allow the maximum number
    // of supported processors.
    //

    if (KeRegisteredProcessors > MAXIMUM_PROCESSORS) {
        KeRegisteredProcessors = MAXIMUM_PROCESSORS;
    }

    //
    // Set barrier that will prevent any other processor from entering the
    // idle loop until all processors have been started.
    //

    KiBarrierWait = 1;

    //
    // Initialize the processor state that will be used to start each of
    // processors. Each processor starts in the system initialization code
    // with address of the loader parameter block as an argument.
    //

    Number = 0;
    Count = 1;
    RtlZeroMemory(&ProcessorState, sizeof(KPROCESSOR_STATE));
    ProcessorState.ContextFrame.StIIP = ((PPLABEL_DESCRIPTOR)KiOSRendezvous)->EntryPoint;
    while (Count < KeRegisteredProcessors) {

        Number++;

        if (Number >= MAXIMUM_PROCESSORS) {
            break;
        }

#if defined(KE_MULTINODE)

        Status = KiQueryProcessorNode(Number,
                                      &ProcessorId,
                                      &NodeNumber);
        if (!NT_SUCCESS(Status)) {

            //
            // No such processor, advance to next.
            //

            continue;
        }

        Node = KeNodeBlock[NodeNumber];

#endif

        //
        // Allocate a DPC stack, an idle thread kernel stack, a panic
        // stack, a PCR page, a processor block,  a kernel node structure
        // and an executive thread object. If the allocation fails, stop
        // starting processors.
        //

#if 0
        //PLJTMP: Need to investigate which pieces need to be in KSEG0
        //and allocate the other stuff per node.  plus deal with any alignment
        //padding for the size below based on roundup of BLOCK1_SIZE.
        ProcessorDataSize = BLOCK1_SIZE + BLOCK2_SIZE;

        MemoryBlock1 = (ULONG_PTR)MmAllocateIndependentPages (ProcessorDataSize,
                                                            NodeNumber);
        if ((PVOID)MemoryBlock1 == NULL) {
            break;
        }

        MemoryBlock2 = MemoryBlock1 + BLOCK1_SIZE;

        //
        // Zero the allocated memory.
        //

        RtlZeroMemory((PVOID)MemoryBlock1, ProcessorDataSize);
#else
        MemoryBlock1 = (ULONG_PTR)ExAllocatePool(NonPagedPool, BLOCK1_SIZE);
        if ((PVOID)MemoryBlock1 == NULL) {
            break;
        }

        MemoryBlock2 = (ULONG_PTR)ExAllocatePool(NonPagedPool, BLOCK2_SIZE);
        if ((PVOID)MemoryBlock2 == NULL) {
            ExFreePool((PVOID)MemoryBlock1);
            break;
        }

        //
        // Zero both blocks of allocated memory.
        //

        RtlZeroMemory((PVOID)MemoryBlock1, BLOCK1_SIZE);
        RtlZeroMemory((PVOID)MemoryBlock2, BLOCK2_SIZE);

#endif

        //
        // Set address of idle thread kernel stack in loader parameter block.
        //

        KeLoaderBlock->KernelStack = MemoryBlock1 + KERNEL_STACK_SIZE;

        //
        // Set address of panic stack in loader parameter block.
        //

        KeLoaderBlock->u.Ia64.PanicStack = MemoryBlock1 + KERNEL_BSTORE_SIZE + 
                                                          (2 * KERNEL_STACK_SIZE);

        //
        // Set the address of the processor block and executive thread in the
        // loader parameter block.
        //

        KeLoaderBlock->Prcb = MemoryBlock2;
        KeLoaderBlock->Thread = KeLoaderBlock->Prcb + ROUND_UP(KPRCB) +
                                                      ROUND_UP(KNODE);
        ((PKPRCB)KeLoaderBlock->Prcb)->Number = (UCHAR)Number;

#if defined(KE_MULTINODE)

        //
        // If this is the first processor on this node, use the
        // space allocated for KNODE as the KNODE.
        //

        if (KeNodeBlock[NodeNumber] == &KiNodeInit[NodeNumber]) {
            Node = (PKNODE)(MemoryBlock1 + ROUND_UP(KPRCB));
            *Node = KiNodeInit[NodeNumber];
            KeNodeBlock[NodeNumber] = Node;
        }

        ((PKPRCB)KeLoaderBlock->Prcb)->ParentNode = Node;
        ((PKPRCB)KeLoaderBlock->Prcb)->ProcessorId = ProcessorId;

#else

        ((PKPRCB)KeLoaderBlock->Prcb)->ParentNode = KeNodeBlock[0];

#endif


        //
        // Set the page frame of the PCR page in the loader parameter block.
        //

        PcrAddress = MemoryBlock1 + (2 * (KERNEL_BSTORE_SIZE + KERNEL_STACK_SIZE));
        PcrPage = MmGetPhysicalAddress((PVOID)PcrAddress);
        KeLoaderBlock->u.Ia64.PcrPage = PcrPage.QuadPart >> PAGE_SHIFT;
        KeLoaderBlock->u.Ia64.PcrPage2 = KiUserSharedDataPage;
        KiKernelPcrPage = KeLoaderBlock->u.Ia64.PcrPage;

        //
        // Initialize the NT page table base addresses in PCR
        //

        NewPcr = (PKPCR) PcrAddress;
        NewPcr->PteUbase = PCR->PteUbase;
        NewPcr->PteKbase = PCR->PteKbase;
        NewPcr->PteSbase = PCR->PteSbase;
        NewPcr->PdeUbase = PCR->PdeUbase;
        NewPcr->PdeKbase = PCR->PdeKbase;
        NewPcr->PdeSbase = PCR->PdeSbase;
        NewPcr->PdeUtbase = PCR->PdeUtbase;
        NewPcr->PdeKtbase = PCR->PdeKtbase;
        NewPcr->PdeStbase = PCR->PdeStbase;

        //
        // Attempt to start the next processor. If attempt is successful,
        // then wait for the processor to get initialized. Otherwise,
        // deallocate the processor resources and terminate the loop.
        //

        Started = HalStartNextProcessor(KeLoaderBlock, &ProcessorState);

        if (Started) {

            //
            //  Wait for processor to initialize in kernel,
            //  then loop for another
            //

            while (*((volatile ULONG_PTR *) &KeLoaderBlock->Prcb) != 0) {
                KeYieldProcessor();
            }

#if defined(KE_MULTINODE)

            Node->ProcessorMask |= 1I64 << Number;

#endif

        } else {

#if 0
            MmFreeIndependentPages((PVOID)MemoryBlock1, ProcessorDataSize);
#else
            ExFreePool((PVOID)MemoryBlock1);
            ExFreePool((PVOID)MemoryBlock2);
#endif
            break;

        }

        Count += 1;
    }

    //
    // All processors have been stated.
    //

    KiAllProcessorsStarted();

    //
    // Allow all processor that were started to enter the idle loop and
    // begin execution.
    //

    KiBarrierWait = 0;

#endif

    //
    // Reset and synchronize the performance counters of all processors.
    //

    KiAdjustInterruptTime (0);
    return;
}

#if !defined(NT_UP)
VOID
KiAllProcessorsStarted(
    VOID
    )

/*++

Routine Description:

    This routine is called once all processors in the system
    have been started.

Arguments:

    None.

Return Value:

    None.

--*/

{
    ULONG i;

#if defined(KE_MULTINODE)

    //
    // Make sure there are no references to the temporary nodes
    // used during initialization.
    //

    for (i = 0; i < KeNumberNodes; i++) {
        if (KeNodeBlock[i] == &KiNodeInit[i]) {

            //
            // No processor started on this node so no new node
            // structure has been allocated.   This is possible
            // if the node contains only memory or IO busses.  At
            // this time we need to allocate a permanent node
            // structure for the node.
            //

            KeNodeBlock[i] = ExAllocatePoolWithTag(NonPagedPool,
                                                   sizeof(KNODE),
                                                   '  eK');
            if (KeNodeBlock[i]) {
                *KeNodeBlock[i] = KiNodeInit[i];
            }
        }
    }

    for (i = KeNumberNodes; i < MAXIMUM_CCNUMA_NODES; i++) {
        KeNodeBlock[i] = NULL;
    }

#endif

    if (KeNumberNodes == 1) {

        //
        // For Non NUMA machines, Node 0 gets all processors.
        //

        KeNodeBlock[0]->ProcessorMask = KeActiveProcessors;
    }
}
#endif


#if defined(KE_MULTINODE)

NTSTATUS
KiNotNumaQueryProcessorNode(
    IN ULONG ProcessorNumber,
    OUT PUSHORT Identifier,
    OUT PUCHAR Node
    )

/*++

Routine Description:

    This routine is a stub used on non NUMA systems to provide a 
    consistent method of determining the NUMA configuration rather
    than checking for the presense of multiple nodes inline.

Arguments:

    ProcessorNumber supplies the system logical processor number.
    Identifier      supplies the address of a variable to receive
                    the unique identifier for this processor.
    NodeNumber      supplies the address of a variable to receive
                    the number of the node this processor resides on.

Return Value:

    Returns success.

--*/

{
    *Identifier = (USHORT)ProcessorNumber;
    *Node = 0;
    return STATUS_SUCCESS;
}

#endif