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