From 05fd2a9268332d770dd082be4af076ba0217cf54 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Mon, 21 Jan 2019 14:14:19 +0800 Subject: [PATCH] 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 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Hao Wu Reviewed-by: Eric Dong Reviewed-by: Ray Ni --- .../Bus/Pci/NvmExpressPei/DevicePath.c | 53 ++++++++ .../Bus/Pci/NvmExpressPei/NvmExpressPei.c | 26 ++++ .../Bus/Pci/NvmExpressPei/NvmExpressPei.h | 36 ++++++ .../Bus/Pci/NvmExpressPei/NvmExpressPei.inf | 8 +- .../Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c | 114 ++++++++++++++++++ 5 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c index fd30ad519f..ff95f20329 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c @@ -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. diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c index bf33b4b9a0..2dbf54fd3f 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c @@ -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 // diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h index 89666160a1..74e2daf511 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h @@ -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 diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf index 0666e5892b..22b703e971 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf @@ -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 diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c new file mode 100644 index 0000000000..eb3af75b89 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiS3.c @@ -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.
+ + 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 + +#include + +/** + 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; +}