return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) + DevicePathNodeLength(Node));\r
}\r
\r
+/**\r
+ Get the size of the current device path instance.\r
+\r
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+ structure.\r
+ @param[out] InstanceSize The size of the current device path instance.\r
+ @param[out] EntireDevicePathEnd Indicate whether the instance is the last\r
+ one in the device path strucure.\r
+\r
+ @retval EFI_SUCCESS The size of the current device path instance is fetched.\r
+ @retval Others Fails to get the size of the current device path instance.\r
+\r
+**/\r
+EFI_STATUS\r
+GetDevicePathInstanceSize (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ OUT UINTN *InstanceSize,\r
+ OUT BOOLEAN *EntireDevicePathEnd\r
+ )\r
+{\r
+ EFI_DEVICE_PATH_PROTOCOL *Walker;\r
+\r
+ if (DevicePath == NULL || InstanceSize == NULL || EntireDevicePathEnd == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Find the end of the device path instance\r
+ //\r
+ Walker = DevicePath;\r
+ while (Walker->Type != END_DEVICE_PATH_TYPE) {\r
+ Walker = NextDevicePathNode (Walker);\r
+ }\r
+\r
+ //\r
+ // Check if 'Walker' points to the end of an entire device path\r
+ //\r
+ if (Walker->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {\r
+ *EntireDevicePathEnd = TRUE;\r
+ } else if (Walker->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {\r
+ *EntireDevicePathEnd = FALSE;\r
+ } else {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Compute the size of the device path instance\r
+ //\r
+ *InstanceSize = ((UINTN) Walker - (UINTN) (DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
/**\r
Check the validity of the device path of a NVM Express host controller.\r
\r
)\r
{\r
EFI_STATUS Status;\r
+ EFI_BOOT_MODE BootMode;\r
EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;\r
UINT8 Controller;\r
UINTN MmioBase;\r
\r
DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));\r
\r
+ //\r
+ // Get the current boot mode.\r
+ //\r
+ Status = PeiServicesGetBootMode (&BootMode);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));\r
+ return Status;\r
+ }\r
+\r
//\r
// Locate the NVME host controller PPI\r
//\r
continue;\r
}\r
\r
+ //\r
+ // For S3 resume performance consideration, not all NVM Express controllers\r
+ // will be initialized. The driver consumes the content within\r
+ // S3StorageDeviceInitList LockBox to see if a controller will be skipped\r
+ // during S3 resume.\r
+ //\r
+ if ((BootMode == BOOT_ON_S3_RESUME) &&\r
+ (NvmeS3SkipThisController (DevicePath, DevicePathLength))) {\r
+ DEBUG ((\r
+ DEBUG_ERROR, "%a: Controller %d is skipped during S3.\n",\r
+ __FUNCTION__, Controller\r
+ ));\r
+ Controller++;\r
+ continue;\r
+ }\r
+\r
//\r
// Memory allocation for controller private data\r
//\r
IN VOID *Ppi\r
);\r
\r
+/**\r
+ Get the size of the current device path instance.\r
+\r
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+ structure.\r
+ @param[out] InstanceSize The size of the current device path instance.\r
+ @param[out] EntireDevicePathEnd Indicate whether the instance is the last\r
+ one in the device path strucure.\r
+\r
+ @retval EFI_SUCCESS The size of the current device path instance is fetched.\r
+ @retval Others Fails to get the size of the current device path instance.\r
+\r
+**/\r
+EFI_STATUS\r
+GetDevicePathInstanceSize (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ OUT UINTN *InstanceSize,\r
+ OUT BOOLEAN *EntireDevicePathEnd\r
+ );\r
+\r
/**\r
Check the validity of the device path of a NVM Express host controller.\r
\r
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
);\r
\r
+/**\r
+ Determine if a specific NVM Express controller can be skipped for S3 phase.\r
+\r
+ @param[in] HcDevicePath Device path of the controller.\r
+ @param[in] HcDevicePathLength Length of the device path specified by\r
+ HcDevicePath.\r
+\r
+ @retval The number of ports that need to be enumerated.\r
+\r
+**/\r
+BOOLEAN\r
+NvmeS3SkipThisController (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,\r
+ IN UINTN HcDevicePathLength\r
+ );\r
+\r
#endif\r
NvmExpressPeiHci.h\r
NvmExpressPeiPassThru.c\r
NvmExpressPeiPassThru.h\r
+ NvmExpressPeiS3.c\r
NvmExpressPeiStorageSecurity.c\r
NvmExpressPeiStorageSecurity.h\r
\r
BaseMemoryLib\r
IoLib\r
TimerLib\r
+ LockBoxLib\r
PeimEntryPoint\r
\r
[Ppis]\r
gEfiPeiVirtualBlockIo2PpiGuid ## SOMETIMES_PRODUCES\r
gEdkiiPeiStorageSecurityCommandPpiGuid ## SOMETIMES_PRODUCES\r
\r
+[Guids]\r
+ gS3StorageDeviceInitListGuid ## SOMETIMES_CONSUMES ## UNDEFINED\r
+\r
[Depex]\r
gEfiPeiMemoryDiscoveredPpiGuid AND\r
- gEdkiiPeiNvmExpressHostControllerPpiGuid\r
+ gEdkiiPeiNvmExpressHostControllerPpiGuid AND\r
+ gEfiPeiMasterBootModePpiGuid\r
\r
[UserExtensions.TianoCore."ExtraFiles"]\r
NvmExpressPeiExtra.uni\r
--- /dev/null
+/** @file\r
+ The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
+ which follows NVM Express specification at PEI phase.\r
+\r
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions\r
+ of the BSD License which accompanies this distribution. The\r
+ full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "NvmExpressPei.h"\r
+\r
+#include <Guid/S3StorageDeviceInitList.h>\r
+\r
+#include <Library/LockBoxLib.h>\r
+\r
+/**\r
+ Determine if a specific NVM Express controller can be skipped for S3 phase.\r
+\r
+ @param[in] HcDevicePath Device path of the controller.\r
+ @param[in] HcDevicePathLength Length of the device path specified by\r
+ HcDevicePath.\r
+\r
+ @retval The number of ports that need to be enumerated.\r
+\r
+**/\r
+BOOLEAN\r
+NvmeS3SkipThisController (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,\r
+ IN UINTN HcDevicePathLength\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 DummyData;\r
+ UINTN S3InitDevicesLength;\r
+ EFI_DEVICE_PATH_PROTOCOL *S3InitDevices;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;\r
+ UINTN DevicePathInstLength;\r
+ BOOLEAN EntireEnd;\r
+ BOOLEAN Skip;\r
+\r
+ //\r
+ // From the LockBox, get the list of device paths for devices need to be\r
+ // initialized in S3.\r
+ //\r
+ S3InitDevices = NULL;\r
+ S3InitDevicesLength = sizeof (DummyData);\r
+ EntireEnd = FALSE;\r
+ Skip = TRUE;\r
+ Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, &DummyData, &S3InitDevicesLength);\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return Skip;\r
+ } else {\r
+ S3InitDevices = AllocatePool (S3InitDevicesLength);\r
+ if (S3InitDevices == NULL) {\r
+ return Skip;\r
+ }\r
+\r
+ Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, S3InitDevices, &S3InitDevicesLength);\r
+ if (EFI_ERROR (Status)) {\r
+ return Skip;\r
+ }\r
+ }\r
+\r
+ if (S3InitDevices == NULL) {\r
+ return Skip;\r
+ }\r
+\r
+ //\r
+ // Only need to initialize the controllers that exist in the device list.\r
+ //\r
+ do {\r
+ //\r
+ // Fetch the size of current device path instance.\r
+ //\r
+ Status = GetDevicePathInstanceSize (\r
+ S3InitDevices,\r
+ &DevicePathInstLength,\r
+ &EntireEnd\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ DevicePathInst = S3InitDevices;\r
+ S3InitDevices = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN) S3InitDevices + DevicePathInstLength);\r
+\r
+ if (HcDevicePathLength >= DevicePathInstLength) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Compare the device paths to determine if the device is managed by this\r
+ // controller.\r
+ //\r
+ if (CompareMem (\r
+ DevicePathInst,\r
+ HcDevicePath,\r
+ HcDevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)\r
+ ) == 0) {\r
+ Skip = FALSE;\r
+ break;\r
+ }\r
+ } while (!EntireEnd);\r
+\r
+ return Skip;\r
+}\r