/*++ Copyright (c) 1989 Microsoft Corporation Module Name: allproc.c Abstract: This module allocates and intializes 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; PUCHAR pStack; 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 it's own GDT // _asm { sgdt Descriptor.Limit } KiCloneDescriptor (&Descriptor, &ProcessorState.SpecialRegisters.Gdtr); pGDT = (PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base; // // Give new processor it's own IDT // _asm { sidt Descriptor.Limit } KiCloneDescriptor (&Descriptor, &ProcessorState.SpecialRegisters.Idtr); // // Give new processor it's 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)ExAllocatePool(NonPagedPool, DOUBLE_FAULT_STACK_SIZE); 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; // // Allocate a kernel stack and ThreadObject for the new processor // pStack = (PUCHAR)MmCreateKernelStack (FALSE); pThreadObject = (PUCHAR)ExAllocatePool (NonPagedPool, sizeof(ETHREAD)); // // Zero initialize these... // RtlZeroMemory ((PVOID) PCRDesc.Base, sizeof (KPCR)); RtlZeroMemory ((PVOID) pThreadObject, sizeof (KTHREAD)); // // Setup context // Push varibles 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 processors 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 ); // // 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 // 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 ((PVOID) pStack, FALSE); break; } // // Wait for processor to initialize in kernel, then loop for another // while (*((volatile ULONG *) &KeLoaderBlock->Prcb) != 0) { } } // // Reset and synchronize the performance counters of all processors. // NumberProcessors = KeNumberProcessors; KiIpiGenericCall ( (PKIPI_BROADCAST_WORKER) HalCalibratePerformanceCounter, (ULONG)(&NumberProcessors) ); // // Allow all processor 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 selectors data, and updated 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 GDTs 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) ExAllocatePool (NonPagedPool, Size); RtlMoveMemory ((PVOID) pDestDescriptor->Base, (PVOID) pSrcDescriptor->Base, Size); } #endif // !NT_UP