]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/CpuMpPei/CpuMpPei.c
UefiCpuPkg/CpuMpPei: Add AsmHltLoop ()
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / CpuMpPei.c
CommitLineData
65e79f93
JF
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
f9d30595
JF
17//
18// Global Descriptor Table (GDT)
19//
20GLOBAL_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//
36GLOBAL_REMOVE_IF_UNREFERENCED IA32_DESCRIPTOR mGdt = {
37 sizeof (mGdtEntries) - 1,
38 (UINTN) mGdtEntries
39 };
40
f8e4e86b
JF
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**/
49VOID
50SortApicId (
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}
8805c912
JF
97
98/**
99 Get CPU MP Data pointer from the Guided HOB.
100
101 @return Pointer to Pointer to PEI CPU MP Data
102**/
103PEI_CPU_MP_DATA *
104GetMpHobData (
105 VOID
106 )
107{
108 EFI_HOB_GUID_TYPE *GuidHob;
109 VOID *DataInHob;
110 PEI_CPU_MP_DATA *CpuMpData;
111
112 CpuMpData = NULL;
113 GuidHob = GetFirstGuidHob (&gEfiCallerIdGuid);
114 if (GuidHob != NULL) {
115 DataInHob = GET_GUID_HOB_DATA (GuidHob);
116 CpuMpData = (PEI_CPU_MP_DATA *)(*(UINTN *)DataInHob);
117 }
118 ASSERT (CpuMpData != NULL);
119 return CpuMpData;
120}
121
7d51bf5c
JF
122/**
123 This function will be called from AP reset code if BSP uses WakeUpAP.
124
125 @param ExchangeInfo Pointer to the MP exchange info buffer
126 @param NumApsExecuting Number of curret executing AP
127**/
128VOID
129EFIAPI
130ApCFunction (
131 IN MP_CPU_EXCHANGE_INFO *ExchangeInfo,
132 IN UINTN NumApsExecuting
133 )
134{
135 PEI_CPU_MP_DATA *PeiCpuMpData;
60ca9e8c
JF
136 UINTN ProcessorNumber;
137 EFI_AP_PROCEDURE Procedure;
7d51bf5c
JF
138 UINTN BistData;
139
140 PeiCpuMpData = ExchangeInfo->PeiCpuMpData;
141 if (PeiCpuMpData->InitFlag) {
142 //
143 // This is first time AP wakeup, get BIST inforamtion from AP stack
144 //
145 BistData = *(UINTN *) (PeiCpuMpData->Buffer + NumApsExecuting * PeiCpuMpData->CpuApStackSize - sizeof (UINTN));
146 PeiCpuMpData->CpuData[NumApsExecuting].ApicId = GetInitialApicId ();
147 PeiCpuMpData->CpuData[NumApsExecuting].Health.Uint32 = (UINT32) BistData;
b4cd9f78 148 //
d1cf9333 149 // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs.
b4cd9f78
JF
150 //
151 MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable);
d1cf9333 152 MicrocodeDetect ();
60ca9e8c
JF
153 } else {
154 //
155 // Execute AP function if AP is not disabled
156 //
157 GetProcessorNumber (PeiCpuMpData, &ProcessorNumber);
158 if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) &&
159 (PeiCpuMpData->ApFunction != 0)) {
160 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy;
161 Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction;
162 Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument);
163 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;
164 }
7d51bf5c
JF
165 }
166
167 //
168 // AP finished executing C code
169 //
170 InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount);
171
fcc82734 172 AsmCliHltLoop ();
7d51bf5c
JF
173}
174
175/**
176 This function will be called by BSP to wakeup AP.
177
178 @param PeiCpuMpData Pointer to PEI CPU MP Data
179 @param Broadcast TRUE: Send broadcast IPI to all APs
180 FALSE: Send IPI to AP by ApicId
181 @param ApicId Apic ID for the processor to be waked
182 @param Procedure The function to be invoked by AP
183 @param ProcedureArgument The argument to be passed into AP function
184**/
185VOID
186WakeUpAP (
187 IN PEI_CPU_MP_DATA *PeiCpuMpData,
188 IN BOOLEAN Broadcast,
189 IN UINT32 ApicId,
190 IN EFI_AP_PROCEDURE Procedure, OPTIONAL
191 IN VOID *ProcedureArgument OPTIONAL
192 )
193{
194 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
195
196 PeiCpuMpData->ApFunction = (UINTN) Procedure;
197 PeiCpuMpData->ApFunctionArgument = (UINTN) ProcedureArgument;
198 PeiCpuMpData->FinishedCount = 0;
199
200 ExchangeInfo = PeiCpuMpData->MpCpuExchangeInfo;
201 ExchangeInfo->Lock = 0;
202 ExchangeInfo->StackStart = PeiCpuMpData->Buffer;
203 ExchangeInfo->StackSize = PeiCpuMpData->CpuApStackSize;
204 ExchangeInfo->BufferStart = PeiCpuMpData->WakeupBuffer;
205 ExchangeInfo->PmodeOffset = PeiCpuMpData->AddressMap.PModeEntryOffset;
206 ExchangeInfo->LmodeOffset = PeiCpuMpData->AddressMap.LModeEntryOffset;
207 ExchangeInfo->Cr3 = AsmReadCr3 ();
208 ExchangeInfo->CFunction = (UINTN) ApCFunction;
209 ExchangeInfo->NumApsExecuting = 0;
210 ExchangeInfo->PeiCpuMpData = PeiCpuMpData;
211
212 //
213 // Get the BSP's data of GDT and IDT
214 //
215 CopyMem ((VOID *)&ExchangeInfo->GdtrProfile, &mGdt, sizeof(mGdt));
216 AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
217
218 if (Broadcast) {
219 SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);
220 } else {
221 SendInitSipiSipi (ApicId, (UINT32) ExchangeInfo->BufferStart);
222 }
223
224 return ;
225}
226
05e107f8
JF
227/**
228 Get available system memory below 1MB by specified size.
229
230 @param WakeupBufferSize Wakeup buffer size required
231
232 @retval other Return wakeup buffer address below 1MB.
233 @retval -1 Cannot find free memory below 1MB.
234**/
235UINTN
236GetWakeupBuffer (
237 IN UINTN WakeupBufferSize
238 )
239{
240 EFI_PEI_HOB_POINTERS Hob;
241 UINTN WakeupBufferStart;
242 UINTN WakeupBufferEnd;
243
244 //
245 // Get the HOB list for processing
246 //
247 Hob.Raw = GetHobList ();
248
249 //
250 // Collect memory ranges
251 //
252 while (!END_OF_HOB_LIST (Hob)) {
253 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
254 if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&
255 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
256 ((Hob.ResourceDescriptor->ResourceAttribute &
257 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
258 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
259 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
260 )) == 0)
261 ) {
262 //
263 // Need memory under 1MB to be collected here
264 //
265 WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength);
266 if (WakeupBufferEnd > BASE_1MB) {
267 //
268 // Wakeup buffer should be under 1MB
269 //
270 WakeupBufferEnd = BASE_1MB;
271 }
272 //
273 // Wakeup buffer should be aligned on 4KB
274 //
275 WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);
276 if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {
277 continue;
278 }
279 //
280 // Create a memory allocation HOB.
281 //
282 BuildMemoryAllocationHob (
283 WakeupBufferStart,
284 WakeupBufferSize,
285 EfiBootServicesData
286 );
287 return WakeupBufferStart;
288 }
289 }
290 //
291 // Find the next HOB
292 //
293 Hob.Raw = GET_NEXT_HOB (Hob);
294 }
295
296 return (UINTN) -1;
297}
65e79f93 298
e66d675d
JF
299/**
300 Get available system memory below 1MB by specified size.
301
302 @param PeiCpuMpData Pointer to PEI CPU MP Data
303**/
304VOID
305BackupAndPrepareWakeupBuffer(
306 IN PEI_CPU_MP_DATA *PeiCpuMpData
307 )
308{
309 CopyMem (
310 (VOID *) PeiCpuMpData->BackupBuffer,
311 (VOID *) PeiCpuMpData->WakeupBuffer,
312 PeiCpuMpData->BackupBufferSize
313 );
314 CopyMem (
315 (VOID *) PeiCpuMpData->WakeupBuffer,
316 (VOID *) PeiCpuMpData->AddressMap.RendezvousFunnelAddress,
317 PeiCpuMpData->AddressMap.RendezvousFunnelSize
318 );
319}
7d51bf5c
JF
320/**
321 This function will get CPU count in the system.
322
323 @param PeiCpuMpData Pointer to PEI CPU MP Data
324
325 @return AP processor count
326**/
327UINT32
328CountProcessorNumber (
329 IN PEI_CPU_MP_DATA *PeiCpuMpData
330 )
331{
d1cf9333
JF
332 //
333 // Load Microcode on BSP
334 //
335 MicrocodeDetect ();
b4cd9f78
JF
336 //
337 // Store BSP's MTRR setting
338 //
339 MtrrGetAllMtrrs (&PeiCpuMpData->MtrrTable);
7d51bf5c
JF
340 //
341 // Send broadcast IPI to APs to wakeup APs
342 //
343 PeiCpuMpData->InitFlag = 1;
344 WakeUpAP (PeiCpuMpData, TRUE, 0, NULL, NULL);
345 //
346 // Wait for AP task to complete and then exit.
347 //
348 MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
349 PeiCpuMpData->InitFlag = 0;
350 PeiCpuMpData->CpuCount += (UINT32) PeiCpuMpData->MpCpuExchangeInfo->NumApsExecuting;
f8e4e86b
JF
351 //
352 // Sort BSP/Aps by CPU APIC ID in ascending order
353 //
354 SortApicId (PeiCpuMpData);
7d51bf5c
JF
355
356 DEBUG ((EFI_D_INFO, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData->CpuCount));
357 return PeiCpuMpData->CpuCount;
358}
359
e66d675d
JF
360/**
361 Prepare for AP wakeup buffer and copy AP reset code into it.
362
363 Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack.
364
365 @return Pointer to PEI CPU MP Data
366**/
367PEI_CPU_MP_DATA *
368PrepareAPStartupVector (
369 VOID
370 )
371{
372 EFI_STATUS Status;
373 UINT32 MaxCpuCount;
374 PEI_CPU_MP_DATA *PeiCpuMpData;
375 EFI_PHYSICAL_ADDRESS Buffer;
376 UINTN BufferSize;
377 UINTN WakeupBuffer;
378 UINTN WakeupBufferSize;
379 MP_ASSEMBLY_ADDRESS_MAP AddressMap;
380
381 AsmGetAddressMap (&AddressMap);
382 WakeupBufferSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);
383 WakeupBuffer = GetWakeupBuffer ((WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1));
384 DEBUG ((EFI_D_INFO, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer));
385
386 //
387 // Allocate Pages for APs stack, CPU MP Data and backup buffer for wakeup buffer
388 //
389 MaxCpuCount = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
390 BufferSize = PcdGet32 (PcdCpuApStackSize) * MaxCpuCount + sizeof (PEI_CPU_MP_DATA)
391 + WakeupBufferSize + sizeof (PEI_CPU_DATA) * MaxCpuCount;
392 Status = PeiServicesAllocatePages (
393 EfiBootServicesData,
394 EFI_SIZE_TO_PAGES (BufferSize),
395 &Buffer
396 );
397 ASSERT_EFI_ERROR (Status);
398
399 PeiCpuMpData = (PEI_CPU_MP_DATA *) (UINTN) (Buffer + PcdGet32 (PcdCpuApStackSize) * MaxCpuCount);
400 PeiCpuMpData->Buffer = (UINTN) Buffer;
401 PeiCpuMpData->CpuApStackSize = PcdGet32 (PcdCpuApStackSize);
402 PeiCpuMpData->WakeupBuffer = WakeupBuffer;
403 PeiCpuMpData->BackupBuffer = (UINTN)PeiCpuMpData + sizeof (PEI_CPU_MP_DATA);
404 PeiCpuMpData->BackupBufferSize = WakeupBufferSize;
405 PeiCpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (WakeupBuffer + AddressMap.RendezvousFunnelSize);
406
407 PeiCpuMpData->CpuCount = 1;
408 PeiCpuMpData->BspNumber = 0;
409 PeiCpuMpData->CpuData = (PEI_CPU_DATA *) (PeiCpuMpData->MpCpuExchangeInfo + 1);
410 PeiCpuMpData->CpuData[0].ApicId = GetInitialApicId ();
411 PeiCpuMpData->CpuData[0].Health.Uint32 = 0;
412 CopyMem (&PeiCpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));
413
414 //
415 // Backup original data and copy AP reset code in it
416 //
417 BackupAndPrepareWakeupBuffer(PeiCpuMpData);
418
419 return PeiCpuMpData;
420}
65e79f93
JF
421/**
422 The Entry point of the MP CPU PEIM.
423
424 This function will wakeup APs and collect CPU AP count and install the
425 Mp Service Ppi.
426
427 @param FileHandle Handle of the file being invoked.
428 @param PeiServices Describes the list of possible PEI Services.
429
430 @retval EFI_SUCCESS MpServicePpi is installed successfully.
431
432**/
433EFI_STATUS
434EFIAPI
435CpuMpPeimInit (
436 IN EFI_PEI_FILE_HANDLE FileHandle,
437 IN CONST EFI_PEI_SERVICES **PeiServices
438 )
439{
e35d0347 440 EFI_STATUS Status;
e66d675d 441 PEI_CPU_MP_DATA *PeiCpuMpData;
7d51bf5c 442 UINT32 ProcessorCount;
65e79f93 443
f9d30595
JF
444 //
445 // Load new GDT table on BSP
446 //
447 AsmInitializeGdt (&mGdt);
e66d675d
JF
448 //
449 // Get wakeup buffer and copy AP reset code in it
450 //
451 PeiCpuMpData = PrepareAPStartupVector ();
7d51bf5c
JF
452 //
453 // Count processor number and collect processor information
454 //
455 ProcessorCount = CountProcessorNumber (PeiCpuMpData);
8805c912
JF
456 //
457 // Build location of PEI CPU MP DATA buffer in HOB
458 //
459 BuildGuidDataHob (
460 &gEfiCallerIdGuid,
461 (VOID *)&PeiCpuMpData,
462 sizeof(UINT64)
463 );
a21fe428
JF
464 //
465 // Update and publish CPU BIST information
466 //
467 CollectBistDataFromPpi (PeiServices, PeiCpuMpData);
e35d0347
JF
468 //
469 // Install CPU MP PPI
470 //
471 Status = PeiServicesInstallPpi(&mPeiCpuMpPpiDesc);
472 ASSERT_EFI_ERROR (Status);
65e79f93 473
e35d0347 474 return Status;
65e79f93 475}