]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
MdeModulePkg: Display new stack base and size
[mirror_edk2.git] / MdeModulePkg / Universal / CapsulePei / UefiCapsule.c
1 /** @file
2 Capsule update PEIM for UEFI2.0
3
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution. The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "Capsule.h"
18
19 #ifdef MDE_CPU_IA32
20 //
21 // Global Descriptor Table (GDT)
22 //
23 GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = {
24 /* selector { Global Segment Descriptor } */
25 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
26 /* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
27 /* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
28 /* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
29 /* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
30 /* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
31 /* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
32 /* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
33 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
34 };
35
36 //
37 // IA32 Gdt register
38 //
39 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
40 sizeof (mGdtEntries) - 1,
41 (UINTN) mGdtEntries
42 };
43
44 /**
45 The function will check if 1G page is supported.
46
47 @retval TRUE 1G page is supported.
48 @retval FALSE 1G page is not supported.
49
50 **/
51 BOOLEAN
52 IsPage1GSupport (
53 VOID
54 )
55 {
56 UINT32 RegEax;
57 UINT32 RegEdx;
58 BOOLEAN Page1GSupport;
59
60 Page1GSupport = FALSE;
61 if (PcdGetBool(PcdUse1GPageTable)) {
62 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
63 if (RegEax >= 0x80000001) {
64 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
65 if ((RegEdx & BIT26) != 0) {
66 Page1GSupport = TRUE;
67 }
68 }
69 }
70
71 return Page1GSupport;
72 }
73
74 /**
75 Calculate the total size of page table.
76
77 @param[in] Page1GSupport 1G page support or not.
78
79 @return The size of page table.
80
81 **/
82 UINTN
83 CalculatePageTableSize (
84 IN BOOLEAN Page1GSupport
85 )
86 {
87 UINTN ExtraPageTablePages;
88 UINTN TotalPagesNum;
89 UINT8 PhysicalAddressBits;
90 UINT32 NumberOfPml4EntriesNeeded;
91 UINT32 NumberOfPdpEntriesNeeded;
92
93 //
94 // Create 4G page table by default,
95 // and let PF handler to handle > 4G request.
96 //
97 PhysicalAddressBits = 32;
98 ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
99
100 //
101 // Calculate the table entries needed.
102 //
103 if (PhysicalAddressBits <= 39 ) {
104 NumberOfPml4EntriesNeeded = 1;
105 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
106 } else {
107 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
108 NumberOfPdpEntriesNeeded = 512;
109 }
110
111 if (!Page1GSupport) {
112 TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
113 } else {
114 TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
115 }
116 TotalPagesNum += ExtraPageTablePages;
117
118 return EFI_PAGES_TO_SIZE (TotalPagesNum);
119 }
120
121 /**
122 Allocates and fills in the Page Directory and Page Table Entries to
123 establish a 4G page table.
124
125 @param[in] PageTablesAddress The base address of page table.
126 @param[in] Page1GSupport 1G page support or not.
127
128 **/
129 VOID
130 Create4GPageTables (
131 IN EFI_PHYSICAL_ADDRESS PageTablesAddress,
132 IN BOOLEAN Page1GSupport
133 )
134 {
135 UINT8 PhysicalAddressBits;
136 EFI_PHYSICAL_ADDRESS PageAddress;
137 UINTN IndexOfPml4Entries;
138 UINTN IndexOfPdpEntries;
139 UINTN IndexOfPageDirectoryEntries;
140 UINT32 NumberOfPml4EntriesNeeded;
141 UINT32 NumberOfPdpEntriesNeeded;
142 PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
143 PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
144 PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
145 PAGE_TABLE_ENTRY *PageDirectoryEntry;
146 UINTN BigPageAddress;
147 PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
148
149 //
150 // Create 4G page table by default,
151 // and let PF handler to handle > 4G request.
152 //
153 PhysicalAddressBits = 32;
154
155 //
156 // Calculate the table entries needed.
157 //
158 if (PhysicalAddressBits <= 39 ) {
159 NumberOfPml4EntriesNeeded = 1;
160 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
161 } else {
162 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
163 NumberOfPdpEntriesNeeded = 512;
164 }
165
166 //
167 // Pre-allocate big pages to avoid later allocations.
168 //
169 BigPageAddress = (UINTN) PageTablesAddress;
170
171 //
172 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
173 //
174 PageMap = (VOID *) BigPageAddress;
175 BigPageAddress += SIZE_4KB;
176
177 PageMapLevel4Entry = PageMap;
178 PageAddress = 0;
179 for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
180 //
181 // Each PML4 entry points to a page of Page Directory Pointer entires.
182 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
183 //
184 PageDirectoryPointerEntry = (VOID *) BigPageAddress;
185 BigPageAddress += SIZE_4KB;
186
187 //
188 // Make a PML4 Entry
189 //
190 PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
191 PageMapLevel4Entry->Bits.ReadWrite = 1;
192 PageMapLevel4Entry->Bits.Present = 1;
193
194 if (Page1GSupport) {
195 PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
196
197 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
198 //
199 // Fill in the Page Directory entries
200 //
201 PageDirectory1GEntry->Uint64 = (UINT64)PageAddress;
202 PageDirectory1GEntry->Bits.ReadWrite = 1;
203 PageDirectory1GEntry->Bits.Present = 1;
204 PageDirectory1GEntry->Bits.MustBe1 = 1;
205 }
206 } else {
207 for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
208 //
209 // Each Directory Pointer entries points to a page of Page Directory entires.
210 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
211 //
212 PageDirectoryEntry = (VOID *) BigPageAddress;
213 BigPageAddress += SIZE_4KB;
214
215 //
216 // Fill in a Page Directory Pointer Entries
217 //
218 PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;
219 PageDirectoryPointerEntry->Bits.ReadWrite = 1;
220 PageDirectoryPointerEntry->Bits.Present = 1;
221
222 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
223 //
224 // Fill in the Page Directory entries
225 //
226 PageDirectoryEntry->Uint64 = (UINT64)PageAddress;
227 PageDirectoryEntry->Bits.ReadWrite = 1;
228 PageDirectoryEntry->Bits.Present = 1;
229 PageDirectoryEntry->Bits.MustBe1 = 1;
230 }
231 }
232
233 for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
234 ZeroMem (
235 PageDirectoryPointerEntry,
236 sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)
237 );
238 }
239 }
240 }
241
242 //
243 // For the PML4 entries we are not using fill in a null entry.
244 //
245 for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
246 ZeroMem (
247 PageMapLevel4Entry,
248 sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
249 );
250 }
251 }
252
253 /**
254 Return function from long mode to 32-bit mode.
255
256 @param EntrypointContext Context for mode switching
257 @param ReturnContext Context for mode switching
258
259 **/
260 VOID
261 ReturnFunction (
262 SWITCH_32_TO_64_CONTEXT *EntrypointContext,
263 SWITCH_64_TO_32_CONTEXT *ReturnContext
264 )
265 {
266 //
267 // Restore original GDT
268 //
269 AsmWriteGdtr (&ReturnContext->Gdtr);
270
271 //
272 // return to original caller
273 //
274 LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)EntrypointContext->JumpBuffer, 1);
275
276 //
277 // never be here
278 //
279 ASSERT (FALSE);
280 }
281
282 /**
283 Thunk function from 32-bit protection mode to long mode.
284
285 @param PageTableAddress Page table base address
286 @param Context Context for mode switching
287 @param ReturnContext Context for mode switching
288
289 @retval EFI_SUCCESS Function successfully executed.
290
291 **/
292 EFI_STATUS
293 Thunk32To64 (
294 EFI_PHYSICAL_ADDRESS PageTableAddress,
295 SWITCH_32_TO_64_CONTEXT *Context,
296 SWITCH_64_TO_32_CONTEXT *ReturnContext
297 )
298 {
299 UINTN SetJumpFlag;
300 EFI_STATUS Status;
301
302 //
303 // Save return address, LongJump will return here then
304 //
305 SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER *) (UINTN) Context->JumpBuffer);
306
307 if (SetJumpFlag == 0) {
308
309 //
310 // Build 4G Page Tables.
311 //
312 Create4GPageTables (PageTableAddress, Context->Page1GSupport);
313
314 //
315 // Create 64-bit GDT
316 //
317 AsmWriteGdtr (&mGdt);
318
319 //
320 // Write CR3
321 //
322 AsmWriteCr3 ((UINTN) PageTableAddress);
323
324 DEBUG ((
325 DEBUG_INFO,
326 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
327 __FUNCTION__,
328 Context->StackBufferBase,
329 Context->StackBufferLength
330 ));
331
332 //
333 // Disable interrupt of Debug timer, since the IDT table cannot work in long mode
334 //
335 SaveAndSetDebugTimerInterrupt (FALSE);
336 //
337 // Transfer to long mode
338 //
339 AsmEnablePaging64 (
340 0x38,
341 (UINT64) Context->EntryPoint,
342 (UINT64)(UINTN) Context,
343 (UINT64)(UINTN) ReturnContext,
344 Context->StackBufferBase + Context->StackBufferLength
345 );
346 }
347
348 //
349 // Convert to 32-bit Status and return
350 //
351 Status = EFI_SUCCESS;
352 if ((UINTN) ReturnContext->ReturnStatus != 0) {
353 Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus);
354 }
355
356 return Status;
357 }
358
359 /**
360 If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
361
362 @param LongModeBuffer The context of long mode.
363 @param CoalesceEntry Entry of coalesce image.
364 @param BlockListAddr Address of block list.
365 @param MemoryResource Pointer to the buffer of memory resource descriptor.
366 @param MemoryBase Base of memory range.
367 @param MemorySize Size of memory range.
368
369 @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
370 @retval Others Failed to execute coalesce in long mode.
371
372 **/
373 EFI_STATUS
374 ModeSwitch (
375 IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer,
376 IN COALESCE_ENTRY CoalesceEntry,
377 IN EFI_PHYSICAL_ADDRESS BlockListAddr,
378 IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
379 IN OUT VOID **MemoryBase,
380 IN OUT UINTN *MemorySize
381 )
382 {
383 EFI_STATUS Status;
384 EFI_PHYSICAL_ADDRESS MemoryBase64;
385 UINT64 MemorySize64;
386 EFI_PHYSICAL_ADDRESS MemoryEnd64;
387 SWITCH_32_TO_64_CONTEXT Context;
388 SWITCH_64_TO_32_CONTEXT ReturnContext;
389 BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
390 EFI_PHYSICAL_ADDRESS ReservedRangeBase;
391 EFI_PHYSICAL_ADDRESS ReservedRangeEnd;
392 BOOLEAN Page1GSupport;
393
394 ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));
395 ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));
396
397 MemoryBase64 = (UINT64) (UINTN) *MemoryBase;
398 MemorySize64 = (UINT64) (UINTN) *MemorySize;
399 MemoryEnd64 = MemoryBase64 + MemorySize64;
400
401 Page1GSupport = IsPage1GSupport ();
402
403 //
404 // Merge memory range reserved for stack and page table
405 //
406 if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {
407 ReservedRangeBase = LongModeBuffer->StackBaseAddress;
408 ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize (Page1GSupport);
409 } else {
410 ReservedRangeBase = LongModeBuffer->PageTableAddress;
411 ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;
412 }
413
414 //
415 // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
416 // If they are overlapped, get a larger range to process capsule data.
417 //
418 if (ReservedRangeBase <= MemoryBase64) {
419 if (ReservedRangeEnd < MemoryEnd64) {
420 MemoryBase64 = ReservedRangeEnd;
421 } else {
422 DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n"));
423 return EFI_OUT_OF_RESOURCES;
424 }
425 } else if (ReservedRangeBase < MemoryEnd64) {
426 if (ReservedRangeEnd < MemoryEnd64 &&
427 ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) {
428 MemoryBase64 = ReservedRangeEnd;
429 } else {
430 MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);
431 }
432 }
433
434 //
435 // Initialize context jumping to 64-bit enviroment
436 //
437 Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer;
438 Context.StackBufferBase = LongModeBuffer->StackBaseAddress;
439 Context.StackBufferLength = LongModeBuffer->StackSize;
440 Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry;
441 Context.BlockListAddr = BlockListAddr;
442 Context.MemoryResource = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryResource;
443 Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;
444 Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;
445 Context.Page1GSupport = Page1GSupport;
446
447 //
448 // Prepare data for return back
449 //
450 ReturnContext.ReturnCs = 0x10;
451 ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction;
452 //
453 // Will save the return status of processing capsule
454 //
455 ReturnContext.ReturnStatus = 0;
456
457 //
458 // Save original GDT
459 //
460 AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr);
461
462 Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext);
463
464 if (!EFI_ERROR (Status)) {
465 *MemoryBase = (VOID *) (UINTN) MemoryBase64;
466 *MemorySize = (UINTN) MemorySize64;
467 }
468
469 return Status;
470
471 }
472
473 /**
474 Locates the coalesce image entry point, and detects its machine type.
475
476 @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
477 @param CoalesceImageMachineType Pointer to machine type of coalesce image.
478
479 @retval EFI_SUCCESS Coalesce image successfully located.
480 @retval Others Failed to locate the coalesce image.
481
482 **/
483 EFI_STATUS
484 FindCapsuleCoalesceImage (
485 OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint,
486 OUT UINT16 *CoalesceImageMachineType
487 )
488 {
489 EFI_STATUS Status;
490 UINTN Instance;
491 EFI_PEI_LOAD_FILE_PPI *LoadFile;
492 EFI_PEI_FV_HANDLE VolumeHandle;
493 EFI_PEI_FILE_HANDLE FileHandle;
494 EFI_PHYSICAL_ADDRESS CoalesceImageAddress;
495 UINT64 CoalesceImageSize;
496 UINT32 AuthenticationState;
497
498 Instance = 0;
499
500 while (TRUE) {
501 Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);
502 if (EFI_ERROR (Status)) {
503 return Status;
504 }
505 Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle);
506 if (!EFI_ERROR (Status)) {
507 Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile);
508 ASSERT_EFI_ERROR (Status);
509
510 Status = LoadFile->LoadFile (
511 LoadFile,
512 FileHandle,
513 &CoalesceImageAddress,
514 &CoalesceImageSize,
515 CoalesceImageEntryPoint,
516 &AuthenticationState
517 );
518 if (EFI_ERROR (Status)) {
519 DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status));
520 return Status;
521 }
522 *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);
523 break;
524 } else {
525 continue;
526 }
527 }
528
529 return Status;
530 }
531
532 /**
533 Gets the reserved long mode buffer.
534
535 @param LongModeBuffer Pointer to the long mode buffer for output.
536
537 @retval EFI_SUCCESS Long mode buffer successfully retrieved.
538 @retval Others Variable storing long mode buffer not found.
539
540 **/
541 EFI_STATUS
542 GetLongModeContext (
543 OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer
544 )
545 {
546 EFI_STATUS Status;
547 UINTN Size;
548 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
549
550 Status = PeiServicesLocatePpi (
551 &gEfiPeiReadOnlyVariable2PpiGuid,
552 0,
553 NULL,
554 (VOID **) &PPIVariableServices
555 );
556 ASSERT_EFI_ERROR (Status);
557
558 Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);
559 Status = PPIVariableServices->GetVariable (
560 PPIVariableServices,
561 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
562 &gEfiCapsuleVendorGuid,
563 NULL,
564 &Size,
565 LongModeBuffer
566 );
567 if (EFI_ERROR (Status)) {
568 DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));
569 }
570 return Status;
571 }
572 #endif
573
574 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
575 /**
576 Get physical address bits.
577
578 @return Physical address bits.
579
580 **/
581 UINT8
582 GetPhysicalAddressBits (
583 VOID
584 )
585 {
586 UINT32 RegEax;
587 UINT8 PhysicalAddressBits;
588 VOID *Hob;
589
590 //
591 // Get physical address bits supported.
592 //
593 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
594 if (Hob != NULL) {
595 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
596 } else {
597 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
598 if (RegEax >= 0x80000008) {
599 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
600 PhysicalAddressBits = (UINT8) RegEax;
601 } else {
602 PhysicalAddressBits = 36;
603 }
604 }
605
606 //
607 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
608 //
609 ASSERT (PhysicalAddressBits <= 52);
610 if (PhysicalAddressBits > 48) {
611 PhysicalAddressBits = 48;
612 }
613
614 return PhysicalAddressBits;
615 }
616 #endif
617
618 /**
619 Build memory resource descriptor from resource descriptor in HOB list.
620
621 @return Pointer to the buffer of memory resource descriptor.
622 NULL if no memory resource descriptor reported in HOB list
623 before capsule Coalesce.
624
625 **/
626 MEMORY_RESOURCE_DESCRIPTOR *
627 BuildMemoryResourceDescriptor (
628 VOID
629 )
630 {
631 EFI_PEI_HOB_POINTERS Hob;
632 UINTN Index;
633 EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor;
634 MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;
635 EFI_STATUS Status;
636
637 //
638 // Get the count of memory resource descriptor.
639 //
640 Index = 0;
641 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
642 while (Hob.Raw != NULL) {
643 ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;
644 if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
645 Index++;
646 }
647 Hob.Raw = GET_NEXT_HOB (Hob);
648 Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
649 }
650
651 if (Index == 0) {
652 DEBUG ((EFI_D_INFO | EFI_D_WARN, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));
653 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
654 //
655 // Allocate memory to hold memory resource descriptor,
656 // include extra one NULL terminate memory resource descriptor.
657 //
658 Status = PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);
659 ASSERT_EFI_ERROR (Status);
660 ZeroMem (MemoryResource, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));
661
662 MemoryResource[0].PhysicalStart = 0;
663 MemoryResource[0].ResourceLength = LShiftU64 (1, GetPhysicalAddressBits ());
664 DEBUG ((EFI_D_INFO, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",
665 MemoryResource[0x0].PhysicalStart, MemoryResource[0x0].ResourceLength));
666 return MemoryResource;
667 #else
668 return NULL;
669 #endif
670 }
671
672 //
673 // Allocate memory to hold memory resource descriptor,
674 // include extra one NULL terminate memory resource descriptor.
675 //
676 Status = PeiServicesAllocatePool ((Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);
677 ASSERT_EFI_ERROR (Status);
678 ZeroMem (MemoryResource, (Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));
679
680 //
681 // Get the content of memory resource descriptor.
682 //
683 Index = 0;
684 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
685 while (Hob.Raw != NULL) {
686 ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;
687 if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
688 DEBUG ((EFI_D_INFO, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
689 Index, ResourceDescriptor->PhysicalStart, ResourceDescriptor->ResourceLength));
690 MemoryResource[Index].PhysicalStart = ResourceDescriptor->PhysicalStart;
691 MemoryResource[Index].ResourceLength = ResourceDescriptor->ResourceLength;
692 Index++;
693 }
694 Hob.Raw = GET_NEXT_HOB (Hob);
695 Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
696 }
697
698 return MemoryResource;
699 }
700
701 /**
702 Checks for the presence of capsule descriptors.
703 Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
704 and save to DescriptorBuffer.
705
706 @param DescriptorBuffer Pointer to the capsule descriptors
707
708 @retval EFI_SUCCESS a valid capsule is present
709 @retval EFI_NOT_FOUND if a valid capsule is not present
710 **/
711 EFI_STATUS
712 GetCapsuleDescriptors (
713 IN EFI_PHYSICAL_ADDRESS *DescriptorBuffer
714 )
715 {
716 EFI_STATUS Status;
717 UINTN Size;
718 UINTN Index;
719 UINTN TempIndex;
720 UINTN ValidIndex;
721 BOOLEAN Flag;
722 CHAR16 CapsuleVarName[30];
723 CHAR16 *TempVarName;
724 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
725 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
726
727 Index = 0;
728 TempVarName = NULL;
729 CapsuleVarName[0] = 0;
730 ValidIndex = 0;
731 CapsuleDataPtr64 = 0;
732
733 Status = PeiServicesLocatePpi (
734 &gEfiPeiReadOnlyVariable2PpiGuid,
735 0,
736 NULL,
737 (VOID **) &PPIVariableServices
738 );
739 if (Status == EFI_SUCCESS) {
740 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);
741 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
742 Size = sizeof (CapsuleDataPtr64);
743 while (1) {
744 if (Index == 0) {
745 //
746 // For the first Capsule Image
747 //
748 Status = PPIVariableServices->GetVariable (
749 PPIVariableServices,
750 CapsuleVarName,
751 &gEfiCapsuleVendorGuid,
752 NULL,
753 &Size,
754 (VOID *) &CapsuleDataPtr64
755 );
756 if (EFI_ERROR (Status)) {
757 DEBUG ((EFI_D_ERROR, "Capsule -- capsule variable not set\n"));
758 return EFI_NOT_FOUND;
759 }
760 //
761 // We have a chicken/egg situation where the memory init code needs to
762 // know the boot mode prior to initializing memory. For this case, our
763 // validate function will fail. We can detect if this is the case if blocklist
764 // pointer is null. In that case, return success since we know that the
765 // variable is set.
766 //
767 if (DescriptorBuffer == NULL) {
768 return EFI_SUCCESS;
769 }
770 } else {
771 UnicodeValueToString (TempVarName, 0, Index, 0);
772 Status = PPIVariableServices->GetVariable (
773 PPIVariableServices,
774 CapsuleVarName,
775 &gEfiCapsuleVendorGuid,
776 NULL,
777 &Size,
778 (VOID *) &CapsuleDataPtr64
779 );
780 if (EFI_ERROR (Status)) {
781 break;
782 }
783
784 //
785 // If this BlockList has been linked before, skip this variable
786 //
787 Flag = FALSE;
788 for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {
789 if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64) {
790 Flag = TRUE;
791 break;
792 }
793 }
794 if (Flag) {
795 Index ++;
796 continue;
797 }
798 }
799
800 //
801 // Cache BlockList which has been processed
802 //
803 DescriptorBuffer[ValidIndex++] = CapsuleDataPtr64;
804 Index ++;
805 }
806 }
807
808 return EFI_SUCCESS;
809 }
810
811 /**
812 Capsule PPI service to coalesce a fragmented capsule in memory.
813
814 @param PeiServices General purpose services available to every PEIM.
815 @param MemoryBase Pointer to the base of a block of memory that we can walk
816 all over while trying to coalesce our buffers.
817 On output, this variable will hold the base address of
818 a coalesced capsule.
819 @param MemorySize Size of the memory region pointed to by MemoryBase.
820 On output, this variable will contain the size of the
821 coalesced capsule.
822
823 @retval EFI_NOT_FOUND if we can't determine the boot mode
824 if the boot mode is not flash-update
825 if we could not find the capsule descriptors
826
827 @retval EFI_BUFFER_TOO_SMALL
828 if we could not coalesce the capsule in the memory
829 region provided to us
830
831 @retval EFI_SUCCESS if there's no capsule, or if we processed the
832 capsule successfully.
833 **/
834 EFI_STATUS
835 EFIAPI
836 CapsuleCoalesce (
837 IN EFI_PEI_SERVICES **PeiServices,
838 IN OUT VOID **MemoryBase,
839 IN OUT UINTN *MemorySize
840 )
841 {
842 UINTN Index;
843 UINTN Size;
844 UINTN VariableCount;
845 CHAR16 CapsuleVarName[30];
846 CHAR16 *TempVarName;
847 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
848 EFI_STATUS Status;
849 EFI_BOOT_MODE BootMode;
850 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
851 EFI_PHYSICAL_ADDRESS *VariableArrayAddress;
852 MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;
853 #ifdef MDE_CPU_IA32
854 UINT16 CoalesceImageMachineType;
855 EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint;
856 COALESCE_ENTRY CoalesceEntry;
857 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
858 #endif
859
860 Index = 0;
861 VariableCount = 0;
862 CapsuleVarName[0] = 0;
863 CapsuleDataPtr64 = 0;
864
865 //
866 // Someone should have already ascertained the boot mode. If it's not
867 // capsule update, then return normally.
868 //
869 Status = PeiServicesGetBootMode (&BootMode);
870 if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {
871 DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n"));
872 Status = EFI_NOT_FOUND;
873 goto Done;
874 }
875
876 //
877 // User may set the same ScatterGatherList with several different variables,
878 // so cache all ScatterGatherList for check later.
879 //
880 Status = PeiServicesLocatePpi (
881 &gEfiPeiReadOnlyVariable2PpiGuid,
882 0,
883 NULL,
884 (VOID **) &PPIVariableServices
885 );
886 if (EFI_ERROR (Status)) {
887 goto Done;
888 }
889 Size = sizeof (CapsuleDataPtr64);
890 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);
891 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
892 while (TRUE) {
893 if (Index > 0) {
894 UnicodeValueToString (TempVarName, 0, Index, 0);
895 }
896 Status = PPIVariableServices->GetVariable (
897 PPIVariableServices,
898 CapsuleVarName,
899 &gEfiCapsuleVendorGuid,
900 NULL,
901 &Size,
902 (VOID *) &CapsuleDataPtr64
903 );
904 if (EFI_ERROR (Status)) {
905 //
906 // There is no capsule variables, quit
907 //
908 DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index));
909 break;
910 }
911 VariableCount++;
912 Index++;
913 }
914
915 DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount));
916
917 //
918 // The last entry is the end flag.
919 //
920 Status = PeiServicesAllocatePool (
921 (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS),
922 (VOID **)&VariableArrayAddress
923 );
924
925 if (Status != EFI_SUCCESS) {
926 DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));
927 goto Done;
928 }
929
930 ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS));
931
932 //
933 // Find out if we actually have a capsule.
934 // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.
935 //
936 Status = GetCapsuleDescriptors (VariableArrayAddress);
937 if (EFI_ERROR (Status)) {
938 DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n"));
939 goto Done;
940 }
941
942 MemoryResource = BuildMemoryResourceDescriptor ();
943
944 #ifdef MDE_CPU_IA32
945 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
946 //
947 // Switch to 64-bit mode to process capsule data when:
948 // 1. When DXE phase is 64-bit
949 // 2. When the buffer for 64-bit transition exists
950 // 3. When Capsule X64 image is built in BIOS image
951 // In 64-bit mode, we can process capsule data above 4GB.
952 //
953 CoalesceImageEntryPoint = 0;
954 Status = GetLongModeContext (&LongModeBuffer);
955 if (EFI_ERROR (Status)) {
956 DEBUG ((EFI_D_ERROR, "Fail to find the variable for long mode context!\n"));
957 Status = EFI_NOT_FOUND;
958 goto Done;
959 }
960
961 Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);
962 if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {
963 DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n"));
964 Status = EFI_NOT_FOUND;
965 goto Done;
966 }
967 ASSERT (CoalesceImageEntryPoint != 0);
968 CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint;
969 Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
970 } else {
971 //
972 // Capsule is processed in IA32 mode.
973 //
974 Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
975 }
976 #else
977 //
978 // Process capsule directly.
979 //
980 Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
981 #endif
982
983 DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status));
984
985 if (Status == EFI_BUFFER_TOO_SMALL) {
986 DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n"));
987 }
988
989 if (Status == EFI_NOT_FOUND) {
990 DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n"));
991 REPORT_STATUS_CODE (
992 EFI_ERROR_CODE | EFI_ERROR_MAJOR,
993 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR)
994 );
995 }
996
997 Done:
998 return Status;
999 }
1000
1001 /**
1002 Determine if we're in capsule update boot mode.
1003
1004 @param PeiServices PEI services table
1005
1006 @retval EFI_SUCCESS if we have a capsule available
1007 @retval EFI_NOT_FOUND no capsule detected
1008
1009 **/
1010 EFI_STATUS
1011 EFIAPI
1012 CheckCapsuleUpdate (
1013 IN EFI_PEI_SERVICES **PeiServices
1014 )
1015 {
1016 EFI_STATUS Status;
1017 Status = GetCapsuleDescriptors (NULL);
1018 return Status;
1019 }
1020 /**
1021 This function will look at a capsule and determine if it's a test pattern.
1022 If it is, then it will verify it and emit an error message if corruption is detected.
1023
1024 @param PeiServices Standard pei services pointer
1025 @param CapsuleBase Base address of coalesced capsule, which is preceeded
1026 by private data. Very implementation specific.
1027
1028 @retval TRUE Capsule image is the test image
1029 @retval FALSE Capsule image is not the test image.
1030
1031 **/
1032 BOOLEAN
1033 CapsuleTestPattern (
1034 IN EFI_PEI_SERVICES **PeiServices,
1035 IN VOID *CapsuleBase
1036 )
1037 {
1038 UINT32 *TestPtr;
1039 UINT32 TestCounter;
1040 UINT32 TestSize;
1041 BOOLEAN RetValue;
1042
1043 RetValue = FALSE;
1044
1045 //
1046 // Look at the capsule data and determine if it's a test pattern. If it
1047 // is, then test it now.
1048 //
1049 TestPtr = (UINT32 *) CapsuleBase;
1050 //
1051 // 0x54534554 "TEST"
1052 //
1053 if (*TestPtr == 0x54534554) {
1054 RetValue = TRUE;
1055 DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));
1056 TestSize = TestPtr[1] / sizeof (UINT32);
1057 //
1058 // Skip over the signature and the size fields in the pattern data header
1059 //
1060 TestPtr += 2;
1061 TestCounter = 0;
1062 while (TestSize > 0) {
1063 if (*TestPtr != TestCounter) {
1064 DEBUG ((EFI_D_INFO, "Capsule test pattern mode FAILED: BaseAddr/FailAddr 0x%X 0x%X\n", (UINT32)(UINTN)(EFI_CAPSULE_PEIM_PRIVATE_DATA *)CapsuleBase, (UINT32)(UINTN)TestPtr));
1065 return TRUE;
1066 }
1067
1068 TestPtr++;
1069 TestCounter++;
1070 TestSize--;
1071 }
1072
1073 DEBUG ((EFI_D_INFO, "Capsule test pattern mode SUCCESS\n"));
1074 }
1075
1076 return RetValue;
1077 }
1078
1079 /**
1080 Capsule PPI service that gets called after memory is available. The
1081 capsule coalesce function, which must be called first, returns a base
1082 address and size, which can be anything actually. Once the memory init
1083 PEIM has discovered memory, then it should call this function and pass in
1084 the base address and size returned by the coalesce function. Then this
1085 function can create a capsule HOB and return.
1086
1087 @param PeiServices standard pei services pointer
1088 @param CapsuleBase address returned by the capsule coalesce function. Most
1089 likely this will actually be a pointer to private data.
1090 @param CapsuleSize value returned by the capsule coalesce function.
1091
1092 @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
1093 coalesced capsule
1094 @retval EFI_SUCCESS if all goes well.
1095 **/
1096 EFI_STATUS
1097 EFIAPI
1098 CreateState (
1099 IN EFI_PEI_SERVICES **PeiServices,
1100 IN VOID *CapsuleBase,
1101 IN UINTN CapsuleSize
1102 )
1103 {
1104 EFI_STATUS Status;
1105 EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData;
1106 UINTN Size;
1107 EFI_PHYSICAL_ADDRESS NewBuffer;
1108 UINTN CapsuleNumber;
1109 UINT32 Index;
1110 EFI_PHYSICAL_ADDRESS BaseAddress;
1111 UINT64 Length;
1112
1113 PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase;
1114 if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) {
1115 return EFI_VOLUME_CORRUPTED;
1116 }
1117 if (PrivateData->CapsuleAllImageSize >= MAX_ADDRESS) {
1118 DEBUG ((EFI_D_ERROR, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData->CapsuleAllImageSize));
1119 return EFI_OUT_OF_RESOURCES;
1120 }
1121 if (PrivateData->CapsuleNumber >= MAX_ADDRESS) {
1122 DEBUG ((EFI_D_ERROR, "CapsuleNumber too big - 0x%lx\n", PrivateData->CapsuleNumber));
1123 return EFI_OUT_OF_RESOURCES;
1124 }
1125 //
1126 // Capsule Number and Capsule Offset is in the tail of Capsule data.
1127 //
1128 Size = (UINTN)PrivateData->CapsuleAllImageSize;
1129 CapsuleNumber = (UINTN)PrivateData->CapsuleNumber;
1130 //
1131 // Allocate the memory so that it gets preserved into DXE
1132 //
1133 Status = PeiServicesAllocatePages (
1134 EfiRuntimeServicesData,
1135 EFI_SIZE_TO_PAGES (Size),
1136 &NewBuffer
1137 );
1138
1139 if (Status != EFI_SUCCESS) {
1140 DEBUG ((EFI_D_ERROR, "AllocatePages Failed!\n"));
1141 return Status;
1142 }
1143 //
1144 // Copy to our new buffer for DXE
1145 //
1146 DEBUG ((EFI_D_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN)((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), (UINTN) NewBuffer, Size));
1147 CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) ((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), Size);
1148 //
1149 // Check for test data pattern. If it is the test pattern, then we'll
1150 // test it and still create the HOB so that it can be used to verify
1151 // that capsules don't get corrupted all the way into BDS. BDS will
1152 // still try to turn it into a firmware volume, but will think it's
1153 // corrupted so nothing will happen.
1154 //
1155 DEBUG_CODE (
1156 CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer);
1157 );
1158
1159 //
1160 // Build the UEFI Capsule Hob for each capsule image.
1161 //
1162 for (Index = 0; Index < CapsuleNumber; Index ++) {
1163 BaseAddress = NewBuffer + PrivateData->CapsuleOffset[Index];
1164 Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize;
1165
1166 BuildCvHob (BaseAddress, Length);
1167 }
1168
1169 return EFI_SUCCESS;
1170 }
1171
1172 CONST EFI_PEI_CAPSULE_PPI mCapsulePpi = {
1173 CapsuleCoalesce,
1174 CheckCapsuleUpdate,
1175 CreateState
1176 };
1177
1178 CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = {
1179 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
1180 &gEfiPeiCapsulePpiGuid,
1181 (EFI_PEI_CAPSULE_PPI *) &mCapsulePpi
1182 };
1183
1184 /**
1185 Entry point function for the PEIM
1186
1187 @param FileHandle Handle of the file being invoked.
1188 @param PeiServices Describes the list of possible PEI Services.
1189
1190 @return EFI_SUCCESS If we installed our PPI
1191
1192 **/
1193 EFI_STATUS
1194 EFIAPI
1195 CapsuleMain (
1196 IN EFI_PEI_FILE_HANDLE FileHandle,
1197 IN CONST EFI_PEI_SERVICES **PeiServices
1198 )
1199 {
1200 //
1201 // Just produce our PPI
1202 //
1203 return PeiServicesInstallPpi (&mUefiPpiListCapsule);
1204 }