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