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