summaryrefslogtreecommitdiff
path: root/Core/MdeModulePkg/Universal/CapsulePei
diff options
context:
space:
mode:
authorGuo Mang <mang.guo@intel.com>2017-04-27 11:05:07 +0800
committerGuo Mang <mang.guo@intel.com>2017-04-27 11:05:07 +0800
commitc23f114d3cfbb29b8734b87213d1ec0de404197b (patch)
tree4f3612573be055139a88213559212a40b7862fee /Core/MdeModulePkg/Universal/CapsulePei
parent001e57a103fce87245bfb7ae9c32ffb499a64135 (diff)
downloadedk2-platforms-c23f114d3cfbb29b8734b87213d1ec0de404197b.tar.xz
MdeModulePkg: Move to new location
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang <mang.guo@intel.com>
Diffstat (limited to 'Core/MdeModulePkg/Universal/CapsulePei')
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/Capsule.h129
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf99
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni26
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni21
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf60
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni30
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni21
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c1297
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h121
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c1225
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S81
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm87
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm87
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c311
14 files changed, 3595 insertions, 0 deletions
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/Capsule.h b/Core/MdeModulePkg/Universal/CapsulePei/Capsule.h
new file mode 100644
index 0000000000..3614c21f87
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/Capsule.h
@@ -0,0 +1,129 @@
+/** @file
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _CAPSULE_PEIM_H_
+#define _CAPSULE_PEIM_H_
+
+#include <PiPei.h>
+#include <Uefi/UefiSpec.h>
+
+#include <Ppi/Capsule.h>
+#include <Ppi/LoadFile.h>
+#include <Ppi/ReadOnlyVariable2.h>
+#include <Guid/CapsuleVendor.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/HobLib.h>
+#include <Library/PeiServicesTablePointerLib.h>
+#include <Library/PrintLib.h>
+#include <Library/PeCoffLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/PcdLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/DebugAgentLib.h>
+#include <IndustryStandard/PeImage.h>
+#include "Common/CommonHeader.h"
+
+#ifdef MDE_CPU_IA32
+
+#pragma pack(1)
+
+//
+// Page-Map Level-4 Offset (PML4) and
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
+//
+
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Reserved:1; // Reserved
+ UINT64 MustBeZero:2; // Must Be Zero
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PageTableBaseAddress:40; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // No Execute bit
+ } Bits;
+ UINT64 Uint64;
+} PAGE_MAP_AND_DIRECTORY_POINTER;
+
+//
+// Page Table Entry 2MB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page
+ UINT64 MustBe1:1; // Must be 1
+ UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PAT:1; //
+ UINT64 MustBeZero:8; // Must be zero;
+ UINT64 PageTableBaseAddress:31; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_ENTRY;
+
+//
+// Page Table Entry 1GB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page
+ UINT64 MustBe1:1; // Must be 1
+ UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PAT:1; //
+ UINT64 MustBeZero:17; // Must be zero;
+ UINT64 PageTableBaseAddress:22; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_1G_ENTRY;
+
+#pragma pack()
+
+typedef
+EFI_STATUS
+(*COALESCE_ENTRY) (
+ SWITCH_32_TO_64_CONTEXT *EntrypointContext,
+ SWITCH_64_TO_32_CONTEXT *ReturnContext
+ );
+
+#endif
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf b/Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf
new file mode 100644
index 0000000000..c54bc21a95
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf
@@ -0,0 +1,99 @@
+## @file
+# Capsule update PEIM supports EFI and UEFI.
+#
+# Caution: This module requires additional review when modified.
+# This driver will have external input - capsule image.
+# This external input must be validated carefully to avoid security issue like
+# buffer overflow, integer overflow.
+#
+# Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions
+# of the BSD License which accompanies this distribution. The
+# full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = CapsulePei
+ MODULE_UNI_FILE = CapsulePei.uni
+ FILE_GUID = C779F6D8-7113-4AA1-9648-EB1633C7D53B
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = CapsuleMain
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ UefiCapsule.c
+ Capsule.h
+ Common/CapsuleCoalesce.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ HobLib
+ BaseMemoryLib
+ PeiServicesLib
+ PeimEntryPoint
+ DebugLib
+ PeiServicesTablePointerLib
+ PrintLib
+ ReportStatusCodeLib
+
+[LibraryClasses.IA32]
+ PeCoffGetEntryPointLib
+ PcdLib
+ DebugAgentLib
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData"
+ ## SOMETIMES_CONSUMES ## Variable:L"CapsuleLongModeBuffer"
+ gEfiCapsuleVendorGuid
+
+[Ppis]
+ gEfiPeiReadOnlyVariable2PpiGuid ## CONSUMES
+ gEfiPeiCapsulePpiGuid ## PRODUCES
+
+[Ppis.IA32]
+ gEfiPeiLoadFilePpiGuid ## SOMETIMES_CONSUMES
+
+[Pcd.IA32]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleCoalesceFile ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES
+
+[FeaturePcd.IA32]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES
+
+[Depex]
+ gEfiPeiReadOnlyVariable2PpiGuid
+
+# [BootMode]
+# FLASH_UPDATE ## SOMETIMES_CONSUMES
+
+# [Hob.IA32]
+# UNDEFINED ## SOMETIMES_CONSUMES # CPU
+
+# [Hob]
+# UNDEFINED ## SOMETIMES_PRODUCES # UEFI_CAPSULE
+
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ CapsulePeiExtra.uni
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni b/Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni
new file mode 100644
index 0000000000..227a965d19
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni
@@ -0,0 +1,26 @@
+// /** @file
+// Capsule update PEIM supports EFI and UEFI.
+//
+// Caution: This module requires additional review when modified.
+// This driver will have external input - capsule image.
+// This external input must be validated carefully to avoid security issue like
+// buffer overflow, integer overflow.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution. The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Capsule update PEIM supporting EFI and UEFI"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Capsule update PEIM supporting EFI and UEFI"
+
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni b/Core/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni
new file mode 100644
index 0000000000..d6d3a67d8f
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni
@@ -0,0 +1,21 @@
+// /** @file
+// CapsulePei Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution. The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Firmware Update PEI Module"
+
+
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf b/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf
new file mode 100644
index 0000000000..8318eaa2d6
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf
@@ -0,0 +1,60 @@
+## @file
+# CapsuleX64 module handles >4GB capsule blocks.
+#
+# The X64 entrypoint to process capsule in long mode.
+# This module is built as X64.
+#
+# Caution: This module requires additional review when modified.
+# This driver will have external input - capsule image.
+# This external input must be validated carefully to avoid security issue like
+# buffer overflow, integer overflow.
+#
+# Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions
+# of the BSD License which accompanies this distribution. The
+# full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = CapsuleX64
+ MODULE_UNI_FILE = CapsuleX64.uni
+ FILE_GUID = F7FDE4A6-294C-493c-B50F-9734553BB757
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Sources]
+ X64/X64Entry.c
+ X64/PageFaultHandler.nasm
+ X64/PageFaultHandler.asm
+ X64/PageFaultHandler.S
+ Common/CapsuleCoalesce.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ CpuExceptionHandlerLib
+ DebugAgentLib
+
+[Depex]
+ FALSE
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ CapsuleX64Extra.uni
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni b/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni
new file mode 100644
index 0000000000..731cf14e59
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni
@@ -0,0 +1,30 @@
+// /** @file
+// CapsuleX64 module handles >4GB capsule blocks.
+//
+// The X64 entrypoint to process capsule in long mode.
+// This module is built as X64.
+//
+// Caution: This module requires additional review when modified.
+// This driver will have external input - capsule image.
+// This external input must be validated carefully to avoid security issue like
+// buffer overflow, integer overflow.
+//
+// Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution. The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Handles >4GB capsule blocks"
+
+#string STR_MODULE_DESCRIPTION #language en-US "The X64 entry point to process capsule in long mode. This module is built as X64.<BR><BR>\n"
+ "This driver will have external input - capsule image. This external input must be validated carefully to avoid security issues like buffer overflow or integer overflow.<BR>"
+
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni b/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni
new file mode 100644
index 0000000000..5395ec576c
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni
@@ -0,0 +1,21 @@
+// /** @file
+// CapsuleX64 Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions
+// of the BSD License which accompanies this distribution. The
+// full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Firmware Update PEI Module over 4GB"
+
+
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c b/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c
new file mode 100644
index 0000000000..3e7054cd38
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c
@@ -0,0 +1,1297 @@
+/** @file
+ The logic to process capsule.
+
+ Caution: This module requires additional review when modified.
+ This driver will have external input - capsule image.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+
+ CapsuleDataCoalesce() will do basic validation before coalesce capsule data
+ into memory.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi.h>
+#include <PiPei.h>
+
+#include <Guid/CapsuleVendor.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+#include <Library/BaseLib.h>
+
+#include "CommonHeader.h"
+
+#define MIN_COALESCE_ADDR (1024 * 1024)
+
+/**
+ Given a pointer to the capsule block list, info on the available system
+ memory, and the size of a buffer, find a free block of memory where a
+ buffer of the given size can be copied to safely.
+
+ @param BlockList Pointer to head of capsule block descriptors
+ @param MemBase Pointer to the base of memory in which we want to find free space
+ @param MemSize The size of the block of memory pointed to by MemBase
+ @param DataSize How big a free block we want to find
+
+ @return A pointer to a memory block of at least DataSize that lies somewhere
+ between MemBase and (MemBase + MemSize). The memory pointed to does not
+ contain any of the capsule block descriptors or capsule blocks pointed to
+ by the BlockList.
+
+**/
+UINT8 *
+FindFreeMem (
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ UINT8 *MemBase,
+ UINTN MemSize,
+ UINTN DataSize
+ );
+
+/**
+ The capsule block descriptors may be fragmented and spread all over memory.
+ To simplify the coalescing of capsule blocks, first coalesce all the
+ capsule block descriptors low in memory.
+
+ The descriptors passed in can be fragmented throughout memory. Here
+ they are relocated into memory to turn them into a contiguous (null
+ terminated) array.
+
+ @param PeiServices pointer to PEI services table
+ @param BlockList pointer to the capsule block descriptors
+ @param NumDescriptors number of capsule data block descriptors, whose Length is non-zero.
+ @param MemBase base of system memory in which we can work
+ @param MemSize size of the system memory pointed to by MemBase
+
+ @retval NULL could not relocate the descriptors
+ @retval Pointer to the base of the successfully-relocated block descriptors.
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+RelocateBlockDescriptors (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ IN UINTN NumDescriptors,
+ IN UINT8 *MemBase,
+ IN UINTN MemSize
+ );
+
+/**
+ Check every capsule header.
+
+ @param CapsuleHeader The pointer to EFI_CAPSULE_HEADER
+
+ @retval FALSE Capsule is OK
+ @retval TRUE Capsule is corrupted
+
+**/
+BOOLEAN
+IsCapsuleCorrupted (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ );
+
+/**
+ Determine if two buffers overlap in memory.
+
+ @param Buff1 pointer to first buffer
+ @param Size1 size of Buff1
+ @param Buff2 pointer to second buffer
+ @param Size2 size of Buff2
+
+ @retval TRUE Buffers overlap in memory.
+ @retval FALSE Buffer doesn't overlap.
+
+**/
+BOOLEAN
+IsOverlapped (
+ UINT8 *Buff1,
+ UINTN Size1,
+ UINT8 *Buff2,
+ UINTN Size2
+ );
+
+/**
+ Given a pointer to a capsule block descriptor, traverse the list to figure
+ out how many legitimate descriptors there are, and how big the capsule it
+ refers to is.
+
+ @param Desc Pointer to the capsule block descriptors
+ @param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero.
+ @param CapsuleSize Optional pointer to where to return the capsule image size
+ @param CapsuleNumber Optional pointer to where to return the number of capsule
+
+ @retval EFI_NOT_FOUND No descriptors containing data in the list
+ @retval EFI_SUCCESS Return data is valid
+
+**/
+EFI_STATUS
+GetCapsuleInfo (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc,
+ IN OUT UINTN *NumDescriptors OPTIONAL,
+ IN OUT UINTN *CapsuleSize OPTIONAL,
+ IN OUT UINTN *CapsuleNumber OPTIONAL
+ );
+
+/**
+ Given a pointer to the capsule block list, info on the available system
+ memory, and the size of a buffer, find a free block of memory where a
+ buffer of the given size can be copied to safely.
+
+ @param BlockList Pointer to head of capsule block descriptors
+ @param MemBase Pointer to the base of memory in which we want to find free space
+ @param MemSize The size of the block of memory pointed to by MemBase
+ @param DataSize How big a free block we want to find
+
+ @return A pointer to a memory block of at least DataSize that lies somewhere
+ between MemBase and (MemBase + MemSize). The memory pointed to does not
+ contain any of the capsule block descriptors or capsule blocks pointed to
+ by the BlockList.
+
+**/
+UINT8 *
+FindFreeMem (
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ UINT8 *MemBase,
+ UINTN MemSize,
+ UINTN DataSize
+ )
+{
+ UINTN Size;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempDesc;
+ UINT8 *MemEnd;
+ BOOLEAN Failed;
+
+ //
+ // Need at least enough to copy the data to at the end of the buffer, so
+ // say the end is less the data size for easy comparisons here.
+ //
+ MemEnd = MemBase + MemSize - DataSize;
+ CurrDesc = BlockList;
+ //
+ // Go through all the descriptor blocks and see if any obstruct the range
+ //
+ while (CurrDesc != NULL) {
+ //
+ // Get the size of this block list and see if it's in the way
+ //
+ Failed = FALSE;
+ TempDesc = CurrDesc;
+ Size = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ while (TempDesc->Length != 0) {
+ Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ TempDesc++;
+ }
+
+ if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) {
+ //
+ // Set our new base to the end of this block list and start all over
+ //
+ MemBase = (UINT8 *) CurrDesc + Size;
+ CurrDesc = BlockList;
+ if (MemBase > MemEnd) {
+ return NULL;
+ }
+
+ Failed = TRUE;
+ }
+ //
+ // Now go through all the blocks and make sure none are in the way
+ //
+ while ((CurrDesc->Length != 0) && (!Failed)) {
+ if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) {
+ //
+ // Set our new base to the end of this block and start all over
+ //
+ Failed = TRUE;
+ MemBase = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length;
+ CurrDesc = BlockList;
+ if (MemBase > MemEnd) {
+ return NULL;
+ }
+ }
+ CurrDesc++;
+ }
+ //
+ // Normal continuation -- jump to next block descriptor list
+ //
+ if (!Failed) {
+ CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer;
+ }
+ }
+ return MemBase;
+}
+
+/**
+ Validate capsule by MemoryResource.
+
+ @param MemoryResource Pointer to the buffer of memory resource descriptor.
+ @param Address Address to be validated.
+ @param Size Size to be validated.
+
+ @retval TRUE No memory resource descriptor reported in HOB list before capsule Coalesce,
+ or it is valid in one MemoryResource.
+ FALSE It is not in any MemoryResource.
+
+**/
+BOOLEAN
+ValidateCapsuleByMemoryResource (
+ IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN UINT64 Size
+ )
+{
+ UINTN Index;
+
+ //
+ // Sanity Check
+ //
+ if (Size > MAX_ADDRESS) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Size(0x%lx) > MAX_ADDRESS\n", Size));
+ return FALSE;
+ }
+
+ //
+ // Sanity Check
+ //
+ if (Address > (MAX_ADDRESS - Size)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Address(0x%lx) > (MAX_ADDRESS - Size(0x%lx))\n", Address, Size));
+ return FALSE;
+ }
+
+ if (MemoryResource == NULL) {
+ //
+ // No memory resource descriptor reported in HOB list before capsule Coalesce.
+ //
+ return TRUE;
+ }
+
+ for (Index = 0; MemoryResource[Index].ResourceLength != 0; Index++) {
+ if ((Address >= MemoryResource[Index].PhysicalStart) &&
+ ((Address + Size) <= (MemoryResource[Index].PhysicalStart + MemoryResource[Index].ResourceLength))) {
+ DEBUG ((EFI_D_INFO, "Address(0x%lx) Size(0x%lx) in MemoryResource[0x%x] - Start(0x%lx) Length(0x%lx)\n",
+ Address, Size,
+ Index, MemoryResource[Index].PhysicalStart, MemoryResource[Index].ResourceLength));
+ return TRUE;
+ }
+ }
+
+ DEBUG ((EFI_D_ERROR, "ERROR: Address(0x%lx) Size(0x%lx) not in any MemoryResource\n", Address, Size));
+ return FALSE;
+}
+
+/**
+ Check the integrity of the capsule descriptors.
+
+ @param BlockList Pointer to the capsule descriptors
+ @param MemoryResource Pointer to the buffer of memory resource descriptor.
+
+ @retval NULL BlockList is not valid.
+ @retval LastBlockDesc Last one Block in BlockList
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+ValidateCapsuleIntegrity (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource
+ )
+{
+ EFI_CAPSULE_HEADER *CapsuleHeader;
+ UINT64 CapsuleSize;
+ UINTN CapsuleCount;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr;
+
+ DEBUG ((EFI_D_INFO, "ValidateCapsuleIntegrity\n"));
+
+ //
+ // Go through the list to look for inconsistencies. Check for:
+ // * misaligned block descriptors.
+ // * The first capsule header guid
+ // * The first capsule header flag
+ // * The first capsule header HeaderSize
+ // * Below check will be done in ValidateCapsuleByMemoryResource()
+ // Length > MAX_ADDRESS
+ // Ptr + sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR) > MAX_ADDRESS
+ // DataBlock + Length > MAX_ADDRESS
+ //
+ CapsuleSize = 0;
+ CapsuleCount = 0;
+ Ptr = BlockList;
+
+ if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
+ return NULL;
+ }
+
+ DEBUG ((EFI_D_INFO, "Ptr - 0x%x\n", Ptr));
+ DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
+ DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
+ while ((Ptr->Length != 0) || (Ptr->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ //
+ // Make sure the descriptor is aligned at UINT64 in memory
+ //
+ if ((UINTN) Ptr & (sizeof(UINT64) - 1)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: BlockList address failed alignment check\n"));
+ return NULL;
+ }
+
+ if (Ptr->Length == 0) {
+ //
+ // Descriptor points to another list of block descriptors somewhere
+ // else.
+ //
+ Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer;
+ if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
+ return NULL;
+ }
+ DEBUG ((EFI_D_INFO, "Ptr(C) - 0x%x\n", Ptr));
+ DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
+ DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
+ } else {
+ if (!ValidateCapsuleByMemoryResource (MemoryResource, Ptr->Union.DataBlock, Ptr->Length)) {
+ return NULL;
+ }
+
+ //
+ //To enhance the reliability of check-up, the first capsule's header is checked here.
+ //More reliabilities check-up will do later.
+ //
+ if (CapsuleSize == 0) {
+ //
+ //Move to the first capsule to check its header.
+ //
+ CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Ptr->Union.DataBlock);
+ //
+ // Sanity check
+ //
+ if (Ptr->Length < sizeof(EFI_CAPSULE_HEADER)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Length(0x%lx) < sizeof(EFI_CAPSULE_HEADER)\n", Ptr->Length));
+ return NULL;
+ }
+ //
+ // Make sure HeaderSize field is valid
+ //
+ if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleHeader->HeaderSize(0x%x) > CapsuleHeader->CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));
+ return NULL;
+ }
+ if (IsCapsuleCorrupted (CapsuleHeader)) {
+ return NULL;
+ }
+ CapsuleCount ++;
+ CapsuleSize = CapsuleHeader->CapsuleImageSize;
+ }
+
+ if (CapsuleSize >= Ptr->Length) {
+ CapsuleSize = CapsuleSize - Ptr->Length;
+ } else {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize(0x%lx) < Ptr->Length(0x%lx)\n", CapsuleSize, Ptr->Length));
+ //
+ // Sanity check
+ //
+ return NULL;
+ }
+
+ //
+ // Move to next BLOCK descriptor
+ //
+ Ptr++;
+ if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
+ return NULL;
+ }
+ DEBUG ((EFI_D_INFO, "Ptr(B) - 0x%x\n", Ptr));
+ DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
+ DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
+ }
+ }
+
+ if (CapsuleCount == 0) {
+ //
+ // No any capsule is found in BlockList
+ //
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleCount(0x%x) == 0\n", CapsuleCount));
+ return NULL;
+ }
+
+ if (CapsuleSize != 0) {
+ //
+ // Capsule data is incomplete.
+ //
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize(0x%lx) != 0\n", CapsuleSize));
+ return NULL;
+ }
+
+ return Ptr;
+}
+
+/**
+ The capsule block descriptors may be fragmented and spread all over memory.
+ To simplify the coalescing of capsule blocks, first coalesce all the
+ capsule block descriptors low in memory.
+
+ The descriptors passed in can be fragmented throughout memory. Here
+ they are relocated into memory to turn them into a contiguous (null
+ terminated) array.
+
+ @param PeiServices pointer to PEI services table
+ @param BlockList pointer to the capsule block descriptors
+ @param NumDescriptors number of capsule data block descriptors, whose Length is non-zero.
+ @param MemBase base of system memory in which we can work
+ @param MemSize size of the system memory pointed to by MemBase
+
+ @retval NULL could not relocate the descriptors
+ @retval Pointer to the base of the successfully-relocated block descriptors.
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+RelocateBlockDescriptors (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ IN UINTN NumDescriptors,
+ IN UINT8 *MemBase,
+ IN UINTN MemSize
+ )
+{
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *NewBlockList;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrBlockDescHead;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *PrevBlockDescTail;
+ UINTN BufferSize;
+ UINT8 *RelocBuffer;
+ UINTN BlockListSize;
+
+ //
+ // Get the info on the blocks and descriptors. Since we're going to move
+ // the descriptors low in memory, adjust the base/size values accordingly here.
+ // NumDescriptors is the number of legit data descriptors, so add one for
+ // a terminator. (Already done by caller, no check is needed.)
+ //
+
+ BufferSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ NewBlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) MemBase;
+ if (MemSize < BufferSize) {
+ return NULL;
+ }
+
+ MemSize -= BufferSize;
+ MemBase += BufferSize;
+ //
+ // Go through all the blocks and make sure none are in the way
+ //
+ TempBlockDesc = BlockList;
+ while (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
+ if (TempBlockDesc->Length == 0) {
+ //
+ // Next block of descriptors
+ //
+ TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
+ } else {
+ //
+ // If the capsule data pointed to by this descriptor is in the way,
+ // move it.
+ //
+ if (IsOverlapped (
+ (UINT8 *) NewBlockList,
+ BufferSize,
+ (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,
+ (UINTN) TempBlockDesc->Length
+ )) {
+ //
+ // Relocate the block
+ //
+ RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, (UINTN) TempBlockDesc->Length);
+ if (RelocBuffer == NULL) {
+ return NULL;
+ }
+
+ CopyMem ((VOID *) RelocBuffer, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);
+ DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size 0x%lX 0x%lX 0x%lX\n", TempBlockDesc->Union.DataBlock, (UINT64)(UINTN)RelocBuffer, TempBlockDesc->Length));
+ TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
+ }
+ TempBlockDesc++;
+ }
+ }
+ //
+ // Now go through all the block descriptors to make sure that they're not
+ // in the memory region we want to copy them to.
+ //
+ CurrBlockDescHead = BlockList;
+ PrevBlockDescTail = NULL;
+ while ((CurrBlockDescHead != NULL) && (CurrBlockDescHead->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ //
+ // Get the size of this list then see if it overlaps our low region
+ //
+ TempBlockDesc = CurrBlockDescHead;
+ BlockListSize = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ while (TempBlockDesc->Length != 0) {
+ BlockListSize += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ TempBlockDesc++;
+ }
+
+ if (IsOverlapped (
+ (UINT8 *) NewBlockList,
+ BufferSize,
+ (UINT8 *) CurrBlockDescHead,
+ BlockListSize
+ )) {
+ //
+ // Overlaps, so move it out of the way
+ //
+ RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, BlockListSize);
+ if (RelocBuffer == NULL) {
+ return NULL;
+ }
+ CopyMem ((VOID *) RelocBuffer, (VOID *) CurrBlockDescHead, BlockListSize);
+ DEBUG ((EFI_D_INFO, "Capsule reloc descriptor block #2\n"));
+ //
+ // Point the previous block's next point to this copied version. If
+ // the tail pointer is null, then this is the first descriptor block.
+ //
+ if (PrevBlockDescTail == NULL) {
+ BlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) RelocBuffer;
+ } else {
+ PrevBlockDescTail->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
+ }
+ }
+ //
+ // Save our new tail and jump to the next block list
+ //
+ PrevBlockDescTail = TempBlockDesc;
+ CurrBlockDescHead = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
+ }
+ //
+ // Cleared out low memory. Now copy the descriptors down there.
+ //
+ TempBlockDesc = BlockList;
+ CurrBlockDescHead = NewBlockList;
+ while ((TempBlockDesc != NULL) && (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ if (TempBlockDesc->Length != 0) {
+ CurrBlockDescHead->Union.DataBlock = TempBlockDesc->Union.DataBlock;
+ CurrBlockDescHead->Length = TempBlockDesc->Length;
+ CurrBlockDescHead++;
+ TempBlockDesc++;
+ } else {
+ TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
+ }
+ }
+ //
+ // Null terminate
+ //
+ CurrBlockDescHead->Union.ContinuationPointer = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;
+ CurrBlockDescHead->Length = 0;
+ return NewBlockList;
+}
+
+/**
+ Determine if two buffers overlap in memory.
+
+ @param Buff1 pointer to first buffer
+ @param Size1 size of Buff1
+ @param Buff2 pointer to second buffer
+ @param Size2 size of Buff2
+
+ @retval TRUE Buffers overlap in memory.
+ @retval FALSE Buffer doesn't overlap.
+
+**/
+BOOLEAN
+IsOverlapped (
+ UINT8 *Buff1,
+ UINTN Size1,
+ UINT8 *Buff2,
+ UINTN Size2
+ )
+{
+ //
+ // If buff1's end is less than the start of buff2, then it's ok.
+ // Also, if buff1's start is beyond buff2's end, then it's ok.
+ //
+ if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Given a pointer to a capsule block descriptor, traverse the list to figure
+ out how many legitimate descriptors there are, and how big the capsule it
+ refers to is.
+
+ @param Desc Pointer to the capsule block descriptors
+ @param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero.
+ @param CapsuleSize Optional pointer to where to return the capsule image size
+ @param CapsuleNumber Optional pointer to where to return the number of capsule
+
+ @retval EFI_NOT_FOUND No descriptors containing data in the list
+ @retval EFI_SUCCESS Return data is valid
+
+**/
+EFI_STATUS
+GetCapsuleInfo (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc,
+ IN OUT UINTN *NumDescriptors OPTIONAL,
+ IN OUT UINTN *CapsuleSize OPTIONAL,
+ IN OUT UINTN *CapsuleNumber OPTIONAL
+ )
+{
+ UINTN Count;
+ UINTN Size;
+ UINTN Number;
+ UINTN ThisCapsuleImageSize;
+ EFI_CAPSULE_HEADER *CapsuleHeader;
+
+ DEBUG ((EFI_D_INFO, "GetCapsuleInfo enter\n"));
+
+ ASSERT (Desc != NULL);
+
+ Count = 0;
+ Size = 0;
+ Number = 0;
+ ThisCapsuleImageSize = 0;
+
+ while (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
+ if (Desc->Length == 0) {
+ //
+ // Descriptor points to another list of block descriptors somewhere
+ //
+ Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
+ } else {
+ //
+ // Sanity Check
+ // It is needed, because ValidateCapsuleIntegrity() only validate one individual capsule Size.
+ // While here we need check all capsules size.
+ //
+ if (Desc->Length >= (MAX_ADDRESS - Size)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Desc->Length(0x%lx) >= (MAX_ADDRESS - Size(0x%x))\n", Desc->Length, Size));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Size += (UINTN) Desc->Length;
+ Count++;
+
+ //
+ // See if this is first capsule's header
+ //
+ if (ThisCapsuleImageSize == 0) {
+ CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Desc->Union.DataBlock);
+ //
+ // This has been checked in ValidateCapsuleIntegrity()
+ //
+ Number ++;
+ ThisCapsuleImageSize = CapsuleHeader->CapsuleImageSize;
+ }
+
+ //
+ // This has been checked in ValidateCapsuleIntegrity()
+ //
+ ASSERT (ThisCapsuleImageSize >= Desc->Length);
+ ThisCapsuleImageSize = (UINTN)(ThisCapsuleImageSize - Desc->Length);
+
+ //
+ // Move to next
+ //
+ Desc++;
+ }
+ }
+ //
+ // If no descriptors, then fail
+ //
+ if (Count == 0) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Count == 0\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // checked in ValidateCapsuleIntegrity()
+ //
+ ASSERT (ThisCapsuleImageSize == 0);
+
+ if (NumDescriptors != NULL) {
+ *NumDescriptors = Count;
+ }
+
+ if (CapsuleSize != NULL) {
+ *CapsuleSize = Size;
+ }
+
+ if (CapsuleNumber != NULL) {
+ *CapsuleNumber = Number;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check every capsule header.
+
+ @param CapsuleHeader The pointer to EFI_CAPSULE_HEADER
+
+ @retval FALSE Capsule is OK
+ @retval TRUE Capsule is corrupted
+
+**/
+BOOLEAN
+IsCapsuleCorrupted (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ )
+{
+ //
+ //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET.
+ //
+ if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {
+ return TRUE;
+ }
+ //
+ //Make sure the flags combination is supported by the platform.
+ //
+ if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
+ return TRUE;
+ }
+ if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Try to verify the integrity of a capsule test pattern before the
+ capsule gets coalesced. This can be useful in narrowing down
+ where capsule data corruption occurs.
+
+ The test pattern mode fills in memory with a counting UINT32 value.
+ If the capsule is not divided up in a multiple of 4-byte blocks, then
+ things get messy doing the check. Therefore there are some cases
+ here where we just give up and skip the pre-coalesce check.
+
+ @param PeiServices PEI services table
+ @param Desc Pointer to capsule descriptors
+**/
+VOID
+CapsuleTestPatternPreCoalesce (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc
+ )
+{
+ UINT32 *TestPtr;
+ UINT32 TestCounter;
+ UINT32 TestSize;
+
+ DEBUG ((EFI_D_INFO, "CapsuleTestPatternPreCoalesce\n"));
+
+ //
+ // Find first data descriptor
+ //
+ while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
+ }
+
+ if (Desc->Union.ContinuationPointer == 0) {
+ return ;
+ }
+ //
+ // First one better be long enough to at least hold the test signature
+ //
+ if (Desc->Length < sizeof (UINT32)) {
+ DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #1\n"));
+ return ;
+ }
+
+ TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;
+ //
+ // 0x54534554 "TEST"
+ //
+ if (*TestPtr != 0x54534554) {
+ return ;
+ }
+
+ TestCounter = 0;
+ TestSize = (UINT32) Desc->Length - 2 * sizeof (UINT32);
+ //
+ // Skip over the signature and the size fields in the pattern data header
+ //
+ TestPtr += 2;
+ while (1) {
+ if ((TestSize & 0x03) != 0) {
+ DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #2\n"));
+ return ;
+ }
+
+ while (TestSize > 0) {
+ if (*TestPtr != TestCounter) {
+ DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n"));
+ return ;
+ }
+
+ TestSize -= sizeof (UINT32);
+ TestCounter++;
+ TestPtr++;
+ }
+ Desc++;
+ while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
+ }
+
+ if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
+ return ;
+ }
+ TestSize = (UINT32) Desc->Length;
+ TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;
+ }
+}
+
+/**
+ Checks for the presence of capsule descriptors.
+ Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
+
+ @param BlockListBuffer Pointer to the buffer of capsule descriptors variables
+ @param MemoryResource Pointer to the buffer of memory resource descriptor.
+ @param BlockDescriptorList Pointer to the capsule descriptors list
+
+ @retval EFI_SUCCESS a valid capsule is present
+ @retval EFI_NOT_FOUND if a valid capsule is not present
+**/
+EFI_STATUS
+BuildCapsuleDescriptors (
+ IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
+ IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
+ OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptorList
+ )
+{
+ UINTN Index;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock;
+
+ DEBUG ((EFI_D_INFO, "BuildCapsuleDescriptors enter\n"));
+
+ LastBlock = NULL;
+ HeadBlock = NULL;
+ TempBlock = NULL;
+ Index = 0;
+
+ while (BlockListBuffer[Index] != 0) {
+ //
+ // Test integrity of descriptors.
+ //
+ if (BlockListBuffer[Index] < MAX_ADDRESS) {
+ TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index], MemoryResource);
+ if (TempBlock != NULL) {
+ if (LastBlock == NULL) {
+ LastBlock = TempBlock;
+
+ //
+ // Return the base of the block descriptors
+ //
+ HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index];
+ } else {
+ //
+ // Combine the different BlockList into single BlockList.
+ //
+ LastBlock->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)BlockListBuffer[Index];
+ LastBlock->Length = 0;
+ LastBlock = TempBlock;
+ }
+ }
+ } else {
+ DEBUG ((EFI_D_ERROR, "ERROR: BlockListBuffer[Index](0x%lx) < MAX_ADDRESS\n", BlockListBuffer[Index]));
+ }
+ Index ++;
+ }
+
+ if (HeadBlock != NULL) {
+ *BlockDescriptorList = HeadBlock;
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ The function to coalesce a fragmented capsule in memory.
+
+ Memory Map for coalesced capsule:
+ MemBase + ---->+---------------------------+<-----------+
+ MemSize | ------------------------- | |
+ | | Capsule [Num-1] | | |
+ | ------------------------- | |
+ | | ................ | | |
+ | ------------------------- | |
+ | | Capsule [1] | | |
+ | ------------------------- | |
+ | | Capsule [0] | | |
+ | ------------------------- | |
+ | Capsule Image | |
+CapsuleImageBase-->+---------------------------+
+ | ------------------------- | |
+ | | CapsuleOffset[Num-1] | | |
+ | ------------------------- | |
+ | | ................ | | CapsuleSize
+ | ------------------------- | |
+ | | CapsuleOffset[1] | | |
+ | ------------------------- | |
+ | | CapsuleOffset[0] | | |
+ |---------------------------| |
+ | | CapsuleNumber | | |
+ | ------------------------- | |
+ | | CapsuleAllImageSize | | |
+ | ------------------------- | |
+ | PrivateData | |
+ DestPtr ---->+---------------------------+<-----------+
+ | | |
+ | FreeMem | FreeMemSize
+ | | |
+ FreeMemBase --->+---------------------------+<-----------+
+ | Terminator |
+ +---------------------------+
+ | BlockDescriptor n |
+ +---------------------------+
+ | ................. |
+ +---------------------------+
+ | BlockDescriptor 1 |
+ +---------------------------+
+ | BlockDescriptor 0 |
+ +---------------------------+
+ | PrivateDataDesc 0 |
+ MemBase ---->+---------------------------+<----- BlockList
+
+ Caution: This function may receive untrusted input.
+ The capsule data is external input, so this routine will do basic validation before
+ coalesce capsule data into memory.
+
+ @param PeiServices General purpose services available to every PEIM.
+ @param BlockListBuffer Pointer to the buffer of Capsule Descriptor Variables.
+ @param MemoryResource Pointer to the buffer of memory resource descriptor.
+ @param MemoryBase Pointer to the base of a block of memory that we can walk
+ all over while trying to coalesce our buffers.
+ On output, this variable will hold the base address of
+ a coalesced capsule.
+ @param MemorySize Size of the memory region pointed to by MemoryBase.
+ On output, this variable will contain the size of the
+ coalesced capsule.
+
+ @retval EFI_NOT_FOUND If we could not find the capsule descriptors.
+
+ @retval EFI_BUFFER_TOO_SMALL
+ If we could not coalesce the capsule in the memory
+ region provided to us.
+
+ @retval EFI_SUCCESS Processed the capsule successfully.
+**/
+EFI_STATUS
+EFIAPI
+CapsuleDataCoalesce (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
+ IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
+ IN OUT VOID **MemoryBase,
+ IN OUT UINTN *MemorySize
+ )
+{
+ VOID *NewCapsuleBase;
+ VOID *CapsuleImageBase;
+ UINTN CapsuleIndex;
+ UINT8 *FreeMemBase;
+ UINT8 *DestPtr;
+ UINTN DestLength;
+ UINT8 *RelocPtr;
+ UINTN CapsuleTimes;
+ UINT64 SizeLeft;
+ UINT64 CapsuleImageSize;
+ UINTN CapsuleSize;
+ UINTN CapsuleNumber;
+ UINTN DescriptorsSize;
+ UINTN FreeMemSize;
+ UINTN NumDescriptors;
+ BOOLEAN CapsuleBeginFlag;
+ EFI_STATUS Status;
+ EFI_CAPSULE_HEADER *CapsuleHeader;
+ EFI_CAPSULE_PEIM_PRIVATE_DATA PrivateData;
+ EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateDataPtr;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrentBlockDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR PrivateDataDesc[2];
+
+ DEBUG ((EFI_D_INFO, "CapsuleDataCoalesce enter\n"));
+
+ CapsuleIndex = 0;
+ SizeLeft = 0;
+ CapsuleTimes = 0;
+ CapsuleImageSize = 0;
+ PrivateDataPtr = NULL;
+ CapsuleHeader = NULL;
+ CapsuleBeginFlag = TRUE;
+ CapsuleSize = 0;
+ NumDescriptors = 0;
+
+ //
+ // Build capsule descriptors list
+ //
+ Status = BuildCapsuleDescriptors (BlockListBuffer, MemoryResource, &BlockList);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG_CODE (
+ CapsuleTestPatternPreCoalesce (PeiServices, BlockList);
+ );
+
+ //
+ // Get the size of our descriptors and the capsule size. GetCapsuleInfo()
+ // returns the number of descriptors that actually point to data, so add
+ // one for a terminator. Do that below.
+ //
+ Status = GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize, &CapsuleNumber);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ DEBUG ((EFI_D_INFO, "CapsuleSize - 0x%x\n", CapsuleSize));
+ DEBUG ((EFI_D_INFO, "CapsuleNumber - 0x%x\n", CapsuleNumber));
+ DEBUG ((EFI_D_INFO, "NumDescriptors - 0x%x\n", NumDescriptors));
+ if ((CapsuleSize == 0) || (NumDescriptors == 0) || (CapsuleNumber == 0)) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (CapsuleNumber - 1 >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(UINT64))) / sizeof(UINT64)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleNumber - 0x%x\n", CapsuleNumber));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Initialize our local copy of private data. When we're done, we'll create a
+ // descriptor for it as well so that it can be put into free memory without
+ // trashing anything.
+ //
+ PrivateData.Signature = EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE;
+ PrivateData.CapsuleAllImageSize = (UINT64) CapsuleSize;
+ PrivateData.CapsuleNumber = (UINT64) CapsuleNumber;
+ PrivateData.CapsuleOffset[0] = 0;
+ //
+ // NOTE: Only data in sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) is valid, CapsuleOffset field is uninitialized at this moment.
+ // The code sets partial length here for Descriptor.Length check, but later it will use full length to reserve those PrivateData region.
+ //
+ PrivateDataDesc[0].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) &PrivateData;
+ PrivateDataDesc[0].Length = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA);
+ PrivateDataDesc[1].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) BlockList;
+ PrivateDataDesc[1].Length = 0;
+ //
+ // Add PrivateDataDesc[0] in beginning, as it is new descriptor. PrivateDataDesc[1] is NOT needed.
+ // In addition, one NULL terminator is added in the end. See RelocateBlockDescriptors().
+ //
+ NumDescriptors += 2;
+ //
+ // Sanity check
+ //
+ if (CapsuleSize >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64)))) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize - 0x%x\n", CapsuleSize));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ //
+ // Need add sizeof(UINT64) for PrivateData alignment
+ //
+ CapsuleSize += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64);
+ BlockList = PrivateDataDesc;
+ //
+ // Sanity check
+ //
+ if (NumDescriptors >= (MAX_ADDRESS / sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
+ DEBUG ((EFI_D_ERROR, "ERROR: NumDescriptors - 0x%x\n", NumDescriptors));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ //
+ // Sanity check
+ //
+ if (DescriptorsSize >= (MAX_ADDRESS - CapsuleSize)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: DescriptorsSize - 0x%lx, CapsuleSize - 0x%lx\n", (UINT64)DescriptorsSize, (UINT64)CapsuleSize));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Don't go below some min address. If the base is below it,
+ // then move it up and adjust the size accordingly.
+ //
+ DEBUG ((EFI_D_INFO, "Capsule Memory range from 0x%8X to 0x%8X\n", (UINTN) *MemoryBase, (UINTN)*MemoryBase + *MemorySize));
+ if ((UINTN)*MemoryBase < (UINTN) MIN_COALESCE_ADDR) {
+ if (((UINTN)*MemoryBase + *MemorySize) < (UINTN) MIN_COALESCE_ADDR) {
+ DEBUG ((EFI_D_ERROR, "ERROR: *MemoryBase + *MemorySize - 0x%x\n", (UINTN)*MemoryBase + *MemorySize));
+ return EFI_BUFFER_TOO_SMALL;
+ } else {
+ *MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase);
+ *MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR;
+ }
+ }
+
+ if (*MemorySize <= (CapsuleSize + DescriptorsSize)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize + DescriptorsSize - 0x%x\n", CapsuleSize + DescriptorsSize));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ FreeMemBase = *MemoryBase;
+ FreeMemSize = *MemorySize;
+ DEBUG ((EFI_D_INFO, "Capsule Free Memory from 0x%8X to 0x%8X\n", (UINTN) FreeMemBase, (UINTN) FreeMemBase + FreeMemSize));
+
+ //
+ // Relocate all the block descriptors to low memory to make further
+ // processing easier.
+ //
+ BlockList = RelocateBlockDescriptors (PeiServices, BlockList, NumDescriptors, FreeMemBase, FreeMemSize);
+ if (BlockList == NULL) {
+ //
+ // Not enough room to relocate the descriptors
+ //
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Take the top of memory for the capsule. UINT64 align up.
+ //
+ DestPtr = FreeMemBase + FreeMemSize - CapsuleSize;
+ DestPtr = (UINT8 *) (((UINTN)DestPtr + sizeof (UINT64) - 1) & ~(sizeof (UINT64) - 1));
+ FreeMemBase = (UINT8 *) BlockList + DescriptorsSize;
+ FreeMemSize = (UINTN) DestPtr - (UINTN) FreeMemBase;
+ NewCapsuleBase = (VOID *) DestPtr;
+ CapsuleImageBase = (UINT8 *)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
+
+ PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;
+
+ //
+ // Move all the blocks to the top (high) of memory.
+ // Relocate all the obstructing blocks. Note that the block descriptors
+ // were coalesced when they were relocated, so we can just ++ the pointer.
+ //
+ CurrentBlockDesc = BlockList;
+ while ((CurrentBlockDesc->Length != 0) || (CurrentBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ if (CapsuleTimes == 0) {
+ //
+ // The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
+ // CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use.
+ //
+ ASSERT (CurrentBlockDesc->Union.DataBlock == (UINT64)(UINTN)&PrivateData);
+ DestLength = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
+ } else {
+ DestLength = (UINTN)CurrentBlockDesc->Length;
+ }
+ //
+ // See if any of the remaining capsule blocks are in the way
+ //
+ TempBlockDesc = CurrentBlockDesc;
+ while (TempBlockDesc->Length != 0) {
+ //
+ // Is this block in the way of where we want to copy the current descriptor to?
+ //
+ if (IsOverlapped (
+ (UINT8 *) DestPtr,
+ (UINTN) DestLength,
+ (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,
+ (UINTN) TempBlockDesc->Length
+ )) {
+ //
+ // Relocate the block
+ //
+ RelocPtr = FindFreeMem (BlockList, FreeMemBase, FreeMemSize, (UINTN) TempBlockDesc->Length);
+ if (RelocPtr == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem ((VOID *) RelocPtr, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);
+ DEBUG ((EFI_D_INFO, "Capsule reloc data block from 0x%8X to 0x%8X with size 0x%8X\n",
+ (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) RelocPtr, (UINTN) TempBlockDesc->Length));
+
+ TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocPtr;
+ }
+ //
+ // Next descriptor
+ //
+ TempBlockDesc++;
+ }
+ //
+ // Ok, we made it through. Copy the block.
+ // we just support greping one capsule from the lists of block descs list.
+ //
+ CapsuleTimes ++;
+ //
+ //Skip the first block descriptor that filled with EFI_CAPSULE_PEIM_PRIVATE_DATA
+ //
+ if (CapsuleTimes > 1) {
+ //
+ //For every capsule entry point, check its header to determine whether to relocate it.
+ //If it is invalid, skip it and move on to the next capsule. If it is valid, relocate it.
+ //
+ if (CapsuleBeginFlag) {
+ CapsuleBeginFlag = FALSE;
+ CapsuleHeader = (EFI_CAPSULE_HEADER*)(UINTN)CurrentBlockDesc->Union.DataBlock;
+ SizeLeft = CapsuleHeader->CapsuleImageSize;
+
+ //
+ // No more check here is needed, because IsCapsuleCorrupted() already in ValidateCapsuleIntegrity()
+ //
+ ASSERT (CapsuleIndex < CapsuleNumber);
+
+ //
+ // Relocate this capsule
+ //
+ CapsuleImageSize += SizeLeft;
+ //
+ // Cache the begin offset of this capsule
+ //
+ ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE);
+ ASSERT ((UINTN)DestPtr >= (UINTN)CapsuleImageBase);
+ PrivateDataPtr->CapsuleOffset[CapsuleIndex++] = (UINTN)DestPtr - (UINTN)CapsuleImageBase;
+ }
+
+ //
+ // Below ASSERT is checked in ValidateCapsuleIntegrity()
+ //
+ ASSERT (CurrentBlockDesc->Length <= SizeLeft);
+
+ CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);
+ DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%lX from 0x%lX to 0x%lX with size 0x%lX\n",(UINT64)CapsuleTimes,
+ CurrentBlockDesc->Union.DataBlock, (UINT64)(UINTN)DestPtr, CurrentBlockDesc->Length));
+ DestPtr += CurrentBlockDesc->Length;
+ SizeLeft -= CurrentBlockDesc->Length;
+
+ if (SizeLeft == 0) {
+ //
+ //Here is the end of the current capsule image.
+ //
+ CapsuleBeginFlag = TRUE;
+ }
+ } else {
+ //
+ // The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
+ // CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use.
+ //
+ ASSERT (CurrentBlockDesc->Length == sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA));
+ ASSERT ((UINTN)DestPtr == (UINTN)NewCapsuleBase);
+ CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);
+ DestPtr += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
+ }
+ //
+ //Walk through the block descriptor list.
+ //
+ CurrentBlockDesc++;
+ }
+ //
+ // We return the base of memory we want reserved, and the size.
+ // The memory peim should handle it appropriately from there.
+ //
+ *MemorySize = (UINTN) CapsuleSize;
+ *MemoryBase = (VOID *) NewCapsuleBase;
+
+ ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE);
+ ASSERT (PrivateDataPtr->CapsuleAllImageSize == CapsuleImageSize);
+ ASSERT (PrivateDataPtr->CapsuleNumber == CapsuleIndex);
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h b/Core/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h
new file mode 100644
index 0000000000..cac444204b
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h
@@ -0,0 +1,121 @@
+/** @file
+ Common header file.
+
+Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _CAPSULE_COMMON_HEADER_
+#define _CAPSULE_COMMON_HEADER_
+
+//
+// 8 extra pages for PF handler.
+//
+#define EXTRA_PAGE_TABLE_PAGES 8
+
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
+
+//
+// This capsule PEIM puts its private data at the start of the
+// coalesced capsule. Here's the structure definition.
+//
+#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'P')
+
+#pragma pack(1)
+typedef struct {
+ UINT64 Signature;
+ UINT64 CapsuleAllImageSize;
+ UINT64 CapsuleNumber;
+ UINT64 CapsuleOffset[1];
+} EFI_CAPSULE_PEIM_PRIVATE_DATA;
+#pragma pack()
+
+typedef struct {
+ ///
+ /// The physical start address of the resource region.
+ ///
+ EFI_PHYSICAL_ADDRESS PhysicalStart;
+ ///
+ /// The number of bytes of the resource region.
+ ///
+ UINT64 ResourceLength;
+} MEMORY_RESOURCE_DESCRIPTOR;
+
+#define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T')
+
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+#pragma pack(1)
+typedef struct {
+ EFI_PHYSICAL_ADDRESS EntryPoint;
+ EFI_PHYSICAL_ADDRESS StackBufferBase;
+ UINT64 StackBufferLength;
+ EFI_PHYSICAL_ADDRESS JumpBuffer;
+ EFI_PHYSICAL_ADDRESS BlockListAddr;
+ EFI_PHYSICAL_ADDRESS MemoryResource;
+ EFI_PHYSICAL_ADDRESS MemoryBase64Ptr;
+ EFI_PHYSICAL_ADDRESS MemorySize64Ptr;
+ BOOLEAN Page1GSupport;
+ UINT64 AddressEncMask;
+} SWITCH_32_TO_64_CONTEXT;
+
+typedef struct {
+ UINT16 ReturnCs;
+ EFI_PHYSICAL_ADDRESS ReturnEntryPoint;
+ UINT64 ReturnStatus;
+ //
+ // NOTICE:
+ // Be careful about the Base field of IA32_DESCRIPTOR
+ // that is UINTN type.
+ // To extend new field for this structure, add it to
+ // right before this Gdtr field.
+ //
+ IA32_DESCRIPTOR Gdtr;
+} SWITCH_64_TO_32_CONTEXT;
+#pragma pack()
+#endif
+
+/**
+ The function to coalesce a fragmented capsule in memory.
+
+ @param PeiServices General purpose services available to every PEIM.
+ @param BlockListBuffer Point to the buffer of Capsule Descriptor Variables.
+ @param MemoryResource Pointer to the buffer of memory resource descriptor.
+ @param MemoryBase Pointer to the base of a block of memory that we can walk
+ all over while trying to coalesce our buffers.
+ On output, this variable will hold the base address of
+ a coalesced capsule.
+ @param MemorySize Size of the memory region pointed to by MemoryBase.
+ On output, this variable will contain the size of the
+ coalesced capsule.
+
+ @retval EFI_NOT_FOUND if we can't determine the boot mode
+ if the boot mode is not flash-update
+ if we could not find the capsule descriptors
+
+ @retval EFI_BUFFER_TOO_SMALL
+ if we could not coalesce the capsule in the memory
+ region provided to us
+
+ @retval EFI_SUCCESS if there's no capsule, or if we processed the
+ capsule successfully.
+**/
+EFI_STATUS
+EFIAPI
+CapsuleDataCoalesce (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
+ IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
+ IN OUT VOID **MemoryBase,
+ IN OUT UINTN *MemorySize
+ );
+
+#endif
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c b/Core/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
new file mode 100644
index 0000000000..cca455ec39
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
@@ -0,0 +1,1225 @@
+/** @file
+ Capsule update PEIM for UEFI2.0
+
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Capsule.h"
+
+#ifdef MDE_CPU_IA32
+//
+// Global Descriptor Table (GDT)
+//
+GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = {
+/* selector { Global Segment Descriptor } */
+/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
+/* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
+/* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
+/* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
+/* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
+/* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
+/* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
+/* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
+/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
+};
+
+//
+// IA32 Gdt register
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
+ sizeof (mGdtEntries) - 1,
+ (UINTN) mGdtEntries
+ };
+
+
+/**
+ The function will check if 1G page is supported.
+
+ @retval TRUE 1G page is supported.
+ @retval FALSE 1G page is not supported.
+
+**/
+BOOLEAN
+IsPage1GSupport (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT32 RegEdx;
+ BOOLEAN Page1GSupport;
+
+ Page1GSupport = FALSE;
+ if (PcdGetBool(PcdUse1GPageTable)) {
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000001) {
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+ if ((RegEdx & BIT26) != 0) {
+ Page1GSupport = TRUE;
+ }
+ }
+ }
+
+ return Page1GSupport;
+}
+
+/**
+ Calculate the total size of page table.
+
+ @param[in] Page1GSupport 1G page support or not.
+
+ @return The size of page table.
+
+**/
+UINTN
+CalculatePageTableSize (
+ IN BOOLEAN Page1GSupport
+ )
+{
+ UINTN ExtraPageTablePages;
+ UINTN TotalPagesNum;
+ UINT8 PhysicalAddressBits;
+ UINT32 NumberOfPml4EntriesNeeded;
+ UINT32 NumberOfPdpEntriesNeeded;
+
+ //
+ // Create 4G page table by default,
+ // and let PF handler to handle > 4G request.
+ //
+ PhysicalAddressBits = 32;
+ ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
+
+ //
+ // Calculate the table entries needed.
+ //
+ if (PhysicalAddressBits <= 39 ) {
+ NumberOfPml4EntriesNeeded = 1;
+ NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
+ } else {
+ NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
+ NumberOfPdpEntriesNeeded = 512;
+ }
+
+ if (!Page1GSupport) {
+ TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
+ } else {
+ TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
+ }
+ TotalPagesNum += ExtraPageTablePages;
+
+ return EFI_PAGES_TO_SIZE (TotalPagesNum);
+}
+
+/**
+ Allocates and fills in the Page Directory and Page Table Entries to
+ establish a 4G page table.
+
+ @param[in] PageTablesAddress The base address of page table.
+ @param[in] Page1GSupport 1G page support or not.
+
+**/
+VOID
+Create4GPageTables (
+ IN EFI_PHYSICAL_ADDRESS PageTablesAddress,
+ IN BOOLEAN Page1GSupport
+ )
+{
+ UINT8 PhysicalAddressBits;
+ EFI_PHYSICAL_ADDRESS PageAddress;
+ UINTN IndexOfPml4Entries;
+ UINTN IndexOfPdpEntries;
+ UINTN IndexOfPageDirectoryEntries;
+ UINT32 NumberOfPml4EntriesNeeded;
+ UINT32 NumberOfPdpEntriesNeeded;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;
+ UINTN BigPageAddress;
+ PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
+ UINT64 AddressEncMask;
+
+ //
+ // Make sure AddressEncMask is contained to smallest supported address field.
+ //
+ AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+
+ //
+ // Create 4G page table by default,
+ // and let PF handler to handle > 4G request.
+ //
+ PhysicalAddressBits = 32;
+
+ //
+ // Calculate the table entries needed.
+ //
+ if (PhysicalAddressBits <= 39 ) {
+ NumberOfPml4EntriesNeeded = 1;
+ NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
+ } else {
+ NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
+ NumberOfPdpEntriesNeeded = 512;
+ }
+
+ //
+ // Pre-allocate big pages to avoid later allocations.
+ //
+ BigPageAddress = (UINTN) PageTablesAddress;
+
+ //
+ // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
+ //
+ PageMap = (VOID *) BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ PageMapLevel4Entry = PageMap;
+ PageAddress = 0;
+ for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+ //
+ // Each PML4 entry points to a page of Page Directory Pointer entires.
+ // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
+ //
+ PageDirectoryPointerEntry = (VOID *) BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ //
+ // Make a PML4 Entry
+ //
+ PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask;
+ PageMapLevel4Entry->Bits.ReadWrite = 1;
+ PageMapLevel4Entry->Bits.Present = 1;
+
+ if (Page1GSupport) {
+ PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;
+ PageDirectory1GEntry->Bits.ReadWrite = 1;
+ PageDirectory1GEntry->Bits.Present = 1;
+ PageDirectory1GEntry->Bits.MustBe1 = 1;
+ }
+ } else {
+ for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
+ //
+ // Each Directory Pointer entries points to a page of Page Directory entires.
+ // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
+ //
+ PageDirectoryEntry = (VOID *) BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ //
+ // Fill in a Page Directory Pointer Entries
+ //
+ PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask;
+ PageDirectoryPointerEntry->Bits.ReadWrite = 1;
+ PageDirectoryPointerEntry->Bits.Present = 1;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+ }
+ }
+
+ for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
+ ZeroMem (
+ PageDirectoryPointerEntry,
+ sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)
+ );
+ }
+ }
+ }
+
+ //
+ // For the PML4 entries we are not using fill in a null entry.
+ //
+ for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+ ZeroMem (
+ PageMapLevel4Entry,
+ sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
+ );
+ }
+}
+
+/**
+ Return function from long mode to 32-bit mode.
+
+ @param EntrypointContext Context for mode switching
+ @param ReturnContext Context for mode switching
+
+**/
+VOID
+ReturnFunction (
+ SWITCH_32_TO_64_CONTEXT *EntrypointContext,
+ SWITCH_64_TO_32_CONTEXT *ReturnContext
+ )
+{
+ //
+ // Restore original GDT
+ //
+ AsmWriteGdtr (&ReturnContext->Gdtr);
+
+ //
+ // return to original caller
+ //
+ LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)EntrypointContext->JumpBuffer, 1);
+
+ //
+ // never be here
+ //
+ ASSERT (FALSE);
+}
+
+/**
+ Thunk function from 32-bit protection mode to long mode.
+
+ @param PageTableAddress Page table base address
+ @param Context Context for mode switching
+ @param ReturnContext Context for mode switching
+
+ @retval EFI_SUCCESS Function successfully executed.
+
+**/
+EFI_STATUS
+Thunk32To64 (
+ EFI_PHYSICAL_ADDRESS PageTableAddress,
+ SWITCH_32_TO_64_CONTEXT *Context,
+ SWITCH_64_TO_32_CONTEXT *ReturnContext
+ )
+{
+ UINTN SetJumpFlag;
+ EFI_STATUS Status;
+
+ //
+ // Save return address, LongJump will return here then
+ //
+ SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER *) (UINTN) Context->JumpBuffer);
+
+ if (SetJumpFlag == 0) {
+
+ //
+ // Build 4G Page Tables.
+ //
+ Create4GPageTables (PageTableAddress, Context->Page1GSupport);
+
+ //
+ // Create 64-bit GDT
+ //
+ AsmWriteGdtr (&mGdt);
+
+ //
+ // Write CR3
+ //
+ AsmWriteCr3 ((UINTN) PageTableAddress);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
+ __FUNCTION__,
+ Context->StackBufferBase,
+ Context->StackBufferLength
+ ));
+
+ //
+ // Disable interrupt of Debug timer, since the IDT table cannot work in long mode
+ //
+ SaveAndSetDebugTimerInterrupt (FALSE);
+ //
+ // Transfer to long mode
+ //
+ AsmEnablePaging64 (
+ 0x38,
+ (UINT64) Context->EntryPoint,
+ (UINT64)(UINTN) Context,
+ (UINT64)(UINTN) ReturnContext,
+ Context->StackBufferBase + Context->StackBufferLength
+ );
+ }
+
+ //
+ // Convert to 32-bit Status and return
+ //
+ Status = EFI_SUCCESS;
+ if ((UINTN) ReturnContext->ReturnStatus != 0) {
+ Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus);
+ }
+
+ return Status;
+}
+
+/**
+ If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
+
+ @param LongModeBuffer The context of long mode.
+ @param CoalesceEntry Entry of coalesce image.
+ @param BlockListAddr Address of block list.
+ @param MemoryResource Pointer to the buffer of memory resource descriptor.
+ @param MemoryBase Base of memory range.
+ @param MemorySize Size of memory range.
+
+ @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
+ @retval Others Failed to execute coalesce in long mode.
+
+**/
+EFI_STATUS
+ModeSwitch (
+ IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer,
+ IN COALESCE_ENTRY CoalesceEntry,
+ IN EFI_PHYSICAL_ADDRESS BlockListAddr,
+ IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
+ IN OUT VOID **MemoryBase,
+ IN OUT UINTN *MemorySize
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS MemoryBase64;
+ UINT64 MemorySize64;
+ EFI_PHYSICAL_ADDRESS MemoryEnd64;
+ SWITCH_32_TO_64_CONTEXT Context;
+ SWITCH_64_TO_32_CONTEXT ReturnContext;
+ BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
+ EFI_PHYSICAL_ADDRESS ReservedRangeBase;
+ EFI_PHYSICAL_ADDRESS ReservedRangeEnd;
+ BOOLEAN Page1GSupport;
+
+ ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));
+ ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));
+
+ MemoryBase64 = (UINT64) (UINTN) *MemoryBase;
+ MemorySize64 = (UINT64) (UINTN) *MemorySize;
+ MemoryEnd64 = MemoryBase64 + MemorySize64;
+
+ Page1GSupport = IsPage1GSupport ();
+
+ //
+ // Merge memory range reserved for stack and page table
+ //
+ if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {
+ ReservedRangeBase = LongModeBuffer->StackBaseAddress;
+ ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize (Page1GSupport);
+ } else {
+ ReservedRangeBase = LongModeBuffer->PageTableAddress;
+ ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;
+ }
+
+ //
+ // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
+ // If they are overlapped, get a larger range to process capsule data.
+ //
+ if (ReservedRangeBase <= MemoryBase64) {
+ if (ReservedRangeEnd < MemoryEnd64) {
+ MemoryBase64 = ReservedRangeEnd;
+ } else {
+ DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else if (ReservedRangeBase < MemoryEnd64) {
+ if (ReservedRangeEnd < MemoryEnd64 &&
+ ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) {
+ MemoryBase64 = ReservedRangeEnd;
+ } else {
+ MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);
+ }
+ }
+
+ //
+ // Initialize context jumping to 64-bit enviroment
+ //
+ Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer;
+ Context.StackBufferBase = LongModeBuffer->StackBaseAddress;
+ Context.StackBufferLength = LongModeBuffer->StackSize;
+ Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry;
+ Context.BlockListAddr = BlockListAddr;
+ Context.MemoryResource = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryResource;
+ Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;
+ Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;
+ Context.Page1GSupport = Page1GSupport;
+ Context.AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+
+ //
+ // Prepare data for return back
+ //
+ ReturnContext.ReturnCs = 0x10;
+ ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction;
+ //
+ // Will save the return status of processing capsule
+ //
+ ReturnContext.ReturnStatus = 0;
+
+ //
+ // Save original GDT
+ //
+ AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr);
+
+ Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext);
+
+ if (!EFI_ERROR (Status)) {
+ *MemoryBase = (VOID *) (UINTN) MemoryBase64;
+ *MemorySize = (UINTN) MemorySize64;
+ }
+
+ return Status;
+
+}
+
+/**
+ Locates the coalesce image entry point, and detects its machine type.
+
+ @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
+ @param CoalesceImageMachineType Pointer to machine type of coalesce image.
+
+ @retval EFI_SUCCESS Coalesce image successfully located.
+ @retval Others Failed to locate the coalesce image.
+
+**/
+EFI_STATUS
+FindCapsuleCoalesceImage (
+ OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint,
+ OUT UINT16 *CoalesceImageMachineType
+ )
+{
+ EFI_STATUS Status;
+ UINTN Instance;
+ EFI_PEI_LOAD_FILE_PPI *LoadFile;
+ EFI_PEI_FV_HANDLE VolumeHandle;
+ EFI_PEI_FILE_HANDLE FileHandle;
+ EFI_PHYSICAL_ADDRESS CoalesceImageAddress;
+ UINT64 CoalesceImageSize;
+ UINT32 AuthenticationState;
+
+ Instance = 0;
+
+ while (TRUE) {
+ Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle);
+ if (!EFI_ERROR (Status)) {
+ Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = LoadFile->LoadFile (
+ LoadFile,
+ FileHandle,
+ &CoalesceImageAddress,
+ &CoalesceImageSize,
+ CoalesceImageEntryPoint,
+ &AuthenticationState
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status));
+ return Status;
+ }
+ *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Gets the reserved long mode buffer.
+
+ @param LongModeBuffer Pointer to the long mode buffer for output.
+
+ @retval EFI_SUCCESS Long mode buffer successfully retrieved.
+ @retval Others Variable storing long mode buffer not found.
+
+**/
+EFI_STATUS
+GetLongModeContext (
+ OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+
+ Status = PeiServicesLocatePpi (
+ &gEfiPeiReadOnlyVariable2PpiGuid,
+ 0,
+ NULL,
+ (VOID **) &PPIVariableServices
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ LongModeBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));
+ }
+ return Status;
+}
+#endif
+
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+/**
+ Get physical address bits.
+
+ @return Physical address bits.
+
+**/
+UINT8
+GetPhysicalAddressBits (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT8 PhysicalAddressBits;
+ VOID *Hob;
+
+ //
+ // Get physical address bits supported.
+ //
+ Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+ if (Hob != NULL) {
+ PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
+ } else {
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000008) {
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+ PhysicalAddressBits = (UINT8) RegEax;
+ } else {
+ PhysicalAddressBits = 36;
+ }
+ }
+
+ //
+ // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
+ //
+ ASSERT (PhysicalAddressBits <= 52);
+ if (PhysicalAddressBits > 48) {
+ PhysicalAddressBits = 48;
+ }
+
+ return PhysicalAddressBits;
+}
+#endif
+
+/**
+ Build memory resource descriptor from resource descriptor in HOB list.
+
+ @return Pointer to the buffer of memory resource descriptor.
+ NULL if no memory resource descriptor reported in HOB list
+ before capsule Coalesce.
+
+**/
+MEMORY_RESOURCE_DESCRIPTOR *
+BuildMemoryResourceDescriptor (
+ VOID
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ UINTN Index;
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor;
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;
+ EFI_STATUS Status;
+
+ //
+ // Get the count of memory resource descriptor.
+ //
+ Index = 0;
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
+ while (Hob.Raw != NULL) {
+ ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;
+ if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+ Index++;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
+ }
+
+ if (Index == 0) {
+ DEBUG ((EFI_D_INFO | EFI_D_WARN, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+ //
+ // Allocate memory to hold memory resource descriptor,
+ // include extra one NULL terminate memory resource descriptor.
+ //
+ Status = PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);
+ ASSERT_EFI_ERROR (Status);
+ ZeroMem (MemoryResource, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+
+ MemoryResource[0].PhysicalStart = 0;
+ MemoryResource[0].ResourceLength = LShiftU64 (1, GetPhysicalAddressBits ());
+ DEBUG ((EFI_D_INFO, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",
+ MemoryResource[0x0].PhysicalStart, MemoryResource[0x0].ResourceLength));
+ return MemoryResource;
+#else
+ return NULL;
+#endif
+ }
+
+ //
+ // Allocate memory to hold memory resource descriptor,
+ // include extra one NULL terminate memory resource descriptor.
+ //
+ Status = PeiServicesAllocatePool ((Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);
+ ASSERT_EFI_ERROR (Status);
+ ZeroMem (MemoryResource, (Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+
+ //
+ // Get the content of memory resource descriptor.
+ //
+ Index = 0;
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
+ while (Hob.Raw != NULL) {
+ ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;
+ if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+ DEBUG ((EFI_D_INFO, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
+ Index, ResourceDescriptor->PhysicalStart, ResourceDescriptor->ResourceLength));
+ MemoryResource[Index].PhysicalStart = ResourceDescriptor->PhysicalStart;
+ MemoryResource[Index].ResourceLength = ResourceDescriptor->ResourceLength;
+ Index++;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
+ }
+
+ return MemoryResource;
+}
+
+/**
+ Checks for the presence of capsule descriptors.
+ Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
+ and save to DescriptorBuffer.
+
+ @param DescriptorBuffer Pointer to the capsule descriptors
+
+ @retval EFI_SUCCESS a valid capsule is present
+ @retval EFI_NOT_FOUND if a valid capsule is not present
+**/
+EFI_STATUS
+GetCapsuleDescriptors (
+ IN EFI_PHYSICAL_ADDRESS *DescriptorBuffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ UINTN Index;
+ UINTN TempIndex;
+ UINTN ValidIndex;
+ BOOLEAN Flag;
+ CHAR16 CapsuleVarName[30];
+ CHAR16 *TempVarName;
+ EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+
+ Index = 0;
+ TempVarName = NULL;
+ CapsuleVarName[0] = 0;
+ ValidIndex = 0;
+ CapsuleDataPtr64 = 0;
+
+ Status = PeiServicesLocatePpi (
+ &gEfiPeiReadOnlyVariable2PpiGuid,
+ 0,
+ NULL,
+ (VOID **) &PPIVariableServices
+ );
+ if (Status == EFI_SUCCESS) {
+ StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);
+ TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
+ Size = sizeof (CapsuleDataPtr64);
+ while (1) {
+ if (Index == 0) {
+ //
+ // For the first Capsule Image
+ //
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ CapsuleVarName,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ (VOID *) &CapsuleDataPtr64
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "Capsule -- capsule variable not set\n"));
+ return EFI_NOT_FOUND;
+ }
+ //
+ // We have a chicken/egg situation where the memory init code needs to
+ // know the boot mode prior to initializing memory. For this case, our
+ // validate function will fail. We can detect if this is the case if blocklist
+ // pointer is null. In that case, return success since we know that the
+ // variable is set.
+ //
+ if (DescriptorBuffer == NULL) {
+ return EFI_SUCCESS;
+ }
+ } else {
+ UnicodeValueToStringS (
+ TempVarName,
+ sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),
+ 0,
+ Index,
+ 0
+ );
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ CapsuleVarName,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ (VOID *) &CapsuleDataPtr64
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // If this BlockList has been linked before, skip this variable
+ //
+ Flag = FALSE;
+ for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {
+ if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64) {
+ Flag = TRUE;
+ break;
+ }
+ }
+ if (Flag) {
+ Index ++;
+ continue;
+ }
+ }
+
+ //
+ // Cache BlockList which has been processed
+ //
+ DescriptorBuffer[ValidIndex++] = CapsuleDataPtr64;
+ Index ++;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Capsule PPI service to coalesce a fragmented capsule in memory.
+
+ @param PeiServices General purpose services available to every PEIM.
+ @param MemoryBase Pointer to the base of a block of memory that we can walk
+ all over while trying to coalesce our buffers.
+ On output, this variable will hold the base address of
+ a coalesced capsule.
+ @param MemorySize Size of the memory region pointed to by MemoryBase.
+ On output, this variable will contain the size of the
+ coalesced capsule.
+
+ @retval EFI_NOT_FOUND if we can't determine the boot mode
+ if the boot mode is not flash-update
+ if we could not find the capsule descriptors
+
+ @retval EFI_BUFFER_TOO_SMALL
+ if we could not coalesce the capsule in the memory
+ region provided to us
+
+ @retval EFI_SUCCESS if there's no capsule, or if we processed the
+ capsule successfully.
+**/
+EFI_STATUS
+EFIAPI
+CapsuleCoalesce (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN OUT VOID **MemoryBase,
+ IN OUT UINTN *MemorySize
+ )
+{
+ UINTN Index;
+ UINTN Size;
+ UINTN VariableCount;
+ CHAR16 CapsuleVarName[30];
+ CHAR16 *TempVarName;
+ EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
+ EFI_STATUS Status;
+ EFI_BOOT_MODE BootMode;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+ EFI_PHYSICAL_ADDRESS *VariableArrayAddress;
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;
+#ifdef MDE_CPU_IA32
+ UINT16 CoalesceImageMachineType;
+ EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint;
+ COALESCE_ENTRY CoalesceEntry;
+ EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
+#endif
+
+ Index = 0;
+ VariableCount = 0;
+ CapsuleVarName[0] = 0;
+ CapsuleDataPtr64 = 0;
+
+ //
+ // Someone should have already ascertained the boot mode. If it's not
+ // capsule update, then return normally.
+ //
+ Status = PeiServicesGetBootMode (&BootMode);
+ if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {
+ DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+
+ //
+ // User may set the same ScatterGatherList with several different variables,
+ // so cache all ScatterGatherList for check later.
+ //
+ Status = PeiServicesLocatePpi (
+ &gEfiPeiReadOnlyVariable2PpiGuid,
+ 0,
+ NULL,
+ (VOID **) &PPIVariableServices
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ Size = sizeof (CapsuleDataPtr64);
+ StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);
+ TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
+ while (TRUE) {
+ if (Index > 0) {
+ UnicodeValueToStringS (
+ TempVarName,
+ sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),
+ 0,
+ Index,
+ 0
+ );
+ }
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ CapsuleVarName,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ (VOID *) &CapsuleDataPtr64
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // There is no capsule variables, quit
+ //
+ DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index));
+ break;
+ }
+ VariableCount++;
+ Index++;
+ }
+
+ DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount));
+
+ //
+ // The last entry is the end flag.
+ //
+ Status = PeiServicesAllocatePool (
+ (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS),
+ (VOID **)&VariableArrayAddress
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));
+ goto Done;
+ }
+
+ ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS));
+
+ //
+ // Find out if we actually have a capsule.
+ // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.
+ //
+ Status = GetCapsuleDescriptors (VariableArrayAddress);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n"));
+ goto Done;
+ }
+
+ MemoryResource = BuildMemoryResourceDescriptor ();
+
+#ifdef MDE_CPU_IA32
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
+ //
+ // Switch to 64-bit mode to process capsule data when:
+ // 1. When DXE phase is 64-bit
+ // 2. When the buffer for 64-bit transition exists
+ // 3. When Capsule X64 image is built in BIOS image
+ // In 64-bit mode, we can process capsule data above 4GB.
+ //
+ CoalesceImageEntryPoint = 0;
+ Status = GetLongModeContext (&LongModeBuffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Fail to find the variable for long mode context!\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+
+ Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);
+ if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {
+ DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+ ASSERT (CoalesceImageEntryPoint != 0);
+ CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint;
+ Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
+ } else {
+ //
+ // Capsule is processed in IA32 mode.
+ //
+ Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
+ }
+#else
+ //
+ // Process capsule directly.
+ //
+ Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
+#endif
+
+ DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status));
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n"));
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n"));
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_MAJOR,
+ (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR)
+ );
+ }
+
+Done:
+ return Status;
+}
+
+/**
+ Determine if we're in capsule update boot mode.
+
+ @param PeiServices PEI services table
+
+ @retval EFI_SUCCESS if we have a capsule available
+ @retval EFI_NOT_FOUND no capsule detected
+
+**/
+EFI_STATUS
+EFIAPI
+CheckCapsuleUpdate (
+ IN EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+ Status = GetCapsuleDescriptors (NULL);
+ return Status;
+}
+/**
+ This function will look at a capsule and determine if it's a test pattern.
+ If it is, then it will verify it and emit an error message if corruption is detected.
+
+ @param PeiServices Standard pei services pointer
+ @param CapsuleBase Base address of coalesced capsule, which is preceeded
+ by private data. Very implementation specific.
+
+ @retval TRUE Capsule image is the test image
+ @retval FALSE Capsule image is not the test image.
+
+**/
+BOOLEAN
+CapsuleTestPattern (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN VOID *CapsuleBase
+ )
+{
+ UINT32 *TestPtr;
+ UINT32 TestCounter;
+ UINT32 TestSize;
+ BOOLEAN RetValue;
+
+ RetValue = FALSE;
+
+ //
+ // Look at the capsule data and determine if it's a test pattern. If it
+ // is, then test it now.
+ //
+ TestPtr = (UINT32 *) CapsuleBase;
+ //
+ // 0x54534554 "TEST"
+ //
+ if (*TestPtr == 0x54534554) {
+ RetValue = TRUE;
+ DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));
+ TestSize = TestPtr[1] / sizeof (UINT32);
+ //
+ // Skip over the signature and the size fields in the pattern data header
+ //
+ TestPtr += 2;
+ TestCounter = 0;
+ while (TestSize > 0) {
+ if (*TestPtr != TestCounter) {
+ 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));
+ return TRUE;
+ }
+
+ TestPtr++;
+ TestCounter++;
+ TestSize--;
+ }
+
+ DEBUG ((EFI_D_INFO, "Capsule test pattern mode SUCCESS\n"));
+ }
+
+ return RetValue;
+}
+
+/**
+ Capsule PPI service that gets called after memory is available. The
+ capsule coalesce function, which must be called first, returns a base
+ address and size, which can be anything actually. Once the memory init
+ PEIM has discovered memory, then it should call this function and pass in
+ the base address and size returned by the coalesce function. Then this
+ function can create a capsule HOB and return.
+
+ @param PeiServices standard pei services pointer
+ @param CapsuleBase address returned by the capsule coalesce function. Most
+ likely this will actually be a pointer to private data.
+ @param CapsuleSize value returned by the capsule coalesce function.
+
+ @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
+ coalesced capsule
+ @retval EFI_SUCCESS if all goes well.
+**/
+EFI_STATUS
+EFIAPI
+CreateState (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN VOID *CapsuleBase,
+ IN UINTN CapsuleSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData;
+ UINTN Size;
+ EFI_PHYSICAL_ADDRESS NewBuffer;
+ UINTN CapsuleNumber;
+ UINT32 Index;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ UINT64 Length;
+
+ PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase;
+ if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+ if (PrivateData->CapsuleAllImageSize >= MAX_ADDRESS) {
+ DEBUG ((EFI_D_ERROR, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData->CapsuleAllImageSize));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ if (PrivateData->CapsuleNumber >= MAX_ADDRESS) {
+ DEBUG ((EFI_D_ERROR, "CapsuleNumber too big - 0x%lx\n", PrivateData->CapsuleNumber));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Capsule Number and Capsule Offset is in the tail of Capsule data.
+ //
+ Size = (UINTN)PrivateData->CapsuleAllImageSize;
+ CapsuleNumber = (UINTN)PrivateData->CapsuleNumber;
+ //
+ // Allocate the memory so that it gets preserved into DXE
+ //
+ Status = PeiServicesAllocatePages (
+ EfiRuntimeServicesData,
+ EFI_SIZE_TO_PAGES (Size),
+ &NewBuffer
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "AllocatePages Failed!\n"));
+ return Status;
+ }
+ //
+ // Copy to our new buffer for DXE
+ //
+ 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));
+ CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) ((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), Size);
+ //
+ // Check for test data pattern. If it is the test pattern, then we'll
+ // test it and still create the HOB so that it can be used to verify
+ // that capsules don't get corrupted all the way into BDS. BDS will
+ // still try to turn it into a firmware volume, but will think it's
+ // corrupted so nothing will happen.
+ //
+ DEBUG_CODE (
+ CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer);
+ );
+
+ //
+ // Build the UEFI Capsule Hob for each capsule image.
+ //
+ for (Index = 0; Index < CapsuleNumber; Index ++) {
+ BaseAddress = NewBuffer + PrivateData->CapsuleOffset[Index];
+ Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize;
+
+ BuildCvHob (BaseAddress, Length);
+ }
+
+ return EFI_SUCCESS;
+}
+
+CONST EFI_PEI_CAPSULE_PPI mCapsulePpi = {
+ CapsuleCoalesce,
+ CheckCapsuleUpdate,
+ CreateState
+};
+
+CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiCapsulePpiGuid,
+ (EFI_PEI_CAPSULE_PPI *) &mCapsulePpi
+};
+
+/**
+ Entry point function for the PEIM
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @return EFI_SUCCESS If we installed our PPI
+
+**/
+EFI_STATUS
+EFIAPI
+CapsuleMain (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ //
+ // Just produce our PPI
+ //
+ return PeiServicesInstallPpi (&mUefiPpiListCapsule);
+}
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S b/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S
new file mode 100644
index 0000000000..9e17cc39ee
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S
@@ -0,0 +1,81 @@
+## @file
+# This is the assembly code for page fault handler hook.
+#
+# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials are
+# licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+ASM_GLOBAL ASM_PFX(PageFaultHandlerHook)
+ASM_PFX(PageFaultHandlerHook):
+ addq $-0x10, %rsp
+ # save rax
+ movq %rax, 0x08(%rsp)
+
+ # pushq %rax # save all volatile registers
+ pushq %rcx
+ pushq %rdx
+ pushq %r8
+ pushq %r9
+ pushq %r10
+ pushq %r11
+ # save volatile fp registers
+ # 68h + 08h(for alignment)
+ addq $-0x70, %rsp
+ stmxcsr 0x60(%rsp)
+ movdqa %xmm0, 0x0(%rsp)
+ movdqa %xmm1, 0x10(%rsp)
+ movdqa %xmm2, 0x20(%rsp)
+ movdqa %xmm3, 0x30(%rsp)
+ movdqa %xmm4, 0x40(%rsp)
+ movdqa %xmm5, 0x50(%rsp)
+
+ addq $-0x20, %rsp
+ call ASM_PFX(PageFaultHandler)
+ addq $0x20, %rsp
+
+ # load volatile fp registers
+ ldmxcsr 0x60(%rsp)
+ movdqa 0x0(%rsp), %xmm0
+ movdqa 0x10(%rsp), %xmm1
+ movdqa 0x20(%rsp), %xmm2
+ movdqa 0x30(%rsp), %xmm3
+ movdqa 0x40(%rsp), %xmm4
+ movdqa 0x50(%rsp), %xmm5
+ addq $0x70, %rsp
+
+ popq %r11
+ popq %r10
+ popq %r9
+ popq %r8
+ popq %rdx
+ popq %rcx
+ # popq %rax # restore all volatile registers
+
+ addq $0x10, %rsp
+
+ # rax returned from PageFaultHandler is NULL or OriginalHandler address
+ # NULL if the page fault is handled by PageFaultHandler
+ # OriginalHandler address if the page fault is not handled by PageFaultHandler
+ testq %rax, %rax
+
+ # save OriginalHandler address
+ movq %rax, -0x10(%rsp)
+ # restore rax
+ movq -0x08(%rsp), %rax
+
+ jz L1
+
+ # jump to OriginalHandler
+ jmpq *-0x10(%rsp)
+
+L1:
+ addq $0x08, %rsp # skip error code for PF
+ iretq
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm b/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm
new file mode 100644
index 0000000000..2f1eab72ef
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm
@@ -0,0 +1,87 @@
+;; @file
+; This is the assembly code for page fault handler hook.
+;
+; Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+;
+; This program and the accompanying materials
+; are licensed and made available under the terms and conditions of the BSD License
+; which accompanies this distribution. The full text of the license may be found at
+; http://opensource.org/licenses/bsd-license.php
+;
+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;
+;;
+
+EXTERN PageFaultHandler:PROC
+
+ .code
+
+PageFaultHandlerHook PROC
+ add rsp, -10h
+ ; save rax
+ mov [rsp + 08h], rax
+
+ ;push rax ; save all volatile registers
+ push rcx
+ push rdx
+ push r8
+ push r9
+ push r10
+ push r11
+ ; save volatile fp registers
+ ; 68h + 08h(for alignment)
+ add rsp, -70h
+ stmxcsr [rsp + 60h]
+ movdqa [rsp + 0h], xmm0
+ movdqa [rsp + 10h], xmm1
+ movdqa [rsp + 20h], xmm2
+ movdqa [rsp + 30h], xmm3
+ movdqa [rsp + 40h], xmm4
+ movdqa [rsp + 50h], xmm5
+
+ add rsp, -20h
+ call PageFaultHandler
+ add rsp, 20h
+
+ ; load volatile fp registers
+ ldmxcsr [rsp + 60h]
+ movdqa xmm0, [rsp + 0h]
+ movdqa xmm1, [rsp + 10h]
+ movdqa xmm2, [rsp + 20h]
+ movdqa xmm3, [rsp + 30h]
+ movdqa xmm4, [rsp + 40h]
+ movdqa xmm5, [rsp + 50h]
+ add rsp, 70h
+
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop rdx
+ pop rcx
+ ;pop rax ; restore all volatile registers
+
+ add rsp, 10h
+
+ ; rax returned from PageFaultHandler is NULL or OriginalHandler address
+ ; NULL if the page fault is handled by PageFaultHandler
+ ; OriginalHandler address if the page fault is not handled by PageFaultHandler
+ test rax, rax
+
+ ; save OriginalHandler address
+ mov [rsp - 10h], rax
+ ; restore rax
+ mov rax, [rsp - 08h]
+
+ jz @F
+
+ ; jump to OriginalHandler
+ jmp qword ptr [rsp - 10h]
+
+@@:
+ add rsp, 08h ; skip error code for PF
+ iretq
+PageFaultHandlerHook ENDP
+
+ END
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm b/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm
new file mode 100644
index 0000000000..91ace09934
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm
@@ -0,0 +1,87 @@
+;; @file
+; This is the assembly code for page fault handler hook.
+;
+; Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+;
+; This program and the accompanying materials
+; are licensed and made available under the terms and conditions of the BSD License
+; which accompanies this distribution. The full text of the license may be found at
+; http://opensource.org/licenses/bsd-license.php
+;
+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;
+;;
+
+extern ASM_PFX(PageFaultHandler)
+
+ DEFAULT REL
+ SECTION .text
+
+global ASM_PFX(PageFaultHandlerHook)
+ASM_PFX(PageFaultHandlerHook):
+ add rsp, -0x10
+ ; save rax
+ mov [rsp + 0x8], rax
+
+ ;push rax ; save all volatile registers
+ push rcx
+ push rdx
+ push r8
+ push r9
+ push r10
+ push r11
+ ; save volatile fp registers
+ ; 68h + 08h(for alignment)
+ add rsp, -0x70
+ stmxcsr [rsp + 0x60]
+ movdqa [rsp + 0x0], xmm0
+ movdqa [rsp + 0x10], xmm1
+ movdqa [rsp + 0x20], xmm2
+ movdqa [rsp + 0x30], xmm3
+ movdqa [rsp + 0x40], xmm4
+ movdqa [rsp + 0x50], xmm5
+
+ add rsp, -0x20
+ call ASM_PFX(PageFaultHandler)
+ add rsp, 0x20
+
+ ; load volatile fp registers
+ ldmxcsr [rsp + 0x60]
+ movdqa xmm0, [rsp + 0x0]
+ movdqa xmm1, [rsp + 0x10]
+ movdqa xmm2, [rsp + 0x20]
+ movdqa xmm3, [rsp + 0x30]
+ movdqa xmm4, [rsp + 0x40]
+ movdqa xmm5, [rsp + 0x50]
+ add rsp, 0x70
+
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop rdx
+ pop rcx
+ ;pop rax ; restore all volatile registers
+
+ add rsp, 0x10
+
+ ; rax returned from PageFaultHandler is NULL or OriginalHandler address
+ ; NULL if the page fault is handled by PageFaultHandler
+ ; OriginalHandler address if the page fault is not handled by PageFaultHandler
+ test rax, rax
+
+ ; save OriginalHandler address
+ mov [rsp - 0x10], rax
+ ; restore rax
+ mov rax, [rsp - 0x8]
+
+ jz .0
+
+ ; jump to OriginalHandler
+ jmp qword [rsp - 0x10]
+
+.0:
+ add rsp, 0x8 ; skip error code for PF
+ iretq
+
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c b/Core/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c
new file mode 100644
index 0000000000..e1871c3c2a
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c
@@ -0,0 +1,311 @@
+/** @file
+ The X64 entrypoint is used to process capsule in long mode.
+
+Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Library/DebugAgentLib.h>
+#include "CommonHeader.h"
+
+#define EXCEPTION_VECTOR_NUMBER 0x22
+
+#define IA32_PG_P BIT0
+#define IA32_PG_RW BIT1
+#define IA32_PG_PS BIT7
+
+typedef struct _PAGE_FAULT_CONTEXT {
+ BOOLEAN Page1GSupport;
+ UINT64 PhyMask;
+ UINTN PageFaultBuffer;
+ UINTN PageFaultIndex;
+ UINT64 AddressEncMask;
+ //
+ // Store the uplink information for each page being used.
+ //
+ UINT64 *PageFaultUplink[EXTRA_PAGE_TABLE_PAGES];
+ VOID *OriginalHandler;
+} PAGE_FAULT_CONTEXT;
+
+typedef struct _PAGE_FAULT_IDT_TABLE {
+ PAGE_FAULT_CONTEXT PageFaultContext;
+ IA32_IDT_GATE_DESCRIPTOR IdtEntryTable[EXCEPTION_VECTOR_NUMBER];
+} PAGE_FAULT_IDT_TABLE;
+
+/**
+ Page fault handler.
+
+**/
+VOID
+EFIAPI
+PageFaultHandlerHook (
+ VOID
+ );
+
+/**
+ Hook IDT with our page fault handler so that the on-demand paging works on page fault.
+
+ @param[in, out] IdtEntry Pointer to IDT entry.
+ @param[in, out] PageFaultContext Pointer to page fault context.
+
+**/
+VOID
+HookPageFaultHandler (
+ IN OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry,
+ IN OUT PAGE_FAULT_CONTEXT *PageFaultContext
+ )
+{
+ UINT32 RegEax;
+ UINT8 PhysicalAddressBits;
+ UINTN PageFaultHandlerHookAddress;
+
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000008) {
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+ PhysicalAddressBits = (UINT8) RegEax;
+ } else {
+ PhysicalAddressBits = 36;
+ }
+ PageFaultContext->PhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;
+ PageFaultContext->PhyMask &= (1ull << 48) - SIZE_4KB;
+
+ //
+ // Set Page Fault entry to catch >4G access
+ //
+ PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook;
+ PageFaultContext->OriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16));
+ IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
+ IdtEntry->Bits.Selector = (UINT16)AsmReadCs ();
+ IdtEntry->Bits.Reserved_0 = 0;
+ IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
+ IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
+ IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32);
+ IdtEntry->Bits.Reserved_1 = 0;
+
+ if (PageFaultContext->Page1GSupport) {
+ PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(2);
+ }else {
+ PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(6);
+ }
+ PageFaultContext->PageFaultIndex = 0;
+ ZeroMem (PageFaultContext->PageFaultUplink, sizeof (PageFaultContext->PageFaultUplink));
+}
+
+/**
+ Acquire page for page fault.
+
+ @param[in, out] PageFaultContext Pointer to page fault context.
+ @param[in, out] Uplink Pointer to up page table entry.
+
+**/
+VOID
+AcquirePage (
+ IN OUT PAGE_FAULT_CONTEXT *PageFaultContext,
+ IN OUT UINT64 *Uplink
+ )
+{
+ UINTN Address;
+ UINT64 AddressEncMask;
+
+ Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex);
+ ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));
+
+ AddressEncMask = PageFaultContext->AddressEncMask;
+
+ //
+ // Cut the previous uplink if it exists and wasn't overwritten.
+ //
+ if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) &&
+ ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & ~AddressEncMask & PageFaultContext->PhyMask) == Address)) {
+ *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0;
+ }
+
+ //
+ // Link & Record the current uplink.
+ //
+ *Uplink = Address | AddressEncMask | IA32_PG_P | IA32_PG_RW;
+ PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = Uplink;
+
+ PageFaultContext->PageFaultIndex = (PageFaultContext->PageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;
+}
+
+/**
+ The page fault handler that on-demand read >4G memory/MMIO.
+
+ @retval NULL The page fault is correctly handled.
+ @retval OriginalHandler The page fault is not handled and is passed through to original handler.
+
+**/
+VOID *
+EFIAPI
+PageFaultHandler (
+ VOID
+ )
+{
+ IA32_DESCRIPTOR Idtr;
+ PAGE_FAULT_CONTEXT *PageFaultContext;
+ UINT64 PhyMask;
+ UINT64 *PageTable;
+ UINT64 PFAddress;
+ UINTN PTIndex;
+ UINT64 AddressEncMask;
+
+ //
+ // Get the IDT Descriptor.
+ //
+ AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr);
+ //
+ // Then get page fault context by IDT Descriptor.
+ //
+ PageFaultContext = (PAGE_FAULT_CONTEXT *) (UINTN) (Idtr.Base - sizeof (PAGE_FAULT_CONTEXT));
+ PhyMask = PageFaultContext->PhyMask;
+ AddressEncMask = PageFaultContext->AddressEncMask;
+
+ PFAddress = AsmReadCr2 ();
+ DEBUG ((EFI_D_ERROR, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress));
+
+ if (PFAddress >= PhyMask + SIZE_4KB) {
+ return PageFaultContext->OriginalHandler;
+ }
+ PFAddress &= PhyMask;
+
+ PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & PhyMask);
+
+ PTIndex = BitFieldRead64 (PFAddress, 39, 47);
+ // PML4E
+ if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
+ AcquirePage (PageFaultContext, &PageTable[PTIndex]);
+ }
+ PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask);
+ PTIndex = BitFieldRead64 (PFAddress, 30, 38);
+ // PDPTE
+ if (PageFaultContext->Page1GSupport) {
+ PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
+ } else {
+ if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
+ AcquirePage (PageFaultContext, &PageTable[PTIndex]);
+ }
+ PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask);
+ PTIndex = BitFieldRead64 (PFAddress, 21, 29);
+ // PD
+ PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
+ }
+
+ return NULL;
+}
+
+
+/**
+ The X64 entrypoint is used to process capsule in long mode then
+ return to 32-bit protected mode.
+
+ @param EntrypointContext Pointer to the context of long mode.
+ @param ReturnContext Pointer to the context of 32-bit protected mode.
+
+ @retval This function should never return actually.
+
+**/
+EFI_STATUS
+EFIAPI
+_ModuleEntryPoint (
+ SWITCH_32_TO_64_CONTEXT *EntrypointContext,
+ SWITCH_64_TO_32_CONTEXT *ReturnContext
+)
+{
+ EFI_STATUS Status;
+ IA32_DESCRIPTOR Ia32Idtr;
+ IA32_DESCRIPTOR X64Idtr;
+ PAGE_FAULT_IDT_TABLE PageFaultIdtTable;
+ IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
+
+ //
+ // Save the IA32 IDT Descriptor
+ //
+ AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
+
+ //
+ // Setup X64 IDT table
+ //
+ ZeroMem (PageFaultIdtTable.IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER);
+ X64Idtr.Base = (UINTN) PageFaultIdtTable.IdtEntryTable;
+ X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER - 1);
+ AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr);
+
+ //
+ // Setup the default CPU exception handlers
+ //
+ Status = InitializeCpuExceptionHandlers (NULL);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Hook page fault handler to handle >4G request.
+ //
+ PageFaultIdtTable.PageFaultContext.Page1GSupport = EntrypointContext->Page1GSupport;
+ PageFaultIdtTable.PageFaultContext.AddressEncMask = EntrypointContext->AddressEncMask;
+ IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) (X64Idtr.Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));
+ HookPageFaultHandler (IdtEntry, &(PageFaultIdtTable.PageFaultContext));
+
+ //
+ // Initialize Debug Agent to support source level debug
+ //
+ InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *) &Ia32Idtr, NULL);
+
+ //
+ // Call CapsuleDataCoalesce to process capsule.
+ //
+ Status = CapsuleDataCoalesce (
+ NULL,
+ (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr,
+ (MEMORY_RESOURCE_DESCRIPTOR *) (UINTN) EntrypointContext->MemoryResource,
+ (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr,
+ (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr
+ );
+
+ ReturnContext->ReturnStatus = Status;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
+ __FUNCTION__,
+ EntrypointContext->StackBufferBase,
+ EntrypointContext->StackBufferLength
+ ));
+
+ //
+ // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode
+ //
+ SaveAndSetDebugTimerInterrupt (FALSE);
+ //
+ // Restore IA32 IDT table
+ //
+ AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
+
+ //
+ // Finish to coalesce capsule, and return to 32-bit mode.
+ //
+ AsmDisablePaging64 (
+ ReturnContext->ReturnCs,
+ (UINT32) ReturnContext->ReturnEntryPoint,
+ (UINT32) (UINTN) EntrypointContext,
+ (UINT32) (UINTN) ReturnContext,
+ (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength)
+ );
+
+ //
+ // Should never be here.
+ //
+ ASSERT (FALSE);
+ return EFI_SUCCESS;
+}