]> git.proxmox.com Git - mirror_edk2.git/commitdiff
SecurityPkg/HddPassword: Add Security feature set support for ATA dev
authorHao Wu <hao.a.wu@intel.com>
Tue, 15 Jan 2019 08:33:09 +0000 (16:33 +0800)
committerHao Wu <hao.a.wu@intel.com>
Fri, 22 Feb 2019 00:20:08 +0000 (08:20 +0800)
REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1529

This commit will add the 'Security feature set' support for ATA devices.

According to the AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)
specification, the Security feature set is an optional feature. In
summary, the feature is a password system that restricts access to user
data stored on an ATA device. A more detailed introduction of this feature
can be referred from the ATA8-ACS spec.

The HddPassword driver is composed of 2 parts:
* A DXE driver and
* A PEI driver

The DXE driver consumes EFI_ATA_PASS_THRU_PROTOCOL instances and installs
an HII GUI to manage the devices. If the managing device supports Security
feature set, the HII page will provide the user with the ability to
set/update/disable the password for this device. Also, if a password is
being set via the Security feature set, a popup window will show during
boot requesting the user to input password.

Another feature supported by this driver is that for those managing
devices with password set, they will be automatically unlocked during the
S3 resume. This is done by the co-work of the DXE driver and the PEI
driver:

The DXE driver will save the password and the identification information
for these devices into a LockBox, which is only allowed to restore during
S3 resume.

The PEI driver, during S3 resume, will restore the content in the LockBox
and will consume EDKII_PEI_ATA_PASS_THRU_PPI instances to unlock devices.

Cc: Chao Zhang <chao.b.zhang@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
SecurityPkg/HddPassword/HddPassword.vfr [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordCommon.h [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordDxe.c [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordDxe.h [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordDxe.inf [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordHiiDataStruc.h [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordPei.c [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordPei.h [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordPei.inf [new file with mode: 0644]
SecurityPkg/HddPassword/HddPasswordStrings.uni [new file with mode: 0644]
SecurityPkg/SecurityPkg.dsc

diff --git a/SecurityPkg/HddPassword/HddPassword.vfr b/SecurityPkg/HddPassword/HddPassword.vfr
new file mode 100644 (file)
index 0000000..2cd3952
--- /dev/null
@@ -0,0 +1,188 @@
+/** @file\r
+  HDD Password Configuration Formset.\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
+#include "HddPasswordHiiDataStruc.h"\r
+\r
+formset\r
+  guid      = HDD_PASSWORD_CONFIG_GUID,\r
+  title     = STRING_TOKEN(STR_HDD_SECURITY_CONFIG),\r
+  help      = STRING_TOKEN(STR_HDD_SECURITY_CONFIG),\r
+  classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID,\r
+\r
+  varstore HDD_PASSWORD_CONFIG,\r
+  name = HDD_PASSWORD_CONFIG,\r
+  guid = HDD_PASSWORD_CONFIG_GUID;\r
+\r
+  form formid = FORMID_HDD_MAIN_FORM,\r
+    title  = STRING_TOKEN(STR_HDD_SECURITY_CONFIG);\r
+\r
+    label HDD_DEVICE_ENTRY_LABEL;\r
+    label HDD_DEVICE_LABEL_END;\r
+\r
+  endform;\r
+\r
+  form\r
+    formid = FORMID_HDD_DEVICE_FORM,\r
+    title = STRING_TOKEN(STR_HDD_SECURITY_HD);\r
+\r
+    subtitle text = STRING_TOKEN(STR_SECURITY_HDD_PWD_DESC);\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+        subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_ONE);\r
+        subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_TWO);\r
+        subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_THREE);\r
+        subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_FOUR);\r
+        subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_FIVE);\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    subtitle text = STRING_TOKEN(STR_HDD_PASSWORD_CONFIG);\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    grayoutif  TRUE;\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 0;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_SUPPORTED),\r
+            text   = STRING_TOKEN(STR_YES),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 1;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_SUPPORTED),\r
+            text   = STRING_TOKEN(STR_NO),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Enabled == 0;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_ENABLED),\r
+            text   = STRING_TOKEN(STR_YES),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Enabled == 1;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_ENABLED),\r
+            text   = STRING_TOKEN(STR_NO),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Locked == 0;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_LOCKED),\r
+            text   = STRING_TOKEN(STR_YES),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Locked == 1;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_LOCKED),\r
+            text   = STRING_TOKEN(STR_NO),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Frozen == 0;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_FROZEN),\r
+            text   = STRING_TOKEN(STR_YES),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Frozen == 1;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_SEC_FROZEN),\r
+            text   = STRING_TOKEN(STR_NO),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.UserPasswordStatus == 0;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_HDD_USER_PASSWORD_STS),\r
+            text   = STRING_TOKEN(STR_INSTALLED),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.UserPasswordStatus == 1;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_HDD_USER_PASSWORD_STS),\r
+            text   = STRING_TOKEN(STR_NOT_INSTALLED),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.MasterPasswordStatus == 0;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_HDD_MASTER_PASSWORD_STS),\r
+            text   = STRING_TOKEN(STR_INSTALLED),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+\r
+    suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.MasterPasswordStatus == 1;\r
+        text\r
+            help   = STRING_TOKEN(STR_EMPTY),\r
+            text   = STRING_TOKEN(STR_HDD_MASTER_PASSWORD_STS),\r
+            text   = STRING_TOKEN(STR_NOT_INSTALLED),\r
+            flags  = 0,\r
+            key    = 0;\r
+    endif;\r
+    endif;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    grayoutif ideqval HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 0;\r
+      checkbox varid  = HDD_PASSWORD_CONFIG.Request.UserPassword,\r
+            prompt      = STRING_TOKEN(STR_HDD_USER_PASSWORD),\r
+            help        = STRING_TOKEN(STR_HDD_USER_PASSWORD_HELP),\r
+            flags       = INTERACTIVE | RESET_REQUIRED,\r
+            key         = KEY_HDD_USER_PASSWORD,\r
+      endcheckbox;\r
+    endif;\r
+\r
+    grayoutif ideqval HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 0;\r
+      checkbox varid  = HDD_PASSWORD_CONFIG.Request.MasterPassword,\r
+            prompt      = STRING_TOKEN(STR_HDD_MASTER_PASSWORD),\r
+            help        = STRING_TOKEN(STR_HDD_MASTER_PASSWORD_HELP),\r
+            flags       = INTERACTIVE | RESET_REQUIRED,\r
+            key         = KEY_HDD_MASTER_PASSWORD,\r
+      endcheckbox;\r
+    endif;\r
+  endform;\r
+\r
+endformset;\r
diff --git a/SecurityPkg/HddPassword/HddPasswordCommon.h b/SecurityPkg/HddPassword/HddPasswordCommon.h
new file mode 100644 (file)
index 0000000..b904b7d
--- /dev/null
@@ -0,0 +1,61 @@
+/** @file\r
+  HDD Password common header file.\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
+#ifndef _HDD_PASSWORD_COMMON_H_\r
+#define _HDD_PASSWORD_COMMON_H_\r
+\r
+//\r
+// The payload length of HDD related ATA commands\r
+//\r
+#define HDD_PAYLOAD                     512\r
+\r
+#define ATA_SECURITY_SET_PASSWORD_CMD   0xF1\r
+#define ATA_SECURITY_UNLOCK_CMD         0xF2\r
+#define ATA_SECURITY_FREEZE_LOCK_CMD    0xF5\r
+#define ATA_SECURITY_DIS_PASSWORD_CMD   0xF6\r
+\r
+//\r
+// The max retry count specified in ATA 8 spec.\r
+//\r
+#define MAX_HDD_PASSWORD_RETRY_COUNT    5\r
+\r
+//\r
+// According to ATA spec, the max length of hdd password is 32 bytes\r
+//\r
+#define HDD_PASSWORD_MAX_LENGTH         32\r
+\r
+#define HDD_PASSWORD_DEVICE_INFO_GUID { 0x96d877ad, 0x48af, 0x4b39, { 0x9b, 0x27, 0x4d, 0x97, 0x43, 0x9, 0xae, 0x47 } }\r
+\r
+typedef struct {\r
+  UINT8             Bus;\r
+  UINT8             Device;\r
+  UINT8             Function;\r
+  UINT8             Reserved;\r
+  UINT16            Port;\r
+  UINT16            PortMultiplierPort;\r
+} HDD_PASSWORD_DEVICE;\r
+\r
+//\r
+// It will be used to unlock HDD password for S3.\r
+//\r
+typedef struct {\r
+  HDD_PASSWORD_DEVICE         Device;\r
+  CHAR8                       Password[HDD_PASSWORD_MAX_LENGTH];\r
+  UINT32                      DevicePathLength;\r
+  EFI_DEVICE_PATH_PROTOCOL    DevicePath[];\r
+} HDD_PASSWORD_DEVICE_INFO;\r
+\r
+#endif // _HDD_PASSWORD_COMMON_H_\r
diff --git a/SecurityPkg/HddPassword/HddPasswordDxe.c b/SecurityPkg/HddPassword/HddPasswordDxe.c
new file mode 100644 (file)
index 0000000..1247f85
--- /dev/null
@@ -0,0 +1,2814 @@
+/** @file\r
+  HDD password driver which is used to support HDD security feature.\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 "HddPasswordDxe.h"\r
+\r
+EFI_GUID   mHddPasswordVendorGuid          = HDD_PASSWORD_CONFIG_GUID;\r
+CHAR16     mHddPasswordVendorStorageName[] = L"HDD_PASSWORD_CONFIG";\r
+LIST_ENTRY mHddPasswordConfigFormList;\r
+UINT32     mNumberOfHddDevices = 0;\r
+\r
+EFI_GUID mHddPasswordDeviceInfoGuid = HDD_PASSWORD_DEVICE_INFO_GUID;\r
+BOOLEAN                         mHddPasswordEndOfDxe = FALSE;\r
+HDD_PASSWORD_REQUEST_VARIABLE   *mHddPasswordRequestVariable = NULL;\r
+UINTN                           mHddPasswordRequestVariableSize = 0;\r
+\r
+HII_VENDOR_DEVICE_PATH          mHddPasswordHiiVendorDevicePath = {\r
+  {\r
+    {\r
+      HARDWARE_DEVICE_PATH,\r
+      HW_VENDOR_DP,\r
+      {\r
+        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),\r
+        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)\r
+      }\r
+    },\r
+    HDD_PASSWORD_CONFIG_GUID\r
+  },\r
+  {\r
+    END_DEVICE_PATH_TYPE,\r
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    {\r
+      (UINT8) (END_DEVICE_PATH_LENGTH),\r
+      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)\r
+    }\r
+  }\r
+};\r
+\r
+\r
+/**\r
+  Check if the password is full zero.\r
+\r
+  @param[in]   Password       Points to the data buffer\r
+\r
+  @retval      TRUE           This password string is full zero.\r
+  @retval      FALSE          This password string is not full zero.\r
+\r
+**/\r
+BOOLEAN\r
+PasswordIsFullZero (\r
+  IN CHAR8                    *Password\r
+  )\r
+{\r
+  UINTN                       Index;\r
+\r
+  for (Index = 0; Index < HDD_PASSWORD_MAX_LENGTH; Index++) {\r
+    if (Password[Index] != 0) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Save device info.\r
+\r
+  @param[in]       ConfigFormEntry       Points to HDD_PASSWORD_CONFIG_FORM_ENTRY buffer\r
+  @param[in,out]   TempDevInfo           Points to HDD_PASSWORD_DEVICE_INFO buffer\r
+\r
+**/\r
+VOID\r
+SaveDeviceInfo (\r
+  IN     HDD_PASSWORD_CONFIG_FORM_ENTRY    *ConfigFormEntry,\r
+  IN OUT HDD_PASSWORD_DEVICE_INFO          *TempDevInfo\r
+  )\r
+{\r
+  TempDevInfo->Device.Bus                = (UINT8) ConfigFormEntry->Bus;\r
+  TempDevInfo->Device.Device             = (UINT8) ConfigFormEntry->Device;\r
+  TempDevInfo->Device.Function           = (UINT8) ConfigFormEntry->Function;\r
+  TempDevInfo->Device.Port               = ConfigFormEntry->Port;\r
+  TempDevInfo->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;\r
+  CopyMem (TempDevInfo->Password, ConfigFormEntry->Password, HDD_PASSWORD_MAX_LENGTH);\r
+  TempDevInfo->DevicePathLength          = (UINT32) GetDevicePathSize (ConfigFormEntry->DevicePath);\r
+  CopyMem (TempDevInfo->DevicePath, ConfigFormEntry->DevicePath, TempDevInfo->DevicePathLength);\r
+}\r
+\r
+/**\r
+  Build HDD password device info and save them to LockBox.\r
+\r
+ **/\r
+VOID\r
+BuildHddPasswordDeviceInfo (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  LIST_ENTRY                        *Entry;\r
+  HDD_PASSWORD_CONFIG_FORM_ENTRY    *ConfigFormEntry;\r
+  HDD_PASSWORD_DEVICE_INFO          *DevInfo;\r
+  HDD_PASSWORD_DEVICE_INFO          *TempDevInfo;\r
+  UINTN                             DevInfoLength;\r
+  UINT8                             DummyData;\r
+  BOOLEAN                           S3InitDevicesExist;\r
+  UINTN                             S3InitDevicesLength;\r
+  EFI_DEVICE_PATH_PROTOCOL          *S3InitDevices;\r
+  EFI_DEVICE_PATH_PROTOCOL          *S3InitDevicesBak;\r
+\r
+  //\r
+  // Build HDD password device info and save them to LockBox.\r
+  //\r
+  DevInfoLength = 0;\r
+  EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {\r
+    ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);\r
+\r
+    //\r
+    // 1. Handle device which already set password.\r
+    // 2. When request to send freeze comamnd, driver also needs to handle device\r
+    //    which support security feature.\r
+    //\r
+    if ((!PasswordIsFullZero (ConfigFormEntry->Password)) ||\r
+        ((ConfigFormEntry->IfrData.SecurityStatus.Supported != 0) &&\r
+         (ConfigFormEntry->IfrData.SecurityStatus.Enabled == 0))) {\r
+      DevInfoLength += sizeof (HDD_PASSWORD_DEVICE_INFO) +\r
+                       GetDevicePathSize (ConfigFormEntry->DevicePath);\r
+    }\r
+  }\r
+\r
+  if (DevInfoLength == 0) {\r
+    return;\r
+  }\r
+\r
+  S3InitDevicesLength = sizeof (DummyData);\r
+  Status = RestoreLockBox (\r
+             &gS3StorageDeviceInitListGuid,\r
+             &DummyData,\r
+             &S3InitDevicesLength\r
+             );\r
+  ASSERT ((Status == EFI_NOT_FOUND) || (Status == EFI_BUFFER_TOO_SMALL));\r
+  if (Status == EFI_NOT_FOUND) {\r
+    S3InitDevices      = NULL;\r
+    S3InitDevicesExist = FALSE;\r
+  } else if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    S3InitDevices = AllocatePool (S3InitDevicesLength);\r
+    ASSERT (S3InitDevices != NULL);\r
+\r
+    Status = RestoreLockBox (\r
+               &gS3StorageDeviceInitListGuid,\r
+               S3InitDevices,\r
+               &S3InitDevicesLength\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+    S3InitDevicesExist = TRUE;\r
+  } else {\r
+    return;\r
+  }\r
+\r
+  DevInfo = AllocateZeroPool (DevInfoLength);\r
+  ASSERT (DevInfo != NULL);\r
+\r
+  TempDevInfo = DevInfo;\r
+  EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {\r
+    ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);\r
+\r
+    if ((!PasswordIsFullZero (ConfigFormEntry->Password)) ||\r
+        ((ConfigFormEntry->IfrData.SecurityStatus.Supported != 0) &&\r
+         (ConfigFormEntry->IfrData.SecurityStatus.Enabled == 0))) {\r
+      SaveDeviceInfo (ConfigFormEntry, TempDevInfo);\r
+\r
+      S3InitDevicesBak = S3InitDevices;\r
+      S3InitDevices    = AppendDevicePathInstance (\r
+                           S3InitDevicesBak,\r
+                           ConfigFormEntry->DevicePath\r
+                           );\r
+      if (S3InitDevicesBak != NULL) {\r
+        FreePool (S3InitDevicesBak);\r
+      }\r
+      ASSERT (S3InitDevices != NULL);\r
+\r
+      TempDevInfo = (HDD_PASSWORD_DEVICE_INFO *) ((UINTN)TempDevInfo +\r
+                                                  sizeof (HDD_PASSWORD_DEVICE_INFO) +\r
+                                                  TempDevInfo->DevicePathLength);\r
+    }\r
+  }\r
+\r
+  Status = SaveLockBox (\r
+             &mHddPasswordDeviceInfoGuid,\r
+             DevInfo,\r
+             DevInfoLength\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = SetLockBoxAttributes (\r
+             &mHddPasswordDeviceInfoGuid,\r
+             LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  S3InitDevicesLength = GetDevicePathSize (S3InitDevices);\r
+  if (S3InitDevicesExist) {\r
+    Status = UpdateLockBox (\r
+               &gS3StorageDeviceInitListGuid,\r
+               0,\r
+               S3InitDevices,\r
+               S3InitDevicesLength\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+  } else {\r
+    Status = SaveLockBox (\r
+               &gS3StorageDeviceInitListGuid,\r
+               S3InitDevices,\r
+               S3InitDevicesLength\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    Status = SetLockBoxAttributes (\r
+               &gS3StorageDeviceInitListGuid,\r
+               LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  ZeroMem (DevInfo, DevInfoLength);\r
+  FreePool (DevInfo);\r
+  FreePool (S3InitDevices);\r
+}\r
+\r
+/**\r
+  Send freeze lock cmd through Ata Pass Thru Protocol.\r
+\r
+  @param[in] AtaPassThru         The pointer to the ATA_PASS_THRU protocol.\r
+  @param[in] Port                The port number of the ATA device to send the command.\r
+  @param[in] PortMultiplierPort  The port multiplier port number of the ATA device to send the command.\r
+                                 If there is no port multiplier, then specify 0xFFFF.\r
+\r
+  @retval EFI_SUCCESS            Successful to send 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 freeze lock cmd.\r
+  @retval EFI_DEVICE_ERROR       Can not send freeze lock cmd.\r
+\r
+**/\r
+EFI_STATUS\r
+FreezeLockDevice (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *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
+                          NULL\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
+  Get attached harddisk identify data through Ata Pass Thru Protocol.\r
+\r
+  @param[in] AtaPassThru         The pointer to the ATA_PASS_THRU protocol.\r
+  @param[in] Port                The port number of the ATA device to send the command.\r
+  @param[in] PortMultiplierPort  The port multiplier port number of the ATA device to send the command.\r
+                                 If there is no port multiplier, then specify 0xFFFF.\r
+  @param[in] IdentifyData        The buffer to store identify data.\r
+\r
+  @retval EFI_SUCCESS            Successful to get identify data.\r
+  @retval EFI_INVALID_PARAMETER  The parameter passed-in is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough memory to get identify data.\r
+  @retval EFI_DEVICE_ERROR       Can not get identify data.\r
+\r
+**/\r
+EFI_STATUS\r
+GetHddDeviceIdentifyData (\r
+  IN  EFI_ATA_PASS_THRU_PROTOCOL    *AtaPassThru,\r
+  IN  UINT16                        Port,\r
+  IN  UINT16                        PortMultiplierPort,\r
+  IN  ATA_IDENTIFY_DATA             *IdentifyData\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) || (IdentifyData == 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_CMD_IDENTIFY_DRIVE;\r
+  Acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | (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_IN;\r
+  Packet.Length   = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;\r
+  Packet.Asb      = Asb;\r
+  Packet.Acb      = &Acb;\r
+  Packet.InDataBuffer     = IdentifyData;\r
+  Packet.InTransferLength = sizeof (ATA_IDENTIFY_DATA);\r
+  Packet.Timeout          = ATA_TIMEOUT;\r
+\r
+  Status = AtaPassThru->PassThru (\r
+                          AtaPassThru,\r
+                          Port,\r
+                          PortMultiplierPort,\r
+                          &Packet,\r
+                          NULL\r
+                          );\r
+\r
+  FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Parse security status according to identify data.\r
+\r
+  @param[in] IdentifyData        The buffer to store identify data.\r
+  @param[in, out] IfrData        IFR data to hold security status.\r
+\r
+**/\r
+VOID\r
+GetHddPasswordSecurityStatus (\r
+  IN     ATA_IDENTIFY_DATA    *IdentifyData,\r
+  IN OUT HDD_PASSWORD_CONFIG  *IfrData\r
+  )\r
+{\r
+  IfrData->SecurityStatus.Supported = (IdentifyData->command_set_supported_82 & BIT1) ? 1 : 0;\r
+  IfrData->SecurityStatus.Enabled   = (IdentifyData->security_status & BIT1) ? 1 : 0;\r
+  IfrData->SecurityStatus.Locked    = (IdentifyData->security_status & BIT2) ? 1 : 0;\r
+  IfrData->SecurityStatus.Frozen    = (IdentifyData->security_status & BIT3) ? 1 : 0;\r
+  IfrData->SecurityStatus.UserPasswordStatus   = IfrData->SecurityStatus.Enabled;\r
+  IfrData->SecurityStatus.MasterPasswordStatus = IfrData->SecurityStatus.Supported;\r
+\r
+  DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Supported            = %x\n", IfrData->SecurityStatus.Supported));\r
+  DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Enabled              = %x\n", IfrData->SecurityStatus.Enabled));\r
+  DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Locked               = %x\n", IfrData->SecurityStatus.Locked));\r
+  DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Frozen               = %x\n", IfrData->SecurityStatus.Frozen));\r
+  DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.UserPasswordStatus   = %x\n", IfrData->SecurityStatus.UserPasswordStatus));\r
+  DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.MasterPasswordStatus = %x\n", IfrData->SecurityStatus.MasterPasswordStatus));\r
+}\r
+\r
+/**\r
+  Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.\r
+\r
+  This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.\r
+\r
+  @param  Event        Event whose notification function is being invoked.\r
+  @param  Context      Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HddPasswordEndOfDxeEventNotify (\r
+  EFI_EVENT                               Event,\r
+  VOID                                    *Context\r
+  )\r
+{\r
+  LIST_ENTRY                        *Entry;\r
+  HDD_PASSWORD_CONFIG_FORM_ENTRY    *ConfigFormEntry;\r
+  EFI_STATUS                        Status;\r
+  ATA_IDENTIFY_DATA                 IdentifyData;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  mHddPasswordEndOfDxe = TRUE;\r
+\r
+  if (mHddPasswordRequestVariable != NULL) {\r
+    //\r
+    // Free the HDD password request variable buffer here\r
+    // as the HDD password requests should have been processed.\r
+    //\r
+    FreePool (mHddPasswordRequestVariable);\r
+    mHddPasswordRequestVariable = NULL;\r
+    mHddPasswordRequestVariableSize = 0;\r
+  }\r
+\r
+  //\r
+  // If no any device, return directly.\r
+  //\r
+  if (IsListEmpty (&mHddPasswordConfigFormList)) {\r
+    gBS->CloseEvent (Event);\r
+    return;\r
+  }\r
+\r
+  BuildHddPasswordDeviceInfo ();\r
+\r
+  //\r
+  // Zero passsword and freeze lock device.\r
+  //\r
+  EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {\r
+    ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);\r
+\r
+    ZeroMem (ConfigFormEntry->Password, HDD_PASSWORD_MAX_LENGTH);\r
+\r
+    //\r
+    // Check whether need send freeze lock command.\r
+    // Below device will be froze:\r
+    // 1. Device not enable password.\r
+    // 2. Device enable password and unlocked.\r
+    //\r
+    if ((ConfigFormEntry->IfrData.SecurityStatus.Supported != 0) &&\r
+        (ConfigFormEntry->IfrData.SecurityStatus.Locked == 0) &&\r
+        (ConfigFormEntry->IfrData.SecurityStatus.Frozen == 0)) {\r
+      Status = FreezeLockDevice (ConfigFormEntry->AtaPassThru, ConfigFormEntry->Port, ConfigFormEntry->PortMultiplierPort);\r
+      DEBUG ((DEBUG_INFO, "FreezeLockDevice return %r!\n", Status));\r
+      Status = GetHddDeviceIdentifyData (\r
+                 ConfigFormEntry->AtaPassThru,\r
+                 ConfigFormEntry->Port,\r
+                 ConfigFormEntry->PortMultiplierPort,\r
+                 &IdentifyData\r
+                 );\r
+      GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);\r
+    }\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+\r
+  gBS->CloseEvent (Event);\r
+}\r
+\r
+/**\r
+  Generate Salt value.\r
+\r
+  @param[in, out]   SaltValue           Points to the salt buffer, 32 bytes\r
+\r
+**/\r
+VOID\r
+GenSalt (\r
+  IN OUT UINT8  *SaltValue\r
+  )\r
+{\r
+  RandomSeed (NULL, 0);\r
+  RandomBytes (SaltValue, PASSWORD_SALT_SIZE);\r
+}\r
+\r
+/**\r
+  Hash the data to get credential.\r
+\r
+  @param[in]   Buffer         Points to the data buffer\r
+  @param[in]   BufferSize     Buffer size\r
+  @param[in]   SaltValue      Points to the salt buffer, 32 bytes\r
+  @param[out]  Credential     Points to the hashed result\r
+\r
+  @retval      TRUE           Hash the data successfully.\r
+  @retval      FALSE          Failed to hash the data.\r
+\r
+**/\r
+BOOLEAN\r
+GenerateCredential (\r
+  IN      UINT8               *Buffer,\r
+  IN      UINTN               BufferSize,\r
+  IN      UINT8               *SaltValue,\r
+     OUT  UINT8               *Credential\r
+  )\r
+{\r
+  BOOLEAN                     Status;\r
+  UINTN                       HashSize;\r
+  VOID                        *Hash;\r
+  VOID                        *HashData;\r
+\r
+  Hash      = NULL;\r
+  HashData  = NULL;\r
+  Status    = FALSE;\r
+\r
+  HashSize = Sha256GetContextSize ();\r
+  Hash     = AllocateZeroPool (HashSize);\r
+  ASSERT (Hash != NULL);\r
+  if (Hash == NULL) {\r
+    goto Done;\r
+  }\r
+\r
+  Status = Sha256Init (Hash);\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+\r
+  HashData = AllocateZeroPool (PASSWORD_SALT_SIZE + BufferSize);\r
+  ASSERT (HashData != NULL);\r
+  if (HashData == NULL) {\r
+    goto Done;\r
+  }\r
+\r
+  CopyMem (HashData, SaltValue, PASSWORD_SALT_SIZE);\r
+  CopyMem ((UINT8 *) HashData + PASSWORD_SALT_SIZE, Buffer, BufferSize);\r
+\r
+  Status = Sha256Update (Hash, HashData, PASSWORD_SALT_SIZE + BufferSize);\r
+  if (!Status) {\r
+    goto Done;\r
+  }\r
+\r
+  Status = Sha256Final (Hash, Credential);\r
+\r
+Done:\r
+  if (Hash != NULL) {\r
+    FreePool (Hash);\r
+  }\r
+  if (HashData != NULL) {\r
+    ZeroMem (HashData, PASSWORD_SALT_SIZE + BufferSize);\r
+    FreePool (HashData);\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Save HDD password variable that will be used to validate HDD password\r
+  when the device is at frozen state.\r
+\r
+  @param[in] ConfigFormEntry        The HDD Password configuration form entry.\r
+  @param[in] Password               The hdd password of attached ATA device.\r
+\r
+**/\r
+VOID\r
+SaveHddPasswordVariable (\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry,\r
+  IN CHAR8                          *Password\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_VARIABLE             *TempVariable;\r
+  UINTN                             TempVariableSize;\r
+  HDD_PASSWORD_VARIABLE             *NextNode;\r
+  HDD_PASSWORD_VARIABLE             *Variable;\r
+  UINTN                             VariableSize;\r
+  HDD_PASSWORD_VARIABLE             *NewVariable;\r
+  UINTN                             NewVariableSize;\r
+  BOOLEAN                           Delete;\r
+  BOOLEAN                           HashOk;\r
+  UINT8                             HashData[SHA256_DIGEST_SIZE];\r
+  UINT8                             SaltData[PASSWORD_SALT_SIZE];\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  Delete = FALSE;\r
+  if (!PasswordIsFullZero (Password)) {\r
+    //\r
+    // It is Set/Update HDD Password.\r
+    //\r
+    ZeroMem (HashData, sizeof (HashData));\r
+    ZeroMem (SaltData, sizeof (SaltData));\r
+    GenSalt (SaltData);\r
+    HashOk = GenerateCredential ((UINT8 *) Password, HDD_PASSWORD_MAX_LENGTH, SaltData, HashData);\r
+    if (!HashOk) {\r
+      DEBUG ((DEBUG_INFO, "GenerateCredential failed\n"));\r
+      return;\r
+    }\r
+  } else {\r
+    //\r
+    // It is Disable HDD Password.\r
+    // Go to delete the variable node for the HDD password device.\r
+    //\r
+    Delete = TRUE;\r
+  }\r
+\r
+  Variable = NULL;\r
+  VariableSize = 0;\r
+  NewVariable = NULL;\r
+  NewVariableSize = 0;\r
+\r
+  Status = GetVariable2 (\r
+             HDD_PASSWORD_VARIABLE_NAME,\r
+             &mHddPasswordVendorGuid,\r
+             (VOID **) &Variable,\r
+             &VariableSize\r
+             );\r
+  if (Delete) {\r
+    if (!EFI_ERROR (Status) && (Variable != NULL)) {\r
+      TempVariable = Variable;\r
+      TempVariableSize = VariableSize;\r
+      while (TempVariableSize >= sizeof (HDD_PASSWORD_VARIABLE)) {\r
+        if ((TempVariable->Device.Bus                == ConfigFormEntry->Bus) &&\r
+            (TempVariable->Device.Device             == ConfigFormEntry->Device) &&\r
+            (TempVariable->Device.Function           == ConfigFormEntry->Function) &&\r
+            (TempVariable->Device.Port               == ConfigFormEntry->Port) &&\r
+            (TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {\r
+          //\r
+          // Found the node for the HDD password device.\r
+          // Delete the node.\r
+          //\r
+          NextNode = TempVariable + 1;\r
+          CopyMem (TempVariable, NextNode, (UINTN) Variable + VariableSize - (UINTN) NextNode);\r
+          NewVariable = Variable;\r
+          NewVariableSize = VariableSize - sizeof (HDD_PASSWORD_VARIABLE);\r
+          break;\r
+        }\r
+        TempVariableSize -= sizeof (HDD_PASSWORD_VARIABLE);\r
+        TempVariable += 1;\r
+      }\r
+      if (NewVariable == NULL) {\r
+        DEBUG ((DEBUG_INFO, "The variable node for the HDD password device is not found\n"));\r
+      }\r
+    } else {\r
+      DEBUG ((DEBUG_INFO, "HddPassword variable get failed (%r)\n", Status));\r
+    }\r
+  } else {\r
+    if (!EFI_ERROR (Status) && (Variable != NULL)) {\r
+      TempVariable = Variable;\r
+      TempVariableSize = VariableSize;\r
+      while (TempVariableSize >= sizeof (HDD_PASSWORD_VARIABLE)) {\r
+        if ((TempVariable->Device.Bus                == ConfigFormEntry->Bus) &&\r
+            (TempVariable->Device.Device             == ConfigFormEntry->Device) &&\r
+            (TempVariable->Device.Function           == ConfigFormEntry->Function) &&\r
+            (TempVariable->Device.Port               == ConfigFormEntry->Port) &&\r
+            (TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {\r
+          //\r
+          // Found the node for the HDD password device.\r
+          // Update the node.\r
+          //\r
+          CopyMem (TempVariable->PasswordHash, HashData, sizeof (HashData));\r
+          CopyMem (TempVariable->PasswordSalt, SaltData, sizeof (SaltData));\r
+          NewVariable = Variable;\r
+          NewVariableSize = VariableSize;\r
+          break;\r
+        }\r
+        TempVariableSize -= sizeof (HDD_PASSWORD_VARIABLE);\r
+        TempVariable += 1;\r
+      }\r
+      if (NewVariable == NULL) {\r
+        //\r
+        // The node for the HDD password device is not found.\r
+        // Create node for the HDD password device.\r
+        //\r
+        NewVariableSize = VariableSize + sizeof (HDD_PASSWORD_VARIABLE);\r
+        NewVariable = AllocateZeroPool (NewVariableSize);\r
+        ASSERT (NewVariable != NULL);\r
+        CopyMem (NewVariable, Variable, VariableSize);\r
+        TempVariable = (HDD_PASSWORD_VARIABLE *) ((UINTN) NewVariable + VariableSize);\r
+        TempVariable->Device.Bus                = (UINT8) ConfigFormEntry->Bus;\r
+        TempVariable->Device.Device             = (UINT8) ConfigFormEntry->Device;\r
+        TempVariable->Device.Function           = (UINT8) ConfigFormEntry->Function;\r
+        TempVariable->Device.Port               = ConfigFormEntry->Port;\r
+        TempVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;\r
+        CopyMem (TempVariable->PasswordHash, HashData, sizeof (HashData));\r
+        CopyMem (TempVariable->PasswordSalt, SaltData, sizeof (SaltData));\r
+      }\r
+    } else {\r
+      NewVariableSize = sizeof (HDD_PASSWORD_VARIABLE);\r
+      NewVariable = AllocateZeroPool (NewVariableSize);\r
+      ASSERT (NewVariable != NULL);\r
+      NewVariable->Device.Bus                = (UINT8) ConfigFormEntry->Bus;\r
+      NewVariable->Device.Device             = (UINT8) ConfigFormEntry->Device;\r
+      NewVariable->Device.Function           = (UINT8) ConfigFormEntry->Function;\r
+      NewVariable->Device.Port               = ConfigFormEntry->Port;\r
+      NewVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;\r
+      CopyMem (NewVariable->PasswordHash, HashData, sizeof (HashData));\r
+      CopyMem (NewVariable->PasswordSalt, SaltData, sizeof (SaltData));\r
+    }\r
+  }\r
+\r
+  if (NewVariable != NULL) {\r
+    Status = gRT->SetVariable (\r
+                    HDD_PASSWORD_VARIABLE_NAME,\r
+                    &mHddPasswordVendorGuid,\r
+                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                    NewVariableSize,\r
+                    NewVariable\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_INFO, "HddPassword variable set failed (%r)\n", Status));\r
+    }\r
+  }\r
+\r
+  if (NewVariable != Variable) {\r
+    FreePool (NewVariable);\r
+  }\r
+  if (Variable != NULL) {\r
+    FreePool (Variable);\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+  Get saved HDD password variable that will be used to validate HDD password\r
+  when the device is at frozen state.\r
+\r
+  @param[in]  ConfigFormEntry       The HDD Password configuration form entry.\r
+  @param[out] HddPasswordVariable   The variable node for the HDD password device.\r
+\r
+  @retval TRUE      The variable node for the HDD password device is found and returned.\r
+  @retval FALSE     The variable node for the HDD password device is not found.\r
+\r
+**/\r
+BOOLEAN\r
+GetSavedHddPasswordVariable (\r
+  IN  HDD_PASSWORD_CONFIG_FORM_ENTRY    *ConfigFormEntry,\r
+  OUT HDD_PASSWORD_VARIABLE             *HddPasswordVariable\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_VARIABLE             *TempVariable;\r
+  HDD_PASSWORD_VARIABLE             *Variable;\r
+  UINTN                             VariableSize;\r
+  BOOLEAN                           Found;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  Variable = NULL;\r
+  VariableSize = 0;\r
+\r
+  Status = GetVariable2 (\r
+             HDD_PASSWORD_VARIABLE_NAME,\r
+             &mHddPasswordVendorGuid,\r
+             (VOID **) &Variable,\r
+             &VariableSize\r
+             );\r
+  if (EFI_ERROR (Status) || (Variable == NULL)) {\r
+    DEBUG ((DEBUG_INFO, "HddPassword variable get failed (%r)\n", Status));\r
+    return FALSE;\r
+  }\r
+\r
+  Found = FALSE;\r
+  TempVariable = Variable;\r
+  while (VariableSize >= sizeof (HDD_PASSWORD_VARIABLE)) {\r
+    if ((TempVariable->Device.Bus                == ConfigFormEntry->Bus) &&\r
+        (TempVariable->Device.Device             == ConfigFormEntry->Device) &&\r
+        (TempVariable->Device.Function           == ConfigFormEntry->Function) &&\r
+        (TempVariable->Device.Port               == ConfigFormEntry->Port) &&\r
+        (TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {\r
+      //\r
+      // Found the node for the HDD password device.\r
+      // Get the node.\r
+      //\r
+      CopyMem (HddPasswordVariable, TempVariable, sizeof (HDD_PASSWORD_VARIABLE));\r
+      Found = TRUE;\r
+      break;\r
+    }\r
+    VariableSize -= sizeof (HDD_PASSWORD_VARIABLE);\r
+    TempVariable += 1;\r
+  }\r
+\r
+  FreePool (Variable);\r
+\r
+  if (!Found) {\r
+    DEBUG ((DEBUG_INFO, "The variable node for the HDD password device is not found\n"));\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+\r
+  return Found;\r
+}\r
+\r
+/**\r
+  Use saved HDD password variable to validate HDD password\r
+  when the device is at frozen state.\r
+\r
+  @param[in] ConfigFormEntry    The HDD Password configuration form entry.\r
+  @param[in] Password           The hdd password of attached ATA device.\r
+\r
+  @retval EFI_SUCCESS           Pass to validate the HDD password.\r
+  @retval EFI_NOT_FOUND         The variable node for the HDD password device is not found.\r
+  @retval EFI_DEVICE_ERROR      Failed to generate credential for the HDD password.\r
+  @retval EFI_INVALID_PARAMETER Failed to validate the HDD password.\r
+\r
+**/\r
+EFI_STATUS\r
+ValidateHddPassword (\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry,\r
+  IN CHAR8                          *Password\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_VARIABLE             HddPasswordVariable;\r
+  BOOLEAN                           HashOk;\r
+  UINT8                             HashData[SHA256_DIGEST_SIZE];\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  if (!GetSavedHddPasswordVariable (ConfigFormEntry, &HddPasswordVariable)) {\r
+    DEBUG ((DEBUG_INFO, "GetSavedHddPasswordVariable failed\n"));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ZeroMem (HashData, sizeof (HashData));\r
+  HashOk = GenerateCredential ((UINT8 *) Password, HDD_PASSWORD_MAX_LENGTH, HddPasswordVariable.PasswordSalt, HashData);\r
+  if (!HashOk) {\r
+    DEBUG ((DEBUG_INFO, "GenerateCredential failed\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (CompareMem (HddPasswordVariable.PasswordHash, HashData, sizeof (HashData)) != 0) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+  } else {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit (%r)\n", __FUNCTION__, Status));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send unlock hdd password cmd through Ata Pass Thru Protocol.\r
+\r
+  @param[in] AtaPassThru         The pointer to the ATA_PASS_THRU protocol.\r
+  @param[in] Port                The port number of the ATA device to send the command.\r
+  @param[in] PortMultiplierPort  The port multiplier port number of the ATA device to send the command.\r
+                                 If there is no port multiplier, then specify 0xFFFF.\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
+UnlockHddPassword (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *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
+                          NULL\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 disable hdd password cmd through Ata Pass Thru Protocol.\r
+\r
+  @param[in] AtaPassThru         The pointer to the ATA_PASS_THRU protocol.\r
+  @param[in] Port                The port number of the ATA device to send the command.\r
+  @param[in] PortMultiplierPort  The port multiplier port number of the ATA device to send the command.\r
+                                 If there is no port multiplier, then specify 0xFFFF.\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 disable hdd password cmd.\r
+  @retval EFI_INVALID_PARAMETER  The parameter passed-in is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough memory to disable hdd password cmd.\r
+  @retval EFI_DEVICE_ERROR       Can not disable hdd password cmd.\r
+\r
+**/\r
+EFI_STATUS\r
+DisableHddPassword (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *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_DIS_PASSWORD_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
+                          NULL\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 set hdd password cmd through Ata Pass Thru Protocol.\r
+\r
+  @param[in] AtaPassThru                The pointer to the ATA_PASS_THRU protocol.\r
+  @param[in] Port                       The port number of the ATA device to send the command.\r
+  @param[in] PortMultiplierPort         The port multiplier port number of the ATA device to send the command.\r
+                                        If there is no port multiplier, then specify 0xFFFF.\r
+  @param[in] Identifier                 The identifier to set user or master password.\r
+  @param[in] SecurityLevel              The security level to be set to device.\r
+  @param[in] MasterPasswordIdentifier   The master password identifier to be set to device.\r
+  @param[in] Password                   The hdd password of attached ATA device.\r
+\r
+  @retval EFI_SUCCESS            Successful to set hdd password cmd.\r
+  @retval EFI_INVALID_PARAMETER  The parameter passed-in is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough memory to set hdd password cmd.\r
+  @retval EFI_DEVICE_ERROR       Can not set hdd password cmd.\r
+\r
+**/\r
+EFI_STATUS\r
+SetHddPassword (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *AtaPassThru,\r
+  IN UINT16                         Port,\r
+  IN UINT16                         PortMultiplierPort,\r
+  IN CHAR8                          Identifier,\r
+  IN CHAR8                          SecurityLevel,\r
+  IN CHAR16                         MasterPasswordIdentifier,\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_SET_PASSWORD_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 | (UINT16)(SecurityLevel << 8)) & (BIT0 | BIT8);\r
+  CopyMem (&((CHAR16 *) Buffer)[1], Password, HDD_PASSWORD_MAX_LENGTH);\r
+  if ((Identifier & BIT0) != 0) {\r
+    ((CHAR16 *) Buffer)[17] = MasterPasswordIdentifier;\r
+  }\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
+                          NULL\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
+  Get attached harddisk model number from identify data buffer.\r
+\r
+  @param[in] IdentifyData    Pointer to identify data buffer.\r
+  @param[in, out] String     The buffer to store harddisk model number.\r
+\r
+**/\r
+VOID\r
+GetHddDeviceModelNumber (\r
+  IN ATA_IDENTIFY_DATA             *IdentifyData,\r
+  IN OUT CHAR16                    *String\r
+  )\r
+{\r
+  UINTN             Index;\r
+\r
+  //\r
+  // Swap the byte order in the original module name.\r
+  // From Ata spec, the maximum length is 40 bytes.\r
+  //\r
+  for (Index = 0; Index < 40; Index += 2) {\r
+    String[Index]      = IdentifyData->ModelName[Index + 1];\r
+    String[Index + 1]  = IdentifyData->ModelName[Index];\r
+  }\r
+\r
+  //\r
+  // Chap it off after 20 characters\r
+  //\r
+  String[20] = L'\0';\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  Get password input from the popup windows.\r
+\r
+  @param[in]      PopUpString1  Pop up string 1.\r
+  @param[in]      PopUpString2  Pop up string 2.\r
+  @param[in, out] Password      The buffer to hold the input password.\r
+\r
+  @retval EFI_ABORTED           It is given up by pressing 'ESC' key.\r
+  @retval EFI_SUCCESS           Get password input successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+PopupHddPasswordInputWindows (\r
+  IN CHAR16         *PopUpString1,\r
+  IN CHAR16         *PopUpString2,\r
+  IN OUT CHAR8      *Password\r
+  )\r
+{\r
+  EFI_INPUT_KEY Key;\r
+  UINTN         Length;\r
+  CHAR16        Mask[HDD_PASSWORD_MAX_LENGTH + 1];\r
+  CHAR16        Unicode[HDD_PASSWORD_MAX_LENGTH + 1];\r
+  CHAR8         Ascii[HDD_PASSWORD_MAX_LENGTH + 1];\r
+\r
+  ZeroMem (Unicode, sizeof (Unicode));\r
+  ZeroMem (Ascii, sizeof (Ascii));\r
+  ZeroMem (Mask, sizeof (Mask));\r
+\r
+  gST->ConOut->ClearScreen(gST->ConOut);\r
+\r
+  Length = 0;\r
+  while (TRUE) {\r
+    Mask[Length] = L'_';\r
+    if (PopUpString2 == NULL) {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        PopUpString1,\r
+        L"---------------------",\r
+        Mask,\r
+        NULL\r
+      );\r
+    } else {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        PopUpString1,\r
+        PopUpString2,\r
+        L"---------------------",\r
+        Mask,\r
+        NULL\r
+      );\r
+    }\r
+    //\r
+    // Check key.\r
+    //\r
+    if (Key.ScanCode == SCAN_NULL) {\r
+      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {\r
+        //\r
+        // Add the null terminator.\r
+        //\r
+        Unicode[Length] = 0;\r
+        break;\r
+      } else if ((Key.UnicodeChar == CHAR_NULL) ||\r
+                 (Key.UnicodeChar == CHAR_TAB) ||\r
+                 (Key.UnicodeChar == CHAR_LINEFEED)\r
+                 ) {\r
+        continue;\r
+      } else {\r
+        if (Key.UnicodeChar == CHAR_BACKSPACE) {\r
+          if (Length > 0) {\r
+            Unicode[Length] = 0;\r
+            Mask[Length] = 0;\r
+            Length--;\r
+          }\r
+        } else {\r
+          Unicode[Length] = Key.UnicodeChar;\r
+          Mask[Length] = L'*';\r
+          Length++;\r
+          if (Length == HDD_PASSWORD_MAX_LENGTH) {\r
+            //\r
+            // Add the null terminator.\r
+            //\r
+            Unicode[Length] = 0;\r
+            Mask[Length] = 0;\r
+            break;\r
+          }\r
+        }\r
+      }\r
+    }\r
+\r
+    if (Key.ScanCode == SCAN_ESC) {\r
+      ZeroMem (Unicode, sizeof (Unicode));\r
+      ZeroMem (Ascii, sizeof (Ascii));\r
+      gST->ConOut->ClearScreen(gST->ConOut);\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  UnicodeStrToAsciiStrS (Unicode, Ascii, sizeof (Ascii));\r
+  CopyMem (Password, Ascii, HDD_PASSWORD_MAX_LENGTH);\r
+  ZeroMem (Unicode, sizeof (Unicode));\r
+  ZeroMem (Ascii, sizeof (Ascii));\r
+\r
+  gST->ConOut->ClearScreen(gST->ConOut);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check if disk is locked, show popup window and ask for password if it is.\r
+\r
+  @param[in] AtaPassThru            Pointer to ATA_PASSTHRU instance.\r
+  @param[in] Port                   The port number of attached ATA device.\r
+  @param[in] PortMultiplierPort     The port number of port multiplier of attached ATA device.\r
+  @param[in] ConfigFormEntry        The HDD Password configuration form entry.\r
+\r
+**/\r
+VOID\r
+HddPasswordRequestPassword (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *AtaPassThru,\r
+  IN UINT16                         Port,\r
+  IN UINT16                         PortMultiplierPort,\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  CHAR16                            PopUpString[100];\r
+  ATA_IDENTIFY_DATA                 IdentifyData;\r
+  EFI_INPUT_KEY                     Key;\r
+  UINT16                            RetryCount;\r
+  CHAR8                             Password[HDD_PASSWORD_MAX_LENGTH];\r
+\r
+  RetryCount = 0;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));\r
+\r
+  UnicodeSPrint (PopUpString, sizeof (PopUpString), L"Unlock: %s", ConfigFormEntry->HddString);\r
+\r
+  //\r
+  // Check the device security status.\r
+  //\r
+  if ((ConfigFormEntry->IfrData.SecurityStatus.Supported) &&\r
+      (ConfigFormEntry->IfrData.SecurityStatus.Enabled)) {\r
+    //\r
+    // As soon as the HDD password is in enabled state, we pop up a window to unlock hdd\r
+    // no matter it's really in locked or unlocked state.\r
+    // This way forces user to enter password every time to provide best safety.\r
+    //\r
+    while (TRUE) {\r
+      Status = PopupHddPasswordInputWindows (PopUpString, NULL, Password);\r
+      if (!EFI_ERROR (Status)) {\r
+        //\r
+        // The HDD is in locked state, unlock it by user input.\r
+        //\r
+        if (!PasswordIsFullZero (Password)) {\r
+          if (!ConfigFormEntry->IfrData.SecurityStatus.Frozen) {\r
+            Status = UnlockHddPassword (AtaPassThru, Port, PortMultiplierPort, 0, Password);\r
+          } else {\r
+            //\r
+            // Use saved HDD password variable to validate HDD password\r
+            // when the device is at frozen state.\r
+            //\r
+            Status = ValidateHddPassword (ConfigFormEntry, Password);\r
+          }\r
+        } else {\r
+          Status = EFI_INVALID_PARAMETER;\r
+        }\r
+        if (!EFI_ERROR (Status)) {\r
+          CopyMem (ConfigFormEntry->Password, Password, HDD_PASSWORD_MAX_LENGTH);\r
+          if (!ConfigFormEntry->IfrData.SecurityStatus.Frozen) {\r
+            SaveHddPasswordVariable (ConfigFormEntry, Password);\r
+          }\r
+          ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);\r
+          Status = GetHddDeviceIdentifyData (AtaPassThru, Port, PortMultiplierPort, &IdentifyData);\r
+          ASSERT_EFI_ERROR (Status);\r
+\r
+          //\r
+          // Check the device security status again.\r
+          //\r
+          GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);\r
+          return;\r
+        }\r
+\r
+        ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);\r
+\r
+        if (EFI_ERROR (Status)) {\r
+          RetryCount ++;\r
+          if (RetryCount < MAX_HDD_PASSWORD_RETRY_COUNT) {\r
+            do {\r
+              CreatePopUp (\r
+                EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                &Key,\r
+                L"Invalid password.",\r
+                L"Press ENTER to retry",\r
+                NULL\r
+                );\r
+            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            continue;\r
+          } else {\r
+            do {\r
+              CreatePopUp (\r
+                EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                &Key,\r
+                L"Hdd password retry count is expired. Please shutdown the machine.",\r
+                L"Press ENTER to shutdown",\r
+                NULL\r
+                );\r
+            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);\r
+            break;\r
+          }\r
+        }\r
+      } else if (Status == EFI_ABORTED) {\r
+        if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {\r
+          //\r
+          // Current device in the lock status and\r
+          // User not input password and press ESC,\r
+          // keep device in lock status and continue boot.\r
+          //\r
+          do {\r
+            CreatePopUp (\r
+              EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+              &Key,\r
+              L"Press ENTER to skip the request and continue boot,",\r
+              L"Press ESC to input password again",\r
+              NULL\r
+              );\r
+          } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));\r
+\r
+          if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {\r
+            gST->ConOut->ClearScreen(gST->ConOut);\r
+            //\r
+            // Keep lock and continue boot.\r
+            //\r
+            return;\r
+          } else {\r
+            //\r
+            // Let user input password again.\r
+            //\r
+            continue;\r
+          }\r
+        } else {\r
+          //\r
+          // Current device in the unlock status and\r
+          // User not input password and press ESC,\r
+          // Shutdown the device.\r
+          //\r
+          do {\r
+            CreatePopUp (\r
+              EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+              &Key,\r
+              L"Press ENTER to shutdown, Press ESC to input password again",\r
+              NULL\r
+              );\r
+          } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));\r
+\r
+          if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {\r
+            gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);\r
+          } else {\r
+            //\r
+            // Let user input password again.\r
+            //\r
+            continue;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Process Set User Pwd HDD password request.\r
+\r
+  @param[in] AtaPassThru            Pointer to ATA_PASSTHRU instance.\r
+  @param[in] Port                   The port number of attached ATA device.\r
+  @param[in] PortMultiplierPort     The port number of port multiplier of attached ATA device.\r
+  @param[in] ConfigFormEntry        The HDD Password configuration form entry.\r
+\r
+**/\r
+VOID\r
+ProcessHddPasswordRequestSetUserPwd (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *AtaPassThru,\r
+  IN UINT16                         Port,\r
+  IN UINT16                         PortMultiplierPort,\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  CHAR16                            PopUpString[100];\r
+  ATA_IDENTIFY_DATA                 IdentifyData;\r
+  EFI_INPUT_KEY                     Key;\r
+  UINT16                            RetryCount;\r
+  CHAR8                             Password[HDD_PASSWORD_MAX_LENGTH];\r
+  CHAR8                             PasswordConfirm[HDD_PASSWORD_MAX_LENGTH];\r
+\r
+  RetryCount = 0;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));\r
+\r
+  if (ConfigFormEntry->IfrData.SecurityStatus.Frozen) {\r
+    DEBUG ((DEBUG_INFO, "%s is frozen, do nothing\n", ConfigFormEntry->HddString));\r
+    return;\r
+  }\r
+\r
+  if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {\r
+    DEBUG ((DEBUG_INFO, "%s is locked, do nothing\n", ConfigFormEntry->HddString));\r
+    return;\r
+  }\r
+\r
+  UnicodeSPrint (PopUpString, sizeof (PopUpString), L"Set User Pwd: %s", ConfigFormEntry->HddString);\r
+\r
+  //\r
+  // Check the device security status.\r
+  //\r
+  if (ConfigFormEntry->IfrData.SecurityStatus.Supported) {\r
+    while (TRUE) {\r
+      Status = PopupHddPasswordInputWindows (PopUpString, L"Please type in your new password", Password);\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = PopupHddPasswordInputWindows (PopUpString, L"Please confirm your new password", PasswordConfirm);\r
+        if (!EFI_ERROR (Status)) {\r
+          if (CompareMem (Password, PasswordConfirm, HDD_PASSWORD_MAX_LENGTH) == 0) {\r
+            if (!PasswordIsFullZero (Password)) {\r
+              Status = SetHddPassword (AtaPassThru, Port, PortMultiplierPort, 0, 1, 0, Password);\r
+            } else {\r
+              if (ConfigFormEntry->IfrData.SecurityStatus.Enabled) {\r
+                Status = DisableHddPassword (AtaPassThru, Port, PortMultiplierPort, 0, ConfigFormEntry->Password);\r
+              } else {\r
+                Status = EFI_INVALID_PARAMETER;\r
+              }\r
+            }\r
+            if (!EFI_ERROR (Status)) {\r
+              CopyMem (ConfigFormEntry->Password, Password, HDD_PASSWORD_MAX_LENGTH);\r
+              SaveHddPasswordVariable (ConfigFormEntry, Password);\r
+              ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);\r
+              ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);\r
+              Status = GetHddDeviceIdentifyData (AtaPassThru, Port, PortMultiplierPort, &IdentifyData);\r
+              ASSERT_EFI_ERROR (Status);\r
+\r
+              //\r
+              // Check the device security status again.\r
+              //\r
+              GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);\r
+              return;\r
+            } else {\r
+              do {\r
+                CreatePopUp (\r
+                  EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                  &Key,\r
+                  L"Set/Disable User Pwd failed or invalid password.",\r
+                  L"Press ENTER to retry",\r
+                  NULL\r
+                  );\r
+              } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            }\r
+          } else {\r
+            do {\r
+              CreatePopUp (\r
+                EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                &Key,\r
+                L"Passwords are not the same.",\r
+                L"Press ENTER to retry",\r
+                NULL\r
+                );\r
+            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            Status = EFI_INVALID_PARAMETER;\r
+          }\r
+        }\r
+\r
+        ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);\r
+        ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);\r
+\r
+        if (EFI_ERROR (Status)) {\r
+          RetryCount ++;\r
+          if (RetryCount >= MAX_HDD_PASSWORD_RETRY_COUNT) {\r
+            do {\r
+              CreatePopUp (\r
+                EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                &Key,\r
+                L"Hdd password retry count is expired.",\r
+                L"Press ENTER to skip the request and continue boot",\r
+                NULL\r
+                );\r
+            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            gST->ConOut->ClearScreen(gST->ConOut);\r
+            return;\r
+          }\r
+        }\r
+      } else if (Status == EFI_ABORTED) {\r
+        do {\r
+          CreatePopUp (\r
+            EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+            &Key,\r
+            L"Press ENTER to skip the request and continue boot,",\r
+            L"Press ESC to input password again",\r
+            NULL\r
+            );\r
+        } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));\r
+\r
+        if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {\r
+          gST->ConOut->ClearScreen(gST->ConOut);\r
+          return;\r
+        } else {\r
+          //\r
+          // Let user input password again.\r
+          //\r
+          continue;\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Process Set Master Pwd HDD password request.\r
+\r
+  @param[in] AtaPassThru            Pointer to ATA_PASSTHRU instance.\r
+  @param[in] Port                   The port number of attached ATA device.\r
+  @param[in] PortMultiplierPort     The port number of port multiplier of attached ATA device.\r
+  @param[in] ConfigFormEntry        The HDD Password configuration form entry.\r
+\r
+**/\r
+VOID\r
+ProcessHddPasswordRequestSetMasterPwd (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *AtaPassThru,\r
+  IN UINT16                         Port,\r
+  IN UINT16                         PortMultiplierPort,\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  CHAR16                            PopUpString[100];\r
+  EFI_INPUT_KEY                     Key;\r
+  UINT16                            RetryCount;\r
+  CHAR8                             Password[HDD_PASSWORD_MAX_LENGTH];\r
+  CHAR8                             PasswordConfirm[HDD_PASSWORD_MAX_LENGTH];\r
+\r
+  RetryCount = 0;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));\r
+\r
+  if (ConfigFormEntry->IfrData.SecurityStatus.Frozen) {\r
+    DEBUG ((DEBUG_INFO, "%s is frozen, do nothing\n", ConfigFormEntry->HddString));\r
+    return;\r
+  }\r
+\r
+  if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {\r
+    DEBUG ((DEBUG_INFO, "%s is locked, do nothing\n", ConfigFormEntry->HddString));\r
+    return;\r
+  }\r
+\r
+  UnicodeSPrint (PopUpString, sizeof (PopUpString), L"Set Master Pwd: %s", ConfigFormEntry->HddString);\r
+\r
+  //\r
+  // Check the device security status.\r
+  //\r
+  if (ConfigFormEntry->IfrData.SecurityStatus.Supported) {\r
+    while (TRUE) {\r
+      Status = PopupHddPasswordInputWindows (PopUpString, L"Please type in your new password", Password);\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = PopupHddPasswordInputWindows (PopUpString, L"Please confirm your new password", PasswordConfirm);\r
+        if (!EFI_ERROR (Status)) {\r
+          if (CompareMem (Password, PasswordConfirm, HDD_PASSWORD_MAX_LENGTH) == 0) {\r
+            if (!PasswordIsFullZero (Password)) {\r
+              Status = SetHddPassword (AtaPassThru, Port, PortMultiplierPort, 1, 1, 1, Password);\r
+            } else {\r
+              Status = EFI_INVALID_PARAMETER;\r
+            }\r
+            if (!EFI_ERROR (Status)) {\r
+              ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);\r
+              ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);\r
+              return;\r
+            } else {\r
+              do {\r
+                CreatePopUp (\r
+                  EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                  &Key,\r
+                  L"Set Master Pwd failed or invalid password.",\r
+                  L"Press ENTER to retry",\r
+                  NULL\r
+                  );\r
+              } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            }\r
+          } else {\r
+            do {\r
+              CreatePopUp (\r
+                EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                &Key,\r
+                L"Passwords are not the same.",\r
+                L"Press ENTER to retry",\r
+                NULL\r
+                );\r
+            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            Status = EFI_INVALID_PARAMETER;\r
+          }\r
+        }\r
+\r
+        ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);\r
+        ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);\r
+\r
+        if (EFI_ERROR (Status)) {\r
+          RetryCount ++;\r
+          if (RetryCount >= MAX_HDD_PASSWORD_RETRY_COUNT) {\r
+            do {\r
+              CreatePopUp (\r
+                EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+                &Key,\r
+                L"Hdd password retry count is expired.",\r
+                L"Press ENTER to skip the request and continue boot",\r
+                NULL\r
+                );\r
+            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+            gST->ConOut->ClearScreen(gST->ConOut);\r
+            return;\r
+          }\r
+        }\r
+      } else if (Status == EFI_ABORTED) {\r
+        do {\r
+          CreatePopUp (\r
+            EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+            &Key,\r
+            L"Press ENTER to skip the request and continue boot,",\r
+            L"Press ESC to input password again",\r
+            NULL\r
+            );\r
+        } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));\r
+\r
+        if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {\r
+          gST->ConOut->ClearScreen(gST->ConOut);\r
+          return;\r
+        } else {\r
+          //\r
+          // Let user input password again.\r
+          //\r
+          continue;\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Process HDD password request.\r
+\r
+  @param[in] AtaPassThru            Pointer to ATA_PASSTHRU instance.\r
+  @param[in] Port                   The port number of attached ATA device.\r
+  @param[in] PortMultiplierPort     The port number of port multiplier of attached ATA device.\r
+  @param[in] ConfigFormEntry        The HDD Password configuration form entry.\r
+\r
+**/\r
+VOID\r
+ProcessHddPasswordRequest (\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL     *AtaPassThru,\r
+  IN UINT16                         Port,\r
+  IN UINT16                         PortMultiplierPort,\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *TempVariable;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *Variable;\r
+  UINTN                             VariableSize;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  if (mHddPasswordRequestVariable == NULL) {\r
+    Status = GetVariable2 (\r
+               HDD_PASSWORD_REQUEST_VARIABLE_NAME,\r
+               &mHddPasswordVendorGuid,\r
+               (VOID **) &Variable,\r
+               &VariableSize\r
+               );\r
+    if (EFI_ERROR (Status) || (Variable == NULL)) {\r
+      return;\r
+    }\r
+    mHddPasswordRequestVariable = Variable;\r
+    mHddPasswordRequestVariableSize = VariableSize;\r
+\r
+    //\r
+    // Delete the HDD password request variable.\r
+    //\r
+    Status = gRT->SetVariable (\r
+                    HDD_PASSWORD_REQUEST_VARIABLE_NAME,\r
+                    &mHddPasswordVendorGuid,\r
+                    0,\r
+                    0,\r
+                    NULL\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+  } else {\r
+    Variable = mHddPasswordRequestVariable;\r
+    VariableSize = mHddPasswordRequestVariableSize;\r
+  }\r
+\r
+  //\r
+  // Process the HDD password requests.\r
+  //\r
+  TempVariable = Variable;\r
+  while (VariableSize >= sizeof (HDD_PASSWORD_REQUEST_VARIABLE)) {\r
+    if ((TempVariable->Device.Bus                == ConfigFormEntry->Bus) &&\r
+        (TempVariable->Device.Device             == ConfigFormEntry->Device) &&\r
+        (TempVariable->Device.Function           == ConfigFormEntry->Function) &&\r
+        (TempVariable->Device.Port               == ConfigFormEntry->Port) &&\r
+        (TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {\r
+      //\r
+      // Found the node for the HDD password device.\r
+      //\r
+      if (TempVariable->Request.UserPassword != 0) {\r
+        ProcessHddPasswordRequestSetUserPwd (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);\r
+      }\r
+      if (TempVariable->Request.MasterPassword != 0) {\r
+        ProcessHddPasswordRequestSetMasterPwd (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);\r
+      }\r
+\r
+      break;\r
+    }\r
+\r
+    VariableSize -= sizeof (HDD_PASSWORD_REQUEST_VARIABLE);\r
+    TempVariable += 1;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+  Get saved HDD password request.\r
+\r
+  @param[in, out] ConfigFormEntry       The HDD Password configuration form entry.\r
+\r
+**/\r
+VOID\r
+GetSavedHddPasswordRequest (\r
+  IN OUT HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *TempVariable;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *Variable;\r
+  UINTN                             VariableSize;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  Variable = NULL;\r
+  VariableSize = 0;\r
+\r
+  Status = GetVariable2 (\r
+             HDD_PASSWORD_REQUEST_VARIABLE_NAME,\r
+             &mHddPasswordVendorGuid,\r
+             (VOID **) &Variable,\r
+             &VariableSize\r
+             );\r
+  if (EFI_ERROR (Status) || (Variable == NULL)) {\r
+    return;\r
+  }\r
+\r
+  TempVariable = Variable;\r
+  while (VariableSize >= sizeof (HDD_PASSWORD_REQUEST_VARIABLE)) {\r
+    if ((TempVariable->Device.Bus                == ConfigFormEntry->Bus) &&\r
+        (TempVariable->Device.Device             == ConfigFormEntry->Device) &&\r
+        (TempVariable->Device.Function           == ConfigFormEntry->Function) &&\r
+        (TempVariable->Device.Port               == ConfigFormEntry->Port) &&\r
+        (TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {\r
+      //\r
+      // Found the node for the HDD password device.\r
+      // Get the HDD password request.\r
+      //\r
+      CopyMem (&ConfigFormEntry->IfrData.Request, &TempVariable->Request, sizeof (HDD_PASSWORD_REQUEST));\r
+      DEBUG ((\r
+        DEBUG_INFO,\r
+        "HddPasswordRequest got: 0x%x\n",\r
+        ConfigFormEntry->IfrData.Request\r
+        ));\r
+      break;\r
+    }\r
+    VariableSize -= sizeof (HDD_PASSWORD_REQUEST_VARIABLE);\r
+    TempVariable += 1;\r
+  }\r
+\r
+  FreePool (Variable);\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+  Save HDD password request.\r
+\r
+  @param[in] ConfigFormEntry        The HDD Password configuration form entry.\r
+\r
+**/\r
+VOID\r
+SaveHddPasswordRequest (\r
+  IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *TempVariable;\r
+  UINTN                             TempVariableSize;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *Variable;\r
+  UINTN                             VariableSize;\r
+  HDD_PASSWORD_REQUEST_VARIABLE     *NewVariable;\r
+  UINTN                             NewVariableSize;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "HddPasswordRequest to save: 0x%x\n",\r
+    ConfigFormEntry->IfrData.Request\r
+    ));\r
+\r
+  Variable = NULL;\r
+  VariableSize = 0;\r
+  NewVariable = NULL;\r
+  NewVariableSize = 0;\r
+\r
+  Status = GetVariable2 (\r
+             HDD_PASSWORD_REQUEST_VARIABLE_NAME,\r
+             &mHddPasswordVendorGuid,\r
+             (VOID **) &Variable,\r
+             &VariableSize\r
+             );\r
+  if (!EFI_ERROR (Status) && (Variable != NULL)) {\r
+    TempVariable = Variable;\r
+    TempVariableSize = VariableSize;\r
+    while (TempVariableSize >= sizeof (HDD_PASSWORD_REQUEST_VARIABLE)) {\r
+      if ((TempVariable->Device.Bus                == ConfigFormEntry->Bus) &&\r
+          (TempVariable->Device.Device             == ConfigFormEntry->Device) &&\r
+          (TempVariable->Device.Function           == ConfigFormEntry->Function) &&\r
+          (TempVariable->Device.Port               == ConfigFormEntry->Port) &&\r
+          (TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {\r
+        //\r
+        // Found the node for the HDD password device.\r
+        // Update the HDD password request.\r
+        //\r
+        CopyMem (&TempVariable->Request, &ConfigFormEntry->IfrData.Request, sizeof (HDD_PASSWORD_REQUEST));\r
+        NewVariable = Variable;\r
+        NewVariableSize = VariableSize;\r
+        break;\r
+      }\r
+      TempVariableSize -= sizeof (HDD_PASSWORD_REQUEST_VARIABLE);\r
+      TempVariable += 1;\r
+    }\r
+    if (NewVariable == NULL) {\r
+      //\r
+      // The node for the HDD password device is not found.\r
+      // Create node for the HDD password device.\r
+      //\r
+      NewVariableSize = VariableSize + sizeof (HDD_PASSWORD_REQUEST_VARIABLE);\r
+      NewVariable = AllocateZeroPool (NewVariableSize);\r
+      ASSERT (NewVariable != NULL);\r
+      CopyMem (NewVariable, Variable, VariableSize);\r
+      TempVariable = (HDD_PASSWORD_REQUEST_VARIABLE *) ((UINTN) NewVariable + VariableSize);\r
+      TempVariable->Device.Bus                = (UINT8) ConfigFormEntry->Bus;\r
+      TempVariable->Device.Device             = (UINT8) ConfigFormEntry->Device;\r
+      TempVariable->Device.Function           = (UINT8) ConfigFormEntry->Function;\r
+      TempVariable->Device.Port               = ConfigFormEntry->Port;\r
+      TempVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;\r
+      CopyMem (&TempVariable->Request, &ConfigFormEntry->IfrData.Request, sizeof (HDD_PASSWORD_REQUEST));\r
+    }\r
+  } else {\r
+    NewVariableSize = sizeof (HDD_PASSWORD_REQUEST_VARIABLE);\r
+    NewVariable = AllocateZeroPool (NewVariableSize);\r
+    ASSERT (NewVariable != NULL);\r
+    NewVariable->Device.Bus                = (UINT8) ConfigFormEntry->Bus;\r
+    NewVariable->Device.Device             = (UINT8) ConfigFormEntry->Device;\r
+    NewVariable->Device.Function           = (UINT8) ConfigFormEntry->Function;\r
+    NewVariable->Device.Port               = ConfigFormEntry->Port;\r
+    NewVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;\r
+    CopyMem (&NewVariable->Request, &ConfigFormEntry->IfrData.Request, sizeof (HDD_PASSWORD_REQUEST));\r
+  }\r
+  Status = gRT->SetVariable (\r
+                  HDD_PASSWORD_REQUEST_VARIABLE_NAME,\r
+                  &mHddPasswordVendorGuid,\r
+                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                  NewVariableSize,\r
+                  NewVariable\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_INFO, "HddPasswordRequest variable set failed (%r)\n", Status));\r
+  }\r
+  if (NewVariable != Variable) {\r
+    FreePool (NewVariable);\r
+  }\r
+  if (Variable != NULL) {\r
+    FreePool (Variable);\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+  Get the HDD Password configuration form entry by the index of the goto opcode actived.\r
+\r
+  @param[in]  Index The 0-based index of the goto opcode actived.\r
+\r
+  @return The HDD Password configuration form entry found.\r
+**/\r
+HDD_PASSWORD_CONFIG_FORM_ENTRY *\r
+HddPasswordGetConfigFormEntryByIndex (\r
+  IN UINT32 Index\r
+  )\r
+{\r
+  LIST_ENTRY                     *Entry;\r
+  UINT32                         CurrentIndex;\r
+  HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry;\r
+\r
+  CurrentIndex    = 0;\r
+  ConfigFormEntry = NULL;\r
+\r
+  EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {\r
+    if (CurrentIndex == Index) {\r
+      ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);\r
+      break;\r
+    }\r
+\r
+    CurrentIndex++;\r
+  }\r
+\r
+  return ConfigFormEntry;\r
+}\r
+\r
+/**\r
+  This function allows the caller to request the current\r
+  configuration for one or more named elements. The resulting\r
+  string is in <ConfigAltResp> format. Any and all alternative\r
+  configuration strings shall also be appended to the end of the\r
+  current configuration string. If they are, they must appear\r
+  after the current configuration. They must contain the same\r
+  routing (GUID, NAME, PATH) as the current configuration string.\r
+  They must have an additional description indicating the type of\r
+  alternative configuration the string represents,\r
+  "ALTCFG=<StringToken>". That <StringToken> (when\r
+  converted from Hex UNICODE to binary) is a reference to a\r
+  string in the associated string pack.\r
+\r
+  @param[in] This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in] Request    A null-terminated Unicode string in\r
+                        <ConfigRequest> format. Note that this\r
+                        includes the routing information as well as\r
+                        the configurable name / value pairs. It is\r
+                        invalid for this string to be in\r
+                        <MultiConfigRequest> format.\r
+  @param[out] Progress  On return, points to a character in the\r
+                        Request string. Points to the string's null\r
+                        terminator if request was successful. Points\r
+                        to the most recent "&" before the first\r
+                        failing name / value pair (or the beginning\r
+                        of the string if the failure is in the first\r
+                        name / value pair) if the request was not\r
+                        successful.\r
+  @param[out] Results   A null-terminated Unicode string in\r
+                        <ConfigAltResp> format which has all values\r
+                        filled in for the names in the Request string.\r
+                        String to be allocated by the called function.\r
+\r
+  @retval EFI_SUCCESS             The Results string is filled with the\r
+                                  values corresponding to all requested\r
+                                  names.\r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  @retval EFI_INVALID_PARAMETER   For example, passing in a NULL\r
+                                  for the Request parameter\r
+                                  would result in this type of\r
+                                  error. In this case, the\r
+                                  Progress parameter would be\r
+                                  set to NULL.\r
+  @retval EFI_NOT_FOUND           Routing data doesn't match any\r
+                                  known driver. Progress set to the\r
+                                  first character in the routing header.\r
+                                  Note: There is no requirement that the\r
+                                  driver validate the routing data. It\r
+                                  must skip the <ConfigHdr> in order to\r
+                                  process the names.\r
+  @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set\r
+                                  to most recent & before the\r
+                                  error or the beginning of the\r
+                                  string.\r
+  @retval EFI_INVALID_PARAMETER   Unknown name. Progress points\r
+                                  to the & before the name in\r
+                                  question.Currently not implemented.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HddPasswordFormExtractConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Request,\r
+  OUT EFI_STRING                             *Progress,\r
+  OUT EFI_STRING                             *Results\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            BufferSize;\r
+  HDD_PASSWORD_CONFIG              *IfrData;\r
+  HDD_PASSWORD_DXE_PRIVATE_DATA    *Private;\r
+  EFI_STRING                       ConfigRequestHdr;\r
+  EFI_STRING                       ConfigRequest;\r
+  BOOLEAN                          AllocatedRequest;\r
+  UINTN                            Size;\r
+\r
+  if (Progress == NULL || Results == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *Progress = Request;\r
+  if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mHddPasswordVendorGuid, mHddPasswordVendorStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ConfigRequestHdr = NULL;\r
+  ConfigRequest    = NULL;\r
+  AllocatedRequest = FALSE;\r
+  Size             = 0;\r
+\r
+  Private = HDD_PASSWORD_DXE_PRIVATE_FROM_THIS (This);\r
+  IfrData = AllocateZeroPool (sizeof (HDD_PASSWORD_CONFIG));\r
+  ASSERT (IfrData != NULL);\r
+  if (Private->Current != NULL) {\r
+    CopyMem (IfrData, &Private->Current->IfrData, sizeof (HDD_PASSWORD_CONFIG));\r
+  }\r
+\r
+  //\r
+  // Convert buffer data to <ConfigResp> by helper function BlockToConfig()\r
+  //\r
+  BufferSize = sizeof (HDD_PASSWORD_CONFIG);\r
+  ConfigRequest = Request;\r
+  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
+    //\r
+    // Request has no request element, construct full request string.\r
+    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
+    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator\r
+    //\r
+    ConfigRequestHdr = HiiConstructConfigHdr (&mHddPasswordVendorGuid, mHddPasswordVendorStorageName, Private->DriverHandle);\r
+    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
+    ConfigRequest = AllocateZeroPool (Size);\r
+    ASSERT (ConfigRequest != NULL);\r
+    AllocatedRequest = TRUE;\r
+    UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);\r
+    FreePool (ConfigRequestHdr);\r
+  }\r
+  Status = gHiiConfigRouting->BlockToConfig (\r
+                                gHiiConfigRouting,\r
+                                ConfigRequest,\r
+                                (UINT8 *) IfrData,\r
+                                BufferSize,\r
+                                Results,\r
+                                Progress\r
+                                );\r
+  FreePool (IfrData);\r
+  //\r
+  // Free the allocated config request string.\r
+  //\r
+  if (AllocatedRequest) {\r
+    FreePool (ConfigRequest);\r
+    ConfigRequest = NULL;\r
+  }\r
+\r
+  //\r
+  // Set Progress string to the original request string.\r
+  //\r
+  if (Request == NULL) {\r
+    *Progress = NULL;\r
+  } else if (StrStr (Request, L"OFFSET") == NULL) {\r
+    *Progress = Request + StrLen (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function applies changes in a driver's configuration.\r
+  Input is a Configuration, which has the routing data for this\r
+  driver followed by name / value configuration pairs. The driver\r
+  must apply those pairs to its configurable storage. If the\r
+  driver's configuration is stored in a linear block of data\r
+  and the driver's name / value pairs are in <BlockConfig>\r
+  format, it may use the ConfigToBlock helper function (above) to\r
+  simplify the job. Currently not implemented.\r
+\r
+  @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]  Configuration  A null-terminated Unicode string in\r
+                             <ConfigString> format.\r
+  @param[out] Progress       A pointer to a string filled in with the\r
+                             offset of the most recent '&' before the\r
+                             first failing name / value pair (or the\r
+                             beginn ing of the string if the failure\r
+                             is in the first name / value pair) or\r
+                             the terminating NULL if all was\r
+                             successful.\r
+\r
+  @retval EFI_SUCCESS             The results have been distributed or are\r
+                                  awaiting distribution.\r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the\r
+                                  Results parameter would result\r
+                                  in this type of error.\r
+  @retval EFI_NOT_FOUND           Target for the specified routing data\r
+                                  was not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HddPasswordFormRouteConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Configuration,\r
+  OUT EFI_STRING                             *Progress\r
+  )\r
+{\r
+  if (Configuration == NULL || Progress == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check routing data in <ConfigHdr>.\r
+  // Note: if only one Storage is used, then this checking could be skipped.\r
+  //\r
+  if (!HiiIsConfigHdrMatch (Configuration, &mHddPasswordVendorGuid, mHddPasswordVendorStorageName)) {\r
+    *Progress = Configuration;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  *Progress = Configuration + StrLen (Configuration);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function is called to provide results data to the driver.\r
+  This data consists of a unique key that is used to identify\r
+  which data is either being passed back or being asked for.\r
+\r
+  @param[in]  This               Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]  Action             Specifies the type of action taken by the browser.\r
+  @param[in]  QuestionId         A unique value which is sent to the original\r
+                                 exporting driver so that it can identify the type\r
+                                 of data to expect. The format of the data tends to\r
+                                 vary based on the opcode that enerated the callback.\r
+  @param[in]  Type               The type of value for the question.\r
+  @param[in]  Value              A pointer to the data being sent to the original\r
+                                 exporting driver.\r
+  @param[out]  ActionRequest     On return, points to the action requested by the\r
+                                 callback function.\r
+\r
+  @retval EFI_SUCCESS            The callback successfully handled the action.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the\r
+                                 variable and its data.\r
+  @retval EFI_DEVICE_ERROR       The variable could not be saved.\r
+  @retval EFI_UNSUPPORTED        The specified Action is not supported by the\r
+                                 callback.Currently not implemented.\r
+  @retval EFI_INVALID_PARAMETERS Passing in wrong parameter.\r
+  @retval Others                 Other errors as indicated.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HddPasswordFormCallback (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  EFI_BROWSER_ACTION                     Action,\r
+  IN  EFI_QUESTION_ID                        QuestionId,\r
+  IN  UINT8                                  Type,\r
+  IN  EFI_IFR_TYPE_VALUE                     *Value,\r
+  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest\r
+  )\r
+{\r
+  HDD_PASSWORD_DXE_PRIVATE_DATA   *Private;\r
+  EFI_STRING_ID                    DeviceFormTitleToken;\r
+  HDD_PASSWORD_CONFIG              *IfrData;\r
+  HDD_PASSWORD_CONFIG_FORM_ENTRY   *ConfigFormEntry;\r
+\r
+  if (ActionRequest != NULL) {\r
+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;\r
+  } else {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_CHANGED)) {\r
+    //\r
+    // Do nothing for other UEFI Action. Only do call back when data is changing or changed.\r
+    //\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Private = HDD_PASSWORD_DXE_PRIVATE_FROM_THIS (This);\r
+\r
+  //\r
+  // Retrive data from Browser\r
+  //\r
+  IfrData = AllocateZeroPool (sizeof (HDD_PASSWORD_CONFIG));\r
+  ASSERT (IfrData != NULL);\r
+  if (!HiiGetBrowserData (&mHddPasswordVendorGuid, mHddPasswordVendorStorageName, sizeof (HDD_PASSWORD_CONFIG), (UINT8 *) IfrData)) {\r
+    FreePool (IfrData);\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  switch (QuestionId) {\r
+  case KEY_HDD_USER_PASSWORD:\r
+    if (Action == EFI_BROWSER_ACTION_CHANGED) {\r
+      DEBUG ((DEBUG_INFO, "KEY_HDD_USER_PASSWORD\n"));\r
+      ConfigFormEntry = Private->Current;\r
+      ConfigFormEntry->IfrData.Request.UserPassword = Value->b;\r
+      SaveHddPasswordRequest (ConfigFormEntry);\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;\r
+    }\r
+    break;\r
+  case KEY_HDD_MASTER_PASSWORD:\r
+    if (Action == EFI_BROWSER_ACTION_CHANGED) {\r
+      DEBUG ((DEBUG_INFO, "KEY_HDD_MASTER_PASSWORD\n"));\r
+      ConfigFormEntry = Private->Current;\r
+      ConfigFormEntry->IfrData.Request.MasterPassword = Value->b;\r
+      SaveHddPasswordRequest (ConfigFormEntry);\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;\r
+    }\r
+    break;\r
+\r
+  default:\r
+    if ((QuestionId >= KEY_HDD_DEVICE_ENTRY_BASE) && (QuestionId < (mNumberOfHddDevices + KEY_HDD_DEVICE_ENTRY_BASE))) {\r
+      if (Action == EFI_BROWSER_ACTION_CHANGING) {\r
+        //\r
+        // In case goto the device configuration form, update the device form title.\r
+        //\r
+        ConfigFormEntry = HddPasswordGetConfigFormEntryByIndex ((UINT32) (QuestionId - KEY_HDD_DEVICE_ENTRY_BASE));\r
+        ASSERT (ConfigFormEntry != NULL);\r
+\r
+        DeviceFormTitleToken = (EFI_STRING_ID) STR_HDD_SECURITY_HD;\r
+        HiiSetString (Private->HiiHandle, DeviceFormTitleToken, ConfigFormEntry->HddString, NULL);\r
+\r
+        Private->Current = ConfigFormEntry;\r
+        CopyMem (IfrData, &ConfigFormEntry->IfrData, sizeof (HDD_PASSWORD_CONFIG));\r
+      }\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  //\r
+  // Pass changed uncommitted data back to Form Browser\r
+  //\r
+  HiiSetBrowserData (&mHddPasswordVendorGuid, mHddPasswordVendorStorageName, sizeof (HDD_PASSWORD_CONFIG), (UINT8 *) IfrData, NULL);\r
+\r
+  FreePool (IfrData);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Updates the HDD Password configuration form to add an entry for the attached\r
+  ata harddisk device specified by the Controller.\r
+\r
+  @param[in] HiiHandle            The HII Handle associated with the registered package list.\r
+  @param[in] AtaPassThru          Pointer to ATA_PASSTHRU instance.\r
+  @param[in] PciIo                Pointer to PCI_IO instance.\r
+  @param[in] Controller           The controller handle of the attached ata controller.\r
+  @param[in] Bus                  The bus number of ATA controller.\r
+  @param[in] Device               The device number of ATA controller.\r
+  @param[in] Function             The function number of ATA controller.\r
+  @param[in] Port                 The port number of attached ATA device.\r
+  @param[in] PortMultiplierPort   The port number of port multiplier of attached ATA device.\r
+\r
+  @retval EFI_SUCCESS             The Hdd Password configuration form is updated.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+  @retval Others                  Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+HddPasswordConfigUpdateForm (\r
+  IN EFI_HII_HANDLE              HiiHandle,\r
+  IN EFI_ATA_PASS_THRU_PROTOCOL  *AtaPassThru,\r
+  IN EFI_PCI_IO_PROTOCOL         *PciIo,\r
+  IN EFI_HANDLE                  Controller,\r
+  IN UINTN                       Bus,\r
+  IN UINTN                       Device,\r
+  IN UINTN                       Function,\r
+  IN UINT16                      Port,\r
+  IN UINT16                      PortMultiplierPort\r
+  )\r
+{\r
+  LIST_ENTRY                       *Entry;\r
+  HDD_PASSWORD_CONFIG_FORM_ENTRY   *ConfigFormEntry;\r
+  BOOLEAN                          EntryExisted;\r
+  EFI_STATUS                       Status;\r
+  VOID                             *StartOpCodeHandle;\r
+  VOID                             *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL               *StartLabel;\r
+  EFI_IFR_GUID_LABEL               *EndLabel;\r
+  CHAR16                           HddString[40];\r
+  ATA_IDENTIFY_DATA                IdentifyData;\r
+  EFI_DEVICE_PATH_PROTOCOL         *AtaDeviceNode;\r
+\r
+  ConfigFormEntry = NULL;\r
+  EntryExisted    = FALSE;\r
+\r
+  EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {\r
+    ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);\r
+\r
+    if ((ConfigFormEntry->Bus == Bus) &&\r
+        (ConfigFormEntry->Device == Device) &&\r
+        (ConfigFormEntry->Function == Function) &&\r
+        (ConfigFormEntry->Port == Port) &&\r
+        (ConfigFormEntry->PortMultiplierPort == PortMultiplierPort)) {\r
+      EntryExisted = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (!EntryExisted) {\r
+    //\r
+    // Add a new form.\r
+    //\r
+    ConfigFormEntry = AllocateZeroPool (sizeof (HDD_PASSWORD_CONFIG_FORM_ENTRY));\r
+    if (ConfigFormEntry == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    InitializeListHead (&ConfigFormEntry->Link);\r
+    ConfigFormEntry->Controller         = Controller;\r
+    ConfigFormEntry->Bus                = Bus;\r
+    ConfigFormEntry->Device             = Device;\r
+    ConfigFormEntry->Function           = Function;\r
+    ConfigFormEntry->Port               = Port;\r
+    ConfigFormEntry->PortMultiplierPort = PortMultiplierPort;\r
+    ConfigFormEntry->AtaPassThru        = AtaPassThru;\r
+\r
+    DEBUG ((DEBUG_INFO, "HddPasswordDxe: Create new form for device[%d][%d] at Bus 0x%x Dev 0x%x Func 0x%x\n", Port, PortMultiplierPort, Bus, Device, Function));\r
+\r
+    //\r
+    // Construct the device path for the HDD password device\r
+    //\r
+    Status = AtaPassThru->BuildDevicePath (\r
+                            AtaPassThru,\r
+                            Port,\r
+                            PortMultiplierPort,\r
+                            &AtaDeviceNode\r
+                            );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    ConfigFormEntry->DevicePath = AppendDevicePathNode (DevicePathFromHandle (Controller), AtaDeviceNode);\r
+    FreePool (AtaDeviceNode);\r
+    if (ConfigFormEntry->DevicePath == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    //\r
+    // Get attached harddisk model number\r
+    //\r
+    Status = GetHddDeviceIdentifyData (AtaPassThru, Port, PortMultiplierPort, &IdentifyData);\r
+    ASSERT_EFI_ERROR (Status);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    GetHddDeviceModelNumber (&IdentifyData, HddString);\r
+    //\r
+    // Compose the HDD title string and help string of this port and create a new EFI_STRING_ID.\r
+    //\r
+    UnicodeSPrint (ConfigFormEntry->HddString, sizeof (ConfigFormEntry->HddString), L"HDD %d:%s", mNumberOfHddDevices, HddString);\r
+    ConfigFormEntry->TitleToken     = HiiSetString (HiiHandle, 0, ConfigFormEntry->HddString, NULL);\r
+    ConfigFormEntry->TitleHelpToken = HiiSetString (HiiHandle, 0, L"Request to set HDD Password", NULL);\r
+\r
+    GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);\r
+\r
+    InsertTailList (&mHddPasswordConfigFormList, &ConfigFormEntry->Link);\r
+\r
+    //\r
+    // Init OpCode Handle\r
+    //\r
+    StartOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+    ASSERT (StartOpCodeHandle != NULL);\r
+\r
+    EndOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+    ASSERT (EndOpCodeHandle != NULL);\r
+\r
+    //\r
+    // Create Hii Extend Label OpCode as the start opcode\r
+    //\r
+    StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));\r
+    StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+    StartLabel->Number       = HDD_DEVICE_ENTRY_LABEL;\r
+\r
+    //\r
+    // Create Hii Extend Label OpCode as the end opcode\r
+    //\r
+    EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));\r
+    EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+    EndLabel->Number       = HDD_DEVICE_LABEL_END;\r
+\r
+    mNumberOfHddDevices = 0;\r
+    EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {\r
+      ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);\r
+\r
+      HiiCreateGotoOpCode (\r
+        StartOpCodeHandle,                                // Container for dynamic created opcodes\r
+        FORMID_HDD_DEVICE_FORM,                           // Target Form ID\r
+        ConfigFormEntry->TitleToken,                      // Prompt text\r
+        ConfigFormEntry->TitleHelpToken,                  // Help text\r
+        EFI_IFR_FLAG_CALLBACK,                            // Question flag\r
+        (UINT16) (KEY_HDD_DEVICE_ENTRY_BASE + mNumberOfHddDevices)   // Question ID\r
+        );\r
+\r
+      mNumberOfHddDevices++;\r
+    }\r
+\r
+    HiiUpdateForm (\r
+      HiiHandle,\r
+      &mHddPasswordVendorGuid,\r
+      FORMID_HDD_MAIN_FORM,\r
+      StartOpCodeHandle,\r
+      EndOpCodeHandle\r
+      );\r
+\r
+    HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+    HiiFreeOpCodeHandle (EndOpCodeHandle);\r
+\r
+    //\r
+    // Check if device is locked and prompt for password.\r
+    //\r
+    HddPasswordRequestPassword (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);\r
+\r
+    //\r
+    // Process HDD password request from last boot.\r
+    //\r
+    ProcessHddPasswordRequest (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Ata Pass Thru Protocol notification event handler.\r
+\r
+  Check attached harddisk status to see if it's locked. If yes, then pop up a password windows to require user input.\r
+  It also registers a form for user configuration on Hdd password configuration.\r
+\r
+  @param[in] Event    Event whose notification function is being invoked.\r
+  @param[in] Context  Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+HddPasswordNotificationEvent (\r
+  IN  EFI_EVENT       Event,\r
+  IN  VOID            *Context\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HDD_PASSWORD_DXE_PRIVATE_DATA     *Private;\r
+  EFI_ATA_PASS_THRU_PROTOCOL        *AtaPassThru;\r
+  UINT16                            Port;\r
+  UINT16                            PortMultiplierPort;\r
+  EFI_HANDLE                        Controller;\r
+  EFI_HANDLE                        *HandleBuffer;\r
+  UINTN                             HandleCount;\r
+  UINTN                             Index;\r
+  EFI_PCI_IO_PROTOCOL               *PciIo;\r
+  UINTN                             SegNum;\r
+  UINTN                             BusNum;\r
+  UINTN                             DevNum;\r
+  UINTN                             FuncNum;\r
+\r
+  if (mHddPasswordEndOfDxe) {\r
+    gBS->CloseEvent (Event);\r
+    return;\r
+  }\r
+\r
+  Private = (HDD_PASSWORD_DXE_PRIVATE_DATA *)Context;\r
+\r
+  //\r
+  // Locate all handles of AtaPassThru protocol\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiAtaPassThruProtocolGuid,\r
+                  NULL,\r
+                  &HandleCount,\r
+                  &HandleBuffer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Check attached hard disk status to see if it's locked\r
+  //\r
+  for (Index = 0; Index < HandleCount; Index += 1) {\r
+    Controller = HandleBuffer[Index];\r
+    Status = gBS->HandleProtocol (\r
+                    Controller,\r
+                    &gEfiAtaPassThruProtocolGuid,\r
+                    (VOID **) &AtaPassThru\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Ignore those logical ATA_PASS_THRU instance.\r
+    //\r
+    if ((AtaPassThru->Mode->Attributes & EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL) == 0) {\r
+      continue;\r
+    }\r
+\r
+    Status = gBS->HandleProtocol (\r
+                    Controller,\r
+                    &gEfiPciIoProtocolGuid,\r
+                    (VOID **) &PciIo\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    Status = PciIo->GetLocation (\r
+                      PciIo,\r
+                      &SegNum,\r
+                      &BusNum,\r
+                      &DevNum,\r
+                      &FuncNum\r
+                      );\r
+    ASSERT_EFI_ERROR (Status);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Assume and only support Segment == 0.\r
+    //\r
+    ASSERT (SegNum == 0);\r
+\r
+    //\r
+    // traverse all attached harddisk devices to update form and unlock it\r
+    //\r
+    Port = 0xFFFF;\r
+\r
+    while (TRUE) {\r
+      Status = AtaPassThru->GetNextPort (AtaPassThru, &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 = AtaPassThru->GetNextDevice (AtaPassThru, 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
+        // Find out the attached harddisk devices.\r
+        // Try to add a HDD Password configuration page for the attached devices.\r
+        //\r
+        gBS->RestoreTPL (TPL_APPLICATION);\r
+        Status = HddPasswordConfigUpdateForm (Private->HiiHandle, AtaPassThru, PciIo, Controller, BusNum, DevNum, FuncNum, Port, PortMultiplierPort);\r
+        gBS->RaiseTPL (TPL_CALLBACK);\r
+        if (EFI_ERROR (Status)) {\r
+          break;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  FreePool (HandleBuffer);\r
+  return ;\r
+}\r
+\r
+/**\r
+  Initialize the HDD Password configuration form.\r
+\r
+  @param[out] Instance             Pointer to private instance.\r
+\r
+  @retval EFI_SUCCESS              The HDD Password configuration form is initialized.\r
+  @retval EFI_OUT_OF_RESOURCES     Failed to allocate memory.\r
+  @retval Others                   Other errors as indicated.\r
+**/\r
+EFI_STATUS\r
+HddPasswordConfigFormInit (\r
+  OUT HDD_PASSWORD_DXE_PRIVATE_DATA    **Instance\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  HDD_PASSWORD_DXE_PRIVATE_DATA    *Private;\r
+\r
+  InitializeListHead (&mHddPasswordConfigFormList);\r
+\r
+  Private = AllocateZeroPool (sizeof (HDD_PASSWORD_DXE_PRIVATE_DATA));\r
+  if (Private == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Private->Signature   = HDD_PASSWORD_DXE_PRIVATE_SIGNATURE;\r
+\r
+  Private->ConfigAccess.ExtractConfig = HddPasswordFormExtractConfig;\r
+  Private->ConfigAccess.RouteConfig   = HddPasswordFormRouteConfig;\r
+  Private->ConfigAccess.Callback      = HddPasswordFormCallback;\r
+\r
+  //\r
+  // Install Device Path Protocol and Config Access protocol to driver handle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Private->DriverHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  &mHddPasswordHiiVendorDevicePath,\r
+                  &gEfiHiiConfigAccessProtocolGuid,\r
+                  &Private->ConfigAccess,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool(Private);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Publish our HII data\r
+  //\r
+  Private->HiiHandle = HiiAddPackages (\r
+                         &mHddPasswordVendorGuid,\r
+                         Private->DriverHandle,\r
+                         HddPasswordDxeStrings,\r
+                         HddPasswordBin,\r
+                         NULL\r
+                         );\r
+  if (Private->HiiHandle == NULL) {\r
+    FreePool(Private);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  *Instance = Private;\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Main entry for this driver.\r
+\r
+  @param ImageHandle     Image handle this driver.\r
+  @param SystemTable     Pointer to SystemTable.\r
+\r
+  @retval EFI_SUCESS     This function always complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HddPasswordDxeInit (\r
+  IN EFI_HANDLE                   ImageHandle,\r
+  IN EFI_SYSTEM_TABLE             *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  HDD_PASSWORD_DXE_PRIVATE_DATA  *Private;\r
+  EFI_EVENT                      Registration;\r
+  EFI_EVENT                      EndOfDxeEvent;\r
+  EDKII_VARIABLE_LOCK_PROTOCOL   *VariableLock;\r
+\r
+  Private = NULL;\r
+\r
+  //\r
+  // Initialize the configuration form of HDD Password.\r
+  //\r
+  Status = HddPasswordConfigFormInit (&Private);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Register HddPasswordNotificationEvent() notify function.\r
+  //\r
+  EfiCreateProtocolNotifyEvent (\r
+    &gEfiAtaPassThruProtocolGuid,\r
+    TPL_CALLBACK,\r
+    HddPasswordNotificationEvent,\r
+    (VOID *)Private,\r
+    &Registration\r
+    );\r
+\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  HddPasswordEndOfDxeEventNotify,\r
+                  NULL,\r
+                  &gEfiEndOfDxeEventGroupGuid,\r
+                  &EndOfDxeEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Make HDD_PASSWORD_VARIABLE_NAME varible read-only.\r
+  //\r
+  Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = VariableLock->RequestToLock (\r
+                             VariableLock,\r
+                             HDD_PASSWORD_VARIABLE_NAME,\r
+                             &mHddPasswordVendorGuid\r
+                             );\r
+    DEBUG ((DEBUG_INFO, "%a(): Lock %s variable (%r)\n", __FUNCTION__, HDD_PASSWORD_VARIABLE_NAME, Status));\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  return Status;\r
+}\r
diff --git a/SecurityPkg/HddPassword/HddPasswordDxe.h b/SecurityPkg/HddPassword/HddPasswordDxe.h
new file mode 100644 (file)
index 0000000..41db055
--- /dev/null
@@ -0,0 +1,148 @@
+/** @file\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
+#ifndef _HDD_PASSWORD_DXE_H_\r
+#define _HDD_PASSWORD_DXE_H_\r
+\r
+#include <Uefi.h>\r
+\r
+#include <IndustryStandard/Atapi.h>\r
+#include <IndustryStandard/Pci.h>\r
+#include <Protocol/AtaPassThru.h>\r
+#include <Protocol/PciIo.h>\r
+#include <Protocol/HiiConfigAccess.h>\r
+#include <Protocol/VariableLock.h>\r
+\r
+#include <Guid/MdeModuleHii.h>\r
+#include <Guid/EventGroup.h>\r
+#include <Guid/S3StorageDeviceInitList.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DxeServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiHiiServicesLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/LockBoxLib.h>\r
+#include <Library/S3BootScriptLib.h>\r
+#include <Library/PciLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+\r
+#include "HddPasswordCommon.h"\r
+#include "HddPasswordHiiDataStruc.h"\r
+\r
+//\r
+// This is the generated IFR binary data for each formset defined in VFR.\r
+// This data array is ready to be used as input of HiiAddPackages() to\r
+// create a packagelist (which contains Form packages, String packages, etc).\r
+//\r
+extern UINT8  HddPasswordBin[];\r
+\r
+//\r
+// This is the generated String package data for all .UNI files.\r
+// This data array is ready to be used as input of HiiAddPackages() to\r
+// create a packagelist (which contains Form packages, String packages, etc).\r
+//\r
+extern UINT8  HddPasswordDxeStrings[];\r
+\r
+#define HDD_PASSWORD_DXE_PRIVATE_SIGNATURE SIGNATURE_32 ('H', 'D', 'D', 'P')\r
+\r
+typedef struct _HDD_PASSWORD_CONFIG_FORM_ENTRY {\r
+  LIST_ENTRY                    Link;\r
+  EFI_HANDLE                    Controller;\r
+  UINTN                         Bus;\r
+  UINTN                         Device;\r
+  UINTN                         Function;\r
+  UINT16                        Port;\r
+  UINT16                        PortMultiplierPort;\r
+  EFI_DEVICE_PATH_PROTOCOL      *DevicePath;\r
+  CHAR16                        HddString[64];\r
+  CHAR8                         Password[HDD_PASSWORD_MAX_LENGTH];\r
+  EFI_STRING_ID                 TitleToken;\r
+  EFI_STRING_ID                 TitleHelpToken;\r
+\r
+  HDD_PASSWORD_CONFIG           IfrData;\r
+  EFI_ATA_PASS_THRU_PROTOCOL    *AtaPassThru;\r
+} HDD_PASSWORD_CONFIG_FORM_ENTRY;\r
+\r
+typedef struct _HDD_PASSWORD_DXE_PRIVATE_DATA {\r
+  UINTN                            Signature;\r
+  EFI_HANDLE                       DriverHandle;\r
+  EFI_HII_HANDLE                   HiiHandle;\r
+  EFI_HII_CONFIG_ACCESS_PROTOCOL   ConfigAccess;\r
+  HDD_PASSWORD_CONFIG_FORM_ENTRY   *Current;\r
+} HDD_PASSWORD_DXE_PRIVATE_DATA;\r
+\r
+#define HDD_PASSWORD_DXE_PRIVATE_FROM_THIS(a)  CR (a, HDD_PASSWORD_DXE_PRIVATE_DATA, ConfigAccess, HDD_PASSWORD_DXE_PRIVATE_SIGNATURE)\r
+\r
+//\r
+//Iterate through the doule linked list. NOT delete safe\r
+//\r
+#define EFI_LIST_FOR_EACH(Entry, ListHead)    \\r
+  for (Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink)\r
+\r
+#define PASSWORD_SALT_SIZE                  32\r
+\r
+#define HDD_PASSWORD_REQUEST_VARIABLE_NAME  L"HddPasswordRequest"\r
+\r
+//\r
+// It needs to be locked before EndOfDxe.\r
+//\r
+#define HDD_PASSWORD_VARIABLE_NAME          L"HddPassword"\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct {\r
+  HDD_PASSWORD_DEVICE   Device;\r
+  HDD_PASSWORD_REQUEST  Request;\r
+} HDD_PASSWORD_REQUEST_VARIABLE;\r
+\r
+//\r
+// It will be used to validate HDD password when the device is at frozen state.\r
+//\r
+typedef struct {\r
+  HDD_PASSWORD_DEVICE   Device;\r
+  UINT8                 PasswordHash[SHA256_DIGEST_SIZE];\r
+  UINT8                 PasswordSalt[PASSWORD_SALT_SIZE];\r
+} HDD_PASSWORD_VARIABLE;\r
+\r
+///\r
+/// HII specific Vendor Device Path definition.\r
+///\r
+typedef struct {\r
+  VENDOR_DEVICE_PATH           VendorDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL     End;\r
+} HII_VENDOR_DEVICE_PATH;\r
+\r
+#pragma pack()\r
+\r
+//\r
+// Time out value for ATA pass through protocol\r
+//\r
+#define ATA_TIMEOUT        EFI_TIMER_PERIOD_SECONDS (3)\r
+\r
+typedef struct {\r
+  UINT32                   Address;\r
+  S3_BOOT_SCRIPT_LIB_WIDTH Width;\r
+} HDD_HC_PCI_REGISTER_SAVE;\r
+\r
+#endif\r
diff --git a/SecurityPkg/HddPassword/HddPasswordDxe.inf b/SecurityPkg/HddPassword/HddPasswordDxe.inf
new file mode 100644 (file)
index 0000000..7a3fc2f
--- /dev/null
@@ -0,0 +1,75 @@
+## @file\r
+#  HddPasswordDxe driver which is used to set/clear hdd password at attached harddisk\r
+#  devices.\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 of the BSD License\r
+#  which accompanies this distribution.  The 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
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = HddPasswordDxe\r
+  FILE_GUID                      = 9BD549CD-86D1-4925-9F7D-3686DDD876FC\r
+  MODULE_TYPE                    = DXE_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = HddPasswordDxeInit\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  HddPasswordDxe.c\r
+  HddPasswordDxe.h\r
+  HddPasswordHiiDataStruc.h\r
+  HddPassword.vfr\r
+  HddPasswordStrings.uni\r
+  HddPasswordCommon.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  CryptoPkg/CryptoPkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  MemoryAllocationLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiHiiServicesLib\r
+  UefiRuntimeServicesTableLib\r
+  DxeServicesTableLib\r
+  BaseMemoryLib\r
+  DebugLib\r
+  HiiLib\r
+  PrintLib\r
+  UefiLib\r
+  LockBoxLib\r
+  S3BootScriptLib\r
+  PciLib\r
+  BaseCryptLib\r
+\r
+[Guids]\r
+  gEfiIfrTianoGuid                              ## CONSUMES ## GUID\r
+  gEfiEndOfDxeEventGroupGuid                    ## CONSUMES ## Event\r
+  gS3StorageDeviceInitListGuid                  ## SOMETIMES_PRODUCES ## UNDEFINED\r
+\r
+[Protocols]\r
+  gEfiHiiConfigAccessProtocolGuid               ## PRODUCES\r
+  gEfiAtaPassThruProtocolGuid                   ## CONSUMES\r
+  gEfiPciIoProtocolGuid                         ## CONSUMES\r
+  gEdkiiVariableLockProtocolGuid                ## CONSUMES\r
+\r
+[Depex]\r
+  gEfiVariableWriteArchProtocolGuid\r
+\r
diff --git a/SecurityPkg/HddPassword/HddPasswordHiiDataStruc.h b/SecurityPkg/HddPassword/HddPasswordHiiDataStruc.h
new file mode 100644 (file)
index 0000000..608b92d
--- /dev/null
@@ -0,0 +1,63 @@
+/** @file\r
+  HddPassword HII data structure used by the driver.\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
+#ifndef _HDD_PASSWORD_HII_DATASTRUC_H_\r
+#define _HDD_PASSWORD_HII_DATASTRUC_H_\r
+\r
+#include <Guid/HiiPlatformSetupFormset.h>\r
+\r
+#define HDD_PASSWORD_CONFIG_GUID \\r
+  { \\r
+    0x737cded7, 0x448b, 0x4801, { 0xb5, 0x7d, 0xb1, 0x94, 0x83, 0xec, 0x60, 0x6f } \\r
+  }\r
+\r
+#define FORMID_HDD_MAIN_FORM          1\r
+#define FORMID_HDD_DEVICE_FORM        2\r
+\r
+#define HDD_DEVICE_ENTRY_LABEL        0x1234\r
+#define HDD_DEVICE_LABEL_END          0xffff\r
+\r
+#define KEY_HDD_DEVICE_ENTRY_BASE     0x1000\r
+\r
+#define KEY_HDD_USER_PASSWORD         0x101\r
+#define KEY_HDD_MASTER_PASSWORD       0x102\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct {\r
+  UINT8     Supported:1;\r
+  UINT8     Enabled:1;\r
+  UINT8     Locked:1;\r
+  UINT8     Frozen:1;\r
+  UINT8     UserPasswordStatus:1;\r
+  UINT8     MasterPasswordStatus:1;\r
+  UINT8     Reserved:2;\r
+} HDD_PASSWORD_SECURITY_STATUS;\r
+\r
+typedef struct {\r
+  UINT8     UserPassword:1;\r
+  UINT8     MasterPassword:1;\r
+  UINT8     Reserved:6;\r
+} HDD_PASSWORD_REQUEST;\r
+\r
+typedef struct _HDD_PASSWORD_CONFIG {\r
+  HDD_PASSWORD_SECURITY_STATUS  SecurityStatus;\r
+  HDD_PASSWORD_REQUEST          Request;\r
+} HDD_PASSWORD_CONFIG;\r
+\r
+#pragma pack()\r
+\r
+#endif\r
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
diff --git a/SecurityPkg/HddPassword/HddPasswordPei.h b/SecurityPkg/HddPassword/HddPasswordPei.h
new file mode 100644 (file)
index 0000000..813b542
--- /dev/null
@@ -0,0 +1,42 @@
+/** @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
+#ifndef _HDD_PASSWORD_PEI_H_\r
+#define _HDD_PASSWORD_PEI_H_\r
+\r
+#include <PiPei.h>\r
+#include <IndustryStandard/Atapi.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PeimEntryPoint.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/PciLib.h>\r
+#include <Library/LockBoxLib.h>\r
+\r
+#include <Ppi/AtaPassThru.h>\r
+\r
+#include "HddPasswordCommon.h"\r
+\r
+\r
+//\r
+// Time out value for ATA PassThru PPI\r
+//\r
+#define ATA_TIMEOUT                          30000000\r
+\r
+#endif\r
diff --git a/SecurityPkg/HddPassword/HddPasswordPei.inf b/SecurityPkg/HddPassword/HddPasswordPei.inf
new file mode 100644 (file)
index 0000000..d240cc1
--- /dev/null
@@ -0,0 +1,54 @@
+## @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 of the BSD License\r
+#  which accompanies this distribution.  The 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
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = HddPasswordPei\r
+  FILE_GUID                      = 91AD7375-8E8E-49D2-A343-68BC78273955\r
+  MODULE_TYPE                    = PEIM\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = HddPasswordPeiInit\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  HddPasswordPei.c\r
+  HddPasswordPei.h\r
+  HddPasswordCommon.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  PeimEntryPoint\r
+  PeiServicesLib\r
+  DebugLib\r
+  BaseLib\r
+  BaseMemoryLib\r
+  MemoryAllocationLib\r
+  PciLib\r
+  LockBoxLib\r
+\r
+[Ppis]\r
+  gEdkiiPeiAtaPassThruPpiGuid                   ## NOTIFY\r
+\r
+[Depex]\r
+  gEfiPeiMasterBootModePpiGuid\r
+\r
diff --git a/SecurityPkg/HddPassword/HddPasswordStrings.uni b/SecurityPkg/HddPassword/HddPasswordStrings.uni
new file mode 100644 (file)
index 0000000..455ecfc
--- /dev/null
@@ -0,0 +1,48 @@
+// /** @file\r
+// String definitions for HddPassword Setup Form.\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
+#langdef en-US  "English"\r
+\r
+#string STR_HDD_SECURITY_CONFIG               #language en-US "HDD Security Configuration"\r
+\r
+#string STR_SECURITY_HDD_PWD_DESC             #language en-US "HDD Password Description :"\r
+\r
+#string STR_SECURITY_HDD_BANNER_ONE           #language en-US "Allows Access to Set, Modify and Clear HardDisk User and"\r
+#string STR_SECURITY_HDD_BANNER_TWO           #language en-US "Master Passwords."\r
+#string STR_SECURITY_HDD_BANNER_THREE         #language en-US "User Password need to be installed for Enabling Security."\r
+#string STR_SECURITY_HDD_BANNER_FOUR          #language en-US "Master Password can be modified only when succesfully"\r
+#string STR_SECURITY_HDD_BANNER_FIVE          #language en-US "unlocked with User Password in POST."\r
+\r
+#string STR_HDD_SECURITY_HD                   #language en-US "HDD Password"\r
+#string STR_HDD_SECURITY_HELP                 #language en-US "Set HDD Password"\r
+#string STR_HDD_PASSWORD_CONFIG               #language en-US "HDD PASSWORD CONFIGURATION:"\r
+#string STR_SEC_SUPPORTED                     #language en-US "Security Supported        :"\r
+#string STR_SEC_ENABLED                       #language en-US "Security Enabled          :"\r
+#string STR_SEC_LOCKED                        #language en-US "Security Locked           :"\r
+#string STR_SEC_FROZEN                        #language en-US "Security Frozen           :"\r
+#string STR_YES                               #language en-US "Yes"\r
+#string STR_NO                                #language en-US "No"\r
+#string STR_HDD_USER_PASSWORD                 #language en-US "Request to set User Password"\r
+#string STR_HDD_USER_PASSWORD_HELP            #language en-US "Request to set HDD User Password. \n*** Reset is required for the request to be processed in next boot *** \n*** G3 circle is required to disable freeze state when Security Frozen state is Yes, otherwise the request will be ignored. *** "\r
+#string STR_HDD_MASTER_PASSWORD               #language en-US "Request to set Master Password"\r
+#string STR_HDD_MASTER_PASSWORD_HELP          #language en-US "Request to set HDD Master Password. \n*** Reset is required for the request to be processed in next boot *** \n*** G3 circle is required to disable freeze state when Security Frozen state is Yes, otherwise the request will be ignored. *** "\r
+\r
+#string STR_INSTALLED                         #language en-US "INSTALLED"\r
+#string STR_NOT_INSTALLED                     #language en-US "NOT INSTALLED"\r
+#string STR_HDD_USER_PASSWORD_STS             #language en-US "HDD User Password Status  :"\r
+#string STR_HDD_MASTER_PASSWORD_STS           #language en-US "HDD Master Password Status:"\r
+#string STR_NULL                              #language en-US ""\r
+#string STR_EMPTY                             #language en-US ""\r
index ab887e8c4d32f39923d2d201c6fc8a988c87edf1..5577ff068781d9f5a2a07a7b28f50c685f4175b3 100644 (file)
   SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordDxe.inf\r
   SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.inf\r
 \r
+  #\r
+  # HDD Password solution\r
+  #\r
+  SecurityPkg/HddPassword/HddPasswordDxe.inf\r
+  SecurityPkg/HddPassword/HddPasswordPei.inf\r
+\r
 [BuildOptions]\r
    MSFT:*_*_IA32_DLINK_FLAGS = /ALIGN:256\r
   INTEL:*_*_IA32_DLINK_FLAGS = /ALIGN:256\r