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