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