+/**\r
+ This routine is called to properly shutdown the Nvm Express controller per NVMe spec.\r
+\r
+ @param[in] ResetType The type of reset to perform.\r
+ @param[in] ResetStatus The status code for the reset.\r
+ @param[in] DataSize The size, in bytes, of ResetData.\r
+ @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or\r
+ EfiResetShutdown the data buffer starts with a Null-terminated\r
+ string, optionally followed by additional binary data.\r
+ The string is a description that the caller may use to further\r
+ indicate the reason for the system reset. ResetData is only\r
+ valid if ResetStatus is something other than EFI_SUCCESS\r
+ unless the ResetType is EfiResetPlatformSpecific\r
+ where a minimum amount of ResetData is always required.\r
+ For a ResetType of EfiResetPlatformSpecific the data buffer\r
+ also starts with a Null-terminated string that is followed\r
+ by an EFI_GUID that describes the specific type of reset to perform.\r
+**/\r
+VOID\r
+EFIAPI\r
+NvmeShutdownAllControllers (\r
+ IN EFI_RESET_TYPE ResetType,\r
+ IN EFI_STATUS ResetStatus,\r
+ IN UINTN DataSize,\r
+ IN VOID *ResetData OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *Handles;\r
+ UINTN HandleCount;\r
+ UINTN HandleIndex;\r
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfos;\r
+ UINTN OpenInfoCount;\r
+ UINTN OpenInfoIndex;\r
+ EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassThru;\r
+ NVME_CC Cc;\r
+ NVME_CSTS Csts;\r
+ UINTN Index;\r
+ NVME_CONTROLLER_PRIVATE_DATA *Private;\r
+\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiPciIoProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ HandleCount = 0;\r
+ }\r
+\r
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {\r
+ Status = gBS->OpenProtocolInformation (\r
+ Handles[HandleIndex],\r
+ &gEfiPciIoProtocolGuid,\r
+ &OpenInfos,\r
+ &OpenInfoCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ for (OpenInfoIndex = 0; OpenInfoIndex < OpenInfoCount; OpenInfoIndex++) {\r
+ //\r
+ // Find all the NVME controller managed by this driver.\r
+ // gImageHandle equals to DriverBinding handle for this driver.\r
+ //\r
+ if (((OpenInfos[OpenInfoIndex].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) &&\r
+ (OpenInfos[OpenInfoIndex].AgentHandle == gImageHandle)) {\r
+ Status = gBS->OpenProtocol (\r
+ OpenInfos[OpenInfoIndex].ControllerHandle,\r
+ &gEfiNvmExpressPassThruProtocolGuid,\r
+ (VOID **) &NvmePassThru,\r
+ NULL,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+ Private = NVME_CONTROLLER_PRIVATE_DATA_FROM_PASS_THRU (NvmePassThru);\r
+\r
+ //\r
+ // Read Controller Configuration Register.\r
+ //\r
+ Status = ReadNvmeControllerConfiguration (Private, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ continue;\r
+ }\r
+ //\r
+ // The host should set the Shutdown Notification (CC.SHN) field to 01b\r
+ // to indicate a normal shutdown operation.\r
+ //\r
+ Cc.Shn = NVME_CC_SHN_NORMAL_SHUTDOWN;\r
+ Status = WriteNvmeControllerConfiguration (Private, &Cc);\r
+ if (EFI_ERROR(Status)) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // The controller indicates when shutdown processing is completed by updating the\r
+ // Shutdown Status (CSTS.SHST) field to 10b.\r
+ // Wait up to 45 seconds (break down to 4500 x 10ms) for the shutdown to complete.\r
+ //\r
+ for (Index = 0; Index < NVME_SHUTDOWN_PROCESS_TIMEOUT * 100; Index++) {\r
+ Status = ReadNvmeControllerStatus (Private, &Csts);\r
+ if (!EFI_ERROR(Status) && (Csts.Shst == NVME_CSTS_SHST_SHUTDOWN_COMPLETED)) {\r
+ DEBUG((DEBUG_INFO, "NvmeShutdownController: shutdown processing is completed after %dms.\n", Index * 10));\r
+ break;\r
+ }\r
+ //\r
+ // Stall for 10ms\r
+ //\r
+ gBS->Stall (10 * 1000);\r
+ }\r
+\r
+ if (Index == NVME_SHUTDOWN_PROCESS_TIMEOUT * 100) {\r
+ DEBUG((DEBUG_ERROR, "NvmeShutdownController: shutdown processing is timed out\n"));\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Register the shutdown notification through the ResetNotification protocol.\r
+\r
+ Register the shutdown notification when mNvmeControllerNumber increased from 0 to 1.\r
+**/\r
+VOID\r
+NvmeRegisterShutdownNotification (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_RESET_NOTIFICATION_PROTOCOL *ResetNotify;\r
+\r
+ mNvmeControllerNumber++;\r
+ if (mNvmeControllerNumber == 1) {\r
+ Status = gBS->LocateProtocol (&gEfiResetNotificationProtocolGuid, NULL, (VOID **) &ResetNotify);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = ResetNotify->RegisterResetNotify (ResetNotify, NvmeShutdownAllControllers);\r
+ ASSERT_EFI_ERROR (Status);\r
+ } else {\r
+ DEBUG ((DEBUG_WARN, "NVME: ResetNotification absent! Shutdown notification cannot be performed!\n"));\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Unregister the shutdown notification through the ResetNotification protocol.\r
+\r
+ Unregister the shutdown notification when mNvmeControllerNumber decreased from 1 to 0.\r
+**/\r
+VOID\r
+NvmeUnregisterShutdownNotification (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_RESET_NOTIFICATION_PROTOCOL *ResetNotify;\r
+\r
+ mNvmeControllerNumber--;\r
+ if (mNvmeControllerNumber == 0) {\r
+ Status = gBS->LocateProtocol (&gEfiResetNotificationProtocolGuid, NULL, (VOID **) &ResetNotify);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = ResetNotify->UnregisterResetNotify (ResetNotify, NvmeShutdownAllControllers);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ }\r
+}\r