Esempio n. 1
0
BOOLEAN
KeInvalidateAllCaches (
    VOID
    )

/*++

Routine Description:

    This function writes back and invalidates the cache on all processors
    in the host configuration.

Arguments:

    None.

Return Value:

    TRUE is returned as the function value.

--*/

{


#if !defined(NT_UP)

    PKAFFINITY Barrier;
    KIRQL OldIrql;
    PKPRCB Prcb;
    KAFFINITY TargetProcessors;

    //
    // Raise IRQL to SYNCH level.
    //
    // Send request to target processors, if any, invalidate the current cache,
    // and wait for the IPI request barrier.
    //

    OldIrql = KeRaiseIrqlToSynchLevel();
    Prcb = KeGetCurrentPrcb();
    TargetProcessors = KeActiveProcessors & ~Prcb->SetMember;
    if (TargetProcessors != 0) {
        Barrier = KiIpiSendRequest(TargetProcessors, 0, 0, IPI_INVALIDATE_ALL);
        WritebackInvalidate();
        KiIpiWaitForRequestBarrier(Barrier);

    } else {
        WritebackInvalidate();
    }

    //
    // Lower IRQL to its previous value.
    //

    KeLowerIrql(OldIrql);

#else

    WritebackInvalidate();

#endif

    return TRUE;
}
Esempio n. 2
0
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)

    ULONG AllocationSize;
    PUCHAR Base;
    PKPCR CurrentPcr = KeGetPcr();
    PVOID DataBlock;
    PVOID DpcStack;
    PKGDTENTRY64 GdtBase;
    ULONG GdtOffset;
    ULONG IdtOffset;
    UCHAR Index;
    PVOID KernelStack;
    ULONG LogicalProcessors;
    ULONG MaximumProcessors;
    PKNODE Node;
    UCHAR NodeNumber = 0;
    UCHAR Number;
    KIRQL OldIrql;
    PKNODE OldNode;
    PKNODE ParentNode;
    PKPCR PcrBase;
    PKPRCB Prcb;
    USHORT ProcessorId;
    KPROCESSOR_STATE ProcessorState;
    PKTSS64 SysTssBase;
    PKGDTENTRY64 TebBase;
    PETHREAD Thread;

    //
    // Ensure that prefetch instructions in the IPI path are patched out
    // if necessary before starting other processors.
    //

    OldIrql = KeRaiseIrqlToSynchLevel();
    KiIpiSendRequest(1, 0, 0, IPI_FLUSH_SINGLE);
    KeLowerIrql(OldIrql);

    //
    // Do not start additional processors if the relocate physical loader
    // switch has been specified.
    // 

    if (KeLoaderBlock->LoadOptions != NULL) {
        if (strstr(KeLoaderBlock->LoadOptions, "RELOCATEPHYSICAL") != NULL) {
            return;
        }
    }

    //
    // If this a multinode system and processor zero is not on node zero,
    // then move it to the appropriate node.
    //

    if (KeNumberNodes > 1) {
        if (NT_SUCCESS(KiQueryProcessorNode(0, &ProcessorId, &NodeNumber))) {
            if (NodeNumber != 0) {
                KiNode0.ProcessorMask = 0;
                KiNodeInit[0] = KiNode0;
                KeNodeBlock[0] = &KiNodeInit[0];
                KiNode0 = *KeNodeBlock[NodeNumber];
                KeNodeBlock[NodeNumber] = &KiNode0;
                KiNode0.ProcessorMask = 1;
            }

        } else {
            goto StartFailure;
        }
    }

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

    AllocationSize = ROUNDUP64(sizeof(KPCR)) +
                     ROUNDUP64(sizeof(KTSS64)) +
                     ROUNDUP64(sizeof(ETHREAD)) +
                     ROUNDUP64(DOUBLE_FAULT_STACK_SIZE) +
                     ROUNDUP64(KERNEL_MCA_EXCEPTION_STACK_SIZE) +
                     ROUNDUP64(NMI_STACK_SIZE) +
                     ROUNDUP64(sizeof(KNODE));

    //
    // 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_R0_DATA;

    //
    // Check to determine if hyper-threading is really enabled. Intel chips
    // claim to be hyper-threaded with the number of logical processors
    // greater than one even when hyper-threading is disabled in the BIOS.
    //

    LogicalProcessors = KiLogicalProcessors;
    if (HalIsHyperThreadingEnabled() == FALSE) {
        LogicalProcessors = 1;
    }

    //
    // If the total number of logical processors has not been set with
    // the /NUMPROC loader option, then set the maximum number of logical
    // processors to the number of registered processors times the number
    // of logical processors per registered processor.
    //
    // N.B. The number of logical processors is never allowed to exceed
    //      the number of registered processors times the number of logical
    //      processors per physical processor.
    //

    MaximumProcessors = KeNumprocSpecified;
    if (MaximumProcessors == 0) {
        MaximumProcessors = KeRegisteredProcessors * LogicalProcessors;
    }

    //
    // Loop trying to start a new processors until a new processor can't be
    // started or an allocation failure occurs.
    //
    // N.B. The below processor start code relies on the fact a physical
    //      processor is started followed by all its logical processors.
    //      The HAL guarantees this by sorting the ACPI processor table
    //      by APIC id.
    //

    Index = 0;
    Number = 0;
    while ((Index < (MAXIMUM_PROCESSORS - 1)) &&
           ((ULONG)KeNumberProcessors < MaximumProcessors) &&
           ((ULONG)KeNumberProcessors / LogicalProcessors) < KeRegisteredProcessors) {

        //
        // If this is a multinode system and current processor does not
        // exist on any node, then skip it.
        //

        Index += 1;
        if (KeNumberNodes > 1) {
            if (!NT_SUCCESS(KiQueryProcessorNode(Index, &ProcessorId, &NodeNumber))) {
                continue;
            }
        }

        //
        // Increment the processor number.
        //

        Number += 1;

        //
        // Allocate memory for the new processor specific data. If the
        // allocation fails, then stop starting processors.
        //

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

        //
        // Allocate a pool tag table for the new processor.
        //

        if (ExCreatePoolTagTable(Number, NodeNumber) == NULL) {
            goto StartFailure;
        }

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

        //
        // Encode the processor number in the upper 6 bits of the compatibility
        // mode TEB descriptor.
        //

        TebBase = (PKGDTENTRY64)((PCHAR)GdtBase + KGDT64_R3_CMTEB);
        TebBase->Bits.LimitHigh = Number >> 2;
        TebBase->LimitLow = ((Number & 0x3) << 14) | (TebBase->LimitLow & 0x3fff);

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

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

        //
        // Set the PCR base address for the next processor, set the processor
        // number, and set the processor speed.
        //
        // 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->ObsoleteNumber = Number;
        PcrBase->Prcb.Number = Number;
        PcrBase->Prcb.MHz = KeGetCurrentPrcb()->MHz;
        Base += ROUNDUP64(sizeof(KPCR));

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

        SysTssBase = (PKTSS64)Base;
        KiSetDescriptorBase(KGDT64_SYS_TSS / 16, GdtBase, SysTssBase);
        Base += ROUNDUP64(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;

        //
        // Initialize the NMI stack address.
        //

        Base += NMI_STACK_SIZE;
        SysTssBase->Ist[TSS_IST_NMI] = (ULONG64)Base;

        //
        // Idle Thread thread object.
        //

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

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

        ProcessorState.SpecialRegisters.Cr0 = ReadCR0();
        ProcessorState.SpecialRegisters.Cr3 = ReadCR3();
        ProcessorState.ContextFrame.EFlags = 0;
        ProcessorState.SpecialRegisters.Tr  = KGDT64_SYS_TSS;
        GdtBase[KGDT64_SYS_TSS / 16].Bytes.Flags1 = 0x89;
        ProcessorState.SpecialRegisters.Cr4 = ReadCR4();

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

        KernelStack = MmCreateKernelStack(FALSE, NodeNumber);
        if (KernelStack == NULL) {
            goto StartFailure;
        }

        DpcStack = MmCreateKernelStack(FALSE, NodeNumber);
        if (DpcStack == NULL) {
            goto StartFailure;
        }

        //
        // Initialize the kernel stack for the system TSS.
        //
        // N.B. System startup must be called with a stack pointer that is
        //      8 mod 16.
        //

        SysTssBase->Rsp0 = (ULONG64)KernelStack - sizeof(PVOID) * 4;
        ProcessorState.ContextFrame.Rsp = (ULONG64)KernelStack - 8;

        //
        // If this is the first processor on this node, then use the space
        // already allocated for the node. Otherwise, the space allocated
        // is not used.
        //

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

        Base += ROUNDUP64(sizeof(KNODE));

        //
        // Set the parent node address.
        //

        PcrBase->Prcb.ParentNode = Node;

        //
        // Adjust the loader block so it has the next processor state. Ensure
        // that the kernel stack has space for home registers for up to four
        // parameters.
        //

        KeLoaderBlock->KernelStack = (ULONG64)DpcStack - (sizeof(PVOID) * 4);
        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) {

            //
            // Restore the old node address in the node address array before
            // freeing the allocated data block (the node structure lies
            // within the data block).
            //

            *OldNode = *Node;
            KeNodeBlock[NodeNumber] = OldNode;
            ExDeletePoolTagTable(Number);
            MmFreeIndependentPages(DataBlock, AllocationSize);
            MmDeleteKernelStack(KernelStack, FALSE);
            MmDeleteKernelStack(DpcStack, FALSE);
            break;
        }

        Node->ProcessorMask |= AFFINITY_MASK(Number);

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

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

    //
    // All processors have been started. If this is a multinode system, then
    // allocate any missing node structures.
    //

    if (KeNumberNodes > 1) {
        for (Index = 0; Index < KeNumberNodes; Index += 1) {
            if (KeNodeBlock[Index] == &KiNodeInit[Index]) {
                Node = ExAllocatePoolWithTag(NonPagedPool, sizeof(KNODE), '  eK');
                if (Node != NULL) {
                    *Node = KiNodeInit[Index];
                    KeNodeBlock[Index] = Node;

                } else {
                    goto StartFailure;
                }
            }
        }

    } else if (KiNode0.ProcessorMask != KeActiveProcessors) {
        goto StartFailure;
    }

    //
    // Clear node structure address for nonexistent nodes.
    //

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

    //
    // Copy the node color and shifted color to the PRCB of each processor.
    //

    for (Index = 0; Index < (ULONG)KeNumberProcessors; Index += 1) {
        Prcb = KiProcessorBlock[Index];
        ParentNode = Prcb->ParentNode;
        Prcb->NodeColor = ParentNode->Color;
        Prcb->NodeShiftedColor = ParentNode->MmShiftedColor;
        Prcb->SecondaryColorMask = MmSecondaryColorMask;
    }

    //
    // Reset the initialization bit in prefetch retry.
    //

    KiPrefetchRetry &= ~0x80;

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

    KeAdjustInterruptTime(0);

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

    KiBarrierWait = 0;

#endif //

    return;

    //
    // The failure to allocate memory or a unsuccessful status was returned
    // during the attempt to start processors. This is considered fatal since
    // something is very wrong.
    //

#if !defined(NT_UP)

StartFailure:
    KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, 0, 0, 20, 0);

#endif

}
Esempio n. 3
0
VOID
KiIpiSendPacket (
    IN KAFFINITY TargetSet,
    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.

    N.B. This function MUST be called from a non-context switchable state.

Arguments:

   TargetProcessors - Supplies the set of processors on which the specfied
       operation is to be executed.

   WorkerFunction  - Supplies the address of the worker function.

   Parameter1 - Parameter3 - Supplies worker function specific parameters.

Return Value:

    None.

--*/

{

#if !defined(NT_UP)

    KREQUEST_PACKET RequestPacket;

    ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);

    //
    // Initialize the worker packet information.
    //

    RequestPacket.CurrentPacket[0] = Parameter1;
    RequestPacket.CurrentPacket[1] = Parameter2;
    RequestPacket.CurrentPacket[2] = Parameter3;
    RequestPacket.WorkerRoutine = WorkerFunction;

    //
    // Send request.
    //

    KiIpiSendRequest(TargetSet, (ULONG64)&RequestPacket, 0, IPI_PACKET_READY);

#else

    UNREFERENCED_PARAMETER(TargetSet);
    UNREFERENCED_PARAMETER(WorkerFunction);
    UNREFERENCED_PARAMETER(Parameter1);
    UNREFERENCED_PARAMETER(Parameter2);
    UNREFERENCED_PARAMETER(Parameter3);

#endif

    return;
}
Esempio n. 4
0
VOID
KeFlushProcessTb (
    VOID
    )

/*++

Routine Description:

    This function flushes the non-global translation buffer on all processors
    that are currently running threads which are child of the current process.

Arguments:

    None.

Return Value:

    None.

--*/

{

    PKAFFINITY Barrier;
    KIRQL OldIrql;
    PKPRCB Prcb;
    PKPROCESS Process;
    KAFFINITY TargetProcessors;

    //
    // Compute the target set of processors, disable context switching,
    // and send the flush entire parameters to the target processors,
    // if any, for execution.
    //

    OldIrql = KeRaiseIrqlToSynchLevel();
    Prcb = KeGetCurrentPrcb();
    Process = Prcb->CurrentThread->ApcState.Process;
    TargetProcessors = Process->ActiveProcessors;
    TargetProcessors &= ~Prcb->SetMember;

    //
    // Send request to target processors, if any, flush the current process
    // TB, and wait for the IPI request barrier.
    //

    if (TargetProcessors != 0) {
        Barrier = KiIpiSendRequest(TargetProcessors, 0, 0, IPI_FLUSH_PROCESS);
        KiFlushProcessTb();
        KiIpiWaitForRequestBarrier(Barrier);

    } else {
        KiFlushProcessTb();
    }

    //
    // Lower IRQL to its previous value.
    //

    KeLowerIrql(OldIrql);
    return;
}
Esempio n. 5
0
VOID
FASTCALL
KeFlushSingleTb (
    IN PVOID Virtual,
    IN BOOLEAN AllProcessors
    )

/*++

Routine Description:

    This function flushes a single entry from translation buffer (TB)
    on all processors that are currently running threads which are
    children of the current process.

Arguments:

    Virtual - Supplies a virtual address that is within the page whose
        translation buffer entry is to be flushed.

    AllProcessors - Supplies a boolean value that determines which
        translation buffers are to be flushed.

Return Value:

    The previous contents of the specified page table entry is returned
    as the function value.

--*/

{

    PKAFFINITY Barrier;
    KIRQL OldIrql;
    PKPRCB Prcb;
    PKPROCESS Process;
    KAFFINITY TargetProcessors;

    //
    // Compute the target set of processors and send the flush single
    // parameters to the target processors, if any, for execution.
    //

    OldIrql = KeRaiseIrqlToSynchLevel();
    Prcb = KeGetCurrentPrcb();
    if (AllProcessors != FALSE) {
        TargetProcessors = KeActiveProcessors;

    } else {
        Process = Prcb->CurrentThread->ApcState.Process;
        TargetProcessors = Process->ActiveProcessors;
    }

    //
    // Send request to target processors, if any, flush the single entry from
    // the current TB, and wait for the IPI request barrier.
    //

    TargetProcessors &= ~Prcb->SetMember;
    if (TargetProcessors != 0) {
        Barrier = KiIpiSendRequest(TargetProcessors, (LONG64)Virtual, 0, IPI_FLUSH_SINGLE);
        KiFlushSingleTb(Virtual);
        KiIpiWaitForRequestBarrier(Barrier);

    } else {
        KiFlushSingleTb(Virtual);
    }

    //
    // Lower IRQL to its previous value.
    //

    KeLowerIrql(OldIrql);
    return;
}
Esempio n. 6
0
VOID
KxFlushEntireTb (
    VOID
    )

/*++

Routine Description:

    This function flushes the entire translation buffer (TB) on all
    processors in the host configuration.

Arguments:

    None.

Return Value:

    None.

--*/

{

    KIRQL OldIrql;

#if !defined(NT_UP)

    PKAFFINITY Barrier;
    PKPRCB Prcb;
    KAFFINITY TargetProcessors;

#endif

    //
    // Raise IRQL to SYNCH level and set TB flush time stamp busy.
    //
    // Send request to target processors, if any, flush the current TB, and
    // wait for the IPI request barrier.
    //

    OldIrql = KeRaiseIrqlToSynchLevel();

#if !defined(NT_UP)

    Prcb = KeGetCurrentPrcb();
    TargetProcessors = KeActiveProcessors & ~Prcb->SetMember;
    KiSetTbFlushTimeStampBusy();
    if (TargetProcessors != 0) {
        Barrier = KiIpiSendRequest(TargetProcessors, 0, 0, IPI_FLUSH_ALL);
        KeFlushCurrentTb();
        KiIpiWaitForRequestBarrier(Barrier);

    } else {
        KeFlushCurrentTb();
    }

#else

    KeFlushCurrentTb();

#endif

    //
    // Clear the TB flush time stamp busy and lower IRQL to its previous value.
    //

    KiClearTbFlushTimeStampBusy();
    KeLowerIrql(OldIrql);
    return;
}
Esempio n. 7
0
VOID
KeFlushMultipleTb (
    IN ULONG Number,
    IN PVOID *Virtual,
    IN BOOLEAN AllProcessors
    )

/*++

Routine Description:

    This function flushes multiple entries from the translation buffer
    on all processors that are currently running threads which are
    children of the current process or flushes a multiple entries from
    the translation buffer on all processors in the host configuration.

Arguments:

    Number - Supplies the number of TB entries to flush.

    Virtual - Supplies a pointer to an array of virtual addresses that
        are within the pages whose translation buffer entries are to be
        flushed.

    AllProcessors - Supplies a boolean value that determines which
        translation buffers are to be flushed.

Return Value:

    The previous contents of the specified page table entry is returned
    as the function value.

--*/

{

    PKAFFINITY Barrier;
    PVOID *End;
    KIRQL OldIrql;
    PKPRCB Prcb;
    PKPROCESS Process;
    KAFFINITY TargetProcessors;

    ASSERT((Number != 0) && (Number <= FLUSH_MULTIPLE_MAXIMUM));

    //
    // Compute target set of processors.
    //

    OldIrql = KeRaiseIrqlToSynchLevel();
    Prcb = KeGetCurrentPrcb();
    if (AllProcessors != FALSE) {
        TargetProcessors = KeActiveProcessors;

    } else {
        Process = Prcb->CurrentThread->ApcState.Process;
        TargetProcessors = Process->ActiveProcessors;
    }

    //
    // Send request to target processors, if any, flush multiple entries in
    // current TB, and wait for the IPI request barrier.
    //

    End = Virtual + Number;
    TargetProcessors &= ~Prcb->SetMember;
    if (TargetProcessors != 0) {
        Barrier = KiIpiSendRequest(TargetProcessors,
                                   (LONG64)Virtual,
                                   Number,
                                   IPI_FLUSH_MULTIPLE);

        do {
            KiFlushSingleTb(*Virtual);
            Virtual += 1;
        } while (Virtual < End);

        KiIpiWaitForRequestBarrier(Barrier);

    } else {
        do {
            KiFlushSingleTb(*Virtual);
            Virtual += 1;
        } while (Virtual < End);
    }

    //
    // Lower IRQL to its previous value.
    //

    KeLowerIrql(OldIrql);
    return;
}