/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    allproc.c

Abstract:

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

Author:

    David N. Cutler (davec) 5-May-2000

Environment:

    Kernel mode only.

Revision History:

--*/

#include "ki.h"

//
// Define local macros.
//

#define ROUNDUP16(x) (((x) + 15) & ~15)

//
// Define prototypes for forward referenced functions.
//

VOID
KiCopyDescriptorMemory (
   IN PKDESCRIPTOR Source,
   IN PKDESCRIPTOR Destination,
   IN PVOID Base
   );

VOID
KiSetDescriptorBase (
   IN USHORT Selector,
   IN PKGDTENTRY64 GdtBase,
   IN PVOID Base
   );

#if defined(KE_MULTINODE)

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

#pragma alloc_text(INIT, KiNotNumaQueryProcessorNode)

#endif

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

ULONG KiBarrierWait = 0;

//
// Statically allocate enough KNODE structures to allow memory management
// to allocate pages by node during system initialization. As processors
// are brought online, real KNODE structures are allocated in the correct
// memory for the node.
//

#if defined(KE_MULTINODE)

PHALNUMAQUERYPROCESSORNODE KiQueryProcessorNode = KiNotNumaQueryProcessorNode;

#pragma data_seg("INITDATA")

KNODE KiNodeInit[MAXIMUM_CCNUMA_NODES];

#endif

VOID
KeStartAllProcessors (
    VOID
    )

/*++

Routine Description:

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

Arguments:

    None.

Return Value:

    None.

--*/

{

#if !defined(NT_UP)

    KAFFINITY Affinity;
    ULONG AllocationSize;
    PUCHAR Base;
    PKPCR CurrentPcr = KeGetPcr();
    PVOID DataBlock;
    PKTSS64 DfTssBase;
    PVOID DpcStack;
    PKGDTENTRY64 GdtBase;
    ULONG GdtOffset;
    ULONG IdtOffset;
    PVOID KernelStack;
    PKTSS64 NmiTssBase;
    PKNODE Node;
    UCHAR NodeNumber;
    UCHAR Number;
    PKPCR PcrBase;
    USHORT ProcessorId;
    KPROCESSOR_STATE ProcessorState;
    NTSTATUS Status;
    PKTSS64 SysTssBase;
    PETHREAD Thread;

    //
    // If processor zero is not on node zero, then move it to the appropriate
    // node.
    //

#if defined(KE_MULTINODE)

    if (KeNumberNodes > 1) {
        Status = KiQueryProcessorNode(0, &ProcessorId, &NodeNumber);
        if (NT_SUCCESS(Status)) {
            if (NodeNumber != 0) {
                KeNodeBlock[0]->ProcessorMask &= ~1;
                KeNodeBlock[NodeNumber]->ProcessorMask |= 1;
                KeGetCurrentPrcb()->ParentNode = KeNodeBlock[NodeNumber];
            }
        }
    }

#else

    NodeNumber = 0;

#endif

    //
    // Calculate the size of the per processor data structures.
    //
    // This includes:
    //
    //   PCR (including the PRCB)
    //   System TSS
    //   Idle Thread Object
    //   Double Fault/NMI Panic Stack
    //   Machine Check Stack
    //   GDT
    //   IDT
    //
    // If this is a multinode system, the KNODE structure is also allocated.
    //
    // A DPC and Idle stack are also allocated, but they are done separately.
    //

    AllocationSize = ROUNDUP16(sizeof(KPCR)) +
                     ROUNDUP16(sizeof(KTSS64)) +
                     ROUNDUP16(sizeof(ETHREAD)) +
                     ROUNDUP16(DOUBLE_FAULT_STACK_SIZE) +
                     ROUNDUP16(KERNEL_MCA_EXCEPTION_STACK_SIZE);

#if defined(KE_MULTINODE)

    AllocationSize += ROUNDUP16(sizeof(KNODE));

#endif

    //
    // Save the offset of the GDT in the allocation structure and add in
    // the size of the GDT.
    //

    GdtOffset = AllocationSize;
    AllocationSize +=
            CurrentPcr->Prcb.ProcessorState.SpecialRegisters.Gdtr.Limit + 1;

    //
    // Save the offset of the IDT in the allocation structure and add in
    // the size of the IDT.
    //

    IdtOffset = AllocationSize;
    AllocationSize +=
            CurrentPcr->Prcb.ProcessorState.SpecialRegisters.Idtr.Limit + 1;

    //
    // 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 fixed part of the processor state that will be used to
    // start processors. Each processor starts in the system initialization
    // code with address of the loader parameter block as an argument.
    //

    RtlZeroMemory(&ProcessorState, sizeof(KPROCESSOR_STATE));
    ProcessorState.ContextFrame.Rcx = (ULONG64)KeLoaderBlock;
    ProcessorState.ContextFrame.Rip = (ULONG64)KiSystemStartup;
    ProcessorState.ContextFrame.SegCs = KGDT64_R0_CODE;
    ProcessorState.ContextFrame.SegDs = KGDT64_R3_DATA | RPL_MASK;
    ProcessorState.ContextFrame.SegEs = KGDT64_R3_DATA | RPL_MASK;
    ProcessorState.ContextFrame.SegFs = KGDT64_R3_CMTEB | RPL_MASK;
    ProcessorState.ContextFrame.SegGs = KGDT64_R3_DATA | RPL_MASK;
    ProcessorState.ContextFrame.SegSs = KGDT64_R3_DATA | RPL_MASK;

    //
    // Loop trying to start a new processors until a new processor can't be
    // started or an allocation failure occurs.
    //

    Number = 0;
    while ((ULONG)KeNumberProcessors < KeRegisteredProcessors) {
        Number++;

#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 memory for the new processor specific data. If the
        // allocation fails, then stop starting processors.
        //

        DataBlock = MmAllocateIndependentPages(AllocationSize, NodeNumber);
        if (DataBlock == NULL) {
            break;
        }

        //
        // Zero the allocated memory.
        //

        Base = (PUCHAR)DataBlock;
        RtlZeroMemory(DataBlock, AllocationSize);

        //
        // Copy and initialize the GDT for the next processor.
        //

        KiCopyDescriptorMemory(&CurrentPcr->Prcb.ProcessorState.SpecialRegisters.Gdtr,
                               &ProcessorState.SpecialRegisters.Gdtr,
                               Base + GdtOffset);

        GdtBase = (PKGDTENTRY64)ProcessorState.SpecialRegisters.Gdtr.Base;

        //
        // Copy and initialize the IDT for the next processor.
        //

        KiCopyDescriptorMemory(&CurrentPcr->Prcb.ProcessorState.SpecialRegisters.Gdtr,
                               &ProcessorState.SpecialRegisters.Idtr,
                               Base + IdtOffset);

        //
        // Set the PCR base address for the next processor and set the
        // processor number.
        //
        // N.B. The PCR address is passed to the next processor by computing
        //      the containing address with respect to the PRCB.
        //

        PcrBase = (PKPCR)Base;
        PcrBase->Number = Number;
        Base += ROUNDUP16(sizeof(KPCR));

        //
        // Set the system TSS descriptor base for the next processor.
        //

        SysTssBase = (PKTSS64)Base;
        KiSetDescriptorBase(KGDT64_SYS_TSS / 16, GdtBase, SysTssBase);
        Base += ROUNDUP16(sizeof(KTSS64));

        //
        // Initialize the panic stack address for double fault and NMI.
        //

        Base += DOUBLE_FAULT_STACK_SIZE;
        SysTssBase->Ist[TSS_IST_PANIC] = (ULONG64)Base;

        //
        // Initialize the machine check stack address.
        //

        Base += KERNEL_MCA_EXCEPTION_STACK_SIZE;
        SysTssBase->Ist[TSS_IST_MCA] = (ULONG64)Base;

        //
        // Idle Thread thread object.
        //

        Thread = (PETHREAD)Base;
        Base += ROUNDUP16(sizeof(ETHREAD));

        //
        // Set other special registers in the processor state.
        //

        ProcessorState.SpecialRegisters.Cr0 = ReadCR0();
        ProcessorState.SpecialRegisters.Cr3 = ReadCR3();
        ProcessorState.ContextFrame.EFlags = 0; // ******fixfix what should this be??
        ProcessorState.SpecialRegisters.Tr  = KGDT64_SYS_TSS;
        GdtBase[KGDT64_SYS_TSS / 16].Bytes.Flags1 = 0x89;
        ProcessorState.SpecialRegisters.Cr4 = CR4_PAE;

        //
        // Allocate a kernel stack and a DPC stack for the next processor.
        //

        KernelStack = MmCreateKernelStack(FALSE, NodeNumber);
        DpcStack = MmCreateKernelStack(FALSE, NodeNumber);
        if ((DpcStack == NULL) || (KernelStack == NULL)) {
            MmFreeIndependentPages(DataBlock, AllocationSize);
            break;
        }

        //
        // Initialize the kernel stack for the system TSS.
        //

        SysTssBase->Rsp0 = (ULONG64)KernelStack;
        ProcessorState.ContextFrame.Rsp = (ULONG64)KernelStack;

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

#if defined(KE_MULTINODE)

        if (KeNodeBlock[NodeNumber] == &KiNodeInit[NodeNumber]) {
            Node = (PKNODE)Base;
            *Node = KiNodeInit[NodeNumber];
            KeNodeBlock[NodeNumber] = Node;
        }

        Base += ROUNDUP16(sizeof(KNODE));
        PcrBase->Prcb.ParentNode = Node;

#else

        PcrBase->Prcb.ParentNode = KeNodeBlock[0];

#endif

        //
        // Adjust the loader block so it has the next processor state.
        //

        KeLoaderBlock->KernelStack = (ULONG64)DpcStack;
        KeLoaderBlock->Thread = (ULONG64)Thread;
        KeLoaderBlock->Prcb = (ULONG64)(&PcrBase->Prcb);

        //
        // Attempt to start the next processor. If a processor cannot be
        // started, then deallocate memory and stop starting processors.
        //

        if (HalStartNextProcessor(KeLoaderBlock, &ProcessorState) == 0) {
            MmFreeIndependentPages(DataBlock, AllocationSize);
            MmDeleteKernelStack(KernelStack, FALSE);
            MmDeleteKernelStack(DpcStack, FALSE);
            break;
        }

#if defined(KE_MULTINODE)

        Node->ProcessorMask |= AFFINITY_MASK(Number);

#endif

        //
        // Wait for processor to initialize.
        //

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

        Number += 1;
    }

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

    KiAllProcessorsStarted();

    //
    // Reset and synchronize the performance counters of all processors, by
    // applying a null adjustment to the interrupt time
    //

    KiAdjustInterruptTime(0);

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

    KiBarrierWait = 0;

#endif // !defined(NT_UP)

    return;
}

VOID
KiSetDescriptorBase (
   IN USHORT Selector,
   IN PKGDTENTRY64 GdtBase,
   IN PVOID Base
   )

/*++

Routine Description:

    This function sets the base address of a descriptor to the specified
    base address.

Arguments:

    Selector - Supplies the selector for the descriptor.

    GdtBase - Supplies a pointer to the GDT.

    Base - Supplies a pointer to the base address.

Return Value:

    None.

--*/

{

    GdtBase = &GdtBase[Selector];
    GdtBase->BaseLow = (USHORT)((ULONG64)Base);
    GdtBase->Bytes.BaseMiddle = (UCHAR)((ULONG64)Base >> 16);
    GdtBase->Bytes.BaseHigh = (UCHAR)((ULONG64)Base >> 24);
    GdtBase->BaseUpper = (ULONG)((ULONG64)Base >> 32);
    return;
}

VOID
KiCopyDescriptorMemory (
   IN PKDESCRIPTOR Source,
   IN PKDESCRIPTOR Destination,
   IN PVOID Base
   )

/*++

Routine Description:

    This function copies the specified descriptor memory to the new memory
    and initializes a descriptor for the new memory.

Arguments:

    Source - Supplies a pointer to the source descriptor that describes
        the memory to copy.

    Destination - Supplies a pointer to the destination descriptor to be
        initialized.

    Base - Supplies a pointer to the new memory.

Return Value:

    None.

--*/

{

    Destination->Limit = Source->Limit;
    Destination->Base = Base;
    RtlCopyMemory(Base, Source->Base, Source->Limit + 1);
    return;
}

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;

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

#if defined(KE_MULTINODE)

    for (i = 0; i < KeNumberNodes; i += 1) {
        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 memory
            // only 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 += 1) {
        KeNodeBlock[i] = NULL;
    }

#endif

    if (KeNumberNodes == 1) {

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

        KeNodeBlock[0]->ProcessorMask = KeActiveProcessors;
    }

    return;
}

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