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