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