Add capsule > 4GB support. When capsule data is put above 4GB, IA32 PEI transfers to long mode to get capsule data.

Signed-off-by: li-elvin
Reviewed-by: lgao4, mdkinney


git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12264 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
li-elvin
2011-09-02 11:34:35 +00:00
parent 4ff7e37b4f
commit ab7017fe2b
13 changed files with 2233 additions and 866 deletions

View File

@@ -33,6 +33,12 @@
[Sources]
CapsuleService.c
[Sources.Ia32, Sources.IPF, Sources.EBC, Sources.ARM]
SaveLongModeContext.c
[Sources.X64]
X64/SaveLongModeContext.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
@@ -47,19 +53,38 @@
UefiRuntimeLib
BaseLib
PrintLib
[LibraryClasses.X64]
LockBoxLib
UefiLib
BaseMemoryLib
HobLib
[Guids]
gEfiCapsuleVendorGuid ## SOMETIMES_PRODUCED (Process across reset capsule image) ## Variable:L"CapsuleUpdateData" for capsule updated data
[Guids.X64]
gEfiAcpiVariableGuid # ALWAYS_CONSUMED
gEfiAcpiS3ContextGuid # ALWAYS_CONSUMED
[Protocols]
gEfiCapsuleArchProtocolGuid ## PRODUCED
[Protocols.X64]
gEfiDxeSmmReadyToLockProtocolGuid # ALWAYS_CONSUMED
[FeaturePcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset
[FeaturePcd.X64]
gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizeNonPopulateCapsule
gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizePopulateCapsule || gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset ## Populate Image requires reset support.
[Pcd.X64]
gEfiMdeModulePkgTokenSpaceGuid.PcdCapsulePeiLongModeStackSize
[Depex]
gEfiVariableWriteArchProtocolGuid ## Depends on variable write functionality to produce capsule data variable

View File

@@ -4,7 +4,7 @@
It installs the Capsule Architectural Protocol defined in PI1.0a to signify
the capsule runtime services are ready.
Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2006 - 2011, 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
@@ -40,6 +40,15 @@ EFI_HANDLE mNewHandle = NULL;
//
UINTN mTimes = 0;
/**
Create the variable to save the base address of page table and stack
for transferring into long mode in IA32 PEI.
**/
VOID
SaveLongModeContext (
VOID
);
/**
Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended
consumption, the firmware may process the capsule immediately. If the payload should persist
@@ -333,6 +342,15 @@ CapsuleServiceInitialize (
{
EFI_STATUS Status;
//
// When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are
// put above 4GB, so capsule PEI will transfer to long mode to get capsule data.
// The page table and stack is used to transfer processor mode from IA32 to long mode.
// Create the base address of page table and stack, and save them into variable.
// This is not needed when capsule with reset type is not supported.
//
SaveLongModeContext ();
//
// Install capsule runtime services into UEFI runtime service tables.
//

View File

@@ -0,0 +1,27 @@
/** @file
Create the NULL function to pass build in IA32/IPF/ARM/EBC.
Copyright (c) 2011, 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>
/**
Only when PEI is IA32 and DXE is X64, we need transfer to long mode in PEI
in order to process capsule data above 4GB. So create a NULL function here for
other cases.
**/
VOID
SaveLongModeContext (
VOID
)
{
}

View File

@@ -0,0 +1,224 @@
/** @file
Create the variable to save the base address of page table and stack
for transferring into long mode in IA32 capsule PEI.
Copyright (c) 2011, 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 <Protocol/Capsule.h>
#include <Protocol/DxeSmmReadyToLock.h>
#include <Guid/CapsuleVendor.h>
#include <Guid/AcpiS3Context.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiRuntimeLib.h>
#include <Library/BaseLib.h>
#include <Library/LockBoxLib.h>
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/HobLib.h>
/**
Allocate EfiACPIMemoryNVS below 4G memory address.
This function allocates EfiACPIMemoryNVS below 4G memory address.
@param Size Size of memory to allocate.
@return Allocated address for output.
**/
VOID*
AllocateAcpiNvsMemoryBelow4G (
IN UINTN Size
)
{
UINTN Pages;
EFI_PHYSICAL_ADDRESS Address;
EFI_STATUS Status;
VOID* Buffer;
Pages = EFI_SIZE_TO_PAGES (Size);
Address = 0xffffffff;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIMemoryNVS,
Pages,
&Address
);
ASSERT_EFI_ERROR (Status);
Buffer = (VOID *) (UINTN) Address;
ZeroMem (Buffer, Size);
return Buffer;
}
/**
DxeSmmReadyToLock Protocol notification event handler.
We reuse S3 ACPI NVS reserved memory to do capsule process
after reset.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
DxeSmmReadyToLockNotification (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
VOID *DxeSmmReadyToLock;
UINTN VarSize;
EFI_PHYSICAL_ADDRESS TempAcpiS3Context;
ACPI_S3_CONTEXT *AcpiS3Context;
EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
UINTN TotalPagesNum;
UINT8 PhysicalAddressBits;
VOID *Hob;
UINT32 NumberOfPml4EntriesNeeded;
UINT32 NumberOfPdpEntriesNeeded;
BOOLEAN LockBoxFound;
Status = gBS->LocateProtocol (
&gEfiDxeSmmReadyToLockProtocolGuid,
NULL,
&DxeSmmReadyToLock
);
if (EFI_ERROR (Status)) {
return ;
}
//
// Get the ACPI NVS pages reserved by AcpiS3Save
//
LockBoxFound = FALSE;
VarSize = sizeof (EFI_PHYSICAL_ADDRESS);
Status = RestoreLockBox (
&gEfiAcpiVariableGuid,
&TempAcpiS3Context,
&VarSize
);
if (!EFI_ERROR (Status)) {
AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context;
ASSERT (AcpiS3Context != NULL);
Status = RestoreLockBox (
&gEfiAcpiS3ContextGuid,
NULL,
NULL
);
if (!EFI_ERROR (Status)) {
LongModeBuffer.PageTableAddress = AcpiS3Context->S3NvsPageTableAddress;
LongModeBuffer.StackBaseAddress = AcpiS3Context->BootScriptStackBase;
LongModeBuffer.StackSize = AcpiS3Context->BootScriptStackSize;
LockBoxFound = TRUE;
}
}
if (!LockBoxFound) {
//
// Page table base address and stack base address can not be found in lock box,
// allocate both here.
//
//
// Get physical address bits supported from CPU HOB.
//
PhysicalAddressBits = 36;
Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
if (Hob != NULL) {
PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
}
//
// IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
//
ASSERT (PhysicalAddressBits <= 52);
if (PhysicalAddressBits > 48) {
PhysicalAddressBits = 48;
}
//
// Calculate page table size and allocate memory for it.
//
if (PhysicalAddressBits <= 39 ) {
NumberOfPml4EntriesNeeded = 1;
NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30);
} else {
NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);
NumberOfPdpEntriesNeeded = 512;
}
TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));
ASSERT (LongModeBuffer.PageTableAddress != 0);
//
// Allocate stack
//
LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize);
LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));
ASSERT (LongModeBuffer.StackBaseAddress != 0);
}
Status = gRT->SetVariable (
EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
&gEfiCapsuleVendorGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),
&LongModeBuffer
);
ASSERT_EFI_ERROR (Status);
//
// Close event, so it will not be invoked again.
//
gBS->CloseEvent (Event);
return ;
}
/**
Create the variable to save the base address of page table and stack
for transferring into long mode in IA32 capsule PEI.
**/
VOID
SaveLongModeContext (
VOID
)
{
VOID *Registration;
if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
//
// Register event to get ACPI NVS pages reserved from lock box, these pages will be used by
// Capsule IA32 PEI to transfer to long mode to access capsule above 4GB.
//
EfiCreateProtocolNotifyEvent (
&gEfiDxeSmmReadyToLockProtocolGuid,
TPL_CALLBACK,
DxeSmmReadyToLockNotification,
NULL,
&Registration
);
}
}