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