--- /dev/null
+/** @file\r
+ HddPassword PEI module which is used to unlock HDD password for S3.\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 "HddPasswordPei.h"\r
+\r
+EFI_GUID mHddPasswordDeviceInfoGuid = HDD_PASSWORD_DEVICE_INFO_GUID;\r
+\r
+\r
+/**\r
+ Send unlock hdd password cmd through ATA PassThru PPI.\r
+\r
+ @param[in] AtaPassThru The pointer to the ATA PassThru PPI.\r
+ @param[in] Port The port number of the ATA device.\r
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA device.\r
+ @param[in] Identifier The identifier to set user or master password.\r
+ @param[in] Password The hdd password of attached ATA device.\r
+\r
+ @retval EFI_SUCCESS Successful to send unlock hdd password cmd.\r
+ @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.\r
+ @retval EFI_DEVICE_ERROR Can not send unlock hdd password cmd.\r
+\r
+**/\r
+EFI_STATUS\r
+UnlockDevice (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort,\r
+ IN CHAR8 Identifier,\r
+ IN CHAR8 *Password\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ATA_COMMAND_BLOCK Acb;\r
+ EFI_ATA_STATUS_BLOCK *Asb;\r
+ EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;\r
+ UINT8 Buffer[HDD_PAYLOAD];\r
+\r
+ if ((AtaPassThru == NULL) || (Password == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in\r
+ // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by\r
+ // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,\r
+ // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it\r
+ // may not be aligned when allocated on stack for some compilers. Hence, we\r
+ // use the API AllocateAlignedPages to ensure this structure is properly\r
+ // aligned.\r
+ //\r
+ Asb = AllocateAlignedPages (\r
+ EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),\r
+ AtaPassThru->Mode->IoAlign\r
+ );\r
+ if (Asb == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Prepare for ATA command block.\r
+ //\r
+ ZeroMem (&Acb, sizeof (Acb));\r
+ ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));\r
+ Acb.AtaCommand = ATA_SECURITY_UNLOCK_CMD;\r
+ Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));\r
+\r
+ //\r
+ // Prepare for ATA pass through packet.\r
+ //\r
+ ZeroMem (&Packet, sizeof (Packet));\r
+ Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT;\r
+ Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;\r
+ Packet.Asb = Asb;\r
+ Packet.Acb = &Acb;\r
+\r
+ ((CHAR16 *) Buffer)[0] = Identifier & BIT0;\r
+ CopyMem (&((CHAR16 *) Buffer)[1], Password, HDD_PASSWORD_MAX_LENGTH);\r
+\r
+ Packet.OutDataBuffer = Buffer;\r
+ Packet.OutTransferLength = sizeof (Buffer);\r
+ Packet.Timeout = ATA_TIMEOUT;\r
+\r
+ Status = AtaPassThru->PassThru (\r
+ AtaPassThru,\r
+ Port,\r
+ PortMultiplierPort,\r
+ &Packet\r
+ );\r
+ if (!EFI_ERROR (Status) &&\r
+ ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&\r
+ ((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));\r
+\r
+ ZeroMem (Buffer, sizeof (Buffer));\r
+\r
+ DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Send security freeze lock cmd through ATA PassThru PPI.\r
+\r
+ @param[in] AtaPassThru The pointer to the ATA PassThru PPI.\r
+ @param[in] Port The port number of the ATA device.\r
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA device.\r
+\r
+ @retval EFI_SUCCESS Successful to send security freeze lock cmd.\r
+ @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.\r
+ @retval EFI_DEVICE_ERROR Can not send security freeze lock cmd.\r
+\r
+**/\r
+EFI_STATUS\r
+FreezeLockDevice (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ATA_COMMAND_BLOCK Acb;\r
+ EFI_ATA_STATUS_BLOCK *Asb;\r
+ EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;\r
+\r
+ if (AtaPassThru == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in\r
+ // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by\r
+ // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,\r
+ // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it\r
+ // may not be aligned when allocated on stack for some compilers. Hence, we\r
+ // use the API AllocateAlignedPages to ensure this structure is properly\r
+ // aligned.\r
+ //\r
+ Asb = AllocateAlignedPages (\r
+ EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),\r
+ AtaPassThru->Mode->IoAlign\r
+ );\r
+ if (Asb == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Prepare for ATA command block.\r
+ //\r
+ ZeroMem (&Acb, sizeof (Acb));\r
+ ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));\r
+ Acb.AtaCommand = ATA_SECURITY_FREEZE_LOCK_CMD;\r
+ Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));\r
+\r
+ //\r
+ // Prepare for ATA pass through packet.\r
+ //\r
+ ZeroMem (&Packet, sizeof (Packet));\r
+ Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;\r
+ Packet.Length = EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER;\r
+ Packet.Asb = Asb;\r
+ Packet.Acb = &Acb;\r
+ Packet.Timeout = ATA_TIMEOUT;\r
+\r
+ Status = AtaPassThru->PassThru (\r
+ AtaPassThru,\r
+ Port,\r
+ PortMultiplierPort,\r
+ &Packet\r
+ );\r
+ if (!EFI_ERROR (Status) &&\r
+ ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&\r
+ ((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));\r
+\r
+ DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Unlock HDD password for S3.\r
+\r
+ @param[in] AtaPassThruPpi Pointer to the EDKII_PEI_ATA_PASS_THRU_PPI instance.\r
+\r
+**/\r
+VOID\r
+UnlockHddPassword (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThruPpi\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *Buffer;\r
+ UINTN Length;\r
+ UINT8 DummyData;\r
+ HDD_PASSWORD_DEVICE_INFO *DevInfo;\r
+ UINT16 Port;\r
+ UINT16 PortMultiplierPort;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ UINTN DevicePathLength;\r
+\r
+ //\r
+ // Get HDD password device info from LockBox.\r
+ //\r
+ Buffer = (VOID *) &DummyData;\r
+ Length = sizeof (DummyData);\r
+ Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Length));\r
+ if (Buffer != NULL) {\r
+ Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);\r
+ }\r
+ }\r
+ if ((Buffer == NULL) || (Buffer == (VOID *) &DummyData)) {\r
+ return;\r
+ } else if (EFI_ERROR (Status)) {\r
+ FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));\r
+ return;\r
+ }\r
+\r
+ Status = AtaPassThruPpi->GetDevicePath (AtaPassThruPpi, &DevicePathLength, &DevicePath);\r
+ if (EFI_ERROR (Status) || (DevicePathLength <= sizeof (EFI_DEVICE_PATH_PROTOCOL))) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Go through all the devices managed by the AtaPassThru PPI instance.\r
+ //\r
+ Port = 0xFFFF;\r
+ while (TRUE) {\r
+ Status = AtaPassThruPpi->GetNextPort (AtaPassThruPpi, &Port);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // We cannot find more legal port then we are done.\r
+ //\r
+ break;\r
+ }\r
+\r
+ PortMultiplierPort = 0xFFFF;\r
+ while (TRUE) {\r
+ Status = AtaPassThruPpi->GetNextDevice (AtaPassThruPpi, Port, &PortMultiplierPort);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // We cannot find more legal port multiplier port number for ATA device\r
+ // on the port, then we are done.\r
+ //\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Search the device in the restored LockBox.\r
+ //\r
+ DevInfo = (HDD_PASSWORD_DEVICE_INFO *) Buffer;\r
+ while ((UINTN) DevInfo < ((UINTN) Buffer + Length)) {\r
+ //\r
+ // Find the matching device.\r
+ //\r
+ if ((DevInfo->Device.Port == Port) &&\r
+ (DevInfo->Device.PortMultiplierPort == PortMultiplierPort) &&\r
+ (DevInfo->DevicePathLength >= DevicePathLength) &&\r
+ (CompareMem (\r
+ DevInfo->DevicePath,\r
+ DevicePath,\r
+ DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) == 0)) {\r
+ //\r
+ // If device locked, unlock first.\r
+ //\r
+ if (!IsZeroBuffer (DevInfo->Password, HDD_PASSWORD_MAX_LENGTH)) {\r
+ UnlockDevice (AtaPassThruPpi, Port, PortMultiplierPort, 0, DevInfo->Password);\r
+ }\r
+ //\r
+ // Freeze lock the device.\r
+ //\r
+ FreezeLockDevice (AtaPassThruPpi, Port, PortMultiplierPort);\r
+ break;\r
+ }\r
+\r
+ DevInfo = (HDD_PASSWORD_DEVICE_INFO *)\r
+ ((UINTN) DevInfo + sizeof (HDD_PASSWORD_DEVICE_INFO) + DevInfo->DevicePathLength);\r
+ }\r
+ }\r
+ }\r
+\r
+Exit:\r
+ ZeroMem (Buffer, Length);\r
+ FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));\r
+\r
+}\r
+\r
+/**\r
+ Entry point of the notification callback function itself within the PEIM.\r
+ It is to unlock HDD password for S3.\r
+\r
+ @param PeiServices Indirect reference to the PEI Services Table.\r
+ @param NotifyDescriptor Address of the notification descriptor data structure.\r
+ @param Ppi Address of the PPI that was installed.\r
+\r
+ @return Status of the notification.\r
+ The status code returned from this function is ignored.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HddPasswordAtaPassThruNotify (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,\r
+ IN VOID *Ppi\r
+ )\r
+{\r
+ DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__));\r
+\r
+ UnlockHddPassword ((EDKII_PEI_ATA_PASS_THRU_PPI *) Ppi);\r
+\r
+ DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+EFI_PEI_NOTIFY_DESCRIPTOR mHddPasswordAtaPassThruPpiNotifyDesc = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEdkiiPeiAtaPassThruPpiGuid,\r
+ HddPasswordAtaPassThruNotify\r
+};\r
+\r
+\r
+/**\r
+ Main entry for this module.\r
+\r
+ @param FileHandle Handle of the file being invoked.\r
+ @param PeiServices Pointer to PEI Services table.\r
+\r
+ @return Status from PeiServicesNotifyPpi.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HddPasswordPeiInit (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MODE BootMode;\r
+\r
+ Status = PeiServicesGetBootMode (&BootMode);\r
+ if ((EFI_ERROR (Status)) || (BootMode != BOOT_ON_S3_RESUME)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "%a: Enters in S3 path.\n", __FUNCTION__));\r
+\r
+ Status = PeiServicesNotifyPpi (&mHddPasswordAtaPassThruPpiNotifyDesc);\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r
+\r