/*++ Copyright (c) 1989 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 structre to the hal to obtain a new processor. This is done for every processor. Author: Ken Reneris (kenr) 22-Jan-92 Environment: Kernel mode only. Phase 1 of bootup Revision History: --*/ #include "ki.h" #ifdef NT_UP VOID KeStartAllProcessors ( VOID ) { // UP Build - this function is a nop } #else extern ULONG KeRegisteredProcessors; static VOID KiCloneDescriptor ( IN PKDESCRIPTOR pSrcDescriptorInfo, IN PKDESCRIPTOR pDestDescriptorInfo ); static VOID KiCloneSelector ( IN ULONG SrcSelector, IN PKGDTENTRY pNGDT, IN PKDESCRIPTOR pDestDescriptor ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,KeStartAllProcessors) #pragma alloc_text(INIT,KiCloneDescriptor) #pragma alloc_text(INIT,KiCloneSelector) #endif #if !defined(NT_UP) ULONG KiBarrierWait = 0; #endif VOID KeStartAllProcessors ( VOID ) /*++ Routine Description: Called by p0 during phase 1 of bootup. This function implements the x86 specific code to contact the hal for each system processor. Arguments: Return Value: All available processors are sent to KiSystemStartup. --*/ { KPROCESSOR_STATE ProcessorState; KDESCRIPTOR Descriptor; KDESCRIPTOR TSSDesc, DFTSSDesc, NMITSSDesc, PCRDesc; PKGDTENTRY pGDT; PVOID pStack; PVOID pDpcStack; ULONG DFStack; PUCHAR pThreadObject; PULONG pTopOfStack; ULONG NewProcessorNumber; BOOLEAN NewProcessor; PKPROCESS Process; PKTHREAD Thread; PKTSS pTSS; PLIST_ENTRY NextEntry; LONG NumberProcessors; // 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; while ((ULONG)KeNumberProcessors < KeRegisteredProcessors) { // Build up a processor state for new processor RtlZeroMemory ((PVOID) &ProcessorState, sizeof ProcessorState); // Give the new processor its own GDT _asm { sgdt Descriptor.Limit } KiCloneDescriptor (&Descriptor, &ProcessorState.SpecialRegisters.Gdtr); pGDT = (PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base; // Give new processor its own IDT _asm { sidt Descriptor.Limit } KiCloneDescriptor (&Descriptor, &ProcessorState.SpecialRegisters.Idtr); // Give new processor its own TSS and PCR KiCloneSelector (KGDT_TSS, pGDT, &TSSDesc); KiCloneSelector (KGDT_R0_PCR, pGDT, &PCRDesc); // Allocate double-fault TSS & stack, and NMI TSS KiCloneSelector (KGDT_DF_TSS, pGDT, &DFTSSDesc); DFStack = (ULONG)ExAllocatePoolWithTag(NonPagedPool, DOUBLE_FAULT_STACK_SIZE, ' eK'); pTSS = (PKTSS)DFTSSDesc.Base; pTSS->Esp0 = DFStack + DOUBLE_FAULT_STACK_SIZE; pTSS->NotUsed2[5] = DFStack + DOUBLE_FAULT_STACK_SIZE; KiCloneSelector (KGDT_NMI_TSS, pGDT, &NMITSSDesc); pTSS = (PKTSS)NMITSSDesc.Base; pTSS->Esp0 = DFStack + DOUBLE_FAULT_STACK_SIZE; pTSS->NotUsed2[5] = DFStack + DOUBLE_FAULT_STACK_SIZE; // Set other SpecialRegisters in processor state _asm { mov eax, cr0 and eax, NOT (CR0_AM or CR0_WP) mov ProcessorState.SpecialRegisters.Cr0, eax mov eax, cr3 mov ProcessorState.SpecialRegisters.Cr3, eax pushfd pop ProcessorState.ContextFrame.EFlags and ProcessorState.ContextFrame.EFlags, NOT EFLAGS_INTERRUPT_MASK } ProcessorState.SpecialRegisters.Tr = KGDT_TSS; pGDT[KGDT_TSS>>3].HighWord.Bytes.Flags1 = 0x89; #if defined(_X86PAE_) ProcessorState.SpecialRegisters.Cr4 = CR4_PAE; #endif // Allocate a DPC stack, idle thread stack and ThreadObject for // the new processor. pStack = MmCreateKernelStack (FALSE); pDpcStack = MmCreateKernelStack (FALSE); pThreadObject = (PUCHAR)ExAllocatePoolWithTag (NonPagedPool, sizeof(ETHREAD), ' eK'); // Zero initialize these... RtlZeroMemory ((PVOID) PCRDesc.Base, sizeof (KPCR)); RtlZeroMemory ((PVOID) pThreadObject, sizeof (KTHREAD)); // Setup context // Push variables onto new stack pTopOfStack = (PULONG) pStack; pTopOfStack[-1] = (ULONG) KeLoaderBlock; ProcessorState.ContextFrame.Esp = (ULONG) (pTopOfStack-2); ProcessorState.ContextFrame.Eip = (ULONG) KiSystemStartup; ProcessorState.ContextFrame.SegCs = KGDT_R0_CODE; ProcessorState.ContextFrame.SegDs = KGDT_R3_DATA; ProcessorState.ContextFrame.SegEs = KGDT_R3_DATA; ProcessorState.ContextFrame.SegFs = KGDT_R0_PCR; ProcessorState.ContextFrame.SegSs = KGDT_R0_DATA; // Initialize new processor's PCR & Prcb NewProcessorNumber = KeNumberProcessors; KiInitializePcr ( (ULONG) NewProcessorNumber, (PKPCR) PCRDesc.Base, (PKIDTENTRY) ProcessorState.SpecialRegisters.Idtr.Base, (PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base, (PKTSS) TSSDesc.Base, (PKTHREAD) pThreadObject, (PVOID) pDpcStack ); // Adjust LoaderBlock so it has the next processors state KeLoaderBlock->KernelStack = (ULONG) pTopOfStack; KeLoaderBlock->Thread = (ULONG) pThreadObject; KeLoaderBlock->Prcb = (ULONG) ((PKPCR) PCRDesc.Base)->Prcb; // Contact hal to start new processor NewProcessor = HalStartNextProcessor (KeLoaderBlock, &ProcessorState); if (!NewProcessor) { // There wasn't another processor, so free resources and // break KiProcessorBlock[NewProcessorNumber] = NULL; ExFreePool ((PVOID) ProcessorState.SpecialRegisters.Gdtr.Base); ExFreePool ((PVOID) ProcessorState.SpecialRegisters.Idtr.Base); ExFreePool ((PVOID) TSSDesc.Base); ExFreePool ((PVOID) DFTSSDesc.Base); ExFreePool ((PVOID) NMITSSDesc.Base); ExFreePool ((PVOID) PCRDesc.Base); ExFreePool ((PVOID) pThreadObject); ExFreePool ((PVOID) DFStack); MmDeleteKernelStack ( pStack, FALSE); MmDeleteKernelStack ( pDpcStack, FALSE); break; } // Wait for processor to initialize in kernel, then loop for another while (*((volatile ULONG *) &KeLoaderBlock->Prcb) != 0) { KeYieldProcessor(); } } // 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; } static VOID KiCloneSelector ( IN ULONG SrcSelector, IN PKGDTENTRY pNGDT, IN PKDESCRIPTOR pDestDescriptor /*++ Routine Description: Makes a copy of the current selector's data, and update the new gdt's linear address to point to the new copy. Arguments: SrcSelector - Selector value to clone pNGDT - New gdt table which is being built DescDescriptor - descriptor structure to fill in with resulting memory Return Value: --*/ ) { KDESCRIPTOR Descriptor; PKGDTENTRY pGDT; ULONG CurrentBase; ULONG NewBase; _asm { sgdt fword ptr [Descriptor.Limit] ; Get GDT's addr } pGDT = (PKGDTENTRY) Descriptor.Base; pGDT += SrcSelector >> 3; pNGDT += SrcSelector >> 3; CurrentBase = pGDT->BaseLow | (pGDT->HighWord.Bits.BaseMid << 16) | (pGDT->HighWord.Bits.BaseHi << 24); Descriptor.Base = CurrentBase; Descriptor.Limit = pGDT->LimitLow; if (pGDT->HighWord.Bits.Granularity & GRAN_PAGE) Descriptor.Limit = (Descriptor.Limit << PAGE_SHIFT) -1; KiCloneDescriptor (&Descriptor, pDestDescriptor); NewBase = pDestDescriptor->Base; pNGDT->BaseLow = (USHORT) NewBase & 0xffff; pNGDT->HighWord.Bits.BaseMid = (UCHAR) (NewBase >> 16) & 0xff; pNGDT->HighWord.Bits.BaseHi = (UCHAR) (NewBase >> 24) & 0xff; } static VOID KiCloneDescriptor ( IN PKDESCRIPTOR pSrcDescriptor, IN PKDESCRIPTOR pDestDescriptor ) /*++ Routine Description: Makes a copy of the specified descriptor, and supplies a return descriptor for the new copy Arguments: pSrcDescriptor - descriptor to clone pDescDescriptor - the cloned descriptor Return Value: --*/ { ULONG Size; Size = pSrcDescriptor->Limit + 1; pDestDescriptor->Limit = (USHORT) Size -1; pDestDescriptor->Base = (ULONG) ExAllocatePoolWithTag (NonPagedPool, Size, ' eK'); RtlMoveMemory ((PVOID) pDestDescriptor->Base, (PVOID) pSrcDescriptor->Base, Size); } #endif // !NT_UP