| /** @file | |
| Implementation of Opal password support library. | |
| Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "OpalPasswordSupportNotify.h" | |
| LIST_ENTRY mDeviceList = INITIALIZE_LIST_HEAD_VARIABLE (mDeviceList); | |
| BOOLEAN gInSmm = FALSE; | |
| EFI_GUID gOpalPasswordNotifyProtocolGuid = OPAL_PASSWORD_NOTIFY_PROTOCOL_GUID; | |
| /** | |
| The function performs determines the available actions for the OPAL_DISK provided. | |
| @param[in] SupportedAttributes The support attribute for the device. | |
| @param[in] LockingFeature The locking status for the device. | |
| @param[in] OwnerShip The ownership for the device. | |
| @param[out] AvalDiskActions Pointer to fill-out with appropriate disk actions. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportGetAvailableActions( | |
| IN OPAL_DISK_SUPPORT_ATTRIBUTE *SupportedAttributes, | |
| IN TCG_LOCKING_FEATURE_DESCRIPTOR *LockingFeature, | |
| IN UINT16 OwnerShip, | |
| OUT OPAL_DISK_ACTIONS *AvalDiskActions | |
| ) | |
| { | |
| BOOLEAN ExistingPassword; | |
| NULL_CHECK(AvalDiskActions); | |
| AvalDiskActions->AdminPass = 1; | |
| AvalDiskActions->UserPass = 0; | |
| AvalDiskActions->DisableUser = 0; | |
| AvalDiskActions->Unlock = 0; | |
| // | |
| // Revert is performed on locking sp, so only allow if locking sp is enabled | |
| // | |
| if (LockingFeature->LockingEnabled) { | |
| AvalDiskActions->Revert = 1; | |
| } | |
| // | |
| // Psid revert is available for any device with media encryption support | |
| // Revert is allowed for any device with media encryption support, however it requires | |
| // | |
| if (SupportedAttributes->MediaEncryption) { | |
| // | |
| // Only allow psid revert if media encryption is enabled. | |
| // Otherwise, someone who steals a disk can psid revert the disk and the user Data is still | |
| // intact and accessible | |
| // | |
| AvalDiskActions->PsidRevert = 1; | |
| AvalDiskActions->RevertKeepDataForced = 0; | |
| // | |
| // Secure erase is performed by generating a new encryption key | |
| // this is only available is encryption is supported | |
| // | |
| AvalDiskActions->SecureErase = 1; | |
| } else { | |
| AvalDiskActions->PsidRevert = 0; | |
| AvalDiskActions->SecureErase = 0; | |
| // | |
| // If no media encryption is supported, then a revert (using password) will not | |
| // erase the Data (since you can't generate a new encryption key) | |
| // | |
| AvalDiskActions->RevertKeepDataForced = 1; | |
| } | |
| if (LockingFeature->Locked) { | |
| AvalDiskActions->Unlock = 1; | |
| } else { | |
| AvalDiskActions->Unlock = 0; | |
| } | |
| // | |
| // Only allow user to set password if an admin password exists | |
| // | |
| ExistingPassword = OpalUtilAdminPasswordExists(OwnerShip, LockingFeature); | |
| AvalDiskActions->UserPass = ExistingPassword; | |
| // | |
| // This will still show up even if there isn't a user, which is fine | |
| // | |
| AvalDiskActions->DisableUser = ExistingPassword; | |
| return TcgResultSuccess; | |
| } | |
| /** | |
| Creates a session with OPAL_UID_ADMIN_SP as OPAL_ADMIN_SP_PSID_AUTHORITY, then reverts device using Admin SP Revert method. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] Psid PSID of device to revert. | |
| @param[in] PsidLength Length of PSID in bytes. | |
| @param[in] DevicePath The device path for the opal devcie. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportPsidRevert( | |
| IN OPAL_SESSION *Session, | |
| IN VOID *Psid, | |
| IN UINT32 PsidLength, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(Psid); | |
| Ret = OpalUtilPsidRevert (Session, Psid, PsidLength); | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, 0, NULL); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Opens a session with OPAL_UID_ADMIN_SP as OPAL_ADMIN_SP_SID_AUTHORITY, | |
| sets OPAL_UID_ADMIN_SP_C_PIN_SID with the new password, | |
| and sets OPAL_LOCKING_SP_C_PIN_ADMIN1 with the new password. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] OldPassword Current admin password | |
| @param[in] OldPasswordLength Length of current admin password in bytes | |
| @param[in] NewPassword New admin password to set | |
| @param[in] NewPasswordLength Length of new password in bytes | |
| @param[in] DevicePath The device path for the opal devcie. | |
| @param[in] SetAdmin Whether set admin password or user password. | |
| TRUE for admin, FALSE for user. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportSetPassword( | |
| IN OPAL_SESSION *Session, | |
| IN VOID *OldPassword, | |
| IN UINT32 OldPasswordLength, | |
| IN VOID *NewPassword, | |
| IN UINT32 NewPasswordLength, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| IN BOOLEAN SetAdmin | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(OldPassword); | |
| NULL_CHECK(NewPassword); | |
| if (SetAdmin) { | |
| Ret = OpalUtilSetAdminPassword(Session, OldPassword, OldPasswordLength, NewPassword, NewPasswordLength); | |
| } else { | |
| Ret = OpalUtilSetUserPassword(Session, OldPassword, OldPasswordLength, NewPassword, NewPasswordLength); | |
| } | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, NewPasswordLength, NewPassword); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Starts a session with OPAL_UID_LOCKING_SP as OPAL_LOCKING_SP_ADMIN1_AUTHORITY and disables the User1 authority. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] Password Admin password | |
| @param[in] PasswordLength Length of password in bytes | |
| @param[out] PasswordFailed Indicates if password failed (start session didn't work) | |
| @param[in] DevicePath The device path for the opal devcie. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportDisableUser( | |
| IN OPAL_SESSION *Session, | |
| IN VOID *Password, | |
| IN UINT32 PasswordLength, | |
| OUT BOOLEAN *PasswordFailed, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(Password); | |
| NULL_CHECK(PasswordFailed); | |
| Ret = OpalUtilDisableUser(Session, Password, PasswordLength, PasswordFailed); | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, PasswordLength, Password); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Enable Opal Feature for the input device. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] Msid Msid | |
| @param[in] MsidLength Msid Length | |
| @param[in] Password Admin password | |
| @param[in] PassLength Length of password in bytes | |
| @param[in] DevicePath The device path for the opal devcie. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportEnableOpalFeature ( | |
| IN OPAL_SESSION *Session, | |
| IN VOID *Msid, | |
| IN UINT32 MsidLength, | |
| IN VOID *Password, | |
| IN UINT32 PassLength, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(Msid); | |
| NULL_CHECK(Password); | |
| Ret = OpalUtilSetAdminPasswordAsSid( | |
| Session, | |
| Msid, | |
| MsidLength, | |
| Password, | |
| PassLength | |
| ); | |
| if (Ret == TcgResultSuccess) { | |
| // | |
| // Enable global locking range | |
| // | |
| Ret = OpalUtilSetOpalLockingRange( | |
| Session, | |
| Password, | |
| PassLength, | |
| OPAL_LOCKING_SP_LOCKING_GLOBALRANGE, | |
| 0, | |
| 0, | |
| TRUE, | |
| TRUE, | |
| FALSE, | |
| FALSE | |
| ); | |
| } | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, PassLength, Password); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Opens a session with OPAL_UID_ADMIN_SP as OPAL_ADMIN_SP_PSID_AUTHORITY, then reverts the device using the RevertSP method. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] KeepUserData TRUE to keep existing Data on the disk, or FALSE to erase it | |
| @param[in] Password Admin password | |
| @param[in] PasswordLength Length of password in bytes | |
| @param[in] Msid Msid | |
| @param[in] MsidLength Msid Length | |
| @param[out] PasswordFailed indicates if password failed (start session didn't work) | |
| @param[in] DevicePath The device path for the opal devcie. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportRevert( | |
| IN OPAL_SESSION *Session, | |
| IN BOOLEAN KeepUserData, | |
| IN VOID *Password, | |
| IN UINT32 PasswordLength, | |
| IN VOID *Msid, | |
| IN UINT32 MsidLength, | |
| OUT BOOLEAN *PasswordFailed, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(Password); | |
| NULL_CHECK(Msid); | |
| NULL_CHECK(PasswordFailed); | |
| Ret = OpalUtilRevert(Session, KeepUserData, Password, PasswordLength, PasswordFailed, Msid, MsidLength); | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, 0, NULL); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Starts a session with OPAL_UID_LOCKING_SP as OPAL_LOCKING_SP_USER1_AUTHORITY or OPAL_LOCKING_SP_ADMIN1_AUTHORITY | |
| and updates the global locking range ReadLocked and WriteLocked columns to FALSE. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] Password Admin or user password | |
| @param[in] PasswordLength Length of password in bytes | |
| @param[in] DevicePath The device path for the opal devcie. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportUnlock( | |
| IN OPAL_SESSION *Session, | |
| IN VOID *Password, | |
| IN UINT32 PasswordLength, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(Password); | |
| Ret = OpalUtilUpdateGlobalLockingRange(Session, Password, PasswordLength, FALSE, FALSE); | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, PasswordLength, Password); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Starts a session with OPAL_UID_LOCKING_SP as OPAL_LOCKING_SP_USER1_AUTHORITY or OPAL_LOCKING_SP_ADMIN1_AUTHORITY | |
| and updates the global locking range ReadLocked and WriteLocked columns to TRUE. | |
| @param[in] Session The opal session for the opal device. | |
| @param[in] Password Admin or user password | |
| @param[in] PasswordLength Length of password in bytes | |
| @param[in] DevicePath The device path for the opal devcie. | |
| **/ | |
| TCG_RESULT | |
| EFIAPI | |
| OpalSupportLock( | |
| IN OPAL_SESSION *Session, | |
| IN VOID *Password, | |
| IN UINT32 PasswordLength, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| TCG_RESULT Ret; | |
| NULL_CHECK(Session); | |
| NULL_CHECK(Password); | |
| Ret = OpalUtilUpdateGlobalLockingRange(Session, Password, PasswordLength, TRUE, TRUE); | |
| if (Ret == TcgResultSuccess && !gInSmm) { | |
| OpalSupportSendPasword (DevicePath, PasswordLength, Password); | |
| } | |
| return Ret; | |
| } | |
| /** | |
| Initialize the communicate Buffer using DataSize and Function. | |
| @param[out] DataPtr Points to the Data in the communicate Buffer. | |
| @param[in] DataSize The Data Size to send to SMM. | |
| @param[in] Function The function number to initialize the communicate Header. | |
| @retval EFI_INVALID_PARAMETER The Data Size is too big. | |
| @retval EFI_SUCCESS Find the specified variable. | |
| **/ | |
| VOID* | |
| OpalInitCommunicateBuffer ( | |
| OUT VOID **DataPtr OPTIONAL, | |
| IN UINTN DataSize, | |
| IN UINTN Function | |
| ) | |
| { | |
| EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; | |
| OPAL_SMM_COMMUNICATE_HEADER *SmmFunctionHeader; | |
| VOID *Buffer; | |
| EDKII_PI_SMM_COMMUNICATION_REGION_TABLE *SmmCommRegionTable; | |
| EFI_MEMORY_DESCRIPTOR *SmmCommMemRegion; | |
| UINTN Index; | |
| UINTN Size; | |
| EFI_STATUS Status; | |
| Buffer = NULL; | |
| Status = EfiGetSystemConfigurationTable ( | |
| &gEdkiiPiSmmCommunicationRegionTableGuid, | |
| (VOID **) &SmmCommRegionTable | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return NULL; | |
| } | |
| ASSERT (SmmCommRegionTable != NULL); | |
| SmmCommMemRegion = (EFI_MEMORY_DESCRIPTOR *) (SmmCommRegionTable + 1); | |
| Size = 0; | |
| for (Index = 0; Index < SmmCommRegionTable->NumberOfEntries; Index++) { | |
| if (SmmCommMemRegion->Type == EfiConventionalMemory) { | |
| Size = EFI_PAGES_TO_SIZE ((UINTN) SmmCommMemRegion->NumberOfPages); | |
| if (Size >= (DataSize + OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data))) { | |
| break; | |
| } | |
| } | |
| SmmCommMemRegion = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) SmmCommMemRegion + SmmCommRegionTable->DescriptorSize); | |
| } | |
| ASSERT (Index < SmmCommRegionTable->NumberOfEntries); | |
| Buffer = (VOID*)(UINTN)SmmCommMemRegion->PhysicalStart; | |
| ASSERT (Buffer != NULL); | |
| SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) Buffer; | |
| CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gOpalPasswordNotifyProtocolGuid); | |
| SmmCommunicateHeader->MessageLength = DataSize + OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data); | |
| SmmFunctionHeader = (OPAL_SMM_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; | |
| SmmFunctionHeader->Function = Function; | |
| if (DataPtr != NULL) { | |
| *DataPtr = SmmFunctionHeader->Data; | |
| } | |
| return Buffer; | |
| } | |
| /** | |
| Send the Data in communicate Buffer to SMM. | |
| @param[in] Buffer Points to the Data in the communicate Buffer. | |
| @param[in] DataSize This Size of the function Header and the Data. | |
| @retval EFI_SUCCESS Success is returned from the functin in SMM. | |
| @retval Others Failure is returned from the function in SMM. | |
| **/ | |
| EFI_STATUS | |
| OpalSendCommunicateBuffer ( | |
| IN VOID *Buffer, | |
| IN UINTN DataSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN CommSize; | |
| EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; | |
| OPAL_SMM_COMMUNICATE_HEADER *SmmFunctionHeader; | |
| EFI_SMM_COMMUNICATION_PROTOCOL *SmmCommunication; | |
| Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &SmmCommunication); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| CommSize = DataSize + OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data); | |
| Status = SmmCommunication->Communicate (SmmCommunication, Buffer, &CommSize); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) Buffer; | |
| SmmFunctionHeader = (OPAL_SMM_COMMUNICATE_HEADER *)SmmCommunicateHeader->Data; | |
| return SmmFunctionHeader->ReturnStatus; | |
| } | |
| /** | |
| Transfer the password to the smm driver. | |
| @param[in] DevicePath The device path for the opal devcie. | |
| @param PasswordLen The input password length. | |
| @param Password Input password buffer. | |
| @retval EFI_SUCCESS Do the required action success. | |
| @retval Others Error occured. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| OpalSupportSendPasword( | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| UINTN PasswordLen, | |
| VOID *Password | |
| ) | |
| { | |
| OPAL_COMM_DEVICE_LIST *Parameter; | |
| VOID *Buffer; | |
| UINTN Length; | |
| EFI_STATUS Status; | |
| UINTN DevicePathLen; | |
| Parameter = NULL; | |
| Buffer = NULL; | |
| if (DevicePath == NULL) { | |
| // | |
| // Assume DevicePath == NULL only when library used by SMM driver | |
| // and should not run to here, just return success. | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| DevicePathLen = GetDevicePathSize (DevicePath); | |
| Length = OFFSET_OF (OPAL_COMM_DEVICE_LIST, OpalDevicePath) + DevicePathLen; | |
| Buffer = OpalInitCommunicateBuffer((VOID**)&Parameter, Length, SMM_FUNCTION_SET_OPAL_PASSWORD); | |
| if (Buffer == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| if (Password != NULL) { | |
| CopyMem((VOID*)Parameter->Password, Password, PasswordLen); | |
| Parameter->PasswordLength = (UINT8)PasswordLen; | |
| } | |
| CopyMem (&Parameter->OpalDevicePath, DevicePath, DevicePathLen); | |
| Status = OpalSendCommunicateBuffer(Buffer, Length); | |
| if (EFI_ERROR(Status)) { | |
| goto EXIT; | |
| } | |
| EXIT: | |
| ZeroMem(Parameter, Length); | |
| return Status; | |
| } | |
| /** | |
| Get saved Opal device list. | |
| @retval return opal device list. | |
| **/ | |
| LIST_ENTRY* | |
| EFIAPI | |
| OpalSupportGetOpalDeviceList ( | |
| VOID | |
| ) | |
| { | |
| return &mDeviceList; | |
| } | |
| /** | |
| Check if the password is full zero. | |
| @param[in] Password Points to the Data Buffer | |
| @retval TRUE This password string is full zero. | |
| @retval FALSE This password string is not full zero. | |
| **/ | |
| BOOLEAN | |
| OpalPasswordIsFullZero ( | |
| IN UINT8 *Password | |
| ) | |
| { | |
| UINTN Index; | |
| for (Index = 0; Index < OPAL_PASSWORD_MAX_LENGTH; Index++) { | |
| if (Password[Index] != 0) { | |
| return FALSE; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Save hdd password to SMM. | |
| @param[in] DevicePath Input device path info for the device. | |
| @param[in] Password The hdd password of attached ATA device. | |
| @param[in] PasswordLength The hdd password length. | |
| @retval EFI_OUT_OF_RESOURCES Insufficient resources to create database record | |
| @retval EFI_SUCCESS The function has been successfully executed. | |
| **/ | |
| EFI_STATUS | |
| OpalSavePasswordToSmm ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| IN UINT8 *Password, | |
| IN UINT8 PasswordLength | |
| ) | |
| { | |
| OPAL_DISK_AND_PASSWORD_INFO *List; | |
| OPAL_DISK_AND_PASSWORD_INFO *Dev; | |
| LIST_ENTRY *Entry; | |
| UINTN DevicePathLen; | |
| DevicePathLen = GetDevicePathSize (DevicePath); | |
| for (Entry = mDeviceList.ForwardLink; Entry != &mDeviceList; Entry = Entry->ForwardLink) { | |
| List = BASE_CR (Entry, OPAL_DISK_AND_PASSWORD_INFO, Link); | |
| if (CompareMem (&List->OpalDevicePath, DevicePath, DevicePathLen) == 0) { | |
| CopyMem(List->Password, Password, OPAL_PASSWORD_MAX_LENGTH); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| Dev = AllocateZeroPool (OFFSET_OF (OPAL_DISK_AND_PASSWORD_INFO, OpalDevicePath) + DevicePathLen); | |
| if (Dev == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Dev->PasswordLength = PasswordLength; | |
| CopyMem(&(Dev->Password), Password, OPAL_PASSWORD_MAX_LENGTH); | |
| CopyMem(&(Dev->OpalDevicePath), DevicePath, DevicePathLen); | |
| InsertHeadList (&mDeviceList, &Dev->Link); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Communication service SMI Handler entry. | |
| This SMI handler provides services for saving HDD password and saving S3 boot script when ready to boot. | |
| @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). | |
| @param[in] RegisterContext Points to an optional handler context which was specified when the | |
| handler was registered. | |
| @param[in, out] CommBuffer A pointer to a collection of Data in memory that will | |
| be conveyed from a non-SMM environment into an SMM environment. | |
| @param[in, out] CommBufferSize The Size of the CommBuffer. | |
| @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers | |
| should still be called. | |
| @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should | |
| still be called. | |
| @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still | |
| be called. | |
| @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SmmOpalPasswordHandler ( | |
| IN EFI_HANDLE DispatchHandle, | |
| IN CONST VOID *RegisterContext, | |
| IN OUT VOID *CommBuffer, | |
| IN OUT UINTN *CommBufferSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| OPAL_SMM_COMMUNICATE_HEADER *SmmFunctionHeader; | |
| UINTN TempCommBufferSize; | |
| UINTN RemainedDevicePathSize; | |
| OPAL_COMM_DEVICE_LIST *DeviceBuffer; | |
| if (CommBuffer == NULL || CommBufferSize == NULL) { | |
| return EFI_SUCCESS; | |
| } | |
| Status = EFI_SUCCESS; | |
| DeviceBuffer = NULL; | |
| TempCommBufferSize = *CommBufferSize; | |
| if (TempCommBufferSize < OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data)) { | |
| return EFI_SUCCESS; | |
| } | |
| SmmFunctionHeader = (OPAL_SMM_COMMUNICATE_HEADER *)CommBuffer; | |
| switch (SmmFunctionHeader->Function) { | |
| case SMM_FUNCTION_SET_OPAL_PASSWORD: | |
| if (TempCommBufferSize <= OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data) + OFFSET_OF (OPAL_COMM_DEVICE_LIST, OpalDevicePath)) { | |
| return EFI_SUCCESS; | |
| } | |
| DeviceBuffer = AllocateCopyPool (TempCommBufferSize - OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data), SmmFunctionHeader->Data); | |
| if (DeviceBuffer == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto EXIT; | |
| } | |
| // | |
| // Validate the DevicePath. | |
| // | |
| RemainedDevicePathSize = TempCommBufferSize - OFFSET_OF (OPAL_SMM_COMMUNICATE_HEADER, Data) | |
| - OFFSET_OF (OPAL_COMM_DEVICE_LIST, OpalDevicePath); | |
| if (!IsDevicePathValid(&DeviceBuffer->OpalDevicePath, RemainedDevicePathSize) || | |
| (RemainedDevicePathSize != GetDevicePathSize (&DeviceBuffer->OpalDevicePath))) { | |
| Status = EFI_SUCCESS; | |
| goto EXIT; | |
| } | |
| if (OpalPasswordIsFullZero (DeviceBuffer->Password) || | |
| DeviceBuffer->PasswordLength == 0 || | |
| DeviceBuffer->PasswordLength > OPAL_PASSWORD_MAX_LENGTH) { | |
| Status = EFI_INVALID_PARAMETER; | |
| goto EXIT; | |
| } | |
| // | |
| // The AsmLfence() call here is to ensure the above range checks for the | |
| // CommBuffer have been completed before calling into OpalSavePasswordToSmm(). | |
| // | |
| AsmLfence (); | |
| Status = OpalSavePasswordToSmm (&DeviceBuffer->OpalDevicePath, DeviceBuffer->Password, DeviceBuffer->PasswordLength); | |
| break; | |
| default: | |
| Status = EFI_UNSUPPORTED; | |
| break; | |
| } | |
| EXIT: | |
| SmmFunctionHeader->ReturnStatus = Status; | |
| if (DeviceBuffer != NULL) { | |
| FreePool (DeviceBuffer); | |
| } | |
| // | |
| // Return EFI_SUCCESS cause only one handler can be trigged. | |
| // so return EFI_WARN_INTERRUPT_SOURCE_PENDING to make all handler can be trigged. | |
| // | |
| return EFI_WARN_INTERRUPT_SOURCE_PENDING; | |
| } | |
| /** | |
| The constructor function. | |
| Register SMI handler when link to SMM driver. | |
| @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| OpalPasswordSupportLibConstructor ( | |
| VOID | |
| ) | |
| { | |
| EFI_SMM_BASE2_PROTOCOL *SmmBase2; | |
| EFI_SMM_SYSTEM_TABLE2 *Smst; | |
| EFI_HANDLE SmmHandle; | |
| EFI_STATUS Status; | |
| Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID**) &SmmBase2); | |
| if (EFI_ERROR (Status)) { | |
| return RETURN_SUCCESS; | |
| } | |
| Status = SmmBase2->InSmm (SmmBase2, &gInSmm); | |
| if (EFI_ERROR (Status)) { | |
| return RETURN_SUCCESS; | |
| } | |
| if (!gInSmm) { | |
| return RETURN_SUCCESS; | |
| } | |
| // | |
| // Good, we are in SMM | |
| // | |
| Status = SmmBase2->GetSmstLocation (SmmBase2, &Smst); | |
| if (EFI_ERROR (Status)) { | |
| return RETURN_SUCCESS; | |
| } | |
| SmmHandle = NULL; | |
| Status = Smst->SmiHandlerRegister (SmmOpalPasswordHandler, &gOpalPasswordNotifyProtocolGuid, &SmmHandle); | |
| ASSERT_EFI_ERROR (Status); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| The Destructor function. | |
| Clean the saved opal device list. | |
| @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| OpalPasswordSupportLibDestructor ( | |
| VOID | |
| ) | |
| { | |
| OPAL_DISK_AND_PASSWORD_INFO *Device; | |
| while (!IsListEmpty (&mDeviceList)) { | |
| Device = BASE_CR (mDeviceList.ForwardLink, OPAL_DISK_AND_PASSWORD_INFO, Link); | |
| RemoveEntryList (&Device->Link); | |
| FreePool (Device); | |
| } | |
| return EFI_SUCCESS; | |
| } |