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