]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
Add PCD for 1G page table
[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 #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
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
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
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
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 // Transfer to long mode
354 //
355 AsmEnablePaging64 (
356 0x38,
357 (UINT64) Context->EntryPoint,
358 (UINT64)(UINTN) Context,
359 (UINT64)(UINTN) ReturnContext,
360 Context->StackBufferBase + Context->StackBufferLength
361 );
362 }
363
364 //
365 // Convert to 32-bit Status and return
366 //
367 Status = EFI_SUCCESS;
368 if ((UINTN) ReturnContext->ReturnStatus != 0) {
369 Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus);
370 }
371
372 return Status;
373 }
374
375 /**
376 If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
377
378 @param LongModeBuffer The context of long mode.
379 @param CoalesceEntry Entry of coalesce image.
380 @param BlockListAddr Address of block list.
381 @param MemoryBase Base of memory range.
382 @param MemorySize Size of memory range.
383
384 @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
385 @retval Others Failed to execute coalesce in long mode.
386
387 **/
388 EFI_STATUS
389 ModeSwitch (
390 IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer,
391 IN COALESCE_ENTRY CoalesceEntry,
392 IN EFI_PHYSICAL_ADDRESS BlockListAddr,
393 IN OUT VOID **MemoryBase,
394 IN OUT UINTN *MemorySize
395 )
396 {
397 EFI_STATUS Status;
398 EFI_PHYSICAL_ADDRESS MemoryBase64;
399 UINT64 MemorySize64;
400 EFI_PHYSICAL_ADDRESS MemoryEnd64;
401 SWITCH_32_TO_64_CONTEXT Context;
402 SWITCH_64_TO_32_CONTEXT ReturnContext;
403 BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
404 EFI_PHYSICAL_ADDRESS ReservedRangeBase;
405 EFI_PHYSICAL_ADDRESS ReservedRangeEnd;
406
407 ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));
408 ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));
409
410 MemoryBase64 = (UINT64) (UINTN) *MemoryBase;
411 MemorySize64 = (UINT64) (UINTN) *MemorySize;
412 MemoryEnd64 = MemoryBase64 + MemorySize64;
413
414 //
415 // Merge memory range reserved for stack and page table
416 //
417 if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {
418 ReservedRangeBase = LongModeBuffer->StackBaseAddress;
419 ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize ();
420 } else {
421 ReservedRangeBase = LongModeBuffer->PageTableAddress;
422 ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;
423 }
424
425 //
426 // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
427 // If they are overlapped, get a larger range to process capsule data.
428 //
429 if (ReservedRangeBase <= MemoryBase64) {
430 if (ReservedRangeEnd < MemoryEnd64) {
431 MemoryBase64 = ReservedRangeEnd;
432 } else {
433 DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n"));
434 return EFI_OUT_OF_RESOURCES;
435 }
436 } else if (ReservedRangeBase < MemoryEnd64) {
437 if (ReservedRangeEnd < MemoryEnd64 &&
438 ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) {
439 MemoryBase64 = ReservedRangeEnd;
440 } else {
441 MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);
442 }
443 }
444
445 //
446 // Initialize context jumping to 64-bit enviroment
447 //
448 Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer;
449 Context.StackBufferBase = LongModeBuffer->StackBaseAddress;
450 Context.StackBufferLength = LongModeBuffer->StackSize;
451 Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry;
452 Context.BlockListAddr = BlockListAddr;
453 Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;
454 Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;
455
456 //
457 // Prepare data for return back
458 //
459 ReturnContext.ReturnCs = 0x10;
460 ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction;
461 //
462 // Will save the return status of processing capsule
463 //
464 ReturnContext.ReturnStatus = 0;
465
466 //
467 // Save original GDT
468 //
469 AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr);
470
471 Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext);
472
473 if (!EFI_ERROR (Status)) {
474 *MemoryBase = (VOID *) (UINTN) MemoryBase64;
475 *MemorySize = (UINTN) MemorySize64;
476 }
477
478 return Status;
479
480 }
481
482 /**
483 Locates the coalesce image entry point, and detects its machine type.
484
485 @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
486 @param CoalesceImageMachineType Pointer to machine type of coalesce image.
487
488 @retval EFI_SUCCESS Coalesce image successfully located.
489 @retval Others Failed to locate the coalesce image.
490
491 **/
492 EFI_STATUS
493 FindCapsuleCoalesceImage (
494 OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint,
495 OUT UINT16 *CoalesceImageMachineType
496 )
497 {
498 EFI_STATUS Status;
499 UINTN Instance;
500 EFI_PEI_LOAD_FILE_PPI *LoadFile;
501 EFI_PEI_FV_HANDLE VolumeHandle;
502 EFI_PEI_FILE_HANDLE FileHandle;
503 EFI_PHYSICAL_ADDRESS CoalesceImageAddress;
504 UINT64 CoalesceImageSize;
505 UINT32 AuthenticationState;
506
507 Instance = 0;
508
509 while (TRUE) {
510 Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);
511 if (EFI_ERROR (Status)) {
512 return Status;
513 }
514 Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle);
515 if (!EFI_ERROR (Status)) {
516 Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile);
517 ASSERT_EFI_ERROR (Status);
518
519 Status = LoadFile->LoadFile (
520 LoadFile,
521 FileHandle,
522 &CoalesceImageAddress,
523 &CoalesceImageSize,
524 CoalesceImageEntryPoint,
525 &AuthenticationState
526 );
527 if (EFI_ERROR (Status)) {
528 DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleRelocate image ffs %r!\n", Status));
529 return Status;
530 }
531 *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);
532 break;
533 } else {
534 continue;
535 }
536 }
537
538 return Status;
539 }
540
541 #endif
542
543 /**
544 Checks for the presence of capsule descriptors.
545 Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
546 and save to DescriptorBuffer.
547
548 @param DescriptorBuffer Pointer to the capsule descriptors
549
550 @retval EFI_SUCCESS a valid capsule is present
551 @retval EFI_NOT_FOUND if a valid capsule is not present
552 **/
553 EFI_STATUS
554 GetCapsuleDescriptors (
555 IN EFI_PHYSICAL_ADDRESS *DescriptorBuffer
556 )
557 {
558 EFI_STATUS Status;
559 UINTN Size;
560 UINTN Index;
561 UINTN TempIndex;
562 UINTN ValidIndex;
563 BOOLEAN Flag;
564 CHAR16 CapsuleVarName[30];
565 CHAR16 *TempVarName;
566 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
567 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
568
569 Index = 0;
570 TempVarName = NULL;
571 CapsuleVarName[0] = 0;
572 ValidIndex = 0;
573
574 Status = PeiServicesLocatePpi (
575 &gEfiPeiReadOnlyVariable2PpiGuid,
576 0,
577 NULL,
578 (VOID **) &PPIVariableServices
579 );
580 if (Status == EFI_SUCCESS) {
581 StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME);
582 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
583 Size = sizeof (CapsuleDataPtr64);
584 while (1) {
585 if (Index == 0) {
586 //
587 // For the first Capsule Image
588 //
589 Status = PPIVariableServices->GetVariable (
590 PPIVariableServices,
591 CapsuleVarName,
592 &gEfiCapsuleVendorGuid,
593 NULL,
594 &Size,
595 (VOID *) &CapsuleDataPtr64
596 );
597 if (EFI_ERROR (Status)) {
598 DEBUG ((EFI_D_ERROR, "Capsule -- capsule variable not set\n"));
599 return EFI_NOT_FOUND;
600 }
601 //
602 // We have a chicken/egg situation where the memory init code needs to
603 // know the boot mode prior to initializing memory. For this case, our
604 // validate function will fail. We can detect if this is the case if blocklist
605 // pointer is null. In that case, return success since we know that the
606 // variable is set.
607 //
608 if (DescriptorBuffer == NULL) {
609 return EFI_SUCCESS;
610 }
611 } else {
612 UnicodeValueToString (TempVarName, 0, Index, 0);
613 Status = PPIVariableServices->GetVariable (
614 PPIVariableServices,
615 CapsuleVarName,
616 &gEfiCapsuleVendorGuid,
617 NULL,
618 &Size,
619 (VOID *) &CapsuleDataPtr64
620 );
621 if (EFI_ERROR (Status)) {
622 break;
623 }
624
625 //
626 // If this BlockList has been linked before, skip this variable
627 //
628 Flag = FALSE;
629 for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {
630 if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64) {
631 Flag = TRUE;
632 break;
633 }
634 }
635 if (Flag) {
636 Index ++;
637 continue;
638 }
639 }
640
641 //
642 // Cache BlockList which has been processed
643 //
644 DescriptorBuffer[ValidIndex++] = CapsuleDataPtr64;
645 Index ++;
646 }
647 }
648
649 return EFI_SUCCESS;
650 }
651
652 /**
653 Gets the reserved long mode buffer.
654
655 @param LongModeBuffer Pointer to the long mode buffer for output.
656
657 @retval EFI_SUCCESS Long mode buffer successfully retrieved.
658 @retval Others Variable storing long mode buffer not found.
659
660 **/
661 EFI_STATUS
662 GetLongModeContext (
663 OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer
664 )
665 {
666 EFI_STATUS Status;
667 UINTN Size;
668 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
669
670 Status = PeiServicesLocatePpi (
671 &gEfiPeiReadOnlyVariable2PpiGuid,
672 0,
673 NULL,
674 (VOID **) &PPIVariableServices
675 );
676 ASSERT_EFI_ERROR (Status);
677
678 Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);
679 Status = PPIVariableServices->GetVariable (
680 PPIVariableServices,
681 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
682 &gEfiCapsuleVendorGuid,
683 NULL,
684 &Size,
685 LongModeBuffer
686 );
687 if (EFI_ERROR (Status)) {
688 DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));
689 }
690 return Status;
691 }
692
693 /**
694 Capsule PPI service to coalesce a fragmented capsule in memory.
695
696 @param PeiServices General purpose services available to every PEIM.
697 @param MemoryBase Pointer to the base of a block of memory that we can walk
698 all over while trying to coalesce our buffers.
699 On output, this variable will hold the base address of
700 a coalesced capsule.
701 @param MemorySize Size of the memory region pointed to by MemoryBase.
702 On output, this variable will contain the size of the
703 coalesced capsule.
704
705 @retval EFI_NOT_FOUND if we can't determine the boot mode
706 if the boot mode is not flash-update
707 if we could not find the capsule descriptors
708
709 @retval EFI_BUFFER_TOO_SMALL
710 if we could not coalesce the capsule in the memory
711 region provided to us
712
713 @retval EFI_SUCCESS if there's no capsule, or if we processed the
714 capsule successfully.
715 **/
716 EFI_STATUS
717 EFIAPI
718 CapsuleCoalesce (
719 IN EFI_PEI_SERVICES **PeiServices,
720 IN OUT VOID **MemoryBase,
721 IN OUT UINTN *MemorySize
722 )
723 {
724 UINTN Index;
725 UINTN Size;
726 UINTN VariableCount;
727 CHAR16 CapsuleVarName[30];
728 CHAR16 *TempVarName;
729 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
730 EFI_STATUS Status;
731 EFI_BOOT_MODE BootMode;
732 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
733 EFI_PHYSICAL_ADDRESS *VariableArrayAddress;
734 #ifdef MDE_CPU_IA32
735 UINT16 CoalesceImageMachineType;
736 EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint;
737 COALESCE_ENTRY CoalesceEntry;
738 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
739 #endif
740
741 Index = 0;
742 VariableCount = 0;
743 CapsuleVarName[0] = 0;
744
745 //
746 // Someone should have already ascertained the boot mode. If it's not
747 // capsule update, then return normally.
748 //
749 Status = PeiServicesGetBootMode (&BootMode);
750 if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {
751 DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n"));
752 Status = EFI_NOT_FOUND;
753 goto Done;
754 }
755
756 //
757 // User may set the same ScatterGatherList with several different variables,
758 // so cache all ScatterGatherList for check later.
759 //
760 Status = PeiServicesLocatePpi (
761 &gEfiPeiReadOnlyVariable2PpiGuid,
762 0,
763 NULL,
764 (VOID **) &PPIVariableServices
765 );
766 if (EFI_ERROR (Status)) {
767 goto Done;
768 }
769 Size = sizeof (CapsuleDataPtr64);
770 StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME);
771 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
772 while (TRUE) {
773 if (Index > 0) {
774 UnicodeValueToString (TempVarName, 0, Index, 0);
775 }
776 Status = PPIVariableServices->GetVariable (
777 PPIVariableServices,
778 CapsuleVarName,
779 &gEfiCapsuleVendorGuid,
780 NULL,
781 &Size,
782 (VOID *) &CapsuleDataPtr64
783 );
784 if (EFI_ERROR (Status)) {
785 //
786 // There is no capsule variables, quit
787 //
788 DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index));
789 break;
790 }
791 VariableCount++;
792 Index++;
793 }
794
795 DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount));
796
797 //
798 // The last entry is the end flag.
799 //
800 Status = PeiServicesAllocatePool (
801 (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS),
802 (VOID **)&VariableArrayAddress
803 );
804
805 if (Status != EFI_SUCCESS) {
806 DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));
807 goto Done;
808 }
809
810 ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS));
811
812 //
813 // Find out if we actually have a capsule.
814 // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.
815 //
816 Status = GetCapsuleDescriptors (VariableArrayAddress);
817 if (EFI_ERROR (Status)) {
818 DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n"));
819 goto Done;
820 }
821
822 #ifdef MDE_CPU_IA32
823 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
824 //
825 // Switch to 64-bit mode to process capsule data when:
826 // 1. When DXE phase is 64-bit
827 // 2. When the buffer for 64-bit transition exists
828 // 3. When Capsule X64 image is built in BIOS image
829 // In 64-bit mode, we can process capsule data above 4GB.
830 //
831 CoalesceImageEntryPoint = 0;
832 Status = GetLongModeContext (&LongModeBuffer);
833 if (EFI_ERROR (Status)) {
834 DEBUG ((EFI_D_ERROR, "Fail to find the variables for long mode context!\n"));
835 Status = EFI_NOT_FOUND;
836 goto Done;
837 }
838
839 Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);
840 if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {
841 DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n"));
842 Status = EFI_NOT_FOUND;
843 goto Done;
844 }
845 ASSERT (CoalesceImageEntryPoint != 0);
846 CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint;
847 Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);
848 } else {
849 //
850 // Capsule is processed in IA32 mode.
851 //
852 Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);
853 }
854 #else
855 //
856 // Process capsule directly.
857 //
858 Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);
859 #endif
860
861 DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status));
862
863 if (Status == EFI_BUFFER_TOO_SMALL) {
864 DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n"));
865 }
866
867 if (Status == EFI_NOT_FOUND) {
868 DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n"));
869 REPORT_STATUS_CODE (
870 EFI_ERROR_CODE | EFI_ERROR_MAJOR,
871 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR)
872 );
873 }
874
875 Done:
876 return Status;
877 }
878
879 /**
880 Determine if we're in capsule update boot mode.
881
882 @param PeiServices PEI services table
883
884 @retval EFI_SUCCESS if we have a capsule available
885 @retval EFI_NOT_FOUND no capsule detected
886
887 **/
888 EFI_STATUS
889 EFIAPI
890 CheckCapsuleUpdate (
891 IN EFI_PEI_SERVICES **PeiServices
892 )
893 {
894 EFI_STATUS Status;
895 Status = GetCapsuleDescriptors (NULL);
896 return Status;
897 }
898 /**
899 This function will look at a capsule and determine if it's a test pattern.
900 If it is, then it will verify it and emit an error message if corruption is detected.
901
902 @param PeiServices Standard pei services pointer
903 @param CapsuleBase Base address of coalesced capsule, which is preceeded
904 by private data. Very implementation specific.
905
906 @retval TRUE Capsule image is the test image
907 @retval FALSE Capsule image is not the test image.
908
909 **/
910 BOOLEAN
911 CapsuleTestPattern (
912 IN EFI_PEI_SERVICES **PeiServices,
913 IN VOID *CapsuleBase
914 )
915 {
916 UINT32 *TestPtr;
917 UINT32 TestCounter;
918 UINT32 TestSize;
919 BOOLEAN RetValue;
920
921 RetValue = FALSE;
922
923 //
924 // Look at the capsule data and determine if it's a test pattern. If it
925 // is, then test it now.
926 //
927 TestPtr = (UINT32 *) CapsuleBase;
928 //
929 // 0x54534554 "TEST"
930 //
931 if (*TestPtr == 0x54534554) {
932 RetValue = TRUE;
933 DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));
934 TestSize = TestPtr[1] / sizeof (UINT32);
935 //
936 // Skip over the signature and the size fields in the pattern data header
937 //
938 TestPtr += 2;
939 TestCounter = 0;
940 while (TestSize > 0) {
941 if (*TestPtr != TestCounter) {
942 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));
943 return TRUE;
944 }
945
946 TestPtr++;
947 TestCounter++;
948 TestSize--;
949 }
950
951 DEBUG ((EFI_D_INFO, "Capsule test pattern mode SUCCESS\n"));
952 }
953
954 return RetValue;
955 }
956
957 /**
958 Capsule PPI service that gets called after memory is available. The
959 capsule coalesce function, which must be called first, returns a base
960 address and size, which can be anything actually. Once the memory init
961 PEIM has discovered memory, then it should call this function and pass in
962 the base address and size returned by the coalesce function. Then this
963 function can create a capsule HOB and return.
964
965 @param PeiServices standard pei services pointer
966 @param CapsuleBase address returned by the capsule coalesce function. Most
967 likely this will actually be a pointer to private data.
968 @param CapsuleSize value returned by the capsule coalesce function.
969
970 @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
971 coalesced capsule
972 @retval EFI_SUCCESS if all goes well.
973 **/
974 EFI_STATUS
975 EFIAPI
976 CreateState (
977 IN EFI_PEI_SERVICES **PeiServices,
978 IN VOID *CapsuleBase,
979 IN UINTN CapsuleSize
980 )
981 {
982 EFI_STATUS Status;
983 EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData;
984 UINTN Size;
985 EFI_PHYSICAL_ADDRESS NewBuffer;
986 UINT32 *DataPtr;
987 UINT32 CapsuleNumber;
988 UINT32 Index;
989 EFI_PHYSICAL_ADDRESS BaseAddress;
990 UINT64 Length;
991
992 DataPtr = NULL;
993 CapsuleNumber = 0;
994 PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase;
995 if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) {
996 return EFI_VOLUME_CORRUPTED;
997 }
998 //
999 // Capsule Number and Capsule Offset is in the tail of Capsule data.
1000 //
1001 Size = (UINTN) PrivateData->CapsuleSize;
1002 DataPtr = (UINT32*)((UINTN)CapsuleBase + (UINTN)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA)+ Size);
1003 DataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) & ~(sizeof (UINT32) - 1));
1004 CapsuleNumber = *DataPtr++;
1005 //
1006 // Allocate the memory so that it gets preserved into DXE
1007 //
1008 Status = PeiServicesAllocatePages (
1009 EfiRuntimeServicesData,
1010 EFI_SIZE_TO_PAGES (Size),
1011 &NewBuffer
1012 );
1013
1014 if (Status != EFI_SUCCESS) {
1015 DEBUG ((EFI_D_ERROR, "AllocatePages Failed!\n"));
1016 return Status;
1017 }
1018 //
1019 // Copy to our new buffer for DXE
1020 //
1021 DEBUG ((EFI_D_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN) (PrivateData + 1), (UINTN) NewBuffer, Size));
1022 CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) (PrivateData + 1), Size);
1023 //
1024 // Check for test data pattern. If it is the test pattern, then we'll
1025 // test it ans still create the HOB so that it can be used to verify
1026 // that capsules don't get corrupted all the way into BDS. BDS will
1027 // still try to turn it into a firmware volume, but will think it's
1028 // corrupted so nothing will happen.
1029 //
1030 DEBUG_CODE (
1031 CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer);
1032 );
1033
1034 //
1035 // Build the UEFI Capsule Hob for each capsule image.
1036 //
1037 for (Index = 0; Index < CapsuleNumber; Index ++) {
1038 BaseAddress = NewBuffer + DataPtr[Index];
1039 Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize;
1040
1041 BuildCvHob (BaseAddress, Length);
1042 }
1043
1044 return EFI_SUCCESS;
1045 }
1046
1047 CONST PEI_CAPSULE_PPI mCapsulePpi = {
1048 CapsuleCoalesce,
1049 CheckCapsuleUpdate,
1050 CreateState
1051 };
1052
1053 CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = {
1054 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
1055 &gPeiCapsulePpiGuid,
1056 (PEI_CAPSULE_PPI *) &mCapsulePpi
1057 };
1058
1059 /**
1060 Entry point function for the PEIM
1061
1062 @param FileHandle Handle of the file being invoked.
1063 @param PeiServices Describes the list of possible PEI Services.
1064
1065 @return EFI_SUCCESS If we installed our PPI
1066
1067 **/
1068 EFI_STATUS
1069 EFIAPI
1070 CapsuleMain (
1071 IN EFI_PEI_FILE_HANDLE FileHandle,
1072 IN CONST EFI_PEI_SERVICES **PeiServices
1073 )
1074 {
1075 //
1076 // Just produce our PPI
1077 //
1078 return PeiServicesInstallPpi (&mUefiPpiListCapsule);
1079 }