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