xref: /reactos/ntoskrnl/ke/i386/mproc.c (revision 4c84e191)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Architecture specific source file to hold multiprocessor functions
5  * COPYRIGHT:   Copyright 2023 Justin Miller <justin.miller@reactos.org>
6  *              Copyright 2023 Victor Perevertkin <victor.perevertkin@reactos.org>
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 typedef struct _APINFO
16 {
17     DECLSPEC_ALIGN(PAGE_SIZE) KIDTENTRY Idt[256];
18     DECLSPEC_ALIGN(PAGE_SIZE) KGDTENTRY Gdt[128];
19     DECLSPEC_ALIGN(16) UINT8 NMIStackData[DOUBLE_FAULT_STACK_SIZE];
20     KIPCR Pcr;
21     ETHREAD Thread;
22     KTSS Tss;
23     KTSS TssDoubleFault;
24     KTSS TssNMI;
25 } APINFO, *PAPINFO;
26 
27 typedef struct _AP_SETUP_STACK
28 {
29     PVOID ReturnAddr;
30     PVOID KxLoaderBlock;
31 } AP_SETUP_STACK, *PAP_SETUP_STACK; // Note: expected layout only for 32-bit x86
32 
33 /* FUNCTIONS *****************************************************************/
34 
35 CODE_SEG("INIT")
36 VOID
37 NTAPI
KeStartAllProcessors(VOID)38 KeStartAllProcessors(VOID)
39 {
40     PVOID KernelStack, DPCStack;
41     ULONG ProcessorCount = 0;
42     PAPINFO APInfo;
43 
44     while (TRUE)
45     {
46         ProcessorCount++;
47         KernelStack = NULL;
48         DPCStack = NULL;
49 
50         // Allocate structures for a new CPU.
51         APInfo = ExAllocatePoolZero(NonPagedPool, sizeof(*APInfo), TAG_KERNEL);
52         if (!APInfo)
53             break;
54         ASSERT(ALIGN_DOWN_POINTER_BY(APInfo, PAGE_SIZE) == APInfo);
55 
56         KernelStack = MmCreateKernelStack(FALSE, 0);
57         if (!KernelStack)
58             break;
59 
60         DPCStack = MmCreateKernelStack(FALSE, 0);
61         if (!DPCStack)
62             break;
63 
64         // Initalize a new PCR for the specific AP
65         KiInitializePcr(ProcessorCount,
66                         &APInfo->Pcr,
67                         &APInfo->Idt[0],
68                         &APInfo->Gdt[0],
69                         &APInfo->Tss,
70                         (PKTHREAD)&APInfo->Thread,
71                         DPCStack);
72 
73         // Prepare descriptor tables
74         KDESCRIPTOR bspGdt, bspIdt;
75         __sgdt(&bspGdt.Limit);
76         __sidt(&bspIdt.Limit);
77         RtlCopyMemory(&APInfo->Gdt, (PVOID)bspGdt.Base, bspGdt.Limit + 1);
78         RtlCopyMemory(&APInfo->Idt, (PVOID)bspIdt.Base, bspIdt.Limit + 1);
79 
80         KiSetGdtDescriptorBase(KiGetGdtEntry(&APInfo->Gdt, KGDT_R0_PCR), (ULONG_PTR)&APInfo->Pcr);
81         KiSetGdtDescriptorBase(KiGetGdtEntry(&APInfo->Gdt, KGDT_DF_TSS), (ULONG_PTR)&APInfo->TssDoubleFault);
82         KiSetGdtDescriptorBase(KiGetGdtEntry(&APInfo->Gdt, KGDT_NMI_TSS), (ULONG_PTR)&APInfo->TssNMI);
83 
84         KiSetGdtDescriptorBase(KiGetGdtEntry(&APInfo->Gdt, KGDT_TSS), (ULONG_PTR)&APInfo->Tss);
85         // Clear TSS Busy flag (aka set the type to "TSS (Available)")
86         KiGetGdtEntry(&APInfo->Gdt, KGDT_TSS)->HighWord.Bits.Type = I386_TSS;
87 
88         APInfo->TssDoubleFault.Esp0 = (ULONG_PTR)&APInfo->NMIStackData;
89         APInfo->TssDoubleFault.Esp = (ULONG_PTR)&APInfo->NMIStackData;
90 
91         APInfo->TssNMI.Esp0 = (ULONG_PTR)&APInfo->NMIStackData;
92         APInfo->TssNMI.Esp = (ULONG_PTR)&APInfo->NMIStackData;
93 
94         // Fill the processor state
95         PKPROCESSOR_STATE ProcessorState = &APInfo->Pcr.Prcb->ProcessorState;
96         RtlZeroMemory(ProcessorState, sizeof(*ProcessorState));
97 
98         ProcessorState->SpecialRegisters.Cr0 = __readcr0();
99         ProcessorState->SpecialRegisters.Cr3 = __readcr3();
100         ProcessorState->SpecialRegisters.Cr4 = __readcr4();
101 
102         ProcessorState->ContextFrame.SegCs = KGDT_R0_CODE;
103         ProcessorState->ContextFrame.SegDs = KGDT_R3_DATA;
104         ProcessorState->ContextFrame.SegEs = KGDT_R3_DATA;
105         ProcessorState->ContextFrame.SegSs = KGDT_R0_DATA;
106         ProcessorState->ContextFrame.SegFs = KGDT_R0_PCR;
107 
108         ProcessorState->SpecialRegisters.Gdtr.Base = (ULONG_PTR)APInfo->Gdt;
109         ProcessorState->SpecialRegisters.Gdtr.Limit = sizeof(APInfo->Gdt) - 1;
110         ProcessorState->SpecialRegisters.Idtr.Base = (ULONG_PTR)APInfo->Idt;
111         ProcessorState->SpecialRegisters.Idtr.Limit = sizeof(APInfo->Idt) - 1;
112 
113         ProcessorState->SpecialRegisters.Tr = KGDT_TSS;
114 
115         ProcessorState->ContextFrame.Esp = (ULONG_PTR)KernelStack;
116         ProcessorState->ContextFrame.Eip = (ULONG_PTR)KiSystemStartup;
117         ProcessorState->ContextFrame.EFlags = __readeflags() & ~EFLAGS_INTERRUPT_MASK;
118 
119         ProcessorState->ContextFrame.Esp = (ULONG)((ULONG_PTR)ProcessorState->ContextFrame.Esp - sizeof(AP_SETUP_STACK));
120         PAP_SETUP_STACK ApStack = (PAP_SETUP_STACK)ProcessorState->ContextFrame.Esp;
121         ApStack->KxLoaderBlock = KeLoaderBlock;
122         ApStack->ReturnAddr = NULL;
123 
124         // Update the LOADER_PARAMETER_BLOCK structure for the new processor
125         KeLoaderBlock->KernelStack = (ULONG_PTR)KernelStack;
126         KeLoaderBlock->Prcb = (ULONG_PTR)&APInfo->Pcr.Prcb;
127         KeLoaderBlock->Thread = (ULONG_PTR)&APInfo->Pcr.Prcb->IdleThread;
128 
129         // Start the CPU
130         DPRINT("Attempting to Start a CPU with number: %lu\n", ProcessorCount);
131         if (!HalStartNextProcessor(KeLoaderBlock, ProcessorState))
132         {
133             break;
134         }
135 
136         // And wait for it to start
137         while (KeLoaderBlock->Prcb != 0)
138         {
139             //TODO: Add a time out so we don't wait forever
140             KeMemoryBarrier();
141             YieldProcessor();
142         }
143     }
144 
145     // The last CPU didn't start - clean the data
146     ProcessorCount--;
147 
148     if (APInfo)
149         ExFreePoolWithTag(APInfo, TAG_KERNEL);
150     if (KernelStack)
151         MmDeleteKernelStack(KernelStack, FALSE);
152     if (DPCStack)
153         MmDeleteKernelStack(DPCStack, FALSE);
154 
155     DPRINT1("KeStartAllProcessors: Successful AP startup count is %lu\n", ProcessorCount);
156 }
157