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