]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/HddPassword/HddPasswordPei.c
SecurityPkg/HddPassword: Add Security feature set support for ATA dev
[mirror_edk2.git] / SecurityPkg / HddPassword / HddPasswordPei.c
diff --git a/SecurityPkg/HddPassword/HddPasswordPei.c b/SecurityPkg/HddPassword/HddPasswordPei.c
new file mode 100644 (file)
index 0000000..1ea63b8
--- /dev/null
@@ -0,0 +1,374 @@
+/** @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