]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/CpuMpPei/CpuMpPei.c
a2f25b94050f012c65a1c65afbfd63e1c02f6840
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / CpuMpPei.c
1 /** @file
2 CPU PEI Module installs CPU Multiple Processor PPI.
3
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "CpuMpPei.h"
16
17 //
18 // Global Descriptor Table (GDT)
19 //
20 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = {
21 /* selector { Global Segment Descriptor } */
22 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
23 /* 0x08 */ {{0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
24 /* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
25 /* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
26 /* 0x20 */ {{0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
27 /* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
28 /* 0x30 */ {{0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
29 /* 0x38 */ {{0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
30 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
31 };
32
33 //
34 // IA32 Gdt register
35 //
36 GLOBAL_REMOVE_IF_UNREFERENCED IA32_DESCRIPTOR mGdt = {
37 sizeof (mGdtEntries) - 1,
38 (UINTN) mGdtEntries
39 };
40
41 /**
42 Sort the APIC ID of all processors.
43
44 This function sorts the APIC ID of all processors so that processor number is
45 assigned in the ascending order of APIC ID which eases MP debugging.
46
47 @param PeiCpuMpData Pointer to PEI CPU MP Data
48 **/
49 VOID
50 SortApicId (
51 IN PEI_CPU_MP_DATA *PeiCpuMpData
52 )
53 {
54 UINTN Index1;
55 UINTN Index2;
56 UINTN Index3;
57 UINT32 ApicId;
58 EFI_HEALTH_FLAGS Health;
59 UINT32 ApCount;
60
61 ApCount = PeiCpuMpData->CpuCount - 1;
62
63 if (ApCount != 0) {
64 for (Index1 = 0; Index1 < ApCount; Index1++) {
65 Index3 = Index1;
66 //
67 // Sort key is the hardware default APIC ID
68 //
69 ApicId = PeiCpuMpData->CpuData[Index1].ApicId;
70 for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) {
71 if (ApicId > PeiCpuMpData->CpuData[Index2].ApicId) {
72 Index3 = Index2;
73 ApicId = PeiCpuMpData->CpuData[Index2].ApicId;
74 }
75 }
76 if (Index3 != Index1) {
77 PeiCpuMpData->CpuData[Index3].ApicId = PeiCpuMpData->CpuData[Index1].ApicId;
78 PeiCpuMpData->CpuData[Index1].ApicId = ApicId;
79 Health = PeiCpuMpData->CpuData[Index3].Health;
80 PeiCpuMpData->CpuData[Index3].Health = PeiCpuMpData->CpuData[Index1].Health;
81 PeiCpuMpData->CpuData[Index1].Health = Health;
82 }
83 }
84
85 //
86 // Get the processor number for the BSP
87 //
88 ApicId = GetInitialApicId ();
89 for (Index1 = 0; Index1 < PeiCpuMpData->CpuCount; Index1++) {
90 if (PeiCpuMpData->CpuData[Index1].ApicId == ApicId) {
91 PeiCpuMpData->BspNumber = (UINT32) Index1;
92 break;
93 }
94 }
95 }
96 }
97 /**
98 This function will be called from AP reset code if BSP uses WakeUpAP.
99
100 @param ExchangeInfo Pointer to the MP exchange info buffer
101 @param NumApsExecuting Number of curret executing AP
102 **/
103 VOID
104 EFIAPI
105 ApCFunction (
106 IN MP_CPU_EXCHANGE_INFO *ExchangeInfo,
107 IN UINTN NumApsExecuting
108 )
109 {
110 PEI_CPU_MP_DATA *PeiCpuMpData;
111 UINTN BistData;
112
113 PeiCpuMpData = ExchangeInfo->PeiCpuMpData;
114 if (PeiCpuMpData->InitFlag) {
115 //
116 // This is first time AP wakeup, get BIST inforamtion from AP stack
117 //
118 BistData = *(UINTN *) (PeiCpuMpData->Buffer + NumApsExecuting * PeiCpuMpData->CpuApStackSize - sizeof (UINTN));
119 PeiCpuMpData->CpuData[NumApsExecuting].ApicId = GetInitialApicId ();
120 PeiCpuMpData->CpuData[NumApsExecuting].Health.Uint32 = (UINT32) BistData;
121 }
122
123 //
124 // AP finished executing C code
125 //
126 InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount);
127
128 }
129
130 /**
131 This function will be called by BSP to wakeup AP.
132
133 @param PeiCpuMpData Pointer to PEI CPU MP Data
134 @param Broadcast TRUE: Send broadcast IPI to all APs
135 FALSE: Send IPI to AP by ApicId
136 @param ApicId Apic ID for the processor to be waked
137 @param Procedure The function to be invoked by AP
138 @param ProcedureArgument The argument to be passed into AP function
139 **/
140 VOID
141 WakeUpAP (
142 IN PEI_CPU_MP_DATA *PeiCpuMpData,
143 IN BOOLEAN Broadcast,
144 IN UINT32 ApicId,
145 IN EFI_AP_PROCEDURE Procedure, OPTIONAL
146 IN VOID *ProcedureArgument OPTIONAL
147 )
148 {
149 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
150
151 PeiCpuMpData->ApFunction = (UINTN) Procedure;
152 PeiCpuMpData->ApFunctionArgument = (UINTN) ProcedureArgument;
153 PeiCpuMpData->FinishedCount = 0;
154
155 ExchangeInfo = PeiCpuMpData->MpCpuExchangeInfo;
156 ExchangeInfo->Lock = 0;
157 ExchangeInfo->StackStart = PeiCpuMpData->Buffer;
158 ExchangeInfo->StackSize = PeiCpuMpData->CpuApStackSize;
159 ExchangeInfo->BufferStart = PeiCpuMpData->WakeupBuffer;
160 ExchangeInfo->PmodeOffset = PeiCpuMpData->AddressMap.PModeEntryOffset;
161 ExchangeInfo->LmodeOffset = PeiCpuMpData->AddressMap.LModeEntryOffset;
162 ExchangeInfo->Cr3 = AsmReadCr3 ();
163 ExchangeInfo->CFunction = (UINTN) ApCFunction;
164 ExchangeInfo->NumApsExecuting = 0;
165 ExchangeInfo->PeiCpuMpData = PeiCpuMpData;
166
167 //
168 // Get the BSP's data of GDT and IDT
169 //
170 CopyMem ((VOID *)&ExchangeInfo->GdtrProfile, &mGdt, sizeof(mGdt));
171 AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
172
173 if (Broadcast) {
174 SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);
175 } else {
176 SendInitSipiSipi (ApicId, (UINT32) ExchangeInfo->BufferStart);
177 }
178
179 return ;
180 }
181
182 /**
183 Get available system memory below 1MB by specified size.
184
185 @param WakeupBufferSize Wakeup buffer size required
186
187 @retval other Return wakeup buffer address below 1MB.
188 @retval -1 Cannot find free memory below 1MB.
189 **/
190 UINTN
191 GetWakeupBuffer (
192 IN UINTN WakeupBufferSize
193 )
194 {
195 EFI_PEI_HOB_POINTERS Hob;
196 UINTN WakeupBufferStart;
197 UINTN WakeupBufferEnd;
198
199 //
200 // Get the HOB list for processing
201 //
202 Hob.Raw = GetHobList ();
203
204 //
205 // Collect memory ranges
206 //
207 while (!END_OF_HOB_LIST (Hob)) {
208 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
209 if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&
210 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
211 ((Hob.ResourceDescriptor->ResourceAttribute &
212 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
213 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
214 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
215 )) == 0)
216 ) {
217 //
218 // Need memory under 1MB to be collected here
219 //
220 WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength);
221 if (WakeupBufferEnd > BASE_1MB) {
222 //
223 // Wakeup buffer should be under 1MB
224 //
225 WakeupBufferEnd = BASE_1MB;
226 }
227 //
228 // Wakeup buffer should be aligned on 4KB
229 //
230 WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);
231 if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {
232 continue;
233 }
234 //
235 // Create a memory allocation HOB.
236 //
237 BuildMemoryAllocationHob (
238 WakeupBufferStart,
239 WakeupBufferSize,
240 EfiBootServicesData
241 );
242 return WakeupBufferStart;
243 }
244 }
245 //
246 // Find the next HOB
247 //
248 Hob.Raw = GET_NEXT_HOB (Hob);
249 }
250
251 return (UINTN) -1;
252 }
253
254 /**
255 Get available system memory below 1MB by specified size.
256
257 @param PeiCpuMpData Pointer to PEI CPU MP Data
258 **/
259 VOID
260 BackupAndPrepareWakeupBuffer(
261 IN PEI_CPU_MP_DATA *PeiCpuMpData
262 )
263 {
264 CopyMem (
265 (VOID *) PeiCpuMpData->BackupBuffer,
266 (VOID *) PeiCpuMpData->WakeupBuffer,
267 PeiCpuMpData->BackupBufferSize
268 );
269 CopyMem (
270 (VOID *) PeiCpuMpData->WakeupBuffer,
271 (VOID *) PeiCpuMpData->AddressMap.RendezvousFunnelAddress,
272 PeiCpuMpData->AddressMap.RendezvousFunnelSize
273 );
274 }
275 /**
276 This function will get CPU count in the system.
277
278 @param PeiCpuMpData Pointer to PEI CPU MP Data
279
280 @return AP processor count
281 **/
282 UINT32
283 CountProcessorNumber (
284 IN PEI_CPU_MP_DATA *PeiCpuMpData
285 )
286 {
287
288 //
289 // Send broadcast IPI to APs to wakeup APs
290 //
291 PeiCpuMpData->InitFlag = 1;
292 WakeUpAP (PeiCpuMpData, TRUE, 0, NULL, NULL);
293 //
294 // Wait for AP task to complete and then exit.
295 //
296 MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
297 PeiCpuMpData->InitFlag = 0;
298 PeiCpuMpData->CpuCount += (UINT32) PeiCpuMpData->MpCpuExchangeInfo->NumApsExecuting;
299 //
300 // Sort BSP/Aps by CPU APIC ID in ascending order
301 //
302 SortApicId (PeiCpuMpData);
303
304 DEBUG ((EFI_D_INFO, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData->CpuCount));
305 return PeiCpuMpData->CpuCount;
306 }
307
308 /**
309 Prepare for AP wakeup buffer and copy AP reset code into it.
310
311 Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack.
312
313 @return Pointer to PEI CPU MP Data
314 **/
315 PEI_CPU_MP_DATA *
316 PrepareAPStartupVector (
317 VOID
318 )
319 {
320 EFI_STATUS Status;
321 UINT32 MaxCpuCount;
322 PEI_CPU_MP_DATA *PeiCpuMpData;
323 EFI_PHYSICAL_ADDRESS Buffer;
324 UINTN BufferSize;
325 UINTN WakeupBuffer;
326 UINTN WakeupBufferSize;
327 MP_ASSEMBLY_ADDRESS_MAP AddressMap;
328
329 AsmGetAddressMap (&AddressMap);
330 WakeupBufferSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);
331 WakeupBuffer = GetWakeupBuffer ((WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1));
332 DEBUG ((EFI_D_INFO, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer));
333
334 //
335 // Allocate Pages for APs stack, CPU MP Data and backup buffer for wakeup buffer
336 //
337 MaxCpuCount = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
338 BufferSize = PcdGet32 (PcdCpuApStackSize) * MaxCpuCount + sizeof (PEI_CPU_MP_DATA)
339 + WakeupBufferSize + sizeof (PEI_CPU_DATA) * MaxCpuCount;
340 Status = PeiServicesAllocatePages (
341 EfiBootServicesData,
342 EFI_SIZE_TO_PAGES (BufferSize),
343 &Buffer
344 );
345 ASSERT_EFI_ERROR (Status);
346
347 PeiCpuMpData = (PEI_CPU_MP_DATA *) (UINTN) (Buffer + PcdGet32 (PcdCpuApStackSize) * MaxCpuCount);
348 PeiCpuMpData->Buffer = (UINTN) Buffer;
349 PeiCpuMpData->CpuApStackSize = PcdGet32 (PcdCpuApStackSize);
350 PeiCpuMpData->WakeupBuffer = WakeupBuffer;
351 PeiCpuMpData->BackupBuffer = (UINTN)PeiCpuMpData + sizeof (PEI_CPU_MP_DATA);
352 PeiCpuMpData->BackupBufferSize = WakeupBufferSize;
353 PeiCpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (WakeupBuffer + AddressMap.RendezvousFunnelSize);
354
355 PeiCpuMpData->CpuCount = 1;
356 PeiCpuMpData->BspNumber = 0;
357 PeiCpuMpData->CpuData = (PEI_CPU_DATA *) (PeiCpuMpData->MpCpuExchangeInfo + 1);
358 PeiCpuMpData->CpuData[0].ApicId = GetInitialApicId ();
359 PeiCpuMpData->CpuData[0].Health.Uint32 = 0;
360 CopyMem (&PeiCpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));
361
362 //
363 // Backup original data and copy AP reset code in it
364 //
365 BackupAndPrepareWakeupBuffer(PeiCpuMpData);
366
367 return PeiCpuMpData;
368 }
369 /**
370 The Entry point of the MP CPU PEIM.
371
372 This function will wakeup APs and collect CPU AP count and install the
373 Mp Service Ppi.
374
375 @param FileHandle Handle of the file being invoked.
376 @param PeiServices Describes the list of possible PEI Services.
377
378 @retval EFI_SUCCESS MpServicePpi is installed successfully.
379
380 **/
381 EFI_STATUS
382 EFIAPI
383 CpuMpPeimInit (
384 IN EFI_PEI_FILE_HANDLE FileHandle,
385 IN CONST EFI_PEI_SERVICES **PeiServices
386 )
387 {
388
389 PEI_CPU_MP_DATA *PeiCpuMpData;
390 UINT32 ProcessorCount;
391
392 //
393 // Load new GDT table on BSP
394 //
395 AsmInitializeGdt (&mGdt);
396 //
397 // Get wakeup buffer and copy AP reset code in it
398 //
399 PeiCpuMpData = PrepareAPStartupVector ();
400 //
401 // Count processor number and collect processor information
402 //
403 ProcessorCount = CountProcessorNumber (PeiCpuMpData);
404
405 return EFI_SUCCESS;
406 }