MdeModulePkg/NvmExpressPei: Consume S3StorageDeviceInitList LockBox

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1409

For the NvmExpressPei driver, this commit will update the driver to
consume the S3StorageDeviceInitList LockBox in S3 phase. The purpose is to
perform an on-demand (partial) NVM Express device
enumeration/initialization to benefit the S3 resume performance.

Cc: Jian J Wang <jian.j.wang@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
This commit is contained in:
Hao Wu 2019-01-21 14:14:19 +08:00
parent 2e15b750c4
commit 05fd2a9268
5 changed files with 236 additions and 1 deletions

View File

@ -88,6 +88,59 @@ NextDevicePathNode (
return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) + DevicePathNodeLength(Node));
}
/**
Get the size of the current device path instance.
@param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL
structure.
@param[out] InstanceSize The size of the current device path instance.
@param[out] EntireDevicePathEnd Indicate whether the instance is the last
one in the device path strucure.
@retval EFI_SUCCESS The size of the current device path instance is fetched.
@retval Others Fails to get the size of the current device path instance.
**/
EFI_STATUS
GetDevicePathInstanceSize (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT UINTN *InstanceSize,
OUT BOOLEAN *EntireDevicePathEnd
)
{
EFI_DEVICE_PATH_PROTOCOL *Walker;
if (DevicePath == NULL || InstanceSize == NULL || EntireDevicePathEnd == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Find the end of the device path instance
//
Walker = DevicePath;
while (Walker->Type != END_DEVICE_PATH_TYPE) {
Walker = NextDevicePathNode (Walker);
}
//
// Check if 'Walker' points to the end of an entire device path
//
if (Walker->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
*EntireDevicePathEnd = TRUE;
} else if (Walker->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {
*EntireDevicePathEnd = FALSE;
} else {
return EFI_INVALID_PARAMETER;
}
//
// Compute the size of the device path instance
//
*InstanceSize = ((UINTN) Walker - (UINTN) (DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
return EFI_SUCCESS;
}
/**
Check the validity of the device path of a NVM Express host controller.

View File

@ -213,6 +213,7 @@ NvmExpressPeimEntry (
)
{
EFI_STATUS Status;
EFI_BOOT_MODE BootMode;
EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;
UINT8 Controller;
UINTN MmioBase;
@ -223,6 +224,15 @@ NvmExpressPeimEntry (
DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
//
// Get the current boot mode.
//
Status = PeiServicesGetBootMode (&BootMode);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
return Status;
}
//
// Locate the NVME host controller PPI
//
@ -279,6 +289,22 @@ NvmExpressPeimEntry (
continue;
}
//
// For S3 resume performance consideration, not all NVM Express controllers
// will be initialized. The driver consumes the content within
// S3StorageDeviceInitList LockBox to see if a controller will be skipped
// during S3 resume.
//
if ((BootMode == BOOT_ON_S3_RESUME) &&
(NvmeS3SkipThisController (DevicePath, DevicePathLength))) {
DEBUG ((
DEBUG_ERROR, "%a: Controller %d is skipped during S3.\n",
__FUNCTION__, Controller
));
Controller++;
continue;
}
//
// Memory allocation for controller private data
//

View File

@ -266,6 +266,26 @@ NvmePeimEndOfPei (
IN VOID *Ppi
);
/**
Get the size of the current device path instance.
@param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL
structure.
@param[out] InstanceSize The size of the current device path instance.
@param[out] EntireDevicePathEnd Indicate whether the instance is the last
one in the device path strucure.
@retval EFI_SUCCESS The size of the current device path instance is fetched.
@retval Others Fails to get the size of the current device path instance.
**/
EFI_STATUS
GetDevicePathInstanceSize (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT UINTN *InstanceSize,
OUT BOOLEAN *EntireDevicePathEnd
);
/**
Check the validity of the device path of a NVM Express host controller.
@ -309,4 +329,20 @@ NvmeBuildDevicePath (
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
);
/**
Determine if a specific NVM Express controller can be skipped for S3 phase.
@param[in] HcDevicePath Device path of the controller.
@param[in] HcDevicePathLength Length of the device path specified by
HcDevicePath.
@retval The number of ports that need to be enumerated.
**/
BOOLEAN
NvmeS3SkipThisController (
IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,
IN UINTN HcDevicePathLength
);
#endif

View File

@ -40,6 +40,7 @@
NvmExpressPeiHci.h
NvmExpressPeiPassThru.c
NvmExpressPeiPassThru.h
NvmExpressPeiS3.c
NvmExpressPeiStorageSecurity.c
NvmExpressPeiStorageSecurity.h
@ -54,6 +55,7 @@
BaseMemoryLib
IoLib
TimerLib
LockBoxLib
PeimEntryPoint
[Ppis]
@ -64,9 +66,13 @@
gEfiPeiVirtualBlockIo2PpiGuid ## SOMETIMES_PRODUCES
gEdkiiPeiStorageSecurityCommandPpiGuid ## SOMETIMES_PRODUCES
[Guids]
gS3StorageDeviceInitListGuid ## SOMETIMES_CONSUMES ## UNDEFINED
[Depex]
gEfiPeiMemoryDiscoveredPpiGuid AND
gEdkiiPeiNvmExpressHostControllerPpiGuid
gEdkiiPeiNvmExpressHostControllerPpiGuid AND
gEfiPeiMasterBootModePpiGuid
[UserExtensions.TianoCore."ExtraFiles"]
NvmExpressPeiExtra.uni

View File

@ -0,0 +1,114 @@
/** @file
The NvmExpressPei driver is used to manage non-volatile memory subsystem
which follows NVM Express specification at PEI phase.
Copyright (c) 2019, 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 "NvmExpressPei.h"
#include <Guid/S3StorageDeviceInitList.h>
#include <Library/LockBoxLib.h>
/**
Determine if a specific NVM Express controller can be skipped for S3 phase.
@param[in] HcDevicePath Device path of the controller.
@param[in] HcDevicePathLength Length of the device path specified by
HcDevicePath.
@retval The number of ports that need to be enumerated.
**/
BOOLEAN
NvmeS3SkipThisController (
IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,
IN UINTN HcDevicePathLength
)
{
EFI_STATUS Status;
UINT8 DummyData;
UINTN S3InitDevicesLength;
EFI_DEVICE_PATH_PROTOCOL *S3InitDevices;
EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
UINTN DevicePathInstLength;
BOOLEAN EntireEnd;
BOOLEAN Skip;
//
// From the LockBox, get the list of device paths for devices need to be
// initialized in S3.
//
S3InitDevices = NULL;
S3InitDevicesLength = sizeof (DummyData);
EntireEnd = FALSE;
Skip = TRUE;
Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, &DummyData, &S3InitDevicesLength);
if (Status != EFI_BUFFER_TOO_SMALL) {
return Skip;
} else {
S3InitDevices = AllocatePool (S3InitDevicesLength);
if (S3InitDevices == NULL) {
return Skip;
}
Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, S3InitDevices, &S3InitDevicesLength);
if (EFI_ERROR (Status)) {
return Skip;
}
}
if (S3InitDevices == NULL) {
return Skip;
}
//
// Only need to initialize the controllers that exist in the device list.
//
do {
//
// Fetch the size of current device path instance.
//
Status = GetDevicePathInstanceSize (
S3InitDevices,
&DevicePathInstLength,
&EntireEnd
);
if (EFI_ERROR (Status)) {
break;
}
DevicePathInst = S3InitDevices;
S3InitDevices = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN) S3InitDevices + DevicePathInstLength);
if (HcDevicePathLength >= DevicePathInstLength) {
continue;
}
//
// Compare the device paths to determine if the device is managed by this
// controller.
//
if (CompareMem (
DevicePathInst,
HcDevicePath,
HcDevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
) == 0) {
Skip = FALSE;
break;
}
} while (!EntireEnd);
return Skip;
}