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