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