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