Align the top of the stack correctly when the PEI Core calls SwitchStack()
[mirror_edk2.git] / EdkModulePkg / Core / Pei / Dispatcher / Dispatcher.c
1 /*++
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 Dispatcher.c
15
16 Abstract:
17
18 EFI PEI Core dispatch services
19
20 Revision History
21
22 --*/
23
24 #include <PeiMain.h>
25
26 VOID *
27 TransferOldDataToNewDataRange (
28 IN PEI_CORE_INSTANCE *PrivateData
29 );
30
31 EFI_GUID gEfiPeiCorePrivateGuid = EFI_PEI_CORE_PRIVATE_GUID;
32
33
34 EFI_STATUS
35 PeiDispatcher (
36 IN EFI_PEI_STARTUP_DESCRIPTOR *PeiStartupDescriptor,
37 IN PEI_CORE_INSTANCE *PrivateData,
38 IN PEI_CORE_DISPATCH_DATA *DispatchData
39 )
40
41 /*++
42
43 Routine Description:
44
45 Conduct PEIM dispatch.
46
47 Arguments:
48
49 PeiStartupDescriptor - Pointer to IN EFI_PEI_STARTUP_DESCRIPTOR
50 PrivateData - Pointer to the private data passed in from caller
51 DispatchData - Pointer to PEI_CORE_DISPATCH_DATA data.
52
53 Returns:
54
55 EFI_SUCCESS - Successfully dispatched PEIM.
56 EFI_NOT_FOUND - The dispatch failed.
57
58 --*/
59 {
60 EFI_STATUS Status;
61 PEI_CORE_TEMP_POINTERS TempPtr;
62 UINTN PrivateDataInMem;
63 BOOLEAN NextFvFound;
64 EFI_FIRMWARE_VOLUME_HEADER *NextFvAddress;
65 EFI_FIRMWARE_VOLUME_HEADER *DefaultFvAddress;
66 VOID *TopOfStack;
67 //
68 // Debug data for uninstalled Peim list
69 //
70 EFI_GUID DebugFoundPeimList[32];
71 REPORT_STATUS_CODE_LIBRARY_DEVICE_HANDLE_EXTENDED_DATA ExtendedData;
72
73 //
74 // save the Current FV Address so that we will not process it again if FindFv returns it later
75 //
76 DefaultFvAddress = DispatchData->BootFvAddress;
77
78 //
79 // This is the main dispatch loop. It will search known FVs for PEIMs and
80 // attempt to dispatch them. If any PEIM gets dispatched through a single
81 // pass of the dispatcher, it will start over from the Bfv again to see
82 // if any new PEIMs dependencies got satisfied. With a well ordered
83 // FV where PEIMs are found in the order their dependencies are also
84 // satisfied, this dipatcher should run only once.
85 //
86 for (;;) {
87 //
88 // This is the PEIM search loop. It will scan through all PEIMs it can find
89 // looking for PEIMs to dispatch, and will dipatch them if they have not
90 // already been dispatched and all of their dependencies are met.
91 // If no more PEIMs can be found in this pass through all known FVs,
92 // then it will break out of this loop.
93 //
94 for (;;) {
95
96 Status = FindNextPeim (
97 &PrivateData->PS,
98 DispatchData->CurrentFvAddress,
99 &DispatchData->CurrentPeimAddress
100 );
101
102 //
103 // If we found a PEIM, check if it is dispatched. If so, go to the
104 // next PEIM. If not, dispatch it if its dependencies are satisfied.
105 // If its dependencies are not satisfied, go to the next PEIM.
106 //
107 if (Status == EFI_SUCCESS) {
108
109 DEBUG_CODE_BEGIN ();
110
111 //
112 // Fill list of found Peims for later list of those not installed
113 //
114 CopyMem (
115 &DebugFoundPeimList[DispatchData->CurrentPeim],
116 &DispatchData->CurrentPeimAddress->Name,
117 sizeof (EFI_GUID)
118 );
119
120 DEBUG_CODE_END ();
121
122 if (!Dispatched (
123 DispatchData->CurrentPeim,
124 DispatchData->DispatchedPeimBitMap
125 )) {
126 if (DepexSatisfied (&PrivateData->PS, DispatchData->CurrentPeimAddress)) {
127 Status = PeiLoadImage (
128 &PrivateData->PS,
129 DispatchData->CurrentPeimAddress,
130 &TempPtr.Raw
131 );
132 if (Status == EFI_SUCCESS) {
133
134 //
135 // The PEIM has its dependencies satisfied, and its entry point
136 // has been found, so invoke it.
137 //
138 PERF_START (
139 (VOID *) (UINTN) (DispatchData->CurrentPeimAddress),
140 "PEIM",
141 NULL,
142 0
143 );
144
145 //
146 // BUGBUG: Used to be EFI_PEI_REPORT_STATUS_CODE_CODE
147 //
148 ExtendedData.Handle = (EFI_HANDLE)DispatchData->CurrentPeimAddress;
149
150 REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
151 EFI_PROGRESS_CODE,
152 EFI_SOFTWARE_PEI_CORE | EFI_SW_PC_INIT_BEGIN,
153 (VOID *)(&ExtendedData),
154 sizeof (ExtendedData)
155 );
156
157 //
158 // Is this a authentic image
159 //
160 Status = VerifyPeim (
161 &PrivateData->PS,
162 DispatchData->CurrentPeimAddress
163 );
164
165 if (Status != EFI_SECURITY_VIOLATION) {
166
167 Status = TempPtr.PeimEntry (
168 DispatchData->CurrentPeimAddress,
169 &PrivateData->PS
170 );
171 }
172
173 REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
174 EFI_PROGRESS_CODE,
175 EFI_SOFTWARE_PEI_CORE | EFI_SW_PC_INIT_END,
176 (VOID *)(&ExtendedData),
177 sizeof (ExtendedData)
178 );
179
180 PERF_END ((VOID *) (UINTN) (DispatchData->CurrentPeimAddress), "PEIM", NULL, 0);
181
182 //
183 // Mark the PEIM as dispatched so we don't attempt to run it again
184 //
185 SetDispatched (
186 &PrivateData->PS,
187 DispatchData->CurrentPeim,
188 &DispatchData->DispatchedPeimBitMap
189 );
190
191 //
192 // Process the Notify list and dispatch any notifies for
193 // newly installed PPIs.
194 //
195 ProcessNotifyList (&PrivateData->PS);
196
197 //
198 // If real system memory was discovered and installed by this
199 // PEIM, switch the stacks to the new memory. Since we are
200 // at dispatch level, only the Core's private data is preserved,
201 // nobody else should have any data on the stack.
202 //
203 if (PrivateData->SwitchStackSignal) {
204 TempPtr.PeiCore = (PEI_CORE_ENTRY_POINT)PeiCore;
205 PrivateDataInMem = (UINTN) TransferOldDataToNewDataRange (PrivateData);
206 ASSERT (PrivateDataInMem != 0);
207 //
208 // Adjust the top of stack to be aligned at CPU_STACK_ALIGNMENT
209 //
210 TopOfStack = (VOID *)((UINTN)PrivateData->StackBase + (UINTN)PrivateData->StackSize - CPU_STACK_ALIGNMENT);
211 TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
212
213 PeiSwitchStacks (
214 (SWITCH_STACK_ENTRY_POINT)(UINTN)TempPtr.Raw,
215 PeiStartupDescriptor,
216 (VOID*)PrivateDataInMem,
217 TopOfStack,
218 (VOID*)(UINTN)PrivateData->StackBase
219 );
220 }
221 }
222 }
223 }
224 DispatchData->CurrentPeim++;
225 continue;
226
227 } else {
228
229 //
230 // If we could not find another PEIM in the current FV, go try
231 // the FindFv PPI to look in other FVs for more PEIMs. If we can
232 // not locate the FindFv PPI, or if the FindFv PPI can not find
233 // anymore FVs, then exit the PEIM search loop.
234 //
235 if (DispatchData->FindFv == NULL) {
236 Status = PeiServicesLocatePpi (
237 &gEfiFindFvPpiGuid,
238 0,
239 NULL,
240 (VOID **)&DispatchData->FindFv
241 );
242 if (Status != EFI_SUCCESS) {
243 break;
244 }
245 }
246 NextFvFound = FALSE;
247 while (!NextFvFound) {
248 Status = DispatchData->FindFv->FindFv (
249 DispatchData->FindFv,
250 &PrivateData->PS,
251 &DispatchData->CurrentFv,
252 &NextFvAddress
253 );
254 //
255 // if there is no next fv, get out of this loop of finding FVs
256 //
257 if (Status != EFI_SUCCESS) {
258 break;
259 }
260 //
261 // don't process the default Fv again. (we don't know the order in which the hobs were created)
262 //
263 if ((NextFvAddress != DefaultFvAddress) &&
264 (NextFvAddress != DispatchData->CurrentFvAddress)) {
265
266 //
267 // VerifyFv() is currently returns SUCCESS all the time, add code to it to
268 // actually verify the given FV
269 //
270 Status = VerifyFv (NextFvAddress);
271 if (Status == EFI_SUCCESS) {
272 NextFvFound = TRUE;
273 DispatchData->CurrentFvAddress = NextFvAddress;
274 DispatchData->CurrentPeimAddress = NULL;
275 //
276 // current PRIM number (CurrentPeim) must continue as is, don't reset it here
277 //
278 }
279 }
280 }
281 //
282 // if there is no next fv, get out of this loop of dispatching PEIMs
283 //
284 if (!NextFvFound) {
285 break;
286 }
287 //
288 // continue in the inner for(;;) loop with a new FV;
289 //
290 }
291 }
292
293 //
294 // If all the PEIMs that we have found have been dispatched, then
295 // there is nothing left to dispatch and we don't need to go search
296 // through all PEIMs again.
297 //
298 if ((~(DispatchData->DispatchedPeimBitMap) &
299 ((1 << DispatchData->CurrentPeim)-1)) == 0) {
300 break;
301 }
302
303 //
304 // Check if no more PEIMs that depex was satisfied
305 //
306 if (DispatchData->DispatchedPeimBitMap == DispatchData->PreviousPeimBitMap) {
307 break;
308 }
309
310 //
311 // Case when Depex is not satisfied and has to traverse the list again
312 //
313 DispatchData->CurrentPeim = 0;
314 DispatchData->CurrentPeimAddress = 0;
315 DispatchData->PreviousPeimBitMap = DispatchData->DispatchedPeimBitMap;
316
317 //
318 // don't go back to the loop without making sure that the CurrentFvAddress is the
319 // same as the 1st (or default) FV we started with. otherwise we will interpret the bimap wrongly and
320 // mess it up, always start processing the PEIMs from the default FV just like in the first time around.
321 //
322 DispatchData->CurrentFv = 0;
323 DispatchData->CurrentFvAddress = DefaultFvAddress;
324 }
325
326 DEBUG_CODE_BEGIN ();
327 //
328 // Debug data for uninstalled Peim list
329 //
330 UINT32 DebugNotDispatchedBitmap;
331 UINT8 DebugFoundPeimPoint;
332
333 DebugFoundPeimPoint = 0;
334 //
335 // Get bitmap of Peims that were not dispatched,
336 //
337
338 DebugNotDispatchedBitmap = ((DispatchData->DispatchedPeimBitMap) ^ ((1 << DispatchData->CurrentPeim)-1));
339 //
340 // Scan bitmap of Peims not installed and print GUIDS
341 //
342 while (DebugNotDispatchedBitmap != 0) {
343 if ((DebugNotDispatchedBitmap & 1) != 0) {
344 DEBUG ((EFI_D_INFO, "WARNING -> InstallPpi: Not Installed: %g\n",
345 &DebugFoundPeimList[DebugFoundPeimPoint]
346 ));
347 }
348 DebugFoundPeimPoint++;
349 DebugNotDispatchedBitmap >>= 1;
350 }
351
352 DEBUG_CODE_END ();
353
354 return EFI_NOT_FOUND;
355 }
356
357 VOID
358 InitializeDispatcherData (
359 IN EFI_PEI_SERVICES **PeiServices,
360 IN PEI_CORE_INSTANCE *OldCoreData,
361 IN EFI_PEI_STARTUP_DESCRIPTOR *PeiStartupDescriptor
362 )
363 /*++
364
365 Routine Description:
366
367 Initialize the Dispatcher's data members
368
369 Arguments:
370
371 PeiServices - The PEI core services table.
372 OldCoreData - Pointer to old core data (before switching stack).
373 NULL if being run in non-permament memory mode.
374 PeiStartupDescriptor - Information and services provided by SEC phase.
375
376 Returns:
377
378 None.
379
380 --*/
381 {
382 PEI_CORE_INSTANCE *PrivateData;
383
384 PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
385
386 if (OldCoreData == NULL) {
387 PrivateData->DispatchData.CurrentFvAddress = (EFI_FIRMWARE_VOLUME_HEADER *) PeiStartupDescriptor->BootFirmwareVolume;
388 PrivateData->DispatchData.BootFvAddress = (EFI_FIRMWARE_VOLUME_HEADER *) PeiStartupDescriptor->BootFirmwareVolume;
389 } else {
390
391 //
392 // Current peim has been dispatched, but not count
393 //
394 PrivateData->DispatchData.CurrentPeim = (UINT8)(OldCoreData->DispatchData.CurrentPeim + 1);
395 }
396
397 return;
398 }
399
400
401 BOOLEAN
402 Dispatched (
403 IN UINT8 CurrentPeim,
404 IN UINT32 DispatchedPeimBitMap
405 )
406 /*++
407
408 Routine Description:
409
410 This routine checks to see if a particular PEIM has been dispatched during
411 the PEI core dispatch.
412
413 Arguments:
414 CurrentPeim - The PEIM/FV in the bit array to check.
415 DispatchedPeimBitMap - Bit array, each bit corresponds to a PEIM/FV.
416
417 Returns:
418 TRUE - PEIM already dispatched
419 FALSE - Otherwise
420
421 --*/
422 {
423 return (BOOLEAN)((DispatchedPeimBitMap & (1 << CurrentPeim)) != 0);
424 }
425
426 VOID
427 SetDispatched (
428 IN EFI_PEI_SERVICES **PeiServices,
429 IN UINT8 CurrentPeim,
430 OUT UINT32 *DispatchedPeimBitMap
431 )
432 /*++
433
434 Routine Description:
435
436 This routine sets a PEIM as having been dispatched once its entry
437 point has been invoked.
438
439 Arguments:
440
441 PeiServices - The PEI core services table.
442 CurrentPeim - The PEIM/FV in the bit array to check.
443 DispatchedPeimBitMap - Bit array, each bit corresponds to a PEIM/FV.
444
445 Returns:
446 None
447
448 --*/
449 {
450 //
451 // Check if the total number of PEIMs exceed the bitmap.
452 // CurrentPeim is 0-based
453 //
454 ASSERT (CurrentPeim < (sizeof (*DispatchedPeimBitMap) * 8));
455 *DispatchedPeimBitMap |= (1 << CurrentPeim);
456 return;
457 }
458
459 BOOLEAN
460 DepexSatisfied (
461 IN EFI_PEI_SERVICES **PeiServices,
462 IN VOID *CurrentPeimAddress
463 )
464 /*++
465
466 Routine Description:
467
468 This routine parses the Dependency Expression, if available, and
469 decides if the module can be executed.
470
471 Arguments:
472 PeiServices - The PEI Service Table
473 CurrentPeimAddress - Address of the PEIM Firmware File under investigation
474
475 Returns:
476 TRUE - Can be dispatched
477 FALSE - Cannot be dispatched
478
479 --*/
480 {
481 EFI_STATUS Status;
482 INT8 *DepexData;
483 BOOLEAN Runnable;
484
485 Status = PeiServicesFfsFindSectionData (
486 EFI_SECTION_PEI_DEPEX,
487 CurrentPeimAddress,
488 (VOID **)&DepexData
489 );
490 //
491 // If there is no DEPEX, assume the module can be executed
492 //
493 if (EFI_ERROR (Status)) {
494 return TRUE;
495 }
496
497 //
498 // Evaluate a given DEPEX
499 //
500 Status = PeimDispatchReadiness (
501 PeiServices,
502 DepexData,
503 &Runnable
504 );
505
506 return Runnable;
507 }
508
509
510 VOID *
511 TransferOldDataToNewDataRange (
512 IN PEI_CORE_INSTANCE *PrivateData
513 )
514 /*++
515
516 Routine Description:
517
518 This routine transfers the contents of the pre-permanent memory
519 PEI Core private data to a post-permanent memory data location.
520
521 Arguments:
522
523 PrivateData - Pointer to the current PEI Core private data pre-permanent memory
524
525 Returns:
526
527 Pointer to the PrivateData once the private data has been transferred to permanent memory
528
529 --*/
530 {
531 return BuildGuidDataHob (&gEfiPeiCorePrivateGuid, PrivateData, sizeof (PEI_CORE_INSTANCE));
532 }
533