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